1 . PyTorch 实现了如今最常用的三种循环神经网络(RNN):RNN(vanilla RNN)、LSTM 和 GRU,此外还有对应的三种 RNNCell,RNN 和 RNNCell 层的区别在于前者能够处理整个序列,而后者一次只处理序列中一个时间点的数据,前者封装更完备更易于使用,后者更具灵活性。实际上 RNN 层的一种后端实现方式就是调用 RNNCell 来实现的。
1 2 3 4 5 6 7 8 9 10 11 12 13 import torch as tfrom torch import nnt.manual_seed(1000 ) input = t.randn(2 , 3 , 4 ) lstm = nn.LSTM(4 , 3 , 1 ) h0 = t.randn(1 , 3 , 3 ) c0 = t.randn(1 , 3 , 3 ) out, hn = lstm(input, (h0, c0)) print(out)
1 2 3 4 5 6 7 8 9 10 11 t.manual_seed(1000 ) input = t.randn(2 , 3 , 4 ) lstm = nn.LSTMCell(4 , 3 ) hx = t.randn(3 , 3 ) cx = t.randn(3 , 3 ) out = [] for i_ in input: hx, cx = lstm(i_, (hx, cx)) out.append(hx) t.stack(out)
词向量在自然语言中应用十分普及,PyTorch 同样提供了 Embedding 层。
1 2 3 4 5 6 7 8 embedding = nn.Embedding(4 , 5 ) embedding.weight.data = t.arange(0 , 20 ).view(4 , 5 ) input = t.arange(3 , 0 , -1 ).long() output = embedding(input) print(output)
2 . 在深度学习中要用到各种各样的损失函数(loss function),这些损失函数可以看成是一种特殊的 layer,PyTorch 也将这些损失函数实现为 nn.Module
的子类,然而在实际应用中通常将这些 loss function 专门提取出来,和主模型互相独立。
交叉熵损失(CrossEntropyLoss):
1 2 3 4 5 6 7 8 9 score = t.randn(3 , 2 ) label = t.Tensor([1 , 0 , 1 ]).long() criterion = nn.CrossEntropyLoss() loss = criterion(score, label) print(loss)
3 . PyTorch 将深度学习中常用的优化方法全部封装在 torch.optim
中,能够方便地扩展成自定义的优化方法,所有的优化方法都是继承基类 optim.Optimizer
,并实现了自己的优化步骤,下面以随机梯度下降(SGD)说明:
优化方法的基本使用方法;
如何对模型的不同部分设置不同的学习率;
如何调整学习率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import torch as tfrom torch import nnfrom torch import optimclass Net (nn.Module) : def __init__ (self) : super(Net, self).__init__() self.features = nn.Sequential( nn.Conv2d(3 , 6 , 5 ), nn.ReLU(), nn.MaxPool2d(2 , 2 ), nn.Conv2d(6 , 16 , 5 ), nn.ReLU(), nn.MaxPool2d(2 , 2 ) ) self.classifier = nn.Sequential( nn.Linear(16 * 5 * 5 , 120 ), nn.ReLU(), nn.Linear(120 , 84 ), nn.ReLU(), nn.Linear(84 , 10 ) ) def forward (self, x) : x = self.features(x) x = x.view(-1 , 16 * 5 * 5 ) x = self.classifier(x) return x net = Net() optimizer = optim.SGD(params=net.parameters(), lr=1 ) optimizer.zero_grad() input = t.randn(1 , 3 , 32 , 32 ) output = net(input) output.backward(output) optimizer.step()
为不同的子网络设置不同的学习率,在 finetune 中经常用到,如果对某个参数不指定学习率,就使用最外层的默认学习率。
1 2 3 4 5 6 optimizer = optim.SGD([ {'params' : net.features.parameters()}, {'params' : net.classifier.parameters(), 'lr' : 1e-2 } ], lr=1e-5 ) print(optimizer)
只为两个全连接层设置较大的学习率,其余层的学习率较小。
1 2 3 4 5 6 7 8 9 10 special_layers = nn.ModuleList([net.classifier[0 ], net.classifier[3 ]]) special_layers_params = list(map(id, special_layers.parameters())) base_params = filter(lambda p: id(p) not in special_layers_params, net.parameters()) optimizer = optim.SGD([ {'params' : base_params}, {'params' : special_layers.parameters(), 'lr' : 0.01 } ], lr=0.001 ) print(optimizer)
id
:用于获取对象的内存地址。map(function, iterable,...)
: 第一个参数 function
以参数序列中的每一个元素调用 function
函数,返回包含每次 function
函数返回值的新列表。filter(function, iterable)
:用于过滤掉不符合条件的元素,返回由符合条件的元素组成的新列表。iterable
是可迭代对象。
对于如何调整学习率,主要有两种做法,一种是修改 optimizer.param_groups
中对应的学习率,另一种是更简单也是较为推荐的做法 —— 新建优化器,但是后者对于使用动量的优化器(如 Adam),会丢失动量状态信息,可能会造成损失函数的收敛出现震荡等情况。
1 2 3 4 5 6 7 8 9 10 11 12 for param_group in optimizer.param_groups: param_group['lr' ] *= 0.1 print(optimizer) old_lr = 0.1 optimizer1 = optim.SGD([ {'params' : net.features.parameters()}, {'params' : net.classifier.parameters(), 'lr' : old_lr * 0.1 } ], lr=1e-5 ) print(optimizer1)
笔记来源:《pytorch-book》