🧠 Как работи MLP разпознавателят?

Пълно обяснение с примерни стойности • mobile‑first

14×14 px 196 входа 32 скрити 10 изхода Sigmoid Backprop

📋 Преглед на кода

Кодът реализира Multilayer Perceptron (MLP) — най-простата изкуствена невронна мрежа.

  • Входен слой: 196 неврона (14×14 пиксела)
  • Скрит слой: 32 неврона с sigmoid
  • Изходен слой: 10 неврона (цифри 0–9)

Мрежата се обучава чрез backpropagation всеки път, когато натиснеш "Train X".

🏗 Архитектура на мрежата

Вход (196)
p₁
p₂
···
p₁₉₆
⟹ W₁
32×196
Скрит (32)
h₁
h₂
···
h₃₂
⟹ W₂
10×32
Изход (10)
y₀
y₁
···
y₉

Размери на матриците:

СлойМатрицаРазмерПараметри
1 (скрит)W₁32×1966 304
2 (изход)W₂10×32330
Общо6 634

Всеки параметър е случайно число между -0.5 и 0.5.

➡️ Forward Pass – предсказание

Стъпка по стъпка

  1. Входен вектор – 196 стойности (0=празно, 1=пълно).
    Пример (4 пиксела): [0.0, 1.0, 0.8, 0.0]
  2. Скрит слой: zⱼ = b₁ⱼ + Σ (xᵢ × W₁ⱼᵢ)
  3. Sigmoid: aⱼ = 1/(1+e⁻ᶻⱼ)
  4. Изходен слой: същото с W₂ и b₂
  5. Изход: 10 числа – най-голямото = предсказана цифра

🔢 Конкретен пример (опростен)

4 пиксела → 2 скрити → 2 изхода. Вход: x = [0.0, 1.0, 0.8, 0.0]

Скрит неврон h₁:

ПроменливаСтойност
W₁[0][0.30, -0.50, 0.10, -0.20]
b₁[0]0.10
Σ xᵢWᵢ0·0.30+1·(-0.50)+0.8·0.10+0 = -0.42
z₁-0.42+0.10 = -0.32
a₁ = σ(z₁)1/(1+e0.32) ≈ 0.421

Скрит неврон h₂:

ПроменливаСтойност
W₁[1][0.15, 0.40, -0.30, 0.05]
b₁[1]-0.05
a₂ = σ(0.16-0.05)σ(0.11) ≈ 0.527

👉 Скрит изход: [0.421, 0.527]

Изходен слой:

НевронW₂BiasΣa = σ(z)
y₀ (0)[0.60, -0.40]0.080.0420.530
y₁ (3)[0.20, 0.70]-0.100.4530.587

📊 y₁ (0.587) > y₀ → предсказана цифра 3 (но ниска увереност).

📈 Сигмоидна функция

σ(x) = 1 / (1 + e⁻ˣ)
σ'(x) = σ(x)·(1−σ(x))

📌 Защо? "Смачква" числата в (0,1), въвежда нелинейност и има проста производна.

🧮 Калкулатор:

Най-бързо учене при x=0 (σ'(0)=0.25).

⬅️ Backpropagation – учене

🎯 Цел: минимизиране на грешката

Target за цифра "3": [0,0,0,1,0,0,0,0,0,0]

Грешка: errorᵢ = targetᵢ − outputᵢ

📐 Стъпки

  1. Грешка изход: δᴸ = (target−output) ⊙ σ'(zᴸ)
  2. Разпространи назад: δᴴ = (W₂ᵀ × δᴸ) ⊙ σ'(zᴴ)
  3. Обнови тегла: ΔW = η × δ × a_prevᵀ
  4. Обнови bias: b_new = b_old + η × δ

η (learning rate) = 0.15

🔢 Пример с числа

Изход: [0.530, 0.587], target: [0, 1]

Стъпка 1: δᴸ

НевронOutputTargetErrorσ'(z)δᴸ
y₀0.5300-0.5300.249-0.132
y₁0.5871+0.4130.242+0.100

Стъпка 2: δᴴ

СкритW₂ᵀ×δᴸσ'(zᴴ)δᴴ
h₁-0.0590.244-0.014
h₂0.1230.249+0.031

Стъпка 3: Обновяване W₂ (η=0.15)

ТеглоСтароΔWНово
W₂[y₀→h₁]0.60-0.00830.592
W₂[y₁→h₂]0.70+0.00790.708

📈 Теглата към верния отговор растат, към грешния – намаляват.

🎯 Пълен пример: рисуване на "3"

СтъпкаДействиеСтойност
1Рисуваш "3" на 14×14 canvas~30-40 пиксела >0
2Нормализация + anti-aliasinggrid[45]=0.8, grid[60]=1.0...
3Forward pass: 196→32→10y₃ = 0.82
4Намиране на maxИндекс 3 → "3"

🖌 Canvas & предобработка

От пиксел до число

Canvas-ът е 14×14 пиксела (визуално уголемен до 280×280 чрез CSS).

// При всяко движение: function addInk(col, row, intensity) { const idx = row * 14 + col; grid[idx] = Math.min(1, grid[idx] + intensity); } // Основен щрих + изглаждане: addInk(col, row, 1.0); // център addInk(col-1, row, 0.4); // ляво addInk(col+1, row, 0.4); // дясно ...

Това прави щриха по-дебел и гладък → по-лесно разпознаване.

💡 Защо кодът работи?

1. Универсална апроксимация

MLP с 1 скрит слой може да научи всяка непрекъсната функция. 32 неврона са достатъчни за цифрите.

2. Sigmoid = нелинейност

Без нея мрежата би била линейна и не би могла да раздели сложните форми на цифрите.

3. Gradient Descent

Backprop изчислява посоката, в която грешката намалява най-бързо, и мести теглата натам.

W_new = W_old − η × ∂Error/∂W

4. Dataset + повторение

200 епохи върху всички запазени примери (с разбъркване) помагат на мрежата да генерализира.

⚡ Резюме

1. Рисуваш → 14×14 → 196 числа 2. Forward: 196→32→10 3. Train X → target=[0..1..0] 4. Backprop: грешка → градиент → обнови W,b 5. Повтори 200× с всички примери 6. Мрежата става все по-точна! 🎉

📐 Формули – справочник

ОперацияФормулаВ кода
Sigmoidσ(x)=1/(1+e⁻ˣ)activate(x)
σ производнаσ'(x)=σ(x)(1−σ(x))activatePrime(x)
Forward скритa₁=σ(W₁·x+b₁)predict()
Forward изходa₂=σ(W₂·a₁+b₂)predict()
Output errorδ₂=(t−a₂)⊙σ'(z₂)train()
Hidden errorδ₁=(W₂ᵀ·δ₂)⊙σ'(z₁)train()
Weight updateΔW=η·δ·a_prevᵀtrain()

🧠 Това е цялата магия — невронна мрежа от нулата, без библиотеки.