Пишем нейросеть на Python с нуля |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
МЕНЮ Искусственный интеллект Поиск Регистрация на сайте Помощь проекту ТЕМЫ Новости ИИ Искусственный интеллект Разработка ИИГолосовой помощник Городские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Нейронные сети начинающим Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Техническое зрение Чат-боты Авторизация |
2020-10-08 22:08 Статья публикуется в переводе, автор оригинального текста Victor Zhou. *** Пишем нейросеть на Python с нуля Термин "нейронные сети" сейчас можно услышать из каждого утюга, и многие верят, будто это что-то очень сложное. На самом деле нейронные сети совсем не такие сложные, как может показаться! Мы разберемся, как они работают, реализовав одну сеть с нуля на Python. Эта статья предназначена для полных новичков, не имеющих никакого опыта в машинном обучении. Поехали! 1. Составные элементы: нейроны Прежде всего нам придется обсудить нейроны, базовые элементы нейронной сети. Нейрон принимает несколько входов, выполняет над ними кое-какие математические операции, а потом выдает один выход. Вот как выглядит нейрон с двумя входами: Внутри нейрона происходят три операции. Сначала значения входов умножаются на веса: Затем взвешенные входы складываются, и к ним прибавляется значение порога b: Наконец, полученная сумма проходит через функцию активации: Функция активации преобразует неограниченные значения входов в выход, имеющий ясную и предсказуемую форму. Одна из часто используемых функций активации – сигмоида: Сигмоида выдает результаты в интервале (0, 1). Можно представить, что она «упаковывает» интервал от минус бесконечности до плюс бесконечности в (0, 1): большие отрицательные числа превращаются в числа, близкие к 0, а большие положительные – к 1. Простой пример Допустим, наш двухвходовой нейрон использует сигмоидную функцию активации и имеет следующие параметры: w=[0, 1] – это всего лишь запись w1=0, w2=1 в векторном виде. Теперь зададим нашему нейрону входные данные: x=[2, 3]. Мы используем скалярное произведение векторов, чтобы записать формулу в сжатом виде: Наш нейрон выдал 0.999 при входах x=[2, 3]. Вот и все! Процесс передачи значений входов дальше, чтобы получить выход, называется прямой связью (feed forward). Пишем код для нейрона Настало время написать свой нейрон! Мы используем NumPy, популярную и мощную расчетную библиотеку для Python, которая поможет нам с вычислениями: Узнаете эти числа? Это тот самый пример, который мы только что рассчитали! И мы получили тот же результат – 0.999. 2. Собираем нейронную сеть из нейронов Нейронная сеть – это всего лишь несколько нейронов, соединенных вместе. Вот как может выглядеть простая нейронная сеть: У этой сети два входа, скрытый слой с двумя нейронами (h1 и h2) и выходной слой с одним нейроном (o1). Обратите внимание, что входы для o1 – это выходы из h1 и h2. Именно это создает из нейронов сеть. Замечание Скрытый слой – это любой слой между входным (первым) слоем сети и выходным (последним). Скрытых слоев может быть много! Пример: прямая связь Давайте используем сеть, изображенную выше, и будем считать, что все нейроны имеют одинаковые веса w=[0, 1], одинаковые пороговые значения b=0, и одинаковую функцию активации – сигмоиду. Пусть h1, h2 и o1 обозначают выходные значения соответствующих нейронов. Что получится, если мы подадим на вход x=[2, 3]? Если подать на вход нашей нейронной сети x=[2, 3], на выходе получится 0.7216. Достаточно просто, не правда ли? Нейронная сеть может иметь любое количество слоев, и в этих слоях может быть любое количество нейронов. Основная идея остается той же: передавайте входные данные по нейронам сети, пока не получите выходные значения. Для простоты мы будем использовать сеть, показанную выше, до конца статьи. Пишем код нейронной сети Давайте реализуем прямую связь для нашей нейронной сети. Напомним, как она выглядит: Мы снова получили 0.7216! Похоже, наша сеть работает. 3. Обучаем нейронную сеть (часть 1) Допустим, у нас есть следующие измерения:
Давайте обучим нашу нейронную сеть предсказывать пол человека по его росту и весу. Мы будем представлять мужской пол как 0, женский – как 1, а также сдвинем данные, чтобы их было проще использовать:
Замечание Я выбрал величину сдвигов (135 и 66), чтобы числа выглядели попроще. Обычно сдвигают на среднее значение. Потери Прежде чем обучать нашу нейронную сеть, нам нужно как-то измерить, насколько "хорошо" она работает, чтобы она смогла работать "лучше". Это измерение и есть потери (loss). Мы используем для расчета потерь среднюю квадратичную ошибку (mean squared error, MSE): Давайте рассмотрим все используемые переменные:
(ytrue-ypred)2 называется квадратичной ошибкой. Наша функция потерь просто берет среднее значение всех квадратичных ошибок – поэтому она и называется средней квадратичной ошибкой. Чем лучшими будут наши предсказания, тем меньшими будут наши потери! Лучшие предсказания = меньшие потери. Обучение нейронной сети = минимизация ее потерь. Пример расчета потерь Предположим, что наша сеть всегда возвращает 0 – иными словами, она уверена, что все люди мужчины. Насколько велики будут наши потери?
Пишем функцию средней квадратичной ошибки Вот небольшой кусок кода, который рассчитает наши потери. Если вы не понимаете, почему он работает, прочитайте в руководстве NumPy про операции с массивами. Отлично. Идем дальше! 4. Обучаем нейронную сеть (часть 2) Теперь у нас есть четкая цель: минимизировать потери нейронной сети. Мы знаем, что можем изменять веса и пороги нейронов, чтобы изменить ее предсказания, но как нам делать это таким образом, чтобы минимизировать потери? Внимание: математика! Этот раздел использует частные производные по нескольким переменным. Если вы плохо знакомы с дифференциальным исчислением, можете просто пропускать математические формулы. Для простоты давайте представим, что в нашем наборе данных только одна Алиса.
Тогда средняя квадратичная ошибка будет квадратичной ошибкой только для Алисы: Другой метод – это рассматривать функцию потерь как функцию от весов и порогов. Давайте отметим все веса и пороги нашей нейронной сети: Теперь мы можем записать функцию потерь как функцию от нескольких переменных: Предположим, мы хотим отрегулировать w1. Как изменится значение потери L при изменении w1? На этот вопрос может ответить частная производная dL/dw1. Как мы ее рассчитаем? Не падайте духом! Здесь математика становится более сложной. Возьмите бумагу и ручку, чтобы не отставать – это поможет вам понять, что происходит. Прежде всего, давайте перепишем эту частную производную через dypred/dw1, воспользовавшись цепным правилом: Мы можем рассчитать dL/dypred, поскольку мы уже выяснили выше, что L=(1-ypred)2: Теперь давайте решим, что делать с dypred/dw1. Обозначая выходы нейронов, как прежде, h1, h2 и o1, получаем: Вспомните, что f() – это наша функция активации, сигмоида. Поскольку w1 влияет только на h1 (но не на h2), мы можем снова использовать цепное правило и записать: Мы можем сделать то же самое для dh1/dw1, снова применяя цепное правило: В этой формуле x1 – это вес, а x2 – рост. Вот уже второй раз мы встречаем f'(x) – производную сигмоидной функции! Давайте вычислим ее: Мы используем эту красивую форму для f'(x) позже. На этом мы закончили! Мы сумели разложить dL/dw1 на несколько частей, которые мы можем рассчитать: Такой метод расчета частных производных "от конца к началу" называется методом обратного распространения (backpropagation). Уффф. Здесь было очень много символов, так что не страшно, если вы пока не все понимаете. Давайте покажем, как это работает, на практическом примере! Пример. Считаем частную производную Мы по-прежнему считаем, что наш набор данных состоит из одной Алисы:
Давайте инициализируем все веса как 1, а все пороги как 0. Если мы выполним прямой проход по нейронной сети, то получим: Наша сеть выдает ypred=0.524, что находится примерно на полпути между Мужским полом (0) и Женским (1). Давайте рассчитаем dL/dw1: Напоминаем: Ранее мы получили формулу для производной сигмоиды f'(x)=f(x)(1-f(x)) Вот и все! Результат говорит нам, что при увеличении w1, функция ошибки чуть-чуть повышается. Обучение: стохастический градиентный спуск Теперь у нас есть все нужные инструменты для обучения нейронной сети! Мы используем алгоритм оптимизации под названием стохастический градиентный спуск (stochastic gradient descent), который определит, как мы будем изменять наши веса и пороги для минимизации потерь. Фактически, он заключается в следующей формуле обновления: Скорость обучения определяет, как быстро наша сеть учится. Все, что мы делаем – это вычитаем eta*dL/dw1 из w1:
Если мы сделаем то же самое для каждого веса и порога в сети, потери будут постепенно уменьшаться, и наша сеть будет выдавать более точные результаты. Процесс обучения сети будет выглядеть примерно так:
Пишем код всей нейронной сети Наконец настало время реализовать всю нейронную сеть.
Код в сети Вы можете запустить этот код и поиграть с ним самостоятельно. Он также доступен на GitHub. По мере обучения сети ее потери постепенно уменьшаются: Теперь мы можем использовать нашу сеть для предсказания пола: Что теперь? Вы сделали это! Давайте перечислим все, что мы с вами сделали:
Перед вами – множество путей, на которых вас ждет масса нового и интересного:
Спасибо за внимание! Источники Источник: proglib.io Комментарии: |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||