Pomoc chronić Wielkiej Rafy Koralowej z TensorFlow na Kaggle Dołącz Wyzwanie

Chętna egzekucja

Zobacz na TensorFlow.org Wyświetl źródło na GitHub Pobierz notatnik

Chętne wykonanie TensorFlow jest imperatywnym środowiskiem programistycznym, które natychmiast ocenia operacje, bez tworzenia grafów: operacje zwracają konkretne wartości zamiast konstruować graf obliczeniowy do późniejszego uruchomienia. Ułatwia to rozpoczęcie pracy z modelami TensorFlow i debugowania, a także ogranicza schematy. Podążać wraz z tym przewodnikiem, uruchom poniższe przykłady kodu w interaktywnym python tłumacza.

Eager Execution to elastyczna platforma uczenia maszynowego do badań i eksperymentów, zapewniająca:

  • Intuicyjny interfejs -Structure kodzie naturalnie i użyć struktur danych Pythona. Szybko iteruj małe modele i małe dane.
  • Łatwiejsze debugowanie-Call ops bezpośrednio do wglądu i zmiany uruchomione modele testowe. Użyj standardowych narzędzi debugowania Pythona do natychmiastowego raportowania błędów.
  • Kontrola przepływu naturalny przepływ sterowania -Za Pythona zamiast kontroli przepływu wykres, upraszczając specyfikację modeli dynamicznych.

Wykonanie Eager obsługuje większość operacji TensorFlow i akcelerację GPU.

Konfiguracja i podstawowe użytkowanie

import os

import tensorflow as tf

import cProfile

W Tensorflow 2.0 przyspieszone wykonywanie jest domyślnie włączone.

tf.executing_eagerly()
True

Teraz możesz uruchomić operacje TensorFlow, a wyniki zwrócą się natychmiast:

x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m))
hello, [[4.]]

Włączenie szybkiego wykonywania zmienia zachowanie operacji TensorFlow — teraz natychmiast oceniają i zwracają swoje wartości do Pythona. tf.Tensor obiektów konkretne wartości odniesienia zamiast uchwytów symboliczne węzłów wykresu obliczeniowego. Ponieważ nie jest obliczeniowa wykres zbudować i uruchomić później w sesji, jest to łatwe do sprawdzenia wyników przy użyciu print() lub debuggera. Ocenianie, drukowanie i sprawdzanie wartości tensorów nie zakłóca przepływu przy obliczaniu gradientów.

Marzą wykonanie działa ładnie z NumPy . Operacje NumPy zaakceptować tf.Tensor argumenty. W TensorFlow tf.math operacje konwersji obiektów Python i tablice numpy do tf.Tensor obiektów. tf.Tensor.numpy metoda zwraca wartość obiektu jako NumPy ndarray .

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]]

Dynamiczny przepływ sterowania

Główną zaletą szybkiego wykonywania jest to, że wszystkie funkcje języka hosta są dostępne podczas wykonywania modelu. Tak więc, na przykład, nie jest łatwo napisać 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

Zawiera warunki warunkowe zależne od wartości tensorów i drukuje te wartości w czasie wykonywania.

Chętny trening

Obliczanie gradientów

Automatyczne rozróżnianie jest przydatny dla realizacji maszynę algorytmy uczenia się, takich jak wstecznej propagacji błędów do szkolenia sieci neuronowych. Podczas chętnie wykonania, należy tf.GradientTape do operacji śledzenia dla obliczania gradientu później.

Można użyć tf.GradientTape do pociągu i / lub gradienty obliczeniowych w chętny. Jest to szczególnie przydatne w przypadku skomplikowanych pętli treningowych.

Ponieważ podczas każdego wywołania mogą wystąpić różne operacje, wszystkie operacje przekazywania do przodu są nagrywane na „taśmie”. Aby obliczyć gradient, odtwórz taśmę do tyłu, a następnie odrzuć. Szczególnie tf.GradientTape można obliczyć tylko jeden gradientu; kolejne wywołania generują błąd w czasie wykonywania.

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)

Wytrenuj modelkę

Poniższy przykład tworzy model wielowarstwowy, który klasyfikuje standardowe cyfry pisane odręcznie MNIST. Demonstruje optymalizator i interfejsy API warstw w celu budowania wykresów, które można trenować w gorliwym środowisku wykonawczym.

# 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)
# 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)
])

Nawet bez szkolenia wywołaj model i sprawdź dane wyjściowe w gorliwym wykonaniu:

for images,labels in dataset.take(1):
  print("Logits: ", mnist_model(images[0:1]).numpy())
Logits:  [[-0.01775933 -0.01194787 -0.08372174 -0.06535977  0.00338565 -0.01974326
  -0.04763228  0.00904049 -0.00144051 -0.01944664]]

Natomiast modele Keras mieć pętlę szkoleniowy wbudowane (używając fit metody), czasem trzeba więcej dostosowywania. Oto przykład pętli treningowej zaimplementowanej z eager:

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]')

png

Zmienne i optymalizatory

tf.Variable przedmioty przechowywać zmienny tf.Tensor -Jak wartości dostępnych podczas treningu, aby automatycznego różnicowania łatwiejsze.

Zbiory zmiennych można hermetyzować w warstwy lub modele wraz z operującymi na nich metodami. Zobacz warstwy klienta Keras i modele dla szczegółów. Główną różnicą pomiędzy warstwami i modeli jest to, że modele dodać metody jak Model.fit , Model.evaluate i Model.save .

Na przykład powyższy przykład automatycznego różnicowania można przepisać:

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])

Następny:

  1. Utwórz model.
  2. Pochodne funkcji straty ze względu na parametry modelu.
  3. Strategia aktualizacji zmiennych na podstawie instrumentów pochodnych.
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: 69.909
Loss at step 000: 67.145
Loss at step 020: 30.170
Loss at step 040: 13.859
Loss at step 060: 6.659
Loss at step 080: 3.479
Loss at step 100: 2.074
Loss at step 120: 1.453
Loss at step 140: 1.178
Loss at step 160: 1.056
Loss at step 180: 1.003
Loss at step 200: 0.979
Loss at step 220: 0.968
Loss at step 240: 0.963
Loss at step 260: 0.961
Loss at step 280: 0.960
print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
Final loss: 0.960
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
W = 2.9515867233276367, B = 2.0210201740264893

Zapisywanie obiektowe

tf.keras.Model obejmuje dogodnym save_weights metodę pozwalającą na łatwe utworzenie punktu kontrolnego:

model.save_weights('weights')
status = model.load_weights('weights')

Korzystanie tf.train.Checkpoint można przejąć pełną kontrolę nad tym procesem.

Ta sekcja jest skrócona wersja przewodnika po przejściach szkoleniowych .

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(checkpoint_path)
'./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>

Aby zapisać i modele obciążeń, tf.train.Checkpoint przechowuje wewnętrzny stan obiektów, bez konieczności ukrytych zmiennych. Aby nagrać stan na model , z optimizer i globalnej kroku, przekazać je do 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 0x7f4ba0648310>

Mierniki zorientowane obiektowo

tf.keras.metrics są przechowywane jako obiekty. Aktualizowanie metryki przekazując nowe dane do płatnych na żądanie, i pobrać wynik za pomocą tf.keras.metrics.result metody, na przykład:

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>

Podsumowania i TensorBoard

TensorBoard to narzędzie wizualizacji do zrozumienia, debugowania i optymalizacji procesu Model treningowy. Wykorzystuje zdarzenia podsumowujące, które są zapisywane podczas wykonywania programu.

Można użyć tf.summary do płytowych podsumowań zmienną chętny wykonania. Na przykład, aby nagrać podsumowania loss raz etapów szkoleniowych 100:

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.1632342765.kokoro-gcp-ubuntu-prod-230753280.22287.0.v2

Zaawansowane tematy automatycznego różnicowania

Modele dynamiczne

tf.GradientTape może być również stosowany w modelach dynamicznych. Ten przykład dla linii Backtracking szukać algorytm wygląda jak normalny kod NumPy, z wyjątkiem istnieją gradienty i jest różniczkowalna, pomimo złożonego przepływu sterowania:

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

Gradienty niestandardowe

Gradienty niestandardowe to łatwy sposób na zastępowanie gradientów. W ramach funkcji forward określ gradient w odniesieniu do wejść, wyjść lub wyników pośrednich. Na przykład, oto prosty sposób na przycięcie normy gradientów w przejściu wstecznym:

@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

Gradienty niestandardowe są powszechnie używane w celu zapewnienia stabilnego numerycznie gradientu dla sekwencji operacji:

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

Tutaj log1pexp funkcja analitycznie uproszczone niestandardowym gradientu. Realizacja poniżej ponownie wykorzystuje wartość dla tf.exp(x) , który jest obliczany podczas podaniu, dzięki czemu jest bardziej wydajny poprzez eliminację zbędnych obliczeń:

@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

Wydajność

Podczas intensywnego wykonywania obliczenia są automatycznie przenoszone na procesory graficzne. Jeśli chcesz kontrolować, gdzie prowadzi obliczenia można ująć je w tf.device('/gpu:0') bloku (lub równowartość 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.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: 1.007401466369629 secs
GPU: 0.04124784469604492 secs

tf.Tensor obiekt może zostać skopiowany do innego urządzenia w celu wykonywania swojej działalności:

if tf.config.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 /tmp/ipykernel_22287/406964202.py: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 /tmp/ipykernel_22287/406964202.py: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.

Benchmarki

Dla modeli obliczeniowych ciężkich, takich jak ResNet50 szkolenie na GPU, chętny wydajność wykonanie jest porównywalna do tf.function wykonania. Jednak luka ta powiększa się w przypadku modeli z mniejszą ilością obliczeń i należy wykonać pracę, aby zoptymalizować ścieżki gorącego kodu dla modeli z wieloma małymi operacjami.

Praca z funkcjami

Podczas gdy szybkie wykonywanie sprawia, że ​​programowanie i debugowanie jest bardziej interaktywne, wykonywanie wykresów w stylu TensorFlow 1.x ma zalety w zakresie rozproszonego szkolenia, optymalizacji wydajności i wdrażania produkcyjnego. W celu wypełnienia tej luki, TensorFlow 2,0 wprowadza function S poprzez tf.function API. Aby uzyskać więcej informacji, zobacz tf.function przewodnika.