Как правильно сохранить и загрузить промежуточную модель в Keras?

Вопрос или проблема

Я работаю с моделью, которая включает 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.

Оцените материал
Добавить комментарий

Капча загружается...