0. まとめ
深層学習では、事前トレーニングを微調整に使用して、モデルの一般化能力を向上させ、収束速度を速め、トレーニング時間を節約することがよくあります。
一般に、事前トレーニングには多くの方法があります. 一般的な方法は、事前トレーニング層の学習率を小さく設定するか、重みを更新せずに事前トレーニング層を固定することです. ここでは後者について説明します.
1.固定層トレーニング
ここで、次のモデルがあるとします。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.layer1=conv_base # conv_base是由conv、BN等组成的卷积基
self.fc1=nn.Linear(512,100) # 分类器
self.fc2=nn.Linear(100, 10) # 分类器
def forward(self,x):
feat=self.layers(x)
out=self.fc1(feat)
out=self.fc2(out)
return out
このうち、layer1 は学習済みの特徴抽出であり、fc1 と fc2 は学習する必要があるため、layer1 のパラメーターを固定し、fc1 と fc2 のパラメーターのみをトレーニングする方法が 2 つあります (基本的に同じ):
- 固定パラメーターに requires_grad=True を設定します
# 首先将固定层layer1的requires_grad属性置False
for name, p in model.layer1.named_parameters(): # 固定feat权重
p.requires_grad = False
# 在优化器中过滤到这些requires_grad参数
para=filter(lambda x:x.requires_grad is not False,model.parameters())
optimizer = torch.optim.SGD(para, lr=0.1)
- オプティマイザーで直接更新するパラメーターのみを指定する
para=[{
"params":model.fc1.parameters()},
{
"params":model.fc2.parameters()}]
optimizer = torch.optim.SGD(para, lr=0.1)
上記の 2 つの方法では、オプティマイザーは、特定のパラメーターをトレーニングするためにパラメーターのみを更新する方法を指定します。
2.問題
上記の方法でトレーニングした後、レイヤー 1 のパラメーターは変更されず、fc1 と fc2 のパラメーターのみが変更されますが、レイヤー 1 のパラメーターを使用してデータ セットを検証すると、インジケーターが変更されていることがわかります。 layer1 は変更されていません。メトリックもトレーニング前と同じである必要があります。
実際問題はtrain() と eval()モードにあります. BatchNorm はデータセットサンプルの平均と分散に従って動作する必要があることがわかっています. トレーニング中, BatchNorm はデータセットの平均と分散を計算します.同時に、この時間の平均と分散が保存されます。検証とテストの場合、データ量は比較的少なく (1 つと非常に少ない)、このときの平均と分散は、トレーニング中に保存された平均と分散を使用して計算されます。
そのため、layer1 パラメータのトレーニングを修正しましたが、model.train() モードの layer1 では、それに含まれる BatchNorm のパラメータが変更されます. layer1 を使用してデータセットを再度テストすると、更新された平均値と std を使用します、インジケータが変化します。
まとめると、layer1 非 BatchNorm レイヤー (conv など) のパラメーターは変更されていませんが、BatchNorm のパラメーターが変更されています。
解決策も非常に簡単です。
# 训练时:先将模型全设为train模型,再将固定曾layer1设为eval模式,这样保证了参数不更新,且BatchNorm层参数也不更新
model.train()
model.layer1.eval()
# 测试时:
model.eval()