Как стать автором
Обновить

Обучаем нейросеть распознавать цифры на выборке от MNIST. Реализация алгоритма обратного распространения на C#

Время на прочтение17 мин
Количество просмотров17K
Всего голосов 18: ↑16 и ↓2+14
Комментарии15

Комментарии 15

Поэтому оставляю за читателем право максимально распараллелить все возможные процессы (Parallel.For, PLINQ и т.п. вам в помощь).

Бесполезно это. Не зря в каждом курсе про машинное обучение (и нейронные сети) расказывают про вычисления над векторами и матрицами. Пока вы для вычислений пытаетесь следовать ОО-парадигме, вы так и будете тратить очень много на накладные расходы.

НЛО прилетело и опубликовало эту надпись здесь

Статья хороша для понимания "как устроены нейронки", но реальные сетки пишутся сильно по-другому.
Весь слой целиком задаётся как матрица (возможно, даже 3-4-мерная) и вычисления выражаются через них. Складывать и перемножать матрицы быстро человечество уже давно умеет.

Если интересно машинное обучение - вот примеры с распознаванием mnist в pytorch и keras.
В Pytorch есть вычисление градиентов, в tensorflow 2.0 - тоже добавили.

Небольшой совет: нынче применяют автодифференцирование :-)
Есть такая классная вещь, как Chain Rule, оно лежит в основе того же PyTorch.
Я как раз на днях имплементил, можно в однофайловом примере посмотреть, как это устроено. Легко повторяется при наличии NumPy/CuPy и терпения. Заранее извиняюсь за черновой код :)
Если вкраце: есть заранее записанные алгоритмы для вычисления производных простых составляющих, из них потом как из лего строят более сложные функции. Производная этой сложной функции элементарно вычисляется, если известно, как считать производные ее составляющих.

И да, @lair прав - в классических нейронных сетях все-таки операции над матрицами происходят, а не ООП. Поиграйтесь с тензорами, в шарпах они вроде как имеются.

Начинание хорошее, продолжайте!

Нечайно минусовал)

Хорошо, спасибо за совет!

И правда, я немного поэксперементировал с Parallel и PLINQ, но никакого прироста производительности не ощутил.


Много лет назад вот в статье "Нейронные сети на Javascript", я делал реализацию на JS, только сам backpropagation я использовал из готовой билиотеки "BranJS" а больше фокус на визуализацию.
Несколько идей по самой реализации:
- В качестве дополнительной нормализации (для песочницы) необходимо определять область с изображением и центрировать ее, иначе если написать цифру не по центру или слишком маленькую в углу то результат будет значительно хуже.
- Переводить из GrayScale в Black and White означает некую потерю информации о изображении. Это ускоряет обучение но все же ведет к потери качества.
- Из соображений производительности сейчас больше применяется пакетный (batch) вариант градиентного спуска

Гм… что-то тут не так

Цифра «8» распознаётся нейросетью как цифра «5» с достоверностью 0,98


Причём, это я ещё по углам натыкал пикселей, чтобы линия более явно чувствовалась, но уверенность нейросети в цифре «5» только росла.

Сетка после "расслоения" изображения в 784 сигнала ничего не знает про геометрию и даже смещение модели вбок на один пиксель может сильно испортить предсказание. Собственно, и количество верных предсказаний такой сетки - 95%.

Если использовать свёрточные слои + max pooling для уменьшения картинки и извлечения признаков, (несколько раз) а только потом "расслоить" маленькую картинку с высокоуровневыми признаками и ещё несколько слоёв как в статье добавить - тогда можно будет достичь 99% точных предсказаний.

Значит, обучающая выборка не была достаточно хорошей, раз нейросеть так легко ломается.

Если посмотрите по коммитам, сначала я вообще хотел нагенерить сам обучающую выборку в пейнте)) Но после 2К изображений мне надоело это занятие и я начал искать...и вышел на mnist))

Нет. Это значит, что выбранная архитектура нейросети не позволяет достичь высокой точности. На датасете mnist тренируют и сравнивают нейронки уже много лет.
Тут результаты: https://en.wikipedia.org/wiki/MNIST_database Практически все лучше результаты принадлежат свёрточным нейронным сетям, причём некоторые без какого-либо препроцессинга и дополнения обучающей выборки выдают error rate 0.25% и меньше

Я не говорю, что датасет MNIST плох. Я говорю, что обучающую выборку из него сформировали недостаточно полную. Условно, если сдвинуть всю картинку на пиксель влево и она начнёт успешно правильно распознаваться, то это значит, что выбранная архитектура сети таки позволяет добиться высокой точности, просто нейросеть не научили как следует.

Да, эксперименты в песочнице меня самого не очень порадовали. Я думал, раз нейронка удачно распознала >95% изображений, то в песочнице она будет почти всегда предсказывать правильно. Но не тут то было. Я заметил, что некоторые цифры она предсказывает лучше остальных.

Помню мое первое знакомство с нейронками было как раз с алгоритмом распознавания цифр. Статья была на питоне и я переписывал все на c#, чтобы понять как эта математика работает. Эта статья довольна знакома и я бы даже поспорил, что корни могут идти отсюда https://proglib.io/p/neural-network-course
https://proglib.io/p/neural-nets-guide

Я нашел эту статью, наверное, за день до публикации. Неплохой обзор. Но у Хайкина вывод алгоритма обратного распространения будет получше.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории