小建儿的小站


  • 首页

  • 关于

  • 标签

  • 分类

  • 搜索

正则化与梯度

发表于 2017-11-12 | 分类于 机器学习 |
字数统计: | 阅读时长 ≈

  课程01《神经网络和深度学习》的学习告一段落,这周开始课程02《改善深层神经网络:超参数调试、正则化及优化》的学习,第一周课程讲述了数据集的划分、方差与偏差的概念、正则化、梯度消失和梯度爆炸等知识点,都是在实际应用中会遇到的一些问题。

数据集的划分

  首先明确一下训练集、验证集和测试集的概念,之前很少强调验证集,导致把验证集和测试集的概念混淆。

训练集

  训练集(training set)用来构建模型、确定模型参数的样本。

验证集

  验证集(validation set)用于模型优化、确定网络结构或者控制模型复杂程度的参数,主要是辅助模型构建。

测试集

  测试集(training set)用于检验训练好的模型的预测能力。
  以前没有仔细区分验证集和测试集,一些demo也只是将数据集划分为training set和test test,此处需要注意验证集的基本概念。ng在视频中说对一般量级的数据进行划分的时候,可以取训练集:验证集:测试集=3:1:1的比例;如果分为两类,一般是训练集:测试集=7:3。在大数量级数据的情况下,验证集和测试集占的比例可能较小。ng特意强调了保证测试集和验证集来自同一分布

方差与偏差

方差

  方差描述的是预测值相对于期望值的波动范围,也可以说是离散程度。方差越大,数据的分布越分散,波动范围越大;反之亦然。

偏差

  描述的是预测值的期望与真实值之间的差距。偏差越大,和真实值差的越多。

下图比较形象的说明了两者的区别



正则化

  当模型在训练集的效果在训练集上效果表现很好,但是在测试集上表现很差的话,说明出现了过拟合的情况。举个例子来说,就好比上学的时候做练习题,如果一个学生练习题做的很好,但是考试好不过,说明学习方法有问题,不能很好的掌握所学知识,出现了过拟合的问题。解决过拟合的问题有以下几种方法:数据集扩增、early stopping、正则化。

数据集扩增

  对抗过拟合的一个有效方法就是用更多的训练数据去训练模型,用于训练模型的数据多了,说明模型使用范围更广泛。就好比一个学生做的练习题多了,把知识都融会贯通了,考试自然就能考高分。
  数据集扩增不太容易办到,ng讲了几个例子可以较方便的扩展数据集,主要包括产生镜像图片、旋转原图、图像加噪声等。

early stopping

  随着代价函数的不断减小,训练误差是一个不断变小的过程,验证误差起初也会不断减小,但是当出现过拟合的时候验证误差会慢慢增加,eraly stopping就是在验证误差即将增大的时候停止迭代的过程。下图能比较明显的表示这个过程。



正则化

  正则化方法是指在进行目标函数优化时,在目标函数后面加上一个正则项,来达到防止过拟合的目的,即$C = J(W,b)+R$,其中$J(W,b)$是原损失函数,$R$是正则项。常见的正则项有L1正则和L2正则。

L1正则化

  L1正则化就是在原损失函数基础上加上L1范数,即$C = J(W,b)+\frac{\lambda}{n}\sum^{n}_{i=1}|w_{i}|$。L1正则化对所求权重产生的影响从下面推导的过程看出来,首先求导
$$\frac{\partial{C}}{\partial{w}}=\frac{\partial{J(W,b)}}{\partial{w}}+\frac{\lambda}{n}sign(w)$$
则梯度更新的过程为
$$w:=w-\eta\frac{\partial{J(W,b)}}{\partial{w}}-\eta\frac{\lambda}{n}sign(w)$$
  当$w>0$时,更新后的$w$变小;而当$w<0$时,更新后的$w$变大,所以产生的效果就是使得$w$趋近于0。

L2正则化

  L1正则化就是在原损失函数基础上加上L1范数,即$C = J(W,b)+\frac{\lambda}{2n}\sum^{n}_{i=1}w_{i}^2$。同样可以通过推导来看L2范数对权重产生的影响,首先求导
$$\frac{\partial{C}}{\partial{w}}=\frac{\partial{J(W,b)}}{\partial{w}}+\frac{\lambda}{n}w$$
则梯度更新的过程为
$$w:=w-\eta\frac{\partial{J(W,b)}}{\partial{w}}-\eta\frac{\lambda}{n}w = (1-\eta\frac{\lambda}{n})w-\eta\frac{\partial{J(W,b)}}{\partial{w}}$$
因为$\eta,\lambda,n$都是大于0的,所以更新后的$w$相对于没加L2正则的时候变小了,有缩减系数的效果。

DropOut正则化

  Dropout是深度学习正则化的一种方式,它指按照一定的概率将一些神经元暂时从网络中丢弃,被丢弃的神经元不会对后面的计算产生影响,效果如下图。



梯度消失和梯度爆炸

  假设有n层网络,b暂时忽略不计,激活函数为线性激活函数,则一个神经元的输出为$z=Wx$,那么经过n层网络输出为
$$y=W^{(n)}W^{(n-1)}W^{(n-2)}……W^{(2)}W^{(1)}x$$
所以当$W$的值稍比单位矩阵大,经过指数变换,得到的结果会很大;相应的若$W$的值稍比1小,经过指数变换,得到的结果也会变小。
  考虑激活函数的话也会产生类似的效果,所以在搭建神经网络时,要考虑激活函数和权重初值,避免出现梯度消失和梯度爆炸的情况。

小结

  关于正则化的问题,有两点要说明。首先,对于正则化缩减系数的理解:所求的$w$是特征的权重值,当出现过拟合时,几乎样本中的每个点都在拟合的曲线上,就像下图所示。




我们可以看出和二次曲线相比,该图像的其斜率较大。由于自变量的阶数对斜率的影响较小,从而可以推断出斜率变大的主要起因是权重值过大。所以减轻过拟合现象就要控制权重值不能过大,这也是为什么要加上正则项。
  第二点就是目前接触到的教材都是告诉我们正则有这样几种方法,但是并没有深究这种方法的理论来源。其实正则项推导和贝叶斯学派有一定的关系,但是我比较疑惑的是添加正则项后的模型效果更好,是否说明贝叶斯学派的理论相比频率学派更优呢?关于如何从贝叶斯学派的理论推导出正则项之后再单开一篇博客进行介绍。

深层神经网络

发表于 2017-11-11 | 分类于 机器学习 |
字数统计: | 阅读时长 ≈

  上篇博文在两层神经网络的基础上进行了正向传播和反向传播的推导,由于网络层数较少,且只考虑了一个样本的情况,使得推导不具有普适性,本章将进行深层神经网络场景下的正向反向传播推导。

正向传播

  假设第$l$层网络有$n$个神经元,第$l-1$层网络有$m$个神经元,$w^{(l)}_{ij}$表示第$l$层第$i$个神经元与第$l-1$层第$j$个神经元的连接权重,$b^{(l)}_{i}$表示第$l$层第i个单元的偏置项,$z^{(l)}_{i}$表示第$l$层第$i$个神经元的输入, $a^{(l)}_{i}$表示第$l$层第$i$个神经元的输入,$f(x)$为激活函数,则有
$$z^{(l)}_{i} = \sum^{m}_{j=1}w^{(l)}_{ij}a^{(l-1)}_{j}+b^{(l)}_{i}$$
$$a^{(l)}_{i}=f[z^{(l)}_{i}]$$
前向传播比较简单,公式推导比较简单,接下来将其向量化。
$$W^{(l)}=\begin{pmatrix}
w^{(l)}_{11} & w^{(l)}_{12} &\cdots & w^{(l)}_{1m}\\\
w^{(l)}_{21} & w^{(l)}_{22} &\cdots & w^{(l)}_{2m}\\
\vdots & \vdots & \ddots &\vdots \\
w^{(l)}_{n1} & w^{(l)}_{n2} &\cdots & w^{(l)}_{nm}\
\end{pmatrix}$$

$$A^{(l-1)}=\begin{bmatrix}
a^{(l-1)}_{1}\
a^{(l-1)}_{2}\
\vdots\
a^{(l-1)}_{m}
\end{bmatrix}$$

$$B^{(l)}=\begin{bmatrix}
b^{(l)}_{1}\
b^{(l)}_{2}\
\vdots\
b^{(l)}_{m}
\end{bmatrix}$$

$$Z^{(l)}=\begin{bmatrix}
z^{(l)}_{1}\
z^{(l)}_{2}\
\vdots\
z^{(l)}_{m}
\end{bmatrix}$$
所以有
$$Z^{(l)}=W^{(l)}A^{(l-1)}+B^{(l)}$$
$$A^{(l)}=f[Z^{(l)}]$$

反向传播

  上篇博文中反向传播使用的均方误差作为损失函数,实际上还可以使用交叉熵等其他公式,所以此处使用$J(W,b)$来代表损失函数,根据链式求导法则有:
$$\bigtriangledown_{W^{(l)}} J(W,b)=\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}\frac{\partial{z^{(l)}}}{\partial{W^{(l)}}}=\delta^{(l)}[A^{(l-1)}]^{T}$$
$$\bigtriangledown_{b^{(l)}} J(W,b)=\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}\frac{\partial{z^{(l)}}}{\partial{b^{(l)}}}=\delta^{(l)}$$
其中$\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}=\delta^{(l)}$,接下来继续求$\delta^{(l-1)}=\frac{\partial{J(W,b)}}{\partial{z^{(l-1)}}}$
$$\delta^{(l-1)}=\frac{\partial{J(W,b)}}{\partial{z^{(l-1)}}}=\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}
\frac{\partial{z^{(l)}}}{\partial{a^{(l-1)}}}
\frac{\partial{a^{(l-1)}}}{\partial{z^{(l-1)}}}
=[W^{(l)}]^{T}\delta^{(l)}f’(z^{(l-1)})$$
其中$\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}=\delta^{(l)}$,$\frac{\partial{z^{(l)}}}{\partial{a^{(l-1)}}} = W^{(l)}$,$\frac{\partial{a^{(l-1)}}}{\partial{z^{(l-1)}}}=f’(z^{(l-1)})$。所以可以从输出层向输入层逐步计算每一层的$\delta^{(l)}$。

神经网络工作流程

  整个神经网络的计算过程如下:

  1. 前向传播,利用前向传播公式计算每一层的输出值$a^{(l)}$
    $$Z^{(l)}=W^{(l)}A^{(l-1)}+B^{(l)}$$
    $$A^{(l)}=f[Z^{(l)}]$$
  2. 对输出层计算残差
    $$\delta^{(l)}=\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}$$
  3. 对其他隐含层计算每一层的残差
    $$\delta^{(l-1)}=\frac{\partial{J(W,b)}}{\partial{z^{(l-1)}}}=\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}
    \frac{\partial{z^{(l)}}}{\partial{a^{(l-1)}}}
    \frac{\partial{a^{(l-1)}}}{\partial{z^{(l)}}}
    =[W^{(l)}]^{T}\delta^{(l)}f’(z^{(l-1)})$$
  4. 计算各层的偏导数$W^{(l)}$、$b^{(l)}$
    $$\bigtriangledown_{W^{(l)}} J(W,b)=\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}\frac{\partial{z^{(l)}}}{\partial{W^{(l)}}}=\delta^{(l)}[A^{(l-1)}]^{T}$$
    $$\bigtriangledown_{b^{(l)}} J(W,b)=\frac{\partial{J(W,b)}}{\partial{z^{(l)}}}\frac{\partial{z^{(l)}}}{\partial{b^{(l)}}}=\delta^{(l+1)}$$
  5. 利用梯度下降算法更新每一层的梯度
    $$W^{(l)}:=W^{(l)}-\eta\bigtriangledown_{W^{(l)}}J(W,b)$$
    $$b^{(l)}:=b^{(l)}-\eta\bigtriangledown_{b^{(l)}} J(W,b)$$

这次还是以推导为主,比较抽象,下次将结合实例进行分析,有助于理解神经网络的工作过程。

浅层神经网络

发表于 2017-10-29 | 分类于 机器学习 |
字数统计: | 阅读时长 ≈

  第三周的课程内容包括单隐层神经网络、如何初始化参数、正向传播、计算导数、梯度下降和反向传播,ng结合简单的两层深度学习模型详细阐述了深度学习的原理,中间的计算过程十分重要,需要大家耐心理解、推导。

神经元

  “神经元”是深度学习网络中的最基本的单元,其结构如下图所示:



该神经元以$x_{1},x_{2},x_{3}$和截距+1为输入,各分量对应的权重值为$\omega_{1},\omega_{2},\omega_{3},b$,所以该神经元对应的输出为$h_{W,b}(x)=f\left(W^TX \right)=f(\sum_{i=1}^{3}\omega_{i}x_{i}+b)$,其中f为被称为激活函数,可以认为是sigmoid函数,后面会介绍更多的激活函数。不难看出这样的一个神经元输入输出的映射关系正是之前推导过的逻辑回归。

两层神经网络

  很多简单的神经元可以构成一张复杂的神经网络,为了方便演示神经网络的工作过程,我们以两层神经网络为例进行分析。两层神经网络的结构如下图所示:



其中最左侧的是输入层,最右边的是输出层,中间的叫做隐含层,一般在说神经网络的层数时不算输入层,所以该模型是两层神经网络。该模型只包含一个隐含层,计算上较为简单,后面会根据该模型进行正向传播的推导。

正向传播

  根据两层神经网络的示意图进行正向传播的推导。首先假设$n_{l}$表示网络的层数,第l层记为$L_{l}$,所以本例中$L_{1}$是输入层,$L_{2}$是隐含层,$L_{3}$是输出层。

  神经网络参数主要有$\left(W,b\right)$,其中$W^{(l)}_{ij}$表示第l层第j个单元和l+1层第i个单元之间的连接参数,即权重。$b^{(l)}_{i}$是第l+1层第i个单元的偏置项。

  $a^{(l)}_{i}$表示第l层第i个单元的输出值。当l=1时,$a^{(1)}_{i}=x_{i}$,也就是第i个节点的输入值。最终的输出结果用$h_{W,b}(x)$表示。

  所以该神经网络的正向传播过程如下。第一层到第二层的传播过程:

$$a^{(2)}_{1}=f\left[W^{(1)}_{11} x_{1}+W^{(1)}_{12} x_{2}+W^{(1)}_{13} x_{3}+b^{(1)}_{1}\right]$$
$$a^{(2)}_{2}=f\left[W^{(1)}_{21} x_{1}+W^{(1)}_{22} x_{2}+W^{(1)}_{23} x_{3}+b^{(1)}_{2}\right]$$
$$a^{(2)}_{3}=f\left[W^{(1)}_{31} x_{1}+W^{(1)}_{32} x_{2}+W^{(1)}_{33} x_{3}+b^{(1)}_{3}\right]$$

第二层到第三层的传播过程
$$a^{(3)}_{1}=f\left[W^{(2)}_{11} a^{(2)}_{1}+W^{(2)}_{12} a^{(2)}_{2}+W^{(2)}_{13} a^{(2)}_{3}+b^{(2)}_{1}\right]$$
$$h_{W,b}(x)=a^{(3)}_{1}$$

我们用$z^{l}_{i}$表示第l层第i单元的输入加权和,例如$z^{(2)}_{i}=\sum_{j=1}^{n}W^{(1)}_{ij}x_{j}+b^{(1)}_{i}$,则$a^{(l)}_{i}=f[z^{(l)}_{i}]$,则上述传播过程可以简化为
$$z^{(2)}=W^{(1)}x+b^{(1)}$$
$$a^{(2)}=f[z^{(2)}]$$
$$z^{(3)}=W^{(2)}a^{(2)}+b^{(2)}$$
$$h_{W,b}(x)=a^{(3)}=f[z^{(3)}]$$

上面的计算过程即为神经网络的正向传播过程。

反向传播

  正向传播计算出输出值$h_{W,b}(x)$后,根据与目标值$t$的误差,进行反向传播调整权重$W$和偏置$b$的值。根据平方和误差函数,针对第$p$个输入样本有$E=\frac{1}{2}\left|\left| h_{W,b} - t\right|\right|^2$,则总误差为$E_{N} = \frac{1}{N}\sum^{N}_{p=1}E_{p}$。误差从输出层反向传播,各层权重通过梯度下降算法进行更新,即$$w=w-\eta*\Delta E_{p}(w)$$其中,$\eta$是每次更新时的步长,$\Delta E_{p}(w)$是第$p$个样本的误差对某一层权重的偏导数。在这里以单输出两层神经网络为例,给出反向传播算法推导过程,为了简化公式,只考虑一个样本的情况。

  对$w^{(1)}_{11},w^{(2)}_{11}$的推导。首先看第三层,输入是$z^{(3)}$,输出为$a^{(3)}$,误差为$E=\frac{1}{2}\left|\left|t - a^{(3)}\right|\right|^2$,有

$$\frac{\partial{E}}{\partial{w^{2}_{11}}}={\frac{\partial{E}}{\partial{z^{(3)}}}}{\frac{\partial{z^{(3)}}}{\partial{w^{(2)}_{11}}}}$$

对于${\frac{\partial{z^{(3)}}}{\partial{w^{(2)}_{11}}}}$,有

$${\frac{\partial{z^{(3)}}}{\partial{w^{(2)}_{11}}}}=a^{(2)}_{1}$$

对于${\frac{\partial{E}}{\partial{z^{(3)}}}}$,有

$${\frac{\partial{E}}{\partial{z^{(3)}}}} = {\frac{\partial{E}}{\partial{a^{(3)}}}}{\frac{\partial{a^{(3)}}}{\partial{z^{(3)}}}}=-(t-a^{(3)})*f’(z^{(3)})$$

若激活函数为simgoid函数,有
$${\frac{\partial{E}}{\partial{z^{(3)}}}} = -(t-a^{(3)})z^{(3)}(1-z^{(3)})$$
该结果在推导前一层权重$w$时会用到,令$\delta_{3}={\frac{\partial{E}}{\partial{z^{(3)}}}}$,代入$\frac{\partial{E}}{\partial{w^{2}_{11}}}$,有
$$\frac{\partial{E}}{\partial{w^{2}_{11}}} = \delta_{3}*a^{(2)}_{1}$$

  对于第二层,需要求$\frac{\partial{E}}{\partial{w^{(1)}_{11}}}$,有
$$\frac{\partial{E}}{\partial{w^{(1)}_{11}}} = \frac{\partial{E}}{\partial{z^{(2)}_{1}}}\frac{\partial{z^{(2)}_{1}}}{\partial{w^{(1)}_{11}}}$$
其中
$$\frac{\partial{z^{(2)}_{1}}}{\partial{w^{(1)}_{11}}} = x_{1}$$
由于第三层只有一个节点,所以有
$$\frac{\partial{E}}{\partial{z^{(2)}_{1}}}= \frac{\partial{E}}{\partial{z^{(3)}_{1}}}\frac{\partial{z^{(3)}_{1}}}{\partial{z^{(2)}_{1}}}=\delta_{3}\frac{\partial{z^{(3)}_{1}}}{\partial{a^{(2)}_{1}}}\frac{\partial{a^{(2)}_{1}}}{\partial{z^{(2)}_{1}}}=\delta_{3}w^{(2)}_{11}z^{(2)}_{1}(1-z^{(2)}_{1})$$
令$\delta_{2}=\frac{\partial{E}}{\partial{z^{(2)}_{1}}}$,则有
$$\frac{\partial{E}}{\partial{w^{(1)}_{11}}}=\delta_{2}*x_{1}$$
其他的权重值与上述推导一致。

激活函数

  激活函数的目的在于引入非线性,将输出映射到不同的空间。如果激活函数是线性的,那么多层次的神经网络和单层神经网络是等价的。比较常见的激活函数有simgoid函数、tanh函数、ReLU函数等。

sigmoid函数

  sigmoid函数在logistic回归推导中给出了其函数图像及表达式,这里不再赘述。其优点在于能将输出映射在[0,1]区间,单调并连续。其缺点在于输入超过一定区间,函数的导数趋近于0,容易出现问题,并且其输出均值不为0.

tanh函数

  tanh函数被定义为$tanh(x)=\frac{1-e^{-2x}}{1+e^{-2x}}$,其函数图像如下所示



tanh函数相对于sigmoid函数来说,收敛速度更快,并且输出以0为中心,但是其依然没有解决梯度消失的问题。

ReLU函数

  RuLU的定义为$y=max(0,x)$,对应的函数图像为



relu函数可以取得较好的结果,在实际应用中也有很多类似的变种函数。relu函数有效缓解了梯度消失的问题,计算方面也比较容易。后面遇到例子时,再详细说明各种激活函数的效果。

小结

  第三周的课程比较基础,通过两层神经网络结构进行了正向、反向传播推导,简单阐述了神经网络的工作原理,然而两层网络只是比较特殊的情况,推导不具有代表性,下一章深层网络将进行普适性的推导。

spark读取jar中文件的路径问题

发表于 2017-10-28 | 分类于 spark |
字数统计: | 阅读时长 ≈

  上周和这周的前两天一直在解决工作中遇到的一个问题,问题的背景是这样的:我们搭建的平台中使用了lucene的IKAnalyzer分词工具,该工具在进行分词时会先读取resource文件夹下的config文件,每次在本地跑ut测试的时候都能顺利通过,但是放到集群上执行的时候会报找不到config文件路径的错误。问题很清楚,但是就是不知道为什么无法找到文件,最后把路径信息打出来发现其中有个“!”,才知道问题的根结在于读取文件的方式。

  先说说java中获取文件路径的几种方式,工程的目录结构如下



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

public void testPath() {
// 第一种:获取类加载的根路径
URL url1 = this.getClass().getResource("/");
System.out.println(url1.getPath());
//输出:/Users/name/test/target/classes/

// 第二种:获取当前类的所在工程路径(不加“/”)
URL url2 = this.getClass().getResource("");
System.out.println(url2.getPath());
//输出:/Users/name/test/target/classes/test/

// 第三种:在资源加载的过程中,使用的逐级向上委托的形式加载,不能以“/”开头
URL url3 =this.getClass().getClassLoader().getResource("");
System.out.println(url3.getPath());
//输出:file:/Users/name/test/target/classes/
URL url4 =this.getClass().getClassLoader().getResource("/");
System.out.println(url3.getPath());
//输出:null

}

  在正常情况下我们可以按照以上方法进行文件的读取,但是当我们把项目打成jar包,使用java -jar运行时,上述方法会在第一个打印方法出报空指针,这说明单独运行jar的时候上述读取文件的方法已经失效,但是我们采用下面的方法仍然可以进行文件的读取。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void testStream() throws IOException {
InputStream in = this.getClass().getResourceAsStream("/folder/3.txt");
int temp = 0;
byte[] bytes = new byte[1024];
int len = 0;
while ((temp = in.read()) != -1) {
bytes[len] = (byte) temp;
len++;
}
in.close();
String content = new String(bytes,0,len);
System.out.println(content);
}

采用getResourceAsStream再运行打出来的jar包就没有问题了。

  起初一直以为是spark运行jar的问题,后来才发现是java读取资源方式有错误。其实这个问题之前在进行web开发时遇到过,当时使用weblogic服务器,总是读取不到resource文件夹下的配置文件,后来也是采用getResourceAsStream的方法才读出来。然而在tomcat服务器下采用getResource的方式可以正常读取,因为tomcat服务器会解析放入的jar包,而weblogic却不会,运行机制不一样导致读取文件的方式也不一样。

  解决完问题后想想这次的bug和之前遇到的本质是一个问题,之前遇到问题解决后也没有仔细思考,还以为是weblogic的特例,也就没有把经验积累下来。虽然这次后知后觉耽误了不少时间,但是印象更加深刻了,工作中遇到的坑还是应该记下来多看看,多总结。

1…56
小建儿

小建儿

码农小白成长记

54 日志
8 分类
18 标签
RSS
© 2019 小建儿
本站访客数: |
| 博客全站共字