pytorch神经网络零基础(1)

官网 https://www.pytorch123.com/SecondSection/neural_networks/

毕设第二周任务–搭建一个简易的神经网络

  1. 明确任务(需要操作的步骤)

1.定义一个包含可训练参数的神经网络
2.迭代整个输入

3.通过神经网络处理输入

4.计算损失(loss)

5.反向传播梯度到神经网络的参数

6.更新网络的参数,典型的用一个简单的更新方法:weight = weight - learning_rate *gradient

1. 定义网络

写在前面

(1) conv2d()卷积函数各参数解析

还有很多函数的解析参考这篇文章
https://blog.csdn.net/qq_38262266/article/details/100096292

(2) an affine operation: y = Wx + b
  1. 仿射函数(affine function)即由 1 阶多项式构成的函数,一般形式为 f ( x ) = A x + b ,
  2. 这里,A 是一个 m × k 矩阵,x 是一个k向量,b 是一个 m 向量,实际上反映了一种从 k 维到 m 维的空间映射关系。
  3. 仿射函数的作用是维度改变或者形状、方向改变,这个过程叫做仿射变换。
    特别的当m = 1 时,向量转化为一个实数,维度由k降低到1(m)。
(3) init 函数

初始化函数,__init__函数中需要调用父类的构造函数,并定义需要训练的参数。在本例中,只有卷积层和全连接层的权重需要被训练。并且其中定义的方法在初始化实例的时候就会自动执行。

(4) forward 函数
  1. forward函数内主要定义不需要被训练的参数,在本例中主要是将卷积结果进行非线性化的ReLU函数以及池化层。
  2. F.max_pool2d(F.relu(self.conv1(x)), (2, 2))函数为池化(下采样)操作,它的输入是第一层卷积层的输出通过ReLU函数变换后的结果,(2,2)则表示池化操作的窗口大小。当然,在窗口是正方形的时候,2的写法等同于(2,2)
  3. x.view(-1, self.num_flat_features(x))的操作是将最后一层池化层的输出进行resize,为之后全连接层的输入做准备。第一个参数-1表示根据第二个参数自动推断第一维的大小。
  4. forward函数中调用了一个辅助函数num_flat_features(self, x),用来求出最后一层池化层的单元个数。。

代码

# 引入包
import torch
import torch.nn as nn
import torch.nn.functional as F

'''
笔记
1. 经典的MNIST数据集。这个数据集由手写数字0~9的黑白照片组成
2. 各个变量的名称: 权值(weights)和偏移(bias),梯度(gradient)
3. @表示点积(dot production),
4. 批量(batch),向前过程(forward pass), stride:步长
'''


# 定义一个类,继承自nn包的module,这个模块中包含类和方法
class Net(nn.Module):
    # 初始化函数,在定义对象的时候会自动执行,self是必须有的,且是第一个参数
    def __init__(self):
        super(Net, self).__init__()  # 对继承自父类的属性进行初始化
        # 1 input image channel(通道), 6 output channels, 5x5 square convolution(卷积)
        # kernel(内核)
        # 添加第一个卷积层,调用了nn里面的Conv2d()
        # 1表示输入单通道图片((说明是单通道,灰色的图片),用6个5*5 的卷积核
        self.conv1 = nn.Conv2d(1, 6, 5) 
         # 卷积层2,上一个的输出作为当前的输入,输入为6个channel,用16个3x3的卷积核
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y = Wx + b
        # 定义了三个全连接层,即fc1与conv2相连,将16张5*5的卷积网络一维化,并输出120个节点。
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
		# 将120个节点转化为84个。
        self.fc2 = nn.Linear(120, 84)
        # 将84个节点输出为10个,即有10个分类结果。
        self.fc3 = nn.Linear(84, 10)
# forward函数内主要定义不需要被训练的参数,
#在本例中主要是将卷积结果进行非线性化的ReLU函数以及池化层
    def forward(self, x):
        # Max pooling over a (2, 2) window
        #池化层将卷积结果进行ReLU变换增加非线性拟合能力
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        # view相当于resize,-1表示自动推断维数,拉成了一维,总特征数不变,为接下来的全连接层做准备
        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 = x.size()[1:]  # all dimensions except the batch dimension
        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)
)

'''
# 现在我们需要来看一看我们的模型,下列语句可以帮助你看一下这个模型的一些具体情况。
# 十个参数是因为 weight和bias分开作为两个参数,两个卷积层和三个全连接层
params = list(net.parameters())
print(len(params))
print(params[0].size())  # conv1's .weight
print(params[1].size())  # conv1's .bias
'''
10
torch.Size([6, 1, 5, 5])
torch.Size([6])
'''

23.迭代整个输入,处理输入

  1. 我们需要生成一张输入的“图片”,方法是使用Pytorch自带的函数随机生成一个Tensor。我们再把这个图片“喂”给我们刚刚定义的网络,代码如下。
  2. 注意:nn.Module能够接收的输入只能为mini_batch,也就是若干条训练数据为一组进行输入。虽然这里只有一张图片,但我们也需要显示声明当前batch的大小为1,也就是通过input的第一维设置为1来说明。
#第一个1是指batch大小为1,第二个是channel为1,后面是图片大小
input = torch.randn(1, 1, 32, 32) 
out = net(input) 
print(out)

'''
tensor([[-0.1152, -0.1095, -0.0269,  0.1053,  0.1754, -0.0459,  0.0563,  0.0296,
          0.1168,  0.0202]], grad_fn=)
'''

4.计算损失函数

我们定义了一个损失函数后,将目标输出和实际输出作为参数传入即可计算出Tensor类型的loss,实现如下。

output = net(input)
target = torch.randn(10)  # a dummy target, for example
target = target.view(1, -1)  # make it the same shape as output
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)

'''
tensor(1.2039, grad_fn=)
'''

5. 反向传播

  1. 为了实现反向传播损失,我们所有需要做的事情仅仅是使用 loss.backward()。你需要清空现存的梯度,要不然帝都将会和现存的梯度累计到一起。net.zero_grad()
  2. 同一轮训练中,backward操作只能执行一次。原因是,执行过一次梯度计算后,计算图的缓冲区就会被释放,再次调用backward就会报错,除非执行下一轮训练。当然,你也可以指明backward的retain_graph参数为True,这样就会保留计算图。
net.zero_grad()     # zeroes the gradient buffers of all parameters

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
None
conv1.bias.grad after backward
tensor([ 0.0151,  0.0026,  0.0050,  0.0047, -0.0076,  0.0098])
'''

6.更新网络参数

有了梯度,我们就可以去更新权重了。最简单的方法就是拿参数原来的值减去梯度与学习率的乘积,就如下面代码所示:weight = weight - learning_rate * gradient

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

但是Pytorch提供了丰富的工具来更新参数,并封装在torch.optim中。权重更新的实现如下:

import torch.optim as optim
 
# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)
 
# in your training loop:
optimizer.zero_grad()   # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()

你可能感兴趣的:(pytorch神经网络零基础(1))