Здравствуйте, Khimik, Вы писали:
K>В интернете приводятся примеры в основном на Питоне, я надеюсь Delphi тоже вполне подходит для такой задачи. Если Питон – более высокоуровневый язык и это достаточно важно, может мне сначала нужно выучить Питон.
1) Верно ли я понимаю, что ты знаешь что такое матрица, тензор (многомерная матрица, преобразование координат нам не потребуется), линейный оператор, нелинейный оператор?
Если да, то на каком-то уровне приближения, и если ограничится сетями для обработки изображения, более или мене любую сеть можно представить в виде последовательности линейных и нелинейных операторов действующих из пространства трёхмерных тензоров одного размера, в пространство 3-х-тензоров другого размера.
Размерности тензоров -- ширина изображения х высота изображения х число каналов.
Нелинейные операторы (слои активации) это какие-то функции, которые вносят в систему нелинейность. Например отображают XxYxN в такие же XxYxN, но по правилу, что если на входе отрицательное значение, то на выходе ноль, а если на входе неотрицательное, то оно сохраняется.
Ключевым тут является то, что для обучения нужно, что бы градиенты (частные производные функции потерь по входным параметрам слоя) можно было вычислить и передать на предыдущий слой. Поэтом есть много вариантов слоёв активации.
Линейные же слои, тоже имеют хитрость. Иногда бывают слои с X1xY1xN1xX2xY2xN2 независимых коэффициентов (тут X1xY1xN1 -- входной тензор слоя, а X2xY2xN2 -- выходной)
Обычно они называются полносвязными (Dense), но сети из таких слоёв имеют много проблем.
а) нельзя менять размер входных/выходных тензоров
б) очень плохо обучаются и глубокую сеть (много слоёв) из таких штук не обучишь.
Поэтому придумали много разных хитрых ограничений на эти операторы. Иногда их делают разряженными, например блочно-диагональными, иногда делают доп. условие, что какие-то коэффициенты совпадают и т. д. Это даёт разные слои (cnn, например)
Хорошей особенностью CNN является то, что там один и тот же набор коэффициентов (ядро) применяется к разным местам (x, y) входного тензора. То есть можно менять размеры входного тензора, используя одно и то же ядро.
Ещё есть нелинейные слои, которые берут и отображают 2Xx2YxN в XxYxN, путем выбора из квадратика 2x2 обычный подход -- выбор максимального значения.
В общем, если посмотреть на это всё с такой точки зрения, то получается, что имеем на входе картинку буквы, например, 28x28x3 (RGB), после первого слоя линейного и слоя активации имеем "картинку", например 26x26x32 — тут 27 -- размер картинки, так как нам для применения ядра пришлось отступить от края на 1 пиксель, в 32 -- число каналов. Смысл этих каналов, примерно соответствует признакам признакового классификатора.
Теперь можно поставить слой пуллинга, который отобразит наш 26х26х32 на 13х13х32, так как из каждого квадратика 2х2 оставит только самое большое значение.
Потом ещё раз можно поставить линейный слой, который уже 64 признака вычислит, например, пуллинг и активацию. И т. д.
В конце можно поставить полносвязанный слой который отображает на 1х1х10 (10 -- число разных цифр, а величина канала -- степень похожести входной картинки на цифру)
Вот мы и получили совсем простую сеть для классификации. Но, что бы её учить, нужно ещё придумать функцию потерь -- функцию, которая покажет, насколько тот или иной ответ сети близок к правильному (к ground truth). Для задач классификации хорошо подходит кросс-энтропия.
Правда перед кросс-энтропией, стоит поставить ещё один слой активации soft-max, называется, который приводит все значения к диапазону 0..1, так что самый большой становится близок к 1, а остальные ближе к 0 смещаются. Иногда эту формулу вставляют в саму функцию вычисления потерь, иногда как отдельный слой ставят.
Ну и нужно где-то добыть dataset с картинками цифр, которые мы будем классифицировать. Ну и нам надо знать какая картинка какую цифру изображает.
Можно разделить dataset на три части -- обучающую тестовую и контрольную. Обучающую и тестовую использовать в процессе
Теперь, когда у нас есть сеть (архитектура), функция потерь и dataset, надо понять, как это учить.
Общая идея такая, что обучающий dataset мы будем случайно делить на пакеты, например по 128 изображений (можно больше), и градиентным спуском пытаться подобрать коэффициенты в сети так, что бы уменьшить суммарные потери на всё пакете, потом на другом пакете, на третьем, и так до исчерпания dataset, или ещё до какого-то момента. Обычно эта процедура называется "эпоха", и после каждой эпохи хорошо бы прямым прогоном сети проверить результаты на тестовой выборке. Так как иногда стохастический градиентный спуск заходит в локальные глубокие экстремумы и для обучающей выборки потери уменьшаются, а для тестовой -- нет. Тогда стоит что-то поменять, например алгоритм обучения (например на метод сопряжённых градиентов или ещё как)
После того, как мы достигнем какого-то результата обучения, например потери перестанут уменьшаться. Или, в задачах классификации, ещё интересно на точность смотреть, например, тоже перестанет увеличиваться, можно ещё по контрольной выборке проверить, что сеть реально обучилась более или менее любым картинкам, для которых наш dataset представителен, а не только обучающей и тестовой выборке.
2) Вот там выше я очень кратко описал то, что нужно для одной классической задачи для нейросетей.
Смотри, этот пирог очень большой и состоит и многих "коржей"
2.1) Слои, архитектуры сетей
2.2) функции потерь (loss)
2.3) dataset — сбор, подготовка, подготовка синтетических datasets, что бы обучить на них, а потом доучить на реальных данных
2.4) Алгоритмы обучения
2.5) Аугментация (не знаю, как по-русски augmentation, это когда по реальным данным делают синтетические, например, если мы распознаём буквы в RGB, можно перевести картинку в цветовое пространство HSV и там "повращать" H и "пошевелить" S)
2.6) Манипуляции с самим сетями. Ну там обучить какие-то слои на одном dataset с какой-то функцией потерь, а потом другие слои на другом и с другой.
Или что-то вроде того
На самом деле писать алгоритм обучения или функцию потерь не особо интересно, писать свои слои интереснее, но это требует предварительного въезда в тему и т. д.
Собирать datasets тоже интересная и сложная задача, но с нуля тоже трудно сделать
Поэтому я тебе советую прочитать "укус питона", заботать numpy, поставить анаконду или юпитер(это если ты на винде, но на линуксе с этим всем проще)
Поставить keras поверх tensorflow и взять оттуда в качестве dataset MNIST (рукописные цифры) а в качестве примера AlexNet и посмотреть как оно работает, учится и всё такое, посмотреть/поотлаживать реализации слоёв и т. д.
А потом уже пробовать приписывать туда что-то своё и смотреть как оно влияет.
После этого можно взять какую-то реальную задачу и попробовать сделать тоже самое (фишкой MNIST является то, что на нём обычно учится всё жизнеспособное, в отличии от реальных задач и datasets)
Потом можешь попробовать понять, как работает tf (это символные вычисления. Когда ты собираешь сеть, tf запоминает граф вычислений, а потом обучалка сети по этому графу решает задачу обратного распространения градиентов)
Потом научиться писать свои лоссы и оптимизационные алгоритмы, потом свои слои и т. д...
Но, если пайтон тебе прямо вот не нравится и всё, хотя по нему много инфы как раз по задаче прототипирования нейросетей, и есть удобные фремворки, можно взять С++ и фремворки для С++ tf, например, есть и под С.
Про Дельфи не знаю, но мне кажется, что на дельфи тут стоит забить. Главное понять как в пайтоне работает numpy
Остальное и так поймёшь, по ходу пьесы
Да, для кераса пайтон (их есть два сильно разных, 2-й и 3-й) нужен 3-й, 3.5 что ли сейчас просят, но лучше в требованиях посмотри.
Ну и в целом удачи!