為了寫春聯,我用 Transformer 訓練了一個 “對穿腸”
大資料文摘 出品
作者:Andy、蔣寶尚
今天是臘月二十九,按照黃曆,是解除、破屋、壞垣的好日子。
中國人的傳統也是在這一天貼對聯,準確來說是春聯。
對聯來源於對偶句和駢儷句,講究工整對仗,充分展現了我們中國人獨特精緻的藝術美感。
古人常以“吟詩作對”交友比試,但到了今天, 為了讓寫出的對聯“出奇不意”,文摘菌決定用最近NLP領域大火的Transfomer模型,訓練一個聰明的AI, 我出上聯,它對下聯, 將這個“對穿腸”的任務交給它。
模型介紹
關於對春聯這個任務,可以明顯看出是一個序列到序列的任務。於是就得祭出現在自然語言處理裡面的大殺器了,Seq2Seq + Attention!
於是,文摘菌便採用了 Transformer 序列到序列模型。Transformer由論文《Attention is All You Need》提出,漸漸有取代RNN成為NLP中主流模型的趨勢,現在更是谷歌雲TPU推薦的參考模型,包括谷歌給自己TPU打廣告的 Bert 就是Transformer模型。
Transformer架構完全捨棄了深度學習裡經典的RNN和CNN結構,而只用注意力機制還搭建。最早在機器翻譯任務上進行測試就超越 RNN,達到了當時SOTA的效果。Transformer 不光像 CNN 一樣克服了 RNN 最被人詬病的訓練慢問題,利用self-attention 機制實現快速並行,層數還可以增加到比較深,充分發掘DNN模型的特性,提升模型準確率。同時,在NLP任務上,還比CNN和RNN的效能都普遍要好,它不為王,誰為王。
大資料文摘之前與百度NLP聯合出品了一篇文章:BERT大火卻不懂 Transformer?讀這一篇就夠了。對Transformer不熟悉的同學可以點選超連結閱讀文章,保你理解的透透的。
這裡我們用6層Transformer單元,裡面的Self-Attention的頭數為8,隱單元數512個。模型採用 tensorflow 框架,GPU 則用深度學習標配的 Tesla K80。
好了,下面先介紹一下資料集。
資料集
文摘菌所使用的資料集來自一位名為馮重樸_梨味齋散葉的博主的新浪部落格,總共包含超過70萬副對聯。
然而,由於未知的原因,這位博主的部落格已經無法訪問了……
但是,這一珍貴的資料集卻在GitHub上廣為流傳。
github地址(指令碼已無法使用,可以用我們上傳的couplets,google drive需科學翻牆):
https://github.com/wb14123/couplet-dataset
google drive:
https://drive.google.com/file/d/13cJWWp_ST2Xqt76pEr5ZWa6LVJwGYC4-/view?usp=sharing
整個資料集解壓過後總共56.9兆,其中訓練資料集56.4兆,測試資料集400多KB。另外,資料是以TXT格式儲存的。全文不含標點15,153,840字,如果每天看100條的話,20多年都看不完。而文摘菌採用Tesla K80,2個小時就能來一個Epoch。
資料預處理
首先進行資料預處理,因為拿到的資料比較乾淨,所以這任務主要就是建立詞表。值得一提的是,資料集的壓縮檔案中存在著一個建立好的詞表。你也可以用資料集給出的詞表。因為對聯任務並不難,所以我們直接使用字粒度來作為輸入,也就是像“我們”這樣的詞,我們是把其當做兩個單元進行輸入,而不是一個。
關鍵程式碼如下:
defmake_vocab(fpaths, out): '''Constructs vocabulary. Args: fpaths: Astring. Input file path. fname: Astring. Output file name. ''' char2cnt = Counter() for path in fpaths: for line inopen(path, 'r'): line = line.strip() ifnot line: *# detect the empty line* continue chars = line.split() char2cnt.update(chars) withopen(out, 'w') as fout: fout.write("{}\t1000000000\n{}\t1000000000\n{}\t1000000000\n{}\t1000000000\n".format("<PAD>", "<UNK>", "<S>", "</S>")) # special tokens for word, cnt in char2cnt.most_common(len(char2cnt)): fout.write(u"{}\t{}\n".format(word, cnt)) print("%d chars written!"% (len(char2cnt)))
建立完詞表之後,所有單個漢字加上一些特殊字元,總共9126個字元。其實我們看看頻率為1的字元就會發現,好像都不認識。一般來說,在序列到序列任務裡面,我們會在詞表裡面忽略頻率小不常用的字詞。但是因為這裡我們詞表並不大,所以就全部採用了。
之後,我們對訓練資料的字長進行統計,來決定訓練時句子的最大長度。統計後發現,對聯資料中單句的長度大部分集中在5-10字之間,最長大概30字左右,所以訓練的最大長度文摘菌選的也是30。這個長度並不長,所以之後整個 Transformer 的大小都不是很大。
模型編寫與訓練
這裡我們首先搭建好模型,之後通過資料將模型中的引數訓練成我們所希望的,之後儲存起來。這裡便是一個epoch儲存一個模型。而測試的時候,就可以直接讀入引數獲得一個訓練好的模型來使用。
此外,我們也可以將儲存的模型分享給別人,讓其他人也可以直接利用我們的訓練結果。
模型和訓練的程式碼太多,這裡就全不放出來了,感興趣的讀者可以去github上下載完整程式碼,也可以在大資料微信公眾號後臺回覆“對聯”下載完整程式碼以及資料集。
github地址:
https://github.com/andy-yangz/coupletsseq2seqtransformer
於是整個模型的訓練過程主要分為四個步驟:
第一步,先下載對聯資料 couplets, 然後解壓到 data 裡去
第二步,調整超引數
第三步,執行 prepro.py預處理資料,產生詞表,也可以用提供的詞表
第四步,執行 train.py 訓練模型
評估
訓練完之後,儲存模型,評估指令碼會自動讀入最新模型來進行評估。
評估的主要步驟包括:
-
建立模型
-
讀入訓練好的引數
-
讀入測試資料
-
將資料喂入模型
-
編碼解碼獲得結果
最主要程式碼應該是解碼這一塊:
### Autoregressive inference preds = np.zeros((hp.batch_size, hp.maxlen), np.int32) for j in range(hp.maxlen): _preds = sess.run(g.preds, {g.x: x, g.y: preds}) preds[:, j] = _preds[:, j]
最開始喂入x,還有一個空的preds,然後獲得一個預測的字,將這個字放在preds相應的位置,之後迴圈,不斷將preds填滿。最後就能獲得完整的預測結果。
這裡每次預測的字,取得都是預測概率最大的那個字,這叫做 greedy decoding(貪婪搜尋解碼),因為每次都是選最大的。這樣雖然快,但是也會出現陷入區域性最優解的情況。更好些解碼方法,可以使用Beam search(束搜尋)。
訓練結果
最後,來看看文摘菌的訓練結果如何。
先皮一下!
話說改革開放四十週年,當然首先要響應一下我們的改革開放。先來段霸榜b站數月,唸詩之王的作品。
接招,我出上聯:
上聯: 改革春風吹滿地,中國人民真爭氣
AI 對:
下聯: 和諧時雨潤千山,小康社稷好生活
好溼好溼,充分說明了改革開放帶給我們的美好生活。似乎還不錯,我們系統的覺悟非常高啊。
再來考考它,博大精深的六學知識吧:
上聯: 文體兩開花
下聯: 武功一代人
emmm,比較微妙,還行吧。
看到武功一代人,文摘菌突然想到了金庸先生。先生為我們創造了一個龐大的武學世界,為了紀念先生,就以下面兩句為上聯吧:
上聯:飛雪連天射白鹿
下聯:落花遍地戲黃蜂
嗯,確實有點世外桃源桃花島的味道。
上聯:笑書神俠倚碧鴛
下聯:閒看仙女下紅塵
這就有意思了,金庸武俠世界裡的女子確實都是灑脫般的人物,尤其是小龍女和它的後輩黃衫女子活脫脫的一塵不染。
上聯: 盤他
下聯: 還我
好像有點都對不上了。
再來考考我們機器人2018年熱點。
上聯: 小豬佩奇社會人
下聯: 大聖傳世文明家
居然對上了。
再來,今年最火劇。
上聯: 延禧攻略
下聯: 棣華家聲
莫非 AI 在暗示我們下一個會火的電視劇嗎,根據名字,似乎是講一個家族中兩兄弟的故事,有意思有意思。
再看看,綜藝節目:
上聯: 創造一零一
下聯: 和諧萬事興
嗯,嗯,非常和諧。
上聯:錦鯉
下聯:銀蛇
普普通通。
再看看美國領導人們。
上聯:特朗普
下聯:小康莊
上聯:奧巴馬
下聯:羅漢松
沒想到雖然都身為世界領導人,對出來的東西都這麼樸素。
好,那在看看貿易戰:
上聯:中美貿易戰
下聯:大中華國風
對的好,對的好,思想很正確。
好了不玩了。再來看幾個正經點的對聯吧。
我們從測試集裡面挑幾個,這裡上聯和下聯是正確的,模型是機器人給出的下聯:
上聯: 騰飛上鐵,銳意改革謀發展,勇當千里馬
下聯: 和諧南供,安全送電保暢通,爭做領頭羊
模型: 奮進開篇,激情發展促和諧,更上一層樓
還有:
上聯: 一句相思吟歲月
下聯: 千杯美酒醉風情
模型: 幾杯寂寞醉春秋
看上去對的還算挺工整。
當然我們也看到了一些問題。比如說模型學到的都是一些和寬泛的對應,名詞動詞相對應做到了,也能保證通順,但是卻在一些需要典故和外界知識的例子中,無法做出合適的迴應。有時就會出現,不知道“上鐵”和“南供”這倆典故,或者出現所謂無情對,上下聯字詞對仗工整但是意思毫不相關。
這是我們訓練了6個epoch,也就是模型將資料看了六遍的結果。這次我們總共儲存了7個模型,從Epoch1到Epoch7模型,其實要說那個好只能是見仁見智了,比如說我就覺得6個Epoch會好些。但是可以確認的是隨著Epoch增加模型就越能學到對聯資料裡的模式。
是不是還挺好玩,感興趣的同學,也跟我們一起訓練一個自己的“對穿腸”吧。
其他模型
關於AI寫對聯,其實之前已經有了基於深度學習seq2seq的模型,同樣其用到的也是TensorFlow和那位馮重樸_梨味齋散葉的博主提供的資料集。
目前,程式碼已經開源,你可以在下面的github中找到:
https://github.com/wb14123/seq2seq-couplet
當然,除了模型程式碼之外,你也可以開啟作者給出的網址,隨意的出對子。
網址:https://ai.binwang.me/couplet/
對了,上面那個AI創作者名叫王斌,英國萊斯特大學讀計算機碩士,現在是一名軟體工程師。
微軟對聯與百度對聯
除了單打獨鬥的程式設計師,嘗試了對聯AI的科技巨頭也不少,文摘菌這裡介紹兩個最有名的專案,不想自己訓練模型的同學,可以直接取用。
比較早出現的對聯AI來自微軟亞洲研究院,自然語言計算組研究開發,沈向洋主導的研究專案。
微軟的對聯AI要謹慎且複雜一點,如果以“改革春風吹滿地”作為上聯,它也會給你許多個下聯作為備選。
最令人感動的是,如果你不喜歡,還可以換一種方式!
為了讓對聯更加對仗工整,你可以選擇分詞的方式。如果你仍然不滿意,可以點選反饋意見按鈕告訴它“家長”。最後,還能生成橫批。
除了微軟這個老前輩,還有一個江湖新秀,那就是今年百度聯合央視網、網易推出的智慧春聯。據說,他能根據每個人的五官特性生成專屬於你的春聯。
嗯,也就是刷臉寫對聯。
文摘菌用李彥巨集的大頭像試了試,可以感受一下這魔幻的過程:
微軟對聯:
http://duilian.msra.cn/
百度對聯(用手機開啟喲):
https://chunlian.news.cntv.cn/
好了,文摘菌要去貼春聯啦, 就到這裡吧 。
志願者介紹
後臺回覆 “ 志願者 ”加入我們
聽說點了「好看」的人都變好看了哦