Keras单模型双损失函数实现及模型截断训练权重问题问询
首先直接回应你的核心问题:
一、model_1和model_2的权重默认不一致
在你当前的代码里,model_1和model_2是完全独立的Keras Model实例——虽然它们初始时共享相同的层结构和初始权重,但一旦你开始训练model_1,只有model_1内部的层权重会被更新,model_2的权重会停留在初始状态,不会自动同步。
这是因为Keras中每个Model对象都维护自己的权重副本,除非你显式让它们共享层(比如先定义层变量,再在两个模型中复用),否则两者的权重是完全分开的。
二、可以完成MinMax博弈训练,但需要手动同步权重
要实现你的MinMax训练逻辑(先训CTC任务的model_1,再训截断得到的分类任务model_2),你需要手动处理权重同步,具体步骤如下:
1. 修复代码中的层定义问题
你的代码里有个明显的笔误:inner_act = Flatten()(inner_act)这行中,inner_act还未定义,应该改为:
inner_act = Flatten()(inner) # inner是经过两次MaxPooling后的特征层
否则代码会直接抛出未定义变量的错误。另外,Model(inputs=input_data, outputs=y_pred)这行没有赋值给变量,属于无效代码,可以删除。
2. 训练model_1后同步权重到model_2
当你完成model_1的CTC任务训练后,需要把model_1中与model_2重叠的层的权重复制到model_2中。比如model_2用到的conv1、max1、conv2、max2、Flatten这些层,和model_1里的对应层结构一致,可以通过以下方式同步:
# 遍历model_2的每一层,从model_1中获取对应层的权重并赋值 for layer_2, layer_1 in zip(model_2.layers, model_1.layers[:len(model_2.layers)-2]): # 注意model_2的最后两层是dense3和sigmoid,model_1里没有,所以只同步前面的共享层 layer_2.set_weights(layer_1.get_weights())
或者更优雅的方式:在定义模型时就共享层对象,比如先把共享的卷积池化层定义为变量,再在两个模型中复用:
# 先定义共享的基础特征提取层 input_data = Input(name='the_input', shape=input_shape, dtype='float32') conv1 = Conv2D(conv_filters, kernel_size, padding='same', activation=act, kernel_initializer='he_normal', name='conv1')(input_data) max1 = MaxPooling2D(pool_size=(pool_size, pool_size), name='max1')(conv1) conv2 = Conv2D(conv_filters, kernel_size, padding='same', activation=act, kernel_initializer='he_normal', name='conv2')(max1) max2 = MaxPooling2D(pool_size=(pool_size, pool_size), name='max2')(conv2) # 构建model_2的分支(分类任务) inner_act = Flatten()(max2) inner_2 = Dense(1, name='dense3')(inner_act) inner_out = Activation('sigmoid')(inner_2) model_2 = Model(inputs=input_data, outputs=inner_out) # 构建model_1的CTC分支 conv_to_rnn_dims = (img_w // (pool_size ** 2), (img_h // (pool_size ** 2)) * conv_filters) reshape = Reshape(target_shape=conv_to_rnn_dims, name='reshape')(max2) dense1 = Dense(time_dense_size, activation=act, name='dense1')(reshape) # ... 后续GRU和CTC相关层定义 ... model_1 = Model(inputs=[input_data, labels, input_length, label_length], outputs=loss_out)
这样定义的话,model_1和model_2会共享conv1、max1、conv2、max2这些层的权重——训练model_1时,这些层的权重会自动同步到model_2,不需要手动复制。
3. 按需控制训练时的可训练层
在MinMax博弈训练中,你可能希望训练model_2时,只更新分类分支的层(比如dense3),而保持基础特征层和model_1一致;或者反过来。可以通过设置层的trainable属性实现:
# 冻结model_2的基础特征层,只训练分类分支 for layer in model_2.layers[:-2]: # 前几层是共享的卷积池化层 layer.trainable = False # 重新编译model_2 model_2.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 然后训练model_2
总结
- 默认情况下
model_1和model_2权重不一致,因为是独立模型; - 通过共享层定义或者手动权重复制,可以实现两者权重同步,进而完成MinMax博弈训练;
- 训练时可以通过冻结层来控制哪些权重被更新,匹配你的博弈训练逻辑。
内容的提问来源于stack exchange,提问作者Md Shopon

