CNN for Sentence Classification-textcnn阅读笔记

Textcnn 论文全名是《Convolutional Neural Networks for Sentence Classification》发表于2014年 是一个最经典的模型,Yoon Kim卷积神经网络CNN应用到文本分类任务,利用多个不同size的kernel来提取句子中的关键信息(类似于多窗口大小的ngram,从而能够更好地捕捉局部相关性。

论文地址:https://arxiv.org/abs/1408.5882

1.主要模型

 

textcnn模型

预处理:word embeddings 将每个单词都对应一个embeddings K 预处理的方式有很多种

Input::输入为N个单词,通过查表得到一个N*K的矩阵

卷积层:通过【3,4,5】选取卷积尺寸大小,对于一句话的词向量矩阵,用多个CNN去对矩阵做计算。论文用的是size为3,4,5的三种,每种的卷积核是100层,就类似于做了3gram,4gram,5gram来提取语义特征吧。每一个卷积核对应的特征的不同。

池化层:池化层采用的是最大池化层,对应的一个卷积核最后通过最大池化层能够得到一个数

还有一种方式是K-max pooling,利用CNN解决文本分类问题的文章还是很多的,比如这篇 A Convolutional Neural Network for Modelling Sentences 最有意思的输入是在 pooling 改成 (dynamic) k-max pooling ,pooling阶段保留 k 个最大的信息,保留了全局的序列信息。

连接层(concat):将所有的池化层进行连接

全连接层:当前节点的所有神经元都和下一个节点的所有神经元有连接

Dropout:dropout一般只使用在全连接层上,防止过拟合,在训练的时候使用,但是在测试的时候是不适用的

(1)取平均的作用:先回到标准的模型即没有dropout,我们用相同的训练数据去训练5个不同的神经网络,一般会得到5个不同的结果,此时我们可以采用 “5个结果取均值”或者“多数取胜的投票策略”去决定最终结果。例如3个网络判断结果为数字9,那么很有可能真正的结果就是数字9,其它两个网络给出了错误结果。这种“综合起来取平均”的策略通常可以有效防止过拟合问题。因为不同的网络可能产生不同的过拟合,取平均则有可能让一些“相反的”拟合互相抵消。dropout掉不同的隐藏神经元就类似在训练不同的网络,随机删掉一半隐藏神经元导致网络结构已经不同,整个dropout过程就相当于对很多个不同的神经网络取平均。而不同的网络产生不同的过拟合,一些互为“反向”的拟合相互抵消就可以达到整体上减少过拟合。

(2)减少神经元之间复杂的共适应关系:因为dropout程序导致两个神经元不一定每次都在一个dropout网络中出现。这样权值的更新不再依赖于有固定关系的隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况 。迫使网络去学习更加鲁棒的特征 ,这些特征在其它的神经元的随机子集中也存在。换句话说假如我们的神经网络是在做出某种预测,它不应该对一些特定的线索片段太过敏感,即使丢失特定的线索,它也应该可以从众多其它线索中学习一些共同的特征。从这个角度看dropout就有点像L1,L2正则,减少权重使得网络对丢失特定神经元连接的鲁棒性提高。

2.例子说明

首先,这个例子的输入是“I like this movie very much!”,也就是说输入的单词个数为七个,而嵌入的维度为5.

其次,经过三个不同尺寸的卷积核进行卷积,每个尺寸的卷积核类型有两个。进行卷积后生成了特征向量,特征向量经过最大池化层后和各自的最大池化层进行拼接,形成了倒数第二层。

最后,倒数第二层这里到输出其实是加了一个全连接层,里头用到的是dropout层进行输出。

3.实验结果:

实验对七个数据集进行了实验,实验中采用了有四种不同的模型,实际上是对预训练向量的处理方式不用。

这里的特征就是词向量,有静态(static)和非静态(non-static)方式。

static方式采用比如word2vec预训练的词向量,训练过程不更新词向量,实质上属于迁移学习了,特别是数据量比较小的情况下,采用静态的词向量往往效果不错。

non-static则是在训练过程中更新词向量。推荐的方式是 non-static 中的 fine-tunning方式(与下游任务结合),它是以预训练(pre-train)的word2vec向量初始化词向量,训练过程中调整词向量,能加速收敛,当然如果有充足的训练数据和资源,直接随机初始化词向量效果也是可以的(例如bert就已经把词向量帮你设定好了,你只需要根据下游任务对词向量进行微调)。

CNN—rand: 随机生成的词向量进行训练

CNN-static: 训练过程中不再更新embeddings。实质上属于迁移学习,特别是在目标领域数据量比较小的情况下,采用静态的词向量效果也不错。(通过设置trainable=False)

CNN-non-static: 在训练过程中对embeddings进行更新和微调(fine tune),能加速收敛。(通过设置trainable=True)

CNN-multichannel: 用多种不同的词向量来代表同一个单词,也就是有多通道的概念。一个通道的词向量是不变的,一个是在训练中进行调试的,但是和单通道比较有好有坏。可以采用一个是Word2vec,一个是Glove来进行训练。

如上图所示:当使用的是静态效果的词向量的时候good的同义词可能会是bad,这就说明了可能在word2vec情况下更关注词语出现在句子中的位置,I feel so good和I feel so bad中good和bad这两个词向量可能是相等的。如果采用的是动态的词向量,那么可能对词向量进行一个调整,使得同义词相似度更加的准确。

4.总结:

尽管很少调整超参数,一个简单的CNN一层卷积的性能非常好。(一层卷积就能达到比较好的效果)

dropout有2%-4%的提升。

为每项任务调整预先培训的向量可以进一步改进(CNN-non-static

试了另外一组预训练的词向量效果不如Word2Vec好。

Adadelta和Adagrad的效果差不多,但是速度更快。

预训练词向量中未出现的单词,初始化采用预训练矩阵的方差做正态分布,效果有提升,并且觉得初始化的方法可以有更多挖掘。

现在预处理的方式有很多种了,这篇文章用的是word2vec,但是现在ELMO,bert层出不穷,或许能给这个模型带来一些活力。

Dropout层例子

 

import tensorflow as tf
dropout = tf.placeholder(tf.float32)#设定一个dropout参数
x = tf.Variable(tf.ones([10, 10]))
y = tf.nn.dropout(x, dropout)
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
a = sess.run(y, feed_dict = {dropout: 0.5})#有0.5的神经元是未被激活的
print(a)
 
结果:
[[0. 2. 0. 2. 2. 2. 0. 2. 2. 2.]
 [2. 0. 0. 0. 2. 2. 0. 2. 0. 2.]
 [0. 0. 2. 2. 2. 0. 2. 2. 2. 2.]
 [0. 0. 2. 2. 0. 0. 2. 2. 0. 2.]
 [0. 2. 0. 0. 2. 0. 0. 0. 0. 0.]
 [2. 0. 0. 0. 0. 2. 0. 0. 0. 0.]
 [0. 2. 0. 0. 2. 2. 2. 0. 2. 0.]
 [0. 2. 2. 2. 0. 0. 0. 2. 0. 2.]
 [0. 0. 2. 0. 2. 2. 0. 2. 0. 0.]
 [0. 2. 2. 2. 2. 0. 2. 0. 2. 2.]]
Process finished with exit code 0

附件:word_cnn.py

import tensorflow as tf


class WordCNN(object):
    def __init__(self, vocabulary_size, document_max_len, num_class):
        self.embedding_size = 128
        self.learning_rate = 1e-3
        self.filter_sizes = [3, 4, 5]
        self.num_filters = 100

        self.x = tf.placeholder(tf.int32, [None, document_max_len], name="x")
        self.y = tf.placeholder(tf.int32, [None], name="y")
        self.is_training = tf.placeholder(tf.bool, [], name="is_training")
        self.global_step = tf.Variable(0, trainable=False)
        self.keep_prob = tf.where(self.is_training, 0.5, 1.0)

        with tf.name_scope("embedding"):
            init_embeddings = tf.random_uniform([vocabulary_size, self.embedding_size])
            self.embeddings = tf.get_variable("embeddings", initializer=init_embeddings)
            self.x_emb = tf.nn.embedding_lookup(self.embeddings, self.x)
            self.x_emb = tf.expand_dims(self.x_emb, -1)

        pooled_outputs = []
        for filter_size in self.filter_sizes:
            conv = tf.layers.conv2d(
                self.x_emb,
                filters=self.num_filters,
                kernel_size=[filter_size, self.embedding_size],
                strides=(1, 1),
                padding="VALID",
                activation=tf.nn.relu)
            pool = tf.layers.max_pooling2d(
                conv,
                pool_size=[document_max_len - filter_size + 1, 1],
                strides=(1, 1),
                padding="VALID")
            pooled_outputs.append(pool)

        h_pool = tf.concat(pooled_outputs, 3)
        h_pool_flat = tf.reshape(h_pool, [-1, self.num_filters * len(self.filter_sizes)])

        with tf.name_scope("dropout"):
            h_drop = tf.nn.dropout(h_pool_flat, self.keep_prob)

        with tf.name_scope("output"):
            self.logits = tf.layers.dense(h_drop, num_class, activation=None)
            self.predictions = tf.argmax(self.logits, -1, output_type=tf.int32)

        with tf.name_scope("loss"):
            self.loss = tf.reduce_mean(
                tf.nn.sparse_softmax_cross_entropy_with_logits(logits=self.logits, labels=self.y))
            self.optimizer = tf.train.AdamOptimizer(self.learning_rate).minimize(self.loss, global_step=self.global_step)

        with tf.name_scope("accuracy"):
            correct_predictions = tf.equal(self.predictions, self.y)
            self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy")

5.textcnn经验分享:

1、TextCNN是一个n-gram特征提取器,对于训练集中没有的n-gram不能很好的提取。对于有些n-gram,可能过于强烈,反而会干扰模型,造成误分类。

2、TextCNN对词语的顺序不敏感,在query推荐中,我把正样本分词后得到的term做随机排序,正确率并没有降低太多,当然,其中一方面的原因短query本身对term的顺序要求不敏感。隔壁组有用textcnn做博彩网页识别,正确率接近95%,在对网页内容(长文本)做随机排序后,正确率大概是85%。

3、TextCNN擅长长本文分类,在这一方面可以做到很高正确率。

4、TextCNN在模型结构方面有很多参数可调。

https://github.com/Jerryten/model_nlp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值