用pytorch框架搭建一个写藏头诗的模型

记得两年多以前,在网上看到一个关于机器生成诗歌的新闻,感觉好神奇。
工作之后开始用pytorch框架,忙里偷闲,自己也试做了这样一个模型。
先展示一下模型生成的两首五言诗(以‘宅女姜璐’以及‘宅女胡盼’为藏头):

宅中逢圣主,天子在中州。
女娲将军幕,胡兵入汉廷。
姜旌连赛路,旌旎入城隅。
璐落三千里,旌旗万里余。

宅中无一酣,家有一壶酒。
女子不相识,君家亦有谁。
胡为白马走,不见白头看。
盼然无所为,今日无人知。

虽然不知讲了些什么,咋眼看去,貌似还行吧(作为一个文学院出来的同学,我的要求有点低哈)。

下面介绍下模型的实现过程。大致可以概括成:对训练数据进行预处理(将每首诗加上开头和结束标志);搭建一个lstm的模型;训练模型;调用训练好的模型,设置一个生成藏头诗的函数。
因为这是关于诗的语言生成,也并没有什么特写的评价指标,每次epoch都保存模型,到时候都试试看生成的诗是什么样子,只能用肉眼来分辨了。

1.
诗词预处理。关于诗词的资料可以去网上下载,毕竟还是有很多文学爱好者的。
将每首诗标上起始和终止符号,因为都选用五言诗,就不必考虑每首诗的长度问题了。然后将诗由字变成数(不用分词,因为诗就是以字为单位的呀)。这个部分相对于其它项目,还是挺简单的。

2.
设置一些基本参数,比如存放路径,学习率,是否是gpu,weight_decay, epoch, batch_sizet 等等

3.
模型搭建。这个模型很简单,而且是通用的,我就粘在下面了,注意返回值里有hidden 和output噢:

import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F

class PoetModel(nn.Module):
    def __init__(self, vocab_size, embedding_dim, hidden_dim):
        super(PoetModel, 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=2)
        self.linear1 = nn.Linear(self.hidden_dim, vocab_size)

    def forward(self, input,hidden=None):
        seq_len,batch_size = input.size()
        if hidden is None:
            h0 = input.data.new(2, batch_size, self.hidden_dim).fill_(0).float()
            c0 = input.data.new(2, batch_size, self.hidden_dim).fill_(0).float()
            h0,c0 = Variable(h0),Variable(c0)
        else:
            h0,c0 = hidden
        # size here : (seq_len, batch_size, embeding_dim)
        embeds = self.embeddings(input)
        # output size: (seq_len, batch_size, hidden_dim)
        output, hidden = self.lstm(embeds, (h0,c0))

        # size here : (seq_len*batch_size, vocab_size)
        output = self.linear1(output.view(seq_len*batch_size, -1))
        return output,hidden

4.
好了,现在就可以训练模型了,这个部分也很简单,都是常规的处理方法。只需要注意输入输出的设置,把一个文本按照一个字错开分别当成input 和 target。比如,一个文本是abcde, 那么输入就是abcd, 输出就是bcde。这样一来,就相当于逐字生成,即a生成b, ab生成c,abc生成d, abcd生成e了。

5.
最后,只需获取保存的模型,再设置一个函数生成诗就好啦。这里有一个小技巧就是,先拿一句已有的诗输入模型,作为生成的诗的意境。比如,在生成

宅中逢圣主,天子在中州。
女娲将军幕,胡兵入汉廷。
姜旌连赛路,旌旎入城隅。
璐落三千里,旌旗万里余。

的时候,我提前输入的一句诗是‘大漠孤烟直,长河落日圆。’

在生成
宅中无一酣,家有一壶酒。
女子不相识,君家亦有谁。
胡为白马走,不见白头看。
盼然无所为,今日无人知。

的时候,我提前输入的一句诗是’细雨鱼儿出,微风燕子斜。’

所以这两首诗的意境就不一样了,是吧。

为什么会这样呢?之前就有讲过,注意模型会不止输出output, 还有hidden, 这个hidden(之后在生成藏头诗的时候会用上)其实就包含了提前输入的那句诗的信息,也可以认为是意境吧。

另外还需要注意的是,这里提前输入的诗也逐个字逐个字输入的,原因很简单,我们一会儿生成诗的时候也是逐个字逐个字的。代码很简单(pre_poet 指提前设好的诗句):

 for word in pre_poet:
     output,hidden = model(input,hidden)
     input = Variable(input.data.new([word2ix[word]])).view(1,1)

现在提前输入一句诗拿到了相应的hidden, 也就是设置好了的诗歌的意境,我们就可以输入藏头的第一个字了,将生成的字再输入再生成直到遇到句号就结束。然后再开始输入藏头的第二个字,重复一样的过程,直到所有的藏头都输完为止。

感觉之前觉得不可思议的事情,自己做了之前还是蛮简单的,不过发明lstm的人可实在是太厉害啦。

猜你喜欢

转载自blog.csdn.net/weixin_42936560/article/details/81673018