Lecture 6主要介绍了深度学习应用的一些小技巧,例如多任务训练、梯度检测、正则化、多种激活函数、参数初始化、学习速率等。
对比上节课我们学到的神经网络,多任务学习就是在输出层用softmax分类器取代标量得分。训练方法依然采用后向传播。
神经网络和传统机器学习方法的不同在于,深度学习需要同时学习词向量和权重。
主要思想:我们在训练多种不同NLP任务(例如NER和POS),可以共享两个任务的词向量和隐藏层的权重,只有输出层的softmax权重不同。损失函数是不同任务损失函数相加,例如:
δ t o t a l = δ P O S + δ N E R \delta^{total} = \delta^{POS} + \delta^{NER} δtotal=δPOS+δNER
参考论文《NLP(almost from scratch, Collobert et al.2011)》
成功的神经网络的通用步骤是这样的:
下面我们根据这些步骤分别介绍使用技巧。
梯度检测我们在第一次作业中用过,从导数的本质上求得参数的梯度,和我们用后向传播计算得到的梯度对比。公式如下:
f ′ ( θ ) ≈ J ( θ ( i + ) ) − J ( θ ( i − ) ) 2 ϵ f\prime (\theta) \approx \frac{J(\theta^{(i+)}) - J(\theta^{(i-)})}{2\epsilon} f′(θ)≈2ϵJ(θ(i+))−J(θ(i−))
其中, θ ( i + ) = θ + ϵ × e i \theta^{(i+)} = \theta + \epsilon \times e_i θ(i+)=θ+ϵ×ei
简单的代码实现为:
old_value = x[ix]
x[ix] = old_value + h
fxh = f(x)
x[ix] = old_value
grad[ix] = (fxh - fx) / h
如果梯度检测失败了应该怎么做?修改代码确定没有bug!
正则化前面课程提到的次数也很多,和大多数分类器一样,神经网络也需要避免过拟合,使得验证集和测试集能够获得良好的表现。正则化后的损失函数为:
J R = J + λ ∑ i = 1 L ∣ ∣ W ( i ) ∣ ∣ F J_R = J + \lambda\sum_{i=1}^L||W^{(i)}||_F JR=J+λi=1∑L∣∣W(i)∣∣F
上式中, ∣ ∣ W ( i ) ∣ ∣ F ||W^{(i)}||_F ∣∣W(i)∣∣F是矩阵 W ( i ) W^{(i)} W(i)的 F F F范数, λ \lambda λ是正则化选项的相对权重。
目前为止我们讨论的非线性神经元有sigmoid,然而在很多应用中有更好的激活函数。常用的有:
公式:
σ ( z ) = 1 1 + e x p ( − z ) ∈ ( 0 , 1 ) \sigma(z) = \frac{1}{1+exp(-z)} \in (0,1) σ(z)=1+exp(−z)1∈(0,1)
梯度:
σ ′ ( z ) = σ ( z ) ( 1 − σ ( z ) ) \sigma\prime(z) = \sigma(z)(1-\sigma(z)) σ′(z)=σ(z)(1−σ(z))
与sigmoid相比,tanh函数收敛更快,输出范围为-1到1。
公式:
t a n h ( z ) = e x p ( z ) − e x p ( − z ) e x p ( z ) + e x p ( − z ) = 2 σ ( 2 z ) − 1 ∈ ( − 1 , 1 ) tanh(z) = \frac{exp(z)-exp(-z)}{exp(z) + exp(-z)} = 2\sigma(2z) -1 \in (-1,1) tanh(z)=exp(z)+exp(−z)exp(z)−exp(−z)=2σ(2z)−1∈(−1,1)
梯度:
t a n h ′ ( z ) = 1 − t a n h 2 ( z ) tanh\prime(z) = 1 - tanh^2(z) tanh′(z)=1−tanh2(z)
hard tanh比tanh更容易计算。
公式:
h a r d t a n h ( z ) = { − 1 : z < − 1 z : − 1 ≤ z ≤ 1 1 : z > 1 hardtanh(z) = \begin{cases} -1 &:&z<-1\\z&:&-1\leq z\leq 1\\1&:&z>1\\\end{cases} hardtanh(z)=⎩⎪⎨⎪⎧−1z1:::z<−1−1≤z≤1z>1
梯度:
h a r d t a n h ′ ( z ) = { 1 : − 1 ≤ z ≤ 1 0 : o t h e r w i s e hardtanh\prime(z) = \begin{cases}1 &:&-1\leq z \leq 1\\0&:&otherwise\end{cases} hardtanh′(z)={10::−1≤z≤1otherwise
公式:
s o f t s i g n ( z ) = z 1 + ∣ z ∣ softsign(z) = \frac{z}{1+|z|} softsign(z)=1+∣z∣z
梯度:
s o f t s i g n ′ ( z ) = s g n ( z ) ( 1 + z ) 2 softsign\prime(z) = \frac{sgn(z)}{(1+z)^2} softsign′(z)=(1+z)2sgn(z)
其中 s g n ( z ) sgn(z) sgn(z)是符号函数,根据z的不同返回不同, s g n ( z ) = { 1 z > 0 0 z = 0 − 1 z < 0 sgn(z) = \begin{cases}1 &z>0&\\0 &z=0&\\-1 &z<0&\end{cases} sgn(z)=⎩⎪⎨⎪⎧10−1z>0z=0z<0
ReLu,Rectify Linear Unit,是一种比较流行的激活函数,因为它的上限不饱和,在计算视觉应用方面获得成功。
公式:
r e c t ( z ) = m a x ( z , 0 ) rect(z) = max(z, 0) rect(z)=max(z,0)
梯度:
r e c t ′ ( z ) = { 1 : z > 0 0 : o t h e r w i s e rect\prime(z) = \begin{cases}1 &:&z>0\\0 &:&otherwise\end{cases} rect′(z)={10::z>0otherwise
传统ReLu激活单元对非正的z不具备传播误差的功能,leaky ReLu改进了这一缺陷,使得晓得误差能够被后向传播。
公式:
l e a k y ( z ) = m a x ( z , k ⋅ z ) leaky(z) = max(z, k\cdot z) leaky(z)=max(z,k⋅z) 其中 0 < k < 1 0<k<1 0<k<1
梯度:
l e a k y ′ ( z ) = { 1 : z > 0 k : o t h e r w i s e leaky\prime (z) = \begin{cases}1 &:&z>0\\k &:&otherwise\\\end{cases} leaky′(z)={1k::z>0otherwise
最近出现的一个非线性网络,公式:
f i ( z ) = m a x j ∈ [ 1 , k ] z i j f_i(z) = max_{j\in[1,k]}z_{ij} fi(z)=maxj∈[1,k]zij
z i j = x T W ⋅ ⋅ i j + b i j z_{ij} = x^TW_{\cdot \cdot ij} + b_{ij} zij=xTW⋅⋅ij+bij
这种方法在一些图片数据集上取得了不错的效果。
论文《Understanding the difficulty of training deep feedfor-ward neural networks (2010), Xavier et al》中研究了权重和偏置的初始值不同对训练的影响,结果表明,当权重矩阵 W ∈ R n ( l + 1 ) × n ( l ) W\in R^{n^{(l+1)}\times n^{(l)}} W∈Rn(l+1)×n(l)采用以下范围的均匀分布来随机初始化时,对sigmoid和tanh激活函数会得到更低的误差率和更快的收敛速度:
U [ − 6 n ( l ) + n ( l + 1 ) , 6 n ( l ) + n ( l + 1 ) ] U[-\sqrt{\frac{6}{n^{(l)} + n^{(l+1)}}}, \sqrt{\frac{6}{n^{(l)} + n^{(l+1)}}}] U[−n(l)+n(l+1)6,n(l)+n(l+1)6]
其中, n ( l ) n^{(l)} n(l)表示输入单元的个数, n l + 1 n^{l+1} nl+1表示输出单元的个数。
目的:维护层层之间激活方差和后向传播的梯度方差。
模型中梯度更新的速度使用学习速率这个变量来控制,在下面公式中, α \alpha α表示学习速率:
θ n e w = θ o l d − α Δ θ J t ( θ ) \theta^{new} = \theta^{old} - \alpha \Delta_{\theta}J_t(\theta) θnew=θold−αΔθJt(θ)
梯度更新的速度并不是越快越好, a l p h a alpha alpha太大,可能会导致无法收敛到最优解。在非凸模型中(我们遇到的大部分模型都是非凸的),很大的学习速率导致损失函数的发散几率更高。
关于学习速率的设置有很多变种,详细信息可以看讲义。
AdaGrad可以说是标准的SGD,但是只有一点不同:它的每个参数的学习速率是不同的。每个参数的学习速率依赖于历史更新信息,换句话说,没有更新过的参数的学习速率可能更高,用公式表示:
θ t , i = θ t − 1 , i − α ∑ τ = 1 t g τ , i 2 g t , i \theta_{t,i} = \theta_{t-1,i} - \frac{\alpha}{\sqrt{\sum_{\tau = 1}^tg_{\tau,i}^2}}g_{t,i} θt,i=θt−1,i−∑τ=1tgτ,i2αgt,i
其中, g t , i = ∂ ∂ θ i t J t ( θ ) g_{t,i} = \frac{\partial}{\partial \theta_i^t}J_t(\theta) gt,i=∂θit∂Jt(θ)
简单的代码实现:
cache += dx**2
x += - learning_rate * dx / np.sqrt(cache + 1e-8)