Создание сложных моделей
Функциональное (Functional) API
tf.keras.Sequential модель - это просто стек слоев, которые не могут представлять произвольные модели. Для построения моделей с комплексной топологией необходимо использовать Keras функциональное (functional) API, таких моделей как:
- Модели с многими входами
- Модели с многими выходами
- Модели с общими слоями (тот же самый слой вызывается несколько раз)
- Модели с непоследовательными потоками данных (например, остаточные соединения)
Построение модели с функциональным API:
- Вызывается экземпляр слоя и возвращается тензор.
- Вводные тензоры и выводные тензоры использованы для определения tf.keras.Model экземпляра.
- Эта модель тренируется так же как Sequential модель.
Следующий пример использует функциональное API для построения простой, полностью связанной сети:
# Выполняет тензор заполнитель
inputs = keras.Input(shape=(32,))
# экземпляр слоя вызывается на тензоре и возвращает тензор
x = keras.layers.Dense(64, activation='relu')(inputs)
x = keras.layers.Dense(64, activation='relu')(x)
predictions = keras.layers.Dense(10, activation='softmax')(x)
# Воплощение модели с заданными вводами и выводами
model = keras.Model(inputs=inputs, outputs=predictions)
# Шаг компиляции определяет тренировочную конфигурацию
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
# Тренируем в течение 5 эпох
model.fit(data, labels, batch_size=32, epochs=5)
Создание подклассов модели
Построим полностью кастомизированную модель посредством создания подкласса tf.keras.Model и определения собственного прохода вперед. Создаем слои в init методе и устанавливаем их как атрибуты экземпляра класса. Определим следующий проход в методе вызова.
Создание подклассов модели особенно полезно при активированном нетерпеливом исполнении, ввиду того, что будущий проход может быть записан императивно.
Ключевой момент: используйте правильное API для задания. Хотя создание подклассов модели предлагает гибкость, это несет цену высокой сложности и возможности пользовательской ошибки. Если возможно используйте функциональное API.
Следующий пример показывает подкласс tf.keras.Model, используя пользовательский проход вперед:
class MyModel(keras.Model):
def init(self, num_classes=10):
super(MyModel, self).init(name='my_model')
self.num_classes = num_classes
# Определяем слои здесь
self.dense_1 = keras.layers.Dense(32, activation='relu')
self.dense_2 = keras.layers.Dense(num_classes, activation='sigmoid')
def call(self, inputs):
# Определяем будущий проход здесь,
# используя слои определенные ранее(в __init__).
x = self.dense_1(inputs)
return self.dense_2(x)
def compute_output_shape(self, input_shape):
# Необходимо переопределить эту функцию
# чтобы использовать модель с подклассами
# как часть модели функционального стиля.
# В ином случае этот метод опционален.
shape = tf.TensorShape(input_shape).as_list()
shape[-1] = self.num_classes
return tf.TensorShape(shape)
# Воплощаем модель с подклассами.
model = MyModel(num_classes=10)
# Шаг компиляции определяет тренировочную конфигурацию.
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
# Тренируем в течение 5 эпох.
model.fit(data, labels, batch_size=32, epochs=5)
Кастомные слои
Создаем кастомный слой созданием подкласса tf.keras.layers.Layer и имплементируя следующие методы:
- build: Создает веса слоя. Добавляет веса с add_weight методом.
- call: Задает будущий проход.
- compute_output_shape: Определяет как вычислить выводную форму слоя при данной входной форме.
Опционально слой может быть сериализован имплеметацией get_config метода и from_config классового метода.
Вот пример кастомного слоя, который имплементирует matmul входа с матрицей ядра:
class MyLayer(keras.layers.Layer):
def init(self, output_dim, **kwargs):
self.output_dim = output_dim
super(MyLayer, self).init(**kwargs)
def build(self, input_shape):
shape = tf.TensorShape((input_shape[1], self.output_dim))
# Создаем переменную тренируемого веса для этого слоя.
self.kernel = self.add_weight(name='kernel',
shape=shape,
initializer='uniform',
trainable=True)
super(MyLayer, self).build(input_shape)
def call(self, inputs):
return tf.matmul(inputs, self.kernel)
def compute_output_shape(self, input_shape):
shape = tf.TensorShape(input_shape).as_list()
shape[-1] = self.output_dim
return tf.TensorShape(shape)
def get_config(self):
base_config = super(MyLayer, self).get_config()
base_config['output_dim'] = self.output_dim
@classmethod
def from_config(cls, config):
return cls(**config)
# Создание слоя с использованием кастомного слоя
model = keras.Sequential([MyLayer(10),
keras.layers.Activation('softmax')])
# Шаг компиляции определяет тренировочную конфигурацию
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
# Тренируем в течение 5 эпох.
model.fit(data, targets, batch_size=32, epochs=5)
Колбеки (callbacks)
Колбек (callback) - это объект переданный модели для кастомизирования и расширения ее модели в ходе тренировки. Можно писать собственный кастомный колбек или использовать встроенный 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 эпох
keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
# Записываем логи TensorBoard в ./logs директорию
keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,
validation_data=(val_data, val_targets))
Сохранение и восстановление
Только веса
Сохраняем и загружаем веса модели, используя tf.keras.Model.save_weights:
# Сохраняем веса в TensorFlow Checkpoint файл
model.save_weights('./my_model')
# Восстанавливаем состояние модели,
# это требует модель с точно такой же архитектурой.
model.load_weights('my_model')
По умолчанию это сохраняет веса модели в файл формата контрольной точки TensorFlow. Веса могут быть также сохранены в 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()
# Пересоздание модели (со свежей инициализацией)
fresh_model = keras.models.from_json(json_string)
# Сериализация модели в YAML формате
yaml_string = model.to_yaml()
# Пересоздание модели
fresh_model = keras.models.from_yaml(yaml_string)
Предостережение: модели с подклассами не сериализуемы, потому что их архитектура определена в Python коде в теле call метода.
Сохранение и восстановление целой модели
Целая модель может быть сохранена в файл, который содержит значения весов, конфигурацию модели, и конфигурацию оптимизатора. Это позволяет создавать контрольную точку модели и возобновлять тренировку позже - с того же самого места, где она была остановлена - без необходимости доступа к изначальному коду.
# Создаем обычную модель
model = keras.Sequential([
keras.layers.Dense(10, activation='softmax', input_shape=(32,)),
keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(data, targets, batch_size=32, epochs=5)
# Сохраняем целую модель в HDF5 файле
model.save('my_model.h5')
# Пересоздаем ту же самую модель, включая веса и оптимизатор.
model = keras.models.load_model('my_model.h5')
Нетерпеливое исполнение
Нетерпеливое исполнение (eager execution) - это императивное программное окружение, которое применяет операции немедленно. Это не требуется для Keras, но поддерживается tf.keras и полезно для проверки программ и отладки.
Все tf.keras API для построения моделей совместимы с нетерпеливым исполнением. И, хотя Sequential и фукнциональное API могут быть использованы, нетерпеливое исполнение имеет особые преимущества при создании подклассов моделей и построении кастомных слоев.
Распределение
Estimators (оценщики)
Estimators API используется для тренировки модели в распределенных окружениях. Это целевые методы использования, такие как распределенная тренировка на крупных наборах данных, которые могут экспортировать модель в производственную среду.
tf.keras.Model может быть тренирована с tf.estimator API преобразованием модели в tf.estimator.Estimator объект с tf.keras.estimator.model_to_estimator.
model = keras.Sequential([layers.Dense(10,activation='softmax'),
layers.Dense(10,activation='softmax')])
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
estimator = keras.estimator.model_to_estimator(model)
Исполнение на многих GPU
tf.keras модели могут выполняться на нескольких GPU, используя tf.contrib.distribute.DistributionStrategy. Это API предоставляет распределенную тренировку на нескольких GPU почти без изменений в существующем коде.
На данный момент tf.contrib.distribute.MirroredStrategy единственная поддерживаемая стратегия распределения. MirroredStrategy выполняет внутри-графовую репликацию с синхронной тренировкой, используя all-reduce на единственной машине. Чтобы использовать DistributionStrategy с Keras, преобразуем tf.keras.Model в tf.estimator.Estimator с tf.keras.estimator.model_to_estimator, затем тренируем как estimator.
Следующий пример распределяет tf.keras.Model по нескольким GPU на единственной машине.
Во-первых, определим простую модель:
model = keras.Sequential()
model.add(keras.layers.Dense(16, activation='relu', input_shape=(10,)))
model.add(keras.layers.Dense(1, activation='sigmoid'))
optimizer = tf.train.GradientDescentOptimizer(0.2)
model.compile(loss='binary_crossentropy', optimizer=optimizer)
model.summary()
Определим входной пайплайн. input_fn возвращает tf.data.Dataset объект, используемый чтобы распределить данные по многим устройствам - с каждым устройством, обрабатывающим часть входного пакета.
def input_fn():
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.repeat(10)
dataset = dataset.batch(32)
return dataset
Затем создаем tf.estimator.RunConfig и устанавливаем train_distribute аргумент к tf.contrib.distribute.MirroredStrategy экземпляру. При создании MirroredStrategy можно определить лист устройств или установить num_gpus аргумент. По умолчанию используются все доступные GPU:
strategy = tf.contrib.distribute.MirroredStrategy()
config = tf.estimator.RunConfig(train_distribute=strategy)
Преобразуем Keras модель в tf.estimator.Estimator экземпляр:
keras_estimator = keras.estimator.model_to_estimator(
keras_model=model,
config=config,
model_dir='/tmp/model_dir')
Наконец, тренируем Estimator экземпляр предоставляя input_fn и steps аргументы:
keras_estimator.train(input_fn=input_fn, steps=10)