In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

Конвертируйте ваш существующий код в TensorFlow 2.0

Смотрите на TensorFlow.org Изучайте код на GitHub Скачайте ноутбук

Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует официальной документации на английском языке. Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в tensorflow/docs репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на docs-ru@tensorflow.org list.

В TensorFlow 2.0 все еще возможно исполнить 1.X код без изменений (за исключением contrib):

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

Однако, это не дает вам воспользоваться преимуществами многих улучшений сделанных в TensorFlow 2.0. Это руководство поможет вам обновить ваш код, сделав его проще, производительнее и легче в поддержке.

Скрипт автоматической конвертации

Первым шагом вы можете попробовать запустить скрипт обновления.

Он выполнит начальный этап обновления вашего кода до TensorFlow 2.0. Но это не может сделать ваш код идиоматичным TensorFlowF 2.0. Ваш код все еще может использовать tf.compat.v1 для доступа к плейсхолдерам, сессиям, коллекциям, и другой функциональности в стиле 1.x.

Сделайте код 2.0-нативным

В этом руководстве рассматриваются несколько примеров преобразования кода TensorFlow 1.x в TensorFlow 2.0. Эти изменения позволят вашему коду воспользоваться преимуществами оптимизации производительности и упрощенных вызовов API.

В каждом случае паттерн следующий:

1. Заменить вызовы tf.Session.run

Каждый вызов tf.Session.run нужо заменить функцией Python.

  • feed_dict и tf.placeholders становятся аргументами функции.
  • fetches становится возвращаемым значением функции.

Вы можете пройти пошагово и отладить функцию, используя стандартные инструменты Python, такие как pdb.

Когда вы убедитесь, что функция работает, добавьте декораторtf.function чтобы она работала эффективно в режиме графа. Смотри Руководство Autograph чтобы узнать больше о том, как это работает.

2. Используйте объекты python для отслеживания переменных и значений потерь

Используйте tf.Variable вместо tf.get_variable.

Каждый variable_scope может быть сконвертирован в объект Python. Как правило это будет что-то из:

  • tf.keras.layers.Layer
  • tf.keras.Model
  • tf.Module

Если вам нужны свести списки переменных (как например tf.Graph.get_collection(tf.GraphKeys.VARIABLES)), используйте аттрибуты .variables и .trainable_variables объектов Layer и Model.

Эти классы Layer и Model реализуют несколько других свойств которые устраняют необходимость глобальных коллекций.

Смотри руководства keras для подробностей.

Предупреждение: Многие символы tf.compat.v1 неявно используют глобальные коллекции.

3. Обновите ваши обучающие циклы

Используйте API наиболее высокого уровня который работает в вашем случае. Предпочтите tf.keras.Model.fit построению своего собственного обучающего цикла.

Эти высокоуровневые функции управляют большим количеством низкоуровневых деталей которые могут быть легко упущены если вы пишете собственный обучающий цикл. Например, они автоматически собирают потери регуляризации и устанавливают аргумент training = True при вызове модели.

4. Обновите ваши конвейеры ввода данных

Используйте наборы данных tf.data для входных данных. Эти объекты эффективны, выразительны и хорошо интегрированы с tensorflow.

Их можно передать напрямую в метод tf.keras.Model.fit.

model.fit(dataset, epochs=5)

Их можно напрямую итерировать в стандартном Python:

for example_batch, label_batch in dataset:
    break

Конвертация моделей

Установка


In [ ]:
import tensorflow.compat.v2 as tf
except Exception:
  pass
tf.enable_v2_behavior()


import tensorflow_datasets as tfds

Низкоуровневые переменные и исполнение оператора

Примеры использования низкоуровневого API включают:

  • использование областей видимости переменных для управления повторным использованием
  • создание переменных с tf.get_variable.
  • явный доступ к коллекциям
  • неявный доступ к коллекциям с такими методами, как:

    • tf.global_variables
    • tf.losses.get_regularization_loss
  • использование tf.placeholder для установления входных данных графа

  • выполнение графа с session.run
  • ручная инициализация переменных

Перед конвертацией

Здесь как могут выглядеть эти паттерны в коде использующем TensorFlow 1.x.

in_a = tf.placeholder(dtype=tf.float32, shape=(2))
in_b = tf.placeholder(dtype=tf.float32, shape=(2))

def forward(x):
  with tf.variable_scope("matmul", reuse=tf.AUTO_REUSE):
    W = tf.get_variable("W", initializer=tf.ones(shape=(2,2)),
                        regularizer=tf.contrib.layers.l2_regularizer(0.04))
    b = tf.get_variable("b", initializer=tf.zeros(shape=(2)))
    return W * x + b

out_a = forward(in_a)
out_b = forward(in_b)

reg_loss = tf.losses.get_regularization_loss(scope="matmul")

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  outs = sess.run([out_a, out_b, reg_loss],
                feed_dict={in_a: [1, 0], in_b: [0, 1]})

После конвертации

В сконвертированном коде:

  • Переменные являются локальными объектами Python.
  • Функция forward все еще определяет вычисления.
  • Вызов sess.run заменен вызовом forward
  • Опциональный декоратор tf.function может быть добавлен для производительности.
  • Регуляризации вычисляются вручную без ссылок на глобальные коллекции.
  • Нет сессий и плейсхолдеров.

In [ ]:
W = tf.Variable(tf.ones(shape=(2,2)), name="W")
b = tf.Variable(tf.zeros(shape=(2)), name="b")

@tf.function
def forward(x):
  return W * x + b

out_a = forward([1,0])
print(out_a)

In [ ]:
out_b = forward([0,1])

regularizer = tf.keras.regularizers.l2(0.04)
reg_loss = regularizer(W)

Модели основанные на tf.layers

Модуль tf.layers используется для содержания layer-функций использующих tf.variable_scope для определения и переиспользования переменных.

До конвертации

def model(x, training, scope='model'):
  with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
    x = tf.layers.conv2d(x, 32, 3, activation=tf.nn.relu,
          kernel_regularizer=tf.contrib.layers.l2_regularizer(0.04))
    x = tf.layers.max_pooling2d(x, (2, 2), 1)
    x = tf.layers.flatten(x)
    x = tf.layers.dropout(x, 0.1, training=training)
    x = tf.layers.dense(x, 64, activation=tf.nn.relu)
    x = tf.layers.batch_normalization(x, training=training)
    x = tf.layers.dense(x, 10, activation=tf.nn.softmax)
    return x

train_out = model(train_data, training=True)
test_out = model(test_data, training=False)

После конвертации

  • Простой стек слоев аккуратно встраивается в tf.keras.Sequential. (Для более сложных моделей см. пользовательские слои и модели, и функциональный API.)
  • Модель отслеживает переменные и потери регуляризации.
  • Преобразование взаимно-однозначно поскольку существует прямое отображение из tf.layers в tf.keras.layers.

Большинство аргументов остались прежними. Но обратите внимание на различия:

  • Аргумент training передается моделью каждому слою при его запуске.
  • Первого аргумента исходной функции model (вводный x) больше нет. Это связано с тем, что слои объекта отделяют построение модели от вызова модели.

Также заметьте что:

  • Если вы использовали регуляризаторы инициализаторов из tf.contrib, у них больше изменений аргументов чем у остальных.
  • Код больше не записывает в коллекции, так что функции наподобие tf.losses.get_regularization_loss больше не возращают эти значения, что может нарушить ваши циклы обучения.

In [ ]:
model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, 3, activation='relu',
                           kernel_regularizer=tf.keras.regularizers.l2(0.04),
                           input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dropout(0.1),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Dense(10, activation='softmax')
])

train_data = tf.ones(shape=(1, 28, 28, 1))
test_data = tf.ones(shape=(1, 28, 28, 1))

In [ ]:
train_out = model(train_data, training=True)
print(train_out)

In [ ]:
test_out = model(test_data, training=False)
print(test_out)

In [ ]:
# Здесь все обучаемые переменные.
len(model.trainable_variables)

In [ ]:
# Здесь потери регуляризации.
model.losses

Смесь переменных и tf.layers

Существующий код часто смешивает низкоуровневые TF 1.x переменные и операции с высокоуровневыми tf.layers.

До конвертации

def model(x, training, scope='model'):
  with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
    W = tf.get_variable(
      "W", dtype=tf.float32,
      initializer=tf.ones(shape=x.shape),
      regularizer=tf.contrib.layers.l2_regularizer(0.04),
      trainable=True)
    if training:
      x = x + W
    else:
      x = x + W * 0.5
    x = tf.layers.conv2d(x, 32, 3, activation=tf.nn.relu)
    x = tf.layers.max_pooling2d(x, (2, 2), 1)
    x = tf.layers.flatten(x)
    return x

train_out = model(train_data, training=True)
test_out = model(test_data, training=False)

После конвертации

Для конвертации этого кода следуйте паттерну отображения слоев в слои как и в предыдущем примере.

tf.variable_scope фактически является слоем сам по себе. Поэтому перепишите его как tf.keras.layers.Layer. См. руководство для подробностей.

В общем паттерн следующий:

  • Собрать параметры слоев в __init__.
  • Создать переменные в build.
  • Выполнить вычисления в call и вернуть результат.

tf.variable_scope по сути является собственным слоем. Поэтому перепишите его как tf.keras.layers.Layer. Смотрите руководство для деталей.


In [ ]:
# Создайте пользовательский слой для части модели
class CustomLayer(tf.keras.layers.Layer):
  def __init__(self, *args, **kwargs):
    super(CustomLayer, self).__init__(*args, **kwargs)

  def build(self, input_shape):
    self.w = self.add_weight(
        shape=input_shape[1:],
        dtype=tf.float32,
        initializer=tf.keras.initializers.ones(),
        regularizer=tf.keras.regularizers.l2(0.02),
        trainable=True)

  # Метод call будет иногда использоваться в режиме графа,
  # training превратится в тензор
  @tf.function
  def call(self, inputs, training=None):
    if training:
      return inputs + self.w
    else:
      return inputs + self.w * 0.5

In [ ]:
custom_layer = CustomLayer()
print(custom_layer([1]).numpy())
print(custom_layer([1], training=True).numpy())

In [ ]:
train_data = tf.ones(shape=(1, 28, 28, 1))
test_data = tf.ones(shape=(1, 28, 28, 1))

# Build the model including the custom layer
model = tf.keras.Sequential([
    CustomLayer(input_shape=(28, 28, 1)),
    tf.keras.layers.Conv2D(32, 3, activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
])

train_out = model(train_data, training=True)
test_out = model(test_data, training=False)

Некоторые вещи на заметку:

  • Подклассы моделей и слоев Keras нужно запускать и в v1 графах (без автоматического контроля зависимостей) и в режиме eager mode

    • Оберните call() в tf.function() чтобы получить autograph и автоматический контроль зависимостей
  • Не забудьте принять аргумент training в call.

    • Иногда это tf.Tensor
    • Иногда это булеан Python.
  • Создайте переменные модели в конструкторе или def build() используя self.add_weight().

    • В build у вас есть доступ к размерности входных данных, так что создайте веса с совпадающими размерностями.
    • Использование tf.keras.layers.Layer.add_weight позволяет Keras отслеживать переменные и потери регуляризации.
  • Не храните tf.Tensors в своих объектах.

    • Они могут быть созданы либо в tf.function либо в контексте eager, и эти тензоры ведут себя по-другому.
    • Используйте tf.Variables для состояния, их всегда можно использовать из обеих контекстов
    • tf.Tensors это только промежуточные значения.

Замечание о Slim и contrib.layers

Большое количество старого TensorFlow 1.x кода использует библиотеку Slim которая входит в пакет TensorFlow 1.x в качестве tf.contrib.layers. В качестве модуля contrib она более не доступна в TensorFlow 2.0, даже в tf.compat.v1. Конвертация кода использовавшего Slim в TF 2.0 запутаннее чем конвертация репозиториев использующих tf.layers. Имеет смысл сперва сконвертировать ваш Slim код сперва вtf.layers, а затем конвертировать в Keras.

  • Уберите arg_scopes, все аргументы должны быть явными
  • Если вы используете их, поделите normalizer_fn и activation_fn каждый в свой собственный слой
  • Separable сверточные слои отображаются в один или более различных слоев Keras (по глубине, поточечно, и separable слои Keras)
  • Slim и tf.layers имеют разные имена аргументов и значения по умолчанию
  • Некоторые аргументы имеют разные размерности
  • Если вы используете предобученные модели Slim, попробуйте tf.keras.applications или TFHub

Некоторые слои tf.contrib возможно не были перемещены в ядро TensorFlow, а вместо этого были перемещены в пакет TF add-ons.

Обучение

Существует много способов подачи данных в модели tf.keras. Они допускают генераторы Python и массивы Numpy в качестве входных данных.

Рекомендуемы способ подачи данных в модель это - использовать пакет tf.data, который содержит набор высокопроизводительных классов для манипуляций с данными.

Если вы все еще используете tf.queue, они поддерживаются только как структуры данных, а не как входные конвейеры.

Использование наборов данных

Пакет TensorFlow Datasets (tfds) содержит утилиты для загрузки предопределенных баз данных как объектов tf.data.Dataset.

Например, загрузим MNISTdataset, используя tfds:


In [ ]:
datasets, info = tfds.load(name='mnist', with_info=True, as_supervised=True)
mnist_train, mnist_test = datasets['train'], datasets['test']

Затем приготовим данные для обучения:

  • Изменим размер каждого изображения.
  • Перемешаем порядок примеров.
  • Соберем batches изображений и меток.

In [ ]:
BUFFER_SIZE = 10 # Используйте намного большее значение для настоящего кода.
BATCH_SIZE = 64
NUM_EPOCHS = 5


def scale(image, label):
  image = tf.cast(image, tf.float32)
  image /= 255

  return image, label

Чтобы пример оставался коротким обрежем данные, чтобы он возвращал только 5 batches:


In [ ]:
train_data = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE).take(5)
test_data = mnist_test.map(scale).batch(BATCH_SIZE).take(5)

STEPS_PER_EPOCH = 5

train_data = train_data.take(STEPS_PER_EPOCH)
test_data = test_data.take(STEPS_PER_EPOCH)

In [ ]:
image_batch, label_batch = next(iter(train_data))

Использование обучающик циклов Keras

Если вам не нужен низкоуровневый коноль процесса обучения модели, рекомендуется использовать встроенные в Keras методы fit, evaluate и predict. Эти методы обеспечивают единый интерфейс обучения модели независимо от реализации (sequential, functional или sub-classed).

Преимущества этих методов включают:

  • Они допускают массивы Numpy, генераторы Python и tf.data.Datasets
  • Они применяют регуляризационные и активационные потери автоматически.
  • Они поддерживают tf.distribute для обучения на нескольких устройствах.
  • Они поддерживают произвольные вызываемые объекты как потери и метрики.
  • Они поддерживают коллбеки такие как tf.keras.callbacks.TensorBoard и пользовательские коллбеки.
  • Они производительны, автоматически используя графы TensorFlow.

Приведем пример обучения модели с ипользованием Dataset. (Подробнее о том как это работает смотри тьюториалы.)


In [ ]:
model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, 3, activation='relu',
                           kernel_regularizer=tf.keras.regularizers.l2(0.02),
                           input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dropout(0.1),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Dense(10, activation='softmax')
])

# Model is the full model w/o custom layers
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(train_data, epochs=NUM_EPOCHS)
loss, acc = model.evaluate(test_data)

print("Loss {}, Accuracy {}".format(loss, acc))

Напишите свой собственный цикл

Если обучающий шаг модели Keras подходит вам, но вне шага вам нужет больший контроль, рассмотрите использование tf.keras.model.train_on_batch method, в вашем собтвенном цикле итерации данных.

Запомните: Многие вещи могут быть реализованы как tf.keras.Callback.

Этот метод имеет много преимуществ перед методами, упомянутыми в предыдущем разделе, но он дает пользователю контроль над внешним циклом.

Вы также можете использовать tf.keras.model.test_on_batch или tf.keras.Model.evaluate чтобы проверить производительность во время обучения.

Примечание: train_on_batch и test_on_batch по умолчанию возвращают потерю и метрики для одного batch. Если вы передаете reset_metrics=False они возвращают накопленные метрики и вы должны помнить своевременно сбрасывать накопители метрик. Таже помните, что некоторые метрики, такие как AUC требуют reset_metrics=False для корректного вычисления.

Чтобы продолжить обучение вышеуказанной модели:


In [ ]:
# Model это полная модель без пользовательских слоев
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

metrics_names = model.metrics_names

for epoch in range(NUM_EPOCHS):
  #Reset the metric accumulators
  model.reset_metrics()

  for image_batch, label_batch in train_data:
    result = model.train_on_batch(image_batch, label_batch)
    print("train: ",
          "{}: {:.3f}".format(metrics_names[0], result[0]),
          "{}: {:.3f}".format(metrics_names[1], result[1]))
  for image_batch, label_batch in test_data:
    result = model.test_on_batch(image_batch, label_batch,
                                 # return accumulated metrics
                                 reset_metrics=False)
  print("\neval: ",
        "{}: {:.3f}".format(metrics_names[0], result[0]),
        "{}: {:.3f}".format(metrics_names[1], result[1]))

Настройте шаг обучения

Если вам нужны большая гибкость и контроль, вы можете получить их реализовав собственный цикл обучения. Есть три шага:

  1. Проитерируйте генератор Python или tf.data.Dataset чтобы получить пакеты примеров.
  2. Используйте tf.GradientTape чтобы собрать градиенты.
  3. Используйте tf.keras.optimizer чтобы применить обновления весов к переменным модели.

Помните:

  • Всегда включайте аргумент training в метод call подклассов слоев и моделей.
  • Убедитесь что вызываете модель с корректно установленным аргументом training.
  • В зависимости от использования, переменные модели могут не существовать, пока модель не будет запущена на пакете данных.
  • Вам нужно вручную обрабатывать такие вещи, как потери регуляризации для модели.

Обратите внимание на упрощения относительно v1:

  • Нет необходимости запускать инициализаторы переменных. Переменные инициализируются при создании.
  • Нет необходимости добавлять зависимости ручного управления. Даже в операциях tf.function действующих как в eager mode.

In [ ]:
model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, 3, activation='relu',
                           kernel_regularizer=tf.keras.regularizers.l2(0.02),
                           input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dropout(0.1),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Dense(10, activation='softmax')
])

optimizer = tf.keras.optimizers.Adam(0.001)
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy()

@tf.function
def train_step(inputs, labels):
  with tf.GradientTape() as tape:
    predictions = model(inputs, training=True)
    regularization_loss = tf.math.add_n(model.losses)
    pred_loss = loss_fn(labels, predictions)
    total_loss = pred_loss + regularization_loss

  gradients = tape.gradient(total_loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))

for epoch in range(NUM_EPOCHS):
  for inputs, labels in train_data:
    train_step(inputs, labels)
  print("Finished epoch", epoch)

Метрики в новом стиле

В TensorFlow 2.0, метрики являются объектами. Метрики работают и eagerly и в tf.function. Объекты-метрики обладают следующими методами:

  • update_state() — добавить новые наблюдения
  • result() — получить текущий результат метрики при данных наблюдаемых значениях
  • reset_states() — очистить все наблюдения.

Объект сам является вызываемым. Вызов обновляет состояние новыми наблюдениями, как и с update_state, и возвращает новый результат метрики

Вам не нужно вручную инициализировать переменные метрики, и, поскольку у TensorFlow 2.0 автоматическое управление зависимостями, вам не нужно беспокоиться и об этом.

В приведенном ниже коде используется метрика для отслеживания среднего значения потерь, наблюдаемых в пользовательском цикле обучения.


In [ ]:
# Создайте метрики
loss_metric = tf.keras.metrics.Mean(name='train_loss')
accuracy_metric = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')

@tf.function
def train_step(inputs, labels):
  with tf.GradientTape() as tape:
    predictions = model(inputs, training=True)
    regularization_loss = tf.math.add_n(model.losses)
    pred_loss = loss_fn(labels, predictions)
    total_loss = pred_loss + regularization_loss

  gradients = tape.gradient(total_loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))
  # Обновите метрики
  loss_metric.update_state(total_loss)
  accuracy_metric.update_state(labels, predictions)


for epoch in range(NUM_EPOCHS):
  # Сбросьте метрики
  loss_metric.reset_states()
  accuracy_metric.reset_states()

  for inputs, labels in train_data:
    train_step(inputs, labels)
  # Получите результаты метрики
  mean_loss = loss_metric.result()
  mean_accuracy = accuracy_metric.result()

  print('Epoch: ', epoch)
  print('  loss:     {:.3f}'.format(mean_loss))
  print('  accuracy: {:.3f}'.format(mean_accuracy))

Сохранение и загрузка

Совместимость контрольных точек

TensorFlow 2.0 использует контрольные точки основанные на объектах.

Контрольные точки в старом стиле основанные на именах по-прежнему могут быть загружены, если вы осторожны с ними. В процессе конвертации кода могут измениться имена переменных, но есть обходные пути.

Самый простой подход - согласовать имена новой модели с именами в контрольной точке.:

  • У переменных все еще есть аргумент name который вы можете установить.
  • Модели Keras также используют аргумент name, который они устанавливают в качестве префикса для своих переменных.
  • Функция tf.name_scope может использоваться для установки префиксов имен переменных. Это сильно отличается от tf.variable_scope. Он влияет только на имена и не отслеживает переменные и их переиспользование.

Если это не работает для вашего случая, попробуйте функцию tf.compat.v1.train.init_from_checkpoint. Она принимает аргумент assignment_map, который определяет соответствие старых и новых имен.

Примечание: В отличие от основанных на объектах контрольных точек, которые могут [отложить загрузку] (checkpoint.ipynb#loading_mechanics), основанные на именах контрольных точек требуют, чтобы при вызове функции были созданы все переменные. Некоторые модели откладывают создание переменных до тех пор, пока вы не вызовете build или не запустите модель на пакете данных.

Совместимость сохраненных моделей

У совместимости для сохраненных моделей нет существенных проблем.

  • TensorFlow 1.x saved_models работают TensorFlow 2.0.
  • TensorFlow 2.0 saved_models даже загруженные работают в TensorFlow 1.x если все операции поддерживаются.

Estimators

Обучение с оценщиками

Оценщики поддерживаются TensorFlow 2.0.

Когда вы используете оценщики, вы можете использовать input_fn(), tf.estimator.TrainSpec, и tf.estimator.EvalSpec из TensorFlow 1.x.

Здесь пример использующий input_fn с train and evaluate specs.

Создание input_fn и train/eval specs


In [ ]:
# Определим input_fn оценщика 
def input_fn():
  datasets, info = tfds.load(name='mnist', with_info=True, as_supervised=True)
  mnist_train, mnist_test = datasets['train'], datasets['test']

  BUFFER_SIZE = 10000
  BATCH_SIZE = 64

  def scale(image, label):
    image = tf.cast(image, tf.float32)
    image /= 255

    return image, label[..., tf.newaxis]

  train_data = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
  return train_data.repeat()

# Define train & eval specs
train_spec = tf.estimator.TrainSpec(input_fn=input_fn,
                                    max_steps=STEPS_PER_EPOCH * NUM_EPOCHS)
eval_spec = tf.estimator.EvalSpec(input_fn=input_fn,
                                  steps=STEPS_PER_EPOCH)

Использование определения модели Keras

Есть некоторые отличия в том, как построить ваши оценщики в TensorFlow 2.0.

Мы рекомендуем вам определить модель используя Keras, потом используйте утилиту tf.keras.model_to_estimator для преобразования вашей модели в оценщика. Нижеприведенный код показывает как использовать эту утилиту когда создаешь и обучаешь оценщик.


In [ ]:
def make_model():
  return tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, 3, activation='relu',
                           kernel_regularizer=tf.keras.regularizers.l2(0.02),
                           input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dropout(0.1),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Dense(10, activation='softmax')
  ])

In [ ]:
model = make_model()

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

estimator = tf.keras.estimator.model_to_estimator(
  keras_model = model
)

tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)

Использование пользовательской model_fn

Если у вас есть существующий пользовательский оценщик model_fn, который вам нужно поддерживать, вы можете конвертировать свойmodel_fn чтобы использовать модель Keras.

Однако по соображениям совместимости пользовательский model_fn будет по-прежнему работать в стиле 1.x графа. Это означает, что нет будет eager execution и нет автоматического управления зависимостей.

Использование моделей Keras в пользовательском model_fn аналогично использованию в пользовательском цикле обучения:

  • Установите фазу training соответствующе, основываясь на аргументе mode.
  • Явно передайте trainable_variables модели оптимизатору.

Но есть важные различия отлосящиеся к пользовательскому циклу:

  • Вместо использования model.losses извлеките потери, используяtf.keras.Model.get_losses_for.
  • Извлеките обновления модели используя tf.keras.Model.get_updates_for

Примечание: "Updates" это изменения которые необходимо применить к модели после каждого пакета. Например, скользящие средние среднего и дисперсии в слое tf.keras.layers.BatchNormalization.

Следующий код создает оценщик из пользовательского model_fn, иллюстрируя все эти проблемы.


In [ ]:
def my_model_fn(features, labels, mode):
  model = make_model()

  optimizer = tf.compat.v1.train.AdamOptimizer()
  loss_fn = tf.keras.losses.SparseCategoricalCrossentropy()

  training = (mode == tf.estimator.ModeKeys.TRAIN)
  predictions = model(features, training=training)

  reg_losses = model.get_losses_for(None) + model.get_losses_for(features)
  total_loss = loss_fn(labels, predictions) + tf.math.add_n(reg_losses)

  accuracy = tf.compat.v1.metrics.accuracy(labels=labels,
                                           predictions=tf.math.argmax(predictions, axis=1),
                                           name='acc_op')

  update_ops = model.get_updates_for(None) + model.get_updates_for(features)
  minimize_op = optimizer.minimize(
      total_loss,
      var_list=model.trainable_variables,
      global_step=tf.compat.v1.train.get_or_create_global_step())
  train_op = tf.group(minimize_op, update_ops)

  return tf.estimator.EstimatorSpec(
    mode=mode,
    predictions=predictions,
    loss=total_loss,
    train_op=train_op, eval_metric_ops={'accuracy': accuracy})

# Создайте оценщик и обучите
estimator = tf.estimator.Estimator(model_fn=my_model_fn)
tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)

Готовые оценщики

Готовые оценщики из семейств tf.estimator.DNN*, tf.estimator.Linear* и tf.estimator.DNNLinearCombined* все еще поддерживаются в TensorFlow 2.0 API, однако, некоторые аргументы изменились:

  1. input_layer_partitioner: Убрано в 2.0.
  2. loss_reduction: Обновлено до tf.keras.losses.Reduction вместо tf.compat.v1.losses.Reduction. Значение по умолчанию также изменилось и стало tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE вместо tf.compat.v1.losses.Reduction.SUM.
  3. optimizer, dnn_optimizer и linear_optimizer: эти аргументы обновились до tf.keras.optimizers вместо tf.compat.v1.train.Optimizer.

Для переноса вышеуказанных изменений:

  1. Для input_layer_partitioner миграция не требуется поскольку Стратегия распределения обработает это автоматически в TF 2.0.
  2. Для loss_reduction, проверьте tf.keras.losses.Reduction для поддерживаемых опций.
  3. Для аргументов optimizer args, если вы не передаете аргумента optimizer, dnn_optimizer или linear_optimizer, или если вы укажете в своем коде аргумент optimizer как string, вам не нужно ничего менять. tf.keras.optimizers используются по умолчанию. Иначе, вам нужно обновить его от tf.compat.v1.train.Optimizer до соответсвующей tf.keras.optimizers

Конвертер контрольных точек

Миграция optimizer повредит контрольные точки в TF 1.x, так какtf.keras.optimizer генерирует другой набор переменных для сохранения в контрольных точках. Чтобы сделать контрольную пригодной к использованию после перехода на TF 2.0, пожалуйста, посмотрите инструмент конвертации контрольных точек для оптимизаторов, чтобы преобразовать контрольные точки из TF 1.x в TF 2.0. Преобразованные контрольные точки можно использовать для восстановления предварительно обученных моделей в TF 2.0.

TensorShape

Этот класс был упрощен для хранения int вместо объектов tf.compat.v1.Dimension. Так что нет необходимости в вызове .value() чтобы получить int.

Отдельные объекты tf.compat.v1.Dimension по-прежнему доступны из tf.TensorShape.dims.

Следующее демонстрирует отличия TensorFlow 1.x и TensorFlow 2.0.


In [ ]:
# Создайте shape и выберите index
i = 0
shape = tf.TensorShape([16, None, 256])
shape

Если у вас есть это в TF 1.x:

value = shape[i].value

Сделайте это в TF 2.0:


In [ ]:
value = shape[i]
value

Если у вас есть это в TF 1.x:

for dim in shape:
    value = dim.value
    print(value)

TСделайте это в TF 2.0:


In [ ]:
for value in shape:
  print(value)

Если у вас есть это в 1.x (Или используется любой другой метод размерности):

dim = shape[i]
dim.assert_is_compatible_with(other_dim)

Сделайте это в TF 2.0:


In [ ]:
other_dim = 16
Dimension = tf.compat.v1.Dimension

if shape.rank is None:
  dim = Dimension(None)
else:
  dim = shape.dims[i]
dim.is_compatible_with(other_dim) # или любой другой метод размерности

In [ ]:
shape = tf.TensorShape(None)

if shape:
  dim = shape.dims[i]
  dim.is_compatible_with(other_dim) # или любой другой метод размерности

Булево значение tf.TensorShape является True если ранг известен, False в противном случае.


In [ ]:
print(bool(tf.TensorShape([])))      # Скаляр
print(bool(tf.TensorShape([0])))     # Вектор длины 0
print(bool(tf.TensorShape([1])))     # Вектор длины 1
print(bool(tf.TensorShape([None])))  # Вектор неизвестной длины
print(bool(tf.TensorShape([1, 10, 100])))       # 3D тензор
print(bool(tf.TensorShape([None, None, None]))) # 3D тензор с неизвестными размерностями
print()
print(bool(tf.TensorShape(None)))  # Тензор неизвестного ранга.

Другие поведенческие изменения

В TensorFlow 2.0 есть несколько других поведенческих изменений, с которыми вы можете столкнуться.

ResourceVariables

TensorFlow 2.0 создает по умолчанию ResourceVariables, а не RefVariables.

ResourceVariables закрыты для записи, и обеспечивают лучшие гарантии согласовенности.

  • Это может изменить поведение в граничных случаях.
  • Это может иногда создавать дополнительные копии и использовать большие объемы памяти
  • Это можно отключить, передав use_resource = False конструкторуtf.Variable.

Control Flow

Реализация control flow была упрощена, поэтому в TensorFlow 2.0 создаются другие графы.

Выводы

Общий процесс следующий:

  1. Запуститсе upgrade script.
  2. Удалите символы contrib.
  3. Переключите ваши модели в объектно ориентированный стиль (Keras).
  4. Используйте циклы обучения и оценки tf.keras или tf.estimator там где вы можете.
  5. Иначе используйте пользовательские циклы, но избегайте сессий и коллекций.

Для преобразования кода в идиоматический TensorFlow 2.0 требуется небольшая работа, но каждое изменение приводит к:

  • Меньшему количеству строк кода.
  • Увеличившейся понятности и простоте.
  • К более легкой отладке.