前言
大家好,我是阿光。
本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。
正在更新中~ ✨
🚨 我的项目环境:
- 平台:Windows10
- 语言环境:python3.7
- 编译器:PyCharm
- PyTorch版本:1.8.1
💥 项目专栏:【PyTorch深度学习项目实战100例】
一、LSTM自动AI作诗
本项目使用了LSTM作为模型实现AI作诗,作诗模式分为两种,一是根据给定诗句继续生成完整诗句,二是给定诗头生成藏头诗。
二、数据集介绍
数据来源于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
: 序号到字的映射 -
word2ix
: 字到序号的映射
三、算法流程介绍
1.输入数据为input,形状为 124 * 16
2.输入数据的label为 124 * 16
- 之后需要对输入数据进行嵌入,如果不嵌入那么每个古诗的字应为对应的索引,为了能够进行训练所以需要将其进行嵌入,然后形成连续性变量。
- 之后我们的数据就变成了 124 * 16 * embedding_dim
- 然后将其导入到LSTM模块中,则形成的形状为 124 * 16 * hidden_dim
- 之后将其导入到全连接层,形成分类,变为的形状为 124 * 16 ,vocab_size
注意
一定要清楚各个位置不同变量的形状,这些在代码中已经注明,一定要弄明白batch_size
,time_step
,embedding_dim
,vocal_size
,num_layers
,hidden_dim
以及input_size
在代码中的意义。
四、定义网络模型
项目中使用的模型是LSTM,在模型中我们定义了三个组件,分别是embedding层
,lstm层
和全连接层
。
- Embedding层:将每个词生成对应的
嵌入向量
,就是利用一个连续型向量来表示每个词 - Lstm层:提取诗句中的语义信息
- Linear层:将结果映射成
vocab_size
大小用于分类,即每个字的概率
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, num_layers)
self.linear = nn.Linear(hidden_dim, vocab_size)
def forward(self, x, hidden=None):
time_step, batch_size = x.size() # 124, 16
if hidden is None:
h_0 = x.data.new(num_layers, batch_size, self.hidden_dim).fill_(0).float()
c_0 = x.data.new(num_layers, batch_size, self.hidden_dim).fill_(0).float()
else:
h_0, c_0 = hidden
embeds = self.embeddings(x)
output, (h_n, c_n) = self.lstm(embeds, (h_0, c_0))
output = self.linear(output.reshape(time_step * batch_size, -1))
return output, (h_n, c_n)
五、给定首句生成古诗
该函数会根据给定的语句生成一段古诗,会根据start_words
继续生成下一个字,对于给定的语句生成相应的hidden
,然后将最后一个字和对应的hidden将其输入到模型中,生成新的下一个字,然后将新生成的字作为新的输入。
此外还可以加入诗句前缀
,加入的目的是会影响生成诗句的风格
,首先利用prefix_words
生成对应的hidden,然后将hidden送入模型生成诗句,此hidden中包含了前缀中的语义信息。
- start_words:给定的初始语句,基于它生成之后的诗句
- prefix_words:前缀诗句,该句会影响诗句的风格,因为首先会学习对应的hidden然后将其和开始词送入模型生成对应的诗句
六、生成藏头诗
生成藏头诗的原理与上述函数同理,只不过是利用给定的藏头字分别作为输入,一旦遇到特殊符号就说明该句生成结束,继续生成下一句藏头诗
同理这个函数也会包含诗句前缀,影响诗句的风格
七、模型训练
对于模型训练最重要的就是模型的输入和输出,针对于写诗这个任务,我们的输入应该是给定一句诗,然后错位1位作为它的标签用于监督,例如:
床前明月光疑是地上霜
那么我们的输入、输出就应该为:
输入数据:床前明月光疑是地上
输出数据:前明月光疑是地上霜
每个时间步对应的输出应该是他下个时间步对应的字
每个时间步的输出应该是vocab_size维度,就是词大小,用于全分类,这样就会根据概率获得该时间步对应的字。
由于本项目中采用的是唐诗数据集,数据为data: (57580,125)
的 numpy 数组,也就是每个样本为一句诗,每首诗中含有125个字,相当于不同的时间步,然后将每个字进行embedding进行编码,这里我们将输入作为124,也就是像上面所说,利用前124个字和他对应之后的124个字进行监督。
def train():
if use_gpu:
device = torch.device("cuda")
else:
device = torch.device("cpu")
# 获取数据
datas = np.load("tang.npz", allow_pickle=True)
data = datas['data']
ix2word = datas['ix2word'].item()
word2ix = datas['word2ix'].item()
data = torch.from_numpy(data)
dataloader = DataLoader(data,
batch_size=batch_size,
shuffle=True)
# 定义模型
model = LSTM(len(word2ix),
embedding_dim=embedding_dim,
hidden_dim=hidden_dim)
Configimizer = optim.Adam(model.parameters(), lr=lr)
criterion = nn.CrossEntropyLoss()
# if model_path:
# model.load_state_dict(torch.load(model_path,map_location='cpu'))
# 转移到相应计算设备上
model.to(device)
loss_meter = meter.AverageValueMeter()
# 进行训练
f = open('result.txt', 'w')
hidden = None
for epoch in range(epochs):
loss_meter.reset()
for li, data_ in tqdm.tqdm(enumerate(dataloader)):
# print(data_.shape) # 16 * 125 batch_size * time_step
data_ = data_.long().transpose(1, 0).contiguous() # 125 * 16 time_step * batch_size
# 注意这里,也转移到了计算设备上
data_ = data_.to(device)
Configimizer.zero_grad()
# n个句子,前n-1句作为输入,后n-1句作为输出,二者一一对应
input_, target = data_[:-1, :], data_[1:, :] # 都是124 * 16
output, hidden = model(input_, hidden) # 1984 * 8293 [batch_size * time_step, vocab_size] 124为时间步大小
# print("Here",output.shape)
# # 这里为什么view(-1)
# output:1984 * 8293
# target.view(-1):1984
# 可以直接放入,因为是交叉熵损失函数,target为标签,而output每个样本的输出为所有样本的类别概率
loss = criterion(output, target.view(-1))
loss.backward()
Configimizer.step()
loss_meter.add(loss.item())
# 进行可视化
if (1 + li) % plot_every == 0:
print("训练损失为%s" % (str(loss_meter.mean)))
f.write("训练损失为%s" % (str(loss_meter.mean)))
for word in list(u"春江花朝秋月夜"):
gen_poetry = ''.join(generate(model, word, ix2word, word2ix))
print(gen_poetry)
f.write(gen_poetry)
f.write("\n\n\n")
f.flush()
torch.save(model.state_dict(), '%s_%s.pth' % (model_prefix, epoch))
八、生成诗句
该函数用于进行测试生成语句,我们会首先加载我们训练好的模型,然后传入续写的诗句或者需要加入的前缀信息,形成诗句。
完整源码
【PyTorch深度学习项目实战100例】—— 使用pytorch实现LSTM自动AI作诗(藏头诗和首句续写)| 第6例_咕 嘟的博客-CSDN博客_pytorch基于 lstm 的自动写诗