文章目录

  • 1.数据集
  • 2.数据读取和预处理
  • 番外:语言模型初窥
  • 3.定义网络
  • 4.训练模型
  • 5.测试模型
  • 6.可视化


1.数据集

由于计算资源的限制,本文选取的是英文小说《小王子》来进行训练。而且只选取了其5/25章来训练,模型可谓相当小,但是麻雀虽小五脏俱全。

数据集下载(已经设置了不需要积分):。

本文语言模型效果:

pytorch使用lstm预测时序数据_数据集

2.数据读取和预处理

没有下载好torchtext的,自己pip install 一下。

from torchtext.legacy import data,datasets
from torchtext.vocab import Vectors
import torch
import numpy as np
import random

# 为了保证实验结果可以复现,我们经常会把各种random seed固定在某一个值
random.seed(53113)
np.random.seed(53113)
torch.manual_seed(53113)


BATCH_SIZE = 32
EMBEDDING_SIZE = 50
MAX_VOCAB_SIZE = 10000
device="cpu"
HIDDEN_SIZE=50
TEXT = data.Field(lower=True)
train, val, test = datasets.LanguageModelingDataset.splits(path="data/", 
    train="littileprince1-5.txt", validation="littileprince1-5.txt", test="littileprince1-5.txt", text_field=TEXT)
TEXT.build_vocab(train, max_size=MAX_VOCAB_SIZE)
print("vocabulary size: {}".format(len(TEXT.vocab)))

VOCAB_SIZE = len(TEXT.vocab)
train_iter, val_iter, test_iter = data.BPTTIterator.splits(
    (train, val, test), batch_size=BATCH_SIZE, device=device, bptt_len=30, repeat=False, shuffle=True)

pytorch使用lstm预测时序数据_数据读取_02


至此,dataloader已经有了,就是上面的train_iter等,我们只需要用这个,val_iter什么的不用。

帮助:

  1. 你需要深究的话,应该去看看torchtext怎么用。上面基本是torchtext的用法,没什么好讲的,所以一下子就构造好了数据集。

番外:语言模型初窥

原文是:
机器学习你好啊,语言模型好啊<eos>
经过我们的处理,其实不使用上述torchtext工具,你也应该自己这么处理。
1.将文章切成很多个句子(这个句子未必是语法上严格的句子,比如可以直接是每10个字称作一个句子。)
句子1:机器学习你好啊
句子2: ,语言模型好啊
那么上述两个句子的语言模型对应的标签为:
标签1:器学习你好啊,
标签2:语言模型好啊<eos>
当然了,由于我们都是批处理的,所以上述两个句子可能都会在一个批里,如果你的批大小为2的话。
另外,loss计算也是根据两个句子(一个批)的预测损失相加,然后更新权重。并不是每预测一个字更新一次。

3.定义网络

import torch.nn as nn
import torch
import torch.nn.functional as F
import torch.optim as optim
class LSTM(nn.Module):
    def __init__(self, vocab_size, embedding_dim, hidden_dim):
        super(LSTM, self).__init__()
        self.hidden_dim = hidden_dim
        self.embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.lstm = nn.LSTM(embedding_dim, hidden_dim,batch_first=True)
        self.linear = nn.Linear(hidden_dim, vocab_size)
        
    def forward(self, input,hc0=None):
        #h0:指的是给定的输入句子,其会转化成这个h0和c0,作为起始的hc0。hc0=(h0,c0)
        if hc0==None:
            hc0=gethc0(input)
        batch_size, seq_len = input.size()
        embeds = self.embeddings(input).float() # [batch, seq_len] => [batch, seq_len,embed_dim]
        output, hc = self.lstm(embeds,hc0)#output:batch, seq_len,hidden_dim
        output = self.linear(output)##output:batch, seq_len,vocab_size
        #output:batch*seq_len,vocab_size
        return output,hc
    
        #hc本来的确不需要,但是如果给定了几个首句,我们就需要这个东西,然后再开始预测。
        #linear层其实对每一个字从hidden_size变成vocab_size,这个参数都是一样的,并不是序列中的位置1和位置2不一样。

帮助:

  1. 上面的forward中的参数hc0只有语言模型会用到,测试网络需要用,平常的NLP任务用LSTM的时候,没有这个参数。
  2. 具体这个参数干嘛用,你到了测试模型的时候仔细看代码就知道什么用了。

4.训练模型

model=LSTM(vocab_size=VOCAB_SIZE, embedding_dim=EMBEDDING_SIZE, hidden_dim=HIDDEN_SIZE)
lr=1e-4
optimizer=optim.Adam(model.parameters(),lr=lr)

loss_fn=nn.CrossEntropyLoss()
epochs=1000
#记录训练误差。
losses=[]
for i in range(epochs):
    model.train()
    for idx,batch in enumerate(train_iter):

        output,hc=model(batch.text,None)
        
        loss=loss_fn(output.view(-1,VOCAB_SIZE),batch.target.view(-1))
        
        optimizer.zero_grad()
        
        loss.backward()
        
        optimizer.step()
        if i%50==1:
            losses.append(loss.item())
            print("epoch:{},loss:{}".format(i,loss.item()))

pytorch使用lstm预测时序数据_语言模型_03

5.测试模型

def gethc0(x):
    return (x.new_zeros((1,x.shape[0],HIDDEN_SIZE),requires_grad=False).float(),
            x.new_zeros((1,x.shape[0],HIDDEN_SIZE),requires_grad=False).float())
def sentence2id(sentence):
    ids=[]
    for i in range(len(sentence)):
        ids.append(TEXT.vocab.stoi[sentence[i]])
    return ids
def test(start_sentence=["<START>"],poem_len=20):
    #输入start_id,并得到最后一个hidden,cell,然后递归传入。
    #指定需要生成多少句诗词。
    
    #转化为id形式。
    poempre=sentence2id(start_sentence)

    #将poempre转化成标准的shape形式#batch_size,seq_len
    poempre=torch.tensor(poempre).unsqueeze(0)
    
    print("给定的句子为:",end=" ")
    for i in range(len(start_sentence)):
        print(start_sentence[i],end=" ")
    print()
    print("续写如下:")    
    for i in range(len(start_sentence)):
        print(start_sentence[i],end=" ")
    model.eval()
    output,hc=model(poempre,None)
    #output:batch*seq_len,vocab_size
    #hc:batch*1*hidden_size
    # 利用hc作为初始状态,利用上一个输出作为这一次的输入。
    for i in range(poem_len):

        #注意不要搞错了,这里的output并不是lstm的output,而是整个网络的output,但是这个hc确实lstm的最后隐状态。
        pred_id=np.argwhere(output==torch.max(output)).max().item()
        print(TEXT.vocab.itos[pred_id],end=" ")
        #需要重新构造input了。#batch_size,seq_len


        new_input=torch.tensor([pred_id]).unsqueeze(0)
        model.eval()
        output,hc=model(new_input,hc)
    print()
    print("生成完毕")
s="It is"
start_sentence=s.split()
poem_len=10
test(start_sentence,poem_len)

pytorch使用lstm预测时序数据_数据集


还行吧,起码语法和习惯过关。

6.可视化

import matplotlib.pyplot as plt
plt.plot(losses)
plt.ylim(ymin=3, ymax=7)

plt.title("The accuracy of LSTM model")
plt.show()

pytorch使用lstm预测时序数据_数据集_05


可以看到,如果训练久一点,训练数据加大一点,效果会更好。