Я уже довольно давно занимаюсь проблемами машинного обучения и глубокими архитектурами (нейронные сети), и мне необходимо было сделать мини-презентацию системы, генерирующую временные ряды для эмуляции различных процессов. Поскольку на серьезные темы лучше говорить с юмором, то я решил подобрать какой либо веселый пример, чтобы выступление слушалось с улыбками на лицах. Нам крупно повезло, поскольку мы живем в одно время с великим оратором, чьи речи заставляют сердца людей биться чаще. Я говорю о Дональде Трампе. Поэтому вполне естественно было бы создать систему, которая галлюцинировала говорила бы как Трамп.
Рекуррентные Нейронные Сети это тип искусственных нейронных сетей, разработанных для распознавания паттернов в последовательности данных, будь это текст, геном, почерк, произнесенные слова, или числовые последовательности, исходящие из сенсоров, рынка акций и правительственных агенств. Основным отличием Рекуррентных Нейронных Сетей (в дальнейшем РНН) от остальных архитектур, является так называемое наличие памяти. РНН сохраняет предыдущие значения в своем состоянии. Можно сказать, что РНН строит динамические модели, т.е. модели, которые меняются с течением времени таким образом, что дают возможность достигать достаточной точности, в зависимости от контекста примеров, которые были предоставлены.
Неплохой туториал о данных архитектурах.
Также хочу порекомендовать отличный пост Андрея Карпатного, который является одним из ведущих исследователей Глубинного Обучения в мире, о невероятной эффективности РНН.
The Unreasonable Effectiveness of Recurrent Neural Networks
Для построения модели мне необходимо было найти корпус речей Трампа. После небольшого поиска, я нашел выступления Трампа на дебатах, плюс несколько интервью и заявлений на различные темы. Я почистил файлы от дат, отметок поведения зала (смех и прочее), перевел в нижний регистр и объединил все в один файл под названием
Второй важный момент, это выбор фреймворка для построения моделей. При желании, можно написать систему с нуля, например для Python существует библиотека CudaMat, для выполнения матричных вычислениях на графических картах. Поскольку, в общем виде, проход по Нейронной Сети это набор матричных умножений с применением функций активации, то на базе CudaMat можно достаточно быстро реализовать различные архитектуры. Но я не буду изобретать велосипед и возьму, как пример, два фреймворка для Deep Learning систем.
Первый, это пожалуй самый распространенный и удобный фреймворк Torch на базе Lua. Lua это пожалуй самый быстрый интерпретируемый язык, с мощным нативным бэкендом для графических ускорителей и state-of-art JIT-компилятором. Why-is-Lua-so-fast Lua активно используется в Deep Mind, достаточно зайти на их GitHub.
Второй, это DeepLearning4j — открытый фреймворк для построения Нейронных Сетей и распределенного обучения для JVM мира, активно поддерживаемый компанией SkyMind. К числу достоинств фреймворка относятся, легкость подключения визуализаций, наличие распределенного обучения (data-paralell), чувство комфорта для Java девелоперов. По сути, моей целью и было сравнить скорости обучения и подходы двух фреймворков.
Хочу также отметить тот момент, что все ниже перечисленное исполнялось на моем лаптопе MacBook Pro(2015), где нет NVIDIA GPU, и поэтому все запускалось на CPU, что ограничивало меня в выборе размера модели и времени, которое я мог потратить на подготовку презентации. Разница между CPU и GPU видна невооруженным взглядом.
GPU
CPU
Начнем с самого простого и быстрого. Установить систему можно отсюда github.com/jcjohnson/torch-rnn Но если ставить эту систему как есть на свою машину, то владельцы Маков столкнутся с проблемой несовместимости новой версии torch-hd5f с остальными зависимостями (и я думаю не только Маков, поскольку DeepMind в курсе этой проблемы). Эта проблема преодолевается, и для этого я отправляю вас сюда. Но лучше и проще всего, это взять готовый Docker image отсюда и запустить контейнер.
Закидываем корпус речей в папку torch/torch-rnn/data/ под именем speeches.txt (лежит тут).
Запускаем контейнер следующей командой
Флаг -v и путь после него означают, что я хочу отобразить путь из моей локальной файловой системы на контейнер Docker (в корневую систему /data/), поскольку в процессе тренинга модель записывает промежуточные результаты, то я не хочу потерять их после уничтожения контейнера. Да и в последующих запусках, я могу использовать готовую модель, как для продолжения тренинга так и для генерации.
Остальные команды идентичны как для контейнерного запуска, так и для запуска в родной системе.
Для начала, необходимо сделать предварительную обработку данных. Перевести в формат hd5f файл с корпусом речей. Пути к файлу написаны для контейнерной версии, поменяйте их на свой каталог.
Запускаем тренинг
если вы запускаете на системе с GPU то флаг -gpu -0, он стоит по умолчанию. Поскольку у меня нет ускорителя, я указал ограничение на размер последовательности до 50 символов. Количество настроек достаточно большое. Я выбрал размер слоя РНН 128, 2 слоя, поскольку иначе мой тренинг мог затянуться очень и очень надолго. Количество эпох и прочих параметров я оставил по умолчанию, поскольку мне нужен был некий стартовый момент.
В процессе тренинга, модель время от времени скидывается на диск, и в дальнейшем можно продолжать тренинг с этих чекпоинтов. В документации это подробно описано. Также, при желании, можно выводить в процессе тренинга сэмплы модели, то есть видеть насколько модель продвигается в изучении структуры языка Дональда. Я покажу как обучается модель на примере DeepLearning4j.
Через несколько часов тренинг заканчивается и в каталоге data появляется новый каталог cv, где находится модель на различных этапах тренинга.
Для семплирования последовательности с модели вводим
Флаг checkpoint показывает откуда брать модель, я установил последнюю запись. Температура достаточно важный параметр функции SoftMax, она показывает насколько детерменирована последовательность. Более высокие значения дают более шумный и стохастический выход, низкие могут повторять вход с незначительными изменениями. Допустимые значения от 0 до 1. Впрочем все это также задокументировано тут.
Результаты модели я покажу ниже.
Фреймворк DeepLearning4j не так популярен и знаменит как torch, в то же время он обладает рядом достоинств. Он основан на массивах ND4J, или как их называют NumPy для Java. Это массивы которые не находятся на куче, и работа с ними идет через нативные вызовы. Также у DeepLearning4j отличная система типов и достаточно интуитивно можно реализовывать все основные архитектуры, единственное, что от знания теории никуда не уйти.
Я выложил код сюда. Код основывается на GravesLSTM Character модели, я использовал разработки Alex Black. Для отображения процесса тренировки модели, я подключил визуализацию данных. Параметры модели постарался подобрать похожими на torch модель. Но различия существуют, и ниже будет видно что происходит в процессе.
По словам Андрея Карпатного, обучение нейронных сетей это больше искусство чем наука. Параметры обучения играют исключительную роль. Нет универсальных правил, а только рекомендации и чувство интуиции. Каждый параметр должен подбираться под конкретный набор проблем. Даже начальная инициализация весов играет роль в том, сойдется ли модель или нет. Как пример, твит Андрея a 20 layer model. weight init N(0,0.02): stuck completely. try weight init N(0, 0.05): optimizes right away. initialization matters a lot :\
Я запускал тренинг с разным параметром learning rate. И вот что выходило.
Первый запуск с learning rate 0.1 и инициализацией XAVIER
Видно, что где-то начиная с шага 3000 модель застряла и функция потерь начала осциллировать вокруг одних и тех же значений. Одна из возможных причин была то, что при большом параметре обучения параметры проскакивают оптимум, и получается осцилляция, градиент скачет. В принципе это неудивительно, так как параметр обучения 0.1 достаточно большой, рекомендуется испытывать его в промежутке от 0.1 до 0.001, а то и меньше. Я начал с большего.
Я запустил систему с параметром 0.01 и вот результат.
Видно что градиентный спуск значительно замедлился, и что хоть градиент и неукоснительно снижается, это требует множества шагов и я прекратил обучение, так как у меня оно съедало мощность лаптопа.
Третий запуск системы с параметром обучения 0.05.
По результатам видно, что градиент спускался быстрее, но опять застрял в конце и начал осциллировать, что неудивительно, поскольку значение 0.05 не намного меньше 0.1
Оптимальный вариант, это задавать снижения параметров обучения в процессе. Яркий пример этому архитектура LeNet. Принцип таков, что мы начинаем с больших параметров и по мере спуска уменьшаем. Вот запуск с таким расписанием.
На нижнем графике видно, как на шаге 4000, размер обновления уменьшается, и одновременно с этим функция потерь пробивает барьер, и начинается дальнейший спуск. Я не доводил тренировку до конца, поскольку мой лаптоп нужен был мне для работы, и я проводил этот анализ буквально за пару дней в свободное время. Помимо всего, существует множество параметров обучения, как регуляризация, момент и прочие. При серьезной работе необходимо тщательно подбирать данные параметры, поскольку незначительные изменения могут оказать решающее значение на то, будет ли сходиться модель, или застрянет ли она на локальном минимуме.
А теперь самое смешное и интересное. Как обучалась и что выдавала нам модель. На примере DeepLearning4j логов.
Поскольку в самом начале Рекуррентная Нейронная Сеть ничего не знает ни о языке, ни о структуре данных, мы ей начинаем скармливать корпус речей. Вот вывод после нескольких минибатчей (сэмплируем модель случайным посевом).
Видно, что модель уже понимает, что слова разделяются пробелами. Появляются даже некоторые местоимения как he, they.
Видно, что наш Шариков уже сказал свой первый абырвалг. Модель правильно начинает схватывать местоимения I, he, we, you. Некоторые простые слова также присутствуют, как bad, know.
Идем дальше.
Уже выглядит как английский язык.
Всего после
Completed 340 minibatches of size 32x1000 characters
Каково. Im not going to be so bad. It was so proud of me. Знакомые нарциссические интонации.
А это? They left you asses, believe me, but I will have great people.
Потом, что то мы видели в Мексике и мы строим, видимо хотели стену, но появился интерес.
Заметьте, такие результаты модель стала выдавать очень быстро, всего то после 340 минибатчей. Поскольку я не доводил тренинг до конца на DeepLearning4j, то посмотрим на результаты модели на примере Torch-RNN.
Видно как сеть начинает галлюцинировать. Размер сэмплов можно менять, указывая различные параметры, также есть разница в указании параметра температуры. Вот пример с низкой температурой — 0.15.
Видно, что сеть начинает заговариваться. Поскольку мистер Трамп говорит довольно простым языком, то наиболее вероятные переходы состояний в сети сводятся к простым конструкциям.
Недостатки данной модели видны невооруженным глазом.
Данный пример немного игрушечен, и в то же время, он показывает основные моменты в тренинге Нейронных Сетей. Если есть какие либо вопросы, или данная тема интересует, то могу в дальнейшем делиться информацией. Материалы на моем Твиттере — @ATavgen
Рекуррентные Нейронные Сети
Рекуррентные Нейронные Сети это тип искусственных нейронных сетей, разработанных для распознавания паттернов в последовательности данных, будь это текст, геном, почерк, произнесенные слова, или числовые последовательности, исходящие из сенсоров, рынка акций и правительственных агенств. Основным отличием Рекуррентных Нейронных Сетей (в дальнейшем РНН) от остальных архитектур, является так называемое наличие памяти. РНН сохраняет предыдущие значения в своем состоянии. Можно сказать, что РНН строит динамические модели, т.е. модели, которые меняются с течением времени таким образом, что дают возможность достигать достаточной точности, в зависимости от контекста примеров, которые были предоставлены.
Неплохой туториал о данных архитектурах.
Также хочу порекомендовать отличный пост Андрея Карпатного, который является одним из ведущих исследователей Глубинного Обучения в мире, о невероятной эффективности РНН.
The Unreasonable Effectiveness of Recurrent Neural Networks
Модели и тренинг
Для построения модели мне необходимо было найти корпус речей Трампа. После небольшого поиска, я нашел выступления Трампа на дебатах, плюс несколько интервью и заявлений на различные темы. Я почистил файлы от дат, отметок поведения зала (смех и прочее), перевел в нижний регистр и объединил все в один файл под названием
speeches.txt
Второй важный момент, это выбор фреймворка для построения моделей. При желании, можно написать систему с нуля, например для Python существует библиотека CudaMat, для выполнения матричных вычислениях на графических картах. Поскольку, в общем виде, проход по Нейронной Сети это набор матричных умножений с применением функций активации, то на базе CudaMat можно достаточно быстро реализовать различные архитектуры. Но я не буду изобретать велосипед и возьму, как пример, два фреймворка для Deep Learning систем.
Первый, это пожалуй самый распространенный и удобный фреймворк Torch на базе Lua. Lua это пожалуй самый быстрый интерпретируемый язык, с мощным нативным бэкендом для графических ускорителей и state-of-art JIT-компилятором. Why-is-Lua-so-fast Lua активно используется в Deep Mind, достаточно зайти на их GitHub.
Второй, это DeepLearning4j — открытый фреймворк для построения Нейронных Сетей и распределенного обучения для JVM мира, активно поддерживаемый компанией SkyMind. К числу достоинств фреймворка относятся, легкость подключения визуализаций, наличие распределенного обучения (data-paralell), чувство комфорта для Java девелоперов. По сути, моей целью и было сравнить скорости обучения и подходы двух фреймворков.
Хочу также отметить тот момент, что все ниже перечисленное исполнялось на моем лаптопе MacBook Pro(2015), где нет NVIDIA GPU, и поэтому все запускалось на CPU, что ограничивало меня в выборе размера модели и времени, которое я мог потратить на подготовку презентации. Разница между CPU и GPU видна невооруженным взглядом.
GPU
CPU
Torch-RNN
Начнем с самого простого и быстрого. Установить систему можно отсюда github.com/jcjohnson/torch-rnn Но если ставить эту систему как есть на свою машину, то владельцы Маков столкнутся с проблемой несовместимости новой версии torch-hd5f с остальными зависимостями (и я думаю не только Маков, поскольку DeepMind в курсе этой проблемы). Эта проблема преодолевается, и для этого я отправляю вас сюда. Но лучше и проще всего, это взять готовый Docker image отсюда и запустить контейнер.
Закидываем корпус речей в папку torch/torch-rnn/data/ под именем speeches.txt (лежит тут).
Запускаем контейнер следующей командой
docker run -v /Users/<YOUR_USER_NAME>/torch/torch-rnn/data:/data --rm -ti crisbal/torch-rnn:base bash
Флаг -v и путь после него означают, что я хочу отобразить путь из моей локальной файловой системы на контейнер Docker (в корневую систему /data/), поскольку в процессе тренинга модель записывает промежуточные результаты, то я не хочу потерять их после уничтожения контейнера. Да и в последующих запусках, я могу использовать готовую модель, как для продолжения тренинга так и для генерации.
Остальные команды идентичны как для контейнерного запуска, так и для запуска в родной системе.
Для начала, необходимо сделать предварительную обработку данных. Перевести в формат hd5f файл с корпусом речей. Пути к файлу написаны для контейнерной версии, поменяйте их на свой каталог.
python scripts/preprocess.py
--input_txt /data/speeches.txt
--output_h5 /data/speeches.h5
--output_json /data/speeches.json
Запускаем тренинг
th train.lua -batch_size 3 -seq_length 50 -gpu -1 -input_h5 /data/speeches.h5 -input_json /data/speeches.json
если вы запускаете на системе с GPU то флаг -gpu -0, он стоит по умолчанию. Поскольку у меня нет ускорителя, я указал ограничение на размер последовательности до 50 символов. Количество настроек достаточно большое. Я выбрал размер слоя РНН 128, 2 слоя, поскольку иначе мой тренинг мог затянуться очень и очень надолго. Количество эпох и прочих параметров я оставил по умолчанию, поскольку мне нужен был некий стартовый момент.
В процессе тренинга, модель время от времени скидывается на диск, и в дальнейшем можно продолжать тренинг с этих чекпоинтов. В документации это подробно описано. Также, при желании, можно выводить в процессе тренинга сэмплы модели, то есть видеть насколько модель продвигается в изучении структуры языка Дональда. Я покажу как обучается модель на примере DeepLearning4j.
Через несколько часов тренинг заканчивается и в каталоге data появляется новый каталог cv, где находится модель на различных этапах тренинга.
Для семплирования последовательности с модели вводим
th sample.lua -gpu -1 -sample 1 -verbose 1 -temperature 0.9 -checkpoint /data/cv/checkpoint_74900.t7 -length 2000
Флаг checkpoint показывает откуда брать модель, я установил последнюю запись. Температура достаточно важный параметр функции SoftMax, она показывает насколько детерменирована последовательность. Более высокие значения дают более шумный и стохастический выход, низкие могут повторять вход с незначительными изменениями. Допустимые значения от 0 до 1. Впрочем все это также задокументировано тут.
Результаты модели я покажу ниже.
DeepLearning4j
Фреймворк DeepLearning4j не так популярен и знаменит как torch, в то же время он обладает рядом достоинств. Он основан на массивах ND4J, или как их называют NumPy для Java. Это массивы которые не находятся на куче, и работа с ними идет через нативные вызовы. Также у DeepLearning4j отличная система типов и достаточно интуитивно можно реализовывать все основные архитектуры, единственное, что от знания теории никуда не уйти.
Я выложил код сюда. Код основывается на GravesLSTM Character модели, я использовал разработки Alex Black. Для отображения процесса тренировки модели, я подключил визуализацию данных. Параметры модели постарался подобрать похожими на torch модель. Но различия существуют, и ниже будет видно что происходит в процессе.
По словам Андрея Карпатного, обучение нейронных сетей это больше искусство чем наука. Параметры обучения играют исключительную роль. Нет универсальных правил, а только рекомендации и чувство интуиции. Каждый параметр должен подбираться под конкретный набор проблем. Даже начальная инициализация весов играет роль в том, сойдется ли модель или нет. Как пример, твит Андрея a 20 layer model. weight init N(0,0.02): stuck completely. try weight init N(0, 0.05): optimizes right away. initialization matters a lot :\
Я запускал тренинг с разным параметром learning rate. И вот что выходило.
Первый запуск с learning rate 0.1 и инициализацией XAVIER
Видно, что где-то начиная с шага 3000 модель застряла и функция потерь начала осциллировать вокруг одних и тех же значений. Одна из возможных причин была то, что при большом параметре обучения параметры проскакивают оптимум, и получается осцилляция, градиент скачет. В принципе это неудивительно, так как параметр обучения 0.1 достаточно большой, рекомендуется испытывать его в промежутке от 0.1 до 0.001, а то и меньше. Я начал с большего.
Я запустил систему с параметром 0.01 и вот результат.
Видно что градиентный спуск значительно замедлился, и что хоть градиент и неукоснительно снижается, это требует множества шагов и я прекратил обучение, так как у меня оно съедало мощность лаптопа.
Третий запуск системы с параметром обучения 0.05.
По результатам видно, что градиент спускался быстрее, но опять застрял в конце и начал осциллировать, что неудивительно, поскольку значение 0.05 не намного меньше 0.1
Оптимальный вариант, это задавать снижения параметров обучения в процессе. Яркий пример этому архитектура LeNet. Принцип таков, что мы начинаем с больших параметров и по мере спуска уменьшаем. Вот запуск с таким расписанием.
На нижнем графике видно, как на шаге 4000, размер обновления уменьшается, и одновременно с этим функция потерь пробивает барьер, и начинается дальнейший спуск. Я не доводил тренировку до конца, поскольку мой лаптоп нужен был мне для работы, и я проводил этот анализ буквально за пару дней в свободное время. Помимо всего, существует множество параметров обучения, как регуляризация, момент и прочие. При серьезной работе необходимо тщательно подбирать данные параметры, поскольку незначительные изменения могут оказать решающее значение на то, будет ли сходиться модель, или застрянет ли она на локальном минимуме.
Результаты
А теперь самое смешное и интересное. Как обучалась и что выдавала нам модель. На примере DeepLearning4j логов.
Поскольку в самом начале Рекуррентная Нейронная Сеть ничего не знает ни о языке, ни о структуре данных, мы ей начинаем скармливать корпус речей. Вот вывод после нескольких минибатчей (сэмплируем модель случайным посевом).
----- Sample 1 -----
Lеfs mint alo she g tor, torink.han, aulb bollg rurr Atans ir'd ciI anlot, ade dos rhant eot taoscare werang he ca m hltayeu.,hare they Woy theaplir horet
iul pe neaf it Yf therg. hhat anoy souk, thau do y RO Bury f
if. haveyhaled Dhorlsy Ato thinanse rank fourile DaniOn Ttovele yhinl ans anu he B
Видно, что модель уже понимает, что слова разделяются пробелами. Появляются даже некоторые местоимения как he, they.
----- Sample 2 -----
aly Eo, He, to bakk st I stire I'micgobbsh brond thet we sthe mikadionee bans. Whether job lyok,.
Whon not I ouuk.
Wewer they sas I dait ond we polntryoiggsiof, waoe have ithale. I bale bockuyte seemer I dant you I Fout whey
We kuow Soush Wharay nestibigiof, You knik is you know, boxw staretho bad
Видно, что наш Шариков уже сказал свой первый абырвалг. Модель правильно начинает схватывать местоимения I, he, we, you. Некоторые простые слова также присутствуют, как bad, know.
Идем дальше.
----- Sample 3 -----
cy doing to whit stoll.
Ho just to deed to was very minioned, now, Fome is a soild say and is is soudd and If no want nonkouahvion. if you beeming thet take is our tough us iss could feor youlk to at Lend and we do toted to start to pasted to doind the we do it.
I jind and I spongly stection Caread.
Уже выглядит как английский язык.
Всего после
Completed 340 minibatches of size 32x1000 characters
----- Sample 4 -----
Second. They left you asses, believe me, but I will have great people.I solling us some -- and you see something youve seen deterner to Mexico, we are building interest.
100,000. Im not going to be so bad.
It was so proud of me. Incredible and or their cities which I kept the same wealthy. They dont want them, were the world companies.
Yes, they get the fraud, except people deals 100. Its like never respaved us. Thats what were going to do an orfone thats seen this.
Каково. Im not going to be so bad. It was so proud of me. Знакомые нарциссические интонации.
А это? They left you asses, believe me, but I will have great people.
Потом, что то мы видели в Мексике и мы строим, видимо хотели стену, но появился интерес.
Заметьте, такие результаты модель стала выдавать очень быстро, всего то после 340 минибатчей. Поскольку я не доводил тренинг до конца на DeepLearning4j, то посмотрим на результаты модели на примере Torch-RNN.
So we’re going to run. But I was going to have all the manufacturing things as you know what we’re not going to be very soon to this country starting a president land the country. It’s the committed to say the greatest state. You know, I like Iran is so badly. I said, "I’m not as you know what? Why aren’t doing my sad by having any of the place so well, you look at 78%, I’ve done and they’re coming in the Hispanics are so many. The different state and then I mean, it’s not going to be these people that are politicians that they said, "You know, every poll said "We will bring it. I think we don’t want to talk about the stupid new of this country. We love it of money. It’s running the cary America great.
Видно как сеть начинает галлюцинировать. Размер сэмплов можно менять, указывая различные параметры, также есть разница в указании параметра температуры. Вот пример с низкой температурой — 0.15.
I want to take our country and they can be a great country and they want to do it. They want to take a look at the world is a lot of things are the worst thing that we have to do it. We have to do it. I mean, they want to tell you that we will be the world is a great country and they want to be a great country is going to be a great people. I mean, I want to be saying that we have to do it. They don’t know what they don’t want to say that we have to do it.
Видно, что сеть начинает заговариваться. Поскольку мистер Трамп говорит довольно простым языком, то наиболее вероятные переходы состояний в сети сводятся к простым конструкциям.
Недостатки данной модели видны невооруженным глазом.
- Размер данных для модели очень мал. Я не агрегировал все возможные речи и выступления, а собрал то, что удобно было быстро собрать. Для такого рода моделей размер данных играет самую важную роль, поскольку нейронные сети очень жадны до данных.
- Размер модели мал (количество слоев), поскольку я был ограничен в вычислительных ресурсах, и мне надо было сделать маленькую демонстрацию. Увеличив модель можно получать более интересные результаты.
Данный пример немного игрушечен, и в то же время, он показывает основные моменты в тренинге Нейронных Сетей. Если есть какие либо вопросы, или данная тема интересует, то могу в дальнейшем делиться информацией. Материалы на моем Твиттере — @ATavgen