Background
环境:python 3.6.0 + tensorflow-gpu 1.12.0 + Keras 2.2.4
用 keras 写的一个模型(训练 Model 中涉及两个 trainable 的子 Model),通过 add_loss
的方式为模型设置损失,形如:
loss_tensor = loss_1(...) + loss_2(...) # 将 loss 写成 tensor
# 此 model 的损失中涉及两个 trainable 的子 model
model.add_loss(loss_tensor) # add_loss 的方式
model.compile(Adam())
这种写法下,用 fit_generator
的方式训练模型时,generator 只需要 yield 模型的输入数据就行,target 的部分直接上 None
,即:
def generator_no_target():
...
yield [data_1, data_2], None
那么讲道理,模型训练应该同当初建立模型时的输出部分无关?
我的意思是:模型有两个输出,写成(写法1
):
model = Model([input_1, input_2], [ouput_1, output_2])
但在这种情况下,写成(写法2
):
model = Model([input_1, input_2], ouput_1)
应该不会影响训练?(因为训练的时候 target data 传的是 None
啊)
Problem
然而,写法1
训练效果正常,写法2
训练效果非常差。
这是为什么?
这种模型输出的设置是怎么影响模型训练过程的?
难道有两个 trainable 的子 model 就要有两个输出吗?
Further Testing
1. 不用 add_loss
的方式,而是定义损失函数
即前面第一块代码写成:
# loss_tensor = loss_1(...) + loss_2(...)
def loss_fn(y_true, y_pred): # 将 loss 写成函数
return loss_1(...) + loss_2(...)
# model.add_loss(loss)
model.compile(Adam(), loss=loss_fn) # 在 compile 里指明 loss 函数
这时训练数据的 target 部分需要传入数据,于是在 generator 的 yield 那里补上 target 的部分,用全 0 补,即 generator 写成:
def generator_with_target():
...
# yield [data_1, data_2], None
fake_target = np.zeros((BATCH_SIZE, DIM), dtype=np.float32) # 全零
# yield [data_1, data_2], fake_target # 若模型有一个输出
yield [data_1, data_2], [fake_target, fake_target] # 若模型有两个输出
(其中 yield
的那句,模型输出只有一个 output 就只返回一个 fake_target,有两个就返回两个)
没有变化,依然有这个奇怪的差异。
2. 不用 fit_generator
,而是用 train_on_batch
但是生成训练数据的 generator 还是之前那个,loss 用 add_loss
设置。
也没有用,同样有这个奇怪的差异。