Джеймс Лой, Технологический университет штата Джорджия. Руководство для новичков, после которого вы сможете создать собственную нейронную сеть на Python.
Мотивация: ориентируясь на личный опыт в изучении глубокого обучения, я решил создать нейронную сеть с нуля без сложной учебной библиотеки, такой как, например, . Я считаю, что для начинающего Data Scientist-а важно понимание внутренней структуры .
Эта статья содержит то, что я усвоил, и, надеюсь, она будет полезна и для вас! Другие полезные статьи по теме:
Большинство статей по нейронным сетям при их описании проводят параллели с мозгом. Мне проще описать нейронные сети как математическую функцию, которая отображает заданный вход в желаемый результат, не вникая в подробности.
Нейронные сети состоят из следующих компонентов:
На приведенной ниже диаграмме показана архитектура двухслойной нейронной сети (обратите внимание, что входной уровень обычно исключается при подсчете количества слоев в нейронной сети).
Создание класса Neural Network на Python выглядит просто:
Выход ŷ простой двухслойной нейронной сети:
В приведенном выше уравнении, веса W и смещения b являются единственными переменными, которые влияют на выход ŷ.
Естественно, правильные значения для весов и смещений определяют точность предсказаний. Процесс тонкой настройки весов и смещений из входных данных известен как .
Каждая итерация обучающего процесса состоит из следующих шагов
Последовательный график ниже иллюстрирует процесс:
Как мы видели на графике выше, прямое распространение - это просто несложное вычисление, а для базовой 2-слойной нейронной сети вывод нейронной сети дается формулой:
Давайте добавим функцию прямого распространения в наш код на Python-е, чтобы сделать это. Заметим, что для простоты, мы предположили, что смещения равны 0.
Однако нужен способ оценить «добротность» наших прогнозов, то есть насколько далеки наши прогнозы). Функция потери как раз позволяет нам сделать это.
Есть много доступных функций потерь, и характер нашей проблемы должен диктовать нам выбор функции потери. В этой работе мы будем использовать сумму квадратов ошибок в качестве функции потери.
Сумма квадратов ошибок - это среднее значение разницы между каждым прогнозируемым и фактическим значением.
Цель обучения - найти набор весов и смещений, который минимизирует функцию потери.
Теперь, когда мы измерили ошибку нашего прогноза (потери), нам нужно найти способ распространения ошибки обратно и обновить наши веса и смещения.
Чтобы узнать подходящую сумму для корректировки весов и смещений, нам нужно знать производную функции потери по отношению к весам и смещениям.
Напомним из анализа, что производная функции - это тангенс угла наклона функции.
Если у нас есть производная, то мы можем просто обновить веса и смещения, увеличив/уменьшив их (см. диаграмму выше). Это называется .
Однако мы не можем непосредственно вычислить производную функции потерь по отношению к весам и смещениям, так как уравнение функции потерь не содержит весов и смещений. Поэтому нам нужно правило цепи для помощи в вычислении.
Фух! Это было громоздко, но позволило получить то, что нам нужно - производную (наклон) функции потерь по отношению к весам. Теперь мы можем соответствующим образом регулировать веса.
Добавим функцию backpropagation (обратного распространения) в наш код на Python-е:
Теперь, когда у нас есть наш полный код на Python-е для выполнения прямого и обратного распространения, давайте рассмотрим нашу нейронную сеть на примере и посмотрим, как это работает.
Наша нейронная сеть должна изучить идеальный набор весов для представления этой функции.
Давайте тренируем нейронную сеть на 1500 итераций и посмотрим, что произойдет. Рассматривая график потерь на итерации ниже, мы можем ясно видеть, что потеря монотонно уменьшается до минимума. Это согласуется с алгоритмом спуска градиента, о котором мы говорили ранее.
Посмотрим на окончательное предсказание (вывод) из нейронной сети после 1500 итераций.
Мы сделали это! Наш алгоритм прямого и обратного распространения показал успешную работу нейронной сети, а предсказания сходятся на истинных значениях.
Заметим, что есть небольшая разница между предсказаниями и фактическими значениями. Это желательно, поскольку предотвращает переобучение и позволяет нейронной сети лучше обобщать невидимые данные.
Я многому научился в процессе написания с нуля своей собственной нейронной сети. Хотя библиотеки глубинного обучения, такие как TensorFlow и Keras, допускают создание глубоких сетей без полного понимания внутренней работы нейронной сети, я нахожу, что начинающим Data Scientist-ам полезно получить более глубокое их понимание.
Я инвестировал много своего личного времени в данную работу, и я надеюсь, что она будет полезной для вас!
Искусственные нейронные сети (ИНС) - математические модели, а также их программные или аппаратные реализации, построенные по принципу организации и функционирования биологических нейронных сетей - сетей нервных клеток живого организма.ИНС представляют собой систему соединённых и взаимодействующих между собой простых процессоров (искусственных нейронов).
Нейронные сети не программируются в привычном смысле этого слова, они обучаются. Возможность обучения - одно из главных преимуществ нейронных сетей перед традиционными алгоритмами. wikipedia
Нейронные сети были вдохновлены нашим собственным мозгом. Модель стандартного нейрона изобретена более пятидесяти лет назад и состоит из трех основных частей:
Работу нейрона можно описать примерно так: дендриды собирают сигналы, полученные от других нейронов, затем сомы выполняют суммирование и вычисление сигналов и данных, и наконец на основе результата обработки могут "сказать" аксонам передать сигнал дальше. Передача далее зависит от ряда факторов, но мы можем смоделировать это поведение как передаточную функцию, которая принимает входные данные, обрабатывает их и готовит выходные данные, если выполняются свойства передаточной функции.
Биологический нейрон - сложная система, математическая модель которого до сих пор полностью не построена. Введено множество моделей, различающихся вычислительной сложностью и сходством с реальным нейроном. Одна из важнейших - формальный нейрон (ФН). Несмотря на простоту ФН, сети, построенные из таких нейронов, могут сформировать произвольную много мерную функцию на выходе (источник: Заенцев И. В. Нейронные сети: основные модели).
Нейрон состоит из взвешенного сумматора и нелинейного элемента. Функционирование нейрона определяется формулами:
Нейрон имеет несколько входных сигналов x и один выходной сигнал OUT . Параметрами нейрона, определяющими его работу, являются: вектор весов w , пороговый уровень θ и вид функции активации F .
Нейронные сети привлекают к себе внимание за счет следующих возможностей:
К основным свойствам нейронных сетей можно отнести:
Способность обучаться . Нейронные сети не программируются, а обучаются на примерах. После предъявления входных сигналов (возможно, вместе с требуемыми выходами) сеть настраивают свои параметры таким образом, чтобы обеспечивать требуемую реакцию.
Обобщение . Отклик сети после обучения может быть до некоторой степени нечувствителен к небольшим изменениям входных сигналов. Эта внутренне присущая способность "видеть"" образ сквозь шум и искажения очень важна для распознавания образов. Важно отметить, что искусственная нейронная сеть делает обобщения автоматически благодаря своей структуре, а не с помощью использования "человеческого интеллекта"" в форме специально написанных компьютерных программ.
Параллелизм . Информация в сети обрабатывается параллельно, что позволяет достаточно выполнять сложную обработку данных с помощью большого числа простых устройств.
Высокая надежность . Сеть может правильно функционировать даже при выходе из строя части нейронов, за счет того, что вычисления производятся локально и параллельно.
Алгоритм решения задач с помощью многослойного персептрона (источник: Заенцев И. В. Нейронные сети: основные модели)
Чтобы построить многослойный персептрон, необходимо выбрать его параметры. Чаще всего выбор значений весов и порогов требует обучения, т.е. пошаговых изменений весовых коэффициентов и пороговых уровней.
Общий алгоритм решения:
Решаемые проблемы
Проблемы решаемые с помощью нейронных сетей ().
Классификация образов . Задача состоит в указании принадлежности входного образа (например, речевого сигнала или рукописного символа), представленного вектором признаков, одному или нескольким предварительно определенным классам. К известным приложениям относятся распознавание букв, распознавание речи, классификация сигнала электрокардиограммы, классификация клеток крови.
Кластеризация/категоризация . При решении задачи кластеризации, которая известна также как классификация образов "без учителя", отсутствует обучающая выборка с метками классов. Алгоритм кластеризации основан на подобии образов и размещает близкие образы в один кластер. Известны случаи применения кластеризации для извлечения знаний, сжатия данных и исследования свойств данных.
Аппроксимация функций . Предположим, что имеется обучающая выборка ((x1,y1), (x2,y2)..., (xn,yn)) (пары данных вход-выход), которая генерируется неизвестной функцией (x), искаженной шумом. Задача аппроксимации состоит в нахождении оценки неизвестной функции (x). Аппроксимация функций необходима при решении многочисленных инженерных и научных задач моделирования.
Предсказание/прогноз . Пусть заданы n дискретных отсчетов {y(t1), y(t2)..., y(tn)} в последовательные моменты времени t1, t2,..., tn . Задача состоит в предсказании значения y(tn+1) в некоторый будущий момент времени tn+1. Предсказание/прогноз имеют значительное влияние на принятие решений в бизнесе, науке и технике. Предсказание цен на фондовой бирже и прогноз погоды являются типичными приложениями техники предсказания/прогноза.
Оптимизация . Многочисленные проблемы в математике, статистике, технике, науке, медицине и экономике могут рассматриваться как проблемы оптимизации. Задачей алгоритма оптимизации является нахождение такого решения, которое удовлетворяет системе ограничений и максимизирует или минимизирует целевую функцию. Задача коммивояжера, относящаяся к классу NP-полных, является классическим примером задачи оптимизации.
Память, адресуемая по содержанию . В модели вычислений фон Неймана обращение к памяти доступно только посредством адреса, который не зависит от содержания памяти. Более того, если допущена ошибка в вычислении адреса, то может быть найдена совершенно иная информация. Ассоциативная память, или память, адресуемая по содержанию, доступна по указанию заданного содержания. Содержимое памяти может быть вызвано даже по частичному входу или искаженному содержанию. Ассоциативная память чрезвычайно желательна при создании мультимедийных информационных баз данных.
Управление . Рассмотрим динамическую систему, заданную совокупностью {u(t), y(t)}, где u(t) является входным управляющим воздействием, а y(t) - выходом системы в момент времени t. В системах управления с эталонной моделью целью управления является расчет такого входного воздействия u(t), при котором система следует по желаемой траектории, диктуемой эталонной моделью. Примером является оптимальное управление двигателем.
Виды архитектур
Архитектура нейронной сети - способ организации и связи отдельных элементов нейросети(нейронов). Архитектурные отличия самих нейронов заключаются главным образом в использовании различных активационных (возбуждающих) функций. По архитектуре связей нейронные сети можно разделить на два класса: сети прямого распространения и рекуррентные сети.
Классификация искусственных нейронных сетей по их архитектуре приведена на рисунке ниже.
Похожая классификация, но немного расширенная
Сеть прямого распространения сигнала (сеть прямой передачи) - нейронная сеть без обратных связей (петель). В такой сети обработка информации носит однонаправленный характер: сигнал передается от слоя к слою в направлении от входного слоя нейросети к выходному. Выходной сигнал (ответ сети) гарантирован через заранее известное число шагов (равное числу слоев). Сети прямого распространения просты в реализации, хорошо изучены. Для решения сложных задач требуют большого числа нейронов.
Сравнительная таблица многослойного персепторна и RBF-сети
Многослойный персептрон | RBF-сети |
---|---|
Граница решения представляет собой пересечение гиперплоскостей | Граница решения - это пересечение гиперсфер, что задает границу более сложной формы |
Сложная топология связей нейронов и слоев | Простая 2-слойная нейронная сеть |
Сложный и медленно сходящийся алгоритм обучения | Быстрая процедура обучения: решение системы уравнений + кластеризация |
Работа на небольшой обучающей выборке | Требуется значительное число обучающих данных для приемлемого результат |
Универсальность применения: кластеризация, аппроксимация, управление и проч | Как правило, только аппроксимация функций и кластеризация |
Значение производной легко выражается через саму функцию. Быстрый расчет производной ускоряет обучение.
Гауссова кривая
Применяется в случаях, когда реакция нейрона должна быть максимальной для некоторого определенного значения NET.
Модули python для нейронных сетей
Простой пример
В качестве примера приведу простую нейроную сеть (простой персептрон ), которая после обучения сможет распознавать летающие объекты, не все, а только чайку :), все остальные входные образы будут распознаваться как НЛО .
# encoding=utf8 import random class NN: def __init__(self, threshold, size): """ Установим начальные параметры. """ self.threshold = threshold self.size = size self.init_weight() def init_weight(self): """ Инициализируем матрицу весов случайными данными. """ self.weights = [ for j in xrange(self.size)] def check(self, sample): """ Считаем выходной сигнал для образа sample. Если vsum > self.threshold то можно предположить, что в sample есть образ чайки. """ vsum = 0 for i in xrange(self.size): for j in xrange(self.size): vsum += self.weights[i][j] * sample[i][j] if vsum > self.threshold: return True else: return False def teach(self, sample): """ Обучение нейронной сети. """ for i in xrange(self.size): for j in xrange(self.size): self.weights[i][j] += sample[i][j] nn = NN(20, 6) # Обучаем нейронную сеть. tsample1 = [ , , , , , , ] nn.teach(tsample1) tsample2 = [ , , , , , , ] nn.teach(tsample2) tsample3 = [ , , , , , , ] nn.teach(tsample3) tsample4 = [ , , , , , , ] nn.teach(tsample4) # Проверим что может нейронная сеть. # Передадим образ чайки, который примерно похож на тот, про который знает персептрон. wsample1 = [ , , , , , , ] print u"чайка" if nn.check(wsample1) else u"НЛО" # Передадим неизвестный образ. wsample2 = [ , , , , , , ] print u"чайка" if nn.check(wsample2) else u"НЛО" # Передадим образ чайки, который примерно похож на тот, про который знает персептрон. wsample3 = [ , , , , , , ] print u"чайка" if nn.check(wsample3) else u"НЛО"
В этой части предсавлены ссылки на статьи из рунета о том, что такое нейросети. Многие из статей написаны оригинальным живым языком и очень доходчивы. Однако здесь по большей части рассматриваются только самые азы, самые простые конструкции. Здесь же можно найти сылки на литературу по нейросетям. Учебники и книги, как им и положено, написаны академическим или приближающимся к нему языком и содержат малопонятные абстрактные примеры построения нейросетей, их обучения и пр. Следует иметь ввиду, что терминология в разных статьях "плавает", что видно по комментариям к статьям. Из-за этого на первых порах может возникнуть "каша в голове".
О том, какие библиотеки существуют для Питона можно кратенько прочитать . Отсюда же я буду брать тестовые примеры для того, чтобв убедиться, что требуемый пакет установлен корректно.
Центральным объектом TensorFlow является граф потока данных, представляющий вычисления. Вершины графа представляют операции (operation), а ребра – тензоры (tensor) (многомерные массивы, являющиеся основой TensorFlow). Граф потока данных в целом является полным описанием вычислений, которые реализуются в рамках сессии (session) и выполняются на устройствах (device) (CPU или GPU). Как и многие другие современные системы для научных вычислений и машинного обучения, TensorFlow имеет хорошо документированный API для Python, где тензоры представлены в виде знакомых массивов ndarray библиотеки NumPy. TensorFlow выполняет вычисления с помощью высоко оптимизированного C++, а также поддерживает нативный API для C и C++.
Установка tensorflow хорошо описана в статье по первой ссылке. Однако, сейчас уже вышла версия Python 3.6.1. Её использовать не получиться. По крайней сере на данный момент (03.06.2017). Требуется версия 3.5.3, которую можно скачать . Ниже приведу последовательность, которая сработала у меня (немного не как к статье с Хабра). Непонятно почему, но Python 64-бит сделан под процессор AMD соответственно и всё остальное под него. После установки Phyton не забываем установить полный доспуп для пользователей если Python устанавливался для всех.
pip install --upgrade pip
pip install -U pip setuptools
pip3 install --upgrade tensorflow
pip3 install --upgrade tensorflow-gpu
pip install matplotlib /*Загружает 8,9 Мб и ещё пару небольших файлов */
pip install jupyter
"Голый Python" может показаться малоинтересным. Поэтому далее инструкция по установке в среду Anaconda . Это альтернативная сборка. Python уже интегрирован в неё.
На сайте опять фигурирует новая версия под Python 3.6, которую пока новый Google-продукт не поддерживает. Поэтому я сразу взял из архива более раннюю версию, а именно Anaconda3-4.2.0 - она подходит. Не забываем установить флажек регистрации Python 3.5. Конкретно Перед установкой Anaconda термилал лучше закрыть иначе он так и будет работать с устаревшим PATH. Также не забываем изменять права доступа пользователей иначе ничего не получиться.
conda create -n tensorflow
activate tensorflow
pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/cpu/tensorflow-1.1.0-cp35-cp35m-win_amd64.whl /*Загружается из Сети 19.4 Мб, потом 7,7 Мб и ещё 0,317 Мб*/
pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/gpu/tensorflow_gpu-1.1.0-cp35-cp35m-win_amd64.whl /*Загружается 48,6 Мб */
Скрин экрана установки: в Anaconda всё проходит удачно.
Аналогично для второго файла.
Ну и в заключение: для того, чтобы всё это заработало, нужно установить пакет CUDA Toolkits от NVIDEA (в случае использования GPU). Текущая поддерживаемая версия 8.0. Ещё нужно будет скачать и распаковать в папку с CUDA библиотеку cuDNN v5.1, но не более новой версии! После всех этих манипуляций TensorFlow заработает.
Пакет Theano входит PyPI самого Python. Сам по себе он маленький - 3,1 Мб, но тянет за собой зависимости ещё на 15 Мб - scipy. Для установки последнего нужен ещё модуль lapack... В общем, установка пакета theano под Windows будет означать "танцы с бубном". Ниже я постараюсь показать последовательность действий для того, чтобы пакет все-таки заработал.
При использовании Anaconda "танцы с бубном" при установке не актуальны. Достаточно команды:
conda install theano
и процесс проходит автоматически. Кстати, загружаються и пакеты GCC.
Под Python 3.5.3 устанавливается и запускается только более ранняя версия 0.17.1 взять которую можно . Есть нормальный инсталятор. Тем не менее прямо так под Windows он работать не будет - нужна библиотека scipy.
Для того, чтобы обозначенные выше два пакета заработали (речь о "голом" Phyton), нужно сделать некоторые предварительные действия.
Для запуска Scikit-Learn и Theano, как уже стало понятно из вышеизложнного, потребуется "потанцевать с бубном". Первое, что нам выдает Yandex - это кладезь мудрости, правда англоязычный, ресурс stackoverflow.com , где мы находим ссылочку на отличный архив почти всех существующих для Python, пакетов, скомпиллированных под Windows - lfd.uci.edu
Здесь есть готовые к установке сборки интересующих в данный момент пакетов, скомпиллированные для разных версий Python. В нашем случае требуются версии файлов, сореджание в своем имени строку "-cp35-win_amd64" потому что именно такой пакет Python был использован для установки. На stakowerflow, если поискать, то можно найти и "инструкции " по установке конкретно наших пакетов.
pip install --upgrade --ignore-installed http://www.lfd.uci.edu/~gohlke/pythonlibs/vu0h7y4r/numpy-1.12.1+mkl-cp35-cp35m-win_amd64.whl
pip install --upgrade --ignore-insalled http://www.lfd.uci.edu/~gohlke/pythonlibs/vu0h7y4r/scipy-0.19.0-cp35-cp35m-win_amd64.whl
pip --upgrade --ignore-installed pandas
pip install --upgrade --ignore-installed matplotlib
Два последних пакета возникли в моей цепочке из-за чужих "танцев с бубном". В зависимостях устанавливаемых пакетов их я не заметил, но видимо какие-то их комноненты нужны для нормального прохождения процесса установки.
Эти две связанные низкоуровневые библиотеки, написанные на Фортране, нужны для установки пакета Theano. Scikit-Learn может работать и на тех, которые "в скрытом виде" уже установились в других пакетах (см. выше). Собственно если Theano установлено версии 0.17 из exe-файла, то тоже заработает. В Anaconda по крайней мере. Тем не менее эти библиотеки тоже можно найти в Сети. Например . Более свежие сборки . Для работы готового пакета предыдущей версии это подходит. Для сборки нового пакета потребуются новые версии.
Также следует отметить, что в совершенно свежей сборке Anaconda пакет Theano устанавливается намного проще - одной командой, но мне, честно говоря, на данном этапе (нулевом) освоения нейросетей больше приглянулся TensorFlow, а он пока с новыми версиями Python не дружит.
Слишком сжато? Давайте разобьём его на более простые части.
Предположим, нам нужно предсказать, как будет выглядеть колонка «выход» на основе входных данных. Эту задачу можно было бы решить, подсчитав статистическое соответствие между ними. И мы бы увидели, что с выходными данными на 100% коррелирует левый столбец.
Обратное распространение, в самом простом случае, рассчитывает подобную статистику для создания модели. Давайте попробуем.
Выходные данные после тренировки: [[ 0.00966449] [ 0.00786506] [ 0.99358898] [ 0.99211957]]
Переменные и их описания.
"*" - поэлементное умножение – два вектора одного размера умножают соответствующие значения, и на выходе получается вектор такого же размера
"-" – поэлементное вычитание векторов
x.dot(y) – если x и y – это вектора, то на выходе получится скалярное произведение. Если это матрицы, то получится перемножение матриц. Если матрица только одна из них – это перемножение вектора и матрицы.
Импортирует numpy, библиотеку линейной алгебры. Единственная наша зависимость.
Def nonlin(x,deriv=False):
Наша нелинейность. Конкретно эта функция создаёт «сигмоиду». Она ставит в соответствие любое число значению от 0 до 1 и преобразовывает числа в вероятности, а также имеет несколько других полезных для тренировки нейросетей свойств.
If(deriv==True):
Эта функция также умеет выдавать производную сигмоиды (deriv=True). Это одно из её полезных свойств. Если выход функции – это переменная out, тогда производная будет out * (1-out). Эффективно.
X = np.array([ , …
Инициализация массива входных данных в виде numpy-матрицы. Каждая строка – тренировочный пример. Столбцы – это входные узлы. У нас получается 3 входных узла в сети и 4 тренировочных примера.
Y = np.array([]).T
Инициализирует выходные данные. ".T" – функция переноса. После переноса у матрицы y есть 4 строки с одним столбцом. Как и в случае входных данных, каждая строка – это тренировочный пример, и каждый столбец (в нашем случае один) – выходной узел. У сети, получается, 3 входа и 1 выход.
Np.random.seed(1)
Благодаря этому случайное распределение будет каждый раз одним и тем же. Это позволит нам проще отслеживать работу сети после внесения изменений в код.
Syn0 = 2*np.random.random((3,1)) – 1
Матрица весов сети. syn0 означает «synapse zero». Так как у нас всего два слоя, вход и выход, нам нужна одна матрица весов, которая их свяжет. Её размерность (3, 1), поскольку у нас есть 3 входа и 1 выход. Иными словами, l0 имеет размер 3, а l1 – 1. Поскольку мы связываем все узлы в l0 со всеми узлами l1, нам требуется матрица размерности (3, 1).
Заметьте, что она инициализируется случайным образом, и среднее значение равно нулю. За этим стоит достаточно сложная теория. Пока просто примем это как рекомендацию. Также заметим, что наша нейросеть – это и есть эта самая матрица. У нас есть «слои» l0 и l1, но они представляют собой временные значения, основанные на наборе данных. Мы их не храним. Всё обучение хранится в syn0.
For iter in xrange(10000):
Тут начинается основной код тренировки сети. Цикл с кодом повторяется многократно и оптимизирует сеть для набора данных.
Первый слой, l0, это просто данные. В X содержится 4 тренировочных примера. Мы обработаем их все и сразу – это называется групповой тренировкой . Итого мы имеем 4 разных строки l0, но их можно представить себе как один тренировочный пример – на этом этапе это не имеет значения (можно было загрузить их 1000 или 10000 без всяких изменений в коде).
L1 = nonlin(np.dot(l0,syn0))
Это шаг предсказания. Мы позволяем сети попробовать предсказать вывод на основе ввода. Затем мы посмотрим, как это у неё получается, чтобы можно было подправить её в сторону улучшения.
В строке содержится два шага. Первый делает матричное перемножение l0 и syn0. Второй передаёт вывод через сигмоиду. Размерности у них следующие:
(4 x 3) dot (3 x 1) = (4 x 1)
Матричные умножения требуют, чтобы в середине уравнения размерности совпадали. Итоговая матрица имеет количество строк, как у первой, а столбцов – как у второй.
Мы загрузили 4 тренировочных примера, и получили 4 догадки (матрица 4х1). Каждый вывод соответствует догадке сети для данного ввода.
L1_error = y - l1
Поскольку в l1 содержатся догадки, мы можем сравнить их разницу с реальностью, вычитая её l1 из правильного ответа y. l1_error – вектор из положительных и отрицательных чисел, характеризующий «промах» сети.
А вот и секретный ингредиент. Эту строку нужно разбирать по частям.
Первая часть: производная
Nonlin(l1,True)
L1 представляет три этих точки, а код выдаёт наклон линий, показанных ниже. Заметьте, что при больших значениях вроде x=2.0 (зелёная точка) и очень малые, вроде x=-1.0 (фиолетовая) линии имеют небольшой уклон. Самый большой угол у точки х=0 (голубая). Это имеет большое значение. Также отметьте, что все производные лежат в пределах от 0 до 1.
Полное выражение: производная, взвешенная по ошибкам
L1_delta = l1_error * nonlin(l1,True)
Математически существуют более точные способы, но в нашем случае подходит и этот. l1_error – это матрица (4,1). nonlin(l1,True) возвращает матрицу (4,1). Здесь мы поэлементно их перемножаем, и на выходе тоже получаем матрицу (4,1), l1_delta.
Умножая производные на ошибки, мы уменьшаем ошибки предсказаний, сделанных с высокой уверенностью. Если наклон линии был небольшим, то в сети содержится либо очень большое, либо очень малое значение. Если догадка в сети близка к нулю (х=0, у=0,5), то она не особенно уверенная. Мы обновляем эти неуверенные предсказания и оставляем в покое предсказания с высокой уверенностью, умножая их на величины, близкие к нулю.
Syn0 += np.dot(l0.T,l1_delta)
Мы готовы к обновлению сети. Рассмотрим один тренировочный пример. В нём мы будем обновлять веса. Обновим крайний левый вес (9.5)
Weight_update = input_value * l1_delta
Для крайнего левого веса это будет 1.0 * l1_delta. Предположительно, это лишь незначительно увеличит 9.5. Почему? Поскольку предсказание было уже достаточно уверенным, и предсказания были практически правильными. Небольшая ошибка и небольшой наклон линии означает очень небольшое обновление.
Но поскольку мы делаем групповую тренировку, указанный выше шаг мы повторяем для всех четырёх тренировочных примеров. Так что это выглядит очень похоже на изображение вверху. Так что же делает наша строчка? Она подсчитывает обновления весов для каждого веса, для каждого тренировочного примера, суммирует их и обновляет все веса – и всё одной строкой.
Понаблюдав за обновлением сети, вернёмся к нашим тренировочным данным. Когда и вход, и выход равны 1, мы увеличиваем вес между ними. Когда вход 1, а выход – 0, мы уменьшаем вес.
Вход Выход 0 0 1 0 1 1 1 1 1 0 1 1 0 1 1 0
Таким образом, в наших четырёх тренировочных примерах ниже, вес первого входа по отношению к выходу будет постоянно увеличиваться или оставаться постоянным, а два других веса будут увеличиваться и уменьшаться в зависимости от примеров. Этот эффект и способствует обучению сети на основе корреляций входных и выходных данных.
Попробуем предсказать выходные данные на основе трёх входных столбцов данных. Ни один из входных столбцов не коррелирует на 100% с выходным. Третий столбец вообще ни с чем не связан, поскольку в нём всю дорогу содержатся единицы. Однако и тут можно увидеть схему – если в одном из двух первых столбцов (но не в обоих сразу) содержится 1, то результат также будет равен 1.
Это нелинейная схема, поскольку прямого соответствия столбцов один к одному не существует. Соответствие строится на комбинации входных данных, столбцов 1 и 2.
Интересно, что распознавание образов является очень похожей задачей. Если у вас есть 100 картинок одинакового размера, на которых изображены велосипеды и курительные трубки, присутствие на них определённых пикселей в определённых местах не коррелирует напрямую с наличием на изображении велосипеда или трубки. Статистически их цвет может казаться случайным. Но некоторые комбинации пикселей не случайны – те, что формируют изображение велосипеда (или трубки).
Вход (l0) Скрытые веса (l1) Выход (l2) 0 0 1 0.1 0.2 0.5 0.2 0 0 1 1 0.2 0.6 0.7 0.1 1 1 0 1 0.3 0.2 0.3 0.9 1 1 1 1 0.2 0.1 0.3 0.8 0
Случайным образом назначив веса, мы получим скрытые значения для слоя №1. Интересно, что у второго столбца скрытых весов уже есть небольшая корреляция с выходом. Не идеальная, но есть. И это тоже является важной частью процесса тренировки сети. Тренировка будет только усиливать эту корреляцию. Она будет обновлять syn1, чтобы назначить её соответствие выходным данным, и syn0, чтобы лучше получать данные со входа.
Error:0.496410031903 Error:0.00858452565325 Error:0.00578945986251 Error:0.00462917677677 Error:0.00395876528027 Error:0.00351012256786
Код должен быть достаточно понятным – это просто предыдущая реализация сети, сложенная в два слоя один над другим. Выход первого слоя l1 – это вход второго слоя. Что-то новое есть лишь в следующей строке.
L1_error = l2_delta.dot(syn1.T)
Использует ошибки, взвешенные по уверенности предсказаний из l2, чтобы подсчитать ошибку для l1. Получаем, можно сказать, ошибку, взвешенную по вкладам – мы подсчитываем, какой вклад в ошибки в l2 вносят значения в узлах l1. Этот шаг и называется обратным распространением ошибок. Затем мы обновляем syn0, используя тот же алгоритм, что и в варианте с нейросетью из двух слоёв.
В этот раз я решил изучить нейронные сети. Базовые навыки в этом вопросе я смог получить за лето и осень 2015 года. Под базовыми навыками я имею в виду, что могу сам создать простую нейронную сеть с нуля. Примеры можете найти в моих репозиториях на GitHub. В этой статье я дам несколько разъяснений и поделюсь ресурсами, которые могут пригодиться вам для изучения.
Так что же такое «нейронная сеть»? Давайте подождём с этим и сперва разберёмся с одним нейроном.
Нейрон похож на функцию: он принимает на вход несколько значений и возвращает одно.
Круг ниже обозначает искусственный нейрон. Он получает 5 и возвращает 1. Ввод - это сумма трёх соединённых с нейроном синапсов (три стрелки слева).
В левой части картинки мы видим 2 входных значения (зелёного цвета) и смещение (выделено коричневым цветом).
Входные данные могут быть численными представлениями двух разных свойств. Например, при создании спам-фильтра они могли бы означать наличие более чем одного слова, написанного ЗАГЛАВНЫМИ БУКВАМИ, и наличие слова «виагра».
Входные значения умножаются на свои так называемые «веса», 7 и 3 (выделено синим).
Теперь мы складываем полученные значения со смещением и получаем число, в нашем случае 5 (выделено красным). Это - ввод нашего искусственного нейрона.
Потом нейрон производит какое-то вычисление и выдает выходное значение. Мы получили 1, т.к. округлённое значение сигмоиды в точке 5 равно 1 (более подробно об этой функции поговорим позже).
Если бы это был спам-фильтр, факт вывода 1 означал бы то, что текст был помечен нейроном как спам.
Иллюстрация нейронной сети с Википедии.
Если вы объедините эти нейроны, то получите прямо распространяющуюся нейронную сеть - процесс идёт от ввода к выводу, через нейроны, соединённые синапсами, как на картинке слева.
После того, как вы посмотрели уроки от Welch Labs, хорошей идеей было бы ознакомиться с четвертой неделей курса по машинному обучению от Coursera , посвящённой нейронным сетям - она поможет разобраться в принципах их работы. Курс сильно углубляется в математику и основан на Octave, а я предпочитаю Python. Из-за этого я пропустил упражнения и почерпнул все необходимые знания из видео.
Сигмоида просто-напросто отображает ваше значение (по горизонтальной оси) на отрезок от 0 до 1.
Первоочередной задачей для меня стало изучение сигмоиды , так как она фигурировала во многих аспектах нейронных сетей. Что-то о ней я уже знал из третьей недели вышеупомянутого курса , поэтому я пересмотрел видео оттуда.
Но на одних видео далеко не уедешь. Для полного понимания я решил закодить её самостоятельно. Поэтому я начал писать реализацию алгоритма логистической регрессии (который использует сигмоиду).
Это заняло целый день, и вряд ли результат получился удовлетворительным. Но это неважно, ведь я разобрался, как всё работает. Код можно увидеть .
Вам необязательно делать это самим, поскольку тут требуются специальные знания - главное, чтобы вы поняли, как устроена сигмоида.
Понять принцип работы нейронной сети от ввода до вывода не так уж и сложно. Гораздо сложнее понять, как нейронная сеть обучается на наборах данных. Использованный мной принцип называется