参照
4.2モデルパラメータへのアクセス、初期化、共有
セクション3.3(線形回帰の簡潔な実現)では、init
モジュールを介してモデルのパラメーターを初期化します。また、モデルパラメータにアクセスする簡単な方法も紹介しました。このセクションでは、モデルパラメータにアクセスして初期化する方法、および複数のレイヤー間で同じモデルパラメータを共有する方法について詳しく説明します。
import torch
from torch import nn
from torch.nn import init
net = nn.Sequential(nn.Linear(4,3), nn.ReLU(), nn.Linear(3, 1))
print(net)
X = torch.rand(2, 4)
Y = net(X).sum()
4.2.1モデルパラメータへのアクセス
前のセクションで説明したSequential
クラスModule
間の継承関係を思い出してください。ためのSequential
インスタンスにおけるモデルパラメータを含む層、我々はでき(イテレータの形で返される)全てのパラメータへのアクセスを介してModule
クラスparameters()
またはnamed_parameters
メソッド、後者はTensor
でその名前が返されるリターン・パラメータに加え
print(type(net.named_parameters()))
for name, param in net.named_parameters():
print(name, param.size())
返された名前の前に、レイヤー番号のインデックスが自動的に付けられていることがわかります。net
単層のパラメータを見てみましょう。Sequential
クラスを使用して構築されたニューラルネットワークの場合、角括弧を介し[]
てネットワークの任意の層にアクセスできます。インデックス0は、非表示レイヤーがSequential
インスタンスによって追加された最初のレイヤーであることを示します。
for name, param in net[0].named_parameters():
print(name, param.size(), type(param))
# 如果一个Tensor是Parameter,那么它会自动被添加到模型的参数列表里
class MyModel(nn.Module):
def __init__(self, **kwargs):
super(MyModel, self).__init__(**kwargs)
self.weight1 = nn.Parameter(torch.rand(20, 20))
self.weight2 = torch.rand(20, 20)
def forward(self, x):
pass
n = MyModel()
for name, param in n.named_parameters():
print(name)
# 上面代码中weight1在参数列表中,但是weight2却没在参数列表中
# 因为Parameters是Tensor,即Tensor拥有的属性它都有,比如可以根据data来访问参数数值,用grad来访问参数梯度
weight_0 = list(net[0].parameters())[0] # 将第0层的W取出
print(net)
print(weight_0)
print(weight_0.grad) # 此时并未对Y做梯度下降,因此会显示None
Y.backward()
print(weight_0.grad)
4.2.2モデルパラメータの初期化
次の例では、重みパラメーターを、平均値が0、標準偏差が0.01の正規分布の乱数に初期化し、偏差パラメーターをゼロにクリアします。
for name, param in net.named_parameters():
if 'weight' in name:
init.normal_(param, mean=0, std=0.01)
print(name, param.data)
# 使用常数来初始化权重参数
for name, param in net.named_parameters():
if 'bias' in name:
init.constant_(param, val=0)
print(name, param.data)
4.2.3カスタム初期化方法
必要な初期化メソッドがinitモジュールで提供されていない場合があります。このとき、初期化メソッドを実装して、他のメソッドと同じように使用できるようにすることができます。
# 我们先看看pytorch如何实现的
def normal_(tensor, mean=0, std= 1):
with torch.no_grad:
return tensor.normal_(mean, std)
これは、インプレースでTensorの値を変更する関数であり、このプロセスでは勾配が記録されないことがわかります。同様に、カスタム初期化メソッドを実装しましょう。次の例では、重みの確率の半分を0に初期化し、残りの半分を[-10、-5]と[5,10]の2つの区間で一様分布の乱数に初期化します。
def init_weight_(tensor):
with torch.no_grad():
tensor.uniform_(-10, 10)
tensor *= (tensor.abs() >= 5).float()
for name, param in net.named_parameters():
if 'weight' in name:
init_weight_(param)
print(name, param.data)
4.2.4モデルパラメータの共有
場合によっては、モデルパラメータを複数のレイヤー間で共有したいことがあります。例を見てみましょう
linear = nn.Linear(1, 1, bias=False)
net = nn.Sequential(linear, linear)
print(net)
for name, param in net.named_parameters():
init.constant_(param, val = 3)
print(name, param.data)
# 在内存中,这两个线性层其实是一个对象
print(id(net[0]) == id(net[1]))
print(id(net[0].weight) == id(net[1].weight))
# 因为模型参数里包含了梯度,所以在反向传播时,这些共享的参数的梯度是累加的
x = torch.ones(1, 1)
y = net(x).sum()
print(y)
y.backward()
print(net[0].weight.grad)