PyTorch学习日志_20201030_神经网络

日期:2020.10.30
主题:PyTorch入门
内容:

  • 根据PyTorch官方教程文档,学习PyTorch中神经网络
    包括:定义网络、损失函数、反向传播、更新权重。

  • 根据自己的理解和试验,为代码添加少量注解。

具体代码如下 ↓

from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.functional as F

"""
{神经网络}
 通过torch.nn包来构建神经网络
    它依赖于autograd包来定义模型并对它们求导。
 一个nn.Module包含各个层和一个forward(input)方法,该方法返回output。
"""

"""
 一个神经网络的典型训练过程如下:
    1.定义包含一些可学习参数(或者叫权重)的神经网络;
    2.在输入数据集上迭代;
    3.通过网络处理输入;
    4.计算loss(输出和正确答案的距离);
    5.将梯度反向传播给网络的参数;
    6.更新网络的权重,一般使用一个简单的规则:
        weight = weight - learning_rate * gradient
"""

"""
【定义网络】
"""
class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        
        # 输入图像channel:1;输出channel:6;5x5卷积核
        self.conv1 = nn.Conv2d(1, 6, 5)     # 定义二维卷积 1 -> 6
        self.conv2 = nn.Conv2d(6, 16, 5)    # 定义二维卷积 6 -> 16
        
        # an affine operation: 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):
        # 2x2 Max pooling 池化操作
        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 = 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
# 只需要定义forward函数,backward函数会在使用autograd时自动定义,backward函数用来计算导数。
# 可以在 forward 函数中使用任何针对张量的操作和计算

net = Net()
print(net)
print('-'*40, '\n')

"""
 二维卷积
 torch.nn.Conv2d(in_channels: int, out_channels: int, 
                 kernel_size: Union[T, Tuple[T, T]])
    
    *channel
        即通道,最初是指电子图片中RGB通道这样的配色方案,
        例如一张RGB图片可以用一个64x64x3的张量来表示,其中,channel=3,
            分别为红色(Red)、绿色(Green)、蓝色(Blue)三个通道。
            进一步,对RGB图片进行卷积操作后,根据过滤器的数量可产生更多的通道——特征图。
        故,一个通道是对某个特征的检测,通道中某一处数值的强弱就是对当前特征强弱的反应。
        
    in_channels
        输入的四维张量[N, C, H, W]中的C,即输入张量的channels数。
        这个形参是确定权重等可学习参数的shape所必需的。
        
    out_channels
        即期望的四维输出张量的channels数。
        这里卷积层的权重和偏置初始化都是采用He初始化的,适合于ReLU函数。
    
    *kernel
        即核函数K(kernel function),指K(x, y) = <f(x), f(y)>,
            其中x和y是n维的输入值,f() 是从n维到m维的映射(通常而言,m>>n)。
            <x, y>是x和y的内积(inner product),亦称点积(dot product)
        它有助于省去在高维空间里进行繁琐计算的“简便运算法”。
            甚至,它能解决无限维空间无法计算的问题!
            (因为有时f()会把n维空间映射到无限维空间)
    
    kernel_size
        即卷积核的大小,一层卷积核的中心pixel可以“看到”输入图 a*b 的区域(连通性)
        一般使用5x5、3x3这种左右两个数相同的卷积核,
            因此这种情况只需要写kernel_size = 5即可。
        若左右两个数不同,比如3x5的卷积核,
            则写作kernel_size = (3, 5),
            注意需要写一个tuple,而不能写一个列表(list)。
        
"""

"""
 线性变换(连同偏置),即 y = x * (W)^T + b
 torch.nn.Linear(in_features: int, out_features: int)
    
    in_features 
        输入张量的大小
    
    out_features
        输出张量的大小
 
"""

# 通过net.parameters()返回模型的可学习参数
params = list(net.parameters())
print(len(params))
print(params[0].size())  # conv1's .weight
print('-'*40, '\n')


# 尝试一个随机的32x32的输入(该网络(LeNet)的期待输入是32x32的张量)。
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
print('-'*40, '\n')

#清零所有参数的梯度缓存,然后进行随机梯度的反向传播
net.zero_grad()
out.backward(torch.randn(1, 10))


"""
 torch.nn只支持小批量处理(mini-batches)。整个torch.nn包只支持小批量样本的输入,不支持单个样本的输入。
    比如,nn.Conv2d 接受一个4维的张量,即nSamples x nChannels x Height x Width
    如果是一个单独的样本,只需要使用input.unsqueeze(0)来添加一个“假的”批大小维度。
"""


"""
 <回顾>
 
 torch.Tensor
    一个多维数组,支持诸如backward()等的自动求导操作,同时也保存了张量的梯度。
    
 nn.Module
    神经网络模块。是一种方便封装参数的方式,具有将参数移动到GPU、导出、加载等功能。
    
 nn.Parameter
    张量的一种,当它作为一个属性分配给一个Module时,它会被自动注册为一个参数。
    
 autograd.Function
    实现了自动求导前向和反向传播的定义,
    每个Tensor至少创建一个Function节点,该节点连接到创建Tensor的函数并对其历史进行编码。
        
"""


"""
【损失函数】
 函数接受一对(output, target)作为输入,计算一个值来估计网络的输出和目标值相差多少。
"""
# nn.MSELoss是比较简单的一种损失函数,计算输出和目标的均方误差(mean-squared error)。
output = net(input)
target = torch.randn(10)  # 本例子中使用模拟数据
target = target.view(1, -1)  # 使目标值与数据值尺寸一致
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)
print('-'*40, '\n')


"""
 如果使用loss的.grad_fn属性跟踪反向传播过程,会看到计算图如下
 input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
       -> view -> linear -> relu -> linear -> relu -> linear
       -> MSELoss
       -> loss

 所以,当调用loss.backward(),整张图开始关于loss微分,
    图中所有设置了requires_grad=True的张量的.grad属性累积着梯度张量。    
"""
# 为了说明这一点,让我们向后跟踪几步
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
print('-'*40, '\n')


"""
【反向传播】
 我们只需要调用loss.backward()来反向传播误差。
 我们需要清零现有的梯度,否则梯度将会与已有的梯度累加。
"""

# 调用loss.backward(),并查看conv1层的偏置(bias)在反向传播前后的梯度。
net.zero_grad()     # 清零所有参数(parameter)的梯度缓存

print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
print('-'*40, '\n')


"""
【更新权重】
 最简单的更新规则是随机梯度下降法(SGD): 
    weight = weight - learning_rate * gradient
"""
learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)
    
    
# 然而,在使用神经网络时,可能希望使用各种不同的更新规则,如SGD、Nesterov-SGD、Adam、RMSProp等。
# 为此,torch.optim包实现了所有的这些方法。
import torch.optim as optim

# 创建优化器(optimizer)
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、付费专栏及课程。

余额充值