课后习题:
假如你用全连接层处理一张256X256的彩色(RGB)图像,输出包含1000个神经元,在使用偏置的情况下,参数数量是:19660900
解析:图像展平后长度为3X256X256,权重参数和偏置参数的数量是3X256X256X1000+1000=19660900
假设用全连接层处理一张256X256的彩色RGB图像,卷积核的高宽是3X3,输出包含10个通道,在使用偏置的情况下,这个卷积层共有多少个参数:280
输入通道数是3,输出通道数是10,所以参数是10X3X3X3+10=280
conv2d = nn.Conv2d(in_channels=3, out_channels=4, kernel_size=3, padding=2),输入一张形状为3×100×100的图像,输出的形状为:
4X102X102
输出通道数是4,上下两侧总共填充4行,卷积核高度是3,所以输出的高度是104 - 3 + 1=102,宽度同理可得。
池化层,参与模型的正向计算,同样也会参加模型的反向传播。
二维互相关运算
代码如下:
import torch
import torch.nn as nn
def corr2d(X, K):
H, W = X.shape
h, w = K.shape
Y = torch.zeros(H - h + 1, W - w + 1)
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y[i, j] = (X[i: i + h, j: j + w] * K).sum()
return Y
卷积层的实现:
class Conv2D(nn.Module):
def __init__(self, kernel_size):
super(Conv2D, self).__init__()
self.weight = nn.Parameter(torch.randn(kernel_size))
self.bias = nn.Parameter(torch.randn(1))
def forward(self, x):
return corr2d(x, self.weight) + self.bias
X = torch.ones(6, 8)
Y = torch.zeros(6, 7)
X[:, 2: 6] = 0
Y[:, 1] = 1
Y[:, 5] = -1
print(X)
print(Y)
conv2d = Conv2D(kernel_size=(1, 2))
step = 30
lr = 0.01
for i in range(step):
Y_hat = conv2d(X)
l = ((Y_hat - Y) ** 2).sum()
l.backward()
# 梯度下降
conv2d.weight.data -= lr * conv2d.weight.grad
conv2d.bias.data -= lr * conv2d.bias.grad
# 梯度清零
conv2d.weight.grad.zero_()
conv2d.bias.grad.zero_()
if (i + 1) % 5 == 0:
print('Step %d, loss %.3f' % (i + 1, l.item()))
print(conv2d.weight.data)
print(conv2d.bias.data)
首先可以把这个函数理解为类型转换函数,将一个不可训练的类型Tensor转换成可以训练的类型parameter并将这个parameter绑定到这个module里面(net.parameter()中就有这个绑定的parameter,所以在参数优化的时候可以进行优化的),所以经过类型转换这个self.v变成了模型的一部分,成为了模型中根据训练可以改动的参数了。使用这个函数的目的也是想让某些变量在学习的过程中不断的修改其值以达到最优化。
conv2d.weight的类型是torch.nn.parameter.Parameter
conv2d.weight.data的类型是Tensor
难点讲解:
X = torch.rand(4, 2, 3, 5)
print(X.shape)
conv2d = nn.Conv2d(in_channels=2, out_channels=3, kernel_size=(3, 5), stride=1, padding=(1, 2))
Y = conv2d(X)
print('Y.shape: ', Y.shape)
print('weight.shape: ', conv2d.weight.shape)
print('bias.shape: ', conv2d.bias.shape)
运行结果如下:
torch.Size([4, 2, 3, 5])
Y.shape: torch.Size([4, 3, 3, 5])
weight.shape: torch.Size([3, 2, 3, 5])
bias.shape: torch.Size([3])
我们使用Pytorch中的nn.Conv2d
类来实现二维卷积层,主要关注以下几个构造函数参数:
in_channels
(python:int) – Number of channels in the input imagout_channels
(python:int) – Number of channels produced by the convolutionkernel_size
(python:int or tuple) – Size of the convolving kernelstride
(python:int or tuple, optional) – Stride of the convolution. Default: 1padding
(python:int or tuple, optional) – Zero-padding added to both sides of the input. Default: 0bias
(bool, optional) – If True, adds a learnable bias to the output. Default: Trueforward
函数的参数为一个四维张量,形状为( N N N, C i n C_{in} Cin, H i n H_{in} Hin, W i n W_{in} Win),返回值也是一个四维张量,形状为( N N N, C o u t C_{out} Cout, H o u t H_{out} Hout, W o u t W_{out} Wout),其中 N N N是批量大小, C C C, H H H, W W W分别表示通道数、高度、宽度。
输入的维度是(4,2,3,5)
其中,4是批量大小,该次训练有4张图片,2是通道数,就是每张图片有2个通道,3是每张图片每个通道的高度上由3个数字,5是每张图片每个通道的宽度上有5个数字
conv2d = nn.Conv2d(in_channels=2, out_channels=3, kernel_size=(3, 5), stride=1, padding=(1, 2))
输入通道是2,对应(4,2,3,5)中的2,输出通道是3,核的大小是3X5,步长为1,填充是(1,2)H方向填充是1,W方向填充为2
所以输出图像中:
H方向=(3+1X2-3+1)=3
W方向= (5+2X2-5+1)=5
所以输出图像的大小是[4,3,3,5]。
一共有3个通道,所以,weight的大小的第一个数字是3
输入通道是2,所以,weight的大小的第二个数字是2
剩下就是代码设置的3,5
如何理解呢?
就是,每个图片输入是2X3X5的,代码设置的是3X5,设置的是一个通道的大小,但实际上有2个通道,所以后面的数字是[2,3,5],输出几个通道,就代表需要几个卷积核卷积几次,所以第一个数字是3
如果原输入的高和宽是 n h n_h nh和 n w n_w nw,卷积核的高和宽是 k h k_h kh和 k w k_w kw,在高的两侧一共填充 p h p_h ph行,在宽的两侧一共填充 p w p_w pw列,则输出形状为:
( n h + p h − k h + 1 ) × ( n w + p w − k w + 1 ) (n_h+p_h-k_h+1)\times(n_w+p_w-k_w+1) (nh+ph−kh+1)×(nw+pw−kw+1)