Multilayer perceptron&Optimization algorithm

Posted weixin_50061989

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Multilayer perceptron&Optimization algorithm相关的知识,希望对你有一定的参考价值。

多层感知机和优化算法

目录

前言

一、Multilayer perceptron

二、BP算法

 三、BP算法的优缺点

四、Optimization algorithm

1、Momentum method

2、Adaptive Gradient

3、RMSProp: Root Mean Square Prop

4、Adam算法

总结


前言

        多层感知机是深度学习的基础,为了训练模型中,使得损失函数更好地接近最小值,对于小批量随机梯度下降提出了更好的优化算法,但是深度学习的目标是减少泛化误差。为了实现后者,除使优化算法来减少训练误差之外,我们还需要注意过拟合。


一、Multilayer perceptron

        传统机器学习的方法无法解决非线性问题,比如XOR问题,就是一个典型的线性不可分问题,如图无法用一条线解决分类问题。 

XOR
XOR图示

         可以使用多层感知机解决这个问题,在输入和输出层加一或多层隐单元,构成多层感知器(多层前馈神经网络)。加一层隐节点(单元)为三层网络,可解决XOR问题,如图所示。

多层感知机解决XOR问题
XOR问题表达式

        从这样一个简单的问题就能看出Multilayer perception的强大,可以解决非线性问题。实际上三层感知器可识别任一凸多边形或无界的凸区域。更多层感知器网络,可识别更为复杂的图形。这里给出两个定理,说明Multilayer perception的强大之处。

定理1 若隐层节点(单元)可任意设置,用三层阈值节点的网络,可以实现任意的二值逻辑函数。
定理2 若隐层节点(单元)可任意设置,用三层S型非线性特性节点的网络,可以一致逼近紧集上的连续函数或按 范数逼近紧集上的平方可积函数。

        这两个定理主要意义在于说明Multilayer perception可以描述任意函数,只用一层隐单元就能描述任意函数,但是实际操作上仅用一层是存在困难的,可能很难训练系统。


二、BP算法

        深度学习有一个损失函数作为目标函数,需要通过使这个目标函数(均方误差或者交叉熵损失)最小来更新权重。反向传播(BP)学习算法,即是梯度下降法在Multilayer perception的运用。很多问题没有解析解,所以使用梯度下降这一基本方法来求最小值。

        Multilayer perception的结构:

Multilayer perception结构

        见图,u、y是网络的输入、输出向量,神经元用节点表示,网络由输入层、隐层和输出层节点组成,隐层可一层,也可多层(图中是单隐层),前层至后层节点通过权联接。由于用BP学习算法,所以常称BP神经网络 。

① 正向传播是输入信号从输入层经隐层,传向输出层,若输出层得到了期望的输出,则学习算法结束;否则,转至反向传播。
② 反向传播是将误差(样本输出与网络输出之差)按原联接通路反向计算,由梯度下降法调整各层节点的权值和阈值,使误差减小。

        网络训练的目的,是使对每一个输入样本,调整网络参数,使输出均方误差最小化。

        考虑迭代算法,设初始权值为, k时刻权值为, 则使用泰勒级数展开,有:

         可以选择

        这样就能使得损失函数逐渐减小。

        BP算法基本思想:

        ① 设置初始权系数为较小的随机非零值;
        ② 给定输入/输出样本对,计算网络输出, 完成前向传播
        ③ 计算目标函数J。如J < , 训练成功,退出;否则转入④
        ④ 反向传播计算 由输出层,按梯度下降法将误差反向传播,逐层调整权值。

        至于算法具体推导的过程,可以参考具体的过程,实际上也很简单,就是一个求梯度的梯度的过程,也就是求偏导的过程,利用损失函数的梯度更新权重,来实现反向传播。如果有多个隐单元,可以看出从输出到输入逐层更新权重时,有一部分在计算过程中是相同的,也就是误差,实际上是对误差进行反传。实际上程序中对于梯度下降的计算,更多的依靠图论,这样可以大大减少多余的步骤。

        可以利用深度学习框架,来实现Multilayer perception,这里参考《动手学深度学习》给出简洁实现:

import torch
from torch import nn
from d2l import torch as d2l

net = nn.Sequential(nn.Flatten(),
                    nn.Linear(784, 256),
                    nn.ReLU(),
                    nn.Linear(256, 10))
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights);

batch_size, lr, num_epochs = 256, 0.1, 10
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=lr)

train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

        使用的数据集为:Fashion-MNIST。结果:

结果

d2l.predict_ch3(net, test_iter) # 在一些测试集上使用
结果

 三、BP算法的优缺点

        为防止训练过程中权重过大,可以在这样一个最优化问题中的目标函数加上一个正则项,比如:

加入正则项的目标函数

         优点:

        学习完全自主;可逼近任意非线性函数。

        缺点:

        算法非全局收敛;收敛速度慢;学习速率α选择;神经网络如何设计(几层?节点数?)

        实际上对这些问题的研究促进了深度学习的发展,寻找问题,解决问题,促进发展。


四、Optimization algorithm

        这里仅关注优化算法的在最小化目标函数的性能,而不是模型的泛化误差,实际上目标函数取最小值时,并不一定使得整个模型的泛化误差最小。在深度学习中,⼤多数⽬标函数都很复杂,没有解析解。必须使⽤数值优化算法。

        深度学习存在很多挑战,比如:局部最小值、鞍点和梯度消失。

        局部最小值如图所示,并不是全局最小值:

局部最小值

         鞍点指函数的所有梯度都消失但既不是全局最⼩值也不是局部最⼩值的任何位置。如图:

鞍点

        可能遇到的最隐蔽问题是梯度消失。例如,假设我们想最⼩化函数f(x) = tanh(x),然后我们恰好从x = 4开始。正如我们所看到的那样,f的梯度接近零。更具体地说,,因此是f′(4) = 0:0013。因此,在我们取得进展之前,优化将会停滞很⻓⼀段时间。事实证明,这是在引⼊ReLU激活函数之前训练深度学习模型相当棘⼿的原因之⼀。

梯度消失

         有一系列算法表现良好。实际上没有必要找到最优解。局部最优解或其近似解仍然⾮常有⽤。

1、Momentum method

        随机梯度下降(SGD)可能会遇到病态曲率问题。

病态曲率

        图为损失函数轮廓。在进入以蓝色标记的山沟状区域之前随机开始。颜色实际上表示损失函数在特定点处的值有多大, 红色表示最大值,蓝色表示最小值。我们想要达到最小值点,为此但需要我们穿过山沟。这个区域就是所谓的病态曲率。

        梯度下降沿着山沟的山脊反弹,向极小的方向移动较慢。这是因为脊的表面在这个方向上弯曲得更陡峭。

        使用泄露平均值(leaky average)代替梯度计算:

        v被称为动量。

        如果把原始的 SGD 想象成一个纸团在重力作用向下滚动,由于质量小受到山壁弹力的干扰大,导致来回震荡;或者在鞍点处因为质量小速度很快减为 0 ,导致无法离开这块平地。
        动量方法相当于把纸团换成了铁球;不容易受到外力的干扰,轨迹更加稳定;同时因为在鞍点处因为惯性的作用,更有可能离开平地。

         利用深度学习框架,给出动量法的简洁实现:

%matplotlib inline
import torch
from d2l import torch as d2l

trainer = torch.optim.SGD
d2l.train_concise_ch11(trainer, 'lr': 0.005, 'momentum': 0.9, data_iter)

 2、Adaptive Gradient

        参数自适应变化:具有较大偏导的参数相应有一个较大的学习率,而具有小偏导的参数则对应一个较小的学习率。

AdaGrad

         这种算法存在问题:学习率是单调递减的,训练后期学习率过小会导致训练困难,甚至提前结束。

3、RMSProp: Root Mean Square Prop

        使用RMSProp对于AdaGrad进行改善,按照动量法中的方式使用泄露平均值,对于AdaGrad进行修改。

RMSProp

         给出RMSProp简洁实现。

import math
import torch
from d2l import torch as d2l

trainer = torch.optim.RMSprop
d2l.train_concise_ch11(trainer, 'lr': 0.01, 'alpha': 0.9, data_iter)

 4、Adam算法

        回顾其他优化算法:随机梯度下降在解决优化问题时⽐梯度下降更有效。在⼀个⼩批量中使⽤更⼤的观测值集,可以通过向量化提供额外效率。这是⾼效的多机、多GPU和整体并⾏处理的关键。添加了⼀种机制,⽤于汇总过去梯度的历史以加速收敛。对每个坐标缩放来实现⾼效计算的预处理器。学习率的调整来分离每个坐标的缩放。

        Adam算法将这些优点进行汇总,是一种更强大和更有效的优化算法。但是仍存在问题,可能由于方差控制不良而发散。

        除了加入历史梯度平方的指数衰减平均(𝑟)外还保留了历史梯度的指数衰减平均(𝑠),相当于动量。

        Adam 行为就像一个带有摩擦力的小球,在误差面上倾向于平坦的极小值。

Adam

         简洁实现:

%matplotlib inline
import torch
from d2l import torch as d2l

trainer = torch.optim.Adam
d2l.train_concise_ch11(trainer, 'lr': 0.01, data_iter)


总结

        主要对于深度学习的基础Multilayer Perception和一些优化算法进行了学习,主要参考了《动手学深度学习》,跟多代码科技参考这本书。

deeplearning.net 0.1 document - Multilayer Perceptron

Multilayer Perceptron

下面我们使用Theano来介绍一下单隐藏层的多层感知机(MLP)。MLP可以看成一个logistic回归分类器,它使用一个已经学习的非线性转换器技术分享处理输入。这个转换器把输入变成一个线性可分离的空间。中间层被看作是隐藏层。单个隐藏层足够让MLPs普遍逼近,但是我们会在后面看到使用多层隐藏层是很有效的。

The Model

一个只有单层隐藏层的MLP可以表示成一下形式:

技术分享

通常地,一个单隐藏层的MLP是一个函数技术分享。其中D是输入x的大小,L是输出向量f(x)的大小,f(x)的矩阵表示如下:

技术分享
其中偏置项b,权重项W和激活函数G和s。

向量技术分享组成了隐藏层。技术分享是一个权值矩阵链接输入层和隐藏层。每一列技术分享代表到第i个隐藏层每个输入的权值。s的传统选择包括tanh函数,技术分享,或者sigmoid函数,技术分享。本教程我们使用tanh函数因为它训练得更快(有时会达到一个更好的局部最小值).tanh和sigmoid函数都是标量到标量的函数但是它们可以使用元素依次运算自然地扩展到向量或张量。

输出向量是技术分享。读者需知道我们之前在前一课的形式。以前,类别的成员概率可以被softmax函数来表示。

要训练一个MLP,我们要学习所有的参数,这里我们使用小批量随机下降。要学习的参数集是技术分享。使用梯度技术分享可以进行后向传播算法。值得感谢的是,Theano会自动实现这个过程,这里就不详述。

Going from logistic regression to MLP

本教程会关注单隐藏层的MLP。我们实现一个类来表示一个隐藏层。为了构造MLP我们将在后面在顶层使用logistic回归层。

class HiddenLayer(object):
    def __init__(self, rng, input, n_in, n_out, W=None, b=None, activation=T.tanh):
    ‘‘‘
    传统MLP的隐藏层:全链接并且使用sigmoid激活函数。权重矩阵大小为(n_in, n_out)
    偏置项b是(n_out,)

    提醒:非线性转换器使用tanh

    隐藏层单元的激活函数: tanh(dot(input, W) + b)

    :rng 类型: numpy.random.RandomState
    :rng 参数: 随机初始化权值

    :input 类型: theano.tensor.dmatrix
    :input 参数:符号张量,形状(n_examples, n_in)

    :n_in 类型: 整数
    :n_in 参数: 输入的维数

    :n_out 类型: 整数
    :n_out 参数: 隐藏层的维数

    :activation 类型: theano.Op
    :actibation 参数: 应用在隐藏层的非线性
    ‘‘‘
    self.input = input

隐藏层权值的初始化需要在对应激活函数的对称区间内抽样。对于tanh函数,技术分享,其中(fan)in是第i-1层单元的数量,(fan)out是第i层单元的数量。对于sigmoid函数技术分享。这样初始化保证了在训练的时候,激活函数的神经运算信息可以更容易地前向或者后向传播。

    # ‘W‘被‘W_values‘初始化,区间为tanh的[sqrt(-6./(n_in+n_hidden)) 
    # 到 sqrt(6./(n_in+n_hidden))]。
    # 定义输出的dtype为theano.config.floatX有利于在GPU上运算。
    # 提示:最佳的权值初始化取决于激活函数的使用。
    #      例如:[Xavier10]的结果建议你,对比tanh使用4倍大的sigmoid的权重
    #      但是我们没有其他函数的信息,所以我们使用和tanh相同的。
    if W is None:
        W_values = numpy.array(
            rng.uniform(
            low = -numpy.sqrt(6. / (n_in + n_out)),
            high = numpy.sqrt(6. / (n_in + n_out)),
            size = (n_in, n_out)
            ),
            dtype = theano.config.floatX
        )
        if activation == theano.tensor.nnet.sigmoid:
            W_values *= 4
        W = thenao.shared(value=W_values, name=‘W‘, borrow=True)
    if b is None:
        b_values = numpy.zeros((n_out,), dtype=theano.config.floatX)
        b = theano.shared(value=b_values, name=‘b‘, borrow=True)
    self.W = W
    self.b = b

我们使用一个给定的非线性函数当作隐藏层的激活函数。默认使用tanh,但是有些情况我们会使用其他函数。

    lin_output = T.dot(input, self.W) + self.b
    self.output = (
        lin_output if activation is None
        else activation(lin_output)
    )

如果你看过类实现图的理论计算技术分享。如果给定这个图当作输入传给之前实现的LogisticRegression类,你会获得MLP的输出。

class MLP(object):
    """Muti-Layer Perceptron Class

    一个多层感知机是一个前馈人工神经网络,它有一个或多个隐藏单元和非线性的激活器。
    中间层有一个激活函数tanh或者sigmoid函数(HiddenLayer类),另外顶层是一个
    softmax层(LogisticRegression)
    """

    def __init__(self, rng, input, n_in, n_hidden, n_out):
        # 当我们处理一个隐藏层,它链接一个激活函数tanh和一个Logistic回归层。
        # 激活函数可以被sigmoid函数或其他代替

        self.hiddenLayer = HiddenLayer(
            rng = rng,
            input=input,
            n_in=n_in,
            n_out=n_hidden,
            activation=T.tanh
        )

        # Logistic回归成获得隐藏层的输入
        self.logRegressionLayer = LogisticRegression(
            input = self.hiddenLayer.output,
            n_in = n_hidden,
            n_out=n_out
        )

本教程我们会使用L1和L2正则

    # L1正则
    self.L1 = (
        abs(self.hiddenLayer.W).sum()
        + abs(self.logRegressionLayer.W).sum()
    )

    # L2正则
    self.L2_sqr = (
        (self.hiddenLayer.W ** 2).sum()
        + (self.logRegressionLayer.W ** 2).sum()
    )

    # 负对数似然
    self.negative_log_likelihood = (
        self.logRegressionLayer.negative_log_likelihood
    )

    # 错误率
    self.errors = self.logRegressionLayer.errors

    # 两层的参数
    self.params = self.hiddenLayer.params + self.logRegressionLayer.parmas

在之前,我们用小批量随机下降法来训练模型。不同的是我们使用损失函数是带有L1和L2正则式的。L1_reg和L2_reg是控制权重的正则式。

    cost = (
        classifier.negative_log_likelihood(y)
        + L1_reg * classifier.L1
        + L2_reg * classifier.L2_sqr
    )

然后我们使用梯度来更新模型的参数。这段代码类似logsitic函数。只是参数数量不同而已。为了做到这样(可以运行在不同数量参数的代码),我们会使用参数列表,在每一代计算梯度。

    # 对列表的参数一个个求导
    gparams = [T.grad(cost, param) for param in classfier.params]

    # 约定怎样更新权值,形式是(值, 更新表达式)对

    # 给定两个相同长度的列表 A=[a1, a2, a3, a4]和
    # B = [b1, b2, b3, b4],zip操作生成一个
    # C = [(a1, b1), ..., (a4, b4)]
    updates = [
        (param, param - learning_rate * gparam)
        for param, gparam in zip(classifier.params, gparams)
    ]

    # 编译一个Theano函数‘train_model’返回损失并且更新权值
    train_model = theano.function(
        inputs = [index],
        outputs = cost,
        updates = updates,
        givens = {
            x: train_set_x[index * batch_size : (index + 1) * batch_size]
            y: train_set_y[index * batch_size : (index + 1) * batch_size]
        }
    )

Putting it All Together

Tips and Tricks for training MLPs

在上面的代码中有一些超参数,它不能用梯度下降来优化。严格来说,找到一个最佳的解集不是一个可行的问题。第一,我们不能独立地优化每一个参数。第二,我们不能一下子应用前面说的梯度下降技术(一部分是因为一些参数是离散的,而另一些是实数的)。第三,优化问题不是一个凸优化,找到一个(局部)最优解会涉及一堆不重要的工作。

在过去的25年里,好消息是研究者在神经网络设计了很多选择超参数的规则。想了解更多可以看Yann LeCun,Leon Bottou,Genevieve Orr和Klaus-Robert的Efficient BackPro。在这里,我们总结一些方法,是一些我们应用在代码中的重点。

Nonlinearity

两个常用的方法是sigmoid和tanh,原因在Section 4.4中。非线性在刚开始对称因为它使输入变成平均值为0的输入到下一层。经验告诉我们,tanh有更好地收敛性质。

Weight initialization

在初始化我们想权值在刚开始时足够小,那样的话激活函数的运算就会在线性工作状态,这样导数最大。另外合适的性质,特别在深度网络,是保存激活器的方差和层与层之间的反向传播梯度的方差。这允许信息在向上和向下都很好地流动,减少层之间的差异。在某些假设下,两个限制的妥协导致了下面的初始化方式:
tanh初始化权值技术分享
sigmoid初始化权值技术分享
数学推导请看Xavier10

Learning rate

在文献上有好的处理方法。最简单的方法就是常数学习率。经验法则是:尝试几个对数空间值(10的-1, 10的-2, …)和缩小(对数的)格子去搜索哪里获得最小检验错误。

多次降低学习率有时是一个很好的方法。一个简单的规则就是技术分享,其中u0是初始学习率,d是下降常数来控制下降速度(通常一个小的正数,10的-3或更小),t是epoch/stage。

Section 4.7参看更多信息。

Number of hidden units

超参数很依赖于数据集。含糊地说,输入的分布越复杂,网络要建模的能力越强,就需要越多的隐藏层。

除非我们使用正则化,典型数量的隐藏层 vs. 泛化表现就像一个U字。

Regularization parameter

经典的方法就是使用L1/L2正则参数,10的-2,10的-3, …,在以后的框架里,优化这个参数不会带来什么显著的变化,但是有时也值得尝试。





以上是关于Multilayer perceptron&Optimization algorithm的主要内容,如果未能解决你的问题,请参考以下文章

翻译: 4.1 多层感知器MLP Multilayer Perceptrons pytorch

Theano-Deep Learning Tutorials 笔记:Multilayer Perceptron

动手学深度学习 3-3 Mlp

前馈神经网络

多层感知器:ConvergenceWarning:随机优化器:达到最大迭代次数,优化尚未收敛。警告?

多层感知器,在 Python 中可视化决策边界 (2D)