实验环境
- Pytorch 1.4.0
- conda 4.7.12
- Jupyter Notebook 6.0.1
- Python 3.7
数据集介绍
chinese-poetry: 最全中文诗歌古典文集数据库
最全的中华古典文集数据库,包含 5.5 万首唐诗、26 万首宋诗、2.1 万首宋词和其他古典文集。诗 人包括唐宋两朝近 1.4 万古诗人,和两宋时期 1.5 千古词人。数据来源于互联网。
实验使用预处理过的二进制文件 tang.npz 作为数据集,含有 57580 首唐诗,每首诗限定在 125 词, 不足 125 词的以 填充。数据集以 npz 文件形式保存,包含三个部分:
- data: (57580,125) 的 numpy 数组,总共有 57580 首诗歌,每首诗歌长度为 125 字符 (不足 125 补空格,超过 125 的丢弃),将诗词中的字转化为其在字典中的序号表示
- ix2word: 序号到字的映射,每个序号和它对应的词,例如序号 898 对应“雪”
- word2ix: 字到序号的映射,每个字和它对应的序号,例如“雪”对应序号是 898
训练过程
数据准备
首先,导入实验所需的库,定义一些宏参数,BATCH_SIZE 表示每个 batch 加载多少个样本、 EPOCHS 表示总共训练批次。如果支持 cuda 就用 gpu 来 run,不支持就用 cpu 来 run。
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import time
import matplotlib.pyplot as plt
from torch.optim.lr_scheduler import *
from torch.utils.data import DataLoader
from torch.autograd import Variable
BATCH_SIZE = 16
EPOCHS = 4
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
我先读取预处理好的数据,再将其转为 Tensor,这次没有使用 Dataset 去封装 data,但是它还是 可以利用 Dataloader 进行多线程加载,因为 data 作为一个 Tensor 对象,自身已经实现了 getitem 和 len 方法。接着构造数据提供器,shuffle=True 设置在每个 epoch 重新打乱数据,保证数据的随机性;还有多线程加载数据。处理好了训练数据。
# 读入预处理的数据
datas = np.load("./tang.npz")
data = datas['data']
ix2word = datas['ix2word'].item()
word2ix = datas['word2ix'].item()
# 转为torch.Tensor
data = torch.from_numpy(data)
train_loader = DataLoader(data, batch_size = BATCH_SIZE, shuffle = True, num_workers = 2)
网络配置
定义一个循环神经网络,输入的字词序号经过 nn.Embedding 得到相应词的词向量表示,然后利用 3 层 LSTM 提取词的所有隐藏元信息,再利用隐藏元的信息进行分类,判断输出属于每一个词的概率。 然后通过全连接的输出层将词向量升维回字词序号,全连接输出层所有激活函数都使用 ReLU 函数。
class PoetryModel(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_dim):
super(PoetryModel, self).__init__()
self.hidden_dim = hidden_dim
self.embedding = nn.Embedding(vocab_size, embedding_dim)
self.lstm = nn.LSTM(embedding_dim, self.hidden_dim, num_layers=3)
self.classifier=nn.Sequential(
nn.Linear(self.hidden_dim, 512),
nn.ReLU(inplace=True),
nn.Linear(512, 2048),
nn.ReLU(inplace=True),
nn.Linear(2048, vocab_size)
)
def forward(self, input, hidden = None):