【PyTorch】2.1 使用Pytorch构建一个神经网络

2 PyTorch初步应用(一)

2.1 使用Pytorch构建一个神经网络

学习目标:

  • 掌握用Pytorch构建神经网络的基本流程
  • 掌握用Pytorch构建神经网络的实现过程

关于 torch.nn:

  • 使用Pytorch来构建神经网络,主要的工具都在torch.nn包中
  • nn依赖于autograd来定义模型,并对其自动求导

构建神经网络的典型流程

  • 定义一个拥有科学系参数的神经网络
  • 遍历训练数据集
  • 处理输入数据使其流进神经网络
  • 计算损失值
  • 将网络参数的梯度进行反向传播
  • 以一定的规则更新网络的权重

定义一个PyTorch实现的神经网络

五层的CNN

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)
        #定义三层全连接网络
        self.fc1=nn.Linear(16*6*6,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        """
        :param x:
        :return:
        """
        #在(2,2)的池化窗口下执行最大池化操作
        x=F.max_pool2d(F.relu(self.conv1(x)),(2,2))#扔进第一个卷积层 -》relu-》池化 2*2的池化
        x=F.max_pool2d(F.relu(self.conv2(x)),2)    #扔进第二个卷积层 -》relu-》池化
        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):
        """
        :param x:把经过卷积之后的张量x,计算其size,x是三维张量,取出后面两个维度
        eg: (2,3,4) 返回3*4=12
        :return:
        """
        #计算size,除了第0个维度上的batch_size
        size=x.size()[1:]
        num_features=1
        for s in size:
            num_features*=s
        return num_features

net=Net()
print("the structure of net:",net)
#输出
the structure of 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 of params:",len(params))
print(params[0].size())
print(params[0])
#输出
len of params: 10
torch.Size([6, 1, 3, 3])
Parameter containing:
......

假设图像的输入尺寸为32*32

input=torch.randn(1,1,32,32)
out=net(input)
print(out)
#输出
tensor([[ 4.0471e-02,  8.6946e-02, -1.8538e-02,  5.5225e-02, -9.2332e-02,
          3.0920e-02, -1.9139e-04, -2.0026e-01, -4.5713e-02,  2.6573e-02]],
       grad_fn=<AddmmBackward0>)

有了输出张量之后,据可以执行梯度归零和反向传播操作

注意:

  • torch.nn构建的神经网络只支持mini-batches的输入,不支持单一样本的输入
  • 比如:nn.Conv2d需要一个4D Tensor,形状为(nSamples,nChannels,Height,Width)如果如数只有但一样会本性是,则需要执行input.unsqueeze(0),主动将3D Tensor扩充成4D Tensor

损失函数

  • 损失函数的输入是一个输入的pair(output,target)然后计算出一个数值来评估output和target之间的差距大小
  • 在torch.nn中有若干不同的损失函数可供使用,比如nn.MSELoss就是计算均方差损失来评估输入和目标值之间的差距
  • 应用nn.MSELoss计算损失的一个例子
input=torch.randn(1,1,32,32)
output=net(input)
target=torch.randn(10)

#改变target的形状为二维张量,为了和output匹配
target=target.view(1,-1)
criterion=nn.MSELoss()
loss=criterion(output,target)
print(loss)

该神经网络的方向传播链条

input->conv2d->relu->maxpool2d
->conv2d->relu->maxpool2d
->view->linear->relu->linear->relu->linear
->MSELoss
->loss

当调用loss.backward()时,整张计算图将对loss进行自动求导,所有属性requires_grad=True的Tensors都将参与梯度求导的运算,并将梯度累加到Tensors中的.grad属性中

output=net(input)
target=torch.randn(10)

#改变target的形状为二维张量,为了和output匹配
target=target.view(1,-1)
criterion=nn.MSELoss()
loss=criterion(output,target)
print(loss)
print(loss.grad_fn)
print(loss.grad_fn.next_functions[0][0])
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])
#输出
tensor(0.6516, grad_fn=<MseLossBackward0>)
<MseLossBackward0 object at 0x0000021826CEEE88>
<AddmmBackward0 object at 0x00000218117E8908>
<AccumulateGrad object at 0x0000021826CEEE88>

反向传播

  • 在PyTorch中执行反向传播非常简便,全部的操作就是loss.backward()
  • 在执行反向传播之前,要先将梯度清零,否则梯度会在不同的批次数据之间被累加
  • 执行一个反向传播的demo
# Pytorch 中执行梯度清零的代码
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)

更新为了参数

  • 更新参数最简单的算法就是SGD 随机梯度下降
  • 具体的算法公式表达式为:weight=weight-learning_rate*gradient
  • 首先用传统的Python代码来实现SGD如下:
#使用传统Python代码实现SGD
learning_rate=0.01
for f in net.parameters():
    f.data.sub_(f.grad.data*learning_rate)
  • 使用PyTorch官方推荐的标准代码如下:
#首先导入优化器的包,optim中包含若干常用的优化算法,比如SGD,Adam等
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()
print("DONE")

小节总结

学习构建一个神经网络的典型流程
  • 定义一个拥有可学习参数的神经网络
  • 遍历训练数据集
  • 处理输入数据时期流经神经网络
  • 计算损失值
  • 将网络参数的梯度进行反向传播
  • 以一定的规则更新网络的权重
学习损失函数的定义
  • 在前面的demo中我们使用的都是torch.nn.MSELoss()计算均方误差
  • 通过loss.backward()进行反向传播计算时,整张计算图将对loss进行自动求导,所有属性require_grad=True的Tensors都将参与梯度求导的运算,并将梯度累加到Tensors中的.grad属性中
学习反向传播的计算方法
  • 在Pytorch中执行反向传播非常简便,全部的操作就是loss.backward()
  • 在执行反向传播之前,要先将梯度清零,否则梯度会在不同的不同批次数据之间被累加
net.zero_grad()
loss.backward()
学习参数的更新方法
  • 定义优化器来执行参数的优化与更新
    optimizer=optim.SGD(net.parameters(),lr=0.01)
  • 通过优化器来执行具体的参数更新
    optimizer.step()

本小节涉及的完整代码

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)
        #定义三层全连接网络
        self.fc1=nn.Linear(16*6*6,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        """
        :param x:
        :return:
        """
        #在(2,2)的池化窗口下执行最大池化操作
        x=F.max_pool2d(F.relu(self.conv1(x)),(2,2))#扔进第一个卷积层 -》relu-》池化 2*2的池化
        x=F.max_pool2d(F.relu(self.conv2(x)),2)    #扔进第二个卷积层 -》relu-》池化
        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):
        """
        :param x:把经过卷积之后的张量x,计算其size,x是三维张量,取出后面两个维度
        eg: (2,3,4) 返回3*4=12
        :return:
        """
        #计算size,除了第0个维度上的batch_size
        size=x.size()[1:]
        num_features=1
        for s in size:
            num_features*=s
        return num_features

net=Net()
# print("the structure of net:",net)
params=list(net.parameters())
# print("len of params:",len(params))
# print(params[0].size())
# print(params[0])
input=torch.randn(1,1,32,32)
# out=net(input)
# print(out)
# print(out.size())
#
# net.zero_grad()
# out.backward(torch.randn(1,10))

output=net(input)
target=torch.randn(10)

#改变target的形状为二维张量,为了和output匹配
target=target.view(1,-1)
criterion=nn.MSELoss()
# loss=criterion(output,target)
# print(loss)
# print(loss.grad_fn)
# print(loss.grad_fn.next_functions[0][0])
# print(loss.grad_fn.next_functions[0][0].next_functions[0][0])

# Pytorch 中执行梯度清零的代码
# 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)

#使用传统Python代码实现SGD
# learning_rate=0.01
# for f in net.parameters():
#     f.data.sub_(f.grad.data*learning_rate)


#首先导入优化器的包,optim中包含若干常用的优化算法,比如SGD,Adam等
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()
print("DONE")

Guess you like

Origin blog.csdn.net/kz_java/article/details/121408561