神经网络基础

神经网络基础

1.神经网络模型

所谓神经网络就是将许多个单一“神经元”联结在一起,这样,一个“神经元”的输出就可以是另一个“神经元”的输入。例如,下图就是一个简单的神经网络:

我们使用圆圈来表示神经网络的输入,标上“+1”的圆圈被称为”’偏置节点”’,也就是截距项。神经网络最左边的一层叫做 ”‘输入层”’,最右的一层叫做”‘输出层’”(本例中,输出层只有一个节点)。中间所有节点组成的一层叫做”’隐藏层”’,因为我们不能在训练样本集中观测到它们的值。同时可以看到,以上神经网络的例子中有3个”’输入单元”’(偏置单元不计在内),3个”’隐藏单元”’及一个”’输出单元”’。

我们用nl 来表示网络的层数,本例中nl=3,我们将第l 层记为Ll ,于是L1是输入层,输出层Lni 。本例神经网络有参数(W,b)=(W(1),b(1),W(2),b(2)) ,其中W(l)ij(下面的式子中用到)是第l层第j 单元与第l+1 层第i 单元之间的联接参数(其实就是连接线上的权重,注意标号顺序),bi(l) 是第l+1 层第i 单元的偏置项。因此在本例中,W(1)∈ℜ3×3 ,W(2)∈ℜ1×3 。注意,没有其他单元连向偏置单元(即偏置单元没有输入),因为它们总是输出+1。同时,我们用sl表示第ll 层的节点数(偏置单元不计在内)。

本例神经网络的计算步骤如下:

我们用z(l)i 表示第l 层第i单元输入加权和(包括偏置单元):

再将激活函数进行广播,及可以得到前向传播过程:

当然神经网络也可以具有多个隐藏层,其计算原理同之前类似。

如果你想预测的输出是多个的,那这种神经网络很适用。(比如,在医疗诊断应用中,患者的体征指标就可以作为向量的输入值,而不同的输出值y**iyi 可以表示不同的疾病存在与否。)

2.前馈神经网络(FF)

前馈神经网络(FF),这是一个很古老的方法——这种方法起源于50年代。它的工作原理通常遵循以下规则:

1.所有节点都完全连接

2.激活从输入层流向输出,无回环

3.输入和输出之间有一层(隐含层)

在大多数情况下,这种类型的网络使用反向传播方法进行训练。

3.感知机

1.算法原理

想法来自生物学的神经元的一些工作方式,多个生物信号 (input singals) 到达树突 (dentrites)并进入细胞核 (cell nucleus),如果这些信号的效果累加达到一个阈值,那么通过轴突 (axon) 产生一个输出信号 (output signals)。在有监督学习与分类的背景下,这样的算法可被用来预测一个样本是否属于某个类别。

正式地,我们可以把这个问题表述为一个二分类任务,并且为了简单起见,将这两个类别分别定义为 1 (正类) 与 -1(负类)。接着定义一个激活函数 (activation function) ,, 它输入的是 xx 与其对应的权重向量 ww 的一个线性组合.

对于一个指定样本 x(i)x(i), 如果 ϕ(z)ϕ(z) 的输出值大于预先定义的一个阈值 Θ, 那么就预测其类别 1. 否则,预测为类别 -1. 在感知器算法中,激活函数 是一个简单的单位阶跃函数 (unit step function)

需要注意一点的是只有当两个类别是线性可分时,感知器算法才能保证收敛。如果两个类别不是线性可分,那么我们可以在训练集上设置一个最大的迭代次数, 或是设置一个可接受的错误分类的阈值。

2.感知机权重的学习过程

采用随机梯度下降法:

推荐一个神经网络可视化的网站 :tensorflow 游乐场

3.使用tensoeflow定义几层简单的神经网络

激活函数使用sigmoid,递归使用链式法则来实现反向传播:

  1. 再次运用的时简单类定义进行神经网络的计算。

  2. tensorflow版本待更

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    import numpy as np
    import math
    import random

    def rand(a,b):
    return (b-a)*random.random()+a

    def make_matrix(m,n,fill=0.0):
    mat=[]
    for i in range(m):
    mat.append([fill]*n)
    return mat

    def sigmoid(x):
    return 1.0/(1.0+math.exp(-x));

    def sigmod_derivate(x):
    return x*(1-x)

    class BPNeuralNetWork:

    def _init_(self):
    self.input_n=0
    self.hidden_n=0
    self.output_n=0
    self.input_cells=[]
    self.hidden_cells=[]
    self.output_cells=[]
    self.input_weights=[]
    self.output_weights=[]

    def setup(self,ni,nh,no):
    self.input_n=ni+1
    self.hidden_n=nh
    self.output_n=no

    self.input_cells=[1.0]*self.input_n
    self.hidden_cells=[1.0]*self.hidden_n
    self.output_cells=[1.0]*self.output_n

    self.input_weights=make_matrix(self.input_n,self.hidden_n)
    self.output_weights=make_matrix(self.hidden_n,self.output_n)
    for i in range(self.input_n):
    for h in range(self.hidden_n):
    self.input_weights[i][h]=rand(-0.2,0.2)
    for h in range(self.hidden_n):
    for j in range(self.output_n):
    self.output_weights[h][j]=rand(-0.2,0.2)

    def predict(self,inputs):
    for i in range(self.input_n-1.0):
    self.input_cells[i]=inputs[i]

    for j in range(self.hidden_n):
    total+=self.input_cells[i]*self.input_weights[i][j]
    self.hidden_cells[i]=sigmoid(total)#不要忘记去线性化

    for k in range(self.output_n):
    total=0.0
    for j in range(self.output_n):
    total+=self.hidden_cells[j]*self.output_weights[j][k]
    self.output_cells[k]=sigmoid(total)
    return self.output_cells[:]


    def back_propagate(self,case,label,learn):

    self.predict(case)

    output_deltas=[0.0]*self.output_n
    for k in range(self.output_n):
    error=label[k]-self.output_cells[k]
    output_deltas[k]=sigmod_derivate(self.output_cells[k])*error

    hidden_deltas=[0.0]*self.hidden_n
    for j in range(self.input_n):
    error=0.0
    for k in range(self.output_n):
    error+=output_deltas[k]*self.output_weights[j][k]
    hidden_deltas[j]=sigmod_derivate(self.hidden_cells[j])*error

    for j in range(self.hidden_n):
    for k in range(self.output_n):
    self.output_weights[j][k]+=learn*output_deltas[k]*self.hidden_cells[j]

    for i in range(self.input_n):
    for j in range(self.hidden_n):
    self.input_weights[i][j]=learn*hidden_deltas[j]*self.input_cells[i]

    error=0

    for o in range(len(label)):
    #L1实现
    error+=0.5*(label[o]-self.output_cells[o])**2
    return error

    def train(self,cases,labels,limit=100,learn=0.05):
    for i in range(limit):
    error=0
    for i in range(len(cases)):
    label=labels[i]
    case=cases[i]
    error+=self.back_propagate(case,label,learn)
    pass

    def test(self):
    cases=[
    [0,0],
    [0,1],
    [1,0],
    [1,1],
    ]
    labels=[[0],[1],[1],[0]]
    dself.setup(2,5,1)
    self.train(cases,labels,10000,0.05)
    for case in cases:
    print(self.predict(case))

    if __name__=='__mian__':
    nn=BPNeuralNetWork()
    nn.test()

4.激活函数的种类以及各自的背景,优缺点

1,定义

在神经元中,输入的 inputs 通过加权,求和后,还被作用了一个函数,这个函数就是激活函数 Activation Function。

使用激活函数对线性值进行去线性化。

2.为什么要使用激活函数(线性模型的局限性)

如果不用激励函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合。如果使用的话,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中

3.分类定义
(1).sigmoid函数

公式:

曲线:

取值范围为(0,1),用于隐层神经元输出
它可以将一个实数映射到(0,1)的区间,可以用来做二分类。
在特征相差比较复杂或是相差不是特别大时效果比较好。
sigmoid缺点

激活函数计算量大,反向传播求误差梯度时,求导涉及除法。反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。

(2) Tanh函数

公式:

曲线:

也称为双切正切函数
取值范围为[-1,1]。
tanh在特征相差明显时的效果会很好,在循环过程中会不断扩大特征果。与 sigmoid 的区别是,tanh 是 0 均值的,因此实际应用中 tanh 会比 sigmoid 更好。

3) ReLU

Rectified Linear Unit(ReLU) - 用于隐层神经元输出

公式:

曲线

输入信号 <0 时,输出都是0,="">0 的情况下,输出等于输入

ReLU 的优点:使用 ReLU 得到的 SGD 的收敛速度会比 sigmoid/tanh 快很多

ReLU 的缺点
训练的时候很”脆弱”,很容易就”die”了
例如,一个非常大的梯度流过一个 ReLU 神经元,更新过参数之后,这个神经元再也不会对任何数据有激活现象了,那么这个神经元的梯度就永远都会是 0.
如果 learning rate 很大,那么很有可能网络中的 40% 的神经元都”dead”了。

5.深度学习中的正则化

1.定义

《统计学习方法》中认为正则化是选择模型的一种方法。

正则化通过对学习算法的修改,旨在减少泛化误差而不是训练误差。目前有很多正则化策略,有些是向机器学习模型中添加限制参数值的额外约束,有些是向目标函数添加额外项来对参数值进行软约束。

2.L1正则化

L1正则化对梯度的影响不是线性地缩放每个wi而是添加了一项与sign(wi)同号的常数。这种形式的梯度不一定能得到直接算术解。

相比L2正则化,L1正则化会产生更稀疏的解。这种稀疏性广泛用于特征选择机制,可以从可用的特征子集中选择出有意义的特征,化简机器学习问题。

L1正则化可通过假设权重w的先验分布为拉普拉斯分布,由最大后验概率估计导出.

3.L2正则化

通过向目标函数添加一个L2范数平方项,使权重更加接近原点。

《Deep Learning》书中也提到:L2正则化能让学习算法“感知”到具有较高方差的输入xx,因此与输出目标的协方差较小的特征的权重将会收缩。L2正则化能够很好的防止过拟合:

  1. 通过引入L2正则,使模型参数偏好比较小的值,这其实也限制的函数空间的大小,有针对的减小了模型容量。一般来说,大的参数值对应于波动剧烈的函数,小的参数值对应于比较平缓的参数,因为小参数对于输入的改变不会那么敏感。发生过拟合往往是因为顾及到了所有样本点,所以此时的函数波动会比较大,如下面右图所示。过拟合的模型往往具有比较大的参数,如果将这部分模型族从假设空间中剔除掉,发生过拟合的可能就变小。

  1. L2正则化能让学习算法“感知”到具有较高方差的输入xx,因此与输出目标的协方差较小的特征的权重将会收缩。因此L2正则化总是倾向于对那些训练集样本共用的特征产生较大的响应,而减小对个别样本独有的特征产生的响应。因此L2正则有抑制那些“独有特征”的作用,这在一定程度上也减小了过拟合的风险。
      L2正则化可通过假设权重ww的先验分布为高斯分布,由最大后验概率估计导出.
4.数据集增强
  • 原因:一般而言,比较成功的神经网络需要大量的参数,许许多多的神经网路的参数都是数以百万计,而使得这些参数可以正确工作则需要大量的数据进行训练,而实际情况中数据并没有我们想象中的那么多
  • 作用:
    1. 增加训练的数据量,提高模型的泛化能力
    2. 增加噪声数据,提升模型的鲁棒性
  • 方法:
    1. 增加数据集,一般较难实现
    2. 利用已有的数据比如翻转、平移或旋转,创造出更多的数据,来使得神经网络具有更好的泛化效果。
  • 分类:
    1. 离线增强 : 直接对数据集进行处理,数据的数目会变成增强因子 x 原数据集的数目 ,这种方法常常用于数据集很小的时候
    2. 在线增强 : 这种增强的方法用于,获得 batch 数据之后,然后对这个 batch 的数据进行增强,如旋转、平移、翻折等相应的变化,由于有些数据集不能接受线性级别的增长,这种方法长用于大的数据集,很多机器学习框架已经支持了这种数据增强方式,并且可以使用 GPU 优化计算。
5.噪声增加

​ 简单提高网络抗噪能力的方法,就是在训练中加入随机噪声一起训练。我们可以在网络的不同位置加入噪声:输入层,隐藏层,输出层。
  数据集增强在某种意义上也能看做是在输入层加入噪声,通过随机旋转、翻转,色彩变换,裁剪等操作人工扩充训练集大小。这样可以使得网络对于输入更加鲁棒。
  当然如果你能保证数据集没错误,不向输出层加入噪声也没关系。解决这个问题常见的办法是标签平滑,通过把确切的分类目标从0和1替换成ϵk−1和1−ϵ,正则化具有k个输出的softmax函数的模型。标签平滑的优势是能够防止模型追求确切的概率而不能学习正确分类。
  从优化过程的角度来看,对权重叠加方差极小噪声等价于对权重是假范数惩罚,可以被解释为关于权重的贝叶斯推断的随即实现(这一点我不是很理解)。换个角度就很结论就很明朗了,因为我们在权重中添加了一些随机扰动,这鼓励优化过程找到一个参数空间,该空间对微小参数变化引起的输出变化影像很小。将模型送入了一个对于微小变化不敏感的区域,不仅找到了最小值,还找到了一个宽扁的最小值区域。

early stop(提前终止)

模型过拟合一般是发生在训练次数过多的情况下,那么只要我们在过拟合之前停止训练即可。这也是深度学习中最常用的正则化形式,主要是因为它的有效性和简单性。提前终止需要验证集损失作为观测指标,当验证集损失开始持续上升时,这时就该停止训练过程了。

Dropout

先介绍集成学习的概念

  • 集成学习通过结合几个模型降低泛化误差的技术。分别训练几个不同的模型,然后让所有模型表决测试样例的输出,也被称为模型平均。模型平均奏效的原因是不同的模型通常不会再测试集上产生完全相同的误差。

    假设我们有k个回归模型,每个模型在每个例子上的误差为ϵi,这个误差服从均值为0,方差E[ϵi^2]=v且协方差E[ϵiϵj]=c的多维正态分布。通过所有集成模型的平均预测所得误差是1k∑iϵi,则该集成预测期的平方误差的期望为:

​ 在误差完全相关即c=v的情况下,E[(1/k*∑iϵi)^2]=v,模型平均对提升结果没有任何帮 助;在误差完全不想关即c=0的情况下,集成模型的误差期望为E[(1/k∑iϵi)^2]=(1/k)v。用一 句话说,平均上,集成至少与它任何成员表现的一样好,并且如果成员误差是独立的,集成将显著地比其成员表现的更好。神经网络中随机初始化的差异、小批量的随机选择、超参数的差异或不同输出的非确定性实现往往足以使得集成中的不同成员具有部分独立的误差。

  • Dropout

    Dropout是每次训练过程中随机舍弃一些神经元之间的连接

Dropout提供了一种廉价的Bagging集成近似,能够训练和评估指数级数量的神经网络。Dropout和Bagging训练不太一样,Bagging所有模型都是独立的,而Dropout是所有模型共享参数,每个模型集成父神经网络参数的不同子集,这中参数共享的方式使得在有限可用内存下表示指数级数量的模型变得可能。
  隐藏单元经过Dropout训练后,它必须学习与不同采样神经元的合作,使得神经元具有更强的健壮性,并驱使神经元通过自身获取到有用的特征,而不是依赖其他神经元去纠正自身的错误。这可以看错对输入内容的信息高度智能化,自适应破坏的一种形式,而不是对输入原始值的破坏。
  Dropout的另一个重要方面是噪声是乘性的。如果是固定规模的加性噪声,那么加了噪声ϵ的ReLU可以简单的学会使hi变得很大(使增加的噪声ϵϵ变得不显著)。乘性噪声就不允许这样病态的去解决噪声鲁棒问题。

深度模型的优化

1.参数初始化策略

1.梯度下降算法

  • 批量梯度下降(Batch gradient descent)

    批量梯度下降每次学习都使用整个训练集,因此其优点在于每次更新都会朝着正确的方向进行,最后能够保证收敛于极值点(凸函数收敛于全局极值点,非凸函数可能会收敛于局部极值点),但是其缺点在于每次学习时间过长,并且如果训练集很大以至于需要消耗大量的内存,并且全量梯度下降不能进行在线模型参数更新。

  • 随机梯度下降(Stochastic gradient descent)

    随机梯度下降算法每次从训练集中随机选择一个样本来进行学习,即: θ=θ−η⋅∇θJ(θ;xi;yi)

    批量梯度下降算法每次都会使用全部训练样本,因此这些计算是冗余的,因为每次都使用完全相同的样本集。而随机梯度下降算法每次只随机选择一个样本来更新模型参数,因此每次的学习是非常快速的,并且可以进行在线更新。

    随机梯度下降最大的缺点在于每次更新可能并不会按照正确的方向进行,因此可以带来优化波动(扰动)

  • 小批量梯度下降(Mini-batch gradient descent)

    Mini-batch 梯度下降综合了 batch 梯度下降与 stochastic 梯度下降,在每次更新速度与更新次数中间取得一个平衡,其每次更新从训练集中随机选择 m,m<n 个样本进行学习,mini-batch梯度下降可以保证收敛性,常用于神经网络中。

以下摘抄自网络:参数初始化策略
2.AdaGrad

AdaGrad 算法,如下图所示,独立地适应所有模型参数的学习率,缩放每个参数反比于其所有梯度历史平方值总和的平方根 (Duchi et al., 2011)。具有损失最大偏导的参数相应地有一个快速下降的学习率,而具有小偏导的参数在学习率上有相对较小的下降。净效果是在参数空间中更为平缓的倾斜方向会取得更大的进步。在凸优化背景中, AdaGrad 算法具有一些令人满意的理论性质。然而,经验上已经发现,对于训练深度神经网络模型而言, 从训练开始时积累梯度平方会导致有效学习率过早和过量的减小。 AdaGrad 在某些深度学习模型上效果不错,但不是全部

1.RMSProp

RMSProp 算法 (Hinton, 2012) 修改 AdaGrad 以在非凸设定下效果更好,改
变梯度积累为指数加权的移动平均。 AdaGrad 旨在应用于凸问题时快速收敛。当应用于非凸函数训练神经网络时,学习轨迹可能穿过了很多不同的结构,最终到达一个局部是凸碗的区域。 AdaGrad 根据平方梯度的整个历史收缩学习率,可能使得学习率在达到这样的凸结构前就变得太小了。 RMSProp 使用指数衰减平均以丢弃遥远过去的历史,使其能够在找到凸碗状结构后快速收敛,它就像一个初始化于该碗状结构的 AdaGrad 算法实例。RMSProp 的标准形式如算法 8.5 所示,结合 Nesterov 动量的形式如算法 8.6 所示。相比于 AdaGrad,使用移动平均引入了一个新的超参数ρ,用来控制移动平均的长度范围。经验上, RMSProp 已被证明是一种有效且实用的深度神经网络优化算法。目前它是深度学习从业者经常采用的优化方法之一。

4.Adam

Adam (Kingma and Ba, 2014) 是另一种学习率自适应的优化算法,如算法 8.7 所示。 “Adam’’ 这个名字派生自短语 “adaptive moments’’。早期算法背景下,它也许最好被看作结合 RMSProp 和具有一些重要区别的动量的变种。首先,在 Adam 中,动量直接并入了梯度一阶矩(指数加权)的估计。将动量加入 RMSProp 最直观的方法是将动量应用于缩放后的梯度。结合缩放的动量使用没有明确的理论动机。其次, Adam 包括偏置修正,修正从原点初始化的一阶矩(动量项)和(非中心的)二阶矩的估计(算法 8.7 )。 RMSProp 也采用了(非中心的)二阶矩估计,然而缺失了修正因子。因此,不像 Adam,RMSProp 二阶矩估计可能在训练初期有很高的偏置。Adam 通常被认为对超参数的选择相当鲁棒,尽管学习率有时需要从建议的默认修改。

2.batch normal层
3.layer normal层