Например, операция может иметь отдельные ядра для CPU и GPU, поскольку на GPU ее реализация более эффективна. Это справедливо для многих действий с матрицами в TensorFlow.
Общий обзор типов доступных операций приведен в таблице 3.1, взятой из оригинальной технической документации TensorFlow [26].
Таблица 3.1. Общая таблица операций в TensorFlow
Теперь, обладая солидными познаниями в области переменных и операций в TensorFlow, мы имеем практически полное описание компонентов графа вычислений этой библиотеки. Не хватает представлений о том, как обеспечить ввод данных в глубокую модель при обучении и тестировании. Переменной недостаточно: она инициализируется лишь однажды. Нам нужен компонент, который мы будем загружать каждый раз при запуске графа вычислений.
TensorFlow решает эту проблему при помощи так называемого заполнителя [27]. Его можно использовать в операциях так же, как и все обычные переменные и тензоры TensorFlow, и он реализуется следующим образом:
x = tf.placeholder(tf.float32, name="x", shape=[None, 784])
W = tf.Variable(tf.random_uniform([784,10], -1, 1), name="W")
multiply = tf.matmul(x, W)
Здесь мы определяем заполнитель, где x — мини-пакет данных, сохраненных как float32. Можно отметить, что в x 784 столбца, то есть каждый образец данных имеет 784 измерения. Для x не определено число строк. Это значит, что он может быть инициализирован произвольным количеством образцов данных. Можно и отдельно умножать каждый из них на W, но представление всего мини-пакета в виде тензора позволяет вычислить результаты для всех образцов данных параллельно. В результате i-я строка тензора multiply соответствует W, умноженному на i-й образец данных.
Как и переменные, которые нужно инициализировать при первом построении графа вычислений, заполнители нужно наполнять каждый раз при запуске графа (или подграфа). Подробнее об этом мы поговорим в следующем разделе.
Программа TensorFlow взаимодействует с графом вычислений в рамках сессии [28]. В ходе сессии TensorFlow происходит создание изначального графа, а также инициализация всех переменных и запуск графа вычислений. Для анализа этих элементов рассмотрим простой скрипт на Python:
import tensorflow as tf
from read_data import get_minibatch()
x = tf.placeholder(tf.float32, name="x", shape=[None, 784])
W = tf.Variable(tf.random_uniform([784, 10], -1, 1), name="W")
b = tf.Variable(tf.zeros([10]), name="biases")
output = tf.matmul(x, W) + b
init_op = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init_op)
feed_dict = {"x": get_minibatch()}
sess.run(output, feed_dict=feed_dict)
Первые четыре строчки после оператора импорта описывают граф вычислений, который строится в ходе сессии, когда она будет создана. Этот граф (без операций инициализации переменных) изображен на рис. 3.2. Затем мы инициализируем переменные, используя сессию для запуска соответствующей операции — sess.run(init_op). Наконец, мы можем запустить подграф, вновь обратившись к sess.run, но уже передав на вход тензор или список тензоров, которые мы хотим вычислить, и feed_dict, который вводит необходимые данные в заполнители.
Рис. 3.2. Пример простого графа вычислений в TensorFlow
Наконец, интерфейс sess.run можно использовать для обучения сетей. Мы рассмотрим это подробнее, когда при помощи TensorFlow будем обучать нашу первую модель на MNIST. Но как именно единственная строка кода (sess.run) выполняет так много функций? Все дело в выразительности лежащего в ее основе графа вычислений. Его функции представлены в виде операций TensorFlow, которые передаются в sess.run в качестве аргументов. Ей остается обратиться к графу вычислений и определить все зависимости, которые образуют соответствующий подграф; убедиться, что все переменные-заполнители, принадлежащие к выявленному подграфу, заданы при помощи feed_dict, и пройти по подграфу (выполнив промежуточные операции), чтобы вычислить исходные аргументы.
Теперь мы обратимся к еще двум важнейшим понятиям из области создания графов вычислений и управления ими.
Области видимости переменной и совместное использование переменных
Мы пока не встречались с этой задачей. Создание сложных моделей часто требует повторного и совместного использования больших наборов переменных, которые желательно создавать в одном месте. К сожалению, попытки обеспечить модульность и читабельность порой приводят к неожиданным проблемам, если мы неосторожны. Рассмотрим пример:
Читать дальше
Конец ознакомительного отрывка
Купить книгу