1. 程式人生 > >用pytorch框架搭建一個寫藏頭詩的模型

用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的人可實在是太厲害啦。