Uploaded by igor2560

TensorFlow для глубокого обучения

advertisement
O'REILLY
TensorFlow для
глубокого обучения
ОТ ЛИНЕЙНОЙ РЕГРЕССИИ ДО ОБУЧЕНИЯ С МАКСИМИЗАЦИЕЙ ПОДКРЕПЛЕНИЯ
Бхарат Рамсундар
Реза Босаг Заде
TensorFlow for Deep Learning
From Linear Regression to
Reinforcement Learning
Bharath Ramsundar and Reza Bosagh Zadeh
Beijing * Boston * Farnham * Sebastopol * Tokyo
O’REILLY
TensorFlow
для глубокого обучения
Бхарат Рамсундар
Реза Босаг Заде
Санкт-Петербург
«БХВ-Петербург»
2019
УДК 004.85
ББК 32.973.26-018
Р21
Рамсундар, Б.
Р21
TensorFlow для глубокого обучения: Пер. с англ. / Б. Рамсундар,
Р. Б. Заде. — СПб.: БХВ-Петербург, 2019. — 256 с.: ил.
ISBN 978-5-9775-4014-8
Книга знакомит с основами программной библиотеки TensorFlow и принципа­
ми глубокого обучения, начиная с нулевого уровня. В книге рассмотрены базовые
вычисления в библиотеке TensorFlow, простые обучающиеся системы и их по­
строение, полносвязные глубокие сети, прототипы и превращение прототипов
в высококачественные модели, сверхточные нейронные сети и обработка изобра­
жений, рекуррентные нейронные сети и наборы естественно-языковых данных,
способы обучения с максимизацией подкрепления на примере известных игр,
приемы тренинга глубоких сетей с помощью графических и тензорных процессоров.
Для разработчиков систем машинного обучения
УЖ 004.85
ББК 32.973.26-018
Группа подготовки издания:
Руководитель проекта
Зав. редакцией
Перевод с английского
Компьютерная верстка
Оформление обложки
Евгений Рыбаков
Екатерина Сависте
Андрея Логунова
Ольги Сергиенко
Карины Соловьевой
©2019BHV
Authorized Russian translation of the English edition of TensorFlow for Deep Learning ISBN 978-1-491-98045-3
©2018 Reza Zadeh, Bharath Ramsundar.
This translation is published and sold by permission of O'Reilly Media, Inc., which owns or controls all rights
to publish and sell the same.
Авторизованный русский перевод английской редакции книги TensorFlow for Deep Learning
ISBN 978-1-491-98045-3 © 2018 Reza Zadeh, Bharath Ramsundar.
Перевод опубликован и продается с разрешения O'Reilly Media, Inc., собственника всех прав на публикацию
и продажу издания.
Подписано в печать 04.02.19.
Формат 70х1001/16. Печать офсетная. Усл. печ. л. 20,64.
Тираж 1000 экз. Заказ №8426.
"БХВ-Петербург", 191036, Санкт-Петербург, Гончарная ул., 20.
Отпечатано с готового оригинал-макета
ООО "Принт-М", 142300, М.О., г. Чехов, ул. Полиграфистов, д. 1
ISBN 978-1-491-98045-3 (англ.)
ISBN 978-5-9775-4014-8 (рус.)
©2018 Reza Zadeh, Bharath Ramsundar
© Перевод на русский язык, оформление ООО "БХВ-Петербург",
ООО "БХВ", 2019
Оглавление
Об авторах........................................................................................................................11
Предисловие....................................................................................................................13
Условные обозначения, принятые в этой книге...............................................................................13
Использование примеров программ................................................................................................... 14
Признательности.................................................................................................................................... 14
Комментарии переводчика................................................................................................................... 15
Исходный код.................................................................................................................................. 16
Протокол установки библиотек................................................................................................... 17
Установка библиотек Python из whl-файлов............................................................................ 17
Глава 1. Введение в глубокое самообучение.............................................................19
Машинное самообучение "питается" информатикой...................................................................... 19
Глубоко обучающиеся примитивы.....................................................................................................21
Полносвязный слой....................................................................................................................... 21
Сверточный слой........................................................................................................................... 22
Слои рекуррентной нейронной сети.......................................................................................... 22
Ячейки долгой краткосрочной памяти......................................................................................23
Глубоко обучающиеся архитектуры.................................................................................................. 24
LeNet................................................................................................................................................ 24
AlexNet............................................................................................................................................. 24
ResNet.............................................................................................................................................. 25
Нейронная модель титрования изображений...........................................................................26
Нейронный машинный перевод Google.................................................................................... 27
Однократные модели.....................................................................................................................27
AlphaGo...........................................................................................................................................29
Генеративно-состязательные сети.............................................................................................. 31
Нейронные машины Тьюринга................................................................................................... 31
Вычислительные каркасы для глубокого самообучения............................................................... 32
Ограничения вычислительной среды TensorFlow.................................................................. 33
Резюме......................................................................................................................................................34
Глава 2. Введение в примитивы TensorFlow........................................................... 35
Введение в тензоры............................................................................................................................... 35
Скаляры, векторы и матрицы......................................................................................................36
Матричная математика................................................................................................................. 39
Тензоры............................................................................................................................................ 41
Тензоры в физике........................................................................................................................... 42
Математические ремарки............................................................................................................. 44
5
Базовые вычисления в TensorFlow.....................................................................................................44
Установка TensorFlow и начало работы.................................................................................. 45
Инициализация константных тензоров................................................................................... 45
Отбор случайных значений для тензоров................................................................................ 47
Сложение и шкалирование тензоров........................................................................................ 48
Матричные операции...................................................................................................................49
Типы тензоров................................................................................................................................ 50
Обработка форм тензоров........................................................................................................... 51
Введение в операцию транслирования..................................................................................... 52
Императивное и декларативное программирование...................................................................... 53
Графы TensorFlow......................................................................................................................... 55
Сеансы TensorFlow....................................................................................................................... 55
Переменные TensorFlow.............................................................................................................. 56
Резюме..................................................................................................................................................... 58
Глава 3. Линейная и логистическая регрессия с помощью TensorFlow............. 59
Математический обзор......................................................................................................................... 59
Функции и дифференцируемость.............................................................................................. 59
Функции потерь............................................................................................................................ 61
Классификация и регрессия.............................................................................................. 62
£2-потеря................................................................................................................................ 63
Режимы сбоя функции £2-потери..................................................................................... 63
Распределения вероятностей............................................................................................ 64
Перекрестно-энтропийная потеря....................................................................................65
Градиентный спуск....................................................................................................................... 66
Системы автоматического дифференцирования..................................................................... 69
Самообучение с помощью TensorFlow............................................................................................. 70
Создание игрушечных наборов данных...................................................................................71
Чрезвычайно краткое введение в NumPy....................................................................... 71
Почему важны игрушечные наборы данных?............................................................... 71
Добавление шума с помощью гауссиан.......................................................................... 72
Игрушечные наборы регрессионных данных............................................................... 73
Игрушечные наборы классификационных данных...................................................... 74
Новые понятия TensorFlow......................................................................................................... 76
Заполнители......................................................................................................................... 76
Словари передачи данных и выборки............................................................................. 76
Области имен....................................................................................................................... 77
Оптимизаторы...................................................................................................................... 77
Взятие градиентов с помощью TensorFlow.................................................................... 78
Сводки и пишущие объекты для TensorBoard.............................................................. 79
Тренировка моделей с помощью TensorFlow.................................................................80
Тренировка линейной и логистической моделей в TensorFlow...................................................80
Линейная регрессия в TensorFlow............................................................................................. 80
Определение и тренировка линейной регрессии в TensorFlow................................. 81
Визуализация линейных регрессионных моделей с помощью TensorBoard........... 83
Метрические показатели для оценивания регрессионных моделей......................... 86
Логистическая регрессия в TensorFlow.................................................................................... 89
Визуализация логистических регрессионных моделей с помощью TensorBoard..... 90
Метрические показатели для оценивания классификационных моделей............... 93
Резюме..................................................................................................................................................... 94
6 | Оглавление
Глава 4. Полносвязные глубокие сети...................................................................... 95
Что такое полносвязная глубокая сеть?............................................................................................. 95
"Нейроны” в полносвязных сетях.......................................................................................................97
Обучающиеся полносвязные сети с обратным распространением..................................... 99
Теорема об универсальной сходимости.................................................................................. 100
Почему именно глубокие сети?................................................................................................. 102
Тренировка полносвязных нейронных сетей..................................................................................102
Заучиваемые представления...................................................................................................... 102
Активации..................................................................................................................................... ЮЗ
Полносвязные сети запоминают...............................................................................................104
Регуляризация...............................................................................................................................104
Отсев..................................................................................................................................... 105
Ранняя остановка................................................................................................................ 106
Регуляризация весов.......................................................................................................... 107
Тренировка полносвязных сетей.............................................................................................. 108
Мини-пакетирование......................................................................................................... 108
Скорости заучивания......................................................................................................... 108
Реализация в TensorFlow..................................................................................................................... 109
Инсталляция DeepChem............................................................................................................. 109
Набор данных Тох21................................................................................................................... 109
Принятие мини-пакетов заполнителей.................................................................................... 110
Реализация скрытого слоя.......................................................................................................... 111
Добавление отсева в скрытый слой.......................................................................................... 112
Реализация мини-пакетирования............................................................................................. 113
Оценивание точности модели.................................................................................................... 113
Использование пакета TensorBoard для отслеживания схождения модели.................... 114
Резюме.................................................................................................................................................... 116
Глава 5. Гнперпараметрнческая оптимизация..................................................... 117
Оценивание модели и гиперпараметрическая оптимизация....................................................... 118
Метрики, метрики, метрики...............................................................................................................120
Бинарно-классификационные показатели...............................................................................120
Метрические показатели мультиклассовой классификации............................................... 123
Регрессионные метрические показатели.................................................................................124
Алгоритмы оптимизации гиперпараметров.................................................................................... 125
Установление ориентира............................................................................................................ 126
Спуск студента магистратуры................................................................................................... 128
Решеточный поиск гиперпараметров....................................................................................... 129
Случайный поиск гиперпараметров......................................................................................... 130
Задание для читателя................................................................................................................... 132
Резюме.................................................................................................................................................... 132
Глава 6. Сверточные нейронные сети..................................................................... 133
Введение в сверточные архитектуры................................................................................................134
Локальные рецептивные поля................................................................................................... 134
Сверточные ядра.......................................................................................................................... 136
Редуцирующие слои.................................................................................................................... 138
Конструирование сверточных сетей........................................................................................ 139
Растянутые свертки..................................................................................................................... 139
Оглавление | 7
Применения сверточных сетей......................................................................................................... 140
Обнаружение и локализация объектов................................................................................... 140
Сегментация изображений........................................................................................................ 141
Графовые свертки....................................................................................................................... 142
Генерирование изображений с помощью вариационных автокодировщиков................ 144
Состязательные модели.................................................................................................... 146
Тренировка сверточной сети в TensorFlow.................................................................................... 147
Набор данных MNIST................................................................................................................ 147
Скачивание набора данных MNIST........................................................................................ 148
Сверточные примитивы TensorFlow....................................................................................... 152
Сверточная архитектура............................................................................................................. 153
Оценивание натренированных моделей................................................................................. 158
Задание для читателя.................................................................................................................. 160
Резюме....................................................................................................................................................160
Глава 7. Рекуррентные нейронные сети................................................................. 161
Обзор рекуррентных архитектур...................................................................................................... 162
Рекуррентные ячейки.......................................................................................................................... 164
Долгая краткосрочная память................................................................................................... 164
Вентильные рекуррентные блоки............................................................................................. 166
Применение рекуррентных моделей................................................................................................ 166
Получение образцов из рекуррентных сетей........................................................................ 167
Модели Seq2seq........................................................................................................................... 167
Нейронные машины Тьюринга.........................................................................................................169
Работа с рекуррентными нейронными сетями на практике........................................................ 171
Обработка корпуса Penn Treebank............................................................................................ 171
Программный код для предобработки.................................................................................... 173
Загрузка данных в TensorFlow.................................................................................................. 175
Базовая рекуррентная архитектура..........................................................................................177
Задание для читателя.................................................................................................................. 179
Резюме....................................................................................................................................................179
Глава 8. Самообучение с максимизацией подкрепления.................................... 181
Марковские процессы принятия решений..................................................................................... 185
Алгоритмы для самообучения с максимизацией подкрепления................................................ 187
0-заучивание................................................................................................................................ 188
Заучивание стратегии................................................................................................................. 189
Асинхронная тренировка........................................................................................................... 191
Ограничения самообучения с максимизацией подкрепления.................................................... 191
Игра крестики-нолики......................................................................................................................... 193
Объектная ориентированность................................................................................................. 194
Абстрактная среда.......................................................................................................................194
Среда игры крестики-нолики.................................................................................................... 195
Слоевая абстракция.................................................................................................................... 198
Определение графа слоев.......................................................................................................... 201
Алгоритм АЗС...................................................................................................................................... 206
Функция потери.......................................................................................................................... 211
Определение рабочих процессов............................................................................................. 213
Разворачивание игровой ситуации в рабочих процессах....................................................214
Тренировка стратегии................................................................................................................217
8 | Оглавление
Задание для читателя........................................................................................................................... 218
Резюме................................................................................................................................................... 218
Глава 9. Тренировка крупных глубоких сетей...................................................... 219
Специальное аппаратное обеспечение для глубоких сетей........................................................ 219
Тренировка на CPU............................................................................................................................. 220
Тренировка на GPU.................................................................................................................... 221
Тензорные процессоры............................................................................................................... 222
Программируемые пользователем вентильные матрицы................................................... 224
Нейроморфные чипы.................................................................................................................. 224
Распределенная тренировка глубоких сетей.................................................................................. 225
Параллелизм данных.................................................................................................................. 226
Параллелизм моделей................................................................................................................. 227
Параллельная тренировка на данных CifarlO с использованием многочисленных GPU......... 228
Скачивание и загрузка данных................................................................................................. 229
Глубокое погружение в архитектуру...................................................................................... 231
Тренировка на многочисленных GPU..................................................................................... 234
Задание для читателя........................................................................................................................... 237
Резюме................................................................................................................................................... 237
Глава 10. Будущее глубокого самообучения.......................................................... 239
Глубокое самообучение вне технологической индустрии...........................................................240
Глубокое самообучение в фармацевтической промышленности...................................... 241
Глубокое самообучение в юстиции.......................................................................................... 241
Глубокое самообучение для робототехники..........................................................................242
Глубокое самообучение в сельском хозяйстве..................................................................... 242
Этическое использование глубокого самообучения.................................................................... 243
Действительно ли универсальный искусственный интеллект неизбежен?..............................245
Куда направиться дальше?................................................................................................................ 246
Предметный указатель............................................................................................... 247
Оглавление | 9
Об авторах
Бхарат Рамсундар (Bharath Ramsundar) получил степень бакалавра гуманитарных
и естественных наук по электротехнике, компьютерным наукам и математике в ин­
ституте Беркли Калифорнийского университета, с отличием закончил выпускной
класс по математике. В настоящее время он является аспирантом в области вычис­
лительной техники в Стэнфордском университете в лаборатории Панде. Его иссле­
довательская работа лежит в сфере применения глубокого обучения при создании
медицинских препаратов. В частности, Бхарат является ведущим разработчиком
DeepChem.io— пакета с открытым исходным кодом, основанного на библиотеке
TensorFlow, который ориентирован на демократизацию использования глубокого
обучения при создании медицинских препаратов. Он стипендиат аспирантуры Гер­
ца, самой избирательной аспирантуры в данной области наук.
Реза Босаг Заде (Reza Bosagh Zadeh) — основатель и генеральный директор ком­
пании Matroid и адъюнкт-профессор в Стэнфордском университете. Его профес­
сиональная деятельность лежит в области машинного обучения, вычислительной
и дискретной прикладной математики. Реза получил докторскую степень по вычис­
лительной математике под руководством Гуннара Карлссона в Стэнфордском
университете. Его награды включают премию KDD Best Paper Award в области от­
крытия знаний в базах данных и премию Gene Golub Outstanding Thesis Award за
выдающуюся дипломную работу. Он работал в технических консультативных сове­
тах Microsoft и Databricks.
В рамках своего исследования Реза занимался построением машинно-обучающихся
алгоритмов, лежащих в основе системы Twitter "кого-читать", которая стала пер­
вым продуктом с использованием машинного обучения в этой социальной сети.
Реза является инициатором создания линейно-алгебраического пакета в Apache
Spark, и его работа была включена в промышленные и академические кластерные
вычислительные среды. В дополнение к исследованиям, Реза разработал и препода­
ет два класса уровня докторантуры в Стэнфордском университете: распределенные
алгоритмы и оптимизация (СМЕ 323) и дискретная математика и алгоритмы
(СМЕ 305).
11
Предисловие
Эта книга познакомит вас с фундаментальными принципами машинного обучения
с использованием TensorFlow— новой программной библиотеки компании Google
для глубокого обучения, которая значительно облегчает инженерам работу по про­
ектированию и внедрению сложных глубоко обучающихся архитектур. Вы позна­
комитесь с тем, как использовать библиотеку TensorFlow для построения систем,
способных обнаруживать объекты на изображениях, понимать человеческий текст
и предсказывать свойства потенциальных лекарств. Кроме того, вы получите
интуитивное понимание возможностей TensorFlow как системы для выполнения
тензорного исчисления и узнаете, как использовать ее для задач, лежащих за преде­
лами традиционной сферы машинного обучения.
Важно отметить, что эта книга является одной из первых книг по глубокому обуче­
нию, написанных для практикующих специалистов. Она обучает фундаментальным
понятиям на практических примерах и развивает понимание основ машинного обу­
чения, начиная с нулевого уровня. Целевая аудитория этой книги — практикующие
разработчики, которые не испытывают проблем с проектированием программных
систем, но отнюдь не всегда справляются с созданием обучающихся систем. По
ходу изложения мы лишь изредка будем использовать элементарную линейную
алгебру и математический анализ, но при этом рассмотрим все необходимые фун­
даментальные принципы. Мы также ожидаем, что наша книга окажется полезной
для ученых и других специалистов, которые не испытывают проблем с написанием
сценариев, но отнюдь не всегда — с проектированием обучающихся алгоритмов.
Условные обозначения, принятые в этой книге
В книге используются следующие типографические условные обозначения:
♦ курсивный шрифт указывает новые термины;
♦ полужирный шрифт служит для выделения интернет-адресов, элементов ин­
терфейса программных продуктов;
♦
моноширинный шрифт используется для листингов программ, а также внутри абзацев
для отсылки на элементы программ, такие как переменные или имена функций,
базы данных, типы данных, переменные среды, инструкции и ключевые слова;
♦ полужирный моноширинный шрифт показывает команды либо другой текст, который
должен быть напечатан непосредственно пользователем;
13
♦ курсивный моноширинный шрифт показывает текст, который должен быть заменен на
предоставленные пользователем значения либо на значения, определяемые кон­
текстом.
Й W
Данный элемент обозначает общее замечание.
Данный элемент обозначает подсказку или совет.
Данный элемент обозначает предупреждение или предостережение.
Использование примеров программ
Дополнительные материалы (примеры кода, упражнения и т. д.) доступны для ска­
чивания по адресу https://github.com/matroid/dlwithtf .
У нас есть веб-страница для этой книги, где мы размещаем опечатки, примеры и
любую дополнительную информацию. Вы можете получить доступ к этой странице
по адресу http://bit.ly/tensorflowForDeepLearning .
Признательности
Бхарат благодарен своему научному руководителю по докторской диссертации за
то, что он дал ему возможность работать над этой книгой по ночам и в выходные
дни, и особо благодарен своей семье за их неизменную поддержку в течение всего
процесса.
Реза благодарен сообществам разработчиков открытого программного обеспече­
ния, на которых основывается подавляющая часть программного обеспечения
и исследований в области информатики. Программное обеспечение с открытым
исходным кодом является одной из крупнейших концентраций человеческих
знаний, когда-либо созданных, и эта книга была бы невозможна без всего сооб­
щества.
14
|
Предисловие
Комментарии переводчика
В центре внимания машинного самообучения и глубокого самообучения как под­
областей информатики находятся алгоритмы, модели и системы, способные обу­
чаться. Обучающаяся система — это система, способная с течением времени улуч­
шать свою работу, используя поступающую информацию1. В зарубежной специа­
лизированной литературе по данной теме в отличие от русского языка для передачи
знаний и приобретения знаний существуют отдельные термины — train (обучить,
натренировать) и learn (обучиться). Приведенное ниже предложение из главы 7 на­
стоящей книги четко это демонстрирует.
Neural Turing machine can be trained end-to-end to learn to perform arbitrary compu­
tations.
Нейронная машина Тьюринга может быть натренирована в сквозном порядке,
чтобы обучиться производить любые вычисления.
Тренировка (training)— это работа, которую выполняет исследователь-проекти­
ровщик для получения работающей самообучающейся модели или системы.
В основе такой модели или системы лежит обучающийся алгоритм, который пред­
ставляет собой "не что иное, как искателя минимумов (или максимумов) для над­
лежащим образом сформулированных функций". Самообучение (learning)— это
работа, которую выполняет алгоритм-ученик, это процесс приобретения новых или
изменения и закрепления существующих знаний, поведения, навыков, ценностей
или предпочтений1
2.
Итак, русский термин "обучение" несет в себе двусмысленность, потому что под
ним может подразумеваться и передача знаний, и получение знаний. С другой
стороны, появление термина learning в любом виде подразумевает исключительно
второе — работу, выполняемую обучающимся алгоритмом, т. е. самообучение,
заучивание алгоритмом весов и параметров. Поэтому в отличие от русского тер­
мина "машинное обучение", который может означать и тренировку машин, и спо­
собность машин обучаться, в своей основе английский термин machine learning
обозначает именно машинное приобретение знаний.
По этому поводу следует особо отметить следующий момент. С начала 1960-х до
середины 1980-х годов в ходу был термин "обучающиеся машины". Проблематика
обучающихся и самопроизводящихся машин изучалась в работах А. Тьюринга
"Может ли машина мыслить?", 1960 г. (обучающиеся машины), К. Шеннона "Рабо­
ты по теории информации и кибернетике" (самовоспроизводящиеся машины),
Н. Винера, "Кибернетика, или управление и связь в животном и машине" (1961),
Н. Нильсона "Обучающиеся машины" (1974) и Я. 3. Цыпкина "Основы теории обу­
чающихся систем" (1970).
1 См. https://ru.wikipedia.org/wiki/06y4aioiuancn_CHCTeMa, а также
https://bigenc.ru/mathematics/text/1810335. — Прим. пер.
2 См. http://www.basicknowledgel01.eom/subjects/learningstyles.html#diy. — Прим. пер.
Предисловие
|
15
В сухом остатке, английский термин machine learning более точно передается тер­
мином "машинное самообучение" или "технология обучающихся машин". Интерес­
ный факт: в испанском языке он переводится как aprendizaje automatico, т. е. авто­
матическое самообучение.
В настоящем переводе далее за основу принят зарубежный подход, который неиз­
бежно заставил подкорректировать терминологию. Вот несколько таких корректи­
ровок. Соответствующие области информатики переведены как машинное само­
обучение и глубокое самообучение. Применяемые в этих областях алгоритмы, моде­
ли и системы переведены как обучающиеся, машинно-обучающиеся и глубоко
обучающиеся. То есть акцент делается не на классификации алгоритма в соответст­
вующей иерархии, а на его характерном свойстве. Далее, методы, которые реали­
зуются в обучающихся алгоритмах, переведены как методы самообучения (ср. ме­
тоды обучения). Как известно, эти методы делятся на три широкие категории. Сле­
дуя принципу бритвы Оккама, они переведены как методы контролируемого
самообучения (ср. обучение с учителем), методы неконтролируемого самообучения
(ср. обучение без учителя) и методы самообучения с максимизацией подкрепления
(ср. обучение с подкреплением). Последний термин обусловлен особенностью ле­
жащего в его основе алгоритма — "учиться максимизировать некое понятие возна­
граждения", получаемого за правильное действие3. Среди многих гиперпараметров,
которые позволяют настроить работу обучающегося алгоритма, имеется rate of
(earing, который переведен как скорость заучивания (ср. темп обучения).
Исходный код
Перевод книги снабжен пояснениями и ссылками, размещенными в сносках. Дан­
ная книга действительно является полезным ресурсом из разряда "все, что вам
нужно знать о глубоком самообучении" на профессиональном уровне.
Элементарная кодовая база книги протестирована в среде Windows 10. При тести­
ровании исходного кода за основу взят Python версии 3.6.4 (время перевода —
апрель 2018 г.).
В ЭТОЙ книге используются библиотеки tensorflow, numpy, matplotlib, scikit-learn И
deepchem (https://deepchem.io ). В обычных условиях библиотеки Python можно ска­
чать и установить из каталога библиотек Python PyPi (https://pypi.python.org/) при
помощи менеджера пакетов pip. Однако следует учесть, что в ОС Windows для ра­
боты некоторых библиотек, в частности scipy, scikit-learn и scikit-image, требует­
ся, чтобы в системе была установлена библиотека Numpy+MKL. Библиотека
Numpy+MKL привязана к библиотеке Intel® Math Kernel Library и включает в свой
состав необходимые динамические библиотеки (DLL) в каталоге пшпру.соге. Биб­
лиотеку Numpy+MKL следует скачать из хранилища whl-файлов на веб-странице
Кристофа Голька из Лаборатории динамики флуоресценции Калифорнийского уни­
верситета в г. Ирвайн (http://www.lfd.uci.edu/~gohlke/pythonlibs/) и установить при
3 См. https://en.wikipedia.org/wiki/Reinforcement_learning . — Прим. пер.
16
|
Предисловие
помощи менеджера пакетов pip как whl (соответствующая процедура установки
пакетов в формате WHL описана далее). Например, для 64-разрядной операцион­
ной системы Windows и среды Python 3.6 команда будет такой:
pip install numpy-1.14.2+mkl-cp36-cp36m--win_amd64 .whl
Стоит также отметить, что эти особенности установки не относятся к ОС Linux
и Mac OS X.
Протокол установки библиотек
Далее предлагается список команд установки библиотек, скачанных с хранилища
whl-файлов.
python -m pip install —upgrade pip
pip install numpy-1.14.2+mkl-cp36-cp36m-win_amd64 .whl
pip install scikit_learn-0.19.1-cp36-cp36m-win_aind64.whl
Библиотеки
tensorflow
и matpiotiib устанавливаются стандартным образом:
pip install tensorflow
pip install matpiotiib
Примечание
В зависимости от базовой ОС, версий языка Python и версий программных
библиотек устанавливаемые вами версии whl-файлов могут отличаться от
приведенных выше, где показаны последние на май 2018 г. версии для
64-разрядной ОС Windows и Python 3.6.4.
Установка библиотек Python из whl-файлов
Библиотеки для Python можно разрабатывать не только на чистом Python. Довольно
часто библиотеки программируются на С (динамические библиотеки), и для них
пишется обертка Python, или же библиотека пишется на Python, но для оптимиза­
ции узких мест часть кода пишется на С. Такие библиотеки получаются очень
быстрыми, однако библиотеки с вкраплениями кода на С программисту на Python
тяжелее установить ввиду банального отсутствия соответствующих знаний либо
необходимых компонентов и настроек в рабочей среде (в особенности в Windows).
Для решения описанных проблем разработан специальный формат (файлы с рас­
ширением whl) для распространения библиотек, который содержит заранее ском­
пилированную версию библиотеки со всеми ее зависимостями. Формат WHL под­
держивается всеми основными платформами (Mac OS X, Linux, Windows).
Установка производится с помощью менеджера библиотек pip. В отличие от обыч­
ной установки командой pip install <имя_бибтотеки>, вместо имени библиотеки
указывается путь к whl-файлу: pip install <путь_к^Ь1-файлу>. Например,
pip install C:\temp\scipy-1.0.1-cp36-cp36m-win_amd64.whl
Предисловие
|
17
Откройте окно командой строки и при помощи команды cd перейдите в каталог, где
размещен ваш whl-файл. Просто скопируйте туда ваш whl-файл. В этом случае
полный путь указывать не понадобится. Например,
pip install scipy-1.О.l-cp36-cp36m-win_amd64.whl
При выборе библиотеки важно, чтобы разрядность устанавливаемой библиотеки
и разрядность интерпретатора совпадали. Пользователи Windows могут брать
whl-файлы с веб-сайта Кристофа Голька. Библиотеки там постоянно обновляются,
и в архиве содержатся все, какие только могут понадобиться.
18
|
Предисловие
ГЛАВА 1
Введение
в глубокое самообучение
Глубокое самообучение произвело революцию в технологической индустрии. Со­
временный машинный перевод, поисковые системы и компьютерные помощники
работают на основе глубокого самообучения. Эта тенденция будет только продол­
жаться по мере того, как глубокое самообучение будет распространяться на робо­
тотехнику, фармацевтические препараты, энергетику и другие области современ­
ных технологий. Для специалиста в области разработки программного обеспечения
приобретение практических навыков в сфере глубокого самообучения быстро ста­
новится первостепенной необходимостью.
В этой главе мы познакомим вас с историей глубокого самообучения и с его более
широким влиянием на исследовательские и деловые сообщества. Далее мы рас­
смотрим несколько самых знаменитых приложений глубокого самообучения. Они
будут включать как выдающиеся машинно-обучающиеся архитектуры, так и фун­
даментальные глубоко обучающиеся примитивы. В конце мы представим краткий
обзор направлений, по которым пойдет развитие глубокого самообучения в течение
последующих нескольких лет, после чего в следующих нескольких главах мы по­
грузимся в TensorFlow.
Машинное самообучение
"питается" информатикой
До недавнего времени будущие инженеры-программисты посещали лекции в спе­
циализированных учебных заведениях, чтобы изучить ряд базовых алгоритмов (по­
иск в графах, сортировку, запросы к базам данных и т. д.). По окончании учебы эти
инженеры применяли полученные знания в конкретной работе. Подавляющая часть
современной цифровой экономики построена на сложных цепочках базовых алго­
ритмов, кропотливо склеенных поколениями инженеров. И большая часть этих сис­
тем не приспособлена к адаптации. Все конфигурации и перенастройки должны
выполняться высококвалифицированными инженерами, что делает такие системы
хрупкими.
Машинное самообучение обещает изменить область разработки программного
обеспечения, позволяя системам динамически адаптироваться. Развернутые
машинно-обучающиеся системы способны обучаться желаемому поведению из баз
данных с примерами. Кроме того, такие системы могут регулярно тренироваться по
мере поступления новых данных. Очень сложные программные системы, приводи­
19
мые в движение машинным самообучением, способны кардинально изменить свое
поведение без существенных модификаций программного кода (только в трениро­
вочных данных). Эта тенденция, скорее всего, лишь ускорится по мере упрощения
и внедрения средств машинного самообучения.
В ходе изменения линий поведения программных систем меняются и роли инжене­
ров-программистов. В некотором роде эта трансформация будет аналогична транс­
формации, последовавшей за развитием языков программирования. Первые ком­
пьютеры тщательно программировались. Провода объединялись в сложную взаи­
мосвязанную сеть. Затем были придуманы перфокарты, позволившие создавать
новые программы без аппаратных изменений в компьютерах. После эпохи перфо­
карт появились первые языки ассемблера. Потом языки более высокого уровня,
такие как Fortran или Lisp. Последующие уровни разработки создавали языки очень
высокого уровня, такие как Python, со сложными экосистемами предварительно
запрограммированных алгоритмов. Современная информатика во многом уже опи­
рается на автогенерируемый программный код. Современные разработчики прило­
жений используют инструменты, например Android Studio, для автогенерации
большей части программного кода, который они хотели бы создать. Таким образом,
каждая последующая волна упрощения расширяла сферу информатики, снижая
барьеры для доступа.
Машинное самообучение обещает снизить барьеры еще больше; программисты
скоро смогут менять поведение систем путем изменения тренировочных данных,
возможно, без написания кода. На стороне пользователя системы, построенные на
понимании естественного языка, такие как Alexa и Siri. дадут непрограммистам
возможность выполнять сложные вычисления. Более того, системы со встроенным
машинным самообучением, вероятно, станут более устойчивыми к ошибкам. Спо­
собность многократно тренировать модели будет означать, что кодовые базы могут
сократиться, а удобство сопровождения повысится. Иными словами, машинное
самообучение, судя по всему, полностью перевесит роль инженеров-програм­
мистов. Сегодняшние программисты должны понимать, каким образом машиннообучающиеся системы учатся, и знать классы ошибок, которые возникают в обыч­
ных машинно-обучающихся системах. Кроме того, им нужно будет понять шабло­
ны проектирования, лежащие в основе машинно-обучающихся систем (сильно раз­
нящихся по стилю и форме от классических шаблонов проектирования программ­
ного обеспечения). И им нужно будет в достаточной мере разбираться в тензорном
исчислении, чтобы понимать, почему сложная глубокая архитектура может вести
себя ненадлежащим образом во время самообучения. Не будет преуменьшением
сказать, что понимание (теории и практики) машинного самообучения станет фун­
даментальным навыком, который каждый компьютерный ученый и инженер-про­
граммист должен будет приобрести в течение следующего десятилетия.
Далее в этой главе мы проведем экскурсию по основам современного глубокого
самообучения. В остальных главах этой книги мы более подробно рассмотрим
темы, которые здесь только затронем.
20
|
Гпава 1
Глубоко обучающиеся примитивы
Большинство глубоких архитектур строятся путем группирования и перегруппиро­
вания ограниченного набора архитектурных примитивов. Такие примитивы, обыч­
но называемые слоями нейронных сетей, являются основополагающими строи­
тельными блоками глубоких сетей. В остальной части этой книги мы предоставим
подробные вводные сведения о таких слоях. Однако в этом разделе мы дадим крат­
кий обзор общих модулей, которые находятся во многих глубоких сетях. Данный
раздел не предназначен для подробного ознакомления с этими модулями. Скорее,
мы стремимся акцентировавать ваше внимание на строительных блоках сложных
глубоких архитектур, чтобы подогреть ваш интерес. Искусство глубокого самообу­
чения заключается в группировании и перегруппировании таких модулей, и мы
хотим направить вас по правильному пути к специальным знаниям в области глу­
бокого самообучения.
Полносвязный слой
Полносвязная сеть преобразует список входов в список выходов. Такое преобразо­
вание называется полносвязным, поскольку любое входное значение может повли­
ять на любое выходное значение. Такие слои будут иметь много заучиваемых
параметров, даже для относительно небольших входных данных, но эти слои обла­
дают большим преимуществом, т. к. они допускают отсутствие структуры во вход­
ных данных. Эта концепция проиллюстрирована на рис. 1.1.
Рис. 1.1. Полносвязный слой. Входящие стрелки представляют входные данные,
а исходящие стрелки — выходные данные. Толщина взаимосвязанных линий
представляет собой величину заученных весов. Полносвязный слой преобразует входные данные
в выходные посредством заученного правила
Введение в глубокое самообучение
|
21
Сверточный слой
Сверточная сеть исходит из особой пространственной структуры во входных дан­
ных. В частности, она принимает допущение, что входные данные, близкие друг
к другу пространственно, семантически связаны. Это допущение имеет наибольший
смысл для изображений, т. к. пикселы, близкие друг к другу, скорее всего, семан­
тически связаны. В результате такого подхода сверточные слои нашли широкое
применение в глубоких архитектурах обработки изображений. Эта концепция про­
иллюстрирована на рис. 1.2.
Так же как в случае с полносвязными слоями, которые преобразуют списки в спи­
ски, сверточные слои преобразуют изображения в изображения. Поэтому сверточ­
ные слои могут использоваться для выполнения сложных преобразований изобра­
жений, таких как применение художественных фильтров к изображениям в прило­
жениях по обработке фотографий.
Рис. 1.2. Сверточный слой. Фигура слева представляет входные данные,
фигура справа — выходные данные. В этом конкретном случае входные данные
имеют форму (32, 32, 3), т. е. входные данные представляют собой изображение 32x32 пиксела с тремя
цветовыми каналами RGB. Выделенная область на входе (слева) — это "локальное рецептивное поле",
группа входных данных, которые обрабатываются вместе, чтобы создать выделенную область
в выходных данных (справа)
Слои рекуррентной нейронной сети
Слои рекуррентной нейронной сети (recurrent neural network, RNN) являются при­
митивами, которые позволяют нейронным сетям обучаться на последовательностях
входных данных. Этот слой исходит из того, что входные данные изменяются от
шага к шагу, следуя четко сформулированному правилу обновления, которому
можно обучиться из данных. Это правило обновления представляет собой предска­
зание следующего состояния в последовательности с учетом всех состояний, кото­
рые поступали ранее. RNN-сеть показана на рис. 1.3.
RNN-слой способен обучиться этому правилу обновления из данных, благодаря
чему RNN-сети очень полезны для таких задач, как моделирование языка, где
инженеры пытаются строить системы, способные предсказывать следующее слово,
которое пользователи напечатают, исходя из накопленных данных.
22
|
Гпава 1
Рис. 1.3. Рекуррентная нейронная сеть. Входы поступают в сеть снизу, а выходы извлекаются сверху.
IV представляет собой заученное преобразование (общее для всех временных шагов).
Сеть представлена концептуально слева и развернута справа, чтобы продемонстрировать,
как обрабатываются входные данные из разных временных шагов
Ячейки долгой краткосрочной памяти
RNN-слои, представленные в предыдущем разделе, в теории способны обучаться
произвольным правилам обновления для работы с последовательностью. На прак­
тике, однако, такие слои неспособны обучаться влияниям из отдаленного прошло­
го. Такие отдаленные влияния имеют решающее значение для надежного модели­
рования языка, поскольку значение составного предложения может зависеть от
связи между отдаленными словами. Ячейка долгой краткосрочной памяти (long
short-term memory, LSTM) является модификацией RNN-слоя, которая позволяет
сигналам пробиваться из более глубокого прошлого в настоящее. LSTM-ячейка
показана на рис. 1.4.
Рис. 1.4. Ячейка долгой кратковременной памяти. На внутреннем уровне LSTM-ячейка
имеет набор специально спроектированных операций, которые обеспечивают значительную часть
обучающей способности классической RNN-сети, сохраняя при этом влияния из прошлых данных.
На рисунке показан один вариант LSTM-ячейки из многих
Введение в глубокое самообучение
|
23
Глубоко обучающиеся архитектуры
Существуют сотни различных глубоко обучающихся моделей, которые сочетают
глубоко обучающиеся примитивы, представленные в предыдущем разделе. Одни
из этих архитектур важны исторически. Другие были первыми демонстрациями
новейших подходов к проектированию, которые повлияли на восприятие того, что
может делать глубокое самообучение.
В этом разделе мы представим подборку различных глубоко обучающихся архи­
тектур, которые повлияли на исследовательское сообщество. Мы хотим подчерк­
нуть, что это изложение является эпизодическим и не претендует на то, чтобы быть
исчерпывающим. Безусловно, в профессиональной литературе содержатся важные
модели, которые здесь не представлены.
LeNet
Архитектура LeNet— пожалуй, первая видная ’’глубокая” сверточная архитектура,
которая была представлена в 1988 г. Она использовалась для выполнения оптиче­
ского распознавания символов (optical character recoginition, OCR) в документах.
Хотя она превосходно выполняла свою задачу, вычислительная емкость LeNet была
экстремальной для компьютерного оборудования, доступного в то время, поэтому
этот подход к проектированию пребывал в (относительной) неизвестности в течение
нескольких десятилетий после его создания. Эта архитектура показана на рис. 1.5.
Сверточный
слой 1
Вход
Свертка
Редуцирующий
слой 1
Подвыборка
Сверточный
слой 2
Свертка
Редуцирующий Скрытый
слой 2
слой
Подвыборка
Свертка
Полная
связь
Рис. 1.5. Архитектура LeNet для обработки изображений. Представленная в 1988 г., она была, пожалуй,
первой глубокой сверточной моделью для обработки изображений
AlexNet
Конкурсная инициатива ImageNet по созданию и сопровождению массивной базы
данных аннотированных изображений (Large Scale Visual Recognition Challenge,
ILSVRC) была впервые организована в 2010 г. для демонстрации успеха, достигну­
того в системах визуального распознавания. Организаторы использовали Amazon
Mechanical Turk— онлайновую платформу для подключения работников к заяви­
телям с целью каталогизации большой коллекции изображений со связанными
с ними списками присутствующих на изображении объектов. Использование плат­
24
|
Гпава 1
формы Mechanical Turk позволило создать коллекцию данных, значительно превы­
шающую собранные ранее.
Первые два года, пока проводился конкурс, торжествовали более традиционные
машинно-обучающиеся системы, которые опирались на такие системы, как HOG и
SIFT (ручные методы извлечения визуальных признаков). В 2012 г. архитектура
AlexNet, опирающаяся на модификацию LeNet, выполняемая на мощных графиче­
ских процессорах (GPU-процессорах), стала доминировать в проекте с частотой
ошибок вдвое меньше, чем у ближайших конкурентов. Эта победа резко оживила
(уже зарождающуюся) тенденцию к глубоко обучающимся архитектурам в области
компьютерного зрения. Архитектура AlexNet проиллюстрирована на рис. 1.6.
224
256
96
-
Ч
Шаг 4
Максимально
Максимально редуцирующий
редуцирующий
слой
4096
4096
Максимально
редуцирующий
слой
слой
Рис. 1.6. Архитектура AlexNet для обработки изображений. Эта архитектура стала победителем
в конкурсе ILSVRC 2012 и способствовала возрождению интереса к сверточной архитектуре
ResNet
С 2012 г. сверточные архитектуры последовательно выигрывали в конкурсе
ILSVRC (наряду со многими другими конкурсами в области компьютерного зре­
ния). С каждым годом проведения конкурса в архитектуре-победителе увеличива­
лась глубина и сложность. Архитектура ResNet, победитель конкурса ILSVRC
2015 г., стала особо заметной: она была расширена до 130 слоев в глубину, в отли­
чие от 8-слойной архитектуры AlexNet.
Очень глубокие сети обучались с большим трудом; когда сети вырастают до такой
глубины, они сталкиваются с проблемой исчезающих градиентов. Сигналы ослаб­
ляются по мере продвижения по сети, что приводит к ослаблению самообучения.
Это затухание можно объяснить математически, но эффект заключается в том, что
каждый дополнительный слой мультипликативно уменьшает силу сигнала, приводя
к ограничению эффективной глубины сетей.
В ResNet было внедрено новшество, которое контролирует затухание — обходное
соединение (рис. 1.7). Эти связи позволяют пропускать часть сигнала из более глу­
боких слоев без затухания, давая возможность эффектно тренировать значительно
более глубокие сети.
Введение в глубокое самообучение
|
25
Тождественное
отображение х
Рис. 1.7. Ячейка ResNet. Обходное соединение (тождественное отображение) с правой стороны
допускает пропускание немодифицированной версии входного сигнала через ячейку.
Эта модификация позволяет эффективно тренировать очень глубокие сверточные архитектуры
Нейронная модель титрования изображений
По мере того как практикующие специалисты перестали испытывать проблемы
с использованием глубоко обучающихся примитивов, они начали экспериментиро­
вать, смешивая и сопоставляя модули примитивов для создания систем более высо­
кого порядка, которые могли бы выполнять более сложные задачи, чем элементар­
ное обнаружение объектов. Нейронные системы титрования автоматически генери­
руют титры для содержимого изображений. Они делают это путем объединения
сверточной сети, которая извлекает информацию из изображений, с LSTM-слоем,
который генерирует описательное предложение для изображения. Вся система тре­
нируется в сквозном порядке. То есть тренировка сверточной сети и LSTM-сети
осуществляется вместе, чтобы достичь желаемой цели создания описательных
предложений для предоставленных изображений.
Такая сквозная тренировка является одной из ключевых инноваций, приводящих
в движение современные глубоко обучающиеся системы, поскольку она уменьшает
1. Входное
изображение
2. Сверточное
извлечение признаков
(карта признаков 14x14)
3. RNN с вниманием
на изображении
4. Пословная
генерация
Рис. 1.8. Архитектура нейронного титрования. Соответствующие входные признаки извлекаются
из входного изображения при помощи сверточной сети. Затем для создания описательного предложения
используется рекуррентная сеть
26
|
Гпава 1
потребность в сложной предобработке входных данных. Модели титрования изо­
бражений, которые не используют глубокое самообучение, должны применять
сложные методы обработки изображений, такие как SIFT, которые не могут быть
натренированы вместе с генератором титров.
Нейронная модель титрования показана на рис. 1.8.
Нейронный машинный перевод Google
В системе нейронного машинного перевода Google (Google-NMT) используется
парадигма сквозной тренировки для создания производственной системы перевода,
которая превращает предложения на исходном языке непосредственно в целевой
язык. Система Google-NMT зависит от фундаментального строительного блока,
являющегося LSTM-слоем, который она укладывает в ярусы более десятка раз и
тренирует на чрезвычайно большом наборе переведенных предложений. Итоговая
архитектура обеспечила прорыв в машинном переводе, сократив разрыв между
человеческим и машинным переводом на 60%. Архитектура Google-NMT показана
на рис. 1.9.
Рис. 1.9. В нейронной системе машинного перевода Google используются глубокая рекуррентная
архитектура для обработки входного предложения и вторая глубокая рекуррентная архитектура
для создания переведенного выходного предложения
Однократные модели
Однократное самообучение— это, пожалуй, самая интересная новая идея в машинном/глубоком самообучении. Для того чтобы обучиться содержательному
поведению, большинство методов глубокого самообучения обычно требуют очень
крупных объемов данных. Архитектура AlexNet, например, пользовалась большим
Введение в глубокое самообучение
|
27
Новое
химическое
соединение
Химическое соединение
Оксид
стирола
Набор данных
Дофамин
Тозиловая
кислота
кофеин
лития
Этанол
Ион
Предсказание
Рис. 1.10. Однократная архитектура использует вид сверточной сети для преобразования
каждой молекулы в вектор. Вектор для оксида стирола сравнивается с векторами
из экспериментального набора данных. Метка для наиболее похожей точки данных (тозиловой кислоты)
выводится в качестве ответа на запрос
28
|
Гпава 1
набором данных ILSVRC, чтобы обучиться визуальному обнаружению объектов.
Вместе с тем большая работа в когнитивной науке показала, что люди могут
обучаться сложным понятиям всего на нескольких примерах. Возьмите хоть ребен­
ка, узнающего о жирафах в первый раз. Ребенок, которому в зоопарке показали
одного жирафа, с тех пор может научиться распознавать всех жирафов, которых он
увидит.
Недавний прогресс в глубоком самообучении проявился в изобретении архитектур,
способных к аналогичным достижениям в самообучении. При наличии лишь не­
скольких примеров того или иного понятия (но при наличии обильных источников
дополнительной информации) такие системы могут научиться делать содержатель­
ные предсказания с очень небольшим количеством точек данных. В одной недавней
статье (автора этой книги) эта идея использовалась для демонстрации того, что
однократные архитектуры могут обучаться в контекстах, в которых не способны
обучаться дети, например в обнаружении медицинских препаратов. Однократная
архитектура для обнаружения препаратов показана на рис. 1.10.
AlphaGo
Го — это древняя настольная игра, очень популярная в Азии. С конца 1960-х годов
компьютерное го представляет собой серьезную задачу для информатики. Приемы,
позволившие компьютерной шахматной системе Deep Blue обыграть гроссмейстера
Гарри Каспарова в 1997 г., не масштабируются на игру го. Одна из причин этого —
го имеет гораздо более крупную доску, чем шахматы; доски для го имеют разли­
новку 19x19 клеток (рис. 1.11), в отличие от 8x8 клеток для шахмат. Поскольку
за один шаг можно делать гораздо больше ходов, игровое дерево возможных ходов
расширяется гораздо быстрее, что делает поиск методом грубой силы даже с по­
мощью современного компьютерного оборудования недостаточным для адекват­
ного игрового процесса.
Рис. 1.11. Иллюстрация игровой доски для игры го. Игроки поочередно размещают
белые и черные фигуры на поле размером 19x19 клеток
Введение в глубокое самообучение
|
29
Компьютерное го гроссмейстерского уровня было, наконец, успешно реализовано
программой AlphaGo, разработанной компаний Google DeepMind. AlphaGo оказа­
лась способной победить одного из сильнейших в мире чемпионов по го, Ли Седоля, в серии из пяти партий. Некоторые из ключевых идей AlphaGo предполагают
использование глубокой стоимостной сети (deep value network) и глубокой страте­
гической сети (deep policy network). Стоимостная сеть дает оценку стоимости пози­
ции на доске. В отличие от шахмат в го по состоянию доски очень трудно догадать­
ся, кто выигрывает: белые или черные. Стоимостная сеть решает эту проблему,
обучаясь делать предсказание из результатов игры. Стратегическая сеть, с другой
стороны, помогает оценивать наилучший ход при наличии текущего состояния
доски. Сочетание этих двух методов с поиском по дереву методом Монте-Карло
(классическим методом поиска) помогло преодолеть большой коэффициент ветв­
ления в играх го. Базовая архитектура программы AlphaGo показана на рис. 1.12.
Стратегия
дебюта
Стратегическая
сеть КС
Рг
Ра
Стоимостная
сеть
Стратегическая
сеть МП
Нейронная
сеть
Градиент
Данные
Позиции самостоятельной игры
Позиции эксперта-человека
a
Стратегическая сеть
Стоимостная сеть
Ра)р(Ф)
Vets')
е
ф
б
Рис. 1.12. Архитектура AlphaGo: а — стратегическая сеть для выбора ходов изначально тренируется
на наборе данных игр профессионалов; эта стратегия затем уточняется за счет самостоятельной игры
(МП — самообучение с максимизацией подкрепления, КС — контролируемое самообучение);
б — обе сети — стоимостная и стратегическая — оперируют представлениями игрового поля
30
|
Гпава 1
Генеративно-состязательные сети
Генеративно-состязательные сети (generative adversarial networks, GAN)— это но­
вый тип глубокой сети, в которой используются две конкурирующие нейронные
сети: генератор и дискриминатор (так называемая соперничающая сеть), которые
сходятся в поединке друг против друга. Генератор пытается извлечь образцы из
тренировочного распределения (например, пытается сгенерировать реалистичные
изображения птиц). Дискриминатор работает над установлением различия образ­
цов, извлеченных генератором, и истинных образцов данных. (Является ли кон­
кретная птица реальным изображением или же она создана генератором?) Такая
’’состязательная” тренировка GAN-сетей, по всей видимости, способна генериро­
вать образцы изображений значительно более высокой точности, чем другие мето­
ды, и может быть полезна для тренировки эффективных дискриминаторов с по­
мощью ограниченных данных. Архитектура GAN-сети показана на рис. 1.13.
Латентная
случайная
величина
Рис. 1.13. Концептуальное изображение генеративно-состязательной сети
GAN-сети оказались способными генерировать очень реалистичные изображения и,
судя по всему, приведут в действие следующее поколение инструментов компью­
терной графики. Качество образцов из таких систем сейчас приближается к фото­
реалистичному. Вместе с тем, относительно этих систем еще предстоит разработать
многие теоретические и практические оговорки, и многое еще необходимо иссле­
довать.
Нейронные машины Тьюринга
Подавляющая часть глубоко обучающихся систем, представленных до сих пор, за­
учивали сложные функции с ограниченными областями применения; например,
обнаружение объектов, титрование изображений, машинный перевод или процесс
игры в го. Но можно ли вообще иметь глубокие архитектуры, которые заучивают
универсальные алгоритмические концепции, такие как сортировка, сложение или
умножение?
Введение в глубокое самообучение
|
31
Нейронная машина Тьюринга (НМТ)— это первая попытка создать глубоко обу­
чающуюся архитектуру, способную обучаться произвольным алгоритмам. Эта
архитектура предусматривает добавление внешнего банка памяти в LSTM-подобную
систему, что позволяет глубокой архитектуре использовать вспомогательное про­
странство для вычисления более изощренных функций. На данный момент НМТподобные архитектуры все еще довольно ограничены и способны обучаться лишь
простым алгоритмам. Тем не менее, методы НМТ остаются активной областью
исследований, и будущие достижения могут превратить эти предварительные
демонстрации в практические обучающиеся инструменты. Архитектура НМТ кон­
цептуально проиллюстрирована на рис. 1.14.
Контроллер
Вход
Выход
Головка чтения/записи
Память
Рис. 1.14. Концептуальное изображение нейронной машины Тьюринга.
В ней добавлен внешний банк памяти, в котором глубокая архитектура
выполняет операции чтения и записи
Вычислительные каркасы
для глубокого самообучения
На протяжении десятилетий исследователи реализовывали программные пакеты
для облегчения построения нейронно-сетевых (глубоко обучающихся) архитектур.
До недавнего времени эти системы имели в основном специальное назначение и
использовались только в академических кругах. Нехватка стандартизированного,
промышленного программного обеспечения затрудняет неэкспертам широкое ис­
пользование нейронных сетей.
Данная ситуация резко изменилась за последние несколько лет. В 2012 г. компания
Google реализовала систему DistBelief и использовала ее для создания и внедрения
многих более простых глубоко обучающихся архитектур. Появление DistBelief и
подобных пакетов, в частности Caffe, Theano, Torch, Keras, MxNet и др., широко
стимулировало их внедрение в информационной индустрии.
32
|
Гпава 1
Библиотека TensorFlow опирается на эту богатую интеллектуальную историю и
строится на основе некоторых из этих пакетов (Theano, в частности) ради принци­
пов проектирования. В TensorFlow (и Theano), например, используется понятие
гензоров как положенный в основу фундаментальный примитив, приводящий
в действие глубоко обучающиеся системы. Акцент на тензоры отличает эти пакеты
от таких систем, как DistBelief или Caffe, которые не допускают подобную гибкость
в построении сложных моделей.
Несмотря на то что в оставшихся главах книги мы сосредоточимся на библиотеке
TensorFlow, понимание положенных в основу принципов должно позволить вам
учитывать полученные знания и применять их почти без труда в альтернативных
вычислительных каркасах для глубокого самообучения.
Ограничения вычислительной среды TensorFlow
Одним из основных текущих недостатков библиотеки TensorFlow является то, что
построение новой глубоко обучающейся архитектуры происходит относительно
медленно (порядка несколько секунд на то, чтобы инициализировать архитектуру).
Поэтому в TensorFlow неудобно строить некоторые сложные глубокие архитекту­
ры, которые динамически изменяют свою структуру. Одна из таких архитектур —
TreeLSTM (рис. 1.15), в которой используется синтаксический разбор деревьев анг­
лийских предложений для выполнения задач, требующих понимания естественного
языка. Поскольку каждое предложение имеет собственное дерево разбора, каждое
предложение требует немного другой архитектуры.
Рис. 1.15. Концептуальное изображение архитектуры TreeLSTM. Форма дерева различается
для каждой точки входных данных, поэтому для каждого примера необходимо строить
отдельный вычислительный граф
Хотя такие модели в принципе могут быть реализованы в TensorFlow, однако это
требует значительной изобретательности из-за ограничений текущего API TensorFlow.
Новые вычислительные каркасы, например Chainer, DyNet и PyTorch, обещают
Введение в глубокое самообучение
|
33
снять эти барьеры, достаточно облегчив конструирование новых архитектур, чтобы
такие модели, как TreeLSTM, можно было конструировать без труда. К счастью,
разработчики TensorFlow уже трудятся над расширениями базового API TensorFlow
(например, Tensorflow Eager), которые облегчат построение динамических архи­
тектур.
Один из выводов состоит в том, что прогресс в вычислительных каркасах для глу­
бокого самообучения является быстрым, и новая сегодняшняя система может стать
старыми завтрашними новостями. Вместе с тем фундаментальные принципы поло­
женного в основу тензорного исчисления уходят корнями в столетия и послужат
читателям добрую службу, независимо от будущих изменений в моделях про­
граммирования. В этой книге будет сделан упор на использование библиотеки
TensorFlow в качестве инструмента для развития интуитивного понимания поло­
женного в основу тензорного исчисления.
Резюме
В этой главе мы объяснили, почему глубокое самообучение является вопросом
первостепенной значимости для современного инженера-программиста, и привели
краткий обзор ряда глубоких архитектур. В следующей главе мы начнем изучать
TensorFlow — программную инфраструктуру Google для построения и тренировки
глубоких архитектур. В последующих главах мы подробно разберем ряд практиче­
ских примеров глубоких архитектур.
Машинное самообучение (и, в частности, глубокое самообучение), как и значи­
тельная часть информатики, является весьма эмпирической дисциплиной. Понять
глубокое самообучение действительно можно, только накопив значительный прак­
тический опыт. По этой причине в оставшуюся часть этой книги мы включили ряд
углубленных практических примеров. Мы рекомендуем вам разобраться в них и
заняться черной работой, экспериментируя со своими собственными идеями при
помощи TensorFlow. Никогда не достаточно понимать алгоритмы только теорети­
чески!
34
|
Гпава 1
ГЛАВА 2
Введение
в примитивы TensorFlow
В этой главе вы познакомитесь с фундаментальными аспектами библиотеки
TensorFlow. Вы узнаете, как выполнять базовые вычисления с ее помощью. Подав­
ляющая часть этой главы посвящена введению понятия тензоров и обсуждению
того, как тензоры представлены и обрабатываются в TensorFlow. Рассмотрение
этого вопроса потребует краткого обзора некоторых математических концепций,
лежащих в основе тензорной математики. В частности, мы кратко рассмотрим
элементарную линейную алгебру и продемонстрируем, как выполнять основные
линейные алгебраические операции с помощью TensorFlow.
Изложение элементарной математики мы будем сопровождать рассмотрением раз­
личий между декларативными и императивными стилями программирования.
В отличие от многих языков программирования библиотека TensorFlow в значи­
тельной степени декларативна. В результате вызова ее операций она добавляет
описание вычисления в ’’вычислительный граф” TensorFlow. В частности, про­
граммный код TensorFlow лишь ’’описывает” вычисления и фактически их не
выполняет. Для того чтобы выполнять программный код TensorFlow, пользователи
должны создавать объекты tf. Session. Мы введем понятие сеансов и опишем,
каким образом пользователи должны выполнять с ними вычисления в TensorFlow.
Мы завершим эту главу обсуждением понятия переменных. Переменные в TensorFlow
содержат тензоры и позволяют выполнять вычисления, модифицирующие пере­
менные, с отслеживанием состояний. Мы продемонстрируем, как создавать пере­
менные и обновлять их значения с помощью TensorFlow.
Введение в тензоры
Тензоры являются фундаментальными математическими конструкциями в таких
областях, как физика и техника. Однако в историческом плане тензоры совершили
гораздо меньше набегов на информатику, которая традиционно была больше связа­
на с дискретной математикой и логикой. Такое положение дел начало значительно
меняться с появлением машинного самообучения и его опорой на непрерывную,
векторную математику. Современное машинное самообучение основано на обра­
ботке и исчислении тензоров.
35
Скаляры, векторы и матрицы
Для начала приведем несколько простых примеров тензоров, с которыми вы, воз­
можно, знакомы. Простейшим примером тензора является скаляр — одиночное по­
стоянное значение, получаемое из вещественных чисел. (Напомним, что вещест­
венные числа являются десятичными числами произвольной точности, причем до­
пускаются как положительные, так и отрицательные числа.) Математически мы
обозначаем вещественные числа буквой R. Более формально мы называем скаляр
тензором нулевого ранга.
Ремарка о полях
Математически искушенные читатели возразят, что совершенно допусти­
мо определять тензоры на комплексных числах или с помощью двоичных
чисел. В более общем случае достаточно, чтобы числа исходили из поля —
математической совокупности чисел, где определены 0, 1, сложение,
умножение, вычитание и деление. Общепринятые поля включают вещест­
венные числа R, рациональные числа Q, комплексные числа С и конеч­
ные поля, такие как Z2. Для простоты в большей части изложения мы
примем за основу вещественные тензоры, но подстановка значений из дру­
гих полей совершенно разумна.
Если скаляры являются тензорами нулевого ранга, что же представляет собой тен­
зор 1-го ранга? Формально, тензор 1-го ранга является вектором, или списком ве­
щественных чисел. Традиционно векторы записываются как векторы-столбцы
а
b
или как векторы-строки
[а
*].
Совокупность всех векторов-столбцов длины 2 обозначается как R2xl, а множество
всех векторов-строк длины 2— как Rlx2. В более вычислительном плане можно
сказать, что форма вектора-столбца есть (2, 1), а форма вектора-строки— (1,2).
Если мы не хотим конкретизировать, является ли вектор вектором-строкой или век­
тором-столбцом, то можем сказать, что он принадлежит множеству R2 и имеет
форму (2). Это понятие формы тензора очень важно для понимания вычислений в
библиотеке TensorFlow, и мы вернемся к нему позже в этой главе.
Простейшим способом использования векторов является представление координат
в реальном мире. Предположим, что мы определяем исходную точку (скажем,
местоположение, в котором вы сейчас находитесь). Тогда любое местоположение
в мире может быть представлено тремя значениями смещения от вашего текущего
местоположения (смещение влево-вправо, смещение вперед-назад, смещение
вверх-вниз). Следовательно, множество векторов (векторное пространство) R3 мо­
жет представлять любое местоположение в мире.
36
|
Гпава 2
В качестве другого примера предположим, что кошку можно описать ее ростом,
весом и окрасом. Тогда кошка из видеоигры может быть представлена в виде век­
тора
рост
вес
окрас
в пространстве R3. Этот тип представления часто называется хешированием при­
знаков (конвертацией признаков в индексы векторов, featurization). То есть хеширо­
вание признаков заключается в представлении реального объекта в виде вектора
(или, в более общем плане, как тензора). Почти все машинно-обучающиеся алго­
ритмы работают с векторами или тензорами. Следовательно, процесс хеширования
признаков в вектор является важной частью любого конвейера машинного само­
обучения. Часто система хеширования признаков в вектор может быть самой слож­
ной частью машинно-обучающейся системы. Предположим, у нас есть молекула
бензола (рис. 2.1).
Рис. 2.1. Представление молекулы бензола
Как преобразовать эту молекулу в вектор, подходящий для запроса к машиннообучающейся системе? Существует ряд потенциальных решений этой задачи, по­
давляющая часть которых используют идею маркировки присутствия подфрагмен­
тов молекулы. Наличие или отсутствие конкретных подфрагментов маркируется
путем назначения индексам двоичного вектора ({0,1} ) соответственно значе­
ний 1/0. Этот процесс показан на рис. 2.2.
Обратите внимание, что этот процесс выглядит (и является) довольно сложным. На
самом деле, один из самых сложных аспектов построения машинно-обучающейся
системы заключается в принятии решения о том, как преобразовывать данные
в тензорный формат. Для некоторых типов данных это преобразование очевидно.
Для других (например, молекул) требуемое преобразование может быть довольно
тонким. Практикующему специалисту по машинному самообучению, как правило,
не нужно изобретать новый метод хеширования признаков, поскольку научная ли­
тература обширна, но нередко требуется прочитать исследовательские работы, что­
бы понять лучшие практические приемы преобразования нового потока данных.
Введение в примитивы TensorFlow |
37
Рис. 2.2. Отбор подфрагментов молекулы для создания признаков (тех, которые содержат ОН).
Эти фрагменты хешируются в индексы вектора фиксированной длины.
Эти позиции имеют значение 1, а все остальные — значение О
Теперь, когда мы установили, что тензоры нулевого ранга являются скаляра­
ми (R), а тензоры 1-го ранга— векторами (R"), нужно выяснить, что же такое
тензор 2-го ранга? Традиционно тензор 2-го ранга является матрицей'.
а
b
с
d
Эта матрица состоит из двух строк и двух столбцов. Множество всех таких матриц
обозначается R2*2. Возвращаясь к нашему понятию формы тензора, форма этой
матрицы составляет (2, 2). Матрицы традиционно используются для представления
преобразований векторов. Например, поворот вектора на плоскости на угол а мо­
жет быть выполнен такой матрицей:
cos a
-sin а
sin а
cos а
Для того чтобы увидеть это, обратите внимание, что единичный вектор [1 0] пре­
образуется матричным умножением в вектор [cos a sin а]. (Мы рассмотрим под­
робное определение матричного умножения далее в этой главе, но на текущий
момент просто покажем результат.)
38
|
Гпава 2
cos а
-sin а
'Г
cos а
sin а
cos а
0
sin а
Рис. 2.3. Положения на единичной окружности параметризуются косинусом и синусом
Это преобразование также можно визуализировать графически. На рис. 2.3 показа­
но, как итоговый вектор соответствует повороту исходного единичного вектора.
Матричная математика
Существует ряд стандартных математических операций над матрицами, которые
машинно-обучающиеся программы используют многократно. Мы кратко рассмот­
рим некоторые наиболее фундаментальные из этих операций.
Транспонирование матрицы — это удобная операция, которая переворачивает мат­
рицу относительно своей диагонали. Пусть А— некоторая матрица. Тогда транс­
понированная матрица Аг определяется соотношением а?=а^. Например,
транспонированная матрица поворота Ra такова:
Rl
cos a
sin a
-sin a
cos a
Сложение матриц определяется только для матриц одинаковой формы и выполня­
ется поэлементно. Например,
1
3
2] Г1
4 + 1
11Г2
1 “ 4
3
5
Точно так же матрицы можно умножать на скаляры. В этом случае каждый эле­
мент матрицы умножается на рассматриваемый скаляр:
2-
1
2
2
4
3
4
6
8
1 Здесь atj — элемент матрицы А в i-й строке и j-м столбце. — Прим. ред.
Введение в примитивы TensorFlow |
39
Иногда можно непосредственно перемножить две матрицы. Понятие матричного
умножения, вероятно, является наиболее важным математическим понятием, свя­
занным с матрицами. Матричное умножение — не то же самое, что поэлементное
умножение матриц! Предположим, что у нас есть матрица А формы (т, п) с т стро­
ками и п столбцами. Тогда А можно умножить справа на любую матрицу В формы
(и, к) (где к — любое положительное целое число), чтобы сформировать матрицу
АВ формы (т, к). В целях фактического математического описания предположим,
что А — это матрица формы (ш, и), а В — матрица формы (п, к). Тогда произведе­
ние матриц АВ определяется следующим соотношением:
к
Ранее мы показали уравнение матричного умножения. Имея формальное определе­
ние, давайте этот пример теперь расширим:
cos а
-sin а
’1]_ cosal -sinaO
cos a
sin а
cos а
sinal + cosa-0
sin a
Основное правило матричного умножения заключается в том, что строки одной
матрицы умножаются на столбцы другой матрицы.
Это определение скрывает ряд тонкостей. Обратите внимание, что матричное
умножение не является коммутативным. То есть, в общем случае АВ ВА . На самом
деле, АВ может существовать, когда ВА не возможно. Предположим, например,
что А — это матрица формы (2, 3), а В — матрица формы (3, 4). Тогда АВ — это
матрица формы (2, 4). Однако ВА не определена, поскольку соответствующие раз­
мерности (4 и 2) не совпадают. В качестве еще одной тонкости отметим, что, как и
в примере поворота, матрицу формы (т, п) можно умножить в правой части на
матрицу формы (и, 1). Однако матрица формы (и, 1)— это просто вектор-столбец.
Поэтому имеет смысл умножать матрицы на векторы. Матрично-векторное умно­
жение является одним из фундаментальных строительных блоков универсальных
машинно-обучающихся систем.
Одно из самых приятных свойств стандартного умножения заключается в том, что
оно является линейной операцией. Функция f называется линейной, если
f (х + у) = f (х) + f (у) и f (ex) = cf (х), где с — это скаляр. Для того чтобы про­
демонстрировать, что скалярное умножение линейно, предположим, что a, b, с, d
являются вещественными числами. Тогда мы имеем:
а*(й-с) = А-(а-с);
а • (с + J) = а • с + а • d.
Здесь мы используем коммутативные и дистрибутивные свойства скалярного
умножения. Теперь предположим, что А, С, D — теперь матрицы, где С и D имеют
одинаковый размер, и можно умножить А справа на С или D (Ь остается вещест­
венным числом). Тогда матричное умножение является линейным оператором:
40
|
Гпава 2
A-(Z>-C) = Z>(AC);
A (C + D) = AC +AD.
Другими словами, матричное умножение является дистрибутивным и коммутирует
со скалярным умножением. Можно показать, что любое линейное преобразование
на векторах соответствует матричному умножению. В качестве аналогии с ин­
форматикой подумайте о линейном преобразовании как об абстрактном методе
в суперклассе. Тогда стандартное умножение и матричное умножение являются
конкретными реализациями этого абстрактного метода для различных подклассов
(соответственно вещественных чисел и матриц).
Тензоры
В предыдущих разделах мы ввели понятие скаляров как тензоров нулевого ранга,
векторов как тензоров 1-го ранга и матриц как тензоров 2-го ранга. Что же такое
тогда тензор 3-го ранга? Прежде чем перейти к общему определению, поразмысли­
те над общими чертами скаляров, векторов и матриц. Скаляры — это одиночные
числа. Векторы — это списки чисел. Для того чтобы выбрать какой-либо элемент
вектора, необходимо знать его индекс. Следовательно, нам нужен один индексный
элемент в вектор (тензор 1-го ранга). Матрицы — это таблицы чисел. Для того что­
бы выбрать какой-либо конкретный элемент матрицы, необходимо знать его строку
и столбец. Значит, нам нужны два индексных элемента (тензор 2-го ранга). Из этого
естественно следует, что тензор 3-го ранга представляет собой набор чисел,
где есть три обязательных индекса. Целесообразно представить тензор 3-го ранга
в виде параллелепипеда (рис. 2.4).
Рис. 2.4. Тензор 3-го ранга может быть визуализирован в виде параллелепипеда
Показанный на рисунке тензор 3-го ранга Т имеет форму их их и. Произвольный
элемент тензора выбирается путем задания тройки чисел (/, у, к) в качестве ин­
дексов.
Существует связь между тензорами и формами. Тензор 1-го ранга имеет форму
размерности 1, тензор 2-го ранга— форму размерности 2 и тензор 3-го ранга —
форму размерности 3. Вы можете возразить, что это противоречит нашему преды­
дущему обсуждению векторов-строк и векторов-столбцов. По нашему определе­
Введение в примитивы TensorFlow
|
41
нию, вектор-столбец имеет форму (л, 1). Разве это не сделает вектор-столбец тензо­
ром 2-го ранга (или матрицей)? Именно так и произошло. Напомним, что вектор,
который не задан как вектор-строка или вектор-столбец, имеет форму (и). Когда
мы указываем, что вектор является вектором-строкой или вектором-столбцом, мы
фактически задаем метод преобразования базового вектора в матрицу. Этот
тип расширения размерности является распространенным приемом обработки тен­
зоров.
Обратите внимание, что есть еще один способ представить тензор 3-го ранга — это
список матриц с одинаковой формой. Предположим, что W есть матрица с формой
(и, и). Тогда тензор Tyt = [W] ... W„] состоит из п копий матрицы W.
Стоит отметить, что черно-белое изображение может быть представлено тензо­
ром 2-го ранга. Предположим, у нас есть черно-белое изображение размером
224x224 пикселов. Тогда элемент (/,/) равен соответственно 1 или 0, тем самым
кодируя черно-белый пиксел. Отсюда следует, что черно-белое изображение может
быть представлено в виде матрицы формы (224, 224). Теперь рассмотрим цветное
изображение размером 224 х 224. Цвет в том или ином пикселе обычно представ­
лен тремя отдельными каналами RGB. То есть пиксел (/, j) представлен в виде кор­
тежа чисел (г, g, b), которые кодируют количество соответственно красного, зеле­
ного и синего в пикселе. Значения г, g,b — это обычно целые числа от 0 до 255. Из
этого следует, что цветное изображение может быть закодировано как тензор
3-го ранга формы (224, 224, 3). Продолжая аналогию, рассмотрим цветное видео.
Предположим, что каждый кадр видео является цветным изображением размера
224 х 224. Тогда минута видео (при 60 кадрах в секунду) будет тензором 4-го ранга
формы (224, 224, 3, 3600). Коллекция из 10 таких видео затем сформирует тензор
5-го ранга формы (10, 224, 224, 3, 3600). В целом тензоры обеспечивают удобное
представление числовых данных. На практике не принято видеть тензоры выше
5-го ранга, но разумно проектировать программное обеспечение для любых тензо­
ров, позволяющее использовать произвольные тензоры, поскольку находчивые
пользователи всегда придумают варианты применения, которые проектировщики
не учитывают.
Тензоры в физике
Тензоры широко используются в физике для кодирования фундаментальных физи­
ческих величин. Например, тензор напряжений обычно используется в материало­
ведении для задания напряжения в точке внутри материала. Математически тензор
напряжений является тензором 2-го ранга формы (3, 3):
СТ11
Т12
^13
СТ22
Т23
Т32
СТ33_
•
Далее предположим, что п является вектором формы (3), который кодирует на­
правление. Напряжение Т" в направлении п задается вектором Т“ =Т-п (обратите
42
|
Гпава 2
Рис. 2.5. Трехмерное изображение компонентов напряжения
внимание на матрично-векторное умножение). Эта взаимосвязь изображена на
рис. 2.5.
В качестве еще одного физического примера приведем уравнение поля общей тео­
рии относительности Эйнштейна. Оно обычно выражается в тензорном формате:
р
1 р
2
.
а
_
gv — ^4
’
где
— тензор кривизны Риччи; Rg^ — метрический тензор; Tgv — тензор
энергии-импульса материи; оставшиеся физические величины — это скаляры. Вме­
сте с тем обратите внимание, что есть важная тонкость, отличающая эти тензоры от
других тензоров, которые мы обсуждали ранее. Такие величины, как метрический
тензор, представляют собой отдельный тензор (в смысле множества чисел) для ка­
ждой точки в пространстве-времени (в математическом смысле, метрический тен­
зор — это тензорное поле). То же самое соблюдается и для ранее рассмотренного
тензора напряжений, и для других тензоров в этих уравнениях. В заданной точке
пространства-времени каждая из этих физических величин с использованием наше­
го обозначения становится симметричным тензором 2-го ранга формы (4, 4).
Часть мощи современных систем тензорного исчисления, таких как TensorFlow,
заключается в том, что некоторые математические механизмы, давно используемые
для классической физики, теперь могут быть адаптированы для решения приклад­
ных задач обработки изображений и понимания языка. В то же время современные
системы тензорного исчисления по-прежнему ограничены по сравнению с матема­
тическим аппаратом в области физики. Например, при использовании TensorFlow
нет простого способа говорить о такой величине, как метрический тензор. Мы на­
деемся, что эта ситуация изменится, поскольку тензорное исчисление становится
Введение в примитивы TensorFlow |
43
более фундаментальным для информатики, и такие системы, как TensorFlow, будут
служить мостом между физическим и вычислительным мирами.
Математические ремарки
До сих пор вы знакомились с тензорами неофициально на примерах и иллюстраци­
ях. В нашем определении тензор— это просто массив чисел. Часто удобно рас­
сматривать тензор как функцию. Наиболее распространенное определение вводит
тензор как полилинейную функцию-отображение из произведения векторных про­
странств в вещественные числа:
Т: Vj xv2x---xv„ ->R.
В этом определении используется ряд терминов, которые вы не встречали. Вектор­
ное пространство — это просто совокупность векторов. Вы встречали несколько
примеров векторных пространств, таких как R3 или в общем случае R". Мы не
потеряем универсальность, утверждая, что v, = RJ*. Как мы определили ранее,
функция f линейна, если f(x + y) = /(*) + /(у) и /(сх) = </(*)• Полилинейная
функция— это просто функция, линейная в каждом аргументе. Эту функцию
можно рассматривать как назначение отдельных значений ячейкам многомерного
массива, если в качестве аргументов указаны индексы массива.
В этой книге мы больше не будем использовать это математическое определение,
но оно послужит полезным мостом для соединения концепций глубокого самообу­
чения, с которыми вы познакомитесь, с многовековыми математическими исследо­
ваниями, которые предпринимались на тензорах физико-математическими сообще­
ствами.
Ковариация и контравариация
Наше определение здесь скрыло много деталей, которым следовало бы
уделить пристальное внимание для формального обращения. Например,
X.-IЛ мы не затрагиваем понятия ковариантных и контравариантных индексов.
То, что мы называем тензором и-го ранга, лучше описать как тензор ранга
(р, q\ или (р, <?)-тензор, где n = p + q,p — число контравариантных индек­
сов, q— число ковариантных индексов. Например, матрицы— это (1,1)тензоры. И еще один нюанс — существуют тензоры 2-го ранга, которые не
являются матрицами! Мы не будем углубляться в эти темы подробно, по­
скольку они не часто появляются в машинном самообучении, но мы реко­
мендуем вам разобраться в том, как ковариация и контравариация влияют
на машинно-обучающиеся системы, которые вы строите.
Базовые вычисления в TensorFlow
Мы потратили некоторое время на математические определения различных тензо­
ров. Настал момент рассказать, как создавать тензоры и манипулировать ими,
используя TensorFlow. В этом разделе мы рекомендуем попутно использовать
44
|
Гпава 2
интерактивный сеанс Python (с IPython). Во многих базовых понятиях библиотеки
TensorFlow легче всего разобраться, экспериментируя с ними напрямую.
Установка TensorFlow и начало работы
Прежде чем продолжить изложение, необходимо установить библиотеку
TensorFlow на компьютер. Детали установки будут варьировать в зависимости от
вашего конкретного оборудования, поэтому мы отсылаем вас к официальной доку­
ментации TensorFlow (https://www.tensorflow.org/api_docs/ ) за более подробной
информацией2.
Хотя интерфейсы для библиотеки TensorFlow существуют на нескольких языках
программирования, в оставшейся части этой книги мы будем использовать про­
граммный интерфейс TensorFlow исключительно для языка Python. Рекомендуется
установить дистрибутив Anaconda Python (https://anaconda.org/anaconda/python),
который поставляется в комплекте с множеством полезных числовых библиотек
вместе с базовым исполняемым файлом Python.
После установки библиотеки TensorFlow рекомендуется во время изучения базово­
го API вызывать ее в интерактивном режиме (пример 2.1). Во время эксперименти­
рования с TensorFlow в интерактивном режиме удобно использовать инструкцию
tf.InteractiveSessionо. Ее вызов в интерактивной оболочке IPython заставит
TensorFlow вести себя почти императивно, облегчая возможность начинающим
специалистам экспериментировать с тензорами. Вы узнаете об императивном и
декларативном стиле более подробно далее в этой главе.
| Пример 2.1. Инициализация интерактивного сеанса TensorFlow
»> import tensorflow as tf
»> tf.InteractiveSession ()
<tensorflow.python.client.session.InteractiveSession>
В остальной части программного кода в этом разделе предполагается, что интерак­
тивный сеанс загружен.
Инициализация константных тензоров
До сих пор мы обсуждали тензоры как абстрактные математические сущности.
Однако такая система, как библиотека TensorFlow, должна работать на реальном
компьютере, поэтому для того чтобы быть полезными для компьютерных програм­
мистов, любые тензоры должны существовать в оперативной памяти компьютера.
Библиотека TensorFlow предоставляет ряд функций, реализующих основные тензо­
ры в памяти. Самые простые из них— tf. zeros о и tf.onesO. Функция tf. zeros о
принимает форму тензора (представленную в виде кортежа Python) и возвращает
2 См. https://www.tensorflow.org/install/install_windows. — Прим. пер.
Введение в примитивы TensorFlow |
45
тензор данной формы, заполненный нулями. Попробуем вызвать эту команду в обо­
лочке (пример 2.2).
Пример 2.2. Создание тензора нулей
»> tf. zeros (2)
<tf.Tensor 'zeros:O' shape=(2,) dtype=float32>
TensorFlow возвращает не значение самого тензора, а ссылку на желаемый тензор.
Для того чтобы принудительно вернуть значение тензора, будем использовать
метод tf. Tensor, eval о тензорных объектов (пример 2.3). Поскольку мы инициали­
зировали tf. interactiveSession (), этот метод вернет нам значение тензора нулей.
I Пример 2.3. Вычисление значения тензора
»> а = tf. zeros (2)
»> а. eval ()
array([ 0., 0.], dtype=float32)
Обратите внимание, что вычисленное значение тензора TensorFlow само является
объектом Python. В частности, a.eval о — это объект numpy.ndarray. NumPy пред­
ставляет собой сложную числовую систему в виде программной библиотеки
Python. Здесь мы не будем углубляться в обсуждение библиотеки NumPy помимо
замечания, что библиотека TensorFlow спроектирована так, чтобы быть в значи­
тельной степени совместимой с принятыми в NumPy обозначениями.
Мы можем вызвать методы tf. zeros о и tf.oneso для создания и вывода на экран
тензоров различных размеров (пример 2.4).
Пример 2.4. Вычисление и вывод тензоров на экран
»> а = tf. zeros ((2, 3))
»> а. eval ()
array([[ 0., 0., 0.],
[ 0., 0., 0.]], dtype=float32)
»> b = tf .ones ((2,2,2))
»> b. eval ()
array([[[ 1., 1.],
[ 1., 1.]],
[[ l.r 1.],
[ 1., 1.]]], dtype=float32)
А если нам нужен тензор, заполненный некоторой величиной, помимо 0/1? Функ­
ция tf .fill о предоставляет для этого хорошую краткую форму (пример 2.5).
46
|
Гпава 2
Пример 2.5. Заполнение тензоров произвольными значениями
»> Ь = tf .fill ((2, 2), value=5.)
>» b.eval ()
array([[ 5., 5.],
[ 5., 5.]], dtype=float32)
tf .constant o — это еще одна функция, похожая на tf .fillо, позволяющая конст­
руировать тензоры, которые не должны изменяться во время исполнения програм­
мы (пример 2.6).
I Пример 2.6. Создание константных тензоров
»> а = tf .constant (3)
»> a.eval ()
3
Отбор случайных значений для тензоров
Хотя работа с константными тензорами удобна для тестирования идей, гораздо
чаще принято инициализировать тензоры случайными значениями. Наиболее рас­
пространенный способ сделать это — отбирать каждое значение в тензоре из слу­
чайного распределения. Функция tf.random normal о позволяет отобрать каждое
значение в тензоре заданной формы из нормального распределения с заданным
средним значением и стандартным отклонением (пример 2.7).
Нарушение симметрии
Многие машинно-обучающиеся алгоритмы обучаются, выполняя обновле­
ния набора тензоров, содержащих веса. Эти уравнения обновления обычно
подчиняются свойству, что веса, инициализированные одинаковым значе­
нием, будут продолжать эволюционировать вместе. Следовательно, если
начальный набор тензоров инициализируется постоянным значением,
то модель не сможет многому обучиться. Для того чтобы исправить
ситуацию, требуется нарушить симметрию. Самый простой способ нару­
шить симметрию — отбирать значения для каждой записи в тензоре слу­
чайным образом.
| Пример 2.7. Отбор случайных значений для тензора из нормального распределения
»> а = tf.random_normal((2, 2), mean=0, stddev=l)
»> a. eval ()
array([[-0.73437649, -0.77678096],
[ 0.51697761,
1.15063596]], dtype=float32)
Следует отметить, что машинно-обучающиеся системы часто используют очень
большие тензоры, которые имеют десятки миллионов параметров. Когда мы отби­
Введение в примитивы TensorFlow |
47
раем десятки миллионов случайных значений из нормального распределения, поч­
ти наверняка окажется, что некоторые отобранные значения будут далеки от сред­
него. Такие большие выборки могут привести к числовой неустойчивости, поэтому
общепринято вместо функции tf. random nomai () выполнять отбор с использовани­
ем функции tf .truncated normai о. Эта функция в терминах API ведет себя так же,
как функция tf. random nomai (), но при этом она отбрасывает и повторно отбирает
все значения, которые больше двух стандартных отклонений от среднего.
Функция tf.random_uniform() ведет себя как функция tf.random_normai() за исклю­
чением того, что случайные значения отбираются из равномерного распределения
в заданном диапазоне (пример 2.8).
Пример 2.8. Отбор случайных значений для тензора из равномерного распределения
»> а = tf.random_uniform((2, 2), minval=-2, maxval=2)
»> a.eval ()
array([[-1.90391684, 1.4179163 ],
[ 0.67762709,
1.07282352]], dtype=float32)
Сложение и шкалирование тензоров
В библиотеке TensorFlow используется перегрузка операторов Python, чтобы упро­
стить базовую тензорную арифметику с помощью стандартных операторов Python
(пример 2.9).
^Пример 2.9. Сложение тензоров
»> с = tf.ones((2, 2))
»> d = tf.ones((2, 2))
>» е = с + d
>» e.eval ()
array([[ 2., 2.],
[ 2., 2.]], dtype=float32)
»> f = 2 * e
»> f.evalO
array([[ 4., 4.],
[ 4., 4.]], dtype=float32)
Таким же образом тензоры можно умножать. Обратите внимание, что при умноже­
нии двух тензоров мы получаем не матричное умножение, а поэлементное умноже­
ние, что можно наглядно увидеть в примере 2.10.
; Пример 2.10. Поэлементное умножение тензоров
I__________________________________ _________ _
»> с = tf .fill ((2,2), 2.)
»> d = tf.fill ((2,2), 7.)
»> e = c * d
48
|
Гпаев 2
»> e.eval ()
array([[ 14., 14.],
[ 14., 14.]], dtype=float32)
Матричные операции
Библиотека TensorFlow предоставляет целый ряд удобств для работы с матрицами.
(Матрицы на сегодняшний день являются наиболее распространенным типом тен­
зоров, используемых на практике.) В частности, TensorFlow предоставляет краткие
формы для создания определенных типов часто используемых матриц. Пожалуй,
наиболее широко используемой является единичная матрица. Единичная матри­
ца— это квадратная матрица, элементы которой равны 0 везде, кроме главной
диагонали, где они равны 1. Функция tf.eyeo позволяет быстро конструировать
единичные матрицы желаемого размера (пример 2.11).
Пример 2.11. Создание единичной матрицы
»> а = tf.eye(4)
»> а. eval ()
array([[ 1., 0., 0., 0.
[ 0., 1., 0., 0.
[ 0., 0., 1., 0.
[ 0., 0., 0., 1.]], dtype=float32 )
Диагональные матрицы — еще один распространенный тип матриц. Как и единич­
ные матрицы, диагональные матрицы являются ненулевыми по диагонали. В отли­
чие от единичных матриц, они могут принимать по диагонали произвольные значе­
ния. Построим диагональную матрицу с восходящими значениями по диагонали
(пример 2.12). Для начала нам понадобится метод построения вектора возрастаю­
щих значений в TensorFlow. Самый простой способ сделать это — вызвать функ­
цию tf.range(начало, граница, дельта}. Обратите внимание, ЧТО значение границы
исключается из диапазона, а дельта — это размер шага для обхода диапазона. По­
лученный вектор затем может быть передан на вход функции tf.diag(диагональ),
которая построит матрицу с заданной диагональю.
Пример 2.12. Создание диагональной матрицы
»> г = tf.ranged, 5, 1)
»> г.eval ()
array([l, 2, 3, 4], dtype=int32)
»> d = tf.diag(г)
»> d.eval ()
array([[l, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]], dtype=int32)
Введение в примитивы TensorFlow |
49
Теперь предположим, что у нас в TensorFlow имеется точно определенная матрица.
Как получить транспонированную матрицу? Функция tf .matrix_transpose() срабо­
тает безупречно (пример 2.13).
Пример 2.13. Получение транспонированной матрицы
»> а = tf.ones((2, 3))
»> a.eval ()
array([[ 1., 1., 1.],
[ 1., 1., 1.]], dtype=float32)
»> at = tf .matrix_transpose (а)
»> at .eval ()
array([[ 1., 1.],
[ l.z 1.],
[ 1., 1.]], dtype=float32)
Теперь предположим, что у нас есть пара матриц, которые мы хотели бы перемно­
жить с помощью матричного умножения. Самый простой способ сделать это —
вызвать функцию tf .matmui о (пример 2.14).
| Пример 2.14. Выполнение матричного умножения
|
»> а = tf.ones((2, 3))
»> a.eval ()
array([[ 1., 1., 1.],
[ 1., 1., 1.]], dtype=float32)
»> b = tf.ones((3, 4))
»> b.eval ()
array([[
1., 1., 1., 1.],
[
1., 1., 1., 1.],
[
1., 1., 1., 1.]],
dtype=float32)
»> c = tf.matmui (a, b)
»> c.evalO
array([[ 3., 3., 3., 3.],
[ 3., 3., 3., 3.]], dtype=float32)
Вы можете проверить, что этот ответ соответствует математическому определению
матричного умножения, которое мы предоставили ранее.
Типы тензоров
Возможно, вы заметили обозначение dtype в предыдущих примерах. Тензоры
в TensorFlow бывают различных типов, таких как tf.float32, tf.float64, tf.int32,
tf.int64. Тензоры точно заданного типа можно создавать, назначая тип dtype
в функции конструирования тензора. Более того, при заданном тензоре можно из­
50
|
Гпава 2
менять его тип при помощи функции приведения типов, таких как
tf .to_float (), tf.to_int32(), tf. to_int64 () И др. (пример 2.15).
tf .to_doubie(),
| Пример 2.15. Создание тензоров различных типов
»> а = tf .ones ((2,2), dtype=tf.int32)
>» a.eval ()
array([[0, 0],
[0, 0]], dtype=int32)
»> b = tf. to_float (a)
»> b.eval ()
array([[ 0., 0.],
[ 0., 0.]], dtype=float32)
Обработка форм тензоров
В TensorFlow тензоры — это просто коллекции чисел, записанных в памяти, а раз­
личные формы — это представления лежащего в основе набора чисел. Они обеспе­
чивают различные способы взаимодействия с этим набором чисел. В разное время
может быть полезно взглянуть на тот же самый набор чисел как формирующий
тензоры с разными формами. Функция tf.reshape() позволяет конвертировать тен­
зоры в тензоры другой формы (пример 2.16).
> Пример 2.16. Манипуляции с формами тензоров
»> а = tf.ones(8)
»> a.eval ()
array([ 1., 1., 1.,
1., 1., 1., 1., 1.], dtype=float32)
»> b = tf. reshape (а,
(4, 2))
»> b.eval ()
array([[ 1., 1.],
[ 1., 1.],
[ 1., 1.],
[ 1., 1.]], dtype=float32)
»> c = tf.reshape(a,
(2, 2, 2))
»> c.eval()
array([[[ 1., 1.],
[ 1., 1.]],
[[ 1., 1.],
[ 1., 1.]]], dtype=float32)
Обратите внимание на то, как с помощью функции tf. reshape о можно превратить
исходный тензор 1-го ранга в тензор 2-го ранга, а затем в тензор 3-го ранга. Хотя
все необходимые манипуляции с формами можно выполнять с помощью
tf. reshape о, иногда может быть удобно осуществить более простые манипуляции
С формой, ИСПОЛЬЗуя такие функции, как tf.expand_dims() ИЛИ tf .squeeze о. Функ­
Введение в примитивы TensorFlow |
51
ция tf .expand_dims о добавляет в тензор новую размерность размера 1. Это полезно
для увеличения ранга тензора на единицу (например, во время конвертации вектора
1-го 0анга в вектор-строку 2-го ранга или вектор-столбец). Функция tf. squeeze о
удаляет из тензора все размерности размера 1. Этот способ полезен, когда нужно
конвертировать вектор-строку или вектор-столбец в плоский вектор.
Таким образом, появляется удобная возможность познакомиться с методом
tf .Tensor.get shape о (пример 2.17). Этот метод позволяет пользователям запраши­
вать форму тензора.
Пример 2.17. Получение формы тензора
I
>» а = tf .ones (2)
»> а. get_shape ()
TensorShape([Dimension(2)])
»> a.eval ()
array([ 1., 1.], dtype=float32)
»> b = tf .expand_dims (a, 0)
»> b. get_shape ()
TensorShape([Dimension(1), Dimension(2)])
»> b.eval ()
array([[ 1., 1.]], dtype=float32)
>» c = tf .expand_dims (a, 1)
»> c.get_shape ()
TensorShape([Dimension(2), Dimension(1)])
»> c.eval ()
array([[ 1.],
[ 1.]], dtype=float32)
»> d = tf .squeeze (b)
>» d.get_shape ()
TensorShape([Dimension(2)])
»> d.eval ()
array([ 1., 1.], dtype=float32)
Введение в операцию транслирования
Транслирование — это термин (введенный в библиотеке NumPy), который приме­
няется для операций, позволяющих складывать между собой матрицы тензорной
системы и векторы разных размеров. Правила транслирования допускают такие
удобства, как добавление вектора в каждую строку матрицы. Эти правила могут
быть довольно сложными, поэтому мы не будем погружаться в их формальное
обсуждение. Часто легче поэкспериментировать и увидеть, как транслирование
работает на практике (пример 2.18).
52
|
Гпава 2
Пример 2.18. Примеры транслирования
»> а = tf.ones((2, 2))
»> a.eval ()
array([[ 1., 1.],
[ 1., 1.]], dtype=float32)
»> b = tf. range (0,
2,
1, dtype=tf. float32)
»> b.eval ()
array([ 0.r 1.]r dtype=float32)
»> c = a + b
»> c.eval ()
array([[ 1., 2.],
[ 1., 2.]], dtype=float32)
Обратите внимание, что вектор ь добавляется в каждую строку матрицы а. Также
обратите внимание на еще одну тонкость: мы явно задаем тип dtype для ь. Если
dtype не задан, то TensorFlow сообщит об ошибке типа. Давайте посмотрим, что бы
произошло, если бы мы не задали dtype (пример 2.19).
Пример 2.19. TensorFlow не выполняет неявное приведение типов,
выводя сообщение о несоответствии типов
»> b = tf.range(О,
2,
1)
»> b.eval ()
array([0 r 1], dtype=int32)
»> с = а + b
ValueError: Tensor conversion requested dtype float32 for Tensor with dtype int32:
'Tensor("range_2:0", shape=(2,), dtype=int32)
В отличие от таких языков, как С, библиотека TensorFlow не выполняет неявное
приведение типов за кадром. Нередко во время выполнения арифметических опе­
раций необходимо выполнять явные приведения типов.
Императивное и декларативное
программирование
Большинство ситуаций в информатике связано с императивным программировани­
ем. Рассмотрим простую программу на Python (пример 2.20).
Пример 2.20. Программа на Python, императивно выполняющая сложение
»> а = 3
»> Ь = 4
»> с = а + b
»> с
7
Введение в примитивы TensorFlow |
53
Эта программа при переводе в машинный код предписывает машине выполнить
примитивные операции сложения двух регистров, где один содержит 3, а другой —
4. Результат равен 7. Этот стиль программирования называется императивным.
поскольку программа явным образом сообщает компьютеру, какие действия вы­
полнять.
Альтернативный стиль программирования— декларативный. В декларативной
системе компьютерная программа представляет собой высокоуровневое описание
вычислений, которые необходимо выполнить. Это описание не указывает компью­
теру, как именно выполнять вычисления. Пример 2.21 является эквивалентом при­
мера 2.20 в TensorFlow.
, Пример 2.21. Программа TensorFlow декларативно выполняет сложение
L
.
___
_______
.
. _
.
________
_________________________ ____________________________________________________ _ ________
I
________________________ —______ I
»> а = tf .constant (3)
»> b = tf .constant (4)
>» c = a + b
»> c
<tf.Tensor 'add_l:0' shape=() dtype=int32>
»> c.evalO
7
Обратите внимание, что значение с не равно 7! Напротив, оно представляет собой
символический тензор. Этот фрагмент кода определяет вычисление сложения двух
значений между собой для создания нового тензора. Фактическое вычисление не
выполняется до тех пор, пока мы не вызовем метод с.eval о. В предыдущих разде­
лах для моделирования императивного стиля в TensorFlow мы использовали метод
eval (), т. к. поначалу декларативное программирование может показаться сложным
для понимания.
Однако декларативное программирование отнюдь не является неизвестным поня­
тием для программной инженерии. Реляционные базы данных и SQL служат при­
мером широко используемой декларативной системы программирования. Такие
команды, как select и join, могут быть реализованы произвольным образом за
кадром при условии, что их базовая семантика соблюдена. Программный код
в TensorFlow лучше рассматривать, как аналог программ SQL; программный код
TensorFlow задает вычисление, которое будет выполнено, а их подробности отданы
на усмотрение TensorFlow. Разработчики TensorFlow за кадром задействуют это
отсутствие подробностей, чтобы адаптировать стиль исполнения программного ко­
да к используемому аппаратному обеспечению будь то CPU (central processing
unit — центральный процессор), GPU (graphics processing unit — графический про­
цессор) или мобильное устройство.
Важно отметить, что главная слабость декларативного программирования заключа­
ется в том, что эта абстракция нередко довольно дырява. Например, без ясного по­
нимания лежащей в основе реализации реляционной базы данных длинные SQLпрограммы могут стать невыносимо неэффективными. Аналогичным образом,
54
|
Гпава 2
большие программы TensorFlow, реализованные без понимания лежащих в основе
обучающихся алгоритмов, вряд ли будут работать хорошо. В остальной части этого
раздела мы начнем разбирать эту абстракцию, процесс, который мы продолжим
выполнять на протяжении всей остальной части книги.
Модуль TensorFlow Eager
Команда разработчиков TensorFlow недавно добавила новый эксперимен­
тальный модуль— TensorFlow Eager, который позволяет пользователям
выполнять вычисления TensorFlow императивно. Со временем этот мо­
дуль, скорее всего, станет предпочтительным для привлечения новых про­
граммистов, изучающих TensorFlow. Тем не менее, на момент написания
данной книги этот модуль по-прежнему остается довольно сырым со мно­
гими недоработками. По этой причине мы не будем знакомить вас с режи­
мом Eager, но рекомендуем обратиться к нему самостоятельно.
Важно подчеркнуть, что подавляющая часть TensorFlow останется декла­
ративной даже после того, как модуль Eager сформируется, поэтому неза­
висимо ни от чего стоит изучать декларативный TensorFlow.
Графы TensorFlow
Любое вычисление в TensorFlow представляется как экземпляр вычислительного
графа tf .Graph. Такой граф состоит из множества экземпляров объектов tf .Tensor
и tf.Operation. Мы рассмотрели объект tf.Tensor, но что же такое объекты
tf.Operation? Вы уже встречали их в этой главе. Вызов такой операции, как
tf .matmui, создает экземпляр tf .Operation, чтобы отметить необходимость выпол­
нения операции матричного умножения.
Когда граф tf.Graph не задан явно, TensorFlow добавляет тензоры и операции
в скрытый глобальный экземпляр tf .Graph. Этот экземпляр может быть извлечен
при ПОМОЩИ функции tf.get_default_graph() (пример 2.22).
Г------------------------------------- ------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 1
Пример 2.22. Получение принятого по умолчанию графа TensorFlow
1
»> tf. get_default_graph ()
<tensorflow.python.framework.ops.Graph>
Существует возможность указывать, что операции TensorFlow должны выполнять­
ся в графах, отличных от заданного по умолчанию. Мы продемонстрируем такие
примеры в последующих главах.
Сеансы TensorFlow
В TensorFlow объект tf. Session о хранит контекст, в котором выполняется вычис­
ление. В начале ЭТОЙ главы МЫ использовали tf.InteractiveSessionо, чтобы
настроить среду для всех вычислений TensorFlow. Этот вызов создал скрытый гло­
Введение в примитивы TensorFlow |
55
бальный контекст для всех выполняемых вычислений. Затем для выполнения на­
ших ДеКЛараТИВНО ОПИСаННЫХ вычислений МЫ ИСПОЛЬЗОВаЛИ МеТОД tf. Tensor, eval о.
За кадром этот вызов вычисляется в контексте скрытого глобального объекта
tf. Session о. Нередко вместо скрытого контекста вычисления может быть удобно
(и часто необходимо) использовать явный контекст (пример 2.23).
: Пример 2.23. Обработка сеансов TensorFlow явным образом
»> sess = tf. Session ()
»> а = tf.ones((2, 2))
»> b = tf .matmul (а, a)
»> b.eval (session=sess)
array([[ 2., 2.],
[ 2., 2.]], dtype=float32)
Этот фрагмент кода вычисляет ь в контексте sess вместо скрытого глобального
сеанса. Фактически мы можем сделать это более явным при помощи альтернатив­
ной формы записи (пример 2.24).
| Пример 2.24. Выполнение вычисления в сеансе
»> sess.run(b)
array([[ 2.,
[ 2.,
2.],
2.]], dtype=float32)
На самом деле, вызов метода b.eval (session=sess) представляет собой синтаксиче­
ский сахар для вызова sess. run (b).
Вся эта информация порой может походить на софистику. Какая разница, какой
сеанс в игре, учитывая, что все различные методы, судя по всему, возвращают один
и тот же ответ? Явно заданные сеансы не проявляют свое значение до тех пор, пока
вы не начнете выполнять вычисления, имеющие состояние, о чем вы узнаете в сле­
дующем разделе.
Переменные TensorFlow
Во всех примерах программного кода этого раздела использовались константные
тензоры. Хотя мы могли комбинировать и рекомбинировать эти тензоры любым
способом, мы никак не могли изменять значение самих тензоров (только создавать
новые тензоры с новыми значениями). До этого момента стиль программирования
был функциональным и не отслеживал состояния. Хотя функциональные вычис­
ления очень полезны, машинное самообучение часто сильно зависит от вычислений
с отслеживанием состояния. Обучающиеся алгоритмы, по сути, представляют со­
бой правила обновления хранящихся тензоров для объяснения предоставляемых
данных. Если нет возможности обновлять эти хранящиеся тензоры, то будет трудно
обучиться.
56
|
Гпава 2
Объект tf. variable о предоставляет оболочку вокруг тензоров, которая позволяет
выполнять вычисления с отслеживанием состояния. Объекты-переменные служат
контейнерами для тензоров. Создать переменную достаточно просто (пример 2.25).
Пример 2.25. Создание переменной TensorFlow
»> а = tf .Variable (tf. ones ( (2, 2)))
»> a
<tf.Variable 'Variable:O' shape=(2, 2) dtype=float32_ref>
Что происходит, когда мы пытаемся вычислить переменную а в качестве тензора,
как в примере 2.26?
I Пример 2.26. Сбой вычисления неинициализированной переменной
! с ошибкой ’’попытка использовать неинициализированную переменную"
»> a.eval ()
FailedPreconditionError : Attempting to use uninitialized value Variable
Вычисление не выполняется, поскольку переменные должны быть явным образом
инициализированы. Самый простой способ инициализации всех переменных —
вызвать функцию tf.global_variabies-initializerо. Выполнение этой операции
в сеансе инициализирует все переменные в программе (пример 2.27).
i Пример 2.27. Вычисление инициализированных переменных
»> sess = tf.Session()
»> sess.run(tf.global_variables_initializer())
»> a.eval (session=sess)
array([[ 1., 1.],
[ 1., 1.]], dtype=float32)
После инициализации мы можем получить значение, хранящееся в переменной, как
если бы это был обычный тензор. До сих пор переменные ненамного отличались от
простых тензоров. Переменные становятся интересными только тогда, когда мы
можем их присваивать. Функция tf. assign о позволяет это делать. При помощи
функции tf. assign о мы можем обновить значение существующей переменной
(пример 2.28).
! Пример 2.28. Присвоение значений переменным
»> sess.run(a.assign(tf .zeros ( (2,2))) )
array([[ 0., 0.],
[ 0., 0.]], dtype=float32)
»> sess.run (a)
array([[ 0., 0.],
[ 0., 0.]], dtype=float32)
Введение в примитивы TensorFlow
|
57
Что произойдет, если попытаться присвоить значение переменной а, не имеющей
форму (2, 2)? Давайте разберемся в примере 2.29.
'Пример 2.29. Присвоение терпит неудачу, когда формы не равны
j
»> sess.run (a.assign (tf .zeros ((3,3))))
ValueError: Dimension 0 in both shapes must be equal, but are 2 and 3 for
'Assign_3' (op: ’Assign') with input shapes: [2,2], [3,3].
Вы видите, что TensorFlow жалуется. Форма переменной фиксируется при инициа­
лизации и должна сохраняться во время обновлений. А вот еще одно интересное
замечание: функция tf. assign о сама по себе является частью базового глобального
экземпляра вычислительного графа tf .Graph. Это позволяет программам TensorFlow
обновлять свое внутреннее состояние при каждом выполнении. Мы будем активно
использовать это функциональное средство в последующих главах.
Резюме
В этой главе мы ввели математическое понятие тензоров и кратко рассмотрели ряд
математических понятий, связанных с тензорами. Затем мы продемонстрировали,
как создавать тензоры в TensorFlow и выполнять те же самые математические опе­
рации в TensorFlow. Мы также кратко представили некоторые лежащие в основе
TensorFlow структуры, такие как вычислительный граф, сеансы и переменные.
Если вы еще не полностью разобрались в понятиях, обсуждаемых в этой главе, то
не переживайте. Мы будем неоднократно использовать их на протяжении остав­
шейся части книги, поэтому у вас будет много возможностей для того, чтобы усво­
ить эти идеи.
В следующей главе мы научим вас строить простые обучающиеся модели для
линейной и логистической регрессии с использованием TensorFlow. Последующие
главы будут строиться на этом фундаменте, чтобы научить вас выполнять трени­
ровку более сложных моделей.
58
|
Гпаеа 2
ГЛАВА 3
Линейная и логистическая регрессия
с помощью TensorFlow
В этой главе мы покажем, как построить простые, но нетривиальные примеры обу­
чающихся систем в TensorFlow. В начале этой главы обсуждаются математические
основы построения обучающихся систем, и, в частности, будут рассмотрены функ­
ции, их непрерывность и дифференцируемость. Мы введем понятие функций
потерь, а затем обсудим то, как машинное самообучение по сути сводится к спо­
собности находить минимальные точки сложных функций потерь. Затем мы рас­
смотрим понятие градиентного спуска и объясним, как его использовать для мини­
мизации функций потерь. Мы закончим изложение кратким обсуждением алгорит­
мической идеи автоматического дифференцирования. Второй раздел посвящен
введению понятий TensorFlow, подкрепленных математическими идеями. Эти по­
нятия включают заполнители, области имен, оптимизаторы и пакет визуализации
TensorBoard и создают условия для практического построения и анализа обучаю­
щихся систем. В заключительном разделе приведены практические примеры тре­
нировки линейной и логистической регрессионных моделей в TensorFlow.
Глава 3 длинная, и в ней представлено много новых идей. Вполне нормально,
если вы не поймете все тонкости идей при первом прочтении. Мы рекомендуем
двигаться вперед и возвращаться, чтобы позже свериться с приведенными здесь
понятиями по мере необходимости. Мы будем многократно использовать фун­
даментальные принципы в оставшейся части книги, чтобы дать вам возможность
постепенно усвоить эти идеи.
Математический обзор
В данном разделе рассматриваются математические инструменты, необходимые
для концептуального понимания машинного самообучения. Мы попытаемся мини­
мизировать количество требуемых греческих символов и вместо этого сосредото­
чимся прежде всего на построении концептуального понимания, а не на техниче­
ских манипуляциях.
Функции и дифференцируемость
В этом разделе предоставлен краткий обзор понятий функций и дифференцируемо­
сти. Функция f— это правило, которое превращает данные на входе в данные на
выходе. Функции имеются во всех языках программирования, и математическое
определение функции не сильно отличается. Однако математические функции,
59
широко используемые в физике и технике, обладают другими важными свойствами,
такими как непрерывность и дифференцируемость. Непрерывная функция, грубо
говоря — это функция, которую можно начертить, не отрывая карандаш от бумаги,
как показано на рис. 3.1. (Это, конечно, не техническое определение, но оно отра­
жает дух свойства непрерывности.)
Рис. 3.1. Несколько непрерывных функций
Дифференцируемость — это свойство гладкости функций. Она говорит, что острые
углы или изгибы в функции недопустимы (рис. 3.2).
Рис. 3.2. Дифференцируемая функция
Ключевым преимуществом дифференцируемых функций является то, что мы мо­
жем использовать наклон функции в определенной точке в качестве руководства
для поиска мест, где функция выше или ниже нашего текущего положения. Это по­
зволяет находить минимумы функции. Производная дифференцируемой функции/
обозначаемая как /', является еще одной функцией, которая характеризует наклон
исходной функции во всех точках. Концептуальная идея заключается в том, что
производная функции в заданной точке дает указатель, сообщающий о направлени­
ях, где функция выше или ниже ее текущего значения. Оптимизационный алгоритм
60
|
Гпава 3
может следовать этому указателю, чтобы приближаться к минимумам. На миниму­
мах функция будет иметь нулевую производную.
Мощь оптимизации под управлением производной сначала не очевидна. Поколения
студентов, штудирующих математический анализ, с трудом продирались через
отупляющие упражнения минимизации крошечных функций на бумаге. Эти
упражнения неполезны тем, что нахождение минимумов функции с небольшим ко­
личеством входных параметров является тривиальным упражнением, которое луч­
ше всего выполнять графически. Мощь оптимизации под управлением производ­
ной становится очевидной только при наличии сотен, тысяч, миллионов или мил­
лиардов переменных. В этих масштабах понимание функции аналитически почти
невозможно, и все визуализации являются сложными упражнениями, чреватыми
непреднамеренным пропуском ключевых атрибутов функции. В этих масштабах
градиент функции V/, обобщение f на многомерные функции, вероятно, являет­
ся самым мощным математическим инструментом для понимания функции и ее
поведения. Мы углубимся в градиенты позже в этой главе. (Концептуально это так,
однако в данной книге мы не будем освещать технические подробности гради­
ентов.)
На очень высоком уровне машинное самообучение — это просто процесс миними­
зации функций: обучающиеся алгоритмы — это не что иное, как искатели миниму­
мов для функций, сформулированных надлежащим образом. Это определение име­
ет преимущество математической простоты. Но каковы же эти особые дифферен­
цируемые функции, которые кодируют полезные решения в своих минимумах и как
их можно найти?
Функции потерь
Для того чтобы решить ту или иную задачу машинного самообучения, аналитик
данных должен найти способ построения функции, минимумы которой кодируют
решения текущей задачи реального мира. К счастью для нашего горемычного ана­
литика данных, литература по машинному самообучению накопила богатый архив
функций потерь, которые выполняют такие кодирования. Практическое машинное
самообучение сводится к пониманию различных типов имеющихся функций потерь
и пониманию того, какая функция потери должна применяться к тем или иным за­
дачам. Другими словами, функция потери — это механизм, с помощью которого
проект в области науки о данных преобразовывается в математику. Все машинное
самообучение, и большая часть искусственного интеллекта, сводится к созданию
правильной функции потери для решения текущей задачи. Мы предоставим вам
краткий обзор некоторых широко используемых семейств функций потерь.
Начнем с того, что функция потери С, чтобы иметь содержательное значение,
должна удовлетворять некоторым математическим свойствам. Во-первых,
С должна использовать как точки данных х, так и метки у. Мы обозначим это, за­
писав функцию потери как £\х, у) . Если использовать понятия из предыдущей
главы, то можно сказать, что х и у являются тензорами, а £ — это отображение из
Линейная и логистическая регрессия с помощью TensorFlow
|
61
пар тензоров в скаляры. Какой должна быть функциональная форма функции поте­
ри? Общепринято делать функции потерь аддитивными. Предположим, что
(х,, у,) — это данные, имеющиеся в прецеденте i, и в общей сложности существует
У прецедентов. После этого функцию потери можно разложить, как
£(х’
у,} ■
1=1
(На практике Ct одинакова для каждой точки данных.) Это аддитивное разложение
позволяет получить много полезных преимуществ. Во-первых, производные факто­
ризуются через сложение, поэтому вычисление градиента общих потерь упрощает­
ся следующим образом:
Уф,у) = ^У£,(^,Л).
1=1
Этот остроумный математический прием означает, что при условии, если более
мелкие функции £, дифференцируемы, значит, то же самое будет и с функцией
полной потери. Из этого следует, что задача проектирования функций потерь сво­
дится к задаче проектирования более мелких функций £((х,, у,). Прежде чем мы
погрузимся в проектирование £., будет полезно сделать небольшое отступление,
которое объяснит разницу между классификационными и регрессионными зада­
чами.
Классификация и регрессия
Машинно-обучающиеся алгоритмы могут быть разбиты на широкие категории:
контролируемые задачи (с учителем) и неконтролируемые задачи (без учителя).
Контролируемые задачи — это такие задачи, для которых имеются как точки дан­
ных х, так и метки у, в то время как неконтролируемые задачи имеют только точки
данных х без меток у. В общем случае контролируемое машинное самообучение
намного сложнее и менее четко сформулировано. (Что значит "понимать" точки х?)
Пока мы не будем углубляться в функции неконтролируемых потерь, поскольку на
практике большинство неконтролируемых потерь умно переиначиваются в контро­
лируемые потери.
Контролируемое машинное самообучение можно разбить на две подзадачи: клас­
сификационную задачу и регрессионную задачу. Классификационная задача — это
задача, в которой вы пытаетесь спроектировать машинно-обучающуюся систему,
которая присваивает дискретную метку, скажем 0/1 (или в более общем случае
0,..., и), конкретной точке данных. Регрессия — это задача проектирования машинно-обучающейся системы, которая прикрепляет вещественную метку (в простран­
стве R) к конкретной точке данных.
На высоком уровне эти проблемы могут показаться несопоставимыми. Дискретные
и непрерывные объекты обычно трактуются по-разному математикой и здравым
смыслом. Однако часть хитрости, используемой в машинном самообучении, за­
ключается в применении непрерывных дифференцируемых функций потерь для
62
|
Гпава 3
кодирования как классификационных, так и регрессионных задач. Как мы уже упо­
минали ранее, подавляющая часть машинного самообучения представляет собой не
что иное, как искусство превращения сложных систем реального мира в достаточно
простые дифференцируемые функции.
В следующих разделах мы познакомим вас с парой математических функций, кото­
рые окажутся очень полезными для преобразования классификационных и регрес­
сионных задач в подходящие функции потерь.
/Лпотеря
Потеря Z2 (произносится как "эль два”) обычно используется для регрессионных
задач. Потеря Z2 (или Z2 -норма, как ее обычно называют в других местах) обеспе­
чивает меру величины вектора:
и=^?Здесь а — вектор длины N. Норма L2 обычно используется для определения рас­
стояния между двумя векторами:
h-bii2=vx,:,(«.-*.r ■
Эта идея с £2 в качестве меры расстояния оказалась очень полезной для решения
регрессионных задач в контролируемом машинном самообучении. Предположим,
что х есть набор данных, а у — это метки. Пусть f есть некоторая дифференцируе­
мая функция, которая кодирует нашу машинно-обучающуюся модель. Тогда, чтобы
побудить f предсказывать у, мы создаем функцию Z2 -потери:
>')=|/W->'l2 •
В качестве краткого примечания, на практике общепринято использовать Z2 -поте­
рю не напрямую, а скорее ее квадрат:
Ца-Ь|224(ч-М2’
i=l
чтобы не заниматься членами вида 1/4х в градиенте. Мы будем многократно ис­
пользовать квадратичную 1} -потерю в оставшейся части этой главы и всей книги.
Режимы сбоя функции /Лпотери
Потеря Z2 резко штрафует крупномасштабные отклонения от истинных меток,
но не делает большой работы по вознаграждению точных совпадений для реаль­
ных меток. Мы можем понять это расхождение математически, изучая поведение
функций х2 и х вблизи начала координат (рис. 3.3).
Линейная и логистическая регрессия с помощью TensorFlow
|
63
Рис. 3.3. Сравнение квадратных и тождественных функций вблизи начала координат
Обратите внимание на то, как х2 уменьшается до 0 при малых значениях х. Как
результат, небольшие отклонения штрафуются Z2-потерей не сильно. В низко­
размерной регрессии это не является серьезной проблемой, но в многомерной
регрессии L2 становится плохой функцией потери, поскольку может иметься
много небольших отклонений, которые вместе делают результат регрессии пло­
хим. Например, при предсказании изображения Z2-потеря создает размытые
изображения, которые визуально не привлекательны. Недавний прогресс в ма­
шинном самообучении привел к разработке способов обучаться функциям
потерь. Эти заученные функции потерь, часто стилизованные в виде генератив­
но-состязательных сетей (GAN-сетей), гораздо больше подходят для многомер­
ной регрессии и способны генерировать неразмытые изображения.
Распределения вероятностей
Прежде чем вводить функции потерь для классификационных задач, полезно сде­
лать небольшое отступление и познакомиться с распределениями вероятностей.
Для начала следует ответить на вопросы: что такое вероятность и почему мы долж­
ны быть в ней заинтересованы для целей машинного самообучения? Вероятность
является обширной темой, поэтому мы погрузимся в нее достаточно глубоко, что­
бы вы всё поняли. На высоком уровне распределения вероятностей реализуют ост­
роумный математический прием, который позволяет смягчить дискретное множе­
ство вариантов выбора, переведя его в бесконечное множество. Предположим, что
вам нужно спроектировать машинно-обучающуюся систему, которая предсказыва­
ет, упадет монета орлом вверх или вниз. Не похоже, что падение орлом вверх/вниз
можно закодировать как непрерывную функцию, а тем более дифференцируемую.
Тогда как воспользоваться механизмом математического анализа или библиотекой
TensorFlow, чтобы решать задачи, связанные с дискретным выбором?
64
|
Гпава 3
Следует внести распределение вероятностей. Вместо трудных вариантов выбора
заставить классификатор предсказывать шанс получать орла вверх или вниз. На­
пример, классификатор может научиться предсказывать, что монета орлом вверх
имеет вероятность 0,75, а монета, упавшая решкой вверх, — вероятность 0,25. Об­
ратите внимание, что вероятности непрерывно меняются! Следовательно, работая с
вероятностями дискретных событий, а не с событиями как таковыми, вы можете
аккуратно обойти проблему, связанную с тем, что математический анализ на самом
деле не работает с дискретными событиями.
Распределение вероятностей р — это просто список вероятностей для располагае­
мых возможных дискретных событий. В этом случае р = (0,75; 0,25). Обратите
внимание, что в качестве альтернативы можно рассматривать р'. {0; 1} -> R, как
отображение из множества, состоящего из двух элементов, в вещественные числа.
Такой взгляд время от времени будет полезен в плане обозначения.
Вкратце отметим, что техническое определение распределения вероятностей будет
сложнее. Существует практическая возможность назначать распределения вероят­
ностей реальным событиям. Мы обсудим такие распределения позже в этой главе.
Перекрестно-энтропийная потеря
Перекрестная энтропия — это математический метод измерения расстояния меж­
ду двумя распределениями вероятностей:
Здесьри^ — это два распределения вероятностей. Форма записи р(х) обозначает,
что вероятность р соответствует событию х. Это определение стоит тщательно
обсудить. Как и норма Z2, Н дает понятие расстояния. Обратите внимание, что
в случае, когда p = q,
Н(р,р) = ~^р(х) log р (х).
Эта величина представляет собой энтропию р и обычно записывается просто, как
Н(р). Это мера того, насколько распределение является беспорядочным; энтропия
максимальна, когда все события равновероятны. Н(р) всегда меньше или равна
Н{р, q). На самом деле, чем дальше распределение q от р, тем больше становится
перекрестная энтропия. Мы не будем углубляться в точные значения этих утвер­
ждений, но интуитивное понимание перекрестной энтропии как механизма рас­
стояния стоит запомнить.
Обратите внимание, что в отличие от нормы L2, Н— асимметрична! То есть,
Н(р, q) * H(q, р). По этой причине рассуждения с перекрестной энтропией могут
быть немного заковыристыми, и лучше всего их делать с некоторой осторож­
ностью.
Возвращаясь к конкретным вопросам, теперь предположим, что р = (у, 1 - у) явля­
ется истинным распределением данных для дискретной системы с двумя исходами,
Линейная и логистическая регрессия с помощью TensorFlow |
65
а Я ~(КреДск’ 1 “ Упредск) — это то> что предсказывается машинно-обучающейся сис­
темой. Тогда перекрестно-энтропийная потеря равна
н (р, q) = У log
+ (1 - у) log (1
Упредск ) ’
Эта форма потери широко используется в машинно-обучающихся системах для
тренировки классификаторов. Эмпирически минимизация
q), судя по всему,
создает классификаторы, которые хорошо воспроизводят предоставленные трени­
ровочные метки.
Градиентный спуск
Итак, к этому моменту вы познакомились с понятием минимизации функции как
косвенным показателем для машинного самообучения. Напомним: для того чтобы
научиться решать желаемую задачу, зачастую достаточно минимизации подходя­
щей функции. Использование этого математического каркаса подразумевает необ­
ходимость применять подходящие функции потерь, такие как потеря £2 или пере­
крестная энтропия H(p,q), с тем, чтобы преобразовать классификационные и рег­
рессионные задачи в подходящие функции потерь.
Заучиваемые веса
До сих пор в этой главе мы объясняли, что машинное самообучение является действием по минимизации надлежаще сформулированной функции
потери £(х, у) . То есть мы пытаемся найти такие аргументы функции по­
тери £, которые ее минимизируют. Однако внимательные читатели
вспомнят, что (х, у) являются фиксированными величинами, которые
нельзя изменить. Какие же аргументы функции £ мы меняем во время
самообучения?
Внесем заучиваемые веса W. Предположим, что f (х) является дифферен­
цируемой функцией, подгонку которой мы хотим выполнить с помощью
нашей машинно-обучающейся модели. Определим, что f параметризуется
выбором W. То есть наша функция фактически имеет два аргумента
f (W, х). Фиксация значения W приводит к функции, которая зависит ис­
ключительно от точек данных х. Такие заучиваемые веса представляют
собой величины, которые фактически выбираются путем минимизации
функции потери1. Позже в этой главе мы увидим, каким образом библио­
тека TensorFlow может использоваться для кодирования заучиваемых
весов при ПОМОЩИ tf.Vartable.
Теперь предположим, что мы закодировали нашу задачу самообучения подходящей
функцией потери. Каким образом можно найти минимумы этой функции потери на
1 Иными словами, когда говорится, что величина (вес, параметр) является заучиваемой, имеется
в виду, что она вычисляется обучающимся алгоритмом путем минимизации функции потери, которая
зависит от тренировочных данных. — Прим. пер.
66
|
Гпава 3
трактике? Ключевой прием, который мы будем использовать, — это минимизация
фадиентного спуска. Предположим, что f есть функция, которая зависит от некото­
рых весов W. Тогда VFF обозначает изменение направления в W, которое макси­
мально увеличит / Из этого следует, что шаг в противоположном направлении
лриблизит нас к минимумам f
Обозначение для градиентов
Мы записали градиент для заучиваемого веса W, как VFF. Иногда удобно
использовать альтернативное обозначение градиента:
dW
Это уравнение следует трактовать так, что градиент VFF кодирует направ­
ление, которое изменяет потерю С в максимальной степени.
Идея градиентного спуска— отыскать минимум функции, неоднократно следуя
в направлении отрицательного градиента. Это правило можно выразить алгоритми­
чески следующим образом:
W = W-aXW,
где а является размером шага и предписывает, сколько веса следует придать но­
вому градиенту VFF . Идея состоит в том, чтобы выполнять много маленьких шаж­
ков в сторону VFF. Обратите внимание, что градиент VFF как таковой является
функцией W, поэтому фактический шаг меняется на каждой итерации. Каждый шаг
делает небольшое обновление весовой матрицы W. Итеративный процесс выпол­
нения обновлений обычно называется заучиванием весовой матрицы W.
Эффективное вычисление градиентов с помощью мини-пакетов
Одна из проблем заключается в том, что вычисление VFF может быть
очень медленным. Неявно VFT зависит от функции потери С. Поскольку
С зависит от всего набора данных, вычисление VFF может стать очень
медленным для больших наборов данных. На практике специалисты обыч­
но вычисляют VFF на части набора данных, называемого мини-пакетом.
Каждый мини-пакет, как правило, состоит из 50-100 элементов данных.
В глубоко обучающемся алгоритме размер мини-пакета является гиперпа­
раметром. Размер а каждого шага— это еще один гиперпараметр. Глу­
боко обучающиеся алгоритмы обычно имеют кластеры гиперпараметров,
которые как таковые не заучиваются посредством стохастического гради­
ентного спуска.
Эта напряженность между заучиваемыми параметрами и гиперпараметра­
ми является одной из слабых и сильных сторон глубоких архитектур. На­
личие гиперпараметров дает много возможностей для использования мощ­
ной интуиции эксперта, в то время как заучиваемые параметры позволяют
данным говорить самим за себя. Однако эта гибкость быстро становится
слабостью, а поведение гиперпараметров выглядит как черная магия, что
Линейная и логистическая регрессия с помощью TensorFlow
|
67
создает начинающим специалистам препятствия для широкого внедрения
глубокого самообучения. Мы потратим значительные усилия на обсужде­
ние гиперпараметрической оптимизации позже в этой книге.
Мы заканчиваем этот раздел введением понятия эпохи. Эпоха — это полный про­
ход алгоритма градиентного спуска по данным х. Говоря конкретнее, эпоха состоит
из множества шагов градиентного спуска, необходимых для просмотра всех дан­
ных в мини-пакете заданного размера. Например, предположим, что набор данных
имеет 1000 точек данных, и в процессе тренировки используется мини-пакет раз­
мером 50 элементов. Тогда эпоха будет состоять из 20 обновлений градиентного
спуска. Каждая эпоха тренировки увеличивает объем полезных знаний, получен­
ных моделью. В математическом смысле это будет соответствовать сокращениям
в значении функции потери в тренировочном наборе.
Ранние эпохи будут вызывать резкое падение функции потери. Этот процесс часто
называют заучиванием априорной информации на том или ином наборе данных.
Хотя кажется, что модель учится быстро, на самом деле она лишь приспосаблива­
ется к пребыванию в той части параметрического пространства, которая имеет от­
ношение к решаемой задаче. Более поздние эпохи будут соответствовать гораздо
меньшим падениям в функции потери, но нередко именно в этих более поздних
эпохах произойдет содержательное самообучение. Несколько эпох, как правило,
дают слишком мало времени для нетривиальной модели на то, чтобы обучиться
чему-нибудь полезному; модели обычно тренируют, используя от 10 до 1000 эпох,
или до тех пор, пока не наступит схождение. Хотя это количество кажется боль­
шим, важно отметить, что количество требуемых эпох обычно не масштабируется
вместе с размером текущего набора данных. Следовательно, градиентный спуск
масштабируется вместе с размером данных линейно, а не квадратично! Это одна из
самых сильных сторон метода стохастического градиентного спуска по сравнению
с другими обучающимися алгоритмами. Более сложные обучающиеся алгоритмы
могут требовать только один проход по набору данных, но могут использовать то­
тальное вычисление, которое масштабируется квадратично с числом точек данных.
В наше время больших наборов данных квадратичное время работы является
фатальным недостатком.
Отслеживание падения в функции потери в зависимости от количества эпох может
быть чрезвычайно полезным визуальным обозначением для понимания процесса
самообучения. Такие графики часто называются кривыми потерь (рис. 3.4). Со вре­
менем опытный практикующий специалист сможет диагностировать общие сбои
в самообучении, просто взглянув на кривую потери. В этой книге мы будем уделять
значительное внимание кривым потерь для различных глубоко обучающихся моде­
лей. В частности, далее в этой главе мы представим TensorBoard — мощный пакет
визуализации, который TensorFlow предоставляет для таких величин, как функции
потерь.
68
|
Гпава 3
Стоимость на тренировочных данных
Рис. 3.4. Пример кривой потери для модели. Обратите внимание, что эта кривая потери получена
из модели, натренированной с помощью истинного градиента (т. е. не мини-пакетного вычисления)
и, следовательно, более гладкой, чем другие кривые потерь, с которыми вы столкнетесь позже
в этой книге
Системы автоматического дифференцирования
Машинное самообучение является искусством определения функций потерь, под­
ходящих для наборов данных, и затем их минимизации. Для минимизации функции
потерь нам нужно вычислить их градиенты и применить алгоритм градиентного
спуска, чтобы итеративно уменьшить потери. Однако нам все еще нужно обсудить,
как на самом деле вычисляются градиенты. До недавнего времени ответ был "от
руки", когда эксперты по машинному самообучению вручную ломали перья и ком­
кали бумагу, вычисляя матричные производные, чтобы вычислить аналитические
формулы для всех градиентов в обучающейся системе. Эти формулы затем вруч­
ную программировали для реализации обучающегося алгоритма. Этот процесс был
заведомо чреват ошибками, и немало экспертов по машинному самообучению
могут поведать истории о случайных ошибках градиента в опубликованных статьях
и производственных системах, остававшихся нераскрытыми в течение многих лет.
Положение дел значительно изменилось с широким распространением механизмов
автоматической дифференциации. Системы, подобные библиотеке TensorFlow, спо­
собны автоматически вычислять градиенты почти для всех функций потерь. Авто­
матическая дифференциация является одним из самых больших преимуществ
TensorFlow и подобных систем, поскольку практикующим специалистам по ма­
Линейная и логистическая регрессия с помощью TensorFlow |
69
шинному самообучению больше не нужно быть экспертами в матричном исчисле­
нии. Тем не менее, по-прежнему есть необходимость в высокоуровневом понима­
нии, каким образом библиотека TensorFlow способна автоматически брать произ­
водные сложных функций. Те читатели, которые в свое время продирались сквозь
вводный курс математического анализа, возможно, помнят, что взятие производ­
ных функций удивительно механическое. Существует ряд простых правил, которые
можно применять для получения производных большинства функций. Например,
ds о*
—е =е .
dx
Эти правила могут быть объединены за счет цепного правила:
7-/(g(jf)) =
dx
W)g' W,
где f — обозначение производной функции f, gr — обозначение производной
функции g. С помощью этих правил легко представить, как можно запрограммиро­
вать механизм автоматической дифференциации для одномерного исчисления.
Действительно, создание такого механизма дифференциации часто входит в учеб­
ный план на первом курсе программирования на занятиях по языку Lisp. (Оказыва­
ется, что правильный разбор функций является гораздо более сложной задачей, чем
получение производных. Язык Lisp делает тривиальным разбор формул с использо­
ванием своего синтаксиса, в то время как на других языках для выполнения этого
упражнения нередко проще дождаться, пока вы не пройдете курс по компиля­
торам.)
Каким образом эти правила могут быть распространены на исчисление более вы­
соких размерностей? Получить правильные математические выкладки сложнее,
поскольку приходится учитывать много других факторов. Например, при наличии
X = АВ, где X, А, В — это матрицы, формула получается следующей:
VA = — = — Br=(VX)Br.
дк
дХ
1
7
Такие формулы могут быть объединены, чтобы обеспечить символическую систе­
му дифференцирования для векторного и тензорного исчисления.
Самообучение с помощью TensorFlow
В оставшейся части этой главы мы рассмотрим понятия, которые вам потребуются,
чтобы изучить основные машинно-обучающиеся модели с использованием библио­
теки TensorFlow. Мы начнем с представления концепции игрушечных наборов
данных и объясним, как создавать содержательные игрушечные наборы данных
с использованием универсальных библиотек Python. Затем мы обсудим новые идеи
TensorFlow, такие как заполнители, словари передача данных, области имен, опти­
70
|
Гпава 3
мизаторы и градиенты. В следующем разделе будет показано, как использовать эти
понятия для тренировки простых регрессионных и классификационных моделей.
Создание игрушечных наборов данных
В этом разделе мы обсудим, как создавать простые, но содержательные синтетиче­
ские, или игрушечные, наборы данных, которые мы будем использовать для трени­
ровки простых контролируемых классификационных и регрессионных моделей.
Чрезвычайно краткое введение в NumPy
Мы будем активно использовать пакет NumPy для определения полезных игрушеч­
ных наборов данных. Пакет Python NumPy позволяет манипулировать тензорами
(которые в NumPy называются массивами). В примере 3.1 показаны некоторые
основы.
Пример 3.1. Несколько примеров элементарного применения пакета NumPy
»> import numpy as пр
»> np. zeros ((2,2))
array([[ 0.,
0.],
[ 0., 0.]])
»> пр.eye(3)
array([[ 1.,
0., 0.],
[ 0., 1., 0.],
[ 0., 0., 1.]])
Вы можете заметить, что манипуляции с массивом NumPy ndarray выглядят удиви­
тельно похоже на манипуляции с тензорами в TensorFlow. Это сходство было спе­
циально спроектировано архитекторами библиотеки TensorFlow. Многие ключевые
сервисные функции TensorFlow имеют аргументы и формы, схожие с аналогичны­
ми функциями в NumPy. Именно по этой причине мы не будем пытаться подробно
знакомить вас с NumPy и предоставим читателям возможность поэкспериментиро­
вать самостоятельно, чтобы разобраться в использовании NumPy. Существует
большое количество интернет-ресурсов, предоставляющих учебные материалы по
NumPy.
Почему важны игрушечные наборы данных?
В машинном самообучении зачастую важно научиться правильно использовать иг­
рушечные наборы данных. Тренировка алгоритма сопряжена с трудностями, и одна
из наиболее распространенных ошибок, которые совершают начинающие специа­
листы, — слишком поспешные попытки натренировать нетривиальные модели на
сложных данных. Эти попытки часто заканчиваются полным провалом, и потенци­
альный специалист в машинном самообучении уходит удрученным и убежденным,
что машинное самообучение не для него.
Линейная и логистическая регрессия с помощью TensorFlow |
71
Настоящий виновник здесь, конечно, не начинающий специалист, а тот факт, что
реальные наборы данных имеют много характерных особенностей. Опытные ана­
литики данных поняли, что реальные наборы данных часто требуют многих преоб­
разований, связанных с очисткой и предобработкой, прежде чем они могут стать
пригодными для применения в машинном самообучении. Глубокое самообучение
усугубляет эту проблему, поскольку большинство глубоко обучающихся моделей,
как известно, чувствительны к погрешностям в данных. Такие проблемы, как ши­
рокий диапазон регрессионных меток или залегающие в основе шаблоны сильных
шумов, могут сбить методы, опирающиеся на градиентный спуск, даже когда дру­
гие машинно-обучающиеся алгоритмы (такие как случайные леса) не будут иметь
проблем.
К счастью, с этими проблемами почти всегда можно справиться, но решение может
потребовать значительной изощренности со стороны аналитика данных. Такие
проблемы чувствительности, пожалуй, являются самым большим препятствием на
пути коммерциализации машинного самообучения как технологии. Мы подробно
рассмотрим стратегии очистки данных, но пока рекомендуем гораздо более про­
стую альтернативу: использовать игрушечные наборы данных!
Игрушечные наборы данных имеют решающее значение для понимания обучаю­
щихся алгоритмов. При наличии очень простых синтетических наборов данных не
составит труда точно определить, обучился ли алгоритм нужному правилу или нет.
На более сложных наборах данных получить такое суждение может оказаться
очень сложным. По этой причине в оставшейся части этой главы по мере освеще­
ния фундаментальных принципов самообучения на основе градиентного спуска
с помощью TensorFlow мы будем использовать только игрушечные наборы данных.
Мы углубимся в практические примеры с использованием реальных данных в по­
следующих главах.
Добавление шума с помощью гауссиан
Ранее мы обсуждали дискретные распределения вероятностей как инструмент для
превращения дискретных вариантов в непрерывные значения. Мы также ссылались
на идею непрерывного распределения вероятностей, но в нее не погружались.
Непрерывные распределения вероятностей (более точно именуемые как функции
плотности вероятности) являются полезным математическим инструментом для
моделирования случайных событий, которые могут иметь диапазон исходов. Для
наших целей достаточно представить функции плотности вероятности, как полез­
ный инструмент для моделирования некоторой ошибки измерения при сборе дан­
ных. Гауссово распределение вероятностей, или гауссиана, широко используется
для моделирования шума.
Обратите внимание, что гауссианы могут иметь разные средние значения ц и
стандартные отклонения о (рис. 3.5). Среднее гауссианы является средним
арифметическим значением, которое она принимает, тогда как стандартное откло­
нение является мерой разброса вокруг этого среднего значения. В общем случае,
добавление гауссовой случайной величины в некоторую величину обеспечивает
72
|
Гпава 3
структурированный способ размыть эту величину, заставляя ее слегка варьировать­
ся. Это очень полезный прием для создания нетривиальных синтетических наборов
Рис. 3.5. Иллюстрации различных гауссовых распределений вероятностей
с разными средними значениями и стандартными отклонениями
Мы кратко отметим, что гауссово распределение также называется нормальным
распределением. Гауссиана со средним ц и стандартным отклонением а записы­
вается, как Л/(ц, п). Это сокращенное обозначение удобно, и в последующих гла­
вах мы будем его использовать неоднократно.
Игрушечные наборы регрессионных данных
Простейшей формой линейной регрессии является заучивание параметров для
одномерной прямой. Допустим, что наши точки данных х— одномерны. Тогда
предположим, что вещественные метки у генерируются линейным правилом:
у = wx + b.
Здесь w, b— это заучиваемые параметры, которые должны быть вычислены из
данных градиентного спуска. Для того чтобы убедиться, что мы можем обучиться
этим параметрам с помощью TensorFlow, создадим искусственный набор данных,
состоящий из точек на прямой. Для того чтобы слегка усложнить задачу самообу­
чения, мы добавим в набор данных немного гауссова шума.
Запишем уравнение для нашей прямой с помехами, внесенными небольшим коли­
чеством гауссова шума:
у = wx + Z> + 7V(0, а) .
Здесь а — это стандартное отклонение шумовой составляющей. Затем, как по­
казано в примере 3.2, мы можем применить NumPy для создания искусственного
набора данных, полученного из этого распределения.
Линейная и логистическая регрессия с помощью TensorFlow |
73
Пример 3.2. Использование NumPy для получения образца
искусственного набора данных
# Сгенерировать синтетические данные.
N - 100
w__true = 5
b__true = 2
noise__scale = .1
x__np = np.random.rand(N,
1)
noise = np.random.normal(scale=noise_scale,
size=(N, 1))
# Конвертировать форму y_np в (N,)
y_np = np. reshape (w__true * x_np + b__true + noise,
(-1))
Построим график этого набора данных при помощи библиотеки Matplotlib на
рис. 3.6 (исходный код можно найти в хранилище GitHub (https://github.com/
matroid/dlwithtf), связанном с этой книгой, чтобы увидеть точную реализацию
построения графика), и проверим, что синтетические данные выглядят разумными.
Как и ожидалось, распределение данных представляет собой прямую линию
с небольшим количеством погрешностей измерения.
Игрушечные данные линейной регрессии у - 5х + 2 + 7V(0,1)
Рис. 3.6. График распределения игрушечных регрессионных данных
Игрушечные наборы классификационных данных
Создать синтетический набор классификационных данных немного сложнее. В ло­
гическом плане мы хотим получить два разных класса точек, которые легко разде­
лимы. Предположим, что набор данных состоит из двух типов точек: (-1, -1) и (1, 1).
74
|
Гпава 3
Тогда обучающийся алгоритм должен будет обучиться правилу, которое разделяет
эти два значения данных.
Л =(-1.-1);
у, =0.1).
Как и раньше, немного усложним задачу, добавив в оба типа точек немного гауссо­
ва шума:
уо=(-1,-1) + У(0, о);
у,=(1,1) + У(0,п).
Вместе с тем здесь есть небольшая хитрость. Наши точки — двумерны, в то время
как гауссов шум, который мы внесли ранее, — одномерен. К счастью, существует
многомерное обобщение гауссианы. Мы не будем обсуждать здесь тонкости мно­
гомерной гауссианы, вам это не нужно, чтобы следить за нашим обсуждением.
Программный код NumPy для создания синтетического набора данных в приме­
ре 3.3 немного сложнее, чем для линейной регрессионной задачи, поскольку мы
должны использовать функцию укладки np.vstack2, чтобы объединить два разных
типа точек данных и связать их с разными метками. (Мы используем связанную
функцию пр.concatenate о, чтобы совместить одномерные метки.)
| Пример 3.3. Получение образца игрушечного набора классификационных данных
■ с помощью NumPy
I-------------------------------- - - -- ------------------------------------------------------------------------------------------------------------------------------
# Сгенерировать синтетические данные
N = 100
# Нули образуют гауссиану, центрированную в (-1, -1)
# эпсилон равняется .1
x_zeros = np.random.multivariate_normal(
mean=np.array((-1, -1)), cov=.l*np.eye(2), size=(N/2,))
y_zeros = np.zeros((N/2,))
# Нули образуют гауссиану, центрированную в (1, 1)
# эпсилон равняется .1
x_ones = np.random.multivariate_normal(
mean=np.array ((1, 1)), cov=.l*np.eye(2), size=(N/2,))
y_ones = np.ones((N/2,))
x_np = np.vstack([x_zeros, x_ones])
y_np = np.concatenate([y_zeros, yones])
На рис. 3.7 показан график данных, сгенерированных приведенным выше про­
граммным кодом с помощью библиотеки Matplotlib, для проверки правильности
распределения. Мы видим, что данные находятся в двух классах, которые четко
разделены.
2 np.vstack () — функция вертикальной укладки массивов в многоярусную последовательность.
См. https://docs.scipy.org/doc/numpy/reference/generated/numpy.vstack.html. — Прим. пер.
Линейная и логистическая регрессия с помощью TensorFlow |
75
Рис. 3.7. График распределения игрушечных классификационных данных
Новые понятия TensorFlow
Для создания простых машинно-обучающихся систем в TensorFlow потребуется
знать несколько новых понятий этой библиотеки.
Заполнители
Заполнитель — это способ ввода информации в вычислительный граф TensorFlow.
Думайте о заполнителях, как о входных узлах, через которые информация поступа­
ет в TensorFlow. Ключевой функцией, используемой для создания заполнителей,
является tf .placeholder (пример 3.4).
, Пример 3.4. Создание заполнителя TensorFlow
»> tf.placeholder(tf.float32, shape=(2,2))
<tf.Tensor ’Placeholder:0’ shape=(2, 2) dtype=float32>
Мы будем использовать заполнители для передачи точек данных х и меток у в наши
регрессионные и классификационные алгоритмы.
Словари передачи данных и выборки
Напомним, что мы можем вычислять тензоры в TensorFlow, используя метод
sess.run(var). Тогда как подавать значения для заполнителей в наших вычислениях
в TensorFlow? Ответ— конструировать словари передачи данных. Словари переда­
ТВ
|
Гпава 3
чи данных — это словари Python, которые увязывают тензоры TensorFlow с объек­
тами-массивами NumPy np.ndarray, содержащими конкретные значения для этих
заполнителей. Словарь передачи данных лучше всего рассматривать, как входные
данные для вычислительного графа TensorFlow. Что же такое выходные данные?
В TensorFlow выходные данные называются выборками (fetches). Вы уже встречали
выборки. Мы широко использовали их в предыдущей главе, не называя их как та­
ковые; выборка — это тензор (или тензоры), значение которого извлекается из вы­
числительного графа после завершения вычисления (используя значения заполни­
телей из словаря передачи данных) (пример 3.5).
Пример 3.5. Использование выборок
|
»> а = tf.placeholder(tt.floatJ2,
shape=(i,))
»> b = tf.placeholder(tf.float32,
shape=(l,))
»> c = a + b
»> with tf.Session () as sess:
c_eval = sess.run(с,
{a:
[1.], b:
[2.]})
print(c_eval)
[ 3.]
Области имен
В сложных программах TensorFlow, безусловно, будет большое количество тензо­
ров, переменных и заполнителей. Функция tf .паше_зсоре(имя) предоставляет про­
стой механизм определения областей видимости для управления этими коллекция­
ми переменных (пример 3.6). Имена всех элементов вычислительного графа, соз­
данных В рамках вызова tf .name scope (имя), ДОПОЛНЯЮТСЯ префиксом имя.
Этот организационный инструмент наиболее полезен в сочетании с пакетом визуа­
лизации TensorBoard, поскольку он помогает системе визуализации автоматически
группировать элементы графа в пределах одной области имен. Вы узнаете больше
о TensorBoard далее в следующем разделе.
^Пример 3.6. Использование областей имен для организации заполнителей
»> N = 5
»> with tf.name_scope("placeholders”) :
х = tf.placeholder(tf.float32,
(N, 1))
у = tf.placeholder(tf.float32,
(N,))
»> x
<tf.Tensor 'placeholders/Placeholder:0' shape=(5, 1) dtype=float32>
Оптимизаторы
Примитивы, представленные в последних двух разделах, уже намекают на то, как
машинное самообучение выполняется в TensorFlow. Вы узнали, как добавлять за­
полнители для точек данных и меток и как использовать тензорные операции для
Линейная и логистическая регрессия с помощью TensorFlow |
77
определения функции потери. Недостающая часть заключается в том, что вы все
еще не знаете, как выполнять градиентный спуск в TensorFlow.
Хотя на самом деле можно определить оптимизационные алгоритмы, такие как
градиентный спуск, непосредственно на Python, используя для этого примитивы
TensorFlow, библиотека TensorFlow предоставляет коллекцию оптимизационных
алгоритмов в модуле tf.train. Эти алгоритмы могут быть добавлены в качестве
узлов в вычислительный граф TensorFlow.
Какой оптимизатор я должен использовать?
^^к
В tf.train имеется большое число возможных оптимизаторов. В качестве краткого предварительного просмотра этот список включает
vw
tf.train.GradientDescentOptimizer, tf.train.Momentumoptimizer, tf.train.
AdagradOptimizer, tf.train.AdamOptimizer
И многие другие. В чем разница
между этими оптимизаторами?
Почти все эти оптимизаторы основаны на идее градиентного спуска.
Вспомните простое правило градиентного спуска, которое мы ввели ранее:
В математическом смысле это правило обновления примитивно. Сущест­
вует целый ряд остроумных математических приемов, обнаруженных
исследователями, которые позволяют выполнять оптимизацию быстрее
без интенсивного использования избыточных вычислений. В общем
случае, хорошим вариантом по умолчанию является оптимизатор
tf.train.AdamOptimizer, который относительно надежен. (Многие методы
в оптимизаторах очень чувствительны к выбору гиперпараметров. Начи­
нающим специалистам лучше избегать заковыристых методов, пока они
в достаточной мере не разберутся в поведении различных оптимизацион­
ных алгоритмов.)
В примере 3.7 приведен короткий фрагмент кода, в котором в вычислительный
граф добавляется оптимизатор, минимизирующий предопределенную потерю 1.
Пример 3.7. Добавление оптимизатора AdamOptimizer
в вычислительный граф TensorFlow
learning_rate = .001
with tf.name_scope("optim"):
train_op = tf.train.AdamOptimizer(learning_rate).minimize (1)
Взятие градиентов с помощью TensorFlow
Ранее мы упоминали, что в TensorFlow можно напрямую реализовывать алгоритмы
градиентного спуска. В то время как в большинстве вариантов использования пере­
определять содержимое модуля tf.train не нужно, может оказаться полезным
взглянуть на значения градиента непосредственно для целей отладки. Функция
tf .gradients о предоставляет для этого полезный инструмент (пример 3.8).
78
|
Гпава 3
Пример 3.8. Прямое взятие градиентов
»> W = tf. Variable ( (3,))
»> 1 = tf. reduce_sum (W)
»> gradW = tf .gradients (1, W)
»> gradW
[<tf.Tensor 'gradients/Sum_grad/Tile:O ’ shape=(l,) dtype=int32>]
Этот фрагмент кода символически стягивает градиенты потери 1 относительно за­
учиваемого параметра (tf.variable) w. Функция tf.gradientsо возвращает список
желаемых градиентов. Обратите внимание, что градиенты как таковые являются
тензорами! TensorFlow выполняет символическое дифференцирование, и это озна­
чает, что сами градиенты являются частями вычислительного графа. Один изящ­
ный побочный эффект символических градиентов TensorFlow заключается в том,
что в TensorFlow имеется возможность укладывать производные в ярусы. Иногда
это может быть полезно для более продвинутых алгоритмов3.
Сводки и пишущие объекты для TensorBoard
Получение визуального понимания структуры тензорной программы может быть
очень полезным. Для этого команда разработчиков TensorFlow предоставляет пакет
TensorBoard. TensorBoard запускает веб-сервер (по умолчанию на localhost), кото­
рый показывает различные полезные визуализации программы TensorFlow. Однако
для того чтобы программы TensorFlow проверялись с помощью TensorBoard, про­
граммисты должны вручную писать инструкции журналирования. Метод
tf. train. Filewriter о задает каталог журналов для программы TensorBoard, а функ­
ция tf .summaryо записывает сводки о различных переменных TensorFlow в задан­
ный каталог журналов. В этой главе мы будем использовать только функцию
tf .summary.scalar о, которая резюмирует скалярную величину, чтобы отслеживать
значение функции потери. Модуль tf содержит функцию tf.summary.merge aiiо,
являющуюся полезным средством ведения журнала, которая для удобства объеди­
няет несколько сводок в одну.
Фрагмент кода в примере 3.9 добавляет сводку о потере и задает каталог журналов.
|~Пример 3.9. Добавление сводного отчета для функции потери
with tf.name_scope ("summaries”) :
tf.summary.scalar("loss", 1)
merged = tf.summary.merge_all()
train_writer = tf.summary.FileWriter('/tmp/lr-train’, tf.get_default_graph())
3 Например, когда имеется две функции. В этом случае градиентные векторы организуют в матрицу,
уложив градиенты один над другим. Когда это делается, получается матрица Якоби. См. http://parrt
cs.usfca.edu/doc/matrix-calculus/index.html. — Прим. пер.
Линейная и логистическая регрессия с помощью TensorFlow |
79
Тренировка моделей с помощью TensorFlow
Предположим, что мы определили заполнители для точек данных и меток и опре­
делили потерю с помощью тензорных операций. Мы добавили в вычислительный
граф узел оптимизатора train op, который можно использовать для выполнения
шагов градиентного спуска (несмотря на то, что мы можем использовать другой
оптимизатор, для удобства мы будем ссылаться на обновления как градиентный
спуск). Каким образом можно итеративно выполнять градиентный спуск, чтобы
обучиться на этом наборе данных?
Самый простой ответ— использовать цикл Python for. В каждой итерации мы
применяем метод sess. run о, чтобы из вычислительного графа извлечь тренировоч­
ную операцию train op вместе с объединенной сводкой merged и потерей 1. Мы пе­
редаем все точки данных и метки в метод sess. run о, используя словарь передачи
данных.
Фрагмент кода в примере 3.10 демонстрирует этот простой метод самообучения.
Обратите внимание, что ради педагогической простоты мини-пакеты мы не приме­
няем. В программном коде последующих глав мини-пакеты будут использоваться
во время тренировки на больших наборах данных.
Гпример 3.10. Простой пример тренировки модели
n_steps = 1000
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# Натренировать модель
for i in range(n_steps):
feed_dict = {x: x_np, y: y_np}
_r summary, loss = sess.run([train_op, merged, 1], feed_dict=feed_diet)
print("шаг %d, потеря: %f" %
(i, loss))
train_writer.add_summary(summary, i)
Тренировка линейной и логистической моделей
в TensorFlow
Этот раздел связывает все концепции TensorFlow, представленные в предыдущем
разделе, для тренировки линейных и логистических регрессионных моделей на иг­
рушечных наборах данных, которые мы ввели ранее в этой главе.
Линейная регрессия в TensorFlow
В этом разделе мы предоставим программный код для определения линейной рег­
рессионной модели в TensorFlow и обучимся ее весам. Эта задача проста, и вы
можете сделать это легко без TensorFlow. Вместе с тем это хорошее упражнение
по работе с библиотекой TensorFlow, поскольку оно сведет вместе новые понятия,
с которыми мы вас знакомили на протяжении всей главы.
80
|
Гпава 3
Определение и тренировка линейной регрессии
в TensorFlow
Модель для линейной регрессии простая:
у = wx + b.
Здесь w и b — это веса, которым мы хотим обучиться. Мы преобразуем эти веса
в объекты tf.variable. Затем мы используем тензорные операции, чтобы сконст­
руировать £2-потерю:
£(х,у) = (у-иа-*)2.
Программный код в примере 3.11 реализует эти математические операции в
TensorFlow. Он также использует функцию tf.name scopeo для группировки раз­
личных операций и добавляет функцию tf.train.AdamOptimizer о для самообучения
и операции tf .summary для использования пакета визуализации TensorBoard.
; Пример 3.11. Определение линейной регрессионной модели
# Сгенерировать граф tensorflow
with tf.name_scope("placeholders "):
x = tf.placeholder(tf.float32,
(N, 1))
у = tf.placeholder(tf.float32,
(N,))
with tf.name_scope("weights"):
# Обратите внимание, что x - это скаляр,
# поэтому W - это одиночный заучиваемый вес.
W = tf .Variable (tf. random__normal ((1, 1)))
b = tf.Variable(tf.random_normal ((1,)))
with tf.name_scope("prediction"):
yjpred = tf.matmul(x, W) + b
with tf .name__scope("loss") :
1 = tf.reduce_sum((y - y_pred)**2)
# Добавить оптимизацию тренировки
with tf.name_scope("optim"):
# Задать скорость заучивания .001, как рекомендовано выше.
train_op = tf.train.AdamOptimizer(.001).minimize(1)
with tf.name_scope ("summaries") :
tf.summary.scalar("loss", 1)
merged = tf.summary.merge_all()
train_writer = tf.summary.FileWriter('/tmp/lr-train', tf.get_default_graph())
Далее пример 3.12 тренирует эту модель, как обсуждалось ранее (не используя
мини-пакеты).
Линейная и логистическая регрессия с помощью TensorFlow |
81
I Пример ЗЛ2. Тренировка линейной регрессионной модели
|
n_steps = 1000
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# Натренировать модель
for i in range(n_steps) :
feed_dict = {x: x_np, y: y_np}
summary, loss = sess.run([train_op, merged, 1], feed_dict=feed_dict)
print (’’шаг %d, потеря: %f’’ %
(i, loss))
train_writer.add_summary(summary,
i)
Весь программный код этого примера размещен в связанном с книгой хранилище
GitHub (https://github.com/matroid/dlwithtf). Мы рекомендуем всем читателям вы­
полнить полный сценарий примера линейной регрессии, чтобы на своем опыте по­
лучить представление о том, как функционирует обучающийся алгоритм. Пример
мал настолько, что для его выполнения читатели не будут нуждаться в каком-то
специализированном вычислительном оборудовании.
Взятие градиентов для линейной регрессии
Уравнение для линейной системы, которую мы моделируем, имеет вид
у = wx + b , где w, b — это заучиваемые веса. Как мы упоминали ранее, по­
теря для этой системы имеет вид £ = (у - wx - Ь)2. Несколько операций
матричного исчисления могут быть использованы для вычисления гради­
ентов заучиваемых параметров непосредственно для w:
V
д£
т
Vw = — = -2(у - wx - Ь)х
dw
и для Ь:
Vb = ^- = -2(y-wx-b).
до
Мы помещаем эти уравнения здесь только в качестве справки для любо­
пытных читателей. Мы не будем пытаться систематически обучать мето­
дам взятия производных функций потерь, с которыми мы сталкиваемся
в этой книге. Однако отметим, что в случае со сложными системами взятие
производной функции потери вручную помогает развить интуитивное по­
нимание того, как глубокая сеть обучается. Это интуитивное понимание
может послужить мощным ориентиром для проектировщика, поэтому мы
призываем продвинутых читателей продолжить изучение этой темы само­
стоятельно.
82
|
Гпава 3
Визуализация линейных регрессионных моделей
с помощью TensorBoard
Модель, определенная в предыдущем разделе, использует пишущий объект
tf.summary.Filewriter для записи журналов в каталог журналов /tmp/lr-train. Мы
можем вызвать TensorBoard в этом каталоге журналов с помощью команды из при­
мера 3.13. (Локальный веб-сервер TensorBoard устанавливается по умолчанию
вместе с TensorFlow.)
1~ Пример 3.13. Запуск веб-сервера TensorBoard
tensorboard —logdir=/tmp/lr-train
Эта команда запустит веб-сервер TensorBoard на порте, закрепленном за localhost.
Откройте этот порт в браузере. Экран TensorBoard будет выглядеть примерно так,
как на рис. 3.8. (Точный внешний вид может меняться в зависимости от вашей вер­
сии TensorBoard.)
Перейдите во вкладку Graphs (Графы), и вы увидите визуализацию архитектуры
TensorFlow, которую мы определили (рис. 3.9).
TensorBoard
scalaps
Write a regex to create a tag group
X
summaries
Q Split on underscores
Q Data download links
Tooltip sorting method: default
*
Smoothing
--------------------------------- —
0.6
Horizontal Axis
STEP
RELATIVE
WALL
Runs
Write a regex to filter runs
Q® ■
Рис. 3.8. Снимок экрана панели TensorBoard
Линейная и логистическая регрессия с помощью TensorFlow |
83
optim
Г loss
j
i f prediction
/
placeholders j (
'j i i
X ,-
weights
Рис. 3.9. Визуализация архитектуры линейной регрессии в TensorBoard (optim — оптимизация,
loss — потеря; prediction — предсказание; placeholders — заполнители; weights — веса)
Обратите внимание, что эта визуализация сгруппировала все элементы вычисли­
тельного графа, принадлежащие различным областям имен tf.name scopes. Различ­
ные группы связаны в вычислительном графе в соответствии с их зависимостями.
Все сгруппированные элементы можно развернуть, чтобы просмотреть их содер­
жимое. На рис. 3.10 показана развернутая архитектура.
Как вы видите, имеется много скрытых узлов, которые внезапно становятся види­
мыми! Функции TensorFlow, такие как tf.train.AdamOptimizer о, часто имеют много
внутренних переменных в собственной области имен tf .name scope. Разворачивание
элементов графа в TensorBoard обеспечивает простой способ заглянуть под капот,
чтобы увидеть, что именно система создает в действительности. Хотя визуализация
выглядит довольно сложной, подавляющая часть этих деталей находится за кадром,
и вам пока не о чем беспокоиться.
Вернитесь во вкладку Ноте (Главная) и откройте раздел Summaries (Сводки).
Теперь вы должны увидеть кривую потери, которая выглядит примерно так, как
показано на рис. 3.11. Обратите внимание на плавную падающую форму кривой.
В начале потеря падает быстро, когда происходит заучивание априорной информа­
ции, а затем убывает и стабилизируется.
Визуальные и невизуальные стили отладки
Нужно ли применять такой инструмент, как TensorBoard, чтобы сполна
воспользоваться возможностями системы TensorFlow? Все зависит от об­
стоятельств. Нужно ли использовать графический интерфейс пользователя
(GUI) или интерактивный отладчик, чтобы стать профессиональным про­
граммистом?
84
|
Гпава 3
optim
beul_pow«r
***** --eSH
gradients
Adam
j
Рис. 3.10. Развернутая визуализация архитектуры
Линейная и логистическая регрессия с помощью TensorFlow |
65
Разные программисты применяют различные стили. Некоторые найдут,
что возможности визуализации TensorBoard — это критическая составная
часть их схемы работы по тензорному программированию. Другие посчи­
тают, что TensorBoard не очень полезен, и будут более широко использо­
вать отладку на основе инструкций print. Оба стиля тензорного програм­
мирования и отладки допустимы, так же как есть замечательные програм­
мисты, которые слепо верят отладчикам, и те, которые их ненавидят.
В целом пакет TensorBoard очень полезен для отладки и развития базового
интуитивного понимания относительно текущего набора данных. Мы
рекомендуем вам следовать стилю, который лучше всего подходит для вас.
TensorBoard
scalars
Write a regex to create a tag group
X
0 Split on underscores
wages
summaries
summaries/loss
2.600e+5
Q] Data download links
2 200e+5
Tooltip sorting method: default
1.800e+5
1.400e+5
1.000e+5
6.000e+4
Smoothing
2.000e+4
------------------- -------------------- 0 6
0.000
3.000k
6.000k
Horizontal Axis
STEP
RELATIVE
WALL
Runs
Write a regex to filter runs
Рис. 3.11. Просмотр кривой потери в TensorBoard
Метрические показатели
для оценивания регрессионных моделей
До этого момента мы не обсуждали, как оценивать, действительно ли натрениро­
ванная модель чему-то обучилась. Первый подход оценивания того, была ли мо­
дель натренирована, — это обратиться к кривой потери, чтобы убедиться, что она
имеет разумную форму. Вы узнали, как это делается в предыдущем разделе. Что
еще попробовать?
Теперь мы хотим, чтобы вы обратились к метрическим показателям, имеющим от­
ношение к модели. Метрический показатель, или метрика, — это инструмент для
сравнения предсказанных меток с истинными метками. Для регрессионных задач
существует два общепринятых метрических показателя: R2 и RMSE (root mean
86
|
Гnaea 3
squared error, среднеквадратическая ошибка). R2 (/?-квадрат) — это мера корреля­
ции между двумя переменными, которая принимает значения от +1 до 0. +1 ука­
зывает на идеальную корреляцию, а 0 — на отсутствие корреляции. Математически
R2 для двух наборов данных / и У определяется как
дг
соу(ЛГ,Г)г
где cov(Jf, У) — это ковариация X и У, мера того, каким образом два набора дан­
ных варьируются совместно;
и aY — это стандартные отклонения, меры того,
насколько каждый набор варьируется индивидуально. В интуитивном плане R2
измеряет, насколько независимая вариация в каждом наборе может быть объяснена
их совместной вариацией.
Несколько типов R2!
На практике широко используются два определения R2. Распространенная
ошибка среди начинающих специалистов (а также экспертов) состоит
в том, что их часто путают. В этой книге мы всегда будем использовать
квадратичный коэффициент корреляции Пирсона (рис. 3.12). Другое
определение называется коэффициентом детерминации. Этот другой
показатель R2 часто гораздо запутаннее, поскольку не имеет нижней гра­
ницы 0, как у квадратичной корреляции Пирсона.
На рис. 3.12 предсказанные и истинные значения сильно коррелируют с R2, рав­
ным почти 1. Похоже, что в этой системе процесс самообучения проделал замечаПредсказанные значения против истинных (Пирсон R2: 0,994)
Рис. 3.12. Вывод графика коэффициента корреляции Пирсона
Линейная и логистическая регрессия с помощью Tensorflow
|
87
тельную работу и успешно обучился истинному правилу. Но не будем торопиться.
Вы заметите, что шкала двух осей на рис. 3.12 не одинаковая! Оказывается, R2 не
штрафует за различия в шкале. Для того чтобы понять, что произошло в этой сис­
теме, нам нужно рассмотреть альтернативный метрический показатель на рис. 3.13.
Истинная модель против обученной модели (RMS: 1,027620)
Рис. 3.13. Вывод графика среднеквадратической ошибки (RMSE)
RMSE является мерой усредненной разницы между предсказанными значениями и
истинными значениями. На рис. 3.13 мы строим предсказанные значения и истин­
ные метки как две отдельные функции, используя точки данных X в качестве нашей
оси х. Обратите внимание, что заученная прямая не является истинной функцией!
RMSE расположен относительно высоко и диагностирует ошибку, в отличие от R2,
который не подхватил эту ошибку.
Что же случилось в этой системе? Почему библиотека TensorFlow не обучилась
правильной функции, несмотря на то, что была натренирована вплоть до схожде­
ния? Этот пример хорошо иллюстрирует один из недостатков алгоритмов гради­
ентного спуска. Они не дают никакой гарантии, что верное решение будет найдено!
Алгоритм градиентного спуска может застрять в локальном минимуме. То есть он
может найти решения, которые выглядят хорошо, но на самом деле не являются
самым низким минимумом функции потери С.
Зачем тогда использовать градиентный спуск? Для простых систем действительно
часто лучше избегать градиентного спуска и использовать другие алгоритмы, кото­
рые имеют более высокие гарантии результативности. Однако в сложных системах,
таких как те, что мы покажем вам в последующих главах, еще не существует
альтернативных алгоритмов, которые работают лучше градиентного спуска. Мы
призываем вас помнить об этом факте, когда будем еще больше погружаться в глу­
бокое самообучение.
88
|
Гпава 3
Логистическая регрессия в TensorFlow
В этом разделе мы определим простой классификатор, используя TensorFlow. Сна­
чала стоит рассмотреть, что собой представляет уравнение для классификатора.
Общепринятый математический прием — задействовать сигмоидальную функцию,
или сигмоиду. Сигмоида, изображенная на рис. 3.14, которую общепринято обо­
значать с, является отображением вещественных чисел R в (0, 1). Это свойство
удобно тем, что мы можем интерпретировать выход из сигмоиды как вероятность
события. (Остроумный прием конвертирования дискретных событий в непрерыв­
ные значения является сквозной темой в машинном самообучении.)
Рис. 3.14. Нанесение сигмоидальной функции
Уравнения для предсказания вероятностей дискретной переменной, принимающей
значение 0 или 1, представлены далее. Эти уравнения определяют простую логи­
стическую регрессионную модель:
у0 =ct(wx + Z>);
yt =l-a(wx + Z>).
TensorFlow предоставляет сервисные функции для вычисления перекрестно­
энтропийных потерь для сигмоидальных значений. Простейшей из этих функций
является tf. пп. sigmoid_cross_entropy_with_iogits (). (Математическая операция взя­
тия логарифма (log) обратна сигмоиде. На практике это просто означает передачу
аргумента wx + b сигмоиды непосредственно в TensorFlow вместо самого сигмои­
дального значения c(wx + b).) Мы рекомендуем вместо ручного определения пере­
крестной энтропии использовать реализацию TensorFlow, поскольку при вычисле­
нии перекрестно-энтропийной потери возникают числовые проблемы.
В примере 3.14 определена простая логистическая регрессионная модель в TensorFlow.
Пример 3.14. Определение простой логистической регрессионной модели
# Сгенерировать граф tensorflow
with tf.name_scope("placeholders"):
# Обратите внимание, что точки данных х - двумерные
х = tf.placeholder(tf.float32,
(N, 2))
у = tf.placeholder(tf.float32,
(N,))
Линейная и логистическая регрессия с помощью TensorFlow
|
89
with tf.name_scope("weights"):
W = tf.Variable(tf.random_normal((2, 1)))
b = tf.Variable(tf.random_normal((1,)))
with tf.name_scope("prediction"):
y_logit = tf.squeeze(tf.matmul(x, W) + b)
# Сигмоида дает вероятность класса 1
y_one_prob = tf.sigmoid(y_logit)
# Округление P(y=l) даст правильное предсказание
y_pred = tf.round(y_one_prob)
with tf.name_scope("loss"):
# Вычислить перекрестно-энтропийный член для каждой точки данных
entropy = tf.nn.sigmoid_cross_entropy_with_logits(logits=y_logit, labels=y)
# Суммировать все вклады
1 = tf.reduce_sum(entropy)
with tf.name_scope("optim"):
train_op = tf.train.AdamOptimizer (.01).minimize(1)
train_writer = tf.summary.FileWriter('/tmp/logistic-train',
tf.get_default_graph())
Тренировочный программный код для этой модели в примере 3.15 идентичен коду
для линейной регрессионной модели.
j Пример 3.15. Тренировка логистической регрессионной модели
n_steps = 1000
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# Натренировать модель
for i in range(n_steps):
feed_dict = {x: x_np, y: y_np}
_, summary, loss = sess.run([train_op, merged, 1], feed_dict=feed_dict)
print("потеря: %f" % loss)
train_writer. add_summary (summary, i)
Визуализация логистических регрессионных моделей
с помощью TensorBoard
Как и прежде, для визуализации модели можно использовать TensorBoard. Начните
с визуализации функции потери (рис. 3.15). Обратите внимание, что, как и раньше,
функция потери следует четкому шаблону. Происходит резкое падение потери
с последующим постепенным сглаживанием.
Вы также можете просмотреть граф TensorFlow в TensorBoard. Поскольку структу­
ра областей имен аналогична структуре, используемой для линейной регрессии,
упрощенный граф выглядит почти идентичным (рис. 3.16).
90
|
Гпава 3
TensorBoard
SCALARS
Write a regex to create a tag group
X
IMAGES
AUDIO
summaries
Q Split on underscores
Q Data download links
Tooltip sorting method: default
▼
Smoothing
-------------------- ..
06
Horizontal Axis
STEP
RELATIVE
WALL
Runs
Write a regex to filter runs
Рис. 3.15. Визуализация функции потери логистической регрессии
Рис. 3.16. Визуализация вычислительного графа для логистической регрессии
(optim — оптимизация, loss — потеря; prediction — предсказание;
placeholders — заполнители; weights — веса)
Линейная и логистическая регрессия с помощью TensorFlow
|
91
Рис. 3.17. Развернутый вычислительный граф для логистической регрессии
92
|
Гпава 3
Однако если вы развернете узлы в этом сгруппированном графе, то обнаружите,
что лежащий в основе вычислительный граф иной (рис. 3.17). В частности, функ­
ция потери сильно отличается от функции, используемой для линейной регрессии
(как и должно быть).
Метрические показатели
для оценивания классификационных моделей
Теперь, когда вы натренировали классификационную модель для логистической
регрессии, вам нужно познакомиться с метрическими показателями, подходящими
для оценивания классификационных моделей. Хотя уравнения для логистической
регрессии сложнее, чем для линейной регрессии, основные метрические показатели
оценки проще. Точность классификации просто проверяет долю точек данных, ко­
торые классифицируются заученной моделью правильно. На самом деле, приложив
немного больше усилий, из уравнений логистической регрессии можно вывести
разделительную линию, заученную моделью логистической регрессии. Эта линия
отображает границу отсечения, которой модель обучилась, чтобы разделять поло­
жительные и отрицательные прецеденты. (Мы оставляем выведение этой линии из
уравнений в качестве упражнения для заинтересованного читателя. Решение нахо­
дится в программном коде для этого раздела.)
Мы показываем заученные классы и разделительную линию на рис. 3.18. Обратите
внимание, что линия четко разделяет положительные и отрицательные примеры
и имеет идеальную точность (1,0). Этот результат затрагивает интересный момент.
Регрессия часто является более сложной задачей, чем классификация. Существует
много возможных линий, которые четко отделяют точки данных на рис. 3.18,
и только одна, которая идеально соответствует данным для линейной регрессии.
Рис. 3.18. Просмотр заученных классов и разделяющей линии для логистической регрессии
Линейная и логистическая регрессия с помощью TensorFlow
|
93
Резюме
В этой главе мы показали вам, как построить и натренировать несколько простых
обучающихся систем в TensorFlow. Мы начали с рассмотрения некоторых фунда­
ментальных математических понятий, в том числе функции потери и градиентного
спуска. Затем мы познакомили вас с несколькими новыми понятиями TensorFlow,
такими как заполнители, области имен и пакет визуализации TensorBoard. Мы за­
кончили главу практическими примерами, которые выполнили тренировку линей­
ной и логистической регрессионных систем на игрушечных наборах данных. В этой
главе мы рассмотрели довольно много материала, и вполне нормально, если вы по­
ка еще не все усвоили. Основной материал, представленный здесь, будет использо­
ваться на протяжении оставшейся части этой книги.
В главе 4 мы познакомим вас с вашей первой глубоко обучающейся моделью и
полносвязными сетями, а также покажем, как определять и тренировать полносвяз­
ные сети в TensorFlow. В последующих главах мы рассмотрим более сложные глу­
бокие сети, но все эти архитектуры будут использовать те же самые фундаменталь­
ные принципы самообучения, что и в этой главе.
94
|
Гпава 3
ГЛАВА 4
Полносвязные глубокие сети
Эта глава познакомит вас с полносвязными глубокими сетями — рабочими лошад­
ками глубокого самообучения, используемыми в тысячах приложений. Основное
преимущество полносвязных сетей заключается в том, что они являются ’’струк­
турно агностическими”, т. е. нам не нужно делать никаких специальных допущений
о природе входных данных (например, что входные данные состоят из изображе­
ний или видео). Мы будем использовать эту универсальность полносвязных глубо­
ких сетей с целью их применения для решения задачи химического моделирования
далее в этой главе.
Мы кратко углубимся в математическую теорию, лежащую в основе полносвязных
сетей. В частности, мы займемся исследованием идеи, что полносвязные архитек­
туры являются ’’универсальными аппроксиматорами”, способными обучаться
любой функции. Эта идея объясняет универсальность полносвязной архитектуры,
но со многими оговорками, которые мы обсудим.
Хотя структурно агностический характер полносвязных сетей позволяет весьма
широко их применять на практике, такие сети, как правило, имеют более низкую
результативность, чем сети специального назначения, настроенные на структуру
предметного пространства. Мы обсудим некоторые ограничения полносвязных
архитектур далее в этой главе.
Что такое полносвязная глубокая сеть?
Полносвязная нейронная сеть состоит из серии полносвязных слоев. Полносвязный
слой — это отображение из пространства R7” в пространство R". Каждая выходная
размерность зависит от каждой входной размерности. В наглядном виде полно­
связный слой представляется так, как показано на рис. 4.1.
Давайте немного углубимся в математическую форму полносвязной сети. Пусть
х g Rm представляет входные данные для полносвязного слоя, а ух g R есть z-й вы­
ход из полносвязного слоя. Тогда ух g R вычисляется следующим образом:
где а — нелинейная функция (пока рассматривайте а как сигмоидальную функ­
цию, введенную в предыдущей главе); w — заучиваемые параметры в сети. Пол­
ный выход у тогда имеет вид:
95
<т(м'
11ill
х +--. + W.
х )
у
im т j
o(w_,X + ... + М'„жХт)
у
л1
1
пт т /
Обратите внимание, что имеется возможность укладывать полносвязные сети
в ярусы. Сеть с многочисленными полносвязными сетями нередко называется глу­
бокой сетью (рис. 4.2).
Рис. 4.1. Полносвязный слой в глубокой сети
Рис. 4.2. Многослойная глубокая полносвязная сеть
96
|
Гпава 4
В качестве краткого примечания к реализации обратите внимание, что уравнение
для одного нейрона очень похоже на скалярное произведение двух векторов
(вспомните обсуждение основ тензоров). В случае слоя нейронов часто удобно для
целей эффективности вычислять у, используя матричное умножение:
y = e(Wx),
где а — это матрица в пространстве R"xm, и нелинейное преобразование а при­
меняется покомпонентно.
"Нейроны" в полносвязных сетях
Узлы полносвязной сети обычно называются ’’нейронами”. Вследствие этого во
всех публикациях полносвязные сети, как правило, принято называть ’’нейронными
сетями”. Эта терминология в значительной степени является исторической случай­
ностью.
В 1940-х годах Уоррен С. Мак-Каллок (Warren S. McCulloch) и Уолтер Питтс
(Walter Pitts) опубликовали первую математическую модель мозга. В своей работе
они утверждали, что нейроны способны вычислять произвольные функции на
булевых величинах. Продолжатели этой работы немного усовершенствовали дан­
ную логическую модель, сделав математические "нейроны" непрерывными функ­
циями, которые варьировались от нуля до единицы. Если входы этих функций ста­
новились достаточно крупными, то нейрон "возбуждался" (принимал значение 1),
а в остальных случаях он находился в состоянии покоя. С добавлением адаптируе­
мых весов это описание совпадает с приведенными выше уравнениями.
Разве не так себя ведет настоящий нейрон? Конечно, нет! Реальный нейрон
(рис. 4.3) представляет собой чрезвычайно сложный механизм с более чем
100 триллионами атомов и десятками тысяч различных сигнальных белков, способ­
ных реагировать на разные сигналы. Микропроцессор является наилучшей анало­
гией нейрона, чем однострочное уравнение.
Во многих отношениях расхождение между биологическими и искусственными
нейронами весьма прискорбно. Непосвященные эксперты читают захватывающие
дух пресс-релизы, утверждающие, что были созданы искусственные нейронные
сети с миллиардами "нейронов" (в то время как мозг имеет всего 100 млрд биоло­
гических нейронов), и думают, что ученые близки к созданию интеллекта на уровне
человека. Не приходится и говорить, что современное состояние глубокого само­
обучения находится на расстоянии десятилетий (если не столетий) от такого дос­
тижения.
Погружаясь в чтение о глубоком самообучении, вы можете столкнуться с крикли­
выми заявлениями об искусственном интеллекте. Не бойтесь опровергать их.
Глубокое самообучение в его текущей форме представляет собой набор методов
для решения задач исчисления на быстром оборудовании. Это не предшественник
Терминатора (рис. 4.4).
Полносвязные глубокие сети
|
97
Рис. 4.3. Более биологически точное представление нейрона
Рис. 4.4. К сожалению (или, вполне возможно, к счастью),
эта книга не научит вас конструировать Терминатора!
Зимы искусственного интеллекта
Искусственный интеллект (ИИ) прошел через несколько этапов бума и
спада в развитии. Такое циклическое развитие характерно для этой облас­
ти. Каждое новое продвижение в машинном самообучении порождает вол­
ну оптимизма, на гребне которой пророки заявляют, что зарождается чело­
векоподобный (или сверхчеловеческий) интеллект. Через пару лет такой
интеллект никак себя не проявляет, и разочарованные спонсоры удаляют­
ся. Получаемый в результате период называется зимой искусственного
интеллекта.
98
|
Гпава 4
До настоящего времени было несколько зим искусственного интеллекта.
В качестве мысленного упражнения мы рекомендуем вам подумать, когда
произойдет следующая такая зима. Нынешняя волна прогресса в глубоком
самообучении позволила решить гораздо больше практических задач, чем
любая предыдущая волна достижений. Возможно ли, что искусственный
интеллект, наконец, поднялся и вышел из цикла бума и спада, или вы ду­
маете, что мы скоро окажемся в "Великой депрессии" ИИ?
Обучающиеся полносвязные сети
с обратным распространением
Первой версией полносвязной нейронной сети был перцептрон (рис. 4.5), создан­
ный Фрэнком Розенблаттом (Frank Rosenblatt) в 1950-х годах. Перцептроны иден­
тичны "нейронам", с которыми мы познакомились в предыдущих уравнениях.
Входные Выходные
сигналы
сигналы
Рис. 4.5. Схематическое представление перцептрона
Перцептроны тренировались на основе специального "правила перцептрона". Не­
смотря на то что перцептроны были умеренно полезны для решения простых задач,
они были принципиально ограничены. Книга "Перцептроны"1 Марвина Мински и
Сеймура Пейперта конца 1960-х годов доказала, что простые перцептроны не
способны обучиться функции xor. На рис. 4.6 представлено доказательство этого
утверждения.
Проблема была преодолена с помощью изобретения многослойного перцептрона
(еще одно название глубокой полносвязной сети). Это изобретение было огромным
достижением, поскольку ранее простые обучающиеся алгоритмы не позволяли глу­
боким сетям обучаться эффективно. Проблема "передачи ответственности" ставила
их в тупик; каким образом алгоритм решает, какой нейрон обучился и чему он обу­
чился?
1 Minsky М., Papert S. Perceptrons. An Introduction to Computational Geometry. — M.I.T. Press, Cambridge,
Mass., 1969. — 258 pp.
Полносвязные глубокие сети
|
99
Рис. 4.6. Линейное правило перцептрона не может обучиться функции XOR
Полное решение этой проблемы требует применения обратного распространения.
Обратное распространение — это обобщенное правило для заучивания весов ней­
ронных сетей. К сожалению, сложные объяснения метода обратного распростране­
ния в публикациях сродни настоящей эпидемии. Эта ситуация вызывает сожаление,
поскольку обратное распространение — это просто еще одно обозначение автома­
тической дифференциации.
Давайте предположим, что f (0, х) — это функция, представляющая собой глубо­
кую полносвязную сеть. Здесь х — это входы в полносвязную сеть, а 0 — заучи­
ваемые веса. Тогда алгоритм обратного распространения просто вычисляет 5/750.
Практические сложности возникают при реализации обратного распространения
для всех возможных функций/, которые возникают на практике. К счастью для нас,
библиотека TensorFlow уже об этом позаботилась!
Теорема об универсальной сходимости
Предыдущее обсуждение затронуло идеи о том, что глубокие полносвязные сети
являются мощными аппроксимациями. Мак-Каллок и Питтс показали, что логиче­
ские сети могут кодировать (почти) любую булеву функцию. Перцептрон Розенб­
латта являлся непрерывным аналогом логических функций Мак-Каллока и Питтса,
но был фундаментально ограничен, и этот факт доказан Мински и Пейпертом.
Многослойные перцептроны пытались разрешить ограниченность простых перцеп­
тронов и эмпирически казались способными обучаться сложным функциям. Однако
теоретически не было ясно, имеет ли эта эмпирическая способность какие-то не­
раскрытые ограничения. В 1989 г. американский ученый Джордж Цыбенко проде­
монстрировал, что многослойные перцептроны способны представлять произволь­
ные функции. Эта демонстрация придала веское обоснование утверждениям об
универсальности полносвязных сетей как обучающейся архитектуры, частично
объяснив их сохраняющуюся популярность.
Вместе с тем, если в конце 1980-х годов были поняты и теория обратного распро­
странения, и теория полносвязных сетей, почему тогда ’’глубокое” самообучение не
стало популярным еще тогда? Значительная часть этой неспособности была вызва­
на вычислительными ограничениями; тренировка полносвязных сетей занимала
100
|
Глава 4
непомерный объем вычислительной мощности. Кроме того, глубокие сети было
очень сложно тренировать из-за отсутствия понимания того, какие гиперпараметры
являются наиболее подходящими. Поэтому большую популярность получили
альтернативные обучающиеся алгоритмы, такие как машины опорных векторов
(SVM), которые имели более низкие вычислительные потребности. Недавний
всплеск популярности глубокого самообучения частично объясняется повышением
доступности более производительного вычислительного оборудования, позволяю­
щего выполнять вычисления быстрее, и частично благодаря применению хороших
схем тренировки, обеспечивающих стабильное самообучение.
Неужели универсальная аппроксимация
настолько удивительна?
Свойства универсальной аппроксимации встречаются в математике чаще,
чем можно было бы ожидать. Например, теорема Вейерштрасса — Стоуна
доказывает, что любая непрерывная функция на замкнутом интервале
может быть приближена многочленной функцией. Если ослабить наши
критерии далее, можно использовать ряды Тейлора и ряды Фурье, предла­
гающие некоторые возможности универсальной аппроксимации (в преде­
лах их областей схождения). Тот факт, что универсальная сходимость —
довольно обычное явление в математике, дает частичное обоснование
эмпирического наблюдения, что существует много малых вариантов пол­
носвязных сетей, которые, судя по всему, дают свойство универсальной
аппроксимации.
Универсальная аппроксимация
не означает универсальное самообучение!
В теореме об универсальной аппроксимации существует критическая тон­
кость. Тот факт, что полносвязная сеть может представлять любую функ­
цию, не означает, что обратное распространение может обучиться любой
функции! Одним из основных ограничений обратного распространения
является то, что нет никакой гарантии ’’сходимости” полносвязной сети,
т. е. отыскания наилучшего доступного решения задачи самообучения.
Когда речь шла о нейронных сетях, этот критический теоретический раз­
рыв вызывал настоящую тошноту у целых поколений специалистов в об­
ласти информатики. Даже сегодня многие ученые предпочитают работать
с альтернативными алгоритмами, которые дают более сильные теоретиче­
ские гарантии.
Эмпирические исследования дали много практических приемов, которые
позволяют обратному распространению находить хорошие решения задач.
Мы подробно рассмотрим многие из этих приемов в оставшейся части
главы. Для практикующего аналитика данных теорема об универсальной
аппроксимации не является чем-то слишком серьезным. Она обнадежива­
ет, но искусство глубокого самообучения заключается в освоении практи­
ческих приемов, которые заставляют самообучение работать.
Полносвязные глубокие сети
|
101
Почему именно глубокие сети?
Тонкость в теореме об универсальной аппроксимации заключается в том, что она
на самом деле справедлива для полносвязных сетей только с одним полносвязным
слоем. Какой тогда смысл в использовании "глубокого” самообучения с многочис­
ленными полносвязными слоями? Оказывается, в академических и практических
кругах этот вопрос до сих пор остается достаточно спорным.
На практике, судя по всему, более глубокие сети могут иногда обучаться более
сложным моделям на больших наборах данных. (Правда, это только эмпирическое
правило; у каждого практикующего специалиста есть множество примеров, когда
глубокие полносвязные сети справляются не очень хорошо.) Это наблюдение за­
ставило исследователей строить гипотезы, что более глубокие сети могут представ­
лять сложные функции "эффективнее", т. е. более глубокая сеть способна обучить­
ся более сложным функциям, чем более мелкие сети с таким же количеством ней­
ронов. Например, архитектура ResNet, кратко упомянутая в главе 7, со 130 слоями,
судя по всему, превосходит своих более мелких конкурентов, таких как AlexNet.
В общем случае, при фиксированном бюджете нейронов более глубокая ярусная
укладка приводит к лучшим результатам.
В публикациях приводится ряд ошибочных "доказательств" этого "факта", но все
они имеют уязвимости. Судя по всему, вопрос глубины в сопоставлении с шириной
затрагивает фундаментальные концепции теории сложности (которая изучает ми­
нимальное количество ресурсов, необходимых для решения конкретных вычисли­
тельных задач). В настоящее время похоже, что теоретическое доказательство (или
опровержение) превосходства глубоких сетей лежит далеко за пределами возмож­
ностей наших математиков.
Тренировка полносвязных нейронных сетей
Как мы уже упоминали ранее, теория полносвязных сетей не соответствует практи­
ке. В этом разделе мы познакомим вас с рядом эмпирических наблюдений о полно­
связных сетях, которые помогают практикующим специалистам. Мы настоятельно
рекомендуем вам использовать наш программный код (представленный далее
в этой главе), чтобы самим проверить наши утверждения.
Заучиваемые представления
Одна из точек зрения на полносвязную сеть заключается в том, что каждый полно­
связный слой осуществляет преобразование признакового пространства, в котором
расположена задача. Идея преобразования представления задачи для придания ей
большей податливости очень стара в технике и физике. Отсюда неудивительно, что
методы глубокого самообучения иногда называют методами "заучивания представ­
лений"2. (Интересный и мало примечательный факт — одна из важнейших конфе­
2 Заучивание представлений (representation learning) — набор методов, позволяющий системе автома­
тически обнаруживать представления, необходимые для обнаружения или классификации признаков
в сырых данных. См. https://en.wikipedia.org/wiki/Feature_learning. — Прим. пер.
102
|
Глава 4
ренций по глубокому самообучению называется Международной конференцией по
заучиванию представлений3.)
Поколения аналитиков использовали преобразования Фурье, Лежандра, Лапласа
и др., чтобы свести сложные уравнения и функции к формам, более приближенным
для рукописного анализа. Одна из точек зрения на глубоко обучающиеся сети
состоит в том, что они осуществляют управляемое данными преобразование, соот­
ветствующее решаемой задаче.
Способность выполнять преобразования, характерные для той или иной задачи,
может быть чрезвычайно мощной. Стандартные методы преобразования не могли
решать задачи анализа изображения или речи, в то время как глубокие сети способ­
ны решать эти задачи с относительной легкостью из-за присущей им гибкости
заученных ими представлений. Эта гибкость имеет свою цену: преобразования,
заученные глубокими архитектурами, как правило, гораздо менее универсальные,
чем математические преобразования, такие как преобразование Фурье. Тем не
менее, наличие глубоких преобразований в аналитическом инструментарии может
быть мощным инструментом решения задач.
Имеется разумный аргумент, что глубокое самообучение— это просто первый
метод заучивания представлений, который работает. В будущем вполне могут
появиться альтернативные методы, которые вытеснят методы глубокого самообу­
чения.
Активации
Ранее мы ввели нелинейную функцию о в качестве сигмоидальной функции. В то
время как сигмоидальная активационная функция является в полносвязных сетях
классическим нелинейным преобразованием, в последние годы исследователи об­
наружили, что другие активационные блоки, в частности выпрямленная линейная
активация (обычно сокращенно ReLU или relu от англ, rectified linear unit)
а(х) = max(x, 0) работает лучше, чем сигмоидальный блок. Это эмпирическое
наблюдение может быть связано с проблемой исчезающего градиента в глубоких
сетях. Для сигмоидальной функции наклон равен нулю почти для всех ее входных
значений, поэтому в случае более глубоких сетей градиент будет стремиться к ну­
лю. Для активационной функции ReLU наклон не равен нулю для гораздо большей
части входного пространства, что позволяет распространять ненулевые градиенты.
На рис. 4.7 представлены сигмоидальные и ReLU-активации.
3 International Conference on Learning Representations (https://iclr.cc/). В кратком обзоре конференции
записано следующее: результативность методов машинного самообучения в значительной степени
зависит от выбора представления данных (или признаков), к которым они применяются. Быстро раз­
вивающаяся область заучивания представлений связана с вопросами о том, каким образом мы лучше
всего сможем обучаться значимым и полезным представлениям данных. — Прим. пер.
Полносвязные глубокие сети
|
103
Насыщенная
Насыщенная
Рис. 4.7. Активационные функции: сигмоидальная (а) и ReLU (6)
Полносвязные сети запоминают
Одним из поразительных аспектов полносвязных сетей является то, что они, как
правило, запоминают тренировочные данные полностью при условии, что предос­
тавлено достаточно времени. Поэтому тренировка полносвязной сети до "схожде­
ния" на самом деле не является содержательным метрическим показателем. Сеть
будет продолжать тренироваться и обучаться до тех пор, пока пользователь готов
ждать.
В случае достаточно больших сетей довольно часто потеря на тренировочных дан­
ных4 стремится к нулю. Это эмпирическое наблюдение — практический пример
возможностей универсальной аппроксимации полносвязных сетей. Вместе с тем
обратите внимание, что стремящаяся к нулю потеря на тренировочных данных не
означает, что сеть обучилась более мощной модели. Весьма вероятно, что модель
начала просто запоминать особенности тренировочного набора, которые не приме­
нимы к любым другим точкам данных.
Здесь стоит разобраться, что именно мы подразумеваем под особенностями. Одним
из интересных свойств многомерной статистики является то, что при наличии дос­
таточно большого набора данных будет иметься обильное множество паразитных
корреляций и шаблонов, которые можно подхватить. На практике полносвязные
сети вполне способны находить и использовать эти ложные корреляции. В целях
успешного моделирования критически важно контролировать сети и предотвра­
щать их неправильное поведение.
Регуляризация
Регуляризация — это общий статистический термин для математической операции,
которая ограничивает запоминание, способствуя самообучению, поддающемуся
обобщению. Существует много различных типов регуляризации, и некоторые из
них мы рассмотрим в нескольких следующих разделах.
4 Имеется в виду показатель потери на тренировочных данных. — Прим. пер.
104
|
Глава 4
Совсем не та регуляризация, которая используется в статистике
Регуляризация имеет долгую историю в статистической литературе, с це­
лыми стопками статей, написанных на эту тему. К сожалению, только
часть этого классического анализа переносится на глубокие сети. Линей­
ные модели, широко используемые в статистике, могут вести себя совер­
шенно иначе, чем глубокие сети, и многое из интуитивного опыта, накоп­
ленного в той среде, может быть совершенно неправильным для глубоких
сетей.
Первое правило для работы с глубокими сетями, в особенности для чита­
телей с предшествующим опытом статистического моделирования, заклю­
чается в доверии эмпирическим результатам по сравнению с прошлым ин­
туитивным опытом. Не исходите из того, что прошлые знания о таких ме­
тодах, как LASSO-регуляризация (Least Absolute Shrinkage and Selection
Operator), имеют большое значение для моделирования глубоких архитек­
тур. Наоборот, подготовьте эксперимент, чтобы методично протестировать
предложенную вами идею. В следующей главе мы вернемся к этому про­
цессу методического экспериментирования и рассмотрим его подробнее.
Отсев
Отсев, или прореживание, — это форма регуляризации, которая случайным обра­
зом сбрасывает какую-то часть узлов, поступающих в полносвязный слой (рис. 4.8).
Здесь сбрасывание узла означает, что его вклад в соответствующую активационную
функцию равняется 0. Поскольку вклад активации отсутствует, градиенты для об­
нуленных узлов также падают до нуля.
Рис. 4.8. Отсев случайным образом обнуляет нейроны сети во время тренировки:
а — стандартная нейронная сеть; б — после применения отсева.
Эмпирически этот метод часто обеспечивает мощную регуляризацию для тренировки сети
Подлежащие отсеву узлы выбираются случайным образом на каждом шаге гради­
ентного спуска. Базовый принцип этой формы регуляризации заключается в том,
что сети будут вынуждены избегать "коадаптации". Вкратце мы объясним, что та­
кое коадаптация и как она возникает в нерегуляризуемых глубоких архитектурах.
Полносвязные глубокие сети
|
105
Предположим, что один нейрон в глубокой сети обучился полезному представле­
нию. Тогда другие нейроны глубже в сети быстро обучатся информативно зависеть
от этого конкретного нейрона. Этот процесс сделает сеть хрупкой, поскольку она
будет чрезмерно зависеть от заученных этим нейроном признаков, которые могут
представлять собой специфическую особенность набора данных, вместо того чтобы
обучиться универсальному правилу.
Отсев предотвращает этот тип коадаптации, потому что больше не будет возмож­
ности зависеть от наличия одиночных мощных нейронов (поскольку этот нейрон
может случайным образом выпасть во время тренировки). Как результат, другие
нейроны будут вынуждены ’’подменить собой выбывшего бойца” и обучиться по­
лезным представлениям. Из этого теоретически следует, что такой процесс должен
привести к более прочным заученным моделям.
На практике отсев имеет пару эмпирических эффектов. Во-первых, он не позволяет
сети запоминать тренировочные данные; с отсевом потеря на тренировочных дан­
ных больше не будет быстро стремиться к 0, даже для очень больших глубоких
сетей. Во-вторых, отсев, как правило, немного повышает предсказательную спо­
собность модели на новых данных. Этот эффект часто имеет место для широкого
спектра наборов данных, отчасти по той причине, что отсев признан мощным изо­
бретением, а не просто простым статистическим приемом.
Следует отметить, что во время выполнения предсказаний отсев должен быть
отключен. Если забыть отключить отсев, то это может привести к предсказаниям,
намного более шумным и менее полезным, чем они были бы в противном случае.
Позже в этой главе мы обсудим, как правильно обращаться с отсевом для трени­
ровки и предсказаний.
Как большие сети избегают переподгонки?
Неожиданным результатом для статистиков с классической подготовкой
является то, что глубокие сети обычно могут иметь внутренних степеней
свободы больше, чем в тренировочных данных. В классической статистике
наличие этих дополнительных степеней свободы сделало бы модель бес­
полезной, поскольку больше не будет гарантии того, что заученная модель
’’реальна” в классическом смысле.
Тогда каким образом глубокая сеть с миллионами параметров может обу­
читься содержательным результатам на наборах данных всего из тысяч
примеров? Отсев может иметь здесь большое значение и предотвратить
грубое запоминание. Но есть также более глубокая необъяснимая тайна,
которая заключается в том, что глубокие сети будут стремиться обучаться
полезным фактам даже при отсутствии отсева. Эта тенденция может быть
следствием некой особенности метода обратного распространения или же
самой полносвязной сетевой структуры, которую мы еще не понимаем.
Ранняя остановка
Как уже упоминалось, полносвязные сети, как правило, запоминают все, что перед
ними находится. На практике часто бывает полезно отслеживать результативность
сети на отложенном ’’контрольном” наборе и останавливать сеть, когда результа­
106
|
Глава 4
тивность в этом контрольном наборе начинает снижаться. Этот простой метод на­
зывается ранней остановкой.
На практике раннюю остановку бывает довольно сложно реализовать. Как вы уви­
дите, кривые потерь для глубоких сетей могут сильно отличаться в ходе нормаль­
ной тренировки. Разработка правила, которое отделяет здоровую вариацию от
заметной нисходящей тенденции, может потребовать значительных усилий. На
практике многие практикующие специалисты просто тренируют модели с разли­
чающимся (фиксированным) количеством эпох и выбирают ту модель, которая
лучше всего подходит для контрольного набора. На рис. 4.9 показано, как точность
на тренировочном и тестовом наборах обычно изменяется по мере прохождения
тренировки.
Рис. 4.9. Точность модели на тренировочном и тестовом наборах данных
по мере прохождения тренировки
В следующей главе мы более подробно рассмотрим надлежащие методы работы
с контрольными наборами.
Регуляризация весов
Классический метод регуляризации, взятый из статистической литературы, штра­
фует заученные веса, которые становятся большими. Обозначим через £(х, у)
функцию потери для конкретной модели и через 0 заучиваемые параметры этой
модели. Тогда регуляризованная функция потери определяется в следующем виде:
С(х, у) = £(х, у) + а||0||,
где ||0|| ||0||2— весовой штраф; — настраиваемый параметр. Два распространенных
варианта для штрафа — штрафы £' и L2
И =ГМ
Полносвязные глубокие сети
|
107
где ||0||2 и ЦОЦ, обозначают соответственно штрафы Z1 и Г. Из личного опыта эти
штрафы, как правило, менее полезны для глубоких моделей, чем отсев и ранняя
остановка. Некоторые практикующие специалисты по-прежнему используют регу­
ляризацию веса, поэтому, безусловно, стоит разобраться в том, как применять эти
штрафы при настройке глубоких сетей.
Тренировка полносвязных сетей
Тренировка полносвязной сети требует нескольких приемов, помимо тех, которые
вы встречали до сих пор в этой книге. Во-первых, в отличие от предыдущих глав,
мы будем тренировать модели на больших наборах данных. С учетом этих наборов
данных мы покажем вам, как использовать мини-пакеты для ускорения градиент­
ного спуска. Во-вторых, мы вернемся к теме настройки скоростей заучивания.
Мини-пакетирование
В случае больших наборов данных (которые могут даже не поместиться в опера­
тивной памяти) отсутствует возможность вычислять градиенты на полном наборе
данных на каждом шаге. Вместо этого практикующие специалисты часто выбирают
небольшой фрагмент данных (обычно 50-500 точек данных) и вычисляют градиент
на этих точках данных. Этот небольшой фрагмент данных традиционно называется
мини-пакетом.
На практике мини-пакетирование, судя по всему, способствует схождению,
поскольку может быть предпринято больше шагов градиентного спуска с тем же
количеством вычислений. Правильный размер мини-пакета— это эмпирический
вопрос, который нередко решается с помощью гиперпараметрической настройки.
Скорости заучивания
Скорость заучивания определяет степень важности каждого шага градиентного
спуска. Установка правильной скорости заучивания может представлять слож­
ность. Многие начинающие специалисты по глубоким сетям устанавливают скоро­
сти заучивания неправильно и с удивлением обнаруживают, что их модели не
обучаются или начинают возвращать значения NaN (не определено). Эта ситуация
значительно улучшилась с развитием таких методов, как ADAM5, которые значи­
тельно упрощают выбор скорости заучивания. Однако если модели ничему не обу­
чаются, то стоит заняться тонкой настройкой скорости заучивания.
5 Adam (Adaptive Moment Estimation, метод адаптивной оценки момента градиента) представляет со­
бой обновление оптимизатора RMSProp. В этом оптимизационном алгоритме используются скользя­
щие средние как градиентов, так и вторых моментов градиентов. RMSProp (метод адаптивного сколь­
зящего среднего градиентов) — это метод адаптивного самообучения без сжатия скорости заучива­
ния. См. Википедию https://en.wikipedia.Org/wiki/Stochastic_gradient_descent#Adam. — Прим. пер.
108
|
Глава 4
Реализация в TensorFlow
В этом разделе мы покажем, как реализовать полносвязную сеть в TensorFlow.
Здесь нам не придется вас знакомить со многими новыми примитивами TensorFlow,
поскольку мы уже рассмотрели подавляющую часть требующихся основ.
Инсталляция DeepChem
В этом разделе для своих экспериментов вы будете использовать набор инструмен­
тальных средств машинного самообучения DeepChem (один из авторов книги был
создателем DeepChem). Подробные указания по установке DeepChem можно найти
в Интернете (https://deepchem.io), но заметим, что инсталляция дистрибутива
Anaconda с помощью инструмента conda, вероятно, будет наиболее удобной.
Набор данных Тох21
Для нашего примера моделирования мы будем использовать химический набор
данных. Токсикологи очень заинтересованы в использовании машинного самообу­
чения в целях предсказания, будет ли то или иное химическое соединение токсич­
ным или нет. Эта задача чрезвычайно сложна, т. к. современная наука имеет лишь
ограниченное представление о метаболических процессах, происходящих в орга­
низме человека. Однако биологи и химики разработали ограниченный набор экспе­
риментов, которые дают показания по токсичности. Если химическое соединение
в одном из этих экспериментов "попадает в цель", то оно, вероятно, будет для чело­
века токсичным при внутреннем употреблении. Вместе с тем эти эксперименты не­
редко являются дорогостоящими, поэтому аналитики данных стремятся построить
машинно-обучающиеся модели, которые могут предсказывать результаты этих
экспериментов на новых молекулах.
Одна из наиболее важных коллекций токсикологических данных называется Тох21.
Она была выпущена Национальными институтами здоровья (NIH) и Агентством по
защите окружающей среды (ЕРА), США, в рамках инициативы в области науки о
данных и использовалась в качестве набора данных в конкурсе построения модели.
Победитель этого конкурса использовал многозадачные полносвязные сети (вари­
ант полносвязных сетей, где каждая сеть предсказывает несколько величин для ка­
ждой точки данных). Мы проанализируем один из наборов данных коллекции
Тох21. Этот набор данных состоит из 10 тыс. молекул, протестированных на взаи­
модействие с рецептором андрогена. Конкурсная задача в области науки данных —
предсказать, будут ли новые молекулы взаимодействовать с рецептором андрогена.
Обработка этого набора данных может представлять сложность, поэтому мы будем
использовать коллекцию наборов данных MoleculeNet, курируемую в рамках про­
екта DeepChem. Каждая молекула в Тох21 переработана проектом DeepChem в битвектор длиной 1024. Загрузка набора данных, таким образом, представляет собой
несколько простых обращений в DeepChem (пример 4.1).
Полносвязные глубокие сети
|
109
Пример 4.1. Загрузка набора данных Тох21
import deepchem as de
(train, valid, test), _ = dc.molnet.Ioad_tox21()
train_X, train_y, train_w = train.X, train.y, train.w
valid_X, valid_y, valid_w = valid.X, valid.y, valid.w
test_X, test_y, test_w = test.X, test.y, test.w
Здесь переменные x содержат обработанные признаковые векторы, переменные у —
метки, а переменные w— веса прецедентов. Метки являются бинарными 1/0 для
химических соединений, которые взаимодействуют или не взаимодействуют
с рецептором андрогена. Тох21 содержит несбалансированные наборы данных, где
положительных прецедентов гораздо меньше, чем отрицательных. Переменные w
содержат рекомендуемый вес для каждого прецедента, в которых больше внимания
уделяется положительным прецедентам (увеличение важности редких прецедентов
является распространенным методом обработки несбалансированных наборов дан­
ных). Для простоты мы не будем использовать эти веса во время тренировки. Все
эти переменные являются массивами NumPy.
В Тох21 имеется больше наборов данных, чем мы здесь будем анализировать, по­
этому нам нужно удалить метки, связанные с этими лишними наборами данных
(пример 4.2).
| Пример 4.2. Удаление лишних наборов данных из Тох21
J
# Удалить лишние задачи
train_y = train_y[:, 0]
valid_y = valid_y[:, 0]
test_y = test_y[:, 0]
train_w = train_w[:, 0]
valid_w = valid_w[:, 0]
test_w = test_w[:, 0]
Принятие мини-пакетов заполнителей
В предыдущих главах мы создавали заполнители, которые принимали аргументы
фиксированного размера. Во время работы с мини-пакетными данными часто
удобно иметь возможность подавать пакеты переменного размера. Предположим,
что набор данных содержит 947 элементов. Тогда с размером мини-пакета, равным
50 элементам, последний пакет будет иметь 47 элементов. Это приведет к фаталь­
ному сбою программного кода из главы 3. К счастью, TensorFlow позволяет запро­
сто исправить ситуацию: использование None в качестве размерного аргумента для
заполнителя дает возможность заполнителю принимать тензоры с произвольным
размером в конкретной размерности (пример 4.3).
110
|
Глава 4
‘ Пример 4.3. Определение заполнителей,
! которые принимают мини-пакеты разных размеров
d = 1024
with tf.name_scope("placeholders"):
x = tf.placeholder(tf.float32,
(None, d))
у = tf.placeholder(tf.float32,
(None,))
Обратите внимание, что d равняется 1024, т. e. размерности наших признаковых
векторов.
Реализация скрытого слоя
Программный код для реализации скрытого слоя очень похож на тот код, который
мы встречали в предыдущей главе для реализации логистической регрессии, как
показано в примере 4.4.
i Пример 4.4. Определение скрытого слоя
with tf.name_scope("hidden-layer"):
W = tf.Variable(tf.random_normal ((d, n_hidden)))
b = tf.Variable(tf.random_normal((n_hidden,)))
x_hidden = tf.nn.relu(tf.matmul(x, W) + b)
Мы применяем функцию tf .name scope о для группировки введенных переменных.
Обратите внимание, что мы используем матричную форму полносвязного слоя. Мы
выбрали форму xW вместо Wx для того, чтобы удобнее справляться поочередно
с каждым мини-пакетом входных данных. (В качестве упражнения попробуйте раз­
работать соответствующие размерности, чтобы понять, почему это именно так.)
Наконец, мы применяем нелинейное преобразование ReLU с помощью встроенной
активационной функции t f. пп. relu ().
Оставшаяся часть программного кода для полносвязного слоя очень похожа на код,
используемый для логистической регрессии в предыдущей главе. Для полноты
в примере 4.5 мы привели полный программный код, используемый для определе­
ния сети. Напоминаем, что полный программный код для всех моделей доступен
в связанном с этой книгой хранилище GitHub. Мы настоятельно рекомендуем вам
попробовать выполнить этот код самостоятельно.
i Пример 4.5. Определение полносвязной архитектуры
I
with tf.name_scope("placeholders"):
x - tf.placeholder(tf.float32,
(None, d))
у = tf.placeholder(tf.float32,
(None,))
with tf.name_scope("hidden-layer"):
W = tf.Variable(tf.random_normal((d, n_hidden)))
b = tf.Variable(tf.random_normal((n_hidden,)))
x_hidden - tf.nn.relu(tf.matmul(x, W) + b)
Полносвязные глубокие сети
|
111
with tf.name_scope("output"):
W = tf.Variable(tf.random_normal((n_hidden, 1)))
b = tf.Variable(tf.random_normal ((1,)))
y_logit = tf.matmul(x_hidden, W) + b
# Сигмоида дает вероятность класса 1
y_one_prob = tf.sigmoid(y_logit)
# Округление P(y=l) даст правильное предсказание
y_pred = tf.round(y_one_prob)
with tf.name_scope("loss"):
# Вычислить перекрестно-энтропийный член для каждой точки данных
y_expand = tf.expand_dims(у, 1)
entropy = tf.nn.sigmoid_cross_entropy_with_logits(logits=y_logit,
1abe1s=y_expand)
# Суммировать все вклады
1 = tf.reduce_sum(entropy)
with tf.name_scope("optim"):
train_op = tf.train.AdamOptimizer(learning_rate).minimize(1)
with tf.name_scope("summaries"):
tf.summary.scalar("loss", 1)
merged = tf.summary.merge_all()
Добавление отсева в скрытый слой
Библиотека TensorFlow берет на себя реализацию отсева за нас во встроенном при­
митиве tf.nn.dropout(х, keep_prob), где keep_prob— это вероятность того, что лю­
бой заданный узел остается. Напомним из нашего предыдущего обсуждения, что
мы хотим включить отсев во время тренировки и отключить его во время выполне­
ния предсказаний. Для того чтобы справиться с этим правильно, мы введем новый
заполнитель для keep probe, как показано в примере 4.6.
■ Пример 4.6. Добавление заполнителя для вероятности отсева
j
keep_prob = tf.placeholder(tf.float32)
Во время тренировки мы передаем желаемое значение, часто 0,5, но во время тес­
тирования мы назначаем keep prob значение 1,0, т. к. хотим, чтобы предсказания
делались всеми заученными узлами. При такой настройке добавление отсева в пол­
носвязную сеть, определенную в предыдущем разделе, представляет собой всего
одну дополнительную строку кода (пример 4.7).
| Пример 4.7. Определение скрытого слоя с отсевом
with rf.name_scope("hidden-layer"):
W = tf.Variable(tf.random_normal((d, n_hidden)))
b = tf.Variable(tf.random_normal((n_hidden,)))
112
|
Глава 4
x_hidden = tf.nn.relu(tf.matmul(x, W) + b)
# Применить отсев
x_hidden = tf.nn.dropout(x_hidden, keep_prob)
Реализация мини-пакетирования
Для того чтобы реализовать мини-пакетирование, нам нужно вытаскивать данные
в размере мини-пакета всякий раз, когда мы вызываем метод sess.run. К счастью
для нас, наши признаки и метки уже находятся в массивах NumPy, и мы можем ис­
пользовать удобный синтаксис NumPy для нарезки частей массивов (пример 4.8).
Пример 4.8. Тренировка на мини-пакетах
step = О
for epoch in range(n_epochs):
pos = 0
while pos < N:
batch_X = train_X[pos:pos+batch_size]
batch_y = train_y[pos:pos+batch_size]
feed_dict = {x: batch_X, y: batch_y, keepjDrob: dropout_prob}
_, summary, loss = sess.run([train_op, merged, 1],
feed_dict=feed_diet)
print("эпоха %d, step %d, потеря: %f" %
(epoch, step, loss))
train_writer.add_summary(summary, step)
step += 1
pos += batch—size
Оценивание точности модели
Для оценки точности модели стандартная практика требует измерения точности
модели на данных, не используемых для тренировки (а именно, на контрольном
наборе). Правда, тот факт, что данные не сбалансированы, вносит свою сложность.
Метрический показатель точности классификации, которую мы использовали
в предыдущей главе, просто измеряет долю точек данных, которые были помечены
правильно. Однако 95% данных в нашем наборе помечены как 0 и только 5% поме­
чены как 1. В результате этого модель "с одними нулями" (которая все помечает
отрицательно) достигнет 95% точности! Это не то, чего мы хотим.
Более приближенным вариантом было бы увеличить вес положительных прецеден­
тов, чтобы они получили большее значение. Для этой цели мы используем реко­
мендуемые веса для каждого прецедента из MoleculeNet для вычисления взвешен­
ной точности классификации, где положительные образцы в 19 раз больше веса
отрицательных образцов. При такой взвешенной точности модель "с одними нуля­
ми" будет иметь точность 50%, что кажется гораздо более разумным.
Полносвязные глубокие сети
|
113
Для
вычисления
score (true,
pred,
взвешенной ТОЧНОСТИ
МЫ
используем
функцию
accuracy­
sample_weight=given_sample_weight) ИЗ ПОДМОДУЛЯ sklearn.metrics.
Она имеет именованный аргумент sample weight, который позволяет нам задавать
желаемый вес для каждой точки данных. Мы используем эту функцию для вычис­
ления взвешенного метрического показателя как на тренировочном, так и на кон­
трольном наборах данных (пример 4.9).
Пример 4.9. Вычисление взвешенной точности
t rairi-Weighted-SCore
accuracy_score (train_y,
train_y_pred,
sample_weight=train_w)
print("Тренировочная взвешенная точность классификации: %f" %
train_weighted—score)
valid_weighted_score = accuracy_score(valid_y, valid_y_pred,
sample_weight=valid_w)
print("Контрольная взвешенная точность классификации: %f" %
valid_weighted_score)
Хотя мы могли бы переопределить эту функцию сами, иногда проще (и менее под­
вержено ошибкам) использовать стандартные функции из программной инфра­
структуры Python для науки о данных. Осведомленность об этой инфраструктуре и
имеющихся функциях является частью обязанностей практикующего аналитика
данных. Теперь мы можем натренировать модель (для 10 эпох в принятых по умол­
чанию настройках) и измерить ее точность:
Тренировочная взвешенная точность классификации: 0.742045
Контрольная взвешенная точность классификации: 0.648828
В главе 5 мы покажем вам методы систематического повышения этой точности и
более тщательной настройки нашей полносвязной модели.
Использование пакета TensorBoard
для отслеживания схождения модели
Теперь, когда мы определили нашу модель, давайте воспользуемся пакетом визуа­
лизации TensorBoard для обследования модели. Сначала проверим структуру графа
в TensorBoard (рис. 4.10).
Граф похож на граф логистической регрессии с одним исключением— добавлен
новый скрытый слой. Давайте развернем скрытый слой, чтобы увидеть, что внутри
(рис. 4.11).
Вы видете, как здесь представлены новые тренируемые переменные и операция
отсева. Кажется, все на своем месте. Теперь давайте закончим, посмотрев на кри­
вую потери во времени (рис. 4.12).
114
|
Глава 4
Рис. 4.10. Визуализация вычислительного графа для полносвязной сети (optim — оптимизация,
loss — потеря; output — выход; hidden-layer — скрытый слой; placeholders — заполнители)
Рис. 4.11. Визуализация развернутого вычислительного графа для полносвязной сети
Полносвязные глубокие сети
|
115
summaries/loss
Рис. 4.12. Визуализация кривой потери для полносвязной сети
Кривая потери отклоняется вниз, как мы видели в предыдущем разделе. Но давайте
увеличим масштаб, чтобы посмотреть, как эта потеря выглядит вблизи (рис. 4.13).
summaries/loss
180,0
220,0
260,0
300,0
Рис. 4.13. Увеличивание масштаба части кривой потери
Обратите внимание, что потеря выглядит гораздо ухабистей! Это одна из цен ис­
пользования мини-пакетной тренировки. У нас больше нет красивых, гладких кри­
вых потерь, которые мы видели в предыдущих разделах.
Резюме
В этой главе мы познакомили вас с полносвязными глубокими сетями. Мы углуби­
лись в математическую теорию этих сетей и исследовали понятие ’’универсальной
аппроксимации”, которое частично объясняет мощную способность полносвязных
сетей к самообучению. Мы закончили главу практическим примером, где вы натре­
нировали глубокую полносвязную архитектуру набора данных Тох21.
В этой главе мы еще не показали, как настраивать полносвязную сеть для достиже­
ния хорошей предсказательной результативности. В главе 5 мы обсудим ’’гиперпа­
раметрическую оптимизацию”, процесс настройки параметров сети, и вы выполни­
те настройку параметров сети Тох21, представленной в этой главе.
116
|
Глава 4
ГЛАВА 5
Гиперпараметрическая
оптимизация
Тренировка глубокой модели и тренировка хорошей глубокой модели — это две
разные вещи. Достаточно легко скопировать и вставить программный код
TensorFlow из Интернета и запустить первый прототип, но гораздо сложнее преоб­
разовать этот прототип в высококачественную модель. Процесс превращения про­
тотипа в высококачественную модель связан со множеством шагов. Мы рассмот­
рим один из таких шагов — гиперпараметрическую оптимизацию.
В первом приближении гиперпараметрическая оптимизация представляет собой
процесс настройки всех тех параметров модели, которые градиентный спуск не за­
учивает. Эти величины называются гиперпараметрами. Рассмотрим полносвязные
сети из предыдущей главы. В то время как весы полносвязных сетей могут быть
заучены из данных, к другим настройкам сети это не относится. Такие гиперпара­
метры включают в свой состав количество скрытых слоев, количество нейронов
в расчете на скрытый слой, скорость заучивания и многое другое. Каким образом
можно систематически находить хорошие значения для этих величин? Ответ на
этот вопрос обеспечивают методы оптимизации гиперпараметров.
Ранее мы уже упоминали, что результативность модели отслеживается на отложен­
ном "контрольном" наборе. Методы гиперпараметрической оптимизации система­
тически пробуют несколько вариантов параметров на контрольном наборе. Наибо­
лее результативный набор значений гиперпараметров затем оценивается на втором
отложенном "тестовом" наборе, чтобы точно измерить истинную результативность
модели. Различные методы гиперпараметрической оптимизации отличаются алго­
ритмом, который они используют, чтобы предложить новые настройки гиперпара­
метров. Эти алгоритмы варьируются от очевидных до довольно сложных. В после­
дующих главах мы рассмотрим некоторые более простые методы, поскольку более
сложные методы гиперпараметрической оптимизации, как правило, требуют очень
больших вычислительных мощностей.
В качестве практического примера мы настроим введенную в главе 4 полносвязную
сеть токсичности Тох21 с целью достичь хорошей результативности. Мы настоя­
тельно рекомендуем вам (как всегда) выполнить методы гиперпараметрической
оптимизации самостоятельно, используя программный код в хранилище GitHub
(https://github.com/matroid/dlwithtf), связанном с этой книгой.
117
Гиперпараметрическая оптимизация
не только для глубоких сетей!
Стоит подчеркнуть, что гиперпараметрическая оптимизация предназначе­
на не только для глубоких сетей. Большинство форм машиннообучающихся алгоритмов имеют параметры, которые не могут быть зауче­
ны с помощью принятых по умолчанию методов самообучения. Эти пара­
метры также называются гиперпараметрами. Далее в этой главе вы увиди­
те некоторые примеры гиперпараметров для случайных лесов (другого
распространенного метода машинного самообучения).
Однако стоит отметить, что глубокие сети, как правило, более чувстви­
тельны к выбору гиперпараметра, чем другие алгоритмы. В то время как
случайный лес при наличии вариантов гиперпараметров, взятых по умол­
чанию, снижает свою результативность лишь слегка, глубокие сети могут
не обучиться и вовсе. По этой причине для будущего специалиста по глу­
бокому самообучению чрезвычайно важно надлежащим образом освоить
методы оптимизации гиперпараметров.
Оценивание модели
и гиперпараметрическая оптимизация
В предыдущих главах мы лишь кратко затронули вопрос о том, как определять,
является ли машинно-обучающаяся модель хорошей или нет. Любой анализ
результативности модели должен включать точное измерение способности модели
к обобщению. То есть, может ли модель выполнять предсказания на точках данных,
которые она никогда не встречала раньше? Лучшим тестом результативности
модели является создание модели, а затем перспективное оценивание данных,
которые становятся доступными после построения модели. Однако этот вид тести­
рования громоздок и поэтому не пригоден для выполнения на регулярной основе.
Во время фазы проектирования практикующий аналитик данных может принять
решение выполнить оценивание многих других типов моделей или обучающихся
алгоритмов, чтобы выяснить, который из них лучший.
Решение этой дилеммы состоит в том, чтобы "отложить" часть имеющегося набора
данных в качестве контрольного набора. Этот контрольный набор будет использо­
ваться для измерения результативности различных моделей (с разными вариантами
гиперпараметров). Также рекомендуется иметь второй отложенный набор (тесто­
вый набор) для точного замера результативности конечной модели, выбранной
методами отбора гиперпараметров.
Допустим, что у вас есть 100 точек данных. Простая процедура заключается в ис­
пользовании 80 этих точек данных для тренировки перспективных моделей и
20 отложенных точек данных для контрольной проверки выбранной модели. Каче­
ство предложенной модели может отслеживаться по ее "оценке" на отложенных
20 точках данных. Модели могут итеративно улучшаться благодаря предложению
новых проектов моделей и принятию только тех, которые улучшают результатив­
ность на отложенном наборе.
118
|
Глава 5
Однако на практике эта процедура приводит к переподгонке. Практикующие спе­
циалисты быстро начинают разбираться в характерных особенностях структуры
отложенного набора и донастраивают модель так, что она начинает давать искусст­
венно завышенные оценки на отложенном наборе. Для того чтобы бороться с этим,
практикующие специалисты обычно разбивают отложенный набор на две части:
одну часть для контрольной проверки гиперпараметров, а другую — для оконча­
тельной контрольной проверки модели. В этом случае, допустим, вы резервируете
10 точек данных для контрольной проверки и 10 для окончательного тестирования.
Это называется расщеплением данных в соотношении 80:10:10.
г—А-i
*
Зачем нужен тестовый набор?
Важный момент, который стоит отметить, — методы гиперпараметрической оптимизации как таковые сами являются формой обучающегося алгоритма. В частности, они представляют собой обучающийся алгоритм для
настройки недифференцируемых величин, которые нелегко поддаются ис­
следованию на основе математического анализа. ’’Тренировочный набор”
для алгоритма заучивания гиперпараметров — это отложенный контроль­
ный набор.
В общем случае не имеет особого смысла производить точный замер ре­
зультативности модели на ее тренировочных наборах. Как всегда, заучен­
ные величины должны уметь обобщать, и поэтому необходимо протести­
ровать результативность на другом наборе. Поскольку тренировочный
набор используется для заучивания весов на основе градиента, а контроль­
ный набор применяется для заучивания гиперпараметров, тестовый набор
необходим, чтобы точно измерить, насколько хорошо заученные гиперпа­
раметры обобщаются на новые данные.
Обучающиеся алгоритмы по принципу черного ящика
Обучающиеся алгоритмы, работающие по принципу черного ящика, не
исходят из какой-то структурной информации о системах, которые они
пытаются оптимизировать. Большинство гиперпараметрических методов
представляют собой черный ящик, т. е. закрытое устройство; они работают
для любого типа глубоко обучающегося и машинно-обучающегося алго­
ритмов.
Методы по принципу черного ящика в целом не масштабируются так же
хорошо, как и методы по принципу белого ящика (такие как градиентный
спуск), поскольку они, как правило, теряются в многомерных пространст­
вах. Из-за отсутствия направленной информации от градиента методы по
принципу черного ящика могут заблудиться даже в 50-размерных про­
странствах (оптимизация 50 гиперпараметров на практике представляет
довольно большую сложность).
Для того чтобы понять, почему, давайте предположим, что существует
50 гиперпараметров, каждый с тремя потенциальными значениями. Тогда
алгоритм по принципу черного ящика должен слепо выполнить поиск
в пространстве размером 3 . Это выполнимо, но поиск потребует большой
вычислительной мощности в целом.
Гиперпараметрическая оптимизация
|
119
Метрики, метрики, метрики
При выборе гиперпараметров следует выбирать те, которые делают проектируемые
модели точнее. В машинном самообучении метрический показатель, или метри­
ка, — это функция, которая измеряет точность предсказаний натренированной мо­
дели. Гиперпараметрическая оптимизация выполняется для оптимизации тех ги­
перпараметров, которые максимизируют (или минимизируют) этот метрический
показатель в контрольном наборе. Хотя на первый взгляд это кажется простым, по­
нятие точности на самом деле может быть довольно тонким. Предположим, у вас
есть бинарный классификатор. Будет ли более важным никогда не помечать непра­
вильно ложные образцы как истинные или же истинные образцы как ложные? Как
выбрать модельные гиперпараметры, удовлетворяющие потребностям ваших при­
ложений?
Ответом окажется подбор правильного метрического показателя. В этом разделе
мы обсудим ряд различных метрических показателей для классификационных и
регрессионных задач. Мы дадим рекомендации по качественным характеристикам,
которые подчеркивают каждый метрический показатель. Лучший показатель не
существует, но для разных приложений есть более подходящие и менее подходя­
щие показатели.
Метрические показатели — это не замена здравому смыслу!
Метрические показатели ужасно слепы. Они оптимизируют всего однуединственную величину. Вследствие этого слепая оптимизация метриче­
ских показателей может привести к совершенно неподходящим результа­
там. Медийные сайты в веб часто принимают решение оптимизировать
показатель кликабельности, т. е. число нажатий пользователями на рекла­
ме и других элементах содержимого веб-страниц. Какой-то предприимчи­
вый молодой журналист или рекламодатель затем понял, что такие назва­
ния, как "Вы никогда не поверите, что произошло, когда X" побуждают
пользователей нажимать с большей частотой. Как и следовало ожидать,
родились клик-наживки — кликбейты. В то время как "кликбейтовые" за­
головки действительно побуждают читателей нажимать, они также оттал­
кивают пользователей и приводят к тому, что те избегают тратить время на
сайты, заполненные кликбейтами. Оптимизация кликабельности привела
к снижению вовлеченности и доверия пользователей.
Урок здесь общий: оптимизация одного метрического показателя часто
происходит за счет отдельной величины. Убедитесь, что величина, кото­
рую вы хотите оптимизировать, действительно является "правильной"
величиной. Не правда ли, интересно, как машинное самообучение попрежнему требует человеческого суждения?
Бинарно-классификационные показатели
Прежде чем познакомить вас с метрическими показателями для бинарных класси­
фикационных моделей, считаем, что вам будет полезно узнать о некоторых вспомо­
гательных величинах. Когда бинарный классификатор строит предсказания на
120
|
Глава 5
наборе точек данных, вы можете разделить все эти предсказания на четыре катего­
рии (табл. 5.1).
Таблица 5.1. Категории предсказаний
Категория
Значение
Истинноположительный исход (true positive, TP)
Предсказание истинно, метка истинная
Ложноположительный исход (false positive, FP)
Предсказание истинно, метка ложная
Истинноотрицательный исход (true negative, TN)
Предсказание ложное, метка ложная
Ложноотрицательный исход (false negative, FN)
Предсказание ложное, метка истинная
Мы также считаем полезным ввести обозначения, приведенные в табл. 5.2.
Таблица 5.2. Положительные и отрицательные исходы
Категория
Значение
Р
Количество положительных меток
N
Количество отрицательных меток
Крайне желательно минимизировать количество ложноположительных и ложноот­
рицательных исходов. Вместе с тем, часто невозможно минимизировать как лож­
ноположительные, так и ложноотрицательные исходы любого конкретного набора
данных из-за ограничений в присутствующем сигнале. Вследствие этого существу­
ет целый ряд метрических показателей, которые обеспечивают различные компро­
миссные соотношения между ложноположительными и ложноотрицательными
исходами. Эти компромиссные соотношения могут быть очень важны для прило­
жений. Предположим, вы проектируете медицинскую диагностику рака молочной
железы. Тогда ложноположительный исход будет означать, что здоровой пациентке
будет диагностирован рак молочной железы. Ложноотрицательный исход отметит
пациентку, страдающую от рака груди, как не имеющую этого заболевания. Ни
один из этих результатов не является желательным, и проектирование правильного
баланса является сложным вопросом биоэтики.
Теперь мы покажем вам ряд различных метрических показателей, которые уравно­
вешивают ложноположительные и ложноотрицательные исходы в разных соот­
ношениях (табл. 5.3). Каждое из этих соотношений оптимизируется для разного
баланса, и мы рассмотрим некоторые из них более подробно.
Точность — самый простой показатель. Он просто подсчитывает долю предсказа­
ний, которые были сделаны классификатором правильно. В простых приложениях
точность должна быть наипервейшим, дежурным метрическим показателем для
практикующего специалиста. Прецизионность и полнота являются наиболее часто
измеряемыми метрическими показателями после точности. Прецизионность — это
мера того, сколько точек данных в долевом выражении, предсказанных положиГиперпараметрическая оптимизация
|
121
Таблица 5.3. Таблица бинарных метрических показателей
Метрический показатель
Формула расчета
Точность
(TP + TN)/(P + N)
Прецизионность
TP/(TP + FP)
Полнота
TP/(TP + FN) = TP/P
Специфичность
TN/(FP + TN) = TN/N
Доля ложноположительных исходов (false positive rate, FPR)
FP/(FP + TN) = FP/N
Доля ложноотрицательных исходов (false negative rate, FNR)
FN/(TP + FN) = FN/P
тельно, оказались положительными на самом деле. Полнота, в свою очередь, изме­
ряет долю положительно помеченных точек данных, которые классификатор поме­
тил положительно. Специфичность измеряет долю точек данных, помеченных как
отрицательные, которые были классифицированы правильно. Доля ложноположи­
тельных исходов измеряет долю точек данных, помеченных как отрицательные, но
которые были классифицированы неправильно как положительные. Доля ложноот­
рицательных исходов — это доля точек данных, помеченных как положительные,
но которые были ошибочно помечены как отрицательные.
Все эти показатели подчеркивают различные аспекты работы классификатора. Они
также могут быть полезны при построении более сложного анализа результативно­
сти бинарного классификатора. Например, предположим, что бинарный классифи­
катор выводит вероятности классов, а не только сырые предсказания. Тогда встает
вопрос о выборе порога отсечения. То есть, при какой вероятности положительно­
го исхода вы помечаете результат, как фактически положительный? Наиболее рас­
пространенный ответ— 0,5. Однако, выбирая более высокие или более низкие по­
роги, часто можно вручную варьировать баланс между прецизионностью, полно­
той, долей ложноположительных исходов (false positive rate, FPR) и долей
истинноположительных исходов (true positive rate, TPR). Эти компромиссные соот­
ношения часто представляются графически.
Кривая рабочей характеристики приемника (receiver operating characteristic, ROC)
показывает компромиссное соотношение между .уолей истинноположительных
исходов и долей ложноположительных исходов п& мере изменения пороговой
вероятности (рис. 5.1).
Площадь под кривой (area under curve, AUC) рабочей характеристики приемника
(ROC-AUC) является общепринятым измеряемым метрическим показателем. Мет­
рический показатель ROC-AUC полезен тем, что он обеспечивает глобальную кар­
тину бинарного классификатора для всех вариантов порога отсечения. Идеальный
метрический показатель будет иметь ROC-AUC 1.0, поскольку доля TRP всегда
будет максимизированной. Для сравнения, случайный классификатор будет иметь
ROC-AUC 0.5. Показатель ROC-AUC часто полезен для несбалансированных набо­
ров данных, поскольку глобальное представление частично учитывает дисбаланс
в наборе данных.
122
|
Глава 5
Доля ложно положительных исходов
(специфичность)
Рис. 5.1. Кривая рабочей характеристики приемника (ROC-кривая)
Метрические показатели
мультиклассовой классификации
Многие распространенные задачи машинного самообучения требуют, чтобы выво­
димые моделями классификационные метки были не только бинарными. К приме­
ру, задача конкурса ImageNet (ILSVRC) требовала от участников строить модели,
которые должны распознавать, какой из тысячи потенциальных классов объектов
имелся в предоставленных изображениях. Или в более простом примере, вполне
возможно, что вы захотите предсказать завтрашнюю погоду, где предусмотрены
классы ’’солнечно”, ’’дождливо” и ’’облачно”. Каким образом измеряется результа­
тивность такой модели?
Самый простой метод — использовать прямое обобщение точности, которое изме­
ряет долю точек данных, помеченных правильно (табл. 5.4).
Таблица 5.4. Метрические показатели мультиклассовой классификации
Метрический показатель
Определение
Точность
Количество правильно помеченных/количество точек данных
Стоит отметить, что существуют мультиклассовые обобщения величин, в частно­
сти точности, полноты и ROC-AUC, и, если это интересно, мы рекомендуем вам
изучить эти определения. На практике существует более простая визуализация —
матрица ошибок (или матрица несоответствий), которая хорошо работает. Для
мультиклассовой задачи с к классами матрица ошибок является матрицей ккк .
ячейка представляет количество точек данных, помеченных как класс i с ис­
тинным классом меток j. На рис. 5.2 показана матрица ошибок.
Не стоит недооценивать мощь человеческого глаза в отлавливании шаблонов сис­
тематических неудач на простых визуализациях! Взгляд на матрицу ошибок может
дать быстрое понимание того, что могут пропустить десятки более сложных мультиклассовых метрических показателей.
Гиперпараметрическая оптимизация
|
123
540
480
420
360
300
240
180
120
60
0
Предсказанная метка
Рис. 5.2. Матрица ошибок для однополосного классификатора
Регрессионные метрические показатели
Вы познакомились с метрическими регрессионными показателями несколько глав
назад. Заметим, что R2 Пирсона и RMSE (среднеквадратическая ошибка) являются
хорошими вариантами по умолчанию.
Ранее мы лишь вкратце рассмотрели математическое определение показателя R2,
но сейчас мы рассмотрим его подробнее. Пусть х представляет предсказания, а
у, — метки. Пусть х и у представляют среднее значение соответственно предска­
занных значений и меток. Тогда R Пирсона (обратите внимание на отсутствие
квадрата) имеет следующий вид:
Это уравнение может быть переписано как
cov(x, у)
-о(х)о(у)’
124
|
Глава 5
где cov — ковариация; и — стандартное отклонение. В интуитивном плане R2
Пирсона измеряет совместные колебания предсказаний и меток от их средних зна­
чений, нормализованных своими соответствующими диапазонами колебаний. Если
предсказания и метки различаются, то эти колебания будут происходить в разных
точках и иметь тенденцию к взаимному погашению, делая R2 меньше. Если пред­
сказания и метки согласуются (как правило, так и есть), то колебания будут проис­
ходить вместе и делать R2 больше. Отметим, что показатель R2 ограничен диапа­
зоном от 0 до z.
RMSE измеряет абсолютную величину ошибки (погрешности) между предсказа­
ниями и истинными величинами. Эта аббревиатура означает квадратный корень из
среднеквадратической ошибки, что примерно аналогично абсолютному значению
ошибки между истинной величиной и предсказанной величиной. Математически
RMSE определяется следующим образом (с использованием того же обозначения,
что и раньше):
RMSE =
|у\(х,-к)2
7 .
V
N
Алгоритмы оптимизации гиперпараметров
Как мы уже упоминали ранее в этой главе, методы гиперпараметрической оптими­
зации — это обучающиеся алгоритмы поиска значений гиперпараметров, оптими­
зирующих выбранный метрический показатель в контрольном наборе. В общем
случае эта целевая функция не может быть дифференцирована, поэтому любой оп­
тимизационный метод обязательно должен быть черным ящиком. В этом разделе
мы покажем вам несколько простых обучающихся алгоритмов по принципу черно­
го ящика для выбора значений гиперпараметров. В качестве примера мы будем ис­
пользовать набор данных Тох21 из главы 4 для демонстрации этих оптимизацион­
ных методов по принципу черного ящика. Набор данных Тох21 достаточно мал,
чтобы сделать эксперимент легким, и вместе с тем достаточно сложен, чтобы про­
цесс гиперпараметрической оптимизации был нетривиальным.
Прежде чем начать, отметим, что ни один из этих алгоритмов по принципу черного
ящика не работает идеально. Как вы вскоре увидите, гиперпараметрическая опти­
мизация на практике требует большого вмешательства человека.
Можно ли автоматизировать оптимизацию гиперпараметров?
Давней мечтой машинного самообучения является автоматизация процесса
отбора модельных гиперпараметров. Авторы таких проектов, как "автома­
тизированное рабочее место статистика" и другие, стремились удалить из
процесса отбора гиперпараметров часть тяжелой работы и сделать
построение модели более доступным для неэкспертов. Однако на практике
за добавленное удобство, как правило, приходилось платить крутую цену
в результативности.
Гиперпараметрическая оптимизация
|
125
В последние годы наблюдается всплеск работы, направленной на совер­
шенствование алгоритмических основ настройки моделей. Гауссовы про­
цессы, эволюционные алгоритмы и самообучение с максимизацией под­
крепления были использованы для заучивания модельных гиперпарамет­
ров и архитектур с очень ограниченным участием человека. Недавняя
работа показала, что при больших объемах вычислительной мощности эти
алгоритмы могут превышать результативность эксперта при настройке мо­
дели! Но накладные расходы серьезны, и требуется в десятки и сотни раз
больше вычислительной мощности.
На данный момент автоматическая настройка модели по-прежнему остает­
ся непрактичной. Все алгоритмы, которые мы рассмотрим в этом разделе,
требуют значительной ручной настройки. Вместе с тем, поскольку качест­
во оборудования совершенствуется, мы ожидаем, что заучивание гиперпа­
раметров станет все более автоматизированным. Мы настоятельно реко­
мендуем всем практикующим специалистам в ближайшем будущем обяза­
тельно освоить тонкости настройки гиперпараметров. Прочное умение
выполнять гиперпараметрическую настройку является тем самым умени­
ем, которое отличает эксперта от новичка.
Установление ориентира
Первый шаг в гиперпараметрической настройке— найти ориентир. Ориентир —
это результативность, достижимая надежным (обычно неглубоко обучающимся)
алгоритмом. Вообще, случайные леса являются превосходным вариантом для уста­
новления ориентиров. Как показано на рис. 5.3, случайные леса представляют со­
бой ансамблевый метод, который тренирует большое количество моделей дерева
принятия решений на подмножествах входных данных и входных признаков. Затем
эти отдельные деревья выбирают исход на основе голосования.
Рис. 5.3. Иллюстрация случайного леса. Здесь v — это входной признаковый вектор
126
|
Глава 5
Случайные леса, как правило, являются достаточно надежными моделями. Они
устойчивы к шуму и не беспокоятся о масштабе входных признаков. (Несмотря на
то что нам не нужно беспокоиться об этом с наборами данных Тох21, поскольку
все наши признаки — бинарные, в целом глубокие сети довольно чувствительны
к своему входному диапазону. Для хорошей результативности полезно выполнять
нормализацию или иным образом шкалировать диапазон входных данных. Мы вер­
немся к этому вопросу в последующих главах.) Они также, как правило, имеют
устойчивую способность к обобщению и для запуска не требуют большой гиперпа­
раметрической настройки. Для некоторых наборов данных преодоление глубокой
сетью результативности случайного леса может потребовать значительной изо­
щренности.
Как создать и натренировать случайный лес? К счастью для нас, библиотека Python
scikit-iearn обеспечивает высококачественную реализацию случайного леса. Биб­
лиотеке scikit-iearn посвящен целый ряд учебных пособий и ознакомительных ма­
териалов, поэтому мы просто покажем программный код тренировки и предсказа­
ния, необходимый здесь для создания модели случайного леса на основе наборов
данных Тох21 (пример 5.1).
; Пример 5.1. Определение и тренировка случайного леса на наборе данных Тох21
from sklearn.ensemble import RandomForestClassifier
# Сгенерировать граф tensorflow
sklearn_model = RandomForestClassifier(
class_weight="balanced", n_estimators=50)
print("Все готово для подгонки модели на тренировочном наборе.")
sklearn_model .fit(train_X, train_y)
train_y_pred = sklearn_model.predict(train_X)
valid_y_pred = sklearn_model.predict(valid_X)
test_y_pred = sklearn_model.predict(test_X)
weighted_score = accuracy_score(train_y, train_y_pred,
sample_weight=train_w)
print("Тренировочная взвешенная точность классификации: %f" %
weighted_score)
weighted_score = accuracy_score(valid_y, valid_y_pred, sample_weight=valid_w)
print("Контрольная взвешенная точность классификации: %f" % weighted_score)
weighted_score = accuracy_score(test_y, test_y_pred, sample_weight=test_w)
print("Тестовая взвешенная точность классификации: %f" % weighted_score)
print("Тренировочная взвешенная точность классификации: %f" %
train_weighted_score)
valid_weighted_score = accuracy_score(valid_y, valid_y_pred,
sample_weight=valid_w)
print("Контрольная взвешенная точность классификации: %f" %
valid_weighted_score )
Гиперпараметрическая оптимизация
|
127
Здесь train x, train y и другие являются сегментами набора данных Тох21, опреде­
ленного в предыдущей главе. Напомним, что все эти величины являются массива­
ми NumPy. Именованный аргумент n estimators обозначает число деревьев приня­
тия решений в нашем лесу. Задание 50 или 100 деревьев часто обеспечивает дос­
тойную результативность. Библиотека scikit-iearn предлагает простой объектноориентированный API с методами fit (х, у) и predict (X). Эта модель достигает сле­
дующей точности по отношению к нашему взвешенному метрическому показателю
точности:
Тренировочная взвешенная точность классификации: 0.989845
Контрольная взвешенная точность классификации: 0.681413
Напомним, что полносвязная сеть из главы 4 достигла результативности:
Тренировочная взвешенная точность классификации: 0.742045
Контрольная взвешенная точность классификации: 0.64882
Похоже, что данный ориентир показывает более высокую точность, чем наша глу­
боко обучающаяся модель! Самое время засучить рукава и приступить к работе.
Спуск студента магистратуры
Самый простой способ испытать хорошие гиперпараметры — просто попробовать
вручную несколько разных вариантов гиперпараметров, чтобы увидеть, какой из
них работает. Эта стратегия может быть удивительно эффективной и поучительной.
Практикующий специалист по глубокому самообучению обязан развить интуитив­
ное понимание структуры глубоких сетей. Учитывая очень слабое состояние тео­
рии, эмпирическая работа— лучший способ научиться строить глубоко обучаю­
щиеся модели. Мы настоятельно рекомендуем самостоятельно попробовать много­
численные варианты полносвязной модели. Будьте последовательны и педантичны;
записывайте свои варианты и результаты в электронную таблицу и систематически
исследуйте гиперпараметрическое пространство. Попробуйте разобраться в эффек­
тах различных гиперпараметров. Что делает тренировку сети быстрее, а что мед­
леннее? Какие диапазоны настроек совершенно нарушают процесс самообучения?
(К сожалению, их довольно легко найти.)
Есть несколько остроумных приемов программной инженерии, которые могут уп­
ростить этот поиск. Создайте функцию, аргументом которой является подлежащий
исследованию гиперпараметр, и пусть она выводит точность на печать. Тогда ис­
пытание новых гиперпараметрических комбинаций потребует только одного вызо­
ва функции. Пример 5.2 показывает, как будет выглядеть сигнатура такой функции
для нашей полносвязной сети из примера Тох21.
I Пример 5.2. Функция, выводящая на экран гиперпараметры
I разных полносвязных сетей для данных Тох21
def eval_tox21_hyperparams(n_hidden=50, n_layers=l, learning_rate=.001,
dropout_prob=0.5, n_epochs=45, batch_size=100,
weight_positives=True) :
128
|
Глава 5
Давайте пройдемся по каждому из этих гиперпараметров. Итак, n_hidden управляет
количеством нейронов в каждом скрытом слое сети, a n iayers— количеством
скрытых слоев. Гиперпараметр learning rate контролирует скорость заучивания,
используемую в градиентном спуске, dropout prob является вероятностью, что ней­
роны не сброшены во время шагов тренировки, n epochs управляет количеством
проходов по всем данным, a batch size является количеством точек данных в каж­
дом пакете.
Единственный новый гиперпараметр здесь— это weight positives. Для несбалан­
сированных наборов данных часто бывает полезно взвешивать прецеденты обоих
классов, чтобы иметь одинаковый вес. Для набора данных Тох21 проект DeepChem
предоставляет для использования веса. Для того чтобы выполнить взвешивание, мы
просто умножаем перекрестно-энтропийные члены прецедента на эти веса (при­
мер 5.3).
; Пример 5.3. Взвешивание положительных образцов для Тох21
entropy = tf.nn.sigmoid_cross_entropy_with_logits(logits=y_logit,
1abe1s=y_expand)
# Умножить на веса
if weight_positives:
w_expand = tf.expand_dims(w, 1)
entropy = w_expand * entropy
Почему метод подбора значений гиперпараметров называется спуском студента
магистратуры? Машинное самообучение до недавнего времени было преимущест­
венно академической областью. Проверенный на практике метод проектирования
нового машинно-обучающегося алгоритма состоит в том, чтобы описать метод,
который желают получить от нового студента магистратуры и поручить ему про­
работать детали. Этот процесс слегка смахивает на обряд посвящения и часто тре­
бует от студента усердно перепробовать множество проектных вариантов. В целом
это очень поучительный опыт, т. к. единственный способ приобрести эстетику
проектирования — это укрепить память о настройках, которые работают и не рабо­
тают.
Решеточный поиск гиперпараметров
После опробования несколько ручных гиперпараметрических настроек, процесс
кажется очень утомительным. Опытные программисты захотят просто написать
цикл for, который будет выполнять итеративный обход вариантов желаемых ги­
перпараметров. Этот процесс до известной степени является методом поиска в ре­
шетке гиперпараметров. Суть метода — для каждого гиперпараметра выбрать спи­
сок значений, которые могут оказаться хорошими для гиперпараметра, написать
вложенный цикл for, который пробует все комбинации этих значений, чтобы найти
их контрольные точности, и отследить наилучших исполнителей.
Гиперпараметрическая оптимизация
|
129
Однако в этом процессе есть одна тонкость. Глубокие сети могут быть довольно
чувствительны к выбору начального случайного значения, используемого для ини­
циализации сети. По этой причине стоит повторить каждый вариант гиперпарамет­
рических настроек несколько раз и усреднить результаты, чтобы заглушить дис­
персию.
Программный код для этой процедуры прост, как показано в примере 5.4.
Пример 5.4. Выполнение поиска в решетке гиперпараметров
на лолносвязной сети Тох21
scores = {}
n_reps = 3
hidden_sizes = [50]
epochs = [10]
dropouts = [.5, 1.0]
num_layers = [1, 2]
for rep in range(n_reps):
for n_epochs in epochs:
for hidden_size in hidden_sizes:
for dropout in dropouts:
for n_layers in num_layers:
score = eval_tox21_hyperparams(n_hidden=hidden_size,
n_epochs=n_epochs,
dropout_prob=dropout,
n_layers=n_layers)
if (hidden_size, n_epochs, dropout, n_layers) not in scores:
scores[(hidden_size, n_epochs, dropout, n_layers)] = []
scores[(hidden_size, n_epochs, dropout,
n_layers)].append(score)
print("Все оценки")
print(scores)
avg_scores = {}
for params, param_scores in scores.iteritems():
avg_scores[params] = np.mean(np.array(param_scores) )
print("Оценки, усредненные на %d повторах" % n_reps)
Случайный поиск гиперпараметров
Для опытных практикующих специалистов нередко будет очень заманчивым по­
вторно использовать магические гиперпараметрические настройки или поисковые
решетки, которые работали в предыдущих приложениях. Эти настройки могут
иметь свою ценность, но они также могут привести нас в заблуждение. Каждая
задача машинного самообучения немного отличается, и оптимальные настройки
130
|
Глава 5
могут лежать в области параметрического пространства, которое мы ранее не рас­
сматривали. По этой причине зачастую стоит попробовать случайные гиперпара­
метрические настройки (где случайные значения выбираются из разумного диапа­
зона).
Существует также более веская причина, почему следует попробовать случайные
поиски. В многомерных пространствах регулярные решетки могут пропускать мно­
го информации, в особенности если интервал между точками сетки не велик.
Выбор случайных вариантов для точек решетки может помочь нам не попасть
в ловушку неконтролируемых решеток. На рис. 5.4 проиллюстрирован этот факт.
Решеточная
схема размещения
Случайная
схема размещения
(ООО
1 о о о
I о о о
Важный параметр
Важный параметр
Рис. 5.4. Иллюстрация того, почему случайный гиперпараметрический поиск
может превосходить поиск в решетке
Как реализовать случайный гиперпараметрический поиск в программе? Остроум­
ный программный прием — отобрать случайные значения, желаемые с самого на­
чала, и сохранить их в списке. Затем случайный гиперпараметрический поиск про­
сто превращается в поиск в решетке этих случайно отобранных списков. Приведем
пример. В случае скоростей заучивания часто полезно испытать широкий диапазон
от 0,1 до 0,000001 или около того. В примере 5.5 для отбора нескольких случайных
скоростей заучивания используется библиотека NumPy.
^Пример 5.5. Отбор случайных образцов скоростей заучивания
n_rates = 5
learning_rates = 10**(-пр.random.uniform(low=l, high=6, size=n_rates))
Здесь мы используем остроумный математический прием. Обратите внимание, что
0,1 = 10-1 и 0,000001 = 10"6 . Отбор вещественных чисел между такими диапазона­
ми, как 1 И 6, выполняется просто С ПОМОЩЬЮ функции пр. random.uniform. Мы
можем возвести эти отобранные значения в степень, чтобы восстановить наши ско­
рости заучивания. Тогда learning rates будет содержать список значений, которые
мы можем использовать в нашем программном коде решеточного гиперпараметри­
ческого поиска из предыдущего раздела.
Гиперпараметрическая оптимизация
|
131
Задание для читателя
В этой главе вы познакомились с основами настройки гиперпараметров, но эти рас­
смотренные инструменты достаточно мощные. В качестве задания попробуйте
настроить полносвязную глубокую сеть, чтобы достигнуть контрольной резуль­
тативности, которая будет выше, чем у случайного леса. Для этого потребуется
немного потрудиться, но ради получения опыта работа стоит того.
Резюме
В этой главе мы рассмотрели основы гиперпараметрической оптимизации, процесс
отбора значений модельных параметров, которые не могут быть заучены автомати­
чески на тренировочных данных. В частности, мы показали вам случайный и реше­
точный гиперпараметрический поиск и продемонстрировали использование такого
программного кода для оптимизации моделей на наборе данных Тох21, представ­
ленном в предыдущей главе.
В главе 6 мы вернемся к нашему обзору глубоких архитектур и познакомим вас
со сверточными нейронными сетями, одним из фундаментальных строительных
блоков современных глубоких архитектур.
132
|
Глава 5
ГЛАВА 6
Сверточные
нейронные сети
Сверточные нейронные сети позволяют глубоким сетям обучаться функциям на
структурированных пространственных данных, таких как изображения, видео и
текст. В математическом смысле сверточные сети обеспечивают инструменты для
эффективного использования локальной структуры данных. Изображения удовле­
творяют определенным естественным статистическим свойствам. Предположим,
мы представляем изображение в виде двумерной пиксельной решетки. Части изо­
бражения, близкие друг к другу в пиксельной решетке, могут изменяться вместе
(например, все пикселы, соответствующие таблице в изображении, вероятно, явля­
ются коричневыми). Сверточные сети учатся использовать эту естественную кова­
риационную структуру с целью эффективного самообучения.
Сверточные сети — старое изобретение. Первые версии сверточных сетей предла­
гались в публикациях, начиная с 1980-х годов. Хотя проекты этих старых сверточ­
ных сетей часто были довольно надежными, они требовали ресурсов, которые
превышали возможности имевшегося в то время аппаратного обеспечения. Как
результат, сверточные сети томились в относительной безвестности, прозябая на
страницах научной литературы.
Эта тенденция резко изменилась после 2012 г., когда стал проводиться конкурс
ILSVRC на лучшие проекты по обнаружению объектов в изображениях, где свер­
точная AlexNet-сеть достигла частот ошибок вдвое меньше, чем у ее ближайших
конкурентов. Сеть AlexNet смогла использовать графические процессоры (graphics
processing unit, GPU) для тренировки старых сверточных архитектур с помощью
значительно более крупных наборов данных. Такое сочетание старых архитектур
с новым оборудованием позволило AlexNet значительно превзойти уровень техно­
логий в области обнаружения объектов в изображениях. Эта тенденция продолжи­
лась, и развитие сверточных нейронных сетей приобрело огромное ускорение по
сравнению с другими технологиями обработки изображений. Без преувеличений,
почти все современные конвейеры обработки изображений теперь приводятся
в движение сверточными нейронными сетями.
Кроме того, произошел ренессанс в проектировании сверточных сетей, который
продвинул сверточные сети далеко за пределы элементарных моделей из 1980-х го­
дов. В первую очередь сети становятся гораздо глубже, при этом мощные совре­
менные сети достигают сотен слоев. Еще одной популярной тенденцией стало
обобщение сверточных архитектур для работы на новых типах данных. Например,
графовые архитектуры позволяют применять сверточные сети к молекулярным
данным, таким как набор данных Тох21, с которым мы столкнулись несколько глав
133
назад! Сверточные архитектуры также оставляют свой след в геномике и в обра­
ботке текста и даже в переводе с одного языка на другой.
В этой главе мы представим основные понятия сверточных сетей. Они будут вклю­
чать основные сетевые компоненты, которые составляют сверточные архитектуры,
и введение в принципы проектирования, определяющие, каким образом эти части
соединяются вместе. Мы также приведем подробный пример, который продемон­
стрирует, как использовать TensorFlow для тренировки сверточной сети. Пример
программного кода для этой главы является адаптированной версией учебного
руководства по сверточным нейронным сетям в документации по TensorFlow. Если
вам интересно узнать об изменениях, которые мы внесли, рекомендуем вам обра­
титься к оригинальному учебному руководству (https://www.tensorflow.org/
tutorials/deep_cnn) на сайте TensorFlow. Как всегда, рекомендуем вам проработать
сценарии для данной главы, размещенные в хранилище GitHub (https://github.com/
matroid/dlwithtf), связанном с этой книгой.
Введение в сверточные архитектуры
Большинство сверточных архитектур состоят из ряда базовых примитивов. Эти
примитивы включают такие слои, как сверточные и редуцирующие. Существует
также набор связанных с ними терминов, в том числе размер локального рецептив­
ного поля, размер шага и количество фильтров. В этом разделе мы кратко рассмот­
рим базовые термины и концепции, положенные в основу сверточных сетей.
Локальные рецептивные поля
Концепция локального рецептивного поля берет свое начало в нейробиологии, где
рецептивное поле нейрона является частью чувственного восприятия тела, которое
влияет на возбуждение нейрона. Когда нейроны обрабатывают чувственный сти­
мул, они имеют определенное поле "обзора", видимое мозгу. Такое поле обзора
традиционно называется локальным рецептивным полем. Этот "обзор" может соот­
ветствовать участку кожи человека или сегменту его поля зрения. На рис. 6.1 про­
иллюстрировано локальное рецептивное поле нейрона.
Сверточные архитектуры заимствуют последнее понятие с вычислительным пере­
осмыслением "локальных рецептивных полей". На рис. 6.2 наглядно представлена
концепция локального рецептивного поля, применяемая к данным изображений.
Каждое локальное рецептивное поле соответствует пиксельному участку в изобра­
жении и обрабатывается отдельным "нейроном". Эти "нейроны" аналогичны тем,
которые находятся в полносвязных слоях. Как и в случае с полносвязными слоями,
к входящим данным (которые исходят из локального рецептивного участка изо­
бражения) применяется нелинейное преобразование.
Слой таких "сверточных нейронов" может быть реорганизован в сверточный слой.
Этот слой можно рассматривать как преобразование одной пространственной об­
ласти в другую. В случае изображений сверточный слой преобразует один пакет
134
|
Глава 6
Рис. 6.1. Локальное рецептивное поле нейрона
Рис. 6.2. Локальное рецептивное поле "нейрона" в сверточной сети
изображений в другой (рис. 6.3). В следующем разделе мы представим более под­
робную информацию о том, как строится сверточный слой.
Стоит подчеркнуть, что локальные рецептивные поля не обязательно должны огра­
ничиваться только данными изображений. Например, в многоярусных сверточных
архитектурах, где выход из одного сверточного слоя поступает на вход следующе­
го, локальное рецептивное поле будет соответствовать ’’участку” обрабатываемых
признаковых данных.
Сверточные нейронные сети
|
135
Рис. 6.3. Сверточный слой выполняет преобразование изображения
Сверточные ядра
В предыдущем разделе мы упомянули, что сверточный слой применяет нелиней­
ную функцию к локальному рецептивному полю во входных данных. Такое ло­
кально применяемое нелинейное преобразование лежит в основе сверточных архи­
тектур, но это не единственная часть. Вторая часть свертки называется сверточным
ядром. Сверточное ядро — это просто матрица весов, во многом подобная весам,
ассоциированным с полносвязным слоем. На рис. 6.4 схематически показано, как
сверточное ядро применяется к входным данным1.
0
0
0
0
0
0
0
0
21
0
0
0
0
0
85
71
0
0
0
0
3
0
0
250 231 127 63
0
250 252 250 209 56
0
0
250 252 250 250 83
0
0
0
0
0
0
0
0
Карта признаков
Изображение
Рис. 6.4. Сверточное ядро применяется к входным данным. Ядерные веса перемножаются поэлементно
с соответствующими числами в локальном рецептивном участке, и получившиеся произведения
суммируются. Обратите внимание, что это соответствует сверточному слою
без нелинейного преобразования
Ключевая идея сверточных сетей заключается в том, что одно и то же (нелинейное)
преобразование применяется к каждому локальному рецептивному полю в изобра­
жении. Визуально изобразите локальное рецептивное поле как скользящее окно,
перетаскиваемое поверх изображения. В каждой позиции локального рецептивного
поля нелинейная функция применяется для получения одного числа, соответст­
вующего конкретному участку изображения. Как показано на рис. 6.4, это преобра­
1 См. интуитивно понятную демонстрацию и объяснение (на англ, яз.) работы сверточной сети на
странице https://ujjwalkarn.me/2016/08/ll/intuitive-explanation-convnets/. — Прим. пер.
136
|
Гпава 6
зование превращает одну решетку чисел в другую решетку чисел. Для графических
данных принято обозначать размер локального рецептивного поля в терминах ко­
личества пикселов на каждом размере рецептивного поля. Например, в сверточных
сетях обычно встречаются размеры локальных рецептивных полей 5x5 и 7x7.
Что делать, если мы хотим указать, что локальные рецептивные поля не должны
перекрываться? Это можно сделать, изменив размер шага сверточного ядра. Размер
шага управляет перемещением рецептивного поля над входными данными. На
рис. 6.4 показано одномерное сверточное ядро с шагами 1 и 2 соответственно. На
рис. 6.5 представлено, как изменение размера шага меняет перемещение рецептив­
ного поля по входным данным.
Это яснее всего видно на одномерном входе. Сеть слева имеет шаг 1, в то время как та, что справа,
имеет шаг 2. Обратите внимание, что каждое локальное рецептивное поле вычисляет
максимум своих входных данных
Теперь обратите внимание, что сверточное ядро, которое мы определили, преобра­
зует решетку чисел в другую решетку чисел. Что делать, если мы хотим получить
более одной решетки чисел на выходе? Это достаточно легко: нам просто нужно
добавить для обработки изображения больше сверточных ядер. Сверточные ядра
также называются фильтрами, поэтому количество фильтров в сверточном слое
управляет количеством преобразованных решеток, которые мы получаем. Коллек­
ция сверточных ядер образует сверточный слой.
Сверточные ядра на многомерных входных данных
В этом разделе мы описали сверточные ядра преимущественно как преоб­
разование решеток чисел в другие решетки чисел. Ссылаясь на наш тен­
зорный язык из предыдущих глав, свертки преобразуют матрицы в мат­
рицы.
Что делать, если ваши входные данные имеют больше размерностей? На­
пример, изображение с цветовой схемой RGB обычно имеет три цветных
канала, поэтому такое изображение по праву является тензором 3-го ранга.
Самый простой способ обработки данных RGB — указать, что каждое ло­
кальное рецептивное поле содержит все цветные каналы, связанные с пик­
селами в этом участке. Тогда можно сказать, что для локального рецеп­
тивного поля размером 5x5 пикселов в трех цветных каналах локальное
рецептивное поле имеет размер 5x5x3.
В общем случае можно обобщить выводы на тензоры более высокой раз­
мерности, соответствующим образом расширив размерность локального
Сверточные нейронные сети
|
137
рецептивного поля. Такое действие также может потребовать многомер­
ных шагов, в особенности если разные размерности должны обрабатывать­
ся по отдельности. Детали просты в разработке, и мы оставляем вам иссле­
дование многомерных сверточных ядер в качестве упражнения.
Редуцирующие слои
В предыдущем разделе мы ввели понятие сверточных ядер. Такие ядра применяют
заучиваемые нелинейные преобразования к локальным участкам входных данных.
Эти преобразования поддаются заучиванию и по теореме об универсальной ап­
проксимации способны обучаться на локальных участках произвольно сложным
входным преобразованиям. Такая гибкость придает сверточным ядрам значитель­
ную часть их мощи. Но в то же время наличие большого количества заучиваемых
весов в глубокой сверточной сети может замедлить процесс ее тренировки.
Вместо использования заучиваемого преобразования можно использовать фикси­
рованное нелинейное преобразование, тем самым уменьшив вычислительные за­
траты на тренировку сверточной сети. Популярным фиксированным нелинейным
преобразованием является максимально редуцирующий слои1. Такие слои отбирают
и передают на выход максимально активирующиеся входные данные в пределах
каждого локального рецептивного участка. Этот процесс показан на рис. 6.6. Реду­
цирующие слои полезны для структурированного уменьшения размерности вход­
ных данных. В более математическом смысле они принимают локальное рецептив­
ное поле и заменяют нелинейную активационную функцию в каждой части поля
функцией max (либо min, либо average).
Редуцирующие слои стали менее полезны по мере совершенствования оборудова­
ния. Хотя редукция по-прежнему эффективна в качестве метода уменьшения разОдиночный срез глубины
Максимальная редукция
с фильтрами 2x2 и шагом 2
6
8
3
4
X
Y
Рис. 6.6. Иллюстрация максимально редуцирующего слоя. Обратите внимание на то, как на выход
передается максимальное значение в каждой цветной области (каодом локальном рецептивном поле)*
2 Максимально редуцирующий слой (max pooling) берет максимальное значение участка/окна окрест­
ных признаков для уменьшения количества активаций и приводит к инвариантности сдвига в изобра­
жении. В отечественной литературе термин pooling layer часто переводится как субдискретизирующий, или подвыборочный, слой. — Прим. пер.
138
|
Глава 6
мерности, новейшие исследования, как правило, избегают использования редуци­
рующих слоев из-за присущей им теряемости данных (из редукции данных невоз­
можно вычленить, какой пиксел на входе породил сообщенную активацию). Тем
не менее редукция появляется во многих стандартных сверточных архитектурах,
поэтому ее стоит понимать.
Конструирование сверточных сетей
В простой сверточной архитектуре к входным данным применяется серия сверточ­
ных и редуцирующих слоев, чтобы обучить ее сложной функции на данных вход­
ного изображения. В формировании этих сетей существует много деталей, но
в своей основе архитектурная конструкция представляет собой не что иное, как
сложную форму компоновки по принципу LEGO. На рис. 6.7 показано, как свер­
точная архитектура строится из составных блоков.
Сверточный
слой 1
Свертка
Редуцирующий
слой 1
Подвыборка
Сверточный
слой 2
Свертка
Редуцирующий Скрытый
слой 2
слой
Выход
Подвыборка
Свертка
Полная
связь
Рис. 6.7. Простая сверточная архитектура, сконструированная
из многоярусных сверточных и редуцирующих слоев
Растянутые свертки
Растянутые свертки, или atrous-свертки, — это новый популярный вид сверточно­
го слоя. Оригинальная идея здесь состоит в том, чтобы оставлять пропуски в ло­
кальном рецептивном поле для каждого нейрона (a trous на французском языке оз­
начает ”с дырами”). В его основе лежит старая концепция из цифровой обработки
сигналов, которая недавно вошла в оборот в сверточной литературе.
Основным преимуществом atrous-свертки является увеличение видимой области
для каждого нейрона. Рассмотрим архитектуру свертки, первый слой которой явля­
ется классическим сверточным слоем с локальными рецептивными полями 3x3.
Затем нейрон на один слой глубже в архитектуре во втором классическом сверточ­
ном слое имеет рецептивную глубину 5x5 (каждый нейрон в локальном рецептив­
ном поле второго слоя сам имеет локальное рецептивное поле в первом слое). За­
тем нейрон на два слоя глубже имеет рецептивный обзор 7 х 7. В общем случае,
нейрон N слоев внутри сверточной архитектуры имеет рецептивный обзор разме­
ром (27V + l)x(27V + l). Этот линейный рост в рецептивном обзоре хорош для не­
больших изображений, но быстро становится помехой для крупных изображений.
Сверточные нейронные сети
|
139
Atrous-свертка обеспечивает экспоненциальный рост в видимом рецептивном поле,
оставляя пропуски в своих локальных рецептивных полях. ”1-растянутая” свертка
не оставляет пропусков, тогда как ”2-растянутая” свертка оставляет один пропуск
между каждым элементом локального рецептивного поля. Ярусная укладка растя­
нутых слоев приводит к экспоненциальному увеличению размеров локального ре­
цептивного поля. На рис. 6.8 показано это экспоненциальное увеличение.
Растянутые свертки бывают очень полезными для больших изображений. Напри­
мер, медицинские изображения могут растягивать тысячи пикселов в каждой раз­
мерности. Создание классических сверточных сетей, имеющих глобальное понима­
ние, потребует неоправданно глубоких сетей. Использование растянутых сверток
позволяет сетям лучше понимать глобальную структуру таких изображений.
Рис. 6.8. Растянутая свертка. Для каодого нейрона оставляются пропуски в локальном рецептивном
поле: а — диаграмма 1-растянутой свертки 3x3; б — применение 2-растянутой свертки 3*3 к свертке (а);
в — применение 4-растянутой свертки 3*3 к свертке (6). Обратите внимание, что слой (а)
имеет рецептивное поле шириной 3, слой (б) — рецептивное поле шириной 7,
слой (а) — рецептивное поле шириной 15
Применения сверточных сетей
В предыдущем разделе мы рассмотрели механизм сверточных сетей и познакомили
вас со многими компонентами, которые составляют эти сети. В этом разделе
мы опишем некоторые приложения, которые позволяют использовать сверточные
архитектуры.
Обнаружение и локализация объектов
Обнаружение объектов— это задача выявления объектов (или сущностей), при­
сутствующих на фотографии. Локализация объекта — это задача определения, где
на изображении существуют объекты, и нанесения "ограничивающей рамки"
вокруг каждого появления объекта. На рис. 6.9 показано, как выглядит обнаруже­
ние и локализация на стандартных изображениях.
Почему обнаружение и локализация важны? Одной из очень полезных задач лока­
лизации является обнаружение пешеходов на снимках, сделанных из автомобиля,
двужущегося на автопилоте. Излишне говорить, что чрезвычайно важно, чтобы та­
кой автомобиль мог идентифицировать всех находящихся поблизости пешеходов.
140
|
Глава 6
Рис. 6.9. Объекты, обнаруженные и локализованные ограничительными рамками
на нескольких демонстрационных изображениях
Другие приложения обнаружения объектов могут использоваться для поиска всех
появлений друзей на фотографиях, загруженных в социальную сеть. Еще одним
приложением может быть выявления потенциальных опасностей столкновения
с беспилотником.
Эта масса применений привела к тому, что задача обнаружения и локализации ста­
ла во главу угла огромного количества исследовательских работ. Конкурс ILSVRC,
упомянутый несколько раз в этой книге, сосредоточен на обнаружении и локализа­
ции объектов, которые можно найти в коллекции ImagetNet.
Сегментация изображений
Сегментация изображения — это задача по маркировке каждого пиксела изобра­
жения объектом, которому он принадлежит. Сегментация связана с локализацией
объектов, но значительно сложнее, т. к. требует точного понимания границ между
Рис. 6.10. Объекты на изображении "сегментированы" на разные категории. Ожидается, что сегментация
изображений окажется очень полезной для таких применений, как беспилотные автомобили
и робототехника, т. к. оно позволит достигать подробнейшего понимания сцен
Сверточные нейронные сети
|
141
объектами на изображениях. До недавнего времени сегментация изображений час­
то выполнялась с помощью графических моделей, т. е. альтернативной формы ма­
шинного самообучения (в отличие от глубоких сетей), но в последнее время свер­
точные сегментации стали более заметными и позволили алгоритмам сегментации
изображений достичь новых рекордов точности и скорости. На рис. 6.10 показан
пример сегментации изображения применительно к данным визуальной информа­
ции самоходных автомобилей.
Графовые свертки
Сверточные алгоритмы, которые мы показали вам к этому моменту, ожидают в ка­
честве своих входных данных прямоугольные тензоры. Такие входы могут быть
в виде изображений, видео или даже предложений. Можно ли обобщить свертки
для применения к нерегулярным входным данным?
Фундаментальной идеей, положенной в основу сверточных слоев, является понятие
локального рецептивного поля. Каждый нейрон выполняет вычисление на входных
данных в своем локальном рецептивном поле, которые обычно составляют смеж­
ные пикселы на входном изображении. Что касается нерегулярных входных дан­
ных, таких как ненаправленный (неориентированный) граф на рис. 6.11, то это про­
стое понятие локального рецептивного поля не имеет смысла; смежные пикселы
отсутствуют. Если мы сможем определить более общее локальное рецептивное
поле для ненаправленного графа, то мы будем в состоянии определять сверточные
слои, которые принимают ненаправленные графы.
Как показано на рис. 6.11, граф состоит из множества узлов, соединенных ребрами.
Одним из потенциальных определений локального рецептивного поля может быть
его определение как содержащего узел и его коллекцию соседей (где два узла счи­
таются соседями, если они соединены ребром). Используя это определение локаль­
ных рецептивных полей, можно сформулировать обобщенные понятия сверточного
и редуцирующего слоев. Эти слои могут быть собраны в графовые сверточные
архитектуры.
Рис. 6.11. Иллюстрация ненаправленного графа, состоящего из узлов, связанных ребрами
142
|
Гпава 6
1.
Вернуть
топологию
и признаки
атомов
2.
Применить
графовые
свертки
и редукции
3.
Применить
стягивание
графа
4.
>
Применить
опорные
слои
Молекула
Редукция графа
1Задать
к = deg(v)
(
2.
for и in neigh(v) <j{v}
задать
d = dist (v, w)
преобразовать
признаки
и> = цМи +
3.
Суммировать все и,
применить
нелинейную функцию
4. X
Вернуть
признаки
для V
Топология —*■
графа
Атомарные —►
признаки
Рис. 6.12. Графовая сверточная архитектура, обрабатывающая молекулярные входные данные.
Молекула смоделирована как ненаправленный граф, в котором атомы формируют узлы, а химические
связи — ребра. 'Топологией графа" является ненаправленный граф, который
соответствует молекуле. "Признаками атома" служат векторы, один на атом.
Это адаптированная версия, взятая из статьи "Обнаружение новых медицинских препаратов
на малых данных с однократным самообучением"3
3 Altae-Tran Н., Ramsundar В., Pappu A. S., PandeLow V. Data Drug Discovery with One-Shot Learning.
Сверточные нейронные сети
|
143
Где могут быть полезными такие графовые сверточные архитектуры? Оказывается,
что в химии молекулы могут быть смоделированы как ненаправленные графы, где
атомы образуют узлы, а химические связи — ребра. Поэтому графовые сверточные
архитектуры особенно полезны в химическом машинном самообучении. Например,
на рис. 6.12 показано, как можно применить графовую сверточную архитектуру
к обработке молекулярных входных данных.
Генерирование изображений
с помощью вариационных автокодировщиков
Все приложения, которые мы описали к этому моменту, представляют собой задачи
контролируемого самообучения. Иными словами, имеются четко сформулирован­
ные входные и выходные данные, и задача по-прежнему (используя сверточную
сеть) остается в том, чтобы узнать сложную функцию, которая преобразует вход
в выход. Существуют ли задачи неконтролируемого самообучения, которые могут
решаться с помощью сверточных сетей? Напомним, что неконтролируемое само­
обучение требует ’’понимания” структуры точек входных данных. Если говорить
о моделировании изображений, то здесь хорошим показателем понимания структу­
ры входных изображений является возможность ’’получать образцы” новых изо­
бражений из входного распределения.
Что означает "получение образца” изображения? Давайте для начала предположим,
что у нас есть набор данных с изображениями собак. Получение образца нового
изображения собаки требует генерирования нового изображения собаки, которого
нет в тренировочных данных*. Идея заключается в том, что мы хотим иметь фото­
графию собаки, которая могла бы быть обоснованно включена в тренировочные
данные, но туда не попала. Как можно решить эту задачу с помощью сверточных
сетей?
Наверное, мы могли бы натренировать модель, принимающую словарные метки,
например ’’собака”, и предсказывающую изображения собак. И вполне возможно,
что для решения этой предсказательной задачи мы сможем натренировать контро­
лируемую модель, но проблема остается по-прежнему в том, что при наличии
входной метки "собака” наша модель сможет генерировать только одну фотогра­
фию собаки. Предположим, что теперь за каждой собакой мы можем закрепить
случайный тег, скажем, "собака3422" или ”собака9879”. Тогда все, что от нас по­
требуется, чтобы получить новое изображение собаки, — это прикрепить новый
случайный тег, скажем, "собака2221", и получить эту новую фотографию собаки.
Вариационные автокодировщики формализуют эти интуитивные подходы и состо­
ят из двух сверточных сетей: кодировщика и декодировщика. Сеть кодировщика
используется для преобразования изображения в плоский "встроенный” вектор.
Сеть декодировщика отвечает за преобразование встроенного вектора в изображе­
ния. Для того чтобы гарантировать способность декодировщика получать образцы
разных изображений, добавляется шум. На рис. 6.13 показан вариационный автоко­
дировщик.
144
|
Глава 6
Латентное пространство
Рис. 6.13. Схематическая иллюстрация вариационного автокодировщика.
Вариационный автокодировщик состоит из двух сверточных сетей —
кодировщика и декодировщика
Фактическая реализация вариационных автокодировщиков влечет за собой еще
больше деталей, но они действительно обладают способностью получать образцы
изображений. Вместе с тем наивные вариационные кодировщики, судя по всему,
порождают размытые образцы (рис. 6.14). Эта размытость может быть вызвана тем,
что А2 -потеря не накладывает строгий штраф на размытость изображения (вспом­
ните наше обсуждение £2 -потери, которая не штрафует небольшие отклонения).
Для создания четких образцов изображений нам понадобятся другие архитектуры.
Рис. 6.14. Образцы изображений, полученных из вариационного автокодировщика,
натренированного на наборе лиц. Обратите внимание,
что образцы изображений довольно размытые
Сверточные нейронные сети
|
145
Состязательные модели
1} -потеря строго штрафует крупные локальные отклонения, но не сильно штрафу­
ет много малых локальных отклонений, вызывая размытость. Как спроектировать
альтернативную функцию потери, которая строже штрафует размытость изображе­
ний? Оказывается, довольно сложно написать функцию потери, которая реализует
этот прием. В то время как наши глаза могут быстро обнаруживать размытость,
наши аналитические инструменты не настолько быстры, чтобы ухватить проблему.
А можно ли каким-то образом "обучиться" функции потери? Сначала эта идея зву­
чит немного бессмысленно, поскольку не понятно, где получить тренировочные
данные? Но оказывается, что существует блестящая идея, которая делает это воз­
можным.
Предположим, мы можем натренировать отдельную сеть, которая заучивает поте­
рю. Назовем эту сеть дискриминатором. Давайте назовем сеть, которая создает
изображения, генератором. Генератор может быть настроен так, чтобы он соперни­
чал с дискриминатором до тех пор, пока генератор не научится производить изо­
бражения, которые являются фотореалистичными. Эта форма архитектуры обычно
называется генеративно-состязательной сетью, или GAN-сетью.
Лица, генерируемые GAN-сетью (рис. 6.15),— гораздо четче, чем те, которые
порождает наивный вариационный автокодировщик (см. рис. 6.14)! Кроме того,
Рис. 6.15. Образцы изображений, полученные из генеративно-состязательной сети (GAN),
натренированной на наборе лиц. Обратите внимание, что полученные образцы изображений менее
расплывчатые, чем образцы изображений из вариационного автокодировщика
146
|
Глава 6
GAN-сети достигли ряда других многообещающих результатов. Например, сеть
CycleGAN, судя по всему, способна обучаться сложным преобразованиям изобра­
жений, таким как превращение лошадей в зебр и наоборот. На рис. 6.16 показано
несколько преобразований изображений CycleGAN.
Зебры
’ Лошади
Рис. 6.16. Сеть CycleGAN способна выполнять сложные преобразования изображений,
такие как преобразование изображений лошадей в изображения зебр (и наоборот)
К сожалению, на практике генеративно-состязательные сети по-прежнему с трудом
поддаются тренировке. Для того чтобы заставить генераторы и дискриминаторы
обучаться разумным функциям, требуется большой запас хитроумных приемов.
Поэтому, хотя и существует много будоражащих демонстраций GAN-сетей, пока
что они еще не дозрели до состояния, когда их можно широко внедрять в промыш­
ленных приложениях.
Тренировка сверточной сети в TensorFlow
В этом разделе мы рассмотрим образец программного кода для тренировки простой
сверточной нейронной сети. В частности, наш образец кода продемонстрирует, как
тренировать сверточную архитектуру LeNet-5 на наборе данных MNIST, используя
библиотеку TensorFlow. Как всегда, мы рекомендуем вам сверяться, попутно вы­
полняя полный образец программного кода, размещенный в хранилище GitHub
(https://github.com/matroid/dlwithtf ), связанном с этой книгой.
Набор данных MNIST
Набор данных MNIST состоит из изображений рукописных цифр. Задача машинно­
го самообучения, связанная с MN1ST, состоит в создании модели, натренированной
на тренировочном наборе цифр, которая обобщается на контрольный набор. На
рис. 6.17 показано несколько изображений, полученных из набора данных MNIST.
Сверточные нейронные сети
|
147
Рис. 6.17. Несколько изображений рукописных цифр из набора данных MNIST.
Задача самообучения состоит в том, чтобы научиться идентифицировать цифру на изображении
Набор данных MNIST имел большое значение для разработки методов машинного
самообучения для компьютерного зрения. Этот набор данных настолько сложен,
что очевидные необучающиеся методы, как правило, справляются не очень хоро­
шо. В то же время MNIST настолько мал, что эксперименты с новыми архитекту­
рами не требуют больших вычислительных мощностей.
Вместе с тем набор данных MNIST, в основном, устарел. Лучшие модели достига­
ют почти 100-процентной точности на тестовом наборе. Обратите внимание, что
этот факт не означает, что задача распознавания рукописных цифр решена! Скорее
всего, исследователи плотно подогнали архитектуру под набор данных MNIST и
воспользовались его характерными особенностями для достижения очень высокой
предсказательной точности. Вследствие этого использовать MNIST для разработки
новых глубоких архитектур больше не рекомендуется. С учетом сказанного,
MNIST по-прежнему остается превосходным набором данных для педагогических
целей.
Скачивание набора данных MNIST
Кодовая база MNIST доступна онлайн на сайте Яна Лекуна (Yann LeCun)
(http://yannJecunxom/exdb/mnist/). Загрузочный сценарий скачивает сырой файл
с веб-сайта. Обратите внимание на то, как этот сценарий кэширует скачанные
данные, чтобы повторные вызовы функции download () не тратили силы впустую.
В качестве более общего замечания, наборы данных для машинного самообучения
вполне нормально хранить в облаке и извлекать их пользовательским кодом перед
обработкой для ввода в обучающийся алгоритм. Набор данных Тох21, к которому
мы обращались через библиотеку DeepChem в главе 4, следовал тому же шаблону
проектирования. В общем случае, если вы хотите разместить большой набор дан­
ных для анализа в облаке, то хранение его в облаке и загрузка на локальный ком­
148
|
Глава 6
пьютер по мере необходимости для обработки считается хорошей практикой.
(Однако этот прием не работает для очень больших наборов данных, где время
передачи по сети становится непомерно дорогим.) См. пример 6.1.
| Пример 6.1. Эта функция скачивает набор данных MNIST
def download(filename):
’’’’’’Скачать данные с веб-сайта Яна Лекуна, если это еще не было сделано.’””’
if not os.path.exists(WORK_DIRECTORY):
os.makedirs(WORK_DIRECTORY)
filepath = os.path.join(WORK_DIRECTORY,
filename)
if not os.path.exists(filepath):
filepath, _ = urllib.request.urlretrieve(SOURCE_URL + filename,
filepath)
size = os.stat(filepath).st_size
print(’Успешно скачан’, filename, size,
’байт.’)
return filepath
Приведенная функция проверяет наличие рабочего каталога work di rectory. Суще­
ствование этого каталога воспринимается так, что набор данных MNIST уже ска­
чан. В противном случае этот сценарий использует библиотеку Python urllib, что­
бы скачать данные, и выводит сведения о количестве скачанных байтов.
Набор данных MNIST хранится в виде сырой строки байтов, в которой закодирова­
ны значения пикселов. Для того чтобы легко обработать эти данные, нам нужно
конвертировать их в массив NumPy. Удобство функции np.frombuffer состоит
в том, что она позволяет преобразовывать сырой байтовый буфер в числовой массив
(пример 6.2). Как мы уже отмечали в других разделах этой книги, глубокие сети
могут быть дестабилизированы входными данными, занимающими широкие диапа­
зоны. Для стабильного градиентного спуска часто необходимо сократить рамки
входных данных, чтобы они охватывали ограниченный диапазон. Исходный набор
данных MNIST содержит значения пикселов в диапазоне от 0 до 255. Для стабиль­
ности этот диапазон должен быть смещен, чтобы иметь нулевое среднее и единич­
ный диапазон (от -0,5 до +0,5).
Пример 6.2. Извлечение изображений из скачанного набора данных
в массивы NumPy
def extract_data(filename, num_images) :
’’’’’’Извлечь изображения в 4-мерный тензор [индекс изображения, у, х, каналы] .
Значения перешкалируются из [0, 255] вплоть до [-0.5, 0.5].
»» »» »»
print(’Извлечение’, filename)
with gzip.open(filename)
as bytestream:
bytestream.read(16)
Сверточные нейронные сети
|
149
buf = bytestream.read(IMAGE_SIZE * IMAGE_SIZE * num_images * NUM_CHANNELS)
data = numpy.frombuffer(buf, dtype=numpy.uint8).astype(numpy.float32)
data = (data - (PIXEL_DEPTH /2.0))
data = data.reshape(num_images,
/ PIXEL_DEPTH
IMAGE_SIZE, IMAGE_SIZE, NUM_CHANNELS)
return data
Метки хранятся в простом файле в виде строки байтов. Данные содержат заголо­
вок, состоящий из 8 байт, а оставшиеся данные содержат метки (пример 6.3).
~
.......
~-----------------------------
Пример 6.3. Функция, извлекающая метки из скачанного набора данных
в массив меток
■
■ 1
i
def extract_labels(filename, num_images):
'"’"Извлечь метки в вектор идентификаторов меток с типом int64."""
print('Извлечение', filename)
with gzip.open(filename) as bytestream:
bytestream.read(8)
buf = bytestream.read(1 * num_images)
labels = numpy.frombuffer(buf,
dtype=numpy.uint8).astype(numpy.int64)
return labels
Учитывая функции, определенные в предыдущих примерах, теперь можно скачать
и обработать тренировочные и тестовые наборы данных MNIST (пример 6.4).
Пример 6.4. Использование функций, определенных в предыдущих примерах.
i Этот фрагмент кода скачивает и обрабатывает тренировочные и тестовые наборы
данных MNIST
- ____ ______
______________________________________________ j
# Получить данные
train_data_filename = download('train-images-idx3-ubyte.gz')
train_labels_filename = download('train-labels-idxl-ubyte.gz')
test_data_filename = download('tl0k-images-idx3-ubyte.gz')
test_labels_filename = download('tlOk-labels-idxl-ubyte.gz')
# Извлечь их в массивы NumPy
train_data = extract_data(train_data_filename,
60000)
train_labels = extract_labels(train_labels_filename, 60000)
test_data = extract_data(test_data_filename, 10000)
test—labels = extract—labels(test_labels_filename, 10000)
Набор данных MNIST не определяет контрольный набор данных для гиперпара­
метрической настройки явным образом. По этой причине в качестве контрольных
данных мы вручную назначаем последние 5000 точек данных тренировочного
набора данных (пример 6.5).
150
|
Гпава 6
Гпример 6.5. Извлечение заключительных 5000 элементов тренировочных данных
I для контроля гиперпараметров
VALIDATION_SIZE = 5000 # размер контрольного набора
# Сгенерировать контрольный набор
validation_data = train_data[:VALIDATION_SIZE,
...]
validation—labels = train_labels[:VALIDATION_SIZE]
train_data = train_data[VALIDATION_SIZE: ,
...]
train_labels = train_labels[VALIDATION_SIZE: ]
Получение образца правильного контрольного набора
В примере 6.5 в качестве контрольного набора для измерения результатив­
ности наших методов самообучения мы используем заключительный
фрагмент тренировочных данных. В этом случае такой метод относительно
безвреден. Распределение данных в тестовом наборе хорошо представлено
распределением данных в контрольном наборе.
Однако в других ситуациях такой тип простого определения контрольного
набора может быть катастрофическим. В молекулярном машинном само­
обучении (приложении машинного самообучения для предсказания
свойств молекул) почти всегда тестовое распределение резко отличается
от тренировочного распределения. Ученые больше всего заинтересованы
в перспективном предсказании, т. е. они хотели бы предсказывать свойства
молекул, которые никогда не испытывались для рассматриваемого свойст­
ва. В этом случае использование последнего фрагмента тренировочных
данных для контроля или даже случайной подвыборки тренировочных
данных приведет к ошибочно высокой точности. Довольно часто молеку­
лярная машинно-обучающаяся модель имеет точность 90% на контроль­
ных данных и, к примеру, 60% на тестовых.
Для того чтобы исправить эту ошибку, необходимо спроектировать мето­
ды получения образца контрольного набора, которые прилагают все уси­
лия, чтобы сделать контрольный набор непохожим на тренировочный. Для
молекулярного машинного самообучения разработан целый ряд таких ал­
горитмов, подавляющая часть которых используют разные математические
оценки различия графов (рассматривая молекулу как математический граф
с атомами в качестве узлов и химическими связями в качестве ребер).
Эта проблема возникает и во многих других областях машинного самообу­
чения. В медицинском или в финансовом машинном самообучении опора
на исторические данные для выполнения предсказаний может быть катаст­
рофической. В каждом приложении машинного самообучения важно кри­
тически рассуждать о том, является ли результативность полученного
образца контрольного набора на самом деле хорошим косвенным показа­
телем истинной результативности.
Сверточные нейронные сети
|
151
Сверточные примитивы TensorFlow
Начнем с введения примитивов TensorFlow, которые используются для построения
сверточных сетей (пример 6.6).
i Пример 6.6. Определение двухмерной свертки в TensorFlow
tr.nn.conv2d(
input,
filter,
strides,
padding,
use_cudnn_on_gpu=None,
data_format=None,
name=None
)
Функция tf.nn.conv2d() — это встроенная в TensorFlow функция, которая опреде­
ляет сверточные СЛОИ. Здесь ВВОДИТСЯ тензор формы (пакет, высота, ширина, кана­
лы) ,
пакет — это количество изображений в мини-пакете.
Обратите внимание, что определенные ранее функции преобразования считывают
данные MNIST в этот формат. Аргумент filter— это тензор формы (высота_фильтра, ширина_фильтра, каналы, выходные_каналы), КОТОрЫЙ задает заучиваемые
веса для нелинейного преобразования, заученного в сверточном ядре. Аргумент
strides содержит шаги фильтра и представляет собой список длиной 4 (по одному
для каждой входной размерности).
Аргумент padding управляет дополнением входных тензоров лишними нулями
(рис. 6.18), тем самым гарантируя, что данные на выходе из сверточного слоя име­
ют ту же форму, что и данные на входе. Если padding=”SAME”, то входные данные
input дополняются нулями, обеспечивая из сверточного слоя вывод тензора той же
формы, что и оригинальный входной тензор изображения. Если padding=”VALiD”, то
дополнение нулями не используется.
,
o jo jo j о о |о |о о
|o |o |o |o |o |o |o |o j
1
0 0 0 0 0 0 0 0 010
0 0 0 0 0 0 0 0 0 10
0
0
0
0
0
0
0
0
32 х 32 X 3
0
0
0
0
0
0
0 0 0 !° 0 0 0 0 0 0
0 0 о о 0 0 0 0 0 0
Объем входных данных: 32x32x3.
Если мы вообразим две границы из нулей
вокруг этого объема, то это даст объем
36x36x3. Затем, когда мы применим наш
сверточный слой стремя фильтрами 5x5x3
и шагом 1, то мы получим такой же
выходной объем 32x32x3
.
i
36
Рис. 6.18. Дополнение нулями для сверточных слоев гарантирует,
что выходное изображение имеет ту же форму, что и у входного изображения
152
|
Глава 6
Фрагмент кода в примере 6.7 определяет в TensorFlow максимально редуцирующий
слой.
| Пример 6.7. Определение максимально редуцирующего слоя в TensorFlow
tf.nn.max_pool(
value,
ksize,
strides,
padding,
data_f ormat='NHWC',
name=None
)
Функция tf .nn.max pool о выполняет редукцию на основе функции max. Здесь value
имеет ту же форму, ЧТО И ВХОД input ДЛЯ tf .nn. conv2d(пакет, высота, ширина, каналы).
Аргумент ksize — это размер окна редукции и представляет собой список длиной 4.
Аргументы strides и padding ведут себя так же, как для tf .nn.conv2d.
Сверточная архитектура
Определенная в этом разделе архитектура будет близко напоминать LeNet-5 —
оригинальную архитектуру, используемую для тренировки сверточных нейронных
сетей на наборе данных MNIST. В то время когда была изобретена архитектура
LeNet-5, она была непомерно ресурсоемкой в вычислительном плане, требуя для
завершения тренировки нескольких недель вычислений. В наши дни, чтобы трени­
ровать модели LeNet-5, к счастью, более чем достаточно современного ноутбука.
На рис. 6.19 представлена структура архитектуры LeNet-5.
Вход
32x32
С1: карты
признаков
6@28х28
S2: карты
признаков
6@14х14
СЗ: карты
признаков
16@10х10
S4: карты
признаков
1б@5х5
С5: слой
120
F6: слой
84
Выход
10
Рис. 6.19. Сверточная архитектура LeNet-5
Где больший объем вычислений имеет реальное значение?
Возраст архитектуры LeNet-5 насчитывает десятилетия, но она по существу
является правильной архитектурой для задачи распознавания цифр. Тем не
менее вычислительные потребности этой архитектуры оставляли ее в от­
носительной неизвестности в течение десятилетий. Тогда вызывает инте-
Сверточные нейронные сети
|
153
pec следующий вопрос: какие исследовательские задачи сегодня решаются
аналогичным образом, но ограничиваются исключительно отсутствием
адекватной вычислительной мощности?
Одним из сопоставимых претендентов является обработка видео. Сверточ­
ные модели достаточно хороши в обработке видеоданных. Однако они
громоздки в хранении и тренировке моделей на больших наборах видео­
данных, поэтому большинство научных работ молчат о результатах на
видеоданных. Вследствие этого наспех скомпоновать хорошую систему
обработки видеоданных не так просто.
Эта ситуация, вероятно, изменится по мере увеличения вычислительных
возможностей, и вполне вероятно, что системы обработки видеоданных
станут гораздо более распространенным явлением. Вместе с тем существу­
ет одно критическое различие между сегодняшними улучшениями в аппа­
ратных средствах и теми, которые происходили в прошлые десятилетия.
В отличие от прошлых лет выполнение закона Мура резко замедлилось.
Это явилось результатом того, что улучшения в оборудовании требуют
более чем естественного сжатия транзисторов и часто значительной изо­
бретательности в проектировании архитектур. Мы вернемся к этой теме
в последующих главах и обсудим архитектурные потребности глубоких
сетей.
Давайте определим веса, необходимые для тренировки нашей сети LeNet-5. Мы
начнем с определения нескольких основных констант, которые используются для
задания наших весовых тензоров (пример 6.8).
^Пример 6.8. Определение основных констант для модели LeNet-5
|
NUM_CHANNELS = 1
IMAGE_SIZE =28
NUM_LABELS =10
Определяемая нами архитектура будет использовать два сверточных слоя впере­
мешку с редуцирующими слоями, заканчиваясь двумя полносвязными слоями. На­
помним, что в редуцирующем слое заучиваемые веса не требуются, поэтому нам
нужно создать веса только для сверточных и полносвязных слоев. Для каждой
функции tf.nn.conv2d() нам нужно создать тензор с заучиваемыми весами, соответ­
ствующий аргументу filter для tf .nn.conv2d. о В этой конкретной архитектуре мы
также добавим сверточное смещение, по одному для каждого выходного канала
(пример 6.9).
| Пример 6.9. Определение заучиваемых весов для сверточных слоев
convl_weights = tf.Variable(
tf.truncated_normal([5, 5, NUM_CHANNELS, 32], # фильтр 5x5,
# глубина 32.
stddev=0.1,
seed=SEED, dtype=tf.float32))
154
|
Глава 6
convl_biases = tf.Variable(tf.zeros([32], dtype=tf.float32))
conv2_weights = tf.Variable(tf.truncated_normal(
[5,
5, 32,
64], stddev=0.1,
seed=SEED, dtype=tf.float32))
conv2_biases = tf.Variable(tf.constant (0.1, shape=[64], dtype=tf.float32))
Обратите внимание, что сверточные веса являются тензорами 4-го ранга, а смеще­
ния— тензорами 1-го ранга. Первый полносвязный слой конвертирует выходы
сверточного слоя в вектор размера 512. Входные изображения начинаются с раз­
мера image_size=28. После двух редуцирующих слоев (каждый из которых умень­
шает входные данные в 2 раза) мы в итоге получаем изображения размером
IMAGE SIZE//4. Мы создаем форму полносвязных весов, учитывая эти размеры.
Второй полносвязный слой используется для обеспечения 10-полосного классифи­
кационного выхода, поэтому, как показано в примере 6.10, он имеет форму весов
(512, 10) и форму смещения (10).
' Пример 6.10. Определение заучиваемых весов для полносвязных слоев
fcl_weights = tf.Variable( # полносвязный, глубина 512.
tf.truncated—normal([IMAGE_SIZE // 4 * IMAGE_SIZE // 4 * 64, 512],
stddev=0.1,
seed=SEED,
dtype=tf.float32))
fcl_biases = tf.Variable(tf.constant (0.1, shape=[512], dtype=tf.float32))
fc2_weights = tf.Variable(tf.truncated_normal ([512, NUM_LABELS],
stddev=0.1,
seed=SEED,
dtype=tf.float32))
fc2_biases = tf.Variable(tf.constant (
0.1, shape=[NUM—LABELS], dtype=tf.float32))
Вычислив все веса, мы теперь можем определить архитектуру сети. Архитектура
имеет шесть слоев по схеме "свертка — редукция — свертка — редукция — полная
связь — полная связь" (пример 6.11).
Пример 6.11. Определение архитектуры LeNet-5. Вызов функции,
определенной в этом примере, создаст экземпляр архитектуры
def model(data, train=False):
”””Определение модели.”””
# Двухмерная свертка с заполнением ’SAME'
(т. е. выходная карта
# признаков имеет тот же размер, что и вход).
# Примечание:
{strides} - это четырехмерный массив,
# форма которого соответствует конфигурации данных:
# [индекс изображения, у, х, глубина].
Сверточные нейронные сети
|
155
conv = tf.nn.conv2d(data,
convl_weights,
strides=[l, 1, 1, 1],
padding=’SAME’)
# Смещение и нелинейное преобразование ReLU.
relu = tf.nn.relu(tf.nn.bias_add(conv,
convl_biases))
# Максимальная редукция. Спецификация размера ядра {ksize} также
# соответствует конфигурации данных. Здесь мы имеем окно редукции
# размером 2 и шаг размером 2.
pool = tf.nn.max_pool(relu,
ksize=[l, 2, 2, 1],
strides=[l, 2, 2, 1],
padding=’SAME’)
conv = tf.nn.conv2d(pool,
conv2_weights,
strides=[l, 1, 1, 1],
padding=’SAME’)
relu = tf.nn.relu(tf.nn.bias_add(conv,
conv2_biases))
pool = tf.nn.max_pool(relu,
ksize=[l, 2, 2, 1],
strides=[l, 2, 2, 1],
padding=’SAME’)
# Реформировать кубоид с картой признаков в двухмерную матрицу,
# чтобы подать ее в полносвязные слои.
pool_shape = pool.get_shape().as_list()
reshape = tf.reshape(
pool,
[pool_shape[0], pool_shape[1] * pool_shape[2] * pool_shape[3]])
# Полносвязный слой. Примечание: операция
автоматически
# транслирует смещения.
hidden = tf.nn.relu(tf.matmul(reshape,
fcl_weights) + fcl_biases)
# Добавить 50% отсев только во время тренировки.
# Отсев также шкалирует активации, что позволяет обходиться
# без перешкалирования во время оценивания модели,
if train:
hidden = tf.nn.dropout(hidden, 0.5, seed=SEED)
return tf.matmul(hidden, fc2_weights) + fc2_biases
Как отмечалось ранее, базовая архитектура сети перемежает слои tf,nn.conv2d,
tf .nn.max pooi с нелинейными преобразованиями и конечным полносвязным слоем.
В целях регуляризации отсеивающий слой применяется после конечного полно­
связного слоя, но только во время тренировки. Обратите внимание, что мы переда­
ем входные данные в функцию model () в качестве аргумента data.
Единственная часть сети, которая должна быть определена,— это заполнители
(пример 6.12). Нам нужно определить два заполнителя для ввода тренировочных
изображений и тренировочных меток. В этой конкретной сети мы также определя­
156
|
Глава б
ем отдельный заполнитель для оценивания, который позволяет нам вводить боль­
шие пакеты во время оценивания.
Пример 6.12. Определение заполнителей для архитектуры
BATCH_S1ZE = 64
EVAL-BATCH-SIZE = 64
train_data_node = tf.placeholder (
tf.float32,
shape=(BATCH_SIZE,
IMAGE_SIZE,
IMAGE_SIZE, NUM_CHANNELS))
train_labels_node = tf.placeholder(tf.int64, shape=(BATCH_SIZE,))
eval_data = tf.placeholder(
tf.float32,
shape=(EVAL_BATCH_SIZE,
IMAGE_SIZE, NUM_CHANNELS))
IMAGE_SIZE,
Располагая этими определениями, теперь мы имеем обработанные данные, конкре­
тизированные входы и веса, а также построенную модель. Сейчас мы готовы на­
тренировать сеть (пример 6.13).
j Пример 6.13. Тренировка архитектуры LeNet-5
# Создать локальный сеанс для выполнения тренировки.
start_time = time.time()
with tf.Session() as sess:
# Выполнить все инициализаторы, чтобы подготовить
# тренируемые параметры.
tf.global—VariableS—initializer().run()
# Пройти тренировочные шаги в цикле.
for step in xrange(int(num_epochs * train_size)
// BATCH_SIZE):
# Вычислить сдвиг текущего мини-пакета в данных.
# Примечание: здесь можно было применить более оптимальную
# рандомизацию по всем эпохам.
offset = (step * BATCH_SIZE)
%
(train_size - BATCH_SIZE)
batch_data = train_data[offset:(offset + BATCH_SIZE),
. ..]
batch_labels = train_labels[offset:(offset + BATCH_SIZE)]
# Этот словарь отображает пакетные данные (в виде массива NumPy)
# на узел в графе, в который они должны быть поданы.
feed_dict = {train_data_node: batch_data,
train_labels_node: batch_labels}
# Выполнить оптимизатор, чтобы обновить веса.
sess.run(optimizer, feed_dict=feed_dict)
Структура этого программного кода подгонки очень похожа на другой код подгон­
ки, который мы встречали ранее в этой книге. На каждом шаге мы создаем словарь
передачи данных в вычислительный граф, а затем запускаем шаг оптимизатора.
Обратите внимание: как и раньше, мы используем мини-пакетную тренировку.
Сверточные нейронные сети
|
157
Оценивание натренированных моделей
Теперь у нас есть натренированная модель. Как оценить точность натренированной
модели? Простой метод— определить метрический показатель ошибки. Как и
в предыдущих главах, для измерения точности мы будем использовать простой
классификационный метрический показатель (пример 6.14).
Пример 6.14. Оценивание ошибки натренированной архитектуры
j
def error_rate(predictions, labels):
"""Вернуть частоту появления ошибок, опираясь на
плотные предсказания и разряженные метки."""
return 100.0 - (
100.0 *
numpy.sum(numpy. argmax(predictions,
1) == labels)
/
predictions.shape[0])
Приведенную выше функцию можно использовать для оценивания ошибки сети по
мере тренировки. Введем дополнительную вспомогательную функцию, которая
вычисляет предсказания для любого набора данных в пакетах (пример 6.15). Необ­
ходимость этой вспомогательной функции обусловлена тем, что наша сеть может
справляться с входными данными только с пакетами фиксированных размеров.
I Пример 6.15. Оценивание одного пакета данных за один раз
L......... .. ........
. __________
.
___
_____ ______
def eval_in_batches(data,
.
.
________________
.
|
__________________________ J
__
sess):
"""Получить предсказания для набора данных путем выполнения
этой функции малыми пакетами."""
size = data.shape[0]
if size < EVAL_BATCH_SIZE:
raise ValueError("размер пакета для eval больше набора данных: %d"
% size)
predictions = numpy.ndarray(shape=(size, NUM_LABELS),
dtype=numpy.float32)
for begin in xrange(0, size, EVAL_BATCH_SIZE) :
end = begin + EVAL_BATCH_SIZE
if end <= size:
predictions[begin:end,
:] = sess.run(
eval—prediction,
feed_dict={eval_data: data[begin:end,
...]})
else:
batch—predictions = sess.run(
eval_prediction,
feed_dict={eval_data: data[-EVAL_BATCH_SIZE: ,
predictions[begin:,
return predictions
158
|
Глава 6
...]})
:] = batch_predictions[begin - size:,
:]
Теперь мы можем добавить небольшой измерительный функционал (во внутренний
цикл тренировки), который периодически оценивает точность модели в контроль­
ном наборе. Тем самым мы можем закончить тренировку, получив оценку точности
на тестовом наборе. В примере 6.16 представлен полный программный код подгон­
ки весов с добавленным измерительным функционалом.
Пример 6.16. Полный программный код тренировки сети
с добавленным измерительным функционалом
# Создать локальный сеанс для выполнения тренировки.
start_time = time. time ()
with tf.Session() as sess:
# Выполнить все инициализаторы, чтобы подготовить
# тренируемые параметры.
tf.global_variables_initializer ().run()
# Пройти тренировочные шаги в цикле.
for step in xrange(int(num_epochs * train_size) // BATCH_SIZE):
# Вычислить сдвиг текущего мини-пакета в данных.
# Примечание: здесь можно было применить более оптимальную
# рандомизацию по всем эпохам.
offset = (step * BATCH_SIZE)
%
(train_size - BATCH_SIZE)
batch_data = train_data[offset:(offset + BATCH_SIZE),
...]
batch_labels = train_labels[offset :(offset + BATCH_SIZE)]
# Этот словарь отображает пакетные данные (в виде массива NumPy)
# на узел в графе, в который они должны быть поданы.
feed_dict = {train_data_node: batch_data,
train_labels_node : batch_labels}
# Выполнить оптимизатор, чтобы обновить веса,
sess.run(optimizer,
feed_dict=feed_dict)
# Напечатать немного дополнительной информации
# после достижения частоты оценивания
if step % EVAL_FREQUENCY = 0:
# fetch some extra nodes' data
1, Ir, predictions = sess.run([loss, learning_rate,
train_prediction],
feed_dict=feed_dict )
elapsed—time = time.time () - start_time
start_time = time. time ()
print('Шаг %d (epoch %.2f), %.lf mc' %
(step, float(step)
* BATCH_SIZE / train_size,
1000 * elapsed_time / EVAL_FREQUENCY))
print('Мини-пакетная потеря: %.3f, скорость заучивания: %.6f
%
(l,lr))
print('Мини-пакетная ошибка: %.lf%%'
% error_rate(predictions, batch_labels))
Сверточные нейронные сети
|
159
print('Контрольная ошибка: %.lf%%* % error_rate(
eval_in_batches(validation_data,
sess),
validation_labels))
sys.stdout.flush()
# В заключении напечатать результат!
test_error = error_rate(eval_in_batches(test_data, sess),
test_labels)
print('Тестовая ошибка: %.lf%%' % test_error)
Задание для читателя
Попробуйте натренировать эту сеть самостоятельно. Вы должны достичь тестовой
ошибки менее 1%!
Резюме
В этой главе мы познакомили вас с основными понятиями проектирования свер­
точных сетей. Эти понятия включают сверточные и редуцирующие слои, которые
составляют основные строительные блоки сверточных сетей. Затем мы обсудили
применения сверточных архитектур, такие как обнаружение, сегментирование и
генерирование изображений. Мы закончили главу углубленным практическим
примером, который продемонстрировал, как тренировать сверточную архитектуру
на наборе данных рукописных цифр MNIST.
В главе 7 мы рассмотрим рекуррентные нейронные сети — еще одну глубоко обу­
чающуюся архитектуру. В отличие от сверточных сетей, предназначенных для
обработки изображений, рекуррентные архитектуры хорошо подходят для обработки
последовательных данных, таких как естественно-языковые наборы данных.
160
|
Глава 6
ГЛАВА 7
Рекуррентные
нейронные сети
К настоящему моменту в этой книге мы познакомили вас с использованием глубо­
кого самообучения для обработки различных видов входных данных. Мы начали с
простой линейной и логистической регрессии на векторах признаков фиксирован­
ных размерностей, а затем продолжили обсуждение полносвязных глубоких сетей.
Эти модели принимают произвольные векторы признаков с фиксированными, пре­
дустановленными размерами и не делают никаких допущений о типе данных, зако­
дированных в этих векторах. С другой стороны, сверточные сети принимают серь­
езные допущения относительно структуры своих данных. Входы в сверточные сети
должны удовлетворять условию локальности, которое позволяет определять ло­
кальное рецептивное поле.
Как использовать сети, которые мы описали ранее, для обработки данных, таких
как предложения естественного языка? Предложения имеют некоторые свойства
локальности (соседние слова обычно связаны), и действительно, для обработки
данных естественно-языковых предложений можно использовать одномерную
сверточную сеть. Вместе с тем, для обработки последовательностей данных боль­
шинство практикующих специалистов прибегают к другому типу архитектуры —
рекуррентной нейронной сети.
Рекуррентные нейронные сети (RNN-сети) изначально спроектированы для того,
чтобы глубокие сети могли обрабатывать последовательности данных. RNN-сети
исходят из того, что поступающие данные принимают форму последовательности
векторов или тензоров. Если мы преобразуем каждое слово в предложении в вектор
(подробнее о том, как это сделать, изложим позже), предложения могут подаваться
в RNN-сети. Точно так же и с видео (рассматриваемое как последовательность изо­
бражений), которое может обрабатываться RNN-сетью аналогичным образом.
К каждой позиции последовательности RNN-сеть применяет произвольное нели­
нейное преобразование. Это нелинейное преобразование является общим для всех
шагов последовательности.
Описание в предыдущем абзаце немного абстрактно, но оказывается чрезвычайно
мощным. В этой главе вы более подробно познакомитесь с тем, каким образом
структурированы RNN-сети, и с тем, как можно реализовать RNN-сеть в библиоте­
ке TensorFlow. Мы также обсудим, каким образом RNN-сети можно использовать
на практике для выполнения таких задач, как получение образцов новых предложе­
ний или генерация текста для таких приложений, как чат-боты.
Практический пример для этой главы тренирует рекуррентную нейронно-сетевую
модель языка на корпусе Penn Treebank, собрании предложений, извлеченных из
161
статей газеты ’’Wall Street Journal”. Этот учебный материал является адаптирован­
ной версией официальной документации TensorFlow по рекуррентным сетям. (Если
вы интересуетесь тем, какие мы внесли изменения, мы рекомендуем обратиться
к оригинальному учебному пособию на веб-сайте TensorFlow.) Как всегда, мы со­
ветуем вам сверяться с программным кодом, размещенным в хранилище GitHub
(https://github.com/matroid/dlwithtf ), связанном с данной книгой.
Обзор рекуррентных архитектур
Рекуррентные архитектуры полезны для моделирования очень сложных наборов
данных, изменяющихся во времени, которые традиционно называются временными
рядами. На рис. 7.1 показано несколько наборов данных временных рядов.
В моделировании временных рядов мы проектируем системы, способные обучаться
эволюционному правилу, которое моделирует эволюционирование рассматривае­
мой системы в зависимости от прошлого. Математически предположим, что на
каждом временном шаге мы получаем точку данных xt. где t — это текущее время.
Тогда методы временных рядов пытаются обучиться некоторой функции f такой,
что
1975
1980
1985
1980
1995
0
20
40
Год
I960
1970
80
60
100
День
1980
1980
0
50
Год
100
150
200
250
День
Рис. 7.1. Несколько наборов данных временных рядов, в моделировании которых
мы можем быть заинтересованы
162
|
Глава?
300
Идея заключается в том, что f хорошо кодирует базисную динамику системы, и ее
заучивание на данных позволит обучающейся системе предсказывать будущее рас­
сматриваемой системы. На практике обучиться функции, которая зависит от всех
прошлых входов,— слишком обременительный процесс, поэтому обучающиеся
системы часто исходят из того, что вся информация о последних точках данных хх,
xt_x может быть закодирована в некий фиксированный вектор h,. Тогда уравне­
ние обновления упрощается в формат
*(+>Л+1 = /(*,> К) •
Обратите внимание, что мы принимаем допущение, что та же самая функция f здесь
применяется для всех временных шагов Л То есть мы исходим из того, что времен­
ные ряды являются стационарными (рис. 7.2). Это допущение нарушается для мно­
гих систем, в особенности для фондового рынка, где сегодняшние правила не обя­
зательно будут соблюдаться завтра.
Рис. 7.2. Математическая модель временного ряда со стационарным эволюционным правилом.
Напомним, что стационарная система — это такая система, чья базисная динамика
не смещается во времени
Какое отношение данное уравнение имеет к рекуррентным нейронным сетям?
Основной ответ вытекает из теоремы об универсальной аппроксимации, которую
мы ввели в главе 4. Функция f может быть сколь угодно сложной, поэтому исполь­
зование полносвязной глубокой сети для заучивания f кажется разумной идеей.
Этот интуитивный подход, по сути, определяет RNN-сеть. Простую рекуррентную
сеть можно рассматривать как полносвязную сеть, которая многократно применя­
ется к каждому временному шагу данных.
На самом деАе рекуррентные нейронные сети действительно становятся интерес­
ными только для сложных многомерных временных рядов. Для более простых сис­
тем существуют классические методы обработки сигналов на основе временных
рядов, которые часто хорошо справляются с моделированием временной динамики.
Однако для сложных систем, таких как речь (см. спектрограммы речи на рис. 7.3),
RNN-сети проявляют себя в полной мере и предлагают возможности, которые дру­
гие методы не могут предложить.
Рекуррентные нейронные сети
|
163
Время
Рис. 7.3. Спектрограмма речи, представляющая частоты, находящиеся в речевом образце
Рекуррентные ячейки
Градиентная неустойчивость
Рекуррентные сети, как правило, с течением времени ухудшают сигнал.
Представьте это, как ослабление сигнала мультипликативным множителем
на каждом временном шаге. Как результат, после 50 шагов сигнал доволь­
но сильно деградирует.
Вследствие этой неустойчивости было сложно тренировать рекуррентные
нейронные сети на более длинных временных рядах. Для борьбы с этой
неустойчивостью был разработан ряд методов, которые мы обсудим в ос­
тавшейся части этого раздела.
Существует несколько концепций простой рекуррентной нейронной сети, которые
в практических приложениях оказались значительно успешнее. В этом разделе мы
кратко рассмотрим некоторые ее варианты.
Долгая краткосрочная память
Часть проблемы, связанной со стандартной рекуррентной ячейкой, заключается
в том, что сигналы из далекого прошлого быстро ослабляются. В результате RNNсети могут не справиться с заучиванием моделей сложных зависимостей. Эта
неспособность становится особенно заметной в таких приложениях, как моделиро­
вание языка, где слова могут иметь сложные зависимости от более ранних фраз.
Одно из возможных решений этой проблемы заключается в том, чтобы позволить
состояниям из прошлого проходить без изменений. Архитектура долгой кратко­
срочной памяти (long short-term memory, LSTM) предлагает механизм, позволяю­
щий прошлому состоянию проходить в настоящее с минимальными изменениями.
В эмпирическом плане использование ’’ячейки” LSTM (показанной на диаграм­
ме 7.4), судя по всему, предлагает превосходную результативность самообучения
164
|
Глава?
=cr(w- [Vi>
Лг=(1-г/>Лг-1+2,-Л,
Рис. 7.4. Ячейка долгой краткосрочной памяти (LSTM). LSTM-ячейки демонстрируют результативность,
превосходящую стандартные рекуррентные нейронные сети в удержании долгосрочных зависимостей
во входных данных. В результате LSTM-ячейки нередко становятся предпочтительнее
для сложных последовательных данных, таких как естественный язык
по сравнению с простыми рекуррентными нейронными сетями, использующими
полносвязные слои.
Так много уравнений!
Уравнения LSTM содержат много сложных членов. Если вам интересно
строгое математическое доказательство, лежащее в основе LSTM, которое
здесь представлено на интуитивном уровне, мы рекомендуем вам воору­
житься карандашом и бумагой и попробовать взять производные LSTMячейки.
Вместе с тем, что касается других читателей, которые в первую очередь
заинтересованы в использовании рекуррентных архитектур для решения
практических задач, мы считаем, что им не обязательно углубляться в де­
тали того, как работают LSTM. Лучше всего, если вы просто закрепите
на уровне интуиции, что прошлое состояние разрешается пропускать без
изменений, и углубленно проработаете пример программного кода для
этой главы.
Оптимизация рекуррентных сетей
В отличие от полносвязных сетей или сверточных сетей, LSTM преду­
сматривают некоторые сложные математические операции и операции по­
тока управления. В результате задача крупномасштабной тренировки
больших рекуррентных сетей оказалась сложной, даже с современным
GPU-оборудованием.
Значительные усилия были вложены в оптимизацию реализаций RNNсетей с целью ускорить их выполнение на GPU-оборудовании. В частно­
сти, компания NVIDIA включила RNN-сети в свою библиотеку CuDNN,
которая предоставляет программный код, специально оптимизированный
для тренировки глубоких сетей на GPU. К счастью для пользователей
TensorFlow, интеграция с библиотеками, такими как CuDNN, выполняется
в самой библиотеке TensorFlow, поэтому вам не нужно слишком беспоко­
Рекуррентные нейронные сети
|
165
иться об оптимизации программного кода (если, конечно, вы не работаете
с крупномасштабными наборами данных). Мы обсудим аппаратные по­
требности для глубоких нейронных сетей более детально в главе 9.
Вентильные рекуррентные блоки
Сложность, как концептуальная, так и вычислительная, LSTM-ячеек побудила ряд
исследователей попытаться упростить уравнения LSTM, сохранив при этом при­
рост результативности и моделирующие возможности исходных уравнений.
Существует несколько претендентов на замену LSTM, но одним из лидеров являет­
ся вентильный рекуррентный блок (gated recurrent units, GRU)1, показанный на
рис. 7.5. GRU-блок удаляет один из подкомпонентов LSTM, но эмпирически, судя
по всему, достигает результативности, аналогичной LSTM. GRU-блоки могут быть
подходящей заменой LSTM-ячеек в проектах моделирования последовательностей.
а
б
Рис. 7.5. Долгая краткосрочная память (а) и вентильный рекуррентный блок (6).
GRU-блоки сохраняют многие достоинства LSTM с более низкими вычислительными затратами
Применение рекуррентных моделей
Хотя рекуррентные нейронные сети являются полезными инструментами для моде­
лирования наборов данных временных рядов, существует множество других при­
ложений рекуррентных сетей. К ним относятся такие приложения, как моделирова­
ние естественного языка, машинный перевод, химический ретросинтез и произ­
вольные вычисления с помощью нейронных машин Тьюринга. В этом разделе мы
предлагаем краткий обзор некоторых из этих захватывающих приложений.
1 Вентильный рекуррентный блок (gated recurrent unit) — это вентильный механизм в рекуррентных
нейронных сетях, введенный в 2014 г. Было показано, что его результативность по моделированию
полифонической музыки и моделированию речевого сигнала аналогична результативности с долгой
краткосрочной памятью. Вместе с тем было показано, что GRU-блоки демонстрируют более высокую
результативность на меньших наборах данных. — Прим. пер.
166
|
Глава?
Получение образцов из рекуррентных сетей
До сих пор мы знакомили вас с тем, как рекуррентные сети могут учиться модели­
ровать временную эволюцию последовательностей данных. Логично предполо­
жить, что, если вы понимаете эволюционное правило для множества последова­
тельностей, вы должны уметь получать из распределения тренировочных последо­
вательностей образцы новых последовательностей. И действительно, оказывается,
что из натренированных моделей могут быть получены хорошие образцы последо­
вательностей. Самое интересное применение RNN-сетей на сегодняшний момент
находится в области моделирования языка. Способность генерировать реалистич­
ные предложения является очень полезным инструментом, который используется,
например, системами автозаполнения и чат-ботами.
Почему для последовательностей не используются GAN-сети?
В главе 6 мы обсудили проблему генерирования новых изображений. Мы
обсудили модели, в частности вариационные автокодировщики, которые
производят только размытые изображения, и познакомились с технологией
генеративно-состязательных сетей, которая демонстрирует способность
производить четкие изображения. Однако вопрос остается открытым: если
GAN-сети востребованы для получения хороших образцов изображений,
почему бы нам не воспользоваться ими для хороших естественно­
языковых предложений?
Оказывается, современные генеративно-состязательные модели при по­
пытке получения из них образцов последовательностей показывают сла­
бые результаты. Причина этого явления не совсем ясна. Теоретическое
понимание GAN-сетей остается очень низким (даже по стандартам теории
глубокого самообучения), но что-то связанное с выявлением теоретико­
игрового равновесия, судя по всему, работает для последовательностей
хуже, чем для изображений.
Модели Seq2seq
Модели ’’последовательность в последовательность ” (sequence-to-sequence, seq2seq) —
это мощные инструменты, позволяющие моделям преобразовывать одну последо­
вательность в другую. Основная идея модели ’’последовательность в последова­
тельность” заключается в использовании кодирующей рекуррентной сети, которая
встраивает (включает) входные последовательности в векторные пространства,
и декодирующей сети, которая позволяет получать образцы выходных последова­
тельностей в соответствии с описанием в предыдущих предложениях. На рис. 7.6
показана модель seq2seq.
Самое интересное начинается, если учесть, что слои кодировщика и декодировщика
сами по себе могут быть глубокими. (RNN-слои могут быть естественным образом
уложены в ярусы.) Система нейронного машинного перевода Google (Google neural
machine translation, GNMT) имеет многоярусные кодирующие и декодирующие
слои. В результате этого мощного репрезентативного потенциала она способна
Рекуррентные нейронные сети
|
167
Кодировщик
Декодировщик
Повторить
Входящая электронная почта
Рис. 7.6. Модели "последовательность-в-последовательность" являются мощными инструментами,
которые могут обучаться преобразованиям последовательностей. Они применяются в машинном
переводе (например, преобразовании последовательности английских слов на севернокитайский язык)
и химическом ретросинтезе (преобразовании последовательности химических продуктов
в последовательность реагентов)
Рис. 7.7. Архитектура нейронного машинного перевода Google (GNMT)
представляет собой глубокую модель seq2seq, которая учится выполнять машинный перевод
выполнять современные переводы далеко за пределами возможностей своих бли­
жайших конкурентов. На рис. 7.7 показана архитектура GNMT.
Несмотря на то что до сих пор мы в основном обсуждали приложения для обработ­
ки естественного языка, архитектура seq2seq имеет целый ряд приложений в других
областях. Один из авторов книги использовал архитектуры seq2seq для выполнения
химического ретросинтеза — процесса деконструирования молекул на более про­
стые составляющие. На рис. 7.8 показан этот процесс.
168
|
Глава?
Н3С-ОН + ОН’
Н3С-ОН
Рис. 7.8. Модель seq2seq для химического ретросинтеза преобразует
последовательность химических продуктов в последовательность химических реагентов
Нейронные машины Тьюринга
Мечта об обучающихся машинах заключалась в том, чтобы продвинуться дальше
по стеку абстракций: перейти от способности обучаться коротким механизмам
сопоставления с образцом к способности обучаться выполнению произвольных вы­
числений. Нейронная машина Тьюринга является мощным шагом в этой эволюции.
Машина Тьюринга была конструктивным вкладом в математическую теорию вы­
числений. Это была первая математическая модель машины, способная выполнять
любые вычисления. Машина Тьюринга содержит ’’ленту”, которая обеспечивает
память для выполняемых вычислений. Вторая часть машины представляет собой
’’головку записи-чтения”, которая на отдельных ячейках ленты осуществляет пре­
образования. Оригинальная идея машины Тьюринга заключалась в том, что для
выполнения сколь угодно сложных вычислений сама ’’головка” не обязательно
должна быть очень сложной.
Нейронная машина Тьюринга (НМТ)— это очень умная попытка преобразовать
саму машину Тьюринга в нейронную сеть. Математический прием в этой трансму­
тации заключается в том, чтобы превратить дискретные действия в мягкие непре­
рывные функции (этот остроумный прием неоднократно появляется в глубоком
самообучении, поэтому возьмите его на заметку!)
Головка машины Тьюринга весьма схожа с RNN-клеткой! Как результат, НМТмашина может быть натренирована в сквозном порядке, чтобы обучиться произво­
дить любые вычисления, по крайней мере в принципе (рис. 7.9). На практике суще­
ствуют серьезные ограничения на диапазон вычислений, которые НМТ-машина
может выполнять. Неустойчивость градиентного потока (как всегда) ограничивает
то, чему она может обучиться. Для разработки потомков НМТ-машины, способных
обучаться более полезным функциям, потребуются дополнительные исследования
и эксперименты.
Рекуррентные нейронные сети
|
169
ь
ь
а
а
а
а
... Лента входа-выхода
Конечное управление
Рис. 7.9. Нейронная машина Тьюринга (HMT) — это способная обучаться версия машины Тьюринга.
Она содержит ленту, на которой может сохранять результаты промежуточных вычислений.
Хотя машины НМТ имеют много практических ограничений, вполне возможно,
что их интеллектуальные потомки будут способны обучаться мощным алгоритмам
Полнота по Тьюрингу
Полнота по Тьюрингу в информатике является важным понятием. Язык
программирования считается полным по Тьюрингу, если он способен вы­
полнять любые вычисления, которые могут быть выполнены машиной
Тьюринга. Машина Тьюринга как таковая была изобретена, чтобы обеспе­
чить математическую модель того, что значит для функции быть ’’вычис­
лимой”. Эта машина предоставляет возможность читать, писать и хранить
в памяти различные инструкции, т. е. абстрактные примитивы, которые
лежат в основе всех вычислительных машин.
Со временем большой объем работ показал, что машина Тьюринга моде­
лирует диапазон вычислений, близких к выполняемым в физическом мире.
В первом приближении, если можно показать, что машина Тьюринга
не способна выполнять вычисления, то и любое другое вычислительное
устройство не способно это делать. С другой стороны, если можно пока­
зать, что вычислительная система может выполнять основные операции
машины Тьюринга, тогда она является ’’полной по Тьюрингу” и способна
выполнять в принципе любые вычисления, которые могут быть выполнены
вообще. Ряд удивительных систем являются полными по Тьюрингу. Мы
рекомендуем вам подробнее почитать литературу по этой теме, если вы
интересуетесь.
Рекуррентные сети являются полными по Тьюрингу
Пожалуй, не удивительно, что HMT-машины способны выполнять любые
вычисления, которые может выполнять машина Тьюринга и, следователь­
но, являются полными по Тьюрингу. Однако менее известный факт заклю­
чается в том, что классические рекуррентные нейронные сети сами по себе
170
|
Глава?
являются полными! Иными словами, рекуррентная нейронная сеть способ­
на обучаться выполнять произвольные вычисления.
Основная идея заключается в том, что оператор перехода может научиться
выполнять основные операции чтения, записи и хранения. Развертывание
рекуррентной сети во времени позволяет выполнять сложные вычисления.
В каком-то смысле этот факт не должен быть слишком удивительным.
Теорема об универсальной аппроксимации уже демонстрирует, что полно­
связные сети способны обучаться произвольным функциям. Сцепление
произвольных функций во времени приводит к произвольным вычислени­
ям. (Правда, технические детали, необходимые для официального под­
тверждения этого утверждения, архисложны.)
Работа с рекуррентными нейронными сетями
на практике
В этом разделе вы познакомитесь с использованием рекуррентной нейронной сети
для моделирования языка на корпусе Penn Treebank — естественно-языковом набо­
ре данных, созданном из статей газеты "Wall Street Journal”. Мы представим при­
митивы TensorFlow, необходимые для выполнения этого моделирования, а также
проведем вас по шагам работы с данными и их предобработки, необходимым для
подготовки данных к тренировке. Мы рекомендуем вам сверяться с программным
кодом в хранилище GitHub (https://github.com/matroid/dlwithtf), связанном с этой
книгой, и попробовать его выполнить самостоятельно.
Обработка корпуса Penn Treebank
Корпус Репп Treebank содержит коллекцию статей газеты "Wall Street Journal", со­
стоящую из миллиона слов. Этот корпус может использоваться для моделирования
на уровне символов или слов (задачи предсказания следующего символа или слова
в предложении при наличии предыдущих). Эффективность моделей измеряется
с помощью перплексии натренированных моделей (подробнее об этом метрическом
показатели позже).
Корпус Penn Treebank состоит из предложений. Каким образом преобразовать
предложения в форму, которая может быть подана в машинно-обучающиеся систе­
мы, такие как рекуррентные языковые модели? Напомним, что машинно-обучаю­
щиеся модели принимают в качестве входных данных тензоры (а рекуррентные
модели принимают последовательности тензоров). Следовательно, нам нужно пре­
образовать слова в тензоры, пригодные для машинного самообучения.
Самый простой способ преобразования слов в векторы — использовать кодирова­
ние "с одним активным состоянием"2. Предположим, что наш языковой набор дан­
2 Словосочетание кодирование с одним активным состоянием (англ, one-hot encoding) пришло из тер­
минологии цифровых интегральных микросхем, где оно описывает конфигурацию микросхемы,
в которой допускается, чтобы только один бит был положительным (активным). В отечественной спе­
циализированной литературе для данного типа кодировщика нередко используется альтернативный
термин — прямой унитарный кодировщик. — Прим. пер.
Рекуррентные нейронные сети
|
171
ных использует словарь из |К| слов. Тогда в этом кодировании каждое слово пре­
образуется в вектор формы (|К|). Все элементы этого вектора равняются нулю,
кроме одного элемента, в индексе, соответствующем текущему слову. Пример
такого встраивания см. на рис. 7.10.
Кодирование с одним активным состоянием
v = {роза, куст, школа, монета}
v(posa)
у(куст)
v(школа)
v(монета)
=
=
-
[1,
[0,
[0,
[0,
0/
1,
0,
0,
0/
0/
1,
о,
0]
0]
0]
1]
Рис. 7.10. Кодирование с одним активным состоянием преобразует слова в векторы
только с одним отличным от нуля элементом (которому, как правило, назначается 1).
Различные индексы в векторе уникально представляют слова из языкового корпуса
Также можно использовать более сложные встраивания. Основная идея аналогична
идее кодирования с одним активным состоянием. Каждое слово ассоциируется
с уникальным вектором. Однако ключевое различие заключается в том, что в дан­
ной системе кодирования можно обучиться вектору непосредственно из данных,
получив для указанного слова его "словарное встраивание"3, которое имеет содер­
жательный смысл для текущего набора данных. Мы покажем вам, как обучаться
словарным встраиваниям позже в этой главе.
Для того чтобы обработать данные корпуса Penn Treebank, нам нужно найти сло­
варь слов, используемых в корпусе, а затем преобразовать каждое слово в связан­
ный с ним словарный вектор. Затем мы покажем, как передавать обработанные
данные в модель TensorFlow.
Ограничения корпуса Репп Treebank
Корпус Penn Treebank является очень полезным набором данных для моде­
лирования языка, но он больше не представляет сложной проблемы для
современных языковых моделей; исследователи уже научились плотно
подгонять модели под характерные особенности этой коллекции. В самых
современных исследованиях используются более крупные наборы данных,
3 Встраивание, или включение, слов (word embedding) — это собирательный термин для набора мето­
дов моделирования языка и заучивания признаков в области обработки естественного языка, где сло­
вам или фразам словаря ставятся в соответствие векторы реальных чисел. Концептуально оно связано
с математическим включением из пространства с одной размерностью в расчете на слово в непрерыв­
ное векторное пространство с гораздо более высокой размерностью. См. https:ZZen.wikipeclia.org/wiki/
Word_embedcling, а также https://ru.wikipedia«org/wiki/BJioMeHHe. На веб-сайте платформы TensorFlow
имеется хорошее руководство по данной теме (https://www.tensorflow.org/tutorials/word2vec) с опи­
санием построения модели, получающей векторные представления слов. — Прим. пер.
172
|
Глава 7
такие как эталон модели языка с корпусом из одного миллиарда слов4. Од­
нако для наших исследовательских целей вполне будет достаточно корпуса
Penn Treebank.
Программный код для предобработки
Фрагмент кода в примере 7.1 считывает сырые файлы, связанные с корпусом Репп
Treebank. Данный корпус хранится в формате одного предложения на строку.
Небольшая обработка строк выполняется средствами Python для замены маркеров
новой строки ”\п" на фиксированный маркер ”<eos>" и затем разбивки файла на
список лексем.
■ Пример 7.1. Функция считывания сырого файла корпуса Репп Treebank
|
def _read_words(filename):
with tf.gfile.GFile(filename,
”r”) as f:
if sys.version_info[0] >= 3:
return f.read().replace("\n”,
"<eos>").split()
else:
return f.readO.decode("utf-8").replace("\n”,
"<eos>").split()
Определив функцию read words о, мы можем построить словарь, связанный с дан­
ным файлом, используя функцию _build_vocab(), определенную в примере 7.2. Мы
просто читаем слова в файле и подсчитываем количество уникальных слов в нем,
используя встроенную в Python библиотеку collections. Для удобства мы конст­
руируем объект-словарь, отображающий слова на их уникальные целочисленные
идентификаторы (их позиции в словаре). Связывая все это вместе, функция
file to word ids о преобразует файл в список идентификаторов слов (пример 7.3).
i Пример 7.2. Функция построения словаря, состоящего из всех слов
в заданном файле
def _build_vocab(filename) :
data = _read_words(filename)
counter = collections.Counter(data)
count_pairs = sorted(counter.items(),
key=lambda x:
(-x[l], x[0]))
words, _ = list(zip(*count_pairs))
word_to_id = diet(zip(words, range(len(words))))
return word_to_id
4 Cm. http://www.statmt.org/lm-benchmark/. — Прим. nep.
Рекуррентные нейронные сети
|
173
j Пример 7.3. Функция преобразования слова из файла в числовые идентификаторы
|
def _file_to_word_ids(filename, word_to_id):
data = _read_words(filename)
return [word_to_id[word] for word in data if word in word_to_id]
Располагая этими сервисными функциями, мы можем обработать корпус Репп
Treebank функцией ptb raw data () (пример 7.4). Обратите внимание, что трениро­
вочный, контрольный и тестовый наборы данных заданы заранее, поэтому нам
остается только прочитать каждый файл в список уникальных индексов.
Пример 7.4. Функция загрузки данных корпуса Penn Treebank
из заданного местоположения
def ptb_raw_data(data_path=None):
""’’Загрузить сырые данные РТВ из каталога данных "data_path".
Читает текстовый файл РТВ, конвертирует строковые значения
в целочисленные идентификаторы и выполняет мини-пакетирование
входных данных.
Набор данных РТВ взят с веб-страницы Томаса Миколова:
http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz
Аргументы:
data_path: строковый путь к каталогу, куда был извлечен архив
simple-examples.tgz.
Возвращаемые значения:
tuple (train_data, valid_data, test_data, vocabulary),
где каждый объект данных может быть передан в PTBIterator.
VI VI VI
"ptb.train.txt")
train_path == os.path.join(data_path,
valid_path = os.path.join(data_path, "ptb.valid.txt")
test_path = os .path, join (data_path,
’’ptb.test.txt")
word__to__id = _build_vocab (train_path)
train_data = _file_to_word_ids(train_path, word__to_id)
valid_data =* _file_to_word_ids(valid_path, word_to_id)
test_data = _file_to_word_ids(test_path, word_to_id)
vocabulary = len(word_to_id)
return train_data, valid_data, test_data, vocabulary
Объекты
tf .GFile И tf .Flags
TensorFlow — это крупный проект, в котором содержится много фрагмен­
тов и компонентов. Хотя подавляющая часть библиотеки посвящена
машинному самообучению, также имеется значительная часть, которая
относится к загрузке и массированию данных. Некоторые из этих функций
174
|
Глава?
предоставляют полезные возможности, которых нет нигде. Однако другие
части загрузочного функционала менее полезны.
Обертка tf.GFile для операций ввода-вывода без блокировки потока и
маршрутизатор импорта tf.Flags предоставляют функционал, который
более или менее идентичен стандартной для Python обработке файлов
и встроенной библиотеке argparse. Происхождение этих инструментов
историческое. В Google в соответствии с внутренними стандартами про­
граммирования требуются собственные обработчики файлов и обработка
флагов. Однако для остальных из нас по возможности лучше придержи­
ваться стандартных инструментов Python для удобочитаемости и стабиль­
ности.
Загрузка данных в TensorFlow
В этом разделе мы рассмотрим программный код, необходимый для загрузки на­
ших обработанных индексов в TensorFlow. Для этого мы познакомим вас с новым
механизмом TensorFlow. До сих пор мы использовали специальные словари для
передачи данных в TensorFlow. В то время как словари передачи данных хороши
для небольших игрушечных наборов данных, они часто не являются удачным вари­
антом для больших наборов данных, поскольку Python добавляет весомые наклад­
ные расходы, связанные с упаковкой и распаковкой словарей. Для получения более
производительного программного кода лучше использовать очереди TensorFlow.
Объект tf.Queue позволяет загружать данные асинхронно. Это дает возможность
отделить вычислительный поток GPU от потока предобработки данных, ограни­
ченного возможностями CPU. Это разъединение особенно полезно для больших
наборов данных, где мы хотим оставить GPU максимально активным.
Объекты tf .Queue можно подавать в заполнители TensorFlow для тренировки моде­
лей и достижения большей производительности. Далее в этой главе мы продемон­
стрируем, как это делается.
Функция ptb_producer, представленная в примере 7.5, преобразует сырые списки
индексов в очереди tf .Queue, которые могут передавать данные в вычислительный
граф TensorFlow. Давайте начнем со знакомства с некоторыми вычислительными
примитивами, которые мы будем использовать. Функция tf.train.range-inputproducer () — это удобная операция, которая создает очередь tf.Queue из входного
тензора. Метод tf. Queue, dequeue о извлекает тензор из очереди для тренировки.
Функция tf .strided_slice() извлекает срез этого тензора, который соответствует
данным текущего мини-пакета.
Пример 7.5. Функция загрузки данных корпуса Penn Treebank
из заданного местоположения
def ptb_producer(raw_data, batch_size, num_steps, name=None):
"""Выполнить итеративный обход сырых данных РТВ.
Этот программный код нарезает сырые данные raw_data на пакеты
примеров и возвращает тензоры, взятые из этих пакетов.
Рекуррентные нейронные сети
|
175
Аргументы:
raw_data - одна часть сырых данных, получаемых из ptb_raw_data.
batch_size - целочисленный размер пакета.
num_steps - целочисленное количество разверток.
паше - имя этой операции (необязательный).
Возвращаемые значения:
Пара тензоров, каждый с формой [batch_size, num_steps].
Второй элемент кортежа - это те же данные, сдвинутые во времени
вправо на одну позицию.
Вызов исключений:
tf.errors.InvalidArgumentError: если размер пакета batch_size или
количество шагов num_steps слишком большое.
tt tt tt
with tf.name_scope (name,
"PTBProducer",
[raw_data, batch_size, num_steps]):
raw_data = tf .convert_to__tensor (raw_data, name="raw_data",
dtype=tf.int32)
data_len = tf.size(raw_data)
batch_len = data_len // batch_size
data = tf.reshape(raw_data[0 : batch_size * batch_len],
[batch_size, batch_len])
epoch_size = (batch_len - 1) // num_steps
assertion = tf.assert_positive(
epoch_size,
message="epoch_size == 0, уменьшить batch_size или num_steps")
with tf.control-dependencies ([assertion]):
epoch_size = tf.identity(epoch_size, name=’’epoch_size")
i = tf.train.range_input_producer(epoch_size,
shuffle=False).dequeue()
x = tf.strided_slice(data,
[0, i * num_steps],
[batch_size,
(i + 1) * num_steps])
x. set_shape([batch_size, num_steps])
у = tf.strided_slice(data,
[0, i * num_steps + 1],
[batch_size,
(i + 1) * num_steps + 1])
y. set_shape([batch_size, num_steps])
return x, у
Модуль tf .data
TensorFlow (начиная с версии 1.4) поддерживает новый модуль tf.data
с новым классом tf.data.Dataset, который обеспечивает специальный API
для представления потоков данных. Вполне вероятно, что tf.data в конеч­
176
|
Глава?
ном итоге заменит очереди в качестве предпочтительного механизма вво­
да, тем более что они имеют хорошо продуманный функциональный API.
На момент написания книги модуль tf.data был только что выпущен и
оставался относительно сырым по сравнению с другими частями API,
поэтому для наших примеров мы решили придерживаться очередей.
Вместе с тем мы рекомендуем вам обязательно ознакомиться с модулем
tf.data самостоятельно.
Базовая рекуррентная архитектура
Мы будем использовать LSTM-ячейку для моделирования данных корпуса Репп
Treebank, поскольку LSTM часто предлагают превосходную результативность для
задач моделирования языка. Функция tf.contrib.rnn.BasicLSTMCeiiо реализует для
нас базовую LSTM-ячейку, поэтому нет необходимости создавать подобный код
самостоятельно (пример 7.6).
I Пример 7.6. Функция обертывания LSTM-ячейки из tf .contrib
L________________.
______________
___________________________ ______ ..
I
_ I
._ .
def lstm_cell():
return tf.contrib.rnn.BasicLSTMCell (
size, forget_bias=0 .0, state_is_tuple=True,
reuse=tf.get_variable_scope().reuse)
Нормально, если используется программный код
из TensorFlow contrib?
Обратите внимание, что реализация LSTM, которую мы используем, взята
из модуля tf.contrib. Допустимо ли использовать программный код из
tf.contrib для промышленных проектов? Единого мнения на этот счет
пока нет. Из нашего личного опыта программный код в tf .contrib более
шаткий, чем код в основной библиотеке TensorFlow, но вместе с тем он
обычно довольно устойчивый. Нередко многие полезные библиотеки и
сервисные функции доступны только в рамках tf .contrib. Мы рекоменду­
ем использовать фрагменты из tf.contrib по мере необходимости, но
возьмите за привычку заменять используемые вами фрагменты их эквива­
лентами из TensorFlow, если такие появляются в основной библиотеке.
Фрагмент кода в примере 7.7 предписывает TensorFlow обучиться словарному
встраиванию для каждого слова в нашем словаре. Ключевой функцией для нас
является функция tf .nn.embedding_lookup(), которая позволяет выполнять правиль­
ную операцию тензорного поиска. Обратите внимание, что нам нужно определить
матрицу встраиваний embedding вручную как переменную TensorFlow.
^Пример 7.7. Обучение словарному встраиванию для каждого слова в словаре
with tf.device("/cpu:0"):
embedding = tf.get_variable(
"embedding",
[vocab_size, size], dtype=tf.float32)
inputs = tf.nn.embedding_lookup(embedding,
input_.input_data)
Рекуррентные нейронные сети
|
177
Имея на руках словарные векторы, нам остается только применить LSTM-ячейку
(используя функцию istm ceiio) к каждому словарному вектору в последователь­
ности. Для этого мы применяем цикл Python for, в котором конструируется необ­
ходимый набор вызовов cell о. Здесь имеется всего один хитрый прием: нам нужно
убедиться, что в каждом временном интервале мы используем одни и те же пере­
менные, т. к. LSTM-ячейка должна выполнять одну и ту же операцию в каждом
временном интервале. К счастью, метод reuse variables о для областей перемен­
ных позволяет сделать это без особых усилий (пример 7.8).
Пример 7.8. Применение LSTM-ячейки к каждому словарному вектору
во входной последовательности
outputs = []
state = self._initial_state
with tf.variable_scope("RNN"):
for time_step in range(num_steps) :
if time_step > 0: tf.get_variable_scope().reuse_variables()
(cell_output, state) = cell(inputs[:, time_step,
:], state)
outputs.append(cell_output)
Теперь остается только определить связанную с графом потерю, для того чтобы
этот граф натренировать. И удобно, что библиотека TensorFlow в tf .contrib пред­
лагает потерю на тренировочных данных языковых моделей. Нужно лишь вызвать
tf.contrib.seq2seq.sequence loss (пример 7.9). За кадром эта потеря оказывается
формой перплексии.
Пример 7.9. Добавление потери для последовательности
# Использовать функцию потери для последовательности из contrib
# и усреднить по всем пакетам.
loss = tf.contrib.seq2seq.sequence_loss(
logits,
input_.targets,
tf.ones([batch_size, num_steps], dtype=tf.float32),
average_across—timesteps=False,
average_ across_batch=True
# Обновить переменные стоимости.
self._cost = cost = tf.reduce_sum(loss)
Перплексия
Метрический показатель перплексии часто используется для задач модели­
рования языка. Этот показатель представляет собой вариант бинарной пе­
рекрестной энтропии, который используется для измерения того, насколь­
ко близким является заученное распределение данных к истинному рас­
пределению. Эмпирически показатель перплексии оказался полезным для
178
|
Глава?
многих задач моделирования языка, и мы используем его здесь в этом
качестве (поскольку sequence loss как раз реализует перплексию, приспо­
собленную для последовательностей, находящихся внутри).
Затем мы можем натренировать этот граф с помощью стандартного метода гради­
ентного спуска. Мы опустили некоторые запутанные детали базового программно­
го кода, но предлагаем вам обратиться к GitHub, если вам интересно. Оценка каче­
ства натренированной модели также оказывается простой, поскольку перплексия
используется как потеря на тренировочных данных и как метрический оценочный
показатель. В результате, чтобы отслеживать процес тренировки модели, мы можем
просто показывать self. cost. Рекомендуем вам натренировать модель самостоя­
тельно!
Задание для читателя
Попробуйте снизить перплексию на данных корпуса Penn Treebank, эксперимен­
тируя с различными модельными архитектурами. Обратите внимание, что без GPU
эти эксперименты могут занимать много времени.
Резюме
Эта глава познакомила вас с рекуррентными нейронными сетями (RNN-сетями) —
мощной архитектурой, обучающейся на последовательных данных. RNN-сети спо­
собны обучаться лежащему в основе эволюционному правилу, которое управляет
последовательностью данных. Хотя RNN-сети можно использовать для моделиро­
вания простых временных рядов, они наиболее эффективны при моделировании
сложных последовательных данных, таких как речь и естественный язык.
Мы познакомили вас с рядом вариантов RNN-сетей, таких как LSTM-ячейки и
GRU-блоки, которые лучше работают с данными со сложными долгосрочными
взаимодействиями, а также сделали краткое отступление, чтобы обсудить захваты­
вающую перспективу нейронных машин Тьюринга. Мы закончили главу углублен­
ным практическим примером, в котором LSTM-ячейки были применены для моде­
лирования корпуса Penn Treebank.
В главе 8 мы познакомим вас с мощным методом самообучения с максимизацией
подкрепления, который позволяет глубоким системам обучаться играть в игры.
Рекуррентные нейронные сети
|
179
ГЛАВА 8
Самообучение
с максимизацией подкрепления
Методы самообучения, которые мы рассмотрели, относятся к категориям контро­
лируемого или неконтролируемого самообучения. В обоих случаях поставленная
задача требует от аналитика данных проектировать глубокую архитектуру, которая
манипулирует и обрабатывает входные данные, и соединять выход из архитектуры
с функцией потери, которая подходит для решения поставленной задачи. Такой
математический каркас широко применим, но не все приложения безупречно укла­
дываются в этот стиль мышления. Давайте рассмотрим задачу тренировки машинно-обучающейся модели для того, чтобы она победила в шахматной игре. Кажется
разумным рассматривать шахматную доску как пространственный вход, используя
сверточную сеть, но с какой функцией потери это будет сопряжено? Ни одна из
наших стандартных функций потерь, таких как перекрестная энтропия или Z,2 -по­
теря, не применима.
Самообучение с максимизацией подкрепления обеспечивает математический кар­
кас, который хорошо подходит для решения игровых задач. Центральная математи­
ческая концепция — это марковский процесс принятия решений, инструмент для
моделирования агентов ИИ, которые взаимодействуют со средами, предлагающими
вознаграждение по завершении определенных действий. Этот каркас доказал свою
гибкость и универсальность, и в последние годы он нашел ряд применений. Стоит
отметить, что самообучение с максимизацией подкрепления как область исследо­
вания является довольно зрелой и существует в узнаваемой форме с 1970-х годов.
Тем не менее до недавнего времени большинство обучающихся систем с максими­
зацией подкрепления были способны решать только игрушечные задачи. Послед­
ние исследования показали, что эти ограничения, судя по всему, существовали по
причине отсутствия сложных механизмов приема данных; для многих игр или ро­
ботизированных сред часто было недостаточно вручную конструируемых призна­
ков. Судя по всему, извлечения глубоких представлений, натренированных в
сквозном порядке на современном оборудовании, в последние годы пробились че­
рез барьеры более ранних обучающихся систем с максимизацией подкрепления и
достигли значительных результатов.
Пожалуй, первый прорыв в глубоком самообучении с максимизацией подкрепле­
ния был осуществлен на аркадных играх ATARI. Аркадные игры ATARI традици­
онно были представлены в игровых залах и предлагали пользователям простые
игры, которые обычно не требуют выработки сложных стратегий, но могут потре­
бовать хороших рефлексов. На рис. 8.1 показан снимок экрана из популярной игры
181
ATARI Breakout. В последние годы, благодаря разработке хорошего программного
обеспечения для эмуляции ATARI, игры ATARI стали тестовой площадкой для
алгоритмов игрового процесса. На первом этапе обучающимся алгоритмам с макси­
мизацией подкрепления, применительно к ATARI, не удавалось достичь превос­
ходных результатов; техническое требование, которое заключалось в том, чтобы
алгоритм понимал состояние видеоигры, провалило большинство попыток. Однако
по мере взросления сверточных сетей исследователи в компании DeepMind поняли,
что сверточные сети могут сочетаться с существующими методами самообучения
с максимизацией подкрепления и тренироваться в сквозном порядке.
Рис. 8.1. Снимок экрана аркадной игры ATARI Breakout. Игроки должны использовать
ракетку внизу экрана, чтобы отбивать мяч, который разбивает плитки вверху экрана
Получившаяся в результате система достигла превосходных результатов и научи­
лась играть во многие игры ATARI (в особенности те, которые зависят от быстрых
рефлексов) на уровне сверхчеловеческих стандартов. На рис. 8.2 показаны баллы
ATARI, полученные алгоритмом DQN компании DeepMind в разных играх. Этот
прорыв стимулировал огромный рост в области глубокого самообучения с макси­
мизацией подкрепления и вдохновил легионы исследователей заняться разведкой
потенциала соответствующих методов. В то же время результаты компании
DeepMind на играх ATARI показали, что методы самообучения с максимизацией
подкрепления способны решать задачи, зависящие только от краткосрочных ходов.
Эти результаты не продемонстрировали, что глубоко обучающиеся системы
способны выигрывать игры, которые требуют большего стратегического планиро­
вания.
182
|
Глава 8
Рис. 8.2. Результаты работы обучающегося алгоритма с максимизацией подкрепления DQN
компании DeepMind на различных играх ATARI. 100% — это балл сильного игрока-человека.
Обратите внимание, что DQN достигает сверхчеловеческой производительности во многих играх,
но довольно слаб в других
Компьютерное го
В 1994 г. компания IBM представила систему Deep Blue, которая позже
смогла победить Гарри Каспарова в широко разрекламированном шахмат­
ном матче. Для того чтобы играть в шахматы на гроссмейстерском уровне,
эта система опиралась на вычисление методом грубой силы, которое рас­
ширяло дерево возможных шахматных ходов (с некоторой помощью со
стороны ручных шахматных эвристик).
Самообучение с максимизацией подкрепления
|
183
Специалисты в области информатики попытались применить аналогичные
методы к другим играм, таким как го. К несчастью для ранних экспери­
ментаторов, игровая доска го с разлиновкой 19x19 значительно больше
шахматной доски 8x8 . В результате, деревья возможных ходов показыва­
ют взрывной рост гораздо быстрее, чем для шахмат, а простые, не тре­
бующие сложных расчетов вычисления показали, что закон Мура займет
очень много времени, прежде чем позволит выработать решение методом
грубой силы в стиле Deep Blue для задачи игры в го. Ситуация усложня­
лась отсутствием простой эвристики для оценки того, кто выигрывает
в середине партии го (определение того, кто впереди — белые или черные,
заведомо является предметом шумной дискуссии лучших аналитиков).
Именно поэтому до самого недавнего времени многие известные специа­
листы в области информатики считали, что на разработку сильного ком­
пьютерного го уйдет по крайней мере десятилетие.
Для того чтобы продемонстрировать мастерство своих обучающихся алгоритмов,
компания DeepMind приняла вызов обучиться играть в го — игру, которая требует
сложного стратегического планирования. В своей изобретательной работе компа­
ния DeepMind показала собственный механизм глубокого самообучения с максими­
зацией подкрепления — AlphaGo, который объединил сверточные сети с древес­
ным поиском, чтобы победить гроссмейстера Ли Седола (рис. 8.3).
Рис. 8.3. Чемпион по го Ли Седол сражается с AlphaGo. Ли Седол в конечном счете проиграл матч
со счетом 1:4, но ему удалось выиграть одну игру. Маловероятно, что эта победа может повториться
в отношении значительно усовершенствованных потомков AlphaGo, таких как AlphaZero
Программа AlphaGo убедительно продемонстрировала, что методы глубокого
самообучения с максимизацией подкрепления способны обучаться распутывать
сложные стратегические игры. Суть прорыва заключалась в осознании того, что
сверточные сети могут научиться оценивать, кто ведет в середине игры — черные
184
|
Гпава 8
или белые, что позволило подрезать игровые деревья до разумных глубин. (Про­
грамма AlphaGo также оценивает, какие ходы являются наиболее выгодными, что
позволяет подрезать пространство игрового дерева во второй раз.) Победа про­
граммы AlphaGo действительно вывела глубокое самообучение с максимизацией
подкрепления в первые ряды, и масса исследователей теперь работают над тем,
чтобы трансформировать системы в стиле AlphaGo в практическую реализацию.
В этой главе мы обсудим обучающиеся алгоритмы с максимизацией подкрепления
и, в частности, глубоко обучающиеся архитектуры на их основе. Затем мы покажем
читателям, как успешно применять самообучение с максимизацией подкрепления
в игре крестики-нолики. Несмотря на простоту игры, тренировка успешного учени­
ка для крестиков-ноликов требует значительной изощренности, в чем вы вскоре
убедитесь сами.
Программный код для этой главы является адаптированной версией библиотеки
DeepChem для самообучения с максимизацией подкрепления, и в частности приме­
ра кода, созданного Питером Истманом (Peter Eastman) и Карлом Лесвингом (Karl
Leswing). Спасибо Питеру за помощь в отладке и настройке примера кода этой
главы.
Марковские процессы принятия решений
Перед тем как начать обсуждение обучающихся алгоритмов с максимизацией под­
крепления, будет полезно точно определить семейство задач, которые пытаются
решать методы самообучения с максимизацией подкрепления. Математический
каркас марковских процессов принятия решений (Markov decision processes, MDP)
очень полезен для формулирования методов самообучения с максимизацией под­
крепления. Традиционно MDP-процессы сопровождаются целым арсеналом грече­
ских символов, но вместо этого мы попытаемся продолжить предоставлять некото­
рое базовое интуитивное понимание.
В центре MDP-процессов находится пара, состоящая из среды и агента. Среда ко­
дирует ’’мир", в котором агент пытается действовать. Примеры сред могут вклю­
чать игровые миры. Например, доска го с сидящим напротив гроссмейстером
Ли Седолом является допустимой средой. Еще одной потенциальной средой может
быть среда, окружающая небольшого робота-вертолета. В раннем выдающемся ус­
пешном эксперименте самообучения с максимизацией подкрепления команда ис­
следователей из Стэнфорда во главе с Эндрю Hr (Andrew Ng) натренировала верто­
лет летать в перевернутом положении, используя самообучение с максимизацией
подкрепления (рис. 8.4).
Агент — это обучающаяся сущность, которая действует в среде. В нашем первом
примере программа AlphaGo как таковая является агентом. Во втором примере
агентом является робот-вертолет (точнее, алгоритм управления в роботе-верто­
лете). Каждый агент имеет набор действий, которые он может выполнять в среде.
Для программы AlphaGo они представляют собой допустимые ходы игры го. Для
вертолета-робота они включают управление главным и вторичным винтами.
Самообучение с максимизацией подкрепления
|
185
Рис. 8.4. Команда исследователей Эндрю Нг из Стэнфорда (2004-2010) натренировала робот-вертолет
летать в перевернутом положении, используя самообучение с максимизацией подкрепления.
Эта работа потребовала сконструировать изощренный физически точный симулятор
Предполагается, что действия агента оказывают влияние на среду. В случае с про­
граммой AlphaGo это влияние имеет детерминированный характер (решение про­
граммы AlphaGo поставить фишку приводит к размещению фишки на доске).
В случае с вертолетом эффект — вероятностный (изменение положения вертолета
может зависеть от ветровых условий, которые нельзя эффективно смоделировать).
Последняя часть модели — это понятие вознаграждения. В отличие от контроли­
руемого самообучения, где присутствуют явно заданные метки, на которых алго­
ритм обучается, или неконтролируемого самообучения, где задача состоит в том,
чтобы обучиться лежащей в основе структуре данных, самообучение с максимиза­
цией подкрепления работает в обстановке частичных, нечастых вознаграждений.
В го вознаграждения достигаются в конце игры по факту победы или поражения
в игре, тогда как в полете вертолета вознаграждения могут представляться за
успешные полеты или завершение фигур пилотажа.
Трудность разработки функции вознаграждения
Одна из самых больших проблем в самообучении с максимизацией под­
крепления— это проектирование вознаграждений, которые побуждают
агентов обучаться желаемому поведению. Даже для простых игр с выигрышем/проигрышем, таких как го или крестики-нолики, эта задача может
быть удивительно сложной. Как должен штрафоваться проигрыш и как
должен вознаграждаться выигрыш? Хороших ответов пока не существует.
186
|
Глава 8
Для более сложных линий поведения эта задача может быть чрезвычайно
трудной. Ряд исследований показали, что простое вознаграждение может
привести к неожиданному и даже потенциально опасному поведению
агентов. Эти системы подстегивают опасения в отношении будущих аген­
тов с большой автономией, связанные с тем, что, если их натренируют
оптимизировать плохие функции вознаграждения, они начнут сеять хаос,
когда будут выпущены в реальный мир.
В общем случае самообучение с максимизацией подкрепления является
менее зрелым, чем методы контролируемого самообучения, и мы предос­
терегаем, что решения о внедрении самообучения с максимизацией под­
крепления в производственных системах должны приниматься с большой
осторожностью. Учитывая неопределенность в отношении заученного
поведения, любая развернутая обучающаяся система с максимизацией
подкрепления должна быть тщательно протестирована.
Алгоритмы для самообучения
с максимизацией подкрепления
Теперь, когда мы познакомили вас с ключевыми математическими структурами,
лежащими в основе самообучения с максимизацией подкрепления, давайте рас­
смотрим проектирование алгоритмов, которые заучивают интеллектуальные линии
поведения для обучающихся агентов. На высоком уровне обучающиеся алгоритмы
с максимизацией подкрепления могут быть разделены на две группы: модельно­
ориентированные и безмодельные алгоритмы. Основное различие между ними со­
стоит в том, пытается ли алгоритм обучиться внутренней модели того, как действу­
ет его среда, или нет. Для более простых сред, таких как крестики-нолики, динами­
ка модели тривиальна. Для других сред, таких как полет вертолета или даже игры
ATARI, положенная в основу среда, судя по всему, чрезвычайно сложная. Отказ от
построения явной модели среды в пользу неявной модели, которая консультирует
агента как действовать, вполне может быть более прагматичным.
Симуляции и самообучение с максимизацией подкрепления
Любой обучающийся алгоритм с максимизацией подкрепления требует
итеративного улучшения результативности текущего агента путем оценки
текущего поведения агента и его изменения для улучшения получаемых
вознаграждений. Эти обновления структуры агента часто связаны с неко­
торым обновлением градиентного спуска, как мы увидим в последующих
разделах. Однако, как вы знаете из предыдущих глав, градиентный спуск
является медленным тренирующим алгоритмом! Для того чтобы обучиться
эффективной модели, могут потребоваться миллионы или даже миллиарды
шагов градиентного спуска.
Это создает проблему, если среда самообучения находится в реальном ми­
ре; каким образом агенту взаимодействовать миллионы раз с реальным
миром? В большинстве случаев это невозможно. В результате подавляю­
Самообучение с максимизацией подкрепления
|
187
щая часть изощренных обучающихся систем с максимизацией подкрепле­
ния критически зависит от симуляторов, которые позволяют взаимодейст­
вовать с имитационной вычислительной версией среды. Для среды полета
вертолета одной из самых трудных задач, с которыми исследователи
столкнулись, было создание точного симулятора физики вертолета, кото­
рый позволял бы вычислительно обучиться эффективным стратегиям по­
лета.
Q-заучивание
В математическом каркасе марковских процессов принятия решений агенты осу­
ществляют действия в среде и получают вознаграждения, которые (предположи­
тельно) связаны с действиями агентов. Функция Q предсказывает ожидаемое возна­
граждение за выполнение конкретного действия в определенном состоянии среды.
Эта концепция кажется очень простой, но загвоздка возникает, когда ожидаемое
вознаграждение включает дисконтированные вознаграждения от будущих дей­
ствий.
Дисконтирование вознаграждений
^^к
Ж
Понятие дисконтированного вознаграждения широко распространено
и часто вводится в контексте финансов. Предположим, знакомый говорит,
что он заплатит вам 10 долларов на следующей неделе. Эти будущие
10 долларов для вас стоят меньше, чем 10 долларов в вашем кармане пря­
мо сейчас. (К примеру, что будет, если оплата никогда не произойдет?)
Поэтому в математическом смысле распространена практика введения
коэффициента дисконтирования у (как правило, между 0 и 1), который
снижает ’’текущую стоимость” будущих платежей. Скажем, ваш знакомый
несколько ненадежен. Вы можете решить установить у = 0,5 и оценить
обещание вашего знакомого, как 10у = 5 долларов сегодня, чтобы учесть
неопределенность в вознаграждениях.
Вместе с тем эти будущие вознаграждения зависят от действий, предпринимаемых
агентом в будущем. В результате функция Q должна быть сформулирована рекур­
сивно в терминах самой себя, поскольку ожидаемые вознаграждения для одного
состояния зависят от вознаграждений для другого состояния. Это рекурсивное
определение делает заучивание функции Q запутанным. Такая рекурсивная зависи­
мость может быть сформулирована явным образом для простых сред с дискретны­
ми пространствами состояний и решена методами динамического программирова­
ния. В случае более универсальных сред до недавнего времени методы g-заучивания не были особо полезными.
Недавно компания DeepMind представила глубокие £)-сети (deep ^-networks, DQN),
которые использовались для распутывания игр ATARI, как упоминалось ранее.
Ключевая оригинальная идея, лежащая в основе DQN, опять-таки является теоре­
мой об универсальной аппроксимации; поскольку функция Q может быть сколь
угодно сложной, мы должны моделировать ее с помощью универсального аппрок­
188
|
Глава 8
симатора, такого как глубокая сеть. Хотя использование нейронных сетей для мо­
делирования Q выполнялось и ранее, компания DeepMind для этих сетей также
ввела понятие воспроизведения опыта, которое позволило им эффективно трениро­
вать модели DQN-сети в масштабе. Воспроизведение опыта позволяет хранить
наблюдавшиеся исходы игр и переходы из состояния в состояние из прошлых игр
и выполнять их повторный отбор во время тренировки (в дополнение к тренировке
на новых играх), тем самым гарантируя, что уроки прошлого не будут сетью за­
быты.
t
Катастрофическое забывание
i
Нейронные сети быстро забывают прошлое. На самом деле, это явление,
У>/9***\ которое носит название катастрофического забывания, может происхо1—- X ‘ дить очень быстро; несколько пакетных обновлений может быть достаточ­
но, чтобы сеть забыла сложное поведение, которое она знала ранее. И без
таких методов, как воспроизведение опыта, которые гарантируют, что сеть
всегда тренируется на эпизодах из прошлых матчей, было бы невозможно
обучиться сложному поведению.
Проектирование тренирующего алгоритма для глубоких сетей, которые не
страдают от катастрофического забывания, сегодня по-прежнему остается
серьезной открытой задачей. Примечательно, что люди не страдают от ка­
тастрофической амнезии; даже если вы не ездили на велосипеде в течение
многих лет, скорее всего, вы по-прежнему помните, как это делается. Соз­
дание нейронной сети с похожей устойчивостью может включать добавле­
ние долгосрочной внешней памяти, по аналогии с нейронной машиной
Тьюринга. К сожалению, до сих пор ни одна из попыток проектирования
устойчивых архитектур не работала хорошо.
Заучивание стратегии
В предыдущем разделе вы познакомились с (9-заучиванием, которое пытается
получать ожидаемые вознаграждения за выполнение определенных действий
в конкретных состояниях среды. Заучивание стратегии является альтернативным
математическим каркасом для поведения обучающегося агента. Оно вводит функ­
цию стратегии л, которая назначает вероятность каждому действию, которое агент
может выполнить в данном состоянии.
Обратите внимание, что стратегии вполне хватает, чтобы определить поведение
агента. При наличии стратегии агент может действовать просто путем получения
образца подходящего действия для текущего состояния среды. Заучивание страте­
гий удобно тем, что стратегии могут быть заучены напрямую посредством алго­
ритма, который называется стратегическим градиентом. Этот алгоритм использу­
ет несколько остроумных математических приемов, позволяющих вычислять стра­
тегические градиенты непосредственно методом обратного распространения для
глубоких сетей. Ключевым понятием является разворачивание (rollout) игровой
ситуации: дать агенту действовать в среде в соответствии со своей текущей страте­
гией и отмечать все поступающие вознаграждения; затем распространять в обратном
Самообучение с максимизацией подкрепления
|
189
направлении, чтобы увеличить вероятность тех действий, которые привели к более
выгодным вознаграждениям. Это описание является точным на высоком уровне, но
позже мы увидим более подробные детали его реализации.
Стратегия часто связана с функцией стоимости V1. Эта функция возвращает ожи­
даемое дисконтированное вознаграждение за выполнение стратегии я, начиная
с текущего состояния среды. Функции V и Q тесно связаны, т. к. обе предоставляют
оценки будущих вознаграждений, начиная с текущего состояния, но V не задает
действие, которое должно быть выполнено, и, напротив, исходит из того, что
образцы действия отбираются из я .
Еще одной универсально определяемой функцией является функция преимущест­
ва А. Она определяет разницу между ожидаемым вознаграждением за выполнение
конкретного действия а в заданном состоянии среды s и вознаграждением за со­
блюдение базовой стратегии я . Математически А определяется в терминах Q и V\
A(s, a) = Q(s, a)-V(s).
Это преимущество полезно в алгоритмах заучивания стратегии, т. к. оно позволяет
алгоритму количественно определять, насколько конкретное действие подходит
лучше, чем текущая рекомендация стратегии.
Стратегический градиент вне самообучения
с максимизацией подкрепления
Несмотря на то что в качестве обучающегося алгоритма с максимизацией
подкрепления мы ввели стратегический градиент, он также может рас­
сматриваться как инструмент для обучающихся глубоких сетей с недиф­
ференцируемыми подмодулями. Что это значит в переводе с математиче­
ского жаргона?
Предположим, что у нас есть сеть, которая вызывает внешнюю программу
внутри самой сети. Эта внешняя программа является черным ящиком; это
может быть сетевой вызов либо вызов подпрограммы COBOL 1970-х го­
дов. Каким образом остальная часть глубокой сети может обучиться, когда
этот модуль не имеет градиента?
Оказывается, стратегический градиент можно переориентировать на оцен­
ку "эффективного” для системы градиента. Простой интуитивный подход
заключается в том, что можно выполнять несколько "разворачиваний"
ситуации, которые используются для вычисления градиентов. Можно
ожидать, что в течение последующих нескольких лет мы увидим исследо­
вания, расширяющие эту идею для создания больших сетей с недифферен­
циальными модулями.
1 См. https://ru.wikipedia.org/wiki/4HCKOHTHpoBaHHafl_CTOHMOCTb. — Прим. пер.
190
|
Глава 8
Асинхронная тренировка
Недостатком методов стратегического градиента, представленных в предыдущем
разделе, является то, что выполнение операций разворачивания требует вычисле­
ния оценки поведения агента в некоторой (вероятно, смоделированной) среде.
Большинство симуляторов— это сложные компоненты программы, которые не
могут выполняться на GPU. В результате прохождение одного шага самообучения
требует продолжительных расчетов, ограниченных возможностями CPU. А это мо­
жет привести к неоправданно медленной тренировке.
Методы самообучения с асинхронной максимизацией подкрепления пытаются
ускорить этот процесс, используя несколько асинхронных потоков CPU для выпол­
нения независимых разворачиваний игровых ситуаций. Эти рабочие потоки разво­
рачивают игру, локально вычисляют обновления стратегического градиента, а за­
тем периодически синхронизируются с глобальным набором параметров. В эмпи­
рическом плане асинхронная тренировка, судя по всему, значительно ускоряет
самообучение с максимизацией подкрепления и позволяет обучаться довольно
сложным стратегиям на ноутбуках. (Без GPU-процессоров! Подавляющая часть
вычислительной мощности используется на разворачивание, поэтому шаги обнов­
ления градиента часто не являются ограничивающим аспектом самообучения
с максимизацией подкрепления.) Наиболее популярным алгоритмом самообучения
с асинхронной максимизацией подкрепления в настоящее время является алгоритм
асинхронной критики стратегии с учетом преимущества (asynchronous advantage
actor-critic, АЗС).
CPU или GPU?
GPU-процессоры необходимы для большинства крупных глубоко обучающихся приложений, но в настоящее время самообучение с максимизацией подкрепления является исключением из этого общего правила. Опора
обучающихся алгоритмов с максимизацией подкрепления на многочис­
ленные разворачивания игровой ситуации в настоящее время, судя по все­
му, смещает реализации самообучения с максимизацией подкрепления
в сторону систем с многоядерными CPU. Вполне вероятно, что в конкрет­
ных приложениях отдельные симуляторы могут быть портированы для
более быстрой работы на GPU, однако в ближайшем будущем моделиро­
вание на основе CPU, скорее всего, будет продолжать доминировать.
Ограничения самообучения
с максимизацией подкрепления
Математический каркас марковских процессов принятия решений чрезвычайно
универсальный. Например, ученые-бихейвиористы регулярно используют марков­
ские процессы принятия решений для понимания и моделирования принятия реше­
ний человеком. Математическая универсальность этого каркаса побудила ученых
утверждать, что решение задачи самообучения с максимизацией подкрепления мо­
Самообучение с максимизацией подкрепления
|
191
жет стимулировать создание искусственных универсальных разумов (artificial
general intelligences, AGI). Ошеломляющий успех программы AlphaGo против
Ли Седола усилил это убеждение, и поэтому исследовательские группы, такие как
OpenAI и DeepMind, направленные на создание AGI, сосредотачивают подавляю­
щую часть своих усилий на разработке новых методов самообучения с максимиза­
цией подкрепления.
Тем не менее имеются серьезные слабые стороны в самообучении с максимизацией
подкрепления в том виде, в котором оно существует сегодня. Тщательный сопоста­
вительный анализ на основе эталонов показал, что методы самообучения с макси­
мизацией подкрепления очень восприимчивы к выбору гиперпараметров (даже
по стандартам глубокого самообучения, которое уже намного лучше отшлифовано
до мелочей, чем другие методы, такие как случайные леса).
Как мы уже упоминали, конструирование функций вознаграждения очень незре­
лое — люди способны внутренне проектировать собственные функции вознаграж­
дения или эффективно учатся копировать функции вознаграждения из наблюдений.
Несмотря на то, что были предложены алгоритмы ’’обратного самообучения с мак­
симизацией подкрепления”, которые непосредственно обучаются функциям возна­
граждения, эти алгоритмы имеют много ограничений на практике.
Помимо фундаментальных ограничений, есть еще много практических вопросов
масштабирования. Люди способны играть в игры, в которых высокоуровневая
стратегия совмещается с тысячами ”микро”-ходов. Например, для профессиональ­
ной игры в стратегическую игру StarCraft (рис. 8.5) требуются сложные стратеги­
ческие хитрости в сочетании с тщательным контролем за сотнями юнитов (боевых
Рис. 8.5. Коллекция подзадач, необходимых для игры в стратегию StarCraft в реальном времени.
В этой игре игроки должны создать армию, которую они могут использовать, чтобы победить
противостоящую силу. Успешная игра в StarCraft требует мастерства планирования ресурсов, разведки
и сложной стратегии. Лучшие компьютерные агенты StarCraft остаются на любительском уровне
192
|
Глава 8
единиц). Игры могут потребовать тысяч локальных ходов, чтобы довести игру до
завершения. Кроме того, в отличие от го или шахмат, StarCraft имеет "туман войны"
(ухудшение видимости на поле боя), где игроки не могут видеть все состояние
игры целиком. Это сочетание обширного игрового состояния и неопределенности
сорвало попытки обучиться максимизировать подкрепления на игре StarCraft. По
этой причине команды исследователей искусственного интеллекта в DeepMind
и других исследовательских группах сосредотачивают серьезные усилия на решении
задачи StarCraft с помощью методов глубокого самообучения с максимизацией
подкрепления. Несмотря на некоторые серьезные усилия, лучшие боты StarCraft
пока остаются на любительском уровне.
В целом существует широкий консенсус в отношении того, что самообучение
с максимизацией подкрепления является полезным методом, который, судя по все­
му, будет иметь большое влияние в течение нескольких следующих десятилетий,
но также ясно и другое: многие практические ограничения методов самообучения
с максимизацией подкрепления будут означать, что в ближайшей перспективе по­
давляющая часть работы будет происходить в исследовательских лабораториях.
Игра крестики-нолики
Крестики-нолики — это простая игра для двух игроков. Игроки ставят крестики и
нолики на игровом поле размером 3 х 3 до тех пор, пока одному игроку не удастся
разместить три свои фигуры в ряд. Первый игрок, который это сделает, выигрыва­
ет. Если ни одному из игроков не удается поставить три фигуры в ряд до того, как
игровое поле заполнится, игра заканчивается вничью. На рис. 8.6 показано игровое
поле игры в крестики-нолики.
Рис. 8.6. Игровое поле игры в крестики-нолики
Крестики-нолики — это хорошая испытательная площадка для методов самообуче­
ния с максимизацией подкрепления. Игра проста настолько, что для тренировки
эффективных агентов не требуется непомерного объема вычислительной мощ­
ности. В то же время, несмотря на простоту игры крестики-нолики, заучивание
эффективного агента требует значительной изощренности. Программный код
TensorFlow данного разд ела, возможно, будет самым сложным примером в этой
книге. В оставшейся части этого раздела мы проведем вас по процессу проектиро­
вания агента TensorFlow, опирающегося на самообучение с асинхронной максими­
зацией подкрепления.
Самообучение с максимизацией подкрепления
|
193
Объектная ориентированность
Программный код, с которым мы знакомили до настоящего момента в этой книге,
в основном состоял из сценариев, дополненных небольшими вспомогательными
функциями. Однако в этой главе мы перейдем к объектно-ориентированному стилю
программирования, который может быть для вас новым, в особенности если вы
родом из научного мира, а не из мира технологий. Вкратце, объектно-ориенти­
рованная программа определяет объекты, моделирующие аспекты мира. Напри­
мер, вы можете определить объекты среды Environment или агента Agent, или возна­
граждения Reward, которые непосредственно соответствуют этим математическим
понятиям. Класс — это шаблон для объектов, который используется для создания
экземпляров многих новых объектов. Например, вы вскоре увидите определение
класса Environment, которое мы будем использовать для определения многих кон­
кретных объектов среды Environment.
Объектная ориентированность особенно эффективна для построения сложных сис­
тем, поэтому мы будем применять ее для упрощения проекта нашей обучающейся
системы с максимизацией подкрепления. На практике ваши реальные глубоко обу­
чающиеся системы (или обучающиеся с максимизацией подкрепления), вероятно,
тоже должны быть объектно-ориентированными, поэтому мы рекомендуем потра­
тить некоторое время на освоение объектно-ориентированного проектирования.
Существует много превосходных книг, в которых раскрыты фундаментальные
принципы объектно-ориентированного проектирования, и мы рекомендуем вам по
мере необходимости с ними сверяться.
Абстрактная среда
Начнем с определения абстрактного класса Environment, который кодирует состоя­
ние системы в списке объектов NumPy (пример 8.1). Объект Environment является
довольно универсальным (это адаптированная версия механизма самообучения
с максимизацией подкрепления проекта DeepChem), поэтому он может легко слу­
жить шаблоном для других проектов самообучения с максимизацией подкрепле­
ния, которые вы, возможно, попытаетесь реализовать.
i Пример 8.1. Этот класс определяет шаблон для конструирования новых сред
class Environment(object):
"""Среда, в которой актор выполняет действия для решения задачи.
Среда имеет текущее состояние, которое представлено либо как
один массив NumPy, либо как список массивов NumPy. Когда действие
совершается, оно заставляет состояние обновиться. Что именно
имеется в виду под "действием", определяется каждым подклассом.
Что касается интерфейса, то это просто произвольный объект.
Среда также вычисляет вознаграждение за каждое действие
и сообщает о завершении задачи (имея в виду, что больше не может быть
выполнено никаких действий).
клл
194
|
Глава 8
def _ init__(self, state_shape, n_actions, state_dtype=None):
"""Подклассы в дополнение к собственной инициализации должны
вызывать конструктор надкласса.
пии
self.state_shape = state_shape
self.n_actions = n_actions
if state_dtype is None:
# Принимается, что все массивы имеют тип float32.
if isinstance(state_shape[0], collections.Sequence):
self.state_dtype = [np.float32] * len(state_shape)
else:
self.state_dtype = np.float32
else:
self.state_dtype = state_dtype
Среда игры крестики-нолики
Нам нужно конкретизировать класс Environment, чтобы создать среду
TicTacToeEnvironment, подходящую для наших нужд. Для этого мы создаем подкласс
класса Environment, который добавляет больше возможностей, сохраняя при этом
основную функциональность исходного надкласса. В примере 8.2 мы определяем
среду TicTacToeEnvironment как подкласс класса Environment, которая добавляет
детали, характерные для игры в крестики-нолики.
Пример 8.2. Класс TicTacToeEnvironmant определяет шаблон
для конструирования новых сред игры в крестики-нолики
class TicTacToeEnvironment(de.rl.Environment):
иии
Играть в крестики-нолики против случайно действующего противника
иии
X = np.array([1.0, 0.0])
О = пр.array([0.0, 1.0])
EMPTY = пр.array([0.0, 0.0])
ILLEGAL_MOVE_PENALTY = -3.0
LOSS_PENALTY = -3.0
NOT—LOSS =0.1
DRAW_REWARD =5.0
WIN_REWARD =10.0
def __ init__ (self):
super(TicTacToeEnvironment, self).__ init__ ([(3, 3, 2)], 9)
self.terminated = None
self.reset()
Самообучение с максимизацией подкрепления
|
195
Первый интересный момент, который здесь следует отметить, заключается в том,
что мы определяем состояние игрового поля, как массив NumPy формы (3, 3, 2). Мы
используем кодирование с одним активным состоянием, состоящее из х и о. (Коди­
рование с одним активным состоянием полезно не только для обработки естествен­
ного языка!)
Второй важный момент, который стоит отметить, состоит в том, что среда явно
определяет функцию вознаграждения, устанавливая штрафы за недопустимые ходы
и проигрыши, а также вознаграждения за ничьи и победы. Этот фрагмент кода
мощно иллюстрирует произвольный характер конструирования функции вознагра­
ждения. Почему именно эти числа?
Эмпирически эти варианты, судя по всему, приводят к стабильному поведению, но
мы рекомендуем вам поэкспериментировать с альтернативными настройками воз­
награждения, чтобы понаблюдать за результатами. В этой реализации мы указыва­
ем, что агент всегда играет крестиками х, но рандомизируем, кто будет ходить пер­
вым: х или о. Функция get_o_move() помещает о на случайную открытую клетку
игрового ПОЛЯ. Подкласс TicTacToeEnvironment Кодирует Противника, КОТОрыЙ Играет
ноликами о, при этом всегда выбирая случайный ход. Функция reseto очищает
игровое поле и помещает фигуру о в случайную клетку, если во время этой игры
о ходит первым (пример 8.3).
Пример 8.3. Еще пара методов ИЗ класса TicTacToeEnviroinaent
J
def reset(self):
self.terminated = False
self.state = [np.zeros(shape=(3, 3, 2), dtype=np.float32)]
# Рандомизировать, кто будет ходить первым,
if random.randint(0, 1) == 1:
move = self.get_0_move()
self.state[0][move[0]][move[1]] = TicTacToeEnvironment .0
def get_0_move(self):
empty_squares = []
for row in range(3):
for col in range(3):
if np.all(self.state[0][row][col] == TicTacToeEnvironment.EMPTY):
empty_squares.append((row, col))
return random.choice(empty_squares)
Сервисный метод game overo сообщает, что игра закончилась, если все клетки
заполнены. Метод check winner () проверяет, не выставил ли указанный игрок три
фигуры подряд и выиграл игру (пример 8.4).
196
|
Глава 8
Пример 84. Служебные методы из класса TicTacToeEnvironment
для обнаружения, когда игра закончилась и кто победил
def check_winner(self, player):
for i in range(3):
row = np.sum(self.state[0][i][:], axis=0)
if np.all(row == player * 3):
return True
col = np.sum(self.state[0][:][i], axis=0)
if np.all(col == player * 3):
return True
diagl = self.state[0][0][0] + self.state[0][1][1] +
self.state[0][2][2]
if np.all(diagl = player * 3):
return True
diag2 = self.state[0][0][2] + self.state[0][1][1] +
self.state[0][2][0]
if np.all(diag2 == player * 3):
return True
return False
def game_over(self) :
for i in range(3):
for j in range(3):
if np.all(self.state[0][i][j] == TicTacToeEnvironment.EMPTY):
return False
return True
В нашей реализации действие — это просто число от 0 до 8, определяющее клетку,
на которую помещается фигура х. Метод step о проверяет, занята ли эта клетка
(возвращает штраф, если клетка занята), затем помещает фигуру. Если х выиграл,
возвращается вознаграждение. Иначе случайный противник о может сделать ход.
Если выиграл о, то будет возвращен штраф. Если игра закончилась вничью,
возвращается вознаграждение. В противном случае игра продолжается с вознаграж­
дением notloss (пример 8.5).
Пример 8.5. Этот метод выполняет шаг симуляции
def step(self, action):
self.state = copy.deepcopy(self.state)
row = action // 3
col = action % 3
# Недопустимый ход - клетка занята.
if not np.all(self.state[0][row][col] == TicTacToeEnvironment.EMPTY):
Самообучение с максимизацией подкрепления |
197
self.terminated = True
return TicTacToeEnvironment .ILLEGAL_MOVE_PENALTY
# Ход X.
self.state[0][row][col] = TicTacToeEnvironment.X
# X выиграл?
if self.check_winner(TicTacToeEnvironment .X):
self.terminated = True
return TicTacToeEnvironment.WIN_REWARD
if self.game_over():
self.terminated = True
return TicTacToeEnvironment .DRAW_REWARD
move = self.get—O_move()
self.state[0][move[0]][move[1]] = TicTacToeEnvironment.0
# О выиграл?
if self.check_winner(TicTacToeEnvironment.0):
self.terminated = True
return TicTacToeEnvironment .LOSS_PENALTY
if self.game_over():
self.terminated = True
return TicTacToeEnvironment.DRAW_REWARD
return TicTacToeEnvironment .NOT_LOSS
Слоевая абстракция
Выполнение обучающегося алгоритма с асинхронной максимизацией подкрепле­
ния, такого как АЗС, требует, чтобы каждый поток имел доступ к отдельной копии
стратегической модели. Для продолжения процесса тренировки эти копии модели
должны периодически синхронизироваться друг с другом. Как проще всего скон­
струировать несколько копий графа TensorFlow, которые можно распределять по
каждому потоку?
Одной из простых возможностей является создание функции, которая формирует
копию модели в отдельном графе TensorFlow. Этот подход работает хорошо, но
становится немного запутанным, в особенности для сложных сетей. Применение
небольшой объектной ориентированности может значительно упростить этот про­
цесс. Поскольку наш программный код самообучения с максимизацией подкрепле­
ния является адаптированной версией библиотеки DeepChem, мы используем
упрощенную версию вычислительного каркаса TensorGraph из DeepChem (см. https://
deepchem.io для подробной информации и документации). Этот вычислительный
198
|
Глава 8
каркас похож на другие высокоуровневые вычислительные каркасы TensorFlow,
такие как Keras. Основной абстракцией во всех таких моделях является введение
объекта Layer, который содержит часть глубокой сети.
Объект Layer— это часть графа TensorFlow, которая принимает список входных
слоев in layers. В этой абстракции глубокая архитектура состоит из направленного
графа слоев. Направленные графы похожи на ненаправленные графы, которые вы
видели в главе 6, но в отличие от них имеют направления на своих ребрах. В дан­
ном случае in layers образуют ребра в сторону нового слоя Layer с направлением,
указывающим на новый слой. Вы познакомитесь с подробностями данной концеп­
ции в следующем разделе.
Мы используем сервисную функцию tf.register_tensor_conversion_function(), ко­
торая позволяет произвольным классам регистрироваться как конвертируемые
в тензоры. Эта регистрация будет означать, что слой может быть конвертирован
в тензор TensorFlow посредством вызова функции tf .convert to tensor. Приватный
метод get input tensors () — это сервисный метод, использующий функцию
tf .convert to tensor о для преобразования входных слоев во входные тензоры. Каж­
дый слой отвечает за реализацию методу create tensor (), который определяет опе­
рации для добавления в вычислительный граф TensorFlow (пример 8.6).
i
:
I
i
Пример 8.6. Объект Layer является фундаментальной абстракцией
в глубоких объектно-ориентированных архитектурах. Он инкапсулирует часть сети,
такую как полносвязный слой или сверточный слой.
В этом примере определяется общий надкласс для всех таких слоев
class Layer(object):
def __ init__ (self, in_layers=None,
**kwargs):
if "name" in kwargs:
self.name = kwargs["name"]
else:
self.name = None
if in_layers is None:
in_layers = list()
if not isinstance(in_layers, Sequence):
in_layers = [in_layers]
self.in_layers = in_layers
self.variable_scope = ""
self.tb_input = None
def create_tensor(self , in_layers=None, **kwargs):
raise NotlmplementedError("Подклассы должны реализовывать себя")
def _get_input_tensors(self, in_layers):
"""Получить входные тензоры для этого слоя.
Самообучение с максимизацией подкрепления
|
199
Параметры
in_layers: список слоев Layers или тензоров;
входы, переданные в create_tensor(). Если None,
то вместо этого будут использоваться входы этого слоя.
wнн
if in_layers is None:
in_layers = self.in_layers
if not isinstance(in_layers, Sequence):
in_layers = [in_layers]
tensors = []
for input in in_layers:
tensors.append(tf.convert_to_tensor(input))
return tensors
def _convert_layer_to_tensor(value, dtype=None, name=None,
as_ref=False):
return tf.convert_to_tensor(value.out_tensor,
dtype=dtype,
name=name)
tf.register_tensor_conversion_function(Layer,
_convert_layer_to_tensor)
Приведенное выше описание является абстрактным, но на практике простым в ис­
пользовании. В примере 8.7 приведен слой сжатия squeeze, который обертывает
функцию tf. squeeze о со слоем Layer (позже вы отметите для себя удобство этого
класса). Обратите внимание, что squeeze является подклассом класса Layer.
^Пример 8.7. Слой Squeeze сжимает свой вход
|
class Squeeze(Layer):
def __ init__ (self, in_layers=None, squeeze_dims=None, **kwargs):
self .squeeze_dims = squeeze_dims
super(Squeeze, self).__ init__ (in_layers, **kwargs)
def create_tensor(self, in_layers=None, **kwargs):
inputs = self._get_input_tensors(in_layers)
parent_tensor = inputs[0]
out_tensor = tf.squeeze(parent_tensor,
squeeze_dims=self.squeeze_dims)
self.out_tensor = out_tensor
return out_tensor
Входной слой input для удобства обертывает заполнители (пример 8.8). Обратите
внимание, что метод Layer.create tensorO должен вызываться для каждого слоя,
который мы используем для построения вычислительного графа TensorFlow.
200
|
Гпава 8
. Пример 8.8. Входной слой input добавляет заполнители в вычислительный граф
class Input(Layer):
def__ init__ (self, shape, dtype=tf.float32, **kwargs):
self._shape = tuple(shape)
self.dtype = dtype
super(Input, self).__ init__ (**kwargs)
def create_tensor(self, in_layers=None,
**kwargs):
if in_layers is None:
in_layers = self.in_layers
out_tensor = tf.placeholder(dtype=self.dtype, shape=self,_shape)
self,out_tensor = out_tensor
return out—tensor
Модули tf.keras И tf .estimator
Вычислительный каркас TensorFlow теперь интегрировал популярный
объектно-ориентированный интерфейс Keras в основную библиотеку
TensorFlow. Keras включает определение класса Layer, близко соответст­
вующее объектам Layer, о которых вы только что узнали в этом разделе.
На самом деле здесь объекты Layer являются адаптированной версией биб­
лиотеки DeepChem. В свою очередь объекты DeepChem были взяты из
более ранней версии Keras.
Стоит отметить, что модуль tf.keras еще не стал стандартным высоко­
уровневым интерфейсом к библиотеке TensorFlow. Модуль tf.estimator
предоставляет альтернативный (хотя и менее богатый) высокоуровневый
интерфейс к сырому коду TensorFlow.
Независимо от того, какой интерфейс в конечном итоге станет стандарт­
ным, мы считаем, что понимание принципов проектирования для создания
собственного интерфейса является поучительным и целесообразным. Воз­
можно, вам понадобится создать новую систему для вашей организации,
которая требует альтернативного проектирования, поэтому четкое пони­
мание принципов проектирования сослужит вам хорошую службу.
Определение графа слоев
В предыдущем разделе мы вкратце упомянули, что глубокая архитектура может
быть представлена как направленный граф объектов Layer. В этом разделе мы при­
меним данный интуитивный подход к объекту TensorGraph. Эти объекты отвечают
за построение базового вычислительного графа TensorFlow.
Объект TensorGraph отвечает за поддержание объектов tf .Graph, tf .Session и списка
слоев (self.layers) на внутреннем уровне (пример 8.9). Направленный граф пред­
ставлен неявно, при помощи списка входных слоев in iayers, принадлежащих каж­
дому объекту Layer. Объект TensorGraph также содержит сервисные функции для
сохранения этого экземпляра tf .Graph на диск и, следовательно, присваивает себе
Самообучение с максимизацией подкрепления
|
201
каталог (используя функцию tempfiie.mkdtempo, если другая не указана) для хране­
ния контрольных точек весов, связанных с базовым графом TensorFlow.
Пример 8.9. Тензорный граф содержит граф слоев; объекты TensorGraph
можно рассматривать, как ’’модельный" объект, содержащий глубокую архитектуру,
которую вы хотите натренировать
class TensorGraph(object):
def __ init__ (self,
batch_size=100,
random_seed=None,
graph=None,
learning_rate=O.001,
model_dir=None,
**kwargs):
II II II
Параметры
batch_size: целочисленный;
размер пакета по умолчания для тренировки и оценивания.
graph: tens orflow.Graph
граф Graph, в котором создаются объекты Tensorflow. Если None, то
создается новый Graph.
learning_rate: вещественный или LearningRateSchedule;
скорость заучивания, используемая для оптимизации,
kwargs
именованные аргументы,
иии
# Управление слоем.
self.layers = diet()
self.features = list()
self.labels = list()
self.outputs = list()
self.task_weights = list()
self.loss = None
self.built = False
self.optimizer = None
self.learning_rate = learning_rate
# Отдельное место для хранения тензорных объектов, которые
# не сериализуются.
# См. TensorGraph._get_tf() для получения подробностей
# ленивого конструирования.
self.tensor_objects = {
’’Graph”: graph,
#"train_op": None,
}
202
|
Гпава 8
self.global—step = 0
self.batch_size = batch_size
self.random_seed = random_seed
if model_dir is not None:
if not os.path.exists(model_dir):
os.makedirs(model_dir)
else:
model—dir = tempfile.mkdtemp()
self.model_dir_is_temp = True
self.model_dir = model_dir
self.save_file = "%s/%s" %
(self.model_dir, "model")
self.model_class = None
Приватный метод _add_iayer() выполняет служебную работу для добавления ново­
го объекта Layer В TensorGraph (например, 8.10).
! Пример 8.10. Метод -add-layer () добавляет новый объект Layer
def _add—layer(self, layer):
if layer.name is None:
layer.name = "%s_%s" %
(layer.__ class__ .__ name__ , len(self.layers) + 1)
if layer.name in self.layers:
return
if isinstance(layer, Input):
self.features.append(layer)
self.layers[layer.name] = layer
for in_layer in layer.in_layers:
self._add_layer(in_layer)
Слои в TensorGraph должны образовывать направленный ациклический граф (в та­
ком графе не может быть циклов). В результате мы можем отсортировать эти слои
топологически. В интуитивном смысле топологическая сортировка "упорядочива­
ет" СЛОИ В графе так, ЧТО входящие СЛОИ in_layers каждого объекта-слоя Layer
предшествуют ему в упорядоченном списке. Такая топологическая сортировка
необходима для того, чтобы убедиться, что все входные слои конкретного слоя
добавлены в граф перед этим слоем (пример 8.11).
г-------------------------------------------------------------------------------------- - ------------------------------!
■ Пример 8.11. Метод topsortO упорядочивает слои в TensorGraph
def topsort(self):
def add_layerS—to_list(layer, sorted_layers):
if layer in sorted_layers :
return
Самообучение с максимизацией подкрепления
|
203
for in_layer in layer.in_layers:
add_layers_to_list(in_layer, sorted_layers)
sorted_layers.append(layer)
sorted_layers = []
for 1 in self.features + self.labels + self.task_weights +
self.outputs:
add_layers_to_list(1, sorted_layers)
add_layers_to_list(self.loss, sorted_layers)
return sorted—layers
Метод build о берет на себя ответственность за заполнение экземпляра tf. Graph
путем вызова метода layer.create tensoro для каждого слоя в топологическом
порядке (пример 8.12).
[ Пример 8.12. Метод build о заполняет лежащий в основе граф TensorFlow
|
def build(self):
if self.built:
return
with self,_get_tf("Graph"),as_default():
self._training_placeholder = tf.placeholder(dtype=tf.float32,
shape=())
if self.random_seed is not None:
tf.set—random_seed(self.random_seed)
for layer in self.topsort():
with tf.name_scope(layer.name):
layer.create_tensor(training=self ._training^placeholder)
self.session = tf.Session()
self.built = True
Метод set losso добавляет в граф потерю за счет тренировки. Метод add outputo
указывает, что рассматриваемый слой может быть извлечен из графа. Метод
set optimizer () задает оптимизатор, используемый для тренировки (пример 8.13).
! Пример 8.13. Методы, добавляющие в вычислительный граф необходимые потери,
: выходы и оптимизаторы
def set_loss(self, layer):
self._add_layer(layer)
self.loss = layer
def add_output(self, layer):
self,_add_layer(layer)
self.outputs.append(layer)
204
|
Гпава 8
def set_optimizer(self , optimizer):
"""Назначить оптимизатор, который будет использоваться для подгонки.'""'
self.optimizer = optimizer
Метод get layer variabieso используется для извлечения заучиваемых объектовпеременных tf .variable, созданных слоем. Приватный метод get tf о применяется
для извлечения экземпляров tf .Graph и оптимизатора, лежащих в основе объекта
TensorGraph. Метод get global step () — ЭТО вспомогательный Метод ДЛЯ Выборки
текущего шага в тренировочном процессе (начиная с 0 при конструировании).
См. пример 8.14.
Пример 8.14. Выбор заучиваемых переменных, связанных с каждым слоем
J
def get_layer_variables(self, layer):
'""'Получить список тренируемых переменных в слое графа.'""'
if not self.built:
self.build()
with self,_get_tf("Graph"),as_default():
if layer.variable_scope ==
return []
return tf.get_collection(
tf.GraphKeys,TRAINABLE_VARIABLES,
scope=layer.variable_scope)
def get_global_step(self ):
return self,_get_tf("GlobalStep”)
def _get_tf(self, obj):
'""'Выбирает лежащие в основе примитивы TensorFlow.
Параметры
obj: строковый.
Если "Graph”, то возвращает экземпляр tf.Graph. Если "Optimizer",
то возвращает оптимизатор. Если "train_op", то возвращает
тренировочную операцию. Если "GlobalStep”, то возвращает
глобальный шаг.
Возвращаемое значение
Объект TensorFlow
II II II
if obj in self.tensor_objects and self.tensor_objects[obj] is not None:
return self.tensor_objects[obj]
if obj == "Graph":
self ,tensor_objects ["Graph"] = tf.GraphO
Самообучение с максимизацией подкрепления
|
205
elif obj == "Optimizer":
self.tensor_objects["Optimizer"] = tf.train.AdamOptimizer (
learning_rate=self.learning_rate,
betal=0.9,
beta2=0.999,
epsilon=le-7)
elif obj == "GlobalStep":
with self._get_tf("Graph").as_default():
self.tensor_objects["GlobalStep"] = tf.Variable(0,
trainable=False)
return self._get_tf(obj)
Наконец, метод restore () восстанавливает сохраненный объект TensorGraph с диска
(пример 8.15). (Как вы увидите позже, TensorGraph автоматически сохраняется во
время тренировки.)
Пример 8.15. Восстановление натренированной модели с диска
def restore(self):
"""Перезагрузить значения всех переменных из файла
самой последней контрольной точки."""
if not self.built:
self.build()
last_checkpoint = tf.train.latest_checkpoint(self.model_dir)
if last_checkpoint is None:
raise ValueError("No checkpoint found")
with self._get_tf("Graph").as_default():
saver = tf.train.Saver()
saver.restore(self.session, last_checkpoint)
Алгоритм АЗС
В этом разделе вы научитесь реализовывать АЗС — обучающийся алгоритм с асин­
хронной максимизацией подкрепления, который вы встречали ранее в этой главе.
АЗС является значительно более сложным обучающимся алгоритмом, чем те, кото­
рые вы встречали ранее2. Он требует выполнения градиентного спуска в несколь­
ких потоках, перемежающегося с кодом разворачивания игровой ситуации и асин­
хронного обновления заученных весов. В результате этой дополнительной сложно­
сти мы определим алгоритм АЗС в объектно-ориентированном стиле. Начнем
с определения объекта азс.
Класс азс реализует алгоритм АЗС (пример 8.16). В основной алгоритм включено
несколько дополнительных танцев с бубном, чтобы стимулировать самообучение,
2 См. https://medium.com/emergent-future/simpie-reinforcement-ieariiing-with-tensorflow-part-8asynchronous-actor-critic-agents-a3c-c88f72a5e9f2 с подробным описанием формализации алгорит­
ма. — Прим. пер.
206
|
Гпава 8
в частности энтропийный член и поддержка обобщенного оценивания преиму­
ществ. Мы не будем раскрывать все эти детали, но для того чтобы получше в них
разобраться, рекомендуем вам перейти по ссылкам на исследовательскую литера­
туру (перечисленную в описании класса в коде).
| Пример 8.16. Определить класс АЗС, инкапсулирующий тренировку алгоритма
i с асинхронной максимизацией подкрепления АЗС
class АЗС(object):
ипп
Реализует алгоритм асинхронной критики стратегии с учетом
преимущества (АЗС).
Алгоритм описан в Mnih V. et al. "Asynchronous Methods for Deep
Reinforcement Learning"
("Асинхронные методы глубокого самообучения
с максимизацией подкрепления", см. https://arxiv.org/abs/1602.01783 ).
Этот класс требует, чтобы стратегия выводила две величины: вектор,
дающий вероятность выполнения каждого действия, и оценку функции
стоимости для текущего состояния. Он оптимизирует оба выходных значения
одновременно, используя потерю, которая представляет собой
сумму трех слагаемых.
1. Потеря стратегии; она пытается максимизировать дисконтированное
вознаграждение за каждое действие.
2. Потеря стоимости; она пытается подогнать оценку стоимости
под фактическое дисконтированное вознаграждение,
которое было
достигнуто на каждом шаге.
3. Энтропийный член для стимулирования обследования.
Этот класс поддерживает среды только с дискретными (не непрерывными)
пространствами действий. Аргумент "action", передаваемый в среду,
является целочисленным индексом выполняемого действия.
Этот класс поддерживает обобщенное оценивание преимуществ, как описано
в Schulman et al. "High-Dimensional Continuous Control Using
Generalized Advantage Estimation"
("Многомерное непрерывное управление
с использованием обобщенного оценивания преимуществ",
см. https://arxiv.org/abs/1506.02438 ).
Это метод нахождения компромиссного соотношения между смещением и
дисперсией в оценке преимуществ, который иногда может приводить
к улучшению скорости схождения. Для настройки компромисса используйте
параметр advantage_lambda.
II II II
self._env = env
self.max_rollout_length = max_rollout_length
self.discount_factor = discount-factor
Самообучение с максимизацией подкрепления
|
207
self.advantage_lambda = advantage_lambda
self.value_weight = value_weight
self.entropy_weight = entropy_weight
self.-Optimizer = None
(self._graph, self.-features, self._rewards, self._actions,
self._actionjprob, self._value, self.-advantages) = self.build_graph(
None,
"global”, model_dir)
with self._graph._get_tf("Graph").as_default():
self._session = tf.SessionO
В самом центре класса азс находится метод build graph () (пример 8.17), создающий
экземпляр TensorGraph (внутри которого лежит вычислительный граф TensorFlow),
кодирующий заученную моделью стратегию. Обратите внимание на то, насколько
сжато это определение по сравнению с другими, которые вы видели ранее! Исполь­
зование объектной ориентированности имеет много преимуществ.
i Пример 8.17. Этот метод строит вычислительный граф для алгоритма АЗС.
I Обратите внимание, что стратегическая сеть здесь определяется
на основе слоевой абстракции Layer, которую вы встречали ранее
def build_graph(self, tf_graph, scope, model_dir):
""’’Сконструировать TensorGraph, содержащий вычисления стратегии
и потери.’””’
state_shape = self._env.state_shape
features = []
for s in state_shape:
features.append(Input(shape=[None] + list(s), dtype=tf.float32))
dl = Flatten(in_layers=features)
d2 = Dense(
in_layers=[dl],
activation_fn=tf.nn.relu,
normalizer_fn=tf.nn.12_normalize,
normalizer_params={"dim": 1},
out_channels=64)
d3 = Dense(
in_layers=[d2],
activation_fn=tf .nn.relu,
normalizer_fn=tf .nn.12_normalize,
normalizer_params={ "dim": 1},
out_channels=32)
d4 = Dense(
in_layers=[d3],
activation_fn=tf.nn.relu,
normalizer_fn=tf.nn.12_normalize,
normalizer_params ={"dim": 1},
out_channe1s=16)
208
|
Гпава 8
d4 = BatchNorm(in_layers=[d4])
d5 = Dense(in_layers=[d4] , activation_fn=None, out_channels=9)
value = Dense(in_layers=[d4], activation_fn=None, out_channels=l)
value = Squeeze(squeeze_dims=l, in_layers=[value])
action_prob = SoftMax(in_layers=[d5])
rewards = Input(shape=(None,))
advantages = Input(shape=(None,))
actions = Input(shape=(None, self._env.n_actions) )
loss = A3CLoss(
self.value_weight,
self.entropy_weight,
in_layers=[rewards, actions, actionjprob, value, advantages])
graph = TensorGraph(
batch_size=self.max_rollout_length,
graph=tf_graph,
mode l_di r=mode l_di r)
for f in features:
graph._add-layer(f)
graph.add_output(actionjprob)
graph.add_output(value)
graph.set_loss(loss)
graph.set—optimi zer(self.—Optimizer)
with graphget_tf("Graph”).as_default():
with tf.variable_scope(scope):
graph.buildO
return graph, features, rewards, actions, action_prob, value, advantages
В этом примере много программного кода. Давайте разберем его на несколько
примеров и обсудим подробнее. Пример 8.18 принимает кодировку среды
TicTacToeEnvironment в виде массива и передает ее непосредственно в экземпляры
input для графа.
Пример 8.18. Этот фрагмент из метода buiid_graph() передает
кодировку среды TicTacToeEnvironment в виде массива
state_shape = self ._env.state_shape
features = []
for s in state_shape:
features.append(Input(shape=[None] +list(s), dtype=tf.float32))
В примере 8.19 показан фрагмент кода, используемый с целью конструирования
входов input для вознаграждений из среды, наблюдаемых преимуществ и выпол­
няемых действий.
Самообучение с максимизацией подкрепления
|
209
Пример 8.19. Этот фрагмент из метода build_graph() определяет объекты input
для вознаграждений, преимуществ и действий
I
I
rewards = Input(shape=(None,))
advantages = Input(shape=(None,))
actions = Input(shape=(None, self._env.n_actions))
Стратегическая сеть отвечает за заучивание стратегии. В примере 8.20 входное со­
стояние игрового поля сначала сглаживается в простой входной вектор признаков.
К сглаженному игровому полю применяется ряд полносвязных (или Dense) преобра­
зований. В самом конце используется слой Softmax для предсказания вероятностей
действий из d5 (обратите внимание, что аргументу out channeis присвоено зна­
чение 9, по одному для каждого возможного хода на игровом поле крестиковноликов).
Пример 8.20. Этот фрагмент из метода build-graph() определяет
стратегическую сеть
dl = Flatten(in_layers=features)
d2 = Dense(
in_layers=[dl],
activation_fn=tf .nn.relu,
normalizer_fn=tf .nn.12_normalize,
normalizer_params={"dim": 1},
out_channels=64 )
ci3 = Dense (
in_layers=[d2],
activation_fn=tf.nn.relu,
normalizer_fn=tf.nn.12_normalize,
normalizer_params={"dim": 1},
out_channels=32 )
d4 = Dense(
in_layers=[d3],
activation_fn=tf.nn.relu,
normalizer_fn=tf.nn.12_normalize,
normalizer_params={"dim": 1},
out_channels=16)
d4 = BatchNorm(in_layers=[d4])
d5 = Dense(in_layers=[d4], activation_fn=None, out_channels=9)
value = Dense(in_layers=[d4], activation_fn=None, out_channels=l)
value = Squeeze(squeeze_dims=l, in_layers=[value] )
action_prob = SoftMax(in_layers=[d5] )
210
|
Глава 8
Конструирование признаков действительно мертво?
В этом разделе мы передаем сырое игровое поле крестиков-ноликов
в TensorFlow, чтобы натренировать стратегию. Вместе с тем важно отме­
тить, что для более сложных, чем крестики-нолики, игр это может не дать
удовлетворительных результатов. Одним из менее известных фактов
о программе AlphaGo является то, что для облегчения программе AlphaGo
самообучения компания DeepMind выполняет сложное конструирование
признаков для извлечения ’’интересных” позиций го на доске. (Этот факт
спрятан в информации, прилагаемой к исследовательской работе компании
DeepMind.)
Факт остается фактом, но самообучение с максимизацией подкрепления
(и методы глубокого самообучения в целом) часто по-прежнему нуждают­
ся в человеко-ориентированном конструировании признаков для извлече­
ния содержательной информации, прежде чем обучающиеся алгоритмы
будут способны обучаться эффективным стратегиям и моделям. Вполне
вероятно, что с ростом вычислительных мощностей, доступных благодаря
совершенствованию аппаратного обеспечения, эта потребность в конст­
руировании признаков будет уменьшаться, но в ближайшем будущем пла­
нируйте вручную извлекать информацию о ваших системах, необходимую
для повышения результативности.
Функция потери
Теперь объектно-ориентированный механизм находится на своем месте, и все гото­
во для определения потери в стратегической сети АЗС. Данная функция потери
будет реализована как объект Layer (удобная абстракция— все части глубокой
архитектуры являются просто слоями). Объект A3CLoss реализует математическую
потерю, состоящую из суммы трех членов: policy loss, value loss и entropy (при­
мер 8.21).
Пример 8.21. Слой, реализующий функцию потери для АЗС
class A3CLoss(Layer):
’’’’’’Это слой вычисляет функцию потери для АЗС.’"”’
def __ init__ (self, value_weight, entropy_weight, **kwargs):
super(A3CLoss, self).__ init__ (**kwargs)
self.value_weight = value_weight
self,entropy_weight = entropy_weight
def create_tensor(self,
**kwargs):
reward, action, prob, value, advantage = [
layer.out_tensor for layer in self.in_layers
]
prob = prob + np.finfo(np.float32).eps
log_prob = tf.log(prob)
Самообучение с максимизацией подкрепления
|
211
policy_loss = -tf.reduce_mean(
advantage * tf.reduce_surn(action * log_prob, axis=l))
value_loss = tf.reduce_mean(tf.square(reward - value))
entropy = -tf.reduce_mean(tf.reduce_surn(prob * log_prob, axis=l))
self.out_tensor = policy_loss + self.value_weight * value_loss \
- self.entropy_weight * entropy
return self.out tensor
В приведенном выше определении есть много частей, поэтому давайте вытащим
фрагменты кода и обследуем их. Слой A3CLoss принимает слои reward, action, prob,
value, advantage в качестве входных данных. Для математической устойчивости мы
конвертируем вероятности в логарифмические вероятности (это численно гораздо
более стабильно). См. пример 8.22.
Пример 8.22. Этот фрагмент кода из слоя АЗсьозз принимает вознаграждение,
действие, вероятность, стоимость, преимущество как входные слои
и вычисляет логарифмическую вероятность
|
j
reward, action, prob, value, advantage = [
layer.out_tensor for layer in self.in_layers
prob = prob + np.finfo(np.float32).eps
log_prob = tf.log(prob)
Функция потери для стратегии вычисляет сумму всех наблюдаемых преимуществ,
взвешенную на логарифмическую вероятность выполняемого действия. (Напом­
ним, что преимуществом является разница между вознаграждением за выполнение
конкретного действия и ожидаемым вознаграждением сырой стратегии для данного
состояния.) Идея здесь заключается в том, что poiicy ioss дает сигнал о том, какие
действия были выгодными, а какие нет (пример 8.23).
Пример 8.23. Этот фрагмент кода из слоя A3CLoss определяет стратегическую потерю I
policy_loss = -tf.reduce_mean(
advantage * tf.reduce_surn(action ★ log_prob, axis=l))
Потеря стоимости вычисляет разницу между нашей оценкой вознаграждения V
и фактическим наблюдаемым значением V (value). Обратите внимание на
применение здесь I? -потери (пример 8.24).
(reward)
Пример 8.24. Этот фрагмент кода из слоя АЗСЬозз определяет потерю стоимости
I
value_ioss = tf.reduce_mean(tf.square(reward - value))
Энтропийный член является дополнением, которое побуждает стратегию иссле­
довать дальше, добавляя небольшой шум. Этот член фактически является формой
212
|
Глава 8
регуляризации для АЗС-сетей. Окончательная потеря, вычисляемая функцией
представляет собой линейную комбинацию потерь этих компонентов (при­
мер 8.25).
A3CLoss,
Пример 8.25. Этот фрагмент кода из слоя азсьозэ определяет энтропийный член,
добавленный в потерю
entropy = -tf.reduce_mean(tf.reduce_sum(prob * log_prob, axis=l))
Определение рабочих процессов
К настоящему времени вы узнали, как строится стратегическая сеть, но еще не уви­
дели, как реализуется процедура асинхронной тренировки. Концептуально асин­
хронная тренировка состоит из отдельных рабочих процессов, выполняющих
градиентный спуск на локально симулируемых разворачиваниях игровой ситуации
и периодически возвращающих полученные знания в глобальный набор весов.
Продолжая наше объектно-ориентированное проектирование, давайте представим
класс Worker.
Каждый экземпляр класса worker содержит копию модели, которая тренируется
асинхронно в отдельном потоке (пример 8.26). Обратите внимание, что
a3c.buiid_grapho используется для конструирования локальной копии вычисли­
тельного графа TensorFlow для рассматриваемого потока. Здесь обратите особое
внимание на locai vars и giobai vars. Нам нужно обеспечить тренировку только тех
переменных, которые связаны с копией стратегии этого рабочего процесса, а не
с глобальной копией переменных (которая используется для обмена информацией
между рабочими потоками). Как следствие, градиенты используют функцию
tf. gradients о, которая берет градиенты потери относительно только локальных
переменных.
Пример 8.26. Класс Marker реализует вычисление, выполняемое каждым потоком
class Worker(object) :
"""Объект Worker создается для каждого тренировочного потока."""
def __ init__ (self, аЗс, index):
self.a3c = аЗс
self.index = index
self.scope = "worker%d" % index
self.env = copy.deepcopy(a3c._env)
self.env.reset()
(self.graph, self.features, self.rewards,
self-actions, self.action_prob,
self.value, self.advantages) = a3c.build_graph(
a3c._graph._get_tf("Graph") , self.scope, None)
Самообучение с максимизацией подкрепления
|
213
with a3c._graph._get_tf ("Graph").as_default():
local_vars = tf.get_collection(
tf.GraphKeys.TRAI NABLE_VARI ABLES,
self.scope)
global_vars = tf.get_collection(
tf.GraphKeys.TRAINABLE_VARIABLES,
"global")
gradients = tf.gradients(self.graph.loss.out_tensor,
local_vars)
grads_and_vars = list(zip(gradients, global_vars))
self.train_op = a3c._graph._get_tf(
"Optimizer").apply_gradients(
grads_and_vars)
self.update_local_variables = tf.group(
* [tf.assign(vl, v2)
for vl, v2 in zip(local_vars,
global_vars)])
self.global_step = self.graph.get_global_step()
Разворачивание игровой ситуации
в рабочих процессах
Каждый рабочий процесс отвечает за симулирование разворачивания игровой си­
туации локально. Метод create_rollout() использует session.run для выборки из
графа TensorFlow вероятностей действий (пример 8.27). Затем он получает образец
действия из этой стратегии, используя функцию NumPy np.random.choice о,
взвешенную на вероятности каждого класса. Вознаграждение за выполняемое
действие вычисляется из среды TicTacToeEnvironment посредством ВЫЗОВа self .env.
step(action).
Пример 8.27. Метод create_roilout симулирует локальное разворачивание
игровой ситуации
def create_rollout(self ):
"""Сгенерировать разворачивание игровой ситуации."""
n_actions = self.env.n_actions
session = self.аЗс._session
states = []
actions = []
rewards = []
values = []
# Генерация разворачивания.
for i in range(self.a3c.max_rollout_length) :
if self.env.terminated:
break
state = self.env.state
214
|
Глава 8
states.append(state)
feed_dict = self,create_feed_dict(state)
results = session.run(
[self,action_prob.out_tensor, self.value.out_tensor],
feed_dict=feed_dict)
probabilities, value = results[:2]
action = np.random.choice(np .arange(n_actions), p=probabilities[0])
actions.append(action)
values.append(float(value) )
rewards.append(self.env.step(action))
# Вычислить оценку вознаграждения для остального эпизода,
if not self.env.terminated:
feed_dict = self,create_feed_dict(self.env.state)
final_value = self,a3c.discount-factor * float(
session.run(self.value.out_tensor,
feed_dict))
else:
final_value =0.0
values.append(final_value)
if self.env.terminated:
self.env.reset()
return states, actions, np.array(rewards), np.array(values)
Метод process—rollouts () выполняет предобработку, необходимую для вычисления
дисконтированных вознаграждений, стоимостей, действий и преимуществ (при­
мер 8.28).
I Пример 8.28. Метод processjrollouts() вычисляет вознаграждения, стоимости,
| действия и преимущества и затем делает шаг градиентного спуска
относительно потери
def process_rollout(self, states, actions, rewards, values, step_count):
’’’’’’Натренировать сеть на основе разворачивания.”””
# Вычислить дисконтированные вознаграждения и преимущества.
if len(states) == 0:
# В многопоточной среде создать разворачивание иногда не удается.
# Не обрабатывать, если испорчено.
print(’’Создать разворачивания не получилось. Пропускаю”)
return
discounted_rewards = rewards.copy()
discounted_rewards[-1] += values[-1]
advantages = rewards - values[:-l] +
self,a3c.discount-factor * np.array(values[1:])
Самообучение с максимизацией подкрепления
|
215
for j in range(len(rewards) - 1, 0, -1):
discounted_rewards[j-1] += self.a3c.discount-factor * \
discounted_rewards[j]
advantages[j-1] += (
self.a3c.discount-factor *
self.a3c.advantage_lambda * advantages[j])
# Конвертировать действия в кодировку с одним активным состоянием.
n_actions = self.env.n_actions
actions_matrix = []
for action in actions:
a = np.zeros(n_actions)
a[action] = 1.0
actions_matrix.append(a)
# Перераспределить состояния в соответствующий набор массивов.
state_arrays = [[] for i in range(len(self.features))]
for state in states:
for j in range(len(state)):
state_arrays[j].append(state[j])
# Построить словарь передачи данных и применить градиенты.
feed_dict = {}
for f,
s
in zip(self.features, state_arrays) :
feed_dict[f.out_tensor] = s
feed_dict[self.rewards.out_tensor] = discounted_rewards
feed_dict[self.actions.out_tensor]
= actions_matrix
feed_dict[self.advantages.out_tensor] = advantages
feed_dict[self.global_step] = step_count
self.a3c._session.run(self.train_op, feed_dict=feed_dict )
Метод worker, run о выполняет тренировочный шаг для объекта worker, опираясь
на то, что метод process_roiiout() за кадром сделает фактический вызов метода
self.аЗс._session.run() (пример 8.29).
i Пример 8.29. Метод run О является высокоуровневой активацией объекта Worker
def run(self, step_count, total_steps):
with self.graph._get_tf("Graph").as_default():
while step_count[0] < total_steps:
self.a3c._session.run(self.update_local_variables )
states, actions, rewards, values = self.create_rollout ()
self.process_rollout(states, actions, rewards, values,
step_count[0])
step_count[0] += len(actions)
216
|
Глава 8
Тренировка стратегии
Метод азс. fit о объединяет все разрозненные части, созданные для тренировки
модели. Метод fit о берет на себя ответственность за порождение рабочих потоков
Worker, используя ДЛЯ ЭТОГО библиотеку Python threading. Поскольку каждый рабо­
чий процесс worker берет на себя ответственность за тренировку как таковую, метод
fit о просто отвечает за периодическое сохранение на диск состояния натрениро­
ванной модели в контрольной точке (пример 8.30).
- --
'
-
.
, Пример 8.30. Метод fit() объединяет все вместе
[и выполняет тренировочный алгоритм АЗС
def fit(self,
total_steps,
max_checkpoints_to_keep=5,
checkpoint_interval=600 ,
restore=False):
’’’’’’Натренировать стратегию.
Параметры
tоtal_s teps: целочисленный;
общее количество временных шагов, выполняемых в среде, по всем
разворачиваниям во всех потоках.
max_checkpoints_to_keep : целочисленный;
максимальное количество хранимых файлов контрольных точек.
Когда достигается это число, более старые файлы удаляются,
checkpoint-interval: вещественный;
временной интервал в секундах, в котором нужно сохранять
контрольные точки
restore: булевый;
если True, то восстановить модель из самой последней контрольной
точки и продолжить тренировку оттуда. Если False, то
натренировать модель заново.
it it it
with self._graph._get_tf("Graph").as_default():
step_count = [0]
workers = []
threads = []
for i in range(multiprocessing.cpu_count()):
workers.append(Worker(self, i))
self._session.run(tf.global_variables_initializer())
if restore:
self.restore()
for worker in workers:
thread = threading.Thread(
name=worker.scope,
target=lambda : worker.run(step_count, total_steps))
Самообучение с максимизацией подкрепления
|
217
threads.append(thread)
thread.start()
variables = tf.get_collection(tf.GraphKeys.GLOBAL-VARIABLES,
scope=’’global")
saver = tf.train.Saver(variables,
inax_to_keep=max_checkpoints —to_keep)
checkpoint-index = 0
while True:
threads = [t for t in threads if t.isAliveO]
if len(threads) > 0:
threads[0].j oin(checkpoint-interval)
checkpoint-index += 1
saver.save(
self.—Session, self._graph.save_file,
global_s tep=checkpoint_index)
if len(threads) == 0:
break
Задание для читателя
Мы настоятельно рекомендуем вам попробовать натренировать модель игры кре­
стики-нолики самостоятельно! Обратите внимание, что этот пример более запутан,
чем другие примеры в книге, и потребует большей вычислительной мощности. Ре­
комендуется использовать компьютер с несколькими ядрами CPU. Это техническое
требование не особо обременительно; хорошего ноутбука должно быть достаточно.
Попробуйте использовать такой инструмент, как htop, чтобы удостовериться, что
программный код действительно многопоточный. Убедитесь в качестве модели,
которую вы тренируете! Подавляющую часть времени вы должны будете одержи­
вать верх над случайным ориентиром, но эта элементарная реализация не даст вам
модель, которая всегда выигрывает. Мы рекомендуем изучить публикации по
самообучению с максимизацией подкрепления и расширить базовую реализацию,
чтобы посмотреть, насколько хорошо вы можете это сделать.
Резюме
В этой главе мы познакомили вас с основными понятиями самообучения с макси­
мизацией подкрепления. Мы провели вас через некоторые недавние успехи мето­
дов самообучения с максимизацией подкрепления на аркадных играх ATARI, в по­
лете вертолета в перевернутом положении и в компьютерном го. Затем мы расска­
зали вам о математическом каркасе марковских процессов принятия решений. Мы
рассмотрели его с учетом подробного практического примера, проведя вас по про­
цессу конструирования агента игры крестики-нолики. В этом алгоритме применя­
ется сложный метод тренировки АЗС, который для ускорения процесса тренировки
использует несколько ядер процессора.
В главе 9 вы подробнее познакомитесь с тренировкой моделей с помощью несколь­
ких графических процессоров.
218
|
Глава 8
ГЛАВА 9
Тренировка
крупных глубоких сетей
Итак, теперь вы знаете, как можно полностью натренировать небольшие модели на
хорошем ноутбуке. Все эти модели могут успешно выполняться на оснащенном
графическими процессорами (GPU) оборудовании с заметным повышением скоро­
сти (за исключением моделей на основе самообучения с максимизацией подкреп­
ления по причинам, рассмотренным в предыдущей главе). Однако тренировка
более крупных моделей по-прежнему требует значительной доработки. В этой главе
мы обсудим различные типы оборудования, которые могут быть использованы для
тренировки глубоких сетей, в том числе графические процессоры (GPU), тензорные
процессоры (TPU) и нейроморфные чипы. Мы также кратко рассмотрим принципы
распределенной тренировки для более крупных глубоко обучающихся моделей.
А закончим главу углубленным практическим примером, взятым и адаптирован­
ным из одного из учебных пособий TensorFlow, демонстрирующим метод трени­
ровки сверточной нейронной сети CIFAR-10 на многопроцессорном сервере для
GPU-вычислений. Мы рекомендуем вам попробовать выполнить этот программный
код самостоятельно, но с готовностью признаем, что получить доступ к многопро­
цессорному серверу для GPU-вычислений сложнее, чем найти хороший ноутбук.
К счастью, доступ к многопроцессорным серверам для GPU-вычислений в облаке
становится возможным и, судя по всему, является наилучшим решением для про­
мышленных потребителей TensorFlow, пытающихся тренировать большие модели.
Специальное аппаратное обеспечение
для глубоких сетей
Как вы смогли убедиться на протяжении всей книги, тренировка глубокой сети
требует цепочек тензорных операций, выполняемых неоднократно на мини-пакетах
данных. Тензорные операции обычно преобразуются в операции матричного ум­
ножения с помощью программного обеспечения, поэтому скорость тренировки
глубоких сетей принципиально зависит от способности быстро выполнять опера­
ции матричного умножения. В то время как центральные процессоры (central
processing unit, CPU) прекрасно справляются с реализацией матричных умножений,
универсальность оборудования CPU означает, что много усилий будет потрачено
на ненужные для математических операций накладные расходы.
Инженеры аппаратного обеспечения отмечали этот факт многие годы, и существует
целый ряд альтернативного аппаратного обеспечения для работы с глубокими
219
сетями. Такое оборудование можно в целом подразделить на две категории: только
вывод и тренировка и вывод. Аппаратное обеспечение только для вывода не может
применяться для тренировки новых глубоких сетей, но может использоваться для
внедрения натренированных моделей в производственной среде, что позволяет по­
тенциально увеличить производительность на порядок. Оборудование для трени­
ровки и вывода позволяет тренировать модели исходно. В настоящее время GPUоборудование компании NVIDIA занимает доминирующее положение на рынке
аппаратного обеспечения для тренировки и вывода из-за значительных инвестиций
в программное обеспечение и образовательно-информационной деятельности ко­
манд разработчиков NVIDIA, но ряд других конкурентов уже начинают "наступать
на пятки" GPU. В этом разделе мы кратко рассмотрим некоторые из этих новых
аппаратных средств. Помимо GPU и CPU, подавляющая часть этих альтернативных
форм аппаратного обеспечения еще не получили широкую доступность, поэтому
значительная часть этого раздела ориентирована на будущее.
Тренировка на CPU
Хотя тренировка на CPU ни в коем случае не является последним словом в трени­
ровке глубоких сетей, она часто довольно хорошо подходит для небольших моде­
лей (как вы убедились на своем личном опыте, читая эту книгу). Для задач само­
обучения с максимизацией подкрепления многоядерный CPU может даже превос­
ходить тренировку на GPU.
CPU также пользуются широкой популярностью для приложений глубоких сетей
категории "только вывод". Большинство компаний вложили значительные средства
в разработку облачных серверов, построенных в основном на серверах Intel. Весьма
вероятно, что первое поколение глубоких сетей, развернутых повсеместно (за пре­
делами технологических компаний), будет в основном внедрено в производствен­
ной среде на таких серверах Intel. Несмотря на то что внедрение на базе CPU не
является достаточным для высокопроизводительного развертывания обучающихся
моделей, его нередко достаточно для первых потребностей клиентов. На рис. 9.1
показан стандартный процессор Intel.
Рис. 9.1. CPU от Intel. Центральные процессоры по-прежнему являются доминирующей формой
компьютерного оборудования, которая присутствует во всех современных ноутбуках, настольных
компьютерах, серверах и телефонах. Подавляющая часть программного обеспечения написана
для работы на CPU. Числовые вычисления (твкие как тренировка нейронной сети) могут исполняться
на CPU, но при этом могут быть медленнее, чем на специальном аппаратном обеспечении,
оптимизированном для численных методов
220
|
Гпава 9
Тренировка на GPU
GPU (graphics processing unit — графический процессор) изначально были спроек­
тированы для выполнения вычислений, требующихся графическому сообществу.
По случайному стечению обстоятельств оказалось, что примитивы, используемые
для определения графических шейдеров, могут быть перепрофилированы для глу­
бокого самообучения. В своих математических основах графика и машинное само­
обучение опираются на матричные умножения. В эмпирическом плане матричные
умножения на GPU предлагают ускорения, на порядок или два порядка превы­
шающие реализации CPU. Каким образом GPU добились такого успеха? Хитрость
в том, что GPU используют тысячи идентичных потоков. Умные хакеры преуспели
в разложении матричного умножения на параллельные операции, которые могут
предложить резкое ускорение. На рис. 9.2 представлена архитектура GPU.
Рис. 9.2. Архитектура GPU от компании NVIDIA. GPU обладают намного большим количеством ядер,
чем CPU, и хорошо подходят для выполнения численных линейно-алгебраических методов, которые
подходят для графических вычислений и вычислений, связанных с машинным самообучением.
Графические процессоры проявили себя в качестве доминирующей аппаратной платформы
для тренировки глубоких сетей
Хотя сегодня существует ряд поставщиков GPU, в настоящее время на рынке GPU
доминирует компания NVIDIA. Подавляющая часть мощности GPU этой компании
связана с собственной библиотекой CUDA (compute unified device architecture, про­
Тренировка крупных глубоких сетей
|
221
граммно-аппаратной архитектурой параллельных вычислений), которая предлагает
примитивы, облегчающие написание программ на GPU. Компания NVIDIA предла­
гает расширение CUDA под названием CUDNN, предназначенное для ускорения
глубоких сетей (см. рис. 9.2). TensorFlow имеет встроенную поддержку расширения
CUDNN, поэтому для ускорения ваших сетей вы также можете использовать
CUDNN посредством TensorFlow.
Насколько важны размеры транзисторов?
В течение многих лет полупроводниковая промышленность отслеживала
рост скоростей чипов, наблюдая за размерами транзисторов. По мере того
как транзисторы становились все меньше, все больше чипов могло быть
упаковано в стандартную микросхему, и алгоритмы могли работать быст­
рее. На момент написания этой книги компания Intel оперирует с 10-нанометровыми транзисторами и работает над переходом на 7 нм. В последние
годы темп сжатия размеров транзисторов значительно замедлился, по­
скольку в таких масштабах возникают огромные проблемы с рассеиванием
тепла.
GPU компании NVIDIA частично преодолели эту тенденцию. В них, как
правило, используются размеры транзисторов с отставанием на одно-два
поколения от лучших транзисторов Intel, и эти GPU ориентированы на ре­
шение проблем, связанных с узкими местами в архитектурах и программ­
ном обеспечении, а не на проектировании транзисторов. До настоящего
времени стратегия компании NVIDIA приносила дивиденды, и компания
достигла доминирования на рынке в сегменте чипов для машинного само­
обучения.
Пока не ясно, как далеко могут зайти архитектурные и программные опти­
мизации. Столкнутся ли вскоре оптимизации GPU с теми же препятствия­
ми закона Мура, что и CPU? Или же умные архитектурные инновации по­
зволят годами ускорять работу GPU? Покажет только время.
Тензорные процессоры
Тензорный процессор (tensor processing unit, TPU) — это специализированная инте­
гральная микросхема (application specific integrated circuit, ASIC), разработанная
компанией Google для того, чтобы увеличить рабочие нагрузки глубокого самообу­
чения, предусмотренные в TensorFlow. В отличие от GPU, TPU-блок имеет урезан­
ную конфигурацию и реализует только абсолютный минимум "on-die"1, требуемый
для выполнения необходимых операций матричного умножения. В отличие от
GPU, TPU-блок зависит от смежного CPU, который выполняет для него подавляю­
щую часть предобработки. Такой сжатый подход позволяет TPU-блоку достигать
более высоких скоростей, чем GPU, при более низких энергетических затратах.
1 On-die termination (ODT) — это технология, где резистивный терминатор размещается внутри полу­
проводникового чипа, а не на печатной плате.
См. https://en.wikipedia.org/wiki/On-die_termination. — Прим. пер.
222
|
Гпава 9
Первая версия TPU допускала вывод только на натренированных моделях, но самая
последняя версия (TPU2) уже допускает тренировку (определенных) глубоких
сетей. Вместе с тем, компания Google опубликовала не так уж много подробностей
о TPU, и доступ ограничен сотрудниками Google, при этом имеются планы вклю­
чить доступ к TPU через облако Google. Компания NVIDIA приняла к сведению
TPU, и вполне вероятно, что будущие выпуски ее GPU будут напоминать TPU, по­
этому пользователи на первых этапах производственного цикла, скорее всего, вы­
играют от инноваций Google независимо от того, кто одержит победу в борьбе за
потребительский рынок глубокого самообучения: Google или NVIDIA.
На рис. 9.3 показан архитектурный проект TPU.
Рис. 9.3. Архитектура тензорного процессора (TPU) компании Google.
TPU — это специализированные чипы, спроектированные компанией Google в целях ускорения
рабочих нагрузок глубокого самообучения. TPU представляет собой сопроцессор и не является
автономной частью аппаратного обеспечения
Что такое микросхемы ASIC?
И CPU, и GPU являются универсальными чипами. CPU обычно поддержи­
вают наборы инструкций в скомпонованном блоке и спроектированы так,
чтобы быть универсальными. Особое внимание уделяется обеспечению
широкого спектра применений. GPU менее универсальны, но по-прежнему
Тренировка крупных глубоких сетей
|
223
позволяют реализовывать широкий спектр алгоритмов на таких языках,
как CUDA.
Специализированные интегральные микросхемы (Application specific
integrated circuits, ASIC) пытаются отойти от универсальности в пользу
концентрации на потребностях конкретного применения. Исторически
сложилось так, что микросхемы ASIC добились лишь ограниченного про­
никновения на рынок. Барабанная дробь закона Мура означала, что уни­
версальные CPU отставали всего на дыхание или два от специализирован­
ных ASIC, и поэтому накладные расходы, связанные с аппаратным проек­
тированием, часто не стоили усилий.
Такое положение дел начало меняться в последние несколько лет. Замед­
ление сжатия транзисторов привело к расширению использования ASIC.
Например, майнинг биткойнов полностью зависит от специализированных
микросхем ASIC, которые реализуют специализированные криптографи­
ческие операции.
Программируемые пользователем
вентильные матрицы
Программируемые пользователем вентильные матрицы (field programmable gate
arrays, FPGA) являются подвидом "программируемых на месте" микросхем ASIC.
Стандартные FPGA часто можно перенастроить с помощью языков описания аппа­
ратных средств, таких как Verilog, для динамической реализации новых проектов
ASIC. FPGA обычно менее эффективны, чем специализированные ASIC, но их
совершенствование может значительно повысить скорость обработки данных
по сравнению с реализациями на основе CPU. Компания Microsoft, в частности,
использовала FPGA для реализации вывода глубокого самообучения и заявляет,
что достигла значительного роста скорости их развертывания. Однако этот подход
пока еще не завоевал широкую популярность за пределами Microsoft.
Нейроморфные чипы
"Нейроны" в глубоких сетях математически моделируют понимание нейрональной
биологии 1940-х годов. Излишне говорить, что биологическое понимание нейро­
нального поведения с тех пор значительно продвинулось. Прежде всего теперь
известно, что нелинейные активации, используемые в глубоких сетях, не являются
точными моделями нейронной нелинейности. "Импульсные цепи" (spike trains) —
более оптимальная модель (рис. 9.4), в которой нейроны активируются в виде крат­
ковременных всплесков (пиков), но чаще всего падают на задний план.
Инженеры аппаратного обеспечения потратили значительные усилия на изучение
возможности создания импульсно-цепных микросхем, отличающихся от сущест­
вующей схемотехники (CPU, GPU, ASIC). Эти разработчики утверждают, что
современные конструкции чипов страдают от фундаментальных ограничений мощ­
ности; мозг потребляет энергии на много порядков меньше, чем компьютерные
чипы, и умные проекты должны стремиться учиться на архитектуре мозга.
224
|
Гпава 9
Рис. 9.4. Нейроны часто активируются в виде кратковременных всплесков, которые называются
импульсными цепями (а). В нейроморфных чипах предпринята попытка смоделировать поведение
импульсных цепей в вычислительном аппаратном оборудовании. Биологические нейроны
представляют собой сложные сущности (6), поэтому такие модели по-прежнему остаются
лишь приблизительными
Ряд проектов в этой области привел к построению больших импульсно-цепных чи­
пов, в которых предпринята попытка развить этот ключевой тезис. Проект
TrueNorth компании IBM преуспел в создании импульсно-цепных процессоров
с миллионами "нейронов" и продемонстрировал, что это оборудование может вы­
полнять элементарное распознавание изображений со значительно меньшими по­
требностями к питанию, чем у существующих конструкций микросхем. Однако,
несмотря на эти успехи, пока непонятно, каким образом транслировать современ­
ные глубокие архитектуры на импульсно-цепные чипы. Без возможности "компи­
лировать" модели TensorFlow на импульсно-цепное оборудование маловероятно,
что такие проекты получат широкое распространение в ближайшем будущем.
Распределенная тренировка глубоких сетей
В предыдущем разделе мы рассмотрели различные варианты аппаратного обеспе­
чения для тренировки глубоких сетей. Вместе с тем большинство организаций,
скорее всего, будут иметь доступ только к CPU и, возможно, к GPU. К счастью,
существует возможность выполнять распределенную тренировку глубоких сетей,
где несколько CPU или GPU совместно используются для тренировки моделей
быстрее и эффективнее. На рис. 9.5 показаны две основные парадигмы тренировки
глубоких сетей с многочисленными CPU/GPU, а именно тренировка с параллелиз­
мом данных и тренировка с параллелизмом моделей. Вы узнаете об этих методах
более подробно в следующих двух разделах.
Тренировка крупных глубоких сетей |
225
Параллелизм данных
Параллелизм модели
Полная
глубокая
сеть на каждом
процессоре
Рис. 9.5. Параллелизм данных и параллелизм моделей — два главных режима распределенной
тренировки глубоких архитектур. Тренировка с параллелизмом данных разбивает большие наборы
данных по многочисленным вычислительным узлам, в то время как тренировка
с параллелизмом моделей разбивает по многочисленным узлам большие модели
Параллелизм данных
Параллелизм данных является наиболее распространенным типом тренировки мно­
гоузловой глубокой сети. Модели с параллелизмом данных распределяют большие
наборы данных по разным машинам. Большинство узлов являются рабочими и
имеют доступ к фрагментам общих данных, используемых для тренировки сети.
Каждый рабочий узел имеет полную копию тренируемой модели. Один узел назна­
чается в качестве супервизора, который через регулярные промежутки времени со­
бирает обновленные веса от рабочих узлов и передает усредненные версии весов
назад рабочим узлам. Обратите внимание, что в этой книге вы уже встречали при­
мер с параллелизмом данных; реализация алгоритма АЗС, представленная в главе 8,
является простым примером тренировки глубокой сети с параллелизмом данных.
В качестве исторической справки: предшественник TensorFlow компании Google,
DistBelief, был основан на тренировке с параллелизмом данных на серверах для
CPU-вычислений. Эта система была способна достигать распределенных скоростей
CPU (используя 32-128 узлов), которые соответствовали или превышали скорости
тренировки на GPU. На рис. 9.6 представлен метод тренировки с параллелизмом
данных, реализованный в DistBelief. Однако успех таких систем, как DistBelief, как
правило, зависит от наличия сетевых соединений с высокой пропускной способ­
ностью, которые обеспечивают быстрый обмен модельными параметрами. Во многих
организациях отсутствует сетевая инфраструктура, обеспечивающая эффективную
многоузловую тренировку с параллелизмом данных на CPU. Однако, как показыва­
ет пример АЗС, существует возможность выполнять тренировку с параллелизмом
данных на одном узле, используя различные ядра CPU. В случае современных
серверов также имеется возможность выполнять тренировку с параллелизмом дан­
ных с помощью многочисленных GPU, которыми оборудован один-единственный
сервер, как мы покажем вам позже.
226
|
Гпава 9
Параметрический
сервер
w' = и- - г|Лм
Рис. 9.6. Поточный метод стохастического градиентного спуска (SGD) поддерживает
многочисленные точные копии модели и тренирует их на разных подмножествах набора данных.
Заученные веса от этих сегментов периодически синхронизируются с глобальными весами,
хранящимися на параметрическом сервере
Параллелизм моделей
Человеческий мозг является единственным известным примером универсального
интеллектуального аппаратного обеспечения, поэтому, естественно, были проведе­
ны сравнения между сложностью глубоких сетей и сложностью мозга. Простые
аргументы говорят о том, что мозг имеет около 100 млрд нейронов; будет ли по­
строение глубоких сетей с таким количеством "нейронов" достаточным для дости­
жения универсального интеллекта? К сожалению, такие аргументы упускают из
виду то, что биологические нейроны значительно сложнее, чем "математические
нейроны". Поэтому простые сравнения дают мало пользы. Тем не менее создание
более крупных глубоких сетей было основным направлением исследований в тече­
ние последних нескольких лет.
Главная трудность тренировки очень больших глубоких сетей заключается в том,
что GPU, как правило, имеют ограниченную память (обычно десятки гигабайт).
Нейронные сети с несколькими сотнями и более миллионов параметров, даже если
их программировать тщательнейшим образом, невозможно натренировать на одном
GPU из-за требований к объему памяти. Тренировочные алгоритмы с параллелиз­
мом моделей пытаются это ограничение обойти, храня крупные глубокие сети
в памяти многочисленных GPU. Несколько команд разработчиков успешно реали­
зовало эти идеи на массивах GPU, натренировав глубокие сети с миллиардами
параметров. К сожалению, эти модели до сих пор не показали улучшения в произ­
водительности, которые оправдывали бы дополнительные трудности. На данный
момент, судя по всему, повышение экспериментальной легкости от использования
более мелких моделей перевешивает выгоды от параллелизма моделей.
Тренировка крупных глубоких сетей
|
227
Межсоединения памяти и аппаратного обеспечения
Внедрение параллелизма моделей требует наличия очень высокой пропу­
скной способности соединений между вычислительными узлами, т. к. каж­
дое обновление градиента в силу объективных причин требует межузлово­
го обмена данными. Обратите внимание, что в то время как параллелизм
данных требует эффективных межсоединений, операции синхронизации
должны выполняться только спорадически после нескольких локальных
обновлений градиента.
Несколько групп разработчиков использовали межсоединения InfiniBand
(InfiniBand — это высокопроизводительный сетевой стандарт с низкой за­
держкой) или проприетарные межсоединения NVLINK компании NVIDIA,
чтобы попытаться построить такие большие модели. Однако результаты
экспериментов до сих пор были неоднозначными, и требования к оборудо­
ванию для таких систем, как правило, являются дорогостоящими.
Параллельная тренировка на данных CifarlO
с использованием многочисленных GPU
В этом разделе мы дадим вам подробное пошаговое руководство по тренировке па­
раллельной сверточной сети на эталонном наборе CifarlO. Набор данных CifarlO
состоит из 60 тыс. изображений размером 32x32. Этот набор часто используется
Самолет
Автомобиль
Птица
Кошка
Олень
Собака
Лягушка
Лошадь
N
Корабль
Грузовик
Рис. 9.7. Набор данных CifarlO состоит из 60 тыс. изображений, взятых из 10 классов.
Некоторые образцы изображений из различных классов приведены здесь
228
|
Гпава 9
для сопоставительного анализа сверточных архитектур. На рис. 9.7 представлены
образцы изображений из набора данных CifarlO.
Архитектура, которую мы будем использовать в этом разделе, загружает отдельные
копии модельной архитектуры на разные GPU и периодически синхронизирует
заученные веса между ядрами (рис. 9.8).
Рис. 9.8. Архитектура с параллелизмом данных, которую вы натренируете в этой главе
Скачивание и загрузка данных
Метод read cifarioo считывает и выполняет разбор сырых данных CifarlO. В при­
мере 9.1 использована функция tf.FixedLengthRecordReader() ДЛЯ чтения СЫрЫХ
данных из файлов CifarlO.
Пример 9.1. Эта функция читает и выполняет разбор данных
из файлов с сырыми данными CifarlO
def read_cifarlO(filename_queue):
"""Читает и выполняет разбор примеров из файлов данных CifarlO.
Рекомендация: если вы хотите получить N-полосный параллелизм чтения,
вызовите эту функцию N раз. Это даст вам N независимых
объектов, читающих разные файлы и позиции внутри этих файлов, что
позволит лучше перетасовать примеры.
Тренировка крупных глубоких сетей |
229
Аргументы:
filename_queue: очередь строковых значений с именами файлов
для чтения.
Возвращаемые значения:
Объект, представляющий одиночный пример, со следующими
полями:
height: количество строк в результате (32)
width:
количество столбцов в результате (32)
depth:
количество цветных каналов в результате (3)
key:
скалярный строковый тензор, описывающий имя файла
и номер записи для этого примера.
label: целочисленный
(int32) тензор с меткой в диапазоне 0..9.
uint8image:: целочисленный (uint8) тензор в формате
[height, width, depth] с данными изображений
II П II
class CIFARlORecord(object) :
pass
result = CIFARlORecord()
# Размерности изображений в наборе данных CifarlO.
# См. http://www.cs.toronto.edu/~kriz/cifar.html по поводу описания
# входного формата.
label_bytes =1
#2 для CifarlOO
result.height = 32
result.width = 32
result.depth = 3
image_bytes = result.height * result.width * result.depth
# Каждая запись состоит из метки, после которой идет изображение
# с фиксированным количеством байтов.
record_bytes = label_bytes + image_bytes
# Прочитать запись, получив имена файлов из очереди filename_queue.
# В формате CifarlO верхний и нижний колонтитулы отсутствуют, поэтому
# оставляем header_bytes и footer_bytes с их значениями по умолчанию,
# равными 0.
reader = tf.FixedLengthRecordReader (record_bytes=record_bytes )
result.key, value = reader.read(filename_queue)
# Конвертировать из строкового типа в вектор из uint8, который имеет
# длину record_bytes.
record_bytes = tf.decode_raw(value, tf.uint8)
# Прочитать запись, получив имена файлов из очереди filename_queue.
# В формате CifarlO верхний и нижний колонтитулы отсутствуют,
# поэтому мы оставляем header_bytes и footer_bytes с их значениями
# по умолчанию, равными 0.
230
|
Гпава 9
reader = tf.FixedLengthRecordReader(record_bytes=record_bytes )
result.key, value = reader.read(filename_queue)
# Конвертировать из строкового типа в вектор из uint8, который имеет
# длину record_bytes.
record_bytes = tf.decode_raw(value, tf.uint8)
# Первые байты представляют метку, которую мы конвертируем
# из uint8->int32.
result.label = tf.cast(
tf.strided_slice(record_bytes,
[0],
[label_bytes]), tf.int32)
# Остальные байты после метки представляют изображение, которое мы
# реформируем из [depth * height * width] в [depth, height, width].
depth_maj or = tf.reshape(
tf.strided_slice(record_bytes,
[label_bytes],
[label_bytes + image_bytes]),
[result.depth, result.height, result.width])
# Конвертировать из [depth, height, width] в [height, width, depth].
result.uint8image = tf.transpose(depth_major,
[1, 2, 0])
return result
Глубокое погружение в архитектуру
Архитектура данной сети — стандартная многослойная сверточная сеть, похожая
на усложненный вариант архитектуры LeNet5, который вы встречали в главе 6.
Метод inference о создает архитектуру (пример 9.2). Данная сверточная архитекту­
ра соответствует относительно стандартной архитектуре со сверточными слоями,
перемежающимися с локальными нормализующими слоями.
Пример 9.2. Эта функция строит архитектуру CifarlO
def inference(images):
'""'Построить модель CifarlO.
Аргументы:
images: изображения, возвращаемые из distorted_inputs ()
либо inputs().
Возвращаемые значения:
логиты.
ппп
# Мы создаем экземпляры всех переменных, применяя tf .get_variable ()
# вместо tf.Variable() для того, чтобы использовать переменные
# совместно во всех тренировочных прогонах на многочисленных GPU.
Тренировка крупных глубоких сетей
|
231
# Если выполнять эту модель только на одном GPU, то функцию
# можно упростить, заменив все экземпляры
# tf.get_variable() на tf.Variable().
#
# convl
with tf.variable_scope('convl') as scope:
kernel = _variable_with_weight_decay('weights',
shape=[5, 5, 3,
64],
stddev=5e-2,
wd=0.0)
conv = tf.nn.conv2d(images, kernel,
[1, 1, 1, 1], padding='SAME')
biases = _variable_on_cpu('biases',
[64],
tf.constant-initializer (0.0))
pre_activation = tf.nn.bias_add(conv, biases)
convl = tf.nn.relu(pre_activation, name=scope.name)
_activation_summary(convl)
# pooll
pooll = tf.nn.max_pool(convl, ksize=[l, 3, 3, 1],
strides=[l, 2, 2, 1],
padding='SAME', name='pooll')
# norml
norml = tf.nn.lrn(pooll, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75,
name=' norml')
# conv2
with tf.variable_scope('conv2') as scope:
kernel = _variable_with_weight—decay('weights',
shape=[5, 5,
64,
64],
stddev=5e-2,
wd=0.0)
conv = tf.nn.conv2d(norml,
kernel,
biases = _variable_on_cpu('biases',
[1, 1, 1, 1], padding='SAME')
[64],
tf.constant-initializer (0.1))
pre_activation = tf.nn.bias_add(conv, biases)
conv2 = tf.nn.relu(pre_activation, name=scope.name)
_activation_summary(conv2)
# norm2
norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75,
name=' norm2')
# pool2
pool2 = tf.nn.maxjpool(norm2, ksize=[l, 3, 3, 1],
strides=[l, 2, 2, 1], padding='SAME',
name='pool2')
232
|
Глава 9
# 1оса13
with tf.variable_scope ('local3') as scope:
# Переместить все в глубину, чтобы можно было выполнить
# одно-единственное матричное умножение.
reshape = tf.reshape(роо12,
[FLAGS.batch_size, -1])
dim = reshape.get_shape()[1].value
weights = _variable_with_weight_decay('weights', shape=[dim, 384],
stddev=0.04, wd=0.004)
biases = _variable_on_cpu('biases',
[384],
tf.constant-initializer(0.1))
local3 = tf.nn.relu(tf.matmul(reshape, weights) + biases,
name=scope.name)
_activation_summary(local3)
# local4
with tf.variable_scope('local4') as scope:
weights = _variable_with_weight_decay('weights', shape=[384, 192],
stddev=0.04, wd=0.004)
biases = _variable_on_cpu('biases',
[192],
tf.constant-initializer(0.1))
local4 = tf.nn.relu(tf.matmul(locals, weights) + biases,
name=scope.name)
_activation_summary(local4)
# Линейная layer(WX + b).
# Здесь мы не применяем softmax, потому что
# tf.nn.sparse_softmax_cross_entropy_with_logits принимает
# нешкалированные логиты и выполняет softmax внутренне
# для эффективности.
with tf.variable_scope ('softmax_linear') as scope:
weights = _variable_with_weight_decay('weights',
[192, cifar10.NUM—CLASSES],
stddev=l/192.0, wd=0.0)
biases = _variable_on_cpu('biases',
[cifarlO.NUM_CLASSES],
tf.constant-initializer(0.0))
softmaX-linear = tf.add(tf.matmul(local4, weights), biases,
name=scope.name)
_activation_summary(softmax_linear)
return softmax_linear
Нет объектной ориентированности?
Сравните программный код модели, приведенный в этой архитектуре,
с кодом стратегии из предыдущей архитектуры. Обратите внимание на то,
как введение объекта Layer позволяет значительно упростить код с сопут­
ствующими улучшениями в удобочитаемости. Это резкое улучшение
читаемости является одной из причин, по которой большинство разработ­
Тренировка крупных глубоких сетей
|
233
чиков на практике предпочитают использовать объектно-ориентированное
наложение поверх TensorFlow.
Тем не менее в этой главе мы используем сырой код TensorFlow, посколь­
ку создание таких классов, как TensorGraph, с учетом многочисленных GPU
потребует значительных дополнительных затрат. В целом сырой код
TensorFlow предоставляет максимальную гибкость, но объектная ориенти­
рованность обеспечивает удобство. Подберите себе абстракцию, которая
подходит для решаемой вами задачи.
Тренировка на многочисленных GPU
Мы создаем отдельный экземпляр модели и архитектуры на каждом GPU. Затем мы
используем CPU для усреднения весов для отдельных узлов GPU (пример 9.3).
1 Пример 9.3. Эта функция тренирует модель CifarlO
def train():
"""Натренировать CifarlO за несколько шагов."""
with tf.Graph().as_default(), tf.device('/cpu:0’):
# Создать переменную для подсчета количества вызовов train().
# Оно равняется количеству обработанных пакетов * FLAGS.num_gpus.
global_step = tf.get_variable(
'global_step’,
[],
initializer=tf.constant-initializer(0), trainable=False)
# Рассчитать план скоростей заучивания.
num_batches_per_epoch = (cifarlO.NUM_EXAMPLES_PER_E РОСH_FOR_TRAIN /
FLAGS.batch_s i ze)
decay_steps = int(num_batches_per_epoch *
cifarlO.NUM—EPOCHS—PER—DECAY)
# Гасить скорость заучивания экспоненциально с учетом
# количества шагов.
1г = tf.train.exponential—decay(cifarlO.INITIAL—LEARNING—RATE,
global_step,
decay_steps,
cifarlO. LEARNING—RATE—DECAY—FACTOR,
staircase=True)
# Создать оптимизатор, который выполняет градиентный спуск,
opt = tf.train.GradientDescentOptimizer(Ir)
# Получить изображения и метки для CifarlO.
images, labels = cifarlO.distorted_inputs ()
batch_queue = tf.contrib.slim.prefetch_queue.prefetch_queue(
[images, labels], capacity=2 * FLAGS.num_gpus)
234
|
Гпава 9
|
Программный код в примере 9.4 выполняет ключевую тренировку на многочислен­
ных GPU. Обратите внимание на то, как разные пакеты исключаются из очереди
ДЛЯ каждого GPU, НО обмен весами посредством tf.get_variable_scoreQ. reuse­
variables о позволяет выполнять тренировку правильно.
Пример 9.4. Этот фрагмент реализует тренировку на многочисленных GPU
I
# Вычислить градиенты для каждого модельного блока.
tower_grads = []
with tf.variable_scope (tf.get_variable_scope()):
for i in xrange(FLAGS.num_gpus) :
with tf.device('/gpu:%d' % i):
with tf.name_scope('%s_%d' %
(cifarlO.TOWER_NAME, i)) as scope:
# Исключить из очереди один пакет для GPU.
image_batch, label_batch = batch_queue.dequeue()
# Рассчитать потерю для одного блока модели Cifar.
# Эта функция конструирует полную модель Cifar, но
# делится переменными между всеми блоками.
loss = tower_loss(scope, image_batch, label_batch)
# Использовать переменные повторно
# для следующего блока.
tf.get_variable_scope().reuse_variables()
# Сохранить сводки из последнего блока.
summaries = tf.get_collection(tf.GraphKeys.SUMMARIES,
scope)
# Рассчитать градиенты для пакета данных
# на этом блоке Cifar.
grads = opt.compute_gradients(loss)
# Отслеживать градиенты во всех блоках.
tower_grads.append(grads)
# Мы должны рассчитать среднее значение каждого градиента.
# Обратите внимание, что это точка синхронизации между всеми блоками,
grads = average_gradients(tower_grads)
Мы заканчиваем тем, что применяем совместную тренировочную операцию и по
мере необходимости записываем контрольные точки со сводной информацией, как
показано в примере 9.5.
Тренировка крупных глубоких сетей
|
235
Пример 9.5. Этот фрагмент кода группирует обновления из различных GPU
й по мере необходимости записывает сводные контрольные точки
# Добавить сводку для отслеживания скорости заучивания,
summaries.append(tf.summary.scalar(’learning_rate’, lr))
# Добавить гистограммы для градиентов.
for grad, var in grads:
if grad is not None:
summaries.append(tf.summary.histogram(var.op.name +
’/gradients’, \grad))
# Применить градиенты для корректировки совместных переменных.
apply_gradient_op = opt.apply_gradients (grads,
global_step=global_step)
# Добавить гистограммы для тренируемых переменных,
for var in tf.trainable_variables():
summaries.append(tf.summary.histogram(var.op.name, var))
# Отследить скользящие средние всех тренируемых переменных.
variable_averages = tf.train.ExponentialMovingAverage(
CifarlO.MOVING_AVERAGE_DECAY, global_step)
variables_averages_op =
variable_averages.apply(tf.trainable_variables ())
# Сгруппировать все обновления в единственную тренировочную операцию.
train_op = tf.group(apply_gradient_op, variables_averages_op)
# Создать средство сохранения.
saver = tf.train.Saver(tf.global_variables())
# Создать сводную операцию из последних сводок, поступивших из блоков
summary_op = tf. summary, merge (summaries)
# Создать операцию инициализации, чтобы выполнить ниже,
init = tf.global_variables_initializer()
# Начать выполнение операции на графе. Именованный аргумент
# allow_soft_jplacement должен иметь значение True, чтобы создавать
# блоки на GPU, т. к. некоторые операции не имеют реализации на GPU.
sess = tf.Session(config=tf.ConfigProto(
allow_soft_placement=True,
log_device_placement=FLAGS. log_device_jplacement))
sess.run(init)
# Запустить исполнителя очередей.
tf.train.start_queue_runners(sess=sess)
236
|
Гпаев 9
summary_writer = tf.summary.FileWriter(FLAGS.train_dir, sess.graph)
for step in xrange(FLAGS.max_steps) :
start_time = time.time()
_t loss_value = sess.run([train_op, loss])
duration = time.time () - start_time
assert not np.isnan(loss_value),
'Модель отклонилась с потерей = NaN’
if step % 10 == 0:
num_examples_per_step = FLAGS.batch_size * FLAGS.num_gpus
examples_per_sec = num_examples_per_step / duration
sec_per_batch = duration / FLAGS.num_gpus
format_str = ('%s: шаг %d, потеря = %.2f (%.lf примеров/с; %.3f ’
’с/пакет)')
print (format_str %
(datetime.now(), step, loss_value,
examples_per_sec, sec_per_batch))
if step % 100 == 0:
summary_str = sess.run(summary_op)
summary_writer.add_summary(summary_str,
step)
# Периодически сохранять контрольную точку модели.
if step % 1000 == 0 or (step + 1) == FLAGS.max_steps:
checkpoint_path = os.path.join(FLAGS.train_dir,
’model.ckpt’)
saver.save(sess, checkpoint_path, global_step=step)
Задание для читателя
Теперь у вас есть все фрагменты, необходимые для тренировки этой модели на
практике. Попробуйте выполнить ее на подходящем сервере с вычислениями
на GPU! Вы можете использовать такие инструменты, как nvidia-smi, чтобы убе­
диться, что все GPU действительно используются.
Резюме
В этой главе вы познакомились с различными типами оборудования, обычно
используемого для тренировки глубоких архитектур. Вы также изучили режимы
работы с параллельностью данных и параллельностью моделей для тренировки
глубоких архитектур на многочисленных CPU или GPU. Мы закончили главу прак­
тическим примером реализации тренировки с параллельностью данных, в котором
выполняется тренировка сверточной сети в TensorFlow.
В главе 10 мы обсудим будущее глубокого самообучения и то, как эффективно
и корректно использовать навыки, которые вы получили в этой книге.
Тренировка крупных глубоких сетей
|
237
ГЛАВА 10
Будущее
глубокого самообучения
В этой книге мы рассмотрели основы современного глубокого самообучения. Мы
обсудили широкий спектр алгоритмов и углубились в ряд сложных тематических
исследований. Читатели, которые работали с примерами, описанными в этой книге,
теперь хорошо подготовлены к использованию глубокого самообучения на работе
и к чтению широкой исследовательской литературы по методам глубокого само­
обучения.
Стоит подчеркнуть, насколько уникален этот набор навыков. Глубокое самообуче­
ние уже оказало огромное влияние на технологическую отрасль, но глубокое само­
обучение начинает кардинально менять состояние практически всех нетехнических
отраслей и даже смещать глобальный геополитический баланс. Ваше понимание
этой эпохальной технологии откроет многие двери, которые вы, возможно, даже не
пытались рисовать в своем воображении. В последней главе мы кратко рассмотрим
несколько важных приложений глубокого самообучения за пределами индустрии
программного обеспечения.
Мы также будем использовать эту главу, чтобы помочь вам ответить на вопрос
о том, как эффективно и этически использовать ваши новые знания. Глубокое са­
мообучение — это технология такой мощи, что практикующим специалистам важ­
но подумать о том, как правильно использовать свои навыки. Уже имелись много­
численные злоупотребления глубокого самообучения, поэтому практикующим
специалистам пойдет только на пользу сделать паузу, прежде чем приступить к по­
строению сложных глубоко обучающихся систем, чтобы задаться вопросом,
соблюдают ли системы, которые они строят, этические нормы. Мы попытаемся
провести краткое обсуждение передовой этической практики, но предостерегаем,
что область этики программного обеспечения настолько сложна, что краткие обсу­
ждения вряд ли отдадут этой теме должное.
Наконец, мы рассмотрим, где происходит глубокое самообучение. Является ли глу­
бокое самообучение первым шагом к созданию универсальных искусственных
интеллектов, вычислительных сущностей, которые имеют полный спектр способ­
ностей человека? Мы сделаем обзор широкого диапазона экспертных заключений
по этому вопросу.
239
Глубокое самообучение
вне технологической индустрии
Технологические компании, такие как Google, Facebook, Microsoft и другие, инве­
стировали крупные средства в инфраструктуру глубокого самообучения. Большин­
ство этих компаний уже знакомы с машинно-обучающимися системами, вероятно,
из прошлого опыта работы, например с рекламными предсказательными или поис­
ковыми системами. Как результат, их переход к глубокому самообучению от ста­
рых машинно-обучающихся систем потребовал лишь небольшого концептуального
сдвига. Кроме того, успех прошлых приложений машинного самообучения убедил
технологический менеджмент в том, что глубокое самообучение может шире при­
меняться в компаниях. По этим причинам в ближайшем будущем компании-раз­
работчики программного обеспечения, вероятно, останутся основными пользова­
телями глубокого самообучения. Если вы собираетесь найти работу, используя
глубокое самообучение в течение следующих нескольких лет, вполне возможно, вы
в итоге окажетесь в технологической компании.
Однако в то же время назревает более широкий сдвиг, в котором глубокое само­
обучение начинает проникать в отрасли, где исторически машинное самообучение
особо не использовалось. В отличие от более простых методов машинного само­
обучения, глубокое самообучение уменьшает потребность в сложной предобработ­
ке признаков и позволяет напрямую вводить перцептивные, текстовые и молеку­
лярные данные. Как результат, ряд отраслей обращают внимание на новые тенден­
ции, и во многих инновационных стартапах уже начались масштабные работы по
перестройке этих отраслей. Теперь мы кратко обсудим некоторые изменения, про­
исходящие в граничных отраслях, и отметим, что в ближайшем будущем может
появиться много новых рабочих мест для экспертов по глубокому самообучению.
Приложения являются синергетическими
Далее вы познакомитесь с рядом глубоко обучающихся приложений в раз­
личных отраслях промышленности. Поразительно, но факт: во всех них
используются одни и те же фундаментальные глубоко обучающиеся алго­
ритмы. Методы, которые вы встречали, такие как полносвязные сети, свер­
точные сети, рекуррентные сети и самообучение с максимизацией подкре­
пления, широко применяются в любой из этих областей. В частности, это
означает, что ключевые улучшения в конструкции сверточной сети прине­
сут плоды в фармацевтических, сельскохозяйственных и робототехниче­
ских приложениях. И напротив, инновации в области глубокого самообу­
чения, обнаруженные инженерами-робототехниками, в свою очередь
отфильтруют и укрепят основополагающие принципы глубокого само­
обучения. Этот творческий цикл, когда фундаментальные принципы со­
вершенствуют приложения, которые совершенствуют фундаментальные
принципы, означает, что глубокое самообучение является силой, призван­
ной остаться всерьез и надолго.
240
|
Гпава 10
Глубокое самообучение
в фармацевтической промышленности
Глубокое самообучение показывает признаки широкомасштабного взлета в области
разработки медицинских препаратов, которая подразделяется на многочисленные
этапы. Есть доклиническая фаза разработки, когда эффекты от потенциальных
лекарств опосредованно тестируются в пробирках и на животных, а затем клиниче­
ская фаза, в которой лекарственный препарат тестируется непосредственно на доб­
ровольцах. Лекарство, которое проходит обе фазы испытаний не на людях и на
людях, допускается к продаже потребителям.
Исследователи начали строить модели, которые оптимизируют каждую часть про­
цесса разработки медицинских препаратов. Например, молекулярное глубокое са­
мообучение было применено к таким задачам, как предсказание потенциальной
токсичности предполагаемых лекарственных препаратов, а также к химическим
задачам, связанным с синтезом и конструированием лекарственных молекул.
Другие исследователи и компании используют глубокие сверточные сети для раз­
работки новых экспериментов, которые тесно отслеживают клеточное поведение
в огромных масштабах, чтобы получить более глубокое понимание неизведанной
биологии. Эти практики оказали определенное воздействие на фармацевтический
мир, но пока ничего драматического не произошло ввиду того, что отсутствует
возможность построить единую модель, которая "конструирует" новейшее лекарст­
венное средство. Однако по мере продолжения усилий по сбору данных и разра­
ботке глубоко обучающихся биологических и химических моделей такое положе­
ние дел может резко измениться в ближайшие несколько лет.
Глубокое самообучение в юстиции
Юридическая сфера в большой степени опирается на прецедент в правовой литера­
туре, для того чтобы аргументировать законность или незаконность новых судеб­
ных дел. Традиционно легионы параюридических исследователей нанимались
крупными юридическими фирмами для выполнения необходимого поиска в право­
вой литературе. В последние годы юридические поисковые системы стали стан­
дартными издержками для большинства современных фирм.
Такие алгоритмы поиска все еще относительно незрелые, и вполне вероятно, что
глубоко обучающиеся системы для нейролингвистической обработки (neurolinguis­
tic processing, NLP) смогут предложить значительные улучшения. Например, ряд
стартапов работают над созданием глубоких систем NLP, которые предлагают
более оптимальные формы запрашивания юридического прецедента. Другие стар­
тапы работают над предсказательными методами, которые используют машинное
самообучение для предсказания исхода судебного разбирательства, в то время как
некоторые даже экспериментируют с методами автоматизированной генерации
юридических аргументов.
В целом, эти сложные применения глубоких моделей потребуют времени, чтобы
созреть, но волна инноваций в области юридического искусственного интеллекта,
вероятно, предвещает драматический сдвиг в юридической профессии.
Будущее глубокого самообучения
|
241
Глубокое самообучение для робототехники
Индустрия робототехники традиционно избегает внедрения машинного самообуче­
ния, поскольку нелегко обосновать, что внедрение машинно-обучающихся систем
в этой области не несет опасности. Это отсутствие гарантий безопасности может
стать основным сдерживающим фактором при создании систем, развертывание ко­
торых не должно создавать опасность вокруг операторов-людей.
Однако в последние годы стало очевидным, что глубоко обучающиеся системы
в сочетании с методами самообучения с низкими потребностями в данных могут
предложить значительные улучшения в задачах управления роботами. Компания
Google продемонстрировала, что самообучение с максимизацией подкрепления
может быть развернуто, чтобы обучаться управлять роботами-манипуляторами,
используя фабрику роботов-манипуляторов с целью обеспечения возможности ши­
рокомасштабной тренировки на реальных роботах (рис. 10.1). Вполне вероятно, что
такие усовершенствованные методы самообучения для роботов начнут находить
применение в более крупной робототехнической индустрии в течение следующих
нескольких лет.
Рис. 10.1. Компания Google обеспечивает работу нескольких роботов-манипуляторов,
которые она использует, чтобы протестировать методы глубокого самообучения с максимизацией
подкрепления для роботизированного управления. В ближайшие несколько лет это
фундаментальное исследование, судя по всему, найдет свой путь в фабричные цеха
Глубокое самообучение в сельском хозяйстве
Промышленное сельское хозяйство уже в большой степени механизировано, а
сложные тракторы используются для посадки и даже сбора урожая. Достижения
в области робототехники и компьютерного зрения ускоряют эту тенденцию в сто­
рону автоматизации. Сверточные сети уже используются для выявления сорняков,
242
|
Гпава 10
которые можно удалять с меньшим количеством пестицидов. Другие компании
экспериментировали с самоходными тракторами, автоматизированным сбором
фруктов и алгоритмической оптимизацией урожайности. На данный момент эти
усилия в основном являются исследовательскими проектами, но в течение сле­
дующего десятилетия они, по всей видимости, перерастут в крупные внедренческие
проекты.
Этическое использование
глубокого самообучения
Подавляющая часть этой книги была посвящена эффективному использованию
глубокого самообучения. Мы рассмотрели множество методов построения глубо­
ких моделей, которые хорошо обобщают различные типы данных. Вместе с тем,
также стоит потратить немного времени на размышления о социальных последст­
виях систем, которые мы строим как инженеры. Глубоко обучающиеся системы
могут инициировать множество потенциально опасных приложений.
Прежде всего, сверточные сети позволят широко применять технологии распозна­
вания лиц. Китай вышел в лидеры по внедрению таких систем для применения
в реальных условиях (рис. 10.2).
Рис. 10.2. Китайское правительство широко внедряет алгоритмы распознавания лиц,
основанные на сверточных сетях. Способность этих систем отслеживать людей, скорее всего,
будет означать, что анонимность в общественных местах в Китае останется в прошлом
Будущее глубокого самообучения
|
243
Обратите внимание, что вездесущее обнаружение лиц будет означать, что общест­
венная анонимность канет в Лету. Любые действия, предпринимаемые в публичной
сфере, будут регистрироваться и отслеживаться корпорациями и правительствами.
Это видение будущего должно показаться тревожным для всех, кто обеспокоен
этическими последствиями глубокого самообучения.
Более того, когда алгоритмы смогут понимать визуальную и перцептивную инфор­
мацию, почти все аспекты человеческой жизни подпадут под алгоритмическое
влияние. Эта тенденция является макроскопической, и не очевидно, что найдется
какой-то инженер, который сможет предотвратить это будущее. Тем не менее
инженеры сохраняют способность ’’голосовать ногами”. Ваши навыки ценны и вос­
требованы; не работайте для компаний, которые следуют неэтичным практикам и
строят потенциально опасные системы.
Смещение в искусственном интеллекте
Машинное самообучение и глубокое самообучение предоставляют воз­
можность без особых усилий обучаться интересным моделям на данных.
Этот основательно проработанный математический процесс может созда­
вать мираж объективности. Стоит решительно отметить, что в такой ана­
лиз могут проникнуть всякого рода смещения и даже предубеждения.
Смещения в базовых данных, взятые из исторических, смещенных записей,
могут привести к тому, что модели будут обучаться принципиально не­
справедливым моделям. В компании Google, не в обиду ей будет сказано,
однажды обнаружили, что ошибочная визуальная предсказательная модель
помечала потребителей-афроамериканцев как горилл, вероятно, из-за сме­
щенных тренировочных данных, которым не хватало адекватного пред­
ставления людей с разным цветом кожи. Несмотря на то, что эта система
была быстро исправлена, как только эта ошибка была доведена до сведе­
ния Google, такие сбои вызывают глубокую тревогу и символизируют бо­
лее фундаментальные проблемы социальной изоляции в технологической
индустрии.
По мере того как искусственный интеллект все чаще используется в таких
приложениях, как процедуры условно-досрочного освобождения заклю­
ченных и утверждения займов в кредитных организациях, для нас стано­
вится все более важным обеспечить, чтобы наши модели не принимали
расистских допущений или не обучались смещениям, уже присутствую­
щим в исторических данных. Если вы работаете с конфиденциальными
данными, делая предсказания, которые могут изменить ход человеческих
жизней, проверьте дважды, а то и трижды, чтобы убедиться, что ваши сис­
темы не становятся жертвой смещений в данных.
244
|
Гпава 10
Действительно ли универсальный
искусственный интеллект неизбежен?
Широко обсуждается вопрос о том, появится ли в скором времени универсальный
искусственный интеллект или нет. Эксперты не согласны с тем, что стоит серьезно
рассчитывать на появление универсального ИИ. Мы считаем, что, хотя нет никако­
го вреда в проведении исследований по "выравниванию ценностей ИИ" и "безопас­
ной функции вознаграждения", системы искусственного интеллекта сегодня и в
обозримом будущем вряд ли быстро достигнут разумности. Как вы узнали на лич­
ном опыте, большинство глубоко обучающихся систем — это просто сложные чис­
ленные механизмы, подверженные многим тонким проблемам числовой стабильно­
сти. Вероятно, потребуются десятилетия фундаментальных достижений, прежде
чем универсальный разум станет реальностью. В то же время, как мы обсуждали
в предыдущем разделе, искусственный интеллект уже оказывает значительное
влияние на человеческие сообщества и промышленные отрасли. Поэтому, безус­
ловно, нужно беспокоиться о влиянии ИИ, но без придумывания безумных стра­
шилок.
Заблуждение о суперинтеллекте
Книга "Искусственный интеллект. Этапы, Угрозы, Стратегии" Ника Бострома* (издательство Оксфордского университета) оказала глубокое влия­
ние на дискурс вокруг ИИ. Основная предпосылка книги состоит в том,
что взрыв интеллекта может произойти, когда модели станут способными
улучшать себя рекурсивно. Сама по себе данная идея не особо радикаль­
ная. Если бы универсальный ИИ должен был появиться, нет никакой при­
чины предположить, что ему не удалось бы быстро самосовершенство ­
ваться.
В то же время, эксперт по глубокому самообучению Эндрю Hr (Andrew
Ng) пошел на рекорд, заявив, что беспокоиться о суперинтеллекте— это
все равно, что беспокоиться о перенаселении на Марсе. Однажды челове­
чество, скорее всего, достигнет Марса. Когда люди высадятся на красной
планете, вполне вероятно будет существовать перенаселенность, которая
даже может стать очень серьезной проблемой. Но ничто из этого не меняет
факта, что Марс сегодня — это пустыня. Таково и состояние литературы
по созданию универсального ИИ!
Так вот, это последнее заявление гиперболическое. Солидный прогресс
в самообучении с максимизацией подкрепления и генеративном моделиро­
вании имеет много перспектив для создания более интеллектуальных аген­
тов. Но подчеркивание возможности появления сверхразумных сущностей
отвлекает от реальных задач автоматизации, всгречающихся на нашем пу­
ти. Безусловно, здесь даже не упоминаются другие серьезные проблемы,
с которыми мы сталкиваемся, такие как глобальное потепление.
’ Bostrom N. Superintelligcncc: Paths, Dangers, Strategies. — Oxford University Press, 2014. — 352 p.
(Востром H. Искусственный интеллект. Этапы. Угрозы. Стратегии. — М.: Манн, Иванов и Фербер,
2016,—496 с.).
Будущее глубокого самообучения
|
245
Куда направиться дальше?
Если вы внимательно прочитали эту книгу и потратили усилия на работу с нашими
образцами программного кода в соответствующем хранилище GitHub, тогда по­
здравляем! Вы уже освоили фундаментальные принципы практического машинно­
го самообучения. Вы сможете тренировать эффективные машинно-обучающиеся
системы на практике.
Однако машинное самообучение— это очень быстро развивающаяся область.
Взрывной рост данной области означает, что каждый год открываются десятки пер­
спективных моделей. Практикующие специалисты по машинному самообучению
должны постоянно находиться в поиске новых моделей. При рассмотрении новых
моделей полезным способом оценки их полезности является попытка подумать
о том, как применить модель к задачам, которые волнуют вас или вашу организа­
цию. Эта проверка— хороший способ организовать большой приток моделей из
исследовательского сообщества, она даст вам инструмент для определения приори­
тетов в изучении методов, которые действительно для вас важны.
Как ответственный специалист по машинному самообучению, обязательно поду­
майте о том, для чего используются ваши научные модели данных. Спросите себя,
используется ли ваша работа по машинному самообучению для улучшения благо­
состояния людей? Если ответ — "нет”, поймите, что с вашими навыками у вас есть
прекрасная возможность найти работу там, где вы сможете использовать свои
суперспособности по машинному самообучению для добра, а не для зла.
Наконец, мы надеемся, что вы получите большое удовольствие. Глубокое самообу­
чение — это невероятно бурлящая область человеческой деятельности, наполнен­
ная захватывающими новыми открытиями, блестящими людьми и возможностью
серьезного воздействия. Нам было приятно поделиться с вами нашими волнением
и страстью к этой области, и мы надеемся, что вы приумножите наши усилия, по­
делившись своими познаниями в глубоком самообучении с окружающим миром.
246
|
Гпава 10
Предметный указатель
А
G
AlexNet24, 133
AlphaGo 30, 184, 185, 192,211
Anaconda Python 45, 109
Application specific integrated circuit (ASIC )
222
argparse 174
Artificial general intelligences (AGI) 192
Asynchronous Advantage Actor-Critic (A3C)
206
GAN-сетьЗ!, 167
Gated recurrent units (GRU) 166
Google neural machine translation (GNMT)
167
Graphics Processing Unit (GPU) 54, 221
I
InfiniBand 228
Intel 222
c
J
Central Processing Unit (CPU) 54, 219
Cifarl0 228
0 загрузка данных 229
0 многослойная сверточно-сетевая
архитектура 231
0 тренировка на многочисленных GPU 234
Compute unified device architecture (CUDA)
221
CuDNN 165
CycleGAN 147
JOIN 54
L
Ь2-потеря 146
Layer 199
LeNet 24
LeNet-5 153
Long short-term memory (LSTM) 164
D
M
Deep Blue 183
Deep Q-networks (DQN) 188
DeepChem 109
0 инсталляция 109
0 самообучение с максимизацией
подкрепления 185
DeepMind 211
DistBelief 32
dtype 50, 53
Markov decision processes (MDP) 185
MNIST 147
MoleculeNet 109
N
ndarray 46, 71
NumPy 46,71
NVIDIA 165, 220-223, 228
NVLINK 228
Field programmable gate arrays (FPGA) 224
247
р
Penn Treebank 161, 171
О базовая рекуррентная архитектура 177
О загрузка данных в TensorFlow 175
О ограничения 172
О предобработка 173
Python API 45
Q
Q-самообучение 188
R
ResNet 25
Root mean squared error (RMSE) 87
s
SELECT 54
skleam.metrics 114
T
Tensor processing unit (TPU) 222
TensorBoard:
О визуализация логистической
регрессионной модели 90
0 области имен 77
TensorFlow 44, 70
0 графы 55
0 документация 45
248
|
Предметный указатель
0 матричные операции 49
0 ограничения 33
0 переменные 56
0 преимущества 13
0 сеанс 55
0 тренировка модели 80
0 установка 45
0 фундаментальные примитивы 33
TensorFlow Eager 55
TensorGraph 201
tf.contrib 177
tf.data 176
tf.estimator 201
tf.FLags 174
tf.float32 50
tf.float64 50
tf.GFile 174
tf.Graph 55, 58, 201
tf. InteractiveSession 45
tf.keras 201
tf.matmul 55
tf.name scopes 84
tf.nn.max_pool 156
tf.Operation 55
tf.Queue 175
tf.Session 55,201
tf Tensor 55
tf.train 78
tf.truncated normal 48
tf.Variable 57
Tox21 117, 125
train op 80
TrueNorth 225
А
Автокодировщик вариационный 167
Агент 185
О игры в крестики-нолики 193
° абстрактная среда 194
° объектная ориентированность 194
° определение графа слоев 201
° слоевая абстракция 198
° среда игры 195
Активация 103
0 выпрямленная линейная 103
Алгоритм:
0 АЗС 206
0 Q-самообучение 188
0 асинхронной критики стратегии 206
0 безмодельный 187
0 для самообучения с максимизацией
подкрепления 187
0 заучивание стратегии 189
0 катастрофическое забывание 189
0 модельно-ориентированный 187
0 обучающийся, принцип черного ящика
119
0 оптимизационный 77
0 решеточный поиск 129
0 случайный поиск гиперпараметров 130
0 тренировка асинхронная 191
Аппроксиматор универсальный 95, 104
Б
Блок вентильный рекуррентный 166
в
Вес:
0 заучиваемый 66, 82, 154
0 сверточный 155
Видеоданные 133, 142
Вознаграждение 181, 186
0 будущее 188
0 дисконтированное 188
Встраивание словарное 172
Выборка 77
Гиперпараметр 67, 117
Градиент 60, 67, 78
Граф 55
0 направленный 199, 201
графовые свертки 142
д
Данные:
0 входные многомерные 137
0 классификационные, набор
синтетический 74
Действие 181
Дифференцируемость 60
Доля истинноположительных исходов 122
3
Забывание катастрофическое 189
Задача:
0 классификационная 62
0 регрессионная 62
Заполнитель 76, 159
Заучивание 68
0 стратегии 189,210
Значение случайное 47
и
Игра:
0 аркадная:
° ATARI 181
° Breakout 182
0 крестики-нолики 193
0 стратегическая StarCraft 192
Изображение RGB 137
Индекс:
0 ковариантный 44
0 контравариантный 44
Инструкция журналирования 79
Интеллект:
0 искусственный, цикл подъема и спада 98
0 универсальный искусственный 191, 245
к
Класс заученный 93
Коадаптация 105
Кодирование с одним активным состоянием
171
Конструирование признаков 211
Концепция развертывания игровой
ситуации 189
Предметный указатель
|
249
Корреляции ложные 104
Коэффициент:
0 детерминации 87
0 корреляции Пирсона, квадратичный 87
Кривая рабочей характеристики приемника
122
л
Лес случайный 126
Линия разделительная 93
Локализация объекта 140
м
Максимизация подкрепления асинхронная
206
Матрица 38
0 вентильная, программируемая
пользователем (FPGA) 224
0 диагональная 49
0 единичная 49
0 ошибок 123
0 сложение с матрицей 39
0 транспонирование 39
0 умножение:
° на матрицу 40
» на скаляр 39
о поэлементное 40
Машина Тьюринга 169
О нейронная 32, 169
Межсоединения устройств памяти 228
Метод:
0 A3C.fit217
0 add output 204
0 build 204
0 build_graph 208
0 c.eval 54
0 eval 54
0 fit 217
0 get_global_step 205
0 getinputtensors 199
0 getlayervariables 205
0 read cifarlO 229
0 restore 206
0 reuse variables 178
0 sess.run 76, 80, 113
0 set loss 204
0 set optimizer 204
0 tf.lnteractiveSession 55
250
|
Предметный указатель
0 tf.Tensor.eval 46, 56
0 tf.Tensor.getshape 52
0 tf.train.FileWriter 79
0 решеточного поиска 129
Метрика 86, 120
Мини-макет 108
Минимумы 67
Мини-пакет 67
Моделирование:
0 временных рядов 162
0 на уровне символов 171
0 на уровне слов 171
0 языка 167
Модель:
0 глубокая 117
0 настройка автоматическая 125
0 натренированная 158
0 оценивание 118
0 последовательность-впоследовательность (seq2seq) 167
0 состязательная 146
0 точность 113
н
Набор:
0 контрольный 151
0 тестовый 119
Нарушение симметрии 47
Неустойчивость градиентная 164
0
Обнаружение объекта 140
Оборудование:
0 для тренировки и вывода 220
0 только для вывода 220
Обучение:
0 глубокое:
° альтернативные применения 240
о этическое использование 243
0 машинное:
□ быстрая эволюция 245
о потенциальное смещение 244
Ограничение вычислительное 100
Операция управления потоком 169
Оптимизация:
0 гиперпараметрическая 117
о автоматизация 125
0 под управлением производных 61
0 слепая 120
Ориентир 126
Ориентированность объектная 194
Остановка ранняя 107
Отладка в визуальном и невизуальном
стиле 84
Отсев 105
Ошибка среднеквадратическая 86, 124
п
Память долгая краткосрочная 164
Параллелизм:
0 данных 226
0 моделей 227
Параметр заучиваемый 67
Параметризация 66
Перевод Google нейронный машинный 27,
167
Перегрузка операторов 48
Переменная 56
0 заучиваемая 205
Переподгонка 106, 119
Перплексия 178
Перцептрон 99
Показатель метрический 86, 120
Поле локальное рецептивное 134, 142
Полнота 121
0 по Тьюрингу 170
Полупроводник 222
Порог отсечения 122
Потеря перекрестно-энтропийная 65, 89
Правило стационарное эволюционное 162
Предсказание перспективное 151
Представление заучиваемое 102
Преобразование:
0 Лапласа 103
0 Лежандра 103
0 полносвязное 21
0 Фурье 103
Прецизионность 121
Приведение типов, неявное 53
Примитив:
0 TensorFlow 35
0 глубокое самообучение 21
Проблема исчезающего градиента 103
Программирование:
0 декларативное 54
0 императивное 53
0 с отслеживанием состояний 56
0 функциональное 56
Проект автоматизированного рабочего
места, статистика 125
Производная 60
Прореживание 105
Пространство векторное 36, 44, 167
Процесс принятия решений, марковский
181, 185
Процессор тензорный 222
р
Размер:
0 транзисторов 222
0 шага 137
Распознавание символов оптическое 24
Распределение:
0 вероятностей 64
0 нормальное 73
Распространение обратное 100, 101
Регрессия линейная 80
Регуляризация 104
0 весов 107
Ряд:
0 Тейлора 101
0 Фурье 101
с
Самообучение:
0 глубокое 19
о примитивы 21
0 машинное:
° молекулярное 151
° скорость чипов 222
0 однократное 27
0 с максимизацией подкрепления 181
° агент игры в крестики-нолики 193
° алгоритм АЗС 206
° алгоритмы 187
° марковский процесс принятия реше­
ний 185
° ограничения 191
° симуляция 187, 191
Свертка растянутая 139
Сеанс 55
Сегментация изображения 141
Сеть:
0 генеративно-состязательная 31
0 глубокая 96
0 крупная глубокая:
° обзор тренировки 219
° параллельная тренировка на данных
CifarlO с использованием многочис­
ленных GPU 228
Предметный указатель
|
251
° специальное аппаратное обеспечение
219
° тренировка на CPU 220
° тренировка распределенная 225
0 полносвязная 21
° глубокая, компоненты 95
° тренировка 108
0 рекуррентная нейронная 22
° машина Тьюринга 169
° оптимизация 165
° полнота по Тьюрингу 170
° применение 166
° рекуррентные архитектуры 162
0 сверточная 22
° нейронная, конструирование 139
0 структурно-агностическая 95
Симуляция 187
0 на основе CPU 191
Скаляр 36
Скорость:
0 заучивания 108
0 чипов 222
Словарь:
0 передачи данных 77
0 построение 173
Слой:
0 максимально редуцирующий 138, 153
0 полносвязный 21
0 сверточный 22, 137
° преобразование 134
Спектрограмма речи 163
Специфичность 122
Спуск:
0 градиентный 67, 179
° стохастический, поточный 226
0 студента магистратуры 129
Среда 181, 185
Среднее значение 72
Стандартное отклонение 72
Суперинтеллект 245
т
Тензор:
0 в физике 42
0 второго ранга 38
0 вычисление значения 46
0 константный, инициализация 45
0 матричные операторы 49
0 нулевого ранга 36
0 обработка формы 51
252
|
Предметный указатель
0 отбор случайных значений 47
0 первого ранга 36
0 сложение и шкалирование 48
0 создание и обработка 44
0 типы 50
0 третьего ранга 41, 51
0 четвертого ранга 42
Теорема:
0 Вейерштрасса-Стоуна 101
0 об универсальной сходимости 100
Технология обнаружения лиц 243
Точность 121
0 обобщение 123
Транслирование 52
Тренировка:
0 асинхронная 191
0 глубокой сети, распределенная 225-227
0 на графическом процессорном
устройстве 221
0 на центральном процессорном
устройстве:
° нейроморфные чипы 224
° преимущества 220
° программируемые пользователем вен­
тильные матрицы 224
° против тренировки на GPU 220
° тренировка на TPU 222
0 сквозная 26
У
Узел оптимизационный 80
ф
Фильтр 137
Форма, обработка 51
Функция 59
0 accuracy score 114
0 tf.assign57
0 tf.constant 47
0 tf.converttotensor 199
0 tf.diag49
0 tf.expanddims 52
0 tf.eye49
0
0
0
0
0
tf.fill 46
tf.FixedLengthRecordReader 229
tf.getdefaultjgraph 55
tf.global-Variables initializer 57
tf.gradients 78
0
О
О
О
О
О
О
О
О
О
О
О
О
О
О
О
О
О
О
О
О
О
О
0
О
О
О
О
О
О
О
tf.matmul 50
tf.matrixtranspose 50
tf.namescope 77, 1И
tf.nn.conv2d 152, 154
tf.nn.embeddinglookup 177
tf.nn.max_pool 153
tf.nn.relu 111
tf.ones45
tf.rand normal 48
tf.randuniform 48
tf.random normal 47
tf.range49
tf.registertensorconversionfunction 199
tf.reshape 51
tf.squeeze 52
tf. summary 79
tf.summary.merge all 79
tf.summary.scalar 79
tf.todouble 51
tf.to float 51
tf.to_int32 51
tf.to_int64 51
tf.train.AdamOptimizer 81
tf.zeros45
градиент 61
минимизации 61, 67
минимум и производная 61, 67
многомерная 61
непрерывная 60
полилинейная 44
потерь 145, 211
О
О
О
О
превосходства 190
приведения типов 50
сигмоидальная 89, 103
стоимости 190
X
Хеширование признаков 37
ц
Цепь импульсная 224
ч
Чип нейроморфный 224
ш
Шахматы профессиональные 183
э
Энтропия перекрестная 65
Эпоха 68
я
Ядро сверточное 136
Ячейка долгой краткосрочной памяти 23
177
Предметный указатель
|
253
TensorFlow для глубокого обучения
Книга позволяет освоить TensorFlow — новую революционную
программную библиотеку Google для глубокого обучения. Для
освоения достаточно иметь базовые знания по линейной ал­
гебре и математическому анализу. Вы на практике познако­
митесь с основами машинного обучения и научитесь решать
как типичные, так и нестандартные задачи. Книга покажет, как
проектировать системы, способные обнаруживать объекты на
изображениях, понимать текст и даже предсказывать свойства
потенциальных лекарств.
Книга содержит множество практических примеров и дает
прочные знания фундаментальных принципов глубокого обу­
чения, начиная с нулевого уровня. Она идеально подходит
практикующим разработчикам с опытом проектирования про­
граммных систем и будет полезна тем специалистам, которые
не испытывают проблем с написанием сценариев, но не всегда
справляются с проектированием обучающихся алгоритмов.
В этой книге вы:
■ познакомитесь с фундаментальными принципами библиотеки
TensorFlow, включая выполнение базовых вычислений;
Это
фантастическая
книга для специалистовпрактиков по машинно­
му обучению, желающих
начать работу в новой
области глубокого обу­
чения. Широта охваты­
ваемых тем делает эту
книгу настоящим спра­
вочником, к которому
вы будете неоднократ­
но возвращаться, чтобы
вывести свои навыки на
следующий уровень.
Марвин Бертин,
инженер-исследователь
по машинному обучению,
компания Freenome
Бхарат Рамсундар (Bharath
Ramsundaij—аспирант в облас­
■ погрузитесь в полносвязные глубокие сети, применяемые в тыся­
чах приложений;
.. |
ти
вычислительной
техники
и глубокого обучения в Стэн­
фордском университете, является
ведущим разработчиком пакета
с открытым исходным кодом
DeepChem.io, основанного на биб­
лиотеке TensorFlow.
■ превратите прототипы в высококачественные модели с помощью
гиперпараметрической оптимизации;
Реза Босаг Заде (Reza Bosagh
Zadeh) — основатель и генераль­
■ построите простые обучающиеся системы, чтобы понять их мате­
матические основы;
■ обработаете изображения с помощью сверточных нейронных
сетей;
■ научитесь справляться с наборами естественно-языковых данных
с помощью рекуррентных нейронных сетей;
■ примените обучение с максимизацией подкрепления на примере
таких игр, как крестики-нолики;
■ натренируете глубокие сети с помощью оборудования, включая
графические и тензорные процессоры.
ISBN 978-5-9775-4014-8
9 785977 540148
191036, Санкт-Петербург,
Гончарная ул., 20
Тел.: (812) 717-10-50,
339-54-17, 339-54-28
E-mail: mail@bhv.ru
Internet: www.bhv.ru
ный директор компании Matroid
и адъюнкт-профессор в Стэн­
фордском университете. Его про­
фессиональная деятельность на­
ходится в области машинного
обучения, вычислительной и дис­
кретной прикладной математики.
Работал в технических консуль­
тативных советах Microsoft, а так­
же занимался построением машинно-обучающихся алгоритмов,
лежащих в основе системы Twit­
ter. Разработал и преподает два
класса уровня докторантуры в
Стэнфордском университете: рас­
пределенные алгоритмы и опти­
мизация и дискретная математика
и алгоритмы.
Download