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
- 一次训练选取的数据样本数
- batch_size的理解_bigheart的博客-CSDN博客_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