一切都是为了构建更深的网络

一切都是为了构建更深的网络

很多实验表明:深度网络可以比浅层网络更有效地表示某些函数。但是,随着深度的增加,优化的问题也就接踵而来。那么,为了解决难以优化的问题,提出了相当多的策略:开发更好的优化器(Optimizer),(Well-designed)精心设计参数的初始化、对特定的激活函数维持方差标准化。但是,以上这些策略过于定制化,不同的数据可能需要不同策略。

Highway Networks由此诞生,它受LSTM网络启发(不懂LSTM网络也没有关系,懂的话就更好)。采用控制门(gate)的思想,控制信息的流动。在前馈网络,我们的输入x,经过一层映射 W H W_H WH和一层非线性转换H,从而生成y,以下是数学表达:
y = H ( x , W H ) y=H(x, W_H) y=H(x,WH)
​ 例如,在全连接网络中, y = sigmoid ( x A + b ) y=\text{sigmoid}(xA + b) y=sigmoid(xA+b),以下是简单的流程图。

x
layer
y

​ highway的中采用了gate的思想,将最后的输出看成两份,一份是经过转变后的(transform gate),一份是原本的信息(carry gate)。数学模型为:
y = α y ′ + β x y = \alpha y' + \beta x y=αy+βx

x
layer
y'
αy'
+
y
βx

这里的α和β的计算为:
α = T ( x , W T ) β = T ( x , W C ) \alpha = T(x,W_T) \\ \beta=T(x,W_C) α=T(x,WT)β=T(x,WC)
为了计算简单,将 β = 1 − α \beta=1-\alpha β=1α。最终数学模型为:
y = H ( x , W H ) ∗ T ( x , W T ) + x ∗ ( 1 − T ( x , W T ) ) y = H(x, W_H) * T(x,W_T) + x * (1-T(x,W_T)) y=H(x,WH)T(x,WT)+x(1T(x,WT))
这里,要注意,x的维度或许与转变之后的维度有所不同,所以在实际中,有两种做法

  1. 在第一层中不使用highway,将此时的y作为输入,highway networks内部使用统一维度进行计算。

  2. 首先将x转变成符合条件的x’,然后再进行计算。本文介绍的文章使用的就是这种技巧。

这里的 H ( x , W H ) H(x,W_H) H(x,WH)有可能是卷积或者递归网络的形式。

  1. 卷积形式,就是每次在感受野计算完成之后,进行highway networks,但是要注意计算后输出矩阵与计算范围矩阵的长宽。
  2. 递归形式,在计算完每个单元后,进行highway networks,同样的,需要注意输入和输入的维度。

-----------------------------------------------分割线,以下讲解LSTM使用highway network的例子-------------------------------------------

接下来,我们主要来探讨文章《Deep Semantic Role Labeling: What Works and What’s Next》的网络结构。

在这里插入图片描述

左边的方框大家应该非常熟悉,就是一个LSTM Cell的输出。

h<sub>t-1</sub>
LSTM Cell
x
h<sub>t</sub>

那么highway的输出就会被改变,根据(12)和(14),(13)式与(8)式输出的式同一个东西,只是 y ′ y' y表明不是最终输出。那么,现在让我们重新构建一个新的LSTM Cell,数学表达式已经在(12)和(14)式,先看看流程如何展示。

h<sub>t-1</sub>
LSTM Cell
x
h'<sub>t</sub>
αh'
+
(1 - α)x

接下来用pytorch来展示,刚好pytroch有LSTM Cell这个组件,可以很简单的修改就能成为一个新的LSTM Cell。

import torch.nn as nn
import torch
import torch.nn.functional as F
class HwLSTMCell(nn.Module):
    def __init__(self, input_size, hidden_size, dropout_ratio):
        """
        	args:
        		input_size:    输入特征的大小
        		hidden_size:   输出特征的大小
        		dropout_ratio: dropout的概率,(0,1)表示需要dropout, (-∞, 0]∪[, +∞)表示不需要
        """
        super().__init__()
        self.input_size = input_size
        self.hidden_size= hidden_size
        self.dropout_ratio = dropout_ratio
      
        self.lstm_cell = nn.LSTMCell(input_size = self.input_size, hidden_size = self.hidden_size)
        self.W_R = nn.Linear(self.input_size + self.hidden_size, self.hidden_size) # (12)式的W_r
        # 个人认为,如果当input_size == hidden_size时,就不需要了,这个方法是为了最后一步相加时维度保持一致
        self.W_h = nn.Linear(self.input_size, self.hidden_size) 
        
        
    def forward(self, input, hidden = None):
        """
        	args:
        		input: 输入, shape = [batch, embed_dim]
        		hidden: 上一个hidden的输出, 是一个元组,(hidden, cell)
        				hidden.shape = [batch, hidden_size]
        				cell.shape   = [batch, hidden_size]
        """
        if hidden is None:
            hidden = input.new_zeros(input.shape[0], self.hidden_size)
            hidden =  (hidden, hidden)
        hy, cy = self.lstm_cell(input, hidden)  # 此时的是hy'
        # 现在的hy还不是最终输出,hy = α * hy' + (1 - α) * x
        # alpha = σ(W * [h_{t-1}, x_t])
        alpha = F.sigmoid(self.W_R(torch.cat([hidden[0], x], dim = 1)))
        hy = alpha * hy + (1 - alpha) * self.W_C(input) # 如果本来的input_size == hidden_size, 就不用这个转换了。
     	if self.training and 0 < self.dropout_ratio and self.dropout_ratio < 1:  # 如果需要dropout的话,就可以这样使用
            hy = F.dropout(hy, p=0.5)
        
        return hy, cy

那么最终的highway版本的LSTM网络可以变为:

class HwLSTM(nn.Module): 
    def __init__(self, input_size, hidden_size, dropout_ratio, batch_first):
        self.input_size = input_size
        self.hidden_size= hidden_size
        self.dropout_ratio = dropout_ratio
        self.batch_first = batch_first
        self.lstm_cell = HwLSTMCell(self.input_size, self.hidden_size, self.dropout_ratio)
        
        def forward(self, input, reverse = True):
            """
            	args: 
            		input: 这里假设是词嵌入层的输出,如果batch_first = True,就需要更改形状为[seq_len, batch_size, embed_dim]
            		reverse:看结果是否需要反向输出
            """
            output, hidden = [], None
            if batch_first:
                input = input.permute(1, 0, 2)
            for i in range(input.shape[0]): # 需要迭代句子的长度
                hidden = self.lstm_cell(input[i], hidden)
                output.append(hidden[0])
            
            if reverse:
                output.reverse()
            return torch.stack(output)

那么,截至到目前为止,highway在lstm上的应用就到这里了。引用的文献和博客也在次文章上以链接的形式体现。流程图我是用mermaid画的,在typroa里,< sub>< /sub >的下标显示是正常的,在CSDN的markdown里编辑就显示不了了,em…。没办法,麻烦大家把里面内容看成是下标吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值