文章目录
- 1.数据集
- 2.数据读取和预处理
- 番外:语言模型初窥
- 3.定义网络
- 4.训练模型
- 5.测试模型
- 6.可视化
1.数据集
由于计算资源的限制,本文选取的是英文小说《小王子》来进行训练。而且只选取了其5/25章来训练,模型可谓相当小,但是麻雀虽小五脏俱全。
数据集下载(已经设置了不需要积分):。
本文语言模型效果:
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)
至此,dataloader已经有了,就是上面的train_iter等,我们只需要用这个,val_iter什么的不用。
帮助:
- 你需要深究的话,应该去看看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不一样。
帮助:
- 上面的forward中的参数hc0只有语言模型会用到,测试网络需要用,平常的NLP任务用LSTM的时候,没有这个参数。
- 具体这个参数干嘛用,你到了测试模型的时候仔细看代码就知道什么用了。
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()))
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)
还行吧,起码语法和习惯过关。
6.可视化
import matplotlib.pyplot as plt
plt.plot(losses)
plt.ylim(ymin=3, ymax=7)
plt.title("The accuracy of LSTM model")
plt.show()
可以看到,如果训练久一点,训练数据加大一点,效果会更好。