Вопрос или проблема
Предположим, у нас есть архитектура глубокой нейронной сети с
слоем, который должен быть общим между двумя “подсетями”.
Пример:
from keras.layers import Input, Dense
from keras.models import Model
main_input = Input(shape=(5, ))
## Модель A: main_input -> A_output
layer_A1 = Dense(10, name="A1")(main_input)
layer_A2 = Dense(10, name="A2")(layer_A1)
layer_A3 = Dense(10, name="A3")(layer_A2)
A_output = Dense(1, name="A_output")(layer_A3)
## Модель B: main_input -> layer_A2 -> B_output
layer_B1 = Dense(10, name="B1")(layer_A2)
B_output = Dense(1, name="B_output")(layer_B1)
model = Model(inputs=main_input,
outputs=[A_output, B_output],
)
model.compile(optimizer="adam",
loss={
'A_output':'mean_squared_error',
'B_output':'mean_squared_error'
},
)
Цель состоит в том, чтобы сначала обучить модель A, чтобы модель B могла учиться на предварительно обученных весах слоя A2. Однако вызов функции fit в текущей архитектуре будет обучать обе модели одновременно и суммировать потери.
Как я могу изменить архитектуру так, чтобы модель A обучалась первой, не создавая отдельные модели? В конечном итоге мне нужно будет вызвать model.predict(new_sample)
, где new_sample
имеет размерность (5,) в примере.
Я разобрался. Чтобы сначала обучить определенные подсети и повторно использовать обученные веса для инициализации других слоев:
from keras.layers import Input, Dense
from keras.models import Model
main_input = Input(shape=(5, ))
## Модель A: main_input -> A_output
layer_A1 = Dense(10, name="A1")(main_input)
layer_A2 = Dense(10, name="A2")(layer_A1)
layer_A3 = Dense(10, name="A3")(layer_A2)
A_output = Dense(1, name="A_output")(layer_A3)
# Сначала обучим модель A
model_A = Model(inputs=main_input, outputs=A_output)
model_A.compile(# args)
model_A.fit(# args)
## Модель B: main_input -> layer_A2 -> B_output
layer_B1 = Dense(10, name="B1")(main_input)
B_output = Dense(1, name="B_output")(layer_B1)
model_B = Model(inputs=main_input, outputs=B_output)
# Установить веса layer_B1 и заморозить; по сути, это копия предварительно обученного layer_A2
trained_A2_weights = model_A.layers[2].get_weights()
model_B.layers[1].set_weights(trained_A2_weights)
model_B.layers[1].trainable = False
model_B.compile(# args)
model_B.fit(# args)
```
Ответ или решение
Последовательное обучение определённых слоёв/подсетей в Keras Functional API
В данной статье мы обсудим как тренировать определённые слои нейронной сети на основе Keras Functional API, таким образом, чтобы они могли быть использованы в других подсетях без создания отдельных моделей. В частности, мы рассмотрим задачу, когда два подмодели используют общий слой, и требуется сначала обучить одну из них, чтобы затем использовать её веса для обучения второй.
Исходная структура модели
Рассмотрим начало решения задачи с помощью простого примера. Мы создадим главные входные данные и две модели, где модель A использует несколько слоёв для получения своего выхода, а модель B использует один из слоёв модели A:
from keras.layers import Input, Dense
from keras.models import Model
main_input = Input(shape=(5, ))
## Модель A: main_input -> A_output
layer_A1 = Dense(10, name="A1")(main_input)
layer_A2 = Dense(10, name="A2")(layer_A1)
layer_A3 = Dense(10, name="A3")(layer_A2)
A_output = Dense(1, name="A_output")(layer_A3)
## Модель B: main_input -> layer_A2 -> B_output
layer_B1 = Dense(10, name="B1")(layer_A2)
B_output = Dense(1, name="B_output")(layer_B1)
model = Model(inputs=main_input, outputs=[A_output, B_output])
model.compile(optimizer="adam",
loss={
'A_output': 'mean_squared_error',
'B_output': 'mean_squared_error'
})
Как видно из примера, вызов model.fit()
будет одновременно обучать обе подмодели и суммировать их потери, что затрудняет последовательное обучение.
Решение задачи
Для успешного выполнения нашей задачи необходимо отделить обучение модели A от модели B. Это можно сделать, создав две отдельные модели и копируя веса из модели A в модель B после завершения её обучения. Рассмотрим следующий код:
# Обучение модели A
model_A = Model(inputs=main_input, outputs=A_output)
model_A.compile(optimizer="adam", loss='mean_squared_error')
# Предположим, у нас есть данные для обучения
model_A.fit(x_train, y_train_A)
# Создание модели B
layer_B1 = Dense(10, name="B1")(layer_A2) # использование того же слоя
B_output = Dense(1, name="B_output")(layer_B1)
model_B = Model(inputs=main_input, outputs=B_output)
# Копирование весов после обучения модели A
trained_A2_weights = model_A.layers[2].get_weights() # получаем веса слоя A2
model_B.layers[1].set_weights(trained_A2_weights) # инициализация слоя B1
model_B.layers[1].trainable = False # заморозка слоя для предотвращения его обучения
model_B.compile(optimizer="adam", loss='mean_squared_error')
model_B.fit(x_train, y_train_B) # обучение модели B
Завершение обучения и выводы
После выполнения вышеприведённых шагов, обе модели обучены корректно с использованием весов из модели A. Теперь можно использовать функцию model.predict(new_sample)
для получения predictions от любой из моделей. При этом new_sample
должен иметь форму (5,).
Данная методология является универсальной и может быть использована в различных сценариях. Она позволяет эффективно обучать сложные архитектуры нейронных сетей и использовать предварительно обученные слои в других подмоделях, улучшая общую производительность и скорость обучения.
Таким образом, с помощью Keras Functional API мы можем эффективно управлять процессом обучения сложной структуры нейронных сетей, избегая необходимости создания и управления большими количеством моделей, что упрощает процесс разработки и оптимизации.