выбран изjacobbuckman.com, Автор: Джейкоб Бакман, составлено Heart of the Machine.
Хотя для большинства людей языком разработки для TensorFlow является Python, это не стандартная библиотека Python. Эта структура нейронной сети работает путем построения «вычислительного графа», и для многих новичков возникают большие трудности с пониманием ее логики. В этой статье Джейкоб Бакман, инженер из Google Brain, попытается помочь вам в решении проблем, с которыми вы столкнетесь при первом знакомстве с TensorFlow.
введение
Что это? кто я?
Меня зовут Джейкоб, и я научный сотрудник проекта Google AI Resident. Я присоединился к проекту летом 2017 года и, несмотря на большой опыт программирования и глубокое понимание машинного обучения, раньше никогда не использовал TensorFlow. В то время я чувствовал, что с моими способностями я должен быть в состоянии быстро приступить к работе. Но чего я не ожидал, так это того, что кривая обучения была довольно крутой, и даже через несколько месяцев после присоединения к проекту я иногда не понимал, как использовать код TensorFlow для реализации идей. Я пишу этот пост самому себе в прошлом как письмо в бутылке: вводное введение, которое мне хотелось бы получить в начале учебы. Я надеюсь, что этот пост в блоге поможет и другим.
Чего не хватало в предыдущих уроках?
За три года с момента выпуска TensorFlow стал краеугольным камнем экосистемы глубокого обучения. Тем не менее, он может быть не интуитивно понятным для новичков, особенно по сравнению с библиотеками нейронных сетей типа «запускай и запускай», такими как PyTorch или DyNet.
Существует множество руководств по началу работы с TensorFlow, от линейной регрессии до классификации MNIST и машинного перевода. Эти конкретные практические руководства являются хорошими ресурсами для запуска и запуска вашего проекта TensorFlow и служат отправной точкой для аналогичных проектов. Но для некоторых разработчиков приложений, чьи приложения не имеют хороших руководств, или для тех, кто хочет сломать шаблон (как это часто бывает в исследованиях), знание TensorFlow определенно может разочаровать.
Я пытаюсь восполнить этот пробел этой статьей. Вместо того, чтобы сосредоточиться на конкретной задаче, я предлагаю более общий подход и расшифровываю абстрактные концепции, лежащие в основе TensorFlow. Как только вы освоите эти концепции, глубокое обучение с TensorFlow станет интуитивно понятным и простым для понимания.
Целевая аудитория
Это руководство предназначено для тех, кто имеет некоторый опыт программирования и машинного обучения и хочет изучить TensorFlow. Например: студент-информатик, который хочет использовать TensorFlow в финальном проекте курса машинного обучения; инженер-программист, которого только что назначили на проект, связанный с глубоким обучением; или сбитый с толку новый резидент-новичок Google AI (крик прошлым Джейкобс). Если вы хотите узнать больше об основах, см. следующие ресурсы:
- Красиво. Беркли. Quota/blog/2016/1…
- colah.github.io/
- Woohoo.U big city.com/course/int R…
- woohoo.course RA.org/learn/machi…
Давайте начнем!
Понимание TensorFlow
TensorFlow не является стандартной библиотекой Python.
Большинство библиотек Python написаны как естественные расширения Python. Когда вы импортируете библиотеку, вы получаете набор переменных, функций и классов, которые расширяют и дополняют ваш «набор инструментов» для кода. Когда вы используете их, вы можете ожидать, какие будут результаты. На мой взгляд, это представление следует полностью отбросить, когда речь идет о TensorFlow. Думать о том, что такое TensorFlow и как он взаимодействует с другим кодом, в корне неверно.
Отношения между Python и TensorFlow можно сравнить с отношениями между Javascript и HTML. Javascript — это полнофункциональный язык программирования, который может делать множество замечательных вещей. HTML — это структура для представления некоторого типа практической вычислительной абстракции (в данном случае контента, который может быть отображен веб-браузером). Роль Javascript на интерактивной веб-странице состоит в том, чтобы собрать HTML-объект, который видит браузер, и затем взаимодействовать с ним, обновляя его до нового HTML, когда это необходимо.
Подобно HTML, TensorFlow представляет собой структуру для представления некоторого типа вычислительной абстракции, называемой «вычислительным графом». Но когда мы используем TensorFlow с Python, первое, что мы делаем с кодом Python, — это строим вычислительный граф. Как только это будет сделано, второе, что мы делаем, — это взаимодействуем с ним (запускаем «сеанс» TensorFlow). Но важно помнить, что граф вычислений находится не внутри переменной, а в глобальном пространстве имен. Как сказал Шекспир: «Вся оперативная память — это сцена, все переменные — просто указатели».
Первая ключевая абстракция: вычислительный граф
Просматривая документацию TensorFlow, вы можете найти косвенные ссылки на «графики» и «узлы». Если вы внимательно читали, возможно, вы даже обнаружили эту страницу (woohoo.tensorflow.org/programmers…), эта страница охватывает то, что я объясню более точным и техническим способом. Этот раздел представляет собой пошаговое руководство высокого уровня, в котором рассматриваются важные интуитивно понятные концепции, но игнорируются некоторые технические детали.
Итак: что такое вычислительный граф? По сути, это глобальная структура данных: ориентированный граф, содержащий инструкции о том, как выполнять вычисления.
Рассмотрим пример построения вычислительного графа. На приведенной ниже диаграмме верхняя половина — это код, который мы выполнили, и его выходные данные, а нижняя половина — результирующий граф вычислений.
import tensorflow as tf
Расчетный график:
Видно, что простой импорт TensorFlow не дает нам интересного вычислительного графа. И только одна пустая глобальная переменная. Но что происходит, когда мы вызываем операцию TensorFlow?
Код:
import tensorflow as tf
two_node = tf.constant(2)
print two_node
вывод:
Tensor("Const:0", shape=(), dtype=int32)
Расчетный график:
Проверьте это! Получили узел. Он содержит константу 2. Удивительно, но это исходит от функции tf.constant. Когда мы печатаем эту переменную, мы видим, что она возвращает объект tf.Tensor, который является указателем на только что созданный узел. Чтобы подчеркнуть этот момент, вот еще один пример:
Код:
import tensorflow as tf
two_node = tf.constant(2)
another_two_node = tf.constant(2)
two_node = tf.constant(2)
tf.constant(3)
Расчетный график:
Каждый раз, когда мы вызываем tf.constant, мы создаем новый узел в графе. Даже если узел функционирует так же, как существующий узел, даже если мы переназначаем узел той же самой переменной или даже если мы вообще не назначаем его переменной, результат тот же.
Код:
import tensorflow as tf
two_node = tf.constant(2)
another_pointer_at_two_node = two_node
two_node = None
print two_node
print another_pointer_at_two_node
вывод:
None
Tensor("Const:0", shape=(), dtype=int32)
Расчетный график:
Хорошо, пойдем дальше:
Код:
import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node ## equivalent to tf.add(two_node, three_node)
Расчетный график:
Теперь мы говорим - это вычислительный граф, который нам действительно нужен! Обратите внимание, что операция + перегружена в TensorFlow, поэтому одновременное добавление двух тензоров добавляет к графу узел, хотя на первый взгляд это не похоже на операцию TensorFlow.
Итак, two_node указывает на узел, содержащий 2, three_node указывает на узел, содержащий 3, а sum_node указывает на узел, содержащий ...+? что случилось? Разве он не должен содержать 5?
Как оказалось, это не так. Вычислительные графы содержат только вычислительные шаги, они не содержат результатов. По крайней мере... пока нет!
Вторая ключевая абстракция: сеанс
Если вы ошибочно понимаете, что в абстракциях TensorFlow тоже есть «Мартовское безумие» (баскетбольный турнир NCAA, большая часть которого проходит в марте), сессия будет семенем №1 каждый год. У сеансов такая запутанная репутация из-за их нелогичного, но повсеместного именования — почти каждый рендеринг TensorFlow явно вызывает tf.Session() по крайней мере один раз.
Роль сеанса заключается в управлении распределением и оптимизацией памяти, что позволяет нам фактически выполнять вычисления, указанные в графе вычислений. Вы можете думать о графе вычислений как о «шаблоне» для вычислений, которые мы хотим выполнить: он перечисляет все шаги. Чтобы использовать вычислительный граф, нам нужно запустить сеанс, который позволит нам фактически выполнить задачу; например, пройтись по всем узлам шаблона, чтобы выделить кучу памяти для хранения результатов вычислений. Чтобы выполнять различные вычисления с помощью TensorFlow, вам нужен как граф, так и сеанс.
Сеанс содержит указатель на глобальный граф, который постоянно обновляется указателями на все узлы. Это означает, что не имеет значения, создается ли сеанс до или после создания узла.
После создания объекта сеанса вы можете использовать sess.run(node) для возврата значения узла, и TensorFlow выполнит все вычисления, необходимые для определения этого значения.
Код:
import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
sess = tf.Session()
print sess.run(sum_node)
вывод:
5
Расчетный график:
Очень хороший! Мы также можем передать список sess.run([node1, node2, ...]) и получить несколько выходных данных:
Код:
import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
sess = tf.Session()
print sess.run([two_node, sum_node])
вывод:
[2, 5]
Расчетный график:
Как правило, вызов sess.run() является одним из самых узких мест в TensorFlow, поэтому чем реже вы его вызываете, тем лучше. Если возможно, возвращайте несколько элементов за один вызов sess.run() вместо того, чтобы делать несколько вызовов.
Заполнители и feed_dict
Вычисления, которые мы сделали до сих пор, были утомительными: нет никаких шансов получить входные данные, поэтому они всегда выводят одно и то же. Более ценное приложение может включать построение вычислительного графа, который принимает входные данные, обрабатывает их некоторым (непротиворечивым) образом и возвращает выходные данные.
Самый простой способ — использовать заполнители. Заполнитель — это узел, который принимает внешний ввод.
Код:
import tensorflow as tf
input_placeholder = tf.placeholder(tf.int32)
sess = tf.Session()
print sess.run(input_placeholder)
вывод:
Traceback (most recent call last):
...
InvalidArgumentError (see above *for* traceback): You must feed a value *for* placeholder tensor 'Placeholder' *with* dtype int32
[[Node: Placeholder = Placeholder[dtype=DT_INT32, shape=<unknown>, _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]
Расчетный график:
... это плохой пример, потому что он выдает исключение. Ожидается, что заполнителям будет присвоено значение. Но мы не указали значение, поэтому TensorFlow рухнул.
Чтобы указать значение, мы используем свойство feed_dixt функции sess.run().
Код:
import tensorflow as tf
input_placeholder = tf.placeholder(tf.int32)
sess = tf.Session()
print sess.run(input_placeholder, feed_dict={input_placeholder: 2})
вывод:
2
Расчетный график:
Это намного лучше. Обратите внимание на формат словаря, переданного в feed_dict, его ключ должен быть переменной, соответствующей узлу-заполнителю в графе (как упоминалось ранее, это фактически означает указатель на узел-заполнитель в графе). Соответствующее значение — это элемент данных, назначаемый каждому заполнителю — обычно это скаляр или массив Numpy.
Третья ключевая абстракция: вычислительные пути
Давайте посмотрим на другой пример с использованием заполнителей:
Код:
import tensorflow as tf
input_placeholder = tf.placeholder(tf.int32)
three_node = tf.constant(3)
sum_node = input_placeholder + three_node
sess = tf.Session()
print sess.run(three_node)
print sess.run(sum_node)
вывод:
3
Traceback (most recent call last):
...
InvalidArgumentError (see above for traceback): You must feed a value *for* placeholder tensor 'Placeholder_2' with dtype int32
[[Node: Placeholder_2 = Placeholder[dtype=DT_INT32, shape=<unknown>, _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]
Расчетный график:
Почему второй вызов sess.run() не работает? Почему ошибка, связанная с input_placeholder, все еще возникает, хотя мы не оцениваем input_placeholder? Ответ заключается в последней ключевой абстракции TensorFlow: вычислении путей. К счастью, эта абстракция довольно интуитивно понятна.
Когда мы вызываем sess.run() для узлов, которые зависят от других узлов в графе, нам также необходимо вычислить значение этих узлов. Если эти узлы имеют зависимости, то нам нужно вычислять эти значения (и так далее...), пока мы не достигнем «вершины» графа вычислений, когда у узла нет родителя.
Путь вычисления sum_node:
Все три узла необходимо оценить, чтобы вычислить значение sum_node. Самое главное, это содержит наши незаполненные заполнители и объясняет исключение!
Теперь посмотрим на путь вычисления three_node:
Согласно структуре графа, нам не нужно вычислять все узлы, чтобы оценить те, которые нам нужны! Поскольку нам не нужно оценивать placehoolder_node при оценке three_node, запуск sess.run(three_node) не вызовет исключения.
Тот факт, что TensorFlow автоматизирует вычисления только с необходимыми узлами, является огромным преимуществом фреймворка. Если граф вычислений очень большой и имеет много ненужных узлов, это может сэкономить время выполнения большого количества вызовов. Это позволяет нам строить большие «многоцелевые» вычислительные графы, которые используют один общий набор основных узлов и выполняют разные действия в зависимости от различных вычислительных путей. Почти для всех приложений важно рассматривать вызовы sess.run() с точки зрения используемого вычислительного пути.
Переменные и побочные эффекты
До сих пор мы видели два типа узлов «без предка»: tf.constant, который одинаков для каждого запуска, и tf.placeholder, который отличается от запуска к запуску. Нам часто приходится рассматривать третий случай: узел, который обычно хранит значение во время выполнения, также может быть обновлен новым значением.
Здесь необходимо ввести переменные.
Переменные имеют решающее значение для глубокого обучения с TensorFlow, поскольку параметры модели являются переменными. Во время обучения вы хотите обновлять параметры на каждом этапе с помощью градиентного спуска, но во время оценки вы хотите сохранить параметры постоянными и передать модели большой и разнообразный набор тестов. Как правило, все обучаемые параметры модели являются переменными.
Чтобы создать переменную, вам нужно использовать tf.get_variable(). Первые два аргумента для tf.get_variable() обязательны, остальные необязательны. Это tf.get_variable(имя, форма). name — это строка, которая однозначно идентифицирует этот переменный объект. Он должен быть уникальным относительно глобального графа, поэтому следите за всеми именами, которые вы использовали, и убедитесь, что нет дубликатов. shape — это массив целых чисел, соответствующих форме тензора, и его синтаксис очень интуитивно понятен: по порядку существует только одно целое число для каждого измерения. Например, форма матрицы 3x8 имеет вид [3, 8]. Чтобы создать скаляр, вам нужно использовать пустой список формы [] .
Код:
import tensorflow as tf
count_variable = tf.get_variable("count", [])
sess = tf.Session()
print sess.run(count_variable)
вывод:
Traceback (most recent call last):
...
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value count
[[Node: _retval_count_0_0 = _Retval[T=DT_FLOAT, index=0, _device="/job:localhost/replica:0/task:0/device:CPU:0"](count)]]
Расчетный график:
Хм, еще одна аномалия. Когда переменный узел создается впервые, его значение в основном «нулевое», и любая попытка его оценки вызовет это исключение. Мы можем оценить значение только после помещения его в переменную. Есть два основных способа поместить значения в переменные: инициализаторы и tf.assign(). Давайте сначала посмотрим на tf.assign():
Код:
import tensorflow as tf
count_variable = tf.get_variable("count", [])
zero_node = tf.constant(0.)
assign_node = tf.assign(count_variable, zero_node)
sess = tf.Session()
sess.run(assign_node)
print sess.run(count_variable)
вывод:
0
Расчетный график:
По сравнению с узлами, которые мы видели до сих пор, tf.assign(target, value) имеет несколько уникальных свойств:
- Идентификационная операция. tf.assign(target, value) не выполняет никаких интересных операций и обычно равен значению.
- побочный эффект. По мере того, как вычисления «проходят» через узел assign_node, побочные эффекты возникают на других узлах графа. На этом этапе побочным эффектом является замена значения count_variable значением, хранящимся в zero_node.
- независимая кромка. Несмотря на то, что узлы count_variable и assign_node связаны в графе, они не зависят друг от друга. Это означает, что при вычислении любого узла вычисления не проходят обратно через ребра. Однако assign_node зависит от zero_node, которому нужно знать, что назначать.
Узел «побочный эффект» лежит в основе большей части рабочего процесса глубокого обучения Tensorflow, поэтому убедитесь, что вы действительно понимаете, что происходит в этом узле. Когда мы вызываем sess.run(assign_node), путь вычислений проходит через assign_node и zero_node.
Расчетный график:
Поскольку вычисления проходят через любой узел на графе, они также выполняют любые побочные эффекты, контролируемые этим узлом, как показано зеленым цветом. Из-за особого побочного эффекта tf.assign память, связанная с переменной count_variable (ранее «нулевая»), теперь постоянно установлена на 0. Это означает, что при следующем вызове sess.run(count_variable) исключение не будет выдано. Вместо этого мы получаем значение 0. успех!
Далее давайте посмотрим на инициализатор:
Код:
import tensorflow as tf
const_init_node = tf.constant_initializer(0.)
count_variable = tf.get_variable("count", [], initializer=const_init_node)
sess = tf.Session()
print sess.run([count_variable])
вывод:
Traceback (most recent call last):
...
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value count
[[Node: _retval_count_0_0 = _Retval[T=DT_FLOAT, index=0, _device="/job:localhost/replica:0/task:0/device:CPU:0"](count)]]
Расчетный график:
Ну, что здесь происходит? Почему инициализатор не работает?
Проблема возникает с разделением между сеансом и графом. Мы установили свойство инициализатора get_variable так, чтобы оно указывало на const_init_node, но оно просто добавляет новое соединение между узлами в графе. Мы ничего не сделали для устранения источника исключения: память, связанная с узлом переменной (хранящаяся в сеансе, а не в графе), по-прежнему имеет значение «null». Нам нужно сделать так, чтобы const_init_node обновлял переменные через сессию.
Код:
import tensorflow as tf
const_init_node = tf.constant_initializer(0.)
count_variable = tf.get_variable("count", [], initializer=const_init_node)
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
print sess.run(count_variable)
вывод:
0
Расчетный график:
Для этого мы добавляем еще один специальный узел: init = tf.global_variables_initializer(). Подобно tf.assign(), это узел с побочными эффектами. В отличие от tf.assign() нам на самом деле не нужно указывать, что это за ввод! tf.global_variables_initializer() будет просматривать глобальный граф по мере его создания и автоматически добавлять зависимости к каждому tf.initializer в графе. Когда мы позже оценим его с помощью sess.run(init) , он скажет каждому инициализатору выполнить инициализацию переменной и позволит нам запустить sess.run(count_variable) без ошибок.
совместное использование переменных
Вы можете столкнуться с кодом Tensorflow с совместным использованием переменных, который включает создание областей и настройку «повторное использование = True». Я настоятельно рекомендую не использовать совместное использование переменных в вашем собственном коде. Если вы хотите использовать одну переменную в нескольких местах, просто программно запишите указатель на этот узел переменной и повторно используйте его при необходимости. Другими словами, вам нужно вызвать tf.get_variable() только один раз для каждой переменной, которую вы хотите сохранить в памяти.
оптимизатор
Наконец: займитесь настоящим глубоким обучением! Если вы будете следовать моему ритму, остальные концепции должны быть для вас довольно простыми.
В глубоком обучении типичное обучение «внутреннего цикла» выглядит следующим образом:
1. Получить ввод и true_output
2. Рассчитать «предполагаемые» значения на основе входных данных и параметров
3. Рассчитайте «убыток» на основе разницы между спекуляцией и истинным_выводом.
4. Обновите параметры в соответствии с градиентом потери
Давайте поместим все в быстрый скрипт для решения простой задачи линейной регрессии:
Код:
import tensorflow as tf
### build the graph## first set up the parameters
m = tf.get_variable("m", [], initializer=tf.constant_initializer(0.))
b = tf.get_variable("b", [], initializer=tf.constant_initializer(0.))
init = tf.global_variables_initializer()
## then set up the computations
input_placeholder = tf.placeholder(tf.float32)
output_placeholder = tf.placeholder(tf.float32)
x = input_placeholder
y = output_placeholder
y_guess = m * x + b
loss = tf.square(y - y_guess)
## finally, set up the optimizer and minimization node
optimizer = tf.train.GradientDescentOptimizer(1e-3)
train_op = optimizer.minimize(loss)
### start the session
sess = tf.Session()
sess.run(init)
### perform the training loop*import* random
## set up problem
true_m = random.random()
true_b = random.random()
*for* update_i *in* range(100000):
## (1) get the input and output
input_data = random.random()
output_data = true_m * input_data + true_b
## (2), (3), and (4) all take place within a single call to sess.run()!
_loss, _ = sess.run([loss, train_op], feed_dict={input_placeholder: input_data, output_placeholder: output_data})
*print* update_i, _loss
### finally, print out the values we learned for our two variables*print* "True parameters: m=%.4f, b=%.4f" % (true_m, true_b)*print* "Learned parameters: m=%.4f, b=%.4f" % tuple(sess.run([m, b]))
вывод:
0 2.32053831 0.57927422 1.552543 1.57332594 0.64356485 2.40612656 1.07462567 2.19987158 1.67751169 1.646242310 2.441034
...99990 2.9878322e-1299991 5.158629e-1199992 4.53646e-1199993 9.422685e-1299994 3.991829e-1199995 1.134115e-1199996 4.9467985e-1199997 1.3219648e-1199998 5.684342e-1499999 3.007017e-11*True* parameters: m=0.3519, b=0.3242
Learned parameters: m=0.3519, b=0.3242
Как видите, потери в основном сводятся к нулю, и у нас есть хорошие оценки истинных параметров. Я надеюсь, что вы не знакомы только со следующими частями кода:
## finally, set up the optimizer and minimization node
optimizer = tf.train.GradientDescentOptimizer(1e-3)
train_op = optimizer.minimize(loss)
Однако теперь, когда вы хорошо понимаете основные концепции Tensorflow, этот код легко объяснить! Первая строка, оптимизатор = tf.train.GradientDescentOptimizer(1e-3), не добавляет узлы в вычислительный граф. Он просто создает объект Python, содержащий полезные вспомогательные функции. Вторая строка, train_op = optimizer.minimize(loss) добавляет узел в граф и сохраняет указатель в переменной train_op. Узел train_op не имеет вывода, но имеет очень сложный побочный эффект:
train_op отслеживает пути вычисления входных данных и потерь, ища узлы переменных. Для каждого найденного узла переменной вычислите градиент этой переменной по отношению к потерям. Затем вычисляется новое значение этой переменной: текущее значение минус произведение градиента на скорость обучения. Наконец, он выполняет операцию присваивания для обновления значения переменной.
Таким образом, в основном, когда мы вызываем sess.run(train_op), он выполняет шаг градиентного спуска для всех наших переменных. Конечно, нам также нужно использовать feed_dict для заполнения заполнителей ввода и вывода, и мы также хотим напечатать значение потери, так как это легче отлаживать.
Отладка с помощью tf.Print
Когда вы начинаете делать более сложные вещи с Tensorflow, вам нужно отлаживать. В общем, исследовать, что происходит на вычислительном графе, довольно сложно. Поскольку у вас никогда не будет доступа к значениям, которые вы хотите напечатать — они заблокированы в вызове sess.run() , поэтому вы не можете использовать обычные операторы печати Python. В частности, допустим, вы хотите проверить промежуточное значение вычисления. Промежуточное значение не существует до тех пор, пока не будет вызван sess.run(). Однако, когда ваш вызов sess.run() возвращается, промежуточное значение снова исчезает!
Давайте рассмотрим простой пример.
Код:
import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
sess = tf.Session()
print sess.run(sum_node)
вывод:
5
Это приводит нас к выводу, что ответ равен 5. Но что, если мы хотим проверить промежуточные значения, two_node и three_node? Один из способов проверки промежуточных значений — добавить возвращаемый параметр в sess.run(), который указывает на каждый промежуточный узел для проверки, а затем, после возврата, распечатать его значение.
Код:
import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
sess = tf.Session()
answer, inspection = sess.run([sum_node, [two_node, three_node]])
print inspection
print answer
вывод:
[2, 3]5
Обычно это работает, но когда код становится более сложным, это может быть немного сложно. Более удобный способ — использовать оператор tf.Print. Как ни странно, tf.Print на самом деле является узлом Tensorflow с выводами и побочными эффектами! У него есть два обязательных параметра: узел для копирования и список содержимого для печати. «Узлом для копирования» может быть любой узел в графе; tf.Print — это операция идентификации, связанная с «копируемым узлом», что означает, что вывод является копией ввода. Однако его побочным эффектом является распечатка всех текущих значений в «списке печати».
Код:
import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node
print_sum_node = tf.Print(sum_node, [two_node, three_node])
sess = tf.Session()
print sess.run(print_sum_node)
вывод:
[2][3]5
Расчетный график:
Важный и несколько тонкий момент о tf.Print : печать является побочным эффектом. Как и все другие побочные эффекты, печать выполняется только тогда, когда вычисления проходят через узел tf.Print. Если узел tf.Print не находится на вычисленном пути, ничего не будет напечатано. В частности, даже если исходный узел, который копирует узел tf.Print, находится на вычислительном пути, сам узел tf.Print может и не находиться. Пожалуйста, обратите внимание на этот вопрос! Когда это происходит (а так всегда происходит), это может сильно разочаровать, если вы четко не найдете проблему. Как правило, лучше всего создавать узел tf.Print сразу после создания узла, который нужно скопировать.
Код:
import tensorflow as tf
two_node = tf.constant(2)
three_node = tf.constant(3)
sum_node = two_node + three_node### this new copy of two_node is not on the computation path, so nothing prints!
print_two_node = tf.Print(two_node, [two_node, three_node, sum_node])
sess = tf.Session()
print sess.run(sum_node)
вывод:
5
Расчетный график:
Вот хороший ресурс(Я о caine.GitHub.IO/tensorflow-…), который содержит некоторые другие полезные советы по отладке.
в заключении
Надеюсь, что этот пост в блоге поможет вам лучше понять, что такое Tensorflow, как он работает и как его использовать. В целом, концепции, представленные в этой статье, важны для всех проектов Tensorflow, но лишь поверхностно. Изучая Tensorflow, вы можете столкнуться с различными другими интересными понятиями, которые вам нужны: условия, итерация, распределенный Tensorflow, переменная область видимости, сохранение и загрузка моделей, мультиграф, многосеанс и многоядерность, очередь загрузчиков данных и так далее. Я буду обсуждать эти темы в будущих сообщениях в блоге. Но если вы используете официальную документацию, несколько примеров кода и немного магии глубокого обучения, чтобы закрепить идеи, которые вы узнали в этой статье, я уверен, что вы сможете понять Tensorflow!
Оригинальная ссылка:Джейкоб Бак man.com/post/tensor…