Обучение глубоких нейронных сетей требует значительных вычислительных ресурсов. Поэтому понятно желание использовать ранее обученные и сохраненные модели нейронных сетей для решения текущих задач. Keras позволят не только работать с загруженными моделями, но и выполнять их модификацию.
Сохранение модели или ее весов можно выполнять как после завершения обучения нейронной сети, так и в процессе ее обучения.
Cохранение модели или ее весов в процессе обучения в Keras обеспечивает функция обратного вызова ModelCheckpoint, например:
import keras.callbacks as cb
callbacks = []
# Обеспечим сохранение обученной модели в файлы вида filesToSave
filesToSave = 'weights.{epoch:03d}-{val_acc:.2f}.hdf5'
print('Сохраняем веса + ' в файлы вида ' + filesToSave)
# Сохраняем только веса (save_weights_only = True)
# Сохраняем веса, если наблюдается рост val_acc (save_best_only = True)
checkpoint = cb.ModelCheckpoint(filesToSave, monitor = 'val_acc', verbose = 0,
save_weights_only = True, save_best_only = True, mode = 'max', period = 1)
callbacks.append(checkpoint)
# Обучение нейронной сети
history = model.fit(<другие параметры метода fit>, callbacks = callbacks)
Приведенный код обеспечит сохранение весов модели (поскольку save_weights_only = True) при каждом улучшении наблюдаемой метрики (задается параметром monitor).
Лучшее решение выбирается по значению критерия val_acc – точность классификации оценочных данных.
Модель нейронной сети после завершения ее обучения сохранит метод save, например:
model.save('G:\\AM\\mnist.h5')
Загрузку модели выполнит метод load_model, например:
from keras.models import load_model
#
fileModelToLoad = 'G:/AM/mnist.h5'
print('Загрузка модели из файла ' + fileModelToLoad)
model = load_model(fileModelToLoad)
Загруженную модель можно использовать сразу для решения задач классификации, можно выполнить ее дополнительно обучить, не внося изменений, а также можно прежде изменить, а затем обучить либо всю модель, либо часть, например, только модифицированные и/или добавленные компоненты модели.
Слой L загруженной модели не будет обучаться (не будут меняться его веса), если в программе до начала обучения указать:
L.trainable = False
Список слоев модели содержит ее свойство layers:
layers = model.layers
Все слои, кроме входного, можно получить, например, в следующем цикле:
for L in layers[1:nL - 3]:
L_nm = L.name
print(L_nm)
Используя этот цикл, можно собрать модифицированную модель, выполнить ее обучение и при получении приемлемого результата использовать модель по назначению.
В приводимой ниже программе загружается модель, обученная на MNIST, и затем выполняется попытка улучшить модель. Загруженная модель имеет следующие слои (перед типом слоя указывается его имя, список слоев возвращает метод model.summary()):
Форма входа (28, 28, 1). На последнем слое в загруженной модели применена функция активации Sigmoid.
Новая модель получается в результате замены трех последних слоев на аналогичные, но с другими значениями параметров:
Далее модель компилируется, и на MNIST обучаются только два ее последних полносвязных слоя (слои прореживания не обучаются, поскольку имеют нулевое число параметров).
Собранная модель имеет 912'132 параметра и только 19'562 из них являются обучаемыми.
Загрузку прежней модели, сборку и компиляцию новой обеспечивает следующий код:
from keras.models import load_model
import keras.losses as ls
#
fileModelToLoad = 'G:/AM/emnist.h5'
print('Загрузка модели из файла ' + fileModelToLoad)
model = load_model(fileModelToLoad)
# Сборка модели
layers = model.layers # Список слоев загруженной модели
nL = len(layers) # Число слоев
inp = layers[0] # Входной слой
# Форму входа возьмем из свойства batch_input_shape
input_shape = inp.batch_input_shape # (None, 28, 28, 1)
input_shape = input_shape[1:] # (28, 28, 1)
# Входной слой нужно задать явно (его нельзя взять из прежней модели)
inp = Input(input_shape)
x = inp
# Берем из прежней модели все слои, кроме трех последних
for L in layers[1:nL - 3]:
L_nm = L.name
print(L_nm)
# Нужно дать слою новое имя
# В противном случае при добавлении слоев будет нарушена
# уникальность их имен
L.name = L_nm + '_m'
# Помечаем слой как необучаемый
L.trainable = False
x = L(x)
# Добавляем к слоям, взятым из прежней модели, три слоя
x = Dense(32, activation = 'linear')(x)
x = Dropout(0.15)(x)
output = Dense(10, activation = 'softmax')(x)
# Model – конструктор модели
model = Model(inputs = inp, outputs = output)
# Вывод сведений о слоях модели
model.summary()
# Компиляция модели
loss = ls.mean_squared_logarithmic_error
model.compile(optimizer = 'Adam', loss = loss, metrics = ['accuracy'])
Далее загружаются данные MNIST (x_train, y_train – обучающие изображения и их метки; x_test, y_test – проверочные изображения и их метки) и выполняется обучение модели, а точнее двух ее последних полносвязных слоев:
history = model.fit(x_train, y_train, batch_size = 256, epochs = 120,
verbose = 2, validation_data = (x_test, y_test))
Помимо изменения структуры модели, в методе compile можно указать иные оптимизатор и функцию потерь. Если выбрать функцию потерь binary_crossentropy, то для получения верного значение точности классификации наряду с метрикой accuracy следует использовать метрику categorical_accuracy:
import keras.losses, keras.metrics
loss = keras.losses.binary_crossentropy
metrics = ['accuracy', keras.metrics.categorical_accuracy]
model.compile(optimizer = 'Adam', loss = loss, metrics = metrics)
Обученные модели нейронных сетей широко представлены в открытом доступе. В частности, работая с Keras, можно загрузить следующие обученные на ImageNet модели глубоких нейронных сетей [1]:
Набор данных ImageNet [2] создан в рамках проекта ImageNet Large Scale Visual Recognition Challenge (широкомасштабное распознавание изображений ImageNet), цель которого – стимулировать разработку методов классификации, распознавания изображений и других методов машинного зрения. Набор данных ImageNet постоянно расширяется. Вот имена некоторых имеющихся в ImageNet классов: