pytorch-11 神经网络的学习

一、梯度下降中的两个关键问题

1 找出梯度向量的方向和大小

2 让坐标点移动起来(进行一次迭代)

二、找出距离和方向:反向传播

1 反向传播的定义与价值

我们是从左向右,从输出向输入,逐渐往前求解导数的表达式,并且我们所使用的节点上的张量,也是从后向前逐渐用到,这和我们正向传播的过程完全相反。这种从左到右,不断使用正向传播中的元素对梯度向量进行计算的方式,就是反向传播。

2 PyTorch实现反向传播

# 3分类,500个样本,20个特征,共3层,第一层13个神经元,第二层8个神经元
# 第一层的激活函数时relu,第二层的激活函数是sigmoid

import torch
import torch.nn as nn
from torch.nn import CrossEntropyLoss
from torch.nn import functional as F

#确定数据
torch.manual_seed(420)
X = torch.rand((500,20),dtype=torch.float32) * 100
y = torch.randint(low=0,high=3,size=(500,),dtype=torch.float32)
input_ = X.shape[1]         # 特征的数目    
output_ = len(y.unique())   # 分类的数目

# 定义神经网络的架构
# logsoftmax + NLLloss / CrossEntropyLoss
# BCE,BCEWithLogitsLoss
class Model(nn.Module):
    def __init__(self,in_features=40,out_features=2):
        super().__init__()
        self.linear1 = nn.Linear(in_features,13,bias=False)
        self.linear2 = nn.Linear(13,8,bias=False)
        self.output = nn.Linear(8,out_features,bias=True)
    
    def forward(self, x):
        sigma1 = torch.relu(self.linear1(x))
        sigma2 = torch.sigmoid(self.linear2(sigma1))
        zhat = self.output(sigma2)
        return zhat
    
torch.manual_seed(420)
net = Model(in_features=input_, out_features=output_)				# 实例化神经网络
zhat = net.forward(X)				# 向前传播

criterion = CrossEntropyLoss()      # 定义损失函数
loss = criterion(zhat, y.long())    # 计算损失函数

net.linear1.weight.grad #还没有梯度

loss.backward(retain_graph=True)    # 反向传播

net.linear1.weight.grad
net.linear1.weight.shape

三、移动坐标点

1 走出第一步

# 权重更新: w(t+1) = w(t) - 步长 * grad
lr = 0.1                         # learning_rate, 0.001,0.01,0.05
w = net.linear1.weight.data     #现有的权重,w(t)
dw = net.linear1.weight.grad    # 本轮梯度, grad

w = w - lr * dw     # 更新权重w

2 动量法Momentum:从第一步到第二步

提升梯度下降的速度。

#momentum
# v(t) = gamma * v(t-1)  - lr * dw
# w(t+1) = w(t) + v(t)
lr = 0.1
gamma = 0.9

w = net.linear1.weight.data     #现有的权重,w(t)
dw = net.linear1.weight.grad    # 本轮梯度, grad

#t = 1,走第一步,进行首次迭代的时候,需要一个v0
dw.shape        #500,20
v = torch.zeros(dw.shape[0], dw.shape[1])

# v(t) = gamma * v(t-1)  - lr * dw
# w(t+1) = w(t) + v(t)
v = gamma * v - lr * dw		# 更新动量
w += v						# 更新权重

3 torch.optim实现带动量的梯度下降


# 导入库
# 确定数据、超参数的确定(lr,gamma)
# 定义伸进网络的架构类Model,类Model需要输入的参数
# 实例化神经网络的类 - 让神经网络准备好进行正向传播
# 定义损失函数
# 定义优化算法

import torch
import torch.nn as nn
import torch.optim as optim
from torch.nn import functional as F

#确定数据
torch.manual_seed(420)
X = torch.rand((500,20),dtype=torch.float32) * 100
y = torch.randint(low=0,high=3,size=(500,),dtype=torch.float32)

lr = 0.1
gamma = 0.9

#定义神经网路的架构
class Model(nn.Module):
    """
        这是一个三分类的神经网络,因此我们需要调用的损失函数多分类交叉熵函数CEL
        CEL类已经内置了softmax功能,因此我们需要修改一下网络架构
        删除forward函数中输出层上的softmax函数,并将最终的输出修改为zhat
    """
    def __init__(self,in_features=10,out_features=2):
        super(Model,self).__init__() #super(请查找这个类的父类,请使用找到的父类替换现在的类)
        self.linear1 = nn.Linear(in_features,13,bias=True) #输入层不用写,这里是隐藏层的第一层
        self.linear2 = nn.Linear(13,8,bias=True)
        self.output = nn.Linear(8,out_features,bias=True)
        
    def forward(self, x):
        sigma1 = torch.relu(self.linear1(x))
        sigma2 = torch.sigmoid(self.linear2(sigma1))
        zhat = self.output(sigma2)
        return zhat

input_ = X.shape[1] #特征的数目
output_ = len(y.unique()) #分类的数目

#实例化神经网络类
torch.manual_seed(420)
net = Model(in_features=input_, out_features=output_)

#定义损失函数
criterion = nn.CrossEntropyLoss()
#定义优化算法
opt = optim.SGD(net.parameters()    #需要进行迭代的权重,现有神经网络架构下全部的权重和截距
                ,lr = lr,momentum = gamma)  # 步长,动量
#向前传播
#本轮向前传播的损失函数值
#反向传播 - 得到了梯度
#更新权重(和动量)
#清空梯度 - 清除原来计算出来的,基于上一个点的坐标计算的梯度

zhat = net.forward(X)                           # 向前传播
loss = criterion(zhat, y.reshape(500).long())   #计算损失函数
loss.backward()                                 # 反向传播

opt.step()          # 步子,走一步,更新权重w,更新动量v,此时坐标点发生了变化,所有的梯度必须重新计算
opt.zero_grad()     # 清除原来储存好的,基于上一个坐标点计算的梯度,为下一次计算梯度腾出空间

print(loss)
print(net.linear1.weight.data[0][:10])

四、开始迭代:batch_size与epoches

1 为什么要有小批量?

  在深度学习中,如果梯度下降的每次迭代都使用全部数据,将会非常耗费计算资源,且样本量越大,计算开销越高。虽然PyTorch被设计成天生能够处理巨量数据,但我们还是需要在数据量这一点上下功夫。这一节,我们开始介绍小批量随机梯度下降(mini-batch stochastic gradient descent,简写为mini-batch SGD)。
  小批量随机梯度下降是深度学习入门级的优化算法(梯度下降是入门级之下的),其求解与迭代流程与传统梯度下降(GD)基本一致,不过二者在迭代权重时使用的数据这一点上存在巨大的不同。传统梯度下降在每次进行权重迭代(即循环)时都使用全部数据,每次迭代所使用的数据也都一致。而mini-batch SGD是每次迭代前都会从整体采样一批固定数目的样本组成批次(batch),并用中的样本进行梯度计算,以减少样本量。
  为什么会选择mini-batch SGD作为神经网络的入门级优化算法呢?有两个比较主流的原因。第一个是,比起传统梯度下降,mini-batch SGD更可能找到全局最小值。

mini-batch SGD在每次迭代前都会随机抽取一批数据,所以每次迭代时带入梯度向量表达式的数据是不同的,梯度的方向同时受到系数和带入的训练数据的影响,因此每次迭代时梯度向量的方向都会发生较大变化。所以对于mini-batch SGD而言,它的梯度下降路线看起来往往是曲折的折线。mini-batch SGD的优势是算法不会轻易陷入局部最优,由于每次梯度向量的方向都会发生巨大变化,因此一旦有机会,算法就能够跳出局部最优,走向全局最优。从整体来看,为了mini-batch SGD这“不会轻易被局部最优困住”的优点,我们在神经网络中使用它作为优化算法(或优化算法的基础)。

  当然,还有另一个理由支持我们使用mini-batch SGD:mini-batch SGD可以提升神经网络的计算效率,让神经网络计算更快。

为了解决计算开销大的问题,我们要使用mini-batch SGD。考虑到可以从全部数据中选出一部分作为全部数据的“近似估计",然后用选出的这一部分数据来进行迭代,每次迭代需要计算的数据量就会更少,计算消耗也会更少,因此神经网络的速度会提升。

2 batch_size与epoches

epoch = 60      #请让神经网络学习60次全部数据
batch =10       #把全数据X划分为10个batch

for epochs in range(epoch):
    for batch in range(batch):
        zhat = net.forward(X)       # 向前传播
        loss = criterion(zhat, y.reshape(500).long()) #计算损失函数
        loss.backward()
        opt.step()                  #步子,走一步,更新权重w,更新动量v
        opt.zero_grad()

3 TensorDataset与DataLoader

#TensorDataset
import torch
from torch.utils.data import TensorDataset

a = torch.randn(500,2,3)        #三维数据 - 二维表格
b = torch.randn(500,3,4,5)      #四维数据 - 图像
c = torch.randn(500,1)          #二维数据 - 标签

# TensorDataset - 合并第一维度的值相等的对象
# 拼接特征x和标签y
for x in TensorDataset(b,c):    # generator
    print(x)
    break
data = TensorDataset(b,c)
# DataLoader - 用来切割小批量的类
#500 - 120, 120, 120, 120, 20
from torch.utils.data import DataLoader
bs = 120
dataset = DataLoader(data
          , batch_size = bs
          , shuffle = True #划分小批量之前请随机打乱我们的数据
          , drop_last = False #你要舍弃最后一个batch吗?
          )

for i in dataset:
    print(i[0].shape)

len(dataset)            #一共有多少个batch
len(dataset.dataset)    #展示里面全部的数据

dataset.dataset[0]      #单个样本
dataset.dataset[0][0]   #单个样本的x
dataset.dataset[0][1]   #单个样本的y
dataset.batch_size      #查看现有的batch_size
#TensorDataset  - 将特征与标签合并到同一个对象中
#DataLoader     - 帮助我们进行小批量的分割
for epochs in range(epoch): #每一个epoch下面进行batch循环
    for x,y in # DataLoader生成的对象

五、在MINST-FASHION上实现神经网络的学习流程

探索初识 fashion-MINST 数据集


#带大家认识fashion-MINST数据集
import torchvision
import torchvision.transforms as transforms #处理数据模块

# Dataloader、TensorDataset  - 对数据的结构、归纳方式进行变换
# torchvision.transforms     - 对数据集的数字本身进行修改

mnist = torchvision.datasets.FashionMNIST(root = "D:\MyData\myDataset" #你的计算机上的某个目录
                                          ,download = False
                                          , train = True
                                          , transform = transforms.ToTensor()
                                         ) #实例化数据
mnist               # 对于数据集的一个说明
len(mnist)          # 60000
mnist.data.shape    # [60000,28,28]


#[60000,1,28,28]
#(sample_size, H- height, W - width, C - color)


mnist.targets.unique()      # tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
mnist.classes               # 10中类别的名称

import matplotlib.pyplot as plt
import numpy
mnist[0][0].shape           # torch.Size([1, 28, 28])
plt.imshow(mnist[0][0].view(28,28).numpy());        #imageshow
plt.imshow(mnist[1][0].view(28,28).numpy());
plt.imshow(mnist[30001][0].view(28,28).numpy());

1 导库,设置各种初始值

#导入库
import torch
from torch import nn
from torch import optim
from torch.nn import functional as F
from torch.utils.data import DataLoader, TensorDataset
import torchvision
import torchvision.transforms as transforms 

2 导入数据,分割小批量

#确定数据、确定超参数
lr = 0.15
gamma = 0
epochs = 10
bs = 128

mnist = torchvision.datasets.FashionMNIST(root = "D:\MyData\myDataset" #你的计算机上的某个目录
                                          ,download = False
                                          , train = True
                                          , transform = transforms.ToTensor()
                                         )

# 切割数据集
batchdata = DataLoader(mnist, batch_size = bs, shuffle = True)

# (500, 20) - 20
# x - 四维 (128,28*28)
input_ = mnist.data[0].numel()          # 输入特征的数量
output_ = len(mnist.targets.unique())

3 定义神经网络的架构

#定义神经网络的架构
class Model(nn.Module):
    def __init__(self, in_features=10, out_features=2):
        super().__init__()
        self.linear1 = nn.Linear(in_features,1280,bias=False)
        self.output = nn.Linear(1280,out_features, bias=False)
    
    def forward(self, x):
        x = x.view(-1,28*28)        # -1作为占位符
        sigma1 = torch.relu(self.linear1(x))
        sigma2 = F.log_softmax(self.output(sigma1),dim=1)
        return sigma2

4 定义训练函数

#定义损失函数、优化算法、梯度下降的流程
#定义一个训练函数
def fit(net,bacthdata,lr=0.01, epochs=5, gamma = 0):
    criterion = nn.NLLLoss()
    opt = optim.SGD(net.parameters(),lr=lr,momentum = gamma)  # 小批量随机梯度下降

    correct = 0 #循环开始之前,预测正确的值为0
    samples = 0 #循环开始之前,模型一个样本都没有见过

    for epoch in range(epochs): #全数据被训练几次
        for batch_idx,(x,y) in enumerate(batchdata):
            y = y.view(x.shape[0]) #降维
            sigma = net.forward(x) #正向传播
            loss = criterion(sigma,y)
            loss.backward()
            opt.step()
            opt.zero_grad()
            
            #求解准确率,全部判断正确的样本数量/已经看过的总样本量
            yhat = torch.max(sigma, 1)[1]       # 预测标签,torch.max函数结果中的索引为1的部分
            correct += torch.sum(yhat == y)
            samples += x.shape[0]
            #每训练一个batch的数据,模型见过的数据就会增加x.shape[0]
            
            if (batch_idx + 1) % 125 == 0 or batch_idx == len(batchdata)-1: #每N个batch我就打印一次
                print("Epoch{}:[{}/{}({:.0f}%)],Loss:{:.6f},Accuracy:{:.3f}".format(
                    epoch+1
                    ,samples
                    ,epochs*len(batchdata.dataset)
                    ,100*samples/(epochs*len(batchdata.dataset))
                    ,loss.data.item()
                    ,float(100*correct/samples)
                     ))
            #分子代表:已经查看过的数据有多少
            #分母代表:在现有的epochs设置,模型一共需要查看多少数据
# 取多分类结果的标签
l = torch.tensor([[0.3,0.4,0.25],[0.7,0.2,0.1]])
l
torch.max(l)
torch.max(l,1)
torch.max(l,1)[1]   #softmax的预测标签

# 计算准确率
l = torch.tensor([True,False,True])
torch.sum(l) #True = 1, False = 0

5 进行训练与评估

#训练与评估
torch.manual_seed(420)
net = Model(in_features=input_, out_features=output_)
fit(net,batchdata,lr=lr,epochs=epochs,gamma=gamma)
  • 30
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白白白飘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值