1 . 图像的卷积操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import torch as t from torch import nn from PIL import Image from torchvision.transforms import ToTensor, ToPILImage
to_tensor = ToTensor() to_pil = ToPILImage() lena = Image.open('path to your image')
input = to_tensor(lena).unsqueeze(0)
kernel = t.ones(3, 3)/-9 kernel[1][1] = 1 conv = nn.Conv2d(1, 1, (3, 3), 1, bias=False) conv.weight.data = kernel.view(1, 1, 3, 3)
out = conv(input) to_pil(out.data.squeeze(0))
|
2 . 池化层可以看作是一种特殊的卷积层,用来下采样,但池化层没有可学习的参数,其 weight 是固定的。
1 2 3 4 5
| pool = nn.AvgPool2d(2, 2) print(list(pool.parameters()))
out = pool(input) to_pil(out.data.squeeze(0))
|
3 . 除了卷积层和池化层,深度学习中还将常用到以下几层:
- Linear:全连接层;
- BatchNorm:批规范化层,分为 1D、2D 和 3D。除了标准的 BatchNorm 之外,还有在风格迁移中常用到的 InstanceNorm 层;
- Dropout:dropout 层用来防止过拟合,同样分为 1D、2D 和 3D。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| input = t.randn(2, 3) linear = nn.Linear(3, 4) h = linear(input) print(h)
bn = nn.BatchNorm1d(4) bn.weight.data = t.ones(4) * 4 bn.bias.data = t.zeros(4)
bn_out = bn(h)
print(bn_out.mean(0), bn_out.var(0, unbiased=False))
dropout = nn.Dropout(0.5) o = dropout(bn_out) print(o)
|
4 . PyTorch 实现了常见的激活函数,这些激活函数可作为独立的 layer 使用,这里介绍一下常用的激活函数 ReLU,其数学表达式为 $ReLU(x)=max(0, x)$。
1 2 3 4 5
| relu = nn.ReLU(inplace=True) input = t.randn(2, 3) print(input) output = relu(input) print(output)
|
ReLU 函数有个 inplace 参数,如果设置为 True,它会把输出直接覆盖到输入中,这样可以节省内存/显存。之所以可以覆盖是因为在计算 ReLU 的反向传播时,只需根据输出就能够推算反向传播的梯度。但是只有少数的 autograd 操作支持 inplace 操作(如 tensor.sigmoid_()
),除非你明确地知道自己在做什么,否则一般不要使用 inplace 操作。
5 . 在以上例子中,基本上都是将每一层的输出直接作为下一层的输入,这种网络称为前馈传播网络,对于此类网络如果每次都写复杂的 forward 函数会有些麻烦,有两种简化方式,ModuleList
和 Sequential
,其中 Sequential
是一个特殊的 Module,它包含几个子 Module,前向传播时会将输入一层接一层地传递下去,ModuleList
也是一个极其特殊的 Module,可以包含几个子 Module,可以像 list 一样使用它,但不能直接把输入传递给 ModuleList。
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
| from collections import OrderedDict
import torch as t from torch import nn
net1 = nn.Sequential() net1.add_module('conv', nn.Conv2d(3, 3, 3)) net1.add_module('batchnorm', nn.BatchNorm2d(3)) net1.add_module('activation_layer', nn.ReLU())
net2 = nn.Sequential( nn.Conv2d(3, 3, 3), nn.BatchNorm2d(3), nn.ReLU() )
net3 = nn.Sequential(OrderedDict[ ('conv1', nn.Conv2d(3, 3, 3)), ('bn1', nn.BatchNorm2d(3)), ('relu1', nn.ReLU()) ])
print('net1:', net1) print('net2:', net2) print('net3:', net3)
print(net1.conv, net2[0], net3.conv1)
input = t.rand(1, 3, 4, 4) output = net1(input) output = net2(input) output = net3(input) output = net3.relu1(net1.batchnorm(net1.conv(input)))
|
1 2 3 4 5 6 7 8 9 10
| import torch as t from torch import nn
modellist = nn.ModuleList([nn.Linear(3, 4), nn.ReLU(), nn.Linear(4, 2)]) input = t.randn(1, 3) for model in modellist: input = model(input)
|
ModuleList
是 Module
的子类,当在 Module
中使用它的时候,就能自动识别为子 module,而 python 自带的 list 则不行。
Input:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from torch import nn
class MyModule(nn.Module): def __init__(self): super(MyModule, self).__init__() self.list = [nn.Linear(3, 4), nn.ReLU()] self.module_list = nn.ModuleList([nn.Conv2d(3, 3, 3), nn.ReLU()])
def forward(self): pass
model = MyModule() print(model)
for name, param in model.named_parameters(): print(name, param)
|
Output:
可见,list 中的子 Module 并不能被主 Module 所识别,而 ModuleList 中的子 Module 能够被主 Module 所识别,这意味着如果用 list 保存子 Module,将无法调整其参数,因其未加入到主 Module 的参数中。
在实际应用中,如果在构造函数 __init__
中用到 list、tuple、dict 等对象时,一定要思考是否应该用 ModuleList 或 ParameterList 代替。
笔记来源:《pytorch-book》