I ff’ed up

Аз съм студент по електротехника, специализирам компютърно инженерство. И да ти кажа, че се прецаках лошо. Избрах тема за дисертация, свързана с обучението за трансфер и не върви толкова добре, защото отлагах да научавам за AI и ML. Така че за една седмица предизвиках себе си да създам невронна мрежа, която разпознава числа, това е ежедневно просто, правено е и преди, нищо ново, освен че ще се опитам да го направя възможно най-бързо.

Откъдето започнах

Гледах плейлиста за невронни мрежи, който страхотният 3blue 1brown има по темата.

Какво е невронна мрежа

Може би вече знаете някои неща за ML и как се държат невронните мрежи. Ако обаче не го направите, намирам за малко по-лесно да разбера как човешкото око работи с мозъка, за да разпознае число. Ето видео за Майкъл от Vsauce, който прави експеримент за поредицата Mind Field.

И така, накратко, невронните мрежи са създадени да „симулират“ действителни неврони. В този случай вземаме входна информация като чертеж на число и нашият модел, състоящ се от мрежа от неврони, трябва да може да ни даде желаното число.

От предишен блог на тема обработка на цифрови изображения. Знаем, че едно изображение може да се разглежда като функция f(x,y) = z, където x и y са координати

От предишен блог за „обработка на цифрови изображения“. Знаем, че едно изображение може да се разглежда като функция f(x,y) = z, където x и y са координати, а z е интензитетът на нашия пиксел. 1 или 255 съответства на бяло, а 0 на черно.

Ще използвам базата данни на MNIST за ръкописни цифри, което е много често срещано. И за да не стане почти невъзможно да се направи за кратко време. Ще използвам Google Colab и python.

Входът.

Изображенията, които ще бъдат анализирани, са прости 28x28 пиксела в сива скала с ръкописни числа. Така че входният слой ще има 28x28 = 784 неврона. Това е нашият първи слой.

Скритите слоеве

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

Всичко се свежда до избора на правилните тегла или параметри за всяка от линиите, които определят правилния път за всеки неврон.

С тази идея можем да приемем следващото уравнение. За всяка стойност на пиксел нека я наречем ai, като i е броят на пикселите. и нека добавим тежест към всеки ред, който се свързва със следващия слой.

така че ще имаме =› a1.w1 + a2.w2 + … + an.wn = c

където a е стойността на пиксела, w е теглото и c е следващият неврон на следващия слой.

Това обаче може да създаде друг проблем. Добавянето може да ни отклони от първоначалната цел, която беше получаването на 0 или 1 за всяко число на активиране на неврон. За да решим този проблем, използваме сигмоидна функция. който получава всякакви входни данни и извежда стойност между 1 и 0.

сега, докато искаме изходът да е между 0 и 1. Не искаме всеки неврон да е активен само защото изходът на сигмоида е по-голям от нула. За да разрешим това, ще трябва да въведем нова променлива, наречена „bias“, така че да можем да имаме малко повече контрол върху това кой неврон се активира.

нашето уравнение сега изглежда така, добавяйки сигмоидната функция и отклонението

σ(a1.w1 + a2.w2 + … + an.wn + b) = c

Където σ е сигмоидната функция. И b е отклонението, трябва всъщност да открием за всеки неврон кое количество е достатъчно добро. Следващата част наистина ме кара да искам да имам по-добър учител по линейна алгебра.

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

За по-късен проект ще се опитам да се поразровя в ReLU, което означава Rectified Linear Unit, служи за подобна цел като сигмоидната функция, но ReLU се оказа по-оптимален за тренировъчни модели.

Как да започнем

Поне за първия рунд на обучение ще зададем всички тегла и отклонения като произволни числа, само за да можем да започнем. Въпреки това се нуждаем от начин да „кажем“ на компютъра, че са на прав път. Нуждаем се от функция „цена“, начин да дадем обратна връзка на мрежата.

От гледна точка на математиката трябва да добавим квадратната стойност на разликата между изхода на последния слой и желания изход за даден вход. Фигурата по-долу го обяснява малко по-добре.

Можете да забележите, че цената е висока, когато изходът е боклук, и малък, когато мрежата уверено отгатва правилно. Сега обаче сме се превърнали в небрежен ядосан родител и бебето ни не знае как да расте. Това, което искаме, е този разход да бъде възможно най-малък. И инструмент, който можем да използваме, за да намерим минимуми в тази функция, е с производни.

Но чакайте, има още. Нашата функция е проста параболична функция. Имаме голямо количество тежести и отклонения за задаване. Изображението по-долу ни подсказва какво трябва да направим. Започваме от произволна точка и от тази произволна точка използваме наклон, който да ни води към следващото по-ниско стъпало. И можем да бъдем една крачка по-близо до желания минимум за нашата разходна функция.

Градиентно спускане

Градиентното спускане е многопроменлив инструмент за смятане, който ни помага да определим „най-бързия маршрут“ или посоката, в която функцията намалява по-бързо. Градиентното спускане може да се означи като ▼C. Това ще помогне на тежестите и пристрастията да се насочат в правилната посока

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

Обратно разпространение

Честно казано, все още съм малко неуверен как работи това, но трябва да продължим. Доколкото разбирам, обратното разпространение е свързано с това, което искате да бъде вторият до последния слой. За да направим това, нека си представим, че имаме следния резултат на последния слой

За вход 2 знаем, че последният слой трябва да увеличи номера на активиране и да го намали за останалите. Трябва да следим „посоката“ за всеки неврон по отношение на това дали искаме да се увеличи или намали. Сигурен съм, че избивам това обяснение, но се надявам да стане малко по-ясно след това.

Засега това е, ще се опитам да споделя кода в следващия блог