Глубокое обучение: быстрый старт для разработчиков

Инструменты, которые помогут вам разработать нейронную сеть в первый же день изучения.
20 октября 2017225057Андрей Никифоров3296215

Здравствуйте!

Глубокое обучение считается чем-то вроде rocket science: звучит круто, выглядит сложно и содержит всякую математику. На самом деле все не так сложно. Вот вам перевод руководства по глубокому обучению «Deep Learning for Developers: Tools You Can Use to Code Neural Networks on Day 1» от Эмиля Волнера.

Текущая волна интереса к глубокому обучению началась лет пять назад. Экспоненциальный рост мощности компьютеров и истории успеха стали тому причиной. Глубокое обучение — технология, управляющая автомобилями, побеждающая людей в играх Atari и диагностирующая рак.


Arif Wahid, Unsplash

Приступая к изучению глубокого обучения, я потратил пару недель на исследования. Я выбирал инструменты, сравнивал облачные сервисы и просматривал онлайн-курсы. Оглядываясь назад, я понимаю, что мог бы начать создавать нейронные сети в первый же день. Вот об этом я и расскажу в этой статье.

Вам не нужна особая подготовка. Достаточно базового понимания Python, командной строки и Jupyter notebook.

Глубокое обучение — часть машинного обучения. Доказано, что это эффективный метод поиска паттернов в сырых данных, таких как звук или изображение. Скажем, вы хотите классифицировать картинки кошек и собак. Без специального программирования, сперва выделим границы изображения. Затем построим паттерны из набора изображений, и наконец выделим носы, хвосты и лапы. Так нейронная сеть в итоге классифицирует собак и кошек.

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

Основная логика

Представьте машину, собранную из случайного набора шестеренок. Шестеренки собраны в слои и взаимодействуют друг с другом. Изначально машина не работает. Шестеренки настроены случайным образом, и их нужно перенастроить так, чтобы машина заработала.

Инженер осматривает все шестеренки и помечает те, которые вызывают ошибку. Он начинает с последнего слоя шестеренок, который испытывает влияние всех ошибок. Как только он понимает, в чем проблема на этом слое, он продвигается выше. Таким образом он может вычислить, как сильно каждая шестеренка влияет на общую ошибку. Мы называем этот процесс обратным распространением.

Теперь инженер настраивает каждую шестеренку, основываясь на том, какую ошибку она дает, и пытается запустить машину снова. Он опять вычисляет ошибку и настраивает каждую шестеренку. Его задача — продолжать этот процесс, пока машина не заработает так, как требуется.

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

Для уменьшения ошибок сеть вычисляет разницу между текущим результатом и эталоном, и настраивает свои шестеренки, чтобы получить как можно меньшую разницу. Наиболее оптимальная минимизация ошибки — градиентный спуск, а ошибка вычисляется функцией ошибка/стоимость.

Мелкая искусственная нейронная сеть

Многие считают, что искусственные нейронные сети — цифровые копии нашего неокортекса. Это не так. Мы слишком мало знаем о собственном мозге, чтобы требовать такое. Это было источником вдохновения для Фрэнка Розенблатта, создателя нейронных сетей.

Поиграйтесь с симулятором нейронной сети пару часов, чтобы примерно почувствовать, как это работает. Мы начнем с создания простой нейронной сети, чтобы познакомиться с синтаксисом TFlearn. Классическая проблема для начала — оператор OR. Хотя нейронные сети лучше подходят для других типов данных, для начала сойдет и такой вариант, чтобы понять, как это вообще работает.

Все программы глубокого обучения следуют одной логике:

  • Сперва подключаем библиотеки, импортируем и очищаем данные. Все входные данные интерпретируются как числа, неважно, изображения это, звуки или данные с сенсоров.
  • Эти длинные списки чисел — входные данные для нашей сети.
  • Теперь описываем нейронную сеть.
  • Выбираем тип и количество слоев сети.
  • Идет процесс обучения.
  • Сеть ищет корреляцию между входными данными и выходными.
  • Финальный шаг выводит предсказание, полученное обученной сетью.

Вот код нашей сети.

# 1. Import library of functions
import tflearn
# 2. Logical OR operator / the data
OR = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]
Y_truth = [[0.], [1.], [1.], [1.]]
# 3. Building our neural network/layers of functions 
neural_net = tflearn.input_data(shape=[None, 2])
neural_net = tflearn.fully_connected(neural_net, 1, activation='sigmoid')
neural_net = tflearn.regression(neural_net, optimizer='sgd', learning_rate=2, loss='mean_square')
# 4. Train the neural network / Epochs
model = tflearn.DNN(neural_net)
model.fit(OR, Y_truth, n_epoch=2000, snapshot_epoch=False)
# 5. Testing final prediction
print("Testing OR operator")
print("0 or 0:", model.predict([[0., 0.]]))
print("0 or 1:", model.predict([[0., 1.]]))
print("1 or 0:", model.predict([[1., 0.]]))
print("1 or 1:", model.predict([[1., 1.]]))

Вывод программы:

Training Step: 2000  | total loss: 0.00072 | time: 0.002s
| SGD | epoch: 2000 | loss: 0.00072 -- iter: 4/4

Testing OR operator

0 or 0: [[ 0.04013482]]
0 or 1: [[ 0.97487926]]
1 or 0: [[ 0.97542077]]
1 or 1: [[ 0.99997282]]

Строка 1
Комментарии начинаются с #

Строка 2
Подключаем библиотеку TFlearn, она позволит нам использовать функции глубокого обучения из Google Tensorflow.

Строки 5–6
Данные из таблицы сохраняются в списки. Точка в конце каждого числа преобразует их в числа с плавающей точкой. Это повысит точность вычисления.

Строка 9
Инициализируем сеть и определяем форму входных данных. Каждый оператор OR идет парой, таким образом имея форму, равную 2. None — дефолтное значение, представляющее размер серии.

Строка 10
Наш выходной слой. Параметр activation определяет функцию, отображающую выходные данные на слой. В нашем случае мы используем сигмоиду, которая отображает значение между 0 и 1.

Вот материал, который более подробно раскроет суть строк 9 и 10: tflearn.org/layers/core

Строка 11
Применяем регрессию. Параметр optimizer выбирает алгоритм минимизации функции потерь. Параметр learning rate определяет, как быстро нужно модифицировать сеть, и параметр loss определяет, как вычислять ошибку.

Строка 14
Выбираем, какую сеть использовать. Здесь же выбираем, где хранить логи обучения.

Строка 15
Тренируем сеть и модель. Выбираем входные данные (OR) и эталонную метку (Y_truth). Параметр n_epoch определяет, сколько раз мы прогоним данные через сеть. snapshot_epoch=True определяет, нужно ли валидировать модель после каждого прогона.

Строки 18–22
Вывод предсказания, сделанного обученной моделью. В нашем случае это вывод вероятности того, что вернется True/1.

Подробнее насчет строк 1422: tflearn.org/models/dnn

Вывод
Первый результат — комбинация [0.] & [0.], с вероятностью в 4% это true, и так далее. Выше можно увидеть, сколько обучений в итоге прошла модель. Поскольку данные помещаются в одну серию, это все равно что одна эпоха. Если данные слишком велики, чтобы поместиться в памяти, вам придется разбить их на части. loss — сумма ошибок по всем эпохам. SGD — стохастический градиентный спуск (Stochastic Gradient Descent), метод минимизации функции потерь. Iter показывает текущий кусок данных и общий размер данных.

Вы обнаружите подобную логику и синтаксис практически в любой сети, основанной на TFlearn. Лучший способ познакомиться с кодом поближе — изменить его и получить кучу ошибок.

Кривая потерь показывает количество ошибок на каждом шаге обучения. С Tensorboard вы можете визуально наблюдать за каждым экспериментом и развивать чувство того, какие параметры меняют обучение. Вот несколько вариаций изначального эксперимента, с которыми вы можете поиграться самостоятельно. Советую потратить несколько часов на то, чтобы освоиться с окружением и параметрами TFlearn.

Эксперименты:

  • Увеличьте количество обучений и эпох
  • Попробуйте поменять или добавить все возможные параметры к каждой функции, руководствуясь документацией: например, вместо  tflearn.fullyconnected(g, 1, activation=’sigmoid’) попробуйте tflearn.fullyconnected(g, 1, activation=’sigmoid’, bias=False)
  • Добавьте числа во входные данные
  • Поменяйте форму входных данных
  • Поменяйте функцию активации
  • Поменяйте метод градиентного спуска
  • Поменяйте подход к вычислению стоимости
  • Попробуйте AND и NOT операторы
  • Попробуйте XOR для тестирования выходных данных, например, поменяйте последний Y_truth с [1.] на [0.]
  • Попробуйте добавить еще уровень сети
  • Попробуйте добиться более быстрого обучения
  • Найдите способ добиться того, чтобы каждый шаг обучения был дольше 0.1 секунды

Начало работы

Python и Tensorflow — самая распространенная связка для глубокого обучения. TFlearn — фреймворк высокого уровня, построенный поверх Tensorflow. Другой распространенный фреймворк — Keras. У него больше возможностей, но я нахожу синтаксис TFlearn более простым и понятным. Оба фреймворка работают поверх Tensorflow.

Вы можете запускать простые нейронные сети, используя CPU вашего ПК. Но многие эксперименты потребуют часов или даже недель на выполнение. Поэтому большинство используют современные GPU, часто в виде облачных сервисов.

Простейшее решение для облачной обработки на GPU — FloydHub. Если вы хотя бы немного умеете обращаться с командной строкой, у вас уйдет не больше пяти минут на запуск.

Установите floyd-cli, пользуясь документацией. Кроме того, у FloydHub есть чат в Intercom, на случай, если вы застряли на каком-то этапе. Давайте запустим вашу первую сеть в FloydHub, используя TFlearn, Jupyter Notebook, и Tensorboard. После установки и входа в FloydHub, скачайте необходимые файлы:

git clone https://github.com/emilwallner/Deep-Learning-101.git
cd Deep-Learning-101
floyd init 101

В браузере откроется дашборд FloydHub, и вам предложат создать новый проект под названием 101. Когда вы закончите с этим, вернитесь в терминал и повторите последнюю команду.

floyd init 101

Теперь вы готовы к запуску сети на FloydHub.

Команда запуска проекта принимает различные параметры. Сейчас наши действия таковы:

  • Примонтируем датасет к FloydHub, используя параметр --data emilwallner/datasets/cifar-10/1:data. Мы можем просмотреть датасет (и вообще большинство публичных датасетов) прямо на FloydHub.
  • Используем облачный GPU с параметром --gpu
  • Подключаем Tensorboard с параметром --tensorboard
  • Запускаем задачу в режиме Jupyter Notebook с параметром --mode jupyter

Окей, поехали:

floyd run --data emilwallner/datasets/cifar-10/1:data --gpu --tensorboard --mode jupyter

Как только команда инициализирует Jupyter в браузере, откройте файл с именем start-here.ipnyb. Это простая нейронная сеть для знакомства с синтаксисом TFlearn. Мы подробно разобрались с ней выше.

В меню выберите Kernel > Restart & Run All. Если вы увидите сообщение, значит все работает. Зайдите в проект на FloydHub, и там вы найдете ссылку на Tensorboard.

Глубокая нейронная сеть

Глубокая сеть — сеть с несколькими скрытыми слоями. Работа сверточных сетей подробно разбирается в руководствах:

Мы же сосредоточимся на высокоуровневых принципах, которые применимы к большинству сетей.

Цель обучения сети в том, чтобы она могла делать предсказания по данным, на которых она не обучалась. Для этого нужно уметь обобщать, балансировать между обучением и забыванием. Вы хотите, чтобы сеть научилась отделять сигнал от шума, но также научилась забывать сигналы, найденные в данных для обучения.

Если сеть обучилась недостаточно, она остается недообученной. В противном случае она переобучена, когда сеть слишком полагается на данные обучения.

Регуляризация — процесс уменьшения переобучения путем забывания определенных сигналов из тренировочных данных. Чтобы лучше понять этот принцип, мы используем датасет CIFAR-10. Это 60 тысяч изображений в десяти категориях, таких как автомобили, грузовики и птицы. Цель — определить категории изображений.

В обычных условиях мы обработаем данные, очистим их и применим фильтры к изображению. Но для простоты мы сфокусируемся только на собственно нейронной сети. Вы можете запустить все примеры, пользуясь инструкцией из репозитория с примерами.

Входной слой — изображение, представленное в числовой форме. Выходной слой классифицирует изображение по категориям. Скрытые слои — смесь сверточных, соединительных слоев и слоев подвыборки.

Выбор количества слоев

Давайте посмотрим, как отличается сеть с одним и тремя наборами слоев. Каждый набор состоит из слоев свертки, подвыборки и соединительного слоя. Первые два эксперимента — experiment-0-few-layers.ipynb и experiment-0-three-layer-sets.ipynb.

# Convolutional network building
network = input_data(shape=[None, 32, 32, 3],
                     data_preprocessing=img_prep,
                     data_augmentation=img_aug)
# One set of layers
network = conv_2d(network, 32, 3, activation='relu')
network = max_pool_2d(network, 2)
network = fully_connected(network, 512, activation='relu')
network = fully_connected(network, 10, activation='softmax')
network = regression(network, optimizer='adam',
                     loss='categorical_crossentropy',
                     learning_rate=0.001)

Глядя на графики в Tensorboard, мы заметим, что сеть с одним набором примерно на 15% аккуратнее сети с несколькими наборами. Дело в том, что сеть с несколькими наборами недообучена.

Посмотрите на графики Accuracy и Accuracy/Validation. Наилучший подход в глубоком обучении — разделить датасет пополам, одну половину используя в обучении, а вторую для валидации. Так вы определите, как хорошо нейронная сеть справляется с предсказанием на новых данных, то есть с ее способностью к обобщению.

Как мы видим, точность тренировочных данных выше точности данных для валидации. Фоновый шум и детали мешают сети хорошо работать на новых данных.

Для борьбы с переобучением вы можете ограничивать сложные функции и вводить шум в нейронную сеть. Общие методы регуляризации — ограничение функций и исключающие слои.

Исключающие слои

Регуляризация исключением похожа на демократию: вместо использования нескольких мощных нейронов мы распределяем мощность по всей сети.

Нейронная сеть вынуждена изучать несколько независимых представлений. При финальном предсказании у сети есть несколько отдельных паттернов. Вот пример нейронной сети с исключающим слоем. В этом сравнении обе сети одинаковы во всем, кроме наличия исключающего слоя.

network = input_data(shape=[None, 32, 32, 3],
                        data_preprocessing=img_prep,
                        data_augmentation=img_aug)
network = conv_2d(network, 32, 3, activation='relu')
network = max_pool_2d(network, 2)
network = conv_2d(network, 64, 3, activation='relu')
network = conv_2d(network, 64, 3, activation='relu')
network = max_pool_2d(network, 2)
network = fully_connected(network, 512, activation='relu')
#The dropout layer
network = dropout(network, 0.5)
network = fully_connected(network, 10, activation='softmax')
network = regression(network, optimizer='adam',
                        loss='categorical_crossentropy',
                        learning_rate=0.001)

В каждом слое сети нейроны работают независимо друг от друга. Некоторые нейроны вносят большее влияние, чем остальные. Исключающий слой случайным образом отбрасывает нейроны. Таким образом, каждый нейрон внесет свой вклад.

Второй популярный метод предотвращения переобучения — применение L1/L2 регуляторной функции на каждый слой.

L1 и L2 регуляризация

Скажем, вы хотите описать признаки лошади. Если описание будет слишком конкретным, вы исключите из выборки много лошадей. Однако если описание будет слишком размытым, в выборку попадут и другие животные. L1/L2 регуляризация помогает сети определить это различие.

network = input_data(shape=[None, 32, 32, 3],
                        data_preprocessing=img_prep,
                        data_augmentation=img_aug)
network = conv_2d(network, 32, 3, activation='relu', regularizer='L2')
network = max_pool_2d(network, 2)
network = conv_2d(network, 64, 3, activation='relu', regularizer='L2')
network = conv_2d(network, 64, 3, activation='relu', regularizer='L2')
network = max_pool_2d(network, 2)
network = fully_connected(network, 512, activation='relu', regularizer='L2')
network = fully_connected(network, 10, activation='softmax')
network = regression(network, optimizer='adam',
                        loss='categorical_crossentropy',
                        learning_rate=0.001)

Если мы сделаем сравнение сетей, аналогичное предыдущему эксперименту, мы получим похожий результат.

Нейронная сеть с регуляризацией показывает себя лучше сети без регуляризации.

Регуляторная функция L2 ограничивает влияние слишком сложных функций. Она вычисляет, как сильно каждая функция влияет на конечный результат, и вводит ограничивающие коэффициенты.

Размер серии

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

#Large batch size
model.fit(X, Y, 
          n_epoch=50, 
          shuffle=True, 
          validation_set=(X_test, Y_test), 
          show_metric=True, 
          batch_size=2000, 
          run_id='cifar_large_batch_size')

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

Большая серия сделает меньше шагов, но расход времени и памяти на каждый шаг больше.

Скорость обучения

Финальный эксперимент — сравнение сетей с малой, средней и высокой скоростью обучения.

#Large learning rate
network = regression(network, 
                     optimizer='adam',
                     loss='categorical_crossentropy',
                     learning_rate=0.01)

Скорость обучения часто считается одним из важнейших параметров, учитывая ее влияние. Она определяет то, как корректируется изменение прогноза на каждом шаге. Если скорость слишком велика или низка, сеть может вообще не выдавать правильного результата, как мы видим на графике выше.

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

Когда вы закончите с экспериментами, не забудьте остановить инстанс на FloydHub, выбрав в меню Cancel.

Следующие шаги

В официальном репозитории TFlearn вы найдете примеры хороших сверточных сетей. Разберитесь с тем, как они устроены, и попробуйте улучшить свою сеть. Лучший результат на сегодняшний день — 96.43% (Graham, 2015).

Прим. переводчика: в репозитории http://rodrigob.github.io/are_we_there_yet собрана таблица лидеров по разным датасетам, в том числе по CIFAR-10.

Кроме того, я рекомендую получше познакомиться с Python и командной строкой. Это поможет меньше отвлекаться на код и больше сосредоточиться на дизайне самой сети. Начните с курса по Python на Codeacademy, и курса по командной строке. Это займет у вас дня три, если вы возьметесь за это всерьез.

Мои благодарности Ignacio Tonoli de Maussion, Per Harald Borgen, Jean-Luc Wingert, Sai Soundararaj, и Charlie Harrington за вычитку черновиков. И благодарность сообществу TFlearn за документацию и исходники.

Об авторе (Эмиль Воллнер)

Это первая статья из серии статей о глубоком обучении. Я потратил десять лет на изучение человеческого обучения. Работаю в бизнес-школе Оксфорда, инвестировал в образовательные стартапы и построил бизнес в сфере технологического обучения. В прошлом году я поступил в Ecole 42, чтобы найти применение своим знаниям в сфере машинного обучения.

Вы можете следить за моим прогрессом в Твиттере. Если у вас есть вопросы или пожелания, оставьте комментарий в Медиум.

Первоначально публиковалось в блоге FloydHub.

Новые комментарии