Пожалуйста, введите доступный Вам адрес электронной почты. По окончании процесса покупки Вам будет выслано письмо со ссылкой на книгу.

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

Список удаленных книг:

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

Список удаленных книг:

Купить Редактировать корзину Логин
Поиск
Расширенный поиск Простой поиск
«+» - книги обязательно содержат данное слово (например, +Пушкин - все книги о Пушкине).
«-» - исключает книги, содержащие данное слово (например, -Лермонтов - в книгах нет упоминания Лермонтова).
«&&» - книги обязательно содержат оба слова (например, Пушкин && Лермонтов - в каждой книге упоминается и Пушкин, и Лермонтов).
«OR» - любое из слов (или оба) должны присутствовать в книге (например, Пушкин OR Лермонтов - в книгах упоминается либо Пушкин, либо Лермонтов, либо оба).
«*» - поиск по части слова (например, Пушк* - показаны все книги, в которых есть слова, начинающиеся на «пушк»).
«""» - определяет точный порядок слов в результатах поиска (например, "Александр Пушкин" - показаны все книги с таким словосочетанием).
«~6» - число слов между словами запроса в результатах поиска не превышает указанного (например, "Пушкин Лермонтов"~6 - в книгах не более 6 слов между словами Пушкин и Лермонтов)
 
 
Страница

Страница недоступна для просмотра

OK Cancel
Антонио Джулли, Суджит Пал Библиотека Keras — инструмент глубокого обучения Реализация нейронных сетей с помощью библиотек Theano и TensorFlow Deep Learning with Keras Implement neural networks with Keras on Theano and TensorFlow Antonio Gulli Sujit Pal BIRMINGHAM – MUMBAI Библиотека Keras – инструмент глубокого обучения Реализация нейронных сетей с помощью библиотек Theano и TensorFlow Антонио Джулли Суджит Пал Москва, 2018 УДК 004.85Keras ББК 32.971.3 Р51 Р51 Антонио Джулли, Суджит Пал Библиотека Keras – инструмент глубокого обучения. Реализация нейронных сетей с помощью библиотек Theano и TensorFlow / пер. с англ. Слинкин А. А. – М.: ДМК Пресс, 2018. – 294 с.: ил. ISBN 978-5-97060-573-8 Книга представляет собой краткое, но обстоятельное введение в современные нейронные сети, искусственный интеллект и технологии глубокого обучения. В ней представлено более 20 работо способных нейронных сетей, написанных на языке Python с использованием модульной библиотеки Keras, работающей поверх библиотек TensorFlow от Google или Theano от компании Lisa Lab. Описан функциональный API библиотеки Keras и возможности его расширения. Рассмотрены алгоритмы обучения с учителем (простая линейная регрессия, классический многослойный перцептрон, глубокие сверточные сети), а также алгоритмы обучения без учителя - автокодировщики и порождающие сети. Дано введение в технологию глубокого обучения с подкреплением и ее применение к построению игр со встроенным искусственным интеллектом. Издание предназначено для программистов и специалистов по анализу и обработке данных. УДК 004.85Keras ББК 32.971.3 Authorized Russian translation of the English edition of Deep Learning with Keras, ISBN 978-1-78712-842-2. Copyright ©Packt Publishing 2017. First published in the English language under the title 'Deep Learning with Keras – (9781787128422)'. This translation is published and sold by permission of Published by Packt Publishing Ltd., which owns or controls all rights to publish and sell the same. Все права защищены. Любая часть этой книги не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Материал, изложенный в данной книге, многократно проверен. Но, по- скольку вероятность технических ошибок все равно существует, издательство не может гарантировать абсолютную точность и правильность приводимых сведений. В связи с этим издательство не несет ответственности за возможные ошибки, связанные с использованием книги. ISBN 978-1-78712-842-2 (англ.) © Packt Publishing, 2017. ISBN 978-5-97060-573-8 (рус.) © Оформление, перевод на русский язык, издание, ДМК Пресс, 2018 Оглавление Об авторах........................................................................................................ 9 О рецензенте..................................................................................................12 Предисловие. . .................................................................................................13 Назначение.................................................................................... 13 Чем глубокое обучение отличается от машинного обучения и искусственного интеллекта......................................................... 14 Краткое содержание книги............................................................. 16 Что необходимо для чтения книги.................................................. 17 На кого рассчитана эта книга......................................................... 17 Графические выделения................................................................ 17 Отзывы. . ......................................................................................... 18 Поддержка клиентов...................................................................... 19 Загрузка кода примеров. . .............................................................................................................19 Загрузка цветных иллюстраций. . ...............................................................................................20 Опечатки. . ............................................................................................................................................20 Нарушение авторских прав. . .......................................................................................................20 Вопросы......................................................................................... 21 Глава 1. Основы нейронных сетей..............................................................22 Перцептрон.................................................................................... 24 Первый пример кода с использованием Keras...................................................................24 Многослойный перцептрон – первый пример нейросети............... 25 Проблемы обучения перцептрона и их решение..............................................................26 Сигмоида.............................................................................................................................................27 Блок линейной ректификации...................................................................................................28 Функции активации........................................................................................................................28 Реальный пример – распознавание рукописных цифр.................... 29 Унитарное кодирование...............................................................................................................30 Определение простой нейронной сети в Keras..................................................................30 Прогон простой сети Keras и создание эталона для сравнения..................................34 Улучшение простой сети в Keras посредством добавления скрытых слоев...........35 Дальнейшее улучшение простой сети Keras с помощью прореживания. . ...............38 Тестирование различных оптимизаторов в Keras..............................................................41 Увеличение числа периодов.......................................................................................................46 Управление скоростью обучения оптимизатора................................................................46 Увеличение числа нейронов в скрытых слоях....................................................................47 Увеличение размера пакета. . ......................................................................................................48 Подведение итогов экспериментов по распознаванию рукописных цифр............49 6 Оглавление  Применение регуляризации для предотвращения переобучения.............................50 Настройка гиперпараметров......................................................................................................52 Предсказание выхода. . ..................................................................................................................52 Практическое изложение алгоритма обратного распространения....52 В направлении глубокого обучения................................................ 54 Резюме.......................................................................................... 55 Глава 2. Установка Keras и описание API. . ..................................................56 Установка Keras. . ............................................................................ 56 Шаг 1 – установка зависимостей. . .............................................................................................56 Шаг 2 – установка Theano............................................................................................................57 Шаг 3 – установка TensorFlow....................................................................................................57 Шаг 4 – установка Keras................................................................................................................58 Шаг 5 – проверка работоспособности Theano, TensorFlow и Keras............................58 Настройка Keras............................................................................. 59 Установка Keras в контейнер Docker............................................... 60 Установка Keras в Google Cloud ML................................................. 62 Установка Keras в Amazon AWS....................................................... 64 Установка Keras в Microsoft Azure................................................... 65 Keras API........................................................................................ 67 Введение в архитектуру Keras. . ..................................................................................................68 Обзор готовых слоев нейронных сетей.................................................................................69 Обзор готовых функций активации.........................................................................................72 Обзор функций потерь..................................................................................................................72 Обзор показателей качества. . .....................................................................................................73 Обзор оптимизаторов....................................................................................................................73 Некоторые полезные операции................................................................................................73 Резюме.......................................................................................... 77 Глава 3. Глубокое обучение с применением сверточных сетей. . ...........79 Глубокая сверточная нейронная сеть.............................................. 80 Локальные рецептивные поля...................................................................................................80 Разделяемые веса и смещения..................................................................................................81 Пулинговые слои. . ............................................................................................................................82 Промежуточные итоги...................................................................................................................83 Пример ГСНС – LeNet..................................................................... 83 Код LeNet в Keras.............................................................................................................................83 О силе глубокого обучения............................................................. 89 Распознавание изображений из набора CIFAR-10 с помощью глубокого обучения........................................................................ 90 Повышение качества распознавания набора CIFAR-10 путем углубления сети.................................................................................95 Повышение качества распознавания набора CIFAR-10 путем пополнения данных........................................................................ 97 Предсказание на основе результатов обучения на наборе CIFAR-10. . .................. 100 Оглавление 7  Очень глубокие сверточные сети для распознавания больших изображений................................................................................ 101 Распознавание кошек с помощью сети VGG-16. . ............................................................. 102 Использование встроенного в Keras модуля VGG-16.................................................... 103 Использование готовых моделей глубокого обучения для выделения признаков........................................................................................................................................ 104 Очень глубокая сеть inception-v3, применяемая для переноса обучения. . .......... 105 Резюме........................................................................................ 108 Глава 4. Порождающие состязательные сети и WaveNet. . ................... 109 Что такое ПСС?............................................................................ 109 Некоторые приложения ПСС.................................................................................................... 111 Глубокие сверточные порождающие состязательные сети........... 114 Применение Keras adversarial для создания ПСС, . подделывающей MNIST. . .............................................................. 118 Применение Keras adversarial для создания ПСС, . подделывающей CIFAR. . ............................................................... 124 WaveNet – порождающая модель для обучения генерации звука.... 132 Резюме........................................................................................ 141 Глава 5. Погружения слов.......................................................................... 143 Распределенные представления.................................................. 144 word2vec...................................................................................... 145 Модель skip-грамм....................................................................................................................... 146 Модель CBOW................................................................................................................................. 150 Извлечение погружений word2vec из модели................................................................. 151 Сторонние реализации word2vec. . ......................................................................................... 154 Введение в GloVe......................................................................... 158 Использование предобученных погружений................................. 159 Обучение погружений с нуля. . ................................................................................................. 161 Настройка погружений на основе предобученной модели word2vec................... 165 Настройка погружений на основе предобученной модели GloVe........................... 169 Поиск погружений. . ...................................................................................................................... 170 Резюме........................................................................................ 174 Глава 6. Рекуррентная нейронная сеть – РНС........................................ 176 Простые ячейки РНС.................................................................... 177 Простая РНС с применением Keras – порождение текста. . ......................................... 179 Топологии РНС............................................................................. 184 Проблема исчезающего и взрывного градиента........................... 186 Долгая краткосрочная память – LSTM.......................................... 188 Пример LSTM – анализ эмоциональной окраски........................................................... 191 Вентильный рекуррентный блок – GRU......................................... 197 Пример GRU – частеречная разметка.................................................................................. 198 Двунаправленные РНС................................................................. 205 8 Оглавление  РНС с запоминанием состояния................................................... 206 Пример LSTM с запоминанием состояния – предсказание потребления электричества. . ............................................................................................................................... 206 Другие варианты РНС. . ................................................................. 212 Резюме........................................................................................ 213 Глава 7. Дополнительные модели машинного обучения..................... 214 Функциональный API Keras........................................................... 215 Регрессионные сети. . ................................................................... 218 Пример регрессии – предсказание содержания бензола в воздухе...................... 218 Обучение без учителя – автокодировщики................................... 223 Пример автокодировщика – векторы предложений..................................................... 225 Композиция глубоких сетей. . ........................................................ 234 Пример – сеть с памятью для ответов на вопросы. . ....................................................... 235 Расширение Keras........................................................................ 242 Пример – использование слоя lambda. . .............................................................................. 242 Пример – построение пользовательского слоя нормировки..................................... 243 Порождающие модели................................................................. 247 Пример – глубокие сновидения............................................................................................. 248 Пример – перенос стиля............................................................................................................ 255 Резюме........................................................................................ 260 Глава 8. Искусственный интеллект играет в игры................................. 262 Обучение с подкреплением.......................................................... 263 Максимизация будущих вознаграждений. . ........................................................................ 264 Q-обучение...................................................................................................................................... 265 Глубокая Q-сеть как Q-функция.............................................................................................. 267 Баланс между исследованием и использованием.......................................................... 268 Воспроизведение опыта............................................................................................................ 269 Пример – глубокая Q-сеть для поимки мяча. . ................................................................... 269 Что дальше?................................................................................. 282 Резюме........................................................................................ 283 Заключение. . ................................................................................................ 285 Keras 2.0 – что нового................................................................... 286 Установка Keras 2.0...................................................................................................................... 287 Изменения API............................................................................................................................... 287 Об авторах Антонио Джулли – директор по программному обеспечению и предприниматель с тягой к созданию и управлению глобальными инновационными технологическими компаниями. Специализируется в области поисковых систем, онлайновых сервисов, машинного обучения, информационного поиска, аналитики и облачных вычислений. Профессиональный опыт приобретал в шести странах Европы и Америки. Антонио работал в должности исполнительного директора, генерального директора, технического директора, вице-президента и руководителя группы в различных отраслях: от издательского бизнеса (Elsevier) до интернет-технологий для конечного пользователя (Ask.com и Tiscali) и НИОКР в сфере высоких технологий (Microsoft и Google). Выражаю благодарность своему талантливому соавтору, Суджиту Палу, за неизменное стремление помочь, не требуя ничего взамен. Я очень ценю его преданность командной работе, благодаря чему эта книга и смогла стать чем-то стоящим. также Франсуа Шолле и многих людей, внесших вклад в Keras, за то, что они тратили свое время и силы на создание впечатляющего инструментария для глубокого обучения, который прост в использовании и не требует сверхъестественных усилий. Спасибо также нашим редакторам из издательства Packt, Дивия Пуджари, Черил Дса и Динешу Павару, и рецензентам из Packt и Google за поддержку и ценные предложения. Без вас эта книга не состоялась бы. Я также благодарен своему начальнику, Брэду, и коллегам Майку и Коррадо из Google, которые подвигли меня написать эту книгу, читали ее черновые варианты и высказывали свое мнение. Еще я признателен кофейне Same Fusy в Варшаве, где у меня впервые появилась мысль написать эту книгу, когда я наслаждался чашечкой чая, выбранной из весьма обширного меню. 10 Об авторах  Это место обладает особой магией, и я горячо рекомендую его всем, ищущим, где бы подстегнуть свое воображение ( http:// ). www.samefusy.pl/ Далее я хочу поблагодарить отдел кадров в Google, пошедший навстречу моему пожеланию отдать все отчисления от продажи этой книги на стипендии представителям этнических меньшинств. Спасибо моим друзьям, Эрику, Лауре, Франческо, Этторе и Антонелле, которые поддерживали меня, когда я в том нуждался. Дружба – большая ценность, и вы – мои настоящие друзья. моему сыну Лоренцо, который побудил меня устроиться в Google, моему сыну Леонардо за постоянное стремление открывать что-то новое и моей дочери Авроре, благодаря которой я встречаю каждый день с улыбкой. И наконец, спасибо моему отцу Элио и матери Марии за их любовь. Суджит Пал – руководитель отдела технологических исследований в Elsevier Labs, работает над созданием интеллектуальных систем поиска по содержимому и метаданным. В область его интересов входят информационный поиск, онтологии, обработка естественных языков, машинное обучение и распределенная обработка. В настоящее время занимается классификацией и установлением сходства изображений с применением моделей глубокого обучения. До этого работал в промышленности безрецептурных медицинских препаратов, где участвовал в построении онтологической системы семантического поиска, организации контекстной рекламы и платформ обработки данных. Ведет посвященный технологиям блог Salmon Run. Выражаю благодарность своему соавтору, Антонио Джулли, пригласившему меня принять участие в написании книги. Это редкая возможность, благодаря которой я многому научился. К тому же, если бы не он, меня бы здесь в буквальном смысле не было. Хочу поблагодарить Рона Дэниэла, директора Elsevier Labs, и Брэдли П. Аллена, главного архитектора в Elsevier, которые познакомили меня с глубоким обучением и заставили поверить в возможности этой технологии. О рецензенте Ник Макклюр в настоящее время работает старшим специалистом по анализу данных в компании PayScale Inc., Сиэтл, штат Вашингтон, США. До этого работал в компании Zillow and Caesars Entertainment. Защитил диссертации по прикладной математике в Университете штата Монтана, колледже Святого Бенедикта и Университете Святого Иоанна. Ник – автор книги «TensorFlow Machine Learning Cookbook», вышедшей в издательстве Packt Publishing. Его страсть – изучать и делиться знаниями об аналитике, машинном обучении и искусственном интеллекте. Плоды своих размышлений Ник публикует в блоге на сайте и в своем fromdata.org аккаунте в Твиттере по адресу . @nfmcclure Предисловие Книга, которую вы держите в руках, – краткое, но обстоятельное введение в современные нейронные сети, искусственный интеллект и технологии глубокого обучения. Она написана специально для программистов и специалистов по анализу и обработке данных. книге представлено более 20 работоспособных нейронных сетей, написанных на языке Python с использованием модульной библиотеки Keras, работающей поверх библиотек TensorFlow от Google или Theano от компании Lisa Lab. Читатель шаг за шагом познакомится с алгоритмами обучения с учителем, начиная с простой линейной регрессии и классического многослойного перцептрона и кончая более сложными глубокими сверточными сетями и порождающими состязательными сетями. В книге также рассматриваются алгоритмы обучения без учителя: автокодировщики и порождающие сети. Подробно объясняется, что такое рекуррентные сети и сети с долгой краткосрочной памятью (long short-term memory, LSTM). Описывается функциональный API библиотеки Keras и обсуждается, как расширить Keras, если встретится задача, для которой в ней нет готового решения. Также рассматриваются более крупные и сложные системы, состоящие из описанных ранее структурных блоков. В заключение дается введение в технологию глубокого обучения с подкреплением и ее применение к построению игр со встроенным искусственным интеллектом. Если говорить о практических приложениях, то в книгу включен код программ для классификации новостей по заранее заданным категориям, для синтаксического анализа текста, для анализа эмоциональной окраски текста, для синтеза текстов и частеречной разметки. Не оставлена без внимания также обработка изображений: распознавание рукописных цифр, классификация изображений по категориям и распознавание объектов с последующим аннотированием изображений. Из области анализа зву- 14 Предисловие  ковых сигналов взят пример распознавания слов, произносимых нескольки м и лицами. Техника обучения с подкреплением применяется для построения глубокой сети Q-обучения, способной автономно играть в игры. Суть книги составляют эксперименты. Каждая сеть представлена несколькими вариантами, качество которых постепенно улучшается путем изменения входных параметров, формы сети, вида функции потерь и применяемых алгоритмов оптимизации. В ряде случаев приводятся сравнительные результаты обучения на CPU и GPU. Чем глубокое обучение отличается от машинного обучения и искусственного интеллекта Искусственный интеллект (ИИ) – очень широкая область исследований, посвященная когнитивным способностям машин: обучение определенному поведению, упреждающее взаимодействие с окружающей средой, способность к логическому выводу и дедукции, компьютерное зрение, распознавание речи, решение задач, представление знаний, восприятие действительности и многое другое (за подробностями отсылаем к книге S. Russell, P. Norvig «Artificial Intelligence: A Modern Approach», Prentice Hall, 2003). Менее формально под ИИ понимается любая ситуация, в которой машины имитируют интеллектуальное поведение, считающееся присущим человеку. Искусственный интеллект заимствует методы исследования из информатики, математики и статистики. Машинное обучение (МО) – отрасль ИИ, посвященная тому, как обучать компьютеры решению конкретных задач без программирования (см. книгу C. M. Bishop «Pattern Recognition and Machine Learning», Springer, 2006). Основная идея МО заключается в том, что можно создавать алгоритмы, способные обучаться на данных и впоследствии давать предсказания. Существует три основных вида МО. В случае обучения с учителем машине предъявляются данные и правильные результаты, а цель состоит в том, чтобы машина обучилась на этих примерах и смогла выдавать осмысленные результаты для данных, которые раньше не видела. В случае обучения без учителя машине предъявляются только сами данные, а она должна выявить структуру без постороннего вмешательства. Предисловие 15  В случае обучения с подкреплением машина ведет себя как агент, который взаимодействует с окружающей средой и обучается находить варианты поведения, приносящие вознаграждение. Глубокое обучение (ГО) – подмножество методов МО, в которых применяются искусственные нейронные сети (ИНС), построенные на базе аналогии со структурой нейронов человеческого мозга (см. статью Y. Bengio «Learning Deep Architectures for AI», Found. Trends, vol. 2, 2009). Неформально говоря, слово «глубокий» подразумевает наличие большого числа слоев в ИНС, но его интерпретация со временем менялась. Если еще четыре года назад считалось, что 10 слоев достаточно, чтобы называть сеть то теперь глубоглубокой, обычно называется сеть, содержащая сотни слоев. Искусственный интеллект Машинное обучение Глубокое обучение Глубокое обучение – это настоящее цунами (см. статью C. D. Manning «Computational Linguistics and Deep Learning» в журнале «Computational Linguistics», vol. 41, 2015) в области машинного обучения в том смысле, что сравнительно небольшое число хитроумных методов с огромным успехом применяется в самых разных областях (обработка изображений, текста, видео и речи, компьютерное зрение), что позволило добиться значительного прогресса по сравнению с результатами, достигнутыми за предшествующие десятки лет. Своими успехами ГО обязано также наличию больших объемов обучающих данных (например, набора ImageNet в области обработки изображений) и относительно дешевых графических процессоров (GPU), позволяющих построить очень эффективную процедуру вычислений. В компаниях Google, Microsoft, Amazon, Apple, Facebook и многих других методы глубокого обучения постоянно используются для анализа больших 16 Предисловие  массивов данных. Теперь эти знания и навыки вышли за рамки чисто академических исследований и стали достоянием крупных промышленных компаний. Они стали неотъемлемой составной частью современной программной продукции, и владение ими обязательно для программиста. В этой книге не предполагается наличие у читателя специальной математической подготовки. Однако же знакомство с языком Python является необходимым условием. Краткое содержание книги В главе 1 «Основы нейронных сетей» излагаются основные сведения о нейронных сетях. В главе 2 «Установка Keras и описание API» описано, как установить Keras в облаке AWS, Microsoft Azure, Google Cloud или на вашу собственную машину, а также дается краткий обзор различных API библиотеки Keras. знаГлава 3 «Глубокое обучение с применением сверточных сетей» комит с понятием сверточной сети. Это фундаментальное новшество стало причиной успеха глубокого обучения в применении к различным предметным областям, от видео до речи, выйдя далеко за пределы обработки изображений, где эта идея первоначально зародилась. Глава 4 «Порождающие состязательные сети и WaveNet» содержит введение в порождающие состязательные сети, используемые для синтеза данных, похожих на порождаемые людьми. Мы представляем глубокую нейронную сеть WaveNet, предназначенную для высококачественной имитации человеческого голоса и звучания музыкальных инструментов. В главе 5 «Погружения слов» обсуждаются методы глубокого обучения, служащие для выявления связей между словами и группировки похожих слов. рассматривается класс В главе 6 «Рекуррентные нейронные сети» нейронных сетей, оптимизированных для обработки последовательных данных, в т. ч. текста. Глава 7 «Дополнительные модели глубокого обучения» содержит краткий обзор функционального API Keras, регрессионных сетей, автокодировщиков и т. д. В главе 8 «Искусственный интеллект играет в игры» вы узнаете о глубоком обучении с подкреплением и о том, как Keras позволяет Предисловие 17  его использовать для построения глубоких сетей, умеющих играть в аркадные игры. Приложение содержит сводку обсуждаемых в книге тем и информацию о нововведениях в версии Keras 2.0. Что необходимо для чтения книги Вам понадобится следующее программное обеспечение:  TensorFlow версии 1.0.0 или выше;   Keras версии 2.0.2 или выше;   Matplotlib версии 1.5.3 или выше;   Scikit-learn версии 0.18.1 или выше;   NumPy версии 1.12.1 или выше.  К оборудованию предъявляются следующие требования:  32- или 64-разрядная архитектура;   CPU с таковой частотой не ниже 2 ГГц;   оперативная память объемом не меньше 4 ГБ;   не менее 10 ГБ свободного места на диске.  На кого рассчитана эта книга Если вы – специалист по анализу и обработке данных со знанием машинного обучения или занимаетесь программированием ИИ и знакомы с нейронными сетями, то эта книга станет неплохой отправной точкой для овладения методами глубокого обучения с применением библиотеки Keras. Знание Python – обязательное условие. Графические выделения В этой книге тип информации обозначается шрифтом. Ниже приведено несколько примеров с пояснениями. Фрагменты кода внутри абзаца, имена таблиц базы данных, папок и файлов, URL-адреса, данные, которые вводит пользователь, и адреса в Твиттере выделяются следующим образом: «Кроме того, мы загружаем истинные метки соответственно в и и Y_train Y_test применяем к ним унитарное кодирование». 18 Предисловие  Кусок кода выглядит так: from keras.models import Sequential model = Sequential() model.add(Dense(12, input_dim=8, kernel_initializer='random_uniform')) Желая привлечь внимание к части кода, мы выделяем ее полужирным шрифтом: # 10 outputs # final stage is softmax model = Sequential() model.add(Dense(NB_CLASSES, input_shape=(RESHAPED,))) model.add(Activation('softmax')) model.summary() Входная и выходная информация командных утилит выглядит так: pip install quiver_engine Новые термины важные фрагменты и выделяются полужирным шрифтом. Например, элементы графического интерфейса в меню или диалоговых окнах выглядят в книге так: «Первоначально наша прос т ая сеть имеет верность 92.22 %, т. е. примерно 8 из 100 рукописных символов распознаются неправильно». Таким значком обозначаются предупреждения и важные примечания. Таким значком обозначаются советы и рекомендации . Отзывы Мы всегда рады отзывам читателей. Расскажите нам, что вы думаете об этой книге – что вам понравилось или, быть может, не понравилось. Читательские отзывы важны для нас, так как помогают выпускать книги, из которых вы черпаете максимум полезного для себя. Чтобы отправить обычный отзыв, просто пошлите письмо на адрес , указав название книги в качестве теfeedback@packtpub.com Если вы являетесь специалистом в некоторой области и хотели Предисловие 19  бы стать автором или соавтором книги, познакомьтесь с инструкциями для авторов по адресу . www.packtpub.com/authors Поддержка клиентов Счастливым обладателям книг Packt мы можем предложить ряд услуг, которые позволят извлечь из своего приобретения максимум пользы. Загрузка кода примеров Вы можете скачать код примеров к этой книге из своей учетной записи на сайте . Если книга была куплеhttp://www.packtpub.com в другом месте, зайдите на страницу http://www.packtpub.com/ , зарегистрируйтесь, и мы отправим файлы по электронной support почте. Для скачивания файлов с кодом выполните следующие дейст вия: 1. Зарегистрируйтесь или зайдите на наш сайт, указав свой адрес электронной почты и пароль. 2. Наведите мышь на вкладку SUPPORT в верхней части страницы. Щелкните по ссылке Code Downloads & Errata. Search. 4. Введите имя книги в поле 5. Выберите интересующую вас книгу. 6. С помощью выпадающего меню укажите, где вы приобрели книгу. 7. Нажмите Code Download. Загруженный файл можно распаковать, воспользовавшись последними версиями программ:  WinRAR / 7-Zip для Windows;   Zipeg / iZip / UnRarX для Mac;   7-Zip / PeaZip для Linux.  Код к этой книге имеется также на странице сайта GitHub по адресу По https://github.com/PacktPublishing/Deep-Learning-with-Keras адресу размещен также код и виhttps://github.com/PacktPublishing/ к другим книгам из нашего обширного каталога. Полюбопытст вуйте! 20 Предисловие  Загрузка цветных иллюстраций Мы также предлагаем PDF-файл, содержащий цветные изображения, встречающиеся в книге. Цвет поможет лучше понять, как изменяются результаты. Этот файл можно скачать по адресу https://www.packtpub.com/sites/default/files/downloads/DeepLearn. проверяли содержимое книги со всем тщанием, но какие- то ошибки все же могли проскользнуть. Если вы найдете в нашей книге ошибку, в тексте или в коде, пожалуйста, сообщите нам о ней. Так вы избавите других читателей от разочарования и поможете нам сделать следующие издания книги лучше. При обнаружении опечатки просьба зайти на страницу http://www.packtpub. , выбрать книгу, щелкнуть по ссылке Errata Submission com/support Form и ввести информацию об опечатке. Проверив ваше сообщение, мы поместим информацию об опечатке на нашем сайте или добавим ее в список замеченных опечаток в разделе Errata для данной книги. Список ранее отправленных опечаток можно просмотреть, выбрав название книги на странице http://www.packtpub.com/books/ . Запрошенная информация появится в разделе content/support Errata. Нарушение авторских прав Незаконное размещение защищенного авторским правом материала в Интернете – проблема для всех носителей информации. В издательстве Packt мы относимся к защите прав интеллектуальной собственности и лицензированию очень серьезно. Если вы обнаружите незаконные копии наших изданий в любой форме в Интернете, пожалуйста, незамедлительно сообщите нам адрес или название веб-сайта, чтобы мы могли предпринять соответствующие меры. Просим отправить ссылку на вызывающий подозрение в пиратстве материал по адресу . copyright@packtpub.com Мы будем признательны за помощь в защите прав наших авторов и содействие в наших стараниях предоставлять читателям полезные сведения. Предисловие 21  Вопросы Если вас смущает что-то в этой книге, вы можете связаться с нами по адресу , и мы сделаем все возможное для questions@packtpub.com решения проблемы. 1 Глава Основы нейронных сетей Искусственные нейронные сети (для краткости нейросети или просто сети) – это класс моделей машинного обучения, в основе которых лежат исследования центральной нервной системы млекопитающих. Нейросеть состоит из нескольких взаимосвязанных нейронов, организованных в слои, которые обмениваются между собой сообщениями (как говорят, возбуждаются) при выполнении определенных условий. Первые исследования относятся к 1950-м годам, когда было введено понятие перцептрона (см. статью F. Rosenblatt «The Perceptron: A Probabilistic Model for Information Storage and Organization in the Brain», Psychological Review, vol. 65, pp. 386–408, 1958), двуслойной сети для выполнения простых операций. Затем в конце 1960-х годов был предложен алгоритм обратного распространения для эффективного обучения многослойных сетей (см. статьи P. J. Werbos «Backpropagation through Time: What It Does and How to Do It», Proceedings of the IEEE, vol. 78, pp. 1550–1560, 1990 и G. E. Hinton, S. Osindero, Y. W. Teh «A Fast Learning Algorithm for Deep Belief Nets, Neural Computing, vol. 18, pp. 1527–1554, 2006). В некоторых работах утверждается, что эти методы корнями уходят глубже, чем принято считать (см. статью J. Schmidhuber «Deep Learning in Neural Networks: An Overview», by, vol. 61, pp. 85–117, 2015). Нейронные сети были предметом интенсивных научных исследований до 1980-х годов, когда на первый план вышли другие, более простые, подходы. Однако с середины 2000-х годов отмечается возрождение интереса к этой теме в связи с прорывным алгоритмом быстрого обучения, предложенным Дж. Хинтоном (см. S. Leven «The Roots of Backpropagation: From Ordered Derivatives to Neural Networks and Political Forecasting», Neural Networks, vol. 9, 1996, и D. E. Rumelhart, G. E. Hinton, R. J. Williams «Learning Representations by Backpropagating Errors», vol. 323, 1986), и появлением (примерно в 2011 году) графических процессоров для массово-параллельных численных расчетов. Поддержка клиентов 23  Эти достижения проложили дорогу современному глубокому обучению, классу нейронных сетей, для которых характерно большое число слоев и которые способны обучать весьма изощренные модели на основе иерархии уровней абстрагирования. Несколько лет назад глубокой считалась сеть с 3–5 слоями, теперь же их число возросло до 100–200. Обучение путем последовательного абстрагирования напоминает модели зрения в мозге человека, эволюционировавшие миллионы лет. Человеческая система зрения действительно состоит из нескольких уровней. Глаз соединен с областью мозга, которая называется первичной зрительной корой, или зрительной ко- рой V1 и занимает задний полюс затылочной доли каждого полушария. Эта область имеется у многих млекопитающих и играет важную роль в распознавании простых образов и обработке информации об изменении ориентации, пространственной частоте и цвете. Согласно некоторым оценкам, первичная зрительная кора содержит примерно 140 миллионов нейронов и 10 миллиардов связей между ними. Кора V1 соединяется с областями V2, V3, V4, V5 и V6, отвечающими за всю последующую обработку изображений и распознавание более сложных объектов: фигур, лиц, животных и многого другого. Такая многослойная организация явилась результатом множества попыток, которые природа предпринимала на протяжении сотен миллионов лет. Согласно оценкам, кора головного мозга человека насчитывает порядка 16 миллиардов нейронов, и примерно 10-25 % из них относятся к зрению (см. статью S. Herculano-Houzel «The Human Brain in Numbers: A Linearly Scaled-up Primate Brain», vol. 3, 2009). Глубокое обучение заимствовало идею многослойной организации у зрительной системы человека: наружные слои искусственных нейронов обучаются базовым свойствам изображений, а более глубокие обрабатывают более сложные концепции. В этой книге рассмотрено несколько важных аспектов нейронных сетей на примере кода с использованием минималистской и весьма эффективной библиотеки глубокого обучения Keras, написанной на языке Python и работающей поверх библиотеки TensorFlow от Google (см. ) https://www.tensorflow.org/ или библиотеки Theano, разработанной в Монреальском университете (см. ). Итак, http://deeplearning.net/software/theano/ приступим. 24 Глава 1. Основы нейронных сетей  В этой главе будут рассмотрены следующие темы:  перцептрон;   многослойный перцептрон;   функции активации;   градиентный спуск;   стохастический градиентный спуск;   алгоритм обратного распространения.  Перцептрон Перцептрон – это простой алгоритм, который получает входной вектор x, содержащий n значений (x , x , ..., x ), которые часто назы1 2 n ваются входными признаками, или просто признаками, и выдает на выходе 1 (да) или 0 (нет). Формально говоря, мы определяем функцию:  1, если wx + b > 0 f(x) = 0 в противном случае Здесь w – вектор весов, wx – скалярное произведение , а b – смещение. Как мы знаем из геометрии, уравнение wx + b = 0 определяет граничную гиперплоскость, положение которой изменяется в зависимости от значений w и b. Если x лежит выше этой гиперплоскости (в двумерном случае – прямой), то ответ положительный, иначе отрицательный. Очень простой алгоритм! С помощью перцептрона нельзя выразить ответ «может быть». Он может ответить да (1) или нет (0), если мы знаем, как определить w и b, а это и есть процесс обучения, который обсуждается в следующих разделах. Первый пример кода с использованием Keras Исходным строительным блоком Keras является модель, а простейшая модель называется последовательной. В Keras последовательная модель представляет собой линейный конвейер (стек) слоев нейронной сети. В следующем фрагменте определен один слой с 12 нейронами, который ожидает получить 8 входных переменных (признаков): Многослойный перцептрон – первый пример нейросети 25  from keras.models import Sequential model = Sequential() model.add(Dense(12, input_dim=8, kernel_initializer='random_uniform')) На этапе инициализации каждому нейрону можно назначить вес. Keras предлагает несколько вариантов, перечислим наиболее употребительные:  : веса инициализируются равномерно рас r andom_uniform пределенными случайными значениями из диапазона (–0.05, 0.05) Иными словами, любое значение из этого интервала выбирается с одинаковой вероятностью;  : веса инициализируются нормально рас random_normal пределенными случайными значениями со средним 0 и стандартным отклонением 0.05. Те, кто не знает, что такое нормальное распределение, могут представлять себе симметричную : все веса инициализируются нулями.  zero Полное описание параметров имеется на странице https:// . keras.io/initializations/ Многослойный перцептрон – первый пример нейросети В этой главе мы определим первый пример сети с несколькими линейными слоями. Исторически перцептроном называлась модель с единственным линейным слоем, поэтому модель с несколькими слоями логично назвать многослойным перцептроном (МСП). На рисунке ниже показана нейронная сеть общего вида с одним входным, одним промежуточным и одним выходным слоем. Вход Выход 26 Глава 1. Основы нейронных сетей  Каждый узел первого слоя получает входной сигнал и возбуждается в соответствии с заранее определенными локальными граничными условиями. Выход первого слоя подается на вход второго, а выход второго – последнему слою, состоящему всего из одного нейрона. Интересно, что такая многослойная организация отдаленно напоминает работу человеческой системы зрения, о чем мы уже говорили. Эта сеть плотная в том смысле, что каждый нейрон одного слоя связан со всеми нейронами предыдущего слоя и всеми нейронами следующего слоя. Проблемы обучения перцептрона и их решение Рассмотрим один нейрон; каков оптимальный выбор веса w и смещения b? В идеале мы хотели бы предъявить набор обучающих примеров и поручить компьютеру выбрать вес и смещение, так чтобы ошибка при вычислении результата была минимальна. Переходя к конкретике, предположим, что имеется набор изображений кошек и другой набор изображений, на которых кошек нет. Для простоты будем считать, что каждый нейрон анализирует только один входной пиксель. Мы хотели бы, чтобы в процессе обработки изображений компьютером наш нейрон изменял свой вес и смещение таким образом, чтобы число изображений, ошибочно распознанных как не кошки, со временем уменьшалось. Этот подход кажется интуитивно очевидным, но для него требуется, чтобы малое изменение веса (и/или смещения) приводило к малому изменению результата. Если имеется большой скачок на выходе, то обпрогрессивное невозможно (разве что пробовать все возможные направления – это называется исчерпывающим поиском – не зная, достигаем ли мы какого-нибудь улучшения). В конце концов, дети ведь учатся постепенно. К сожалению, для перцептрона такое «постепенное» поведение не характерно. Перцептрон выдает значение 0 или 1, разница между ними велика, и это никак не способствует его обучению, что видно из следующего рисунка: Многослойный перцептрон – первый пример нейросети 27  Нам нужно что-то более гладкое – непрерывная, дифференцируемая функция, монотонно возрастающая от 0 до 1. Сигмоида Сигмоида определяется следующим образом: Как видно из графика ниже, она непрерывна и изменяется от 0 до 1, когда аргумент пробегает область определения (–∞, ∞): 28 Глава 1. Основы нейронных сетей  Нейрон может воспользоваться сигмоидой для вычисления нелинейной функции = wx + b). Отметим, что когда величина σ(z z = wx + b очень велика и положительна, e так что а –z → 0, σ(z) → 1, когда эта величина велика по модулю и отрицательна, то e –z → ∞, так что Иными словами, нейрон с сигмоидной функцией σ(z) → 0. активации ведет себя подобно перцептрону, но изменяется плавно и может порождать такие значения, как 0.5539 или 0.123191. В некотором смысле сигмоидный нейрон умеет давать ответ «может быть». Блок линейной ректификации Сигмоида – не единственная гладкая функция, применяемая в нейронных сетях. В последнее время стала очень популярна совсем простая функция, называемая блоком линейной ректификации (rectified linear unit, ReLU), поскольку в экспериментах она дает замечательные результаты. ReLU определяется формулой f(x) = max(0, а ее график показан на рисунке ниже. Как видим, она x), равна нулю для отрицательных значений x и линейно возрастает для положительных. Функции активации Сигмоида и ReLU называются функциями активации. В разделе «Тестирование различных оптимизаторов в Keras» мы увидим, что непрерывное изменение, характерное для этих функций, крайне важно для разработки алгоритмов обучения, которые адаптируются постепенно, стремясь уменьшить ошибку сети. На рисунке ниже показана схема применения функции активации к входноσ вектору (x , x , ..., x ), вектору весов (w , w , w ), смещению b 1 2 m 1 2 m и сумматору Σ: Реальный пример – распознавание рукописных цифр 29  Взвешенная сумма Функция активации Keras поддерживает несколько функций активации, их полный перечень приведен на странице . https://keras.io/activations/ Реальный пример – распознавание рукописных цифр В этом разделе мы построим сеть, умеющую распознавать рукописные цифры. Для этого будем использовать набор данных MNIST (см. ), включающий 60 000 обучаhttp://yann.lecun.com/exdb/mnist/ и 10 000 тестовых примеров. В обучающих примерах человеком проставлены правильные ответы. Например, с изображением рукописной цифры три ассоциирована метка 3. Когда имеется набор данных, содержащий правильные ответы, говорят об обучении с учителем. Обучающие примеры используются для обучения сети. С тестовыми примерами также ассоциированы правильные ответы, но идея в том, чтобы притвориться, будто они неизвестны, дать сети возможность сделать предсказание, а затем, сравнив его с правильным ответом, оценить, насколько хорошо сеть научилась распознавать цифры. Так что тес т овые примеры применяются только для проверки сети – что и не удивительно. Все изображения в наборе MNIST полутоновые, размера 28 28 пикселей. × На рисунке приведено несколько примеров. 30 Глава 1. Основы нейронных сетей  Унитарное кодирование Во многих приложениях удобно преобразовывать категориальные (нечисловые) признаки в числовые. Например, категориальный признак – цифру, принимающую значение d от 0 до 9, – можно представить бинарным вектором длины 10, в котором d-ый элемент равен 1, а все остальные – нулю. Такое представление называется encoding) и очень часто унитарным кодированием применяется в добыче данных, когда алгоритм обучения рассчитан на работу с числами. Определение простой нейронной сети в Keras Воспользуемся библиотекой Keras, чтобы определить сеть, распознающую рукописные цифры из набора MNIST. Начнем с очень простой нейросети и постепенно будем ее улучшать. Keras предоставляет средства для загрузки набора данных и разбиения его на обучающий, , и тестовый, . Для подX_train вычислений на GPU данные преобразуются к типу float32 и нормируются на интервал [0, 1]. Кроме того, в и Y_train Y_test мы загружаем правильные метки и применяем к ним унитарное кодирование. Вот как выглядит код: from __future__ import print_function import numpy as np from keras.datasets import mnist from keras.models import Sequential from keras.layers.core import Dense, Activation from keras.optimizers import SGD from keras.utils import np_utils np.random.seed(1671) # для воспроизводимости результатов # сеть и ее обучение NB_EPOCH = 200 BATCH_SIZE = 128 VERBOSE = 1 NB_CLASSES = 10 # количество результатов = числу цифр OPTIMIZER = SGD() # СГС-оптимизатор, обсуждается ниже в этой главе N_HIDDEN = 128 VALIDATION_SPLIT=0.2 # какая часть обучающего набора зарезервирована для контроля # данные: случайно перетасованы и разбиты на обучающий и тестовый набор # (X_train, y_train), (X_test, y_test) = mnist.load_data() # X_train содержит 60000 изображений размера 28x28 --> преобразуем в массив 60000 x 784 RESHAPED = 784 Реальный пример – распознавание рукописных цифр 31  # X_train = X_train.reshape(60000, RESHAPED) X_test = X_test.reshape(10000, RESHAPED) X_train = X_train.astype('float32') X_test = X_test.astype('float32') # нормировать # X_train /= 255 X_test /= 255 print(X_train.shape[0], 'train samples') print(X_test.shape[0], 'test samples') # преобразовать векторы классов в бинарные матрицы классов Y_train = np_utils.to_categorical(y_train, NB_CLASSES) Y_test = np_utils.to_categorical(y_test, NB_CLASSES) Во входном слое с каждым пикселем изображения ассоциирован один нейрон, т. е. всего получается 28 28 = 784 нейрона. × Обычно значения, ассоциированные с пикселями, нормируются с целью привести их к диапазону [0, 1] (это значит, что яркость каждого пикселя делится на максимально возможную яркость 255). На выходе получается 10 классов, по одному для каждой цифры. Последний слой состоит из единственного нейрона с функцией активации softmax, являющейся обобщением сигмоиды. Softmax сплющивает k-мерный вектор, содержащий произвольные вещественные числа, в k-мерный вектор вещественных чисел из интервала (0, 1). В нашем случае она агрегирует 10 ответов, выданных предыдущим слоем из 10 нейронов: # 10 выходов # на последнем этапе softmax model = Sequential() model.add(Dense(NB_CLASSES, input_shape=(RESHAPED,))) model.add(Activation('softmax')) model.summary() Определенную таким образом модель необходимо откомпилировать, т. е. привести к виду, допускающему исполнение базовой библиотекой (Theano или TensorFlow). Перед компиляцией необходимо принять несколько решений:  выбрать т. е. конкретный алгоритм, который оптимизатор,  будет обновлять веса в процессе обучения модели;  выбрать целевую функцию, которую оптимизатор исполь-  зует для навигации по пространству весов (часто целевая 34 Глава 1. Основы нейронных сетей  Подчеркнем, что обучающий и тестовый набор не должны пересекаться. Не имеет никакого смысла оценивать модель на примере, который она видела во время обучения. Смысл обучения в том, чтобы модель могла обобщаться на ранее не встречавшиеся данные, а не в том, чтобы она запоминала то, что уже известно. score = model.evaluate(X_test, Y_test, verbose=VERBOSE) print("Test score:", score[0]) print('Test accuracy:', score[1]) Вы только что определили свою первую нейронную сеть на Keras, можете принимать поздравления. Всего несколько строк кода – и компьютер умеет распознавать рукописные цифры. Теперь выполним этот код и оценим качество. Прогон простой сети Keras и создание эталона для сравнения На рисунке ниже показано, что происходит во время прогона программы: Сначала распечатывается архитектура сети, мы видим типы слоев, форму выхода, количество оптимизируемых параметров и характер связей между слоями. Затем сеть обучается на 48 000 примерах, а 12 000 примеров зарезервировано для контроля. Построенная нейронная сеть тестируется на 10 000 примерах. Как видим, Keras использует для вычислений базовую библиотеку TensorFlow. Пока что не будем вдаваться в детали обучения, но отметим, что программа выполнила 200 итераций и с каждым разом верность улучшалась. Реальный пример – распознавание рукописных цифр 35  По завершении обучения мы проверяем модель на тестовом наборе и видим, что на обучающем наборе получена верность 92.36%, на контрольном – 92.27%, а на тестовом – 92.22%. Это значит, что доля неправильно распознанных рукописных символов составляет чуть менее одного на десять. Безусловно, этот результат можно улучшить. Улучшение простой сети в Keras посредством добавления скрытых слоев Мы достигли верности 92.36% на обучающем наборе, 92.27% – на контрольном и 92.22% – на тестовом. Для начала неплохо, но есть возможность добиться большего. Посмотрим, как. Первое улучшение – включить в сеть дополнительные слои. После входного слоя поместим первый плотный слой с N_HIDDEN нейронами и функцией активации . Этот слой называется relu скрытым, потому что он напрямую не соединен ни с входом, ни с выходом. После первого скрытого слоя добавим еще один, также содержащий нейронов, а уже за ним будет расположен N_HIDDEN выходной слой с 10 нейронами, которые возбуждаются, если распознана соответствующая цифра. Вот код, определяющий новую сеть: from __future__ import print_function import numpy as np from keras.datasets import mnist from keras.models import Sequential from keras.layers.core import Dense, Activation from keras.optimizers import SGD from keras.utils import np_utils np.random.seed(1671) # для воспроизводимости результатов # сеть и ее обучение NB_EPOCH = 20 BATCH_SIZE = 128 VERBOSE = 1 NB_CLASSES = 10 # количество результатов = числу цифр 36 Глава 1. Основы нейронных сетей  OPTIMIZER = SGD() # СГС-оптимизатор, обсуждается ниже в этой главе N_HIDDEN = 128 VALIDATION_SPLIT=0.2 # какая часть обучающего набора зарезервирована для контроля # данные: случайно перетасованы и разбиты на обучающий и тестовый набор # (X_train, y_train), (X_test, y_test) = mnist.load_data() # X_train содержит 60000 изображений размера 28x28 --> преобразуем в массив 60000 x 784 RESHAPED = 784 # X_train = X_train.reshape(60000, RESHAPED) X_test = X_test.reshape(10000, RESHAPED) X_train = X_train.astype('float32') X_test = X_test.astype('float32') # нормировать X_train /= 255 X_test /= 255 print(X_train.shape[0], 'train samples') print(X_test.shape[0], 'test samples') # преобразовать векторы классов в бинарные матрицы классов Y_train = np_utils.to_categorical(y_train, NB_CLASSES) Y_test = np_utils.to_categorical(y_test, NB_CLASSES) # M_HIDDEN скрытых слоев # 10 выходов # на последнем этапе softmax model = Sequential() model.add(Dense(N_HIDDEN, input_shape=(RESHAPED,))) model.add(Activation('relu')) model.add(Dense(N_HIDDEN)) model.add(Activation('relu')) model.add(Dense(NB_CLASSES)) model.add(Activation('softmax')) model.summary() model.compile(loss='categorical_crossentropy', optimizer=OPTIMIZER, metrics=['accuracy']) history = model.fit(X_train, Y_train, batch_size=BATCH_SIZE, epochs=NB_EPOCH, verbose=VERBOSE, validation_split=VALIDATION_SPLIT) score = model.evaluate(X_test, Y_test, verbose=VERBOSE) print("Test score:", score[0]) print('Test accuracy:', score[1]) Выполним этот код и посмотрим, какие результаты дает такая многослойная сеть. Неплохо. Добавив два скрытых слоя, мы достигли верности 94.50% на обучающем наборе, 94.63% – на кон- 38 Глава 1. Основы нейронных сетей  Дальнейшее улучшение простой сети Keras с помощью прореживания Наше последнее достижение – верность 94.50% на обучающем наборе, 94.63% – на контрольном и 94.41% – на тестовом. Второе улучшение совсем простое. Мы применим прореживание – с вероятностью случайным образом отбрасывать некоdropout значения, распространяющиеся внутри сети, состоящей из плотных скрытых слоев. Это хорошо известная форма регуляризации в машинном обучении. Как ни странно, отбрасывание некоторых значений приводит к повышению качества: from __future__ import print_function import numpy as np from keras.datasets import mnist from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation from keras.optimizers import SGD from keras.utils import np_utils np.random.seed(1671) # для воспроизводимости результатов # сеть и ее обучение NB_EPOCH = 20 BATCH_SIZE = 128 VERBOSE = 1 NB_CLASSES = 10 # количество результатов = числу цифр OPTIMIZER = SGD() # СГС-оптимизатор, обсуждается ниже в этой главе N_HIDDEN = 128 VALIDATION_SPLIT=0.2 # какая часть обучающего набора зарезервирована для контроля DROPOUT = 0.3 # данные: случайно перетасованы и разбиты на обучающий и тестовый набор # (X_train, y_train), (X_test, y_test) = mnist.load_data() # X_train содержит 60000 изображений размера 28x28 --> преобразуем в массив 60000 x 784 RESHAPED = 784 Реальный пример – распознавание рукописных цифр 39  # X_train = X_train.reshape(60000, RESHAPED) X_test = X_test.reshape(10000, RESHAPED) X_train = X_train.astype('float32') X_test = X_test.astype('float32') # нормировать X_train /= 255 X_test /= 255 # преобразовать векторы классов в бинарные матрицы классов Y_train = np_utils.to_categorical(y_train, NB_CLASSES) Y_test = np_utils.to_categorical(y_test, NB_CLASSES) # M_HIDDEN скрытых слоев model = Sequential() model.add(Dense(N_HIDDEN, input_shape=(RESHAPED,))) model.add(Activation('relu')) model.add(Dropout(DROPOUT)) model.add(Dense(N_HIDDEN)) model.add(Activation('relu')) model.add(Dropout(DROPOUT)) model.add(Dense(NB_CLASSES)) model.add(Activation('softmax')) model.summary() model.compile(loss='categorical_crossentropy', optimizer=OPTIMIZER, metrics=['accuracy']) history = model.fit(X_train, Y_train, batch_size=BATCH_SIZE, epochs=NB_EPOCH, verbose=VERBOSE, validation_split=VALIDATION_SPLIT) score = model.evaluate(X_test, Y_test, verbose=VERBOSE) print("Test score:", score[0]) print('Test accuracy:', score[1]) Выполнив те же 20 итераций, что и раньше, мы увидим, что сеть достигла верности 91.54% на обучающем наборе, 94.48% – на контрольном и 94.25% – на тестовом: Реальный пример – распознавание рукописных цифр 41  Интересно понаблюдать за тем, как возрастает верность на обучающем и тестовом наборе при увеличении числа периодов. Как видно из приведенного ниже графика, эти две кривые сходятся, когда число периодов приблизительно равно 250, так что последующее обучение ничего не даст. Замечено, что сети со случайным прореживанием внутренних слоев часто лучше обобщаются на новые примеры из тестового набора. Интуитивно это можно объяснить тем, что каждый нейрон становится «умнее», потому что знает, что нельзя полагаться на соседей. В процессе тестирования прореживание не производится, т. е. используются все тщательно настроенные нейроны. Короче говоря, в общем случае рекомендуется проверять, как будет работать сеть, если применена та или иная форма прореживания. Тестирование различных оптимизаторов в Keras Мы определили и использовали сеть, теперь будет полезно на интуитивном уровне объяснить, как происходит обучение сети. Остановимся на одном популярном методе обучения – градиентном спуске (ГС). Представим себе общую функцию стоимости C(w) от одной переменной w с графиком такого вида: 42 Глава 1. Основы нейронных сетей  Функция стоимости Начальный вес Градиент Градиентный спуск можно уподобить альпинисту, спускающемуся с горы в долину. Гора представлена функцией C, а долина – ее минимальным значением C . Альпинист находится в начальной min точке w и перемещается небольшими шажками. На каждом шаге r 0 градиент дает направление максимального роста. Математически ac это направление определяется частной производной в точке w , ∂ w r в которой альпинист оказался на шаге r. Поэтому, двигаясь протиac w ) − воположном направлении , альпинист будет направлятьr в сторону долины. На каждом шаге альпинист может учитывать длину своей ноги пред следующим шагом. В терминологии метода градиентного спуска это называется скоростью обучения Если η ≥ 0. она слишком мала, то альпинист будет двигаться медленно, а если слишком велика, то есть шанс проскочить мимо долины. Вспомним, что сигмоида – гладкая функция, так что мы можем вычислить ее производную. Можно доказать, что производная сигмоиды 1 σ ( x ) = 1 + e − x 46 Глава 1. Основы нейронных сетей  Верность Верность Верность Увеличение числа периодов Предпримем еще одну попытку: увеличим число периодов обучения с 20 до 200. Увы, время вычислений при этом увеличивается в 10 раз, но никакого выигрыша мы не получаем. Эксперимент оказался неудачным, но мы узнали, что увеличение времени обучения необязательно приводит к улучшению. Успех обучения обусловлен скорее применением удачных методов, а не временем, потраченным на расчеты. Результаты шестого варианта представлены на следующем графике: Верность Верность Верность Управление скоростью обучения оптимизатора Мы можем еще попробовать изменить скорость обучения оптимизатора. Из следующего графика видно, что оптимальное значение близко к 0.001, а это как раз и есть значение, подразумеваемое Реальный пример – распознавание рукописных цифр 47  по умолчанию. Прекрасно! Adam работает, не требуя никакой настройки. числа нейронов в скрытых слоях Еще одна возможность – изменить число нейронов во внутренних скрытых слоях. На следующем графике показаны результаты, получаемые при увеличении числа нейронов, т. е. сложности модели. Как видно, время вычислений быстро растет, поскольку приходится оптимизировать все больше параметров. Но достигаемый выигрыш при этом становится все меньше и меньше. Общее число параметров Общее параметров число параметров число Общее На следующем графике показано, как изменяется время одной итерации при росте числа скрытых нейронов. 48 Глава 1. Основы нейронных сетей  Время выполнения одной итерации Время выполнения одной итерации итерации одной выполнения Время А на этом графике мы видим изменение верности при росте числа нейронов. Верность Верность Верность Увеличение размера пакета Алгоритм градиентного спуска пытается минимизировать функцию стоимости одновременно на всех примерах из обучающего набора и для всех представленных в нем признаков. Алгоритм стохастического градиентного спуска обходится гораздо дешевле, потому что в нем рассматривается только BATCH_SIZE Реальный пример – распознавание рукописных цифр 49  примеров. Посмотрим, как зависит поведение модели от этого параметра. Как видим, оптимальная верность достигается, когда : BATCH_SIZE=128 Верность Верность Верность Подведение итогов экспериментов по распознаванию рукописных цифр Итак, опробовав пять вариантов, мы смогли улучшить выбранный показатель качества с 92.36% до 97.93%. Сначала мы определили простую однослойную сеть средствами Keras. Затем мы улучшили качество, добавив скрытые слои. Дальнейшего улучшения удалось добиться путем включения случайного прореживания сети и выбора подходящего оптимизатора. Полученные результаты сведены в таб л ицу ниже. На обучающем На контрольном На тестовом Модель/Верность наборе наборе наборе Простая 92.36% 92.37% 92.22% С двумя скрытыми 94.50% 94.63% 94.41% слоями (128) С прореживанием 97.7% 98.10% 97.73% (30%) (200 периодов) 97.84% RMSprop 97.97% 97.59% (20 периодов) 97.93% Adam 98.28% 98.03% (20 периодов) Однако следующие два эксперимента не принесли существенного улучшения. Увеличение числа внутренних нейронов влечет 50 Глава 1. Основы нейронных сетей  за собой усложнение модели и рост объема вычислений, но дает едва осязаемый выигрыш. То же самое относится к увеличению числа периодов обучения. Наш последний эксперимент состоял в изменении параметра оптимизатора . BATCH_SIZE Применение регуляризации для предотвращения переобучения Интуитивно представляется, что хорошая модель машинного обучения должна давать малую ошибку на обучающих данных. Математически это равносильно минимизации построенной моделью функции потерь на обучающих данных и выражается следующей формулой: min: {loss(Training Data | Model} Однако этого может оказаться недостаточно. Модель может стать избыточно сложной, стремясь уловить все связи, присущие обучающим данным. У такого увеличения сложности есть два нежелательных последствия. Во-первых, для выполнения сложной модели нужно много времени. Во-вторых, сложная модель может показывать великолепное качество на обучающих данных – по- скольку она запомнила все присутствующие в них связи, но гораздо худшее на контрольных – поскольку модель не обобщается на новые данные. Таким образом, обучение свелось не к способности к обобщению, а к запоминанию. На следующем графике показана типичная функция потерь, которая убывает как на обучающем, так и на контрольном наборе. Однако в какой-то момент потеря на контрольных данных начинает расти из-за переобучения. Переобучение Потеря Контрольные данные Обучающие данные Период Реальный пример – распознавание рукописных цифр 51  Эвристическое правило состоит в том, что если в процессе обучения мы наблюдаем возрастание потери на контрольном наборе после первоначального убывания, значит, модель слишком сложна и слишком близко подогнана к обучающим данным. В машинном обучении этот феномен называется переобучением. Для решения проблемы переобучения необходимо как-то выразить сложность модели и управлять ею. И как это сделать? Но ведь модель по существу – всего лишь вектор весов. Поэтому ее сложность можно представить в виде количества ненулевых весов. Иными словами, если две модели M1 и M2 дают примерно одинаковое качество в терминах функции потерь, то следует предпочесть ту, в которой количество ненулевых весов меньше. Для управления важностью выбора более простой модели можно завести гиперпараметр ≥ 0 и минимизировать следующую функцию: λ min: {loss(Training Data | Model} + complexity(Model) * λ В машинном обучении применяются три способа регуляризации. Р егуляризация по норме L1 (известная также под назва-  нием lasso): сложность модели выражается в виде суммы модулей весов.  Р егуляризация по норме L2 (гребневая): сложность моде-  ли выражается в виде суммы квадратов весов.  Э ластичная сеть: для выражения сложности модели при-  меняется комбинация двух предыдущих способов. Отметим, что идею регуляризации можно применить к весам, к модели и к активации. Таким образом, регуляризация может способствовать повышению качества сети, особенно если налицо очевидное переобучение. Оставляем эксперименты в качестве упражнения для интересующихся читателей. Keras поддерживает все три формы регуляризации. Добавить регуляризацию просто, ниже показано задание L2-регуляризатора ядра (вектора весов W): from keras import regularizers model.add(Dense(64, input_dim=64, kernel_regularizer=regularizers.l2(0.01))) Полное описание параметров имеется на странице https:// . keras.io/regularizers/ 52 Глава 1. Основы нейронных сетей  Настройка гиперпараметров Описанные выше эксперименты помогли составить представление о том, какие имеются способы настройки нейросети. Однако то, что годится для данного примера, может не подойти в других случаях. Для каждой сети имеется много допускающих оптимизацию параметров (количество скрытых нейронов, , колиBATCH_SIZE периодов и ряд других, зависящих от сложности сети). Настройкой гиперпараметров называется процесс поиска оптимального сочетания этих параметров, при котором достигается минимум функции стоимости. Если имеется n параметров, то можно считать, что они определяют n-мерное пространство, а наша цель – найти в этом пространстве точку, в которой функция стоимости принимает минимальное значение. Для достижения этой цели можно, например, создать координатную сетку в пространстве и для каждого ее узла вычислить значение функции стоимости. Иными словами, выполнить полный перебор всех комбинаций параметров. Предсказание выхода Обученную сеть естественно использовать для предсказания. В Keras это очень просто: # вычислить предсказание predictions = model.predict(X) Для заданного входного вектора можно вычислить несколько значений:  : вычисляет потерю;  m odel.evaluate()  : вычисляет категориальные выхо m odel.predict_classes() ды;  : вычисляет вероятности классов.  m odel.predict_proba() Практическое изложение алгоритма обратного распространения Многослойный перцептрон обучается на данных с помощью процесса, называемого обратным распространением. Его можно описать как постоянное исправление ошибок по мере их обнаружения. Посмот р им, как он работает. Практическое изложение алгоритма обратного распространения 53  Напомним, что с любой нейронной сетью ассоциирован набор весов, которые служат для вычисления выходных значений по входным. Кроме того, в нейронной сети может быть несколько скрытых слоев. Первоначально всем весам присваиваются случайные значения. Затем сеть активируется для каждого входного значения из обучающего набора: значения распространяются в прямом направлении от входного слоя через скрытые к выходному, который и выдает предсказание: Вход Выход Прямое распространение Поскольку истинное наблюдаемое значение для обучающего набора известно, мы можем вычислить ошибку предсказания. Идея заключается в том, чтобы выполнить обратное распространение ошибки и с помощью подходящего алгоритма оптимизации, например градиентного спуска, подправить веса нейронной сети с целью уменьшения ошибки: Вход Выход Обратное распространение ошибки 54 Глава 1. Основы нейронных сетей  Процесс прямого распространения сигнала от входного слоя к выходному и обратного распространения ошибки повторяется несколько раз, пока ошибка не станет ниже заранее заданного порогового значения. Весь процесс изображен на следующем рисунке: Метка Функция потерь Признаки Предсказание Модель Оптимизатор вычисляет обновленные веса Признаки – это входные данные, а метки служат для управления процессом обучения. Модель обновляется таким образом, что функция потерь на каждом шаге минимизируется. В нейронной сети важен не столько отклик отдельно взятого нейрона, сколько весь набор корректируемых весов в каждом слое. Поэтому сеть постепенно изменяет внутренние веса, так чтобы увеличить количество правильно предсказанных меток. Конечно, для минимизации расхождения в процессе обучения принципиально важно, чтобы были выбраны подходящие признаки, а данные были размечены правильно. В направлении глубокого обучения Экспериментируя с распознаванием рукописных цифр, мы пришли к выводу, что чем ближе к 99 % достигнутая верность, тем труднее ее улучшить. Если мы хотим продвинуться дальше, то нужна какая-то новая идея. Чего нам не хватает? Важнейшее наблюдение состоит в том, что до сих пор мы не учитывали информацию о расположении изображения в пространстве. Так, приведенный ниже код преобразует растровое изображение, представляющее все цифры, в плоский вектор, в котором вся пространственная информация потеряна: # X_train содержит 60000 изображений размера 28x28 --> преобразуем в массив 60000 x 784 Резюме 55  X_train = X_train.reshape(60000, 784) X_test = X_test.reshape(10000, 784) Однако же наш мозг работает иначе. Напомним, что в основе зрения лежит несколько областей коры, каждая из которых распознает все более крупные структуры, сохраняя при этом информацию о локализации в пространстве. Сначала мы видим отдельные пиксели, затем распознаем среди них простые геометрические формы, а затем все более сложные элементы: предметы, лица, тела людей, животных и т. д. В главе 3 мы познакомимся со специальным типом сети глубокого обучения, сверточной нейронной сетью (СНС), разработанной так, чтобы можно было одновременно сохранить пространственную локализацию в изображении и обучаться все более высоким уровням абстракции: ближний к входным данным слой обучается распознаванию простых образов, а чем слой дальше, тем более сложные образы он распознает. Но прежде чем переходить к обсуждению СНС, нам предстоит рассмотреть некоторые особенности архитектуры Keras и дополнительные концепции машинного обучения. Резюме В этой главе мы познакомились с основами нейронных сетей: что такое перцептрон и многослойный перцептрон, как определяются нейронные сети в Keras, как постепенно улучшить качество сети по сравнению с эталоном и как настраивать гиперпараметры. Кроме того, мы теперь знаем о некоторых полезных функциях активации (сигмоиде и блоке линейной активации ReLU), о том, как обучать сеть с помощью алгоритма обратного распространения, основанного на методе градиентного спуска или стохастического градиентного спуска, или на более сложных методах типа Adam и RMSprop. В следующей главе мы увидим, как установить Keras в облаке AWS, Microsoft Azure, Google Cloud или на свою локальную машину. Мы также дадим обзор API Keras. 2 Глава Установка Keras и описание API В предыдущей главе мы обсудили основные принципы нейронных сетей и привели несколько примеров сетей, умеющих распознавать рукописные цифры из набора данных MNIST. В этой главе мы обсудим установку Keras, Theano и TensorFlow. Мы увидим, как настроить рабочее окружение и за короткое время перейти от смутной идеи к работоспособной нейросети. Затем мы поговорим о том, как произвести установку в инфраструктуре, основанной на контейнерах Docker, и в облаках Google GCP, Amazon AWS и Microsoft Azure. Попутно мы представим обзор API Keras и опишем некоторые полезные операции, в т. ч. загрузку и сохранение архитектуры и весов нейронной сети, раннюю остановку, сохранение истории, контрольные точки и взаимодействие с TensorBoard и Quiver. Установка Keras В следующих разделах мы покажем, как установить Keras на различные платформы. Шаг 1 – установка зависимостей Первым делом установим пакет , поддерживающий рабоnumpy с многомерными массивами и матрицами, а также с математическими функциями. Затем установим библиотеку для научных расчетов . После этого имеет смысл установить пакет scipy scikit, считающийся в машинном обучении на Python универсальlearn средством – ножом швейцарской армии. Кроме того, полез- Установка Keras 57  но будет установить библиотеку обработки изображений и pillow библиотеку сериализации данных , которой Keras пользуется h5py для сохранения моделей. Для установки всего необходимого достаточно одной команды. Можно вместо этого установить дистрибутив Anaconda Python, который уже содержит , , , , и numpy scipy scikit-learn h5py pillow множество других библиотек, используемых в научных расчетах (см. S. Ioffe, C. Szegedy «Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift», arXiv.org/ , 2015). Список пакетов, входящих в состав Anaconda abs/1502.03167 Python, см. на странице https://docs.continuum.io/anaconda/pkg. На снимке экрана ниже показана процедура установки неdocs пакетов. Шаг 2 – установка Theano Установить Theano поможет : pip Шаг 3 – установка TensorFlow Теперь можно установить TensorFlow, следуя инструкциям на сайте TensorFlow по адресу https://www.tensorflow.org/versions/ . И на этот раз r0.11/get_started/os_setup.html#pip-installation для установки нужного пакета используется , как показано на pip рисунке ниже. 58 Глава 2. Установка Keras и описание API  Шаг 4 – установка Keras Теперь можно установить Keras: Шаг 5 – проверка работоспособности Theano, TensorFlow и Keras Проверим созданное окружение. Сначала попробуем определить сигмоиду в Theano. Как видим, это очень просто: нужно просто записать математическую формулу и применить ее ко всем элементам матрицы. Запустите оболочку Python Shell и введите показанный ниже код: Настройка Keras 59  Итак, Theano работает. Для проверки TensorFlow просто импортируем набор данных MNIST, как показано на рисунке ниже. В главе 1 мы уже видели несколько примеров нейросетей, созданных в Keras: Настройка Keras Конфигурационный файл Keras очень прост. Загрузите его в редактор . Вот список параметров: vi Параметр Значения Определяет представление изображений: image_dim_ordering – принятое в TensorFlow, – в Theano tf th epsilon Значение константы epsilon в вычислениях floatx Может принимать значение или float32 float64 backend Может принимать значение или tensorflow theano Значение параметра определяет интуиth неочевидный порядок измерений изображения: (глубина, ширина, высота) вместо (ширина, высота, глубина), как в случае . Ниже приведены значения параметров на моей машине: tf 60 Глава 2. Установка Keras и описание API  При установке версии TensorFlow с поддержкой GPU Keras автоматически будет использовать GPU, если в качестве базовой библиотеки выбрана TensorFlow. Установка Keras в контейнер Docker Один из самых простых способов начать работу с TensorFlow и Keras – установить их в контейнер Docker. Удобно воспользоваться готовым образом Docker для глубокого обучения, созданным сообществом; он содержит все популярные библиотеки ГО (TensorFlow, Theano, Torch, Caffe и т. д.). Необходимые файлы имеются в репозитории на GitHub по адресу https://github.com/saiprashanths/dl. В предположении, что Docker уже установлен и работает (см. docker ), установка не вызывает https://www.docker.com/products/overview никаких трудностей: На следующем снимке экрана показано, как после получения образа из Git строится контейнер Docker: Установка Keras в контейнер Docker 61  А здесь мы видим, как этот контейнер запускается: Из контейнера можно активировать поддержку сервера Jupyter Notebooks (см. ): http://jupyter.org/ Система будет работать прямо на локальной машине: Есть также возможность получить доступ к TensorBoard (см. ), https://www.tensorflow.org/how_tos/summaries_and_tensorboard/ для чего нужно выполнить показанную ниже команду: В результате вы увидите такую страницу: 62 Глава 2. Установка Keras и описание API  Установка Keras в Google Cloud ML Установить Keras в облако Google Cloud очень просто. Сначала нужно установить командный интерфейс (файл можно скачать по адресу ), к платформе Google Cloud; https://cloud.google.com/sdk/ после этого мы сможем использовать CloudML, управляемую службу, которая позволяет без труда строить модели машинного обучения средствами TensorFlow. Прежде чем переходить к Keras, воспользуемся Google Cloud в сочетании с TensorFlow, чтобы обучить модель на наборе данных MNIST, который имеется на GitHub. Код будет находитьcя на локальной машине, а обучение происходить в облаке. На следующем снимке экрана показан протокол обучения: Чтобы наблюдать за последовательным уменьшением перекрестной энтропии, можно воспользоваться TensorBoard: Установка Keras в Google Cloud ML 63  График перекрестной энтропии показан на рисунке ниже. Чтобы воспользоваться Keras поверх TensorFlow, нужно просто скачать исходный код Keras с сайта PyPI ( https://pypi.Python.org/ или более позднюю версию), а затем использоpypi/Keras/1.2.0 Keras как пакетное решение для CloudML: Ниже в качестве примера приведен скрипт : trainer.task2.py from keras.applications.vgg16 import VGG16 from keras.models import Model from keras.preprocessing import image 64 Глава 2. Установка Keras и описание API  from keras.applications.vgg16 import preprocess_input import numpy as np # готовая, уже обученная модель глубокого обучения VGG-16 base_model = VGG16(weights='imagenet', include_top=True) for i, layer in enumerate(base_model.layers): print (i, layer.name, layer.output_shape) Установка Keras в Amazon AWS Установить TensorFlow и Keras в облако Amazon очень просто. Можно даже использовать готовый образ машины , своTFAMI.v3 и бесплатный (см. https://github.com/ritchieng/tensorflow): образ устанавливается меньше чем за пять минут и поддерживает TensorFlow, Keras, OpenAI Gym и все необходимые зависимос т и. По состоянию на январь 2017 году поддерживались следующие версии:  TensorFlow 0.12   Keras 1.1.0   TensorLayer 1.2.7   CUDA 8.0   CuDNN 5.1   Python 2.7   Ubuntu 16.04  Кроме того, образ работает на вычислительных инстанTFAMI.v3 P2 (см. ), как поhttps://aws.amazon.com/ec2/instance-types/#p2 на следующем снимке экрана: Установка Keras в Microsoft Azure 65  Ниже перечислены некоторые характеристики инстансов P2:  процессоры Intel Xeon E5-2686v4 (Broadwell);   графические процессоры NVIDIA K80 с 2496 параллельными  ядрами и 12 ГБ памяти;  поддержка прямого обмена данными между GPU;   расширенные сетевые возможности (см.  https://aws.amazon.com/ec2/faqs/#What_networking_capabilities_are_includ- агрегированная пропускная способed_in_this_feature сети 20 Гб/с. Образ работает также на вычислительных инстансах G2 TFAMI.v3 (см. ), обладающих, https://aws.amazon.com/ec2/instance-types/#g2 в частности, такими свойствами:  процессоры Intel Xeon E5-2670 (Sandy Bridge);   графические процессоры NVIDIA с 1536 ядрами CUDA и 4 ГБ  видеопамяти. Установка Keras в Microsoft Azure Один из способов установить Keras в облако Azure – сначала установить поддержку Docker, а затем скачать контейнерную версию TensorFlow плюс Keras. В сети можно найти подробные инструкции по установке Keras и TensorFlow в сочетании с Docker, но по сущест ву это то, что мы уже видели в предыдущем разделе (см. https:// blogs.msdn.microsoft.com/uk_faculty_connection/2016/09/26/ ). tensorflow-on-docker-with-microsoft-azure/ 66 Глава 2. Установка Keras и описание API  Если вы используете Theano в качестве базовой библиотеки, то для запуска Keras достаточно всего лишь загрузить готовый пакет, имеющийся в коллекции Cortana Intelligence Gallery (см. https:// ). В слеgallery.cortanaintelligence.com/Experiment/Theano-Keras-1 примере показано, как можно импортировать Theano и Keras в Azure ML непосредственно в виде ZIP-файла и использовать их в модуле Execute Python Script. Этим примером мы обязаны Хай Ниню (см. ), по существу здесь Keras https://goo.gl/VLR25o выполняется внутри метода : azureml_main() # Скрипт ДОЛЖЕН содержать функцию azureml_main, являющуюся # точкой входа в этот модуль. import pandas as pd import theano import theano.tensor as T from theano import function from keras.models import Sequential from keras.layers import Dense, Activation import numpy as np # Эта функция принимает два необязательных аргумента: # Param<dataframe1>: a pandas.DataFrame # Param<dataframe2>: a pandas.DataFrame def azureml_main(dataframe1 = None, dataframe2 = None): # Здесь должна быть логика программы # print('Input pandas.DataFrame #1:rnrn{0}'.format(dataframe1)) # Если zip-файл подключен к стороннему входному порту, # то он распаковывается в каталог ".Script Bundle–, который # добавляется в sys.path. Поэтому, если zip-файл содержит # Python-файл mymodule.py, то его можно импортировать так: # import mymodule model = Sequential() model.add(Dense(1, input_dim=784, activation="relu")) model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy']) data = np.random.random((1000,784)) labels = np.random.randint(2, size=(1000,1)) model.fit(data, labels, nb_epoch=10, batch_size=32) model.evaluate(data, labels) return dataframe1 На рисунке ниже показан пример использования Microsoft Azure ML для выполнения Theano и Keras: Keras API 67  Keras API Keras обладает модульной минималистской и легко расширяемой архитектурой. Франсуа Шолле, автор Keras, пишет: При разработке библиотеки основное внимание уделялось поддержке быстрых экспериментов. Сокращение пути от идеи – к результату ключ к успешной исследовательской работе. С помощью Keras определяются высокоуровневые нейронные сети, работающие поверх библиотеки TensorFlow (см. https:// ) или Theano (см. github.com/tensorflow/tensorflow https://github. ). Дадим некоторые пояснения. com/Theano/Theano  М одульность. Модель представляет собой последователь-  ность или граф автономных модулей, которые соединяются между собой, как детали конструктора LEGO, образуя нейросеть. В библиотеке имеется множество готовых модулей, реализующих различные типы слоев, функций стоимости, 68 Глава 2. Установка Keras и описание API  оптимизаторов, схем инициализации, функций активации и методов регуляризации.  М инимализм. Библиотека написана на Python, все модули  короткие и самодокументированные.  Р асширяемость. В библиотеку можно добавлять новую  функциональность. Этой теме посвящена глава 7. Введение в архитектуру Keras В этом разделе мы рассмотрим самые важные компоненты Keras, применяемые для определения нейронных сетей. Сначала определим, что такое тензор, затем обсудим различные способы соединения готовых модулей и в заключение опишем наиболее употребительные модули. Что такое тензор? Keras пользуется библиотекой Theano или TensorFlow для эффективных вычислений с тензорами. Но что такое тензор? Да просто многомерный массив или матрица. Обе библиотеки умеют эффективно выполнять символические вычисления с тензорами, а это основной строительный блок для создания нейронных сетей. Соединение моделей Keras В Keras есть два способа соединения моделей:  последовательная композиция;   функциональная композиция.  Рассмотрим их подробнее. Последовательная композиция В этом случае готовые модели соединяются в линейный конвейер слоев, напоминающий стек или очередь. В главе 1 мы встречались с такими последовательными конвейерами, например: model = Sequential() model.add(Dense(N_HIDDEN, input_shape=(784,))) model.add(Activation('relu')) model.add(Dropout(DROPOUT)) model.add(Dense(N_HIDDEN)) model.add(Activation('relu')) model.add(Dropout(DROPOUT)) model.add(Dense(nb_classes)) Keras API 69  model.add(Activation('softmax')) model.summary() Функциональная композиция Функциональный API позволяет определять более сложные модели, например, ациклические графы, модели с разделяемыми слоями или с несколькими выходами. Примеры будут приведены в главе 7. Обзор готовых слоев нейронных сетей Keras предоставляет несколько готовых слоев. Мы рассмотрим наиболее употребительные и отметим, в каких главах эти слои используются. плотный слой Плотная модель – это полносвязный слой нейронной сети. Примеры мы уже видели в главе 1. Ниже приведен прототип модели со всеми параметрами: keras.layers.core.Dense(units, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) Рекуррентные нейронные сети – простая, LSTM и GRU Рекуррентные нейронные сети – это класс нейронных сетей, в которых используется последовательная природа входных данных. Входными данными может быть текст, речь, временные ряды и вообще любой объект, в котором появление элемента последовательности зависит от предшествующих элементов. В главе 6 мы будем обсуждать рекуррентные сети трех видов: простые, LSTM и GRU. Ниже приведены прототипы моделей со всеми параметрами: keras.layers.recurrent.Recurrent(return_sequences=False, go_backwards=False, stateful=False, unroll=False, implementation=0) keras.layers.recurrent.SimpleRNN(units, activation='tanh', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, 70 Глава 2. Установка Keras и описание API  recurrent_dropout=0.0) keras.layers.recurrent.GRU(units, activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0) keras.layers.recurrent.LSTM(units, activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0) Сверточные и пулинговые слои Сверточные сети – класс нейронных сетей, в которых сверточные и пулинговые операции используются для постепенного обучения довольно сложных моделей с повышающимся уровнем абстракции. Такой способ обучения напоминает модель человеческого зрения, сложившуюся в результате миллионов лет эволюции. Сверточные сети обсуждаются в главе 3. Ниже приведены прототипы моделей со всеми параметрами: keras.layers.convolutional.Conv1D(filters, kernel_size, strides=1, padding='valid', dilation_rate=1, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) keras.layers.convolutional.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) keras.layers.pooling.MaxPooling1D(pool_size=2, strides=None, padding='valid') keras.layers.pooling.MaxPooling2D(pool_size=(2, 2), strides=None, padding='valid', data_format=None) 72 Глава 2. Установка Keras и описание API  Обзор готовых функций активации К числу готовых функций активации относятся, в частности, сигмоида, линейная функция, гиперболический тангенс и блок линейной ректификации (ReLU). Несколько примеров мы уже видели в главе 1, а в последующих главах встретим и другие. На рисунке ниже приведены графики вышеперечисленных функций. Сигмоида Линейная функция Гиперболический тангенс ReLU Обзор функций потерь Функции потерь (или целевые функции) (см. https://keras.io/ ) можно отнести к четырем категориям: losses/  Верность, используемая в задачах классификации. Таких функ-  ций четыре: (средняя верность по всем предbinary_accuracy в задачах бинарной классификации), categorical_ (средняя верность по всем предсказаниям в задачах accuracy многоклассовой классификации), sparse_categorical_accuracy (используется, когда метки разреженные) и top_k_categorical_ (успехом считается случай, когда истинный целевой accuracy класс находится среди первых предсказаний). top_k  Ошибка, измеряющая различие между предсказанными и  фактическими значениями. Варианты таковы: (средне mse Keras API 73  квадра т ическая ошибка), (квадратный корень из rmse среднеквадратической ошибки), (средняя абсолютная mae ошибка), (средняя ошибка в процентах), (средняя mape msle квадратично-логарифмическая ошибка).  Кусочно-линейная функция потерь, которая обычно приме-  няется для обучения классификаторов. Существует два варианта: кусочно-линейная, определяемая как max(1 – y y , * true pred 0) и квадратичная кусочно-линейная, равная квадрату кусочно-линейной. Классовая потеря используется для вычисления пере-  крестной энтропии в задачах классификации. Существует несколько вариантов, включая бинарную перекрестную энтропию (см. ) и https://en.wikipedia.org/wiki/Cross_entropy категориальную перекрестную энтропию. Несколько примеров целевых функций мы видели в главе 1, а дополнительные будут приведены в следующих главах. Обзор показателей качества Функции показателей качества (см. ) https://keras.io/metrics/ аналогичны целевым функциям. Единственное различие между ними состоит в том, что результаты вычисления показателей не используются на этапе обучения модели. Примеры мы видели в главе 1, а дополнительные будут приведены ниже. Обзор оптимизаторов К числу оптимизаторов относятся СГС, RMSprop и Adam. Несколько примеров мы видели в главе 1, а дополнительные (Adagrad и Adadelta, см. ) будут приведены в слеhttps://keras.io/optimizers/ главах. Некоторые полезные операции Ниже перечислены некоторые вспомогательные операции, включенные в Keras API. Их цель – упростить создание сетей, процесс обучения и сохранение промежуточных результатов. Сохранение и загрузка весов и архитектуры модели Для сохранения и загрузки архитектуры модели служат следующие функции: 74 Глава 2. Установка Keras и описание API  # сохранить в формате JSON json_string = model.to_json() # сохранить в формате YAML yaml_string = model.to_yaml() # восстановить модель из JSON-файла from keras.models import model_from_json model = model_from_json(json_string) # восстановить модель из YAML-файла model = model_from_yaml(yaml_string) Для сохранения и загрузки параметров модели служат следующие функции: from keras.models import load_model # создать HDF5-файл 'my_model.h5' model.save('my_model.h5') # удалить существующую модель del model # вернуть откомпилированную модель, идентичную исходной model = load_model('my_model.h5') Обратные вызовы для управления процессом обучения Процесс обучения можно остановить, когда показатель качества перестает улучшаться. Для этого служит следующая функция обратного вызова: keras.callbacks.EarlyStopping(monitor='val_loss', min_delta=0, patience=0, verbose=0, mode='auto') Историю потерь можно сохранить, определив такие обратные вызовы: class LossHistory(keras.callbacks.Callback): def on_train_begin(self, logs={}): self.losses = [] def on_batch_end(self, batch, logs={}): self.losses.append(logs.get('loss')) model = Sequential() model.add(Dense(10, input_dim=784, init='uniform')) model.add(Activation('softmax')) model.compile(loss='categorical_crossentropy', optimizer='rmsprop') history = LossHistory() model.fit(X_train,Y_train, batch_size=128, nb_epoch=20, verbose=0, callbacks=[history]) print history.losses Резюме 77  Использование Quiver совместно с Keras В главе 3 мы будем обсуждать сверточные сети, специально предназначенные для обработки изображений. А сейчас дадим краткий обзор приложения Quiver (см. https://github.com/jakebi), полезного для интерактивной визуализации признаков an/quiver сверточных сетей. После простой установки для его использования достаточно одной строки: pip install quiver_engine from quiver_engine import server server.launch(model) Эта команда запускает сервер визуализации на порту . Quiver позволяет визуально исследовать нейронlocalhost:5000 сеть, как показано в следующем примере: Резюме В этой главе мы обсудили, как установить Theano, TensorFlow и Keras:  на локальную машину;   в контейнер Docker;   в облако Google GCP, Amazon AWS и Microsoft Azure.  Помимо этого мы рассмотрели несколько модулей Keras и такие распространенные операции, как загрузка и сохранение архитек- 78 Глава 2. Установка Keras и описание API  тур и весов нейронных сетей, ранняя остановка, сохранение истории, контрольные точки, взаимодействие с TensorBoard и Quiver. В следующей главе мы познакомимся со сверточными сетями, фундаментальной новацией в глубоком обучении, которая успешно применяется в таких разных предметных областях, как обработка текста, видео и речи, а не только для обработки изображений, как первоначально задумывалось. 3 Глава Глубокое обучение с применением сверточных сетей В предыдущих главах мы обсуждали плотные сети, где каждый нейрон связан со всеми нейронами соседних слоев. Мы применили плотные сети к классификации рукописных цифр из набора данных MNIST. В этом контексте каждому пикселю входного изображения сопоставляется отдельный нейрон, так что всего получается 784 (28 × 28 пикселей) входных нейронов. Однако при такой стратегии игнорируется пространственная структура и связи внутри изображения. Так, следующий фрагмент кода преобразует растровые изображения всех цифр в плоский вектор, что приводит к потере информации о пространственной локализации: # X_train содержит 60000 изображений размера 28x28 --> преобразуем в # массив 60000 x 784 X_train = X_train.reshape(60000, 784) X_test = X_test.reshape(10000, 784) Сверточные сети задействуют пространственную информацию и потому хорошо подходят для классификации изображений. В них используется специальная архитектура, инспирированная данными, полученными в физиологических экспериментах со зрительной корой. Как уже отмечалось, наша зрительная система состоит из нескольких уровней коры, причем каждый последующий распознает все более крупные структуры в поступающей информации. Сначала мы видим отдельные пиксели, затем различаем в них простые геометрические формы, а затем – все более сложные элементы: предметы, лица, тела людей и животных и т. п. 80 Глава 3. Глубокое обучение с применением сверточных сетей  Сверточные сети завораживают. На протяжении краткого времени они стали революционной технологией, перевернувшей представления о возможном в таких областях, как обработка текста, видео и речи, а не только изображений. В этой главе мы рассмотрим следующие темы:  глубокие сверточные нейронные сети;   классификация изображений.  Глубокая сверточная нейронная сеть Глубокая сверточная нейронная сеть (ГСНС) состоит из большого числа слоев. Обычно в ней чередуются слои двух типов – сверточные и пулинговые. Глубина фильтра возрастает слева направо. На последних этапах обычно используется один или несколько полносвязных слоев. Карты признаков Вход Выход Свертка Свертка Субдискретизация Субдискретизация Полносвязный В основе сверточных сетей лежат три идеи:  локальное рецептивное поле;   разделяемые веса;   пулинг.  Рассмотрим их поочередно. Локальные рецептивные поля Для сохранения пространственной информации удобно представлять каждое изображение матрицей пикселей. Тогда для кодирования локальной структуры можно просто соединить подматрицу соседних входных нейронов с одним скрытым нейроном следующего слоя, который и представляет одно локальное рецептивное поле. Эта операция, называемая сверткой, и дала название типу сетей. Глубокая сверточная нейронная сеть 81  Используя перекрывающиеся подматрицы, мы сможем закодировать больше информации. Предположим, к примеру, что размер каждой подматрицы равен 5 × 5 и что эти подматрицы используются для обработки изображений размера 28 × 28 из набора MNIST. Тогда мы сумеем создать 23 × 23 нейронов локального рецептивного поля в следующем скрытом слое. Действительно, подматрицу можно сдвинуть только на 23 позиции, а затем она уйдет за границу изображения. В Keras размер одной подматрицы, называемый длиной шага (stride length), является гиперпараметром, который можно настроить в процессе конструирования сетей. Определим карту признаков при переходе от одного слоя к другому. Конечно, можно завести несколько карт признаков, которые обучаются независимо. Например, для обработки изображений из набора MINST можно начать с 28 × 28 входных нейронов, а затем организовать k карт признаков размера 23 × 23 (с шагом 5 × 5) в следующем скрытом слое. Разделяемые веса и смещения Допустим, мы хотим отойти от строкового представления пикселей и получить возможность обнаруживать один и тот же признак независимо от того, в каком месте изображения он находится. На ум сразу приходит мысль воспользоваться общим набором весов и смещений для всех нейронов в скрытых слоях. Тогда каждый слой обучится распознавать множество позиционно-независимых признаков в изображении. Если входное изображение имеет размер (256, 256) с тремя каналами в порядке (TensorFlow), то его можно представить тензором tf (256, 256, 3). Отметим, что в режиме th (Theano) индекс канала глубины равен 1, а в режиме tf (TensoFlow) – 3. В Keras, чтобы добавить сверточный слой с 32 выходами и фильтром размера 3 × 3, мы пишем: model = Sequential() model.add(Conv2D(32, (3, 3), input_shape=(256, 256, 3)) То же самое можно записать и по-другому: model = Sequential() model.add(Conv2D(32, kernel_size=3, input_shape=(256, 256, 3)) Это значит, что свертка с ядром 3 × 3 применяется к изображению размера 256 × 256 с тремя входными каналами (входными 82 Глава 3. Глубокое обучение с применением сверточных сетей  фильтрами), и в результате получается 32 выходных канала (выходных фильтра). Пример свертки приведен на следующем рисунке. Пулинговые слои Допустим, мы хотим агрегировать выход карты признаков. И в этом случае можно воспользоваться пространственной смежностью выходов, порожденных из одной карты признаков, и агрегировать значения подматрицы в одно выходное значение, которое дает сводное описание ассоциированного с данной смысла, физической областью. Max-пулинг Часто применяется max-пулинг, когда просто берется максимальный отклик в области. В Keras, чтобы определить слой maxпулинга размера 2 × 2, мы пишем model.add(MaxPooling2D(pool_size = (2, 2))) На следующем рисунке приведен пример max-пулинга: Усредненный пулинг Другой вариант – усредненный пулинг, когда берется среднее арифметическое откликов в некоторой области. Пример ГСНС – LeNet 83  В Keras реализовано еще много пулинговых слоев, их полный перечень приведен на странице . Все https://keras.io/layers/pooling/ операции пулинга сводятся к тому или иному способу агрегирования значений в заданной области. Промежуточные итоги Мы изложили основные понятия сверточных сетей. В СНС операции свертки и пулинга применяются в одном направлении (время) для звуковых и текстовых данных, в двух направлениях (ширина и высота) для изображений и в трех направлениях (ширина, высота, время) для видео. В случае изображений перемещение фильтра по входной матрице порождает карту, дающую отклики фильтра для каждого положения в пространстве. Иначе говоря, сверточная сеть состоит из нескольких собранных в стопку фильтров, которые обучаются распознавать конкретные визуальные признаки независимо от того, в каком месте изображения они находятся. В начальных слоях сети признаки простые, а затем становятся все более сложными. Пример ГСНС – LeNet Ян Лекун (Yann le Cun) предложил (см. статью Y. LeCun, Y. Bengio «Convolutional Networks for Images, Speech, and Time-Series», Brain Theory Neural Networks, vol. 3361, 1995) семейство сверточных сетей, получившее название LeNet, обученных распознаванию рукописных цифр из набора MNIST и устойчивых к простым геометрическим преобразованиям и искажению. Основная идея состоит в наличии чередующихся слоев, реализующих операции свертки и max-пулинга. Операции свертки основаны на тщательно подобранных локальных рецептивных полях с весами, разделяемыми между несколькими картами признаков. Последние слои полносвязные – как в традиционном МСП со скрытыми слоями и функцией активации softmax в выходном слое. Код LeNet в Keras Для определения сети LeNet используется модуль двумерной сверточной сети: keras.layers.convolutional.Conv2D(filters, kernel_size, padding='valid') 84 Глава 3. Глубокое обучение с применением сверточных сетей  Здесь – число сверточных ядер (например, размерность filters выхода), – одно целое число или кортеж (либо список) kernel_size из двух целых чисел, задающих ширину и высоту двумерного окна свертки (если указано одно число, то ширина и высота одинаковы), а означает, что используется дополнение. Существуpadding='same' два режима: означает, что свертка вычисляется padding='valid' только там, где фильтр целиком помещается в области входа, по- этому выход оказывается меньше входа, а – что разpadding='same' выхода такой же (same), как размер входа, для чего входная область дополняется нулями по краям. Мы также используем модуль : MaxPooling2D keras.layers.pooling.MaxPooling2D(pool_size=(2, 2), strides=(2, 2)) Здесь – кортеж из двух целых чисел, определяpool_size=(2, 2) ющих коэффициенты уменьшения изображения по вертикали и по горизонтали. Таким образом, означает, что изображение (2, 2) уменьшается вдвое в обоих направлениях. Наконец, параметр определяет шаг обработки. strides=(2, 2) Теперь перейдем к коду. Сначала импортируется ряд модулей: from keras import backend as K from keras.models import Sequential from keras.layers.convolutional import Conv2D from keras.layers.convolutional import MaxPooling2D from keras.layers.core import Activation from keras.layers.core import Flatten from keras.layers.core import Dense from keras.datasets import mnist from keras.utils import np_utils from keras.optimizers import SGD, RMSprop, Adam import numpy as np import matplotlib.pyplot as plt Затем определяется сеть LeNet: #define the ConvNet class LeNet: @staticmethod def build(input_shape, classes): model = Sequential() # CONV => RELU => POOL Первый слой – сверточный с функцией активации ReLU, за ним следует слой max-пулинга. В нашей сети будет 20 сверточных фильтров размера 5 × 5. Размер выхода такой же, как размер входа – 28 × 28. Поскольку первым элементом конвейера является модуль О силе глубокого обучения 89  цифр примерно 8 распознавались неправильно. А использование глубокой архитектуры позволило добиться прироста 7% и получить верность 99.20% – неправильно распознается лишь одна цифра из 100. Верность Верность Верность О силе глубокого обучения Чтобы лучше понять силу глубокого обучения и сверточных сетей, мы можем поставить еще один эксперимент: уменьшить размер обучающего набора и понаблюдать за снижением качества. Для этого разобьем набор из 50 000 примеров на два набора:  размер собственно обучающего набора будет последова уменьшаться и составлять 5900, 3000, 1800, 600 и 300 примеров;  остальные примеры будут входить в контрольный набор,  используемый для оценки хода обучения. Тестовый набор остается неизменным и содержит 10 000 примеров. такой конфигурации сравним только что определенную сверточную сеть глубокого обучения с первой нейронной сетью, определенной в главе 1. На следующем графике видно, что глубокая сеть всегда превосходит простую и разрыв тем больше, чем меньше обучающих примеров. При 5900 примерах верность глубокой сети рав- на 96.68% против 85.56% у простой. Но важнее, что при жалких 300 90 Глава 3. Глубокое обучение с применением сверточных сетей  примерах верность глубокой сети все еще составляет 72.44%, тогда как у простой сети она снизилась до 48.26%. Все эксперименты проводились для четырех итераций обучения. Тем самым подтверждается грандиозный прогресс, достигнутый в результате изобретения глубокого обучения. На первый взгляд, это может показаться удивительным с математической точки зрения, потому что в глубокой сети гораздо больше неизвестных (весов), а, следовательно, и экспериментальных точек вроде бы должно быть больше. Однако сверточные сети выигрывают от сохранения пространственной информации, добавления свертки, пулинга и карт признаков, а эти механизмы совершенствовались в ходе миллионолетней эволюции (ведь такая организация подсмотрена у зрительной коры головного мозга). Обзор современных результатов для набора данных MNIST опубликован на странице http://rodrigob.github.io/are_we_there_yet/ . По состоянию на январь build/classification_datasets_ results.html 2017 года лучшим достижением была частота ошибок 0.21%. Распознавание изображений из набора CIFAR-10 с помощью глубокого обучения Набор данных CIFAR-10 содержит 60 000 цветных изображений размера 32 × 32 пикселя с 3 каналами, разбитых на 10 классов. Распознавание изображений из набора CIFAR-10 ... 91  В обучающем наборе 50 000 изображений, в тестовом – 10 000. На следующем рисунке, взятом из репозитория CIFAR ( https://www. ), представлены случайно выбранные cs.toronto.edu/~kriz/cifar.html примеры из каждого класса: самолет автомобиль птица кошка олень собака лягушка лошадь корабль грузовик Задача состоит в том, чтобы распознать не предъявлявшиеся ранее изображения и отнести их к одному из 10 классов. Прежде всего импортируем ряд модулей, определим некоторые константы и загрузим набор данных: from keras.datasets import cifar10 from keras.utils import np_utils from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation, Flatten from keras.layers.convolutional import Conv2D, MaxPooling2D from keras.optimizers import SGD, Adam, RMSprop import matplotlib.pyplot as plt # набор CIFAR_10 содержит 60K изображений 32x32 с 3 каналами IMG_CHANNELS = 3 IMG_ROWS = 32 IMG_COLS = 32 # константы Повышение качества распознавания набора CIFAR-10 ... 95  Повышение качества распознавания набора CIFAR-10 путем углубления сети Один из способов повысить качество распознавания – определить более глубокую сеть с несколькими операциями свертки. В данном случае мы возьмем такую последовательность модулей: conv+conv+maxpool+dropout+conv+conv+maxpool И в конце – стандартная последовательность . dense+dropout+dense Функцией активации всегда будет ReLU. Вот как выглядит код определения новой сети: model = Sequential() model.add(Conv2D(32, (3, 3), padding='same', input_shape=(IMG_ROWS, IMG_COLS, IMG_CHANNELS))) model.add(Activation('relu')) model.add(Conv2D(32, (3, 3), padding='same')) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Conv2D(64, (3, 3), padding='same')) model.add(Activation('relu')) model.add(Conv2D(64, 3, 3)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(512)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(NB_CLASSES)) model.add(Activation('softmax')) 96 Глава 3. Глубокое обучение с применением сверточных сетей  Теперь прогоним программу. Сначала сохраним сеть, а затем выполним 40 итераций. Как показано на следующем снимке экрана, достигнута верность 76.9%. Таким образом, мы улучшили предыдущий результат на 10.5%. Для полноты картины построим еще графики зависимости верности и потери от числа итераций: Повышение качества распознавания набора CIFAR-10 ... 97  Повышение качества распознавания набора CIFAR-10 путем пополнения данных Еще один способ повысить качество – сгенерировать дополнительные обучающие изображения. Идея состоит в том, чтобы взять стандартный набор данных CIFAR и пополнить его, подвергнув изображения различным преобразованиям: вращению, параллельному переносу, масштабированию, отражению относительно горизонтальной и вертикальной оси, перестановке каналов и т. д. Приведем соответствующий код: from keras.preprocessing.image import ImageDataGenerator from keras.datasets import cifar10 import numpy as np NUM_TO_AUGMENT=5 # загрузить набор данных (X_train, y_train), (X_test, y_test) = cifar10.load_data() # пополнение print("Augmenting training set images...") datagen = ImageDataGenerator( rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, zoom_range=0.2, horizontal_flip=True, fill_mode='nearest') Аргумент – это диапазон углов в градусах (0–180), rotation_range на которые можно поворачивать изображения (случайным обра- 98 Глава 3. Глубокое обучение с применением сверточных сетей  зом). Аргументы и – диапазоны случайного width_shift height_shift параллельного переноса по горизонтали и по вертикали. Аргумент задает диапазон случайного масштабирования изобраzoom_range что случайным образом отобранную horizontal_flip половину изображений нужно отразить относительно вертикальной оси, а определяет стратегию вычисления новых пикfill_mode образующихся при повороте или параллельном переносе: xtas, ytas = [], [] for i in range(X_train.shape[0]): num_aug = 0 x = X_train[i] # (3, 32, 32) x = x.reshape((1,) + x.shape) # (1, 3, 32, 32) for x_aug in datagen.flow(x, batch_size=1, save_to_dir='preview', save_prefix='cifar', save_format='jpeg'): if num_aug >= NUM_TO_AUGMENT: break xtas.append(x_aug[0]) num_aug += 1 В результате пополнения мы получим много новых изображений, сгенерированных на основе стандартного набора CIFAR-10: Теперь посмотрим, что это нам дает. Мы генерируем новые изображения, а затем обучаем ту же самую сверточную сеть, что и раньше, на пополненном наборе данных. Эффективности ради генератор работает параллельно обучению модели. Это позволяет пополнять набор на CPU и одновременно обучать сеть на GPU. Код показан ниже: 100 Глава 3. Глубокое обучение с применением сверточных сетей  Обзор современных результатов для набора данных CIFAR-10 опубликован на странице http://rodrigob.github.io/are_we_there_yet/ . По состоянию на январь build/classification_datasets_ results.html 2017 года лучшим достижением была верность 96.53%. Предсказание на основе результатов обучения на наборе CIFAR-10 Пусть теперь мы хотим использовать обученную на наборе CIFAR-10 модель для массовой обработки изображений. Поскольку мы сохранили модель вместе с весами, то обучать ее каждый раз не нужно. import numpy as np import scipy.misc from keras.models import model_from_json from keras.optimizers import SGD # загрузить модель model_architecture = 'cifar10_architecture.json' model_weights = 'cifar10_weights.h5' model = model_from_json(open(model_architecture).read()) model.load_weights(model_weights) # загрузить изображения img_names = ['cat-standing.jpg', 'dog.jpg'] imgs = [np.transpose(scipy.misc.imresize(scipy.misc.imread(img_name), (32,32)), (1, 0, 2)).astype('float32') for img_name in img_names] imgs = np.array(imgs) / 255 # обучить optim = SGD() model.compile(loss='categorical_crossentropy', optimizer=optim, metrics=['accuracy']) # предсказать predictions = model.predict_classes(imgs) print(predictions) Давайте посмотрим, что предсказывает модель для изображений Как и ожидалось, мы получаем категории 3 (кошка) и 5 (собака): Очень глубокие сверточные сети для распознавания ... 101  Очень глубокие сверточные сети для распознавания больших изображений В 2014 году был внесен интересный вклад в распознавание изображений (см. K. Simonyan, A. Zisserman «Very Deep Convolutional Networks for Large-Scale Image Recognition», 2014). В этой работе показано, что, увеличив число весовых слоев до 16–19, можно добиться значительного улучшения по сравнению с предшествующими конфигурациями. В одной из рассматриваемых моделей (D или VGG-16) было 16 слоев. Для обучения модели на наборе данных ImageNet ILSVRC-2012 ( ) http://image-net.org/challenges/LSVRC/2012/ была написана программа на Java с использованием библиотеки Caffe ( ). Этот набор содержит изоhttp://caffe.berkeleyvision.org/ из 1000 классов, разбитые на три набора: обучающий (1.3 миллиона изображений), контрольный (50 000 изображений) и тестовый (100 000 изображений). Все изображения трехканальные размера 224 × 224. Для этой модели ошибка непопадания в первые 5 классов составила 7.5% на наборе ILSVRC-2012-val и 7.4% на наборе ILSVRC-2012-test. Приведем цитату с сайта ImageNet: Цель соревнования – оценить содержание фотографий для целей поиска и автоматического аннотирования с применением подмножества большого размеченного вручную набора ImageNet (10 миллионов помеченных изображений объектов из 10 с лишним тысяч категорий) для обучения. Тестовые изображения не содержат никаких аннотаций – ни сегментации, ни меток, а алгоритм должен вывести метки изображенных объектов. Веса, полученные в результате обучения модели, реализованной на Caffe, были преобразованы к виду, понятному Keras (см. https:// ), так что их можно gist.github.com/baraldilorenzo/07d7802847aaad0a35d3 загрузить в модель, которая ниже определена так же, как в оригинальной статье: from keras.models import Sequential from keras.layers.core import Flatten, Dense, Dropout from keras.layers.convolutional import Conv2D, MaxPooling2D, ZeroPadding2D from keras.optimizers import SGD import cv2, numpy as np # определить сеть VGG16 102 Глава 3. Глубокое обучение с применением сверточных сетей  def VGG_16(weights_path=None): model = Sequential() model.add(ZeroPadding2D((1,1),input_shape=(3,224,224))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(128, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(128, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(256, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(256, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(256, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(512, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(512, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(512, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(512, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(512, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Conv2D(512, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(Flatten()) # верхние слои сети VGG model.add(Dense(4096, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(4096, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(1000, activation='softmax')) if weights_path: model.load_weights(weights_path) return model Распознавание кошек с помощью сети VGG-16 Теперь протестируем сеть на изображении . Очень глубокие сверточные сети для распознавания ... 103  im = cv2.resize(cv2.imread('cat.jpg'), (224, 224)).astype(np.float32) im = im.transpose((2,0,1)) im = np.expand_dims(im, axis=0) # Тестировать предобученную модель model = VGG_16('/Users/gulli/Keras/codeBook/code/data/vgg16_weights.h5') optimizer = SGD() model.compile(optimizer=optimizer, loss='categorical_crossentropy') out = model.predict(im) print np.argmax(out) Программа возвращает класс 285 – кошку породы сфинкс (см. ): https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a Использование встроенного в Keras модуля VGG-16 Приложения Keras – это предварительно построенные и обученные глубокие модели. Веса автоматически загружаются при создании экземпляра модели и хранятся в каталоге . ~/.keras/models/ Использовать встроенный код очень просто: from keras.models import Model from keras.preprocessing import image from keras.optimizers import SGD from keras.applications.vgg16 import VGG16 import matplotlib.pyplot as plt import numpy as np import cv2 # готовая модель с предобученными на наборе imagenet весами model = VGG16(weights='imagenet', include_top=True) sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True) model.compile(optimizer=sgd, loss='categorical_crossentropy') # сделать размер таким же, как у изображений, на которых обучалась модель VGG16 im = cv2.resize(cv2.imread('steam-locomotive.jpg'), (224, 224)) im = np.expand_dims(im, axis=0) # предсказание out = model.predict(im) plt.plot(out.ravel()) 104 Глава 3. Глубокое обучение с применением сверточных сетей  plt.show() print np.argmax(out) # должна быть напечатана категория 820 – паровоз Теперь возьмем изображение паровоза: На таком ездил мой дедушка. Выполнив программу, мы получим категорию 820, которой в наборе ImageNet обозначается паВажно также, что вероятность всех ровоз. остальных классов очень мала, как видно из следующего графика: В заключение этого раздела отметим, что VGG-16 – лишь одна из моделей, встроенных в Keras. Полный перечень предобученных моделей приведен на странице . https://keras.io/applications/ Использование готовых моделей глубокого обучения для выделения признаков Модель VGG-16 и вообще любую ГСНС можно очень просто использовать для выделения признаков. Ниже показана реализация этой идеи для выделения признаков в конкретном слое. from keras.applications.vgg16 import VGG16 from keras.models import Model from keras.preprocessing import image from keras.applications.vgg16 import preprocess_input import numpy as np # предварительно построенная и обученная модель глубокого обучения VGG16 base_model = VGG16(weights='imagenet', include_top=True) Очень глубокие сверточные сети для распознавания ... 105  for i, layer in enumerate(base_model.layers): print (i, layer.name, layer.output_shape) # выделить признаки из слоя block4_pool model = Model(input=base_model.input, output=base_model.get_layer('block4_pool').output) img_path = 'cat.jpg' img = image.load_img(img_path, target_size=(224, 224)) x = image.img_to_array(img) x = np.expand_dims(x, axis=0) x = preprocess_input(x) # получить признаки features = model.predict(x) Возникает вопрос, а зачем может понадобиться выделять признаки из промежуточного слоя ГСНС? Дело в том, что если сеть обучилась классифицировать изображения, то каждый ее слой обучился находить признаки, необходимые для окончательной классификации. Нижние слои идентифицируют такие низкоуровневые признаки, как цвета и границы, а верхние составляют из них признаки более высокого уровня, например, геометрические фигуры или объекты. Следовательно, промежуточный слой способен выделить из изображения важные признаки, которые могут оказаться полезны для других видов классификации. Преимуществ тут несколько. Во-первых, можно опереться на находящиеся в открытом доступе крупномасштабные обученные модели и перенести результаты их обучения на другие предметные области. Во-вторых, можно сэкономить время на дорогостоящем обучении большой модели. В-третьих, можно получить разумное решение даже тогда, когда для некоторой предметной области недостаточно обучающих примеров. Кроме того, мы получаем хорошее начальное приближение для решения имеющейся задачи вместо случайной гипотезы. Очень глубокая сеть inception-v3, применяемая для переноса обучения Перенос обучения – весьма эффективная техника глубокого обучения, имеющая приложения в разных областях. Идея очень проста, для ее объяснения воспользуемся аналогией. Предположим, вы хотите изучить новый язык, скажем, испанский. Тогда полезно начать с того, что вы уже знаете о каком-то другом языке, например, английском. 106 Глава 3. Глубокое обучение с применением сверточных сетей  В русле этой идеи специалисты по компьютерному зрению активно используют предобученные СНС для порождения представлений в новых задачах, где набор данных не настолько велик, чтобы обучить СНС с нуля. Еще одна часто встречающаяся тактика – взять предобученную на наборе ImageNet сеть и настроить ее под новую задачу. Inception-v3 – очень глубокая сверточная сеть, разработанная Google. Keras реализует полную сеть, показанную на рисунке ниже, и модель, предобученная на наборе ImageNet, включена в дистрибутив. По умолчанию в этой модели используются трехканальные изображения размера 299 × 299: В основу этого схематического примера положено приложение, имеющееся на странице . Предполагаhttps://keras.io/applications/ что есть обучающий набор данных D в предметной области, отличной от ImageNet. В D имеется 1024 входных признака и 200 выходных категорий. Рассмотрим следующий фрагмент кода: from keras.applications.inception_v3 import InceptionV3 from keras.preprocessing import image from keras.models import Model from keras.layers import Dense, GlobalAveragePooling2D from keras import backend as K # создать базовую предобученную модель base_model = InceptionV3(weights='imagenet', include_top=False) Мы используем обученную сеть inception-v3 и не включаем верхние слои, потому что хотим адаптировать ее к D. В нашей модели выходным будет плотный слой softmax-классификации с 200 классами. Для преобразования входных данных к форме, пригодной для этого плотного слоя, применяется модуль . GlobalAveragePooling2D В действительности тензор имеет форму (samples, base_model.output channels, rows, cols), если , или форму (samples, rows, dim_ordering="th" 108 Глава 3. Глубокое обучение с применением сверточных сетей  for in layer True model.layers[172:]: layer.trainable = Затем модель перекомпилируется, чтобы изменения вступили в силу: # используем СГС с малой скоростью обучения from keras.optimizers import SGD model.compile(optimizer=SGD(lr=0.0001, momentum=0.9), loss='categorical_crossentropy') # снова обучаем модель (на этот раз настраиваем 2 верхних слоя inception) # и верхние слои Dense model.fit_generator(...) Теперь мы имеем новую глубокую сеть, в которой повторно используется часть стандартной сети Inception-v3, но обучена она на данных из другой предметной области посредством переноса обучения. Разумеется, для достижения приемлемой верности можно настроить много параметров. Но в качестве отправной точки мы теперь используем очень большую предобученную сеть, и, следовательно, можем отказаться от полного обучения на наших машинах и воспользоваться тем, что уже есть в Keras. Резюме В этой главе мы научились использовать глубокие сверточные сети для распознавания рукописных цифр из набора MNIST с высокой верностью. Затем мы воспользовались набором данных CIFAR 10, чтобы построить глубокий классификатор с 10 категориями, и набором ImageNet для построения точного классификатора с 1000 категорий. Кроме того, мы узнали, как можно использовать большие предобученные сети типа VGG16 и очень глубокие сети типа InceptionV3. В заключение мы обсудили технику переноса обучения, позволяющую адаптировать готовые модели, обученные на больших наборах данных, к новым предметным областям. В следующей главе мы познакомимся с применением порождающих состязательных сетей к задаче синтеза данных, похожих на порождаемые людьми. Мы также представим глубокую нейронную сеть WaveNet для высококачественного воспроизведения человеческого голоса и звучания музыкальных инструментов. 4 Глава Порождающие состязательные сети и WaveNet порождающие состязательные сеВ этой главе мы обсудим ти (ПСС) (generative adversarial network – GAN) и сеть WaveNet. Ян Лекун, один из отцов глубокого обучения, считает ПСС самой интересной идеей за последние 10 лет развития МО ( https://www. quora.com/What-are-some-recent-and-potentially-upcoming-breakthroughs). ПСС способны обучаться порождению синтеin-deep-learning данных, которые выглядят в точности как настоящие. Например, компьютер можно научить рисовать и создавать реалистичные изображения. Идея была предложена Яном Гудфеллоу (см. I. Goodfellow «NIPS 2016 Tutorial: Generative Adversarial Networks», 2016), который работал в Монреальском университете, в компании Google Brain и, в последнее время, в OpenAI ( https:// ). WaveNet – глубокая порождающая сеть, предложенная openai.com/ компанией Google DeepMind для обучения компьютеров высококачественному воспроизведению человеческого голоса и звучания музыкальных инструментов. Мы обсудим следующие темы:  что такое ПСС;   глубокие сверточные ПСС;   приложения ПСС.  Что такое ПСС? Основная идея ПСС аналогична подделке произведений искусства, т. е. созданию работ ( ), ошибочно https://en.wikipedia.org/wiki/Art 110 Глава 4. Порождающие состязательные сети и WaveNet  приписываемых другим, обычно более известным, авторам. ПСС обучает две нейронные сети одновременно, как показано на рисунке ниже. Генератор G(Z) порождает подделку, а дискриминатор D(Y) судит о том, насколько репродукция реалистична, основываясь на наблюдениях аутентичных произведений и копий. D(Y) принимает вход (например, изображение) и выражает свое суждение Y о его подлинности – в общем случае значение, близкое к 0, означает подлинный, а близкое к единице – подделка. G(Z) принимает на входе случайный шум Z и обучается обманывать D, заставляя его думать, что результат работы G(Z) – подлинное произведение. Таким образом, цель обучения дискриминатора – максимизировать D(Y) для всех изображений из истинного распределения данных и минимизировать для изображений, не выбранных из истинного распределения. Следовательно, G и D ведут себя как противники в некоторой игре, отсюда и название состязательное обучение. Отметим, что и обучаются попеременно, а в качестве целевой функG D ции выступает функция потерь, оптимизируемая методом градиентного спуска. Порождающая модель обучается подделывать, а дискриминантная распознавать подделки. Дискриминантная сеть (обычно стандартная сверточная нейронная сеть) пытается классифицировать изображение как настоящее или сгенерированное. Важная новая идея – обратное распространение через дискриминатор и генератор с целью корректировать параметры генератора таким образом, чтобы генератор мог обучиться, как успешнее обманывать дискриминатор. В конечном итоге генератор научится порождать поддельные изображения, неотличимые от настоящих. Случайный Порождающая модель шум (фальсификатор) подделка Насколько Дискриминантная модель похоже на (арбитр) подлинник? подлинник от ПСС требуется найти точку равновесия в игре двух игроков. Чтобы обучение оказалось эффективным, необходимо, чтобы обновление, в результате которого один игрок опуска- Что такое ПСС? 111  ется вниз, одновременно приводило к опусканию другого игрока. Задумайтесь об этом! Если фальсификатор научится обманывать арбитра в каждом случае, то самому фальсификатору больше нечему учиться. Иногда оба игрока достигают равновесия, но это не гарантируется, и игра может продолжаться долго. На следующем графике показан пример обучения обоих игроков. потеря дискриминатора потеря генератора Некоторые приложения ПСС Мы видели, что генератор обучается подделывать данные. Это значит, что он обучается создавать новые синтетические данные, которые выглядят так, будто созданы человеком. Прежде чем переходить к коду, хочу продемонстрировать результаты из недавней статьи Han Zhang, Tao Xu, Hongsheng Li, Shaoting Zhang, Xiaolei Huang, Xiaogang Wang, Dimitris Metaxas «StackGAN: Text to Photo-Realistic Image Synthesis with Stacked Generative Adversarial Networks» (код доступен по адресу https://github.com/hanzhanggit/ ). StackGAN 112 Глава 4. Порождающие состязательные сети и WaveNet  Здесь ПСС используется для синтеза изображений по текстовому описанию. Результаты впечатляют. В первом столбце мы видим реальные изображения из тестового набора, а во всех остальных – изображения, сгенерированные на стадии I и II сети StackGAN. Дополнительные примеры можно найти на YouTube ( https://www. ): youtube.com/watch?v=SuRyL5vhCIM&feature=youtu.be Теперь посмотрим, как можно научить ПСС подделывать набор данных MNIST. В этом случае в качестве генератора и дискриминатора ПСС используются сверточные сети (см. A. Radford, L. Metz, and S. Chintala «Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks», arXiv: 1511.06434, 2015). В начале генератор порождает нечто неразборчивое, но после нескольких итераций синтетические цифры становятся все более отчетливыми. На следующем рисунке панели упорядочены по номеру итерации и, как видите, качество постепенно повышается. 114 Глава 4. Порождающие состязательные сети и WaveNet  улыбающаяся нейтральная нейтральный улыбающийся женщина женщина мужчина мужчина мужчина мужчина женщина женщина в очках в очках без очков без очков Глубокие сверточные порождающие состязательные сети Глубокие сверточные порождающие состязательные сети (ГСПСС, англ. DCGAN) впервые описаны в статье A. Radford, L. Metz, and S. Chintala «Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks», arXiv: 1511.06434, 2015. В генераторе используется 100-мерное пространство с равномерным распределением Z, которое проецируется на пространство меньшей размерности с помощью последовательности парных операций свертки. Пример показан на рисунке ниже. Глубокие сверточные порождающие состязательные сети 115  Рис. 1. Генератор ГСПСС используется для моделирования сцены LSUN. 100-мерное равномерное распределение Z проецируется на сверточное представление меньшей размерности с многими картами признаков. Затем последовательность сверток с дробным шагом (в некоторых недавних работах они ошибочно называются обращенными свертками) преобразует это высокоуровневое × представление в изображение размера 64 64 пикселя. Обращаем внимание на отсутствие полносвязных и пулинговых слоев. Генератор ГСПСС можно описать следующим кодом на Keras; имеется также другая реализация по адресу https://github.com/ . jacobgil/keras-dcgan def generator_model(): model = Sequential() model.add(Dense(input_dim=100, output_dim=1024)) model.add(Activation('tanh')) model.add(Dense(128*7*7)) model.add(BatchNormalization()) model.add(Activation('tanh')) model.add(Reshape((128, 7, 7), input_shape=(128*7*7,))) model.add(UpSampling2D(size=(2, 2))) model.add(Convolution2D(64, 5, 5, border_mode='same')) model.add(Activation('tanh')) model.add(UpSampling2D(size=(2, 2))) model.add(Convolution2D(1, 5, 5, border_mode='same')) model.add(Activation('tanh')) return model Отметим, что здесь используется синтаксис Keras 1.x. Но благодаря унаследованным интерфейсам код будет работать и в версии Keras 2.0. Правда, выдаются предупреждения, показанные на следующем рисунке: 118 Глава 4. Порождающие состязательные сети и WaveNet  Применение Keras adversarial для создания ПСС, подделывающей MNIST Keras adversarial ( ) – наhttps://github.com/bstriner/keras-adversarial на Python пакет с открытым исходным кодом, предназначенный для построения ПСС. Его автор – Бен Страйнер (Ben Striner) ( и https://github.com/bstriner https://github.com/bstriner/ ). Поскольку версия Keras 2.0 keras-adversarial/blob/master/LICENSE.txt появилась совсем недавно, я рекомендую скачать самую последнюю версию этого пакета: git clone --depth=50 --branch=master https://github.com/bstriner/keras-adversarial.git И установить его: python setup.py install Совместимость с Keras 2.0 обсуждается по адресу https://github. . com/bstriner/keras-adversarial/issues/11 Если генератор G и дискриминатор D основаны на одной и той же модели M, то их можно объединить в состязательную модель; она получает тот же вход, что и M, но цели и показатели качества различны для и В библиотеке определена следующая функция G D. создания модели: adversarial_model = AdversarialModel(base_model=M, player_params=[generator.trainable_weights, discriminator.trainable_weights], player_names=["generator", "discriminator"]) Если генератор и дискриминатор основаны на разных модеG то можно воспользоваться такой функцией: adversarial_model = AdversarialModel(player_models=[gan_g, gan_d], player_params=[generator.trainable_weights, discriminator.trainable_weights], player_names=["generator", "discriminator"]) Рассмотрим пример вычислений для MNIST: import matplotlib as mpl # Эта строка позволяет использовать mpl без определения DISPLAY mpl.use('Agg') Ниже рассматривается открытый исходный код ( https://github. com/bstriner/keras-adversarial/blob/master/examples/example_gan_convolu). В нем используется синтаксис Keras 1.x, но код работает tional.py Применение Keras adversarial для создания ПСС ... 119  и с Keras 2.x благодаря набору вспомогательных функций в файле . Содержимое файла приведено в приложении, legacy.py legacy.py а также по адресу https://github.com/bstriner/keras-adversarial/blob/ . master/keras_adversarial/legacy.py Сначала импортируется ряд модулей. Мы уже встречались со всеми, кроме LeakyReLU, специальной версии ReLU, которая допускает малый градиент, когда нейрон не активен. Экспериментально показано, что в ряде случаев функция LeakyReLU может улучшить каче«Empirical ПСС (см. B. Xu, N. Wang, T. Chen, M. Li Evaluation of Rectified Activations in Convolutional Network», arXiv:1505.00853, 2014). from keras.layers import Dense, Reshape, Flatten, Dropout, LeakyReLU, Input, Activation, BatchNormalization from keras.models import Sequential, Model from keras.layers.convolutional import Convolution2D, UpSampling2D from keras.optimizers import Adam from keras.regularizers import l1, l1l2 from keras.datasets import mnist import pandas as pd import numpy as np Затем импортируются специальные модули для ПСС: from keras_adversarial import AdversarialModel, ImageGridCallback, simple_gan, gan_targets from keras_adversarial import AdversarialOptimizerSimultaneous, normal_latent_sampling, AdversarialOptimizerAlternating from image_utils import dim_ordering_fix, dim_ordering_input, dim_ordering_reshape, dim_ordering_unfix Состязательные модели обучаются в ходе игры с несколькими игроками. Если дана базовая модель с n целями и k игроками, то создается модель с n*k целями, в которой каждый игрок оптимизирует потерю на своих целях. Кроме того, функция порожsimple_gan ПСС с заданными целями . Отметим, что в библиоgan_targets цели для генератора и дискриминатора противоположны, это стандартная практика для ПСС: def gan_targets(n): """ Стандартные цели обучения [generator_fake, generator_real, discriminator_fake, discriminator_real] = [1, 0, 0, 1] :param n: число примеров :return: массив целей """ generator_fake = np.ones((n, 1)) 124 Глава 4. Порождающие состязательные сети и WaveNet  После 5–6 итераций мы уже имеем искусственные изображения приемлемого качества, т. е. компьютер обучился порождать рукописные цифры: Применение Keras adversarial для создания ПСС, подделывающей CIFAR Теперь применим ПСС для подделывания набора данных CIFAR-10 и получения синтетических изображений, похожих на настоящие. Исходный код находится по адресу https://github.com/bstriner/keras). Как и раньше, adversarial/blob/master/examples/ example_gan_cifar10.py используется синтаксис Keras 1.x, но благодаря вспомогательным Применение Keras adversarial для создания ПСС ... 125  функциям из файла legacy.py код работает и для Keras 2.x. Сначала импортируется ряд пакетов: import matplotlib as mpl # Эта строка позволяет использовать mpl без определения DISPLAY mpl.use('Agg') import pandas as pd import numpy as np import os from keras.layers import Dense, Reshape, Flatten, Dropout, LeakyReLU, Activation, BatchNormalization, SpatialDropout2D from keras.layers.convolutional import Convolution2D, UpSampling2D, MaxPooling2D, AveragePooling2D from keras.models import Sequential, Model from keras.optimizers import Adam from keras.callbacks import TensorBoard from keras.regularizers import l1l2 from keras_adversarial import AdversarialModel, ImageGridCallback, simple_gan, gan_targets from keras_adversarial import AdversarialOptimizerSimultaneous, normal_latent_sampling, fix_names import keras.backend as K from cifar10_utils import cifar10_data from image_utils import dim_ordering_fix, dim_ordering_unfix, dim_ordering_shape Затем определяется генератор, в котором используется комбинация сверток с регуляризацией по норме L1 и L2, пакетной нормировкой и повышающей передискретизацией. Параметр axis=1 говорит, по какому измерению тензора производить нормировку сначала, а означает попризнаковую нормировку. Эта конmode=0 сеть стала результатом многочисленных экспериментов, но по сути дела она по-прежнему является последовательностью операций двумерной свертки и повышающей передискретизации, в начале которой используется модуль , а в конце – функция Dense активации . Кроме того, во всех свертках применяется функsigmoid активации и пакетная нормировка : LeakyReLU BatchNormalization def model_generator(): model = Sequential() nch = 256 reg = lambda: l1l2(l1=1e-7, l2=1e-7) h = 5 model.add(Dense(input_dim=100, output_dim=nch * 4 * 4, W_regularizer=reg())) model.add(BatchNormalization(mode=0)) model.add(Reshape(dim_ordering_shape((nch, 4, 4)))) model.add(Convolution2D(nch/2, h, h, border_mode='same', W_regularizer=reg())) 132 Глава 4. Порождающие состязательные сети и WaveNet  Ниже мы видим настоящие изображения из набора CIFAR-10 справа и подделки слева: WaveNet – порождающая модель для обучения генерации звука WaveNet – глубокая порождающая модель для генерации звуковых сигналов. Эта прорывная технология была изобретена компанией Google DeepMind ( https://deepmind.com/blog/wavenet-generative-model) для обучения пользователей тому, как говорить с комraw-audio/ Результаты поистине впечатляют, вы можете найти в сети примеры того, как компьютер говорит голосами знаменитостей, например, Мэтта Деймона. Спрашивается, почему синтез WaveNet – порождающая модель для обучения генерации звука 133  аудиосигналов – такая трудная задача? Слышимый нами цифровой звук имеет частоту 16 000 отсчетов в секунду (иногда 48 000 и даже больше), и построить прогностическую модель, которая обучается воспроизводить отсчет на основе всех предыдущих – очень непрос т ое дело. Тем не менее эксперименты показывают, что WaveNet улучшила качество систем речевого воспроизведения текста (text-to-speech, TTS), уменьшив различие с человеческим голосом на 50 % для американского варианта английского языка и мандаринского диалекта китайского. Более того, DeepMind доказала, что WaveNet можно также использовать для генерации звучания музыкальных инструментов, в частности фортепьяно. Теперь пора дать несколько определений. TTS-системы обычно делят на два класса:  Компиляционный синтез. Каждый фрагмент произнесен речи сначала запоминается, а затем восстанавливается, когда нужно воспроизвести голос. Однако этот подход не масштабируется, потому что воспроизвести возможно только ранее запомненные фрагменты и нельзя воспроизвести новые голоса или другие типы звучания, для которых нет запомненных фрагментов.  Параметрический синтез. В этом случае создается модель  для хранения всех характерных признаков синтезируемого аудио. До появления WaveNet звучание, генерируемое параметрическими TTS-системами, было менее естественным, чем у компиляционных. WaveNet удалось улучшить качество за счет прямого моделирования порождения звука вместо применения промежуточных алгоритмов обработки сигналов. В принципе WaveNet можно рассматривать как стек одномерных сверточных слоев (с двумерной сверткой мы познакомились в главе 3) с постоянным шагом 1 и без слоев пулинга. Отметим, что вход и выход по построению имеют одинаковый размер, по- этому сверточная сеть прекрасно подходит для моделирования таких последовательных данных, как звуковые сигналы. Однако было показано, что для достижения большого размера рецептивного поля выходного нейрона (напомним, что рецептивным полем нейрона называется множество нейронов предыдущего слоя, от которых данный нейрон получает входные сигналы) необходимо либо иметь много больших фильтров, либо чрезмерно Резюме 141  convolution1d_16[0][0] convolution1d_18[0][0] convolution1d_20[0][0] activation_1 (Activation) (None, 1152, 256) 0 merge_11[0][0] convolution1d_21 (Convolution1D) (None, 1152, 256) 65792 activation_1[0][0] activation_2 (Activation) (None, 1152, 256) 0 convolution1d_21[0][0] convolution1d_22 (Convolution1D) (None, 1152, 256) 65792 activation_2[0][0] output_softmax (Activation) (None, 1152, 256) 0 convolution1d_22[0][0] Total params: 4,129,536 Trainable params: 4,129,536 Non-trainable params: 0 DeepMind обучала сеть на наборах данных, содержащих речь нескольких людей, это заметно улучшило способность обучаться разделяемому представлению языков и интонаций, а, значит, приблизило результаты к естественному звучанию речи. В Интернете ( ) имеется https://deepmind.com/blog/wavenet-generative-model-raw-audio/ потрясающая подборка примеров синтезированной речи, и интересно отметить, что качество звучания повышается, когда WaveNet видит не только звуковые сигналы, но дополнительный текст, преобразованный в последовательность лингвистических и фонетических признаков. Мне больше всего нравятся примеры, в которых одно и то же предложение произносится с различной интонацией. И конечно, завораживает музыка, созданная и исполняемая сетью WaveNet. Послушайте, не пожалеете! Резюме В этой главе мы обсудили порождающие сверточные сети (ПСС). Типичная ПСС состоит из двух сетей: одна обучается порождать синтетические данные, похожие на подлинные, а другая – отличать подлинные данные от поддельных. Обе сети соревнуются друг с другом и тем самым способствуют взаимному совершен- 142 Глава 4. Порождающие состязательные сети и WaveNet  ствованию. Мы рассмотрели исходный код сетей, обучающихся подделывать изображения из наборов MNIST и CIFAR-10. Кроме того, мы обсудили глубокую порождающую сеть WaveNet, разработанную компанией Google DeepMind для обучения компьютеров высококачественному воспроизведению человеческого голоса и звучания музыкальных инструментов. WaveNet непосредственно порождает звуковые сигналы, применяя параметрический синтез речи на основе сверточных сетей с пропусками. Сверточная сеть с пропусками – это специальный вид сверточных сетей, в которых сверточные фильтры имеют дырки, что позволяет рецептивному полю экспоненциально расти с увеличением глубины и тем самым охватывать тысячи временных шагов аудио. DeepMind показала, что сеть WaveNet можно использовать для синтеза человеческого голоса и звучания музыкальных инструментов с качеством, значительно превышающим предыдущие достижения. В следующей главе мы обсудим погружения слов – набор методов глубокого обучения для обнаружения связей между словами и группировки похожих слов. 5 Глава Погружения слов В википедии погружение, или векторное представление слов (word embedding) определяется как общее название различных методов языкового моделирования и обучения признаков, применяемых обработке естественных языков в (ОЕЯ, англ. NLP), когда слова или фразы из словаря отображаются на векторы вещественных чисел. Погружение слов – это способ преобразовать текстовое представление слов в числовые векторы, допускающие анализ стандартными алгоритмами машинного обучения, принимающими на входе числа. В главе 1 мы уже встречались с одним видом погружения слов – унитарным кодированием. Это самый простой подход к погружению. Напомним, что унитарным кодом слова будет вектор, число элементов которого равно размеру словаря, такой, что элемент, соответствующий данному слову, равен 1, а все остальные 0. Основная проблема унитарного кодирования в том, что нет никакого способа представить сходство слов. В любом заданном корпусе текстов мы ожидаем, что между словами «кошка» и «собака» или «нож» и «вилка» есть какое-то сходство. Сходство векторов вычисляется с помощью скалярного произведения, т. е. суммы произведений соответственных элементов. В случае унитарного кодирования скалярное произведение любых двух слов равно нулю. Для преодоления ограничений унитарного кодирования соинформационного поиска общество ОЕЯ заимствовало из (ИП) идею векторизации текста с использованием документа в качестве контекста. Здесь стоит отметить такие подходы, как TF-IDF ( ), латентно-семантиhttps://en.wikipedia.org/wiki/Tf%E2%80%93idf анализ (ЛСА) ( https://en.wikipedia.org/wiki/Latent_semantic_ ) и тематическое моделирование ( analysis https://en.wikipedia.org/ ). Но эти представления улавливают несколько wiki/Topic_model иную, документо-центрическую, идею семантического сходства. 144 Глава 5. Погружения слов  Серьезная разработка методов погружения слов началась в 2000 году. Погружение слов отличается от предшествующих методов, применяемых в ИП, тем, что слова используются как собственный контекст, что приводит к более естественной форме семантического сходства с точки зрения понимания человеком. В настоящее время погружение слов – общепринятая техника векторизации текста во всех задачах ОЕЯ: классификация текстов, кластеризация документов, частеречная разметка, распознавание именованных сущностей, анализ эмоциональной окраски и т. п. В этой главе мы рассмотрим две формы погружения слов, GloVe и word2vec, известные под общим названием «распределенное представление слов». Эти представления оказались более эффективными и потому широко распространены в среде специалистов по глубокому обучению и ОЕЯ. Мы также узнаем о способах порождения собственных погружений в программе на Keras, а равно о том, как использовать и настраивать предобученные модели на основе word2vec и GloVe. Будут рассмотрены следующие темы:  построение различных распределенных представлений  слов в контексте;  построение моделей на основе погружений для решения та-  ких задач ОЕЯ, как грамматический разбор предложения и анализ эмоциональной окраски. Распределенные представления Распределенное представление – это попытка уловить смысл слова путем рассмотрения его связей с другими словами в контексте. Эта идея сформулирована в следующем высказывании Дж. Р. Фирта (J. R. Firth) (см. статью Andrew M. Dai, Christopher Olah, Quoc V. Le «Document Embedding with Paragraph Vectors», arXiv:1507.07998, 2015), лингвиста, который первым выдвинул ее: Мы узнаем слово по компании, с которой оно дружит. Рассмотрим такие два предложения: Париж – столица Франции. Берлин – столица Германии. Даже если вы совсем не знаете географию (или русский язык), все равно нетрудно сообразить, что пары слов (Париж, Берлин) и word2vec 145  (Франция, Германия) как-то связаны и что между соответственными словами связи одинаковы, т. е. Париж : Франция :: Берлин : Германия Следовательно, задача распределенного представления – найти такую общую функцию преобразования слова в соответствуюφ ему вектор, что справедливы соотношения следующего вида: («Париж») – («Франция») ≈ («Берлин») – («Германия») φ φ φ φ Иными словами, цель распределенного представления – преобразовать слова в векторы, так чтобы сходство векторов коррелировало с семантическим сходством слов. В следующих разделах мы рассмотрим два наиболее известных погружения слов: word2vec и GloVe. word2vec Группа моделей word2vec была разработана в 2013 году группой исследователей Google под руководством Томаша Миколова (Tomas Mikolov). Модели обучаются без учителя на большом корпусе текстов и порождают векторное пространство слов. Размерность пространства погружения word2vec обычно меньше размерности пространства погружения для унитарного кодирования, которая равна размеру словаря. Кроме того, это пространство погружения плотнее разреженного погружения при унитарном кодировании. Существуют две архитектуры word2vec:  непрерывный мешок слов (Continuous Bag Of Words, CBOW);   skip-граммы.  В архитектуре CBOW модель предсказывает текущее слово, если известно окно окружающих его слов. Кроме того, порядок контекстных слов не влияет на предсказание (это допущение модели мешка слов). В архитектуре skip-грамм модель предсказывает окружающие слова по известному центральному слову. Согласно заявлению авторов, CBOW быстрее, но skip-граммы лучше предсказывают редкие слова. Интересно отметить, что хотя word2vec создает погружения, используемые в моделях глубокого обучения ОЕЯ, оба варианта word2vec, которые мы будем обсуждать и которые снискали наибольший успех и признание, являются мелкими нейронными сетями. 146 Глава 5. Погружения слов  Модель skip-грамм Модель skip-грамм обучается предсказывать окружающие слова по известному центральному. Чтобы понять, как она работает, рассмотрим такое предложение: I love green eggs and ham. (Я люблю зеленые яйца и ветчину) 1 В предположении, что размер окна равен 3, предложение можно разложить на такие пары (контекст, слово): ([I, green], love) ([love, eggs], green) ([green, and], eggs) ... Поскольку модель skip-грамм предсказывает контекстное слово по центральному, мы можем преобразовать этот набор данных в набор пар (вход, выход). То есть, зная входное слово, мы ожидаем, что модель skip-грамм предскажет соответствующее выходное: (love, (love, (green, (green, (eggs, (eggs, ... I), green), love), eggs), green), and), Мы можем также сгенерировать дополнительные отрицательные примеры, объединяя в пару каждое входное слово со случайным словом из словаря, например: (love, Sam), (love, zebra), (green, thing), ... Наконец, мы генерируем положительные и отрицательные примеры для классификатора: ((love, I), 1), ((love, green), 1), ..., ((love, Sam), 0), ((love, zebra), 0), ... Теперь можно обучить классификатор, который принимает вектор слов и контекстный вектор и предсказывает 0 или 1 в зависимости от того, какой пример видит: положительный или отрицательный. Результатом обучения сети являются веса слоя погружения слов (серый блок на рисунке ниже): 1 – – «Green eggs and ham» детская сказка, написанная доктором Сьюзом. Прим. перев. word2vec 147  слово контекст Скалярное произведение Метка Опишем, как строится модель skip-грамм в Keras. Предположим, что размер словаря равен 5000, размер выходного пространства погружения 300, размер окна 1. Последнее означает, что контекст состоит из предыдущего и следующего слова. Сначала импортируем нужные модули и инициализируем переменные: from keras.layers import Merge from keras.layers.core import Dense, Reshape from keras.layers.embeddings import Embedding from keras.models import Sequential vocab_size = 5000 embed_size = 300 Затем создадим последовательную модель слова. Входом модели являются идентификаторы слов в словаре. Весам погружений первоначально присваиваются небольшие случайно выбранные значения. В процессе обучения модель будет обновлять веса, применяя алгоритм обратного распространения. Следующий слой адаптирует форму входа под размер погружения: word_model = Sequential() word_model.add(Embedding(vocab_size, embed_size, embeddings_initializer="glorot_uniform", input_length=1)) word_model.add(Reshape((embed_size, ))) 150 Глава 5. Погружения слов  Модель CBOW Теперь рассмотрим модель CBOW из семейства word2vec. Напомним, что она предсказывает слово по известным контекстным словам. Таким образом, для первого кортежа из примера ниже CBOW должна предсказать слово love, зная контекстные слова I и green: ([I, green], love) ([love, eggs], green) ([green, and], eggs) ... Как и модель skip-грамм, модель CBOW представляет собой классификатор, который получает на входе контекстные слова и предсказывает целевое слово. Но архитектура проще, чем в модели skip-грамм. Входными данными модели являются идентификаторы контекстных слов. Они поступают на вход слоя погружения, веса которого инициализируются небольшими случайными значениями. Этот слой преобразует каждый идентификатор в вектор размера . Следовательно, каждая строка входного конembed_size преобразуется в матрицу размера . (2*window_size, embed_size) Затем матрица подается на вход слоя lambda, который вычисляет среднее по всем погружениям. Полученная величина передается плотному слою, который создает плотный вектор размера vocab_ для каждой строки. В качестве функции активации в плотном size слое используется softmax, которая возвращает вероятность, равную максимальному элементу выходного вектора. Идентификатор с максимальной вероятностью соответствует целевому слову. контекстные слова word2vec 151  Ниже приведен код модели на Keras. Мы снова предполагаем, что размер словаря равен 5000, размер выходного пространства погружения 300, размер контекстного окна 1. Сначала импортируем пакеты и инициализируем переменные: from keras.models import Sequential from keras.layers.core import Dense, Lambda from keras.layers.embeddings import Embedding import keras.backend as K vocab_size = 5000 embed_size = 300 window_size = 1 Затем строим последовательную модель, в которую включаем слой погружения с весами, инициализированными малыми случайными значениями. Отметим, что длина входа этоinput_length слоя равна числу контекстных слов. Каждое контекстное слово подается на вход слоя, и веса обновляются в процессе обратного распространения. На выходе слоя получается матрица погружений контекстных слов, которая усредняется в один вектор (на каждую строку входа) слоем lambda. Наконец, плотный слой преобразует строки в плотный вектор размера . Целевым словом будет vocab_size то, для которого вероятность идентификатора в плотном выходном векторе максимальна. model = Sequential() model.add(Embedding(input_dim=vocab_size, output_dim=embed_size, embeddings_initializer='glorot_uniform', input_length=window_size*2)) model.add(Lambda(lambda x: K.mean(x, axis=1), output_shape=(embed_size,))) model.add(Dense(vocab_size, kernel_initializer='glorot_uniform', activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer="adam") В качестве функции потерь здесь используется categorical_ – типичный выбор для случая, когда категорий две или crossentropy больше (в нашем примере ). vocab_size Код этого примера находится в файле в исходном keras_cbow.py коде к этой главе. Извлечение погружений word2vec из модели Выше уже отмечалось, что хотя обе модели семейства word2vec можно свести к задаче классификации, сама эта задача нас не интересует. А интересен нам побочный эффект классификации – ма- 152 Глава 5. Погружения слов  трица весов, которая преобразует слово из словаря в плотное распределенное представление низкой размерности. Есть немало примеров того, как распределенные представления выявляют удивительную синтаксическую и семантическую информацию. Так, на следующем рисунке, взятом из презентации Томаша Миколова на конференции NIPS 2013 (см. T. Mikolov, I. Sutskever, K. Chen, G. S. Corrado, J. Dean, Q. Le, T. Strohmann «Learning Representations of Text using Neural Networks», NIPS 2013), показано, что векторы, соединяющие слова, имеющие одинаковый смысл, но относящиеся к разным полам, приблизительно параллельны в редуцированном двумерном пространстве и что зачастую можно получить согласующиеся с интуицией результаты, производя арифметические действия над векторами слов. В презентации много таких примеров. Интуитивно кажется, что процесс обучения привносит достаточно информации во внутреннюю кодировку, чтобы предсказать выходное слово, встречающееся в контексте входного. Поэтому точки, представляющие слова в этом пространстве, располагаются ближе к точкам слов, с которыми они встречаются совместно. В результате похожие слова образуют кластеры. И слова, встречающиеся вместе с похожими словами, тоже образуют кластеры. Следовательно, векторы, соединяющие точки, представляющие семантически связанные слова в распределенном представлении, демонстрируют регулярное поведение. Keras предоставляет средства для извлечения весов из обученных моделей. В случае skip-грамм веса слоя погружения можно получить следующим образом: merge_layer = model.layers[0] word_model = merge_layer.layers[0] 154 Глава 5. Погружения слов  Сторонние реализации word2vec В предыдущих разделах было подробно рассмотрено семейство моделей word2vec. Вы понимаете, как работают модели skip-грамм и CBOW и как самостоятельно построить их реализацию с помощью Keras. Однако существуют готовые реализации word2vec и в предположении, что ваша задача не слишком сложна и не сильно отличается от типичной, имеет смысл воспользоваться одной из них и не изобретать велосипед. Одна такая реализация word2vec – библиотека gensim. И хотя эта книга посвящена Keras, а не gensim, мы решили включить ее обсуждение, поскольку Keras не поддерживает word2vec, а интеграция gensim и Keras – распространенная практика. Установка gensim не вызывает сложностей и подробно описана на странице https://radimrehurek.com/gensim/in. показано, как построить модель word2vec с помощью gensim и обучить ее на тексте из корпуса text8, доступном по адресу Этот файл содержит около 17 http://mattmahoney.net/dc/text8.zip миллионов слов, взятых из статей википедии. Текст был подвергнут очистке – удалению разметки, знаков препинания и символов, не принадлежащих кодировке ASCII. Первые 100 миллионов знаков очищенного текста и составили корпус text8. Он часто используется в качестве примера для модели word2vec, потому что обучение на нем происходит быстро и дает хорошие результаты. Сначала импортируем необходимые пакеты: from gensim.models import KeyedVectors import logging import os Затем читаем поток слов из корпуса text8 и разбиваем его на предложения по 50 слов в каждом. Библиотека gensim содержит встроенный обработчик text8, который делает нечто подобное. Поскольку мы хотим показать, как построить модель для любого (предпочтительно большого) корпуса, который может и не помещаться целиком в память, то продемонстрируем порождение этих предложений с помощью генератора Python. word2vec 155  Класс порождает предложения по слов в кажText8Sentences из файла text8. В данном случае мы таки читаем весь файл в память, но при обходе файлов, находящихся в нескольких каталогах, генератор позволяет загрузить в память часть данных, обработать ее и отдать вызывающей стороне: class Text8Sentences(object): def __init__(self, fname, maxlen): self.fname = fname self.maxlen = maxlen def __iter__(self): with open(os.path.join(DATA_DIR, "text8"), "rb") as ftext: text = ftext.read().split(" ") sentences, words = [], [] for word in text: if len(words) >= self.maxlen: yield words words = [] words.append(word) yield words Теперь займемся вызывающей программой. В библиотеке gensim используется имеющийся в Python механизм протоколирования для уведомления о ходе обработке, поэтому для начала активируем его. В следующей строке создается экземпляр класса , а затем модель обучается на предложениях из наText8Sentences данных. Мы задали размер векторов погружения 300 и рассматриваем только слова, которые встречаются в корпусе не менее 30 раз. Размер окна по умолчанию равен 5, поэтому контекстом для слова w будут слова w , w , w , w , w , w , w , w , w , w . i i-5 i-4 i-3 i-2 i-1 i+1 i+2 i+3 i+4 i+5 По умолчанию создается модель CBOW, но это можно изменить, задав параметр : sg=1 logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) DATA_DIR = "../data/" sentences = Text8Sentences(os.path.join(DATA_DIR, "text8"), 50) model = word2vec.Word2Vec(sentences, size=300, min_count=30) Реализация word2vec производит два прохода по данным: сначала строится словарь, а затем – фактическая модель. За ходом работы можно следить по распечатке на консоли: 158 Глава 5. Погружения слов  Код этого примера находится в файле в исходword2vec_gensim.py коде к этой главе. Введение в GloVe Погружение слов GloVe (Global Vector – глобальный вектор) было предложено в работе J. Pennington, R. Socher, and C. Manning «GloVe: Global Vectors for Word Representation», Proceedings of the 2014 Conference on Empirical Methods in Natural Language Processing (EMNLP), pp. 1532–1543, 2013. Авторы описывают GloVe как алгоритм обучения без учителя, цель которого – получение векторных представлений слов. Обучение производится на агрегированной глобальной статистике совместной встречаемости слов из корпуса, а получающиеся представления вскрывают интересные линейные структуры в векторном пространстве слов. GloVe отличается от word2vec тем, что word2vec – прогностическая модель, тогда как GloVe основана на счетчиках. На первом шаге строится большая матрица совместной встречаемости пар (слово, контекст) в обучающем корпусе. Каждый элемент матрицы описывает, как часто слово, представленное данной строкой, встречается в контексте (обычно это последовательность слов), представленном данным столбцом. контекст признаки контекст признаки слова слова матрица (признак, матрица совместной матрица контекст) встречаемости слова (слово, и контекста признак) Алгоритм GloVe преобразует матрицу совместной встречаемости в пару матриц: (слово, признак) и (признак, контекст). Этот процесс называется факторизацией матрицы и выполняется стохастического градиентного итеративно с помощью метода ≈ спуска (СГС). В форме уравнения он записывается так: R = P * Q R' Здесь R – исходная матрица совместной встречаемости. Сначала мы инициализируем и случайными значениями и пытаемP Q Использование предобученных погружений 159  ся воссоздать путем их перемножения. Разница между реконR' матрицей R' и исходной R показывает, как надо изменить значения P и Q, чтобы R' стала ближе к R, т. е. ошибка реконструкции уменьшилась. Эта операция повторяется несколько раз, пока алгоритм СГС не сойдется и ошибка реконструкции не станет ниже заданного порогового значения. Получившаяся в этот момент матрица (слово, признак) и является погружением в смысле GloVe. Для ускорения процесса СГС часто выполняется параллельно, как описано в статье «Hogwild!: A Lock-Free Approach to Parallelizing Stochastic Gradient Descent» ( https://people.eecs.berke). что прогностические модели на основе нейронных сетей типа word2vec и основанные на счетчиках модели типа GloVe преследуют одну и ту же цель. Те и другие строят векторное пространство, так что положение слова в нем зависит от соседних слов. Нейронная сеть начинает работу с отдельных примеров совместной встречаемости слов, а основанные на счетчиках модели – со статистики совместной встречаемости всех слов в корпусе. Недавно было опубликовано несколько работ, в которых демонстрируется корреляция между моделями обоих типов. В этой книге мы не будем подробно рассматривать генерацию векторов GloVe. Хотя в общем случае GloVe отличается большей верностью, чем word2vec, и быстрее обучается при использовании распараллеливания, поддержка ее на Python пока не столь развитая, как word2vec. На данный момент единственным доступным инструментом был проект GloVe-Python ( https://github. ), предлагающий модельную реализацию com/maciejkula/glove-python GloVe на Python. Использование предобученных погружений Вообще говоря, обучать модель word2vec или GloVe с нуля следует только тогда, когда имеется очень большой объем узкоспециализированных текстов. Чаще всего тем или иным способом используются предобученные погружения. Есть три основных способа включения погружений в собственную сеть:  обучение погружений с нуля;  160 Глава 5. Погружения слов   настройка погружений на основе предобученных моделей  GloVe/word2vec;  поиск погружений в предобученных моделях GloVe/  word2vec. В первом случае веса погружений инициализируются небольшими случайными значениями и обучаются методом обратного распространения. Такой способ мы видели на примере моделей skip-грамм и CBOW в Keras. Это режим по умолчанию при использовании слоя Embedding в собственной сети. Во втором случае из предобученной модели берется матрица весов и используется для инициализации весов слоя погружения. Сеть также обновляет веса методом обратного распространения, но модель сходится быстрее вследствие хорошего выбора начальных весов. В третьем случае погружения слов ищутся в предобученной модели, и входные данные преобразуются в векторные погружения. На преобразованных данных можно затем обучить любую модель (необязательно сеть глубокого обучения). Если предобученная модель обучалась на текстах из той же предметной области, что и целевая, то этот способ обычно дает прекрасные результаты и является самым дешевым. Для англоязычных текстов общего характера можно использовать модель word2vec от Google, обученную на 10 миллиардах слов из набора данных Google news. Размер словаря составляет примерно 3 миллиона слов, а размерность пространства погружения рав- на 300. Модель Google news (размером около 1.5 ГБ) можно скачать по адресу https://drive.google.com/file/d/0B7XkCwpI5KDYNlNUTTlSS21pQmM/ . edit?usp=sharing С сайта GloVe можно скачать модель, обученную на 6 миллиардах лексем из англоязычной википедии и корпусе текстов, содержащем порядка миллиарда слов. Размер словаря составляет примерно 400 000 слов, для скачивания доступны модели с размерностью пространства погружения 50, 100, 200 и 300. Размер модели составляет приблизительно 822 МБ. Скачать модель можно по адресу . Там же имеются http://nlp.stanford.edu/data/glove.6B.zip более крупные модели, обученные на данных из репозитория проекта Common Crawl и из Twitter. В следующих разделах мы рассмотрим, как использовать эти модели тремя вышеупомянутыми способами. Использование предобученных погружений 161  Обучение погружений с нуля В этом примере мы обучим одномерную сверточную нейронную сеть (СНС) классифицировать предложения как окрашенные положительно или отрицательно. В главе 3 мы уже рассматривали обучение двумерной СНС для классификации изображений. Напомним, что в этой СНС используется пространственная структура изображения путем обеспечения локальной связности между нейронами соседних слоев. Для слов предложения характерна линейная структура, аналогичная пространственной структуре в изображении. Традиционные (не на базе глубокого обучения) подходы к языковому моделированию подразумевают создание словесных ( n-грамм https:// , улавливающих эту линейную структу) Одномерные СНС делают нечто похожее, обучая сверточные фильтры, которые затрагивают сразу несколько слов предложения, и применяя к результатам max-пулинг, чтобы создать вектор, представляющий важнейшие смысловые аспекты предложения. Существует еще один класс нейронных сетей, рекуррентные нейронные сети (РНС), специально предназначенные для обработки последовательных данных, в т. ч. текста, который есть не что иное, как последовательность слов. Порядок обработки в РНС не такой, как в СНС. Подробнее о РНС мы будем говорить в следующей главе. В нашей сети входной текст преобразуется в последовательность индексов слов. Для грамматического разбора текста мы воспользовались библиотекой для обработки естественных языков NLTK (natural language toolkit). Можно было бы применить для этой цели регулярные выражения, но статистические модели, предлагаемые NLTK, более пригодны для разбора текста. Если вас интересуют погружения слов, то, скорее всего, вы занимаетесь ОЕЯ, так что NLTK уже установлена. По адресу приведена http://www.nltk.org/install.html информация об установке NLTK. Понадобятся также включенные в состав NLTK данные – обученные модели, корпусы текстов и прочее. Инструкции по их установке имеются на странице . http://www.nltk.org/data.html Последовательность индексов слов загружается в слой погружения заданного размера (в нашем случае число слов в самом 162 Глава 5. Погружения слов  длинном предложении). По умолчанию слой погружения инициализируется случайными значениями. Выход слоя погружения соединяется с одномерным сверточным слоем, который сворачивает (в нашем примере) словесные триграммы 256 различными способами (по сути дела применяет различные обученные линейные комбинации весов к погружениям слов). Эти признаки затем сводятся к единственному слову слоем глобального max-пулинга. Вектор длины 256 подается на вход плотного слоя, который выводит вектор длины 2. Функция активации softmax возвращает две вероятности: положительной и отрицательной эмоциональной окраски. Сеть показана на рисунке ниже. последовательность идентификаторов слов Посмотрим, как это реализуется с помощью Keras. Сначала – импорт пакетов. После этого задается начальное значение генератора случайных чисел – 42. Это сделано для того, чтобы результаты прогона программы были воспроизводимыми (напомним, что матрицы весов инициализируются случайными значениями). from keras.layers.core import Dense, Dropout, SpatialDropout1D from keras.layers.convolutional import Conv1D from keras.layers.embeddings import Embedding from keras.layers.pooling import GlobalMaxPooling1D Использование предобученных погружений 165  на нашем обучающем наборе, указав размер пакета 64 и число периодов 20: model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"]) history = model.fit(Xtrain, Ytrain, batch_size=BATCH_SIZE, epochs=NUM_EPOCHS, validation_data=(Xtest, Ytest)) Протокол обучения показан ниже: Как видим, на тестовом наборе верность сети составила 98.6%. Код этого примера находится в файле learn_embedding_from_scratch. в исходном коде к этой главе. py Настройка погружений на основе предобученной модели word2vec В этом примере мы будем использовать ту же сеть, что в предыдущем. В программе единственное существенное отличие – дополнительный код для загрузки модели word2vec и построения матрицы весов для слоя погружения. Как всегда, начинаем с импорта и задания начального значения случайного генератора для воспроизводимости. Помимо того, что было импортировано в предыдущем примере, мы еще импортируем модель word2vec из пакета gensim: from gensim.models import KeyedVectors from keras.layers.core import Dense, Dropout, SpatialDropout1D from keras.layers.convolutional import Conv1D 166 Глава 5. Погружения слов  from keras.layers.embeddings import Embedding from keras.layers.pooling import GlobalMaxPooling1D from keras.models import Sequential from keras.preprocessing.sequence import pad_sequences from keras.utils import np_utils from sklearn.model_selection import train_test_split import collections import matplotlib.pyplot as plt import nltk import numpy as np np.random.seed(42) Далее задаются константы. По сравнению с предыдущим случаем мы уменьшили с 20 до 10. Напомним, что инициNUM_EPOCHS весов значениями, взятыми из предобученной модели, обычно ускоряет сходимость: INPUT_FILE = "../data/umich-sentiment-train.txt" WORD2VEC_MODEL = "../data/GoogleNews-vectors-negative300.bin.gz" VOCAB_SIZE = 5000 EMBED_SIZE = 300 NUM_FILTERS = 256 NUM_WORDS = 3 BATCH_SIZE = 64 NUM_EPOCHS = 10 В следующей части из набора данных извлекаются слова и создается словарь самых частых термов, после чего набор данных разбирается с целью создания списка списков дополненных слов. Кроме того, метки преобразуются в категориальный формат. И наконец, мы разбиваем данные на обучающий и тестовый набор. Этот блок ничем не отличается от предыдущего примера, где были приведены подробные пояснения. counter = collections.Counter() fin = open(INPUT_FILE, "rb") maxlen = 0 for line in fin: _, sent = line.strip().split("t") words = [x.lower() for x in nltk.word_tokenize(sent)] if len(words) > maxlen: maxlen = len(words) for word in words: counter[word] += 1 fin.close() word2index = collections.defaultdict(int) for wid, word in enumerate(counter.most_common(VOCAB_SIZE)): Использование предобученных погружений 169  0.9845 - val_loss: 0.0194 - val_acc: 0.9929 Epoch 10/10 4960/4960 [==============================] - 7s - loss: 0.0389 - acc: 0.9853 - val_loss: 0.0254 - val_acc: 0.9915 2126/2126 [==============================] - 1s Test score: 0.025, accuracy: 0.993 После 10 периодов обучения модель показывает верность 99.3% на тестовом наборе. Это лучше, чем предыдущий пример, где была достигнута верность 98.6% после 20 периодов. Код этого примера находится в файле finetune_word2vec_embeddings. в исходном коде к этой главе. py Настройка погружений на основе предобученной модели GloVe Погружения на основе предобученной модели GloVe настраиваются примерно так же, как в случае модели word2vec. На самом деле отличается только код построения матрицы весов для слоя погружения. Только его мы и рассмотрим. Есть несколько видов предобученных моделей GloVe. Мы будем работать с той, что обучена на 6 миллиардах лексем и на корпусе текстов объемом порядка миллиарда слов из англоязычной вики- педии. Размер словаря модели составляет примерно 400 000 слов, имеются загружаемые файлы для размерности погружения 50, 100, 200 и 300. Мы возьмем файл для размерности 300. Единственное, что нужно изменить в коде предыдущего примера, – часть, где создается модель word2vec и инициализируется ее матрица весов. А если бы мы взяли модель с размерностью, отличной от 300, то нужно было бы еще изменить константу . EMBED_SIZE Векторы записаны в файле в текстовом формате через пробел, поэтому наша первая задача – прочитать их в словарь . Это word2emb делается аналогично разбору строки файла данных для модели word2vec. GLOVE_MODEL = "../data/glove.6B.300d.txt" word2emb = {} fglove = open(GLOVE_MODEL, "rb") for line in fglove: cols = line.strip().split() word = cols[0] embedding = np.array(cols[1:], dtype="float32") word2emb[word] = embedding fglove.close() 170 Глава 5. Погружения слов  Затем создаем матрицу весов погружения размера × vocab_sz EMи заполняем ее векторами из словаря . Векторы, BED_SIZE word2emb которые соответствуют словам, имеющимся в словаре, но отсутствующим в модели GloVe, остаются нулевыми: embedding_weights = np.zeros((vocab_sz, EMBED_SIZE)) for word, index in word2index.items(): try: embedding_weights[index, :] = word2emb[word] except KeyError: pass Код этого примера находится в файле finetune_glove_embeddings.py в исходном коде к этой главе. Ниже показаны результаты выполнения программы: При обучении на протяжении 10 периодов достигается верность 99.1%, что почти не уступает результатам, полученным после настройки весов модели word2vec. Поиск погружений И последняя стратегия – поиск погружений в предобученной сети. Для этого проще всего задать в наших примерах параметр слоя погружения равным . Тогда при обратном расtrainable не будут обновляться веса этого слоя: model.add(Embedding(vocab_sz, EMBED_SIZE, input_length=maxlen, weights=[embedding_weights], trainable=False)) model.add(SpatialDropout1D(Dropout(0.2))) Использование предобученных погружений 171  Поступив так в примерах для моделей word2vec и GloVe, мы получим соответственно верность 98.7% и 98.9% после 10 периодов обучения. Однако в общем случае предобученные погружения используются не так. Обычно производится предварительная обработка набора данных, цель которой – построить векторные представления слов путем поиска в какой-то предобученной модели, а затем воспользоваться этими данными для обучения другой модели. Вторая модель не будет содержать слоя погружения и вообще может не быть сетью глубокого обучения. В примере ниже описана плотная сеть, которая принимает на входе вектор размера 100, представляющий предложение, и выводит 1, если предложение имеет положительную эмоциональную окраску, и 0 – если отрицательную. Мы по-прежнему используем набор данных из конкурса UMICH S1650, содержащий примерно 7000 предложений. Как и раньше, большие куски кода повторяются, поэтому мы заострим внимание только на новых частях, нуждающихся в пояснении. В начале импортируются пакеты, инициализируется генератор случайных чисел и задаются значения констант. Для создания 100-мерных векторов для каждого предложения нам понадобится модель GloVe размерности 100, которая хранится в файле keras.layers.core import Dense, Dropout, SpatialDropout1D from keras.models import Sequential from keras.preprocessing.sequence import pad_sequences from keras.utils import np_utils from sklearn.model_selection import train_test_split import collections import matplotlib.pyplot as plt import nltk import numpy as np np.random.seed(42) INPUT_FILE = "../data/umich-sentiment-train.txt" GLOVE_MODEL = "../data/glove.6B.100d.txt" VOCAB_SIZE = 5000 EMBED_SIZE = 100 BATCH_SIZE = 64 NUM_EPOCHS = 10 Далее мы читаем предложения и создаем таблицу частот слов. Из этой таблицы мы отбираем 5000 самых частых лексем и строим 174 Глава 5. Погружения слов  Плотная сеть с предварительной обработкой на 100-мерной модели GloVe дает верность 96.5% на тестовом наборе после обучения на протяжении 10 периодов. Сеть с предварительной обработкой на 300-мерной модели word2vec дает верность 98.5%. Код этого примера находится в файле transfer_glove_embeddings. (для примера с моделью GloVe) и в файле py transfer_word2vec_embed(для примера с моделью word2vec) в исходном коде к этой dings.py главе. Резюме В этой главе мы изучили, как преобразовать слова из текста в векторные представления с сохранением дистрибутивной семантики слов. Мы также на интуитивном уровне поняли, почему погружения слов в векторное пространство демонстрируют такое поведение и почему они полезны для применения в глубоких моделях текстовых данных. Затем мы рассмотрели две популярные модели погружения слов, word2vec и GloVe, и уяснили, как они работают. Мы также познакомились с применением библиотеки gensim для обучения модели word2vec на данных. Наконец, мы узнали о различных способах использования погружений в собственной сети. Первый – обучить веса погружений с нуля в процессе обучения сети. Второй – импортировать веса погружений из предобученной модели word2vec или GloVe в свою Резюме 175  сеть и настроить их в процессе обучения сети. Третий – использовать предобученные веса непосредственно в своем приложении. В следующей главе мы узнаем о рекуррентных нейронных сетях, оптимизированных для обработки последовательности, в т. ч. текста. 6 Глава Рекуррентная нейронная сеть – РНС В главе 3 мы познакомились со сверточными нейронными сетями (СНС) узнали, как в них используется пространственная геометрия входных данных. Так, операции свертки и пулинга применяются вдоль временной оси для звуковых данных, в двух пространственных измерениях для изображений и в трех измерениях (высота, ширина, время) для видео. В этой главе мы будем говорить о рекуррентных нейронных сетях (РНС, англ. RNN) – классе нейронных сетей, в которых учитывается последовательный характер входных данных. На вход такой сети может подаваться текст, речь, временной ряд или еще какие-то данные, в которых появление элемента в последовательности зависит от предшествующих элементов. Например, следующим словом в предложении «собака…» скорее будет «лает», чем «машина», и именно его РНС предскажет с большей вероятностью. РНС можно рассматривать как граф, состоящий из элементарных ячеек, каждая из которых выполняет одну и ту же операцию для каждого элемента последовательности. РНС обладают большой гибкостью и применяются для решения таких задач, как распознавание речи, языковое моделирование, машинный перевод, анализ эмоциональной окраски, подписывание изображений и многих других. РНС можно адаптировать к различным типам задач, изменяя конфигурацию ячеек в графе. Мы рассмотрим несколько таких конфигураций и их применение к конкретным задачам. Мы также узнаем об основном ограничении простой ячейки РНС и о двух ее вариантах: долгой краткосрочной памяти (long short term memory, LSTM) и вентильном рекуррентном блоке (gated recurrent unit, GRU), позволяющих преодолеть это ограни- Простые ячейки РНС 177  чение. LSTM и GRU можно подставить вместо простой ячейки, и зачастую это заметно улучшает качество сети. Хотя LSTM и GRU – не единственные варианты, эмпирически показано (см. статьи «An R. Jozefowicz, W. Zaremba, I. Sutskever Empirical Exploration of Recurrent Network Architectures», JMLR, 2015 и K. Greff «LSTM: A Search Space Odyssey», arXiv:1503.04069, 2015), что для большинства задач обработки последовательностей они оказываются наилучшими. мы дадим несколько рекомендаций о том, как повысить качество РНС, и о том, когда и как их следует применять. В этой главе рассматриваются следующие вопросы:  простая ячейка РНС;   реализация РНС для порождения текста с помощью Keras;   топологии РНС;   LSTM, GRU и другие варианты РНС.  Простые ячейки РНС Традиционно в нейронных сетях на основе многослойных перцептронов предполагается, что все входы независимы. Для последовательных данных это предположение нарушается. В предыдущем разделе мы видели пример, когда первое слово предложения влияет на второе. То же относится и к речи – разговаривая в шумной комнате, я могу выдвинуть разумную гипотезу о слове, которое не расслышал, исходя из слов, произнесенных собеседником ранее. Для временных рядов, например цен на акции или прогнозов погоды, также характерна зависимость от прошлых данных, это явление называется долговременным трендом. В ячейках РНС эта зависимость представляется с помощью скрытого состояния, или памяти, в которой хранится сводка прошлой информации. Значение скрытого состояния в любой момент времени – функция его значения на предыдущем шаге и значения данных на текущем шаге: h = , x ), φ(h t t–1 t где h и h – значения скрытого состояния на шаге t и t–1 соотt t-1 ветственно, и x – входное значение в момент t. Отметим, что это t уравнение рекуррентное, т. е. можно выразить через и и h h x t-1 t-2 t-1 т. д., пока мы не дойдем до начала последовательности. Именно 178 Глава 6. Рекуррентная нейронная сеть – РНС  так в РНС кодируется и запоминается информация о сколь угодно длинной последовательности. Мы также можем представить ячейку РНС графически, как показано в левой части следующего рисунка. В момент t ячейка получает на входе значение x и выводит значение y . Часть y (скрытое t t t состояние ) подается обратно на вход ячейки для использования h t на следующем шаге t+1. Если параметры традиционной нейронной сети хранятся в матрице весов, то параметры РНС задаются тремя матрицами весов, U, V и W, соответствующими входу, выходу и скрытому состоянию. временные шаги Еще один взгляд на РНС – развертка, показанная на том же рисунке справа. Это означает, что мы рисуем сеть на протяжении всей последовательности. На рисунке изображена РНС с тремя слоями, пригодная для обработки последовательностей с тремя элементами. Заметим, что матрицы весов U, V и W разделяются между всеми шагами, поскольку на каждом шаге к разным данным применяются одни и те же операции. Благодаря использованию одних и те же весов на всех временных шагах удается существенно снизить количество обучаемых параметров РНС. Вычисления, выполняемые РНС, можно также описать в виде уравнений. Внутреннее состояние РНС в момент t определяется значением вектора , равного результату применения нелинейh tanh к сумме произведения матрицы весов W на скрытое состояние h в момент t–1 и произведения матрицы весов U на входt-1 значение x в момент t. Выбор нелинейности tanh, а не какой-то t Простые ячейки РНС 179  другой, связан с тем, что ее вторая производная очень медленно убывает, приближаясь к нулю. Поэтому градиенты остаются в линейной части функции активации, что помогает справиться с проблемой исчезающего градиента. Подробнее об этой проблеме мы поговорим ниже. Выходной вектор в момент равен результату применения y t t функции softmax к произведению матрицы весов V на скрытое состояние h и представляет набор вероятностей выхода: t = tanh(Wh + ) h Ux t t–1 t y = softmax(Vh ) t t Keras предоставляет слой рекуррентной сети SimpleRNN (см. ), включающий всю описанную выhttps://keras.io/layers/recurrent/ логику, а также более эффективные варианты LSTM и GRU, которые будут рассмотрены ниже. Чтобы использовать их, необязательно точно понимать, как они работают. Однако знать структуру и уравнения полезно, если вы захотите построить свою РНС для решения поставленной задачи. Простая РНС с применением Keras – порождение текста РНС активно используются в обработке естественных языков (ОЕЯ, англ. NLP) для решения различных задач. Одна из них – построение языковых моделей. Такая модель позволяет предсказать вероятность появления слова в тексте при условии известных предыдущих слов. Языковые модели важны для таких высокоуровневых приложений, как машинный перевод, исправление правописания и т. д. Побочным эффектом умения предсказывать следующее слово по известным предыдущим является порождающая модель, которая генерирует текст путем выборки слов из выходного распределения. В случае языкового моделирования входом обычно является последовательность слов, а выходом – последовательность предсказанных слов. В роли обучающих данных выступает имеющийся непомеченный текст, и метка в момент становится y t t входом x в момент t+1. t+1 Первым нашим примером использования Keras для построения РНС будет языковая модель, обученная предсказывать следующий символ по 10 предыдущим на тексте «Алисы в Стране чудес». Мы 180 Глава 6. Рекуррентная нейронная сеть – РНС  остановились на модели для предсказания символа, потому что у нее меньше словарь и обучение проходит быстрее. Но та же идея применима и к предсказанию слов, нужно только символы заменить словами. Обученная модель будет использована для порождения нового текста в том же стиле. Сначала импортируем модули: from __future__ import print_function from keras.layers import Dense, Activation from keras.layers.recurrent import SimpleRNN from keras.models import Sequential from keras.utils.visualize_util import plot import numpy as np Входной текст «Алисы в Стране чудес» (на английском языке) берем с сайта проекта Гутенберг по адресу http://www.gutenberg.org/ . Файл содержит символы конца строки и символы files/11/11-0.txt не в кодировке ASCII, поэтому произведем предварительную обработку и запишем результат в переменную : text fin = open("../data/alice_in_wonderland.txt", 'rb') lines = [] for line in fin: line = line.strip().lower() line = line.decode("ascii", "ignore") if len(line) == 0: continue lines.append(line) fin.close() text = " ".join(lines) Поскольку наша РНС будет предсказывать символы, то и словарь состоит и множества символов, встречающихся в тексте. Таковых в нашем случае 42. Мы будем иметь дело не с самими символами, а с их индексами, поэтому в следующем фрагменте создаются необходимые таблицы соответствия: chars = set([c for c in text]) nb_chars = len(chars) char2index = dict((c, i) for i, c in enumerate(chars)) index2char = dict((i, c) for i, c in enumerate(chars)) Следующий шаг – создание входных строк и меток. Для этого проходим по тексту с шагом символов (в нашем случае 1) и STEP выделяем отрезки длиной (в нашем случае 10). Следующий SEQLEN после отрезка символ будем меткой: 184 Глава 6. Рекуррентная нейронная сеть – РНС  Порождение следующего символа или слова – не единственное, на что способна такая модель. Подобные модели успешно применялись для предсказания цен акций (см. A. Bernal, S. Fok, «Financial R. Pidaparthi Market Time Series Prediction with Recurrent Neural Networks», 2012) и для генерации классической музыки (см. G. Hadjeres, F. Pachet «DeepBach: A Steerable Model for Bach Chorales Generation», arXiv:1612.01010, 2016). Андрей Карпатый приводит еще несколько любопытных примеров и исходный код для Linux в статье «The Unreasonable Effectiveness of Recurrent Neural Networks» в своем блоге по адресу http://karpathy.github.io/2015/05/21/rnn. этого примера находится в файле в исalice_chargen_rnn.py коде к этой главе. Сами данные можно найти на сайте проекта Гутенберг. Топологии РНС API многослойного перцептрона и СНС ограничены. Обе архитектуры принимают на входе и порождают на выходе тензоры фиксированного размера, а для преобразования входа в выход выполняют фиксированное число шагов, определяемое числом слоев сети. У РНС такого ограничения нет – входом, выходом или тем и другим могут быть последовательности. Это означает, что для решения конкретных задач РНС можно конфигурировать разными способами. Как мы уже знаем, РНС комбинирует входной вектор с предыдущим вектором состояния для получения нового вектора состояния. Это можно рассматривать как аналог выполнения программы с некоторыми входными данными и внутренними переменными. Следовательно, РНС можно считать способом описания компьютерных программ. На самом деле, доказано, что РНС являются полными по Тьюрингу исполнителями (см. H. T. Siegelmann, E. D. Sontag «On the Computational Power of Neural Nets», Proceedings of the Fifth Annual Workshop on Computational Learning Theory, ACM, 1992) в том смысле, что при задании надлежащих весов они могут моделировать произвольные программы. Умение работать с последовательностями открывает возможность для различных топологий, некоторые из которых мы обсудим ниже. Топологии РНС 185  (a) многие-ко-многим (1) (b) один-ко-многим (c) многие-ко-многим (2) (d) многие-к-одному Все эти топологии вытекают из общей базовой структуры, показанной на предыдущем рисунке. В базовой структуре все входные последовательности имеют одинаковую длину, а выход порождается на каждом временном шаге. Пример мы уже видели в сети порождения символов, обученной на тексте «Алисы в Стране чудес». Другой пример РНС типа многие-ко-многим – сеть машинного перевода на рисунке (b), являющаяся представителем общего семейства сетей последовательность-в-последовательность (см. O. Vinyals «Grammar as a Foreign Language», Advances in Neural Information Processing Systems, 2015). Они принимают на входе последовательность и порождают другую последовательность. В случае машинного перевода входом может быть, например, последовательность английских слов, а выходом – переведенное предложение на испанском языке. Такой же тип модели испольчастеречной разметки, зуется для когда входом являются слова предложения, а выходом – соответствующие метки частей речи. От предыдущей топологии эта отличается тем, что в некоторые моменты времени может отсутствовать вход, а в некоторые – вы- ход. С примером такой сети мы встретимся ниже. Еще один вариант топологии – сеть типа один-ко-многим на рисунке (c), примером которой может служить сеть для подписывания изображений (см. A. Karpathy, F. Li «Deep Visual-Semantic Alignments for Generating Image Descriptions», Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, 2015), 186 Глава 6. Рекуррентная нейронная сеть – РНС  где входом является изображение, а выходом – последовательность слов. Пример сети типа многие-к-одному на рисунке (d) – сеть анализа эмоциональной окраски предложений, когда входом является последовательность слов, а выходом – индикатор положительной или отрицательной окраски (см. R. Socher «Recursive Deep Models for Semantic Compositionality over a Sentiment Treebank», Proceedings of the Conference on Empirical Methods in Natural Language Processing (EMNLP). Vol. 1631, 2013). Ниже в этой главе мы рассмотрим пример такой топологии (правда, существенно упрощенный по сравнению с цитированной выше моделью). Проблема исчезающего и взрывного градиента Как и в традиционных нейронных сетях, обучение РНС включает обратное распространение. Различие в том, что поскольку на всех шагах используются одинаковые параметры, то градиент в каждом выходе зависит не только от текущего временного шага, но и от предыдущих. Этот процесс называется обратным распространением во времени (backpropagation through time, BPTT) (см. статью G. E. Hinton, D. E. Rumelhart, R. J. Williams «Learning Internal Representations by Backpropagating errors», Parallel Distributed Processing: Explorations in the Microstructure of Cognition 1, 1985). Рассмотрим небольшую трехслойную РНС, показанную на рисунке выше. В процессе прямого распространения (сплошные Проблема исчезающего и взрывного градиента 187  линии) сеть порождает предсказания, которые сравниваются с метками для вычисления потери L на каждом временном шаге. t В процессе обратного распространения (пунктирные линии) на каждом временном шаге вычисляются градиенты функции потерь по параметрам U, V и W, и сумма градиентов применяется для обновления параметров. В следующем уравнении показан градиент функции потерь по W – матрице, в которой закодированы веса для долгосрочных зависимостей. Мы акцентируем внимание на этой части обновления, потому что именно она – причина проблемы исчезающего и взрывного градиента. Два других градиента функции потерь по матрицам U и V также суммируются по всем временным шагам: L ∂ L ∂ ∑ = t ∂ W ∂ W t Теперь посмотрим, что происходит с градиентом функции потерь на последнем временном шаге (t = 3). Как видим, этот градиент можно разложить в произведение трех подградиентов, применив правило дифференцирования сложной функции. Градиент скрытого состояния h по W можно затем представить в виде сум2 градиентов каждого скрытого состояния по предыдущему. Наконец, градиент скрытого состояния по предыдущему можно разложить в произведение градиентов текущего скрытого состояния по предыдущему: ∂ L ∂ L ∂ y ∂ h 3 = 3 ⋅ 3 ⋅ 2 W y h W ∂ ∂ ∂ ∂ 3 2 2 ∂ L ∂ y ∂ h ∂ h ∑ 3 3 = ⋅ ⋅ 2 ⋅ t ∂ y ∂ h ∂ h ∂ W t = 0 3 2 t  ∂ h  2 ∂ L ∂ y ∂ h 2 ∑ ∏ j 3 3 = ⋅ ⋅ ⋅ t   ∂ y ∂ h ∂ h ∂ W   j = t + 1 t = 0 3 2 j − 1 Аналогично вычисляются градиенты функции потерь и L L 1 2 (на шагах 1 и 2) по W, после чего их сумма используется для обновления градиента по W. Мы не станем в этой книге вдаваться в математические детали. Если вам это интересно, почитайте в 188 Глава 6. Рекуррентная нейронная сеть – РНС  блоге WILDML ( ) статью, содержащую очень https://goo.gl/l06lbX хорошее объяснение BPTT с подробными математическими выкладками. а нам последнего выражения градиента в формуле выше достаточно, чтобы понять, откуда возникает проблема исчезающего и взрывного градиента в РНС. Рассмотрим случай, когда отдельные градиенты скрытого состояния по предыдущему меньше 1. При обратном распространении через несколько временных шагов произведение градиентов становится все меньше и меньше, что и ведет к проблеме исчезающего градиента. С другой стороны, если градиенты больше 1, то произведения растут – и вот вам проблема взрывного градиента. Из-за эффекта исчезающего градиента получается, что градиенты на отдаленных шагах не дают никакого вклада в процесс обучения, так что РНС не может учесть долговременные зависимости. Эта проблема может возникнуть и в традиционной нейронной сети, но в случае РНС она проявляется более рельефно, потому что в РНС больше слоев (временных шагов), через которые происходит обратное распространение. Взрывные градиенты обнаруживаются проще, поскольку когда нечисградиент становится слишком большим и превращается в ло (NaN), процесс обучения аварийно завершается. Рост градиентов можно контролировать, обрезая их при достижении заданного порога (см. R. Pascanu, T. Mikolov, Y. Bengio «On the Difficulty of Training Recurrent Neural Networks», ICML, Pp 1310–1318, 2013). Существует несколько подходов к смягчению проблемы исчезающих градиентов, в частности, хорошая инициализация матрицы использование функции активации ReLU вместо tanh и предоW, слоев без учителя, но наиболее популярны архитектуры LSTM и GRU. Они специально проектировались для борьбы с исчезающим градиентом и более эффективно обучаются долговременным зависимостям. Долгая краткосрочная память – LSTM LSTM – это вариант РНС, способный обучаться долгосрочным зависимостям. LSTM-сети впервые были предложены Хохрайтером и Шмидхубером, а затем улучшены многими другими исследователями. Они хорошо работают для широкого круга задач и являются самым популярным типом РНС. Долгая краткосрочная память – LSTM 189  Мы видели, как в простой РНС для реализации рекуррентности используется комбинация скрытого состояния на предыдущем шаге и текущих входных данных в слое с функцией активации. В LSTM-сети рекуррентность реализуется аналогично, но tanhслоев не один, а четыре, и взаимодействуют они весьма специфичным образом. На рисунке ниже показаны преобразования, применяемые к скрытому состоянию на временном шаге t: Выглядит сложно, но мы рассмотрим эту схему по шагам. На горизонтальной линии сверху показано состояние ячейки оно c, представляет внутреннюю память блока. На линии снизу показано скрытое состояние, а вентили и – это механизмы, благодаi, f, o g ря которым LSTM-сеть обходит проблему исчезающего градиента. В процессе обучения LSTM находит параметры этих вентилей. Чтобы лучше понять, как эти вентили модулируют скрытое состояние LSTM-сети, рассмотрим формулы вычисления скрытого состояния в момент по состоянию на предыдущем шаге: h t h t t‑1 i = (W h + U x ) σ i t–1 i t f = (W h + U x ) σ f t–1 f t o = (W h + U x ) σ o t–1 o t = tanh(W + ) g h U x g t–1 g t c = (c  f)  (g  i) t t–1 h = tahn(c )  o t t Долгая краткосрочная память – LSTM 191  Пример LSTM – анализ эмоциональной окраски Keras предоставляет слой LSTM, которым мы воспользуемся, чтобы построить и обучить РНС типа многие-к-одному. Сеть будет принимать предложение (последовательность слов) и выдавать индикатор эмоциональной окраски (положительной или отрицательной). Обучающий набор состоит примерно из 7000 коротких предложений, предлагавшихся на конкурсе Kaggle UMICH SI650 по классификации эмоциональной окраски ( https://inclass.kaggle. ). Каждое предложение снабжено меткой 1 (поcom/c/si650winter11 окраска) или 0 (отрицательная окраска). Как обычно, начинаем с импорта: from keras.layers.core import Activation, Dense, Dropout, SpatialDropout1D from keras.layers.embeddings import Embedding from keras.layers.recurrent import LSTM from keras.models import Sequential from keras.preprocessing import sequence from sklearn.model_selection import train_test_split import collections import matplotlib.pyplot as plt import nltk import numpy as np import os Предварительно займемся исследовательским анализом данных. Нам нужно знать, сколько уникальных слов встречается в корпусе текстов и сколько слов в каждом предложении: maxlen = 0 word_freqs = collections.Counter() num_recs = 0 ftrain = open(os.path.join(DATA_DIR, "umich-sentiment-train.txt"), 'rb') for line in ftrain: label, sentence = line.strip().split("t") words = nltk.word_tokenize(sentence.decode("ascii", "ignore").lower()) if len(words) > maxlen: maxlen = len(words) for word in words: word_freqs[word] += 1 num_recs += 1 ftrain.close() Для нашего корпуса получаем такие числа: maxlen : 42 len(word_freqs) : 2313 192 Глава 6. Рекуррентная нейронная сеть – РНС  Зная количество уникальных слов , мы задаем len(word_freqs) фиксированный размер словаря, а все остальные слова считаем несловарными и заменяем их фиктивным словом UNK (unknown). На этапе предсказания это позволит нам обрабатывать ранее не встречавшиеся слова как несловарные. Зная число слов в предложении ( ), мы можем задать фикmaxlen длину предложения и более короткие предложения дополнять нулями, а более длинные обрезать. Хотя РНС способна обрабатывать последовательности переменной длины, достигается это обычно дополнением и обрезанием, как описано выше, или группировкой входных данных в пакеты, содержащие последовательности одинаковой длины. Мы будем использовать первый подход. Что касается второго, Keras рекомендует пакеты длины 1 (см. ). https://github.com/fchollet/keras/issues/40 Исходя из вычисленных показателей, мы задаем VOCABULARY_SIZE равным . Это слов в словаре плюс фиктивное слово UNK 2002 2000 плюс фиктивное слово PAD (используется для дополнения предложений до фиксированного числа слов, в нашем случае MAX_SENTENCE_ ). LENGTH = 40 DATA_DIR = "../data" MAX_FEATURES = 2000 MAX_SENTENCE_LENGTH = 40 Далее нам понадобится пара таблиц соответствия. Входными данными для РНС является строка индексов слов, причем слова упорядочены по убыванию частоты встречаемости в обучающем наборе. Таблицы соответствия позволяют находить индекс по слову и слово по индексу (включая фиктивные слова PAD и UNK): vocab_size = min(MAX_FEATURES, len(word_freqs)) + 2 word2index = {x[0]: i+2 for i, x in enumerate(word_freqs.most_common(MAX_FEATURES))} word2index["PAD"] = 0 word2index["UNK"] = 1 index2word = {v:k for k, v in word2index.items()} Затем мы преобразуем входные предложения в последовательности индексов слов, дополняя их до слов. ПоMAX_SENTENCE_LENGTH в нашем случае результатом является бинарная величина (положительная или отрицательная эмоциональная окраска), обрабатывать метки не нужно: X = np.empty((num_recs, ), dtype=list) y = np.zeros((num_recs, )) Вентильный рекуррентный блок – GRU 197  Если хотите выполнить эту программу на своей машине, то нужно будет скачать данные с сайта Kaggle. Код этого примера находится в файле в соumich_sentiment_lstm.py исходного кода к этой главе. Вентильный рекуррентный блок – GRU Вентильный рекуррентный блок (gated recurrent unit, GRU) – это вариант LSTM, впервые предложенный К. Чо (см. «Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation», arXiv:1406.1078, 2014). Он также обладает устойчивостью к проблеме исчезающего градиента, но его внутренняя структура проще, а потому и обучается он быстрее, т. е. для обновления скрытого состояния нужно меньше вычислений. На следующем рисунке показаны вентили в ячейке GRU: Вместо трех вентилей в ячейке LSTM – входного, забывания и выходного, в ячейке GRU всего два вентиля: обновления z и сброса r. Вентиль обновления определяет, какую часть предыдущего запомненного значения сохранять, а вентиль сброса – как смешивать новый вход с предыдущей памятью. Не существует никакого постоянного состояния ячейки, отличного от скрытого состояния, как в LSTM. Механизм работы GRU описывается следующими формулами: 198 Глава 6. Рекуррентная нейронная сеть – РНС  z = (W h + U x ) σ z t–1 z t = (W + ) r h U x σ r t–1 r t c = tanh(W (h  r) + U x ) c t–1 c t h = (z  c)  ((1 – z)  h ) t t–1 Согласно эмпирическим оценкам (см. статьи R. Jozefowicz, W. Zaremba, and I. Sutskever «An Empirical Exploration of Recurrent Network Architectures», JMLR, 2015 и J. Chung «Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling», arXiv:1412.3555. 2014), качество GRU и LSTM сравнимо, и дать априорную рекомендацию, какую модель выбрать для конкретной задачи, невозможно. GRU быстрее обучаются и требуют меньше данных для достижения обобщаемости, но в ситуациях, когда обучающих данных достаточно, большая выразительная способность LSTM может приводить к лучшим результатам. Как и LSTM, GRU можно подставить вместо ячейки типа SimpleRNN. Keras предоставляет встроенные реализации LSTM и GRU наряду с рассмотренным выше классом SimpleRNN. Пример GRU – частеречная разметка В Keras есть реализация GRU, которой мы воспользуемся для построения сети частеречной разметки, которая распознает грамматические категории слов: существительные, глаголы, прилагательные и т. д. Раньше частеречную разметку приходилось выполнять вручную, но теперь это делается автоматически с помощью статистических моделей. В последние годы к этой задаче было также применено глубокое обучение (см. R. Collobert «Natural Language Processing (almost) from Scratch», Journal of Machine Learning Research, pp. 2493–2537, 2011). В качестве обучающих данных нам нужны предложения, в которых проставлены метки частей речи. Один из таких наборов, Penn Treebank ( ), содержит https://catalog.ldc.upenn.edu/ldc99t42 размеченный людьми корпус текстов, содержащий примерно 4.5 миллиона слова на американском диалекте английского языка. Но этот ресурс платный. 10%-ая выборка набора Penn Treebank свободно доступна в составе пакета NLTK ( ), и мы http://www.nltk.org/ воспользуемся ей для обучения нашей сети. Наша модель принимает последовательность слов предложения и выводит метки частей речи для каждого слова. Так, для входной Вентильный рекуррентный блок – GRU 199  последовательности, состоящей из слов [The, cat, sat, on, the, mat, .], выходная последовательность будет содержать метки [DT, NN, VB, IN, DT, NN]. Начинаем с импорта: from keras.layers.core import Activation, Dense, Dropout, RepeatVector, SpatialDropout1D from keras.layers.embeddings import Embedding from keras.layers.recurrent import GRU from keras.layers.wrappers import TimeDistributed from keras.models import Sequential from keras.preprocessing import sequence from keras.utils import np_utils from sklearn.model_selection import train_test_split import collections import nltk import numpy as np import os Далее скачиваем корпус NLTK Treebank в формате, удобном для последующей обработки – в уже разобранном виде. Показанный ниже код загружает данные в два параллельных файла: в одном слова, составляющие предложения, а в другом – метки частей речи. DATA_DIR = "../data" fedata = open(os.path.join(DATA_DIR, "treebank_sents.txt"), "wb") ffdata = open(os.path.join(DATA_DIR, "treebank_poss.txt"), "wb") sents = nltk.corpus.treebank.tagged_sents() for sent in sents: words, poss = [], [] for word, pos in sent: if pos == "-NONE-": continue words.append(word) poss.append(pos) fedata.write("{:s}n".format(" ".join(words))) ffdata.write("{:s}n".format(" ".join(poss))) fedata.close() ffdata.close() И на этот раз мы выполним предварительную обработку, что- бы определить размер словаря. Но теперь у нас будет два словаря: исходных слов и целевых меток. Требуется вычислить количество уникальных элементов в каждом словаре. Кроме того, нам нужно знать максимальное число слов в предложении и количество записей. В силу взаимной однозначности частеречной разметки последние два значения одинаковы для обоих словарей. Двунаправленные РНС 205  Двунаправленные РНС Выход РНС в момент времени t зависит от выходов на всех предшествующих временных шагах. Но вполне может случиться, что выход зависит также от будущих выходов. Это справедливо, в частности, для приложений ОЕЯ, когда атрибуты слова или фразы, которые мы пытаемся предсказать, могут зависеть от контекста, определяемого всем объемлющим предложением, а не только предшествующими словами. Двунаправленные РНС помогают построить архитектуру сети, которая придает одинаковую важность началу и концу предложения, и позволяют увеличить объем данных, доступных для обучения. Двунаправленная РНС – это две РНС, собранные вместе, которые читают входные данные в разных направлениях. В нашем примере одна РНС будет читать слова от начала предложения к концу, а другая – от конца к началу. Выход на каждом временном шаге будет зависеть от скрытого состояния обеих РНС. Keras поддерживает двунаправленные РНС с помощью обертывающего слоя . Так, в случае частеречной разметки мы Bidirectional могли бы сделать LSTM-сети двунаправленными, обернув их слоем как показано ниже: Bidirectional from keras.layers.wrappers import Bidirectional model = Sequential() model.add(Embedding(s_vocabsize, EMBED_SIZE, input_length=MAX_SEQLEN)) model.add(SpatialDropout1D(Dropout(0.2))) model.add(Bidirectional(LSTM(HIDDEN_SIZE, dropout=0.2, recurrent_dropout=0.2))) model.add(RepeatVector(MAX_SEQLEN)) model.add(Bidirectional(LSTM(HIDDEN_SIZE, return_sequences=True))) model.add(TimeDistributed(Dense(t_vocabsize))) model.add(Activation("softmax")) Получаемое качество сравнимо с качеством в случае однонаправленной LSTM-сети: 206 Глава 6. Рекуррентная нейронная сеть – РНС  РНС с запоминанием состояния РНС может сохранять состояние при переходе от одного пакета к другому во время обучения. Иначе говоря, скрытое состояние, вычисленное для одного пакета обучающих данных, используется в качестве начального скрытого состояния для следующего пакета. Но этот режим необходимо задавать явно, потому что в Keras РНС по умолчанию не запоминает состояние и сбрасывает его после обработки каждого пакета. Запоминание состояния позволяет РНС строить свое внутреннее состояние на протяжении обработки всей последовательности обучающих данных и даже использовать его на этапе предсказания. К достоинствам РНС с запоминанием состояния следует отнести меньший размер сети и (или) сокращение времени обучения, а к недостаткам – то, что мы теперь несем ответственность за выбор такого размера пакета, который отражает периодичность данных, и за сброс состояния после каждого периода. Кроме того, данные не следует перетасовывать в процессе обучения, потому что для сетей с запоминанием состояния порядок предъявления данных существенен. Пример LSTM с запоминанием состояния – предсказание потребления электричества В этом примере мы предскажем потребление электричества с помощью LSTM-сети с запоминанием и без запоминания состояния и сравним результаты. Напомним, что в Keras РНС по умолчанию не запоминает состояние. В моделях с запоминанием внутреннее состояние, вычисленное для элемента i в предыдущем пакете, будет использоваться в качестве начального состояния элемента i в следующем пакете. Для обучения мы будем использовать набор данных из репозитория машинного обучения UCI ( https://archive.ics.uci.edu/ml/ ), который содержит инфорdatasets/ElectricityLoadDiagrams20112014 о потреблении электричества 370 потребителями с интервалом 15 минут за период с 2011 по 2014 год. Для примера мы случайно выбрали потребителя с номером 250. Большинство задач можно решить с помощью РНС без запоминания состояния, поэтому, прибегая к РНС с запоминанием состояния, нужно понимать, зачем вы это делаете. Как правило, РНС с запоминанием состояния 207  такая необходимость возникает, когда в данных имеется периодическая составляющая. И для потребления электричества действительно характерна периодичность – потребление выше днем и ниже ночью. Выделим данные для потребителя 250, нарисуем график за первые десять дней и сохраним данные в двоичном формате NumPy для следующего шага. import numpy as np import matplotlib.pyplot as plt import os import re DATA_DIR = "../data" fld = open(os.path.join(DATA_DIR, "LD2011_2014.txt"), "rb") data = [] cid = 250 for line in fld: if line.startswith(""";"): continue cols = [float(re.sub(",", ".", x)) for x in line.strip().split(";")[1:]] data.append(cols[cid]) fld.close() NUM_ENTRIES = 1000 plt.plot(range(NUM_ENTRIES), data[0:NUM_ENTRIES]) plt.ylabel("electricity consumption") plt.xlabel("time (1pt = 15 mins)") plt.show() np.save(os.path.join(DATA_DIR, "LD_250.npy"), np.array(data)) На рисунке ниже показан получившийся график: электричества Потребление Двунаправленные РНС 205  Двунаправленные РНС Выход РНС в момент времени t зависит от выходов на всех предшествующих временных шагах. Но вполне может случиться, что выход зависит также от будущих выходов. Это справедливо, в частности, для приложений ОЕЯ, когда атрибуты слова или фразы, которые мы пытаемся предсказать, могут зависеть от контекста, определяемого всем объемлющим предложением, а не только предшествующими словами. Двунаправленные РНС помогают построить архитектуру сети, которая придает одинаковую важность началу и концу предложения, и позволяют увеличить объем данных, доступных для обучения. Двунаправленная РНС – это две РНС, собранные вместе, которые читают входные данные в разных направлениях. В нашем примере одна РНС будет читать слова от начала предложения к концу, а другая – от конца к началу. Выход на каждом временном шаге будет зависеть от скрытого состояния обеих РНС. Keras поддерживает двунаправленные РНС с помощью обертывающего слоя . Так, в случае частеречной разметки мы Bidirectional могли бы сделать LSTM-сети двунаправленными, обернув их слоем как показано ниже: Bidirectional from keras.layers.wrappers import Bidirectional model = Sequential() model.add(Embedding(s_vocabsize, EMBED_SIZE, input_length=MAX_SEQLEN)) model.add(SpatialDropout1D(Dropout(0.2))) model.add(Bidirectional(LSTM(HIDDEN_SIZE, dropout=0.2, recurrent_dropout=0.2))) model.add(RepeatVector(MAX_SEQLEN)) model.add(Bidirectional(LSTM(HIDDEN_SIZE, return_sequences=True))) model.add(TimeDistributed(Dense(t_vocabsize))) model.add(Activation("softmax")) Получаемое качество сравнимо с качеством в случае однонаправленной LSTM-сети: 206 Глава 6. Рекуррентная нейронная сеть – РНС  РНС с запоминанием состояния РНС может сохранять состояние при переходе от одного пакета к другому во время обучения. Иначе говоря, скрытое состояние, вычисленное для одного пакета обучающих данных, используется в качестве начального скрытого состояния для следующего пакета. Но этот режим необходимо задавать явно, потому что в Keras РНС по умолчанию не запоминает состояние и сбрасывает его после обработки каждого пакета. Запоминание состояния позволяет РНС строить свое внутреннее состояние на протяжении обработки всей последовательности обучающих данных и даже использовать его на этапе предсказания. К достоинствам РНС с запоминанием состояния следует отнести меньший размер сети и (или) сокращение времени обучения, а к недостаткам – то, что мы теперь несем ответственность за выбор такого размера пакета, который отражает периодичность данных, и за сброс состояния после каждого периода. Кроме того, данные не следует перетасовывать в процессе обучения, потому что для сетей с запоминанием состояния порядок предъявления данных существенен. Пример LSTM с запоминанием состояния – предсказание потребления электричества В этом примере мы предскажем потребление электричества с помощью LSTM-сети с запоминанием и без запоминания состояния и сравним результаты. Напомним, что в Keras РНС по умолчанию не запоминает состояние. В моделях с запоминанием внутреннее состояние, вычисленное для элемента i в предыдущем пакете, будет использоваться в качестве начального состояния элемента i в следующем пакете. Для обучения мы будем использовать набор данных из репозитория машинного обучения UCI ( https://archive.ics.uci.edu/ml/ ), который содержит инфорdatasets/ElectricityLoadDiagrams20112014 о потреблении электричества 370 потребителями с интервалом 15 минут за период с 2011 по 2014 год. Для примера мы случайно выбрали потребителя с номером 250. Большинство задач можно решить с помощью РНС без запоминания состояния, поэтому, прибегая к РНС с запоминанием состояния, нужно понимать, зачем вы это делаете. Как правило, РНС с запоминанием состояния 207  такая необходимость возникает, когда в данных имеется периодическая составляющая. И для потребления электричества действительно характерна периодичность – потребление выше днем и ниже ночью. Выделим данные для потребителя 250, нарисуем график за первые десять дней и сохраним данные в двоичном формате NumPy для следующего шага. import numpy as np import matplotlib.pyplot as plt import os import re DATA_DIR = "../data" fld = open(os.path.join(DATA_DIR, "LD2011_2014.txt"), "rb") data = [] cid = 250 for line in fld: if line.startswith(""";"): continue cols = [float(re.sub(",", ".", x)) for x in line.strip().split(";")[1:]] data.append(cols[cid]) fld.close() NUM_ENTRIES = 1000 plt.plot(range(NUM_ENTRIES), data[0:NUM_ENTRIES]) plt.ylabel("electricity consumption") plt.xlabel("time (1pt = 15 mins)") plt.show() np.save(os.path.join(DATA_DIR, "LD_250.npy"), np.array(data)) На рисунке ниже показан получившийся график: электричества Потребление 208 Глава 6. Рекуррентная нейронная сеть – РНС  Как видите, имеется отчетливая суточная периодичность, так что для этой задачи модель с запоминанием состояния отлично подойдет. Кроме того, из результатов наблюдения следует, что имеет смысл взять равным 96 (количество 15-минутных BATCH_SIZE отсчетов за 24 часа). Мы будем показывать одновременно код обеих версий: с запоминанием и без запоминания состояния, поскольку почти весь код совпадает. На отличия будем специально обращать внимание. Как обычно, сначала импортируем необходимые библиотеки и классы: from keras.layers.core import Dense from keras.layers.recurrent import LSTM from keras.models import Sequential from sklearn.preprocessing import MinMaxScaler import numpy as np import math import os Затем загрузим данные для потребителя 250 в большой массив (размера 140256) из сохраненного ранее двоичного файла NumPy и приведем его к диапазону (0, 1). Наконец, изменим форму входных данных на трехмерную, как того требует наша сеть: DATA_DIR = "../data" data = np.load(os.path.join(DATA_DIR, "LD_250.npy")) data = data.reshape(-1, 1) scaler = MinMaxScaler(feature_range=(0, 1), copy=False) data = scaler.fit_transform(data) При обработке каждого пакета модель принимает последовательность 15-минутных отсчетов и предсказывает следующий. Длина входной последовательности определяется переменной . NUM_TIMESTEPS По результатам экспериментов мы выбрали значение , NUM_TIMESTEPS=20 т. е. длина входной последовательности равна 20, а выходной – 1. На следующем шаге входной массив преобразовывается в тензоры и X Y формы и . И наконец, входной тензор преобразует(None, 4) (None, 1) X ся в трехмерный в соответствии с требованиями сети: X = np.zeros((data.shape[0], NUM_TIMESTEPS)) Y = np.zeros((data.shape[0], 1)) for i in range(len(data) - NUM_TIMESTEPS - 1): X[i] = data[i:i + NUM_TIMESTEPS].T Y[i] = data[i + NUM_TIMESTEPS + 1] # изменить форму X, приведя его к трем измерениям (отсчеты, временные шаги, признаки) X = np.expand_dims(X, axis=2) РНС с запоминанием состояния 209  Далее мы разбиваем тензоры и на обучающий и тестовый X Y набор в пропорции 70:30. Поскольку мы работаем с временными рядами, то просто выбираем точку разделения и разрезаем данные на две части, не пользуясь функцией , которая train_test_split дополнительно перетасовывает данные: sp = int(0.7 * len(data)) Xtrain, Xtest, Ytrain, Ytest = X[0:sp], X[sp:], Y[0:sp], Y[sp:] print(Xtrain.shape, Xtest.shape, Ytrain.shape, Ytest.shape) Сначала определяется модель без сохранения соединения. Кроме того, задаются значения переменных и . BATCH_SIZE NUM_TIMESTEPS Размер выхода LSTM-сети определяется переменной , HIDDEN_SIZE это еще один гиперпараметр, который обычно выставляется по результатам экспериментов. Мы задали значение 10, потому что наша цель – просто сравнить две сети: NUM_TIMESTEPS = 20 HIDDEN_SIZE = 10 BATCH_SIZE = 96 # 24 часа (количество 15-минутных интервалов) # без сохранения состояния model = Sequential() model.add(LSTM(HIDDEN_SIZE, input_shape=(NUM_TIMESTEPS, 1), return_sequences=False)) model.add(Dense(1)) Определение соответствующей модели с сохранением состояния очень похоже (см. ниже). В конструкторе LSTM нужно задать параметр , а вместо параметра , в котором stateful=True input_shape подразумевается, что размер пакета определяется на этапе выполнения, мы задаем параметр , где этот размер batch_input_shape указывается явно. Кроме того, размеры обучающего и тестового набора данных должны быть кратны размеру пакета. Как этого добиться, мы увидим ниже при рассмотрении кода обучения. # с сохранением состояния model = Sequential() model.add(LSTM(HIDDEN_SIZE, stateful=True, batch_input_shape=(BATCH_SIZE, NUM_TIMESTEPS, 1), return_sequences=False)) model.add(Dense(1)) Код компиляции модели одинаков для обеих РНС. Отметим, что в роли показателя качества выступает не верность, как обычно, а среднеквадратическая ошибка, поскольку мы имеем задачу регрессии: нас интересует не совпадение предсказания с меткой, а расхождение между предсказаниями и метками. Полный перечень встроенных в Keras показателей качества имеется в документации. 210 Глава 6. Рекуррентная нейронная сеть – РНС  model.compile(loss="mean_squared_error", optimizer="adam", metrics=["mean_squared_error"]) Для обучения модели без состояния достаточно одной строки, которая вам, наверное, уже стала привычной: BATCH_SIZE = 96 # 24 часа (количество 15-минутных интервалов) # без сохранения состояния model.fit(Xtrain, Ytrain, epochs=NUM_EPOCHS, batch_size=BATCH_SIZE, validation_data=(Xtest, Ytest), shuffle=False) Соответствующий код для модели с сохранением состояния показан ниже. В нем есть три важных момента. Во-первых, размер пакета должен отражать периодичность данных, поскольку РНС с сохранением состояния сопоставляет состояния соответственных элементов соседних пакетов, а это значит, что при правильном размере пакета сеть будет обучаться быстрее. Размеры обучающего и тестового набора должны быть кратны размеру пакета. Мы обеспечили выполнение этого условия, отбросив последние несколько записей в обоих наборах. Во-вторых, мы в цикле обучаем модель на протяжении заданного числа периодов, при этом состояние сохраняется при переходе от пакета к пакету. Но после каждого периода состояние модели необходимо сбросить вручную. В-третьих, данные необходимо подавать строго последовательно. По умолчанию Keras перетасовывает данные в каждом пакете, а это нарушает соответствие элементов, необходимое для эффективного обучения РНС с сохранением состояния. Чтобы отменить тасование, задается параметр при обращении shuffle=False к : model.fit() BATCH_SIZE = 96 # 24 часа (количество 15-минутных интервалов) # с сохранением состояния # размеры обучающего и тестового набора должны быть кратны BATCH_SIZE train_size = (Xtrain.shape[0] // BATCH_SIZE) * BATCH_SIZE test_size = (Xtest.shape[0] // BATCH_SIZE) * BATCH_SIZE Xtrain, Ytrain = Xtrain[0:train_size], Ytrain[0:train_size] Xtest, Ytest = Xtest[0:test_size], Ytest[0:test_size] print(Xtrain.shape, Xtest.shape, Ytrain.shape, Ytest.shape) for i in range(NUM_EPOCHS): print("Epoch {:d}/{:d}".format(i+1, NUM_EPOCHS)) model.fit(Xtrain, Ytrain, batch_size=BATCH_SIZE, epochs=1, validation_data=(Xtest, Ytest), shuffle=False) model.reset_states()