Практические упражнения в TensorFlow и что изучать далее

Универсальный пример с использованием TensorFlow

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

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

Если вы не хотите писать код самостоятельно или сделали в нём ошибку, найдите в репозитарии github файл tf_example.py.

Мы начинаем с импорта библиотек TensorFlow, Numpy и Matplotlib.

import tensorflow as tf

import numpy as np

import matplotlib.pyplot as plt

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

Nclass = 500

D = 2

M = 3

K = 3

X1 = np.random.randn(Nclass, D) + np.array([0, -2])

X2 = np.random.randn(Nclass, D) + np.array([2, 2])

X3 = np.random.randn(Nclass, D) + np.array([-2, 2])

X = np.vstack([X1, X2, X3]).astype(np.float32)

Y = np.array([0]*Nclass + [1]*Nclass + [2]*Nclass)

 

plt.scatter(X[:,0], X[:,1], c=Y, s=100, alpha=0.5)

plt.show()

N = len(Y)

T = np.zeros((N, K))

for i in xrange(N):

    T[i, Y[i]] = 1

Таким образом, у нас есть три гауссова облака с центрами (0, -2), (2, 2) и (-2, 2) по 500 примеров в каждом классе. На случай, если вы забыли, как выглядят данные, мы выведем на экран диаграмму рассеяния. Мы также создали переменные показателей для целевых переменных.

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

def init_weights(shape):

    return tf.Variable(tf.random_normal(shape, stddev=0.01))

Нам также понадобится функция forward, похожая на ту, которую мы писали ранее с помощью Numpy, но с использованием функций библиотеки TensorFlow.

def forward(X, W1, b1, W2, b2):

    Z = tf.nn.sigmoid(tf.matmul(X, W1) + b1)

    return tf.matmul(Z, W2) + b2

Далее мы создадим заполнители TensorFlow, представляющие наши данные X и Y. TensorFlow создаёт граф и знает, как всё посчитать, хотя у нас ещё нет никаких значений. Таким образом, переменная tfX является лишь заполнителем для данных, причём вторая размерность равна D, поскольку это размерность наших данных, а в первую размерность мы можем подставить любую величину. Переменная tfY также является заполнителем со второй размерностью, равной K.

tfX = tf.placeholder(tf.float32, [None, D])

tfY = tf.placeholder(tf.float32, [None, K])

Далее мы создаём весовые коэффициенты подобно тому, как мы это делали в Numpy.

W1 = init_weights([D, M])

b1 = init_weights([M])

W2 = init_weights([M, K])

b2 = init_weights([K])

Теперь опишем исходящую переменную, хотя ей и не присвоено ещё никакого значения.

py_x = forward(tfX, W1, b1, W2, b2)

Ещё одно отличие TensorFlow – как мы определяем функцию затрат.

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(py_x, tfY))

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

train_op = tf.train.GradientDescentOptimizer(0.05).minimize(cost)

predict_op = tf.argmax(py_x, 1)

Далее мы должны сделать такие штуки, как сессии. Не переживайте, если вы не знаете, что это, на данный момент достаточно того, что они нам нужны. И сразу же инициируем все наши переменные.

sess = tf.Session()

init = tf.initialize_all_variables()

sess.run(init)

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

for i in xrange(1000):

    sess.run(train_op, feed_dict={tfX: X, tfY: T})

    pred = sess.run(predict_op, feed_dict={tfX: X, tfY: T})

    if i % 10 == 0:

        print(np.mean(Y == pred)

Запустим нашу программу. Мы опять видим наши данные, после чего получаем окончательную точность.

Визуализация обучения нейронной сети с помощью TensorFlow Playground

На этой лекции мы познакомимся с наглядным представлением процесса обучения нейронной сети, и для этого разработчики TensorFlow создали действительно замечательный инструмент, находящийся по адресу playground.tensorflow.org.

Вначале вы увидите ровно то, что сейчас показано на экране. Сейчас вы можете увидеть проблему XOR и «проблему пончика». Как видите, это действительно важные вещи, поскольку об этом говорят даже ребята из Google.

Итак, вначале мы рассмотрим знаменитую «проблему пончика» с использованием логистической регрессии, поэтому количество скрытых слоёв равно нулю. Как мы знаем, если у нас есть просто x1 и x2, мы не сможем найти решение для «проблемы пончика». И действительно, как вы можете видеть, ошибки для проверочного и учебного набора составляют около 0,5.

Но мы знаем, что если добавить ещё и радиус, то решение вполне возможно, поскольку радиус является единственным разделяющим признаком. Радиус будет равен \sqrt[2]{x_1+x_2} . Конечно, квадратный корень не нужен, достаточно  x_{1}^{2} и  x_{2}^{2} . Для этого мы можем взять отдельно x1 и отдельно x2. В таком случае при обучении модели мы видим, что проверочная ошибка очень близка к нулю, хотя графическое отображение и не обновилось. В общем, визуализация немного глючит, но если вы немного пошевелите мышкой, вы можете заставить её работать.

Теперь попробуем решить проблему XOR. Вы знаете, что просто задав x1 и x2, задачу решить невозможно. И действительно, мы вновь видим ошибку в районе 0,5. Но мы также знаем, что добавив ещё один признак x1x2, решение задачи возможно. Попробуем вновь обучить модели и получим, что ошибка снижается примерно до нуля, будучи в зависимости лишь от x1x2 – цвет и ширина линий показывают нам силу сигнала.

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

Вернёмся теперь к нашему «пончику» и проделаем то же самое. Начнём с двух нейронов и посмотрим, что получится. Как видим, получается не очень. Попробуем три нейрона. Похоже, некоторые проблемы всё равно остаются. Добавим ещё один нейрон. И, похоже, для нейронов четвёрка является волшебным числом для нахождения решения.

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

Ещё одна, более сложная проблема, о которой мы не говорили, – это проблема спирали. Как вы можете видеть, её определённо невозможно решить, основываясь лишь на x1 и x2. Вы можете предположить, что следует добавить sin x1  и sin x2, поскольку они являются периодическими функциями и могут помочь обучению, но видно что они не помогают. Мы даже можем подключить все дополнительные признаки. Решение несколько неустойчиво, а ошибка всё равно колеблется в районе 0,5. Мы можем попытаться изменить функцию активации, хотя на самом деле это не имеет никакого значения, поскольку пока что нет никаких скрытых слоёв.

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

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

Но, как вы видите, один слой и 8 нейронов с этим плохо справляются. Увеличим количество скрытых слоёв до 6 и добавим максимально возможное количество нейронов в каждый из них – у нас получится 6 скрытых слоёв по 8 нейронов в каждом. Посмотрим, сможет ли нейронная сеть обучиться. Как вы можете видеть, обучение проходит куда медленнее, чем в предыдущих примерах, поскольку это намного более сложная задача. Кажется, нейронная сеть выровнялась на одном месте. Попробуем другую функции активации, например relu, и посмотрим, что получится. Фигура подрагивает, но кажется, ошибка уменьшается. Через некоторое время, судя по всему, сеть обучилась очень кривой спирали. Любопытно, что у нас получилось с функцией relu, но не получилось с гиперболическим тангенсом.

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

Куда двигаться дальше?

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

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

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

Что же ещё можно изучать о нейронных сетях?

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

Нейронные сети также являются новейшими инструментами в NLP, то есть обработке естественных языков, позволяя моделировать зависимости между словами и предложениями и представлять слова в виде числовых векторов, поскольку нейронные сети работают именно с ними. Одним из известных примеров такого рода является Word2vec, позволяющий выполнять такие операции, как знаменитое «король – мужчина + женщина = королева».

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

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

Dropout и DropConnect являются новыми способами регуляризации нейронной сети и стали альтернативой обычным L1 и L2-регуляризации, которыми мы привыкли пользоваться.

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

Так куда же двигаться дальше? Много чему ещё есть поучиться, когда речь заходит о глубоком обучении, и прямо здесь, на Udemy, есть соответствующие курсы для освоения материала, о котором говорилось ранее. Вы, думаю, легко найдёте эти курсы, когда кликните на мой профайл. Далее посетите мой сайт https://lazyprogrammer.me и убедитесь, что вы подписаны на рассылку. Я выдаю купоны на мои курсы на Udemy и постоянно пишу бесплатные статьи по машинному обучению. Это лучший способ оставаться в курсе того, чем я занимаюсь в рамках бесплатных учебных материалов, которые я выпускаю, чтобы помочь вам развиваться в сфере глубокого обучения.

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

Наконец, подписывайтесь на меня в твиттере и заходите на мою страничку в Facebook. Это ещё один способ удостовериться, что вы первые в очереди при публикации курса и выдаче купонов.

И последнее, я хотел бы поблагодарить вас за приобретение этого курса!

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

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