当前位置:   article > 正文

基于IndRNN的微博短文本情感分析设计与实现

基于IndRNN的微博短文本情感分析设计与实现

目录

摘要

数据集描述

模型构建与实现

1.IndRNN模型原理

2. IndRNN代码实现

3.词嵌入特征实现(word2vec)

实验结果对比分析

实验环境:

实验设计:

RNN模型实验结果:

LSTM模型实验结果:

IndRNN模型实验结果:

实验结果对比:

实验结果应用 

 结论 

参考文献


摘要

伴随着移动互联网的迅速发展,微博成为了人们在移动互联网时代获取信息和发表观点最便捷的媒介.微博上存在大量对于社会现象,时政新闻,经济热点等事件的评论.做好这些热点微博评论的情感分析工作,有助于把握社会人群的观点态度,能够更敏感地分辨热点事件在社会当中的影响,同时对管理部门进行舆情监控,制定决策有着重要的参考意义.此外,对不同领域的热点微博评论进行情感分析,合理运用分析结果也有助于促进领域内的产业发展和升级.本文设计并实现了一个针对热点微博评论的情感分析GUI界面,其中最为核心的内容为两个:一是引入IndRNN(Independently Recurrent Neural Networks)建立基于IndRNN的微博文本分析模型,本文在一个中文数据集上进行了实验,其实验结果与经典的LSTM、RNN模型相比,实现的IndRNN模型在微博情感分析任务上取得了较高的准确率。二是基于python爬虫设计实现了微博话题的实时监测分析,整合封装好的IndRNN模型,可以根据输入话题名称进行爬取相关话题评论并进行情感倾向的分析与结果的统计。

数据集描述

本文数据集为中文微博评论短文本,总共148978个评论文本,包含电影、娱乐、明星新闻等相关评论,其中正向情感文本有77386个,具有消极情感倾向的文本 71592个。其中选取整个数据样本的20%作为测试集,其他样本作为训练集。其数据统计如下表所示:

样本类型训练集测试集
积极样本个数6190915477
消极样本个数5727414318

                          

 

模型构建与实现

1.IndRNN模型原理

传统RNN存在的问题

  1. 传统RNN,层内按时间展开时参数共享,出现梯度消失和梯度爆炸的问题
  2. 层内神经元相互联系,难以对神经元的行为作出合理解释
  3. LSTM/GRU使用饱和激活函数(Sigmoid/Tanh),梯度在层间衰减,难以做成多层网络

IndRNN公式分析

       RNN的隐含层状态更新公式:

       IndRNN的隐含层状态更新公式:

其中循环权重u是一个向量,⊙表示阿达马积(Hadamard product)。同一图层中的每个神经元都与其他神经元不相连,通过叠加两层或更多层的IndRNN,可以将神经元连接。 也就是说每层中的神经元都相互独立,并且它们可以跨层连接。

       两者隐含层状态更新公式的对比:

              1.RNN的计算为上一时刻的隐含层状态ht1和权重向量U相乘

              2.IndRNN的计算为上一时刻的隐含层状态ht1和权重向量U点乘(层内神经元独立了)

IndRNN的优势:

  • 将RNN层内神经元解耦,使它们相互独立,提高神经元的可解释性
  • 能够使用Relu等非饱和激活函数,解决层内和层间梯度消失/爆炸问题,同时模型也具有鲁棒性,可构建更深的网络(21层)
  • 相比于LSTM能处理更长(N>5000)的序列信息
IndRNN结构,BN为Batch Normalization的缩写题

2. IndRNN代码实现

ind_rnn_cell.py文件通过调用tensorflow框架实现了IndRNN的封装。
  1. class IndRNNCell(LayerRNNCell):
  2. def __init__(self,
  3. num_units,
  4. recurrent_min_abs=0,
  5. recurrent_max_abs=None,
  6. recurrent_kernel_initializer=None,
  7. input_kernel_initializer=None,
  8. activation=None,
  9. reuse=None,
  10. name=None):
  11. super(IndRNNCell, self).__init__(_reuse=reuse, name=name)
  12. # Inputs must be 2-dimensional.
  13. self.input_spec = base_layer.InputSpec(ndim=2)
  14. self._num_units = num_units
  15. self._recurrent_min_abs = recurrent_min_abs
  16. self._recurrent_max_abs = recurrent_max_abs
  17. self._recurrent_initializer = recurrent_kernel_initializer
  18. self._input_initializer = input_kernel_initializer
  19. self._activation = activation or nn_ops.relu
  20. @property
  21. def state_size(self):
  22. return self._num_units
  23. @property
  24. def output_size(self):
  25. return self._num_units
  26. def build(self, inputs_shape):
  27. if inputs_shape[1].value is None:
  28. raise ValueError("Expected inputs.shape[-1] to be known, saw shape: %s"
  29. % inputs_shape)
  30. input_depth = inputs_shape[1].value
  31. if self._input_initializer is None:
  32. self._input_initializer = init_ops.random_normal_initializer(mean=0.0,
  33. stddev=0.001)
  34. self._input_kernel = self.add_variable(
  35. "input_kernel",
  36. shape=[input_depth, self._num_units],
  37. initializer=self._input_initializer)
  38. if self._recurrent_initializer is None:
  39. self._recurrent_initializer = init_ops.constant_initializer(1.)
  40. self._recurrent_kernel = self.add_variable(
  41. "recurrent_kernel",
  42. shape=[self._num_units],
  43. initializer=self._recurrent_initializer)
  44. # Clip the absolute values of the recurrent weights to the specified minimum
  45. if self._recurrent_min_abs:
  46. abs_kernel = math_ops.abs(self._recurrent_kernel)
  47. min_abs_kernel = math_ops.maximum(abs_kernel, self._recurrent_min_abs)
  48. self._recurrent_kernel = math_ops.multiply(
  49. math_ops.sign(self._recurrent_kernel),
  50. min_abs_kernel
  51. )
  52. # Clip the absolute values of the recurrent weights to the specified maximum
  53. if self._recurrent_max_abs:
  54. self._recurrent_kernel = clip_ops.clip_by_value(self._recurrent_kernel,
  55. -self._recurrent_max_abs,
  56. self._recurrent_max_abs)
  57. self._bias = self.add_variable(
  58. "bias",
  59. shape=[self._num_units],
  60. initializer=init_ops.zeros_initializer(dtype=self.dtype))
  61. self.built = True
  62. def call(self, inputs, state):
  63. """Run one time step of the IndRNN.
  64. Calculates the output and new hidden state using the IndRNN equation
  65. `output = new_state = act(W * input + u (*) state + b)`
  66. where `*` is the matrix multiplication and `(*)` is the Hadamard product.
  67. Args:
  68. inputs: Tensor, 2-D tensor of shape `[batch, num_units]`.
  69. state: Tensor, 2-D tensor of shape `[batch, num_units]` containing the
  70. previous hidden state.
  71. Returns:
  72. A tuple containing the output and new hidden state. Both are the same
  73. 2-D tensor of shape `[batch, num_units]`.
  74. """
  75. gate_inputs = math_ops.matmul(inputs, self._input_kernel)
  76. recurrent_update = math_ops.multiply(state, self._recurrent_kernel)
  77. gate_inputs = math_ops.add(gate_inputs, recurrent_update)
  78. gate_inputs = nn_ops.bias_add(gate_inputs, self._bias)
  79. output = self._activation(gate_inputs)
  80. return output, output

但是我们在日常应用实现时没有必要那么麻烦,通过将IndRNNCell.py代码复制进入我们自己命名的ind_rnn.py文件,通过from ind_rnn import IndRNNCell, RNN进行模型的调用,然后通过以下代码完成对IndRNN的调用。

  1. cells = [IndRNNCell(NUM_UNITS),
  2. IndRNNCell(NUM_UNITS)]
  3. print('Build model...')
  4. model = Sequential()
  5. model.add(RNN(cells, input_shape=(TIME_STEPS, 2)))

3.词嵌入特征实现(word2vec)

本文统一采用Word2Vec进行训练词向量特征,其词嵌入维度为128.其实现的代码如下:

  1. # 创建词语字典,并返回每个词语的索引,词向量,以及每个句子所对应的词语索引
  2. def create_dictionaries(model=None,
  3. combined=None):
  4. """ 该函数完成 3 个工作,构建单词到index 的映射,构建单词到向量的映射,转化训练和测试字典
  5. """
  6. if (combined is not None) and (model is not None):
  7. gensim_dict = Dictionary()
  8. gensim_dict.doc2bow(model.wv.vocab.keys(), allow_update=True)
  9. w2indx = {v: k + 1 for k, v in gensim_dict.items()} # 所有频数超过word2vec_exposures的词语的索引
  10. w2vec = {word: model.wv[word] for word in w2indx.keys()} # 所有频数超过word2vec_exposures的词语的词向量
  11. def parse_dataset(combined):
  12. # 把单词转为 int
  13. data = []
  14. for sentence in combined:
  15. new_txt = []
  16. for word in sentence:
  17. try:
  18. new_txt.append(w2indx[word])
  19. except:
  20. new_txt.append(0)
  21. data.append(new_txt)
  22. return data
  23. combined = parse_dataset(combined)
  24. combined = sequence.pad_sequences(combined, maxlen=max_len) # 每个句子所含词语对应的索引,所以句子中含有频数小于exposure的词语,索引为0
  25. return w2indx, w2vec, combined
  26. else:
  27. print('No data provided...')
  28. # 创建词语字典,并返回每个词语的索引,词向量,以及每个句子所对应的词语索引
  29. def word2vec_train(combined):
  30. model = Word2Vec(size=vocab_dim,
  31. min_count=word2vec_exposures,
  32. window=word2vec_window_size,
  33. workers=cpu_count,
  34. iter=word2vec_iterations)
  35. model.build_vocab(combined)
  36. model.train(combined, total_examples=model.corpus_count, epochs=model.epochs)
  37. model.save('lstm_data/Word2vec_model.pkl')
  38. index_dict, word_vectors, combined = create_dictionaries(model=model, combined=combined)
  39. return index_dict, word_vectors, combined

实验结果对比分析

实验环境:

python3.6.5、tensorflow2.3.1、 keras2.1.5等,在这个基础环境上进行安装其他依赖库。

由于keras和tensorflow版本不匹配问题:

遇到报错:AttributeError: module 'tensorflow._api.v2.train' has no attribute 'Optimizer':将tf.train.Optimizer,更改为tf.optimizers.Optimizer即可。

遇到报错:AttributeError: module 'tensorflow' has no attribute 'placeholder',将import tensorflow as tf 替换成:import tensorflow.compat.v1 as tftf.disable_v2_behavior()即可

遇到报错:AttributeError: module 'tensorflow' has no attribute 'get_default_graph',将tf.get_default_graph替换为tf.compat.v1.get_default_graph即可

遇到报错:ModuleNotFoundError: No module named 'keras.legacy',将keras版本降低为2.1.5版本即可。

实验设计

在上述环境和数据集下,分别设计并实现RNN、LSTM和IndRNN模型,其训练模型的参数均为:迭代次数为5,batch_size = 512,句子输入维度为128,word2vec_iterations = 3  # Word2Vec 的迭代次数,一般比较快;word2vec_exposures = 6  # 出现次数超过这个数目的词才会被计算词向量;word2vec_window_size = 7  # Word2Vec 的参数,窗口大小。loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']等。即是保证公用参数一致。

RNN模型实验结果:

左:RNN模型损失函数图;右:RNN准确率图;

                                                                          

LSTM模型实验结果:

左:LSTM模型损失函数图;右:LSTM准确率图;

                                                                        

       

IndRNN模型实验结果:

IndRNN为2层,其神经网络参数及神经元个数与其他两个模型一致。

左:IndRNN模型损失函数图;右:IndRNN准确率图

                                                                              

实验结果对比:

        模型                            准确率
        RNN               0.87807
        LSTM                0.9560
       IndRNN                0.9459

由实验结果可以发现 IndRNN模型的准确率明显高于传统RNN模型的准确率,但是其并没有LSTM神经网络效果更好,可能是两层IndRNN网络模型在大型的数据集上实验效果没有LSTM神经网络具有记忆能力的模型效果要好,也可能是参数的调节没有使IndRNN模型达到最好的实验结果。而且本文只采用了accuracy进行评价模型性能,如果要想准确的评价IndRNN模型,还需要更多的评价指标,比如P、R、R等;同时要对模型进行更为复杂的参数调节,在这里只是一个简单实现的讨论,不在做过多研究与探索,大家可以自行尝试。如果模型实现存在问题,可以联系Q:525894654。

实验结果应用 

将训练好的模型加载应用到微博评论的舆情分析上 ,其实现结果如下图所示:

                                                        

 结论 

近年来,随着社交网络的逐渐成熟和移动终端技术的迅猛发展,微博作为一种网络传播的主要媒体形式,越来越受到人们的青睐。用户通过在微博上表达观点传播思想,抒发个人情感的同时,也产生了大量带有个人主观情感特征的信息,这些信息中包含着不同趋向的情感特征,进而对网络舆情的传播能产生巨大的影响。本文使用IndRNN模型对互联网上微博短文本的情感分析问题进行了相关研究,该方法首先将训练的词向量作为原始特征向量,然后进行情感分类。在实现方法上, 传统的RNN(RecurrentNeuralNetwork,循环神经网络)模型可以充分地利用上下文信息构建语言模型,在情感分析领域取得了较好的效果,但该方法存在梯度消失与梯度爆炸问题.本研究在RNN循环神经网络和LSTM长短期记忆神经网络的基础上,建立了IndRNN模型,对微博文本情感进行分析,通过在数据集上的相关实验,结果表明,IndRNN网络模型比RNN网络模型情感分析分类预测准确率更高, 但是比LSTM神经网络模型准确率略低,其原因可能是148978个评论文本中有部分文本内容较长,且LSTM神经网络特殊的门控制单元能够记忆更多有用的历史特征信息,对文本的分类产生重要影响。最后,我们将训练好的模型应用于微博的舆情监测上,结合python的bs4库实现评论的实时爬取,通过加载训练好的模型,可以完成对收集到的评论文本进行情感分析及舆论导向的统计。

参考文献

1.Li S , Li W , Cook C , et al. Independently Recurrent Neural Network (IndRNN): Building A Longer and Deeper RNN[C]// Computer Vision and Pattern Recognition (CVPR 2018). 2018.

2.钮成明, 詹国华, 李志华. 基于深度神经网络的微博文本情感倾向性分析[J]. 计算机系统应用, 2018, 27(11):207-212.

3.伍行素, 陈锦回. 基于LSTM深度神经网络的情感分析方法[J]. 上饶师范学院学报, 2018, 38(06):16-20.

4.吴国栋, 刘国良, 张凯,等. SVM和RNN在网络评论情感分析中的比较研究[J]. 上海工程技术大学学报, 2019, 033(004):378-383.

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/479938
推荐阅读
相关标签
  

闽ICP备14008679号