our_knp/viz/digits-compare/index.html

180 lines
No EOL
30 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru"><head><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><!-- frame-runtime --><!-- /frame-runtime --><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><style>:root{color-scheme:light;box-sizing:border-box;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}html{scroll-padding-top:env(safe-area-inset-top,0px)}body{margin:0;padding:0;font:14px -apple-system,BlinkMacSystemFont,sans-serif;background:#fff;color:#000}img{max-width:100%}[hidden]:not([hidden=until-found i]){display:none!important}</style></head><body>
<title>Цифры на «Алтае»</title>
<meta name="claude:viewport-fit-sides" content="cover">
<link rel="stylesheet" href="./fonts.css">
<style>
/* Layout: one column of three numbered stages (до / внутри / после) under a sticky network switch; pixel views are always on a dark "scope" ground */
:root {
--bg: #F2F4F6; --panel: #FFFFFF; --ink: #16202A; --muted: #586674; --line: #D8DEE4;
--scope: #0D1318; --scope-dim: #27323C; --spike: #CF5A16; --charge: #137F89; --neg: #B0435A; --good: #2E7D4B; --bad: #B23B3B;
--spike-on-scope: #F2903F; --charge-on-scope: #38BAC3; --neg-on-scope: #E06A80;
--f-display: "Rubik", "Trebuchet MS", sans-serif; --f-body: "IBM Plex Sans", "Segoe UI", Arial, sans-serif; --f-mono: "JetBrains Mono", "Courier New", monospace;
}
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) {
--bg: #0E1318; --panel: #161D24; --ink: #E6EBEF; --muted: #95A3AF; --line: #27323C;
--scope: #080C10; --scope-dim: #222C35; --spike: #F08A3E; --charge: #3DBDC6; --neg: #E07085; --good: #5DBE7C; --bad: #EE6B6B; color-scheme: dark } }
:root[data-theme="dark"] {
--bg: #0E1318; --panel: #161D24; --ink: #E6EBEF; --muted: #95A3AF; --line: #27323C;
--scope: #080C10; --scope-dim: #222C35; --spike: #F08A3E; --charge: #3DBDC6; --neg: #E07085; --good: #5DBE7C; --bad: #EE6B6B; color-scheme: dark }
* { box-sizing: border-box }
body { background: var(--bg); color: var(--ink); font: 16px/1.5 var(--f-body); }
body::before { content: ""; position: fixed; inset: 0 0 auto; height: env(safe-area-inset-top, 0px); z-index: 10; background: var(--bg) }
.wrap { max-width: 1120px; margin: 0 auto; padding-left: max(16px, env(safe-area-inset-left, 0px)); padding-right: max(16px, env(safe-area-inset-right, 0px)); padding-block: 28px 64px; display: flex; flex-direction: column; gap: 28px }
h1, h2, h3 { font-family: var(--f-display); font-weight: 600; margin: 0; text-wrap: balance; line-height: 1.15 }
h1 { font-size: clamp(28px, 4.2vw, 42px) }
h2 { font-size: 24px }
h3 { font-size: 17px }
p { margin: 0 }
.lead { color: var(--muted); max-width: 68ch; font-size: 17px }
.mono { font-family: var(--f-mono); font-variant-numeric: tabular-nums }
.muted { color: var(--muted) }
.small { font-size: 14px }
.eyebrow { font-family: var(--f-mono); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--spike) }
.head { display: flex; flex-direction: column; gap: 10px }
.switch { position: sticky; top: env(safe-area-inset-top, 0px); z-index: 5; background: var(--bg); padding-block: 8px; display: flex; flex-wrap: wrap; gap: 8px }
.switch button { flex: 1 1 240px; text-align: left; font: inherit; color: var(--ink); background: var(--panel); border: 1.5px solid var(--line); border-radius: 12px; padding: 10px 14px; cursor: pointer; display: flex; flex-direction: column; gap: 2px }
.switch button b { font-family: var(--f-display); font-weight: 600; font-size: 16px }
.switch button span { color: var(--muted); font-size: 14px }
.switch button[aria-pressed="true"] { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink) }
button:focus-visible, canvas:focus-visible, input:focus-visible { outline: 2px solid var(--charge); outline-offset: 2px }
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 20px; display: flex; flex-direction: column; gap: 16px; min-width: 0 }
.row { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start }
.row > * { min-width: 0 }
.grow { flex: 1 1 280px }
.btn { font: inherit; font-size: 15px; color: var(--ink); background: var(--bg); border: 1px solid var(--line); border-radius: 10px; padding: 7px 14px; cursor: pointer }
.btn:hover { border-color: var(--muted) }
.btn.primary { background: var(--ink); color: var(--panel); border-color: var(--ink) }
.pad { width: 280px; max-width: 100%; aspect-ratio: 1; display: block; border-radius: 14px; background: #000; touch-action: none; cursor: crosshair }
.px { image-rendering: pixelated; background: var(--scope); border-radius: 10px; display: block }
.fig { display: flex; flex-direction: column; gap: 6px; align-items: flex-start }
.cap { font-size: 13px; color: var(--muted); max-width: 22ch; line-height: 1.35 }
.gal { display: flex; flex-wrap: wrap; gap: 5px }
.gal button { padding: 2px; border: 2px solid var(--line); border-radius: 9px; background: var(--scope); cursor: pointer; line-height: 0 }
.gal button[aria-pressed="true"] { border-color: var(--spike) }
.gal canvas { width: 34px; height: 34px; image-rendering: pixelated; border-radius: 5px }
.answer { display: grid; grid-template-columns: auto 1fr; gap: 4px 18px; align-items: center }
.answer .digit { grid-row: span 3; font-family: var(--f-mono); font-size: 84px; font-weight: 600; line-height: 1 }
.verdict { font-weight: 600 }
.verdict.ok { color: var(--good) } .verdict.no { color: var(--bad) }
.facts { display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: 14px; color: var(--muted) }
.facts b { color: var(--ink); font-family: var(--f-mono); font-weight: 600 }
.stage-title { display: flex; align-items: baseline; gap: 12px }
.stage-title .n { font-family: var(--f-mono); font-size: 14px; color: var(--muted); border: 1px solid var(--line); border-radius: 99px; padding: 1px 9px }
.flow { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px }
.flow .arrow { align-self: center; color: var(--muted); font-size: 22px; padding-top: 0 }
.transport { display: flex; flex-wrap: wrap; align-items: center; gap: 12px }
.transport input[type=range] { flex: 1 1 200px; accent-color: var(--spike) }
.story { background: var(--bg); border-radius: 12px; padding: 12px 16px; max-width: 80ch }
.views { display: grid; grid-template-columns: auto auto minmax(0, 1fr); gap: 20px; align-items: start }
@media (max-width: 760px) { .views { grid-template-columns: 1fr 1fr } .views > .outs { grid-column: 1 / -1 } }
.lanes { display: flex; flex-direction: column; gap: 3px; width: 100% }
.lane { display: grid; grid-template-columns: 18px minmax(60px, 1fr) 64px; gap: 10px; align-items: center; padding: 3px 8px; border-radius: 8px; font-family: var(--f-mono); font-size: 14px }
.lane.fire { background: color-mix(in srgb, var(--spike) 16%, transparent) }
.lane.lead { box-shadow: inset 0 0 0 1.5px var(--good) }
.lane .bar { height: 12px; border-radius: 4px; background: var(--line); position: relative; overflow: hidden }
.lane .bar i { position: absolute; inset: 0 auto 0 0; background: var(--charge) }
.lane .bar i.votes { background: var(--spike) }
.lane .dots { display: flex; gap: 3px; flex-wrap: wrap }
.lane .dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--spike) }
.lane b { font-weight: 400; color: var(--muted); text-align: right }
.colgrid { display: grid; grid-template-columns: 16px auto; gap: 0 6px; align-items: stretch }
.colgrid .labels { display: grid; grid-template-rows: repeat(10, 1fr); font-family: var(--f-mono); font-size: 12px; color: var(--muted); text-align: right }
.colgrid .labels span { display: flex; align-items: center; justify-content: flex-end }
.legend { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 13px; color: var(--muted) }
.legend span::before { content: ""; display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; background: var(--c) }
.ramp { display: inline-block; width: 120px; height: 10px; border-radius: 3px; vertical-align: -1px; margin: 0 6px }
.inspect { display: grid; grid-template-columns: auto auto minmax(0, 1fr); gap: 18px; align-items: start; border-top: 1px solid var(--line); padding-top: 16px }
@media (max-width: 760px) { .inspect { grid-template-columns: 1fr 1fr } .inspect > .txt { grid-column: 1 / -1 } }
.voters { display: flex; flex-wrap: wrap; gap: 8px }
.voters button { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 4px; border: 1.5px solid var(--line); border-radius: 10px; background: var(--panel); color: var(--ink); cursor: pointer; font: 12px var(--f-mono) }
.voters button[aria-pressed="true"] { border-color: var(--spike) }
.voters canvas { width: 56px; height: 56px; image-rendering: pixelated; border-radius: 6px; background: var(--scope) }
.spark { width: 100%; max-width: 360px; height: 90px; display: block }
.after { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px }
@media (max-width: 760px) { .after { grid-template-columns: 1fr } }
.cost { display: flex; flex-direction: column; gap: 10px }
.cost .line { display: grid; grid-template-columns: 1fr auto; gap: 12px; border-bottom: 1px solid var(--line); padding-bottom: 6px; font-size: 15px }
.cost .line b { font-family: var(--f-mono); font-weight: 600 }
.train p + p { margin-top: 8px }
.train ol { margin: 8px 0 0; padding-left: 22px; display: flex; flex-direction: column; gap: 6px; max-width: 80ch }
@media (prefers-reduced-motion: reduce) { * { transition: none !important } }
</style>
<div class="wrap">
<header class="head">
<p class="eyebrow">KNP · AltAI · импульсные сети</p>
<h1>Как сеть на «Алтае» узнаёт цифру</h1>
<p class="lead">Нарисуйте цифру или выберите тестовую. Ниже видно всё, что с ней происходит: как обычный процессор готовит картинку, как спайки идут через сеть такт за тактом и как из них получается ответ. Расчёт точный и целочисленный, по тем же правилам, что на чипе.</p>
</header>
<nav class="switch" id="switch" aria-label="Сеть"><button type="button" data-net="snn" aria-pressed="true"><b>Последняя версия: Сеть по тактам</b><span>97,4% на тесте · 8 тактов · обучена на компьютере</span></button><button type="button" data-net="cols" aria-pressed="false"><b>Первая версия: Колонки STDP</b><span>92,2% на тесте · 15 тактов · обучена в KNP</span></button></nav>
<section class="panel">
<div class="row">
<div class="fig">
<canvas id="pad" class="pad" width="280" height="280" aria-label="Поле для рисования цифры"></canvas>
<div class="row" style="gap:10px;align-items:center"><button class="btn" id="clear">Очистить</button><span class="small muted" id="src">Тестовая цифра «3», №381</span></div>
</div>
<div class="grow" style="display:flex;flex-direction:column;gap:16px">
<div class="answer" aria-live="polite">
<span class="digit" id="ans">3</span>
<span class="small muted" id="ansNet">Последняя версия: Сеть по тактам</span>
<span class="verdict ok" id="verdict">верно</span>
<span class="small muted" id="ansNote">ответ ясен с такта 4 из 8</span>
</div>
<div class="facts" id="netFacts"><span>тактов <b>9</b></span><span>входных спайков <b>142</b></span><span>спайков нейронов <b>42</b></span><span>сложений на чипе <b>19&nbsp;604</b></span></div>
<div style="display:flex;flex-direction:column;gap:6px">
<span class="small muted">Тестовые цифры MNIST — сеть их не видела при обучении</span>
<div class="gal" id="gal" role="group" aria-label="Тестовые цифры"><button type="button" aria-label="Цифра 4, тест №447" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 7, тест №124" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 3, тест №381" aria-pressed="true"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 9, тест №92" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 3, тест №158" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 2, тест №222" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 8, тест №290" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 7, тест №0" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 0, тест №3" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 4, тест №4" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 4, тест №115" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 2, тест №321" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 2, тест №1" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 1, тест №2" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 1, тест №5" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 4, тест №6" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 9, тест №7" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 5, тест №8" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 9, тест №9" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 0, тест №10" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 6, тест №11" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 9, тест №12" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 0, тест №13" aria-pressed="false"><canvas width="28" height="28"></canvas></button><button type="button" aria-label="Цифра 1, тест №14" aria-pressed="false"><canvas width="28" height="28"></canvas></button></div>
</div>
</div>
</div>
</section>
<section class="panel" aria-labelledby="t1">
<div class="stage-title"><span class="n">1</span><h2 id="t1">До сети: обычный процессор готовит вход</h2></div>
<p class="muted" id="beforeText">Сеть училась на цифрах определённого вида, поэтому процессор сначала приводит рисунок к нему: вписывает в квадрат 20×20, ставит центр масс в центр поля 28×28, выпрямляет наклон и уменьшает с усреднением, чтобы края стали серыми. Потом каждый пиксель ярче 32 получает момент своего единственного спайка: чем ярче, тем раньше.</p>
<div class="flow" id="flow"><div class="fig"><canvas width="28" height="28" class="px" aria-label="1. исходная цифра MNIST" style="width: 140px; height: 140px; max-width: 100%;"></canvas><span class="cap">1. исходная цифра MNIST</span></div><span class="arrow" aria-hidden="true">→</span><div class="fig"><canvas width="28" height="28" class="px" aria-label="2. в квадрат 20×20, центр масс в центр" style="width: 140px; height: 140px; max-width: 100%;"></canvas><span class="cap">2. в квадрат 20×20, центр масс в центр</span></div><span class="arrow" aria-hidden="true">→</span><div class="fig"><canvas width="28" height="28" class="px" aria-label="3. наклон выпрямлен" style="width: 140px; height: 140px; max-width: 100%;"></canvas><span class="cap">3. наклон выпрямлен</span></div><span class="arrow" aria-hidden="true">→</span><div class="fig"><canvas width="28" height="28" class="px" aria-label="4. уменьшение с усреднением: вход сети" style="width: 140px; height: 140px; max-width: 100%;"></canvas><span class="cap">4. уменьшение с усреднением: вход сети</span></div><span class="arrow" aria-hidden="true">→</span><div class="fig"><canvas width="28" height="28" class="px" aria-label="Моменты спайков" style="width: 140px; height: 140px; max-width: 100%;"></canvas><span class="cap">5. моменты спайков: яркое стреляет на такте 0, тусклое — на 7-м. Всего 142 спайков</span></div></div>
</section>
<section class="panel" aria-labelledby="t2">
<div class="stage-title"><span class="n">2</span><h2 id="t2">Внутри сети: такт за тактом</h2></div>
<div class="transport">
<button class="btn primary" id="play">▶ Пуск</button>
<input type="range" id="tick" min="0" max="8" value="0" aria-label="Такт">
<span class="mono" id="tickLab">такт 8 из 8</span>
</div>
<p class="story" id="story">Такт 8, последний. Новых пикселей нет: выходы получают последние спайки скрытого слоя. Выходы получили 1 спайков скрытого слоя с прошлого такта, выстрелили «3». Лидер «3» больше не сменится — ответ ясен уже сейчас.</p>
<div class="views" id="views"><div class="fig"><span class="cap">784 входа: пиксели, стреляющие сейчас</span><canvas width="28" height="28" class="px" aria-label="Входные спайки на этом такте" style="width: 168px; height: 168px; max-width: 100%;"></canvas></div><div class="fig"><span class="cap">256 скрытых нейронов (нажмите на любой)</span><canvas width="16" height="16" class="px" aria-label="256 скрытых нейронов" tabindex="0" style="width: 168px; height: 168px; max-width: 100%; cursor: pointer;"></canvas></div><div class="fig outs"><span class="cap">10 выходов: заряд до порога 8 и спайки</span><div class="lanes"><div class="lane"><span>0</span><div class="bar" title="заряд 0 из 8"><i style="width:0%"></i></div><div class="dots"><b>0</b></div></div><div class="lane"><span>1</span><div class="bar" title="заряд 1 из 8"><i style="width:12.5%"></i></div><div class="dots"><b>0</b></div></div><div class="lane"><span>2</span><div class="bar" title="заряд 3 из 8"><i style="width:37.5%"></i></div><div class="dots"><i></i><b></b></div></div><div class="lane fire lead"><span>3</span><div class="bar" title="заряд 0 из 8"><i style="width:0%"></i></div><div class="dots"><i></i><i></i><i></i><i></i><i></i><b></b></div></div><div class="lane"><span>4</span><div class="bar" title="заряд 2 из 8"><i style="width:25%"></i></div><div class="dots"><b>0</b></div></div><div class="lane"><span>5</span><div class="bar" title="заряд 3 из 8"><i style="width:37.5%"></i></div><div class="dots"><b>0</b></div></div><div class="lane"><span>6</span><div class="bar" title="заряд 0 из 8"><i style="width:0%"></i></div><div class="dots"><b>0</b></div></div><div class="lane"><span>7</span><div class="bar" title="заряд 4 из 8"><i style="width:50%"></i></div><div class="dots"><i></i><i></i><i></i><b></b></div></div><div class="lane"><span>8</span><div class="bar" title="заряд 3 из 8"><i style="width:37.5%"></i></div><div class="dots"><b>0</b></div></div><div class="lane"><span>9</span><div class="bar" title="заряд 2 из 8"><i style="width:25%"></i></div><div class="dots"><i></i><b></b></div></div></div></div></div>
<div class="legend" id="legend"><span style="--c: var(--spike-on-scope)">спайк на этом такте</span><span style="--c: var(--charge-on-scope)">заряд нейрона</span><span style="--c: var(--scope-dim)">пиксель уже выстрелил</span></div>
<div class="inspect" id="inspect"><div class="fig"><span class="cap">Нейрон №112: что он ищет</span><canvas width="28" height="28" class="px" aria-label="Веса нейрона 112" style="width: 168px; height: 168px; max-width: 100%;"></canvas><span class="cap">бирюзовый — вес «+», красный — вес «−»</span></div><div class="fig"><span class="cap">что дала ему эта картинка</span><canvas width="28" height="28" class="px" aria-label="Что этот нейрон получил от картинки" style="width: 168px; height: 168px; max-width: 100%;"></canvas><span class="cap">сумма весов по закрашенным пикселям: +31</span></div><div class="txt" style="display:flex;flex-direction:column;gap:10px"><h3>Нейрон №112: 3 спайков за картинку</h3><p class="small muted">Скрытый нейрон — это детектор кусочка формы. Бирюзовые пиксели заряжают его, красные — разряжают. Набрал 8 — выстрелил и отправил спайк выходам.</p><p class="small"><b>Связи к выходам: </b>0: −2 · 1: +2 · 3: +4 · 4: −1 · 5: −1 · 6: +2 · 7: +2 · 8: −2 · 9: −2</p><div><span class="cap" style="max-width:none">заряд по тактам: растёт, когда приходят веса; дошёл до порога — спайк (оранжевая точка), и порог вычитается. Красный пунктир — сумма за такт оказалась ниже нуля, и заряд обрезан до 0: хранить минус нейрону не разрешено настройкой</span><svg viewBox="0 0 360 110" class="spark" role="img" aria-label="Заряд нейрона по тактам"><line x1="16" x2="344" y1="42.42857142857143" y2="42.42857142857143" stroke="#95A3AF" stroke-dasharray="4 4" stroke-width="1"></line><text x="344" y="38.42857142857143" text-anchor="end" font-size="11" fill="#95A3AF" font-family="JetBrains Mono, monospace">порог 8</text><line x1="16" x2="344" y1="84.71428571428571" y2="84.71428571428571" stroke="#95A3AF" stroke-width="1" opacity="0.5"></line><text x="344" y="96.71428571428571" text-anchor="end" font-size="11" fill="#95A3AF" font-family="JetBrains Mono, monospace">0</text><line x1="303" x2="303" y1="84.71428571428571" y2="90" stroke="#E07085" stroke-width="2" stroke-dasharray="3 3"></line><circle cx="303" cy="90" r="2.5" fill="#E07085"></circle><path d="M8,84.71428571428571 L16,53 L16,53 L57,16 L57,58.285714285714285 L98,53 L98,53 L139,42.42857142857143 L139,84.71428571428571 L180,63.57142857142857 L180,63.57142857142857 L221,58.285714285714285 L221,58.285714285714285 L262,37.142857142857146 L262,79.42857142857143 L303,84.71428571428571 L303,84.71428571428571 L344,84.71428571428571 L344,84.71428571428571" fill="none" stroke="#3DBDC6" stroke-width="2" stroke-linejoin="round"></path><circle cx="57" cy="16" r="4" fill="#F08A3E"></circle><rect x="55" y="2" width="4" height="10" rx="1" fill="#F08A3E"></rect><circle cx="139" cy="42.42857142857143" r="4" fill="#F08A3E"></circle><rect x="137" y="2" width="4" height="10" rx="1" fill="#F08A3E"></rect><circle cx="262" cy="37.142857142857146" r="4" fill="#F08A3E"></circle><rect x="260" y="2" width="4" height="10" rx="1" fill="#F08A3E"></rect><line x1="344" x2="344" y1="0" y2="94" stroke="#E6EBEF" stroke-width="1" opacity="0.35"></line><text x="16" y="107" text-anchor="start" font-size="11" fill="#95A3AF" font-family="JetBrains Mono, monospace">такт 0</text><text x="344" y="107" text-anchor="end" font-size="11" fill="#95A3AF" font-family="JetBrains Mono, monospace">такт 8</text></svg></div><h3>Больше всего за «3» проголосовали</h3><div class="voters"><button type="button" aria-pressed="true" aria-label="Нейрон 112"><canvas width="28" height="28"></canvas><span>№112 +12</span></button><button type="button" aria-pressed="false" aria-label="Нейрон 150"><canvas width="28" height="28"></canvas><span>№150 +8</span></button><button type="button" aria-pressed="false" aria-label="Нейрон 109"><canvas width="28" height="28"></canvas><span>№109 +4</span></button><button type="button" aria-pressed="false" aria-label="Нейрон 168"><canvas width="28" height="28"></canvas><span>№168 +4</span></button><button type="button" aria-pressed="false" aria-label="Нейрон 120"><canvas width="28" height="28"></canvas><span>№120 +2</span></button><button type="button" aria-pressed="false" aria-label="Нейрон 165"><canvas width="28" height="28"></canvas><span>№165 +2</span></button></div></div></div>
</section>
<section class="panel" aria-labelledby="t3">
<div class="stage-title"><span class="n">3</span><h2 id="t3">После сети: ответ и цена</h2></div>
<div class="after">
<div style="display:flex;flex-direction:column;gap:10px"><p id="ruleText">Процессор считает спайки десяти выходных нейронов. Ответ — цифра, чей нейрон выстрелил больше всех; при равенстве — тот, кто выстрелил раньше.</p><div class="lanes" id="final"><div class="lane"><span>0</span><div class="bar"><i class="votes" style="width:0%"></i></div><b>0 сп.</b></div><div class="lane"><span>1</span><div class="bar"><i class="votes" style="width:0%"></i></div><b>0 сп.</b></div><div class="lane"><span>2</span><div class="bar"><i class="votes" style="width:20%"></i></div><b>1 сп.</b></div><div class="lane lead"><span>3</span><div class="bar"><i class="votes" style="width:100%"></i></div><b>5 сп.</b></div><div class="lane"><span>4</span><div class="bar"><i class="votes" style="width:0%"></i></div><b>0 сп.</b></div><div class="lane"><span>5</span><div class="bar"><i class="votes" style="width:0%"></i></div><b>0 сп.</b></div><div class="lane"><span>6</span><div class="bar"><i class="votes" style="width:0%"></i></div><b>0 сп.</b></div><div class="lane"><span>7</span><div class="bar"><i class="votes" style="width:60%"></i></div><b>3 сп.</b></div><div class="lane"><span>8</span><div class="bar"><i class="votes" style="width:0%"></i></div><b>0 сп.</b></div><div class="lane"><span>9</span><div class="bar"><i class="votes" style="width:20%"></i></div><b>1 сп.</b></div></div></div>
<div class="cost" id="cost"><h3>Сколько работы у чипа</h3><div class="line"><span>входных спайков</span><b>142</b></div><div class="line"><span>спайков нейронов</span><b>42</b></div><div class="line"><span>сложений «спайк → вес»</span><b>19&nbsp;604</b></div><div class="line"><span>обычная сеть 784→256→10, умножений</span><b>203&nbsp;264</b></div><p class="small muted">Каждый входной спайк задевает в среднем ≈139 из 256 скрытых нейронов: у остальных вес 0, и такая связь на чипе не создаётся. Поэтому работы примерно 143 × 139 ≈ 20 тыс. сложений.</p></div>
</div>
</section>
<section class="panel train" aria-labelledby="t4">
<h2 id="t4">Как эта сеть обучена</h2>
<div id="train"><p>Обучена на обычном компьютере (PyTorch), а не в KNP: на чип загружаются готовые целые веса. 55 000 цифр MNIST, 30 эпох (проходов по всем цифрам), около 17 минут на процессоре без видеокарты.</p><ol><li>При обучении сеть считает ровно как чип: 8 тактов, целые веса, порог 8, вычитание порога после спайка, задержка в такт между слоями.</li><li>Спайк — это ступенька, у неё нет производной. При расчёте ошибки ступенька подменяется плавной «горкой» (суррогатный градиент), и сеть понимает, куда сдвигать веса.</li><li>С 9-й эпохи веса округляются до допустимых на чипе 0, ±1, ±2, ±4, и сеть доучивается уже с ними. Плюс небольшой штраф за лишние спайки.</li><li>Половина обучающих цифр перерисована пером постоянной толщины, как мышью, плюс сдвиги и повороты. Поэтому сеть хорошо узнаёт рисунки: 95,9% на перерисованном тесте.</li><li>Лучшая эпоха выбрана на 5000 отложенных цифрах отдельным целочисленным симулятором; тест открыт один раз.</li></ol></div>
</section>
</div>
<script src="./app.js"></script>
</body></html>