Неврон
Накратко: Невронът е сума от входовете, умножени по тегла, плюс отместване, пусната през активация като сигмоида или ReLU.
Проблем
Мрежата спря плащането. Защо?
Магазинът сменя старото правило за измами с „невронна мрежа“ от външна фирма. На първия ден клиент плаща 230 € от нова сметка с чужда карта, а плащането спира за ръчна проверка. Клиентът пише в поддръжката: „Защо?“
Никой не знае. За екипа мрежата е черна кутия: влизат признаците на плащането, излиза число. А докато не знаеш какво става вътре, не можеш да обясниш решението, да поправиш грешка или да кажеш дали числото изобщо е смислено.
Кутията се отваря. Вътре няма нищо, което не си виждал: умножение, събиране и една функция.
Идеята
Неврон: сума с тегла
Изкуственият неврон получава няколко числа (входовете). Всеки вход има свое тегло: колко силно влияе и в коя посока. Невронът умножава всеки вход по теглото му, събира и добавя още едно число, отместването (bias). Резултатът се казва z.
После z минава през функция, която решава какво излиза. Тя се казва функция на активация. Засега е сигмоидата: тя свива всяко z до число между 0 и 1.
Познато ли ти е? Това е логистичната регресия: претеглена сума, сигмоида, вероятност и праг. Един неврон със сигмоида е точно тя. Невронната мрежа е много такива неврони, свързани един след друг.
Виж го
Невронът в код
Трите функции: претеглената сума, сигмоидата и невронът, който ги свързва.
static double WeightedSum(double[] x, double[] w, double b){ var z = b; // отместването for (var i = 0; i < x.Length; i++) { z += x[i] * w[i]; // вход × тегло } return z;}
static double Sigmoid(double z) => 1 / (1 + Math.Exp(-z));
static double Neuron(double[] x, double[] w, double b) => Sigmoid(WeightedSum(x, w, b));Плащанията имат три признака: сумата в стотици евро, нова сметка (1 или 0) и чужда карта (1 или 0). Теглата са сложени на ръка, за да се смятат наум. Как мрежата ги намира сама, е урокът за обратното разпространение.
// сума (стотици €), нова сметка, чужда картаdouble[] w = [0.9, 1.4, 1.8];var b = -4.0;double[][] payments =[ [0.40, 0, 0], [1.20, 1, 0], [8.90, 1, 1], [0.60, 0, 1], [6.40, 0, 0], [2.30, 1, 1],];Console.WriteLine(" сума € нова чужда z p");foreach (var x in payments){ var z = WeightedSum(x, w, b); var p = Sigmoid(z); var verdict = p >= 0.5 ? "проверка" : "ок"; Console.WriteLine($"{x[0] * 100,8:F0} {x[1],5:F0} {x[2],7:F0} {z,6:F2} {p,6:F3} {verdict}");} сума € нова чужда z p 40 0 0 -3.64 0.026 ок 120 1 0 -1.52 0.179 ок 890 1 1 7.21 0.999 проверка 60 0 1 -1.66 0.160 ок 640 0 0 1.76 0.853 проверка 230 1 1 1.27 0.781 проверкаСега можеш да отговориш на клиента. Плащането за 230 € е последният ред: z = −4 + 2,3 × 0,9 + 1 × 1,4 + 1 × 1,8 = −4 + 2,07 + 1,4 + 1,8 = 1,27. Сигмоидата дава 0,781, над прага 0,5. Новата сметка и чуждата карта добавиха 3,2 към сумата. Без тях z щеше да е −1,93, а вероятността около 0,13.
Виж го
Сигналът минава през неврона
static double WeightedSum(double[] x, double[] w, double b){ var z = b; // отместването for (var i = 0; i < x.Length; i++) { z += x[i] * w[i]; // вход × тегло } return z;}
static double Sigmoid(double z) => 1 / (1 + Math.Exp(-z));
static double Neuron(double[] x, double[] w, double b) => Sigmoid(WeightedSum(x, w, b));Стъпка по стъпка, с думи
- Плащане за 230 € от нова сметка с чужда карта. Входовете са 2,30, 1 и 1.
- z започва от отместването: −4,00.
- Всеки вход по теглото си: 2,30 × 0,9 = 2,07, 1 × 1,4 = 1,40, 1 × 1,8 = 1,80.
- Сборът: z = −4 + 2,07 + 1,40 + 1,80 = 1,27.
- Сигмоидата свива z между 0 и 1: σ(1,27) = 0,781.
- 0,781 е над прага 0,5: плащането отива за проверка.
- 40 € от стара сметка с българска карта: z = −4 + 0,36 = −3,64, p = 0,026. Ок.
- Невронът: сума с тегла, отместване и сигмоида. Това е логистичната регресия.
Отместването е първо, после всеки вход добавя своя дял. Сигмоидата не променя реда: по-голямо z дава по-голямо p. Тя само го свива между 0 и 1, за да се чете като вероятност.
Пробвай
Намери грешката
Колега пише претеглената сума сам. С отместване −4 всяко плащане излиза с много по-малко z, отколкото трябва, и нищо не стига до проверка. На кой ред е грешката?
Избери реда с грешката.
Отместването се добавя за всеки вход, а не веднъж: при три входа то влиза три пъти. С b = −4 сумата е с 8 по-малка, отколкото трябва. Правилно е z да започва от b и в цикъла да се добавя само вход × тегло.
Проблем
Три неврона, а колкото един
Колега иска мрежата да хваща по-сложни правила. Слага два неврона, които четат плащането, и трети, който чете техните изходи. Активация няма никъде: всеки неврон връща самата сума z.
Програмата пресмята трите неврона и, отделно, един неврон, чиито тегла са смятани от техните:
// два неврона четат плащането, без активацияdouble[] w1 = [0.5, 1.0, 1.5];var b1 = -1.0;double[] w2 = [1.0, -0.5, 0.5];var b2 = 0.5;// трети неврон чете техните изходиdouble[] v = [1.5, -0.5];var c = 0.5;// същото като един неврон с тези теглаvar w3 = new double[3];for (var i = 0; i < 3; i++){ w3[i] = v[0] * w1[i] + v[1] * w2[i];}var b3 = v[0] * b1 + v[1] * b2 + c;Console.WriteLine($"w = [{w3[0]:F2}, {w3[1]:F2}, {w3[2]:F2}], b = {b3:F2}");
double[][] payments = [[0.40, 0, 0], [1.20, 1, 0], [8.90, 1, 1]];Console.WriteLine(" сума € три неврона един неврон");foreach (var x in payments){ var a1 = WeightedSum(x, w1, b1); var a2 = WeightedSum(x, w2, b2); var output = WeightedSum([a1, a2], v, c); var one = WeightedSum(x, w3, b3); Console.WriteLine($"{x[0] * 100,8:F0} {output,13:F4} {one,12:F4}");}w = [0.25, 1.75, 2.00], b = -1.25 сума € три неврона един неврон 40 -1.1500 -1.1500 120 0.8000 0.8000 890 4.7250 4.7250Колоните съвпадат до последната цифра. Трите неврона са колкото един. Причината е училищна алгебра: сума от суми е пак сума. Каквото и да наредиш едно след друго, без активация резултатът е една права (по-точно равнина) с други тегла.
Идеята
Активацията пречупва правата
Новото идва едва когато между невроните има функция, която не е права. Двете най-честите:
- Сигмоида: свива z до между 0 и 1. Удобна е за изхода, когато искаш вероятност.
- ReLU (rectified linear unit): max(0, z). Положителното минава, отрицателното става 0. Това е най-честата активация в скритите неврони на днешните мрежи.
static double Relu(double z) => Math.Max(0, z);double[] zs = [-3, -1, 0, 1, 3];Console.WriteLine(" z sigmoid ReLU");foreach (var z in zs){ Console.WriteLine($"{z,6:F2} {Sigmoid(z),8:F4} {Relu(z),5:F2}");} z sigmoid ReLU -3.00 0.0474 0.00 -1.00 0.2689 0.00 0.00 0.5000 0.00 1.00 0.7311 1.00 3.00 0.9526 3.00И двете са огънати. Сигмоидата е „S“, а ReLU е права с пречупване при нулата. Сума от пречупени линии вече не е права, затова неврони с активация един след друг могат да опишат правило, което един неврон не може. Такова правило идва в следващия урок.
Пробвай
Предвиди изхода
Неврон с ReLU оценява колко дни закъснение има доставката. Входовете са разстоянието в стотици километри и дали колетът е тежък. Закъснението не може да е отрицателно, затова ReLU. Какво ще отпечата?
double[] w = [0.5, 1.0]; // стотици км, тежък колет (1 или 0)var b = -1.0;var near = Relu(WeightedSum([1.5, 0], w, b));var far = Relu(WeightedSum([4.4, 0], w, b));var heavy = Relu(WeightedSum([3.0, 1], w, b));Console.WriteLine($"{near:F1} {far:F1} {heavy:F1}");Близката доставка: 0,5 × 1,5 − 1 = −0,25, а ReLU я прави 0. Далечната: 0,5 × 4,4 − 1 = 1,2. Тежкият колет: 0,5 × 3 + 1 − 1 = 1,5. Със сигмоида щеше да излезе 0,4 0,8 0,8, а без ReLU първото число щеше да е отрицателно: −0,25.
Пробвай
Попълни
Довърши ReLU.
static double Relu(double z) => (0, z);Math.Max(0, z) връща по-голямото от 0 и z: положителното минава, отрицателното става 0.
Идеята
Кога един неврон стига, а кога вреди
- Правилото е почти право. Ако признаците сами по себе си казват отговора (по-голяма сума, повече риск), един неврон, тоест логистична регресия, прави колкото и голяма мрежа. Учи се по-бързо и иска по-малко данни.
- Данните са малко. Мрежа със стотици тегла и 200 примера ги наизустява. Това е преобучението.
- Трябва да обясниш решението. Теглата на един неврон се четат: „новата сметка добави 1,4“. В мрежа с много слоеве отговорът „защо“ е много по-труден, а при спряно плащане или отказан кредит той се иска.
- Правилото е известно и твърдо. „Над 5 000 € винаги ръчна проверка“ е
if, не модел. - Числата на входа са в различен мащаб. Сумата в евро (40, 890) до признаци 0 и 1 дава тегла, които трудно се учат. Затова сумата тук е в стотици евро. В истински модел входовете се нормират.
По-точно, за любопитните
Формулите. За неврон с входове x₁, x₂, x₃:
z = w₁·x₁ + w₂·x₂ + w₃·x₃ + b
p = σ(z) = 1 / (1 + e^(−z))
С думи: всеки вход по теглото си, плюс отместването, после сигмоидата. e е числото на Ойлер (около 2,718), а Math.exp(-z) е e на степен −z.
Защо три неврона без активация са един. Ако a₁ = w₁·x + b₁ и a₂ = w₂·x + b₂, то v₁·a₁ + v₂·a₂ + c = (v₁·w₁ + v₂·w₂)·x + (v₁·b₁ + v₂·b₂ + c). Това е един неврон с тегла v₁·w₁ + v₂·w₂. Точно това смята програмата с трите неврона.
Други активации: tanh (като сигмоида, но между −1 и 1), leaky ReLU (отрицателното не е 0, а малко, например 0,01·z), softmax (на изхода, когато класовете са повече от два: превръща няколко числа във вероятности със сбор 1). Има и много други, но ReLU и сигмоидата са достатъчни, за да разбереш всичко в модула.
С какво се правят мрежи в истинска работа. Тук всичко е цикли и масиви, за да виждаш всяко число. Истинските мрежи са с милиони тегла, а сметките са с матрици на видеокарта. Практиката на машинното обучение е предимно на Python: NumPy за масивите, scikit-learn за класическите модели, PyTorch за невронните мрежи. Има и други езици: ML.NET за C#, DJL и Tribuo за Java, TensorFlow.js и ONNX Runtime за JavaScript. Често моделът се учи на Python, а се ползва от програма на друг език (например през ONNX).
Името. „Неврон“ идва от биологията: клетка, която събира сигнали и „пали“, щом сборът мине прага. Приликата спира дотук. Изкуственият неврон е функция с числа.
Упражнение
Неврон за отзивите
Реши задачата в редактора си.
Магазинът иска да подреди отзивите на добри и лоши. За всеки отзив вече са преброени хубавите думи („бърза“, „качествен“) и лошите („счупен“, „закъсня“). Напиши неврон със сигмоида с тегла 1,1 за хубавите думи и −1,7 за лошите и отместване 0,2. Пусни го върху петте отзива (хубави, лоши): (3, 0), (1, 2), (0, 0), (2, 1), (0, 3). Отзивът е добър, ако p е поне 0,5.
хубави думи 3, лоши думи 0: p = 0.971 → добърхубави думи 1, лоши думи 2: p = 0.109 → лошхубави думи 0, лоши думи 0: p = 0.550 → добърхубави думи 2, лоши думи 1: p = 0.668 → добърхубави думи 0, лоши думи 3: p = 0.007 → лошОтзивът без нито една дума излиза добър с 0,550: това е само отместването. Тоест без други сведения моделът леко вярва, че отзивът е добър.
Още задачи
- Трети признак. Добави удивителните знаци като признак с тегло −0,3 и виж кои отзиви сменят решението си.
- Прагът. Сложи праг 0,7 вместо 0,5. Кои отзиви стават лоши?
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: Какво е машинно учене · Линейна регресия · Класификация: измама или не · Преобучение: моделът, който наизустява
- Продължава в: Слоеве · Обратно разпространение · Обучението от начало до край · Цифрите от касовата бележка
- Задачи по темата: Невронна мрежа отвътре