Перевод обзорного руководства с сайта Tensorflow.org. Это руководство даст вам основы для начала работы с Keras. Чтение займет 10 минут.

Импортируйте tf.keras


tf.keras является реализацией TensorFlow спецификации Keras API. Это высокоуровневый API для построения и обучения моделей включающий первоклассную поддержку для TensorFlow-специфичной функциональности, такой как eager execution, конвейеры tf.data, и Estimators. tf.keras делает использование TensorFlow проще не жертвуя при этом гибкостью и, производительностью.

Для начала, импортируйте tf.keras как часть установки вашей TensorFlow:

from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf

from tensorflow import keras

tf.keras может выполнять любой Keras-совместимый код, но имейте ввиду:

  • Версия tf.keras в последнем релизе TensorFlow может отличаться от последней версии keras в PyPI. Проверьте tf.keras.__version__.
  • Когда сохраняете веса моделей, tf.keras делает это по умолчанию в формате checkpoint. Передайте параметр save_format='h5' для использования HDF5 (или добавьте к имени файла расширение .h5).

Постройте простую модель


Последовательная модель


В Keras, вы собираете слои (layers) для построения моделей (models). Модель это (обычно) граф слоев. Наиболее распространенным видом модели является стек слоев: модель tf.keras.Sequential.

Построим простую полносвязную сеть (т.е. многослойный перцептрон):

from tensorflow.keras import layers

model = tf.keras.Sequential()
# Добавим к модели полносвязный слой с 64 узлами:
model.add(layers.Dense(64, activation='relu'))
# Добавим другой слой:
model.add(layers.Dense(64, activation='relu'))
# Добавим слой softmax с 10 выходами:
model.add(layers.Dense(10, activation='softmax'))

Настройте слои


Доступно много разновидностей слоев tf.keras.layers. Большинство из них используют общий конструктор аргументов:

  • activation: Установка функции активации для слоя. В этом параметре указывается имя встроенной функции или вызываемый объект. У параметра нет значения по умолчанию.
  • kernel_initializer и bias_initializer: Схемы инициализации создающие веса слоя (ядро и сдвиг). В этом параметре может быть имя или вызываемый объект. По умолчанию используется инициализатор "Glorot uniform".
  • kernel_regularizer и bias_regularizer: Схемы регуляризации добавляемые к весам слоя (ядро и сдвиг), такие как L1 или L2 регуляризации. По умолчанию регуляризация не устанавливается.

Следующие примеры экземпляров слоев `tf.keras.layers.Dense` используют аргументы конструктора:

# Создадим слой с сигмоидой:
layers.Dense(64, activation='sigmoid')
# Или:
layers.Dense(64, activation=tf.keras.activations.sigmoid)

# Линейный слой с регуляризацией L1 с коэфициентом 0.01 примененной к матрице ядра:
layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))

# Линейный слой с регуляризацией L2 с коэффициентом 0.01 примененной к вектору сдвига:
layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))

# Линейный слой с ядром инициализированным случайной ортогональной матрицей:
layers.Dense(64, kernel_initializer='orthogonal')

# Линейный слой с вектором сдвига инициализированным значениями 2.0:
layers.Dense(64, bias_initializer=tf.keras.initializers.Constant(2.0))

Обучение и оценка


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


После того как модель сконструирована, настройте процесс ее обучения вызовом метода compile:

model = tf.keras.Sequential([
# Добавляем полносвязный слой с 64 узлами к модели:
layers.Dense(64, activation='relu', input_shape=(32,)),
# Добавляем другой:
layers.Dense(64, activation='relu'),
# Добавляем слой softmax с 10 выходами:
layers.Dense(10, activation='softmax')])

model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

tf.keras.Model.compile принимает три важных аргумента:

  • optimizer: Этот объект определяет процедуру обучения. Передайте в него экземпляры оптимизатора из модуля tf.keras.optimizers, такие как tf.keras.optimizers.Adam или tf.keras.optimizers.SGD. Если вы просто хотите использовать параметры по умолчанию, вы также можете указать оптимизаторы ключевыми словами, такими как 'adam' или 'sgd'.
  • loss: Это функция которая минимизируется в процессе обучения. Среди распространенных вариантов среднеквадратичная ошибка (mse), categorical_crossentropy, binary_crossentropy. Функции потерь указываются по имени или передачей вызываемого объекта из модуля tf.keras.losses.
  • metrics: Используются для мониторинга обучения. Это строковые имена или вызываемые объекты из модуля tf.keras.metrics.
  • Кроме того, чтобы быть уверенным, что модель обучается и оценивается eagerly, проверьте что вы передали компилятору параметр run_eagerly=True

Далее посмотрим несколько примеров конфигурации модели для обучения:

# Сконфигурируем модель для регрессии со среднеквадратичной ошибкой.
model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss='mse',       # mean squared error
              metrics=['mae'])  # mean absolute error

# Сконфигурируем модель для категориальной классификации.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.01),
              loss=tf.keras.losses.CategoricalCrossentropy(),
              metrics=[tf.keras.metrics.CategoricalAccuracy()])

Обучение на данных NumPy


Для небольших датасетов используйте помещающиеся в память массивы NumPy для обучения и оценки модели. Модель «обучается» на тренировочных данных, используя метод `fit`:

import numpy as np

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

model.fit(data, labels, epochs=10, batch_size=32)

tf.keras.Model.fit принимает три важных аргумента:

  • epochs: Обучение разбито на *эпохи*. Эпоха это одна итерация по всем входным данным (это делается небольшими партиями).
  • batch_size: При передаче данных NumPy, модель разбивает данные на меньшие блоки (batches) и итерирует по этим блокам во время обучения. Это число указывает размер каждого блока данных. Помните, что последний блок может быть меньшего размера если общее число записей не делится на размер партии.
  • validation_data: При прототипировании модели вы хотите легко отслеживать её производительность на валидационных данных. Передача с этим аргументом кортежа входных данных и меток позволяет модели отопражать значения функции потерь и метрики в режиме вывода для передаваемых данных в конце каждой эпохи.

Вот пример использования validation_data:

import numpy as np

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

val_data = np.random.random((100, 32))
val_labels = np.random.random((100, 10))

model.fit(data, labels, epochs=10, batch_size=32,
          validation_data=(val_data, val_labels))

Обучение с использованием наборов данных tf.data


Используйте Datasets API для масштабирования больших баз данных или обучения на нескольких устройствах. Передайте экземпляр `tf.data.Dataset` в метод fit:

# Создает экземпляр учебного датасета:
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)

model.fit(dataset, epochs=10)

Поскольку Dataset выдает данные пакетами, этот кусок кода не требует аргумента batch_size.

Датасеты могут быть также использованы для валидации:

dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)

val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))
val_dataset = val_dataset.batch(32)

model.fit(dataset, epochs=10,
          validation_data=val_dataset)

Оценка и предсказание


Методы tf.keras.Model.evaluate и tf.keras.Model.predict могут использовать данные NumPy и tf.data.Dataset.

Вот так можно оценить потери в режиме вывода и метрики для предоставленных данных:

# С массивом Numpy
data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

model.evaluate(data, labels, batch_size=32)

# С датасетом
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)

model.evaluate(dataset)

А вот как предсказать вывод последнего уровня в режиме вывода для предоставленных данных в виде массива NumPy:

Построение сложных моделей


The Functional API


Модель tf.keras.Sequential это простой стек слоев с помощью которого нельзя представить произвольную модель. Используйте Keras functional API для построения сложных топологий моделей, таких как:

  • Модели с несколькими входами,
  • Модели с несколькими выходами,
  • Модели с общими слоями (один и тот же слой вызывается несколько раз),
  • Модели с непоследовательными потоками данных (напр. остаточные связи).

Построение модели с functional API работает следующим образом:

  1. Экземпляр слоя является вызываемым и возвращает тензор.
  2. Входные и выходные тензоры используются для определения экземпляра tf.keras.Model
  3. Эта модель обучается точно так же как и `Sequential` модель.

Следующий пример использует functional API для построения простой, полносвязной сети:

inputs = tf.keras.Input(shape=(32,))  # Возвращает входной плейсхолдер

# Экземпляр слоя вызывается на тензор и возвращает тензор.
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)

Создайте экземпляр модели с данными входами и выходами.

model = tf.keras.Model(inputs=inputs, outputs=predictions)

# Шаг компиляции определяет конфигурацию обучения.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Обучение  за 5 эпох
model.fit(data, labels, batch_size=32, epochs=5)

Сабклассинг моделей


Создайте полностью настраиваемую модель с помощью сабклассинга tf.keras.Model и определения вашего собственного прямого распространения. Создайте слои в методе __init__ и установите их как атрибуты экземпляра класса. Определите прямое распространение в методе call.

Сабклассинг модели особенно полезен когда включен eager execution, поскольку он позволяет написать прямое распространение императивно.

Примечание: если вам нужно чтобы ваша модель всегда выполнялась императивно, вы можете установить dynamic=True когда вызываете конструктор super.
Ключевой момент: Используйте правильный API для работы. Хоть сабклассинг модели обеспечивает гибкость, за нее приходится платить большей сложностью и большими возможностями для пользовательских ошибок. Если это возможно выбирайте functional API.
Следующий пример показывает сабклассированную модель tf.keras.Model использующую пользовательское прямое распространение, которое не обязательно выполнять императивно:

class MyModel(tf.keras.Model):
  def __init__(self, num_classes=10):
    super(MyModel, self).__init__(name='my_model')
    self.num_classes = num_classes
    # Определим свои слои тут.
    self.dense_1 = layers.Dense(32, activation='relu')
    self.dense_2 = layers.Dense(num_classes, activation='sigmoid')

  def call(self, inputs):
    # Определим тут свое прямое распространение,
    # с использованием ранее определенных слоев (в `__init__`).
    x = self.dense_1(inputs)
    return self.dense_2(x)

Создайте экземпляр класса новой модели:

model = MyModel(num_classes=10)

# Шаг компиляции определяет конфигурацию обучения.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Обучение за 5 эпох.
model.fit(data, labels, batch_size=32, epochs=5)

Пользовательские слои


Создайте пользовательский слой сабклассингом tf.keras.layers.Layer и реализацией следующих методов:

  • __init__: Опционально определите подслои которые будут использоваться в этом слое.
  • * build: Создайте веса слоя. Добавьте веса при помощи метода add_weight
  • call: Определите прямое распространение.
  • Опционально, слой может быть сериализован реализацией метода get_config и метода класса from_config.

Ниже пример пользовательского слоя который осуществляет умножение матрицы (matmul) поданной на вход с матрицей ядра:

class MyLayer(layers.Layer):

  def __init__(self, output_dim, **kwargs):
    self.output_dim = output_dim
    super(MyLayer, self).__init__(**kwargs)

  def build(self, input_shape):
    # Создадим обучаемую весовую переменную для этого слоя.
    self.kernel = self.add_weight(name='kernel',
                                  shape=(input_shape[1], self.output_dim),
                                  initializer='uniform',
                                  trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.kernel)

  def get_config(self):
    base_config = super(MyLayer, self).get_config()
    base_config['output_dim'] = self.output_dim
    return base_config

  @classmethod
  def from_config(cls, config):
    return cls(**config)

Создайте модель с использованием вашего пользовательского слоя:

model = tf.keras.Sequential([
    MyLayer(10),
    layers.Activation('softmax')])

# Шаг компиляции определяет конфигурацию обучения
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Обучение за 5 эпох.
model.fit(data, labels, batch_size=32, epochs=5)

Колбеки


Колбек это объект переданный модели чтобы кастомизировать и расширить ее поведение во время обучения. Вы можете написать свой пользовательский колбек или использовать встроенный tf.keras.callbacks который включает в себя:

tf.keras.callbacks.ModelCheckpoint: Сохранение контрольных точек модели за регулярные интервалы.
tf.keras.callbacks.LearningRateScheduler: Динамичное изменение шага обучения.
tf.keras.callbacks.EarlyStopping: Остановка обучения в том случае когда результат при валидации перестает улучшаться.
tf.keras.callbacks.TensorBoard: Мониторинг поведения модели с помощью
TensorBoard

Для использования tf.keras.callbacks.Callback, передайте ее методу модели fit:

callbacks = [
  # Остановить обучение если `val_loss` перестанет улучшаться в течение 2 эпох
  tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
  # Записать логи TensorBoard в каталог `./logs` directory
  tf.keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,
          validation_data=(val_data, val_labels))

Сохранение и восстановление


Сохранение только значений весов


Сохраните и загрузите веса модели с помощью tf.keras.Model.save_weights:

model = tf.keras.Sequential([
layers.Dense(64, activation='relu', input_shape=(32,)),
layers.Dense(10, activation='softmax')])

model.compile(optimizer=tf.keras.optimizers.Adam(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Сохраним веса в файл TensorFlow Checkpoint
model.save_weights('./weights/my_model')

# Восстановим состояние модели
# для этого необходима модель с такой же архитектурой.     
model.load_weights('./weights/my_model')

По умолчанию веса модели сохраняются в формате TensorFlow checkpoint. Веса могут быть также сохранены в формате Keras HDF5 (значение по умолчанию для универсальной реализации Keras):

# Сохранение весов в файл HDF5
model.save_weights('my_model.h5', save_format='h5')

# Восстановление состояния модели
model.load_weights('my_model.h5')

Сохранение только конфигурации модели


Конфигурация модели может быть сохранена — это сериализует архитектуру модели без всяких весов. Сохраненная конфигурация может восстановить и инициализировать ту же модель, даже без кода определяющего исходную модель. Keras поддерживает форматы сериализации JSON и YAML:

# Сериализация модели в формат JSON
json_string = model.to_json()
json_string

import json
import pprint
pprint.pprint(json.loads(json_string))

Восстановление модели (заново инициализированной) из JSON:

fresh_model = tf.keras.models.model_from_json(json_string)

Сериализация модели в формат YAML требует установки `pyyaml` перед тем как импортировать TensorFlow:

yaml_string = model.to_yaml()
print(yaml_string)

Восстановление модели из YAML:

fresh_model = tf.keras.models.model_from_yaml(yaml_string)

Внимание: сабклассированные модели не сериализуемы, потому что их архитектура определяется кодом Python в теле метода `call`.

Сохранение всей модели в один файл


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

# Создадим простую модель
model = tf.keras.Sequential([
  layers.Dense(10, activation='softmax', input_shape=(32,)),
  layers.Dense(10, activation='softmax')
])
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(data, labels, batch_size=32, epochs=5)

# Сохраним всю модель в файл HDF5
model.save('my_model.h5')

# Пересоздадим в точности эту модель включая веса и оптимизатор.
model = tf.keras.models.load_model('my_model.h5')

Eager execution


Eager execution — это императивное программирование среда которая выполняет операции немедленно. Это не требуется для Keras, но поддерживается tf.keras и полезно для проверки вашей программы и отладки.

Все строящие модели API `tf.keras` совместимы eager execution. И хотя могут быть использованы `Sequential` и functional API, eager execution особенно полезно при сабклассировании модели и построении пользовательских слоев — эти API требуют от вас написание прямого распространения в виде кода (вместо API которые создают модели путем сборки существующих слоев).

Распределение


Множественные GPU


tf.keras модели можно запускать на множестве GPU с использованием tf.distribute.Strategy. Этот API обеспечивает распределенное обучение на нескольких GPU практически без изменений в существующем коде.

На данный момент, tf.distribute.MirroredStrategy единственная поддерживаемая стратегия распределения. MirroredStrategy выполняет репликацию в графах с
синхронным обучением используя all-reduce на одной машине. Для использования `distribute.Strategy`, вложите инсталляцию оптимизатора, конструкцию и компиляцию модели в `Strategy` `.scope()`, затем обучите модель.

Следующий пример распределяет tf.keras.Model между множеством GPU на одной машине.

Сперва определим модель внутри области распределенной стратегии:

strategy = tf.distribute.MirroredStrategy()

with strategy.scope():
  model = tf.keras.Sequential()
  model.add(layers.Dense(16, activation='relu', input_shape=(10,)))
  model.add(layers.Dense(1, activation='sigmoid'))

  optimizer = tf.keras.optimizers.SGD(0.2)

  model.compile(loss='binary_crossentropy', optimizer=optimizer)

model.summary()

Затем обучим модель на данных как обычно:

x = np.random.random((1024, 10))
y = np.random.randint(2, size=(1024, 1))
x = tf.cast(x, tf.float32)
dataset = tf.data.Dataset.from_tensor_slices((x, y))
dataset = dataset.shuffle(buffer_size=1024).batch(32)

model.fit(dataset, epochs=1)

После проверки перевод появится также на сайте Tensorflow.org. Если вы хотите поучаствовать в переводе документации сайта Tensorflow.org на русский, обращайтесь в личку или комментарии. Любые исправления и замечания приветствуются.

Комментарии (7)


  1. DesertFlow
    27.12.2019 04:35

    Вот умеют в Tensorflow навести сложность на ровном месте. Keras как раз хорош тем, что в свое время упростил начальные шаги в нейронных сетях. Набираете массив примеров, скармливаете его Keras, тот какое-то время думает, и готово. Потом можете показывать ему по одному или пачками примеры, а он будет выдавать для них предсказание сети.


    Но все это разбивается о реальность. Нейросети стали такими сложными, что через одну используют cuda компилируемые части и custom objects, которые не сохраняются в Keras моделях. И все, приходится тащить кучу python кода с каждой моделью, который с каждой новой версией Tensorflow превращается в тыкву (даже не обязательно мажорной, минорные версии тоже часто ломают обратную совместимость). И все это в основном заслуга Tensorflow. Сейчас большая часть нейросетей, написанных на Tensorflow даже несколько месяцев назад, уже не запускаются. В том числе с их официальных репозиториев. Киберпанк, который мы заслужили.


    1. cry_san
      27.12.2019 06:32

      PyTorch в этом плане лучше?


      1. DesertFlow
        27.12.2019 12:27
        +1

        Шило на мыло… Хотя они оба постепенно приходят к некоему общему виду. И это не считая внешних попыток объединения, вроде ONNX.


        Встроенный Keras в Tensorflow это очень правильное решение. Он там давно был, просто плохо поддерживался (версия отставала и не все было). А потом они вроде были наняли разработчика Keras на какое-то время, чтобы он активнее занимался интеграцией. Или из-за Eager поднапряглись. В любом случае, сейчас он там нормально функционален.


        Но все то наследие, которое было наработано на Tensorflow за последние несколько лет (предобученные нейросети, исходники научных работ и т.д.) уже через несколько месяцев будет безвозвратно потеряно.


        Там дело не только несовместимости версий самой Tensorflow. Оно тянет за собой другие проблемы — с драйверами для GPU, с компиляторами CUDA и т.д… Спустя некоторое время не удается запустить сохраненную нейросеть даже в виртуальном окружении в питоне. Приходится городить совсем изолированную систему, а данные в нейросеть и обратно гонять через какие-нибудь вебсокеты. Ужасный геморой. Надо было изначально делать что-то вроде аналога .exe, чтобы сохраненная нейросеть содержала в себе все что нужно и могла работать всегда в будущем. А там сейчас даже в одном Tensorflow зоопарк форматов сохранения. И они продолжают плодить новые.


      1. Celsius
        27.12.2019 18:30

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


    1. GokenTanmay
      27.12.2019 08:41

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

      Требования по версиям пакетов ни кто не отменял. И данная проблема у TF и её каверов есть с момента выхода «следующей» версии. И это правильно. И к этому как и ко всему хорошему быстро привыкаешь. И следует помнить что требования по версиям рекурсивны вплоть до уровня железа :)


      1. DesertFlow
        27.12.2019 12:13

        Ну да, только виртуальные окружения и спасают. Хотя там есть проблемы с разделяемыми ресурсами, такими как установленные версии Cuda. Которые тоже не очень продуманы, т.к. путь к текущей прописывается в PATH.


        Но я не об этом. А в том, что эти фреймворки возникли очень быстро на волне хайпа, поэтому о долговременной совместимости тогда никто не думал. Лепили на коленке побыстрее и все. А теперь мы расхлебываем последствия.


        1. iroln
          28.12.2019 01:46

          Если бы лепили долго, оно быть может и не взлетело бы, как какой-нибудь CNTK от MS. Давно не следил, но вроде его вообще забросили, а доля рынка у него стремится к нулю. Сейчас на коне PyTorch (переродившийся Torch) за счёт гибкости и динамических графов вычислений.


          Посмотрим, что будет дальше, всё же это молодая (в плане вычислительной возможности) область, прорыв в которой случился только благодаря современному железу, которая очень быстро развивается и в любой момент может повернуть совсем в другую сторону. То, что появились такие вещи как TF, Keras, PyTorch уже очень круто, а люди, которые над этим работают, заслуживают большого уважения.