图像相关层主要包括卷积层(Conv)、池化层(Pool)、等。这些层在实际应用中可以分为一维(1D)、二维(2D)和三维(3D),池化又分为平均池化(SvgPool)、
最大值池化(MaxPool)、自适应池化(AdaptiveAvgPool)等。卷积层除了常用的前向卷积外,还有逆卷积(TransposeConv).
import torch as t
from torch import nn
from torch.autograd import Variable as V
from PIL import Image
from torchvision.transforms import ToTensor, ToPILImage
to_tensor=ToTensor()
to_pil=ToPILImage()
lena=Image.open('C:/Users/Duan/Pictures/lena.jpg')
print(type(lena))
print(lena.size)
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(V(input))
to_pil(out.data.squeeze(0))
池化可以看做一种特殊的卷积层,用来下采样。但池化没有可学习的参数,其weight是固定的。
import torch as t
from torch import nn
from torch.autograd import Variable as V
from PIL import Image
from torchvision.transforms import ToTensor, ToPILImage
to_tensor=ToTensor()
to_pil=ToPILImage()
lena=Image.open('C:/Users/Duan/Pictures/1.jpg')
input=to_tensor(lena).unsqueeze(0)
pool=nn.AvgPool2d(2,2)
list(pool.parameters())
out=pool(V(input))
to_pil(out.data.squeeze(0))
池化处理后的lena图
除了卷积层和池化层,深度学习中还经常用到以下几个层:
用法:
全连接层:
#输入batchsize=2,维度3
import torch as t
from torch import nn
from torch.autograd import Variable as V
input=V(t.randn(2,3))
linear=nn.Linear(3,4)
h=linear(input)
h
运行结果:
tensor([[-1.0120, -0.4962, 0.0714, -0.9935],
[-0.0530, 0.0573, 0.1812, -0.7866]])
#4channel .初始化标准差为4,均值为0
bn=nn.BatchNorm1d(4)
bn.weight.data=t.ones(4)*4
bn.bias.data=t.zeros(4)
bn_out=bn(h)
bn_out.mean(0),bn_out.var(0,unbiased=False)
运行结果:
(tensor(1.00000e-05 *
[-1.7226, -0.1788, -0.0596, 0.0000]),
tensor([ 2.6517, 15.8695, 15.9834, 15.9828]))
#每个元素以0.5的概率舍弃
dropout=nn.Dropout(0.5)
o=dropout(bn_out)
o #有一半左右的数变为0
结果:
tensor([[ 0.0000, 0.0000, 0.0000, -7.9957],
[-0.0000, -7.9673, -0.0000, 0.0000]])
Pytorch实现了常用的激活函数。。这些激活函数可以作为独立的layer使用。这里将介绍最常用的 激活函数ReLU,其数学表达式为为:
ReLU(x)=max(0,x) R e L U ( x ) = m a x ( 0 , x )
relu=nn.ReLU(inplace=True)
input=V(t.randn(2,3))
print("input")
print(input)
output=relu(input)
print("output")
print(output)
结果:
input
tensor([[-0.5211, 0.7563, -1.8086],
[ 0.0911, -1.1649, -0.5653]])
output
tensor([[ 0.0000, 0.7563, 0.0000],
[ 0.0911, 0.0000, 0.0000]])
ReLU函数有个inplace参数,如果设为True,他会把输出直接覆盖到原输入中,这样可以节省内存/显存。
之所以可以直接覆盖是因为在计算ReLU的反向传播时,只需根据输出就能够推算出反向传播的梯度。但是只有少数的autograd操作支持inplace操作(如variable.sigmoid_()
),除非你明确地知道自己在做什么,否则一般不要使用inplace操作。
在以上例子中,都是讲每一层的输出直接作为下一层的输入,这种网络成为前馈传播网络(Feedforward Neural Network).对于此网络,如果每次都写复杂的forward函数会有些麻烦,在此就有两种简化方式:ModuleList和Sequential.
其中Sequential是一个特殊的Module,他包含几个子Module,前向传播时会将输入一层一层地传递下去。
ModuleList也是一个特殊的Module,可以包含几个子Module,可以像list一样使用它,但不能直接把输入传给ModuleList.
#Sequential的三种写法
#*******************
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()
)
#*******************
from collections import OrderedDict
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)
结果:
net1 Sequential(
(conv): Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
(batchnorm): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(activation_layer): ReLU()
)
net2 Sequential(
(0): Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
net3 Sequential(
(conv1): Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
(bn1): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU()
)
可以根据名字或序号取出子Module
net1.conv,net2[0],net3.conv1
结果:
(Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1)),
Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1)),
Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1)))
用法:
input=V(t.randn(1,3,4,4))
output=net1(input)
output=net2(input)
output=net3(input)
output=net3.relu1(net1.batchnorm(net1.conv(input)))
input=V(t.randn(1,3,4,4))
print(input.size())
output=net3.relu1(net1.batchnorm(net1.conv(input)))
print(output.size())
结果:
torch.Size([1, 3, 4, 4])
torch.Size([1, 3, 2, 2])
modulelist=nn.ModuleList([nn.Linear(3,4),nn.ReLU(),nn.Linear(4,2)])
input=V(t.randn(1,3))
for model in modulelist:
input=model(input)
除ModuleLis之外还有ParameterList,它是一个可以包含很多个parameter的类list对象。
在实际应用中,使用方式与ModuleList类似。
在构造函数__init__
中用到list, tuple, dict等对象时,一定要考虑是否应该用ModuleLis或ParameterList。