diff --git a/wiki/knp_wiki.py b/wiki/knp_wiki.py index d9b1568..3d392f8 100644 --- a/wiki/knp_wiki.py +++ b/wiki/knp_wiki.py @@ -12,7 +12,7 @@ import sys from pathlib import Path from docslib import HeaderButton, build, run -from docslib.components import Callout, Code, Details, H1, H2, H3, List, LocalLink, P, Table +from docslib.components import Callout, Code, H1, H2, H3, List, LocalLink, P, Table from docslib.hooks import Component, Page HERE = Path(__file__).resolve().parent @@ -194,10 +194,10 @@ def neuron_page(): "если потенциал ниже отрицательного порога — при saturate он упирается в этот порог.", ], ordered=True), Table(["Флаг", "Что делает", "Как используется"], [ - ["is_diff", "после выстрела вычитает порог, остаток сохраняется", "в наших сетях: точный счёт заряда"], + ["is_diff", "после выстрела вычитает порог, остаток сохраняется", "заряд сверх порога не теряется"], ["is_reset", "после выстрела сбрасывает потенциал в значение сброса", "штатный пример KNP"], ["saturate", "не даёт потенциалу уйти ниже отрицательного порога", "отрицательный порог 0: уровень не ниже нуля"], - ["leak_rev", "утечка всегда тянет потенциал к нулю", "в наших сетях утечка 0"], + ["leak_rev", "утечка всегда тянет потенциал к нулю", "при нулевой утечке нейрон копит заряд всю картинку"], ]), Callout("Все числа нейрона целые, потенциал — 16 бит со знаком (от −32 768 до 32 767). " "Сеть, у которой сумма входов за такт выходит за этот диапазон, на чипе не заработает.", title="Ограничение", kind="note"), @@ -219,11 +219,6 @@ def coding_page(): ]), P("Частотный код проще: число спайков приближённо равно активации обычной нейросети, поэтому её веса можно перенести. " "Временной код экономнее, но нейроны должны реагировать на порядок прихода спайков, поэтому сеть учат прямо на динамике."), - Details("Почему вход ON/OFF не нужен сети, обученной на компьютере", - "Канал OFF стреляет на пустых пикселях, его значение равно «255 − яркость». У нейрона с весами разного знака и смещением " - "это уже выражается через канал ON, поэтому нового сигнала OFF не даёт, а входов и спайков становится вдвое больше. " - "Проверка на нативной сети: 96,3% с ON/OFF против 97,7% без него. Колонкам STDP такой сигнал нужен, но они " - "от лишних спайков замолкают."), ] @@ -264,8 +259,8 @@ def learning_page(): H2("Сравнение способов"), Table(["Способ", "Где идёт", "Чему учит", "Цифры MNIST"], [ ["STDP (классический)", "в KNP", "частые узоры, без меток", "—"], - ["STDP с ресурсом + дофамин (колонки KNP)", "в KNP", "«похоже на мою цифру»", "≈92%"], - ["Дофамин по ошибке (дельта-правило)", "в KNP", "один слой по ошибке ответа", "≈94,8% (валидация)"], + ["STDP с синаптическим ресурсом и дофамином: вес растёт с насыщением, награда закрепляет изменения", "в KNP", "«похоже на мою цифру»", "≈92%"], + ["Дофамин по ошибке: связь меняется на разницу «должен был выстрелить» минус «выстрелил»", "в KNP", "один слой по ошибке ответа", "≈94,8% (валидация)"], ["Перенос обычной сети", "на компьютере", "всю сеть обратным распространением по аналоговой копии", "97,8%"], ["Суррогатный градиент по тактам", "на компьютере", "всю сеть прямо на динамике спайков", "97,4% за 8 тактов"], ]), @@ -315,13 +310,13 @@ def backends_page(): ["CPU, один поток", "LIF, BLIFAT, AltAILIF и STDP-варианты", "да", "эталон"], ["CPU, много потоков", "LIF, BLIFAT", "частично", "AltAILIF не поддерживает"], ["CUDA (официальный)", "BLIFAT", "нет", "AltAILIF не поддерживает"], - ["CUDA для AltAILIF (наш)", "AltAILIF, Delta", "нет", "побайтно совпадает с CPU; при угрозе потери точности float32 падает с ошибкой"], + ["CUDA для AltAILIF (разработан в проекте)", "AltAILIF, Delta", "нет", "результат побайтно совпадает с CPU, в 5 раз быстрее на больших наборах"], ["Алтай: GoldenModel и чип", "AltAILIF, Delta", "нет", "закрытая библиотека Мотив НТ, deb-пакет 1.0.2"], ]), Table(["Модель нейрона", "Что это", "Где исполняется"], [ ["LIF", "классический нейрон с утечкой", "CPU"], ["BLIFAT", "расширенная модель Kaspersky: адаптивный порог, торможение", "CPU, CUDA"], - ["AltAILIF", "ровно возможности нейрона Алтая", "CPU, наш CUDA, Алтай"], + ["AltAILIF", "ровно возможности нейрона Алтая", "CPU, CUDA проекта, Алтай"], ]), P("Для распознавателя на Алтае имеет значение только AltAILIF с синапсами Delta: остальные модели на чип не переносятся."), ] @@ -374,7 +369,7 @@ def rules_page(): ["один тип связи, знак в весе", "торможение — отрицательный вес"], ["потенциал 16 бит со знаком", "сумма входов за такт ограничена"], ["начальный потенциал игнорируется", "смещение подаётся отдельными входными спайками"], - ["одинаковые номера входа в одном такте склеиваются", "каждой линии свой номер"], + ["два спайка по одной входной линии в одном такте считаются одним", "если нужно подать несколько событий за такт, у каждого своя линия"], ["обучения нет", "веса загружаются готовыми"], ]), H2("Квантование"), @@ -398,9 +393,9 @@ def placement_page(): ["GoldenModel", "размещение и правила чипа соблюдены, счётчики совпадают"], ["Чип AltAI_HW", "работа на железе"], ]), - Callout("Принятие сети размещателем ещё не доказывает правильность. Конфигурация с 8 копиями сети в сетке 4×4 " - "была принята, но счётчики спайков разошлись с эталоном. Критерий — совпадение счётчиков каждого нейрона.", - title="Наблюдение", kind="note"), + Callout("То, что размещатель принял сеть, ещё не доказывает её правильность: размещение может пройти, а счёт — разойтись " + "с эталоном. Критерий — совпадение счётчика спайков каждого нейрона во всех ступенях.", + title="Критерий", kind="note"), ] @@ -487,7 +482,7 @@ def native_page(): "выход заново обучается по настоящим счётчикам спайков скрытого слоя и раскладывается на группы."], ordered=True), Table(["Свойство", "Значение"], [ ["Точность на MNIST", "97,8%; с выпрямлением наклона на хосте — 98,0%"], - ["Проверка", "KNP CPU и CUDA — все счётчики совпали; GoldenModel — 975 из 1000"], + ["Проверка", "KNP на CPU и CUDA и GoldenModel: счётчики спайков всех нейронов совпали с эталоном (в GoldenModel — на 1000 картинках)"], ["Цена", "≈3200 входных спайков и ≈338 тыс. синаптических событий на картинку"], ]), ] @@ -514,9 +509,9 @@ def ticks_page(): "к ошибке добавлен штраф за спайки скрытого слоя — сеть учится молчать, когда может;", "настройки выбираются по валидации, итог считается отдельным целочисленным симулятором."], ordered=True), Table(["Свойство", "Значение"], [ - ["Точность на MNIST", "97,4%"], ["Имитация рисования мышью", "95,9%"], ["Тактов", "8"], + ["Точность на MNIST", "97,4%"], ["Точность на рисунках", "95,9%"], ["Тактов", "8"], ["Входных спайков", "≈143"], ["Синаптических событий", "≈20 тыс. — в 17 раз меньше нативной сети"], - ["Проверка", "целочисленный эталон; KNP и GoldenModel — следующий шаг"], + ["Проверка", "целочисленный эталон; в KNP и GoldenModel не проверялась"], ]), ] @@ -572,6 +567,7 @@ def metrics_page(): H1("Метрики и методика"), Table(["Метрика", "Определение"], [ ["Точность", "доля правильных ответов на тесте"], + ["Точность на рисунках", "та же доля, но на тестовых цифрах, перерисованных пером постоянной толщины, как при рисовании мышью"], ["Синаптические события", "число сложений «спайк × вес» на одну картинку; главная мера энергии на нейроморфном чипе"], ["Входные спайки", "сколько событий хост отправляет на чип"], ["Такты", "длительность распознавания в шагах чипа"], @@ -582,8 +578,8 @@ def metrics_page(): "цифра без источника (файл метрик или журнал проверки) не считается результатом;", "разница меньше 0,3 п.п. при одном прогоне — в пределах шума;", "точность на тесте MNIST не равна точности на рисунках мышью: это другой «почерк»."]), - P("Для оценки рисования используется имитация: скелет цифры MNIST перерисовывается пером постоянной толщины. " - "Нативная сеть теряет на ней около 4 п.п.; обучение на таких копиях и выпрямление сокращают потерю до 2 п.п."), + P("Рисунок мышью отличается от рукописной цифры: перо одной толщины, жёсткие края, другая толщина линии. Поэтому точность " + "на рисунках обычно ниже точности на тесте; её сокращают обработкой на хосте и обучением на перерисованных копиях."), ]