CNN-LSTM 的批量归一化层

Batch normalization layer for CNN-LSTM

假设我有这样一个模型(这是一个时间序列预测的模型):

ipt   = Input((data.shape[1] ,data.shape[2])) # 1
x     = Conv1D(filters = 10, kernel_size = 3, padding = 'causal', activation = 'relu')(ipt) # 2
x     = LSTM(15, return_sequences = False)(x) # 3
x = BatchNormalization()(x) # 4
out   = Dense(1, activation = 'relu')(x) # 5

现在我想给这个网络添加批量归一化层。考虑到batch normalization doesn't work with LSTM,我可以在Conv1D层之前添加它吗?我认为在 LSTM.

之后有一个批量归一化层是合理的

还有,我可以在这个网络的什么地方添加Dropout?一样的地方? (在批量归一化之后还是之前?)

Update:我使用的 LayerNormalization 实现是 inter-layer,而不是 recurrent与原始论文一样;后者的结果可能会更好。


BatchNormalization 可以 使用 LSTM - 链接的 SO 给出了错误的建议;事实上,在我的脑电图分类应用中,它占主导地位LayerNormalization。现在针对您的情况:

  • "Can I add it before Conv1D"?不要 - 相反,事先标准化您的数据,否则您将使用劣质变体来做同样的事情
  • 同时尝试:BatchNormalization 激活前和激活后 - 适用于 Conv1DLSTM
  • 如果您的模型与您展示的完全一样,LSTM 之后的 BN 引入噪声的能力可能会适得其反,这可能会混淆分类器层 - 但这是关于之前的一层输出,不是 LSTM
  • 如果您不在 return_sequences=False 之前使用带有 return_sequences=True 的堆叠 LSTM,您可以将 Dropout 放置在任何地方 - 在 LSTM 之前、之后或两者
  • Spatial Dropout:丢弃 units / channels 而不是随机激活(见底部) ; LeCun, et al 在论文中证明了在减少 CNN 中的协同适应方面更有效,其中包含适用于 RNN 的想法。可以大大增加收敛时间,也可以提高性能
  • 对于 LSTM
  • recurrent_dropout 仍然优于 Dropout - 但是,你可以两者都做;只是不要与 activation='relu' 一起使用,因为 LSTM 根据错误
  • 不稳定
  • 对于您维度的数据,任何类型的 Pooling 都是多余的并且可能会损害性能;稀缺数据通过非线性比简单的平均操作更好地转换
  • 我强烈建议在您的转化后使用 SqueezeExcite 块;这是一种自我关注的形式 - 请参阅 paper;我对以下 1D 的实现
  • 我还建议尝试 activation='selu' AlphaDropout'lecun_normal' 初始化,每篇论文 Self Normalizing Neural Networks
  • 免责声明:以上建议可能不适用于 NLP 和类似嵌入的任务

下面是一个示例模板,您可以将其用作起点;我还推荐以下 SO 以供进一步阅读:Regularizing RNNs, and

from keras.layers import Input, Dense, LSTM, Conv1D, Activation
from keras.layers import AlphaDropout, BatchNormalization
from keras.layers import GlobalAveragePooling1D, Reshape, multiply
from keras.models import Model
import keras.backend as K
import numpy as np


def make_model(batch_shape):
    ipt = Input(batch_shape=batch_shape)
    x   = ConvBlock(ipt)
    x   = LSTM(16, return_sequences=False, recurrent_dropout=0.2)(x)
    # x   = BatchNormalization()(x)  # may or may not work well
    out = Dense(1, activation='relu')

    model = Model(ipt, out)
    model.compile('nadam', 'mse')
    return model

def make_data(batch_shape):  # toy data
    return (np.random.randn(*batch_shape),
            np.random.uniform(0, 2, (batch_shape[0], 1)))

batch_shape = (32, 21, 20)
model = make_model(batch_shape)
x, y  = make_data(batch_shape)

model.train_on_batch(x, y)

使用的函数:

def ConvBlock(_input):  # cleaner code
    x   = Conv1D(filters=10, kernel_size=3, padding='causal', use_bias=False,
                 kernel_initializer='lecun_normal')(_input)
    x   = BatchNormalization(scale=False)(x)
    x   = Activation('selu')(x)
    x   = AlphaDropout(0.1)(x)
    out = SqueezeExcite(x)    
    return out

def SqueezeExcite(_input, r=4):  # r == "reduction factor"; see paper
    filters = K.int_shape(_input)[-1]

    se = GlobalAveragePooling1D()(_input)
    se = Reshape((1, filters))(se)
    se = Dense(filters//r, activation='relu',    use_bias=False,
               kernel_initializer='he_normal')(se)
    se = Dense(filters,    activation='sigmoid', use_bias=False, 
               kernel_initializer='he_normal')(se)
    return multiply([_input, se])

Spatial Dropout:将noise_shape = (batch_size, 1, channels)传递给Dropout——效果如下;代码见 Git gist