自学内容网 自学内容网

深度学习笔记12

1.神经网络的代价函数

        神经网络可同时用于解决分类问题和回归问题,对于不同的问题会在输出层后,加上不同的变换函数。一般来说,回归问题使用恒等函数f(x)=x,分类问题使用sigmoid或softmax函数。而不同的变换函数,也对应不同的代价函数。

神经网络解决回归问题

        在使用神经网络解决回归问题时,会在输出层后,加上恒等函数,不会对输入值做任何修改。如果神经网络只预测一个回归值,那么使用的代价函数和线性回归中的代价函数一样,都是均方误差函数MSE。如果线性回归需要预测多个回归值,则需要将每个回归目标的均方误差计算出来,然后相加得到总误差。

总的代价函数:

MSE=\frac{1}{mn}\sum_{i=1}^{m}\sum_{j=1}^{n}(y_{ij}-\hat{y}_{ij})^2

m:样本个数

n:目标个数

y_{ij}:真实值

\hat{y}_{ij}:预测值

i:第i个样本

j:第j个目标

\sum_{j=1}^{n}:计算单独每个样本的n个目标的真实值和预测值的平方差

\sum_{i=1}^{m}:将m个样本对应的误差加到一起

神经网络解决分类问题 

        当使用神经网络解决分类问题时,最后一层的每个神经元都会对应一个类型,每个神经元的输出通过变换函数转换为类别对应的概率。如果每个类别之间是互斥的,就将神经元的输出值,输入到softmax函数中,将其转化为这几个互斥的类别对应的概率(p_1+p_2+...=1)。

        另一种情况是多标签分类,每个类别之间互不打扰,相互独立,这时要将神经元的输出值,分别输入到sigmoid函数中,经过sigmoid函数的计算,可以得到这些不同类别的概率,它们是多个无关联的、0-1之间的实数。

代价函数

         在解决分类问题时,一般使用交叉熵损失函数。对于互斥的分类问题,神经网络会使用与softmax回归形式完全相同的交叉熵损失函数。对于多标签分类问题,神经网络使用与逻辑回归形式相似的交叉熵损失函数。

互斥的多分类问题:E=-\frac{1}{m}\sum_{i=1}^{m}\sum_{k=1}^{n}y_k^{(i)}log(p_k^{(i)})

多标签分类问题:E=-\frac{1}{m}\sum_{i=1}^{m}\sum_{k=1}^{n}(y_k^{(i)}log(p_k^{(i)})+(1-y_k^{(i)})log(1-p_k^{(i)}))

2.小批量梯度下降算法        

        梯度下降算法有三种常见的形式:批量梯度下降、随机梯度下降和小批量梯度下降

  • 批量梯度下降:

         每次迭代中,批量梯度下降算法都会基于所有的训练样本,计算损失函数的梯度,因此可以得到一条平滑的收敛曲线。训练数据:100个样本,迭代轮数50,在每一轮迭代中都会一起使用这100个样本,计算整个训练集的梯度,更新模型参数,所以总更新次数:50次

  • 随机梯度下降:

        会在一轮完整的迭代过程中,遍历整个训练集,但是每次更新只基于一个样本计算梯度,这样会得到一条震荡的收敛曲线。训练数据:100个样本,迭代轮数50,每一轮迭代会遍历这100个样本,每次汇集孙某一个样本的梯度,更新模型参数,所以总更新次数:100*50=5000

  • 小批量梯度下降:

        结合批量梯度下降和随机梯度下降的优点,每次迭代会从训练集中,随机选择一个小批量,计算梯度,更新模型。训练数据:100个样本,迭代轮数50,小批量大小20,在每一轮迭代中会有5次小批量的迭代,所以总更新次数:(100/20)*50=250

梯度下降算法比较
优点缺点
批量梯度下降每次迭代会使用整个训练集计算梯度,可以得到准确的梯度方向如果数据集非常大时,就导致每次迭代的速度都非常慢,计算成本就会很高
随机梯度下降每次只用一个样本训练,所以迭代速度会非常快,迭代具有震荡属性,可以跳出局部最优解更新的方向会不稳定,可能永远都不会真正的收敛
小批量梯度下降结合随机梯度下降的高效性和批量梯度下降的稳定性,它比随机梯度下降有更稳定的收敛,同时又比批量梯度下降计算的更快
方法是否稳定迭代速度局部最优解
批量梯度下降稳定可能停留
随机梯度下降不稳定可跳出
小批量梯度下降较稳定较快可跳出

小批量梯度下降算法的实现 

1.小批量数据的准备

import numpy as np
import matplotlib.pyplot as plt
import torch
from torch.utils.data import DataLoader
from torch.utils.data import TensorDataset
#设置一个固定的随机种子,确保每次运行得到相同的数据
np.random.seed(0)
#随机生成100个横坐标x,范围在0-2
x=2*np.random.rand(100,1)
#生成带有噪音的纵坐标y,数据基本分布在y=2x+3附近
y=3+2*x+np.random.randn(100,1)*0.5
plt.scatter(x, y,marker='x',color='red')

#将训练数据x、y转为张量
x=torch.from_numpy(x).float()
y=torch.from_numpy(y).float()

#使用TensorDataset,将x和y组成训练集
dataset=TensorDataset(x,y)
#使用DataLoader,构造随机的小批量数据
dataloader=DataLoader(dataset,
                      #使用一个小批量的数据规模为20
                      batch_size=20,
                      #随机打乱数据的顺序
                      shuffle=True)
print("dataloader len=%d"%(len(dataloader)))
for index,(data,label) in enumerate(dataloader):
    print("index = %d num=%d"%(index,len(data )))

x=2*np.random.rand(100,1) 

  • np.random.rand(100,1):使用 NumPyrand 函数生成一个形状为 (100,1)(100,1)(100,1) 的随机数组,里面的数值均匀分布在 000 到 111 之间。

  • 2 * np.random.rand(100,1):将生成的数组乘以 2,使得数值范围扩展到 000 到 222 之间。

y=3+2*x+np.random.randn(100,1)*0.5 

np.random.randn(100,1) * 0.5:生成形状为 (100,1)(100,1)(100,1) 的随机噪声项,使 y 中的值带有一些波动。np.random.randn(100,1) 使用标准正态分布(均值为 0,标准差为 1)生成随机数,将其乘以 0.5 缩小标准差,使得噪声更小、波动更平滑。

np.random.rand 生成的随机数均匀分布在 [0,1),而 np.random.randn 生成的随机数服从标准正态分布,中心在 0。 

DataLoader(dataset, batch_size=20, shuffle=True)

  • DataLoaderPyTorch 中的一个类,用于将数据集分成小批量并进行迭代读取。这样可以有效地处理大量数据而不必一次性全部加载到内存中,特别是在训练深度学习模型时非常重要。
  • dataset

    • 这是要加载的数据集,即前面用 TensorDataset(x, y) 定义的 datasetDataLoader 将使用该数据集来加载数据。
  • batch_size=20

    • 设定每个小批量的大小为 20,意味着每次加载器会返回 20 个样本。
    • 在训练过程中,小批量(batch)数据能够加速模型的梯度计算,同时可以让模型在批量数据的平均梯度上进行更新,有助于稳定训练。
  • shuffle=True

    • 设置 shuffle=True 会在每个 epoch(轮次)开始前打乱数据集的顺序,以避免模型训练受样本顺序的影响,有助于提升模型的泛化能力。
    • 在训练时,数据的随机性能够帮助模型更好地学习到数据的真实分布,防止过拟合。

w = torch.randn(1, requires_grad=True)

        初始化了模型的权重参数 w,并启用了自动求导功能(requires_grad=True),表示 w 将在训练过程中计算其梯度。 尽管我们在定义时启用了 requires_grad=True,只是告诉 PyTorch 这个张量(比如 w)需要计算和记录梯度,但这并不会主动计算出梯度。实际上,梯度计算只在调用 loss.backward() 时才会触发

dataloader len=5
index = 0 num=20
index = 1 num=20
index = 2 num=20
index = 3 num=20
index = 4 num=20 

2.小批量梯度下降算法迭代 

#带迭代的参数w和b
w=torch.randn(1,requires_grad=True)
b=torch.randn(1,requires_grad=True)
#进入模型的迭代循环
for epoch in range(1,51):#迭代轮数
    #在一个迭代轮次中,以小批量的方式,使用dataloader对数据进行遍历
    #batch_idx表示当前遍历的批次
    #data和label表示这个批次的训练数据和标记
    for batch_idx,(data,label) in enumerate(dataloader):
        h=x*w+b#计算当前直线的预测值,保存到h
        #计算预测值h和真实值y之间的均方误差,保存到loss中
        loss=torch.mean((h-y)**2)
        loss.backward()#计算代价loss关于参数w和b的偏导数
        #进行梯度下降,沿着梯度下降的反方向,更新w和b的值
        w.data-=0.01*w.grad.data
        b.data-=0.01*b.grad.data
        #清空张量w和b中的梯度信息,为下一次迭代做准备
        w.grad.zero_()
        b.grad.zero_()
        #每次迭代,都打印当前迭代的轮数epoch
        #数据的批次batch_idx和loss损失值
        print("epoch (%d) batch (%d) loss=%.3lf"%(epoch,batch_idx,loss.item()))

for batch_idx, (data, label) in enumerate(dataloader):

  • enumerate(dataloader) 是一个迭代器,它会遍历 dataloader 并在每次迭代时返回当前批次的索引 batch_idx 和该批次的数据 (data, label)
  • dataloader 是由 DataLoader 创建的对象,负责将数据集 dataset 分割为小批量,以便模型可以逐批次读取数据进行训练

 

3.图像绘制 

#打印w和b的值,并绘制直线
print('w=%.3lf,b=%.3lf'%(w.item(),b.item()))
w=w.item()
b=b.item()
x=np.linspace(0,2,100)
h=w*x+b
plt.plot(x,h)
plt.show()

 

4.结果分析

        每次运行代码时返回的 wb 不一样,主要是由于以下几个原因:

  • 随机初始化:

w = torch.randn(1, requires_grad=True) b = torch.randn(1, requires_grad=True)

        这里的 torch.randn 会从标准正态分布(均值 0,标准差 1)中随机生成 wb 的初始值。每次运行代码时,wb 的初始值通常是不同的,这会导致训练过程中的更新路径不同,从而影响最终的值。

  • 数据中的随机噪声

y = 3 + 2 * x + np.random.randn(100, 1) * 0.5

        虽然设置了 np.random.seed(0),使得每次运行生成的 xy 都相同,但在模型初始化时 wb 是随机的,每次不同的初始 wb 会影响模型在不同批次上的更新路径,进而影响训练结果。

  • 小批量数据的随机顺序:

dataloader = DataLoader(dataset, batch_size=20, shuffle=True)

  DataLoadershuffle=True 参数会在每个 epoch 开始时随机打乱数据的顺序。每次运行代码时,小批量数据的顺序会不同,因此参数更新的路径也会不同。

 

 


原文地址:https://blog.csdn.net/weixin_51828505/article/details/143632241

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!