• 08-循环神经网络实现文本情感分类


    目录

    1.Pytorch中LSTM和GRU模块的使用

    1.1 LSTM的介绍

    1.2 LSTM使用示例api

    1.3 模型的损失函数

    1.4 双向LSTM

    1.5 LSTM和GRU的使用注意点

    2.使用LSTM完成文本情感分类

    2.1 修改模型


    1.Pytorch中LSTM和GRU模块的使用

    1.1 LSTM的介绍

    1.2 LSTM使用示例api

     

    1. """
    2. lstm的使用实例
    3. """
    4. import torch.nn as nn
    5. import torch
    6. batch_size=10
    7. seq_len=20#句子的长度
    8. vocab_size=100#词典的数量
    9. embedding_dim=30#用长度为30的向量表示一个词语
    10. hidden_size=18
    11. num_layer=2
    12. #构造一个batch的数据
    13. input=torch.randint(low=0,high=100,size=[batch_size,seq_len])#[10,20]
    14. #数据经过embedding处理
    15. embedding=nn.Embedding(vocab_size,embedding_dim=embedding_dim)
    16. input_embeded=embedding(input)#[10,20,30]
    17. #把embedding之后的数据传入lstm
    18. lstm=nn.LSTM(input_size=embedding_dim,hidden_size=hidden_size,num_layers=num_layer,batch_first=True,bidirectional=True)#bidirectional:双向LSTM
    19. output,(h_n,c_n)=lstm(input_embeded)
    20. print(output.size())#[10,20,18] ==>num_layer=2,则[10,20,18] ==>若bidirectional=True,则[10,20,18*2]
    21. print('*'*100)
    22. print(h_n.size())#[1*1,10,18] ==>num_layer=2,则[2*1,10,18] ==>若bidirectional=True,则[2*2,10,18]
    23. print('*'*100)
    24. print(c_n.size())#[1,10,18] ==>num_layer=2,则[2,10,18] ==>若bidirectional=True,则[4,10,18]
    25. #获取最后一个时间步上的输出
    26. last_output=output[:,-1,:]
    27. #获取最后一次的hidden_state
    28. last_hidden_state=h_n[-1,:,:]
    29. print(last_output==last_hidden_state)#全是1,说明是一样的
    30. #获取双向LSTM中正向的最后一个时间步的output
    31. #last_output=output[:,-1,:18]
    32. #反向
    33. last_output=output[:,0,18:]
    34. #获取双向LSTM中正向的最后一个hidden_state
    35. last_hidden_state=h_n[-2,:,:]
    36. #反向
    37. last_hidden_state=h_n[-1,:,:]
    38. #1:第一层的正向
    39. #-1:第一层的反向
    40. #1:第二层的正向
    41. #-1:第二层的反向
    42. print(last_hidden_state.eq(last_output))

    1.3 模型的损失函数

    1.4 双向LSTM

    1.5 LSTM和GRU的使用注意点

    2.使用LSTM完成文本情感分类

    2.1 修改模型

     

    lib.py内容如下:

    1. import pickle
    2. ws=pickle.load(open('./model/ws.pkl','rb'))
    3. max_len=200
    4. batch_size=512
    5. test_batch_size=1000
    6. hidden_size=128
    7. num_layers=2
    8. bidriectional=True
    9. dropout=0.4

    word_sequence.py内容如下:

    1. """
    2. 实现的是:构建字典,实现方法把句子转化为数字序列和其翻转
    3. """
    4. class Word2Sequence:
    5. UNK_TAG = 'UNK' # 不常见的单词 标记
    6. PAD_TAG = "PAD" # padding填充,即测试集中遇到新单词 标记
    7. UNK = 0
    8. PAD = 1
    9. def __init__(self):
    10. self.dict = {
    11. self.UNK_TAG: self.UNK,
    12. self.PAD_TAG: self.PAD
    13. }
    14. self.count = {} # 统计词频
    15. def fit(self, sentence):
    16. """
    17. 把单个句子保存到dict中
    18. :param sentence:[word1,word2,word3,...]
    19. :return:
    20. """
    21. for word in sentence:
    22. self.count[word] = self.count.get(word, 0) + 1
    23. def build_vocab(self, min=5, max=None, max_features=None):
    24. """
    25. 生成词典
    26. :param min:最小出现的次数
    27. :param max:最大出现的次数
    28. :param max_features:一共保留多少个词语
    29. :return:
    30. """
    31. # 删除count中词频小于min的word
    32. if min is not None:
    33. self.count = {word: value for word, value in self.count.items() if value > min}
    34. # 删除count中词频大于max的word
    35. if max is not None:
    36. self.count = {word: value for word, value in self.count.items() if value < max}
    37. # 限制保留的词语数
    38. if max_features is not None:
    39. temp = sorted(self.count.items(), key=lambda x: x[-1], reverse=True)[:max_features] # 降序,按照values值
    40. self.count = dict(temp) # 转换为字典
    41. for word in self.count:
    42. self.dict[word] = len(self.dict)
    43. # 得到一个翻转的字典
    44. self.inverse_dict = dict(zip(self.dict.values(), self.dict.keys()))
    45. def transform(self, sentence, max_len=None):
    46. """
    47. 把句子转化为数字序列
    48. :param sentence:[word1,word2,...]
    49. :param max_len:int,对句子进行填充或裁剪裁剪
    50. :return:
    51. """
    52. if max_len is not None:
    53. if max_len > len(sentence): # 填充
    54. sentence = sentence + [self.PAD_TAG] * (max_len - len(sentence))
    55. elif max_len < len(sentence): # 裁剪
    56. sentence = sentence[:max_len]
    57. return [self.dict.get(word, self.UNK) for word in sentence]
    58. def inverse_transform(self, indices):
    59. """
    60. 把序列转化为句子
    61. :param indices:[1,2,3,4,...]
    62. :return:
    63. """
    64. return [self.inverse_dict.get(idx) for idx in indices]
    65. def __len__(self):
    66. return len(self.dict)
    67. if __name__ == '__main__':
    68. # ws=Word2Sequence()
    69. # ws.fit(['我', '是', '谁'])
    70. # ws.fit(['我', '是', '我'])
    71. # ws.build_vocab(min=0)
    72. # print(ws.dict)
    73. # ret=ws.transform(['我','爱','北京'],max_len=10)
    74. # print(ret)
    75. # ret=ws.inverse_transform(ret)
    76. # print(ret)
    77. pass

     dataset.py内容如下:

    1. import jieba
    2. from keras.datasets import imdb#情感文本分类数据集
    3. import torch,os,re
    4. from lib import *#导入模型
    5. from torch.utils.data import DataLoader,Dataset
    6. def tokenlize(content):
    7. re.sub('<.*?>',' ',content)#将特殊字符替换成空格
    8. fileters=["\.",":",'\t','\n','\x97','\x96','#','$','%','&']#删去这些字符
    9. content=re.sub('|'.join(fileters),' ',content)
    10. tokens=[i.strip() for i in content.split()]
    11. return tokens
    12. class ImdbDataset(Dataset):
    13. def __init__(self,train=True):
    14. self.train_data_path=r'D:\各种编译器的代码\pythonProject12\机器学习\NLP自然语言处理\datas\IMDB文本情感分类数据集\aclImdb\train'
    15. self.test_data_path=r'D:\各种编译器的代码\pythonProject12\机器学习\NLP自然语言处理\datas\IMDB文本情感分类数据集\aclImdb\test'
    16. data_path=self.train_data_path if train else self.test_data_path
    17. #1.把所有的文件名放入列表
    18. temp_data_path=[os.path.join(data_path,'pos'),os.path.join(data_path,'neg')]#不需要\符号了 两个文件夹
    19. self.total_file_path=[]#所有评论文件的path
    20. for path in temp_data_path:
    21. file_name_list=os.listdir(path)
    22. file_path_list=[os.path.join(path,i) for i in file_name_list if i.endswith('.txt')]#当前文件夹中所有的文件名字
    23. self.total_file_path.extend(file_path_list)#正例 负例文件名字都在
    24. def __getitem__(self, index):
    25. file_path=self.total_file_path[index]
    26. #获取label
    27. label_str=file_path.split("\\")[-2]
    28. label=0 if label_str=='neg' else 1#文本类型数字化
    29. #获取内容
    30. tokens=tokenlize(open(file_path,'r',encoding='utf8').read())
    31. return tokens,label
    32. def __len__(self):
    33. return len(self.total_file_path)
    34. def collate_fn(batch):
    35. """
    36. :param batch:([tokens,label],[tokens,label]...)
    37. :return:
    38. """
    39. content,label=zip(*batch)
    40. content=[ws.transform(i,max_len=max_len) for i in content]
    41. content=torch.LongTensor(content)
    42. label=torch.LongTensor(label)
    43. return content,label
    44. def get_dataloader(train=True,batch_size=batch_size):
    45. imdb_dataset=ImdbDataset(train)
    46. data_loader=DataLoader(imdb_dataset,batch_size=128,shuffle=True,collate_fn=collate_fn)
    47. return data_loader
    48. if __name__ == '__main__':
    49. for idx,(input,target) in enumerate(get_dataloader()):
    50. print(idx)
    51. print(input)
    52. print(target)
    53. break

    main.py内容如下:

    1. from word_sequence import Word2Sequence
    2. import pickle,os
    3. from dataset import tokenlize
    4. from tqdm import tqdm#打印可迭代对象的进度条
    5. if __name__ == '__main__':
    6. ws = Word2Sequence()
    7. path = r'D:\各种编译器的代码\pythonProject12\机器学习\NLP自然语言处理\datas\IMDB文本情感分类数据集\aclImdb\train'
    8. temp_data_path = [os.path.join(path, 'pos'), os.path.join(path, 'neg')] # 不需要\符号了 两个文件夹
    9. for data_path in temp_data_path:
    10. file_paths=[os.path.join(data_path,file_name) for file_name in os.listdir(data_path) if file_name.endswith('.txt')]
    11. for file_path in tqdm(file_paths):
    12. sentence=tokenlize(open(file_path,'r',encoding='utf8').read())
    13. ws.fit(sentence)
    14. ws.build_vocab(min=10,max_features=10000)
    15. pickle.dump(ws,open(r'./model/ws.pkl','wb'))#保存ws
    16. print(len(ws))

    model.py内容如下:

    1. '''
    2. 定义模型
    3. 模型优化方法:
    4. 添加一个新的全连接层作为输出层,激活函数处理
    5. 把双向的lstm的结果output传给一个单向的LSTM再进行处理
    6. '''
    7. import torch,os
    8. import torch.nn as nn
    9. import torch.nn.functional as F
    10. from torch.optim import Adam
    11. from dataset import get_dataloader
    12. from lib import *
    13. import numpy as np
    14. from tqdm import tqdm#打印进度条
    15. class MyModel(nn.Module):
    16. def __init__(self):
    17. super(MyModel,self).__init__()
    18. self.embedding=nn.Embedding(len(ws),100)
    19. #加入LSTM
    20. self.lstm=nn.LSTM(input_size=100,hidden_size=hidden_size,num_layers=num_layers,
    21. batch_first=True,bidirectional=bidriectional,dropout=dropout)
    22. self.fc=nn.Linear(hidden_size*2,2)
    23. def forward(self,input):
    24. """
    25. :param input:[batch_size,max_len]
    26. :return:
    27. """
    28. x=self.embedding(input) #进行embedding操作,形状:[batch_size,max_len,100]
    29. #x:[batch_size,max_len,2*hidden_size],h_n:[2*2,batch_size,hidden_size]
    30. x,(h_n, c_n)=self.lstm(x)
    31. #获取两个方向最后一次的output,进行concat操作
    32. output_fw=h_n[-2,:,:]#正向最后一次的输出
    33. output_bw=h_n[-1,:,:]#反向最后一次的输出
    34. output=torch.concat([output_bw,output_fw],dim=-1)#[batch_size,hidden_size*2]
    35. out=self.fc(output)
    36. return F.log_softmax(out,dim=-1)
    37. model=MyModel()
    38. optimizer=Adam(model.parameters(),lr=0.001)
    39. if os.path.exists('./model/model.pkl'):
    40. model.load_state_dict(torch.load('./model/model.pkl'))
    41. optimizer.load_state_dict(torch.load('./model/optimizer.pkl'))
    42. def train(epoch):
    43. for idx,(input,target) in enumerate(get_dataloader(train=True)):
    44. optimizer.zero_grad()#梯度归0
    45. output=model(input)
    46. loss=F.nll_loss(output,target)
    47. loss.backward()
    48. optimizer.step()
    49. print(epoch,idx,loss.item())
    50. if idx%100==0:
    51. torch.save(model.state_dict(),'./model/model.pkl')
    52. torch.save(optimizer.state_dict(),'./model/optimizer.pkl')
    53. def eval():
    54. loss_list=[]#损失列表
    55. acc_list=[]#准确率列表
    56. for idx,(input,target) in enumerate(get_dataloader(train=False,batch_size=test_batch_size)):
    57. with torch.no_grad():
    58. output=model(input)
    59. cur_loss=F.nll_loss(output,target)
    60. loss_list.append(cur_loss)
    61. #计算准确率
    62. pred=output.max(dim=-1)[-1]
    63. cur_acc=pred.eq(target).float().mean()
    64. acc_list.append(cur_acc)
    65. print('totol loss,acc:',np.mean(loss_list),np.mean(acc_list))
    66. if __name__ == '__main__':
    67. for i in range(1):
    68. train(i)

    运行model.py文件即可。运行时间可能比较长,耐心等待即可。

  • 相关阅读:
    强强联手,NVIDIA 与 Ampere Computing 重磅推出云原生服务器平台
    Java 基础常见知识点&面试题总结(下),2022 最新版!
    【Vue学习之从入门到神经】
    SpringMVC的概念和使用以及bean加载控制
    python+vue企业人力资源管理系统django569
    大数据-消息队列:Pulsar
    Linux—进程间通信之System V共享内存
    10月11日,每日信息差
    计算机网络概述
    迎重阳,话养老:平安养老险如何助力国民“养老梦”?
  • 原文地址:https://blog.csdn.net/m0_58086930/article/details/127243132