Разминка в Theano и Tensorflow. Итоги

Разминка в Theano

Здравствуйте и вновь добро пожаловать на занятия по теме «Глубокое обучение с подкреплением: глубокое обучение на языке Python, часть 7».

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

Как вы знаете, глубокие нейронные сети легче всего писать с помощью структур вроде Theano и Tensorflow, так что не приходится выводить градиенты самостоятельно, и мы неторопливо подходили к этому моменту. Вначале мы рассмотрели Q-обучение без каких-либо приближений функций, затем рассмотрели Q-обучение с линейным приближением функций и градиентным спуском при помощи Ski-kit Learn, а после этого – рассмотрели тот же метод, но без применения Ski-kit Learn и написанием вместо этого модели с нуля в Numpy. Теперь же мы повторно реализуем то же самое, но при помощи Theano. Это сделано с целью напомнить вам все важнейшие части разработки нейронной сети в Theano – создание графа входных данных, определение общих переменных, являющихся параметрами, которые могут быть обновлены, создание функции затрат, определение обновлений и компиляция функций для обучения и прогнозирования. Если вы не хотите писать код самостоятельно, хотя я настоятельно рекомендую сделать именно так, поскольку тут лишь несколько строк, то соответствующий файл в репозитарии называется theano_warmup.py и находится в папке cartpole.

Итак, первое, что вы можете заметить, – что этот файл весьма невелик. Всё, что нам нужно, – это создать SGDRegressor, чтобы заменить им предыдущий, из прежнего скрипта Q-обучения. Вначале мы импортируем библиотеки Numpy и Theano, а также q_learning – программу, которую мы только что рассматривали.

# ***

from __future__ import print_function, division

from builtins import range

import numpy as np

import theano

import theano.tensor as T

import q_learning

Следующим идёт класс SGDRegressor. Основная часть работы – в конструкторе. Вначале мы выводим на экран надпись «Hello Theano!», чтобы удостовериться, что при запуске программы используется SGDRegressor. Далее мы, как обычно, инициируем w и подставляем его в общие переменные Theano. Затем мы создаём наши входные данные и целевые переменные – X и Y. Как обычно, X является двухмерным, а Y – одномерным. После этого мы вычисляем наш прогноз ŷ, являющийся произведением X и w, а затем – вычисляем квадрат ошибок, который является функцией затрат. Мы получаем градиент и определяем обновление для градиентного спуска. Затем компилируем две функции – одну для обучения и одну для прогноза. В функциях partial_fit и predict мы просто вызываем соответственно функции train_op и predict_op.

class SGDRegressor:

  def __init__(self, D):

    print(“Hello Theano!”)

    w = np.random.randn(D) / np.sqrt(D)

    self.w = theano.shared(w)

    self.lr = 10e-2

    X = T.matrix(‘X’)

    Y = T.vector(‘Y’)

    Y_hat = X.dot(self.w)

    delta = Y – Y_hat

    cost = delta.dot(delta)

    grad = T.grad(cost, self.w)

    updates = [(self.w, self.w – self.lr*grad)]

    self.train_op = theano.function(

      inputs=[X, Y],

      updates=updates,

    )

    self.predict_op = theano.function(

      inputs=[X],

      outputs=Y_hat,

    )

  def partial_fit(self, X, Y):

    self.train_op(X, Y)

  def predict(self, X):

    return self.predict_op(X)

В разделе main мы лишь заменяем SGDRegressor из файла q_learning на только что созданный и вызываем функцию main.

if __name__ == ‘__main__’:

  q_learning.SGDRegressor = SGDRegressor

  q_learning.main()

Запустим программу и посмотрим, что у нас получится.

Разминка в Tensorflow

Теперь мы сделаем разминку в Tensorflow – вновь-таки, просто переписав класс SGDRegressor. И вновь-таки, я настоятельно рекомендую попробовать сначала сделать всё самостоятельно, тем более, что теперь вы уже знаете, в какой форме это делается. Соответствующий файл в репозитарии называется TF_warmup.py и находится в папке cartpole.

Итак, вначале у нас идут все те же импорты, с тем лишь исключением, что теперь мы импортируем библиотеку Tensorflow, а не Theano.

from __future__ import print_function, division

from builtins import range

import numpy as np

import tensorflow as tf

import q_learning

В конструкторе для класса SGDRegressor мы вначале выводим на экран сообщение «Hello TensorFlow!», чтобы знать, что запущен именно этот код. Затем мы создаём обновляемые параметры, которые в Tensorflow называются переменными, а также указываем форму и тип учебных данных, которые в Tensorflow называются заполнителями. Обратите внимание, что w теперь является двухмерной матрицей с размером второй размерности, равной 1. Это связано с тем, что если w будет одномерным, то функция matmul выдаст сообщение об ошибке. Поэтому позже, когда мы будем делать прогноз, нам придётся провести сглаживание с изменением формы, так чтобы прогноз был одномерным и мог быть вычтен из целевой переменной. Разобравшись со всем этим, мы можем определить функцию затрат, которая является квадратом ошибок:

class SGDRegressor:

  def __init__(self, D):

    print(“Hello TensorFlow!”)

    lr = 10e-2

    # create inputs, targets, params

    # matmul doesn’t like when w is 1-D

    # so we make it 2-D and then flatten the prediction

    self.w = tf.Variable(tf.random_normal(shape=(D, 1)), name=’w’)

    self.X = tf.placeholder(tf.float32, shape=(None, D), name=’X’)

    self.Y = tf.placeholder(tf.float32, shape=(None,), name=’Y’)

    # make prediction and cost

    Y_hat = tf.reshape( tf.matmul(self.X, self.w), [-1] )

    delta = self.Y – Y_hat

    cost = tf.reduce_sum(delta * delta)

    # ops we want to call later

    self.train_op = tf.train.GradientDescentOptimizer(lr).minimize(cost)

    self.predict_op = Y_hat

Далее мы инициируем параметры и начинаем сессию. Обратите внимание, что здесь используется функция InteractiveSession. Это позволяет использовать одну и ту же сессию с разными функциями:

    # start the session and initialize params

    init = tf.global_variables_initializer()

    self.session = tf.InteractiveSession()

    self.session.run(init)

Функции partial_fit и predict имеют вполне ожидаемый вид – мы просто вызываем ранее созданные train_op и predict_op.

  def partial_fit(self, X, Y):

    self.session.run(self.train_op, feed_dict={self.X: X, self.Y: Y})

  def predict(self, X):

    return self.session.run(self.predict_op, feed_dict={self.X: X})

В разделе main мы лишь заменяем SGDRegressor из файла q_learning на вновь созданный и вызываем функцию main.

if __name__ == ‘__main__’:

  q_learning.SGDRegressor = SGDRegressor

  q_learning.main()

Запустим программу и посмотрим, что у нас получится.

Подключение нейронной сети

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

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

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

Одним из недостатков этого является эффект катастрофического забывания. Эта идея недавно привлекла к себе большое внимание, поскольку исследователи нашли способы успешно реализовать трансфертное обучение – другими словами, они смогли обучить ИИ одной игре и использовать части нейронной сети для улучшения обучения в другой. При этом они убедились, что нейронная сеть по-прежнему была способна играть в старую игру, то есть нейронная сеть не просто научилась играть в новую игру и забыла старую. Причина же, по которой это исследование заслуживает внимания, заключается в том, что это не то, как нейронная сеть работает по умолчанию – в более типичных случаях в действительности ожидается, что нейронная сеть забудет, как играть в первую игру.

Однако этот эффект забывания применим не только различным задачам. Как вы видели, когда мы выполняем стохастический и пакетный градиентный спуск, функция затрат может немного «прыгать». Я бы сказал, что данный эффект, по-видимому, более выражен в задачах с высоко нелинейной регрессией, а именно этим мы сейчас как раз и занимаемся. В идеале нам нужно, чтобы данные в функции затрат представляли истинное распределение данных. Когда мы обучаемся одновременно на всех учебных данных – это в действительности только приближение к истинному распределению. Это похоже на клинические испытания лекарства – у нас может быть выборка из тысяч людей, и мы надеемся, что они представляют собой всё население в целом.

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

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

Раздел OpenAI Gym. Итоги

И наконец мы подведём итоги всему тому, что изучили в этой части. Эта часть была полностью посвящена знакомству с OpenAI Gym и затем – постепенному созданию всё более и более сложных агентов обучения с подкреплением с использованием уже имеющихся знаний.

Прежде всего мы начали с рассмотрения интерфейса OpenAI Gym. Мы увидели, как можем присоединиться к любой среде, рассмотрели её пространство состояний и пространство действий и как отыграть эпизод. Мы также увидели, как можно сохранить видео, в котором наш агент отыгрывает эпизод.

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

Затем мы вновь рассмотрели тележку с шестом, однако с применением Q-обучения и ячейками для состояний. Их использование позволило нам использовать табличный метод, с которым вы знакомы по предыдущему курсу. Этот пример, кроме прочего, помогает напомнить о Q-обучении, являющимся простым примером метода TD(0), рассмотренного ранее.

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

Затем мы рассмотрели задачу о машине на склоне с Q-обучением и сетями радиальных базисных функций. Это первый случай в данном курсе, когда мы использовали метод приближений. Выполнение этого примера позволило нам изучить некоторые важные детали реализации алгоритма, такие как выбор экземпляров радиальных базисных функций, комбинирование признаков и других инструментов библиотеки Sci-kit Learn.

Далее мы применили Q-обучение с сетями радиальных базисных функций для решения задачи о тележке с шестом. Мы видели, что пришлось сделать некоторые изменения, поскольку пространство состояний для тележки с шестом расширяется до бесконечности, тогда как выборка OpenAI Gym является равномерной из пространства состояний, без учёта вероятности попадания в какое-либо конкретное состояние. Мы знаем, что это не позволяет нам получить хорошие экземпляры для наших сетей радиальных базисных функций, и в данном примере сделали следующий шаг и создали собственное линейное приближение функции с градиентным спуском в Numpy.

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

Далее мы сделали такую же разминку в Tensorflow, где вновь переписали приближение функции. Это позволило нам освежить знакомство с важными особенностями Tensorflow, такими как создание переменных, заполнители, сессии и оптимизаторы.

Андрей Никитенко
Андрей Никитенко
Задать вопрос эксперту
Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: