Bi-LSTM(Attention)

@

1.理论

1.1 文本分类和预测(翻译)

文本分类的输入处理和预测(翻译)不同:

  1. 预测(翻译)通常用eye()把每个输入向量转换为one-hot向量,
  2. 但文本分类模型通常用Embedding初始化一个嵌入矩阵用来训练,不需要one-hot向量

1.2 注意力模型

1.2.1 Attention模型

注意力机制(Attention Mechanism)的本质是对于给定目标,通过生成一个权重系数对输入进行加权求和,来识别输入中哪些特征对于目标是重要的,哪些特征是不重要的;

为了实现注意力机制,我们将输入的原始数据看作<Key,Value>键值对的形式,根据给定的任务目标中的查询值Query计算Key与Query之间的相似系数,可以得到Value值对应的权重系数,即注意力权重,之后再用权重系数对Value值进行加权求和,即可得到输出.我们使用Q,K,V分别表示Query,Key和Value.

注意力机制在深度学习各个领域都有很多的应用.不过需要注意的是,注意力并不是一个统一的模型,它只是一个机制,在不同的应用领域有不同的实现方法。

  • 注意力权重系数W的公式如下:\(W=softmax⁡(QK^T)\)
  • 注意力权重系数W与Value做点积操作(加权求和)得到融合了注意力的输出:

    \(Attention(Q,K,V)=W⋅V=softmax⁡(QK^T)⋅V\)

注意力模型的详细结构如下图所示:

在本实验中,Query是指final_hidden_state,Key和Value都是指lstm_output,注意力权重W是指attn_weights

  1. 两个输入值用bmm()进行加权求和得到注意力权重attn_weights(由于final_hidden_state是一维的,所以不需要像seq2seq2中一样遍历时间步)
  2. 然后注意力权重attn_weights和lstm_output再进行用bmm()进行加权求和,得到context,即融合了注意力的输出(不同任务处理方式不同,Bi-LSTM文本分类不需要和Seq2Seq任务一样把context再和decoder_output进行combine和fc)

1.2.2 Bi-LSTM(Attention)模型结构

文本分类中的Attention结构:

2.实验

2.1 实验步骤

  1. 数据预处理,得到字典、样本数等基本数据
  2. 构建Bi-LSTM(Attention)模型,分别设置模型的输入
  3. 训练
    1. 代入数据
    2. 得到模型输出值,取其中最大值的索引,找到字典中对应的字母,即为模型预测的下一个字母.
    3. 把模型输出值和真实值相比,求得误差损失函数,运用Adam动量法梯度下降
  4. 测试
  5. 可视化注意力权重矩阵

2.2 算法模型

  1. """
  2. Task: 基于Bi-LSTM和注意力机制的文本情感分类
  3. Author: ChengJunkai @github.com/Cheng0829
  4. Email: chengjunkai829@gmail.com
  5. Date: 2022/09/14
  6. Reference: Tae Hwan Jung(Jeff Jung) @graykode
  7. """
  8. import numpy as np
  9. import torch, time, os, sys
  10. import torch.nn as nn
  11. import torch.optim as optim
  12. import torch.nn.functional as F
  13. import matplotlib.pyplot as plt
  14. '''1.数据预处理'''
  15. def pre_process(sentences):
  16. word_sequence = " ".join(sentences).split()
  17. word_list = []
  18. '''
  19. 如果用list(set(word_sequence))来去重,得到的将是一个随机顺序的列表(因为set无序),
  20. 这样得到的字典不同,保存的上一次训练的模型很有可能在这一次不能用
  21. (比如上一次的模型预测碰见i:0,love:1,就输出you:2,但这次模型you在字典3号位置,也就无法输出正确结果)
  22. '''
  23. for word in word_sequence:
  24. if word not in word_list:
  25. word_list.append(word)
  26. word_dict = {w:i for i, w in enumerate(word_list)}
  27. word_dict["''"] = len(word_dict)
  28. word_list = word_list.append("''")
  29. vocab_size = len(word_dict) # 词库大小16
  30. max_size = 0
  31. for sen in sentences:
  32. if len(sen.split()) > max_size:
  33. max_size = len(sen.split()) # 最大长度3
  34. for i in range(len(sentences)):
  35. if len(sentences[i].split()) < max_size:
  36. sentences[i] = sentences[i] + " ''" * (max_size - len(sentences[i].split()))
  37. return sentences, word_list, word_dict, vocab_size, max_size
  38. def make_batch(sentences):
  39. # 对于每个句子,返回包含句子内每个单词序号的列表
  40. inputs = [np.array([word_dict[n] for n in sen.split()]) for sen in sentences] # [6,3]
  41. targets = [out for out in labels]
  42. inputs = torch.LongTensor(np.array(inputs)).to(device)
  43. targets = torch.LongTensor(np.array(targets)).to(device)
  44. '''情感分类构建嵌入矩阵,没有eye()'''
  45. return inputs, targets
  46. class BiLSTM_Attention(nn.Module):
  47. def __init__(self):
  48. super(BiLSTM_Attention, self).__init__()
  49. '''情感分类构建嵌入矩阵,没有eye()'''
  50. self.embedding = nn.Embedding(vocab_size, embedding_dim)
  51. self.lstm = nn.LSTM(embedding_dim, n_hidden, bidirectional=True)
  52. self.out = nn.Linear(2*n_hidden, num_classes)
  53. def forward(self, X):
  54. # input : [batch_size, n_step, embedding_dim] [6,3,2]
  55. input = self.embedding(X)
  56. # input : [n_step, batch_size, embedding_dim] [3,6,2]
  57. # input : [输入序列长度(时间步长度),样本数,嵌入向量维度]
  58. input = input.permute(1, 0, 2)
  59. # hidden_state : [num_layers(=1)*num_directions(=2), batch_size, n_hidden]
  60. # hidden_state : [层数*网络方向,样本数,隐藏层的维度(隐藏层神经元个数)]
  61. hidden_state = torch.zeros(1*2, len(X), n_hidden).to(device)
  62. # cell_state : [num_layers*num_directions, batch_size, hidden_size]
  63. # cell_state : [层数*网络方向,样本数,隐藏层的维度(隐藏层神经元个数)]
  64. cell_state = torch.zeros(1*2, len(X), n_hidden).to(device)
  65. # final_hidden_state, final_cell_state : [num_layers(=1)*num_directions(=2), batch_size, n_hidden]
  66. ltsm_output, (final_hidden_state, final_cell_state) = self.lstm(input, (hidden_state, cell_state))
  67. # ltsm_output : [batch_size, n_step, n_hidden*num_directions(=2)]
  68. ltsm_output = ltsm_output.permute(1, 0, 2)
  69. attn_output, attention = self.attention_net(ltsm_output, final_hidden_state)
  70. # model : [batch_size, num_classes], attention : [batch_size, n_step]
  71. return self.out(attn_output), attention
  72. '''两次bmm加权求和,相当于两次for循环'''
  73. # lstm_output : [batch_size, n_step, n_hidden*num_directions(=2)] [6,3,16]
  74. # final_hidden_state : [num_layers(=1)*num_directions(=2), batch_size, n_hidden] [2,6,8]
  75. def attention_net(self, lstm_output, final_hidden_state):
  76. # final_hidden_state : [batch_size, n_hidden*num_directions(=2), 1(=n_layer)] [6,16,1]
  77. final_hidden_state = final_hidden_state.view(-1, 2*n_hidden, 1)
  78. '''第一次bmm加权求和:: lstm_output和final_hidden_state生成注意力权重attn_weights'''
  79. # [6,3,16]*[6,16,1] -> [6,3,1] -> attn_weights : [batch_size, n_step] [6,3]
  80. attn_weights = torch.bmm(lstm_output, final_hidden_state).squeeze(2) # 第3维度降维
  81. softmax_attn_weights = F.softmax(attn_weights, 1) # 按列求值 [6,3]
  82. '''第二次bmm加权求和 : lstm_output和注意力权重attn_weights生成上下文向量context,即融合了注意力的模型输出'''
  83. # [batch_size, n_hidden*num_directions, n_step] * [batch_size,n_step,1] \
  84. # = [batch_size, n_hidden*num_directions, 1] : [6,16,3] * [6,3,1] -> [6,16,1] -> [6,16]
  85. context = torch.bmm(lstm_output.transpose(1, 2), softmax_attn_weights.unsqueeze(2)).squeeze(2)
  86. softmax_attn_weights = softmax_attn_weights.to('cpu') # numpy变量只能在cpu上
  87. '''各个任务求出context之后的步骤不同,LSTM的上下文不需要和Seq2Seq中的一样和decoder_output连接'''
  88. return context, softmax_attn_weights.data.numpy()
  89. if __name__ == '__main__':
  90. chars = 30 * '*'
  91. embedding_dim = 3 # embedding size
  92. n_hidden = 8 # number of hidden units in one cell
  93. num_classes = 2 # 0 or 1
  94. '''GPU比CPU慢的原因大致为:
  95. 数据传输会有很大的开销,而GPU处理数据传输要比CPU慢,
  96. 而GPU在矩阵计算上的优势在小规模神经网络中无法明显体现出来
  97. '''
  98. device = ['cuda:0' if torch.cuda.is_available() else 'cpu'][0]
  99. # 3 words sentences (=sequence_length is 3)
  100. sentences = ["i love you", "he loves me", "don't leave", \
  101. "i hate you", "sorry for that", "this is awful"]
  102. labels = [1, 1, 1, 0, 0, 0] # 1 is good, 0 is not good.
  103. '''1.数据预处理'''
  104. sentences, word_list, word_dict, vocab_size, max_size = pre_process(sentences)
  105. inputs, targets = make_batch(sentences)
  106. '''2.构建模型'''
  107. model = BiLSTM_Attention()
  108. model.to(device)
  109. criterion = nn.CrossEntropyLoss()
  110. optimizer = optim.Adam(model.parameters(), lr=0.001)
  111. if os.path.exists('model_param.pt') == True:
  112. # 加载模型参数到模型结构
  113. model.load_state_dict(torch.load('model_param.pt', map_location=device))
  114. '''3.训练'''
  115. print('{}\nTrain\n{}'.format('*'*30, '*'*30))
  116. loss_record = []
  117. for epoch in range(10000):
  118. optimizer.zero_grad()
  119. output, attention = model(inputs)
  120. output = output.to(device)
  121. loss = criterion(output, targets)
  122. loss.backward()
  123. optimizer.step()
  124. print(loss)
  125. if loss >= 0.001: # 连续30轮loss小于0.01则提前结束训练
  126. loss_record = []
  127. else:
  128. loss_record.append(loss.item())
  129. if len(loss_record) == 30:
  130. torch.save(model.state_dict(), 'model_param.pt')
  131. break
  132. if (epoch + 1) % 1000 == 0:
  133. print('Epoch:', '%04d' % (epoch + 1), 'Loss = {:.6f}'.format(loss))
  134. torch.save(model.state_dict(), 'model_param.pt')
  135. '''4.测试'''
  136. print('{}\nTest\n{}'.format('*'*30, '*'*30))
  137. test_text = 'sorry i hate you'
  138. # 返回包含每个单词序号的列表矩阵(为了有2个维度,还要加一个中括号升维)
  139. tests = [np.array([word_dict[n] for n in test_text.split()])]
  140. test_batch = torch.LongTensor(np.array(tests)).to(device)
  141. predict, attn_test = model(test_batch)
  142. predict = predict.data.max(1, keepdim=True)[1]
  143. print('The emotion of "%s" is '%test_text, end='')
  144. if predict[0][0] == 0:
  145. print('bad!')
  146. else:
  147. print('good!')
  148. '''5.可视化注意力权重矩阵'''
  149. fig = plt.figure(figsize=(0.5*len(sentences), 0.5*len(sentences[0]))) # [batch_size, n_step]
  150. ax = fig.add_subplot(1, 1, 1)
  151. # attention : (6, 3)
  152. ax.matshow(attention, cmap='viridis')
  153. word_show = ['单词'] * len(sentences[0])
  154. word_show = [word_show[i] + str(i+1) for i in range(len(sentences[0]))] # ['word_1', 'word_2', 'word_3']
  155. ax.set_xticklabels([''] + word_show, fontdict={'fontsize': 14} , fontproperties='SimSun')
  156. sentence_show = ['句子'] * len(sentences)
  157. sentence_show = [sentence_show[i] + str(i+1) for i in range(len(sentence_show))] # ['sentence_1', 'sentence_2', 'sentence_3', 'sentence_4', 'sentence_5', 'sentence_6']
  158. ax.set_yticklabels([''] + sentence_show, fontdict={'fontsize': 14}, fontproperties='SimSun')
  159. plt.show()

NLP之基于Bi-LSTM和注意力机制的文本情感分类的更多相关文章

  1. NLP之基于TextCNN的文本情感分类

    TextCNN @ 目录 TextCNN 1.理论 1.1 基础概念 最大汇聚(池化)层: 1.2 textCNN模型结构 2.实验 2.1 实验步骤 2.2 算法模型 1.理论 1.1 基础概念 在 ...

  2. 基于Bert的文本情感分类

    详细代码已上传到github: click me Abstract:    Sentiment classification is the process of analyzing and reaso ...

  3. NLP文本情感分类传统模型+深度学习(demo)

    文本情感分类: 文本情感分类(一):传统模型 摘自:http://spaces.ac.cn/index.php/archives/3360/ 测试句子:工信处女干事每月经过下属科室都要亲口交代24口交 ...

  4. pytorch 文本情感分类和命名实体识别NER中LSTM输出的区别

    文本情感分类: 文本情感分类采用LSTM的最后一层输出 比如双层的LSTM,使用正向的最后一层和反向的最后一层进行拼接 def forward(self,input): ''' :param inpu ...

  5. NLP(二十二)利用ALBERT实现文本二分类

      在文章NLP(二十)利用BERT实现文本二分类中,笔者介绍了如何使用BERT来实现文本二分类功能,以判别是否属于出访类事件为例子.但是呢,利用BERT在做模型预测的时候存在预测时间较长的问题.因此 ...

  6. NLP采用Bert进行简单文本情感分类

    参照当Bert遇上Kerashttps://spaces.ac.cn/archives/6736此示例准确率达到95.5%+ https://github.com/CyberZHG/keras-ber ...

  7. 自注意力机制(Self-attention Mechanism)——自然语言处理(NLP)

    近年来,注意力(Attention)机制被广泛应用到基于深度学习的自然语言处理(NLP)各个任务中.随着注意力机制的深入研究,各式各样的attention被研究者们提出.在2017年6月google机 ...

  8. AAAI2018中的自注意力机制(Self-attention Mechanism)

    近年来,注意力(Attention)机制被广泛应用到基于深度学习的自然语言处理(NLP)各个任务中.随着注意力机制的深入研究,各式各样的attention被研究者们提出,如单个.多个.交互式等等.去年 ...

  9. LSTM实现中文文本情感分析

    1. 背景介绍 文本情感分析是在文本分析领域的典型任务,实用价值很高.本模型是第一个上手实现的深度学习模型,目的是对深度学习做一个初步的了解,并入门深度学习在文本分析领域的应用.在进行模型的上手实现之 ...

随机推荐

  1. 结束语句之 break

    C 语言自学之 break Dome1: 找出0-50之间的所有素数,所谓素数就是只能被1和它本身整除的数字,比如:7,13,23等.                运行结果: 2  3  5  7 ...

  2. 手把手教你 Apache DolphinScheduler 本地开发环境搭建 | 中英文视频教程

    点击上方 蓝字关注我们 最近,一些小伙伴反馈对小海豚的本地开发环境搭建过程不太了解,这不就有活跃的贡献者送来新鲜的视频教程!在此感谢@Tianqi-Dotes 的细致讲解 贡献者还贴心地录制了中英文两 ...

  3. Blazor VS Vue

    Vue--​​两分钟概述 Vue 是一个JavaScript 框架. 在其最简单的模式中,您可以简单地将核心 Vue 脚本包含在您的应用程序中,然后开始构建您的组件. 除此之外,对于更复杂的应用程序, ...

  4. 老梗新玩「GitHub 热点速览 v.22.34」

    作者:HelloGitHub-小鱼干 不知道你是否和我有一样的烦恼,最近的流行梗当自己要用拿来造词时,就陷入了不知道咋"换壳"的尴尬地步.sao-gen-gen 大大减少了你老梗新 ...

  5. kafka报错 日志压缩报错直接退出

    Resetting  first dirty ofset to log start  offset 2971862 since the checkpointed offset 12675089 is ...

  6. 项目实践2:(问卷)用html和css做一个网页

    好家伙,又来写项目了 1.以下是考题,姑且把他理解为甲方吧. 2.以下是附带的题目素材 开干.

  7. KingbaseESV8R6等待事件之lwlock buffer_content

    前言 等待事件是排查数据库性能的指标之一.简单理解,cpu在处理业务时由于业务逻辑,和不可避免的数据库其他原因造成的前台进程等待,这里的等待事件包含buffer类,io类,以及网络类等等,当我们遇到等 ...

  8. 用【Unity】中的【3D Object】画【数学函数】图形 —— 正弦函数... { }

    效果 场景搭建 创建一个空物体,并将其命名为 "GameManager",并[Reset]它 创建一个 "Sphere",并将其[Scale]设置为(0.1,0 ...

  9. 监控linux多个cpu的负载情况

    监控linux多个cpu的负载情况 top然后按数字键1

  10. CMake | 将路径添加到 CMAKE_PREFIX_PATH

    1. CMAKE_PREFIX_PATH CMAKE_PREFIX_PATH是一个分号分隔的路径列表,用来指明软件/库安装路径前缀,以供find_package(),find_program(),fi ...