1.3 Neural Networks

本文详细介绍如何使用PyTorch构建神经网络,包括卷积神经网络的搭建、前向传播流程、损失函数计算及反向传播,同时展示了权重更新的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.3 Neural Networks

使用torch.nn包来构建神经网络。

上一讲已经讲过了autograd,nn包依赖autograd包来定义模型并求导。 一个nn.Module包含各个层和一个forward(input)方法,该方法返回output。

例如:

关于卷积神经网络的基础介绍,可以参考这里:https://github.com/zergtant/pytorch-handbook/blob/master/chapter2/2.4-cnn.ipynb

import torch
torch.__version__   #打印版本
'1.1.0'

在这里,我们通常有一些约定:

import torch.nn as nn将torch.nn别名为nn

import torch.nn.functional as F将torch.nn.functional别名为F

这样别名方便后面调用。

torch.nn.functional,这个包中包含了神经网络中使用的一些常用函数,这些函数的特点是,不具有可学习的参数(如ReLU,pool,DropOut等),这些函数可以放在构造函数中,也可以不放,但是这里建议不放。

在这里插入图片描述
简单的前馈神经网络,它接受一个输入,然后一层接着一层地传递,最后输出计算的结果。

神经网络的典型训练过程如下:

定义包含一些可学习的参数(或者叫权重)神经网络模型;

在数据集上迭代;

通过神经网络处理输入;

计算损失(输出结果和正确值的差值大小);

将梯度反向传播回网络的参数;

更新网络的参数,主要使用如下简单的更新原则: weight = weight - learning_rate * gradient

super() 函数是用于调用父类(超类)的一个方法,通过该函数调用Net的超类,然后通过 super(Net,self).init()进行初始化,因为__init__()也称为构造方法中的属性无法被继承,所以通过这种方式调用超类中的属性,如果在超类中找不到该属性会继续查找超类的超类等。
关于super函数的详细介绍:https://www.runoob.com/python/python-func-super.html

torch.nn.Conv2d函数:

Torch.nn.Conv2d(in_channels,out_channels,kernel_size,stride=1,padding=0,dilation=1,groups=1,bias=True)

in_channels:输入通道数

out_channels:输出通道数

kernel_size:滤波器(卷积核)大小,用f表示,宽和高相等的卷积核可以用一个数字表示,例如kernel_size=3;否则用不同数字表示,例如kernel_size=(5,3)

stride : 表示滤波器滑动的步长,用s表示;

padding:是否进行零填充,padding=0表示四周不进行零填充,padding=1表示四周进行1个像素点的零填充,用p表示

bias:默认为True,表示使用偏置

假设输入图像大小为n*n,那么,用卷积核对输入图像卷积之后,输出图像的大小是多少呢?公式如下:
n − f + 2 p s + 1 \frac{n-f+2 p}{s}+1 snf+2p+1

关于stride、padding、dilation的理解可以看这里的动画演示:https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md#dilated-convolution-animations

groups:这个参数是指将输入沟道分为几组进行计算输出沟道,如果groups为1,就是只有一组,就是说每一个输出沟道的计算要用到所有的输入沟道;如果groups为2,就是把输入沟道分为2组,在计算每一个输出沟道时,每一次只用其中一组的输入沟道来计算,以此类推。

torch.nn.Linear函数:

torch.nn.Linear(in_features,out_features,bias = True )

对传入数据应用线性变换:y = Ax+ b

举例子:

m = torch.nn.Linear(20, 30)

x=torch.randn(128, 20)

out=m(x)

产生的变量有weight和bias,其中weight的形状是[30, 20],bias的形状是[30]

其功能就是:out1=torch.mm(x, m.weight.t()) + m.biastorch.mm表示两个矩阵相乘,

out.shape:[128,30]

也就是,out=out1

参数:

in_features - 每个输入样本的大小

out_features - 每个输出样本的大小

bias - 如果设置为False,则图层不会学习附加偏差。默认值:True

Shape:

Input: (N,in_features)

Output: (N,out_features)

Variables:

Linear.weight,学习权重,shape:(out_features,in_features).

Linear.bias,偏置,shape:out_features

note:设置完初始值,根据反向传播梯度值修正参数

详细可见:https://pytorch.org/docs/master/nn.html?highlight=nn linear#torch.nn.Linear

torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False):

kernel_size –核的大小,用f表示

stride – 滑动步长,用s表示

padding – 是否填充0,用p表示

dilation – a parameter that controls the stride of elements in the window

return_indices – if True, will return the max indices along with the outputs. Useful for torch.nn.MaxUnpool3d later

ceil_mode – when True, will use ceil instead of floor to compute the output shape

假设输入大小为n*n,经过池化后的输出大小为,公式: n − f s + 1 \frac{n-f}{s}+1 snf+1

原理如下:

在这里插入图片描述 torch.nn.ReLU(inplace=False)

修正线性单元(Rectified linear unit,ReLU)

非线性激活函数的一种,同类型的函数还有sigmoid函数,tanh函数,softplus函数等等。

公式:ReLU(x)=max(0,x),具有单侧抑制作用

sigmoid函数为sigmoid(x)= 1/(1+e^-x),而Softplus(x)=log(1+ex)。

它的作用是如果计算出的值小于0,就让它等于0,否则保持原来的值不变,训练后的网络完全具备适度的稀疏性。

定义网络

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 input image channel, 6 output channels, 5x5 square convolution
        # kernel
        self.conv1 = nn.Conv2d(1,6,5) #构造输入通道为1,输出通道为6,卷积核大小5*5的卷积层
        self.conv2 = nn.Conv2d(6,16,5)
        #放射操作:y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 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))
        x = F.max_pool2d(F.relu(self.conv2(x)),2)  #如果卷积核尺寸长宽相同,只需要指定一个
        x = x.view(-1,self.num_flat_features(x)) #x.view调整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 = 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=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=400, 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)
)





<bound method Module.parameters of Net(
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=400, 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)
)>

在模型中必须要定义 forward 函数,backward 函数(用来计算梯度)会被autograd自动创建。 可以在 forward 函数中使用任何针对 Tensor 的操作。

net.parameters()返回可被学习的参数(权重)列表和值

params = list(net.parameters())
print(len(params))
print(params[0].size()) #conv1的权重
10
torch.Size([6, 1, 5, 5])

测试随机输入32×32。 注:这个网络(LeNet)期望的输入大小是32×32,如果使用MNIST数据集来训练这个网络,请把图片大小重新调整到32×32。

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
tensor([[-2.5615, -1.4061,  0.5991,  ...,  1.1176,  0.4557,  1.8589],
        [-1.7022, -2.6855, -0.1328,  ...,  0.3439, -0.1854,  0.4611],
        [-0.6718,  1.2699,  0.6530,  ..., -0.3008,  0.3943,  2.4400],
        ...,
        [-1.5000, -0.7443, -0.9159,  ..., -1.1712,  0.1082,  0.6138],
        [ 0.7130,  0.2771, -0.7458,  ..., -0.2253, -0.1215, -0.5637],
        [ 0.1070,  0.0779,  0.1620,  ...,  0.0665,  1.4905, -0.2640]])
tensor([[ 0.0322,  0.0024, -0.0632,  0.0245, -0.0616,  0.0039,  0.0120,  0.0593,
         -0.0247,  0.0740]], grad_fn=<AddmmBackward>)

将所有参数的梯度缓存清零,然后进行随机梯度的的反向传播:

net.zero_grad()
out.backward(torch.randn(1,10))

NOTE:

torch.nn 只支持小批量输入。整个 torch.nn 包都只支持小批量样本,而不支持单个样本。 例如,nn.Conv2d 接受一个4维的张量, 每一维分别是sSamples * nChannels * Height * Width(样本数*通道数*高*宽)。 如果你有单个样本,只需使用 input.unsqueeze(0) 来添加其它的维数.

其中,通道数的概念,对一幅灰度图来说,通道数为1;对一幅RGB彩色图来说,通道数为3.

在继续之前,我们回顾一下到目前为止用到的类。

回顾:

torch.Tensor:一个用过自动调用 backward()实现支持自动梯度计算的多维数组 , 并且保存关于这个向量的梯度 w.r.t.

nn.Module:神经网络模块。封装参数、移动到GPU上运行、导出、加载等。

nn.Parameter:一种变量,当把它赋值给一个Module时,被 自动 地注册为一个参数。

autograd.Function:实现一个自动求导操作的前向和反向定义,每个变量操作至少创建一个函数节点,每一个Tensor的操作都回创建一个接到创建Tensor和 编码其历史 的函数的Function节点。

重点如下:

定义一个网络

处理输入,调用backword

还剩:

计算损失

更新网络权重

损失函数

一个损失函数接受一对 (output, target) 作为输入,计算一个值来估计网络的输出和目标值相差多少。

output为网络的输出,target为实际值

nn包中有很多不同的损失函数。 nn.MSELoss是一个比较简单的损失函数,它计算输出和目标间的均方误差,

例如

output = net(input)
target = torch.randn(10)  # 随机值作为样例
target = target.view(1, -1)  # 使target和output的shape相同
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)
tensor(0.7265, grad_fn=<MseLossBackward>)
print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU
<MseLossBackward object at 0x0000013F5B232438>
<AddmmBackward object at 0x0000013F5B232198>
<AccumulateGrad object at 0x0000013F5B232438>

反向传播

调用loss.backward()获得反向传播的误差。

但是在调用前需要清除已存在的梯度,否则梯度将被累加到已存在的梯度。

现在,我们将调用loss.backward(),并查看conv1层的偏差(bias)项在反向传播前后的梯度。

net.zero_grad()  #将所有模型参数梯度置零
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

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.])

梯度下降(SGD):

weight = weight - learning_rate * gradient

我们可以使用简单的Python代码实现这个规则:

learning_rate = 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
但是当使用神经网络是想要使用各种不同的更新规则时,比如SGD、Nesterov-SGD、Adam、RMSPROP等,PyTorch中构建了一个包torch.optim实现了所有的这些规则。 使用它们非常简单:

import torch.optim as optim

#创建一个权重更新函数
optimizer = optim.SGD(net.parameters(),lr=0.01)

#训练
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值