Odpowiedz już dziś na lokalne wydarzenie TensorFlow Everywhere!
Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Wprowadzenie do wykresów i funkcji tf

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

Przegląd

Ten przewodnik znajduje się pod powierzchnią TensorFlow i Keras, aby zobaczyć, jak działa TensorFlow. Jeśli zamiast tego chcesz od razu zacząć korzystać z Keras, zapoznaj się z naszą kolekcją przewodników po Keras .

W tym przewodniku dowiesz się, w jaki sposób TensorFlow umożliwia wprowadzanie prostych zmian w kodzie, aby uzyskać wykresy, w jaki sposób wykresy są przechowywane i reprezentowane oraz jak możesz ich używać do przyspieszania modeli.

To jest ogólny przegląd, który omawia, w jaki sposób tf.function pozwala na przełączanie się z wykonywania zachłannego do wykonywania wykresów. tf.function specyfikacja funkcji tf.function znajduje się w przewodniku po funkcji tf.function .

Co to są wykresy?

W poprzednich trzech przewodnikach widzieliście, że TensorFlow działa z niecierpliwością . Oznacza to, że operacje TensorFlow są wykonywane przez Python, operacja po operacji i zwracają wyniki z powrotem do Pythona.

Chociaż gorliwe wykonywanie ma kilka unikalnych zalet, wykonywanie wykresów umożliwia przenoszenie poza Python i zwykle zapewnia lepszą wydajność. Wykonywanie wykresów oznacza, że ​​obliczenia tensorowe są wykonywane jako wykres TensorFlow , czasami nazywany tf.Graph lub po prostu „wykresem”.

Grafy to struktury danych zawierające zestaw obiektów tf.Operation , które reprezentują jednostki obliczeniowe; i obiekty tf.Tensor , które reprezentują jednostki danych przepływające między operacjami. Są zdefiniowane w kontekście tf.Graph . Ponieważ te wykresy są strukturami danych, można je zapisywać, uruchamiać i przywracać bez oryginalnego kodu Pythona.

Tak wygląda wykres TensorFlow przedstawiający dwuwarstwową sieć neuronową po wizualizacji w TensorBoard.

Prosty wykres TensorFlow

Zalety wykresów

Dzięki wykresowi masz dużą elastyczność. Wykresu TensorFlow można używać w środowiskach, które nie mają interpretera języka Python, takich jak aplikacje mobilne, urządzenia wbudowane i serwery zaplecza. TensorFlow używa wykresów jako formatu zapisanych modeli podczas eksportowania ich z języka Python.

Wykresy są również łatwo optymalizowane, co pozwala kompilatorowi na dokonywanie transformacji, takich jak:

  • Statycznie wnioskuj wartość tensorów przez zawijanie stałych węzłów w swoich obliczeniach („zawijanie na stałe”) .
  • Oddzielne części podrzędne obliczenia, które są niezależne, i rozdziel je między wątki lub urządzenia.
  • Uprość operacje arytmetyczne, eliminując typowe wyrażenia podrzędne.

Istnieje cały system optymalizacji Grappler , który wykonuje to i inne przyspieszenia.

Krótko mówiąc, wykresy są niezwykle przydatne i pozwalają TensorFlow działać szybko , równolegle i wydajnie na wielu urządzeniach .

Jednak nadal chcesz dla wygody zdefiniować nasze modele uczenia maszynowego (lub inne obliczenia) w Pythonie, a następnie automatycznie tworzyć wykresy, gdy ich potrzebujesz.

Korzystanie z wykresów

Tworzysz i uruchamiasz wykres w TensorFlow używając tf.function , jako bezpośrednie wywołanie lub jako dekorator. tf.function przyjmuje zwykłą funkcję jako dane wejściowe i zwraca Function . Function to obiekt wywoływany w Pythonie, który buduje wykresy TensorFlow na podstawie funkcji Pythona. Użyć Function w taki sam sposób jak jego odpowiednik Python.

import tensorflow as tf
import timeit
from datetime import datetime
# Define a Python function.
def a_regular_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# `a_function_that_uses_a_graph` is a TensorFlow `Function`.
a_function_that_uses_a_graph = tf.function(a_regular_function)

# Make some tensors.
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0)

orig_value = a_regular_function(x1, y1, b1).numpy()
# Call a `Function` like a Python function.
tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()
assert(orig_value == tf_function_value)

Z zewnątrz Function wygląda jak zwykła funkcja, którą piszesz za pomocą operacji TensorFlow. Jednak pod spodem jest zupełnie inaczej . Function hermetyzuje kilka tf.Graph za jednym API . W ten sposób Function może zapewnić korzyści wynikające z wykonywania wykresów , takie jak szybkość i możliwość wdrażania .

tf.function odnosi się do funkcji i wszystkich innych funkcji, które wywołuje :

def inner_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Use the decorator to make `outer_function` a `Function`.
@tf.function
def outer_function(x):
  y = tf.constant([[2.0], [3.0]])
  b = tf.constant(4.0)

  return inner_function(x, y, b)

# Note that the callable will create a graph that
# includes `inner_function` as well as `outer_function`.
outer_function(tf.constant([[1.0, 2.0]])).numpy()
array([[12.]], dtype=float32)

Jeśli korzystałeś z TensorFlow 1.x, zauważysz, że w żadnym momencie nie trzeba było definiować tf.Session Placeholder ani tf.Session .

Konwersja funkcji Pythona na wykresy

Każda funkcja, którą napiszesz za pomocą TensorFlow, będzie zawierała mieszankę natywnych operacji TF i logiki Pythona, takich jak klauzule if-then , pętle, break , return , continue i inne. Podczas gdy operacje TensorFlow są łatwo przechwytywane przez tf.Graph , logika specyficzna dla Pythona musi przejść dodatkowy krok, aby stać się częścią wykresu. tf.function wykorzystuje bibliotekę o nazwie AutoGraph ( tf.autograph ) do konwersji kodu Pythona na kod generujący wykresy.

def simple_relu(x):
  if tf.greater(x, 0):
    return x
  else:
    return 0

# `tf_simple_relu` is a TensorFlow `Function` that wraps `simple_relu`.
tf_simple_relu = tf.function(simple_relu)

print("First branch, with graph:", tf_simple_relu(tf.constant(1)).numpy())
print("Second branch, with graph:", tf_simple_relu(tf.constant(-1)).numpy())
First branch, with graph: 1
Second branch, with graph: 0

Chociaż jest mało prawdopodobne, że będziesz musiał bezpośrednio przeglądać wykresy, możesz sprawdzić dane wyjściowe, aby zobaczyć dokładne wyniki. Nie są one łatwe do odczytania, więc nie musisz zbyt uważnie patrzeć!

# This is the graph-generating output of AutoGraph.
print(tf.autograph.to_code(simple_relu))
def tf__simple_relu(x):
    with ag__.FunctionScope('simple_relu', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()

        def get_state():
            return (retval_, do_return)

        def set_state(vars_):
            nonlocal retval_, do_return
            (retval_, do_return) = vars_

        def if_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = ag__.ld(x)
            except:
                do_return = False
                raise

        def else_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = 0
            except:
                do_return = False
                raise
        ag__.if_stmt(ag__.converted_call(ag__.ld(tf).greater, (ag__.ld(x), 0), None, fscope), if_body, else_body, get_state, set_state, ('retval_', 'do_return'), 2)
        return fscope.ret(retval_, do_return)


# This is the graph itself.
print(tf_simple_relu.get_concrete_function(tf.constant(1)).graph.as_graph_def())
node {
  name: "x"
  op: "Placeholder"
  attr {
    key: "_user_specified_name"
    value {
      s: "x"
    }
  }
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "shape"
    value {
      shape {
      }
    }
  }
}
node {
  name: "Greater/y"
  op: "Const"
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "value"
    value {
      tensor {
        dtype: DT_INT32
        tensor_shape {
        }
        int_val: 0
      }
    }
  }
}
node {
  name: "Greater"
  op: "Greater"
  input: "x"
  input: "Greater/y"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "cond"
  op: "StatelessIf"
  input: "Greater"
  input: "x"
  attr {
    key: "Tcond"
    value {
      type: DT_BOOL
    }
  }
  attr {
    key: "Tin"
    value {
      list {
        type: DT_INT32
      }
    }
  }
  attr {
    key: "Tout"
    value {
      list {
        type: DT_INT32
        type: DT_BOOL
      }
    }
  }
  attr {
    key: "_lower_using_switch_merge"
    value {
      b: true
    }
  }
  attr {
    key: "_read_only_resource_inputs"
    value {
      list {
      }
    }
  }
  attr {
    key: "else_branch"
    value {
      func {
        name: "cond_false_34"
      }
    }
  }
  attr {
    key: "output_shapes"
    value {
      list {
        shape {
        }
        shape {
        }
      }
    }
  }
  attr {
    key: "then_branch"
    value {
      func {
        name: "cond_true_33"
      }
    }
  }
}
node {
  name: "cond/Identity"
  op: "Identity"
  input: "cond"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "cond/Identity_1"
  op: "Identity"
  input: "cond:1"
  attr {
    key: "T"
    value {
      type: DT_BOOL
    }
  }
}
node {
  name: "Identity"
  op: "Identity"
  input: "cond/Identity"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
library {
  function {
    signature {
      name: "cond_false_34"
      input_arg {
        name: "cond_placeholder"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_BOOL
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const"
      }
    }
    node_def {
      name: "cond/Const_1"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_1"
      }
    }
    node_def {
      name: "cond/Const_2"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_2"
      }
    }
    node_def {
      name: "cond/Const_3"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_3"
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const_3:output:0"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity"
      }
    }
    node_def {
      name: "cond/Const_4"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_4"
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond/Const_4:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity_1"
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
  function {
    signature {
      name: "cond_true_33"
      input_arg {
        name: "cond_identity_x"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_BOOL
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond_identity_x"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity"
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const"
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond/Const:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity_1"
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
}
versions {
  producer: 561
  min_consumer: 12
}


W większości przypadków tf.function będzie działać bez specjalnych uwag. Istnieją jednak pewne zastrzeżenia, a przewodnik po funkcjach tf. Może w tym pomóc, a także pełne odniesienie do AutoGraph

Polimorfizm: jedna Function , wiele wykresów

tf.Graph specjalizuje się do określonego typu danych wejściowych (na przykład, z określonym tensorydtype lub przedmiotów z tym samym id() ).

Za każdym razem, gdy wywołujesz Function z nowymi dtypes i kształtami w swoich argumentach, Function tworzy nowy tf.Graph dla nowych argumentów. dtypes i kształty danych tf.Graph są znane jako sygnatura wejściowa lub po prostu sygnatura .

Function przechowuje tf.Graph odpowiadający temu podpisowi w ConcreteFunction . ConcreteFunction to opakowanie wokół tf.Graph .

@tf.function
def my_relu(x):
  return tf.maximum(0., x)

# `my_relu` creates new graphs as it sees more signatures.
print(my_relu(tf.constant(5.5)))
print(my_relu([1, -1]))
print(my_relu(tf.constant([3., -3.])))
tf.Tensor(5.5, shape=(), dtype=float32)
tf.Tensor([1. 0.], shape=(2,), dtype=float32)
tf.Tensor([3. 0.], shape=(2,), dtype=float32)

Jeśli Function została już wywołana z tym podpisem, Function nie tworzy nowego tf.Graph .

# These two calls do *not* create new graphs.
print(my_relu(tf.constant(-2.5))) # Signature matches `tf.constant(5.5)`.
print(my_relu(tf.constant([-1., 1.]))) # Signature matches `tf.constant([3., -3.])`.
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor([0. 1.], shape=(2,), dtype=float32)

Ponieważ jest poparty wieloma wykresami, możemy powiedzieć, że Function jest polimorficzna . Umożliwia to obsługę większej liczby typów danych wejściowych niż pojedynczy tf.Graph , a także optymalizację każdego tf.Graph celu uzyskania lepszej wydajności.

# There are three `ConcreteFunction`s (one for each graph) in `my_relu`.
# The `ConcreteFunction` also knows the return type and shape!
print(my_relu.pretty_printed_concrete_signatures())
my_relu(x=[1, -1])
  Returns:
    float32 Tensor, shape=(2,)

my_relu(x)
  Args:
    x: float32 Tensor, shape=(2,)
  Returns:
    float32 Tensor, shape=(2,)

my_relu(x)
  Args:
    x: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

Korzystanie z funkcji tf.function

Do tej pory widzieliście, jak można przekonwertować funkcję Pythona na wykres po prostu używając tf.function jako dekoratora lub wrappera. Jednak w praktyce tf.function działanie funkcji tf.function Może być trudne! W kolejnych sekcjach dowiesz się, jak sprawić, by kod działał zgodnie z oczekiwaniami za pomocą tf.function .

Wykonywanie wykresów a szybkie wykonanie

Kod w Function może być wykonywany zarówno chętnie, jak i jako wykres. Domyślnie Function wykonuje swój kod jako wykres:

@tf.function
def get_MSE(y_true, y_pred):
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)
y_true = tf.random.uniform([5], maxval=10, dtype=tf.int32)
y_pred = tf.random.uniform([5], maxval=10, dtype=tf.int32)
print(y_true)
print(y_pred)
tf.Tensor([1 8 6 3 8], shape=(5,), dtype=int32)
tf.Tensor([9 8 3 6 1], shape=(5,), dtype=int32)

get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=26>

Aby sprawdzić, czy Function „s wykres robi tego samego obliczenia jak jego odpowiednik funkcji Python, możemy wykonać to chętnie z tf.config.run_functions_eagerly(True) . Jest to przełącznik, który wyłącza Function do tworzenia i uruchamiania wykresów , zamiast normalnego wykonywania kodu.

tf.config.run_functions_eagerly(True)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=26>
# Don't forget to set it back when you are done.
tf.config.run_functions_eagerly(False)

Jednak Function może zachowywać się inaczej w przypadku wykresu i przyspieszonego wykonywania. Funkcja print języku Python jest jednym z przykładów różnic między tymi dwoma trybami. Zobaczmy, co się stanie, gdy wstawimy instrukcję print do naszej funkcji i będziemy ją wielokrotnie wywoływać.

@tf.function
def get_MSE(y_true, y_pred):
  print("Calculating MSE!")
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)

Obserwuj, co jest drukowane:

error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!

Czy wynik jest zaskakujący? get_MSE drukowane tylko raz, mimo że zostało wywołane trzy razy.

Aby wyjaśnić, instrukcja print jest wykonywana, gdy Function uruchamia oryginalny kod w celu utworzenia wykresu w procesie znanym jako „śledzenie” . Śledzenie przechwytuje operacje TensorFlow do wykresu, a print nie jest przechwytywany na wykresie. Ten wykres jest następnie wykonywany dla wszystkich trzech wywołań bez ponownego uruchamiania kodu Pythona .

Aby sprawdzić poczytalność, wyłączmy wykonywanie wykresów, aby porównać:

# Now, globally set everything to run eagerly to force eager execution.
tf.config.run_functions_eagerly(True)
# Observe what is printed below.
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!
Calculating MSE!
Calculating MSE!

tf.config.run_functions_eagerly(False)

print jest efektem ubocznym Pythona i istnieją inne różnice , o których należy pamiętać podczas konwertowania funkcji na Function .

tf.function najlepsze praktyki

Przyzwyczajenie się do zachowania Function może zająć trochę czasu. Aby szybko rozpocząć pracę, początkujący użytkownicy powinni bawić się dekorowaniem funkcji zabawek za pomocą funkcji @tf.function aby uzyskać doświadczenie w przechodzeniu od chętnego do wykonywania wykresów.

Projektowanie pod kątem tf.function może być najlepszymtf.function na pisanie programów TensorFlow obsługujących wykresy. Oto kilka porad:

Widząc przyspieszenie

tf.function zwykle poprawia wydajność Twojego kodu, ale stopień przyspieszenia zależy od rodzaju wykonywanych obliczeń. Małe obliczenia mogą być zdominowane przez narzut związany z wywołaniem wykresu. Możesz zmierzyć różnicę w wydajności w następujący sposób:

x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)

def power(x, y):
  result = tf.eye(10, dtype=tf.dtypes.int32)
  for _ in range(y):
    result = tf.matmul(x, result)
  return result
print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000))
Eager execution: 1.9166935040000226

power_as_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000))
Graph execution: 0.5355543040000157

tf.function jest powszechnie używany do przyspieszania pętli treningowych, jak widać tutaj w przypadku Keras.

Wydajność i kompromisy

Wykresy mogą przyspieszyć twój kod, ale proces ich tworzenia wiąże się z pewnym narzutem. W przypadku niektórych funkcji utworzenie wykresu zajmuje więcej czasu niż wykonanie wykresu. Ta inwestycja jest zwykle szybko zwracana wraz ze wzrostem wydajności kolejnych wykonań, ale ważne jest, aby mieć świadomość, że kilka pierwszych kroków dowolnego uczenia dużego modelu może być wolniejsze z powodu śledzenia.

Bez względu na to, jak duży jest Twój model, chcesz uniknąć częstego śledzenia. Przewodnik po tf.function omawia, jak ustawić specyfikacje wejścia i jak używać argumentów tensora, aby uniknąć odtworzenia. Jeśli zauważysz, że osiągasz wyjątkowo słabe wyniki, dobrze jest sprawdzić, czy przypadkowo nie odtwarzasz.

Kiedy jest śledzenie Function ?

Aby dowiedzieć się, kiedy Function śledzi, dodaj instrukcję print do jej kodu. Zasadą jest, że Function wykona instrukcję print każdym razem, gdy będzie śledzić.

@tf.function
def a_function_with_python_side_effect(x):
  print("Tracing!") # An eager-only side effect.
  return x * x + tf.constant(2)

# This is traced the first time.
print(a_function_with_python_side_effect(tf.constant(2)))
# The second time through, you won't see the side effect.
print(a_function_with_python_side_effect(tf.constant(3)))
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(11, shape=(), dtype=int32)

# This retraces each time the Python argument changes,
# as a Python argument could be an epoch count or other
# hyperparameter.
print(a_function_with_python_side_effect(2))
print(a_function_with_python_side_effect(3))
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

Tutaj widzisz dodatkowe śledzenie, ponieważ nowe argumenty Pythona zawsze powodują utworzenie nowego wykresu.

Następne kroki

Bardziej szczegółowe omówienie można znaleźć zarówno na tf.function z tf.function interfejsie API tf.function iw przewodniku .