В этом посте рассматривается процесс внедрения трансферного обучения для классификации изображений. Я буду использовать архитектуру VGG16 с весами imagenet, но этот процесс можно использовать и с другими архитектурами моделей, такими как VGG19, ResNet-50 и т. д.

Первое, что нам нужно сделать, это сделать необходимый импорт. Я буду использовать Keras в этом посте.

from keras.layers import Input
from keras.models import Model
from keras.applications import VGG16
from keras.layers import Flatten, Dropout, Dense
from keras.callbacks import ModelCheckpoint, EarlyStopping

Поскольку настройку данных для обучения и тестирования я описал в предыдущем посте, давайте перейдем к построению и обучению модели. Подход, который я буду использовать здесь, заключается в том, чтобы использовать модель VGG16 для вывода признаков на слои FC для проведения классификации. Для этого мы должны указать, что нам не нужен исходный выходной слой модели VGG16, установив для include_top значение False. input_shape должен быть установлен на любые размеры вашего изображения. В этом случае изображения RGB имеют размер 224x224 пикселя, поэтому форма (224, 224, 3). Если вы не знакомы с тем, как создается эта модель, возможно, вы привыкли настраивать свои модели последовательно. Эта модель настроена с помощью функционального API Keras.

vgg_model = VGG16(weights='imagenet',
                  include_top=False,
                  input_shape=(224, 224, 3))
x = vgg_model.output
x = Flatten()(x) # Flatten dimensions to for use in FC layers
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x) # Dropout layer to reduce overfitting
x = Dense(256, activation='relu')(x)
x = Dense(5, activation='softmax')(x) # Softmax for multiclass
transfer_model = Model(inputs=vgg_model.input, outputs=x)

Перед тренировкой всегда полезно изучить структуру вашей сети. Простой способ сделать это в Keras — использовать model.summary(), который даст вам все имя, выходную форму и количество параметров для каждого слоя. Сводка для модели, созданной выше, показана ниже.

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         (None, 224, 224, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 25088)             0         
_________________________________________________________________
dense_1 (Dense)              (None, 512)               12845568  
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 256)               131328    
_________________________________________________________________
dense_3 (Dense)              (None, 5)                 1285      
=================================================================
Total params: 27,692,869
Trainable params: 27,692,869
Non-trainable params: 0
_________________________________________________________________

Как видно из сводки модели, в части VGG16 нашей модели можно обучить множество параметров. Поскольку эти слои уже предварительно обучены с помощью весов imagenet, их пока не нужно обучать. Вместо этого мы заморозим их, используя приведенный ниже код.

for layer in transfer_model.layers[:19]:
    layer.trainable = False
# Make sure you have frozen the correct layers
for i, layer in enumerate(transfer_model.layers):
    print(i, layer.name, layer.trainable)

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

custom_model2.compile(loss='categorical_crossentropy',
                      optimizer='adam',
                      metrics=['accuracy'])
checkpoint = ModelCheckpoint("VGG16_1.h5", monitor='val_acc', verbose=1, save_best_only=True, save_weights_only=False, mode='auto', period=1)
early = EarlyStopping(monitor='val_acc', min_delta=0, patience=10, verbose=1, mode='auto')
model_hist2 = custom_model2.fit_generator(
    train_generator,
    steps_per_epoch = train_generator.samples // batch_size,
    validation_data = validation_generator, 
    validation_steps = validation_generator.samples // batch_size,
    epochs = epochs,
    callbacks = [checkpoint, early])

После обучения модель достигла точности проверки 72%. Это немного хуже, чем базовая модель, которую я настроил в первом посте, поэтому в следующих постах я рассмотрю различные архитектуры.