使用LSTM来完成文本情感分类最终篇

LSTM和GRU的注意点:

  • 第一次调用之前,需要初始化隐藏状态,如果不初始化,默认创建全为0的隐藏状态
  • 往往使用LSTM or GRU的输出最后一维的结果,来代表LSTM、GRU对文本处理的结果,其形状为[batch_size,num_directions*hidden_size]
    • 并不是所有模型都会使用最后一维的结果
    • 如果实例化LSTM的过程中,batch_size=False,则output[-1] or output[-1,:,: ]可以获取最后一维
    • 如果实例化LSTM的过程中,batch_size=False,则output[:,-1,:]可以获取最后一维
  • 如果结果是(seq_len,batch_size,num_directions*hidden_size),则需要把他转换为(batch_size,seq_len,num_directions*hidden_size)的形状,使用output.permute(1,0,2)
  • 使用双向LSTM的时候,往往会分别使用两个方向的最后一次的output,最为当前数据经过双向LSTM的结果     即
    • torch.cat(h_n[-2,:,:],h_n[-1,:,:])#正向/反向
    • 最后表示的size是[batch_size,hidden_size*2]

使用LSTM来完成文本情感分类:

torch.device()
  • torch.device代表将torch.Tensor分配到的设备的对象,有cpu和cuda两种
输入nvidia-smi
  • 查看cuda版本

with torch.no_grad():

  • 一般要停止跟踪历史记录(和使用内存),可以将代码块使用 with torch.no_grad(): 包装起来。
    在评估模型时,这是特别有用,因为模型在训练阶段具有 requires_grad = True 的可训练参数有利于调参,但在评估阶段我们不需要梯度。
torch.max(tensor,dim=0/1)[0\1]
  • 前面一个0是指找出他的列的最大值返回其索引,1是指找出行的最大值返回其索引
  • 后一个0/1是指他这个输出的的是两个·tensor,第0个tensor是每行的最大值组成的tensor,第1个tensor是每行的最大值的标签组成的tensor
batch_size

还有就是大家要注意这个cuda的版本和pytorch的版本是否一致

dataset.py:

#1、完成数据集的准备
from torch.utils.data import  Dataset,DataLoader
import os
import re
from lib import ws,max_len
import torch
import lib
def tokenlize(content):#分词
    tokens=[]
    fileters=['\t','\n','\x97','\x96','#','<.*?>']
    content=re.sub("|".join(fileters),' ',content)#为什么要用|.join我也不是很清楚
    tokens.extend(i.strip() for i in content.split())
    return  tokens
class ImdDataset(Dataset):
    def __init__(self,train=True):
        self.train_data_path= r"E:/python eye/project2/emation/aclImdb/train"
        self.test_data_path= r"E:/python eye/project2/emation/aclImdb/test"
        data_path=self.train_data_path if train else self.test_data_path
        #把所有文件名放入列表
        temp_data_path=[os.path.join(data_path,"pos"),os.path.join(data_path,"neg")]
        self.total_file_path=[]#所有评论的文件
        for path in temp_data_path:
            file_name_path=os.listdir(path)
            file_path_list=[os.path.join(path,i) for  i in file_name_path if i.endswith(".txt")]
            self.total_file_path.extend(file_path_list)
    def __getitem__(self, index):#有这个方法你才可以用
        file_path= self.total_file_path[index]
        file_path="".join(file_path)
        label_str=file_path.split('\\')[-2]
        label=0 if label_str =="neg" else 1
        #获取内容
        content=tokenlize(open(file_path,encoding='utf-8').read())
        return content,label
    def __len__(self):
        return len(self.total_file_path)
def collate_fn(batch):
    content,label=list(zip(*batch))
    content=[ws.transfrom(i,max_len=max_len)for i in content]
    content=torch.LongTensor(content)
    label=torch.LongTensor(label)
    return  content,label
def get_dataloader(train=True,batch_size=lib.batch_size):
    imdb_dataset=ImdDataset(train)
    data_loader=DataLoader(imdb_dataset,batch_size=batch_size,shuffle=True,collate_fn=collate_fn)
    return data_loader
# for idx,(input,target) in enumerate(get_dataloader()):
    # print(idx)
    # print(input)
    # print(target)
    # break







word.py :

import os
class word :
    UNK_TAG = "UNK"
    PAD_TAG = "PAD"
    UNK = 0
    PAD = 1
    def __init__(self):
        '''
        1、特殊字符存没有出现的词语
        2、对短句子进行填充
        '''
        self.dict={
            self.UNK_TAG:self.UNK,
            self.PAD_TAG:self.PAD
        }
        self.count={}
    def fit(self,sentence):
        '''
        把单个句子保存到词典中去
        '''
        for word in sentence:
            self.count[word]=self.count.get(word,0)+1
    def build_vocab(self,min=5,max=None,max_features=None):
        """
        生成词典
        :param min: 词语最小出现的次数
        :param max: 词语最大出现的次数
        :param max_features: 一共保留多少个词语
        :return:
        """
        self.count={word:value for word,value in self.count.items() if value>min}#当他的次数大于min时候进行保留
        self.count = {word: value for word, value in self.count.items() if value <max}#当他的次数小于max进行保留
        sorted(self.count.items(),key=lambda x:x[1],reverse=True)[:max_features]
        for ward in self.count:
            self.dict[ward]=len(self.dict)
        #得到一个翻转的dict的字典
        self.inverse_dict=dict(zip(self.dict.values(),self.dict.keys()))
    def transfrom(self,sentence,max_len=None):
        """
        将句子转换为一个序列
        :param sentence:
        :return:
        """
        if len(sentence) is not max_len:
            if max_len > len(sentence):
                sentence=sentence+[self.PAD_TAG]*(max_len-len(sentence))
            if max_len < len(sentence):
                sentence=sentence[:max_len]
        return [self.dict.get(ward,self.UNK) for ward in sentence]
    def inverse_transfrom(self,indices):
        """
        将序列转换成一个句子
        :param indices:
        :return:
        """
        return [self.inverse_dict.get(idx) for idx in indices]
    def __len__(self):
        return len(self.dict)






model.py: 

import torch.nn as nn
import torch.optim
import os
import numpy as np
import numpy
from tqdm import tqdm
'''
定义模型
'''
from lib import  ws,max_len,hidden_size
from  torch import  optim
import torch.nn.functional as F
from dataset import get_dataloader
import lib
class my_model(nn.Module):
    def __init__(self):
        super(my_model, self).__init__()
        self.embedding=nn.Embedding(len(ws),100)#句子最大长度
        #加入LSTM
        self.lstm=nn.LSTM(input_size=100,hidden_size=hidden_size,num_layers=lib.num_layer,
                batch_first=True,bidirectional=lib.bidriectional,dropout=lib.dropout)
        self.fc1=nn.Linear(lib.hidden_size*2,2)
    def forward(self,input):
        x=self.embedding(input)
        x,(h_n,c_n)=self.lstm(x)
        '''
        x:[batch_size,max_len,2*hidden_size]
        h_n:[num_layers*num_directions,batch_size,hidden_size]
        '''
        output=torch.cat([h_n[-2,:,:],h_n[-1,:,:]],dim=-1)#[batch_size,hidden_size*2]
        out=self.fc1(output)
        return F.log_softmax(out,dim=-1)
model1=my_model().to(lib.device)
optimizer=optim.Adam(model1.parameters(),0.001)
if os.path.exists("./model/model.pkl"):
    model1.load_state_dict(torch.load("E:/python eye/project2/NLP/model/model.pkl"))
    optimizer.load_state_dict(torch.load("E:/python eye/project2/NLP/model/optimizer.pkl"))
def train():
    for idx,(input,target) in enumerate(get_dataloader()):
        #梯度为0
        input=input.to(lib.device)
        target=target.to(lib.device)

        optimizer.zero_grad()
        output=model1(input)
        loss=F.nll_loss(output,target)
        loss.backward()#梯度返回
        optimizer.step()
        print(idx,loss.item())
        if idx%100==0:
            torch.save(model1.state_dict(), "E:/python eye/project2/NLP/model/model.pkl")
            torch.save(optimizer.state_dict(),"E:/python eye/project2/NLP/model/optimizer.pkl")
def eval():#魔性的评估
    loss_list=[]#所有损失
    acc_list=[]#准确率
    for idx,(input,target) in tqdm(enumerate(get_dataloader(False,batch_size=lib.test_batch_size))):
        input=input.to(lib.device)#到gpu上面
        target=target.to(lib.device)
        with torch.no_grad():
            output=model1(input)
            cur_loss=F.nll_loss(output,target)
            loss_list.append(cur_loss.cpu().item())
             #计算准确率
            pred=output.max(1)[1]#不是很理解
            curacc=pred.eq(target).float().mean()
            acc_list.append(curacc.cpu().item())#转成cpu类型
    print("total loss,acc:",np.mean(loss_list),np.mean(acc_list))
if __name__=='__main__':
    eval()


 lib.py

都是参数

import pickle
import torch
ws=pickle.load(open("../model/ws.pkl", "rb"))
max_len=200
hidden_size=512
num_layer=3
bidriectional=True
dropout=0.4
device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
batch_size=128
test_batch_size=4

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小李小于

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值