当前位置:   article > 正文

文本分类第一话:textcnn_文本分类一段话

文本分类一段话

TextCNN实现文本分类
文本分类系列将会有几篇,所有的代码将都会放在我的github里。
我的github:https://github.com/422xiaomage/Textcnn_tensorflow
1、TextCNN原理
在这里我们首先先祭出下面这张原理图,然后在细细说出textcnn内部是如何进行计算的。
textcnn原理图如果学过cnn的,看这个textcnn一定是小意思,由于对图像不太了解,所以只知道这里存在的两个区别,一是图像是三通道,而文本只是一通道的,所以肯定比计算图像的卷积的要简单啊。其次这里在做卷积计算时,由于这个向量通常是词向量,所以很明显,这里肯定不能左右n-gram啊。这样说可能还不太明白。下面将基于上面这幅原理图,细细的解释每一步的计算。
其实很简单,看图,这里我们将图中的英文句子换成中文句子(因为小编喜欢中文啦),假设我们向网络里面输入一个文本“小白的一篇深度学习博客,有问题还望大家指出。”,然后呢,我们可以分词,分词结果为“小白/ 的/ 一篇/ 深度学习/ 博客,/ 有问题/ 还/ 望/ 大家/ 指出”,再通过word2vec训练出的词向量来表示每一个词,假设是300维的吧,那么输入便是一个[11,300]的矩阵。输入层是一个[11,300]的矩阵,然后下一层就是卷积层,这里其实也很简单,卷积层一般是取[1,2,3,4,5]这几个尺寸的卷积核,当然你可以只用一个尺寸的,也可以用几个尺寸的然后最后将结果拼接,下面将详细解释这一层是怎么计算的。假设我们取了3、4两个大小的卷积核,这个3、4值得是上下滑动的窗口大小,左右窗口我们上面说过,一般左右不滑动,因为一行就是一个词向量,个人认为切断会破坏语义,所以对于我们取了3、4两个大小的卷积核来说,每一个卷积核实际上就是[3,词向量长度]的矩阵和[4,词向量长度]的矩阵,然后我们对输入进行依次取[3*词向量长度]大小的子矩阵,与卷积核进行计算,对应位置相乘并求和,得到的是一个数,然后对输入向下滑动一行,得到一个新的子矩阵,在进行卷积计算,然后依次滑动到结束,会得到一个[(7-3+1),1]的向量,那么如果这样的卷积核有128个,那么得到的就是一个[(7-3+1),128]的矩阵,然后到池化层,这里我们选择每一列最大的数作为特征,然后将这128列的所有最大的数据拼成一个128维的向量,当然这里池化层也可以选则最大的两位数,那相应的就是256维向量了,同理对[4,词向量长度]的卷积核也会得到一个128维的向量,最后将这两个128维的向量拼接成一个256维的向量,然后经过全连接层,便完成了这个模型的构建了啊。
小编上面虽然用了大量的文字,但其实原理很简单,只要看看就明白了,也不需要任何学习卷积神经网络的基础。到这里我们便可以根据这个思路利用tensorflow大模型了。当然如果你想要用keras搭,会更加容易。
2、Textcnn模型代码部分
如果想使用keras搭的话,github搜keras,看到那个星最多的项目,进去有一个 examples文件夹,里面有官方的源码。
当然小编这里是学习使用tensorflow搭textcnn,使用的是搜狐2019校园算法大赛的数据。下面闲扯一小段,由于这个数据属于工业数据,所以效果肯定很粗糙啊,本人当时比赛的时候,做了一些小处理,只用了这单一模型,跑的效果是0.6,用bert跑的效果比这个模型稍微好了那么一点点,但是用bert几乎没怎么对数据进行处理。这次也是几乎没对数据进行处理用textcnn跑了一下下,只有0.45左右的效果吧,确实很差。
可能模型上还有可以改进的地方,本人毕竟tensorflow小白一枚,这次也是打算通过这些一步步学习tensorflow。
不得不说中间遇到的一个问题,作为一个小白,必须记录一下,也希望能在大家也遇到这个问题的时候给大家提供一波帮助。训练的时候loss一直飙升,从4到了300000,这个loss我是真的服气了,查了很久,终于找到了一个问题,小白将三个类别one_hot时,将标签0编码one_hot成了[0,0,0],这个问题让我查了很多资料,都没找到,只能通过反复的看代码,才发现。果然还是要多看多写代码啊。由于今天时间不够,明天将会将完善后的代码上传至我的github里,力争做到能让大家开箱就用,下载下来就能跑自己的数据。
下面放出模型部分代码:

class TextCNN(object): 

	def __init__(self, config, wordEmbedding):

	    self.inputX = tf.placeholder(tf.int32, [None, config.sequenceLength], name="inputX")
        self.inputY = tf.placeholder(tf.float32, [None, 3], name="inputY")
        self.inputY_1 = tf.placeholder(tf.float32, [None, 1], name="inputY")
        self.dropoutKeepProb = tf.placeholder(tf.float32, name="dropoutKeepProb")
        self.T = []
        # 定义l2损失
        l2Loss = tf.constant(0.0)
        # 词嵌入层
        with tf.name_scope("embedding"):

            # 利用预训练的词向量初始化词嵌入矩阵
            self.W = tf.Variable(tf.cast(wordEmbedding, dtype=tf.float32, name="word2vec") ,name="W")
            # 利用词嵌入矩阵将输入的数据中的词转换成词向量,维度[batch_size, sequence_length, embedding_size]
            self.embeddedWords = tf.nn.embedding_lookup(self.W, self.inputX)
            # 卷积的输入是四维[batch_size, width, height, channel],因此需要增加维度,用tf.expand_dims来增大维度
            self.embeddedWordsExpanded = tf.expand_dims(self.embeddedWords, -1)

        # 创建卷积和池化层
        pooledOutputs = []
        # 有三种size的filter,3, 4, 5,textCNN是个多通道单层卷积的模型,可以看作三个单层的卷积模型的融合
        for i, filterSize in enumerate(config.model.filterSizes):
            with tf.name_scope("conv-maxpool-%s" % filterSize):
                # 卷积层,卷积核尺寸为filterSize * embeddingSize,卷积核的个数为numFilters
                # 初始化权重矩阵和偏置
                #第三维1是通道数量,对于文本来说,通道数量固定是1
                filterShape = [filterSize, config.model.embeddingSize, 1, config.model.numFilters]
                W = tf.Variable(tf.truncated_normal(filterShape, stddev=0.1), name="W")
                b = tf.Variable(tf.constant(0.1, shape=[config.model.numFilters]), name="b")
                conv = tf.nn.conv2d(
                    self.embeddedWordsExpanded,
                    W,
                    strides=[1, 1, 1, 1],
                    padding="VALID",
                    name="conv")
                #tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
                #input见上面,是一个四维向量;filter见上面计算,也是一个四维计算;stride步长,分别表示 在batch_size上的步长,高度的步长,
                # 宽度的步长以及深度的步长,对应的是input的四个维度,对于文本来说可以改变的只有宽度的步长;padding 是填充,这里只有两个值 SAME 和 VALID,
                #SAME表示需要在句子前面进行填充,卷积后的尺寸与句子长度一致,VALID,卷积后尺寸长度是句子长度-filter尺寸+1
                # relu函数的非线性映射
                h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
                #tf.nn.bias_add(conv, b)一个叫bias的向量加到一个叫value的矩阵上,是向量与矩阵的每一行进行相加,得到的结果和value矩阵大小相同。
                # 池化层,最大池化,池化是对卷积后的序列取一个最大值
                pooled = tf.nn.max_pool(
                    h,
                    ksize=[1, config.sequenceLength - filterSize + 1, 1, 1],  # ksize shape: [batch, height, width, channels]
                    strides=[1, 1, 1, 1],
                    padding='VALID',
                    name="pool")
                pooledOutputs.append(pooled)  # 将三种size的filter的输出一起加入到列表中

        # 得到CNN网络的输出长度
        numFiltersTotal = config.model.numFilters * len(config.model.filterSizes)
        
        # 池化后的维度不变,按照最后的维度channel来concat
        self.hPool = tf.concat(pooledOutputs, 3)
        
        # 摊平成二维的数据输入到全连接层
        self.hPoolFlat = tf.reshape(self.hPool, [-1, numFiltersTotal])

        # dropout
        with tf.name_scope("dropout"):
            self.hDrop = tf.nn.dropout(self.hPoolFlat, self.dropoutKeepProb)
       
        # 全连接层的输出
        with tf.name_scope("output"):
            outputW = tf.get_variable(
                "outputW",
                shape=[numFiltersTotal, 3],
                initializer=tf.contrib.layers.xavier_initializer())
            outputB= tf.Variable(tf.constant(0.1, shape=[3]), name="outputB")
            l2Loss += tf.nn.l2_loss(outputW)
            l2Loss += tf.nn.l2_loss(outputB)
            self.predictions = tf.nn.xw_plus_b(self.hDrop, outputW, outputB, name="predictions")
            #self.predictions = tf.nn.softmax(self.prediction, name="softmax")
            #少了一个softmax层
            self.binaryPreds = tf.cast(tf.arg_max(self.predictions, 1), tf.float32, name="binaryPreds")
        
        # 计算三元元交叉熵损失
        with tf.name_scope("loss"):
            losses = tf.nn.softmax_cross_entropy_with_logits(logits=self.predictions, labels=self.inputY)
            self.loss = tf.reduce_mean(losses) + config.model.l2RegLambda * l2Loss
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/242689
推荐阅读
相关标签
  

闽ICP备14008679号