Помогают защитить Большой Барьерный Риф с TensorFlow на Kaggle Присоединяйтесь вызов

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

Посмотреть на 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)>]

Естественно, это относится и к последовательным моделям. При создании экземпляра модели последовательной без формы ввода, он не «построен»: он не имеет веса (и вызов 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
_________________________________________________________________

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

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

# 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(...)

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

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

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