提交 69164b73 编写于 作者: S ShusenTang

finish 9.2

上级 fc0e9d5c
......@@ -83,7 +83,8 @@ Dive into Deep Learning with PyTorch.
[8.4 多GPU计算](https://github.com/ShusenTang/Dive-into-DL-PyTorch/blob/master/docs/chapter08_computational-performance/8.4_multiple-gpus.md)
### 9. 计算机视觉
[9.1 图像增广](https://github.com/ShusenTang/Dive-into-DL-PyTorch/blob/master/docs/chapter09_computer-vision/9.1_image-augmentation.md)
[9.1 图像增广](https://github.com/ShusenTang/Dive-into-DL-PyTorch/blob/master/docs/chapter09_computer-vision/9.1_image-augmentation.md)
[9.2 微调](https://github.com/ShusenTang/Dive-into-DL-PyTorch/blob/master/docs/chapter09_computer-vision/9.2_fine-tuning.md)
持续更新中......
......
......@@ -123,47 +123,72 @@ Linear(in_features=512, out_features=1000, bias=True)
```
> 注: 如果你使用的是其他模型,那可能没有成员变量`fc`(比如models中的VGG预训练模型),所以正确做法是查看对应模型源码中其定义部分,这样既不会出错也能加深我们对模型的理解。[`pretrained-models.pytorch`](https://github.com/Cadene/pretrained-models.pytorch)仓库貌似统一了接口,但是我还是建议使用时查看一下对应模型的源码。
我们新建一个神经网络作为目标模型。它的定义与预训练的源模型一样,但最后的输出个数等于目标数据集的类别数。在下面的代码中,目标模型实例`finetune_net`的成员变量`features`中的模型参数被初始化为源模型相应层的模型参数。由于`features`中的模型参数是在ImageNet数据集上预训练得到的,已经足够好,因此一般只需使用较小的学习率来微调这些参数。而成员变量`output`中的模型参数采用了随机初始化,一般需要更大的学习率从头训练。假设`Trainer`实例中的学习率为$\eta$,我们设成员变量`output`中的模型参数在迭代中使用的学习率为$10\eta$。
可见此时`pretrained_net`最后的输出个数等于目标数据集的类别数1000。所以我们应该将最后的`fc`成修改我们需要的输出类别数:
```
pretrained_net.fc = nn.Linear(512, 2)
print(pretrained_net.fc)
```
输出:
```
Linear(in_features=512, out_features=2, bias=True)
```
此时,`pretrained_net``fc`层就被随机初始化了,但是其他层依然保存着预训练得到的参数。由于是在很大的ImageNet数据集上预训练的,所以参数已经足够好,因此一般只需使用较小的学习率来微调这些参数,而`fc`中的随机初始化参数一般需要更大的学习率从头训练。PyTorch可以方便的对模型的不同部分设置不同的学习参数,我们在下面代码中将`fc`的学习率设为已经预训练过的部分的10倍。
```{.python .input n=9}
finetune_net = model_zoo.vision.resnet18_v2(classes=2)
finetune_net.features = pretrained_net.features
finetune_net.output.initialize(init.Xavier())
# output中的模型参数将在迭代中使用10倍大的学习率
finetune_net.output.collect_params().setattr('lr_mult', 10)
``` python
output_params = list(map(id, pretrained_net.fc.parameters()))
feature_params = filter(lambda p: id(p) not in output_params, pretrained_net.parameters())
lr = 0.01
optimizer = optim.SGD([{'params': feature_params},
{'params': pretrained_net.fc.parameters(), 'lr': lr * 10}],
lr=lr, weight_decay=0.001)
```
### 9.2.1.3 微调模型
我们先定义一个使用微调的训练函数`train_fine_tuning`以便多次调用。
```{.python .input n=10}
def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5):
train_iter = gdata.DataLoader(
train_imgs.transform_first(train_augs), batch_size, shuffle=True)
test_iter = gdata.DataLoader(
test_imgs.transform_first(test_augs), batch_size)
ctx = d2l.try_all_gpus()
net.collect_params().reset_ctx(ctx)
net.hybridize()
loss = gloss.SoftmaxCrossEntropyLoss()
trainer = gluon.Trainer(net.collect_params(), 'sgd', {
'learning_rate': learning_rate, 'wd': 0.001})
d2l.train(train_iter, test_iter, net, loss, trainer, ctx, num_epochs)
``` python
def train_fine_tuning(net, optimizer, batch_size=128, num_epochs=5):
train_iter = DataLoader(ImageFolder(os.path.join(data_dir, 'hotdog/train'), transform=train_augs),
batch_size, shuffle=True)
test_iter = DataLoader(ImageFolder(os.path.join(data_dir, 'hotdog/test'), transform=test_augs),
batch_size)
loss = torch.nn.CrossEntropyLoss()
d2l.train(train_iter, test_iter, net, loss, optimizer, device, num_epochs)
```
我们将`Trainer`实例中的学习率设得小一点,如0.01,以便微调预训练得到的模型参数。根据前面的设置,我们将以10倍的学习率从头训练目标模型的输出层参数。
根据前面的设置,我们将以10倍的学习率从头训练目标模型的输出层参数。
```{.python .input n=11}
train_fine_tuning(finetune_net, 0.01)
``` python
train_fine_tuning(pretrained_net, optimizer)
```
输出:
```
training on cuda
epoch 1, loss 3.1183, train acc 0.731, test acc 0.932, time 41.4 sec
epoch 2, loss 0.6471, train acc 0.829, test acc 0.869, time 25.6 sec
epoch 3, loss 0.0964, train acc 0.920, test acc 0.910, time 24.9 sec
epoch 4, loss 0.0659, train acc 0.922, test acc 0.936, time 25.2 sec
epoch 5, loss 0.0668, train acc 0.913, test acc 0.929, time 25.0 sec
```
作为对比,我们定义一个相同的模型,但将它的所有模型参数都初始化为随机值。由于整个模型都需要从头训练,我们可以使用较大的学习率。
```{.python .input n=12}
scratch_net = model_zoo.vision.resnet18_v2(classes=2)
scratch_net.initialize(init=init.Xavier())
train_fine_tuning(scratch_net, 0.1)
``` python
scratch_net = models.resnet18(pretrained=False, num_classes=2)
lr = 0.1
optimizer = optim.SGD(scratch_net.parameters(), lr=lr, weight_decay=0.001)
train_fine_tuning(scratch_net, optimizer)
```
输出:
```
training on cuda
epoch 1, loss 2.6686, train acc 0.582, test acc 0.556, time 25.3 sec
epoch 2, loss 0.2434, train acc 0.797, test acc 0.776, time 25.3 sec
epoch 3, loss 0.1251, train acc 0.845, test acc 0.802, time 24.9 sec
epoch 4, loss 0.0958, train acc 0.833, test acc 0.810, time 25.0 sec
epoch 5, loss 0.0757, train acc 0.836, test acc 0.780, time 24.9 sec
```
可以看到,微调的模型因为参数初始值更好,往往在相同迭代周期下取得更高的精度。
......@@ -177,27 +202,7 @@ train_fine_tuning(scratch_net, 0.1)
* 一般来说,微调参数会使用较小的学习率,而从头训练输出层可以使用较大的学习率。
## 练习
* 不断增大`finetune_net`的学习率。精度会有什么变化?
* 进一步调节对比试验中`finetune_net``scratch_net`的超参数。它们的精度是不是依然有区别?
*`finetune_net.features`中的参数固定为源模型的参数而不在训练中迭代,结果会怎样?你可能会用到以下代码。
```{.python .input}
finetune_net.features.collect_params().setattr('grad_req', 'null')
```
* 事实上`ImageNet`数据集里也有“hotdog”(热狗)这个类。它在输出层对应的权重参数可以用以下代码获取。我们可以怎样使用这个权重参数?
```{.python .input n=13}
weight = pretrained_net.output.weight
hotdog_w = nd.split(weight.data(), 1000, axis=0)[713]
hotdog_w.shape
```
## 参考文献
-----------
> 注:除代码外本节与原书基本相同,[原书传送门](https://zh.d2l.ai/chapter_computer-vision/fine-tuning.html)
[1] GluonCV工具包。https://gluon-cv.mxnet.io/
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册