深度学习(7)——CNN优化之路optimization最优化策略

梯度下降

关于梯度下降上一篇博客说了,细节参考这里:https://blog.csdn.net/weixin_41761357/article/details/109689613
这里所谓的梯度下降是指所有样本的梯度下降。
假如有x1,x2,x3……xn, N个样本;
在这里插入图片描述

每个样本有x11,x12,x13……x1m ,M个特征;
在这里插入图片描述

模型有K个参数。
在这里插入图片描述

为了衡量预测值和真实值之间的差异,需要损失函数loss<y^,y> ,对于第i个参数

在这里插入图片描述
模型对1~N个样本会生成N个预测值,对每个样本都有一个loss,要把所有样本的loss加和求均值,通过梯度下降对参数计算梯度,参数更新公式:
在这里插入图片描述
模型的K个参数独立更新,相互之间无影响。那么有以下三种梯度下降的更新策略:

1.基于全部样本更新一次

原始梯度下降:全部训练集样本送入模型,可以分批送入也可以一次性全送入,这里要区分送入和更新,如果机器不能容纳全部样本,那么分批送入模型进行计算loss也可以,只要保证更新的时候是基于全部样本的loss进行更新的即可。

2.基于分批样本更新一次

mini-batch最小梯度下降:全部样本分批送入模型,分批进行更新,每批次样本数量为batch-size。

3.基于每个样本更新一次

SGD随机梯度下降:batchsize=1,计算一个样本的loss就更新一次。
现如今,SGD指代基于mini-batch的梯度下降,很少有人使用batch-size为1的方法,所以统称2、3为SGD,并且所谓的SGD都是指2。

Optimization梯度下降的优化策略

一、基于动量(冲量)的优化策略

1.Momentum Based基于动量(冲量)的优化策略-SGD

陷入局部极值的现象:
在这里插入图片描述
模拟小球运动,小球在下坠过程中会有惯性和摩擦力,由于惯性的作用只要凹陷不是太深就可以冲出凹陷到下一个谷峰运动,最终可以跌落到谷底;而由于摩擦力,惯性不是100%的,并且能量最终会消耗,所以不会无穷无尽的在谷峰之间来回震荡。
在这里插入图片描述
为了模拟小球坠落的过程,给梯度下降也以数学形式赋予一个“惯性”:
在这里插入图片描述
m代表动量,初始时为0,随着梯度下降的进行逐渐增加;
γ代表摩擦力,是一个大于0小于1的系数;
η是学习率。
总结:
在这里插入图片描述
在这里插入图片描述

2.Nestrov基于超前梯度的优化策略-NAG

不用当前时刻的梯度计算,用未来的梯度更新,可以更快的进行收敛。
在这里插入图片描述
NAG计算公式如下:与SGD基本保持一致,只是梯度计算的位置进行了更改。SGD在当前梯度进行计算,NAG在下一步梯度进行计算。
在这里插入图片描述

二、基于自适应的优化策略Adaptive Methods

自适应主要有两点,有基于学习率的自适应(学习率过快让其自适应慢下来,过慢让其自适应快起来),还有不同参数的自适应(有的参数过快让其慢下来,有的参数过慢让其块起来)。

1.Adaptive Methods-Adagrad

记录更新过程中历史梯度的平方和(平方才能将负数梯度也加上),将历史梯度作为分母与学习率相除。如果之前过快那么历史梯度的平方和就会很大,作为分母相除之后得到的学习率就会变小。
在这里插入图片描述
其中,⊙表示向量对应位置相乘,例如[1,2,3]⊙[4,5,6]得[4,10,18]

2.Adaptive Methods-RMSProp

改进Adagrad,如果更新过程中的所以梯度全进行记录并参与计算,那么通常太久之前的梯度是没有什么参考价值的,近期的梯度应该更具备参考价值。所以不用全部的梯度,加入时间因素,采取最近的梯度,用系数γ<1来表示时间占比是过去多还是现在多。
在这里插入图片描述

s是历史梯度,▽J⊙▽J是现在的梯度。

3.Adaptive Methods-Adam

对学习率不敏感,可以傻瓜式学习,省去了人工调参的麻烦(比如RMSProp需要调试时间系数γ,而Adam不需要手调超参数)。结合了动量优化策略和自适应优化策略。
在这里插入图片描述
关于第一个括弧,在Adam中,β_1和β_2充当时间系数,参考了RMSProp的时间思想,控制动量m和梯度s的历史占比系数。
在这里插入图片描述

关于第二个大括号,t是迭代次数,m’和s’控制一开始的节奏,默认β_1=0.9;β_2=0.999;随着迭代次数的增加,小于1的次方越来越小,分母越来越大。
在这里插入图片描述

SUMMARY

在这里插入图片描述
Adam目前应用最广泛,但不代表Adam最好,可以先用Adam训练到一个较小值,然后转成SGD,现阶段SGD基本都是基于动量的,没有不用的SGD。

代码展示

本部分将通过一个简单的线性函数y,比较不同梯度下降优化策略的效果影响

import numpy as np
import matplotlib.pyplot as plt
import math
#定义函数f(x)
def f(x):
    return x[0]**2 + 50*x[1]**2
#定义函数的导数计算公式
def g(x):
    return np.array([2*x[0],100*x[1]])
#可视化
x = np.linspace(-200,200,1000) #取X值
y = np.linspace(-200,200,1000) #取Y值
X,Y = np.meshgrid(x,y)         #关联X和Y对应关系
Z = X**2+50*Y**2
def my_contour(X,Y,Z,arr=None):
    a = plt.contour(X,Y,Z)
    plt.contour(X, Y, Z)
    plt.scatter(0,0,marker='*',c='red')
    if arr is not None:
        arr = np.array(arr)
        for i in range(len(arr-1))
            plt.plot(arr[i:i+2,0],arr[i:i+2,1])
    plt.show()
my_contour(X, Y, Z)
1.普通SGD(无动量无自适应)
#原始SGD
def vgd(x_start,step,epoch,g,printf=False): #下降开始时的初始值,学习率,迭代次数,梯度,是否打印信息
    x = np.array(x_start,dtype=np.float32)
    passing_dots = [x.copy()] #记录路径
    for i in range (epoch-1):
    ####################################
        gradient = g(x)       #计算梯度
        x = x -step*gradient  #梯度下降,得到新的点
     #################################   
        passing_dots.append() #记录路径
        if printf:            #如果需要打印,打印如下信息
            print(f'Epoch{i+1} grad{gradient} x{x}')
     return x,passing_dots

打印结果

res,arr_vgd = vgd(x_start=[150,75],step=0.01,epoch=10,g=g,printf=False)
my_contour(X,Y,Z,arr_vgd)

在这里插入图片描述
迭代次数不够,取epoch=100:
在这里插入图片描述
更新太少,增加学习率step=0.02:
在这里插入图片描述
严重震荡,改学习率为0.018:
在这里插入图片描述
看起来舒服多了,但是学习率对SGD影响还挺大的,要不停的调学习率。

2.基于动量的SGD

在这里插入图片描述

#MGD
def mgd(x_start,step,epoch,g,printf=False,discount=0.): #下降开始时的初始值,学习率,迭代次数,梯度,是否打印信息,超参数γ模拟摩擦
    x = np.array(x_start,dtype=np.float32)
    passing_dots = [x.copy()]#记录路径
    momentum = np.zeros_like(x)  # 1.先声明一个动量,动量初始为0,和输入形状一致
    for i in range (epoch-1):
    ##################################
        gradient = g(x)             #计算梯度,计算方法不变
        # x = x -step*gradient      #梯度下降公式发生改变
        momentum = momentum*discount+gradient   #2.计算动量
        x = x - step * momentum                 #3.基于动量更新
     ###################################   
        passing_dots.append() #记录路径
        if printf:            #如果需要打印,打印如下信息
            print(f'Epoch{i+1} grad{gradient} x{x}')
     return x,passing_dots
res,arr_mgd = mgd(x_start=[150,75],step=0.01,epoch=10,g=g,printf=False) #在该区间以0.01学习率迭代十次观察SGD效果
my_contour(X,Y,Z,arr_mgd) #可视化

在这里插入图片描述
相较于普通的SGD,MGD只需要10个epoch就基本能收敛到点了,所以收敛速度是有提升的,但是通过调试动量系数也能很大的影响收敛的曲线。

3.基于动量的NAG
#NAG
def nag(x_start,step,epoch,g,printf=False,discount=0.): #下降开始时的初始值,学习率,迭代次数,梯度,是否打印信息,超参数γ模拟摩擦力
    x = np.array(x_start,dtype=np.float32)
    passing_dots = [x.copy()]#记录路径
    momentum = np.zeros_like(x)  # 1.先声明一个动量,动量初始为0,和输入形状一致
    for i in range (epoch-1):
    #####################################
        gradient = g(x)                           #计算梯度
        x_future = x- step*discount*momentum      #1.通过计算的梯度得到未来的x
        gradient = g(x_future)                    #2.计算未来的梯度
        momentum = momentum*discount+gradient     #基于未来的梯度进行更新动量
        x = x - step * momentum                   #基于动量更新参数
  ################################################# 
        passing_dots.append() #记录路径
        if printf:            #如果需要打印,打印如下信息
            print(f'Epoch{i+1} grad{gradient} x{x}')
     return x,passing_dots
res,arr_nag = nag(x_start=[150,75],step=0.01,epoch=10,g=g,printf=False,discount=0.9) #在该区间以0.01学习率迭代十次观察SGD效果
my_contour(X,Y,Z,arr_nag) #可视化

在这里插入图片描述
依赖学习率,但是收敛速度是快的。

4.基于自适应的RMSProp

在这里插入图片描述

#RMSprop
def rmsprop(x_start,step,epoch,g,printf=False,decay_rate=0.9): #下降开始时的初始值,学习率,迭代次数,梯度,是否打印信息,超参数时间占比
    x = np.array(x_start,dtype=np.float32)
    passing_dots = [x.copy()]#记录路径
    # momentum = np.zeros_like(x)  #没有动量机制
    cache = np.array([0,0])        #1.引入cache记录梯度平方和
    for i in range (epoch-1):
##########################################
        gradient = g(x)                                 #计算梯度
        cache = cache*decay_rate+gradient**2*(1-decay_rate) #计算S
        x = x-step*gradient/np.sqrt(cache+1e-8)
########################################
        passing_dots.append() #记录路径
        if printf:            #如果需要打印,打印如下信息
            print(f'Epoch{i+1} grad{gradient} x{x}')
     return x,passing_dots
res,arr_rmsprop = rmsprop(x_start=[150,75],step=0.01,epoch=10,g=g,printf=False,decay_rate=0.9) #在该区间以0.01学习率迭代十次观察SGD效果
my_contour(X,Y,Z,arr_rmsprop) #可视化

step取一个大值才能看见明显变化,因为要除以平方和,取小的话观察不到效果。

4.基于自适应的Adam
#Adam
def adam(x_start,step,epoch,g,printf=False,b1=0.9,b2=0.999,e = 1e-8): #下降开始时的初始值,学习率,迭代次数,梯度,是否打印信息,超参数时间占比
    x = np.array(x_start,dtype=np.float32)
    passing_dots = [x.copy()]#记录路径
    # momentum = np.zeros_like(x)  #没有动量机制
    mt = np.zeros(2)        #记录历史m
    vt = np.zeros(2)        #记录历史s
    for i in range (epoch-1):
############################
        gradient = g(x)
        mt = b1*mt +(1-b1)*gradient      #m
        vt = b2*mt +(1-b2)*gradient**2   #s
        mtt = mt/(1-b1**(i+1))           #m'
        vtt = vt / (1 - b2 ** (i + 1))   #s'
        vtt_sqrt = np.array([math.sqrt(vtt[0]),math.sqrt(vtt[1])])
        x = x -step*mtt/(vtt_sqrt+e)
###############################
        passing_dots.append() #记录路径
        if printf:            #如果需要打印,打印如下信息
            print(f'Epoch{i+1} grad{gradient} x{x}')
     return x,passing_dots
res,arr_adam = adam(x_start=[150,75],step=0.01,epoch=10,g=g,printf=False,b1=0.9,b2=0.999) #在该区间以0.01学习率迭代十次观察SGD效果
my_contour(X,Y,Z,arr_adam)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值