首先简单实现构造LSTM模型以及使用LSTM进行计算,代码如下
import torch
import torch.nn as nn
class rnn(nn.Module):
def __init__(self,input_dim,output_dim,num_layer):
super(rnn,self).__init__()
self.layer1 = nn.LSTM(input_dim,output_dim,num_layer)
def forward(self,input):
output,hidden=self.layer1(input)#LSTM的输出包括隐藏层计算结果以及隐藏层状态,因此=左边必须是两个值
return output,hidden
if __name__=="__main__":
#实例化
encoder = rnn(input_dim = 10, output_dim = 5, num_layer = 2)
#创建输入
input = torch.randn(20,15,10)#参数分别代表sequence_length,batch_size,word_embedding
#计算
output,hidden=encoder(input)
print(output.shape)#输出结果的维度为sequence_length,batch_size,output_dim(最后一层神经网络的输出层维度)
print(hidden[0].shape)#输出结果的维度为num_layer,batch_size,output_dim
个人疑惑小结:
1)numlayer代表LSTM的堆叠层数,该值为2时,则将第一层LSTM的输出作为第二层LSTM 的输入继续计算
2)bidirectional是决定每一层LSTM是否是双向的,当LSTM为双向模型时,最终每个token的输出向量为正反向向量的拼接,例如设置的隐藏层维度为100维,经双向LSTM计算后,每个token的维度为200维。
3)LSTM会返回output和hidden两个值,其中output包含输入序列中每个字的输出向量,最后一个字的输出向量代表整句话的语义向量;hidden是一个元组,元组中的第一个元素代表每个字的隐藏层向量,第二个元素代表所需遗忘的元素,有一个问题就是output和hidden[0]的数据维度为什么不一致??
#---------分割线----------
后续解答:output是包含所有字的语义向量,其维度为[batch_size,seq_len,hidden_size],hidden[0]代表整句话的语义向量,其维度为[1,batch_size,hidden_size]。最近在看机器翻译的相关内容,突然理解到output中每个字的语义向量就是每个时间步的向量。
4)RNN的输出和LSTM类似,同样包含output和hidden两部分,output仍然代表每个字最终的输出向量,不同之处在于hidden不再是一个元组,只有一个元素代表最后一个字的输出向量。
在实例化LSTM时需要指定模型内部结构的固有的输入层维度,隐藏层维度等参数,而在运行模型时需要指定句子长度,batch_size等参数
在实际文本分类的应用过程中存在其他一些细节问题,接下来记录使用LSTM进行文本分类的代码
import torch
import torch.nn as nn
import numpy as np
import torch.nn.functional as F
from torch.nn import CrossEntropyLoss
from torch.utils.data import TensorDataset,DataLoader,SequentialSampler
class model(nn.Module):
def __init__(self,input_dim,hidden_dim,num_layer,num_class,batch_first = True):
super(model,self).__init__()
self.layer1 = nn.LSTM(input_dim,hidden_dim,num_layer)
self.layer2 = nn.Linear(hidden_dim,num_class)
def forward(self,inputs):
layer1_output,layer1_hidden = self.layer1(inputs)
layer2_output = self.layer2(layer1_output)
layer2_output = layer2_output[:,-1,:]#取出一个batch中每个句子最后一个单词的输出向量即该句子的语义量!!!!!!!,layer2_output的维度是[batch_size,sequence_length,embedding_dim],layer2_output[:,-1,:]-1表示第二个维度sequence_length的最后一个数据也就是一句话中最后一个字的语义向量。
return layer2_output
#-------或者使用隐藏层向量作为线性层的输入-------
layer1_output,layer1_hidden = self.layer1(inputs)
layer2_output = self.layer2(layer1_hidden)
return layer2_output
def fill_sentence(embeddings,embedding_dim):
'''
由于句子长短不一致,本函数对句子中的字转换成向量以后进行补齐(用0填补)
'''
fill_embeddings = []
length = [len(embedding) for embedding in embeddings]
max_len = max(length)
for embedding in embeddings:
if len(embedding) < max_len:
fill_zero = np.zeros((max_len-len(embedding),embedding_dim))
fill_embedding = np.append(embedding,fill_zero)
fill_embedding = fill_embedding.reshape(-1,embedding_dim)
fill_embeddings.append(fill_embedding)
else:
fill_embeddings.append(embedding)
return np.array(fill_embeddings)
def create_dataset(tesnsor_data,tensor_label):
dataset = TensorDataset(tesnsor_data,tensor_label)
return dataset
def create_dataloader(dataset,sampler,batch_size):
dataloader = DataLoader(dataset = dataset,sampler = sampler,batch_size = batch_size)
return dataloader
def create_tensors(embedding_dim):
data = ['今天去看展览','今天加班','中午刚睡醒','明天休息']
labels = [0,1,0,0]
embeddings = []
for line in data:
embeddings.append(np.random.rand(len(line),embedding_dim))#此处随机生成字向量,可以换成加载glove等训练好的词向量
new_embeddings = fill_sentence(embeddings,embedding_dim)
tensor_data = torch.tensor(new_embeddings,dtype = torch.float32)
tensor_label = torch.tensor(np.array(labels),dtype = torch.int64)
return tensor_data,tensor_label
def train(lstm_model,dataloader):
loss_fun = CrossEntropyLoss()
for step,batch in enumerate(dataloader):
inputs = batch[0]
true_labels = batch[1]
model_output = lstm_model(inputs)
softmax_output = F.softmax(model_output,dim=-1)
loss = loss_fun(model_output,true_labels)#交叉熵损失函数的输入model_output是模型预测各类别的概率,即softmax计算后的值。
loss.backward()#先写到这里,后续再补充!!
print(loss)
def run():
tensors_data,tensors_label = create_tensors(5)
dataset = create_dataset(tensors_data,tensors_label)
sampler = SequentialSampler(dataset)
dataloader = create_dataloader(dataset = dataset,sampler = sampler,batch_size = 2)
lstm_model = model(input_dim = 5,hidden_dim = 20,num_layer = 1,num_class = 2)
train(lstm_model,dataloader)
run()