Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Server
b5d3855c2c Host digit comparison artifact and expose all KNP demos through Caddy 2026-10-21 14:43:13 +00:00
58 changed files with 2514 additions and 0 deletions

View file

@ -0,0 +1,11 @@
# Состояние 21 октября 2026
После перезагрузки user@10.9.8.230 по запросу пользователя восстановлены все готовые демо: http://10.9.8.230:8080/altai_viz.html, /aug_viz.html, /fast_viz.html; http://10.9.8.230:8081/native_viz.html; http://10.9.8.230:8082/. Проверены внешние HTTP200 и настоящие API-инференсы CPU/GoldenModel MNIST и CUDA Pet lab. PID в штатных runtime/server.pid и runtime/web-server.pid. Запуск nohup, автозапуска после reboot нет. Подробности reports/2026-10-21/журнал.md.
Все ограничения/цифры 15 октября сохраняются. Коллекций нет, транспорт completed, модель и данные неизменны. Муха отложена. viz/app.html является незаполненным шаблоном, не отдельным демо. Готового транспортного web-демо нет. Никаких commit/push.
Добавлена точная самостоятельная копия Claude Artifact «Цифры на Алтае»: http://knp-digits.t.snw.su/ и http://10.9.8.230:8080/digits-compare/. Источник mnist-artifact/, README и SOURCE.json; экспорт Chrome в reports/2026-10-21/artifact. Браузерный симулятор STDP vs SNN8, не новый KNP/GM результат. При развёртывании сохранены веса и исходный код расчёта, локализованы шрифты, убрана оболочка Claude. Browser smoke48 примеров/mouse/touch/темы/инспектор прошёл. Сайт на станции viz/digits-compare; публичная копия /opt/knp-digits через Caddy. Ничего не коммитили/пушили.
Исходники вики сохранены по просьбе пользователя без запуска: /root/knp_docs, origin ssh://git@git.snw.su:30301/pgups/knp_docs.git, main540cdb9fed49a1c910437e0944454e8a56f88ffd. Полный verified Git bundle reports/2026-10-21/knp_docs.bundle. Пользователь сказал «сохрани пока», продолжение/развёртывание репозитория не запрошено.
Все шесть KNPдемо теперь через Caddy reverse_proxy на рабочую станцию: http://knp-columns.t.snw.su/, http://knp-shifts.t.snw.su/, http://knp-fast.t.snw.su/, http://knp-native.t.snw.su/, http://knp-pets.t.snw.su/, http://knp-digits.t.snw.su/. Последний больше не file_server /opt/knp-digits, а proxy8080/digits-compare. Старый статический backup /opt/knp-digits сохранён. Caddy validate/reload и HTTP/API проверки успешны; wiki не проксировалась. Актуальная вики только knp.snw.su, fs.snw.su устарел.

7
deployment/README.md Normal file
View file

@ -0,0 +1,7 @@
# KNP demo hosting
`knp-demos.Caddyfile` contains the six HTTP reverse proxy hosts deployed on 2026-10-21. Import these blocks into the hosting server's Caddy configuration, preserving its other sites. Run `caddy validate --config /etc/caddy/Caddyfile` before reloading Caddy.
Upstream is `10.9.8.230`: browser demos and `digits-compare` on port8080, native CPU/GoldenModel API on8081, Pet lab CUDA API on8082. Start commands and limitations are in the experiment READMEs and reports/2026-10-21/журнал.md. The upstream Python servers currently require manual restart after a machine reboot.
The wiki is maintained separately at https://knp.snw.su/ and is not included in these proxy hosts. Its Git source is ssh://git@git.snw.su:30301/pgups/knp_docs.git.

View file

@ -0,0 +1,30 @@
# KNP demo reverse proxies. Import into the main Caddyfile.
http://knp-digits.t.snw.su {
rewrite * /digits-compare{uri}
reverse_proxy 10.9.8.230:8080
}
http://knp-columns.t.snw.su {
rewrite / /altai_viz.html
reverse_proxy 10.9.8.230:8080
}
http://knp-shifts.t.snw.su {
rewrite / /aug_viz.html
reverse_proxy 10.9.8.230:8080
}
http://knp-fast.t.snw.su {
rewrite / /fast_viz.html
reverse_proxy 10.9.8.230:8080
}
http://knp-native.t.snw.su {
rewrite / /native_viz.html
reverse_proxy 10.9.8.230:8081
}
http://knp-pets.t.snw.su {
reverse_proxy 10.9.8.230:8082
}

16
mnist-artifact/README.md Normal file
View file

@ -0,0 +1,16 @@
# Цифры на «Алтае»: копия Claude Artifact
Публичная копия: http://knp-digits.t.snw.su/
На рабочей машине: http://10.9.8.230:8080/digits-compare/
Источник пользователя: Chrome Ctrl+S экспорт https://claude.ai/artifact/Vc1q1WCZuPY2QAHBSGtrKF, архив получен с http://drop.t.snw.su/mearl. Оригинал сохранён в reports/2026-10-21/artifact/chrome-export.zip, SHA256 в SOURCE.json.
`python3 mnist-artifact/prepare.py` извлекает внутренний saved_resource.html, удаляет только служебный runtime Claude, локализует шрифты и очищает сохранённые Chrome кнопки перед их повторным созданием приложением. Исходный JS приложения, веса, тексты и расчёт сохраняются; перед исходным JS добавлено очищение двух контейнеров. Добавлены lang=ru и viewport для самостоятельного открытия страницы.
Это браузерный целочисленный симулятор двух моделей: STDP-колонки и temporal SNN8. Подписанные в артефакте test accuracy 92,2%/97,4% взяты из источника, при переносе заново не измерялись. Этот перенос не является KNP/CUDA/GoldenModel проверкой новых весов и не связан с подтверждённым native MNIST 97,76%.
В исходнике Clear очищает поле рисования, сохраняя предыдущий результат до нового ввода. Это поведение артефакта сохранено.
Проверки: 24 галерейных изображения в каждой из двух сетей, переключение моделей, инспектор нейрона, ползунок тактов, mouse/touch рисование, desktop/mobile 390px без горизонтального переполнения, светлая/тёмная темы, отсутствие JS errors и внешних запросов. Артефакты проверки в reports/2026-10-21/artifact/browser-checks.json и PNG.
Хостинг: Caddy /etc/caddy/Caddyfile, отдельный HTTP block knp-digits.t.snw.su, файлы /opt/knp-digits. Caddy validate и reload успешны. На станции статические файлы размещены в /home/user/KNP/viz/digits-compare, все 21 SHA256 сверены. Существующие серверы, модели, библиотеки и данные не менялись. Git commit/push не выполнялись.

View file

@ -0,0 +1,37 @@
{
"source": "https://claude.ai/artifact/Vc1q1WCZuPY2QAHBSGtrKF",
"export_url": "http://drop.t.snw.su/mearl",
"archive_sha256": "94bf2e99581aec06cbec3078c50c93dcffd1907e40b954006d1fa444aae4eaf7",
"inner_html_sha256": "4807bae6973ccc560b51aaf7c60f4b2b8be8d529aeb6848db86036522df2249d",
"original_app_sha256": "61ea4ea4db5926c7c8a17f7ce057707fab9b01c6a121fd2e619c42d91b92846c",
"changes": [
"remove Claude iframe runtime",
"clear saved controls before original startup",
"local font files",
"viewport and Russian document language"
],
"inference_code_and_weights": "preserved verbatim after the control-reset prelude",
"files": {
"app.js": "4ef53f5ebcac9b3b489daaf2dfb409a9d520fcc80e5e9502c4e789dce86e9744",
"fonts/04b5819e5fda60389774.woff2": "197b6dbe5c6bec7276c59b02d70a16ee6acd44322fc2f8d0226c07f8d57976c2",
"fonts/0c46fc34202128a8b356.woff2": "bdb2b33ab916e9e7b42edb61f8aea5f5dd83ad773d6ee4e9b55665daf8954ce8",
"fonts/13ff14c8cbbde52dd267.woff2": "7d0037a170882a49e65a45c38ef195237bb07b1cc791d078379f0ac0d7ae1935",
"fonts/23f931dc9e4c044ca281.woff2": "35e30483bb79be6fd8de0fec4e038fda9e637fc7337f6ff61418c7d08077878b",
"fonts/547062430ce141b89109.woff2": "691cd1d9b4c0cdf31a1dcf04259c86f92c85e69f6622abab7964d81e36890691",
"fonts/560980fcddc4e6f493d8.woff2": "f598dd0700f155e2facfaa8ecfacace397d4c2c153e9ce37ab7ad310e8505bc2",
"fonts/6b5e37b52d26eeac6a19.woff2": "62213be8a78b42f1e29d1452d91e2f8b3e745572a9dd98d3941e39fa00b37d76",
"fonts/6d5e7fb80500edbb2ccc.woff2": "e2291e842cf5af167122a22881a740c7f2dda7716f1e8cd76680264f4a859470",
"fonts/90a469f8becd8517007a.woff2": "f5052e4870bf211fc22df5b85e791a292a77b9baef8cef1dc5a8e62a783ea1e0",
"fonts/99228c3b93e78c59da72.woff2": "73e7f7b1c980416ae0c2461268b14c7cfcce0668e683dc43fbb11a82a227b9f2",
"fonts/99e400330f36ae77c882.woff2": "0a557721b1f8b36d3f3f84442689a71ca4a744300abcb46a1953f51bfc663b66",
"fonts/b19c2080652a3c2b1288.woff2": "db5ff4db83e580426280e9337a58dc57d3a83784a1b03ad80914651594441d52",
"fonts/c062627ec16ce1b40510.woff2": "09472f217ad1ebb61a7669cf532f9f2f80685e07652ee862fde9a79665aef479",
"fonts/c4064400db3750ce826c.woff2": "c89b9cc0bc6262bd4f8d8494b6961601f3aefa829d08c2e3635f4d501d3a47c2",
"fonts/cc27b30471250057c5e6.woff2": "83c005d49d8a6a50474c73a5a36ac0468076e9c4a29da7bdb14995d80560a5be",
"fonts/eb83d8d40af4411f395f.woff2": "e17cfd15fb96909d64095015f958207063a0c07191da3512df7d560a781aebdf",
"fonts/f582230492bfd11eff7b.woff2": "d160e20920ae4d6556518d352d3af27a74e9b0de3d8fe17b1c1044fc75aa2f81",
"fonts/fd8b74284fe6ff9705c0.woff2": "9409ee21811f0f5272b7e1d828fe8838005b0573481ef906e7e55b63f6610ab0",
"fonts.css": "564d3c95a3b80d7c5ae21347ab77028cb10125812c24520a12f584fa880a6f61",
"index.html": "cfb514c642469601364b4482d3ce7cde715ef8fbbf0b0f099fb5dd43466b6eb3"
}
}

63
mnist-artifact/prepare.py Normal file
View file

@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Extract the saved inner artifact, preserving its UI and inference code."""
from pathlib import Path
import concurrent.futures
import hashlib
import json
import re
import urllib.request
import zipfile
ROOT = Path(__file__).resolve().parent
ARCHIVE = ROOT.parent / 'reports/2026-10-21/artifact/chrome-export.zip'
OUT = ROOT / 'site'
OUT.mkdir(exist_ok=True)
with zipfile.ZipFile(ARCHIVE) as z:
names = z.namelist()
inner = next(n for n in names if n.endswith('/saved_resource.html'))
original = z.read(inner).decode('utf-8')
css = z.read(next(n for n in names if n.endswith('/css2'))).decode('utf-8')
scripts = re.findall(r'<script\b[^>]*>(.*?)</script>', original, re.S)
assert len(scripts) == 3 and scripts[2].lstrip().startswith('const D = ')
app = scripts[2]
# Chrome saves the already populated DOM. The original code appends these controls.
reset = '// Remove Chrome-saved controls before the original application recreates them.\n' \
'document.getElementById("switch").replaceChildren();\n' \
'document.getElementById("gal").replaceChildren();\n'
(OUT / 'app.js').write_text(reset + app)
html = re.sub(r'<script\b[^>]*>.*?</script>', '', original, flags=re.S)
html = re.sub(r'<!-- saved from url=.*?-->', '', html)
html = html.replace('<html>', '<html lang="ru">', 1)
html = html.replace('<head>', '<head><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">', 1)
html = html.replace('href="./css2"', 'href="./fonts.css"')
html = re.sub(r'<link rel="preconnect"[^>]*>', '', html)
html = html.replace('</body>', '<script src="./app.js"></script>\n</body>')
(OUT / 'index.html').write_text(html)
fonts = OUT / 'fonts'
fonts.mkdir(exist_ok=True)
urls = sorted(set(re.findall(r'url\((https://[^)]+)\)', css)))
def fetch(url):
name = hashlib.sha256(url.encode()).hexdigest()[:20] + '.woff2'
target = fonts / name
if not target.exists():
with urllib.request.urlopen(url, timeout=30) as r:
target.write_bytes(r.read())
return url, './fonts/' + name
with concurrent.futures.ThreadPoolExecutor(max_workers=6) as pool:
for url, local in pool.map(fetch, urls):
css = css.replace(url, local)
(OUT / 'fonts.css').write_text(css)
manifest = {
'source': 'https://claude.ai/artifact/Vc1q1WCZuPY2QAHBSGtrKF',
'export_url': 'http://drop.t.snw.su/mearl',
'archive_sha256': hashlib.sha256(ARCHIVE.read_bytes()).hexdigest(),
'inner_html_sha256': hashlib.sha256(original.encode()).hexdigest(),
'original_app_sha256': hashlib.sha256(app.encode()).hexdigest(),
'changes': ['remove Claude iframe runtime', 'clear saved controls before original startup',
'local font files', 'viewport and Russian document language'],
'inference_code_and_weights': 'preserved verbatim after the control-reset prelude',
'files': {str(p.relative_to(OUT)): hashlib.sha256(p.read_bytes()).hexdigest()
for p in sorted(OUT.rglob('*')) if p.is_file()},
}
(ROOT / 'SOURCE.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + '\n')
print(json.dumps({'fonts': len(urls), 'files': len(manifest['files']), 'archive_sha256': manifest['archive_sha256']}))

329
mnist-artifact/site/app.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,396 @@
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/13ff14c8cbbde52dd267.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/fd8b74284fe6ff9705c0.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/560980fcddc4e6f493d8.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/99228c3b93e78c59da72.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/f582230492bfd11eff7b.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/6d5e7fb80500edbb2ccc.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/13ff14c8cbbde52dd267.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/fd8b74284fe6ff9705c0.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/560980fcddc4e6f493d8.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/99228c3b93e78c59da72.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/f582230492bfd11eff7b.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/6d5e7fb80500edbb2ccc.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/13ff14c8cbbde52dd267.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/fd8b74284fe6ff9705c0.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/560980fcddc4e6f493d8.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/99228c3b93e78c59da72.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/f582230492bfd11eff7b.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/6d5e7fb80500edbb2ccc.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/6b5e37b52d26eeac6a19.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/eb83d8d40af4411f395f.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/99e400330f36ae77c882.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/c4064400db3750ce826c.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/b19c2080652a3c2b1288.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/cc27b30471250057c5e6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/6b5e37b52d26eeac6a19.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/eb83d8d40af4411f395f.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/99e400330f36ae77c882.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/c4064400db3750ce826c.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/b19c2080652a3c2b1288.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/cc27b30471250057c5e6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* arabic */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/90a469f8becd8517007a.woff2) format('woff2');
unicode-range: U+0600-06FF, U+0750-077F, U+0870-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC7, U+10EC9-10EEE, U+10EF0-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
/* cyrillic-ext */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/23f931dc9e4c044ca281.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/0c46fc34202128a8b356.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* hebrew */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/04b5819e5fda60389774.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* latin-ext */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/c062627ec16ce1b40510.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/547062430ce141b89109.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* arabic */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/90a469f8becd8517007a.woff2) format('woff2');
unicode-range: U+0600-06FF, U+0750-077F, U+0870-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC7, U+10EC9-10EEE, U+10EF0-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
/* cyrillic-ext */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/23f931dc9e4c044ca281.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/0c46fc34202128a8b356.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* hebrew */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/04b5819e5fda60389774.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* latin-ext */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/c062627ec16ce1b40510.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/547062430ce141b89109.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,180 @@
<!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>

View file

@ -0,0 +1,396 @@
{
"url": "http://knp-digits.t.snw.su/",
"page_errors": [],
"failed_requests": [],
"switch_buttons": 2,
"gallery_buttons": 24,
"gallery_inferences": [
{
"net": "snn",
"label": 4,
"answer": 4,
"ticks": 9,
"synaptic": 23597,
"input": 166
},
{
"net": "snn",
"label": 7,
"answer": 4,
"ticks": 9,
"synaptic": 19654,
"input": 139
},
{
"net": "snn",
"label": 3,
"answer": 3,
"ticks": 9,
"synaptic": 19604,
"input": 142
},
{
"net": "snn",
"label": 9,
"answer": 9,
"ticks": 9,
"synaptic": 13563,
"input": 97
},
{
"net": "snn",
"label": 3,
"answer": 3,
"ticks": 9,
"synaptic": 17912,
"input": 129
},
{
"net": "snn",
"label": 2,
"answer": 2,
"ticks": 9,
"synaptic": 34053,
"input": 244
},
{
"net": "snn",
"label": 8,
"answer": 4,
"ticks": 9,
"synaptic": 23105,
"input": 166
},
{
"net": "snn",
"label": 7,
"answer": 7,
"ticks": 9,
"synaptic": 15195,
"input": 109
},
{
"net": "snn",
"label": 0,
"answer": 0,
"ticks": 9,
"synaptic": 26241,
"input": 189
},
{
"net": "snn",
"label": 4,
"answer": 4,
"ticks": 9,
"synaptic": 16383,
"input": 117
},
{
"net": "snn",
"label": 4,
"answer": 4,
"ticks": 9,
"synaptic": 14714,
"input": 105
},
{
"net": "snn",
"label": 2,
"answer": 7,
"ticks": 9,
"synaptic": 15480,
"input": 110
},
{
"net": "snn",
"label": 2,
"answer": 2,
"ticks": 9,
"synaptic": 22048,
"input": 154
},
{
"net": "snn",
"label": 1,
"answer": 1,
"ticks": 9,
"synaptic": 10150,
"input": 69
},
{
"net": "snn",
"label": 1,
"answer": 1,
"ticks": 9,
"synaptic": 11161,
"input": 77
},
{
"net": "snn",
"label": 4,
"answer": 4,
"ticks": 9,
"synaptic": 18462,
"input": 131
},
{
"net": "snn",
"label": 9,
"answer": 9,
"ticks": 9,
"synaptic": 17206,
"input": 120
},
{
"net": "snn",
"label": 5,
"answer": 5,
"ticks": 9,
"synaptic": 22631,
"input": 161
},
{
"net": "snn",
"label": 9,
"answer": 9,
"ticks": 9,
"synaptic": 22909,
"input": 164
},
{
"net": "snn",
"label": 0,
"answer": 0,
"ticks": 9,
"synaptic": 24243,
"input": 172
},
{
"net": "snn",
"label": 6,
"answer": 6,
"ticks": 9,
"synaptic": 23217,
"input": 163
},
{
"net": "snn",
"label": 9,
"answer": 9,
"ticks": 9,
"synaptic": 18540,
"input": 132
},
{
"net": "snn",
"label": 0,
"answer": 0,
"ticks": 9,
"synaptic": 22741,
"input": 162
},
{
"net": "snn",
"label": 1,
"answer": 1,
"ticks": 9,
"synaptic": 12139,
"input": 84
},
{
"net": "cols",
"label": 4,
"answer": 9,
"ticks": 15,
"synaptic": 237200,
"input": 1186
},
{
"net": "cols",
"label": 7,
"answer": 9,
"ticks": 15,
"synaptic": 180000,
"input": 900
},
{
"net": "cols",
"label": 3,
"answer": 7,
"ticks": 15,
"synaptic": 132800,
"input": 664
},
{
"net": "cols",
"label": 9,
"answer": 4,
"ticks": 15,
"synaptic": 101800,
"input": 509
},
{
"net": "cols",
"label": 3,
"answer": 2,
"ticks": 15,
"synaptic": 165400,
"input": 827
},
{
"net": "cols",
"label": 2,
"answer": -1,
"ticks": 15,
"synaptic": 377000,
"input": 1885
},
{
"net": "cols",
"label": 8,
"answer": 9,
"ticks": 15,
"synaptic": 215400,
"input": 1077
},
{
"net": "cols",
"label": 7,
"answer": 7,
"ticks": 15,
"synaptic": 130600,
"input": 653
},
{
"net": "cols",
"label": 0,
"answer": 0,
"ticks": 15,
"synaptic": 264800,
"input": 1324
},
{
"net": "cols",
"label": 4,
"answer": 4,
"ticks": 15,
"synaptic": 137200,
"input": 686
},
{
"net": "cols",
"label": 4,
"answer": 4,
"ticks": 15,
"synaptic": 121600,
"input": 608
},
{
"net": "cols",
"label": 2,
"answer": 7,
"ticks": 15,
"synaptic": 136400,
"input": 682
},
{
"net": "cols",
"label": 2,
"answer": 2,
"ticks": 15,
"synaptic": 203200,
"input": 1016
},
{
"net": "cols",
"label": 1,
"answer": 1,
"ticks": 15,
"synaptic": 70000,
"input": 350
},
{
"net": "cols",
"label": 1,
"answer": 1,
"ticks": 15,
"synaptic": 99400,
"input": 497
},
{
"net": "cols",
"label": 4,
"answer": 4,
"ticks": 15,
"synaptic": 150600,
"input": 753
},
{
"net": "cols",
"label": 9,
"answer": 9,
"ticks": 15,
"synaptic": 149400,
"input": 747
},
{
"net": "cols",
"label": 5,
"answer": 5,
"ticks": 15,
"synaptic": 216800,
"input": 1084
},
{
"net": "cols",
"label": 9,
"answer": 9,
"ticks": 15,
"synaptic": 223200,
"input": 1116
},
{
"net": "cols",
"label": 0,
"answer": 0,
"ticks": 15,
"synaptic": 213600,
"input": 1068
},
{
"net": "cols",
"label": 6,
"answer": 6,
"ticks": 15,
"synaptic": 197000,
"input": 985
},
{
"net": "cols",
"label": 9,
"answer": 9,
"ticks": 15,
"synaptic": 162800,
"input": 814
},
{
"net": "cols",
"label": 0,
"answer": 0,
"ticks": 15,
"synaptic": 210600,
"input": 1053
},
{
"net": "cols",
"label": 1,
"answer": 1,
"ticks": 15,
"synaptic": 126000,
"input": 630
}
],
"desktop_and_mobile_drawing": "passed",
"tick_scrubbing_and_neuron_selection": "passed",
"mobile_no_horizontal_overflow": true
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

View file

@ -0,0 +1,74 @@
[
{
"host": "knp-columns",
"path": "/",
"status": 200,
"bytes": 556820
},
{
"host": "knp-shifts",
"path": "/",
"status": 200,
"bytes": 601167
},
{
"host": "knp-fast",
"path": "/",
"status": 200,
"bytes": 7164
},
{
"host": "knp-fast",
"path": "/fast-model.js",
"status": 200,
"bytes": 8641
},
{
"host": "knp-fast",
"path": "/fast-gallery.json",
"status": 200,
"bytes": 32550
},
{
"host": "knp-native",
"path": "/",
"status": 200,
"bytes": 7693
},
{
"host": "knp-native",
"path": "/native-model.js",
"status": 200,
"bytes": 9101
},
{
"host": "knp-pets",
"path": "/",
"status": 200,
"bytes": 14798
},
{
"host": "knp-pets",
"path": "/api/info",
"status": 200,
"bytes": 287
},
{
"host": "knp-digits",
"path": "/",
"status": 200,
"bytes": 30683
},
{
"host": "knp-digits",
"path": "/app.js",
"status": 200,
"bytes": 549257
},
{
"host": "knp-digits",
"path": "/fonts.css",
"status": 200,
"bytes": 15020
}
]

View file

@ -0,0 +1,33 @@
# Проверка состояния перед продолжением
По просьбе пользователя прочитаны передачи и отчёты 15 октября, выполнена read-only SSH-проверка user@10.9.8.230. Машина knp доступна, uptime около 15 минут. Порты 8080/8081/8082 не слушают; прежние демо после перезапуска не работают. Транспортный pipeline.json: completed, done=true, failed=false; runner.pid содержит исторический PID49452. Ничего не запускали, не пересобирали, модели и данные не меняли. Пароли в файлы не сохранялись.
Последняя остановка по передаче: муха отложена пользователем; Pet lab не совместим с Алтаем-2 как есть; native MNIST имеет отдельное подтверждение GoldenModel только в режиме replicas<=2. Возможное продолжение: восстановление демо, аппаратная адаптация отдельной новой модели либо улучшение транспорта по validation с сохранением test.
## Восстановлены все готовые демо
По запросу пользователя запущены прежние серверы через nohup: 8080 PID8214 (mnist-viz/start.sh), 8081 PID8225 (mnist-altai-native/server.py), 8082 PID8236 (pets-knp/server.py). PID сохранены в штатные runtime-файлы. С внешней машины HTTP200 у altai_viz.html, aug_viz.html, fast_viz.html, native_viz.html, fast-model.js, native-model.js, Pet lab и /api/info. Реальные запросы: MNIST первый сохранённый пример ->5 в CPU (~115ms) и GM (~5680ms); Pet lab первый gallery sample ->cat, outputs[1,0], ~445ms. Это smoke, новая accuracy не измерялась. viz/app.html содержит DATA=null и является шаблоном экспорта, самостоятельным демо не считается. Транспортного web-сервера нет, муха остаётся остановлена. Модели/библиотеки не менялись, системный автозапуск не добавлен.
## Запрошен перенос Claude Artifact
Пользователь просит целиком скопировать и захостить https://claude.ai/artifact/Vc1q1WCZuPY2QAHBSGtrKF; вики https://fs.snw.su/knp/ дана только как дополнительный источник, не как замена артефакта. Встроенный docslib-manifest вики прочитан: revision 3a6bbbb01711ce1e14f5, updated_at 2026-10-19T08:25:20.902+00:00, 23 страницы; client.js.custom содержит веса и виджеты трёх моделей, включая temporal SNN8. Эти новые данные пока не подтверждены нашими KNP-прогонами, переносить доказательства старой модели на них нельзя.
Ссылка Claude отдаёт оболочку с frame UUID e7a36cae-a7f7-4c83-b0f2-ff0fa979600e. В Chromium фактический API /api/frame/<uuid>?via=user_open&bk=initial&actor=id&vt=1 отвечает HTTP403 Cloudflare Just a moment; оболочка показывает Page not found. Исходный код и визуальное содержимое артефакта не получены. Аналогичный результат у обычного HTTP-запроса API. Нельзя считать Page not found доказательством удаления артефакта, поскольку причина наблюдаемого отказа — Cloudflare. Подменять артефакт отдельной страницей по вики пользователь не просил. Новая страница не создана, прежние демо не менялись. Для точного переноса требуется экспорт HTML/React исходника артефакта либо доступный источник.
## Артефакт из Chrome Ctrl+S перенесён и опубликован
Пользователь предоставил ZIP http://drop.t.snw.su/mearl, SHA256 94bf2e99581aec06cbec3078c50c93dcffd1907e40b954006d1fa444aae4eaf7. ZIP содержит полный inner saved_resource.html с данными COLS/SNN/gallery и кодом. Источник архивирован в reports/2026-10-21/artifact/chrome-export.zip. Создан mnist-artifact/prepare.py и site, SOURCE.json фиксирует SHA256 оригинального приложения и результата. Удалён Claude runtime/оболочка, встроенный код расчёта сохранён дословно после двух строк очистки DOM: Chrome сохранил уже сгенерированные кнопки, без очистки они дублируются. Шрифты (18 файлов) локализованы, добавлены viewport/lang. Веса, обучение и UI исходника не переписывались. Исходная кнопка Clear очищает только рисунок, сохраняет прежний результат до нового ввода; это поведение сохранено.
Публичный URL http://knp-digits.t.snw.su/ через отдельный Caddy block, /opt/knp-digits. Конфигурация проверена caddy validate перед reload; прежние block не менялись, HTTPS не включался. На станции /home/user/KNP/viz/digits-compare, URL http://10.9.8.230:8080/digits-compare/; все 21 SHA256 совпали. Оба URL HTTP200, JS200. Перед записью на станции подтверждены completed pipeline и отсутствие collection. Исходные модели/библиотеки/данные и прежние демо не менялись.
Chromium: 24 примера ×2 сети=48 реальных браузерных распознаваний, 2 кнопки переключателя, 24 gallerybuttons, стартовая3->3; slider0/8, выбор нейрона, mouse/touch strokes, мобильный390 без horizontaloverflow, light/dark; JS errors0 и failedrequests0. Сохранены browser-checks.json и desktop/dark/mobile PNG. При первой touch-проверке начало жеста попало под sticky navigation; тест исправлен, прикладной код не менялся, реальное touch распознавание прошло. Перенос не является повторной оценкой accuracy или проверкой KNP/GoldenModel новой SNN. Указанные92,2%/97,4% — сведения источника.
## Сохранён исходный Git-репозиторий вики
По просьбе пользователя только сохранён ssh://git@git.snw.su:30301/pgups/knp_docs.git. Полный clone /root/knp_docs, main HEAD540cdb9fed49a1c910437e0944454e8a56f88ffd («Добавить run.sh для сервера, домен knp.snw.su»), рабочее дерево чистое. Резервная копия полной истории и refs: reports/2026-10-21/knp_docs.bundle, git bundle verify успешен, SHA256 5a5c9bfb8f0f65d9fad3ef3073143f4dc1d13c141879f3163f890a715a1bc7db. Код не запускался, сайты/модели/станция не менялись, commit/push нет.
## Все готовые локальные демо доступны через Caddy reverse proxy
По запросу пользователя добавлены отдельные HTTPдомены: knp-columns.t.snw.su ->10.9.8.230:8080/altai_viz.html; knp-shifts ->8080/aug_viz.html; knp-fast ->8080/fast_viz.html; knp-native ->8081/native_viz.html; knp-pets ->8082/. knp-digits.t.snw.su переведён с локального file_server на reverse_proxy 10.9.8.230:8080 с префиксом /digits-compare. Корневые URL первых четырёх переписываются на соответствующую страницу, относительные assets/API продолжают работать. Wiki исключена, никаких новых транспортных/flyсайтов не создавалось. Прочие Caddyhosts сохранены, validate перед reload успешен, HTTPS не включали.
Проверены12 HTML/JS/JSON/CSS/API URL — HTTP200, результаты proxy-checks.json. Реальное PetAPI через новый домен:cat,outputs[1,0],402ms. NativePOST через домен: blank200. Сервисы/модели/библиотеки на станции не менялись, commit/push нет. Актуальная вики по уточнению пользователя только https://knp.snw.su/; fs.snw.su устарел и больше не используется.
## Подготовка push по явному запросу пользователя
Пользователь явно разрешил «отпушь все». Основной checkout /root/our_knp, origin ssh://git@git.snw.su:30301/int/our_knp.git, main. Сохранены только относящиеся к KNP шесть Caddyblocks в deployment/knp-demos.Caddyfile, без чужой серверной конфигурации. Исходники артефакта, локальные шрифты, browserproofs, исходный Chromeexport, bundle вики и передачи включаются в commit. Проверка экспорта не обнаружила непустых строковых значений assetToken/access_token/refresh_token/share_key/consentToken/token. Репозиторий самой вики /root/knp_docs чистый, изменений для push там нет.

329
viz/digits-compare/app.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,396 @@
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/13ff14c8cbbde52dd267.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/fd8b74284fe6ff9705c0.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/560980fcddc4e6f493d8.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/99228c3b93e78c59da72.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/f582230492bfd11eff7b.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/6d5e7fb80500edbb2ccc.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/13ff14c8cbbde52dd267.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/fd8b74284fe6ff9705c0.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/560980fcddc4e6f493d8.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/99228c3b93e78c59da72.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/f582230492bfd11eff7b.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/6d5e7fb80500edbb2ccc.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/13ff14c8cbbde52dd267.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/fd8b74284fe6ff9705c0.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/560980fcddc4e6f493d8.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/99228c3b93e78c59da72.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/f582230492bfd11eff7b.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(./fonts/6d5e7fb80500edbb2ccc.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/6b5e37b52d26eeac6a19.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/eb83d8d40af4411f395f.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/99e400330f36ae77c882.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/c4064400db3750ce826c.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/b19c2080652a3c2b1288.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/cc27b30471250057c5e6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/6b5e37b52d26eeac6a19.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/eb83d8d40af4411f395f.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/99e400330f36ae77c882.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/c4064400db3750ce826c.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/b19c2080652a3c2b1288.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/cc27b30471250057c5e6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* arabic */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/90a469f8becd8517007a.woff2) format('woff2');
unicode-range: U+0600-06FF, U+0750-077F, U+0870-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC7, U+10EC9-10EEE, U+10EF0-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
/* cyrillic-ext */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/23f931dc9e4c044ca281.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/0c46fc34202128a8b356.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* hebrew */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/04b5819e5fda60389774.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* latin-ext */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/c062627ec16ce1b40510.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./fonts/547062430ce141b89109.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* arabic */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/90a469f8becd8517007a.woff2) format('woff2');
unicode-range: U+0600-06FF, U+0750-077F, U+0870-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC7, U+10EC9-10EEE, U+10EF0-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
/* cyrillic-ext */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/23f931dc9e4c044ca281.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/0c46fc34202128a8b356.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* hebrew */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/04b5819e5fda60389774.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* latin-ext */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/c062627ec16ce1b40510.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./fonts/547062430ce141b89109.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,180 @@
<!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>

View file

@ -0,0 +1,37 @@
{
"source": "https://claude.ai/artifact/Vc1q1WCZuPY2QAHBSGtrKF",
"export_url": "http://drop.t.snw.su/mearl",
"archive_sha256": "94bf2e99581aec06cbec3078c50c93dcffd1907e40b954006d1fa444aae4eaf7",
"inner_html_sha256": "4807bae6973ccc560b51aaf7c60f4b2b8be8d529aeb6848db86036522df2249d",
"original_app_sha256": "61ea4ea4db5926c7c8a17f7ce057707fab9b01c6a121fd2e619c42d91b92846c",
"changes": [
"remove Claude iframe runtime",
"clear saved controls before original startup",
"local font files",
"viewport and Russian document language"
],
"inference_code_and_weights": "preserved verbatim after the control-reset prelude",
"files": {
"app.js": "4ef53f5ebcac9b3b489daaf2dfb409a9d520fcc80e5e9502c4e789dce86e9744",
"fonts/04b5819e5fda60389774.woff2": "197b6dbe5c6bec7276c59b02d70a16ee6acd44322fc2f8d0226c07f8d57976c2",
"fonts/0c46fc34202128a8b356.woff2": "bdb2b33ab916e9e7b42edb61f8aea5f5dd83ad773d6ee4e9b55665daf8954ce8",
"fonts/13ff14c8cbbde52dd267.woff2": "7d0037a170882a49e65a45c38ef195237bb07b1cc791d078379f0ac0d7ae1935",
"fonts/23f931dc9e4c044ca281.woff2": "35e30483bb79be6fd8de0fec4e038fda9e637fc7337f6ff61418c7d08077878b",
"fonts/547062430ce141b89109.woff2": "691cd1d9b4c0cdf31a1dcf04259c86f92c85e69f6622abab7964d81e36890691",
"fonts/560980fcddc4e6f493d8.woff2": "f598dd0700f155e2facfaa8ecfacace397d4c2c153e9ce37ab7ad310e8505bc2",
"fonts/6b5e37b52d26eeac6a19.woff2": "62213be8a78b42f1e29d1452d91e2f8b3e745572a9dd98d3941e39fa00b37d76",
"fonts/6d5e7fb80500edbb2ccc.woff2": "e2291e842cf5af167122a22881a740c7f2dda7716f1e8cd76680264f4a859470",
"fonts/90a469f8becd8517007a.woff2": "f5052e4870bf211fc22df5b85e791a292a77b9baef8cef1dc5a8e62a783ea1e0",
"fonts/99228c3b93e78c59da72.woff2": "73e7f7b1c980416ae0c2461268b14c7cfcce0668e683dc43fbb11a82a227b9f2",
"fonts/99e400330f36ae77c882.woff2": "0a557721b1f8b36d3f3f84442689a71ca4a744300abcb46a1953f51bfc663b66",
"fonts/b19c2080652a3c2b1288.woff2": "db5ff4db83e580426280e9337a58dc57d3a83784a1b03ad80914651594441d52",
"fonts/c062627ec16ce1b40510.woff2": "09472f217ad1ebb61a7669cf532f9f2f80685e07652ee862fde9a79665aef479",
"fonts/c4064400db3750ce826c.woff2": "c89b9cc0bc6262bd4f8d8494b6961601f3aefa829d08c2e3635f4d501d3a47c2",
"fonts/cc27b30471250057c5e6.woff2": "83c005d49d8a6a50474c73a5a36ac0468076e9c4a29da7bdb14995d80560a5be",
"fonts/eb83d8d40af4411f395f.woff2": "e17cfd15fb96909d64095015f958207063a0c07191da3512df7d560a781aebdf",
"fonts/f582230492bfd11eff7b.woff2": "d160e20920ae4d6556518d352d3af27a74e9b0de3d8fe17b1c1044fc75aa2f81",
"fonts/fd8b74284fe6ff9705c0.woff2": "9409ee21811f0f5272b7e1d828fe8838005b0573481ef906e7e55b63f6610ab0",
"fonts.css": "564d3c95a3b80d7c5ae21347ab77028cb10125812c24520a12f584fa880a6f61",
"index.html": "cfb514c642469601364b4482d3ce7cde715ef8fbbf0b0f099fb5dd43466b6eb3"
}
}