2.6 手写数字识别之优化算法

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: 这篇文章探讨了在手写数字识别任务中,如何通过优化算法来找到使损失函数达到最小的参数取值。文章首先讨论了学习率对模型训练的影响,然后介绍了四种主流的优化算法:SGD、Momentum、AdaGrad和Adam,并说明了每种算法的特点和适用场景。此外,文章还强调了模型参数初始化的重要性,并介绍了几种常用的参数初始化方法,最后指出在实际应用中,使用预训练模型可以加速网络训练并提高精度。

第2.5节我们明确了分类任务的损失函数(优化目标)的相关概念和实现方法,本节我们依旧横向展开"横纵式"教学法,如 图1 所示,本节主要探讨在手写数字识别任务中,使得损失达到最小的参数取值的实现方法。

图1:“横纵式”教学法 — 优化算法

前提条件

在优化算法之前,需要进行数据处理、设计神经网络结构,代码与上一节保持一致,此处直接调用封装好的代码。

In [1]

import paddle
from data_process import get_MNIST_dataloader
from MNIST_network import MNIST
train_loader, test_loader = get_MNIST_dataloader()

2.6.1 学习率

在深度学习神经网络模型中,通常使用标准的随机梯度下降算法更新参数,学习率代表参数更新幅度的大小,即步长。当学习率最优时,模型的有效容量最大,最终能达到的效果最好学习率和深度学习任务类型有关,合适的学习率往往需要大量的实验和调参经验。探索学习率最优值时需要注意如下两点:

  • 学习率不是越小越好。学习率越小,损失函数的变化速度越慢,意味着我们需要花费更长的时间进行收敛,如 图2左图所示。
  • 学习率不是越大越好。只根据总样本集中的一个批次计算梯度,抽样误差会导致计算出的梯度不是全局最优的方向,且存在波动。在接近最优解时,过大的学习率会导致参数在最优解附近震荡,损失难以收敛,如 图2 右图所示。

图2: 不同学习率(步长过大/过小)的示意图

在训练前,我们往往不清楚一个特定问题设置成怎样的学习率是合理的,因此在训练时可以尝试调小或调大,通过观察Loss下降的情况判断合理的学习率,设置学习率的代码如下所示。

In [2]

import paddle.nn.functional as F
#仅优化算法的设置有所差别deftrain(model):
    model.train()
    
    #设置不同初始学习率
    opt = paddle.optimizer.SGD(learning_rate=0.001, parameters=model.parameters())
    # opt = paddle.optimizer.SGD(learning_rate=0.0001, parameters=model.parameters())# opt = paddle.optimizer.SGD(learning_rate=0.01, parameters=model.parameters())
    
    EPOCH_NUM = 10
    loss_list = []
    for epoch_id inrange(EPOCH_NUM):
        for batch_id, data inenumerate(train_loader()):
            #准备数据
            images, labels = data
            images = paddle.to_tensor(images)
            labels = paddle.to_tensor(labels)
            
            #前向计算的过程
            predicts = model(images)
            
            #计算损失,取一个批次样本损失的平均值
            loss = F.cross_entropy(predicts, labels)
            avg_loss = paddle.mean(loss)
            
            #每训练了100批次的数据,打印下当前Loss的情况if batch_id % 200 == 0:
                loss = avg_loss.numpy()[0]
                loss_list.append(loss)
                print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, loss))
            
            #后向传播,更新参数的过程
            avg_loss.backward()
            # 最小化loss,更新参数
            opt.step()
            # 清除梯度
            opt.clear_grad()
   
    #保存模型参数
    paddle.save(model.state_dict(), 'mnist.pdparams')
    return loss_list
    
#创建模型    
model = MNIST()
#启动训练过程
loss_list = train(model)

W0902 13:59:43.727392 98 device_context.cc:447] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1

W0902 13:59:43.731014 98 device_context.cc:465] device: 0, cuDNN Version: 7.6.

epoch: 0, batch: 0, loss is: 2.672663927078247

epoch: 0, batch: 200, loss is: 1.5871143341064453

epoch: 0, batch: 400, loss is: 0.8906817436218262

epoch: 0, batch: 600, loss is: 0.8545184135437012

epoch: 0, batch: 800, loss is: 0.5248807668685913...

绘制loss变化曲线:

In [4]

from tools import plot
plot(loss_list)

2.6.2 学习率的四种主流优化算法

学习率是优化器的一个参数,调整学习率看似是一件非常麻烦的事情,需要不断的调整步长,观察训练时间和Loss的变化。经过科研人员的不断的实验,当前已经形成了四种比较成熟的优化算法:SGD、Momentum、AdaGrad和Adam,效果如 图3 所示。

图3: 不同学习率算法效果示意图

  • SGD: 随机梯度下降算法,每次训练少量数据,抽样偏差导致的参数收敛过程中震荡
  • Momentum: 引入物理“动量”的概念,累积速度,减少震荡,使参数更新的方向更稳定

每个批次的数据含有抽样误差,导致梯度更新的方向波动较大。如果我们引入物理动量的概念,给梯度下降的过程加入一定的“惯性”累积,就可以减少更新路径上的震荡,即每次更新的梯度由“历史多次梯度的累积方向”和“当次梯度”加权相加得到。历史多次梯度的累积方向往往是从全局视角更正确的方向,这与“惯性”的物理概念很像,也是为何其起名为“Momentum”的原因。类似不同品牌和材质的篮球有一定的重量差别,街头篮球队中的投手(擅长中远距离投篮)往往更喜欢稍重篮球。一个很重要的原因是,重的篮球惯性大,更不容易受到手势的小幅变形或风吹的影响。

  • AdaGrad: 根据不同参数距离最优解的远近,动态调整学习率。学习率逐渐下降,依据各参数变化大小调整学习率

通过调整学习率的实验可以发现:当某个参数的现值距离最优解较远时(表现为梯度的绝对值较大),我们期望参数更新的步长大一些,以便更快收敛到最优解。当某个参数的现值距离最优解较近时(表现为梯度的绝对值较小),我们期望参数的更新步长小一些,以便更精细的逼近最优解。类似于打高尔夫球,专业运动员第一杆开球时,通常会大力打一个远球,让球尽量落在洞口附近。当第二杆面对离洞口较近的球时,他会更轻柔而细致的推杆,避免将球打飞。与此类似,参数更新的步长应该随着优化过程逐渐减少,减少的程度与当前梯度的大小有关。根据这个思想编写的优化算法称为“AdaGrad”,Ada是Adaptive的缩写,表示“适应环境而变化”的意思。RMSProp是在AdaGrad基础上的改进,学习率随着梯度变化而适应,解决AdaGrad学习率急剧下降的问题。

  • Adam: 由于动量和自适应学习率两个优化思路是正交的,因此可以将两个思路结合起来,这是当前广泛应用的算法。

说明:

每种优化算法均有更多的参数设置,详情可查阅飞桨的官方API文档。理论最合理的未必在具体案例中最有效,所以模型调参是很有必要的,最优的模型配置往往是在一定“理论”和“经验”的指导下实验出来的。


我们可以尝试选择不同的优化算法训练模型,观察训练时间和损失变化的情况,代码实现如下。

In [5]

#仅优化算法的设置有所差别deftrain(model):
    model.train()
    
    #四种优化算法的设置方案,可以逐一尝试效果
    opt = paddle.optimizer.SGD(learning_rate=0.01, parameters=model.parameters())
    # opt = paddle.optimizer.Momentum(learning_rate=0.01, momentum=0.9, parameters=model.parameters())# opt = paddle.optimizer.Adagrad(learning_rate=0.01, parameters=model.parameters())# opt = paddle.optimizer.Adam(learning_rate=0.01, parameters=model.parameters())
    
    EPOCH_NUM = 3for epoch_id inrange(EPOCH_NUM):
        for batch_id, data inenumerate(train_loader()):
            #准备数据
            images, labels = data
            images = paddle.to_tensor(images)
            labels = paddle.to_tensor(labels)
            
            #前向计算的过程
            predicts = model(images)
            
            #计算损失,取一个批次样本损失的平均值
            loss = F.cross_entropy(predicts, labels)
            avg_loss = paddle.mean(loss)
            
            #每训练了100批次的数据,打印下当前Loss的情况if batch_id % 200 == 0:
                print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))
            
            #后向传播,更新参数的过程
            avg_loss.backward()
            # 最小化loss,更新参数
            opt.step()
            # 清除梯度
            opt.clear_grad()
    #保存模型参数
    paddle.save(model.state_dict(), 'mnist.pdparams')
    
#创建模型    
model = MNIST()
#启动训练过程
train(model)

epoch: 0, batch: 0, loss is: [3.179514]

epoch: 0, batch: 200, loss is: [0.41207898]

epoch: 0, batch: 400, loss is: [0.14528263]

epoch: 0, batch: 600, loss is: [0.17151298]

epoch: 0, batch: 800, loss is: [0.20650986]

2.6.3 模型参数初始化

模型参数初始化是指在训练前,给要训练的参数一个初始的值。比如我们最终的目的是走到山谷底,如果一开始把你放到半山腰和放到山脚,那是否能够顺利走到谷底以及走到谷底的速度是有很大差距的。同样,在我们训练神经网络的时候,训练参数初始值不同也会导致模型收敛速度和最终训练效果的不同

在PaddlePaddle框架中,MNIST模型中用到的Conv2DLinear层都有weight_attrbias_attr两个参数,默认为None,表示使用默认的权重参数属性。这两个参数可以使用模型的named_parameters()函数获得。

通过下面的代码我们可以查看模型每层的参数情况

模型的state_dict函数可 获取当前层及其子层的所有参数和可持久性buffers。并 将所有参数和buffers存放在dict结构中。

In [6]

model = MNIST()
# 遍历所有的参数名和参数值for name, param in model.named_parameters():
    print(name, param)  
    
state_dict = model.state_dict() #collections.OrderedDict类型print(state_dict.keys())
# 根据某个参数的名字,打印对应的参数值# 如打印“conv1.weight”的参数值print("conv1.weight:",state_dict["conv1.weight"])

conv1.weight Parameter containing:

Tensor(shape=[20, 1, 5, 5], dtype=float32, place=CUDAPlace(0), stop_gradient=False,

[[[[ 0.00685916, -0.02827659, -0.00629800, 0.17840426, 0.07073421],

[ 0.15301149, 0.14512494, -0.08644208, -0.04089865, 0.06347622],

[-0.17223580, 0.53609502, -0.31562763, 0.31539580, 0.11763398],

[ 0.06057844, 0.12100003, -0.19036214, -0.32686502, -0.04253779],

[-0.24231870, -0.09162699, -0.00356645, 0.13624071, 0.35424659]]],

[[[ 0.19513041, -0.18949197, -0.06306379, -0.33953783, -0.12144631],

[-0.52912796, -0.04904719, 0.22196575, -0.21367665, -0.14066246],

[-0.47838733, 0.19762798, 0.32846957, -0.06876396, -0.05648636],

[-0.49627945, 0.04398900, -0.02770269, -0.09062629, -0.25601473],

[ 0.22629717, 0.12104782, -0.21879482, -0.29909152, 0.15422553]]],..

通过print(state_dict.keys())打印的结果为['conv1.weight', 'conv1.bias', 'conv2.weight', 'conv2.bias', 'fc.weight', 'fc.bias'],对应我们模型结构:两个卷积层,一个全连接层。每一层都有一个权重项和偏置项。由于有两个卷积层,所以卷积的权重名称有对应的编号。

paddle.ParamAttr可创建一个参数属性的对象,用户可设置参数的名称name、初始化方式initializer、学习率learning_rate、正则化规则regularizer、是否需要训练trainable、梯度裁剪方式need_clip、是否做模型平均do_model_average等属性。其参数初始化方式initializer默认值为None,表示权重参数采用Xavier初始化方式,偏置参数采用全0初始化方式。Xavier初始化方式可以缓解梯度消失的问题。

下面简单介绍几个常用的权重初始化方法:

下面我们通过paddle.ParamAttr基于常量初始化函数Constant完成全连接层的参数初始化,具体如下:

In [7]

import paddle
from paddle.nn import Linear
weight_attr = paddle.ParamAttr(initializer=paddle.nn.initializer.Constant(3))
#weight_attr = paddle.ParamAttr(initializer=paddle.nn.initializer.XavierNormal())
fc = Linear(in_features=20, out_features=10,weight_attr=weight_attr)
print(fc.state_dict())

OrderedDict([('weight', Parameter containing:

Tensor(shape=[20, 10], dtype=float32, place=CUDAPlace(0), stop_gradient=False,

[[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.],

[3., 3., 3., 3., 3., 3., 3., 3., 3., 3.]])), ('bias', Parameter containing:

Tensor(shape=[10], dtype=float32, place=CUDAPlace(0), stop_gradient=False,

[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]))])

在我们的实际应用中,由于训练数据有限、数据获取较难、训练资源有限等原因,往往利用在大规模开源数据集上训练得到的模型参数作为我们自己模型的初始值******(也称为预训练模型),这样可以加速网络训练、并得到较高精度******

作业 2-3

在手写数字识别任务上,哪种优化算法的效果最好?多大的学习率最优?(可通过Loss的下降趋势来判断)

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
相关文章
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的优化算法及其应用
【10月更文挑战第8天】 本文将探讨深度学习中常用的优化算法,包括梯度下降法、Adam和RMSProp等,介绍这些算法的基本原理与应用场景。通过实例分析,帮助读者更好地理解和应用这些优化算法,提高深度学习模型的训练效率与性能。
106 63
|
4天前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
4天前
|
机器学习/深度学习 人工智能 算法
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
24 0
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
|
12天前
|
算法
基于粒子群算法的分布式电源配电网重构优化matlab仿真
本研究利用粒子群算法(PSO)优化分布式电源配电网重构,通过Matlab仿真验证优化效果,对比重构前后的节点电压、网损、负荷均衡度、电压偏离及线路传输功率,并记录开关状态变化。PSO算法通过迭代更新粒子位置寻找最优解,旨在最小化网络损耗并提升供电可靠性。仿真结果显示优化后各项指标均有显著改善。
|
7天前
|
机器学习/深度学习 算法 数据挖掘
基于GWO灰狼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了基于分组卷积神经网络(GroupCNN)和灰狼优化(GWO)的时间序列回归预测算法。算法运行效果良好,无水印展示。使用Matlab2022a开发,提供完整代码及详细中文注释。GroupCNN通过分组卷积减少计算成本,GWO则优化超参数,提高预测性能。项目包含操作步骤视频,方便用户快速上手。
|
8天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于WOA鲸鱼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了一种基于WOA优化的GroupCNN分组卷积网络时间序列预测算法。使用Matlab2022a开发,提供无水印运行效果预览及核心代码(含中文注释)。算法通过WOA优化网络结构与超参数,结合分组卷积技术,有效提升预测精度与效率。分组卷积减少了计算成本,而WOA则模拟鲸鱼捕食行为进行优化,适用于多种连续优化问题。
|
19天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GA遗传优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
该算法结合了遗传算法(GA)与分组卷积神经网络(GroupCNN),利用GA优化GroupCNN的网络结构和超参数,提升时间序列预测精度与效率。遗传算法通过模拟自然选择过程中的选择、交叉和变异操作寻找最优解;分组卷积则有效减少了计算成本和参数数量。本项目使用MATLAB2022A实现,并提供完整代码及视频教程。注意:展示图含水印,完整程序运行无水印。
|
16天前
|
机器学习/深度学习 算法 决策智能
【机器学习】揭秘深度学习优化算法:加速训练与提升性能
【机器学习】揭秘深度学习优化算法:加速训练与提升性能
|
17天前
|
机器学习/深度学习 算法
深度学习中的优化算法及其应用
本文探讨了深度学习中常用的优化算法,包括梯度下降、随机梯度下降、动量方法和Adam方法。通过对比这些算法的优缺点及适用场景,帮助读者更好地理解和应用这些优化方法。
21 2
|
21天前
|
算法 数据挖掘
基于粒子群优化算法的图象聚类识别matlab仿真
该程序基于粒子群优化(PSO)算法实现图像聚类识别,能识别0~9的数字图片。在MATLAB2017B环境下运行,通过特征提取、PSO优化找到最佳聚类中心,提高识别准确性。PSO模拟鸟群捕食行为,通过粒子间的协作优化搜索过程。程序包括图片读取、特征提取、聚类分析及结果展示等步骤,实现了高效的图像识别。

热门文章

最新文章