华为云开发者实验:LSTM 模型生成文本

实验目标与基本要求

  • 理解 LSTM 模型:掌握长短期记忆网络(LSTM)的基本原理和结构。
  • 熟悉 ModelArts 平台:了解华为云 ModelArts 平台的基本功能和操作流程。
  • 掌握 Notebook 开发环境:学习如何在 ModelArts 的 Notebook 环境中编写和运行代码。

任务一:创建开发环境

步骤 1 配置 notebook 参数

主要参数信息如下,其余配置请保持默认配置

  • 名称:notebook-LSTM(名称固定)
  • 镜像:选择“公共镜像”,并选择“pytorch1.8-cuda10.2-cudnn7-ubuntu18.04”
  • 资源类型:选择“公共资源池”
  • 类型:选择“GPU”
  • 规格:选择“GPU: 1*Pnt1(16GB)|CPU: 8 核 64GB”

任务二: 运行代码

对下文的每个代码片段,在 PyTorch-1.8 开发环境中,新建一个代码块进行运行:

步骤 1 安装需要的包

输入:

1
!pip install torchnet

输出:

1
2
3
4
5
6
7
8
Looking in indexes: http://repo.myhuaweicloud.com/repository/pypi/simple
Requirement already satisfied: torchnet in /home/ma-user/anaconda3/envs/PyTorch-1.8/lib/python3.7/site-packages (0.0.4)
Requirement already satisfied: visdom in /home/ma-user/anaconda3/envs/PyTorch-1.8/lib/python3.7/site-packages (from torchnet) (0.2.4)
s (from requests->visdom->torchnet) (1.26.12)
Requirement already satisfied: charset-normalizer~=2.0.0 in /home/ma-user/anaconda3/envs/PyTorch-1.8/lib/python3.7/site-packages (from requests->visdom->torchnet) (2.0.12)
Requirement already satisfied: certifi>=2017.4.17 in /home/ma-user/anaconda3/envs/PyTorch-1.8/lib/python3.7/site-packages (from requests->visdom->torchnet) (2022.9.24)
WARNING: You are using pip version 21.0.1; however, version 24.0 is available.
You should consider upgrading via the '/home/ma-user/anaconda3/envs/PyTorch-1.8/bin/python3.7 -m pip install --upgrade pip' command.

步骤 2 获取数据

1
2
import moxing as mox
mox.file.copy_parallel('obs://modelarts-labs-bj4-v2/course/NLP_Course/LSTM','LSTM_poem-master')

输出结果可刷新查看右边的文件夹:
1721202420900

步骤 3 数据集来源和读取数据集

本次采用的是唐诗数据集,一共有接近 60000 首唐诗,不需要标签,因为 AI 自动写诗可以看成是语言模型的一个应用

其中一首诗的一句如下:

上句下句
度 门 能 不 访冒 雪 屡 西 东

任务定义:给出一首诗的开头几个词,或者首句(随便),续写之后的句子。

测试结果初窥:

输入上句下句
度 门 能 不度门能不见今日复不知
举头望举头望山中一年一年年
会当会当年少年春风吹新开

读取数据集

输入:

1
2
3
4
5
%cd /home/ma-user/work/LSTM_poem-master
import numpy as np
file_path="tang.npz"
poem=np.load(file_path,allow_pickle=True)
poem.files

查看结果:

1
2
/home/ma-user/work/LSTM_poem-master
['ix2word', 'word2ix', 'data']

一些其他操作:

1
2
ix2word,word2ix,data=poem['ix2word'],poem['word2ix'],poem['data']
data.shape
1
(57580, 125)
1
2
3
4
char2ix=word2ix.item()
ix2char=ix2word.item()
vocab_size=len(char2ix)
vocab_size
1
8293
1
2
3
4
pad_id=char2ix["</s>"]
start_id=char2ix["<START>"]
end_id=char2ix["<EOP>"]
print(pad_id,start_id,end_id)
1
8292 8291 8290

步骤 4 数据预处理

减少训练的量,选 2000 首诗进行训练。

1
2
data=data[:2000]
data.shape

结果:

1
(2000, 125)

</s> 放在放到后面,将数据进行转换。

1
2
3
4
5
6
7
8
9
10
11
12
#把</s>放在放到后面。
def reverse(poem):
ind=np.argwhere(poem==start_id).item()
new_poem=poem[ind:len(poem)]
pad=poem[0:ind]
return np.hstack((new_poem,pad))

#将数据进行转换。
for i in range(len(data)):
data[i]=reverse(data[i])
for i in range(data.shape[1]):
print(ix2char[data[3][i]],end=" ")

结果:

1
<START> 庭 树 忽 已 暗 , 故 人 那 不 来 。 祗 因 厌 烦 暑 , 永 日 坐 霜 台 。 <EOP> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s>

步骤 5 定义训练参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Config(object):
num_layers = 3 # LSTM层数
data_path = 'data/' # 诗歌的文本文件存放路径
pickle_path = 'tang.npz' # 预处理好的二进制文件
author = None # 只学习某位作者的诗歌
constrain = None # 长度限制
category = 'poet.tang' # 类别,唐诗还是宋诗歌(poet.song)
# lr = 1e-3
lr = 1e-4
weight_decay = 1e-4
use_gpu = True
#epoch = 10
epoch = 20
# batch_size = 16
batch_size = 32
maxlen = 125 # 超过这个长度的之后字被丢弃,小于这个长度的在前面补空格
plot_every = 200 # 每20个batch 可视化一次
# use_env = True # 是否使用visodm
env = 'poetry' # visdom env
max_gen_len = 200 # 生成诗歌最长长度
debug_file = '/tmp/debugp'
model_path = "/home/ma-user/work/LSTM_poem-master/checkpoints/tang_new.pth" # 预训练模型路径
prefix_words = '仙路尽头谁为峰?一见无始道成空。' # 不是诗歌的组成部分,用来控制生成诗歌的意境
start_words = '闲云潭影日悠悠' # 诗歌开始
acrostic = False # 是否是藏头诗
model_prefix = 'checkpoints/' # 模型保存路径
embedding_dim = 256
hidden_dim = 512

本段无输出

步骤 6 定义模型结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import torch.nn as nn

class PoetryModel(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_dim):
super(PoetryModel, self).__init__()
self.hidden_dim = hidden_dim
# 词向量层,词表大小 * 向量维度
self.embeddings = nn.Embedding(vocab_size, embedding_dim)
# 网络主要结构
self.lstm = nn.LSTM(embedding_dim, self.hidden_dim, num_layers=Config.num_layers)
# 进行分类
self.linear = nn.Linear(self.hidden_dim, vocab_size)

def forward(self, input, hidden=None):
seq_len, batch_size = input.size()
#print(input.shape)
if hidden is None:
h_0 = input.data.new(Config.num_layers, batch_size, self.hidden_dim).fill_(0).float()
c_0 = input.data.new(Config.num_layers, batch_size, self.hidden_dim).fill_(0).float()
else:
h_0, c_0 = hidden
# 输入 序列长度 * batch(每个汉字是一个数字下标),
# 输出 序列长度 * batch * 向量维度
embeds = self.embeddings(input)
# 输出hidden的大小: 序列长度 * batch * hidden_dim
output, hidden = self.lstm(embeds, (h_0, c_0))
output = self.linear(output.view(seq_len * batch_size, -1))
return output, hidden

本段无输出

步骤 7 定义首句生成诗歌和藏头诗函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import torch as t
import numpy as np
from torch.utils.data import DataLoader
from torch import optim
from torch import nn
from torchnet import meter
import tqdm



# 给定首句生成诗歌
def generate(model, start_words, ix2word, word2ix, prefix_words=None):
results = list(start_words)
start_words_len = len(start_words)
# 第一个词语是<START>
input = t.Tensor([word2ix['<START>']]).view(1, 1).long()
if Config.use_gpu:
input = input.cuda()
hidden = None

# 若有风格前缀,则先用风格前缀生成hidden
if prefix_words:
# 第一个input是<START>,后面就是prefix中的汉字
# 第一个hidden是None,后面就是前面生成的hidden
for word in prefix_words:
output, hidden = model(input, hidden)
input = input.data.new([word2ix[word]]).view(1, 1)

# 开始真正生成诗句,如果没有使用风格前缀,则hidden = None,input = <START>
# 否则,input就是风格前缀的最后一个词语,hidden也是生成出来的
for i in range(Config.max_gen_len):
output, hidden = model(input, hidden)
# print(output.shape)
# 如果还在诗句内部,输入就是诗句的字,不取出结果,只为了得到
# 最后的hidden
if i < start_words_len:
w = results[i]
input = input.data.new([word2ix[w]]).view(1, 1)
# 否则将output作为下一个input进行
else:
# print(output.data[0].topk(1))
top_index = output.data[0].topk(1)[1][0].item()
w = ix2word[top_index]
results.append(w)
input = input.data.new([top_index]).view(1, 1)
if w == '<EOP>':
del results[-1]
break
return results


# 生成藏头诗
def gen_acrostic(model, start_words, ix2word, word2ix, prefix_words=None):
result = []
start_words_len = len(start_words)
input = (t.Tensor([word2ix['<START>']]).view(1, 1).long())
if Config.use_gpu:
input = input.cuda()
# 指示已经生成了几句藏头诗
index = 0
pre_word = '<START>'
hidden = None

# 存在风格前缀,则生成hidden
if prefix_words:
for word in prefix_words:
output, hidden = model(input, hidden)
input = (input.data.new([word2ix[word]])).view(1, 1)

# 开始生成诗句
for i in range(Config.max_gen_len):
output, hidden = model(input, hidden)
top_index = output.data[0].topk(1)[1][0].item()
w = ix2word[top_index]
# 说明上个字是句末
if pre_word in {'。', ',', '?', '!', '<START>'}:
if index == start_words_len:
break
else:
w = start_words[index]
index += 1
# print(w,word2ix[w])
input = (input.data.new([word2ix[w]])).view(1, 1)
else:
input = (input.data.new([top_index])).view(1, 1)
result.append(w)
pre_word = w
return result

本段无输出

步骤 8 定义模型和训练函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
%cd /home/ma-user/work/
import os
import torch as t
import numpy as np
from torch.utils.data import DataLoader
from torch import optim
from torch import nn
# from model import *
from torchnet import meter
import tqdm


def train():
if Config.use_gpu:
Config.device = t.device("cuda")
else:
Config.device = t.device("cpu")
device = Config.device
# 获取数据
datas = np.load("LSTM_poem-master/tang.npz", allow_pickle=True)
data = datas['data']
ix2word = datas['ix2word'].item()
word2ix = datas['word2ix'].item()
data = t.from_numpy(data)
dataloader = DataLoader(data,
batch_size=Config.batch_size,
shuffle=True,
num_workers=2)

# 定义模型
model = PoetryModel(len(word2ix),
embedding_dim=Config.embedding_dim,
hidden_dim = Config.hidden_dim)
Configimizer = optim.Adam(model.parameters(),lr=Config.lr)
criterion = nn.CrossEntropyLoss()
if Config.model_path:
model.load_state_dict(t.load(Config.model_path,map_location='cpu'))
# 转移到相应计算设备上
model.to(device)
loss_meter = meter.AverageValueMeter()

save_dir = '/home/ma-user/work/LSTM_poem-master/Output'


# 进行训练
f = open('result.txt','w')
for epoch in range(Config.epoch):
loss_meter.reset()
for li,data_ in tqdm.tqdm(enumerate(dataloader)):
#print(data_.shape)
data_ = data_.long().transpose(1,0).contiguous()
# 注意这里,也转移到了计算设备上
data_ = data_.to(device)
Configimizer.zero_grad()
# n个句子,前n-1句作为输入,后n-1句作为输出,二者一一对应
input_,target = data_[:-1,:],data_[1:,:]
output,_ = model(input_)
# print("Here",output.shape)
# 这里为什么view(-1)
# print(target.shape,target.view(-1).shape)
loss = criterion(output,target.view(-1))
loss.backward()
Configimizer.step()
loss_meter.add(loss.item())
# 进行可视化
if (1+li)%Config.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()
# model_save_path = os.path.join(save_dir, '%s_%s.pth' % (Config.model_prefix, epoch))
# t.save(model.state_dict(), model_save_path)
model_save_path = os.path.join(save_dir, 'model_epoch_%s.pth' % epoch)
t.save(model.state_dict(), model_save_path)


if __name__ == '__main__':

train()

结果输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/home/ma-user/work
198it [00:12, 16.55it/s]
训练损失为0.6370207652449614
春江潮水连海平,海上明月共潮生。灩灩随波千万里,何处春江无去程。龙舟漾漾枫林远,津邑野花风日暮。不知名物远悠悠,只见渔乡夜闻落。日暮西风摇落时,枫树千竿转回举。林中愁歇多少情,雨后还待夜行客。梦依青嶂上重天,出处皆防陇上园。松根翳重暮归鸟,日晚猎僧眠石门。江鸥一宿五湖水,波摇荡漾澄平碧。林际鹤鸣人未归,江边橘树人应稀。林下酒瓶明月月,年年零落过江头。吾君无事无人识,觉后多为萧寺侯。龟逸四门逃豁讳,
江上为云尉,蚕何贱马蹄。胡笳断行路,河汉送长愁。长劒功皆在,孤舟去自浮。杂雷随盖毂,阴气隔云流。竞就临川拜,差盘密柳游。梅梅蒙放锦,锦蘂堕青油。粉壁交王母,红楼醉画楼。参差盘岫阁,竞结华芳秋。迥比千门路,飘摇两岸愁。遥期方尚往,灞水赋新游。远梦惊阶雪,余香染䌽裘。鸣鸿思不乐,事没事难尤。自窃蹉跎集,欣欣庶厚黎。果辞文翰旧,忝谒子丞余。
花界无多地,烟霞尽日闲。浮名高自显,好是此为邻。静口潜通药,过时即问邻。心知无所念,物外尽君亲。澹荡情无限,抟风事不伸。灯应乌不厌,蝶与泪俱新。似镜疑含泪,垂纶似早春。细霑芳草色,虚以落篱巾。墙似藏峰鹤,藤闻近水鳞。柳夭迷岸翠,莲阁认枝新。保实通周沐,狂流过洞秦。醉眠当宴处,思唱小楼尘。
朝光九霄澹,送子大丈夫。霜露含绮绮,凉衣曜清珰。清夜漏漏丽,清江月霓高。非君在何许,意与时人嗤。忆昔天路遥,游春物芳菲。神仙去何际,鸞舞琼瑶枝。皓齿一以时,悯妖反自怡。炎风入广户,清露含芳思。分辉委素义,清旷为良芝。服意岂云异,至今常坐奇。答之北堂上,时俗羇嚣师。纵广无知己,溘来无定时。我心保真性,何必平生知。
秋天杳茫茫茫如皎,予今欲示吴江间。荷蓑寄茧绕户间,扁舟断歌弄梨枣。沙台衣暖心欲折,雨雪濛濛四江转。庭前一面木脂簪,侧开洞房垂柳深。越人侍女争开口,开窗独倚香帘帏。暂来云雨心寂寞,寥落清光不能返。自然匣中素书珠,解与我心还不著。愿君昨来心自怜,使我愁颜莫如此。日照莲光风动时,高阁东邻来往去。劝尔为君下石文,醍醐只在头陀地。愿得君子好老时,不放一行登四壁。
月出海门寻不定,山东斗中开新壁。初时沽酒不及钱,祝贾经年逐沧海。废船不问邑中儿,秪为不待卢晋生。胡兵驾吹人不起,河嶽已作风尘生。对此何人千万古?炎纲旧识雌殷声。昭阳旧曲慰心室,空令马蹄心自息。秋风霜中鸿雁飞,空把金缸泪销血。行魂不到马头空,远路还思暮天白。落花飞尽空路愁,白羽不成何足贵。看着还经镜底目,一只如刀系时雪。不是老人愁杀人,少年不得还乡人。
202it [00:13, 7.28it/s]
夜梦神州天道忙?定本自通奇制场。四十有生缘此艺,出中更见并天王。五更十二山精女,周转十三声真相。丰铜怪火烧相与,灵通逐水心中识。风吹吹,万草光,二物如是是非王。只有一世古中人,十二石桥迎第一。都持两刀入府名,五将变化如焚云。诸子振尸一如玉,不能终身没杀死。亦能得一个生灵骨,生死由来始知足。圣罪须慈伏武臣,文夫气内随人断。五世功名辨不谐,穷愁错报人生死。

···

1798it [01:55, 16.34it/s]
训练损失为0.5768661327494516
春江潮水连海平,海上明月共潮生。灩灩随波千万里,何处春江无月明?江流宛转遶芳甸,月照花林皆似霰。空里流霜不觉飞,汀上白沙看不见。江天一色无纤尘,皎皎空中孤月轮。江畔何人初见月,江月何年初照人?人生代代无穷已,江月年年秖相似。不知江月待何人?但见长江共云阔,空使人心不同別。世人爱我百余年,且遇佳人养君去。日暮东西头半空,昨日逢君一局禅。
江上为云水,若云有遮家。谁能剪借翔,上出作蛟凰。飞栖蓬莱殿,千寻玉座堂。五丁有嘉蘂,三数藏云芳。娇豔芙蓉姿,衔张凤容珰。筝声入绮弦,歌声转轻芳。恩华委华饰,不敢弄琼瑶。妾身与长游,从此一何长。传鹤出楚门,凭高望长堂。香是香可斫,夜静鱼亦长。隐居无一物,但见此梦长。朝风动小林,春草生绿塘。色入花露落,露浓花绿黄。屏风送雨来,早起芙蓉香。
花界无生地,慈宫有相年。规模随胜势,耳意极神仙。万象随师论,三孤得诫前。一闻虽有感,勤倚已无缘。世途空无事,浮图独有权。本师将已偶,行远未遭妍。谷忽横戎塞,天威启曲田。关山高入汉,关界本成仙。日日云楼静,秋郊朔漠旋。山河分手去,甲合数西偏。鸡犬随人聚,邹差历日终。延嗟感恩日,激鼓下朝空。更有千年雁,飞飞一夜天。
朝发淇水南,将寻北燕路。魏家旧城阙,寥落无人住。独自哭辽城,谁人问劳许。与君相送远,往事辞家住。出门无处人,望日不能饭。见君还自取,不作流中宿。忆昔恶应看,少年半成竹。今年三十六,不敢暂欢苦。我家能种芳,岁暮日日暮。不属百丈来,犹无一少客。眼看人欲聘,已学天阴黑。自古实不知,倾朱还刻况。为之即有所,何用偷无种。但好度三年,一理无虚。
秋天清,寒夜滴沈沈沈。不用雄雄风,羇骚即隐关。一朝四州上,君子名重关。博山最高山,大道自然通。彭州府中寺,举世同老农。窜尔俱往时,形影在乾坤。日夕望远近,涕泪悲飞鸿。况当春序迟,及念怀义同。古才百推至,言语千岁风。盛时且献恩,所用齿冠风。回首北斗上,弥笑不见功。并苦千里中,得丧时丁风。揽辔到北海,唯余天下中。昔日又一日,何须变穷通。
月出海门寻不得,中夜一夜一绳出。欹斜时见人寂莫,林猨犬吠人梦起。风飘野鸟入簷下,月下马头秋欲暮。知君久离旧山谪,玉堂掩锁开缄宿。南邻野叟来往时,北上重人知意迟。脱却珠眉蟾烂熳,宫女看来白玉札。扬姬小女唱豪颐,琵琶瑟瑟如玉琴。太行十二九盘路,楼上日月生光芒。君凡无言即相觅,云是龙高不得遗。假似众童相向下,仰摇直至落轮刀。初宿大名复如此,人间便得何时断。身逐霓旌动故山,穷时愿得长生药。
1800it [01:56, 15.50it/s]
夜梦归乡远,离书到岳阳。马归三岛上,诗卷六州傍。亭树千家茂,邻家数若忙。海鱼惊戴褐,家吏灌拏床。照榻初朝局,谙蛬思旅菜。老夫欺酒伴,胆炙置茶香。米足侵炎利,柴曾擘早凉。外缘名寄鹤,林灶病为场。念后身忘地,安知道路长。心清通市井,醉臥倚肩床。若问天涯兴,空无白太长。荒疆归路永,垂白看头长。

198it [00:12, 16.39it/s]
训练损失为0.47638893872499466
春江潮水连海平,海上明月共潮生。灩灩随波千万里,何处春江无月明?江流宛转遶芳甸,月照花林皆似霰。空里流霜不觉飞,汀上白沙看不见。江天一色无纤尘,皎皎空中孤月轮。江畔何人初见月,江月何年初照人?人生代代无穷已,江月年年秖相似。不知江月待何人?但见长江共云阔,空使人心不同別。世人爱与贫居多,欲折还归牧童戏。昨宵寅话敬平事,泪下禅心转江汉。

步骤 9 定义预测函数并且测试生成结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
%cd /home/ma-user/work/LSTM_poem-master

def userTest():
print("正在初始化......")
# 加载数据
datas = np.load("tang.npz", allow_pickle=True)
data = datas['data']
ix2word = datas['ix2word'].item()
word2ix = datas['word2ix'].item()

# 定义模型
model = PoetryModel(len(ix2word), Config.embedding_dim, Config.hidden_dim)

# 指定模型文件路径
model_path = '/home/ma-user/work/LSTM_poem-master/Output/model_epoch_19.pth' # 假设文件名为 model_epoch_final.pth
if os.path.isfile(model_path):
# 加载模型
model.load_state_dict(t.load(model_path, map_location=t.device('cpu')))
print("模型加载成功!\n")
else:
print("模型文件不存在,请检查路径!")
return

# 配置GPU或CPU
if Config.use_gpu:
model.to(t.device('cuda'))
print("初始化完成!\n")

# 用户交互部分
while True:
print("欢迎使用唐诗生成器,\n"
"输入1 进入首句生成模式\n"
"输入2 进入藏头诗生成模式\n")
mode = int(input())
if mode == 1:
print("请输入您想要的诗歌首句,可以是五言或七言")
start_words = str(input())
gen_poetry = ''.join(generate(model, start_words, ix2word, word2ix))
print("生成的诗句如下:%s\n" % (gen_poetry))
elif mode == 2:
print("请输入您想要的诗歌藏头部分,不超过16个字,最好是偶数")
start_words = str(input())
# 确保 gen_acrostic 函数存在并且正确实现
gen_poetry = ''.join(gen_acrostic(model, start_words, ix2word, word2ix))
print("生成的诗句如下:%s\n" % (gen_poetry))

if __name__ == '__main__':
userTest()

输出结果如下:

步骤 10 查看训练输出的模型

打开 notebook 中的如下 Output 文件,可以查看到代码训练的输出模型内容。