Посмотреть на TensorFlow.org | Запускаем в Google Colab | Посмотреть исходный код на GitHub | Скачать блокнот |
Настраивать
import numpy as np
import tensorflow as tf
from tensorflow import keras
Введение
Обучение Передача состоит из взятия функции выучили на одной проблеме, а также использование их на новой, аналогичной проблемой. Например, особенности модели, которая научилась определять енотов, могут быть полезны для запуска модели, предназначенной для идентификации тануки.
Переносное обучение обычно выполняется для задач, в которых в наборе данных слишком мало данных для обучения полномасштабной модели с нуля.
Наиболее распространенное воплощение трансферного обучения в контексте глубокого обучения - это следующий рабочий процесс:
- Возьмите слои из ранее обученной модели.
- Заморозьте их, чтобы не уничтожить любую информацию, которую они содержат, во время будущих тренировочных раундов.
- Добавьте несколько новых обучаемых слоев поверх замороженных слоев. Они научатся превращать старые функции в прогнозы для нового набора данных.
- Обучите новые слои в вашем наборе данных.
И последнее, необязательный шаг, это тонкая настройка, которая состоит из размораживания всей модели , полученной выше (или части его), и повторно обучение его на новых данных с очень низкой скоростью обучения. Это потенциально может привести к значительным улучшениям за счет постепенной адаптации предварительно обученных функций к новым данным.
Во- первых, мы будем идти над Keras trainable
API подробно, который лежит в основе большинства передачи обучения и тонкой настройки рабочих процессов.
Затем мы продемонстрируем типичный рабочий процесс, взяв модель, предварительно обученную на наборе данных ImageNet, и повторно обучим ее на наборе данных классификации Kaggle «кошки против собак».
Это взято из глубокого обучения с Python и 2016 блоге «строит мощные модели классификации изображений с использованием очень мало данных» .
Замораживание слоев: понимание trainable
атрибут
Слои и модели имеют три атрибута веса:
-
weights
список всех весов переменных слоя. -
trainable_weights
список тех , которые предназначены для обновления ( с помощью градиентного спуска) , чтобы минимизировать потери во время тренировки. -
non_trainable_weights
список тех, которые не предназначены для обучения. Обычно они обновляются моделью во время прямого прохода.
Пример: Dense
слой имеет 2 обучаемых весы (ядро и смещение)
layer = keras.layers.Dense(3)
layer.build((None, 4)) # Create the weights
print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2 trainable_weights: 2 non_trainable_weights: 0
В общем, все веса - это веса, которые можно тренировать. Единственный встроенный слой , который имеет не-обучаемый вес является BatchNormalization
слой. Он использует необучаемые веса, чтобы отслеживать среднее значение и дисперсию входных данных во время тренировки. Чтобы узнать , как использовать не обучаемые веса в ваших собственных слоях, увидеть руководство по написанию новых слоев с нуля .
Пример: BatchNormalization
слой имеет 2 обучаемых веса и 2 без обучаемых весов
layer = keras.layers.BatchNormalization()
layer.build((None, 4)) # Create the weights
print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 4 trainable_weights: 2 non_trainable_weights: 2
Слои и модель также имеет логический атрибут trainable
. Его значение можно изменить. Установка layer.trainable
для False
ходов все взвешивает слой с обучаемого , не включенным в обучаемом. Это называется «замораживание» слой: состояние замерзшего слоя не будет обновляться в процессе обучения (либо при обучении с fit()
или при обучении с любой пользовательской петлей , которая опирается на trainable_weights
применять градиентные обновления).
Пример: установка trainable
к False
layer = keras.layers.Dense(3)
layer.build((None, 4)) # Create the weights
layer.trainable = False # Freeze the layer
print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2 trainable_weights: 0 non_trainable_weights: 2
Когда тренируемый вес становится недоступным для тренировки, его значение больше не обновляется во время тренировки.
# Make a model with 2 layers
layer1 = keras.layers.Dense(3, activation="relu")
layer2 = keras.layers.Dense(3, activation="sigmoid")
model = keras.Sequential([keras.Input(shape=(3,)), layer1, layer2])
# Freeze the first layer
layer1.trainable = False
# Keep a copy of the weights of layer1 for later reference
initial_layer1_weights_values = layer1.get_weights()
# Train the model
model.compile(optimizer="adam", loss="mse")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))
# Check that the weights of layer1 have not changed during training
final_layer1_weights_values = layer1.get_weights()
np.testing.assert_allclose(
initial_layer1_weights_values[0], final_layer1_weights_values[0]
)
np.testing.assert_allclose(
initial_layer1_weights_values[1], final_layer1_weights_values[1]
)
1/1 [==============================] - 1s 640ms/step - loss: 0.0945
Не следует путать layer.trainable
атрибут с аргументом training
в layer.__call__()
(который контролирует , должен ли слой запустить его вперед проход в режиме вывода или в режиме обучения). Для получения дополнительной информации см Keras FAQ .
Рекурсивная установка trainable
атрибута
Если вы установите trainable = False
на модели или на любом слое , который имеет подуровни, все дети слои становятся не обучаемым , а также.
Пример:
inner_model = keras.Sequential(
[
keras.Input(shape=(3,)),
keras.layers.Dense(3, activation="relu"),
keras.layers.Dense(3, activation="relu"),
]
)
model = keras.Sequential(
[keras.Input(shape=(3,)), inner_model, keras.layers.Dense(3, activation="sigmoid"),]
)
model.trainable = False # Freeze the outer model
assert inner_model.trainable == False # All layers in `model` are now frozen
assert inner_model.layers[0].trainable == False # `trainable` is propagated recursively
Типичный рабочий процесс передачи-обучения
Это подводит нас к тому, как в Keras можно реализовать типичный рабочий процесс трансферного обучения:
- Создайте экземпляр базовой модели и загрузите в нее предварительно обученные веса.
- Закрепить все слои в базовой модели, установив
trainable = False
. - Создайте новую модель поверх выходных данных одного (или нескольких) слоев базовой модели.
- Обучите новую модель на новом наборе данных.
Обратите внимание, что альтернативным, более легким рабочим процессом также может быть:
- Создайте экземпляр базовой модели и загрузите в нее предварительно обученные веса.
- Пропустите через него новый набор данных и запишите выходные данные одного (или нескольких) слоев базовой модели. Это называется выделением признаков.
- Используйте эти выходные данные в качестве входных данных для новой, меньшей модели.
Ключевым преимуществом этого второго рабочего процесса является то, что вы запускаете базовую модель для своих данных только один раз, а не один раз за эпоху обучения. Так что это намного быстрее и дешевле.
Однако проблема с этим вторым рабочим процессом заключается в том, что он не позволяет вам динамически изменять входные данные вашей новой модели во время обучения, что требуется, например, при увеличении данных. Трансферное обучение обычно используется для задач, когда в вашем новом наборе данных слишком мало данных для обучения полномасштабной модели с нуля, и в таких сценариях очень важно увеличение данных. Итак, далее мы сосредоточимся на первом рабочем процессе.
Вот как выглядит первый рабочий процесс в Керасе:
Сначала создайте экземпляр базовой модели с предварительно обученными весами.
base_model = keras.applications.Xception(
weights='imagenet', # Load weights pre-trained on ImageNet.
input_shape=(150, 150, 3),
include_top=False) # Do not include the ImageNet classifier at the top.
Затем заморозьте базовую модель.
base_model.trainable = False
Сверху создайте новую модель.
inputs = keras.Input(shape=(150, 150, 3))
# We make sure that the base_model is running in inference mode here,
# by passing `training=False`. This is important for fine-tuning, as you will
# learn in a few paragraphs.
x = base_model(inputs, training=False)
# Convert features of shape `base_model.output_shape[1:]` to vectors
x = keras.layers.GlobalAveragePooling2D()(x)
# A Dense classifier with a single unit (binary classification)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)
Обучите модель на новых данных.
model.compile(optimizer=keras.optimizers.Adam(),
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy()])
model.fit(new_dataset, epochs=20, callbacks=..., validation_data=...)
Тонкая настройка
Как только ваша модель сойдется с новыми данными, вы можете попытаться разморозить всю или часть базовой модели и переобучить всю модель от начала до конца с очень низкой скоростью обучения.
Это необязательный последний шаг, который потенциально может дать вам дополнительные улучшения. Это также может привести к быстрой переобучению - имейте это в виду.
Крайне важно , чтобы только сделать этот шаг после того , как модель с замороженными слоями обучена конвергенцией. Если вы смешиваете случайно инициализированные обучаемые слои с обучаемыми слоями, которые содержат предварительно обученные функции, случайно инициализированные слои вызовут очень большие обновления градиента во время обучения, что разрушит ваши предварительно обученные функции.
Также критически важно использовать очень низкую скорость обучения на этом этапе, потому что вы обучаете гораздо большую модель, чем в первом раунде обучения, на наборе данных, который обычно очень мал. В результате вы очень быстро рискуете переобучиться, если примените большие обновления веса. Здесь вам нужно только инкрементально повторно адаптировать предварительно натренированные веса.
Вот как осуществить тонкую настройку всей базовой модели:
# Unfreeze the base model
base_model.trainable = True
# It's important to recompile your model after you make any changes
# to the `trainable` attribute of any inner layer, so that your changes
# are take into account
model.compile(optimizer=keras.optimizers.Adam(1e-5), # Very low learning rate
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy()])
# Train end-to-end. Be careful to stop before you overfit!
model.fit(new_dataset, epochs=10, callbacks=..., validation_data=...)
Важное замечание о compile()
и trainable
Вызов compile()
на модели предназначается , чтобы «заморозить» поведение этой модели. Это означает , что trainable
значение атрибутов в то время как модель скомпилированной должно сохраняться на протяжении всего срока службы этой модели, пока compile
не вызывается снова. Следовательно, при изменении любого trainable
значения, убедитесь , что вызов compile()
снова на вашу модель для ваших изменений , которые необходимо принять во внимание.
Важные замечания о BatchNormalization
слоя
Многие модели изображения содержат BatchNormalization
слои. Этот слой - особый случай во всех мыслимых смыслах. Вот несколько вещей, о которых следует помнить.
-
BatchNormalization
содержит 2 без обучаемого веса , которые обновляются во время тренировки. Это переменные, отслеживающие среднее значение и дисперсию входных данных. - При установке
bn_layer.trainable = False
, тоBatchNormalization
слой будет работать в режиме вывода, и не будет обновлять свою среднюю и дисперсию статистики. Это не так для других слоев в целом, как вес обучаемости и умозаключения / режимы обучения два ортогональных понятия . Но два связаны в случаеBatchNormalization
слоя. - Когда вы разморозить модель , которая содержит
BatchNormalization
слоев, чтобы сделать тонкую настройку, вы должны держатьBatchNormalization
слои в режиме вывода при прохожденииtraining=False
при вызове базовой модели. В противном случае обновления, применяемые к необучаемым весам, внезапно разрушат то, чему модель научилась.
Вы увидите этот шаблон в действии в сквозном примере в конце этого руководства.
Передача обучения и точной настройки с помощью индивидуального цикла обучения
Если вместо fit()
, вы используете свой собственный цикл обучения низкоуровневого, рабочий процесс остается по существу то же самое. Вы должны быть осторожны , чтобы только принять во внимание список model.trainable_weights
при применении обновления градиента:
# Create base model
base_model = keras.applications.Xception(
weights='imagenet',
input_shape=(150, 150, 3),
include_top=False)
# Freeze base model
base_model.trainable = False
# Create new model on top.
inputs = keras.Input(shape=(150, 150, 3))
x = base_model(inputs, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)
loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)
optimizer = keras.optimizers.Adam()
# Iterate over the batches of a dataset.
for inputs, targets in new_dataset:
# Open a GradientTape.
with tf.GradientTape() as tape:
# Forward pass.
predictions = model(inputs)
# Compute the loss value for this batch.
loss_value = loss_fn(targets, predictions)
# Get gradients of loss wrt the *trainable* weights.
gradients = tape.gradient(loss_value, model.trainable_weights)
# Update the weights of the model.
optimizer.apply_gradients(zip(gradients, model.trainable_weights))
То же самое и для точной настройки.
Сквозной пример: точная настройка модели классификации изображений на наборе данных "кошки против собак"
Чтобы закрепить эти концепции, давайте рассмотрим конкретный пример обучения и точной настройки сквозной передачи. Мы загрузим модель Xception, предварительно обученную в ImageNet, и будем использовать ее в наборе данных классификации Kaggle «кошки против собак».
Получение данных
Во-первых, давайте возьмем набор данных кошек против собак с помощью TFDS. Если у вас есть свой собственный набор данных, вы , вероятно , хотите использовать утилиту tf.keras.preprocessing.image_dataset_from_directory
для создания аналогичных меченого набора данных объектов из набора изображений на диске , поданных в папки класса конкретного.
Переносное обучение наиболее полезно при работе с очень маленькими наборами данных. Чтобы наш набор данных был небольшим, мы будем использовать 40% исходных обучающих данных (25 000 изображений) для обучения, 10% для проверки и 10% для тестирования.
import tensorflow_datasets as tfds
tfds.disable_progress_bar()
train_ds, validation_ds, test_ds = tfds.load(
"cats_vs_dogs",
# Reserve 10% for validation and 10% for test
split=["train[:40%]", "train[40%:50%]", "train[50%:60%]"],
as_supervised=True, # Include labels
)
print("Number of training samples: %d" % tf.data.experimental.cardinality(train_ds))
print(
"Number of validation samples: %d" % tf.data.experimental.cardinality(validation_ds)
)
print("Number of test samples: %d" % tf.data.experimental.cardinality(test_ds))
Number of training samples: 9305 Number of validation samples: 2326 Number of test samples: 2326
Это первые 9 изображений в наборе обучающих данных - как видите, все они разного размера.
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
for i, (image, label) in enumerate(train_ds.take(9)):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(image)
plt.title(int(label))
plt.axis("off")
Мы также можем видеть, что метка 1 - это «собака», а метка 0 - «кошка».
Стандартизация данных
Наши необработанные изображения имеют разные размеры. Кроме того, каждый пиксель состоит из 3 целых значений от 0 до 255 (значения уровня RGB). Это не очень подходит для питания нейронной сети. Нам нужно сделать 2 вещи:
- Стандартизируйте изображение до фиксированного размера. Выбираем 150х150.
- Нормализация значение пикселей в диапазоне от -1 до 1. Мы будем делать это с помощью
Normalization
слоя как часть самой модели.
В целом рекомендуется разрабатывать модели, которые принимают на вход необработанные данные, в отличие от моделей, которые принимают уже предварительно обработанные данные. Причина в том, что если ваша модель ожидает предварительно обработанные данные, каждый раз, когда вы экспортируете свою модель для использования в другом месте (в веб-браузере, в мобильном приложении), вам необходимо заново реализовать точно такой же конвейер предварительной обработки. Это очень быстро становится очень сложным. Таким образом, мы должны провести минимально возможную предварительную обработку перед тем, как перейти к модели.
Здесь мы изменим размер изображения в конвейере данных (потому что глубокая нейронная сеть может обрабатывать только непрерывные пакеты данных), и мы сделаем масштабирование входного значения как часть модели, когда мы ее создадим.
Изменим размер изображения до 150x150:
size = (150, 150)
train_ds = train_ds.map(lambda x, y: (tf.image.resize(x, size), y))
validation_ds = validation_ds.map(lambda x, y: (tf.image.resize(x, size), y))
test_ds = test_ds.map(lambda x, y: (tf.image.resize(x, size), y))
Кроме того, давайте запакуем данные и воспользуемся кешированием и предварительной выборкой для оптимизации скорости загрузки.
batch_size = 32
train_ds = train_ds.cache().batch(batch_size).prefetch(buffer_size=10)
validation_ds = validation_ds.cache().batch(batch_size).prefetch(buffer_size=10)
test_ds = test_ds.cache().batch(batch_size).prefetch(buffer_size=10)
Использование случайного увеличения данных
Когда у вас нет большого набора данных изображения, рекомендуется искусственно вводить разнообразие образцов, применяя случайные, но реалистичные преобразования к обучающим изображениям, такие как случайное горизонтальное переворачивание или небольшие случайные вращения. Это помогает подвергнуть модель различным аспектам обучающих данных, замедляя переобучение.
from tensorflow import keras
from tensorflow.keras import layers
data_augmentation = keras.Sequential(
[layers.RandomFlip("horizontal"), layers.RandomRotation(0.1),]
)
Визуализируем, как выглядит первое изображение первого пакета после различных случайных преобразований:
import numpy as np
for images, labels in train_ds.take(1):
plt.figure(figsize=(10, 10))
first_image = images[0]
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
augmented_image = data_augmentation(
tf.expand_dims(first_image, 0), training=True
)
plt.imshow(augmented_image[0].numpy().astype("int32"))
plt.title(int(labels[0]))
plt.axis("off")
2021-09-01 18:45:34.772284: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.
Построить модель
Теперь давайте построим модель, которая следует схеме, которую мы объяснили ранее.
Обратите внимание, что:
- Мы добавим
Rescaling
слоя входных значений шкалы (первоначально в[0, 255]
диапазоне) в[-1, 1]
диапазон. - Мы добавляем
Dropout
слой перед слоем классификации, для регуляризации. - Мы уверены , чтобы пройти
training=False
при вызове базовой модели, так что он работает в режиме вывода, так что статистика batchnorm не обновляется , даже после того, как мы разморозить базовую модель для тонкой настройки.
base_model = keras.applications.Xception(
weights="imagenet", # Load weights pre-trained on ImageNet.
input_shape=(150, 150, 3),
include_top=False,
) # Do not include the ImageNet classifier at the top.
# Freeze the base_model
base_model.trainable = False
# Create new model on top
inputs = keras.Input(shape=(150, 150, 3))
x = data_augmentation(inputs) # Apply random data augmentation
# Pre-trained Xception weights requires that input be scaled
# from (0, 255) to a range of (-1., +1.), the rescaling layer
# outputs: `(inputs * scale) + offset`
scale_layer = keras.layers.Rescaling(scale=1 / 127.5, offset=-1)
x = scale_layer(x)
# The base model contains batchnorm layers. We want to keep them in inference mode
# when we unfreeze the base model for fine-tuning, so we make sure that the
# base_model is running in inference mode here.
x = base_model(x, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dropout(0.2)(x) # Regularize with dropout
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)
model.summary()
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/xception/xception_weights_tf_dim_ordering_tf_kernels_notop.h5 83689472/83683744 [==============================] - 2s 0us/step 83697664/83683744 [==============================] - 2s 0us/step Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_5 (InputLayer) [(None, 150, 150, 3)] 0 _________________________________________________________________ sequential_3 (Sequential) (None, 150, 150, 3) 0 _________________________________________________________________ rescaling (Rescaling) (None, 150, 150, 3) 0 _________________________________________________________________ xception (Functional) (None, 5, 5, 2048) 20861480 _________________________________________________________________ global_average_pooling2d (Gl (None, 2048) 0 _________________________________________________________________ dropout (Dropout) (None, 2048) 0 _________________________________________________________________ dense_7 (Dense) (None, 1) 2049 ================================================================= Total params: 20,863,529 Trainable params: 2,049 Non-trainable params: 20,861,480 _________________________________________________________________
Тренируйте верхний слой
model.compile(
optimizer=keras.optimizers.Adam(),
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy()],
)
epochs = 20
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
Epoch 1/20 151/291 [==============>...............] - ETA: 3s - loss: 0.1979 - binary_accuracy: 0.9096 Corrupt JPEG data: 65 extraneous bytes before marker 0xd9 268/291 [==========================>...] - ETA: 1s - loss: 0.1663 - binary_accuracy: 0.9269 Corrupt JPEG data: 239 extraneous bytes before marker 0xd9 282/291 [============================>.] - ETA: 0s - loss: 0.1628 - binary_accuracy: 0.9284 Corrupt JPEG data: 1153 extraneous bytes before marker 0xd9 Corrupt JPEG data: 228 extraneous bytes before marker 0xd9 291/291 [==============================] - ETA: 0s - loss: 0.1620 - binary_accuracy: 0.9286 Corrupt JPEG data: 2226 extraneous bytes before marker 0xd9 291/291 [==============================] - 29s 63ms/step - loss: 0.1620 - binary_accuracy: 0.9286 - val_loss: 0.0814 - val_binary_accuracy: 0.9686 Epoch 2/20 291/291 [==============================] - 8s 29ms/step - loss: 0.1178 - binary_accuracy: 0.9511 - val_loss: 0.0785 - val_binary_accuracy: 0.9695 Epoch 3/20 291/291 [==============================] - 9s 30ms/step - loss: 0.1121 - binary_accuracy: 0.9536 - val_loss: 0.0748 - val_binary_accuracy: 0.9712 Epoch 4/20 291/291 [==============================] - 9s 29ms/step - loss: 0.1082 - binary_accuracy: 0.9554 - val_loss: 0.0754 - val_binary_accuracy: 0.9703 Epoch 5/20 291/291 [==============================] - 8s 29ms/step - loss: 0.1034 - binary_accuracy: 0.9570 - val_loss: 0.0721 - val_binary_accuracy: 0.9725 Epoch 6/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0975 - binary_accuracy: 0.9602 - val_loss: 0.0748 - val_binary_accuracy: 0.9699 Epoch 7/20 291/291 [==============================] - 9s 29ms/step - loss: 0.0989 - binary_accuracy: 0.9595 - val_loss: 0.0732 - val_binary_accuracy: 0.9716 Epoch 8/20 291/291 [==============================] - 8s 29ms/step - loss: 0.1027 - binary_accuracy: 0.9566 - val_loss: 0.0787 - val_binary_accuracy: 0.9678 Epoch 9/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0959 - binary_accuracy: 0.9614 - val_loss: 0.0734 - val_binary_accuracy: 0.9729 Epoch 10/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0995 - binary_accuracy: 0.9588 - val_loss: 0.0717 - val_binary_accuracy: 0.9721 Epoch 11/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0957 - binary_accuracy: 0.9612 - val_loss: 0.0731 - val_binary_accuracy: 0.9725 Epoch 12/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0936 - binary_accuracy: 0.9622 - val_loss: 0.0751 - val_binary_accuracy: 0.9716 Epoch 13/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0965 - binary_accuracy: 0.9610 - val_loss: 0.0821 - val_binary_accuracy: 0.9695 Epoch 14/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0939 - binary_accuracy: 0.9618 - val_loss: 0.0742 - val_binary_accuracy: 0.9712 Epoch 15/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0974 - binary_accuracy: 0.9585 - val_loss: 0.0771 - val_binary_accuracy: 0.9712 Epoch 16/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0947 - binary_accuracy: 0.9621 - val_loss: 0.0823 - val_binary_accuracy: 0.9699 Epoch 17/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0947 - binary_accuracy: 0.9625 - val_loss: 0.0718 - val_binary_accuracy: 0.9708 Epoch 18/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0928 - binary_accuracy: 0.9616 - val_loss: 0.0738 - val_binary_accuracy: 0.9716 Epoch 19/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0922 - binary_accuracy: 0.9644 - val_loss: 0.0743 - val_binary_accuracy: 0.9716 Epoch 20/20 291/291 [==============================] - 8s 29ms/step - loss: 0.0885 - binary_accuracy: 0.9635 - val_loss: 0.0745 - val_binary_accuracy: 0.9695 <keras.callbacks.History at 0x7f849a3b2950>
Сделайте раунд точной настройки всей модели
Наконец, давайте разморозим базовую модель и обучим всю модель от начала до конца с низкой скоростью обучения.
Важно отметить, что хотя базовая модель становится обучаемой, он по - прежнему работает в режиме вывода , так как мы прошли training=False
при вызове, когда мы построили модель. Это означает, что уровни пакетной нормализации внутри не будут обновлять свою пакетную статистику. Если бы они это сделали, они бы разрушили представления, изученные моделью до сих пор.
# Unfreeze the base_model. Note that it keeps running in inference mode
# since we passed `training=False` when calling it. This means that
# the batchnorm layers will not update their batch statistics.
# This prevents the batchnorm layers from undoing all the training
# we've done so far.
base_model.trainable = True
model.summary()
model.compile(
optimizer=keras.optimizers.Adam(1e-5), # Low learning rate
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy()],
)
epochs = 10
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_5 (InputLayer) [(None, 150, 150, 3)] 0 _________________________________________________________________ sequential_3 (Sequential) (None, 150, 150, 3) 0 _________________________________________________________________ rescaling (Rescaling) (None, 150, 150, 3) 0 _________________________________________________________________ xception (Functional) (None, 5, 5, 2048) 20861480 _________________________________________________________________ global_average_pooling2d (Gl (None, 2048) 0 _________________________________________________________________ dropout (Dropout) (None, 2048) 0 _________________________________________________________________ dense_7 (Dense) (None, 1) 2049 ================================================================= Total params: 20,863,529 Trainable params: 20,809,001 Non-trainable params: 54,528 _________________________________________________________________ Epoch 1/10 291/291 [==============================] - 43s 131ms/step - loss: 0.0802 - binary_accuracy: 0.9692 - val_loss: 0.0580 - val_binary_accuracy: 0.9764 Epoch 2/10 291/291 [==============================] - 37s 128ms/step - loss: 0.0542 - binary_accuracy: 0.9792 - val_loss: 0.0529 - val_binary_accuracy: 0.9764 Epoch 3/10 291/291 [==============================] - 37s 128ms/step - loss: 0.0400 - binary_accuracy: 0.9832 - val_loss: 0.0510 - val_binary_accuracy: 0.9798 Epoch 4/10 291/291 [==============================] - 37s 128ms/step - loss: 0.0313 - binary_accuracy: 0.9879 - val_loss: 0.0505 - val_binary_accuracy: 0.9819 Epoch 5/10 291/291 [==============================] - 37s 128ms/step - loss: 0.0272 - binary_accuracy: 0.9904 - val_loss: 0.0485 - val_binary_accuracy: 0.9807 Epoch 6/10 291/291 [==============================] - 37s 128ms/step - loss: 0.0284 - binary_accuracy: 0.9901 - val_loss: 0.0497 - val_binary_accuracy: 0.9824 Epoch 7/10 291/291 [==============================] - 37s 127ms/step - loss: 0.0198 - binary_accuracy: 0.9937 - val_loss: 0.0530 - val_binary_accuracy: 0.9802 Epoch 8/10 291/291 [==============================] - 37s 127ms/step - loss: 0.0173 - binary_accuracy: 0.9930 - val_loss: 0.0572 - val_binary_accuracy: 0.9819 Epoch 9/10 291/291 [==============================] - 37s 127ms/step - loss: 0.0113 - binary_accuracy: 0.9958 - val_loss: 0.0555 - val_binary_accuracy: 0.9837 Epoch 10/10 291/291 [==============================] - 37s 127ms/step - loss: 0.0091 - binary_accuracy: 0.9966 - val_loss: 0.0596 - val_binary_accuracy: 0.9832 <keras.callbacks.History at 0x7f83982d4cd0>
После 10 эпох тонкая настройка дает нам здесь хорошее улучшение.