Стройте умные модели с функциональным API кераса

Стройте умные модели с функциональным 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 или подклассом модели не является бинарным решением, которое ограничивает вас в одну категорию моделей. Все модели вkerasAPI может взаимодействовать друг с другом, будь они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)))

Первоначально опубликовано наTensorflowВеб -сайт, эта статья появляется здесь под новым заголовком и имеет лицензию в CC на 4.0. Образцы кода, разделенные по лицензии Apache 2.0.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE