MENU

论文笔记-1:《Train very deep networks》

August 21, 2018 • Read: 2069 • 论文学习阅读设置

利用零碎的时间,读一读深度学习领域的经典论文,本项目所有论文摘选自:
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

7. 参考文献

Last Modified: January 7, 2019
Archives Tip
QR Code for this page
Tipping QR Code
Leave a Comment

已有 1 条评论
  1. 深度菜鸟 深度菜鸟

    很棒继续加油