![]() | ![]() | ![]() | ![]() |
A execução ansiosa do TensorFlow é um ambiente de programação imperativo que avalia as operações imediatamente, sem construir gráficos: as operações retornam valores concretos em vez de construir um gráfico computacional para executar mais tarde. Isso facilita os primeiros passos com o TensorFlow e os modelos de depuração, além de reduzir o clichê. Para acompanhar este guia, execute os exemplos de código abaixo em um interpretador python
interativo.
A execução rápida é uma plataforma flexível de aprendizado de máquina para pesquisa e experimentação, fornecendo:
- Uma interface intuitiva - Estruture seu código naturalmente e use estruturas de dados Python. Repita rapidamente em modelos pequenos e dados pequenos.
- Depuração mais fácil - chame operações diretamente para inspecionar modelos em execução e testar alterações. Use ferramentas de depuração padrão do Python para relatórios de erros imediatos.
- Fluxo de controle natural - use o fluxo de controle Python em vez do fluxo de controle gráfico, simplificando a especificação de modelos dinâmicos.
A execução rápida é compatível com a maioria das operações do TensorFlow e aceleração de GPU.
Configuração e uso básico
import os
import tensorflow as tf
import cProfile
No Tensorflow 2.0, a execução rápida é habilitada por padrão.
tf.executing_eagerly()
True
Agora você pode executar as operações do TensorFlow e os resultados retornarão imediatamente:
x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m))
hello, [[4.]]
Permitir a execução rápida muda o comportamento das operações do TensorFlow - agora elas avaliam e retornam imediatamente seus valores para Python. Objetos tf.Tensor
referenciam valores concretos em vez de identificadores simbólicos para nós em um gráfico computacional. Como não há um gráfico computacional para construir e executar posteriormente em uma sessão, é fácil inspecionar os resultados usando print()
ou um depurador. Avaliar, imprimir e verificar os valores do tensor não interrompe o fluxo de gradientes de computação.
A execução rápida funciona bem com o NumPy . As operações NumPy aceitam argumentos tf.Tensor
. As operações tf.math do tf.math
convertem objetos Python e matrizes NumPy em objetos tf.Tensor
. O método tf.Tensor.numpy
retorna o valor do objeto como um ndarray
NumPy.
a = tf.constant([[1, 2],
[3, 4]])
print(a)
tf.Tensor( [[1 2] [3 4]], shape=(2, 2), dtype=int32)
# Broadcasting support
b = tf.add(a, 1)
print(b)
tf.Tensor( [[2 3] [4 5]], shape=(2, 2), dtype=int32)
# Operator overloading is supported
print(a * b)
tf.Tensor( [[ 2 6] [12 20]], shape=(2, 2), dtype=int32)
# Use NumPy values
import numpy as np
c = np.multiply(a, b)
print(c)
[[ 2 6] [12 20]]
# Obtain numpy value from a tensor:
print(a.numpy())
# => [[1 2]
# [3 4]]
[[1 2] [3 4]]
Fluxo de controle dinâmico
Um grande benefício da execução antecipada é que toda a funcionalidade da linguagem host está disponível enquanto seu modelo está sendo executado. Então, por exemplo, é fácil escrever fizzbuzz :
def fizzbuzz(max_num):
counter = tf.constant(0)
max_num = tf.convert_to_tensor(max_num)
for num in range(1, max_num.numpy()+1):
num = tf.constant(num)
if int(num % 3) == 0 and int(num % 5) == 0:
print('FizzBuzz')
elif int(num % 3) == 0:
print('Fizz')
elif int(num % 5) == 0:
print('Buzz')
else:
print(num.numpy())
counter += 1
fizzbuzz(15)
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
Isso tem condicionais que dependem de valores de tensor e imprime esses valores em tempo de execução.
Treinamento ansioso
Gradientes de computação
A diferenciação automática é útil para implementar algoritmos de aprendizado de máquina, como retropropagação para treinamento de redes neurais. Durante a execução inicial, usetf.GradientTape
para rastrear operações para gradientes de computação posteriormente.
Você pode usartf.GradientTape
para treinar e / ou computar gradientes comtf.GradientTape
. É especialmente útil para loops de treinamento complicados.
Visto que diferentes operações podem ocorrer durante cada chamada, todas as operações forward-pass são gravadas em uma "fita". Para calcular o gradiente, reproduza a fita ao contrário e descarte. Umtf.GradientTape
específico só pode calcular um gradiente; as chamadas subsequentes geram um erro de tempo de execução.
w = tf.Variable([[1.0]])
with tf.GradientTape() as tape:
loss = w * w
grad = tape.gradient(loss, w)
print(grad) # => tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)
tf.Tensor([[2.]], shape=(1, 1), dtype=float32)
Treine uma modelo
O exemplo a seguir cria um modelo multicamadas que classifica os dígitos manuscritos MNIST padrão. Ele demonstra o otimizador e APIs de camada para construir gráficos treináveis em um ambiente de execução rápido.
# Fetch and format the mnist data
(mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data()
dataset = tf.data.Dataset.from_tensor_slices(
(tf.cast(mnist_images[...,tf.newaxis]/255, tf.float32),
tf.cast(mnist_labels,tf.int64)))
dataset = dataset.shuffle(1000).batch(32)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz 11493376/11490434 [==============================] - 0s 0us/step
# Build the model
mnist_model = tf.keras.Sequential([
tf.keras.layers.Conv2D(16,[3,3], activation='relu',
input_shape=(None, None, 1)),
tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
tf.keras.layers.GlobalAveragePooling2D(),
tf.keras.layers.Dense(10)
])
Mesmo sem treinamento, chame o modelo e inspecione a saída em execução rápida:
for images,labels in dataset.take(1):
print("Logits: ", mnist_model(images[0:1]).numpy())
Logits: [[ 0.06289896 -0.03877686 -0.07346137 -0.03169462 0.02922358 -0.02436475 -0.00588411 0.03256026 0.01715117 -0.02714448]]
Embora os modelos keras tenham um loop de treinamento integrado (usando o método de fit
), às vezes você precisa de mais personalização. Aqui está um exemplo de um loop de treinamento implementado com ansioso:
optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
loss_history = []
def train_step(images, labels):
with tf.GradientTape() as tape:
logits = mnist_model(images, training=True)
# Add asserts to check the shape of the output.
tf.debugging.assert_equal(logits.shape, (32, 10))
loss_value = loss_object(labels, logits)
loss_history.append(loss_value.numpy().mean())
grads = tape.gradient(loss_value, mnist_model.trainable_variables)
optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables))
def train(epochs):
for epoch in range(epochs):
for (batch, (images, labels)) in enumerate(dataset):
train_step(images, labels)
print ('Epoch {} finished'.format(epoch))
train(epochs = 3)
Epoch 0 finished Epoch 1 finished Epoch 2 finished
import matplotlib.pyplot as plt
plt.plot(loss_history)
plt.xlabel('Batch #')
plt.ylabel('Loss [entropy]')
Text(0, 0.5, 'Loss [entropy]')
Variáveis e otimizadores
tf.Variable
objetos tf.Variable
armazenam valores mutáveis do tipo tf.Tensor
acessados durante o treinamento para tornar a diferenciação automática mais fácil.
As coleções de variáveis podem ser encapsuladas em camadas ou modelos, junto com métodos que operam nelas. Consulte Camadas e modelos personalizados do Keras para obter detalhes. A principal diferença entre camadas e modelos é que os modelos adicionam métodos como Model.fit
, Model.evaluate
e Model.save
.
Por exemplo, o exemplo de diferenciação automática acima pode ser reescrito:
class Linear(tf.keras.Model):
def __init__(self):
super(Linear, self).__init__()
self.W = tf.Variable(5., name='weight')
self.B = tf.Variable(10., name='bias')
def call(self, inputs):
return inputs * self.W + self.B
# A toy dataset of points around 3 * x + 2
NUM_EXAMPLES = 2000
training_inputs = tf.random.normal([NUM_EXAMPLES])
noise = tf.random.normal([NUM_EXAMPLES])
training_outputs = training_inputs * 3 + 2 + noise
# The loss function to be optimized
def loss(model, inputs, targets):
error = model(inputs) - targets
return tf.reduce_mean(tf.square(error))
def grad(model, inputs, targets):
with tf.GradientTape() as tape:
loss_value = loss(model, inputs, targets)
return tape.gradient(loss_value, [model.W, model.B])
Próximo:
- Crie o modelo.
- As derivadas de uma função de perda em relação aos parâmetros do modelo.
- Uma estratégia para atualizar as variáveis com base nas derivadas.
model = Linear()
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
print("Initial loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
steps = 300
for i in range(steps):
grads = grad(model, training_inputs, training_outputs)
optimizer.apply_gradients(zip(grads, [model.W, model.B]))
if i % 20 == 0:
print("Loss at step {:03d}: {:.3f}".format(i, loss(model, training_inputs, training_outputs)))
Initial loss: 68.602 Loss at step 000: 65.948 Loss at step 020: 30.156 Loss at step 040: 14.091 Loss at step 060: 6.878 Loss at step 080: 3.637 Loss at step 100: 2.180 Loss at step 120: 1.525 Loss at step 140: 1.231 Loss at step 160: 1.098 Loss at step 180: 1.038 Loss at step 200: 1.011 Loss at step 220: 0.999 Loss at step 240: 0.994 Loss at step 260: 0.991 Loss at step 280: 0.990
print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
Final loss: 0.990
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
W = 3.001922369003296, B = 2.0047335624694824
Salvamento baseado em objeto
Um tf.keras.Model
inclui um método save_weights
conveniente que permite criar facilmente um ponto de verificação:
model.save_weights('weights')
status = model.load_weights('weights')
Usando tf.train.Checkpoint
você pode assumir o controle total sobre esse processo.
Esta seção é uma versão abreviada do guia para pontos de verificação de treinamento .
x = tf.Variable(10.)
checkpoint = tf.train.Checkpoint(x=x)
x.assign(2.) # Assign a new value to the variables and save.
checkpoint_path = './ckpt/'
checkpoint.save('./ckpt/')
'./ckpt/-1'
x.assign(11.) # Change the variable after saving.
# Restore values from the checkpoint
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))
print(x) # => 2.0
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=2.0>
Para salvar e carregar modelos, tf.train.Checkpoint
armazena o estado interno dos objetos, sem exigir variáveis ocultas. Para registrar o estado de um model
, um optimizer
e uma etapa global, passe-os para um tf.train.Checkpoint
:
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
tf.keras.layers.GlobalAveragePooling2D(),
tf.keras.layers.Dense(10)
])
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
checkpoint_dir = 'path/to/model_dir'
if not os.path.exists(checkpoint_dir):
os.makedirs(checkpoint_dir)
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
root = tf.train.Checkpoint(optimizer=optimizer,
model=model)
root.save(checkpoint_prefix)
root.restore(tf.train.latest_checkpoint(checkpoint_dir))
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f9abd45f1d0>
Métricas orientadas a objetos
tf.keras.metrics
são armazenados como objetos. Atualize uma métrica passando os novos dados para o que pode ser chamado e recupere o resultado usando o método tf.keras.metrics.result
, por exemplo:
m = tf.keras.metrics.Mean("loss")
m(0)
m(5)
m.result() # => 2.5
m([8, 9])
m.result() # => 5.5
<tf.Tensor: shape=(), dtype=float32, numpy=5.5>
Resumos e TensorBoard
TensorBoard é uma ferramenta de visualização para entender, depurar e otimizar o processo de treinamento do modelo. Ele usa eventos de resumo que são gravados durante a execução do programa.
Você pode usar tf.summary
para registrar resumos de variáveis em execução tf.summary
. Por exemplo, para registrar resumos de loss
uma vez a cada 100 etapas de treinamento:
logdir = "./tb/"
writer = tf.summary.create_file_writer(logdir)
steps = 1000
with writer.as_default(): # or call writer.set_as_default() before the loop.
for i in range(steps):
step = i + 1
# Calculate loss with your real train function.
loss = 1 - 0.001 * step
if step % 100 == 0:
tf.summary.scalar('loss', loss, step=step)
ls tb/
events.out.tfevents.1602033841.kokoro-gcp-ubuntu-prod-892356553.1670.619697.v2
Tópicos de diferenciação automática avançada
Modelos dinâmicos
tf.GradientTape
também pode ser usado em modelos dinâmicos. Este exemplo de um algoritmo de pesquisa de linha de retrocesso se parece com o código NumPy normal, exceto que há gradientes e é diferenciável, apesar do fluxo de controle complexo:
def line_search_step(fn, init_x, rate=1.0):
with tf.GradientTape() as tape:
# Variables are automatically tracked.
# But to calculate a gradient from a tensor, you must `watch` it.
tape.watch(init_x)
value = fn(init_x)
grad = tape.gradient(value, init_x)
grad_norm = tf.reduce_sum(grad * grad)
init_value = value
while value > init_value - rate * grad_norm:
x = init_x - rate * grad
value = fn(x)
rate /= 2.0
return x, value
Gradientes personalizados
Gradientes personalizados são uma maneira fácil de substituir gradientes. Dentro da função de avanço, defina o gradiente em relação às entradas, saídas ou resultados intermediários. Por exemplo, esta é uma maneira fácil de cortar a norma dos gradientes na passagem reversa:
@tf.custom_gradient
def clip_gradient_by_norm(x, norm):
y = tf.identity(x)
def grad_fn(dresult):
return [tf.clip_by_norm(dresult, norm), None]
return y, grad_fn
Gradientes personalizados são comumente usados para fornecer um gradiente numericamente estável para uma sequência de operações:
def log1pexp(x):
return tf.math.log(1 + tf.exp(x))
def grad_log1pexp(x):
with tf.GradientTape() as tape:
tape.watch(x)
value = log1pexp(x)
return tape.gradient(value, x)
# The gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# However, x = 100 fails because of numerical instability.
grad_log1pexp(tf.constant(100.)).numpy()
nan
Aqui, a função log1pexp
pode ser simplificada analiticamente com um gradiente personalizado. A implementação abaixo reutiliza o valor de tf.exp(x)
que é calculado durante o passe para frente - tornando-o mais eficiente ao eliminar cálculos redundantes:
@tf.custom_gradient
def log1pexp(x):
e = tf.exp(x)
def grad(dy):
return dy * (1 - 1 / (1 + e))
return tf.math.log(1 + e), grad
def grad_log1pexp(x):
with tf.GradientTape() as tape:
tape.watch(x)
value = log1pexp(x)
return tape.gradient(value, x)
# As before, the gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# And the gradient computation also works at x = 100.
grad_log1pexp(tf.constant(100.)).numpy()
1.0
atuação
A computação é descarregada automaticamente para GPUs durante a execução rápida. Se você deseja controlar onde um cálculo é executado, você pode colocá-lo em um tf.device('/gpu:0')
(ou equivalente de CPU):
import time
def measure(x, steps):
# TensorFlow initializes a GPU the first time it's used, exclude from timing.
tf.matmul(x, x)
start = time.time()
for i in range(steps):
x = tf.matmul(x, x)
# tf.matmul can return before completing the matrix multiplication
# (e.g., can return after enqueing the operation on a CUDA stream).
# The x.numpy() call below will ensure that all enqueued operations
# have completed (and will also copy the result to host memory,
# so we're including a little more than just the matmul operation
# time).
_ = x.numpy()
end = time.time()
return end - start
shape = (1000, 1000)
steps = 200
print("Time to multiply a {} matrix by itself {} times:".format(shape, steps))
# Run on CPU:
with tf.device("/cpu:0"):
print("CPU: {} secs".format(measure(tf.random.normal(shape), steps)))
# Run on GPU, if available:
if tf.config.experimental.list_physical_devices("GPU"):
with tf.device("/gpu:0"):
print("GPU: {} secs".format(measure(tf.random.normal(shape), steps)))
else:
print("GPU: not found")
Time to multiply a (1000, 1000) matrix by itself 200 times: CPU: 0.9788374900817871 secs GPU: 0.04241943359375 secs
Um objeto tf.Tensor
pode ser copiado para um dispositivo diferente para executar suas operações:
if tf.config.experimental.list_physical_devices("GPU"):
x = tf.random.normal([10, 10])
x_gpu0 = x.gpu()
x_cpu = x.cpu()
_ = tf.matmul(x_cpu, x_cpu) # Runs on CPU
_ = tf.matmul(x_gpu0, x_gpu0) # Runs on GPU:0
WARNING:tensorflow:From <ipython-input-1-876293b5769c>:4: _EagerTensorBase.gpu (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.identity instead. WARNING:tensorflow:From <ipython-input-1-876293b5769c>:5: _EagerTensorBase.cpu (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.identity instead.
Benchmarks
Para modelos de computação pesada, como o treinamento ResNet50 em uma GPU, o desempenho de execução rápida é comparável à execução de tf.function
. Mas essa lacuna fica maior para modelos com menos computação e há trabalho a ser feito para otimizar caminhos de hot code para modelos com muitas operações pequenas.
Trabalhe com funções
Enquanto a execução rápida torna o desenvolvimento e a depuração mais interativos, a execução de gráfico estilo TensorFlow 1.x tem vantagens para treinamento distribuído, otimizações de desempenho e implantação de produção. Para preencher essa lacuna, o TensorFlow 2.0 apresenta function
por meio da API tf.function
. Para obter mais informações, consulte o guia tf.function .