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

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

Настраивать

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

Когда использовать последовательную модель

Sequential модель подходит для простого стека слоев , где каждый слой имеет ровно один тензор входной и один выходной тензор.

Схематически следующая Sequential модель:

# Define Sequential model with 3 layers
model = keras.Sequential(
    [
        layers.Dense(2, activation="relu", name="layer1"),
        layers.Dense(3, activation="relu", name="layer2"),
        layers.Dense(4, name="layer3"),
    ]
)
# Call model on a test input
x = tf.ones((3, 3))
y = model(x)

эквивалентна этой функции:

# Create 3 layers
layer1 = layers.Dense(2, activation="relu", name="layer1")
layer2 = layers.Dense(3, activation="relu", name="layer2")
layer3 = layers.Dense(4, name="layer3")

# Call layers on a test input
x = tf.ones((3, 3))
y = layer3(layer2(layer1(x)))

Последовательная модель не подходит , когда:

  • Ваша модель имеет несколько входов или несколько выходов
  • Любой из ваших слоев имеет несколько входов или несколько выходов
  • Вам нужно сделать общий доступ к слою
  • Вам нужна нелинейная топология (например, остаточное соединение, многоветвевая модель)

Создание последовательной модели

Вы можете создать модель Sequential, передав список слоев конструктору Sequential:

model = keras.Sequential(
    [
        layers.Dense(2, activation="relu"),
        layers.Dense(3, activation="relu"),
        layers.Dense(4),
    ]
)

Ее слои доступны через layers атрибутов:

model.layers
[<keras.layers.core.Dense at 0x7fdc784478d0>,
 <keras.layers.core.Dense at 0x7fdbbc3c4650>,
 <keras.layers.core.Dense at 0x7fdbbc3c4a10>]

Вы также можете создать Последовательная модель пошагово с помощью add() метод:

model = keras.Sequential()
model.add(layers.Dense(2, activation="relu"))
model.add(layers.Dense(3, activation="relu"))
model.add(layers.Dense(4))

Обратите внимание , что есть также соответствующий pop() метод для удаления слоев: Последовательная модель ведет себя очень похож на список слоев.

model.pop()
print(len(model.layers))  # 2
2

Также отметим , что последовательный конструктор принимает name аргумента, так же как и любой слой или модели в Keras. Это полезно для аннотирования графов TensorBoard семантически значимыми именами.

model = keras.Sequential(name="my_sequential")
model.add(layers.Dense(2, activation="relu", name="layer1"))
model.add(layers.Dense(3, activation="relu", name="layer2"))
model.add(layers.Dense(4, name="layer3"))

Предварительное указание входной формы

Как правило, все слои в Keras должны знать форму своих входных данных, чтобы иметь возможность создавать свои веса. Итак, когда вы создаете такой слой, изначально он не имеет весов:

layer = layers.Dense(3)
layer.weights  # Empty
[]

Он создает свои веса при первом обращении к входным данным, поскольку форма весов зависит от формы входных данных:

# Call layer on a test input
x = tf.ones((1, 4))
y = layer(x)
layer.weights  # Now it has weights, of shape (4, 3) and (3,)
[<tf.Variable 'dense_6/kernel:0' shape=(4, 3) dtype=float32, numpy=
 array([[ 0.5319189 , -0.8767905 , -0.63919735],
        [-0.6276014 ,  0.1689707 , -0.57695866],
        [ 0.6710613 ,  0.5354214 , -0.00893992],
        [ 0.15670097, -0.15280598,  0.8865864 ]], dtype=float32)>,
 <tf.Variable 'dense_6/bias:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>]

Естественно, это относится и к моделям Sequential. При создании экземпляра модели последовательной без формы ввода, он не «построен»: он не имеет веса (и вызов model.weights приводит к ошибке с указанием только это). Веса создаются, когда модель впервые видит некоторые входные данные:

model = keras.Sequential(
    [
        layers.Dense(2, activation="relu"),
        layers.Dense(3, activation="relu"),
        layers.Dense(4),
    ]
)  # No weights at this stage!

# At this point, you can't do this:
# model.weights

# You also can't do this:
# model.summary()

# Call the model on a test input
x = tf.ones((1, 4))
y = model(x)
print("Number of weights after calling the model:", len(model.weights))  # 6
Number of weights after calling the model: 6

После того, как модель «построена», вы можете вызвать его summary() метод , чтобы отобразить его содержимое:

model.summary()
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_7 (Dense)              (1, 2)                    10        
_________________________________________________________________
dense_8 (Dense)              (1, 3)                    9         
_________________________________________________________________
dense_9 (Dense)              (1, 4)                    16        
=================================================================
Total params: 35
Trainable params: 35
Non-trainable params: 0
_________________________________________________________________

Однако при поэтапном построении модели Sequential может быть очень полезно иметь возможность отображать сводку модели на данный момент, включая текущую выходную форму. В этом случае, вы должны начать свою модель, передавая Input объект вашей модели, так что он знает , что его входную форму с самого начала:

model = keras.Sequential()
model.add(keras.Input(shape=(4,)))
model.add(layers.Dense(2, activation="relu"))

model.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_10 (Dense)             (None, 2)                 10        
=================================================================
Total params: 10
Trainable params: 10
Non-trainable params: 0
_________________________________________________________________

Обратите внимание , что Input объект не отображается как часть model.layers , так как это не слой:

model.layers
[<keras.layers.core.Dense at 0x7fdbbc37c390>]

Простая альтернатива просто передать input_shape аргумент первого слоя:

model = keras.Sequential()
model.add(layers.Dense(2, activation="relu", input_shape=(4,)))

model.summary()
Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_11 (Dense)             (None, 2)                 10        
=================================================================
Total params: 10
Trainable params: 10
Non-trainable params: 0
_________________________________________________________________

Модели, построенные с такой предопределенной входной формой, всегда имеют вес (даже до того, как будут показаны какие-либо данные) и всегда имеют определенную выходную форму.

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

Обычный рабочий процесс отладки: add() + summary()

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

model = keras.Sequential()
model.add(keras.Input(shape=(250, 250, 3)))  # 250x250 RGB images
model.add(layers.Conv2D(32, 5, strides=2, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(3))

# Can you guess what the current output shape is at this point? Probably not.
# Let's just print it:
model.summary()

# The answer was: (40, 40, 32), so we can keep downsampling...

model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(3))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(2))

# And now?
model.summary()

# Now that we have 4x4 feature maps, time to apply global max pooling.
model.add(layers.GlobalMaxPooling2D())

# Finally, we add a classification layer.
model.add(layers.Dense(10))
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 123, 123, 32)      2432      
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 121, 121, 32)      9248      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 40, 40, 32)        0         
=================================================================
Total params: 11,680
Trainable params: 11,680
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 123, 123, 32)      2432      
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 121, 121, 32)      9248      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 40, 40, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 38, 38, 32)        9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 36, 36, 32)        9248      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 12, 12, 32)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 10, 10, 32)        9248      
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 8, 8, 32)          9248      
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 4, 4, 32)          0         
=================================================================
Total params: 48,672
Trainable params: 48,672
Non-trainable params: 0
_________________________________________________________________

Очень практично, правда?

Что делать, если у вас есть модель

Когда архитектура вашей модели будет готова, вы захотите:

Извлечение признаков с последовательной моделью

После того, как последовательная модель была построена, она ведет себя как функциональная модель API . Это означает , что каждый слой имеет input и output атрибут. Эти атрибуты можно использовать для выполнения аккуратных действий, например, для быстрого создания модели, которая извлекает выходные данные всех промежуточных слоев в последовательной модели:

initial_model = keras.Sequential(
    [
        keras.Input(shape=(250, 250, 3)),
        layers.Conv2D(32, 5, strides=2, activation="relu"),
        layers.Conv2D(32, 3, activation="relu"),
        layers.Conv2D(32, 3, activation="relu"),
    ]
)
feature_extractor = keras.Model(
    inputs=initial_model.inputs,
    outputs=[layer.output for layer in initial_model.layers],
)

# Call feature extractor on test input.
x = tf.ones((1, 250, 250, 3))
features = feature_extractor(x)

Вот аналогичный пример, в котором объекты извлекаются только из одного слоя:

initial_model = keras.Sequential(
    [
        keras.Input(shape=(250, 250, 3)),
        layers.Conv2D(32, 5, strides=2, activation="relu"),
        layers.Conv2D(32, 3, activation="relu", name="my_intermediate_layer"),
        layers.Conv2D(32, 3, activation="relu"),
    ]
)
feature_extractor = keras.Model(
    inputs=initial_model.inputs,
    outputs=initial_model.get_layer(name="my_intermediate_layer").output,
)
# Call feature extractor on test input.
x = tf.ones((1, 250, 250, 3))
features = feature_extractor(x)

Перенос обучения с последовательной моделью

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

Вот два распространенных плана трансферного обучения с использованием последовательных моделей.

Во-первых, предположим, что у вас есть последовательная модель, и вы хотите заморозить все слои, кроме последнего. В этом случае, вы бы просто итерация над model.layers и множество layer.trainable = False на каждом слое, за исключением последнего. Нравится:

model = keras.Sequential([
    keras.Input(shape=(784)),
    layers.Dense(32, activation='relu'),
    layers.Dense(32, activation='relu'),
    layers.Dense(32, activation='relu'),
    layers.Dense(10),
])

# Presumably you would want to first load pre-trained weights.
model.load_weights(...)

# Freeze all layers except the last one.
for layer in model.layers[:-1]:
  layer.trainable = False

# Recompile and train (this will only update the weights of the last layer).
model.compile(...)
model.fit(...)

Другим распространенным планом является использование модели Sequential для объединения предварительно обученной модели и некоторых недавно инициализированных слоев классификации. Нравится:

# Load a convolutional base with pre-trained weights
base_model = keras.applications.Xception(
    weights='imagenet',
    include_top=False,
    pooling='avg')

# Freeze the base model
base_model.trainable = False

# Use a Sequential model to add a trainable classifier on top
model = keras.Sequential([
    base_model,
    layers.Dense(1000),
])

# Compile & train
model.compile(...)
model.fit(...)

Если вы занимаетесь переносом обучения, вы, вероятно, обнаружите, что часто используете эти два паттерна.

Это все, что вам нужно знать о моделях Sequential!

Чтобы узнать больше о построении моделей в Keras, см.: