From 26f4909ead993a2f956a5e2212d346445d163cbe Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 18 May 2026 15:57:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8=20"?= =?UTF-8?q?=D0=B4=D0=BE=D0=BC=D0=BE=D0=B9"=20=D0=B8=20=D0=B3=D0=B0=D0=B9?= =?UTF-8?q?=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HOW_IT_WORKS.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ hamming.rb | 10 +++--- hopfield.rb | 10 +++--- views/index.erb | 5 ++- 4 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 HOW_IT_WORKS.md diff --git a/HOW_IT_WORKS.md b/HOW_IT_WORKS.md new file mode 100644 index 0000000..9e1d053 --- /dev/null +++ b/HOW_IT_WORKS.md @@ -0,0 +1,81 @@ +# Как работает приложение + +## Запуск + +```bash +bundle install +ruby app.rb # веб-интерфейс на http://localhost:4567 +# или +ruby main.rb # только CLI: загружает паттерны, обучает, выводит инфо +``` + +В Docker: собирается образ, `generate_excel.rb` генерирует `patterns.xlsx` при старте, Sinatra слушает на 4567. + +--- + +## Данные + +Паттерны хранятся в `patterns.xlsx`. Каждый лист = один символ (T, H, E, X...). +Ячейка `x` → `1`, остальное → `-1`. Размер: 8×8 = вектор из 64 значений. + +`loader.rb` читает Excel через гем `roo` и возвращает `{name => matrix_8x8}`. +В вычислениях матрицы разворачиваются в векторы через `flatten`. + +Если хочешь добавить/изменить паттерны — правь `generate_excel.rb` и перезапусти. + +--- + +## Веб-интерфейс + +Одна страница (`views/index.erb`), тёмная тема, 8×8 кликабельная сетка ввода. + +**Что можно сделать:** +- Нарисовать паттерн кликами (или добавить шум кнопкой) +- Нажать «Распознать» — сервер прогоняет через обе сети +- Включить «Пошаговый режим» — видно, как Хопфилд сходится шаг за шагом +- Включить «Показывать формулы» — рядом с анимацией появляются LaTeX-формулы вычислений + +--- + +## Маршруты (`app.rb`) + +| Маршрут | Что делает | +|---|---| +| `GET /` | Загружает паттерны, отдаёт HTML | +| `POST /recall` | Быстро: вектор → итоговый результат обеих сетей | +| `POST /recall_steps` | То же + все промежуточные состояния для анимации | +| `POST /recall_trace` | То же + net-значения, сколько нейронов изменилось на каждом шаге, трассировка MAXNET | + +Все POST принимают JSON `{vector: [...64 числа...]}`, возвращают JSON. + +--- + +## Алгоритмы + +### Хопфилд (`hopfield.rb`) + +**Обучение** — один раз при запуске: +``` +W[i][j] += v[i] * v[j] для каждого паттерна v, i ≠ j +``` +Матрица весов 64×64, кэшируется в `weights.cache`. + +**Recall** — итерации до сходимости: +``` +new[i] = sign( Σ_j W[i][j] * x[j] ) # sign: ≥0 → 1, <0 → -1 +``` +Все нейроны обновляются синхронно. Стоп: состояние не изменилось (EPS=0.01) или 200 итераций. + +### Хэмминг (`hamming.rb`) + +**Шаг 1 — слой Хэмминга** (один раз): +``` +output[k] = (dot(pattern_k, input) + 64) / 2 +``` +Результат: число от 0 до 64 — сколько бит совпало с k-м эталоном. + +**Шаг 2 — MAXNET** (итерации до одного победителя): +``` +new[i] = max(output[i] - eps * Σ_{j≠i} output[j], 0) # eps = 0.9 / кол-во паттернов +``` +Нейроны конкурируют: слабые гаснут, остаётся один. Его индекс = ближайший паттерн. diff --git a/hamming.rb b/hamming.rb index 1545e3d..57dfaa2 100644 --- a/hamming.rb +++ b/hamming.rb @@ -17,13 +17,11 @@ MAX_MAXNET_ITER = 100 def recall_hamming(pattern_vectors, input_vec) eps = 0.9 / pattern_vectors.length - outputs = hamming_layer(pattern_vectors, input_vec) - MAX_MAXNET_ITER.times do - next_out = maxnet_step(outputs, eps) - return next_out.each_with_index.max_by { |v, _| v }[1] if next_out.count { |v| v > 1e-9 } <= 1 - outputs = next_out + iter = 0 + while outputs.count { |v| v > 1e-9 } > 1 && iter < MAX_MAXNET_ITER + outputs = maxnet_step(outputs, eps) + iter += 1 end - outputs.each_with_index.max_by { |v, _| v }[1] end diff --git a/hopfield.rb b/hopfield.rb index 1b8db9f..3877518 100644 --- a/hopfield.rb +++ b/hopfield.rb @@ -40,10 +40,12 @@ MAX_HOPFIELD_ITER = 200 def recall_hopfield(weights, input_vec) current = input_vec.dup - MAX_HOPFIELD_ITER.times do - next_vec = step_hopfield(weights, current) - return current if vectors_equal?(current, next_vec) + next_vec = step_hopfield(weights, current) + iter = 1 + while !vectors_equal?(current, next_vec) && iter < MAX_HOPFIELD_ITER current = next_vec + next_vec = step_hopfield(weights, current) + iter += 1 end - current + next_vec end \ No newline at end of file diff --git a/views/index.erb b/views/index.erb index f1e0311..3d9658c 100644 --- a/views/index.erb +++ b/views/index.erb @@ -263,7 +263,10 @@ -

Сети Хопфилда и Хэмминга

+
+

Сети Хопфилда и Хэмминга

+ Домой +

Эталонные образы