构建神经网络
学习目标
掌握用Pytorch构建神经网络的基本流程.
掌握用Pytorch构建神经网络的实现过程.
关于torch.nn:
使用Pytorch来构建神经网络, 主要的工具都在torch.nn包中.
nn依赖于autograd来定义模型, 并对其自动求导
构建神经网络的典型流程:
定义一个拥有可学习参数的神经网络
遍历训练数据集
处理输入数据使其流经神经网络
计算损失值
将网络参数的梯度进行反向传播
以一定的规则更新网络的权重
定义一个Pytorch实现的神经网络:
# 导入若干工具包
import torch
import torch.nn as nn
import torch.nn.functional as F
# 定义一个简单的网络类
class Net(nn.Module):
def __init__(self):
# 定义初始化函数
super(Net, self).__init__()
# 定义第一层卷积神经网络, 输入通道维度=1, 输出通道维度=6, 卷积核大小3*3
self.conv1 = nn.Conv2d(1, 6, 3)
# 定义第二层卷积神经网络, 输入通道维度=6, 输出通道维度=16, 卷积核大小3*3
self.conv2 = nn.Conv2d(6, 16, 3)
# 定义三层全连接网络,输入图像大小,120自己指定,16 * 6 * 6根据输入的张量推导
self.fc1 = nn.Linear(16 * 6 * 6, 120)
self.fc2 = nn.Linear(120, 84) # 120同上,84,自己指定
self.fc3 = nn.Linear(84, 10) #84同上,最后10个分类
def forward(self, x):
# 前向逻辑
# 在(2, 2)的池化窗口下执行最大池化操作
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x)) # 重新设置成二维,然后放到fc1中,平滑处理
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
# 经过卷积的张量x
# 计算size, 除了第0个维度上的batch_size,维度扁平化
size = x.size()[1:]
num_features = 1
for s in size:
num_features *= s
return num_features
# 实例化类
net = Net()
# 打印类
print(net)
# 模型中所有的可训练参数, 可以通过net.parameters()来获得
params = list(net.parameters())
print(len(params))
# 打印params第0个参数的size
print(params[0].size())
步长(1,1)
输入维度:576,输出维度120
…
…
Net(
(conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
(fc1): Linear(in_features=576, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)
)
10 # 长度是10, self.fc3 = nn.Linear(84, 10)
torch.Size([6, 1, 3, 3]) # 对应(conv1): Conv2d(1, 6, kernel_size=(3, 3)
假设图像的输入尺寸为32 * 32:
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
print(out.size())
输出结果,1行10列
tensor([[ 0.1242, 0.1194, -0.0584, -0.1140, 0.0661, 0.0191,
-0.0966, 0.0480, 0.0775, -0.0451]], grad_fn=<AddmmBackward>)
torch.Size([1,10])
有了输出张量后, 就可以执行梯度归零和反向传播的操作了
net.zero_grad()
out.backward(torch.randn(1, 10))
torch.nn构建的神经网络只支持mini-batches的输入, 不支持单一样本的输入.
比如: nn.Conv2d 需要一个4D Tensor, 形状为(nSamples, nChannels,
Height, Width). 如果你的输入只有单一样本形式, 则需要执行
input.unsqueeze(0), 主动将3D Tensor扩充成4D Tensor
torch.nn.Linear()函数理解
class torch.nn.Linear(in_features,out_features,bias = True)
从init函数中可以看出Linear中包含四个属性:
1)in_features: 上层神经元个数【每个输入样本的大小】
2)out_features: 本层神经元个数【每个输出样本的大小】
3)weight:权重,形状[out_features , in_features]
4)bias: 偏置,形状[out_features]。网络层是否有偏置,默认存在,且维度为[out_features ];若bias=False,则该网络层无偏置,图层不会学习附加偏差。
Pytorch的nn.Conv2d()详解
in_channels
输入的四维张量[N, C, H, W]中的C了,即输入张量的channels数。这个形参是确定权重等可学习参数的shape所必需的。
out_channels
期望的四维输出张量的channels数
kernel_size
卷积核的大小,般我们会使用5x5、3x3这种左右两个数相同的卷积核,因此这种情况只需要写kernel_size = 5这样的就行了。如果左右两个数不同,比如3x5的卷积核,那么写作kernel_size = (3, 5),注意需要写一个tuple,而不能写一个列表(list)。
stride = 1
卷积核在图像窗口上每次平移的间隔,即所谓的步长。同Tensorflow
padding = 0
Pytorch与Tensorflow在卷积层实现上最大的差别就在于padding上。
Padding即所谓的图像填充,后面的int型常数代表填充的多少(行数、列数),默认为0。
这里的填充包括图像的上下左右。以padding = 1为例,若原始图像大小为32x32,那么padding后的图像大小就变成了34x34,而不是33x33。
Pytorch不同于Tensorflow的地方在于,Tensorflow提供的是padding的模式,比如same、valid Reference,且不同模式对应了不同的输出图像尺寸计算公式。而Pytorch则需要手动输入padding的数量,当然,Pytorch这种实现好处就在于输出图像尺寸计算公式是唯一的。
大多数情况下的kernel_size、padding左右两数均相同,且不采用空洞卷积(dilation默认为1),因此只需要记这种在深度学习课程里学过的公式就好了。
O = (I - K + 2P)/ S +1
o 为output的size
i 为input的size
k 为kernal的size
p 为padding的取值
dilation = 1
这个参数决定了是否采用空洞卷积,默认为1(不采用)。从中文上来讲,这个参数的意义从卷积核上的一个参数到另一个参数需要走过的距离,那当然默认是1了,毕竟不可能两个不同的参数占同一个地方吧(为0)。
groups = 1
决定了是否采用分组卷积解释
bias = True
是否要添加偏置参数作为可学习参数的一个,默认为True。
padding_mode = ‘zeros’
padding的模式,默认采用零填充。