Вопрос или проблема
Я работаю с моделью, которая включает 3 этапа «внедрения» моделей в Keras.
Концептуально первой является модель CNN с переносом обучения, например, MobileNetV2. (Модель 1) Затем она оборачивается моделью, которая состоит из небольшой DNN. (Модель 2) Наконец, во время обучения все это оборачивается моделью, которая конкатенирует несколько выходов из модели 2, вычисляет потери, а затем осуществляет обратное распространение ошибки в модель 2 и в будущем в модель 1. (Модель 3)
Для вывода позже я просто хочу сохранить веса моделей 1 и 2. У меня возникло множество проблем с этим из-за того, что, похоже, в некоторых версиях Keras есть ошибки (я использую 2.2.2), а также загрузка весов более явно, похоже, приводит к рандомизированным весам и поэтому не работает корректно. Вместо того чтобы пытаться устранить неполадки того, что идет не так с любым сценарием, я просто пытаюсь определить наилучшие практики для сохранения промежуточных вложенных моделей.
def create_model_2(IN_DIM=(224, 224, 3), OUT_DIM=128):
# Сначала определите модель с переносом обучения
initial_img = Input(shape=(IN_DIM))
black_box = MobileNetV2(include_top=False, input_shape=IN_DIM, weights="imagenet", pooling="avg")(initial_img)
bb_model = Model(b_img, black_box)
# зафиксируйте слои для модели переноса обучения
for layer in bb_model.layers:
layer.trainable = False
#########################
###### БАШЕННЫЙ БЛОК ######
#########################
img = Input(shape=(IN_DIM))
x = bb_model(img)
# добавьте несколько слоев, чтобы попытаться обучиться
x = Dense(64, activation='relu', name="new_fc0")(x)
x = Dense(OUT_DIM, activation='relu', name="new_fc1")(x)
# L2 норма для проекции на единичную сферу
out = Lambda(lambda x: K.l2_normalize(x, axis=1), name="final_l2_norm")(x)
_model_2 = Model(img, out)
return _model_2
Затем структура Модели 3:
IN_DIM = (224, 224, 3) # mobilenetv2=(224, 224, 3) Iv3=(299, 299, 3)
OUT_DIM = 32
model_2 = create_model_2(IN_DIM, OUT_DIM)
# затем определите изображения для триплетов
anchor_img = Input(shape=IN_DIM)
pos_img = Input(shape=IN_DIM)
neg_img = Input(shape=IN_DIM)
# создайте три вектора, представляющих изображения
anchor_in = model_2(anchor_img)
positive_in = model_2(pos_img)
negative_in = model_2(neg_img)
# конкатенируйте вектора в один большой вектор для ввода в «обработчик» потерь триплета
merged_vector = concatenate([anchor_in, positive_in, negative_in], axis=-1)
# короче говоря, определите модель:
model_3 = Model(inputs=[anchor_img, pos_img, neg_img], outputs=merged_vector)
Модель, кажется, работает и обучается нормально:
OPTIMIZER = SGD(lr=learning_rate, momentum=0.9)
final_model.compile(optimizer=OPTIMIZER, loss=triplet_loss, metrics=[avg_AP_dist, avg_AN_dist])
history = final_model.fit_generator(generator=training_generator,
epochs=5, # коротко для отладки
use_multiprocessing=True,
workers=4)
Но сохранение модели после обучения неясно:
out_file = "../../models/{:}_epoch_{:}_weights.h5".format(MODEL_DESC, 5)
model_2.save_weights(out_file) # сохраните фактические веса Башни, отбросьте обертку "усилителя"
print("Сохранено: {:}".format(out_file))
Или:
out_file = "../../models/{:}_epoch_{:}_weights.h5".format(MODEL_DESC, 5)
model_2.save(out_file) # сохраните фактические веса Башни, отбросьте обертку "усилителя"
print("Сохранено: {:}".format(out_file))
Или что-то еще?
Текущие способы сбоев, похоже, возникают, если я пытаюсь загрузить только веса в новую инстанцию model_2, и я получаю:
ValueError: axes don't match array
Что, как я обнаружил, может быть связано с ошибкой в Keras. Если я сохраняю модель (.save(), а не .save_weights()), тогда она загружается без проблем, но вывод не стабильный и, похоже, ужасный/рандомный.)
Спасибо.
Все еще получаю следующую трассировку:
<snip>/src/notebooks/vectorizer.py in load_model()
65
66 # загрузите веса
---> 67 loaded_model.load_weights(weights_path)
68
69 print("Модель готова")
/opt/conda/lib/python3.6/site-packages/keras/engine/network.py in load_weights(self, filepath, by_name, skip_mismatch, reshape)
1164 else:
1165 saving.load_weights_from_hdf5_group(
-> 1166 f, self.layers, reshape=reshape)
1167
1168 def _updated_config(self):
/opt/conda/lib/python3.6/site-packages/keras/engine/saving.py in load_weights_from_hdf5_group(f, layers, reshape)
1043 original_keras_version,
1044 original_backend,
-> 1045 reshape=reshape)
1046 if len(weight_values) != len(symbolic_weights):
1047 raise ValueError('Layer #' + str(k) +
/opt/conda/lib/python3.6/site-packages/keras/engine/saving.py in preprocess_weights_for_loading(layer, weights, original_keras_version, original_backend, reshape)
680 weights = convert_nested_time_distributed(weights)
681 elif layer.__class__.__name__ in ['Model', 'Sequential']:
--> 682 weights = convert_nested_model(weights)
683
684 if original_keras_version == '1':
/opt/conda/lib/python3.6/site-packages/keras/engine/saving.py in convert_nested_model(weights)
668 weights=weights[:num_weights],
669 original_keras_version=original_keras_version,
670 original_backend=original_backend))
671 weights = weights[num_weights:]
672 return new_weights
/opt/conda/lib/python3.6/site-packages/keras/engine/saving.py in preprocess_weights_for_loading(layer, weights, original_keras_version, original_backend, reshape)
680 weights = convert_nested_time_distributed(weights)
681 elif layer.__class__.__name__ in ['Model', 'Sequential']:
--> 682 weights = convert_nested_model(weights)
683
684 if original_keras_version == '1':
/opt/conda/lib/python3.6/site-packages/keras/engine/saving.py in convert_nested_model(weights)
656 weights=weights[:num_weights],
657 original_keras_version=original_keras_version,
658 original_backend=original_backend))
659 weights = weights[num_weights:]
660
/opt/conda/lib/python3.6/site-packages/keras/engine/saving.py in preprocess_weights_for_loading(layer, weights, original_keras_version, original_backend, reshape)
799 weights[0] = np.reshape(weights[0], layer_weights_shape)
800 elif layer_weights_shape != weights[0].shape:
--> 801 weights[0] = np.transpose(weights[0], (3, 2, 0, 1))
802 если layer.__class__.__name__ == 'ConvLSTM2D':
803 weights[1] = np.transpose(weights[1], (3, 2, 0, 1))
/opt/conda/lib/python3.6/site-packages/numpy/core/fromnumeric.py in transpose(a, axes)
596
597 """
--> 598 return _wrapfunc(a, 'transpose', axes)
599
600
/opt/conda/lib/python3.6/site-packages/numpy/core/fromnumeric.py in _wrapfunc(obj, method, *args, **kwds)
49 def _wrapfunc(obj, method, *args, **kwds)
50 пытайтесь:
---> 51 return getattr(obj, method)(*args, **kwds)
52
53 # Происходит ошибка AttributeError, если объект не имеет
ValueError: axes don't match array
Попробуйте сохранить модель в формате JSON и веса в формате HDF5 с помощью save_weights()
.
# сохраните модель
model_json = model_2.to_json()
with open("model_2.json", "w") as j_file:
j_file.write(model_json)
# сохраните веса
model.save_weights("model_2.h5")
Позже, чтобы загрузить модель:
# загрузите модель
j_file = open('model_2.json', 'r')
loaded_json_model = j_file.read()
j_file.close()
loaded_model = model_from_json(loaded_json_model)
# загрузите веса
loaded_model.load_weights("model_2.h5")
После того как вы завершили обучение модели:
final_model.save('model.h5')
Чтобы перезагрузить модель, просто используйте:
from keras import load_model
model=load_model('model.h5')
Ответ или решение
Правильное сохранение и загрузка промежуточной модели в Keras
При работе с моделями глубокого обучения в Keras, особенно когда на нескольких уровнях связываются вложенные модели, важно знать, как правильно сохранять и загружать веса. Это не только позволит избежать проблем, связанных с несоответствием весов, но и обеспечит стабильность и повторяемость ваших экспериментов. В данной статье я подробно разберу лучшие практики.
1. Структура вложенных моделей
Как показано в вашем примере, вы создаёте две модели:
- Модель 1: Использует MobileNetV2 как базу с передачей обучения.
- Модель 2: небольшая DNN, оборачивающая модель 1 и добавляющая новые слои.
Кроме того, Модель 3 объединяет несколько выходов из модели 2 для дальнейшего обучения с использованием функции потерь, такой как триплетная потеря.
2. Сохранение модели и весов
Во-первых, сохранение может быть выполнено несколькими способами. Рассмотрим способ с использованием JSON для структуры модели и HDF5 для весов:
# Сохранение структуры модели в JSON
model_json = model_2.to_json()
with open("model_2.json", "w") as json_file:
json_file.write(model_json)
# Сохранение весов модели
model_2.save_weights("model_2_weights.h5")
print("Модель и веса успешно сохранены.")
3. Загрузка модели и весов
Чтобы загрузить модель и её веса, необходимо выполнить аналогичную последовательность действий:
from keras.models import model_from_json
# Загрузка структуры модели
with open("model_2.json", "r") as json_file:
loaded_model_json = json_file.read()
loaded_model = model_from_json(loaded_model_json)
# Загрузка весов в загруженную модель
loaded_model.load_weights("model_2_weights.h5")
Данная схема позволяет избежать проблемы совместимости между весами и структурой модели, что часто приводит к ошибкам, таким как ValueError: axes don't match array
, которые вы описали.
4. Специфика работы с вложенными моделями
Если у вас действительно сложная структура моделей, вы можете столкнуться с проблемами загрузки весов. Один из подходов для этого — использование by_name
при загрузке весов. Это особенно полезно, когда вы имеете дело с несколькими моделями:
loaded_model.load_weights("model_2_weights.h5", by_name=True)
Использование флага by_name
позволяет Keras соответствовать весам по именам слоев, что может снизить вероятность ошибок.
5. Порядок сохранения и загрузки для моделей с несколькими уровнями
Когда вы работаете с многослойными и вложенными моделями, целесообразно сохранять и загружать вес для каждой модели по отдельности, что вы можете делать таким образом:
# Сохранение весов Model 1
model_1.save_weights("model_1_weights.h5")
# Сохранение весов Model 2
model_2.save_weights("model_2_weights.h5")
# Загрузка весов
model_1.load_weights("model_1_weights.h5")
model_2.load_weights("model_2_weights.h5")
Заключение
Использование предложенной схемы сохранения и загрузки моделей в Keras обеспечивает большую надёжность процессов и уменьшает количество ошибок, связанных с несоответствием весов. Убедитесь, что вы обновляете используемую версию Keras, так как в более ранних версиях, например, 2.2.2, могут встречаться баги, влияющие на работу моделей. С помощью этих рекомендаций вы сможете эффективно управлять своими моделями и обеспечивать их корректную работу как на этапе обучения, так и на этапеInference.