本次复现较为顺利,没有什么bug。
训练Train
训练了两次,下图右侧生成的效果明显好于上次构建的Tensorflow模型。不知为何train和生成是同时进行?
亲测训练实验:
改main函数:
1,批量修改cuda改成cpu
2,epoch改成1
3,在main函数下面添加语句train()
4,执行main.py函数
打开visdom server出现问题,解决方法如下:
小白安装Visdom简明教程_zealwx的博客-CSDN博客
其中,修改的server.py没找到,于是修改了run_server.py。
Statics作者百度网盘分享链接也失效了。于是没有执行statics替换那步。不过直接解决了visdom无法打开的问题
Generate生成
# coding:utf8
import sys, os
import torch as t
from torch.utils.data import DataLoader
from data import get_data
from model import PoetryModel
from torch import nn
from utils import Visualizer
import tqdm
from torchnet import meter
import ipdb
class Config(object):
data_path = 'data' # 诗歌的文本文件存放路径
pickle_path = 'data/tang.npz' # 预处理好的二进制文件
lr = 1e-3
weight_decay = 1e-4
use_gpu = False
epoch = 50
env = 'poetry1' # visdom env
batch_size = 128
maxlen = 125 # 超过这个长度之后的字被丢弃,小于这个长度的在前面补空格
max_gen_len = 200 # 生成诗歌最长长度
model_path = 'checkpoints/tang_5.pth' # 预训练模型路径
start_words = '美丽与智慧的月儿' # 诗歌开始
model_prefix = 'checkpoints/tang' # 模型保存路径
plot_every = 5 # 每20个batch 可视化一次
debug_file = '/tmp/debugp/'
opt = Config()
def gen(**kwargs):
"""
给定几个词,根据这几个词接着生成一首完成的诗词
例如,start_words为'海内存知己',可以生成
海内存知己,天涯尚未安。
故人归旧国,新月到新安。
海气生边岛,江声入夜滩。
明朝不可问,应与故人看。
"""
for k, v in kwargs.items():
setattr(opt, k, v)
device = t.device('cpu') if opt.use_gpu else t.device('cpu')
data, word2ix, ix2word = get_data(opt)
model = PoetryModel(len(word2ix))
model.load_state_dict(t.load(opt.model_path))
model.to(device)
model.eval()
src = [word2ix[word] for word in opt.start_words]
res = src = [word2ix['<START>']] + src
max_len = 100
for _ in range(max_len):
src = t.tensor(res).to(device)[:, None]
src_mask = generate_square_subsequent_mask(src.shape[0])
src_pad_mask = src == len(word2ix) - 1
src_pad_mask = src_pad_mask.permute(1, 0).contiguous()
memory, logits = model(src, src_mask.cpu(), src_pad_mask.cpu())
next_word = logits[-1, 0].argmax().item()
if next_word == word2ix['<EOP>']:
break
res.append(next_word)
res = [ix2word[_] for _ in res[1:]]
print(''.join(res))
def gen_acrostic(**kwargs):
"""
生成藏头诗
start_words为'深度学习'
生成:
深山高不极,望望极悠悠。
度日登楼望,看云上砌秋。
学吟多野寺,吟想到江楼。
习静多时选,忘机尽处求。
"""
for k, v in kwargs.items():
setattr(opt, k, v)
device = t.device('cpu') if opt.use_gpu else t.device('cpu')
data, word2ix, ix2word = get_data(opt)
model = PoetryModel(len(word2ix))
model.load_state_dict(t.load(opt.model_path))
model.to(device)
model.eval()
start_word_len = len(opt.start_words)
index = 0 # 用来指示已经生成了多少句藏头诗
src_base = [word2ix[word] for word in opt.start_words]
res = [word2ix['<START>']] + [src_base[index]]
index += 1
max_len = 100
for _ in range(max_len):
src = t.tensor(res).to(device)[:, None]
src_mask = generate_square_subsequent_mask(src.shape[0])
src_pad_mask = src == len(word2ix) - 1
src_pad_mask = src_pad_mask.permute(1, 0).contiguous()
memory, logits = model(src, src_mask.cpu(), src_pad_mask.cpu())
next_word = logits[-1, 0].argmax().item()
# 如果遇到句号感叹号等,把藏头的词作为下一个句的输入
if next_word in {word2ix[u'。'],word2ix[u'!'],word2ix['<START>']}:
# 如果生成的诗歌已经包含全部藏头的词,则结束
if index == start_word_len:
res.append(next_word)
break
# 把藏头的词作为输入,预测下一个词
res.append(next_word)
res.append(src_base[index])
index += 1
else:
res.append(next_word)
res = [ix2word[_] for _ in res[1:]]
print(''.join(res))
def train(**kwargs):
for k, v in kwargs.items():
setattr(opt, k, v)
device = t.device('cpu') if opt.use_gpu else t.device('cpu')
vis = Visualizer(env = opt.env)
# 获取数据
data, word2ix, ix2word = get_data(opt)
data = t.from_numpy(data)
dataloader = DataLoader(data, batch_size=opt.batch_size, shuffle=True, num_workers=1)
# 模型定义
model = PoetryModel(len(word2ix))
optimizer = t.optim.Adam(model.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)
criterion = nn.CrossEntropyLoss(ignore_index=len(word2ix)-1)
if opt.model_path:
model.load_state_dict(t.load(opt.model_path))
model.to(device)
loss_meter = meter.AverageValueMeter()
for epoch in range(opt.epoch):
loss_meter.reset()
for ii, data_ in tqdm.tqdm(enumerate(dataloader)):
# 训练
data_ = data_.long().transpose(1, 0).contiguous()
data_ = data_.to(device)
optimizer.zero_grad()
input_, target = data_[:-1, :], data_[1:, :]
src_mask = generate_square_subsequent_mask(input_.shape[0])
src_pad_mask = input_ == len(word2ix) - 1
src_pad_mask = src_pad_mask.permute(1,0).contiguous()
memory, logit = model(input_, src_mask.to(device), src_pad_mask.to(device))
mask = target != word2ix['</s>']
target = target[mask] # 去掉前缀的空格
logit = logit.flatten(0, 1)[mask.view(-1)]
loss = criterion(logit, target)
loss.backward()
optimizer.step()
loss_meter.add(loss.item())
# 可视化
if (1 + ii) % opt.plot_every == 0:
if os.path.exists(opt.debug_file):
ipdb.set_trace()
vis.plot('loss', loss_meter.value()[0])
# 诗歌原文
poetrys = [[ix2word[_word] for _word in data_[:, _iii].tolist()]
for _iii in range(data_.shape[1])][:16]
vis.text('</br>'.join([''.join(poetry) for poetry in poetrys]), win=u'origin_poem')
gen_poetries = []
# 分别以这几个字作为诗歌的第一个字,生成8首诗
for word in list(u'美丽和智慧的月儿'):
gen_poetry = ''.join(generate(model, word, word2ix, ix2word))
gen_poetries.append(gen_poetry)
# gen_poetries = generate(model, u'春江花月夜凉如水', ix2word, word2ix)
vis.text('</br>'.join([''.join(poetry) for poetry in gen_poetries]), win=u'gen_poem')
if (epoch+1) % opt.plot_every == 0:
t.save(model.state_dict(), '%s_%s.pth' % (opt.model_prefix, epoch+1))
@t.no_grad()
def generate(model, start_words, word2ix, ix2word, max_len=100):
model.eval()
device = t.device('cpu') if opt.use_gpu else t.device('cpu')
src = [word2ix[word] for word in start_words]
res = src = [word2ix['<START>']] + src
for _ in range(max_len):
src = t.tensor(res).to(device)[:, None]
src_mask = generate_square_subsequent_mask(src.shape[0])
src_pad_mask = src == len(word2ix)-1
src_pad_mask = src_pad_mask.permute(1,0).contiguous()
memory,logits = model(src, src_mask.cpu(), src_pad_mask.cpu())
next_word = logits[-1,0].argmax().item()
if next_word == word2ix['<EOP>']:
break
res.append(next_word)
if next_word == word2ix['<EOP>']:
break
res = [ix2word[_] for _ in res]
model.train()
return res
def generate_square_subsequent_mask(sz):
mask = (t.triu(t.ones(sz, sz)) == 1).transpose(0, 1) # 生成下三角矩阵(下三角全为True,其余位False)
mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
return mask
if __name__ == '__main__':
import fire
fire.Fire()
#train(batch_size=128,pickle_path='tang.npz',lr=1e-3,epoch=2)
#train()
gen_acrostic()
利用.pth文件,进行文本生成
对main源代码进行如下更改:
1, model_path = 'checkpoints/tang_5.pth' # 预训练模型路径
start_words = '美丽与智慧的月儿' # 诗歌开始
2,在if __name__==’__main__’:下面加入测试字段:
gen()
3,将gen()函数换成gen_acrostic(),则可生产藏头诗
美人歌舞罢,独自对花时。今日花前去,明朝叶上垂。
丽句传歌处,清吟对酒时。山僧同宿寺,野客共归期。野寺僧来远,江楼客到迟。相思不相见,应喜白云知。
和风吹雨过江城,万里云山一望平。不见江南人不见,空闻塞上戍楼声。
智者不可见,愚者不可论。愚人不识面,智者不识门。
慧星现,现在四方同。若在诸天幷五日,不然一法定无踪,不是一般通。
的的无消息,啾啾空夜啼。不知人事少,何处更相携。
月落星稀夜未央,月明灯暗满衣裳。
儿童不识汉家翁,一种青山万岁中。
结论:
1,利用模型.pth可以生成两种情形或格式的文本。(说不定也可以生成歌词。)
2,预训练模型.pth训练的次数越多(5~50分析),模型倾向越好。
3,每次同等情况下,生成的结果一致,可重复性高。