【PyTorch深度学习项目实战100例】—— 使用pytorch实现LSTM自动AI作诗(藏头诗和首句续写)| 第6例

1,175 阅读6分钟

前言

大家好,我是阿光。

本专栏整理了《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 3. 之后需要对输入数据进行嵌入,如果不嵌入那么每个古诗的字应为对应的索引,为了能够进行训练所以需要将其进行嵌入,然后形成连续性变量。 4. 之后我们的数据就变成了 124 * 16 * embedding_dim 5. 然后将其导入到LSTM模块中,则形成的形状为 124 * 16 * hidden_dim 6. 之后将其导入到全连接层,形成分类,变为的形状为 124 * 16 ,vocab_size

「注意」 一定要清楚各个位置不同变量的形状,这些在代码中已经注明,一定要弄明白batch_sizetime_stepembedding_dimvocal_sizenum_layershidden_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然后将其和开始词送入模型生成对应的诗句
# 给定首句生成诗歌
def generate(model, start_words, ix2word, word2ix, prefix_words=None):
    results = list(start_words)  # 开始词的列表 ["床", "前", "明", "月", "光"]
    start_words_len = len(start_words)  # 开始词的长度
    # 第一个词语是<START>
    input = torch.Tensor([word2ix['<START>']]).view(11).long()  # "<START>"对应的索引8094
    if use_gpu:
        input = input.cuda()
    hidden = None

    # 若有风格前缀,则先用风格前缀生成hidden
    # 第一个"<START>"作为默认输入,然后遍历前缀词列表(不同于开始词),一个词一个词作为输入,生成hidden
    # 这种方式形成的hidden就是根据床前明月光形成的hidden,这样形成的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(11)

    # 开始真正生成诗句,如果没有使用风格前缀,则hidden = None,input = <START>
    # 否则,input就是风格前缀的最后一个词语,hidden也是生成出来的
    for i in range(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(11)
        # 否则将output作为下一个input进行
        else:
            # print(output.data[0].topk(1))
            #             top_index = output.data[0].topk(1)[1][0].item()
            top_index = output.argmax().item()  # 最后生成的词的下标,output为8293维度,获取概率最大的索引
            w = ix2word[top_index]  # 根据该下标获得对应的字
            results.append(w)  # 将生成的词加入结果诗句中
            input = input.data.new([top_index]).view(11)
        # 如果生成的词为结束词"<EOP>",则结束生成诗句,并将"<EOP>"从结果列表中删除
        if w == '<EOP>':
            del results[-1]
            break
    return results

六、生成藏头诗

生成藏头诗的原理与上述函数同理,只不过是利用给定的藏头字分别作为输入,一旦遇到特殊符号就说明该句生成结束,继续生成下一句藏头诗

同理这个函数也会包含诗句前缀,影响诗句的风格

七、模型训练

对于模型训练最重要的就是模型的输入和输出,针对于写诗这个任务,我们的输入应该是给定一句诗,然后错位1位作为它的标签用于监督,例如:

床前明月光疑是地上霜 那么我们的输入、输出就应该为: 输入数据:床前明月光疑是地上 输出数据:前明月光疑是地上霜

每个时间步对应的输出应该是他下个时间步对应的字 每个时间步的输出应该是vocab_size维度,就是词大小,用于全分类,这样就会根据概率获得该时间步对应的字。

由于本项目中采用的是唐诗数据集,数据为data: (57580,125) 的 numpy 数组,也就是每个样本为一句诗,每首诗中含有125个字,相当于不同的时间步,然后将每个字进行embedding进行编码,这里我们将输入作为124,也就是像上面所说,利用前124个字和他对应之后的124个字进行监督。

八、生成诗句

该函数用于进行测试生成语句,我们会首先加载我们训练好的模型,然后传入续写的诗句或者需要加入的前缀信息,形成诗句。

完整源码

【PyTorch深度学习项目实战100例】—— 使用pytorch实现LSTM自动AI作诗(藏头诗和首句续写)| 第6例_咕 嘟的博客-CSDN博客_pytorch基于 lstm 的自动写诗