Тензор флоу как использовать
TensorFlow для начинающих. Часть 1: общие сведения, установка библиотеки
TensorFlow — это опенсорсная библиотека, созданная Google, которая используется при разработке систем, использующих технологии машинного обучения. Эта библиотека включает в себя реализацию множества мощных алгоритмов, рассчитанных на решение распространённых задач машинного обучения, среди которых можно отметить распознавание образов и принятие решений.
Этот материал посвящён основам TensorFlow и рассчитан на читателей, которые ничего не знают о данной библиотеке.
TensorFlow: современная библиотека машинного обучения
Проект TensorFlow был переведён Google в разряд опенсорсных в 2015 году. Его предшественником был проект DistBelief, годы опыта, накопленные в ходе работы с которым, отразились на TensorFlow.
Разработчики библиотеки TensorFlow стремились к тому, чтобы она была бы гибкой, эффективной, расширяемой, переносимой. В результате ей можно пользоваться в самых разных вычислительных средах — от тех, которые формируются мобильными устройствами, до сред, представленных огромными кластерами. Библиотека позволяет быстро готовить к реальной работе обученные модели, что устраняет необходимость в создании особых реализаций моделей для продакшн-целей.
Библиотека TensorFlow, с одной стороны, привлекает к себе внимание опенсорс-сообщества и открыта для инноваций, а с другой — пользуется поддержкой крупной корпорации. Это позволяет говорить о том, что у неё есть все шансы на стабильное развитие.
Эта библиотека, благодаря совместным усилиям всех тех, кто работает над ней, подходит для решения задач самых разных масштабов. От тех, которые возникают перед самостоятельным разработчиком, до тех, которые встают перед стартапами и даже перед крупными компаниями вроде Google. С того момента, как эта библиотека стала опенсорсной, с ноября 2015 года, она стала одной из интереснейших библиотек машинного обучения. Её всё чаще и чаще используют при проведении исследований, при разработке реальных приложений, при обучении.
TensorFlow постоянно улучшается, её постоянно снабжают чем-то новым, оптимизируют. Кроме того, растёт и сообщество, сформированное вокруг этой библиотеки.
О названии TensorFlow
Тензор (tensor) — это стандартный способ представления данных в системах глубокого обучения. Тензоры — это многомерные массивы, расширение двумерных таблиц (матриц) для представления данных, имеющих более высокие размерности. Проще говоря, тензор — это n-мерная матрица.
В целом, если вы привыкли к работе с матрицами, тензоры можно представлять себе так же, как вы представляете себе матрицы.
Начнём с установки TensorFlow.
Установка TensorFlow
Если вы начинаете работу с чистой установки Python (возможно, вы установили Python специально ради изучения TensorFlow), для установки TensorFlow достаточно воспользоваться pip :
Этот подход прост, но у него есть некоторые неприятные особенности. Они заключаются в том, что при установке TensorFlow, вместо уже установленных пакетов, будут установлены определённые версии пакетов-зависимостей этой библиотеки.
Для того чтобы установить TensorFlow в виртуальном окружении, сначала нужно такое окружение создать. Мы собираемся разместить его в папке
Выше мы создали виртуальное окружение tensorflow в папке
/envs (оно представлено папкой
/envs/tensorflow ). Для того чтобы активировать это окружение, воспользуемся следующей командой:
После этого приглашение командной строки должно измениться, указывая на активированное виртуальное окружение:
Теперь можно установить TensorFlow в виртуальное окружение с использованием pip :
Такая установка не повлияет на другие пакеты, установленные на компьютере.
Для выхода из виртуального окружения можно воспользоваться такой командой:
После этого приглашение командной строки примет обычный вид.
До недавних пор TensorFlow было очень сложно использовать в среде Windows. Однако после выхода TensorFlow 0.12 особых проблем в этой сфере больше не наблюдается. А именно, для установки CPU-версии TensorFlow под Windows достаточно выполнить следующую команду:
А для установки GPU-версии — следующую:
При установке такой версии TensorFlow предполагается, что у вас уже есть CUDA 8.
Теперь библиотека TensorFlow на вашем компьютере установлена, а значит, пришло время с ней поработать. Начнём, как это обычно бывает при изучении новых технологий, с «Hello World!».
Уважаемые читатели! Этот материал представляет собой перевод начала данной публикации, посвящённой основам TensorFlow. Как вы думаете, стоит ли переводить её дальше?
Учебное пособие по TensorFlow: 10 минутное практическое занятие по TensorFlow для начинающих [перевод]
Привет, Хабр! Представляю вашему вниманию перевод статьи «TensorFlow Tutorial: 10 minutes Practical TensorFlow lesson for quick learners» автора Ankit Sachan.
Этот туториал по TensorFlow предназначен для тех, кто имеет общее представление о машинном обучении и пытается начать работу с TensorFlow.
Прежде всего у вас должен быть установлен TensorFlow. Вы можете его установить по этому руководству. Этот урок разделен на две части: в первой части мы объясняем основы на рабочем примере, во второй части мы строим модель линейной регрессии.
Часть 1. Основы TensorFlow
TensorFlow — это библиотека для численных расчетов, в которой данные проходят через граф. Данные в TensorFlow представлены n-мерными массивами — тензорами. Граф составлен из данных (тензоров) и математических операций.
В узлах графа находятся математические операции.
Ребра графа представляют тензоры, которые «перетекают» между операциями.
Есть еще один аспект, в котором TensorFlow сильно отличается от любого другого языка программирования. В TensorFlow вам сначала нужно создать проект того, что вы хотите создать. Изначально при создании графа переменные не имеют значений. Позже, когда вы создали полный граф, вы запускаете его в сессии и только тогда переменные будут принимать какие-либо значения.
Давайте начнем учиться на практике. Запустите python и импортируйте TensorFlow:
Для определения операций используется граф, при этом все операции выполняются в сессии. Графы и сессии создаются независимо друг от друга. Относитесь к графу как к проекту строительства, а к сессии как к строительной площадке.
1.1. Графы в TensorFlow
Граф это основа TensorFlow, все вычисления (операции), переменные находятся на графе. Все, что происходит в коде, находится на дефолтном графе, предоставленном TensorFlow. Вы можете получить доступ к этому графу следующим образом:
А так вы можете получить список всех операций в этом графе:
Сейчас ваш граф пуст. Если вам необходимо напечатать название каждой операции выполните следующий код:
Вывод снова будет пустым. В будущем, после добавления операций в граф, мы будем использовать этот код для вывода их названий.
1.2. Сессии в TensorFlow
Граф только определяет вычисления (создает проект), но не имеет никаких переменных, никаких значений, если мы не запустим граф или часть графа в сессии.
Вы можете создать сессию так:
Не забывайте, что начав сессию вам необходимо ее завершить или использовать блок with… as, например вот так:
Плюсом использования такого подхода является то что сессия будет закрыта автоматически в конце блока with
1.3. Тензоры в TensorFlow
TensorFlow хранит данные в тензорах — многомерных массивах очень похожих на массивы numPy.
a) Константы — это значения которые не могут быть изменены. Они определяются так:
при этом при попытке вывода вы получите следующее:
Как вы можете видеть, это отличается от других языков программирования, например от python. Вы не можете распечатать или получить доступ к константе до тех пор пока не запустите сессию. Давайте так и сделаем:
Этот код выведет 1.0
b) Переменные это тоже тензоры которые походи на переменные в других языках программирования:
В отличие от констант переменные могут менять свое содержимое. Однако переменные в TensorFlow должны быть инициализированы в отдельной операции инициализации. Может быть достаточно трудоемко инициализировать все переменные, но TensorFlow предоставляет механизм для инициализации всех переменных за раз:
Теперь необходимо запустить операцию инициализации до того как вы попытаетесь получить доступ к переменной:
Этот код выведет 2.0
Если вы теперь попробуете распечатать операции в графе
то получите следующий вывод:
Как видите, мы объявили «a» как Const и она была добавлена к графику. Аналогично, для переменной b в граф TensorFlow было добавлено много состояний «test_var», таких как test_var/initial_value, test_var/read и т. д. Вы можете визуализировать всю сеть, используя TensorBoard, который представляет собой инструмент для визуализации графика TensorFlow и процесса обучения.
с) Плейсхолдеры — это тензоры, ожидающие инициализации данными. Они используются для данных которые будут предоставлены только когда код фактически будет выполнен в сеансе. То, что передается в плейсхолдер это словарь который содержит названия плейсхолдеров и их значения:
Данный код выведет 6.0
1.4. Устройства в TensorFlow
TensorFlow имеет очень мощные встроенные возможности для запуска вашего кода на GPU, CPU или кластерах. Он предоставляет вам возможность выбрать устройство, на котором вы хотите запускать ваш код. Однако, это не то о чем стоит задумываться когда вы только начинаете знакомство с TF.
Итак вот полная картина того, как устроены вычисления в TensorFlow:
Часть 2. Туториал по TensorFlow с простым примером
В этой части мы рассмотрим код для запуска линейной регрессии. Перед этим давайте рассмотрим некоторые основные функции TensorFlow, которые мы будем использовать в коде.
Создание случайного нормального распределения:
Используйте random_normal для создания случайных значений из нормального распределения. В этом примере w — это переменная размерности 784 * 10 со случайными значениями со стандартным отклонением 0,01.
Reduce_mean — вычисляет среднее значение массива
Данный код выведет 35
argmax — очень похож на argmax в Python. Получает максимальное значение из тензора вдоль указанной оси.
Выведет массив ([2, 0]), который показывает индекс максимального значения в каждой из строк a.
Задача линейной регрессии
Постановка задачи: имеется большое количество точек данных, пытаетесь им в соответствие прямую линию. Для этого примера мы создадим 100 точек данных и попытаемся поставить им в соответствие линию.
2.1. Создание тренировочного набора данных
2.2. Плейсхолдеры
2.3. Моделирование
Модель линейной регрессии имеет вид
— мы должны вычислить значение w. Давайте инициализируем w нулевым значением и создадим модель для решения этой проблемы. Мы определяем функцию стоимость как
TensorFlow поставляется со многими оптимизаторами, которые рассчитывают и обновляют градиенты после каждой итерации, пытаясь минимизировать указанной функции стоимости. Мы собираемся определить операцию обучения используя GradientDescentOptimizer, чтобы минимизировать нашу функцию стоимости при скорости обучения 0,01. Затем мы запустим это операцию обучения в цикле.
2.4. Обучение
До этого момента мы только определили граф. Никаких вычислений не происходило, ни одна из переменных TensorFlow не имеет значения. Чтобы запустить этот граф, нам нужно создать сессию и выполнить ее. Перед этим нам нужно создать операцию инициализации для всех переменных:
Обратите внимание, что первое, что было сделано, — это инициализация переменных путем вызова init внутри session.run(). Позже мы запускаем train_op, скармливая ей feed_dict. Наконец, мы печатаем значение w (снова внутри sess.run()), которое должно быть около 3.
Упражнение:
Если вы создадите новую сессию после этого кода и попытаетесь напечатать w, что выведется?
Да, вы правильно поняли, выведется 0.0. Как только мы вышли из сессии, созданной ранее, все операции перестают существовать.
Надеюсь, этот урок даст вам быстрый старт с TensorFlow. Пожалуйста, не стесняйтесь задавать свои вопросы в комментариях. Полный код можно скачать здесь.
Введение в машинное обучение с tensorflow
Если мы в ближайшие пять лет построим машину с интеллектуальными возможностями одного человека, то ее преемник уже будет разумнее всего человечества вместе взятого. Через одно-два поколения они попросту перестанут обращать на нас внимание. Точно так же, как вы не обращаете внимания на муравьев у себя во дворе. Вы не уничтожаете их, но и не приручаете, они практически никак не влияют на вашу повседневную жизнь, но они там есть.
Сет Шостак
Введение.
Серия моих статей является расширенной версией того, что я хотел увидеть когда только решил познакомиться с нейронными сетями. Он рассчитан в первую очередь на программистов, желающих познакомится с tensorflow и нейронными сетями. Уж не знаю к счастью или к сожалению, но эта тема настолько обширна, что даже мало-мальски информативное описание требует большого объёма текста. Поэтому, я решил разделить повествование на 4 части:
Установка tensorflow
Хотя описание процесса установки tensorflow и не является целью статьи, я вкратце опишу процесс установки cpu-версии для 64-разрядных windows систем и дополнений, используемых далее в тексте. В общем случае процедуру установки можно посмотреть на сайте tensorflow.
Знакомство с tensorflow
Принципы работы с tensorflow достаточно просты. Мы должны составить граф операций, затем передать в этот граф данные и дать команду произвести вычисления. На картинке ниже вы можете увидеть 3 примера таких графов:
Граф слева содержит только одну вершину, представляющую константу со значением 1. Здесь и далее по тексту, в таких иллюстрациях, кругами с серой штриховкой будут обозначаться вершины с константами, а без штриховки вершины с операциями. Центральный граф иллюстрирует операцию сложения. Если мы попросим tensorflow вычислить значение вершины, представляющей операцию сложения, то он вычислит значения направленных в него рёбер графа и просуммирует их(т.е. будет возвращено 3). На правом же графе у нас две вершины с операциями — вычитание и возведение в квадрат. Если мы попытаемся вычислить вершину, представляющую возведение в квадрат, то tensorflow сперва выполнит вычитание. Я думаю концепция графов вычислений ни у кого затруднений не вызовет.
Пустой граф можно создать функцией tf.Graph(), кроме того граф по-умолчанию создаётся при подключении библиотеки и если вы не будете явно указывать граф, то будет использоваться именно он. В примере ниже показано, как можно создать две константы в двух разных графах.
Передача данных и выполнение операций происходят в сессиях. Запуск сессии осуществляется вызовом tf.Session, а её закрытие вызовом метода close на объекте сессии. Можно использовать конструкцию with, которая автоматический закрывает сессию:
Надеюсь про графы и сессии в общих чертах ясно, подробно их функционал тут разбираться не будет, тем кто хочет досконально разобрать эти механизмы стоит ознакомится непосредственно с документацией. А далее мы переходим к построению графов. В предыдущих примерах в граф добавлялись константы и настало время узнать что это такое и чем они отличаются от placeholder’ов и переменных. В примере ниже построен более сложный граф, представляющий выражение .
Итак, placeholder — это узел, через который в модель будут передаваться новые данные, а переменная(Variable) — это узел, который может изменяться по ходу выполнения графа. Я надеюсь, что вышеописанный материал всем понятен, т.к. его как раз достаточно для того, чтобы приступить к обучению первой модели. В предыдущем фрагменте кода мы составили граф линейной функции , теперь же давайте пойдём немного дальше и аппроксимируем функцию
по набору точек. Да, я знаю, что всех уже задолбала эта задача, как и распознование символов и ещё ряд клишейных примеров, но смиритесь, вам предстоит пройти через всех них…
Первый обучающий алгоритм
Чтобы tensorflow мог обучать модель нам нужно добавить ещё 2 вещи: функцию потерь и сам алгоритм оптимизации.
Функция потерь — это функция, которая принимает значение функции предсказанное моделью и фактическое значение, а возвращает расстояние между ними(будем называть это значение ошибкой). Например, если мы предсказываем вещественное значение, то в качестве функции потерь можно взять квадрат разности аргументов или модуль их разности. Если у нас задача классификации, то функция потерь может возвращать 0 при правильном ответе и 1 при ошибках. Грубо говоря, функция потерь должна вернуть неотрицательное вещественное число и оно должно быть тем больше, чем сильнее модель ошибается и тогда задача обучения модели сведётся к минимизации. И хотя последнее предложение не совсем корректно, зато в полной мере отражает идею машинного обучения.
Из методов оптимизации мы рассмотрим только классический градиентный спуск. Про него написано уже очень много, поэтому я не буду разбирать его «по кирпичику» и вдаваться в детали(материал и так выходит не маленький). Однако его нужно понимать, поэтому постараюсь коротко и наглядно объяснить метод при помощи визуализаций. Ниже представлены 2 варианта одного и того же графика — . Задача метода — найти локальный минимум, т.е. из точки(взятой наугад, на графике
) попасть в углубление (синяя зона на графиках).
Суть метода в том, чтобы идти в направлении противоположном градиенту функции в текущей точке. Градиент — это вектор, который указывает в направлении наибольшего роста функции. Математически это вектор из производных по всем аргументам — . Функция взята наугад и вычисления на ней мы проводить не станем, для практики у нас есть более простой пример, для начала посмотрите на визуализацию нескольких шагов алгоритма:
Отдельно стоит сказать о скорости с которой нужно двигаться к минимуму(применительно к задаче машинного обучения, это будет называться скоростью обучения). Для получения первых результатов, нам достаточно будет подобрать фиксированную скорость. Однако часто бывает хорошей идеей понижать её по ходу выполнения алгоритма, т.е. двигаться всё меньшими и меньшими шажками. Пока этого достаточно, более подробно будем разбирать метод с практикой, по мере необходимости.
Наш граф выглядит примерно так:
зелёным выделены входные узлы, а красным оптимизируемые переменные.
Первое, что должно броситься в глаза, это несовпадение размерностей у входных узлов с переменными. Входные узлы принимают массивы по 5 элементов, а переменные являются числами. Это называется пакетными вычислениями(broadcasting). Грубо говоря, когда нужно произвести вычисления над массивами, один из которых имеет лишнее измерение, вычисления производятся отдельно для каждого элемента бОльшего массива и результатом будет массив бОльшей размерности. Т.е. [1,2,3,4,5] + 1 = [2,3,4,5,6], это довольно сложно сформулировать, но должно быть интуитивно понятно.
далее полученные значения вычитаются из эталонных, возводятся в квадрат и вычисляется среднее значение:
отличие примерно в сотую от выведенного в лог значения вызвана округлением до сотых во время расчётов. Итак, ошибку мы посчитали, теперь пришло время разобраться с оптимизацией. На изображении графа выше, пунктирными стрелками, показано что оптимизатор изменяет переменные. У вас уже должно быть интуитивное понимание того, как работает градиентный спуск. В этом примере используется стохастический градиентный спуск со скоростью 0.5. Давайте по порядку, мы оптимизируем переменные a и b, так что по ним и находим градиент:
Нам нужно улучшить значение по всему набору точек, поэтому вычисляем среднее значение градиента, для удобства по каждой переменной отдельно:
Ну и, наконец, меняем значения оптимизируемых переменных с учётом заданной скорости:
Значения и
и есть искомые значения переменных. Эти вычисления повторяются в цикле на каждом наборе точек. Почему такой метод называется стохастическим? Потому что мы вычисляем градиент только на небольшом фрагменте данных(пакете), а не на всех точках сразу. Таким образом стохастический спуск требует гораздо меньше вычислений, но не гарантирует уменьшение ошибки на каждой итерации. Как ни странно этот «шум» в величине сходимости по времени может оказаться даже полезен, т.к. позволяет «выкорабкиваться» из локальных минимумов.
Собственно, на этом пока можно закончить. Статья получилась даже относительно небольшой, что не может не радовать. Я впервые пишу такого рода материал, так что если вы считаете, что в статье недостаточно подробно или в неверном ключе рассмотренны какие-то моменты, то, пожалуйста, напишите об этом — статья обязательно будет дополнена и скорректирована на основе конструктивной критики. Кроме того это поможет лучше подготовить к публикации последующие части, которые обязательно будут тут выложены (разумеется за исключением сценария при котором статья будет встречена негативно).
В заключение очень хотелось бы поблагодарить своего друга Николая Саганенико, за помощь в подготовке материала. Именно благодаря ему моя небольшая шпаргалка для личного пользования превратилась в вышеизложенный поток сознания.
Библиотека глубокого обучения Tensorflow
Здравствуй, Хабр! Цикл статей по инструментам для обучения нейронных сетей продолжается обзором популярного фреймворка Tensorflow.
Tensorflow (далее — TF) — довольно молодой фреймворк для глубокого машинного обучения, разрабатываемый в Google Brain. Долгое время фреймворк разрабатывался в закрытом режиме под названием DistBelief, но после глобального рефакторинга 9 ноября 2015 года был выпущен в open source. За год с небольшим TF дорос до версии 1.0, обрел интеграцию с keras, стал значительно быстрее и получил поддержку мобильных платформ. В последнее время фреймворк развивается еще и в сторону классических методов, и в некоторых частях интерфейса уже чем-то напоминает scikit-learn. До текущей версии интерфейс менялся активно и часто, но разработчики пообещали заморозить изменения в API. Мы будем рассматривать только Python API, хотя это не единственный вариант — также существуют интерфейсы для C++ и мобильных платформ.
Установка
TF устанавливается стандартно через python pip. Есть нюанс: существуют отдельные алгоритмы установки для работы на CPU и на видеокартах.
В случае с CPU всё просто: нужно поставить из pip пакет под названием tensorflow.
Во втором случае нужно:
Впрочем, документация утверждает, что поддерживаются и более ранние версии CUDA Toolkit и cuDNN, но рекомендует устанавливать версии, указанные выше.
Разработчики рекомендуют устанавливать TF в отдельную среду с virtualenv, чтобы избежать возможные проблемы с версионированием и зависимостями.
Еще один вариант установки — Docker. По умолчанию из контейнера будет работать только CPU-версия, но если использовать специальный nvidia docker, то можно использовать и GPU.
Сам я не пробовал, но говорят, что TF работает даже с Windows. Установка проводится через тот же pip и, говорят, работает без проблем.
Я пропускаю процесс сборки из исходников, однако и такой вариант может иметь смысл. Дело в том, что пакет из репозитория собирается без поддержки SSE и прочих плюшек. В последних версиях TF проверяет наличие таких плюшек и сообщает, что из исходников он будет работать быстрее.
Подробно процесс установки описан тут.
Документация
Документации и примеров очень много.
Лучше всего ориентироваться на официальную документацию — из-за быстрого развития и частой смены api, в интернете очень много туториалов и скриптов, которые ориентированы на старые версии (ну как старые… полугодовой давности) со старым API, они не будут работать с последними версиями фреймворка.
Базовые элементы TF
С помощью «Hello, world» убедимся, что всё установилось правильно:
Первой же строчкой подключаем TF. Уже сложилось правило вводить для фреймворка соответствующее сокращение. Этот же кусочек кода встречается в документации и позволяет удостовериться в том, что всё установилось правильно.
Граф вычислений
Работа c TF строится вокруг построения и выполнения графа вычислений. Граф вычислений — это конструкция, которая описывает то, каким образом будут проводиться вычисления. В классическом императивном программировании мы пишем код, который выполняется построчно. В TF привычный императивный подход к программированию необходим только для каких-то вспомогательных целей. Основа TF — это создание структуры, задающей порядок вычислений. Программы естественным образом структурируются на две части — составление графа вычислений и выполнение вычислений в созданных структурах.
Граф вычислений в TF по смыслу не отличается от такового в Theano. В предыдущей статье цикла дано отличное описание этой сущности.
В TF граф состоит из плейсхолдеров, переменных и операций. Из этих элементов можно собрать граф, в котором будут вычисляться тензоры. Тензоры — многомерные массивы, они служат «топливом» для графа. Тензором может быть как отдельное число, вектор признаков из решаемой задачи или изображение, так и целый батч описаний объектов или массив из изображений. Вместо одного объекта мы можем передать в граф массив объектов и для него будет вычислен массив ответов. Работа TF с тензорами похожа на то, как обрабатывает массивы numpy, в функциях которого можно указать ось массива, относительно которой будет выполняться вычисление.
Сессии
Далее в посте будут появляться стандартные для TF картинки с изображениями графов, сгенерированные встроенной утилитой под названием Tensorboard. Обозначения там вот такие:
Переменная | Операция | Вспомогательный результат |
---|---|---|
Узел графа обычно содержит данные. | Делает что-то с переменными. Сюда же относятся плейсхолдеры, которые делают подстановку значений в граф. | Всякое кэширование и побочные вычисления типа градиентов, обычно так обозначают ссылку на отдельную часть графа. |
Тензоры, операции и переменные
Создадим, к примеру, тензор, заполненный нулями.
Вообще, API в TF будет во многом напоминать numpy и tf.zeros() — далеко не единственная функция, имеющая прямой аналог в numpy. Чтобы увидеть значение тензора, его нужно выполнить. Подробнее о выполнении графа чуть ниже, пока что обойдемся тем, что выведем значение тензора и сам тензор.
Различие между строчками состоит в том, что в первой строке происходит вычисление тензора, а во второй строке мы просто печатаем представление объекта.
Описание тензора показывает нам несколько важных вещей:
Над тензорами можно совершать разнообразные операции:
В примере выше мы используем конструкцию sess.run — это метод исполнения операций графа в сессии. В первой строчке я создал тензор из усеченного нормального распределения. Для него используется стандартная генерация нормального распределения, но из него исключается всё, что выпадает за пределы двух стандартных отклонений. Помимо этого генератора есть равномерное, простое нормальное, гамма и еще несколько других распределений. Очень характерная для TF штука — уже реализовано большинство популярных вариантов выполнения операции и, возможно, перед изобретением велосипеда стоит взглянуть на документацию. Второй тензор — это заполненный значением 0.5 многомерный массив размера 2х2 и это что-то похожее на numpy и его функции создания многомерных массивов.
Создадим теперь переменную на основе тензора:
Переменная участвует в вычислениях в качестве узла вычислительного графа, сохраняет состояние, и ей нужна какая-нибудь инициализация. Так, если в следующем примере обойтись без первой строчки, то TF выкинет исключение.
Операции над переменными создают вычислительный граф, который можно потом выполнить. Еще есть плейсхолдеры — объекты, которые параметризуют граф и отмечают места для подстановки внешних значений. Как написано в официальной документации, плейсхолдер — это обещание подставить значение потом. Создадим плейсхолдер и назначаем ему тип данных и размер:
Еще такой пример использования. Здесь два плейсхолдера служат входными узлами для сумматора:
Простейшие вычисления.
В качестве примера создадим и вычислим несколько выражений.
Попробуем собрать что-нибудь более практически значимое.
Вот, например, сигмоида:
И вот такой граф для неё.
В фрагменте с запуском вычисления функции есть один момент, который отличает этот пример от предыдущих. Дело в том, что в плейсхолдер вместо одного скалярного значения мы передаем целый массив. TF обрабатывает все значения массива вместе, в рамках одного тензора (помним, что массив == тензор). Точно таким же образом мы можем передавать в граф объекты целыми батчами и поставлять нейронной сети картинки целиком.
В целом работа с тензорами напоминает работу с массивами в numpy. Однако, есть некоторые отличия. Когда мы хотим понизить размерность, каким-либо образом объединив значения в тензоре по определенному измерению, мы пользуемся теми функциями, которые начинаются с reduce_.
Если сравнить c API Theano — в TF нет деления на векторы и матрицы, но вместо этого приходится следить за размерностями тензоров в графе и есть механизм вывода формы тензора, который позволяет получить размерности еще до runtime.
Машинное обучение
Разберем для начала уже не раз упоминавшуюся здесь классическую линейную регрессию, с детальным описанием которой можно ознакомиться тут, однако для обучения будем использовать метод градиентного спуска.
Куда же без этой картинки
Начну с линейной регрессии, а потом добавлю полиномиальные признаки.
Данные будут синтетические — синус с нормальным шумом:
Выглядеть они будут примерно так:
Я еще разобью выборку на обучающую и контрольную в пропорции 70/30, но этот и некоторые другие рутинные моменты оставлю в полном исходнике, ссылка на который будет чуть ниже.
Сначала построим простую линейную регрессию.
Тут я создаю два плейсхолдера для признака и ответа и формулу вида .
Нюанс — в плейсхолдере параметр формы (shape) содержит None. Размерность плейсхолдера означает, что плейсходер потребляет двумерные тензоры, но по одной из осей размер тензора не определен и может быть любым. Это сделано для того, чтобы пользователь мог передавать значения в граф сразу целыми батчами. Такие специфические размерности называют динамическими, TF рассчитывает действительную размерность связанных элементов во время выполнения графа.
Плейсхолдер для признака используется в формуле, а вот плейсхолдер для ответа я подставлю в функцию потерь :
В TF реализован десяток методов оптимизации. Мы будем использовать классический градиентный спуск, указав ему в параметрах скорость обучения.
Метод minimize создаст нам операцию, вычисление которой будет минимизировать функцию потерь.
Инициализация переменных — она необходима для дальнейших вычислений:
Всё, наконец-то можно обучать. Я запущу 100 эпох обучения на обучающей части выборки, после каждого обучения буду устраивать контроль на отложенной части.
Получается вот такая динамика обучения:
В этом графе присутствуют вспомогательные переменные с градиентами и операции инициализации, они вынесены в отдельный блок.
А вот и результаты вычисления модели:
Значения для графика я вычислил вот таким способом:
Полный листинг программы есть тут. Результат получился предсказуемым для такой простой линейной модели.
Полиномиальная регрессия
Попробуем разнообразить регрессию полиномиальными признаками, регуляризацией и изменением скорости обучения модели.
В генерации набора данных добавим некоторое количество степеней и нормируем признаки с помощью PolynomialFeatures и StandardScaler из библиотеки scikit-learn. Первый объект создаст нам полиномиальных признаков сколько захотим, а второй нормирует их.
Для перехода к полиномиальной регрессии заменим всего несколько строк в графе вычислений:
Фактически сейчас мы считаем . Очевидно, существует опасность переобучения модели на ровном месте, поэтому добавим регуляризационные штрафы на веса. Штрафы добавим к функции потерь (loss в примерах) в виде дополнительных слагаемых и получим почти что ElasticNet из sklearn.
Ради примера добавлю еще одно существенное изменение, которое коснется темпа обучения. Довольно часто при обучении тяжелых нейросетей это просто необходимая мера для того, чтобы избежать проблем с обучением и получить приемлемый результат. Очень простая идея — по мере обучения постепенно понижать параметр шага, избегая больших неприятностей.
Вместо константного темпа будем использовать экспоненциальное затухание, которое я взял прямиком из документации:
Внутри функции скрывается формула:
decay_steps в нашем примере присвоено значение 100000, decay_rate — 0.96.
Получаем вот такие темпы снижения ошибок на обучении и контроле:
Помимо экспоненциального затухания есть и другие функции, которые позволяют снижать скорость обучения, и конечно ничто не мешает создать еще одну функцию под свои нужды.
Сама модель после обучения и разнообразных подборов параметров будет выглядеть как-то так:
Для получения этого результата я изменял коэффициенты при регуляризации и скорости обучения. Перебор значений констант в формуле потерь — отличный способ увидеть своими глазами эффект работы регуляризаторов, крайне рекомендую поиграть с настройками в полном исходнике полиномиальной регрессии, он доступен тут.
Сохранение и загрузка графов
Мы получили модель и было бы неплохо её сохранить. В TF всё достаточно просто — в API есть специальный объект-сериализатор, который делает две вещи:
Вот всё что нужно — это создать этот объект:
Сохранение состояния текущей сессии производится с помощью метода save :
Уже как-то принято, что сохраненные состояния модели называют чекпойнтами, отсюда название папок и расширения файлов. Восстановление производится с помощью метода restore :
Tensorboard
Крайне полезная система в составе TF — web-dashboard, который позволяет собирать статистику из дампов и логов и наблюдать, что же всё-таки происходит во время вычислений. Крайне удобно то, что дашборд работает на веб-сервере и можно, например, запустив tensorboard на удаленной машине в облаке, наблюдать происходящее у себя в окне браузера.
Еще при использовании Tensorboard важно также не забывать про параметр name у переменных. Имя, которое будет присвоено переменной, потом будет использоваться для отрисовки графа, выбора в пользовательском интерфейсе дашборда, в общем, везде. Для небольших графов это не критично, но с ростом сложности задачи могут возникнуть проблемы с пониманием происходящего.
Есть несколько типов функций, которые сохраняют данные переменных различным образом:
Данная функция позволит собрать гистограмму для выхода слоя и примерно оценить динамику изменений при обучении. Функция tf.summary.scalar(«accuracy», learning_rate) будет сохранять число. Еще можно сохранять аудио и картинки.
Для сохранения логов нужно чуть больше: сначала нужно создать FileWriter для записи файла.
И объединить всю статистику в одном объекте.
Теперь нужно вот этот объект merged передать на выполнение в сессию и потом с помощью метода FileWriter добавить новые данные, полученные от сессии.
Впрочем, для простого сохранения графа достаточно вот такого кода:
И нюанс: по умолчанию Tensorboard локально доступен по адресу 127.0.1.1:6006. Надеюсь, сохранил читателям несколько секунд времени и нейронов этим замечанием.
Многослойный перцептрон
Разберем каноничный пример с запоминанием функции xor, которую линейная модель не может усвоить из-за невозможности линейного разделения пространства признаков.
Многослойные сети усваивают функцию из-за того, что делают неявное преобразование пространства признаков в разделимое или же (в зависимости от реализации) делают нелинейное разбиение этого пространства. Мы реализуем первый вариант — создадим двухслойный перцептрон с нелинейной активацией слоев. Первый слой будет делать нелинейное преобразование, а второй слой — это практически линейная регрессия, которая работает на преобразованном пространстве признаков.
В качестве элемента нелинейности будем использовать функцию relu.
Определим структуру сети:
В отличие от keras и других более высокоуровневых библиотек, TF, подобно Theano предполагает детальное определение каждого слоя как совокупности некоторых арифметических операций. Это верно не для всех видов слоёв, например, сверточные и dropout-слои определяются одной функцией, в то время как обычный полносвязный слой представляет собой объявление не только переменных для весов и сдвигов, но и самих операций (перемножение весов с выходом предыдущего слоя, добавление сдвига, применение функции активации).
Разумеется, довольно часто всё это оборачивается в подобную функцию:
При этом по моему собственному опыту объявление и инициализацию переменных удобнее оставлять снаружи: иногда требуется использовать их еще где-нибудь внутри графа (типичный пример — сиамские нейронные сети с общими весами) или же просто иметь доступ для простого логирования в файл и вывода текущих значений, а tensorboard по каким-то причинам пользоваться не хочется.
Используем элементарную функцию потерь:
В сравнении с регрессией практически ничего не изменилось: тот же процесс обучения, та же функция потерь. Единственная часть кода, которая сильно изменилась — это код построения вычислительного графа. Дошло до того, что у меня накопился набор скриптов под конкретные задачи, в которых я меняю только подачу данных и граф вычислений.
Конечно, в данном примере нет проверки на отложенной выборке. Убедиться в правильности работы сети можно с помощью вычисления выхода нейронной сети в графе:
Полный код данного примера доступен тут. На ноутбуке пятилетней давности весь код отрабатывает за 5 секунд.
Конечно, в случае более сложных моделей добавляется валидация на отложенной выборке и слежение за качеством по мере обучения и встроенные в TF методы подачи данных в граф.
Управление ресурсами
Довольно часто мир бывает несправедлив, и задача может не помещаться полностью в одно устройство. Или же менеджмент купил всего одну Tesla, и разработчики периодически вступают в конфликты из-за занятой карточки. В TF на такие случаи есть механизмы управления вычислениями. Внутри фреймворка устройства обозначаются как «/cpu:0», «/gpu:0» и т.д. Самое простое — можно указывать, где конкретно будет «жить» та или иная переменная:
В этом примере переменная а отправится на процессор.
Еще можно передать в сессию конфигурационный объект, с помощью которого можно изменять проведение вычислений графа. Выглядит это примерно так:
Допустим, в команде разработчиков можно договориться об ограничении потребления памяти GPU. Сделать это можно с помощью такого кода:
Заключение
TF буквально за год-полтора разросся настолько, что впору делать отдельные обзоры о применении сверточных и рекуррентных сетей, обучения с подкреплением и о применении фреймворка к различным задачам. В этом кратком обзоре не затронуты темы структур для чтения данных, свертки, разнообразные методы оптимизации и высокоуровневые библиотеки-обертки. Продолжить знакомство можно, попробовав для начала «поиграть» с настройками обучения и разобравшись с различными оптимизаторами. Ну и конечно попробовать TF в соревнованиях на Kaggle!
Автор выражает благодарность sovcharenko, Ferres и bauchgefuehl за помощь в подготовке текста.
UPD:
Jupyter-ноутбук со статьей и всеми исходниками доступен тут.