情感分类——BiLSTM
序
- Bi-LSTM的理论就不再说了,本文在前文TextCNN的基础上,将模型改为Bi-LSTM,本意是比较模型效果,但是实际结果不太理想,有过拟和问题。
最最前面
- 写着写着感觉变了味道,本来准备水一下,结果拓展了很多出来,很零碎。
- 如果你对理论不太清楚,可以去看:http://colah.github.io/posts/2015-08-Understanding-LSTMs/
- 如果你对tensorflow使用RNN不是很了解,我很建议你去看完:https://zhuanlan.zhihu.com/p/28196873
- 看完后食用本文更轻松。
模型结构
模型注意
- 使用tensorflow唯一需要注意的就是维度信息了,代码很简洁。
- 一个需要注意的点是,它好像对于batch_size要求很“高”,就是最后的一个batch如果不够batch,比如一个batch为32,结果最后一个只有23,它就会报错…个人没有深究:
https://stackoverflow.com/questions/49951822/invalidargumenterror-concatop-dimensions-of-inputs-should-match,个人直接取整了(去掉最后的不足) - 本人由于过拟和的原因,同样在全连接后加了层drop_out。
实验数据
- 训练数据:本人找的英文情感分析语聊1万多条
下载连接:http://www.cs.cornell.edu/people/pabo/movie-review-data/ - 词库:由1数据提取(去除低频词)
- word2vec向量:自己训练了一份,找了份官方的向量:
https://fasttext.cc/docs/en/english-vectors.html - 自己训练vocab_size小,所以本人就GPU跑,官方的1百万的vocab_size,个人只能CPU跑。
- 这次放上自己的代码…个人比较喜欢写注释,代码只是model的代码,供参考使用,代码风格借鉴很多代码…个人正在逐步养成。
单层Bi_LSTM代码
import tensorflow as tfclass Text_BiLSTM(object):def __init__(self,Config):self.config = Config# 占位符self.input_x = tf.placeholder(tf.int32,[None,self.config.seq_length],name="input_x")# [None,n_classes]self.input_y = tf.placeholder(tf.float32,[None,self.config.num_classes],name="input_y")self.keep_prob = tf.placeholder(tf.float32,name="keep_porb")# 计数self.global_step = tf.Variable(0,trainable=False,name = "global_step")# 损失self.l2_loss = tf.constant(0.0)self.bi_lstm()def bi_lstm(self):with tf.name_scope("embedding"):# size: [vocab_size, embedding_size]self.embedding_table = tf.Variable(self.config.pre_training,dtype=tf.float32,name="embedding_table")# size:[ batch_size,vovab_size,embedding_size ]# 对应到RNN里: [ -1,max_time,n_inputs ]self.embeddings = tf.nn.embedding_lookup(self.embedding_table,self.input_x)with tf.name_scope("Bi_LSTM"):# 双向rnn: 构建基本LSTM单元self.lstm_fw_cell = tf.contrib.rnn.BasicLSTMCell(self.config.state_size)self.lstm_be_cell = tf.contrib.rnn.BasicLSTMCell(self.config.state_size)# 初始化隐藏层self.init_fw = self.lstm_fw_cell.zero_state(self.config.batch_size, dtype=tf.float32)self.init_be = self.lstm_be_cell.zero_state(self.config.batch_size, dtype=tf.float32)# 动态展开:调用函数,自动按照时间步循环更新,时间步由数据维度决定# outputs# 它是time_steps步里所有的输出。# 它的形状为 (output_fw, output_bw)# 其中单个维度:(batch_size, time_steps, cell.output_size),# 但要注意,tensorflow为了简便,其实这里的输出不是softmax后的记过,而是直接将隐藏计算出的 a(符号而已)直接输出# final_state# final_states# state是最后一步的隐状态,同样分(fw,bw)# 单个形状为(batch_size, cell.state_size)# 由于是lstm,它有两个隐态:C和a所以,这是一个元组。_, self.final_states = tf.nn.bidirectional_dynamic_rnn(self.lstm_fw_cell,self.lstm_be_cell,self.embeddings,initial_state_fw = self.init_fw,initial_state_bw = self.init_be)with tf.name_scope("concat"):# 提取前向和后向内的h单元# [batch_size, state_size * 2 ]self.a_final_state = tf.concat([self.final_states[0].c,self.final_states[1].c],axis=1)# drop_outself.final_output = tf.nn.dropout(self.a_final_state, self.keep_prob)# 全连接层 , units:输出的维度大小,改变inputs的最后一维# 也可以softmaxself.logits = tf.layers.dense(self.final_output,units=self.config.num_classes,kernel_regularizer=tf.contrib.layers.l2_regularizer(self.config.l2_reg_lambda))self.probs = tf.nn.softmax(self.logits, -1)# 返回最大数值的下标, axis=1 : 按行查找self.pred_ids = tf.argmax(input=self.probs, axis=1)with tf.name_scope("loss"):# 损失函数,结果出来batch维度??self.cross_entroy = tf.nn.softmax_cross_entropy_with_logits(logits=self.logits,labels=self.input_y)self.loss = tf.reduce_mean(self.cross_entroy)with tf.name_scope('optimizer'):# 综合了 Momentum 和 RMSProp 方法,# 对每个参数保留一个学习率与一个根据过去梯度信息求得的指数衰减均值optimizer = tf.train.AdamOptimizer(self.config.lr)# 防止梯度爆炸的clip截断操作# 梯度和更新变量的元组对gradients, variables = zip(*optimizer.compute_gradients(self.loss))gradients, _ = tf.clip_by_global_norm(gradients, self.config.clip)self.optim = optimizer.apply_gradients(zip(gradients, variables), global_step=self.global_step)with tf.name_scope('accuracy'):# 按行计算,相等的那就返回True,反正返回False,返回的值的矩阵维度和第一个参数一样correct_pred = tf.equal(tf.argmax(self.input_y,1),self.pred_ids)# 所以求平均时要强转self.acc = tf.reduce_mean(tf.cast(correct_pred,tf.float32))
多层
- 实际上,都过拟合了,还加层数,简直…,但是自己很少用多层,今天好好搞一下。
MultiRNNCell
- 第一步,先保证我们会用,这里直接给出源码里的例子,这个一定是正确做法:
num_units = [128, 64]
cells = [BasicLSTMCell(num_units=n) for n in num_units]
stacked_rnn_cell = MultiRNNCell(cells)
- 但是,我到现在依稀记得说这里有个坑,没找到当初看的网址,这里找到了另一个:
- (https://tangshusen.me/2018/11/13/tf-multi-rnn-bug/)
- 你可以看它的参考网址,里面有较为详细的讨论。
- 首先我们把错误的实例放出来:
one_cell = tf.nn.rnn_cell.LSTMCell(num_units=rnn_size)
decoder_cell = tf.nn.rnn_cell.MultiRNNCell([one_cell for _ in range(dec_num_layers)])
# decoder_cell = tf.nn.rnn_cell.MultiRNNCell([one_cell]*dec_num_layers])也是错误的
放上原话:
LSTM cell objects and one object is the copy of other (since the pointers of the two objects are same)
正确做法:
cell_list = [tf.nn.rnn_cell.LSTMCell(num_units=rnn_size) for _ in range(dec_num_layers)]
decoder_cell = tf.nn.rnn_cell.MultiRNNCell(cell_list)
我帮你验证了一下,请看仔细点,标记的那个为我页面查找的显示情况,证明它他唯一
知道了怎么正确用它,我我还想看一下,它到底在干什么。
首先,我们要知道,它的参数: list of RNNCells that will be composed in this order
他的目的:Create a RNN cell composed sequentially of a number of RNNCells,翻译过来就是:创建一个由多个RNN单元格按顺序组成的RNN单元格
很抽象,确实,他的源码更神奇…看完我觉得没必要深究,对于单向的:个人感觉就是做了个容器,把n个的RNN单元包了起来,从此可以宏观上看成一个。对于双向的:我参考的代码是分别对前向和后向做MultiRNNCell??我的直觉告诉我,这不就相当于前向和后向独立了吗??
单层的很容易想象:
但是我有一个疑问?
双向的LSTM多层到底是什么情况,他们是怎么连接的??(这里我指的是,前向的lstm和后向的lstm是每一层都concat到一起,是还是直到最后才concat)
我竟然没有找到图解!!!!行吧,我通过代码,分析了一波维度,先把现象列出来:
(这里的lstm_fw_cell,lstm_be_cell都是多层的)
_, self.states = tf.nn.bidirectional_dynamic_rnn(self.lstm_fw_cell,self.lstm_be_cell,self.embeddings,dtype=tf.float32,)
- 我得到的想象是:首先 self.states 是个元组:(fw , be)
- 单纯看一个,如 fw: 它也是一个元组,维度是 你lstm的层数!!!!
- 再往里面一层就是(c, h)
- 根据这个现象,我认为应该是前向和后向在最后一层才concat到一起(当然,我是这么认为的,而且只有想通了才能写后面concat的代码)
多层代码
- 写的比较嘈…轻喷
- 有两个dropout层,为了全都写了,可以自行去掉。
- 这里放的是改动的代码部分,其他请参考上面:
with tf.name_scope("Bi_LSTM"):# 双向rnn: 构建基本LSTM单元,为了不出错,写的很干脆.....self.lstm_fw_cell = tf.nn.rnn_cell.MultiRNNCell([tf.nn.rnn_cell.DropoutWrapper(tf.contrib.rnn.BasicLSTMCell(self.config.state_size),output_keep_prob=(1 - self.config.bi_prob)) for _ in range(self.config.num_layers)])self.lstm_be_cell = tf.nn.rnn_cell.MultiRNNCell([tf.nn.rnn_cell.DropoutWrapper(tf.contrib.rnn.BasicLSTMCell(self.config.state_size),output_keep_prob=(1 - self.config.bi_prob)) for _ in range(self.config.num_layers)])_, self.states = tf.nn.bidirectional_dynamic_rnn(self.lstm_fw_cell,self.lstm_be_cell,self.embeddings,dtype=tf.float32)with tf.name_scope("concat"):self.output_fw = self.states[0]self.output_bw = self.states[1] # 原形状为[batch_size,max_len,hidden_num]# 提取前向和后向内的h单元# [batch_size, state_size * 2 ]self.a_final_state = tf.concat([self.output_fw[-1].c,self.output_bw[-1].c],axis=-1)# drop_outself.final_output = tf.nn.dropout(self.a_final_state, self.keep_prob)
CPU与GPU交换
- 安装tensorflow_gpu后,默认就是gpu跑(确实飞起的感觉),我小笔记本2G显存…所以大数据我跑不了。
- 在1上跑CPU,有个trick是,在代码最前面加上:
( 前提是:你没有tf.device("/device:GPU:0"),tf.Session(config=gpu_config)等这种很明显和下面不兼容的代码)
os.environ[“CUDA_DEVICE_ORDER”] = “PCI_BUS_ID”
os.environ[“CUDA_VISIBLE_DEVICES”] = “-1”
个人主要参数
- 这里放出主要的参数:(没有细致调参,感兴趣可以试试)
state_size = 128 # 隐藏参数维度,可以理解为记忆细胞的维度# 正则化的参数keep_prob = 0.5 # droppoutbi_prob = 0.5num_layers = 3l2_reg_lambda = 0.5 # l2 regularization lambdalr = 0.0005 # learning ratelr_decay = 0.9 # learning rate decayclip = 6.0 # gradient clipping thresholdnum_epochs = 20 # epochsbatch_size = 32 # batch_size
实验效果
好惭愧,依然有过拟和现象…我fo了
上面的代码( 两个dropout ),在自己训练的词向量上的结果,效果不好。
No optimization over 1000 steps, stop training
Train acc is 0.8958333333333334
Value acc is 0.5242659443315797
MAX train acc is 1.0
MAX value acc is 0.6905766526019691改成官方词向量,去了第一个dropout,效果如下:
Train acc is 0.9421875
Value acc is 0.6133028598218473
MAX train acc is 1.0
MAX value acc is 0.6690107829348335注:这个效果是我做整个对比实验最好的,当然,其他参数如果用心调一下应该会更好,只是这里就这样了。
END
- 由于整个内容是半个下午加晚上搞的,比较仓促,有错误请见谅。
- 本篇可以比较本人前一篇关于TextCNN分类的一起看。
- 最后给自己挖个坑:其实还有一个半成品:gate_conv,介于这两篇之间的产物,但是由于实在找不到它用于分类的论文或者代码,只有它做语言模型的代码…而且最关键的是,论文和代码都不是那种很轻松看得懂的,论文说的很模糊,很多东西都没说清楚,代码作者有些地方和论文作者思想不一样,导致我看得好难受,所以这里留个坑,看能不能补上。
- 本篇完!
参考
[1] tensorflow 双向LSTM搭建
https://www.jianshu.com/p/9c96354fc767
[2] Tensorflow中GRU和LSTM的权重初始化
http://cairohy.github.io/2017/05/05/ml-coding-summarize/Tensorflow%E4%B8%ADGRU%E5%92%8CLSTM%E7%9A%84%E6%9D%83%E9%87%8D%E5%88%9D%E5%A7%8B%E5%8C%96/
[3]tensorflow中的LSTM
https://jasdeep06.github.io/posts/Understanding-LSTM-in-Tensorflow-MNIST/
[4] 多层Bi_LSTM的代码(但是个人觉得有错,但整体还可以)
https://github.com/chilynn/sequence-labeling/blob/master/code/bilstm_crf/BILSTM_CRF.py
[5] No module named ‘tensorflow.models’ 的问题解决方法
https://blog.csdn.net/RineZ/article/details/81671382
[6] 下载tensorflow model的连接:
https://github.com/tensorflow/models
[7]Cannot stack LSTM with MultiRNNCell and dynamic_rnn
https://stackoverflow.com/questions/47371608/cannot-stack-lstm-with-multirnncell-and-dynamic-rnn
[8]using dynamic_rnn with multiRNN gives error
https://stackoverflow.com/questions/48865554/using-dynamic-rnn-with-multirnn-gives-error/53277463#53277463
情感分类——BiLSTM相关推荐
- 基于pytorch的Bi-LSTM中文文本情感分类
基于pytorch的Bi-LSTM中文文本情感分类 目录 基于pytorch的Bi-LSTM中文文本情感分类 一.前言 二.数据集的准备与处理 2.1 数据集介绍 2.2 文本向量化 2.3 数据集处 ...
- SIGIR 2021 | 基于不确定性正则化与迭代网络剪枝的终身情感分类方法
导读 终身学习能力对于情感分类器处理网络上连续的意见信息流而言至关重要.然而,执行终身学习对于深度神经网络来说是困难的,因为持续地训练可用信息会不可避免地会导致灾难性遗忘.发表在信息检索领域顶会 SI ...
- TF使用例子-情感分类
北京站 | NVIDIA DLI深度学习培训 2018年1月26日 NVIDIA 深度学习学院 带你快速进入火热的DL领域 阅读全文 正文共10052个字,4张图,预计阅读时间26分钟. 这次改写一下 ...
- 深度学习在情感分类中的应用
简介与背景 情感分类及其作用 情感分类是情感分析的重要组成部分,情感分类是针对文本的情感倾向进行极性分类,分类数量可以是二分类(积极或消极),也可以是多分类(按情感表达的不同程度),情感分析在影音评论 ...
- 使用语言学特征进行文本情感分类《Linguistically Regularized LSTM for Sentiment Classification》
原文链接 本文发表于自然语言处理领域顶级会议 ACL 2017 代码链接 摘要 本文主要是做句子情感分类任务的研究,前人做的工作大多都依赖于短语级别的标注,这样费时费力,而一旦仅使用句子级别的标注的话 ...
- 疫情微博文本情感分类 (简化版SMP2020赛题)
编者按 代码仅供参考,欢迎交流:请勿用于任何形式的课程作业.如有任何错误,敬请批评指正~ Pytorch系列文章: Pytorch实验一:从零实现Logistic回归和Softmax回归 Pytorc ...
- 循环神经网络实现文本情感分类之使用LSTM完成文本情感分类
循环神经网络实现文本情感分类之使用LSTM完成文本情感分类 1. 使用LSTM完成文本情感分类 在前面,使用了word embedding去实现了toy级别的文本情感分类,那么现在在这个模型中添加上L ...
- 循环神经网络实现文本情感分类之Pytorch中LSTM和GRU模块使用
循环神经网络实现文本情感分类之Pytorch中LSTM和GRU模块使用 1. Pytorch中LSTM和GRU模块使用 1.1 LSTM介绍 LSTM和GRU都是由torch.nn提供 通过观察文档, ...
- 打破情感分类准确率80分天花板!更充分的知识图谱结合范式
来源:夕小瑶的卖萌屋本文约3600字,建议阅读7分钟 本文带你了解NLP的研究者们怎么让模型像人类一样,学会"知识". NLP的研究者们一直都在尝试,怎么样让模型像人类一样,学会& ...
最新文章
- 基于jquery的serializeArray
- python编写的软件界面-用Python写一个带图形界面的文件压缩软件
- 在windows上搭建react-native的android环境
- Blazor 组件之间使用 EventCallback 进行通信
- Linux操作系统需要做的准备
- 央视放出荣耀9X系列广告宣传片:麒麟810处理器+侧面指纹识别
- python辅助脚本教程_[Python] 用python做一个游戏辅助脚本,完整思路
- 神龙神龙你擦亮眼,阿里巴巴要“上天”!
- SpringBoot-JPA删除不成功,只执行了查询语句
- java功能模块_Java 13功能
- Java基础篇:嵌套 if 语句
- 算法笔记--无向图的桥、割点、边双连通分量和点双连通分量
- tensorRt加速tensorflow模型推理(inception V3为例)
- C语言 队列的实现(链表实现)
- DO56 物流信息网
- Excel如何实现随机抽取
- 笔记本电脑禁用自带键盘
- 猫游记页游mysql_5款曾经极其火爆的页游,最后一款90后没听过80后才玩过
- amigo幸运字符什么意思_python3基础01数值和字符串(一)
- Vue文字走马灯(文字轮播)组件