Pytorch中的register_buffer()
Pytorch中的register_buffer
回顾模型保存:torch.save(model.state_dict()),model.state_dict()是一个字典,里边存着我们模型各个部分的参数。
在model中,我们需要更新其中的参数,训练结束将参数保存下来。但在某些时候,我们可能希望模型中的某些参数参数不更新(从开始到结束均保持不变),但又希望参数保存下来(model.state_dict() ),这是我们就会用到 register_buffer() 。
例子1:使用类成员变量(类成员变量并不会在我们的model.state_dict(),即无法保存)
成员变量(self.tensor)在前向传播中用到,希望它也能保存下来,但他不在我们的state_dict中。
例子2:使用类成员变量(类成员变量并不会随着model.cuda()复制到gpu上)
将上一个例子中的模型复制到GPU上,但成员变量并不会随着model.cuda()复制到gpu上。torch中如果有数据不在同一个“地方”进行“运算”,程序会报错, 即self.tensor在 “ cpu ” 上,模型和 x 在 “ cuda:0 ” 上。
例子3:使用register_buffer()
self.register_buffer(‘my_buffer’, self.tensor):my_buffer是名字,str类型;self.tensor是需要进行register登记的张量。这样我们就得到了一个新的张量,这个张量会保存在model.state_dict()中,也就可以随着模型一起通过.cuda()复制到gpu上。
成员变量:不更新,但是不算是模型中的参数(model.state_dict())
通过register_buffer()登记过的张量:会自动成为模型中的参数,随着模型移动(gpu/cpu)而移动,但是不会随着梯度进行更新。
模型保存下来的参数有两种:一种是需要更新的Parameter,另一种是不需要更新的buffer。在模型中,利用backward反向传播,可以通过requires_grad来得到buffer和parameter的梯度信息,但是利用optimizer进行更新的是parameter,buffer不会更新,这也是两者最重要的区别。这两种参数都存在于model.state_dict()的OrderedDict中,也会随着模型“移动”(model.cuda())。
与model.parameters()和model.named_parameters()相同,只是一个是对模型中的parameter访问,一个是对模型中的buffer访问。
buffer变量和parameter变量一样,都可以通过backward()得到梯度信息,但区别是优化器optimizer更新的parameter变量,所以buffer并不会更新。
在构造模型时候,可以将某些Parameter从模型中通过“ .detach() ” 方法或直接将Parameter的requires_grad设置为False,使得此变量不求梯度,也可达到不更新的效果。
- 通过nn.Paramter()将张量设置为变量,同时设置requires_grad为False
- 这个变量也会随着模型保存,并且随着模型“移动”
- 可达到与buffer相同的效果
为什么要存在buffer:
buffer与parameter具有 “同等地位”,所以将某些不需要更新的变量“拿出来”作为buffer,可能更方便操作,可读性也更高,对Paramter的各种操作(固定网络的等)可能也不会“误伤到” buffer这种变量。buffer最重要的意义应该是需要得到梯度信息时,不会更新因为optimizer而更新,这也是parameter所不能代替的。
最近发现bn中的running_mean,running_var, num_batches_tracked这三个参数是buffer类型的,这样既可以用state_dict()保存,也不会随着optimizer更新。
此外,我们要注意,state_dict()只会保存parameters和buffers类型的变量,如果我们有变量没有转成这两种类型,最后是不会被保存的!!!