【NLP方向】【万时计划】58-64

txtRNN

指得是利用循环神经网络解决文本分类的问题,文本分类是自然语言处理的一个基本任务,试图推断出文本的标签或标签集合。

原理

当对序列进行处理的时候,一般采用循环神经网络RNN,尤其是LSTM、GRU等变体更为常用。此处的对象文本可以是一个句子,也可以是文档(短文本、若干句子)或篇章(长文本),因此,每段 文本的长度都不尽相同。在对文本进行分类的时候,我们一般会指定一个固定的输入序列/文本长度,该长度 可以是最长文本/序列的长度,此时其他文本进行填充。或进行其他操作将测试集中的文本/序列的长度规范化。

首先,需对文本进行分词,然后指定一个序列长度n,并使用词嵌入得到每个词固定维度的向量表示。对于每一个输入文本/序列,我们可以在RNN的每一个时间步长上输入文本中一个单词的向量表示,计算当前时间步长上的隐藏状态,然后用于当前时间步骤的输出以及传递到传递给下一个时间步长并和下一个单词的词向量一起作为RNN单元输入,然后再计算下一个时间步长上RNN的隐藏状态,依次重复,直到处理完输入文本的每一个单词,由于输入文本长度为n,所以要经历n个时间步长。

textRNN网络结构
structure1

流程:embedding—>BiLSTM—>concat final output/average all output—>softmax layer

在这里插入图片描述

一般取前向/反向LSTM在最后一个时间步长上隐藏状态,然后拼接,再经过一个softmax函数进行一个多分类。或者取前向/反向LSTM在每个时间步长上的隐藏状态,对每个时间步长上的两个隐藏状态进行拼接,然后对所有时间步长上拼接后的隐藏状态取均值,再经过一个softmax层进行一个多分类,也可添加dropout/L2正则化或BatchNormalization来防止过拟合以及加速模型训练。

structure2

流程:embedding->BiLSTM->(dropout)->concat output->UniLSTM->(dropout)->softmax layer

在这里插入图片描述

与结构1不同的是,在双向LSTM的基础上又堆叠了一个单项LSTM。把双向LSTM在每个时间步长上的两个隐藏状态进行拼接,作为上层单向LSTM每个时间步长上的一个输入,最后取上层单向LSTM最后一个时间步长上的隐藏状态,再经过一个softmax层进行一个多分类。

textRNN效果与textcnn不相上下,但RNN的训练速度相对偏慢,一般两层就足够多了。

"""
embedding_dim #词向量维度
seq_length		#序列长度
num_classes		#类别数
vocab_size		#词汇表大小
num_layers		#隐藏层层数
hidden_dim		#隐藏层神经元
rnn = 'gru'		#LSTM或者GRU单元
dropout_keep_prob#dropout保留比例
learning_rate	#学习率		
batch_size		#每批训练大小		
num_epochs		#共迭代轮次
print_per_batch	#每多少轮输出一次结果
save_per_batch	#每多少轮存入tensorboard
"""
class TextRNN(object):
    def __init__(self, config): 
        self.config = config
        #三个待输入数据
        self.input_x = tf.placeholder(tf.int32, [None, self.config.seq_length], name='input_x')
        self.input_y = tf.placeholder(tf.float32, [None, self.config.num_length], name='input_y')
        self.keep_prob = tf.placeholder(tf.float32, name='keep_prob')
        self.run()
        
    def rnn(self):
        def lstm_cell():		#LSTMself核
            return tf.contrib.rnn.BasicLSTMCell(self.config.hidden_dim, state_is_tuple=True)
        def gru_cell():			#GRU核
            return tf.contrib.rnn.GRUCell(self.config.hidden_dim)
        
        def dropout():			#为每个rnn核后面加一个dropout层
            if(self.config.rnn == 'lstm'):
                cell = lstm_cell()
            else:
                cell = gru_cell()
            return tf.contrib.rnn.DropoutWrapper(cell, output_keep_prob=self.keep_prob)
        #词向量映射
        with tf.device('/cpu:0'):
            embedding = tf.get_variable('embedding', [self.config.vocab_size, self.config.embedding_dim])
            embedding_inputs = tf.nn.embedding_lookup(embedding, self.input_x)
        with tf.name_scope("rnn"):
            #多层rnn网络
            cells = [dropout() for _ in range(self.config.num_layers)]
            rnn_cell = tf.contrib.rnn.MultiRNNCell(cells, state_is_tuple=True)
            
            _outputs, _ = tf.nn.dynamic_rnn(cell=rnn_cell, inputs=embedding_inputs, dtype=tf.float32)
            last = _output[:, -1, :] #取得最后一个时序输出作为结果
        with tf.name_scope("score"):
            #全连接层,后面的dropout以及RELU激活
            fc = tf.layers.dense(last, self.config.num_class, name='fc1')
            fc = tf.contrib.layers.dropout(fc, self.keep_prob)
            fc = tf.nn.relu(fc)
            
            #分类器
            self.logits = tf.layers.dense(fc, self.config.num_classes, name='fc2')
            self.y_preb_cls = tf.argmax(tf.nn.softmax(self.logits), 1)	#预测类别
            
        with tf.name_scope("optimize"):
            #损失函数,交叉熵
            cross_entropy = tf.nn.softmax_cross_entry_with_logits(logits=self.logits, labels=self.input_y)
            self.loss = tf.reduce_mean(cross_entropy)
            #优化器
            self.optim = tf.train.AdamOptimizer(learning_rate=self.config.learning_rate).minimize(self.loss)
            
        with tf.name_scope("accuracy"):
            #准确率
            correct_pred = tf.equl(tf.argmax(self.input_y, 1), self.y_pred_cls)
            self.acc = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
        
textCNN

在以上的语言模型中,我们将文本数据看作是只有一个维度的时间序列,且很自然地使用循环神经网络来表征这样的数据。其实,我们也可以将文本当作一维图像,从而可以用一维卷积神经网络来捕捉临近关键词之间的管联。

一维卷积层

与二维卷积层一样,一维卷积层使用一维的互相关运算。在一维互相关运算中,卷积窗口从输入数组的最左方开始,按照从左往右的顺序,依次在输入数组上滑动。当卷积窗口滑动到某一位置时,窗口中的输入子数组与核数组按元素相乘并求和,得到输出数组中相应位置的元素。如下图所示,输入是一个宽为7的一维数组,核数组的宽度为2.看到输出的宽度为7-2+1=6,且输出第一个元素是由输入最左侧宽为2的子数组与核数组按元素相乘后再相加得到的。

在这里插入图片描述

多输入通道的一维互相关也与多输入通道的二维互相关运算类似:在每个通道上,将核与相应的输入做一维互相关运算,并将通道之间的结果相加相加得到输出结果。下图展示了含三个输入通道的一维互相关运算,其中阴影部分为第一个输出元素及其计算所使用的输入和核数组元素:

在这里插入图片描述

由二维互相关运算的定义可知,多输入通道的一维互相关运算可以看作单输入通道的二维互相关运算。如下图所示,也可将上图中多输入通道的一维互相关运算以等价的单输入通道的二维互相关运算呈现。此处,核的高度等于输入的高。下图阴影部分为第一个输出元素及其计算 所使用的输入和核数组元素:

在这里插入图片描述

以上都是输出只有一通道。扩展:可以在一维卷积层指定多个输出通道,从而扩展卷积层中的模型参数。

时序最大池化层

textCNN中使用的时序最大池化层max-over-time pooling层实际上对应一维全局最大池化层:假设输入包含多个通道,各通道由不同时间步上的数值组成,各通道的输出即该通道所有时间步中最大的数值。因此,时序最大池化层的输入在各个通道上的时间步数可以不同。为提升计算性能,我们常常将不同长度的时序样本组成一个小批量的等长样本。人工添加补位字符没有实际意义。由于时序最大池化层的主要目的是抓取时序中最重要的特征,它通常能使模型不受人工添加的字符影响。

textCNN模型

该模型主要使用了一维卷积层和时序最大池化层。假设输入的文本序列由n个词组成,每个词用d维的词向量表示。那么输入样本的宽为n,高为1,输入通道数为d。textCNN的计算主要分为以下几步:

  1. 定义多个一维卷积核,并使用这些卷积核对输入分别做卷积计算。宽度不同的卷积核可能会捕捉到不同个数的相邻词的相关性。

  2. 对输出的所有通道分别做时序最大池化,再将这些通道的池化输出值连结为向量 。

  3. 通过全连接层将连结后的向量变换为有关各类别的输出。这一步可以使用丢弃层应对过拟合。

    此处输⼊是⼀个有11个词的句⼦,每个词用6维词向量表⽰。因此输⼊序列的宽为11,输入通道数为6。给定2个一维卷积核,核宽分别为2和4,输出通道数分别设为4和5。因此,一维卷积计算后,4个输出通道的宽为 11 - 2 + 1 = 10,而其他5个通道的宽为 11 - 4 + 1 = 8。尽管每个通道的宽不同,我们依然可以对各个通道做时序最大池化,并将9个通道的池化输出连结成⼀个9维向量。最终使⽤全连接将9维向量变换为2维输出,即正面情感和负面情感的预测。

在这里插入图片描述

python代码性能优化

  1. python是动态强类型语言

    一个变量所指向对象的类型在运行时才确定,编译器做不了任何预测,也就无从优化。

  2. python是解释型语言

    边执行边解释,执行一句转化一句,但是 不支持JIT

  3. python中一切皆对象

    包括类,类的实例,数字,模块。每个对象都需要维护引用计数,增加了额外的工作。

  4. python中的GIL,由于GIL的存在,python中的多线程并不能真正的并发。如果是在IO Bound的业务场景,这个问题并不大,但是在CPU Bound的场景,这就很致命了。一般都是使用多进程(pre fork)或者加上协程。任何python线程执行前,必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以多线程在python中只能交替执行,即使100个线程跑在100核cpu上也只用到1个核.

  5. 垃圾回收。python采用引用计数、标记-清除和分代的垃圾回收策略,其中引用计数存在循环引用的缺陷。每次垃圾回收的时候都会中断正在执行的程序,造成所谓的卡顿。

python代码优化通常包含两方面内容:减小代码的体积,提高代码的运行效率。

一些常用的优化技巧:

  • 使用dict或set查找元素

    python字典和集合中使用了hash table,因此,查找操作的复杂度为O(1),而list实际是个数组,在list中,查找需要遍历整个list,其复杂度为O(n),因此对成员的查找访问等操作字典要比list快。

  • 使用set求交集、并集和差

    set的union,intersection,difference操作要比list的迭代要快。因此如果涉及到求list交集,并集或者差的问题可以转换为set来操作。

    语法操作说明
    set(list1)|set(list2)union包含list1和list2所有数据的集合
    set(list1)&set(list2)intersection包含list1和list2中共同元素的新集合
    set(list1)-set(list2)difference在list1中出现但不在list2中出现的元素集合
  • 优化循环

    对循环的优化所遵循的原则是尽量减少循环过程中的计算量,循环外能做的事不要放在循环内。

  • 优化包含多个判断表达式的顺序

    对于and,应该把满足条件少的放在前面,对于or,把满足条件多的放在前面。

  • 使用join合并迭代器中的字符串

    在字符串连接的过程中尽量使用join()而非+

  • 合理使用生成器(generator)

    将列表生成式中的中括号改为小括号,即变为生成器。此外还可以利用yield创建generator。

  • 使用局部变量,避免"global"关键字。python访问局部变量会比全局变量快得多,因此可以利用这一特性提升性能。

  • 使用if is进行判断

    if done is not None比语句 if done != None更快

  • 使用连级比较"x < y < z"而不是"x < y and y < z"

  • while 1 要比 while True 更快

  • 不借助中间变量交换两个变量的值

    a, b=b, a而不是c=a;a=b;b=c来交换a,b的值,可以快一倍

  • 在排序的时候尽可能多地使用键和内置sort()方法。

softmax激活实现多分类

该激活函数用途十分广泛,它可以将任意实数xs转换成0到1之间的一个概率P(xs).在机器学习和深度学习中,Softmax激活函数可应用于回归(softmax回归)、神经网络多分类器的输出层等。

Softmax激活函数计算步骤其实十分简洁,现给定n个实数 x 1 , x 2 , . . . , x n {{x}_{1}},{{x}_{2}},...,{{x}_{n}} x1,x2,...,xn,Softmax激活函数计算过程如下:

  1. 计算exp(xs)即 e x s , s = 1 , 2 , . . . , n {{e}^{xs}},s=1,2,...,n exs,s=1,2,...,n,将其作为分子Nu;
  2. 求和 ∑ exp ⁡ ( x s ) \sum{\exp (xs)} exp(xs)将求和结果作为分母De;
  3. 计算xs的概率=分子/分母

数学变换: y s = S o f t max ⁡ ( x s ) = P ( x s ) = exp ⁡ ( x s ) ∑ j = 1 n exp ⁡ ( x j ) ( s = 1 , 2 , . . . , n ) {{y}_{s}}=Soft\max (xs)=P(xs)=\frac{\exp (xs)}{\sum\nolimits_{j=1}^{n}{\exp (xj)}}(s=1,2,...,n) ys=Softmax(xs)=P(xs)=j=1nexp(xj)exp(xs)(s=1,2,...,n)

Softmax激活函数将神经网络的原始输出(对输入函数的值做复杂加权和非线性处理后的结果)变换为概率分布。输出的是一个0到1之间的概率,且各个概率之和为1,这些概率形成一个概率分布。

Softmax算例:设有[-1,0,3,5]四个数,计算Softmax激活函数:

  1. 计算分母 D e = exp ⁡ ( − 1 ) + exp ⁡ ( 0 ) + exp ⁡ ( 3 ) + exp ⁡ ( 5 ) De=\exp (-1)+\exp (0)+\exp (3)+\exp (5) De=exp(1)+exp(0)+exp(3)+exp(5)

    de = sum(np.exp(xs))
    
  2. 在这里插入图片描述

    从上表看出xs值越大,其概率越高

在这里插入图片描述

  1. python实现

    import numpy as np
    def softmax(xs):
        return np.exp(xs)/sum(np.exp(xs))
    x = np.array([-1, 0, 3, 5])
    print("输出:\n", softmax(x))
    

如何在二维卷积层中指定多个输出通道

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值