Электронная библиотека » Нихиль Будума » » онлайн чтение - страница 4


  • Текст добавлен: 27 января 2020, 12:00


Автор книги: Нихиль Будума


Жанр: Управление и подбор персонала, Бизнес-Книги


Возрастные ограничения: +16

сообщить о неприемлемом содержимом

Текущая страница: 4 (всего у книги 18 страниц) [доступный отрывок для чтения: 5 страниц]

Шрифт:
- 100% +
Управление моделями на CPU и GPU

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

«/cpu:0»

CPU нашей машины.

«/gpu:0»

Первый GPU нашей машины (если есть).

«/gpu:1»

Второй GPU нашей машины (если есть).

Если у операции есть ядра CPU и GPU и разрешено использование GPU, TensorFlow автоматически будет выбирать вариант для GPU. Чтобы посмотреть, какие устройства использует граф вычислений, мы можем инициализировать сессию TensorFlow, установив параметр log_device_placement в значение True:

sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))

Если мы хотим использовать конкретное устройство, можно его выбрать с помощью конструкции with tf.device[31]31
  https://www.tensorflow.org/api_docs/python/tf/device.


[Закрыть]
. Но если оно недоступно, возникнет ошибка. Нужно, чтобы TensorFlow нашла другое доступное устройство, если выбранное не существует; можно передать флаг allow_soft_placement в переменную сессии таким образом[32]32
  https://www.tensorflow.org/api_docs/python/tf/ConfigProto.


[Закрыть]
:

with tf.device(‘/gpu:2'):

a = tf.constant([1.0, 2.0, 3.0, 4.0], shape=[2, 2], name='a')

b = tf.constant([1.0, 2.0], shape=[2, 1], name='b')

c = tf.matmul(a, b)

sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True))

sess.run(c)

TensorFlow также позволяет строить модели, которые используют несколько GPU. Они создаются в виде башни, как показано на рис. 3.3.


Рис. 3.3. Создание моделей для нескольких GPU в виде башни


Ниже приведен пример кода для нескольких GPU:

c = []

for d in [‘/gpu:0', ‘/gpu:1']:

with tf.device(d):

a = tf.constant([1.0, 2.0, 3.0, 4.0], shape=[2, 2], name='a')

b = tf.constant([1.0, 2.0], shape=[2, 1], name='b')

c. append(tf.matmul(a, b))

with tf.device(‘/cpu:0'):

sum = tf.add_n(c)

sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))

sess.run(sum)

Создание модели логистической регрессии в TensorFlow

Мы рассмотрели базовые понятия TensorFlow и можем построить простую модель для набора данных MNIST. Как вы, наверное, помните, наша цель – распознать рукописные цифры по черно-белым изображениям 28×28 единиц. Первая сеть, которую мы построим, реализует простой алгоритм машинного обучения – логистическую регрессию[33]33
  Cox D. R. The Regression Analysis of Binary Sequences // Journal of the Royal Statistical Society. Series B (Methodological). 1958. Pp. 215–242.


[Закрыть]
.

Логистическая регрессия – метод, с помощью которого мы можем вычислить вероятность того, что входные данные относятся к одному из целевых классов. Определим вероятность того, что данное изображение – 0, 1… или 9.

Наша модель использует матрицу W, которая представляет веса соединений в сети, и вектор b, соответствующий смещению, для вычисления того, принадлежит ли входящее значение x классу i, при помощи функции мягкого максимума (softmax), о которой мы уже говорили выше:



Наша задача – определить значения W и b, которые будут наиболее эффективно и точно классифицировать входящие данные. Сеть логистической регрессии можно выразить в схеме (рис. 3.4). Для простоты мы опустили смещения и их соединения.


Рис. 3.4. Интерпретация логистической регрессии как примитивной нейросети


Легко заметить, что сеть для интерпретации логистической регрессии довольно примитивна. У нее нет скрытых слоев, а следовательно, ее способность усваивать сложные взаимоотношения ограничена. У нас есть выходная функция мягкого максимума размерности 10, поскольку у нас 10 возможных исходов для каждого входного значения. Более того, есть входной слой размера 784 – один входной нейрон для каждого пиксела изображения! Модель в целом способна корректно классифицировать наш набор данных, но еще есть куда расти. До конца этой главы и в главе 5 мы будем стараться повысить точность нашей работы. Но сначала посмотрим, как реализовать эту логистическую сеть в TensorFlow, чтобы обучить ее на нашем компьютере.

Модель логистической регрессии строится в четыре этапа:

1) inference: создается распределение вероятностей по выходным классам для мини-пакета[34]34
  Для каждого экземпляра данных в мини-пакете нейронная сеть выдает вероятность принадлежности данных к каждому классу (то есть вероятность того, что на исходном изображении 0, 1, 2 и так далее до 9). Прим. науч. ред.


[Закрыть]
;

2) loss: вычисляется значение функции потерь (в нашем случае перекрестная энтропии);

3) training: отвечает за вычисление градиентов параметров модели и ее обновление;

4) evaluate: определяется эффективность модели.

Для мини-пакета из 784-мерных векторов, соответствующих изображениям MNIST, мы можем выразить логистическую регрессию через функцию мягкого максимума от входных данных, умноженных на матрицу, которая представляет веса соединений входного и выходного слоев.

Каждая строка выходного тензора содержит распределение вероятностей по классам для соответствующего образца данных в мини-выборке:

def inference(x):

tf.constant_initializer(value=0)

W = tf.get_variable("W", [784, 10], initializer=init)

b = tf.get_variable("b", [10], initializer=init)

output = tf.nn.softmax(tf.matmul(x, W) + b)

return output

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

def loss(output, y):

dot_product = y * tf.log(output)

# Reduction along axis 0 collapses each column into a

# single value, whereas reduction along axis 1 collapses

# each row into a single value. In general, reduction along

# axis i collapses the ith dimension of a tensor to size 1.

xentropy = – tf.reduce_sum(dot_product, reduction_indices=1)

loss = tf.reduce_mean(xentropy)

return loss

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

def training(cost, global_step):

optimizer = tf.train.GradientDescentOptimizer(learning_rate)

train_op = optimizer.minimize(cost, global_step=global_step)

return train_op

Наконец, мы можем создать простой вычислительный подграф для оценки модели на проверочных или тестовых данных:

def evaluate(output, y):

correct_prediction = tf.equal(tf.argmax(output, 1) tf.argmax(y, 1))

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

return accuracy

На этом настройка графа в TensorFlow для модели логистической регрессии завершена.

Журналирование и обучение модели логистической регрессии

У нас есть все компоненты, можно сводить их воедино. Чтобы сохранять важную информацию в процессе обучения модели, мы записываем в журнал несколько сводок статистики. Например, мы используем команды tf.scalar_summary[35]35
  https://www.tensorflow.org/api_docs/python/tf/summary/scalar.


[Закрыть]
и tf.histogram_summary[36]36
  https://www.tensorflow.org/api_docs/python/tf/summary/histogram.


[Закрыть]
для записи ошибки в каждом мини-пакете, ошибки на проверочном множестве и распределения параметров. Для примера приведем скалярную сводку статистик для функции потерь:

def training(cost, global_step):

tf.scalar_summary(«cost», cost)

optimizer = tf.train.GradientDescentOptimizer(learning_rate)

train_op = optimizer.minimize(cost, global_step=global_step)

return train_op

На каждой эпохе мы запускаем tf.merge_all_summaries[37]37
  https://www.tensorflow.org/api_docs/python/tf/summary/merge_all.


[Закрыть]
, чтобы собрать все записанные сводки, и с помощью tf.train.SummaryWriter сохраняем журнал на диске. В следующем разделе мы расскажем, как визуализировать эти журналы при помощи встроенного инструмента TensorBoard.

Помимо сводок статистики, мы сохраняем параметры модели с помощью tf.train.Saver. По умолчанию это средство поддерживает пять последних контрольных точек, которые мы можем восстанавливать для дальнейшего использования. В результате получаем следующий скрипт на Python:

# Parameters

learning_rate = 0.01

training_epochs = 1000

batch_size = 100

display_step = 1

with tf.Graph(). as_default():

# mnist data image of shape 28*28=784

x = tf.placeholder("float", [None, 784])

# 0–9 digits recognition => 10 classes

y = tf.placeholder("float", [None, 10])

output = inference(x)

cost = loss(output, y)

global_step = tf.Variable(0, name='global_step', trainable=False)

train_op = training(cost, global_step)

eval_op = evaluate(output, y)

summary_op = tf.merge_all_summaries()

saver = tf.train.Saver()

sess = tf.Session()

summary_writer = tf.train.SummaryWriter("logistic_logs/", graph_def=sess.graph_def)

init_op = tf.initialize_all_variables()

sess.run(init_op)

# Training cycle

for epoch in range(training_epochs):

avg_cost = 0.

total_batch = int(mnist.train.num_examples/batch_size)

# Loop over all batches

for i in range(total_batch):

mbatch_x, mbatch_y = mnist.train.next_batch(batch_size)

# Fit training using batch data

feed_dict = {x: mbatch_x, y: mbatch_y}

sess.run(train_op, feed_dict=feed_dict)

# Compute average loss

minibatch_cost = sess.run(cost, feed_dict=feed_dict)

avg_cost += minibatch_cost/total_batch

# Display logs per epoch step

if epoch % display_step == 0:

val_feed_dict = {

x: mnist.validation.images,

y: mnist.validation.labels

}

accuracy = sess.run(eval_op, feed_dict=val_feed_dict)

print "Validation Error: ", (1 – accuracy)

summary_str = sess.run(summary_op, feed_dict=feed_dict)

summary_writer.add_summary(summary_str, sess.run(global_step))

saver.save(sess, "logistic_logs/model-checkpoint", global_step=global_step)

print "Optimization Finished!"

test_feed_dict = {

x: mnist.test.images,

y: mnist.test.labels

}

accuracy = sess.run(eval_op, feed_dict=test_feed_dict)

print "Test Accuracy: ", accuracy

Запуск этого скрипта обеспечивает нам аккуратность[38]38
  Аккуратность – одна из мер оценки качества работы нейронной сети (и других алгоритмов машинного обучения), показывающая, какая доля экземпляров данных была правильно классифицирована. Прим. науч. ред.


[Закрыть]
в 91,9 % по тестовому набору из 100 эпох обучения. Уже неплохо, но в последнем разделе главы мы постараемся улучшить этот результат при помощи нейросети с прямым распространением сигнала.

Применение TensorBoard для визуализации вычислительного графа и обучения

Настроив журналирование сводок статистики так, как показано в предыдущем разделе, мы можем визуализировать собранные данные. В TensorFlow предусмотрен инструмент, который обеспечивает простой в использовании интерфейс навигации по сводкам[39]39
  https://www.tensorflow.org/get_started/graph_viz.


[Закрыть]
. Запуск TensorBoard несложен:

tensorboard – logdir=<absolute_path_to_log_dir>

Параметр logdir должен быть установлен на каталог, в котором tf.train.SummaryWriter фиксировал сводки статистики. Нужно прописывать абсолютный, а не относительный путь, иначе TensorBoard может не найти журналы. Если мы успешно запустили TensorBoard, этот инструмент будет предоставлять доступ к данным через http://localhost:6006/ – этот адрес можно открыть в браузере.

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


Рис. 3.5. Представление событий в TensorBoard


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


Рис. 3.6. Графическое представление в TensorBoard

Создание многослойной модели для MNIST в TensorFlow

Используя модель логистической регрессии, мы сократили частоту ошибок в наборе данных MNIST до 8,1 %. Показатель впечатляющий, но для практического применения он не особо подходит.

Например, если система используется для проверки чеков на четырехзначные суммы (от 1000 до 9999 долларов), ошибки будут допускаться почти в 30 % случаев! Чтобы создать более точную программу для чтения цифр из MNIST, построим нейросеть с прямым распространением сигнала.

Мы создаем такую модель с двумя скрытыми слоями, каждый из которых состоит из 256 нейронов ReLU (рис. 3.7).


Рис. 3.7. Сеть с прямым распространением сигнала на нейронах ReLU с двумя скрытыми слоями


Мы можем использовать большую часть кода из примера для логистической регрессии, внеся всего несколько изменений:

def layer(input, weight_shape, bias_shape):

weight_stddev = (2.0/weight_shape[0])**0.5

w_init = tf.random_normal_initializer(stddev=weight_stddev)

bias_init = tf.constant_initializer(value=0)

W = tf.get_variable("W", weight_shape, initializer=w_init)

b = tf.get_variable("b", bias_shape, initializer=bias_init)

return tf.nn.relu(tf.matmul(input, W) + b)

def inference(x):

with tf.variable_scope("hidden_1"):

hidden_1 = layer(x, [784, 256], [256])

with tf.variable_scope("hidden_2"):

hidden_2 = layer(hidden_1, [256, 256], [256])

with tf.variable_scope("output"):

output = layer(hidden_2, [256, 10], [10])

return output

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

Проблема усугубляется при росте числа слоев в модели, а следовательно, и сложности поверхности ошибок. Один из способов ее устранения – умная инициализация. Исследование 2015 года, опубликованное К. Хе и его коллегами, показывает, что для нейронов ReLU дисперсия весов в сети должна быть равна 2/nin, где nin – число входов в нейрон[40]40
  He K. et al. Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification // Proceedings of the IEEE International Conference on Computer Vision. 2015.


[Закрыть]
. Любопытному читателю стоит рассмотреть, что произойдет при изменении инициализации. Например, если снова заменить tf.random_normal_initializer на tf.random_uniform_initializer, который мы использовали в примере с логистической регрессией, результаты серьезно ухудшатся.

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

def loss(output, y):

xentropy = tf.nn.softmax_cross_entropy_with_logits(output, y)

loss = tf.reduce_mean(xentropy)

return loss

Работа программы на протяжении 300 эпох выдает значительные улучшения по сравнению с моделью логистической регрессии. Она функционирует с аккуратностью 98,2 %, почти на 78 % снижая частоту ошибок на знак по сравнению с первым вариантом.

Резюме

В этой главе мы больше узнали о том, как использовать TensorFlow в качестве библиотеки для представления и обучения наших моделей. Мы поговорили о ряде ее важных свойств, в том числе управлении сессиями, переменными, операциями, графами вычислений и устройствами. В последних разделах мы на основе полученных знаний обучили и визуализировали модель логистической регрессии и нейросеть с прямым распространением сигнала при помощи стохастического градиентного спуска. И если модель логистической сети совершала много ошибок на наборе данных MNIST, то нейросеть с прямым распространением сигнала гораздо эффективнее: в среднем всего 1,8 ошибки на 100 символов. Мы улучшим этот показатель в главе 5.

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

Глава 4. Не только градиентный спуск

Проблемы с градиентным спуском

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

Однако полное их обучение сопряжено с разными сложностями, которые требуют множества технологических инноваций, в том числе больших размеченных массивов данных (ImageNet, CIFAR и т. д.), более передового «железа» с ускорителями GPU, а также новинок в области алгоритмов.

Многие годы исследователи прибегали к поуровневому «жадному» предварительному обучению для обработки сложных поверхностей ошибок в моделях глубокого обучения[41]41
  Bengio Y. et al. Greedy Layer-Wise Training of Deep Networks // Advances in Neural Information Processing Systems. 2007. Vol. 19. P. 153.


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

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

Локальные минимумы на поверхности ошибок глубоких сетей

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

Представьте себе, что вы – муравей, живущий в континентальной части США. Вас выбросили где-то в случайном месте, и ваша задача – найти самую низкую точку на этой поверхности. Как это сделать? Если вы можете видеть только то, что вас непосредственно окружает, задача кажется неразрешимой. Если бы поверхность США имела форму миски (была бы, говоря математически, выпуклой) и мы смогли бы удачно установить темп обучения, можно было бы воспользоваться алгоритмом градиентного спуска и в конце концов добраться до дна. Но рельеф США очень сложный. И даже если мы найдем какую-то долину (локальный минимум), мы не узнаем, действительно ли это самая низкая точка на карте (глобальный минимум). В главе 2 мы говорили о том, как мини-пакетный градиентный спуск помогает в продвижении по сложной поверхности ошибок, на которой есть проблемные районы с нулевым градиентом. Но, как видно из рис. 4.1, даже стохастическая поверхность ошибок не спасает от глубокого локального минимума.


Рис. 4.1. Мини-пакетный градиентный спуск помогает избежать мелкого локального минимума, но редко эффективен при наличии глубокого локального минимума


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

Определимость модели

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

Внутри слоя полносвязной нейросети с прямым распространением сигнала любая перестановка нейронов не изменит данные на выходе. Проиллюстрируем это при помощи простого слоя из трех нейронов на рис. 4.2. Оказывается, что в слое из n нейронов существует n! способов перестановки параметров. А для глубокой сети с l слоев, каждый из которых состоит из n нейронов, имеется n!l эквивалентных конфигураций.


Рис. 4.2. Перестройка нейронов в слое нейросети приводит к эквивалентным конфигурациям в силу симметрии


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

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

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


Страницы книги >> Предыдущая | 1 2 3 4 5 | Следующая
  • 4 Оценок: 5

Правообладателям!

Данное произведение размещено по согласованию с ООО "ЛитРес" (20% исходного текста). Если размещение книги нарушает чьи-либо права, то сообщите об этом.

Читателям!

Оплатили, но не знаете что делать дальше?


Популярные книги за неделю


Рекомендации