利用零碎的时间,读一读深度学习领域的经典论文,本项目所有论文摘选自:
Github 开源项目 awesome-deep-learning-papers。
背景
本篇论文题目为《Training Very Deep Networks》,作者提出了一种全新的网络-高速网络结构 (Highway Networks),用于优化深度神经网络
由于梯度爆炸和梯度消失而导致的训练困难的问题。而且,ResNet的思路和这篇文章所提出的想法个人感觉如出一辙,另外这篇论文发表于2015年05月份,ResNet发表于2015年12月份...
1. 论文目的
这篇论文的目的比较明确,就是为了解决深度神经网络
难以训练的问题。
2. 论文思路
作者的解决办法有点类似LSTM的门控结构,通过设定两个门:transform gate 和carry gate 来控制当前层的输出形式,将其拆分成两部分:当前层的直接输入和经过非线性映射后的部分,简单画了一个图示方便理解:
其中:
门 C 控制了有多少由上一层传入的信息不经转换直接传到下一层;
门 T 控制了有多少信息经过转换后传到下一层。
我们知道,简单的前馈神经网络通常由L层组成,在每一层都是一个非线性变换函数H()作用于它的输入x,并转换成输出y(此处忽略下标),因此每一层可以表示为:
$$ y = H(x,W_{H}) \tag{1}$$
作者提出的 Highway Network 在每一层增加了两个非线性变换函数$T(x,W_{T})$和$C(x,W_{C})$,因此在 highway network 中,每一层的形式如下
$$y = H(x,W_{H})\cdot T(x,W_{T}) + x \cdot C(x,W_{C}) \tag{2}$$
为了简便,论文作者设定C=1-T,这样上式便可以简写为:
$$y = H(x,W_{H})\cdot T(x,W_{T}) + x \cdot (1 - T(x,W_{T}) )) \tag{3}$$
此处还有一个小技巧,因为在公式(3)中H和T的维度需要保持一致,作者给出了两种方式,一种是替换x为经过下采样(sub-sampling)或者零填充(zero-padding)的$\hat{x}$;另一种为使用未包含高速网络层(highway layer)来改变维度。
公式(3)还可以进一步简化
$$ y = \begin{cases}x,&if T(x,W_{T}) = 0,\\H(x,W_{H}),&if T(x,W_{T} = 1).\end{cases} \tag{4}$$
最重要的部分,反向传播的梯度
$$frac{dy}{dx}=begin{cases}
I,&if T(x,W_{T}) = 0,\Hprime (x,W_{H}), &if T(x,W_{T}) = 1.
end{cases} tag{5}$$
3. 高速网络结构实际效果
为了证明这种网络结构的实际效果,作者对比了有无高速网络结构(highway network)在不同深度下的训练集的交叉熵损失,如下图所示(从两边往中间看):
从上图中可以明显看出,对于普通神经网络(plain network):层数越深,及时训练轮数增加,也很难收敛,交叉熵居高不下;对于高速神经网络(highway network):相比普通神经网络,当层数超过10层时,均有明显的改进效果,其中50层时效果最好。
注:本文的重点是优化深度神经网络的训练困难问题,在作者采用的数据集上可能精度没有普通的高,但是随着层数的增加,带有高速网络结构的明显比普通的要高很多。
作者也给出了更直观的图示
4. 训练高速神经网络
对于转变函数T,作者采用的形式为$T(x) = \sigma(W_{T}^T x + b_{T})$,其中偏重$b_{T}$初始化为类似-1,-3这样的负值,这样初始化的目的是让网络更倾向于携带行为(即在最开始训练的时候保证信息流通的完整性)。优化器采用了带动量(momentum)的SGD。
5. pytorch 实现高速网络结构
import torch
import torch.nn as nn
class HighwayMLP(nn.Module):
def __init__(self,
input_size,
gate_bias=-2,
activation_function=nn.functional.relu,
gate_activation=nn.functional.softmax):
super(HighwayMLP, self).__init__()
self.activation_function = activation_function
self.gate_activation = gate_activation
self.normal_layer = nn.Linear(input_size, input_size)
self.gate_layer = nn.Linear(input_size, input_size)
self.gate_layer.bias.data.fill_(gate_bias)
def forward(self, x):
normal_layer_result = self.activation_function(self.normal_layer(x))
gate_layer_result = self.gate_activation(self.gate_layer(x))
multiplyed_gate_and_normal = torch.mul(normal_layer_result, gate_layer_result)
multiplyed_gate_and_input = torch.mul((1 - gate_layer_result), x)
return torch.add(multiplyed_gate_and_normal,
multiplyed_gate_and_input)
class HighwayCNN(nn.Module):
def __init__(self,
input_size,
gate_bias=-1,
activation_function=nn.functional.relu,
gate_activation=nn.functional.softmax):
super(HighwayCNN, self).__init__()
self.activation_function = activation_function
self.gate_activation = gate_activation
self.normal_layer = nn.Linear(input_size, input_size)
self.gate_layer = nn.Linear(input_size, input_size)
self.gate_layer.bias.data.fill_(gate_bias)
def forward(self, x):
normal_layer_result = self.activation_function(self.normal_layer(x))
gate_layer_result = self.gate_activation(self.gate_layer(x))
multiplyed_gate_and_normal = torch.mul(normal_layer_result, gate_layer_result)
multiplyed_gate_and_input = torch.mul((1 - gate_layer_result), x)
return torch.add(multiplyed_gate_and_normal,
multiplyed_gate_and_input)
6. keras 实现高速网络结构
import keras.backend as K
from keras.layers import Dense, Activation, Multiply, Add, Lambda
import keras.initializers
def highway_layers(value, n_layers, activation="tanh", gate_bias=-3):
dim = K.int_shape(value)[-1]
gate_bias_initializer = keras.initializers.Constant(gate_bias)
for i in range(n_layers):
gate = Dense(units=dim, bias_initializer=gate_bias_initializer)(value)
gate = Activation("sigmoid")(gate)
negated_gate = Lambda(
lambda x: 1.0 - x,
output_shape=(dim,))(gate)
transformed = Dense(units=dim)(value)
transformed = Activation(activation)(value)
transformed_gated = Multiply()([gate, transformed])
identity_gated = Multiply()([negated_gate, value])
value = Add()([transformed_gated, identity_gated])
return value
很棒继续加油