在使用pytorch过程中,我发现了torch中存在3个功能极其类似的方法,它们分别是model.parameters()
、model.named_parameters()
和model.state_dict()
,下面就具体来说说这三个函数的差异
首先,说说比较接近的model.parameters()
和model.named_parameters()
。这两者唯一的差别在于,named_parameters()
返回的list中,每个元祖打包了2个内容,分别是layer-name和layer-param,而parameters()
只有后者。后面只谈model.named_parameters()
和model.state_dict()
间的差别。
它们的差异主要体现在3方面:
首先说第一个,这很简单,model.state_dict()
是将layer_name : layer_param的键值信息存储为dict形式,而model.named_parameters()
则是打包成一个元祖然后再存到list当中;
第二,model.state_dict()
存储的是该model中包含的所有layer中的所有参数;而model.named_parameters()
则只保存可学习、可被更新的参数,model.buffer()
中的参数不包含在model.named_parameters()
中
最后,model.state_dict()
所存储的模型参数tensor的require_grad
属性都是False,而model.named_parameters()
的require_grad
属性都是True
实际上,我们以上提到的model.parameters()
、model.named_parameters()
都和这个module有关,毕竟模型参数在初始化中就是用这个模块被实例化的。以神经网络中最常见的nn.Linear()
为例,这是pytorch1.4的nn.Linear()
源码:
class Linear(Module):
__constants__ = ['bias']
def __init__(self, in_features, out_features, bias=True):
super(Linear, self).__init__()
self.in_features = in_features
self.out_features = out_features
self.weight = Parameter(torch.Tensor(out_features, in_features))
if bias:
self.bias = Parameter(torch.Tensor(out_features))
else:
self.register_parameter('bias', None)
self.reset_parameters()
如上代码第7、第9行,大家可以发现,我们新建的线性层的参数weight和bias的初始化,都是使用nn.Parameters()
做的,那么我们再看看这个module的玄机(同样是torch源码):
class Parameter(torch.Tensor):
def __new__(cls, data=None, requires_grad=True):
if data is None:
data = torch.Tensor()
return torch.Tensor._make_subclass(cls, data, requires_grad)
注意第2行,这里正是说明为何model.parameters()
迭代出来的所有参数的require_grad属性都是True了,因为它们在被创建时,默认的require_grad就是True。这也符合逻辑,即,使用nn.Parameter()创建的变量是模型参数,本就是要参与学习和更新的;而不参与模型学习和更新的呢?我们再看一个例子:
class _BatchNorm(Module):
def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=True, track_running_stats=True):
super(_BatchNorm, self).__init__()
self.num_features = num_features
self.eps = eps
self.momentum = momentum
self.affine = affine
self.track_running_stats = track_running_stats
if self.affine:
self.weight = Parameter(torch.Tensor(num_features))
self.bias = Parameter(torch.Tensor(num_features))
else:
self.register_parameter('weight', None)
self.register_parameter('bias', None)
if self.track_running_stats:
self.register_buffer('running_mean', torch.zeros(num_features))
self.register_buffer('running_var', torch.ones(num_features))
self.register_buffer('num_batches_tracked', torch.tensor(0, dtype=torch.long))
else:
self.register_parameter('running_mean', None)
self.register_parameter('running_var', None)
self.register_parameter('num_batches_tracked', None)
self.reset_parameters()
如以上代码16-18行,这一行定义的三个参数是不参与模型学习的,所以定义在buffer中,buffer的定义方法为self.register_buffer()
,所以模型不参与学习和更新的参数是这样定义的、