楔子
許多年之後,面對書桌上的兩句殘詞,貶居黃州的東坡居士將會回想起,他在故鄉眉山見到朱姓老尼的那個遙遠的下午。彼時的東坡還不是東坡,還只是一個七歲孩童。有一天,他在家附近偶遇一位年約九十的朱姓老尼。老尼看到蘇軾天資聰穎,就跟他聊起自己年輕時的經歷,曾跟隨師父進入後蜀主孟昶的宮中。一日,天酷熱,孟昶和他的妃子花蕊夫人深夜納涼於摩訶池上。面對此情此景,蜀主即興賦詞一首...老尼將她印象中僅存的打頭兩句告訴了蘇軾。
四十年後,蘇軾貶居於黃州,想起這段往事,遺憾於孟昶的詞只餘兩句,突發奇想要將這兩句詞續寫完整。他先猜測出這首詞的詞牌名---「洞仙歌令」,但要還原整首詞作,必須深刻結合寫詞人當時之心情以及伴隨而來的意境。蘇軾因而循著僅存的兩句詞,根據老尼給他的描述,竭力在腦海中還原蜀主當時的創作場景和心境,最終將詞續完,成就名作《洞仙歌》:
冰肌玉骨,自清涼無汗。水殿風來暗香滿。繡簾開,一點明月窺人,人未寢,倚枕釵橫鬢亂。起來攜素手,庭戶無聲,時見疏星渡河漢。試問夜如何,夜已三更,金波淡,玉繩低轉。但屈指西風幾時來,又不道流年暗中偷換。
以上就是文學史上有名的「東坡續詞」,雖說是文學史上的一段佳話,但筆者從中隱約看到了數理思維的影子:詩詞的創作過程有如在求解一個「最優化問題」:
在一定的約束條件下,如詩詞要遵守的平仄、押韻、對仗/對偶、五七變式、詞譜、情境等,詩詞創作者用文字將自己內心的真實感動用語言文字表達出來,在「戴著鐐銬在跳舞」的情況下,竭力達到音韻美、精煉美、言辭美、朦朧美、情感美、繪畫美和形式美的至臻境界...
此時,詩詞之精美和數理之嚴密是可以完美結合的。
既然詩歌的創作是有規律的,那麼,通過一定的數據挖掘手段,我們能夠從中發現一些insight。在本文中,筆者循著這個思路,將運用若干文本挖掘方法對手頭的詩歌語料庫(該詩歌原始語料庫地址為https://github.com/Werneror/Poetry)進行深入挖掘和分析,該詩歌語料庫的基本統計數據如下:
從上表可以看到,該詩歌語料庫中共計近85萬餘首詩歌,詩歌作者數量達29377位之多;其中,欄位包括「題目」、「朝代」、「作者」和「內容(詩歌)」。
為了方便後續的分析,筆者僅取其中的律詩和絕句,且僅取其中的五言和七言,排律(如《春江花月夜》、《長恨歌》等)、雜言(如李白的將進酒)等就不在本文的分析範圍之內。
經過數據清洗後,最終得詩504443,佔到原資料庫的59.1%。以下分別是清洗後的詩歌數據統計結果和部分樣例:
針對上述數據,筆者在本文中主要有兩個大目標:
構建一個包含熱門題材標籤的詩歌語料庫,用於後續的詩歌題材分類和詩歌生成任務;
基於上述詩歌語料庫的各類文本挖掘和語義分析,以期得到有趣味的發現。
針對上述目標,本文的實現路線圖,同時也是本文的行文脈絡,如下所示(點擊圖片可放大查看):
值得注意的是,上述實現路徑中,涉及到自然語言處理的兩大組成部分,即自然語言理解(分詞、語義建模、語義相似度、聚類和分類等)和自然語言生成(詩歌生成和詩歌翻譯), 看完也會對自然語言處理有一定的了解。信息量大,請耐心享用~
1 詩歌分詞和熱詞發現
給定一首詩歌文本,在其中隨機取一個片段,如何判斷這個片段是否是一個有意義的詞彙呢?
如果這個片段左右的搭配變化較多、很豐富,同時片段內部的成分搭配很固定,那麼,我們可以認為這個片段是一個詞彙,比如下圖中所示的「摩詰」就是符合這個定義,那麼它就是一個詞彙。
在具體實施的算法中,衡量片段外部左右搭配的豐富程度的指標叫「自由度」,可以用(左右)信息熵來度量;而片段內部搭配的固定程度叫「凝固度」,可以用子序列的互信息來度量。
在這裡,筆者利用Jiayan(甲言)對這54餘萬首詩歌進行自動分詞,在結果中按照詞彙出現頻率從高到低進行排序,最終從語料庫中抽取若干有意義的高頻詞。其中,詞彙的長度從1到4。
抽取結果如下(點擊圖片可放大查看):
筆者觀察其中部分結果,發現一字詞、二字詞才能算得上一般意義上的詞彙,如「不」、「爍」、「歲寒」、「留滯」等 ;三字詞和四字詞一般是多類詞性詞彙的組合,嚴格上講,應該算作短語或者固定表達,如「隨流水」、」雲深處」、「人間萬事」、「江湖萬裡」等。但本文為了表述方便,筆者將它們統一稱之為詞。
下面,筆者分別展示詞長從1到4的TOP100的高頻詞詞雲(點擊圖片可放大查看)。
一字高頻詞中,除去「不」、「無」、「有」這類「虛詞」,單看「人山風日天雲春花年月水」這11個高頻字,暗合了中國天人合一哲學傳統,作詩如作畫,作詩者是把人放到自然環境、天地歲月這個時空大畫卷中,七情六慾、天人感應,詩情畫意就由感而生,詩意盎然了!
「詩畫本一律」,古人誠不我欺!
二字高頻詞中,較為顯眼的是「萬裡」、「千裡」,它們描繪出巨大的空間感,在詩歌中經常跟「宏景」「貶謫」、「思鄉」、「閨怨」等主題捆綁在一起。
此外,「明月」、「故人」、「白雲」、「功名」、「人間」、「平生」和「相逢」等詞彙也是橫亙古今的熱門用語。
三字高頻詞中,數字的使用很是常見,如「二三子」、「二十四」、「一樽酒」、「二千石」等。其中,最值得一提的是詩人們用數詞對時空的描繪:表達時間跨度的,如「二十年」、「四十年」、「五百年」、「十年前」、「千載後」等;表達空間距離的,如「千裡外」、「三百裡」、「百尺樓」...古人總是喜歡把自己置身於浩瀚渺茫的時空之中,去思考自己匆匆的人生。正如東坡在《赤壁賦》的感慨:「寄蜉蝣於天地,渺滄海之一粟。哀吾生之須臾,羨長江之無窮!」
在四字高頻詞中,空間方位的詞彙較多,如「南北東西」、「江南江北」、「東西南北」等詞。因四字詞詞長較長,像「人間萬事」、「千巖萬壑」、「明月清風」、「白雲深處」、「相逢一笑」等詞就擁有較高的信息量,能夠還原大部分的詩歌意境了。
2 訓練含納詩歌詞彙語義關聯性的詞嵌入模型
詞嵌入模型可以從海量的詩歌文本中自動學習到字詞之間的關聯關係,據此可實現字詞關聯度分析、字詞相似度分析、聚類分析等任務。
然而,電腦程式不能直接處理字符串形式的文本數據,所以筆者首當其衝的一個步驟就是將詩歌文本數據分詞,之後再「翻譯」為計算機可以處理的數據形式,這由一個名為「文本向量化」的操作來實現。
先談分詞,它跟前面的高頻詞挖掘有聯繫,是後續所有分析任務的起始點。
結合前面積累的詞庫,再基於有向無環詞圖、句子最大概率路徑和動態規划算法對這54萬首詩歌進行分詞操作。現試舉一例:
分詞前:
「萬物生芸芸,與我本同氣。氤氳隨所感,形體偶然異。丘嶽孰為高,塵粒孰為細。忘物亦忘我,優遊何所覬。」
分詞後:
['萬物', '生', '芸芸', ',', '與', '我', '本', '同', '氣', '。','氤氳', '隨', '所', '感', ',','形體', '偶然', '異', '。', '丘嶽', '孰', '為', '高', ',', '塵', '粒', '孰', '為', '細', '。', '忘', '物', '亦', '忘我', ',', '優遊', '何', '所', '覬', '。']
分詞之後再做適當處理就可以「餵給」詞嵌入模型(這裡是Word2vec)進行訓練了。
基於Word2vec詞嵌入模型能從大量未標註的文本數據中「學習」到字/詞向量,而且這些字/詞向量包含了字詞之間的語義關聯關係(可以是語義相關或句法相關),正如現實世界中的「物以類聚,類以群分」一樣,字詞可以由它們身邊的字(上下文語境)來定義,而Word2vec詞嵌入模型恰恰能學習到這種詞彙和語境之間的關聯性。
其基本原理如下圖所示(點擊圖片可放大查看):
訓練完該模型後,將其訓練結果投射到三維空間,則是如下景象(點擊圖片可放大查看):
在訓練Word2vec的過程中,模型會從大量的詩歌文本數據中學習到詞彙之間的2類關聯關係,即聚合關係和組合關係。
聚合關係:如果詞彙A和詞彙B可以互相替換,則它們具有聚合關係。換言之,如果詞彙A和詞彙B含有聚合關係,在相同的語義或者句法類別中可以利用其中一個來替換另一個,但不影響對整個句子的理解。例如,「蕭蕭」、「瀟瀟」都是象聲詞,多用於描述雨聲,具有聚合關係,那麼「山下蘭芽短浸溪,松間沙路淨無泥,蕭蕭暮雨子規啼」中的「蕭蕭」可以換做「瀟瀟」。
組合關係:如果詞彙A和詞彙B可以在句法關係上相互結合,那麼它們具有組合關係。例如,「雨打梨花深閉門,忘了青春,誤了青春。賞心樂事共誰論?」中的「忘了」和「誤了」都和「青春」存在組合關係,都是「動詞+名詞」的動賓結構。
現在來尋找與「兵燓」存在語義關聯性的若干詞彙:
結果大都是跟「戰爭」&「創傷」相關的詞彙,語義關聯關係捕獲能力較強,後續的熱門詩歌體裁挖掘任務也會用到詞嵌入模型的這個特性。
3 度量詩歌詞彙之間的語義關聯關係
3.1 利用餘弦相似性度量詩歌詞彙關聯度
度量詞彙之間的相似度或者關聯度,我們一般會使用兩個詞彙的詞向量之間的餘弦值,詞向量之間的夾角越小,則餘弦值越大,越接近1,則語義相關度越高;反之,相關度越低。如下圖所示,展示了「甲兵」、「兵戈」和「烽火」之間的餘弦相似度的可視化示意圖(點擊圖片可放大查看):
通過上述詞嵌入模型,similarity(「甲兵」,「兵戈」) = 0.75,similarity(「甲兵」,「烽火」) = 0.37,similarity(「兵戈」,「烽火」) = 0.48。則在這三個詞彙中,「甲兵」和「兵戈」之間的語義相關度最高,其次是「兵戈」和「烽火」,最次的「甲兵」和「烽火」。
這種給一個數值來識別詞彙相關不相關的方法優點在於表達簡潔、計算高效,比如接下來將要進行的熱門詩歌題材發現/聚類。但是,這種詞彙相關度的計算沒有把詞彙之間的相關度的「因果路徑」直觀的反映出來。
那麼,有沒有一種直觀的方法來展示詞彙之間的語義相關性,並且能看到為什麼它們是存在這樣的關聯關係(也就是找到詞彙關聯路徑或者語義演變路徑)?
答案是---當然有。
我們需要把這個找尋詞彙語義演變路徑的任務轉換成一個TSP問題(旅行商問題)。
3.2 利用A*算法找尋詞彙之間的語義演變路徑
TSP問題(Traveling Salesman Problem)又譯為旅行推銷員問題,是數學領域中著名問題之一。假設有一個旅行商人要拜訪n個城市,他必須選擇所要走的路徑,路徑的限制是每個城市只能拜訪一次,而且最後要回到原來出發的城市。路徑的選擇目標是要求得的路徑路程為所有路徑之中的最小值。
回到詞彙相關度度量的問題上來,如果我們能在上述訓練得到的詞嵌入空間中找到兩個詞彙之間的最短「語義演變」線路,我們就能直觀的呈現出這2個詞彙之間產生語義關聯的「前因後果」。
要實現這個目的,有一個很棒的算法可以實現 ---A*算法(A* search algorithm)。
A*算法,也叫A*(A-Star)算法,是一種靜態路網中求解最短路徑最有效的直接搜索方法,也是解決許多搜索問題的有效算法。算法中的距離估算值與實際值越接近,最終搜索速度越快。下圖中(點擊圖片可放大查看),網狀結果即是之前構建的word2vec詞嵌入空間,節點是其中分布的詞彙,邊由字詞之間的餘弦相關度構成。
筆者基於上面的詞嵌入模型,結合A*算法來計算兩個詞彙之間的最短語義路徑,部分結果如下所示(點擊圖片可放大查看):
在上圖的5個詞彙對中,「漁樵」和「躬耕」之間的語義距離最短,也就是語義相關度最高,它們之間的語義演變路徑也就越短,中間只隔了2個詞彙;「燕市」和「寶婺」的語義距離最大,語義相關度最小,二者的語義演變路徑隔了12個詞彙。
可以看到,語義關聯性越弱(distance值越大)的兩個詞彙之間的最短語義演變路徑就越長,反之越短,所以語義距離與語義演變路徑長度呈正相關關係,語義關聯度與語義演變路徑呈負相關關係。
有了前面的詞嵌入模型和語義相關度做「鋪墊」,後續的熱門詩歌題材發現就水到渠成了~
4 用文本聚類進行熱門詩歌題材發現
先開宗明義,在本文中,關於「詩歌題材」中的「題材」二字的定義,筆者認為是:
作為詩歌創作材料的社會生活的某些方面,亦特指詩人用以表現作品主題思想的素材,通常是指那些經過集中、取捨、提煉而進入作品的生活事件或生活現象。一言以蔽之,寫景、摹物、抒情、記事、明理皆是「題材」。
因為事先不知道這54萬餘首詩歌中到底會存在多少個題材,所以筆者選取的聚類算法沒有預設聚類數這個參數,且兼顧運行效率和節省計算資源,能利用前面訓練好的word2vec詞嵌入模型和語義關聯度計算。
此時,有個很好的選擇 --- 社區發現算法中的Infomap。
4.1 基於社區發現的熱門詩歌題材發現
字詞是承載詩歌題材的最小語義單元,如「五雲山上五雲飛,遠接群峰近拂堤。若問杭州何處好,此中聽得野鶯啼」,看到其中的「五雲山」和「群峰」,則可以給該詩打上一個「山川巍峨」的題材標籤。由此,筆者接下來會基於社區發現算法,結合「詞彙簇群--->詞彙簇群語義特徵--->題材標籤」的思路來發現熱門詩歌題材。
先說說基於社區發現的大致原理。
我們知道,在社交網絡中,每個用戶相當於每一個點,用戶之間通過互相的關注關係構成了整個線上人際網絡。
在這樣的網絡中,有的用戶之間的連接較為緊密,有的用戶之間的連接關係較為稀疏。其中連接較為緊密的部分可以被看成一個社區,其內部的節點之間有較為緊密的連接,而在兩個社區間則相對連接較為稀疏。
如何去劃分上述的社區便稱為社區發現的問題。
基於社區發現算法的話題聚類/發現,在於挖掘詞彙語義網絡中居於頭部的大型「圈子」。
將詞彙擬人化,詞彙之間存在的相似度/關聯度可以視為詞彙之間的親密程度,那麼,詩歌題材發現任務可以看做是找到不同成員組成的「圈子」,圈子的特性可以根據其中的成員特徵來確定,換言之,題材的名稱可以根據其中聚合的詞彙的內涵來擬定,比如某個詞彙簇群中包含「衛霍」、「甲兵」、「徵戰」等詞彙,那麼這個題材可以命名為「戰爭」。示意圖如下所示(點擊圖片可放大查看):
運行社區發現算法後,居於頭部的熱門題材詞彙簇群的可視化呈現如下(點擊圖片可放大查看):
其中,不同顏色表徵不同的題材,字體大小代表其出現頻次,詞彙之間的距離遠近表徵其相關程度大小。
經聚類得到634個題材,根據熱度(題材下轄詞彙數量)的降序排列呈現最終結果,如下所示(點擊圖片可放大查看):
4.2 甄別熱門詩歌題材
在這一環節中,筆者的在於根據一些詩歌領域知識,找到上述運行結果中熱門題材及其下轄的題材專屬性詞彙。其中,「題材專屬性詞彙」的內涵主要有以下兩點:
詞彙不能再做進一步切割,否則詞義會發生變化,比如,「丈夫」在古漢語中的意義是「男子漢」,在一個獨立的詞彙,若將其切割為「丈」和 「夫」,則原意喪失殆盡;
詞彙僅在一個題材中出現,具有排他性,如「杖藜」只出現在「雲遊四方」這個題材中,不會出現在「金戈鐵馬」、「對酒當歌」、「悼亡故人」等其他詩歌題材中。
根據筆者在前文中的定義,寫景、摹物、抒情、記事、明理皆是「題材」,這裡的熱門題材甄別採取「抓大放小」的原則。
此外,雖然聚類出的結果較為理想,但還是存在些許噪音,比如,出現少許跟題材相關性不強的詞彙、題材區分度較低的詞彙、詞彙簇群中的詞彙過少(如低於10個)等,這些都是需要被刨除掉的情況。
經過筆者的仔細甄別,共甄別出23個熱門詩歌題材,分別是山川巍峨、田園躬耕、羈旅思鄉、金戈鐵馬、詠史懷古、詠物抒懷、贈友送別、愛情閨怨、悼亡故人、樓船畫舫、花開荼蘼、對酒當歌、騏驥駿馬、得道修仙、世事變遷、靜悟禪機、壯懷激烈、雲遊四方、黯然神傷、星宿璀璨、報效君恩、嚶嚶鳥語、蓑笠綸竿等,當然這些並不是全部的題材,限於筆者學識,仍然有大量題材沒有發掘出來。枚舉部分結果如下(點擊圖片可放大查看):
在這一環節,筆者根據對詩歌背景知識的了解,篩選出部分熱門詩歌題材,並形成題材對應的關鍵詞規則體系,後續可用於對這54萬餘首詩歌進行基於關鍵詞的詩歌題材分類。
值得注意的是,由於這一環節挑選關鍵詞過於苛刻,導致數量較少,規則體系不甚健全。所以,在對詩歌語料庫進行正式的詩歌題材分類前,筆者需要使用一些「小手段」,對上述熱門題材的關鍵詞規則進行擴充。
5 根據線性分類器特徵延伸關鍵詞
在這裡,筆者先利用已得到的熱門題材分類體系及其關鍵詞規則給這54萬餘首詩歌打上題材標籤,允許出現同一首詩歌命中多個標籤的情形。除去其中未命中題材標籤的數據,共計443,589行,其中多數詩歌打上了2個及以上的題材標籤。
部分結果如下所示(點擊圖片可放大查看):
有了帶標籤的數據以後,筆者將多標籤問題轉換為單標籤問題,再將上述詩歌文本及其對應的標籤「餵進」線性分類器,根據線性分類器的權重來找到每個類別下最具代表性的詞彙,也就是題材專有性詞彙。這裡選擇線性分類器而不是時下流行的深度學習分類器的原因就在於它的可解釋性,能讓我們清楚的知道是哪些顯著的特徵(此處是詞彙)讓詩歌分到這個題材類別下的。其大致原理如下圖所示(點擊圖片可放大查看):
在筆者測試的眾多線性分類器中,即RandomForestClassifier、Perceptron、PassiveAggressiveClassifier、MultinomialNB、RidgeClassifier、SGDClassifier,RidgeClassifier的區分效果最好,其F1_score為0.519,鑑於是詞袋模型,語義表示較為簡單,且原本是多標籤分類任務,這個結果尚可接受。基於RidgeClassifier的特徵詞彙權重的降序排列結果,可得到上述23個熱門詩歌題材分類中的若干題材專有性詞彙,部分結果展示如下(點擊圖片可放大查看):
這樣,各個類別各取TOP500詞彙,經過筆者的甄別和梳理後,各個題材關鍵詞規則得到了不同程度的擴充,使得該分類標籤體系可以較好的輔助完成詩歌題材多標籤分類任務,且後續可以結合分類結果做不斷的擴充。
基於這個更加完善的詩歌題材分類體系,筆者運行完之後得到58W+行數據,在之前的基礎上增加了14W+行數據,數據規模提升明顯!
至此,筆者第一個目標,即熱門詩歌題材標籤語料庫構建完畢,後續的文本挖掘任務可以在此基礎上進行。
由分類標籤及其分類模型反向推導其中最具代表性的特徵詞彙,這是一個「數據--->規律」的歸納過程,很好的體現了數據驅動思維;而模型將學習歸納得到的「經驗」推廣到新樣本的標籤預測任務中,則體現了「規則--->數據」的演繹過程。
6 基於分類標籤的各類統計分析
針對上述58W+行數據構成的詩歌題材語料庫,將其中的題材分類標籤和各類meta data(如風格、朝代、作者等)做交叉分析,得到很多有意思的分析結果。
6.1 詩歌題材&風格分析
將詩歌數據集的風格標籤和題材標籤進行交叉列表的成分佔比分析,得到的結果如下(點擊圖片可放大查看):
其中,可以發現一些明顯的統計描述性特徵:
「贈友送別」和「嚶嚶鳥語」這兩個題材在所有詩歌風格中的佔比都較高,是兩個較為「熱門」的題材;
「悼亡故人」和「壯懷激烈」這兩個題材在所有詩歌風格中的佔比都較低,是兩個較為「冷門」的題材。
6.2 題材標籤共現分析
前面的詩歌題材分類是多標籤分類,也就是可能會出現同一首詩歌對應多個題材標籤的情況。在這種情況下,我們可以進行題材標籤的共現分析,也就是多次同時出現的題材標籤,它們之間會存在一定的關聯性。
現對標籤共現的情況進行建模,得到的結果可視化呈現如下所示(點擊圖片可放大查看):
上圖中,線條的粗細表示共現的頻次多寡,越粗表示共現頻次越高,反之越低。其中,有幾對標籤對的共現頻率較高:
世事變遷 - 黯然神傷
羈旅思鄉 - 世事變遷
詠史懷古 - 蓑笠綸竿
世事變遷 - 金戈鐵馬
對酒當歌 - 世事變遷
悼亡故人 - 世事變遷
其中,「黯然神傷」和「世事變遷」的相關性最高,這個很好理解,畢竟「物是人事事休,欲語淚先流」,類似因感嘆逝事而傷感的詩句還有「人世幾回傷往事,山形依舊枕寒流」、「一生事業總成空,半世功名在夢中」;「羈旅思鄉」和「世事變遷」之間的相關性第二高,此類的詩句有「少小離家老大回,鄉音無改鬢毛衰」、「去日兒童皆長大,昔年親友半凋零」等。
此外,我們也可以發現,在出現2個及兩個以上題材標籤的詩歌中,「世事變遷」和其他題材同時出現的概率很大:世事變遷可能導致詩人黯然神傷;也可能是戰爭導致兵連禍結,產生出「興,百姓苦,亡,百姓苦」的感慨;抑或是「桃李春風一杯酒,江湖夜雨十年燈」的對酒當歌。
6.3 詩歌題材趨勢分析
筆者將詩歌數據集中的朝代按照時間順序由遠及近進行排列,並合併其中年代接近的朝代,將其與23個熱門詩歌題材做(佔比)交叉分析,得到下圖(點擊圖片可放大查看):
在上圖中,可以分別從橫向維度(朝代)和縱向(詩歌題材)維度來看。
從橫向維度上看,有兩個題材經久不衰,即「贈友送別」和「嚶嚶鳥語」。
古時候由於交通不便,通信極不發達,親人朋友之間往往一別數載難以相見,所以古人特別看重離別。離別之際,人們往往設酒餞別,折柳相送,有時還要吟詩話別,因此「贈友送別」就成為古代文人吟詠的一個永恆的題材。在這濃濃的感傷之外,往往還有其他寄寓:或用以激勵勸勉,如「莫愁前路無知己,天下誰人不識君」;或用以抒發友情,如「桃花潭水深千尺,不及汪倫送我情」;或用於寄託詩人自己的理想抱負,如「洛陽親友如相問,一片冰心在玉壺」;甚至洋溢著積極向上的青春氣息,充滿希望和夢想,如「海內存知己,天涯若比鄰」。
「嚶嚶鳥語」題材的詩歌一般用「比興」的手法來寄寓自己的情感,筆者所了解的有兩類:一是通過寫鳥語描摹詩人淡薄、回歸山野自然的平靜心境,這方面的詩王摩詰寫的最多,如「月出驚山鳥,時鳴春澗中」、「漠漠水田飛白鷺,陰陰夏木囀黃鸝」、「雉雊麥苗秀,蠶眠桑葉稀」等;二是通過子規(杜鵑)、鴻雁等意象來表達詩人淡淡的憂傷,如「楊花落盡子規啼,聞道龍標過五溪」的依依惜別之情、「兩邊山木合,終日子規啼」的思鄉歸家之情、「雁盡書難寄,愁多夢不成」的思君心切...
從縱向維度上看,隋末唐初時期除了上述提及的兩大熱門題材外,關於「報效君恩」題材的詩歌佔比較高。彼時適逢華夏第三次大一統,「貞觀之治」、「開元之治」這兩大盛世榮耀大唐在「朕即國家」的時代,廣大熱血青年渴望馳騁疆場,建功立業,報效國家。
此外,筆者也注意到,從金代到到當代,「花開荼蘼」、「羈旅思鄉」、「金戈鐵馬」和「靜悟禪機」等題材就一直葆有較高的熱度,結合前面提及的2大經久不衰的詩歌題材,這表明這段時期的詩歌創作方向具有一定的延續性。
從上表中,我們能有一些發現,但如果想更獲取一些更深層次、潛藏在表層數據中的信息,我們還需要用高階的數據挖掘方法將其轉換一下。在這裡,筆者使用多元對應分析的方法將其高維表示(也就是上面的21*23維的圖表)映射為二維表示(分解為2個二維矩陣,題材為23*2,朝代為21*2),從而更直觀的揭示出詩歌題材之間、詩歌題材與朝代之間的關聯關係,如下圖所示(點擊圖片可放大查看):
在上圖中,有兩類坐標---外圍有半徑圓圈的紅色點是朝代的,「x」的詩歌題材的坐標。
漢代的坐標「孤懸海外」是因為數據量過小,統計特徵不甚明顯,故筆者在這裡不做分析。
在圖的左上角,魏晉、南北朝、隋末唐初、隋這幾個朝代的圓圈重合度較高,說明它們的詩歌題材數量分布較為相似,聯想到這幾個朝代前後相繼,這又一次體現了詩歌創作具有時代延續性的特徵。
同樣,唐代及其以後的圓圈呈「扎堆狀」,標明它們的詩歌寫作題材的數量分布較為相似,反映出唐以降的朝代在詩歌創作題材方面的差異度較小,題材創作方向的創新性不高。究其原因,在於詩歌在唐代已經進化到「究極狀態」:
唐詩的題材和意境也幾乎無所不包,修辭手段的運用已達到爐火純青的程度。它不僅繼承了漢魏民歌、樂府傳統,並且大大發展了歌行體的樣式;不僅繼承了前代的五、七言古詩,並且發展為敘事言情的長篇巨製;不僅擴展了五言、七言形式的運用,還創造了風格特別優美整齊的近體詩。近體詩是當時的新體詩,它的創造和成熟,是唐代詩歌發展史上的一件大事。它把我國古曲詩歌的音節和諧、文字精煉的藝術特色,推到前所未有的高度,為古代抒情詩找到一個最典型的形式,至今還特別為人民所喜聞樂見。
唐詩代表了中華詩歌的最高成就,無疑是中華以及世界文壇上濃墨重彩的筆觸!這對於想要另闢新境的宋代詩人來說無疑是巨大的壓力。正如王安石和魯迅所言:
「世間好語言,已被老杜道盡;世間俗語言,已被樂天道盡」,
「我以為一切好詩,到唐朝已被做完,此後倘非翻出如來掌心之『齊天大聖』,大可不必再動手了」。
7 通過GPT-2生成表達流暢的詩歌
從某種程度上講,詩歌生成是從另一維度對詩歌進行深度分析。
生成什麼詩歌,跟詩歌生成模型「吃下去」什麼是息息相關的。詩歌生成模型的「生成」不是「無源之水」、「無本之木」,它是在充分學習和吸收前人的若干詩作後,習得了一定的「創作手法」,因而能生成效果尚可的詩歌。
同時,我們也能從生成的結果中發現詩歌創作的一些規律,做一些深入探究。
7.1 詩歌生成示例分析
在這一部分,筆者用於訓練詩歌生成模型的語料庫是基於熱門題材標籤體系得到的帶有題材標籤(目前是23個)的律詩(七言和五言)和絕句(七言和五言),它們都滿足詩歌的結構性、音調性和語義性的要求。
這裡筆者採用的是GPT2(Generative Pre-Training 2nd),它是一個無監督語言模型,能夠生成具有連貫性的文本段落,在許多語言建模任務基準中取得了領先級表現(數據量級和參數量級擺在那裡,當然跟它的後浪GPT3不能比...)。而且該模型在沒有任務特定訓練的情況下,能夠做到初步的閱讀理解、機器翻譯、問答和自動摘要。其核心思想可以總結為「給定越多參數以及越多樣、越大量的文本,無監督訓練一個語言模型或許就可讓該模型具備更強的自然語言理解能力,並在沒有任何監督的情況下開始學會解決不同類型的 NLP 任務」。
在文本的詩歌生成任務中,筆者從零到一訓練一個詩歌生成的GPT2模型,力求讓該模型學習到詩歌數據集中的各類顯性特徵(題材與詩歌的關係、詩歌與風格的關係、藏頭字和詩歌的關係等)和隱性特徵(主要是詩歌的韻律),其大致原理如下圖所示:
相比3年前筆者寫《用文本挖掘剖析近5萬首<全唐詩>》時用的LSTM詩歌生成模型,GPT2模型進步巨大:
生成的詩歌更加通順,每一聯的出句和入句的銜接也顯得更為自然
能成全局(即整首詩)著眼,記憶能力好,考慮上下文語境,前後生成的詩句緊密關聯,不會出現「跳題材」的情況
能學習到詩歌數據中較為隱性的特徵,如押韻、平仄、對仗、疑問語氣等
因擁有上述3個優勢,生成的詩歌「廢品率」大大降低
下面,筆者將「花式」呈現GPT2的詩歌生成能力:
1)生成的詩歌可能會和前人寫的詩句有一定的相關性,但是GPT2模型可以進行「魔改」,很難看出直接的「抄襲對象」,例如以下由GPT2模型生成的七言律詩,每一聯都能在語料庫中找到語義最為接近的一句:
戰鼙傳響徹神州,萬裡中原一白頭。
兵後英雄誰不死,眼前豪傑已無憂。
乾坤納納歸天地,歲月悠悠老鬥牛。
安得扁舟成獨往,五湖煙浪是東流。
2)很多生成的詩歌能較好的學習到韻律,比如符合《平水韻》的用韻規則:
上一個示例就是《平水韻》中七律平起(首句入韻)的一個樣例:
平平仄仄仄平平(韻)
仄仄平平仄仄平(韻)
仄仄平平平仄仄
平平仄仄仄平平(韻)
平平仄仄平平仄
仄仄平平仄仄平(韻)
仄仄平平平仄仄
平平仄仄仄平平(韻)
戰鼙傳響徹神州【州:十一尤】 押韻
萬裡中原一白頭【頭:十一尤】 押韻
兵後英雄誰不死
眼前豪傑已無憂【憂:十一尤】 押韻
乾坤納納歸天地
歲月悠悠老鬥牛【牛:十一尤】 押韻
安得扁舟成獨往
五湖煙浪是東流【流:十一尤】 押韻
另外再看兩個case:
即使一些詩歌不是嚴格的用韻(即一首詩只能押一個韻),也會自動採用鄰韻的方式來緩解韻律失和的問題。
3)對人張若虛的樂府舊題《春江花月夜》(因為是樂府詩,字數較多,未參與詩歌生成模型的訓練)中的若干詩句,採用對對子的方法生成下聯,毫無違和感:
【上聯】春江潮水連海平,海上明月共潮生 【下聯】古塔風雲繞山青,峰頂彩霞共靄暉
【上聯】灩灩隨波千萬裡,何處春江無月明 【下聯】幽幽如幻五十年,此間塵世有風流
【上聯】江流宛轉繞芳甸,月照花林皆似霰 【下聯】山勢氤氳浮翠靄,風梳楊柳欲飛煙
【上聯】斜月沉沉藏海霧,碣石瀟湘無限路 【下聯】春江漫捲漾煙雨,長河日夜有緣人
【上聯】不知江月待何人,但見長江送流水 【下聯】只向漁家尋舊跡,試問何處答耕桑
【上聯】白雲一片去悠悠,青楓浦上不勝愁 【下聯】碧水千層橫漠漠,赤雁南來正飛歌
【上聯】鴻雁長飛光不度,魚龍潛躍水成文 【下聯】漁家小住月相隨,鷗鷺往來船載歌
【上聯】此時相望不相聞,願逐月華流照君 【下聯】何處覓人猶有約?欲邀仙子去還家
【上聯】江畔何人初見月?江月何年初照人 【下聯】天下有地可藏龍,海風哪處可觀心
【上聯】江天一色無纖塵,皎皎空中孤月輪 【下聯】楊柳千絲猶有夢,蒙蒙江上滿船燈
4)嵌入筆者所在的公司名稱---「達觀數據」作為藏頭字,分別以「山川巍峨」、「得道修仙」、「田園躬耕」為題材,生成三首七言律詩:
《其一》
達摩七十二峰高,天下英雄氣鬱陶。
觀海三山雲縹緲,登樓千仞勢岧嶢。
數莖白髮身長健,百尺丹梯路不遙。
據險未能窮勝覽,憑欄徙倚思飄颻。
《其二》
達者由來本自然,人間何處不神仙。
觀空已悟三千界,閱世方知二十年。
數點青山隨杖屨,一行白鳥下雲煙。
據鞍更欲凌風去,回首孤城落照邊。
《其三》
達摩萬象付評量,造化從來自一方。
觀物有時開口笑,看人無處著心忙。
數間茅屋臨流水,幾個漁舟傍夕陽。
據得此中真面目,乾坤何地不滄桑。
5)在訓練和生成式引入更多的詩歌標籤信息,如風格、題材和藏頭字,引導GPT2朝期望的方向的生成詩歌。訓練好模型後,筆者輸入詩歌風格(七言絕句、七言律詩)、藏頭字(滄海月明)和題材(金戈忒嗎、世事變遷、靜悟禪機等),生成如下詩歌:
七言絕句<sep>戢戈為武<sep>金戈鐵馬<sep>戢兵十萬羽林郎,戈壁縱橫百戰場。為報將軍能破敵,武功原是好封疆。
七言律詩<sep>滄海月明<sep>金戈鐵馬<sep>滄海乾戈一戰場,西風吹淚灑衣裳。海門萬裡烽煙息,天地千秋雨露涼。月下樓船喧鼓角,夜深笳鼓動旌旗。明年此夕登臨處,獨倚闌幹對夕陽。
七言律詩<sep>滄海月明<sep>金戈鐵馬<sep>滄波渺渺接長河,極目蒼茫感慨多。海上樓船三百裡,雲間樓閣五千過。月明古渡漁歌起,秋入荒城鼓角和。明日相逢又相別,不堪回首淚滂沱。
七言律詩<sep>滄海月明<sep>金戈鐵馬<sep>滄波渺渺白鷗群,極目蒼茫萬裡雲。海上樓船三萬裡,天邊烽火一孤墳。月明古渡漁歌起,霜落空山雁影分。明日相逢又相別,故鄉回首淚紛紛。
七言律詩<sep>滄海月明<sep>世事變遷<sep>滄桑劫火幾興亡,回首滄桑感慨傷。海上樓臺空劫火,人間禾黍自悲涼。月明古渡漁歌起,風靜寒潮雁影長。明日相逢又相別,故鄉回首一悽涼。
七言律詩<sep>滄海月明<sep>靜悟禪機<sep>滄浪萬頃白鷗群,此地曾經此地分。海上樓臺今夜月,山中樓閣幾秋雲。月明古寺僧初定,潮落空江雁正聞。明日相逢又相別,不知何處是離群。
上述生成結果,平仄符合,押韻亦可,詩意也不錯。不敢說很完美,但至少很多人寫不出如此觀感的詩歌。
此外,上述按題材生成的結果,筆者進行了大量的題材詩歌生成測試,結果表明詩歌題材和生成詩歌之間的關聯性較高,這也從側面驗證了筆者上述構建的詩歌題材語料庫具有一定的合理性。
此外,筆者還通過生成的詩句發現了古今詩歌表達方面的一些差異,例如,筆者以「金戈鐵馬」作為生成題材,分別用毛主席《人民解放軍佔領南京》和陳老總的《梅嶺三章》中的首聯打頭,各生成9首詩歌,結果如下(點擊可查看大圖):
上面兩張圖中佔據中間C位的是原詩歌,其餘的詩歌由毛主席和陳老總詩歌的首聯「引導」而成,基本含有「金戈鐵馬」相關的意象,題材貼合度較高,大都跟徵戰、戌邊、殺敵保國有關,比如:
聞道漢家多戰伐,將軍今日重南邦。
旌旗影動三軍肅,刁鬥聲傳五夜長。
中原戰血三千裡,南國英魂一斷腸。
西風鼓角寒吹雁,南國旌旗夜渡河。
...
然而,可能跟學習了大量封建時代的詩歌有關,這些生成的詩歌到末尾大都是一個悲情的基調,略顯消極,如以下幾句:
從此邊城多戰伐,不須笳鼓更悲涼。
一路寒聲送歸雁,秋深不見客愁窗。
我欲從君尋舊隱,扁舟重訪草堂堂。
獨有英靈知此意,不堪回首淚沾裳。
回首故園歸未得,西風蕭瑟動悲歌。
回首不堪惆悵事,夕陽芳草滿汀波。
...
上述生成的詩句缺乏革命主義的樂觀豪情,這是封建時代的詩歌不具備的特質,但這恰好毛主席和陳老總這兩首詩歌的與眾不同之處。且看這兩句:
天若有情天亦老,人間正道是滄桑。
投身革命即為家,血雨腥風應有涯。「
文章合為時而著,歌詩合為事而作」,上述的結果也恰恰從側面體現了詩歌創作具有時代感和現實感,儘管是寫同一題材,但由於詩人的人生軌跡和面臨的時代背景不一樣,胸中所內含的氣象也大不相同。
上述由GPT2生成的詩歌看起來都還不錯,很多到了以假亂真的地步,這種情況下,我們該如何甄別出其中哪些是人寫的,哪些是機器寫的?
機器寫作詩歌歸根到底還是一個統計學問題,「解鈴還須繫鈴人」,甄別「真偽」的事情還得統計學來解決。
7.2 人機詩歌創作的差異比較
詩歌生成建模大致的原理是:通過大量詩歌語料,詩歌生成模型能學習到任一詩句中相鄰的字詞之間的依賴關係,比如出現一個「漠」,GPT2按照學習到的經驗,會猜測接下來會出現哪個字,這些字都會以概率的形式「存放」在GPT2模型的「記憶」之中,如:
「漠」:0.1205,
「北」:0.0914,
「然」:0.0121,
「視」:0.00124,
...
一般情況下,機器「作詩」時會選擇過往出現機率最高的字,以此類推,直到碰到「終止符」才結束,逐漸生成整首詩歌。
這是最簡單的情形,生成的效果也就非常一般,很多時候是文理不通。
為了保證生成效果,一般會(同時)用到一些複雜的生成策略,如Beam Search、Top-k sampling、Top-p sampling(NUCLEUS SAMPLING,核採樣)、Repetition_penalty(對重複性進行懲罰)、Length_penalty(對生成過長的詩句進行懲罰)等,這樣會兼顧詩歌生成的一些其他因素,如流暢度、豐富度、一致性等,詩歌生成的效果也能得到較大的提升。
筆者基於哈佛大學的GLTR( Statistical Detection and Visualization of Generated Text)來探究下機器和人作詩時的一些差異,該工具輸入的是詩歌,輸出的是機器和人作的詩歌的字出現概率分布統計,我們從中可以發現詩歌「鍊字」的一些奧秘。筆者試舉一例:
在上圖中,色塊的顏色代表的是字所在的概率區間,紅色代表出現概率TOP10的字,黃色的是TOP100,綠色的是TOP1000,紫色的是TOP10000。
從結果中,我們可以看到機器作詩時,紅色和黃色的字概率分布區間佔比較大,逐字生成時一般是從頭部的字概率分布中來取,從而導致會詩句生成較為常見的表達;人創作詩歌時,各顏色代表的字概率分布區間佔比較為接近,至少是差異不大,最終導致詩歌的表達千變萬化,不落俗套。
古時詩人作詩,重在「鍊字」。鍊字,指錘鍊詞語,指詩人經過反覆琢磨,從詞彙寶庫中挑選出最貼切、最精確、最形象生動的詞語來描摹事物或表情達意。從這個角度來看,具有統計學意義的「選字」策略基本不可取 --- 不是詞不達意就是容易落「俗套」。
比如,陶淵明的那句「採菊東籬下,悠然見南山」中「見」換成「望」就不好。雖然按從詩歌數據集學到的概率來講,「望」在過往出現的概率遠大於「見」,但「見」通「」現,有「無意中看見」的含義,標明作者是不經意間抬起頭來看見南山,表達了整個詩句中那種悠然自得的感觸,好像在不經意間看到了山中美景,符合「山氣日夕佳,飛鳥相與還」這種非常自然的、非常率真的意境,而「望」則顯得有些生硬。
8 將詩歌翻譯成通俗易懂的白話文
詩歌翻譯,也就是將文言文色彩濃重、一般人不易看懂的詩歌翻譯成現代人容易理解的白話文。
筆者此處用到的模型是兩個BERT構成的Encoder-Decoder,目標是輸入一句或者一首詩歌,生成相應的白話文翻譯。考慮到古現代漢語存在大量詞彙方面的語義延續性,不像中英互譯這樣,源語句和目標語句之間的語義和語法結構差異極大,它們會有很多共享詞彙,所以訓練的語料數量可以適當少些。詩歌翻譯模型會從大量的翻譯語句對中學習到它們之間的語義對應關係,而且是以較為流暢(較理想的情況下)的形式進行輸出。
以下是簡要原理圖:
筆者編寫爬蟲採集了某詩歌分享網站上的若干詩歌及其翻譯數據,經清洗後,得到3萬條訓練語句。部分訓練數據如下表所示:
訓練模型後,筆者選取部分未出現在訓練集中的語句作為文本翻譯的示例,部分示例如下:
【詞】問君何事輕離別,一年能幾團圓月。楊柳乍如絲,故園春盡時。(菩薩蠻·問君何事輕離別,清,納蘭性德)
【翻譯1】你在什麼時候才能夠回家?只有那美麗的桃花已經過去了。
【翻譯2】請你問我為什麼這樣的輕易消失呢?一年裡正是三次月下的清明之夜。楊柳剛像絲絲般柔軟,就好似在家鄉春天已經快要過去了。
【詞】山抹微雲,天粘衰草,畫角聲斷譙門。水流明月,風送殘花,詩心韻動江樓。(《滿庭芳·山抹微雲》,前一句作者是宋,秦觀,後一句作者是GPT2)
【翻譯】遠處的山峰上是一片輕薄的薄雲,在天空中飄散著;烏鴉點綴在枯草叢裡,號角聲響徹四周。明月照耀下清輝的銀河映射向西邊,傳來陣陣寒梅花的音樂,這時節讓人肝腸寸斷。
【對聯】帝道真如,如今都成過去事。民心所向,自古都是往來人。(楊度自輓聯,上聯作者是民國,楊度,下一聯作者是GPT2)
【翻譯】京城大道實在了解我的知識,就像當年地方一樣經歷過了。百姓心裡依舊保護著百姓生活世界,自古以來都是從前人們來往的人間吧!
【詩句】萬戶千門入建章,金繩界路柳絲黃。和風偏度樂聲細,晴日自隨宮泉長。(《宮詞·萬戶千門入建章》,宋,王仲修)
【翻譯】皇上的宅院已經關閉了建立功名利祿,皇家大道路兩旁的楊柳依舊是那樣的嫩芽。春天來到這裡,宮殿高聳入雲霄,宮殿中傳出一陣清脆婉轉的流水。
【詩句】沅湘兩水清且淺,林花夾岸灘聲激。洞庭浩渺通長江,春來水漲連天碧。(《少年湖南歌》,民國,楊度)
【翻譯1】沅江兩岸的流淌在這裡是多麼高遠呢?樹叢生的野草和小洲環繞著江面,河畔的波濤好像是那樣寬闊無際;春天來了時節,水面上漲起伏著一片青色。
【翻譯2】沅江兩岸的流淌著一片清澈的江水清澈,茂密的樹林環繞在河岸上。洞庭湖廣闊無際,春水滔滔不斷地流向遠方。
【翻譯3】瀟湘兩岸的流淌著一片清澈的水,樹林間的花瓣隨風飄蕩。洞庭湖廣闊無際,波濤洶湧,波光粼粼,好像是天空相接。
【翻譯4】沅水湘江清澈見底,水波蕩漾,岸邊樹木繁茂如淺的流動。洞庭湖浩淼遠望去,水天相接處連成一片。
從結果上來看,3萬來句的效果還馬馬虎虎,很多翻譯不是直譯過來的,更傾向於「意譯」,機器翻譯的時候會「腦補」一些場景,如對「山抹微雲,...,詩心韻動江樓」的翻譯,機器能夠「揣摩」出「這時節讓人肝腸寸斷」,開始「有內味」了。
如果採用一些手段擴充下語料,如將整首詩歌和對應翻譯逐句拆分、對白話文部分進行文本增強(同義詞替換、隨機插入、隨機交換等)和將意譯改為直譯等,訓練處的模型可能會更強大些,翻譯效果能提升不少。
結語
通過上述詩歌語料庫分析流程,筆者想說一下對於(文本)數據挖掘的一些看法:
所謂挖掘,通常帶有「發現、尋找、歸納、提煉」等內涵,既然需要去發現和提煉,那麼,所要找尋的內容往往都不是顯而易見的,而是「隱蔽」和「藏匿」於文本之中,或者是人無法直接在大範圍內發現和歸納出來的。如果要抽絲剝繭,需要結合領域知識(如文中的詩歌常識),運用多種分析手段(如文中的各類NLU和NLG方法),有時甚至需要逆向思維(如文中的詩歌生成),且各類分析最好是一個前後相繼、互為補充有機整體,這樣才能以最高的效率來完成文本數據的探索任務。
參考資料:
《數學與文學的共鳴》,丘成桐
《迦陵說詩.嘉瑩說詩講稿》, 葉嘉瑩
《文本數據管理與分析》,翟成祥
《文本數據挖掘》,宗成慶
《古代漢語基礎》,吳鴻清
《詩詞格律》,王力
《語言的科學》,諾姆.喬姆斯基
《現代漢語詞彙學教程》,周薦
《語言的認知研究和計算分析》,袁疏林
《自然語言處理的認知方法》,Bernadette Sharp
《自然語言處理入門》,何晗
https://github.com/Werneror/Poetry
https://github.com/kpu/kenlm
https://github.com/jiaeyan/Jiayan
《Catching a Unicorn with GLTR: A tool to detect automatically generated text》,http://gltr.io
《Better Language Models and Their Implications》,https://openai.com/blog/better-language-models/
《自由度+凝固度+統計的新詞發現》,https://blog.csdn.net/qq_39006282/article/details/91357603
ABOUT | 作者
@蘇格蘭折耳喵:達觀數據高級解決方案經理。擅長數據分析和可視化表達,熱衷於用數據發現洞察,指導實踐。