всем привет сегодня мы запрограммируем не разительно языке си плюс плюс и в итоге мы получим такую программу которую угадываю цифры [музыка] [музыка] но сделаем краткий экскурс нейросети я изобразил у такую не рассеять конфигурацию не такая это 5 входных нейронов один скрытый слой с двумя нейронами и два выходных нейрона а естественно конфигурации может быть любой но принцип работы остается неизменным а каждый нейрон входного слоя связан с каждым нейронов скрытого слоя связью эту связь называют весом вес это число и она является обучаема параметрам то есть в процессе обучения мы его будем изменять но о том как мы это будем делать я расскажу потом учимся работу нейросетей основана на линейной алгебре чтобы в этом убедиться давайте посчитаем значение вот этого нейрона на скрытом слое чтобы это сделать мы должны посчитать зверь иную сумму она считается так и 00 это значение вот этого нейрона умножая на без связи w 000 здесь первый индекс отвечает за номер слоя 2 за номер нерона из предыдущего слоя и последний за номер нейрона и следующего слоя дальше + n 10 умножаем на вес w0 10 дальше + n 20 умножаю на вес w0 20 ну и так далее еще + + + b 0 0 это нейрон смещение он отделён от входного слоя и у него значение не изменяется обычно это или минус 1 или + 1 я взял один а то есть и 01 это будет равна сумма и от нуля до четырех oблaдaeт 0 и 0 ножа им на н а н и т 0 и плюс еще бьюз плюс 20000 дальше а n 11 аналогично это будет равно сумма и от 0 до 4 w 0 и 1 ножа им на n это нолик и еще плюс плюс b01 или в матричном виде это будет выглядеть так это переписал матрицу весов мы умножаем на вектор-столбец еще + by eus и получаем значение нейронов на скрытом слоя но этого мало а когда мы считаем взвешенную сумму то мы должны понять активируется линер он или нет это пришло из биологией и для этого вводят функцию активации давайте обозначим как f и аналогично запишем для последнего слоя а это я переписал матрица весов дальше мы умножаем на вектор-столбец еще + by eus и получая значение нейронов на скрытом слоя отметьте важная особенность f&f должна быть нелинейный иначе наша не разлить не сможет работу с нелинейными зависимостями а давайте представим что этот и линейной функции активации и тогда смотрите а матричное умножение это линейное преобразование дальше plus belles это тоже какой-то линейное преобразование а искусство линейной алгебры мы знаем что попадает линейное преобразование эквивалентна одному линейному преобразованию ну и аналогично с ф ф это линейные функции это линейное преобразование и получается так что вот это все это одно линейное преобразование ну и аналогично здесь это тоже какое-то одно линейное преобразование а используя ту же самое правило мы получаем что у dota все это одно линейное преобразование из многослойно нейросети наша не раз сеть стала однослойный нас это не устраивает потому что однослойной нейросети могут работать только самыми простыми данными а теперь разберемся какие существуют функцию активации а вообще их много но основные из них я привел здесь это 7 до 1 разделить на 1 плюс i степени минус x и множество ее значение от 0 до 1 и там ударил у или рилз утечкой это x если x больше либо равно чем 0,01 x если x меньше чем 0 также есть обычный лилу это просто макс 0x но он используется менее часто потому что при x меньше чем 0 а значение просто отсекаются и нейрон активироваться уже не будут еще тангенс множество его значения + 1 до 1 а какую функцию ситуации выбирать это решать вам но могу сказать что мотре лу это самая популярная функция потому что тут нету экспонент и поэтому вычислений будут идти быстрее а теперь начнем прыгать я создал обычный проект и теперь давайте добавим класс и основной класс это будет network добавим еще один класс magic там будем реализовывать умножение матриц и добавим еще один класс это класс активационный функций активы а теперь реализуем самый ноги класс матрица у него будет та мерный массив типа дабл magic матрицу нас будет произвольного размера ролл это строки каллум это столбцы методы их будет больше но основные здесь и нет это инициализация наши матрицы там будем выделять память рэнд это заполнение случайными числами нашей матрицы мульти это умножение матрицы на vita в толпе с на входе не будет константной а ссылка на объект класса magic дальше б.н. это размерность b и c это то что мы хотим получить сам вектор это понятно кстати коми я их сделал чтобы их можно было вызвать не обращаясь к кому-то конкретному объекту класса матрицы а теперь определения здесь мы запоминаем строки и столбцы и выделяем память под нашу матрицу не заполняем ее нулями дальше здесь мы вполне нашему отелю случайными числами вот по такой зависимости она может быть в принципе любой дальше здесь мы проверяем модулем и умножать матрицу на вектор-столбец то есть мы проверяем чтобы количество строк равнялось количеству столбцов иначе мы выбрасываем исключение но и здесь цикле но мы производим умножение тут объяснять особо нечего это линейная алгебра первого курса сам вектор тут тоже объяснять особо нечего как я уже говорил мы будем обучать нашу нейросеть надо достать и мне ст а ты круто находится в черно-белом виде также есть официальный сайт откуда можно скачать данные в общем там находится 60 тысяч цифр для обучения и 10000 и цифр для теста тут то своя файловая система в общем тут есть файлик с цифрами от 0 до 9 и еще один файлик с пикселями для них но и аналогично для тестовых цифру также у них есть свой файловый формат в общем там все находится в бинарном виде она париться не надо я это уже все считал и записав текстовый читабельный вид тут находится цифр для обучения я еще добавил свои цифры сорок шесть штук а вот они в самом конце я потом объясню для чего ну и также цифры для теста вот они цифра семь цифра двойка вот так она выглядит цифра 1 цифра нолик ну и так далее в общем мы будем обучать нашу не рассеет на этих данных как вы могли заметить я сжал данный вне стен диапазон от 0 до 1 то есть нормировать и отсюда совет ограничение на активационная функция а 7 до все окей тут интервал от 0 до 1 нас это устраивает дальше реалу тут есть утечка снизу но сверху данные ничем не ограничены а у нас диапазон от 0 до 1 надо это исправить дальше тангенс тут интервал от -1 до 1 а у нас диапазон от 0 до 1 и нас это не устраивает то есть нам надо как-то изменить их под нашу задачу давайте этим займемся а вот так и изменил функция активации добавил лилу утечку сверху он так она стала выглядеть и записывается вот так ходить интервал не изменился но мы учит тот факт что диапазон данных от 0 до 1 у тангенса я добавил утечку снизу так он стал выглядеть и записывается вот так а теперь давайте посчитаем производные они на пригодятся для подсчетов градиентов начнем 7 д а остальные вы сами запишите и тогда f штрих от x это будет равно сначала берем по степени потом по аргументу то есть там знаки минуса выйдут получается е в степени минус x разделить на 1 плюс i степени минус x в квадрате а замечаем что вот это f от x это будет равно f от x в квадрате умножить на е в степени минус x и давайте добавим и отнимем 1 то есть плюс 1 минус 1 замечаем что вот это это 11 лет на f от x то это будет равно f от x умножить на один минус f от x вот так нууу лилу тут все очень просто здесь будет одна сотая 1 и одна сотая у тангенса уже поинтереснее но вы это сами посчитаете я спешу ответ f штрих от x это будет равно 1 минус тангенс в квадрате x если x больше либо равно чем 0,01 на 1 минус тангенс в квадрате x если x меньше чем 0 вот так а теперь разберем с классом активационный функции добавим сюда перечисляемых а у нас будет всего 3 активационной функции там нумерации идет с нуля но я начал для удобства с одного тогда создадим объект перечислимого типа и отзовем его акт по ну и добавим методы asset дальше юз на входе будет вектор белье и его размер ну и аналогично бой + dr то есть производная целью и int n вот так ну а теперь определение сет тут мы просим ввести активационный функцию один это 7 до двойка и тори лун и тройка это тангенс иначе мы выбрасываем исключения и работа программы of наливается дальше you stood все формула которых я говорил ранее то есть для 7 допустим это 1 разделить на 1 плюс i степени минус x ареалу аналогично вот мы это записывали ну и станкин сам тоже дальше user это производная все эти формулы мы с вами вывели ранее то есть вот 1x дальше реалу ну и тангенс то есть все эти формулы я записал а теперь самый главный класс класс нет вот что у нас в нем эль это сколько слоев сайт этот сколько нейронов по каждому слою а активы сша фланг это понятно активационной функции это матрица весов и to be so смещение это значение нейронов это ошибки для нейронов это значение нерона смещения у нас они равны одному но мало ли мы закатим это изменить дальше идут методы мы с ними потом разберемся еще добавила структуру data network а это удобно чтобы сравнивать нейросеть с разной конфигурации и низводит много переменных под них а теперь определение и не здесь мы у xfan называем этот сет который мы определили ранее дальше инициализируем генератор случайных чисел запоминаем сколько слоев наше нейронной сети in выделяем память под массив сайт и заполняем его значению дальше выделяем память под матрицу весов и заполняем ее рандомными числами аналогично с весами смещения а уделяем под них память и заполняем их про данными числами вот по такой зависимости она может быть в принципе любой даль выделяем память под все остальные массивы включая нейроны смещения и заполняя и единичками это могут быть тоже принципе любые числа то ли прям конфиг тут мы выводим на экран сколько слоев наше нейрон осетин и массив says на экран дальше сайт ебут тут мы подаем на вход в нейросеть данные у нас это будут изображение принести там размер изображения 28 на 28 пикселей то есть всего получается цветут 84 входных нейрона а теперь разберем с функцией прямого распространения на самом деле мы уже записали это просто матрица весов умножаем на вектор столбе значению нейронов еще плюс плюс дальше функция активации и получаем значение нейронов на следующем слое но и аналогично на других слоях а теперь разберемся как же мы будем получать ответы от нашей нейросети так как я уже говорил мы будем обучать нашу не рассеет надо то с этим не ст м размер изображения 28 на 28 пикселей то есть всего получается 84 входных нерона давайте изобразим примерную картинку есть входной слой там 1 2 3 и так далее а дальше есть скрытый слой сколько там нейронов это пока что не важно итак а цифры всего от 0 до 9 10 то и нейронов будет 10 то есть выходной слой будет состоять из 10 нейронов am 1 2 3 далее 10 далее мы скажем что индекс нерона отвечает за цифру как индексация в си плюс плюс начинается с 0 то это очень удобно это будет отвечает за цифру нолик этот за один этот за 2 ну и так далее этот за цифру 9 далее мы загружены какую-то цифру делаем прямого распространение и получаем какие-то ответы на выходных нейронах мы скажем что где максимальное значение там ответ допустим максимальное значение оказалась здесь значит не россии думают что этот сейф ради если наказава здесь значит не россии думает что эта цифра 9 ну и так далее а максимальное значение может быть равно одному так мы сжали данный диапазон от 0 до 1 кстати отсюда уже можно легко найти ошибку для выходных миронов так мы знаете правильный ответ значит то там ответ должен быть равен одному а на всех остальных должен быть равен нулю но об этом я расскажу потом ну а теперь определение ford фильм мы ее уже сто раз разбирали но давайте еще раз общем матрицу весов умножаем на вектор-столбец значений нейрона передаем туда еще размер это чтобы хотим получить дальше суммируем этот вектор с биосом и используем функцию активации вот и все дальше мы должны получить ответ от нашей нейросети для этого я написал отдельную функцию search макс индекс что она делает она принимает на вход вектор значений и ищет в нем максимальный элемент и возвращает индекс этого элемента то есть нашу цифру вот дальше printf элис тут все легко мы выводим на экран индекс а и значение нерона на экран на этом слое ну а сейчас начинается самое интересное не переключайтесь итак как же обучается нейросеть давайте представим что вы подали на вход цифру 2 далее мы используем функцию ворот fit и получаю какие-то значения на выходные к нейронах а мы знаем цифру следовательно мы знаем где должно быть максимальное значение оно должно быть здесь давайте ведь он еще одно обозначение как эталонный ответ то есть это был предсказанный ответ а это будет эталоны ответ и толо обозначим его как д то есть d2 должно быть равно одному как у нас максимальное значение это один а на всех остальных мы значение обнулять то есть d1 должно быть равно нулю д 0 равно нулю ну и так далее д 9 равно нулю окей теперь видим функции ошибки или функции потери как сумму квадратов отклонений и обозначим ее как е т е н она зависит от весов по wp равно эта сумма и от нуля до девяти t и t минус и berg & t в квадрате нам нужно эту функцию минимизировать то есть найти минимум общаясь отдельный курс методы оптимизации но блэра сетях очень прочно закрепился метод градиентного спуска и вал модификации а есть такая цитата который я взял из книги когда градиентный спуск неприменим решение состоит в том что мы все-таки его и применить ну из неё следует важность этого метода чем же заключается метод градиентного спуска изобразим график функции ошибки е но у нас функция многомерные тут много весов поэтому изобразим график для случае 1 перемены для удобства а потом будет легко обобщить для многомерного случая а это будет осенью это будет ось w и есть какой-нибудь функция такая вот и пусть при начале инициализации весов мы находимся где-нибудь вот здесь w нолик есть такая штука как градиент это вектор он смотрит в сторону наискорейшего возрастание функции и записывается как оператор на блоге или просто град е если здесь а знаком минус то он будет смотреть в сторону наискорейшего нее функции а это то что нам надо а это вектор то у него есть компоненты если всего лишь одна переменная то это просто одна производная давайте запишем операционную формулу тобою и плюс 1 равно w & t минус d&ad в но сюда еще добавляет альфа альфа это скорость обучение или лене кроет это какая-то константа в интервале от 0 до 1 но она очень важна потому что если ее взять очень большой то мы будем скакать по оврагам и никогда не попадем в минимум если ее взять очень маленькой таскать и самым будем очень медленно но стратегии управления скорость обучения интуитивно понятно а сначала мы берем большой а потом с каждым итерации делаем ее все меньше и меньше и меньше но вспомнил что у нас не одна переменная а много переменных и поэтому наш градиент будет состоять не из одной производный а из много частных производных то есть тут будет а a&w нолик нолик ну и тогда или и аналогично с биоса тут будет д и о tmb палит нолик и так далее то есть нам надо посчитать каждую частную производную и сдвинуть каждый вес вот по такой итерационной формуле остается вопрос как же посчитать эти частные производные но на самом деле все легко и сделаем еще одну вещь разделен две операции взялись развешены суммы функций активации то есть s01 это взвешенная сумма о которой я говорил ранее дальше мы используем активации и получаем значение нейрона ну и аналогично на выходном слое это я сделал чтобы было удобнее считать градиенты ну что поехали искать наши градиенты давайте найдем d&ad w100 ну а по остальным 3 месяца аналогично а тогда используем правило дифференцирования сложной функции и получай d&ad с0 2 умножить на d с 02 по dw 100 давайте найдем даны производная она сама легкая распишем сумму с02 это было взвешенная сумма а это будет равно дабы 100 умножить на n 01 дальше плюс aw110 умножить на n 11 еще плюс bios b10 окей да еще производная df02 о dw 100 это будет равно это будет равно n 01 вот и все давайте найдем дано производна она уже поинтереснее но перед этим распишем ошибку ошибку мы уже писали это была сумма а там было раньше и от нуля до девяти но у нас будет и от 0 до 1 потому что всего лишь два выходных нейрон а сумма д и т минус или и t в квадрате теперь ищем производную то есть d&ad s02 это будет равно но заметим что данный вес он влияет только на эрик 0 на данный нейрон он никак не влияет и поэтому производные вместо сумма можно записать только одно слагаем то есть это будет равно d 10 минус y 0 в квадрате о df02 это будет равно 2 умножить на d 0 минус y 0 и умножить на минус d y 0 о д с 02 а теперь вспоминаем что такое эрик 0 а эрик 0 это f от с 02 по нашему графу и тогда производная d y 0 о д с 02 и то есть f штрих от s02 вот для чего мы искали производной функции активации именно для этого ну а теперь запишем итоговый форм то есть и по dw 100 будет равно к минус вынесем это будет минус 2 давайте эта штука обозначим как и psion 0 для удобства просто -2 и psy а 0 умножить на f штрих от x 0 2 и умножить на n 01 ну а теперь запишем это рациону you формулу для веса то есть w100 это будет равно w 100 минус альфа умножить на d&ad дал бы и 100 если мы сдвинем вес по вот этой формуле то мы уменьшим общую ошибку а это уже хорошо градиенты по остальные весам ищутся аналогично найдите и самостоятельная вас подожду ну а я спешу общую формулу то есть да и о dw 1g и это будет равно минус 2 и все это умножить на f штрих от сыт 2 и умножить на n ag1 вот так теперь нам нужно посчитать градиенты по вот эти весам но там все то же самое поэтому я сейчас все стираю и мы начинаем ах да чуть не забыл давайте еще надел дейэн ты по весам смещений это будет очень быстро то есть ты им по и b10 коды b11 ищется аналогично опять же используем правило дифференцирования сложных функций и получаем d&ad с0 2 умножить на d с 02 по db10 смотрим нашу сумму и производная по b10 это есть один то есть это равно одному ну а это производные мы уже искали вот она и получая минус 2 умножить на эту 0 умножить на r штрих код с 02 если искать о бы 11 то тут изменится на 1 и здесь тоже а сдвигаются они аналогично ну что поехали искать наши градиенты давайте найдем пол первую весу а по остальным весам и что по налоги есть ли хотим найти д е о dw нолик нолик норик используем правило дифференцирования сложной функции и получаем дтп с 0 1 умножить на d с 01 по dw нолик нолик 0 и разберем с крайне производной она сама легкое s01 e01 и то есть взвешенная сумма то есть эта сумма и от 0 до 4 w 0 и 0 умножить на n и t0 и еще плюс bios плюс b 0 0 окей и производная по w 000 и то есть и 00 то есть это равно n 0 0 и это производный т.е. о.п. с 01 я же используя правила дифференцирования сложной функции да и е о н 0 1 умножить на d n 01 о п с 0 1 0 1 0 1 и то есть f от s01 это равно f от s01 и производное и то есть f штрих от s01 окей а теперь найдем вот эту производную но смотрите она нейрон он оказывает влияние и на этот нейрон и на этот нейрон и поэтому производные она уже будет равна сумме производных по цепному правилу то есть и pa01 это будет равно сумме сумма и от 0 до 1 и е о б о с и т 2 умножить на d сип-2 о д н 0 1 опять же начинаем с крайне производной она самая легкая а с и т 2 ну мы это уже писали но давайте еще раз чтобы было все понятно давайте для примера с02 с02 это будет равно w 100 умножить на n 01 + w 110 умножить на n 11 еще + by us + b 10 и и производные по t 0 1 и то есть w100 а если вы распишите с 12 то производной по и 01 это будет w 1 0 1 то есть это будет равно w10 и вот так ну а вот эту производную мы уже искали давайте я напомню чему она равна т е о с и т и то есть это есть минус 2 и все это умножить на f штрих от сыт 2 вот так запишем удобную форму то есть и е а dw уволит нолик но ли это будет равно смотрите и 00 война и штрих-код с 01 умножая на сумму сумма и от 0 до 1 -2 и все это умножить на f штрих от сыт 2 и умножить на w 10 это давайте вот эту штуку вынесем за знак суммы куда-нибудь вот сюда и обозначим вот это вот как дельта и т2 и вот эту все по аналогии обозначим как это 01 это дельта нейронов это обозначение часто делают книгах и она будет для нас удобным а теперь смотрите это но мы умножаем на весь связи w100 дальше плюс дельта 1 умножаем на вес связи 101 эти дети от выходных нейронов и протаскиваем к этому нерона отсюда и идет названием ipr погибшим или обратно и устранения ошибки и запишем удобную форму для котировки риссов то есть w 000 это будет равно w 000 минус альфа умножить на d и по pw 000 и поставляем то что мы нашли вот сюда это будет равно w 000 + по альфа умножить на n 0 0 и умножить на дельта 01 обычно вот эту двойку здесь не пишут а сразу ее суют в альфа мы так и сделаем я остальных весов формула аналогично это и смотрите чтобы изменить данный вес тут изменится только дельта 1100 остается также допустим чтобы изменить данный вес изменится только n10 а дельта 01 остается также ну и аналогично для остальных да я забыл еще провиса смещения давайте быстренько их распишем то есть и е о т б 00 p01 аналогично а это будет равно опять же используем правило дифференцирования сложной функции и получаем д е о т с 01 но ведь надо с 01 по г б 00 смотрим s01 это такая взвешенная сумма и производной под ip00 и то есть один это равно одному а вот эту производную мы уже искали бога на то есть это будет равно это 01 ну а изменяется он также то есть b 0 0 это будет равно b 0 0 + альфа умножить на дельта 01 прям b01 тут будет только 11 вот и все заметьте важную вещь что дельта для скрытых нейронов но можем записать как матричное умножение матрицы весов умножить на дельта и выходных нейронов давайте это запишем то есть смотрите вот матрицы весов мы умножаем на дельты выходных неронов дальше на производный функции активации и получаем дельты для вот этих вот двух нейронов а но заметим одну важную вещь но изначально у нас матрица лесов была такая тут был w1 10 а теперь sw 101 то есть данный матрица она является транспонированной по отношению к той и это нужно помнить вернемся классу activate faction и перегрузе метод xd теперь тут будет на входе ней вектор а какое то значение типа дабл и возвращаемый тип у него будет дабл у кого-то может быть опрос почему же я тут не использовал всегда был то есть вот так чтобы он возвращал указатель на тип дабл это я сделал для того чтобы не думать об очистке памяти потому что иначе на батут пришлют выделять память под директор дальше мы возвращаем указатель старый будет теряться а это не очень удобно поэтому у меня есть будет тип бой ну а реализация абсолютно аналогичная я убрал отсюда циклы и добавил ритер белье вот и все и еще измене класс magic добавим сюда метод multi t это для умножения транспонированной матрицы и перегрузе император круглые скобки теперь реализация а здесь я изменил только цикл по сравнению с нашим предыдущим методом то есть я не создавал новую матрицу а просто изменю цикл и получил умножение то спланированной матрицы на обед а в столбец ну а тут я просто возвращаю элемент матрицы по ссылке а теперь самый главный мертвы бэкапа гейш а ходе у него правильная цифра дальше в цикле считают это для выходных неронов это ipsy он 0 на f3 а дальше в цикле считаю intel для скрытых нейронов это матрица весов на дельте а и забываем нажать на производные функции активации а дальше в этом пейтер это обновление весов а в цикле мы просто обновляем сервиса это значение нейронов на дельту и на лене кроет и аналогично с bios а вот и все самое сложное уже позади и осталось еще два метода это сохранение весов а и чтение весов из файла эту подключаем библиотеку австрии и сделаем еще одну вещь в классе magic себе перегрузил операторы потока ввода и вывода как это сделал смотрите алеф операндом является поток пойдем по всей матрице и записываем элементов матрицы в поток если это seal то информация будет у нас на экране если это австрии mta информация будет у нас файле а так они оба являются наследниками класса a stream о все будет работать и возвращаем ссылку на поток аналогично с сыном и экстримом теперь мотор или space открываем файл и записываем сервисов алек ну и то же самое с red white открываем файл и считываем сервиса из файла вот и все так ну и наверное самая скучная это файл cpp а тут у каждого может быть что-то свое но что я сделал я создал структуру do the info это для цифр то есть есть цифры от 0 до 9 и пикселя к ней у нас 28 на 28 пикселей шире отдать нету у меня есть файлик какой speak . тексте тут сколько слоев и сколько нейронов на каждому слою то есть массив says много слоев брать не советую потому что уже доказали что даже одного скрытого слоя вполне хватает для обучения но и обучаться она будет дольше дальше наша функция g data то есть это уже для цифр у нас есть по алекс и парками lip mist и мы оттуда считываем данные а сколько обучающих или тестовых цифр выделяем память под дату и уже в цикле считываем данные а то есть начало идет цифра а потом уже к ней идут пиксели вот и все дальше идет main это все необходимые переменные райт и the right answer это количество правильных ответов за одну эпоху про это правильная цифра это предсказанная цифра это максимальное количество правильных ответов за одну эпоху и под этой эпохи дальше инициализации наши нейросети то есть читаем данные из конфига инициализируем нейросеть и выводим не конфигурацию на экран а узнаем хотим ли мы ее обучать если да то заходим в цикл отсчитываем данные излив места идет циклы while мы хотим ее обучает до 100 процентов правильных ответов но понятно что это не всегда реально и поэтому менять ограничение на эпохи и обучает до 20 эпох но это можно изменить а самый главный цикл for вот он выйдём вовсе обучающим примером подаем на вход в нейросеть пикселей а это правильная цифра из даты белый парус fit и получаем предсказанный у цифру от нейросети если они не совпадают по обучаем нейросеть то есть ищем дельты те которые мы искали и обновляем наши веса я использую экспоненциальное затухание то сначала лене cray большой 015 а потом из каждой итерации все меньше и меньше и меньше что сохраняя besan или если оно обучено то считываемых из файла тестовых цифра пять же считываем данные из фалика и идем в цикле и проверяем если она угадала цифру то увеличиваем счетчик и выводим это все на экран вот и все давайте запустим и посмотрим я буду использовать силу она самая быстрая обучаем обучение будет идти долго поэтому я ускорю видео [музыка] [аплодисменты] [музыка] [музыка] [музыка] [музыка] [музыка] [музыка] [музыка] обучение пошло успешно она заняла 12 минут в принципе не так то и много обучилась надо 99 3 процента это неплохой результат ведь давайте проверим на тестовых цифрах мем 1 1128 это неплохой результат я создал пайку теберду при kitchen о том как это можно сделать я рассказывал своем ролике об open gl подсказка будет наверху общем я перенес все файла который мы сделали без изменений еще я добавил виджет pain стенд дину будем рисовать нашу цифру для этого я перегрузим этот painted and и еще парочку методов это маус призывает mouse and mouse релиз avent они отвечают за нажатие клавиши мышки за движение клавиши мышки и за отпускание клавиши мышки ах да еще вспомни что я добавлял свои цитру вниз сорок шесть штук это я сделал потому что на плохо укладывал мои cipro ка дай тут рисовал а она плохо готова цифру 9 7 и 6 это потому что смотрите а цифра 9 внести вот такая у меня жена вот такая на более человеческая и поэтому неплохо угадывал но я обучил нейросеть и без своих цифр то есть вот ровно 60 тысяч обучение прошло успешно то же самое время и даже лучше чем с моими цифрами но это в принципе очевидно а теперь файл cpp но о нем я подробно рассказывать не буду потому что это уже не относится к теме неровно сетей и скорее всего это не будет кому то интересно ноги расскажу о том как я рисую цифры осмотрите адама жмем клавишу мышки то вызывается данный метод и мы делаем флаг дро утру дальше когда мы двигаем мышкой и если флаг дроу активен то мы запишем координаты мышки вектор и по этим координатам и рисуем эллипсы в данном цикле а цвет кисти я взял зеленым но это можно изменить там есть красный голубой там желтый и так далее когда мы отпускаем клавишу мышки то вызывается данный метод флаг дроу делаем falls а делаем скрин экрана открываем текстовый файлик преобразовываем q pixma ip ваку имидж уменьшаем размер изображения до 28 на 28 пикселей как это вместе дальше идем в цикле по всему изображению и узнаем цвет каждого пикселя интересует именно зеленый цвет поэтому тут зеленый вот и записываем это все файлик вот и все спасибо за просмотр надеюсь я вас не утомил если было что-то непонятно или какие то вопросы то оставляйте в комментариях я на них отвечу так же я сделал опрос на тему следующего видео ссылка будет описание