Към съдържанието
wannacode

Слоеве

Накратко: Слоевете свързват неврони един след друг: скритият слой позволява на мрежата да научи правила, които една права не може да начертае, като XOR.

Проблем

Правилото, което една права не чертае

Екипът по измамите иска ново правило: плащането е подозрително, когато държавата на картата и държавата на връзката (по IP адреса) се разминават. Българска карта от чужд IP: някой ползва открадната карта в чужбина. Чужда карта от български IP: открадната карта, ползвана тук. Българска карта от България и чужда карта от чужбина (клиент, който живее там) са наред.

Признаците са два, по 0 или 1, където 1 е чужбина. Случаите са четири, а подозрителни са (1, 0) и (0, 1). Програмата пробва 15 000 неврона: всяко тегло от −6 до 6 през 0,5 и отместване от −5,75 до 5,75. За всеки брои колко случая познава при праг 0,5:

double[][] cases = {{0, 0}, {1, 0}, {0, 1}, {1, 1}};
int[] labels = {0, 1, 1, 0}; // 1 = подозрително
int tried = 0, best = 0;
double[] bestW = {0, 0};
double bestB = 0;
for (int i = -12; i <= 12; i++) {
for (int j = -12; j <= 12; j++) {
for (int k = -12; k <= 11; k++) {
double[] w = {i / 2.0, j / 2.0};
double b = k / 2.0 + 0.25;
tried++;
int right = 0;
for (int n = 0; n < 4; n++) {
int guess = neuron(cases[n], w, b) >= 0.5 ? 1 : 0;
if (guess == labels[n]) right++;
}
if (right > best) {
best = right;
bestW = w;
bestB = b;
}
}
}
}
System.out.printf("опитани неврони: %d, най-добрият: %d от 4%n", tried, best);
System.out.printf("w = [%.2f, %.2f], b = %.2f%n", bestW[0], bestW[1], bestB);
for (int n = 0; n < 4; n++) {
double p = neuron(cases[n], bestW, bestB);
boolean right = (p >= 0.5 ? 1 : 0) == labels[n];
System.out.printf("%.0f %.0f: p = %.3f %s%n", cases[n][0], cases[n][1], p,
right ? "вярно" : "грешно");
}
Изход
опитани неврони: 15000, най-добрият: 3 от 4
w = [-6.00, 0.50], b = -0.25
0 0: p = 0.438 вярно
1 0: p = 0.002 грешно
0 1: p = 0.562 вярно
1 1: p = 0.003 вярно

Най-добрият познава 3 от 4, а чуждата карта от български IP казва „ок“. Никой от 15 000 не познава четирите. И не е, защото решетката от тегла е рядка: следващата карта показва защо.

Идеята

Защо една права не стига

Нарисувай случаите като точки на квадрат: картата е хоризонталата, връзката е вертикалата. Подозрителните (1, 0) и (0, 1) са в два срещуположни ъгъла, а наред са другите два.

Невронът казва „да“, когато z е над нулата. Границата z = 0, тоест w₁·карта + w₂·IP + b = 0, е права. От едната ѝ страна невронът казва „да“, от другата „не“. А права, която оставя двата подозрителни ъгъла от едната страна, оставя с тях поне един от другите два. Опитай с молив: не става.

Логистичната регресия от урока за класификацията има същата граница, затова и тя не може. Трябват две прави.

Идеята

Два неврона по средата

Между входа и изхода слагаме два неврона. Всеки чертае своя права:

  • h1: „поне едното е от чужбина“. Тегла 6 и 6, отместване −3: z е над нулата, щом поне един вход е 1.
  • h2: „и двете са от чужбина“. Тегла 6 и 6, отместване −9: z е над нулата само когато и двата входа са 1.

Изходът чете h1 и h2 с тегла 10 и −10 и отместване −5: „поне едното, но не и двете“. Подозрителното е ивицата между двете прави.

Невроните по средата са скрит слой. Скрит, защото никой не им дава верен отговор: входът е от данните, изходът има етикет, а какво смятат междинните, мрежата решава сама. Тук теглата са избрани на ръка, за да значат нещо, което се казва с думи. Научените обикновено не се казват така лесно.

Пресмятането отляво надясно, слой по слой, е проходът напред (forward pass):

static final double[][] W = {{6, 6}, {6, 6}};
static final double[] B = {-3, -9};
static final double[] V = {10, -10};
static final double C = -5;
static double forward(double[] x) {
// поне едното е от чужбина
double h1 = neuron(x, W[0], B[0]);
// и двете са от чужбина
double h2 = neuron(x, W[1], B[1]);
double[] h = {h1, h2}; // скритият слой
return neuron(h, V, C);
}

Виж го

Проходът напред, число по число

Един случай: чужда карта от български IP, (1, 0). Всяка стъпка печата своето z и изхода си:

double[] x = {1, 0}; // чужда карта, IP от България
double z1 = weightedSum(x, W[0], B[0]);
double h1 = sigmoid(z1);
System.out.printf("h1: z = %.2f, h1 = %.4f%n", z1, h1);
double z2 = weightedSum(x, W[1], B[1]);
double h2 = sigmoid(z2);
System.out.printf("h2: z = %.2f, h2 = %.4f%n", z2, h2);
double[] h = {h1, h2};
double z = weightedSum(h, V, C);
double p = sigmoid(z);
System.out.printf("изход: z = %.2f, p = %.4f%n", z, p);
Изход
h1: z = 3.00, h1 = 0.9526
h2: z = -3.00, h2 = 0.0474
изход: z = 4.05, p = 0.9829

h1: 6 × 1 + 6 × 0 − 3 = 3, а сигмоидата дава 0,9526. h2: 6 × 1 + 6 × 0 − 9 = −3, тоест 0,0474. Изходът: 10 × 0,9526 − 10 × 0,0474 − 5 = 4,05, а сигмоидата дава 0,9829. Подозрително.

Четирите случая с forward:

double[][] cases = {{0, 0}, {1, 0}, {0, 1}, {1, 1}};
System.out.println("карта IP p");
for (double[] x : cases) {
double p = forward(x);
String verdict = p >= 0.5 ? "подозрително" : "ок";
System.out.printf("%5.0f %3.0f %7.4f %s%n", x[0], x[1], p, verdict);
}
Изход
карта IP p
0 0 0.0107 ок
1 0 0.9829 подозрително
0 1 0.9829 подозрително
1 1 0.0107 ок

Четири от четири. Същите тегла, същата сигмоида, само един слой повече.

Виж го

Две прави и сигналът през мрежата

static final double[][] W = {{6, 6}, {6, 6}};
static final double[] B = {-3, -9};
static final double[] V = {10, -10};
static final double C = -5;
static double forward(double[] x) {
// поне едното е от чужбина
double h1 = neuron(x, W[0], B[0]);
// и двете са от чужбина
double h2 = neuron(x, W[1], B[1]);
double[] h = {h1, h2}; // скритият слой
return neuron(h, V, C);
}
Стъпка по стъпка, с думи
  1. Четири случая: карта и IP, 1 е чужбина. Подозрителни са (1, 0) и (0, 1).
  2. Най-добрият един неврон чертае една права и улучва 3 от 4: (1, 0) остава от грешната страна.
  3. Скритият слой: две прави, h1 „поне едното“ и h2 „и двете“. Подозрителното е между тях.
  4. Преминаването напред за чужда карта от българско IP: x = (1, 0).
  5. h1: 6 × 1 + 6 × 0 − 3 = 3,00, σ дава 0,9526. h2: 6 × 1 + 6 × 0 − 9 = −3,00, σ дава 0,0474.
  6. Изходът чете h: 10 × 0,9526 − 10 × 0,0474 − 5 = 4,05, σ дава 0,9829.
  7. 0,9829 е над 0,5: подозрително.
  8. Четирите случая през същата мрежа: 0,0107; 0,9829; 0,9829; 0,0107. Четири от четири.

Горе са четирите случая. Една права винаги оставя един грешен. Двете прави на скрития слой затварят ивица, а изходът казва „подозрително“ само в нея.

Пробвай

Предвиди изхода

Същото правило с ReLU вместо сигмоида: h1 = ReLU(карта + IP), h2 = ReLU(карта + IP − 1), изход h1 − 2·h2. Какво ще отпечата за четирите случая?

double[][] cases = {{0, 0}, {1, 0}, {0, 1}, {1, 1}};
double[] out = new double[4];
for (int n = 0; n < 4; n++) {
double[] x = cases[n];
double h1 = relu(x[0] + x[1]);
double h2 = relu(x[0] + x[1] - 1);
out[n] = h1 - 2 * h2;
}
System.out.printf("%.0f %.0f %.0f %.0f%n", out[0], out[1], out[2], out[3]);
Отговор

Пробвай

Намери грешката

Колега пише forward сам. Мрежата отговаря както един неврон: три от четири. На кой ред е грешката?

Избери реда с грешката.

Пробвай

Попълни

Какво получава изходният неврон?

double[] h = {h1, h2};
return neuron(, V, C);

Идеята

Кога скритият слой помага и кога вреди

  • Помага, когато признаците си влияят. „Чужда карта“ сама не значи нищо, „чужд IP“ сам също. Значи съчетанието. Скритият слой учи такива съчетания, без да му ги казваш.
  • Ако знаеш съчетанието, напиши го. Признакът „картата ≠ връзката“ е един ред код. С него стига един неврон или if. Мрежа, която учи нещо, което можеш да напишеш, е по-бавна, иска данни и греши повече.
  • Повече неврони са повече тегла. Тук са 9 числа. Скрит слой от 100 неврона върху 20 признака е 2 201 числа. Повече тегла значи повече данни, иначе мрежата наизустява.
  • Всеки слой струва при всяко плащане. Проходът напред минава през всички тегла за всяка заявка. При голяма мрежа и много плащания това е време и пари.
  • Скритите неврони не са длъжни да значат нещо. Тук значат, защото теглата са избрани на ръка. Научените обикновено са смес, която не се казва с думи.
По-точно, за любопитните

С формули. Скритият слой е h = σ(W·x + b): W е таблица (матрица) 2 × 2 с теглата, b е двете отмествания, а σ се прилага за всеки елемент. Изходът е p = σ(v·h + c). Проходът напред е тези две реда един след друг. Затова в истинските библиотеки слоят е умножение на матрица по вектор.

Дълбоко учене. Мрежа с много скрити слоеве се казва дълбока (deep). Всеки слой строи върху предишния: първите хващат прости съчетания, следващите съчетават тях.

Колко неврона стигат. Доказано е, че един скрит слой с достатъчно неврони може да доближи колкото искаш всяка непрекъсната функция (в ограничена област). Само че „достатъчно“ може да са много, и никой не казва как да намериш теглата. Затова на практика слоевете са няколко.

Малко история. През 1969 г. Мински и Пейпърт показват, че един слой (перцептрон) не може да научи точно това правило (XOR, „или едното, или другото“). Интересът към невронните мрежи спада за години. Решението със скрит слой е известно, но не и как да се учат теглата му. Това идва с обратното разпространение.

Упражнение

Един слой за всички

Реши задачата в редактора си.

forward от урока знае, че скритите неврони са два. Напиши общ layer(x, w, b): получава входовете, таблица с теглата (по един ред за всеки неврон) и отместванията, и връща масив с изхода на всеки неврон. После построй същата мрежа като два слоя: скритият с теглата от урока и изходният с един неврон (тегла 10 и −10, отместване −5). Пусни четирите случая през layer(layer(x, w1, b1), w2, b2).

Изход
карта IP p
0 0 0.0107 ок
1 0 0.9829 подозрително
0 1 0.9829 подозрително
1 1 0.0107 ок

Същата таблица като в урока: layer е общото, а мрежата е два реда с тегла.

Още задачи

  • Трети скрит неврон. Добави неврон с тегла 0 и 0 и отместване 0 в скрития слой (и тегло 0 за него в изхода). Изходът не трябва да се промени.
  • ReLU в скрития слой. Направи layer с активация като параметър и сглоби мрежата от „Предвиди изхода“.

Тест

Провери се

1. Защо един неврон не може да научи „картата и връзката са от различни държави“?
2. Какво е скрит слой?
3. Какво е проходът напред (forward pass)?
4. Скритият слой има два неврона със сигмоида, а изходът е сигмоида. Колко числа учи мрежата?
5. Знаеш, че подозрителното е разминаването на двете държави. Кое е най-разумно?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·