IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 动手实现深度神经网络5 神经网络的优化之参数更新优化 -> 正文阅读

[人工智能]动手实现深度神经网络5 神经网络的优化之参数更新优化

动手实现深度神经网络5 神经网络的优化之参数更新优化

在之前的神经网络中,我们都是使用随机梯度下降法SGD对参数进行更新的,然而,虽然SGD实现简单易于理解,但是,它在解决某些问题时可能很没有效率。

SGD的缺点

首先我们来看一个例子:image-20220305091417471

这个函数的图像和等高线如下:

image-20220305091511505

它的各点处的梯度如下图:

image-20220305091557920

可以看到,在各个点的梯度在x方向上的分量很小,也就是说在根据梯度更新参数时,只会向x方向移动很少的举例,这样显然要花费很长时间才能到达最小值处(中间)。

下面的基于SGD的最优化的更新路径也证实了上面的推测:

image-20220305091835206

可以看到,SGD优化的路径呈“之”字形朝最小值(0, 0)移动,效率非常低。

optimizer

在介绍对其他更新参数的方法之前,我们先来把“像SGD一样负责参数修改的代码”封装为一个类,这些类统称为optimizer(负责优化的人),这些类都会实现一个update方法用于“根据梯度修改参数”。

新建一个optimizer.py文件,把SGD以及之后要介绍地其他类都放在这个文件里面。

#optimizer.py

class SGD:
    def __init__(self, lr=0.01):
        self.lr = lr;

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]

Momentum

Momentum引入了新的变量v,用于表示速度,它把梯度理解为物体受到的力,还有新变量momentum用于表示“不管物体受不受力,都会作用于物体上的空气阻力”。

img
class Momentum:
    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr;
        self.momentum = momentum;
        self.v = None

    def update(self, params, grads):
        if self.v == None:
            self.v = {}
            for k, v in params.items():
                # v[k]是一个与params[k]同形的矩阵
                self.v[k] = np.zeros_like(v)

        # v可以看做物理上的速度
        # 在梯度方向上受力
        # momentum可以看做是不管受不受力,都会是速度减小的空气阻力
        for key in params.keys():
            self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
            params[key] += self.v[key]

简单来说就是:在每一次更新参数时,速度v会受到“阻力momentum”和“作用力grads”的影响而改变,而参数则会根据速度v的值改变

Momentum方法给人的感觉就像是小球在地面上滚动一样,下图是基于Momentum的最优化的更新路径:

image-20220305093730886

AdaGrad

在神经网络的学习中,学习率(η)的值很重要。学习率过小, 会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散而不能 正确进行。 在关于学习率的有效技巧中,有一种被称为学习率衰减(learning rate decay)的方法,即随着学习的进行,使学习率逐渐减小。实际上,一开始“多” 学,然后逐渐“少”学的方法,在神经网络的学习中经常被使用

AdaGrad进一步发展了这个想法,针对“一个一个”的参数,赋予其“定制”的值,具体如何做到定制呢?AdaGrad引入一个变量h,记录每个参数对应的以前的所有梯度值的平方和,在更新参数时,通过乘以1/h0.5 ,就可以调整学习的尺度,这意味着,参数的元素中变动较大(被大幅更新)的元素的学习率将变小

class AdaGrad:
    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        # h保存了以前所有梯度的平方和
        # 在更新参数时,通过乘以 根号h分之一,就可以调整学习的尺度。这意味着,参数的元素中变动较大(被大幅更新)的元素的学习率将变小
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            # 最后一行加上了微小值1e - 7。这是为了防止当self.h[key]中有0时,将0用作除数的情况
            params[key]-=self.lr*grads[key]/(np.sqrt(self.h[key])+1e-7)

下图是基于AdaGrad的最优化的更新路径:

image-20220305094133584

RMSProp

AdaGrad听起来很美好,但是仍然存在问题:

“AdaGrad会记录过去所有梯度的平方和。因此,学习越深入,h值越大,更新的幅度就越小。实际上,如果无止境地学习,更新量就会变为 0, 完全不再更新。”

为了解决这一问题,我们引入RMSProp,它会逐渐“忘记”很早之前的梯度,避免h值过大。

class RMSProp:
    def __init__(self, lr=0.01, decay_rate = 0.99):
        self.lr = lr
        self.decay_rate = decay_rate
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            # h每次会乘以decay_rate,以免学习率过小
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

Adam

Momentum参照小球在碗中滚动的物理规则进行移动,AdaGrad为参 数的每个元素适当地调整更新步伐。如果将这两个方法融合在一起,就是Adam的基本思想。当然Adam的具体理论是很复杂的,我们这里不过多介绍。

class Adam:
    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1  # Momentum1
        self.beta2 = beta2  # Momentum2
        self.iter = 0
        self.m = None
        self.v = None

    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)

        self.iter += 1
        lr_t = self.lr * np.sqrt(1.0 - self.beta2 ** self.iter) / (1.0 - self.beta1 ** self.iter)

        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key] ** 2 - self.v[key])

            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

几种方式的比较

我们以前面自己手动实现的两层神经网络手写数字识别为例,比较一下这五种方法。


#0:读入数据
(x_train,t_train),(x_test,t_test)=load_mnist(normalize=True)

train_size=x_train.shape[0]
batch_size=128
max_iterations=2000

#1:实现四个网络,分别采用四种更改参数的方法
optimizers={}
optimizers['SGD']=SGD()
optimizers['Momentum']=Momentum()
optimizers['AdaGrad']=AdaGrad()
optimizers['RMSProp']=RMSProp()
optimizers['Adam']=Adam()

networks={}
train_loss={}
for key in optimizers.keys():
    networks[key]=MyTwoLayerNet(input_size=784,hidden_size_1=100,output_size=10)
    train_loss[key]=[]

#2:开始训练
for  i in range(max_iterations):
    batch_mask=np.random.choice(train_size,batch_size)
    x_batch=x_train[batch_mask]
    t_batch=t_train[batch_mask]

    for key in optimizers.keys():
        grads=networks[key].gradient(x_batch,t_batch)
        optimizers[key].update(networks[key].params,grads)

        loss=networks[key].loss(x_batch,t_batch)
        train_loss[key].append(loss)

    if i % 100 == 0:
        print( "===========" + "iteration:" + str(i) + "===========")
        for key in optimizers.keys():
            loss = networks[key].loss(x_batch, t_batch)
            print(key + ":" + str(loss))

# 3:绘制图形
markers = {"SGD": "o", "Momentum": "x", "AdaGrad": "s","RMSProp":".", "Adam": "D"}
x = np.arange(max_iterations)
for key in optimizers.keys():
    # 这里的smooth_curve是一种使图像平滑的方法
    plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(0, 1)
plt.legend()
plt.show()

比较结果是:

Figure_1_2

可以看到,与SGD相比,其他几种方法学习得更快。当然,这只是在我自己设计的神经网络上的表现,不同的网络结构,学习率等超参数的设置等都会比较实验结果。


上面介绍了几种方法,那么用哪种方法好呢?很遗憾,目前并不存在能在所有问题中都表现良好的方法。这几种方法各有各的特点,都有各自擅长解决的问题和不擅长解决的问题

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:02:47  更:2022-03-06 13:03:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 17:37:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码