1 问题的描述和分析
1.1 问题的描述
在同一个jupyter notebook中,实例化了一个模型A,然后又实例化了一个模型B。
我们训练的是模型B,并且在训练过程中保存了模型B的参数和对应的优化器参数。
结束训练后,退出notebook后再重新进入。实例化模型B,优化器(这里的模型B和优化器和之前训练的是一模一样的),然后载入相应的参数。
开始训练模型B,然后出现了报错。
1.2 问题的分析
在刚开始的时候,实例化了一个模型A,假设这个模型A中有10层卷积层,它们命名为:
conv2d_i (i=0, 1, 2, ..., 9)
然后又实例化了一个模型B,假设这个模型中有7层卷积层,它们命名为:
conv2d_i (i=10, 11, 12, ..., 17)
我们训练模型B时保存的优化器参数保存的是conv2d_i.w_balabala (i=10, 11, 12, ..., 17)
结束训练后,退出notebook后再重新进入。实例化模型B,其中的7层卷积层,它们命名为:
conv2d_i (i=0, 1, 2, ..., 7)
实例化优化器,然后载入相应的参数。
这时模型B的参数可以正常载入,而优化器载入的参数为conv2d_i.w_balabala (i=10, 11, 12, ..., 17)
然后训练,B模型要找优化器要参数,B模型找的是自己的层的名称对应的参数,也就是B模型要找的是conv2d_i.w_balabala (i=0, 1, 2, ..., 7),而优化器中只有命名为conv2d_i.w_balabala (i=10, 11, 12, ..., 17)的参数,所以就报错:“conv2d_0.w_balabala 应该要在优化器的参数列表中”
原话 “conv2d_0.w_0_moment1_0 should in state dict”
2解决办法
法1:在我们预训练模型B的时候,要确保这个模型B是我们开启notebook后第一个实例化的模型。然后在我们之后重新训练模型B的时候,也要确保这个模型B是开启notebook后第一个实例化的模型。
法2:如果你是已经训练了模型,现在要重新训练,那就只能修改优化器参数字典的变量的命名方式了。具体方法如下:
import re # 正则表达
import paddle
def ResetOptDict(dict_path):
r = r"\w+_\d+"
r1 = r"\w+_\d"
opt__state_dict = paddle.load(dict_path) # 优化器保存的参数字典
new_opt__state_dict = {}
layer_num = {}
for k, v in opt__state_dict.items():
m=re.match(r, k)
m1 = re.match(r1, k)
layer_name = k[:m1.end()-1] #linear_
moment_name = k[m.end():] # .w_0_moment1_0
layer_moment_name = layer_name + moment_name # linear_.w_0_moment1_0
if layer_moment_name not in layer_num:
layer_num[layer_moment_name] = 0
else:
layer_num[layer_moment_name] += 1
new_key_name = layer_name + str(layer_num[layer_moment_name]) + moment_name
new_opt__state_dict[new_key_name] = v
return new_opt__state_dict
# 载入新的字典
dict_path = 'work/checkpoints/save_dir_final.pdopt' # 优化器保存的路径
opt__state_dict = ResetOptDict(dict_path)
optimizer.set_state_dict(opt__state_dict)