大家好,又见面了,我是你们的朋友全栈君。
一.概述
DCNN(A Convolutional Neural Network for Modelling Sentences)by NalKalchbrenner等,又是文本分类论文的一力作。”准确表达句子的语义是语言理解的核心”,通过学习机器学习和TextCNN,我们可以知道n-gram特征是NLP文本任务和句子表达的一种重要方法。TextCNN通过不同步长的卷积核(例如2,3,4,5,7)构建n-gram特征,以及最大池化(max-pooling)选择特征,再加上神经网络全局优化的思想,在文本分类任务中取得了不错的效果。
那么,除了TextCNN外,还有没有更加先进的CNN提取n-gram信息的神经网络呢?
当然有啦,那就是DCNN!
DCNN名为动态卷积神经网络,注意,’D’代表的是动态(Dynamic )的意思,而不是有可能望文生义的’深度(Deep)’的意思。
DCNN,动态卷积神经网络的思想是什么,对比TextCNN有什么进步呢? 一句话,DCNN能够捕捉长距离词语或者是字的语义信息。具体说来,就是TextCNN中,每一个卷积核选择的Max-Pooling池化手段,只能选择一个n-gram信息。举个例子来说,比如说卷积核的步长是3,对于句子’花落惊飞雨’,如果文本最大长度是5,那么可以提取到字符级别的[‘花落惊’、’落惊飞’, ‘惊飞雨’]三个n-gran信息,max-pooling就是取到其中最大的一个。
聪明的你一定可以发现,着不就是连词吗,没错,就是那么回事。可以发现,这些n-gram信息是没有间隔的连续的,其实和连续词袋差不多。它并不能抽取【’花落’、’雨’】这样的特征,不得不说不是一种遗憾。
而我们今天所说的DCNN,特点就是Dynamic k-max pooling,是为了解决以上问题的。动态抽取远距离特征,思想如下:
DCNN不同于TextCNN,它有着’宽卷积’、’动态池化’和Folding层等三个特点。
二.DCNN原理图
2.1 老规矩,还是放DCNN的网络原理图
2.2 DCNN网络层信息
如上图,DCNN主要包含one-dim宽卷积层、Dynamic k-max pooling动态topk池化层、Folding层和全连接层。
1. one-dim宽卷积层,
one-dim就是每一个维度,例如你有300维的word2vec作embedding层,就有300个dim,如上图网络原理图中的红色卷积所示。
图像任务中宽卷积层可以更有效提取图边角信息,在NLP文本分类任务中也一样,可以更有效提取句子的句首和句尾信息,毕竟出现得多了,提取它们也是显而易见的,这不难理解。通常得做法可以通过补零,然后再用普通卷积方式(如same)实现它,形式化如下图论文给出的图形所示:
2. 动态 K-Max pooling层
动态k-max池化层也很好理解,原始的avg-pooling就是所有卷积的求平均,one-max pooling就是选择最大的那个数。那么,顾名思义,k-max pooling就是选择最大的top k个数啦。
dynamic k-max pooling前面的动态,就是根据网络结构和预设的top k,通过公式动态地选择k值。我们预定义一个每层的最小k值(例如k=3,也和n-gram中的3,4,5差不多啦),那么当前层数1的k_curr= Max( k,len_max * (L – L_curr) / L ),其中L表示卷积网络深度,len_max表示文本最大长度,L_curr表示当前所在层。论文计算方式如下图:
3. Folding层
Folding层没什么可说的,论文中实验它也没啥作用,看着高大上吧。原理对于宽卷积层的one-dim,还是假设你有300维的word2vec作embedding层,句子sentence的embedding也有300dim维度。那么Folding就是第一维和第二维相加,第三维和第四维相加。
直观来看,也没有什么意义,毕竟每个字向量或者是词向量的维度上的数字并没有什么特殊的含义,论文中,实验结果也证明了这一点。
4. 调参
论文中给出了两个实例,
一个是有两个宽卷积核的:
句子长度: 7
宽卷积尺寸: 第一层3,第二层2
池化层k值: 第一层5,第二层3;(最小常量为3)
另一个是有三个款卷积核的:
句子长度: 18
宽卷积尺寸: 第一层7,第二层5,第三层3,(这个没有说,论文中选(10,7)、(8,5)、 (7,5))
池化层k值: 第一层12,第二层6,第三层是3;(最小常量可选3,4,5,6等)
三.DCNN代码实现
1. 代码实现有点小麻烦,宽卷积(wide_convolution)第一次用,不太熟;动态k-max卷积实现有点麻烦,没有keras抽象,需要用tf或者keras.backend实现;folding倒是简单些;
github:https://github.com/yongzhuo/Keras-TextClassification/tree/master/keras_textclassification/m06_TextDCNN
2.主体代码
2.1 model
代码语言:javascript复制 def create_model(self, hyper_parameters):
"""
构建神经网络,只有3层静态
:param hyper_parameters:json, hyper parameters of network
:return: tensor, moedl
"""
super().create_model(hyper_parameters)
embedding_output = self.word_embedding.output
pools = []
for i in range(len(self.filters)):
# 第一个,宽卷积,动态k-max池化
conv_1 = wide_convolution(name="wide_convolution_{}".format(i),
filter_num=self.filters_num, filter_size=self.filters[i][0])(embedding_output)
top_k_1 = select_k(self.len_max, len(self.filters[i]), 1) # 求取k
dynamic_k_max_pooled_1 = dynamic_k_max_pooling(top_k=top_k_1)(conv_1)
# 第二个,宽卷积,动态k-max池化
conv_2 = wide_convolution(name="wide_convolution_{}_{}".format(i, i),
filter_num=self.filters_num, filter_size=self.filters[i][1])(dynamic_k_max_pooled_1)
top_k_2 = select_k(self.len_max, len(self.filters[i]), 2)
dynamic_k_max_pooled_2 = dynamic_k_max_pooling(top_k=top_k_2)(conv_2)
# 第三层,宽卷积,Fold层,动态k-max池化
conv_3 = wide_convolution(name="wide_convolution_{}_{}_{}".format(i, i, i), filter_num=self.filters_num,
filter_size=self.filters[i][2])(dynamic_k_max_pooled_2)
fold_conv_3 = prem_fold()(conv_3)
top_k_3 = select_k(self.len_max, len(self.filters[i]), 3) # 求取k
dynamic_k_max_pooled_3 = dynamic_k_max_pooling(top_k=top_k_3)(fold_conv_3)
pools.append(dynamic_k_max_pooled_3)
pools_concat = Concatenate(axis=1)(pools)
pools_concat_dropout = Dropout(self.dropout)(pools_concat)
x = Flatten()(pools_concat_dropout)
output = Dense(units=self.label, activation=self.activate_classify)(x)
# output = Dense(units=self.label, activation='linear')(pools_concat)
self.model = Model(inputs=self.word_embedding.input, outputs=output)
self.model.summary(120)
2.2 wide_convolution
代码语言:javascript复制class wide_convolution(Layer):
"""
paper: http://www.aclweb.org/anthology/P14-1062
paper title: "A Convolutional Neural Network for Modelling Sentences"
宽卷积, 如果s表示句子最大长度, m为卷积核尺寸,
则宽卷积输出为 s m − 1,
普通卷积输出为 s - m 1.
github keras实现可以参考: https://github.com/AlexYangLi/TextClassification/blob/master/models/keras_dcnn_model.py
"""
def __init__(self, filter_num=300, filter_size=3, **kwargs):
self.filter_size = filter_size
self.filter_num = filter_num
super().__init__(**kwargs)
def build(self, input_shape):
super().build(input_shape)
def call(self, inputs):
x_input_pad = ZeroPadding1D((self.filter_size-1, self.filter_size-1))(inputs)
conv_1d = Conv1D(filters=self.filter_num,
kernel_size=self.filter_size,
strides=1,
padding='VALID',
kernel_initializer='normal', # )(x_input_pad)
activation='tanh')(x_input_pad)
return conv_1d
def compute_output_shape(self, input_shape):
return input_shape[0], input_shape[1] self.filter_size - 1, input_shape[-1]
2.3 动态k-max卷积
代码语言:javascript复制class dynamic_k_max_pooling(Layer):
"""
paper: http://www.aclweb.org/anthology/P14-1062
paper title: A Convolutional Neural Network for Modelling Sentences
Reference: https://stackoverflow.com/questions/51299181/how-to-implement-k-max-pooling-in-tensorflow-or-keras
动态K-max pooling
k的选择为 k = max(k, s * (L-1) / L)
其中k为预先选定的设置的最大的K个值,s为文本最大长度,L为第几个卷积层的深度(单个卷积到连接层等)
github tf实现可以参考: https://github.com/lpty/classifier/blob/master/a04_dcnn/model.py
"""
def __init__(self, top_k=3, **kwargs):
self.top_k = top_k
super().__init__(**kwargs)
def build(self, input_shape):
super().build(input_shape)
def call(self, inputs):
inputs_reshape = tf.transpose(inputs, perm=[0, 2, 1])
pool_top_k = tf.nn.top_k(input=inputs_reshape, k=self.top_k, sorted=False).values
pool_top_k_reshape = tf.transpose(pool_top_k, perm=[0, 2, 1])
return pool_top_k_reshape
def compute_output_shape(self, input_shape):
return input_shape[0], self.top_k, input_shape[-1]
2.4 folding
代码语言:javascript复制class prem_fold(Layer):
"""
paper: http://www.aclweb.org/anthology/P14-1062
paper title: A Convolutional Neural Network for Modelling Sentences
detail: 垂直于句子长度的方向,相邻值相加,就是embedding层300那里,(0 1,2 3...298 299)
github tf实现可以参考: https://github.com/lpty/classifier/blob/master/a04_dcnn/model.py
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self, conv_shape):
super().build(conv_shape)
def call(self, convs):
conv1 = convs[:, :, ::2]
conv2 = convs[:, :, 1::2]
conv_fold = Add()([conv1, conv2])
return conv_fold
def compute_output_shape(self, conv_shape):
return conv_shape[0], conv_shape[1], int(conv_shape[2] / 2)
希望对你有所帮助!
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/137821.html原文链接:https://javaforall.cn