步骤:
构建神经网络的典型流程 定义一个拥有可学习参数的神经网络 遍历训练数据集 处理输入数据使其流经神经网络 计算损失值 将网络参数的梯度进行反向传播 以一定的规则更新网络的权重 以cnn为例
##首先定义一个pytorch实现的神经网洛
# 导入工具包
import torch
import torch.nn as nn
import torch.nn.functional as F
# 定义一个简单的网络类
# 卷积计算(W-F+2P)/S+1
# 此公式中各个元素含义如下所示:
#
# W代表输入图像大小WxW;
#
# F代表Filter大小FxF;
#
# P代表padding的像素数;
#
# S代表步长;
#
# 新矩阵的通道数为设置的卷积核的个数.
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
# 输入通道指的是输入了几个二维信息
# 定义第一层卷积神经网络,输入通道=1,输出通道=6,卷积核大小3*3
self.conv1=nn.Conv2d(1,6,3,stride=1,padding=0)
# 定义第二层卷积神经网络,输入通道=6,输出通道=16,卷积核大小3*3
self.conv2=nn.Conv2d(6,16,3)
# 定义三层全连接网络
# 输入图像为32*32 经过3*3卷积为6*6再乘上16个通道???答因为后面有2*2的池化层
self.fc1=nn.Linear(16*6*6,120)
self.fc2=nn.Linear(120,84)
self.fc3=nn.Linear(84,10)
def forward(self,x):
# 在(2,2)的池化窗口下执行最大池化操作
x=F.max_pool2d(F.relu(self.conv1(x)),(2,2))
print("x1=",x.size())
x=F.max_pool2d(F.relu(self.conv2(x)),2)
print("x2=",x.size())
x=x.view(-1,self.num_flat_features(x))
x=F.relu(self.fc1(x))
x=F.relu(self.fc2(x))
x=self.fc3(x)
return x
def num_flat_features(self,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(
(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)
)
## 模型中所有可训练参数可以通过net.parameters()来获得
params = list(net.parameters())
print(len(params))
# print(params)
print(params[0].size())
10 torch.Size([6, 1, 3, 3])
输入数据
input=torch.randn(1,1,32,32)
print(input.size())
out=net(input)
print(out)
torch.Size([1, 1, 32, 32])
x1= torch.Size([1, 6, 15, 15])
x2= torch.Size([1, 16, 6, 6])
tensor([[-0.0311, 0.0812, 0.0231, -0.0665, -0.1095, -0.0343, 0.0829, -0.1456,
0.0823, -0.0481]], grad_fn=<AddmmBackward0>)
# 有了输出张量后,就可以执行梯度归零和反向传播的操作
net.zero_grad()
out.backward(torch.randn(1,10))
## 注意torch.nn构建的神经网络支支持mini-batch的输入,不支持单一样本的输入
## 比如:nn.conv2d需要一个4D的Tensor,形状为(nsample,nchnnels,height,,width)如果输入只有单一样本形式,
## 则需要执行input.unsqueeze(0),主动将3D Tensor扩充成4D Tensor
损失函数:
## 损失函数是用真实值与预测值算loss反向更新权重
out=net(input)
target=torch.randn(10)
print("output=",out)
print("target=",target)
# 改变target的形状为二维张量,为了和output匹配
target=target.view(1,-1)
print(target)
criterion=nn.MSELoss()
loss=criterion(out,target)
print(loss)
x1= torch.Size([1, 6, 15, 15])
x2= torch.Size([1, 16, 6, 6])
output= tensor([[-0.0311, 0.0812, 0.0231, -0.0665, -0.1095, -0.0343, 0.0829, -0.1456,
0.0823, -0.0481]], grad_fn=<AddmmBackward0>)
target= tensor([-1.1732, -1.1593, -0.1919, 0.4548, -0.1894, -0.8209, 0.0904, 0.3107,
-0.4256, -0.9506])
tensor([[-1.1732, -1.1593, -0.1919, 0.4548, -0.1894, -0.8209, 0.0904, 0.3107,
-0.4256, -0.9506]])
tensor(0.5067, grad_fn=<MseLossBackward0>)
## 跟踪loss反向传播的方向,使用.grad_fn属性打印,可以看到一张完整的计算图
# 下面是正向传播
# input->conv2d->relu->maxpool2d->conv2d->relu->maxpool3d
# ->view->linear->relu->linear->relu->linear
# ->MSELoss
# ->loss
## 当调用loss.backward()时,所有属性requires_grad()=True的tensor都会参与计算,并将其累积到.grad属性
print(loss.grad_fn)
print(loss.grad_fn.next_functions[0][0])#Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])#relu
<MseLossBackward0 object at 0x0000017C80763B08>
<AddmmBackward0 object at 0x0000017C80763A48>
<AccumulateGrad object at 0x0000017C80763B08>
## 反向传播,直接使用loss.backward()
## 每次执行钱需要梯度清零,否则梯度会在不同批次之间被累加
# 清零
net.zero_grad()
print("conv1.bias.grad before backward")
print(net.conv1.bias.grad)
##Pytorch执行反向传播的代码
loss.backward()
print("conv1.bias.grad after backward")
print(net.conv1.bias.grad)
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([ 0.0065, 0.0051, -0.0055, 0.0043, 0.0023, -0.0050])
更新参数
1,传统方法
## 更新参数最简单的方法就是使用SGD
## 计算方法为weight=weight-lr*gradient
## 首先使用原始python代码实现SGD
learningrate=0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learningrate)#做差计算
2,pytorch推荐使用方法
## 使用pytorch推荐标准代码
import torch.optim as optim
## 通过optim创建优化器对象
optimizer = optim.SGD(net.parameters(),lr=0.01) ##重要
## 将优化器执行梯度清零的操作
optimizer.zero_grad()## 重要
output=net(input)
loss=criterion(output,target)
## 对损失值执行反向传播的操作
loss.backward()###重要
##参数的更新通过一行标准代码执行
optimizer.step()###重要