
Стройте умные модели с функциональным API кераса
12 июня 2025 г.Обзор контента
- Общие слои
- Извлекать и повторно использовать узлы на графике слоев
- Расширить API, используя пользовательские слои
- Когда использовать функциональный API
- Функциональные сильные стороны API
- Функциональные слабости API
- Микса и матча стили API
Общие слои
Еще одно хорошее использование для функционального API - это модели, которые используютОбщие слоиПолем Общие слои-это экземпляры слоев, которые повторно используются несколько раз в одной и той же модели-они изучают функции, которые соответствуют нескольким путям на графике слоев.
Общие слои часто используются для кодирования входов из подобных пространств (скажем, два разных часа текста, которые имеют сходной словарный запас). Они обеспечивают обмен информацией по этим различным входным данным, и они позволяют обучать такую модель меньше данных. Если данное слово видно на одном из входов, это принесет пользу обработке всех входов, которые проходят через общий слой.
Чтобы поделиться слоем в функциональном API, вызовите один и тот же экземпляр слоя несколько раз. Например, вотEmbedding
слой, разделенный на два разных входа текста:
# Embedding for 1000 unique words mapped to 128-dimensional vectors
shared_embedding = layers.Embedding(1000, 128)
# Variable-length sequence of integers
text_input_a = keras.Input(shape=(None,), dtype="int32")
# Variable-length sequence of integers
text_input_b = keras.Input(shape=(None,), dtype="int32")
# Reuse the same layer to encode both inputs
encoded_input_a = shared_embedding(text_input_a)
encoded_input_b = shared_embedding(text_input_b)
Извлекать и повторно использовать узлы на графике слоев
Поскольку график слоев, которые вы манипулируете, является статической структурой данных, к нему можно получить доступ и осмотреть. И именно так вы можете построить функциональные модели в качестве изображений.
Это также означает, что вы можете получить доступ к активациям промежуточных слоев («узлов» на графике) и повторно использовать их в другом месте, что очень полезно для чего -то вроде извлечения функций.
Давайте посмотрим на пример. Это модель VGG19 с весами, предварительно проведенными на ImageNet:
vgg19 = keras.applications.VGG19()
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels.h5
574710816/574710816 [==============================] - 4s 0us/step
И это промежуточные активации модели, полученные путем запроса структуры графиков данных:
features_list = [layer.output for layer in vgg19.layers]
Используйте эти функции для создания новой модели эксплуатации функций, которая возвращает значения активации промежуточного уровня:
feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)
img = np.random.random((1, 224, 224, 3)).astype("float32")
extracted_features = feat_extraction_model(img)
Это пригодится для таких задач, какПеренос нервного стиля, среди прочего.
Расширить API, используя пользовательские слои
keras
Включает в себя широкий спектр встроенных слоев, например:
- Сверточные слои:
Conv1D
ВConv2D
ВConv3D
ВConv2DTranspose
- Объединение слоев:
MaxPooling1D
ВMaxPooling2D
ВMaxPooling3D
ВAveragePooling1D
- Слои RNN:
GRU
ВLSTM
ВConvLSTM2D
BatchNormalization
ВDropout
ВEmbedding
, и т. д.
Но если вы не найдете то, что вам нужно, легко расширить API, создав свои собственные слои. Все слои подклассLayer
класс и реализация:
call
Метод, который указывает вычисление, сделанное слоем.build
Метод, который создает веса слоя (это всего лишь соглашение о стиле, поскольку вы можете создавать веса в__init__
, также).
Чтобы узнать больше о создании слоев с нуля, прочитайтеПользовательские слои и моделигид.
Ниже приводится основная реализацияkeras.layers.Dense
:
class CustomDense(layers.Layer):
def __init__(self, units=32):
super().__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(
shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True,
)
self.b = self.add_weight(
shape=(self.units,), initializer="random_normal", trainable=True
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)
model = keras.Model(inputs, outputs)
Для поддержки сериализации в вашем пользовательском слое определитеget_config
Метод, который возвращает аргументы конструктора экземпляра слоя:
@keras.saving.register_keras_serializable()
class CustomDense(layers.Layer):
def __init__(self, units=32):
super().__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(
shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True,
)
self.b = self.add_weight(
shape=(self.units,), initializer="random_normal", trainable=True
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
def get_config(self):
return {"units": self.units}
inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)
model = keras.Model(inputs, outputs)
config = model.get_config()
new_model = keras.Model.from_config(config)
Необязательно, реализовать метод классаfrom_config(cls, config)
который используется при воссоздании экземпляра слоя, учитывая его словарь конфигурации. Реализация по умолчаниюfrom_config
является:
def from_config(cls, config):
return cls(**config)
Когда использовать функциональный API
Если вы используете функциональный API Keras для создания новой модели или просто подклассModel
класс напрямую? В целом, функциональный API более высокий уровень, проще и безопаснее, и имеет ряд функций, которые модели подклассов не поддерживают.
Тем не менее, подклассирование модели обеспечивает большую гибкость при создании моделей, которые нелегко выражать в качестве направленных ациклических графиков слоев. Например, вы не могли бы реализовать Tree-RNN с функциональным API и придется подклассModel
напрямую.
Для углубленного взгляда на различия между функциональным API и модельным подклассом, чтениеЧто такое символические и императивные API в Tensorflow 2.0?Полем
Функциональные сильные стороны API:
Следующие свойства также соответствуют последовательным моделям (которые также являются структурами данных), но не являются истинными для подклассовых моделей (которые являются байтоном Python, а не структурами данных).
Менее многословный
Нетsuper().__init__(...)
, нетdef call(self, ...):
, и т. д.
Сравнивать:
inputs = keras.Input(shape=(32,))
x = layers.Dense(64, activation='relu')(inputs)
outputs = layers.Dense(10)(x)
С подклассной версией:
class MLP(keras.Model):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.dense_1 = layers.Dense(64, activation='relu')
self.dense_2 = layers.Dense(10)
def call(self, inputs):
x = self.dense_1(inputs)
return self.dense_2(x)
# Instantiate the model.
mlp = MLP()
# Necessary to create the model's state.
# The model doesn't have a state until it's called at least once.
_ = mlp(tf.zeros((1, 32)))
Проверка модели при определении графика подключения
В функциональном API входная спецификация (Shape и DTYPE) создается заранее (используяInput
) Каждый раз, когда вы называете слой, слой проверяет, что спецификация, передаваемая ему, соответствует его предположениям, и он поднимает полезное сообщение об ошибке, если нет.
Это гарантирует, что любая модель, которую вы можете построить с помощью функционального API, будет работать. Вся отладка-кроме отладки, связанной с конвергенцией-происходит статически во время конструкции модели, а не во время выполнения. Это похоже на проверку типов в компиляторе.
Функциональная модель обезжирима и осмотрина
Вы можете построить модель как график, и вы можете легко получить доступ к промежуточным узлам на этом графике. Например, для извлечения и повторного использования активаций промежуточных слоев (как видно в предыдущем примере):
features_list = [layer.output for layer in vgg19.layers]
feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)
Функциональная модель может быть сериализована или клонирована
Поскольку функциональная модель является структурой данных, а не частью кода, она безопасно сериализуется и может быть сохранена в виде единого файла, который позволяет воссоздать точно такую же модель, не имея доступа к любому из исходного кода. УвидетьРуководство по сериализации и сохранениюПолем
Для сериализации подклассной модели необходимо указатьget_config()
иfrom_config()
Метод на уровне модели.
Функциональная слабость API:
Он не поддерживает динамические архитектуры
Функциональный API рассматривает модели как даги слоев. Это верно для большинства архитектур глубокого обучения, но не все - например, рекурсивные сети или RNN Tree не следуют этому предположению и не могут быть реализованы в функциональном API.
Микса и матча стили API
Выбор между функциональным API или подклассом модели не является бинарным решением, которое ограничивает вас в одну категорию моделей. Все модели вkeras
API может взаимодействовать друг с другом, будь ониSequential
модели, функциональные модели или подклассные модели, которые написаны с нуля.
Вы всегда можете использовать функциональную модель илиSequential
модель как часть подклассной модели или слоя:
units = 32
timesteps = 10
input_dim = 5
# Define a Functional model
inputs = keras.Input((None, units))
x = layers.GlobalAveragePooling1D()(inputs)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)
@keras.saving.register_keras_serializable()
class CustomRNN(layers.Layer):
def __init__(self):
super().__init__()
self.units = units
self.projection_1 = layers.Dense(units=units, activation="tanh")
self.projection_2 = layers.Dense(units=units, activation="tanh")
# Our previously-defined Functional model
self.classifier = model
def call(self, inputs):
outputs = []
state = tf.zeros(shape=(inputs.shape[0], self.units))
for t in range(inputs.shape[1]):
x = inputs[:, t, :]
h = self.projection_1(x)
y = h + self.projection_2(state)
state = y
outputs.append(y)
features = tf.stack(outputs, axis=1)
print(features.shape)
return self.classifier(features)
rnn_model = CustomRNN()
_ = rnn_model(tf.zeros((1, timesteps, input_dim)))
(1, 10, 32)
Вы можете использовать любой подклассный слой или модель в функциональном API, если он реализуетcall
Метод, который следует за одним из следующих шаблонов:
call(self, inputs, **kwargs)
-- Гдеinputs
является тензором или вложенной структурой тензоров (например, список тензоров), и где**kwargs
являются незенсорными аргументами (не внедорожными).call(self, inputs, training=None, **kwargs)
-- Гдеtraining
является логическим, указывающим, должен ли слой вести себя в режиме обучения и режиме вывода.call(self, inputs, mask=None, **kwargs)
-- Гдеmask
является тензором логической маски (например, полезно для RNN).call(self, inputs, training=None, mask=None, **kwargs)
-Конечно, вы можете одновременно иметь как маскирование, так и поведение, специфичное для обучения.
Кроме того, если вы реализуетеget_config
Метод на вашем пользовательском слое или модели, функциональные модели, которые вы создаете, все еще будут последовательными и клонируемыми.
Вот быстрый пример пользовательского RNN, написанного с нуля, используемого в функциональной модели:
units = 32
timesteps = 10
input_dim = 5
batch_size = 16
@keras.saving.register_keras_serializable()
class CustomRNN(layers.Layer):
def __init__(self):
super().__init__()
self.units = units
self.projection_1 = layers.Dense(units=units, activation="tanh")
self.projection_2 = layers.Dense(units=units, activation="tanh")
self.classifier = layers.Dense(1)
def call(self, inputs):
outputs = []
state = tf.zeros(shape=(inputs.shape[0], self.units))
for t in range(inputs.shape[1]):
x = inputs[:, t, :]
h = self.projection_1(x)
y = h + self.projection_2(state)
state = y
outputs.append(y)
features = tf.stack(outputs, axis=1)
return self.classifier(features)
# Note that you specify a static batch size for the inputs with the `batch_shape`
# arg, because the inner computation of `CustomRNN` requires a static batch size
# (when you create the `state` zeros tensor).
inputs = keras.Input(batch_shape=(batch_size, timesteps, input_dim))
x = layers.Conv1D(32, 3)(inputs)
outputs = CustomRNN()(x)
model = keras.Model(inputs, outputs)
rnn_model = CustomRNN()
_ = rnn_model(tf.zeros((1, 10, 5)))
Первоначально опубликовано на
Оригинал