AutoGraph помогает писать код сложных графов с использованием обычного Python. За кулисами AutoGraph автоматически преобразует ваш код в эквивалентный код графов TensorFlow. AutoGraph уже поддерживает большую часть языка Python, и этот охват продолжает расти.р>
Настройка
Импортируем TensorFlow, AutoGraph и вспомогательные модули:
from future import division, print_function, absolute_import
import tensorflow as tf
layers = tf.keras.layers
from tensorflow.contrib import autograph
import numpy as np
import matplotlib.pyplot as plt
Стремительное исполнение (eager execution) включено для демонстрационных целей, но AutoGraph работает в окружениях как стремительного, так и в графового исполнения:
tf.enable_eager_execution()
Примечание: Преобразованный код AutoGraph предназначен для запуска во время выполнения графа. Когда стремительное исполнение включено, используйте явные графы (как показано в этом примере) или tf.contrib.eager.defun
.
Автоматическое преобразование Python потока управления
AutoGraph преобразует большую часть языка Python в эквивалентный код построения графов TensorFlow.
Примечание: В реальных приложениях пакетирование важно для повышения производительности. Лучший код для преобразования в AutoGraph - это код, в котором поток управления определяется на уровне batch. Если вы принимаете решения на индивидуальном уровне example, вы должны индексировать и группировать примеры, чтобы поддерживать производительность при применении логики потока управления.
AutoGraph преобразует функцию, например:
def square_if_positive(x):
if x > 0:
x = x * x
else:
x = 0.0
return x
В функцию, которая использует построение графа:
print(autograph.to_code(square_if_positive))
from future import print_function
import tensorflow as tf
def tf__square_if_positive(x):
try:
with ag__.function_scope('square_if_positive'):
def if_true():
with ag__.function_scope('if_true'):
x_1, = x,
x_1 = x_1 * x_1
return x_1,
def if_false():
with ag__.function_scope('if_false'):
x_2, = x,
x_2 = 0.0
return x_2,
x = ag__.utils.run_cond(tf.greater(x, 0), if_true, if_false)
return x
except:
ag__.rewrite_graph_construction_error(ag_source_map__)
tf__square_if_positive.autograph_info__ = {}
Код написанный для стремительного исполнения может выполняться в tf.Graph с теми же результатами, но с преимуществами графового исполнения:
print('Eager results: %2.2f, %2.2f' %
(square_if_positive(tf.constant(9.0)),
square_if_positive(tf.constant(-9.0))))
Eager results: 81.00, 0.00
Генерируем графовую версию и выполняем ее:
# -*- coding: utf-8 -*-
tf_square_if_positive = autograph.to_graph(square_if_positive)
with tf.Graph().as_default():
# Работает как обычная операция: принимает тензоры,
# возвращает тензоры.
# Вы можете проверять граф,
# используя tf.get_default_graph().as_graph_def()
g_out1 = tf_square_if_positive(tf.constant( 9.0))
g_out2 = tf_square_if_positive(tf.constant(-9.0))
with tf.Session() as sess:
print('Graph results: %2.2f, %2.2f\n' %
(sess.run(g_out1), sess.run(g_out2)))
Graph results: 81.00, 0.00
AutoGraph поддерживает общие Python выражения, такие как while, for, if, break, и return, с поддержкой вложенности. Сравните эту функцию со сложной графовой версией, показанной в следующих блоках кода:
# Продолжаем в цикле
def sum_even(items):
s = 0
for c in items:
if c % 2 > 0:
continue
s += c
return s
print('Eager result: %d' % sum_even(tf.constant([10,12,15,20])))
tf_sum_even = autograph.to_graph(sum_even)
with tf.Graph().as_default(), tf.Session() as sess:
print('Graph result: %d\n\n' %
sess.run(tf_sum_even(tf.constant([10,12,15,20]))))
Eager result: 42
Graph result: 42
print(autograph.to_code(sum_even))
from future import print_function
import tensorflow as tf
def tf__sum_even(items):
try:
with ag__.function_scope('sum_even'):
s = 0
def extra_test(s_2):
with ag__.function_scope('extra_test'):
return True
def loop_body(loop_vars, s_2):
with ag__.function_scope('loop_body'):
c = loop_vars
continue_ = tf.constant(False)
def if_true():
with ag__.function_scope('if_true'):
continue__1, = continue_,
continue__1 = tf.constant(True)
return continue__1,
def if_false():
with ag__.function_scope('if_false'):
return continue_,
continue_ = ag__.utils.run_cond(tf.greater(c % 2, 0),
if_true, if_false)
def if_true_1():
with ag__.function_scope('if_true_1'):
s_1, = s_2,
s_1 += c
return s_1,
def if_false_1():
with ag__.function_scope('if_false_1'):
return s_2,
s_2 = ag__.utils.run_cond(tf.logical_not(continue_),
if_true_1, if_false_1)
return s_2,
s = ag__.for_stmt(items, extra_test, loop_body, (s,))
return s
except:
ag__.rewrite_graph_construction_error(ag_source_map__)
tf__sum_even.autograph_info__ = {}
Декоратор
Если вам не нужен простой доступ к изначальной Python функции, используйте convert декоратор:
@autograph.convert()
def fizzbuzz(i, n):
while i < n:
msg = ''
if i % 3 == 0:
msg += 'Fizz'
if i % 5 == 0:
msg += 'Buzz'
if msg == '':
msg = tf.as_string(i)
print(msg)
i += 1
return i
with tf.Graph().as_default():
final_i = fizzbuzz(tf.constant(10), tf.constant(16))
# Работает как обычная операция: принимает тензоры,
# возвращает тензоры.
# Вы можете проверять граф, используя
# tf.get_default_graph().as_graph_def()
with tf.Session() as sess:
sess.run(final_i)
Buzz
11
Fizz
13
14
FizzBuzz
Примеры
Продемонстрируем некоторые полезные свойства языка Python
Assert
AutoGraph автоматически преобразовывает Python assert выражение в эквивалентный tf.Assert код:
@autograph.convert()
def inverse(x):
assert x != 0.0, 'Do not pass zero!'
return 1.0 / x
with tf.Graph().as_default(), tf.Session() as sess:
try:
print(sess.run(inverse(tf.constant(0.0))))
except tf.errors.InvalidArgumentError as e:
print('Got error message:\n %s' % e.message)
Got error message:
assertion failed: [Do not pass zero!]
[[node inverse/Assert/Assert (defined at /tmp/tmpzsz37apn.py:6)
= Assert[T=[DT_STRING], summarize=3,
_device="/job:localhost/replica:0/task:0/device:CPU:0"]
(inverse/NotEqual, inverse/Assert/Assert/data_0)]]
Используйте Python print функцию в графе:
@autograph.convert()
def count(n):
i=0
while i < n:
print(i)
i += 1
return n
with tf.Graph().as_default(), tf.Session() as sess:
sess.run(count(tf.constant(5)))
Lists (списки)
Добавляйте в списки (lists) в циклах (тензор list операции автоматически созданы):
@autograph.convert()
def arange(n):
z = []
# Мы запрашиваем dtype элемента списка
autograph.set_element_type(z, tf.int32)
for i in tf.range(n):
z.append(i)
# когда мы закончили со списком
# складываем его в стек (как np.stack)
return autograph.stack(z)
with tf.Graph().as_default(), tf.Session() as sess:
sess.run(arange(tf.constant(10)))
Вложенный поток контроля
@autograph.convert()
def nearest_odd_square(x):
if x > 0:
x = x * x
if x % 2 == 0:
x = x + 1
return x
with tf.Graph().as_default():
with tf.Session() as sess:
print(sess.run(nearest_odd_square(tf.constant(4))))
print(sess.run(nearest_odd_square(tf.constant(5))))
print(sess.run(nearest_odd_square(tf.constant(6))))
17
25
37
While цикл
@autograph.convert()
def square_until_stop(x, y):
while x < y:
x = x * x
return x
with tf.Graph().as_default():
with tf.Session() as sess:
print(sess.run(square_until_stop(tf.constant(4), tf.constant(100))))
256
For цикл
@autograph.convert()
def squares(nums):
result = []
autograph.set_element_type(result, tf.int64)
for num in nums:
result.append(num * num)
return autograph.stack(result)
with tf.Graph().as_default():
with tf.Session() as sess:
print(sess.run(squares(tf.constant(np.arange(10)))))
[ 0 1 4 9 16 25 36 49 64 81]
Break
@autograph.convert()
def argwhere_cumsum(x, threshold):
current_sum = 0.0
idx = 0
for i in tf.range(len(x)):
idx = i
if current_sum >= threshold:
break
current_sum += x[i]
return idx
N = 10
with tf.Graph().as_default():
with tf.Session() as sess:
idx = argwhere_cumsum(tf.ones(N), tf.constant(float(N/2)))
print(sess.run(idx))
5
Взаимодействие с tf.Keras
Теперь когда вы увидели основы, давайте построим некоторые компоненты модели с помощью autograph.
Довольно просто интегрировать autograph с tf.keras.
Функции без состояния
Для функций без состояния, таких как collatz показанной ниже, простейший путь включить их в keras модель - обернуть их как слой, используя tf.keras.layers.Lambda.
import numpy as np
@autograph.convert()
def collatz(x):
x = tf.reshape(x,())
assert x > 0
n = tf.convert_to_tensor((0,))
while not tf.equal(x, 1):
n += 1
if tf.equal(x%2, 0):
x = x // 2
else:
x = 3 * x + 1
return n
with tf.Graph().as_default():
model = tf.keras.Sequential([
tf.keras.layers.Lambda(collatz, input_shape=(1,), output_shape=())
])
result = model.predict(np.array([6171]))
result
array([261], dtype=int32)
Пользовательские слои и модели
Простейший путь использовать AutoGraph с Keras слоями и моделями - это @autograph.convert() call метод.
Вот простой пример:
# K используется для проверки в каком мы режиме
# - в тренировочном или тестовом.
K = tf.keras.backend
class StochasticNetworkDepth(tf.keras.Sequential):
def init(self, pfirst=1.0, plast=0.5, *args,**kwargs):
self.pfirst = pfirst
self.plast = plast
super().init(*args,**kwargs)
def build(self,input_shape):
super().build(input_shape.as_list())
self.depth = len(self.layers)
self.plims = np.linspace(self.pfirst, self.plast,
self.depth + 1)[:-1]
@autograph.convert()
def call(self, inputs):
training = tf.cast(K.learning_phase(), dtype=bool)
if not training:
count = self.depth
return super(StochasticNetworkDepth, self).call(inputs), count
p = tf.random_uniform((self.depth,))
keeps = (p <= self.plims)
x = inputs
count = tf.reduce_sum(tf.cast(keeps, tf.int32))
for i in range(self.depth):
if keeps[i]:
x = self.layers[i](x)
# возвращаем вывод последнего слоя и количество выполненных слоев
return x, count
Попробуем модель на данных с формой как в MNIST наборе:
train_batch = np.random.randn(64, 28, 28, 1).astype(np.float32)
Построим простой стек conv слоев, в созданной нами модели:
with tf.Graph().as_default() as g:
model = StochasticNetworkDepth(
pfirst=1.0, plast=0.5)
for n in range(20):
model.add(
layers.Conv2D(filters=16, activation=tf.nn.relu,
kernel_size=(3, 3), padding='same'))
model.build(tf.TensorShape((None, None, None, 1)))
init = tf.global_variables_initializer()
Теперь протестируем модель, чтобы убедиться, что она ведет себя так как ожидается в train и test режимах:
# Используем явную сессию здесь, таким образом мы можем установить
# train/test switch, и проверить количество слоев,
# возвращаенное call методом
with tf.Session(graph=g) as sess:
init.run()
for phase, name in enumerate(['test','train']):
K.set_learning_phase(phase)
result, count = model(tf.convert_to_tensor(train_batch,
dtype=tf.float32))
result1, count1 = sess.run((result, count))
result2, count2 = sess.run((result, count))
delta = (result1 - result2)
print(name, "sum abs delta: ", abs(delta).mean())
print(" layers 1st call: ", count1)
print(" layers 2nd call: ", count2)
print()
test sum abs delta: 0.0
layers 1st call: 20
layers 2nd call: 20
train sum abs delta: 0.0010305465
layers 1st call: 14
layers 2nd call: 17
Более сложный пример: внутриграфовый тренировочный цикл
В предыдущих примерах показано как AutoGraph может быть использован внутри Keras слоев и моделей. Keras модели могут также быть использованы в AutoGraph коде.
Ввиду того что написание потока контроля в AutoGraph является простым, то выполнение тренировочного цикла в TensorFlow графе должно быть также простым.
Этот пример показывает как натренировать простую Keras модель для MNIST с целым тренировочным процессом - загрузкой пакетов, вычислением градиентов, обновлением параметров, вычислением валидационной аккуратности, и повторением до схождения - все выполнено внутри графа.
Загрузка данных
(train_images, train_labels), (test_images, test_labels) = \
tf.keras.datasets.mnist.load_data()
Downloading data from
https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
Определение модели
def mlp_model(input_shape):
model = tf.keras.Sequential((
tf.keras.layers.Dense(100, activation='relu',
input_shape=input_shape),
tf.keras.layers.Dense(100, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')))
model.build()
return model
def predict(m, x, y):
y_p = m(tf.reshape(x, (-1, 28 * 28)))
losses = tf.keras.losses.categorical_crossentropy(y, y_p)
l = tf.reduce_mean(losses)
accuracies = tf.keras.metrics.categorical_accuracy(y, y_p)
accuracy = tf.reduce_mean(accuracies)
return l, accuracy
def fit(m, x, y, opt):
l, accuracy = predict(m, x, y)
# Autograph автоматически добавляет необходимые
#
tf.control_dependencies
здесь.
# (Без них ничего не зависит от opt.minimize,
# поэтому ничего не выполняется.)
# Это выполняется схожим образом с кодом с нетерпеливого исполнения.
opt.minimize(l)
return l, accuracy
def setup_mnist_data(is_training, batch_size):
if is_training:
ds = tf.data.Dataset.from_tensor_slices((train_images,
train_labels))
ds = ds.shuffle(batch_size * 10)
else:
ds = tf.data.Dataset.from_tensor_slices((test_images, test_labels))
ds = ds.repeat()
ds = ds.batch(batch_size)
return ds
def get_next_batch(ds):
itr = ds.make_one_shot_iterator()
image, label = itr.get_next()
x = tf.to_float(image) / 255.0
y = tf.one_hot(tf.squeeze(label), 10)
return x, y
Определение тренировочного цикла
# Используйте recursive = True
# для того чтобы рекурсивно преобразовать функции вызванные этой.
@autograph.convert(recursive=True)
def train(train_ds, test_ds, hp):
m = mlp_model((28 * 28,))
opt = tf.train.AdamOptimizer(hp.learning_rate)
# Мы сохраним наши потери в список. Для того чтобы AutoGraph
# преобразовывал эти списки в их графовые эквиваленты,
# нам необходимо определить тип элементов листов.
train_losses = []
autograph.set_element_type(train_losses, tf.float32)
test_losses = []
autograph.set_element_type(test_losses, tf.float32)
train_accuracies = []
autograph.set_element_type(train_accuracies, tf.float32)
test_accuracies = []
autograph.set_element_type(test_accuracies, tf.float32)
# Этот тренировочный цикл целиком будет выполняться внутри графа.
i = tf.constant(0)
while i < hp.max_steps:
train_x, train_y = get_next_batch(train_ds)
test_x, test_y = get_next_batch(test_ds)
step_train_loss, step_train_accuracy = fit(m, train_x, train_y, opt)
step_test_loss, step_test_accuracy = predict(m, test_x, test_y)
if i % (hp.max_steps // 10) == 0:
print('Step', i, 'train loss:', step_train_loss, 'test loss:',
step_test_loss, 'train accuracy:', step_train_accuracy,
'test accuracy:', step_test_accuracy)
train_losses.append(step_train_loss)
test_losses.append(step_test_loss)
train_accuracies.append(step_train_accuracy)
test_accuracies.append(step_test_accuracy)
i += 1
# Мы записали значения наших потерь и аккуратностей
# в список в графе с помощью AutoGraph.
# Для того чтобы вернуть значения как Tensor,
# нам необходимо сложить их друг на друга перед их возвращением.
return (autograph.stack(train_losses),
autograph.stack(test_losses),
autograph.stack(train_accuracies),
autograph.stack(test_accuracies))
Теперь построим граф и выполним тренировочный цикл:
with tf.Graph().as_default() as g:
hp = tf.contrib.training.HParams(
learning_rate=0.005,
max_steps=500,
)
train_ds = setup_mnist_data(True, 50)
test_ds = setup_mnist_data(False, 1000)
(train_losses, test_losses, train_accuracies,
test_accuracies) = train(train_ds, test_ds, hp)
init = tf.global_variables_initializer()
with tf.Session(graph=g) as sess:
sess.run(init)
(train_losses, test_losses, train_accuracies,
test_accuracies) = sess.run([train_losses,
test_losses,
train_accuracies,
test_accuracies])
plt.title('MNIST train/test losses')
plt.plot(train_losses, label='train loss')
plt.plot(test_losses, label='test loss')
plt.legend()
plt.xlabel('Training step')
plt.ylabel('Loss')
plt.show()
plt.title('MNIST train/test accuracies')
plt.plot(train_accuracies, label='train accuracy')
plt.plot(test_accuracies, label='test accuracy')
plt.legend(loc='lower right')
plt.xlabel('Training step')
plt.ylabel('Accuracy')
plt.show()
Step 0 train loss: 2.39437 test loss: 2.3646827
train accuracy: 0.02 test accuracy: 0.036
Step 50 train loss: 0.32149887 test loss: 0.5104491
train accuracy: 0.9 test accuracy: 0.839
Step 100 train loss: 0.33964032 test loss: 0.32525328
train accuracy: 0.88 test accuracy: 0.898
Step 150 train loss: 0.27726996 test loss: 0.37781104
train accuracy: 0.9 test accuracy: 0.867
Step 200 train loss: 0.18788506 test loss: 0.274848
train accuracy: 0.94 test accuracy: 0.921
Step 250 train loss: 0.35899135 test loss: 0.26668844
train accuracy: 0.86 test accuracy: 0.912
Step 300 train loss: 0.11612513 test loss: 0.24186224
train accuracy: 0.98 test accuracy: 0.916
Step 350 train loss: 0.18267077 test loss: 0.25310495
train accuracy: 0.96 test accuracy: 0.915
Step 400 train loss: 0.17388473 test loss: 0.18192373
train accuracy: 0.96 test accuracy: 0.939
Step 450 train loss: 0.12562008 test loss: 0.19091153
train accuracy: 0.96 test accuracy: 0.94
Читайте также другие статьи в блоге:
TensorFlow: стремительное исполнение (eager execution), базовое использование
TensorFlow: стремительное исполнение (eager execution), продвинутое использование