用Python分析《紅樓夢》:見證了賈府的興衰,你是否還能“笑道”世事無常__Python

來源:互聯網
上載者:User

沒讀過《紅樓夢》也能知道前後四十回是不是一個作者寫的。很久以前,資料俠黎晨,用機器學習的演算法分析了《紅樓夢》,認為後四十回和前八十回內容上有明顯差距。不過,資料俠樓宇卻不這麼認為,他覺得原先的判定方法不夠嚴謹,於是他使用了無字典分詞的方式,剔除了情節對分析的影響,再次用機器學習的演算法分析了這部文學名著。


本文授權轉自DT資料俠(ID:DTdatahero)

作者 | 資料俠樓宇



 ▍構建全文索引與全文字典


兩個月以來,我通過互連網自學了一些文本處理的知識,用自然語言處理和機器學習演算法對《紅樓夢》進行了一些分析。這個過程中我找到了一些有趣的發現。


我開始做這件事情是因為之前看到了一篇挺好玩的文章,大概內容是,作者用“結巴分詞”這個開源軟體統計了紅樓夢中各詞彙的出現次數(也就是詞頻),然後用詞頻作為每個章回的特徵,最終用“主成份分析”演算法把每個章回映射到三維空間中,從而比較各個章回的用詞有多麼相似。(DT君註:資料俠黎晨原文《從沒看過紅樓夢,如何用機器學習判定後40回並非曹雪芹所寫》)作者的結論是後四十回的用詞和前八十回有明顯的差距。


我覺得文章有兩個小問題:首先,作者用的結巴分詞裡的詞典是根據現代文的語料獲得的,而《紅樓夢》是半文半白的,這樣的分詞方法準確性存疑;其次,雖然作者用《三國演義》做了對比,但是依然沒有有力地證明用詞差異沒有受到情節變化的影響。於是我決定自己做一遍實驗,用無字典分詞的方法來分詞,並且嘗試剔除情節對分析的影響,看看結果會不會有所不同。


在處理文章之前,我需要建立一個全文索引。這樣是為了快速地尋找原文內容,加速後面的計算。我使用了尾碼樹這個結構作為索引,用了Ukkonen演算法快速地建立了整篇《紅樓夢》的尾碼樹(Ukkonen 演算法的速度非常快,用專業的語言描述,它的時間複雜度是 O(n))。這樣我們就有了全文索引了。


接下來我們就要構建一個字典了。


等等,我們不是要無字典分詞嗎,為什麼還要製作字典。其實無字典分詞並不是完全不用字典,只是說字典是根據原文產生的,而不是提前製作的。為了進行分詞,我們還是需要先找出文章中哪些內容像是單詞,才能確定如何進行切分。


那麼怎麼確定哪些內容像單詞呢。最容易想到的方法就是:把所有出現次數高的片段都當成單詞。聽上去很有道理,所以我們可以試一試,用尾碼樹查詢紅樓夢中的所有重複的片段,然後按出現次數排個序:


寶玉(3983)、笑道(2458)、太太(1982)、什麼(1836)、鳳姐(1741)、了一(1697)、賈母(1675)、一個(1520)、也不(1448)、夫人(1437)、黛玉(1370)、我們(1233)、那裡(1182)、襲人(1144)、姑娘(1142)、去了(1090)、寶釵(1079)、不知(1074)、王夫人(1061)、起來(1059)


上面是出現頻率前20的片段,括弧內是出現次數。可以看到效果還不錯,很多片段都是單詞。然而,排在第六名的“了一”明明不是個單詞,出現次數卻比賈母還要高。可見這樣的篩選方法還是有一定問題的。而且,這樣被誤當成單詞的片段還有很多,例如“了的”、“的一”之類的。


為了排除這樣的組合,我們可以用“凝固度”來進行進一步地篩選。凝固度可以排除單字的頻率對組合頻率的影響。經過實驗,我發現整體效果還是不錯的。


DT君註:凝固度指的是,一個片段出現的頻率比左右兩部分分別出現的頻率的乘積高出多少倍。值得注意的是,頻率表示的是出現的比例,而頻數表示的是出現的次數。凝固度的思想是,如果片段實際出現的機率比被隨機組合出來的機率高出很多倍,就說明這樣的組合應該不是意外產生的,而是有一些關聯的。這個關聯很可能就是因為這個片段是一個不可分割的整體,也就是單詞。


然而凝固度也有一定的問題。我們會發現還是有很多片段是半個詞,且也具有很高的凝固度。例如:“香院”(完整的詞應該是“梨香院”)、“太太太太”(完整的詞應該是“老太太太太”)。


想想也有道理,這些片段雖然是半個詞,但是它們確實也跟完整的單詞一樣是“凝固”在一起的。所以,光看凝固度是不夠的,還要通過上下文判斷這個詞是否完整。


為了排除掉不完整的單詞,我們可以使用自由度來繼續過濾,自由度描述的是一個片段相鄰的字有多麼的不固定,一個真正的詞應該相互之間的聯絡應

該是獨特的,不太會出現上文說的情況。也就是說如果片段的自由度比較高,就說明這個詞應該是完整的。


DT君註:自由度的思想是,如果一個組合是一個不完整的單詞,那麼它總是作為完整單詞的一部分出現,所以相鄰的字就會比較固定。比如說,“香院”在原文中出現了 23 次,而“梨香院”出現了 22 次,也就是說“梨”在“香院”的左邊一起出現的頻率高達 95.7%,所以我們有把握認為”香院”不是完整的單詞。而自由度描述的就是一個片段的相鄰字有多麼的多樣、不固定。


有了這些明確的評判標準,我們就可以把單詞篩選出來了。我最終選擇的判斷標準是:出現次數大於等於5,且凝固度、左側自由度、右側自由度都大於1。然而這個標準還是太寬鬆了。於是,我又設計了一個公式,把這些資料綜合起來:

 也就是說,我簡單粗暴地把凝固度和自由度乘了起來,作為每個片段的分數。這樣只要其中一個標準的值比較低,總分就會比較低。於是我的判斷標準裡又多了一條:總分還要大於等於100。


經過層層遴選之後,單詞表初步成型了。我從最終結果中隨機抽取了100個條目,其中有47個是希望得到的單詞:這意味單詞表的正確率只有一半左右。不過,在錯誤的條目裡,很多條目的切分其實是正確的,只是有好幾個詞粘到了一起。所以其實我們沒有必要通過調高篩選標準的方法來進行更嚴格的過濾了。隨後分詞演算法將會解決單詞沒有被切開的問題。


此外,根據字典的正確率和字典的大小,我計算出紅樓夢的詞彙量大概是 1.6 萬。



 ▍維特比演算法找出最具效率的分詞方案


之前在篩選單詞的時候,思路就是用各種各樣的數值標準進行判斷。而對於“分詞”這個看似更加困難的問題,思路也是類似的:制定一個評價切分方案的評分標準,然後找出評分最高的切分方案。


評分標準是什麼呢。最簡單的標準就是,把切分之後每個片段是單詞的機率都乘起來,作為這個切分方案正確的機率,也就是評分標準。我們假設,一個片段是單詞的機率,就是這個片段在原文中的出現頻率。


有了評分標準之後,還有一個問題:如何找出分數最高的切分方案呢。肯定不能一個一個地嘗試每一種方案,不然速度實在是太慢了。我們可以用一個數學方法來簡化計算:維特比演算法。


維特比演算法本質上就是一個動態規划算法。它的想法是這樣的:對於句子的某個局部來說,這一部分的最佳切分方案是固定的,不隨內容相關的變化而變化;如果把這個最佳切分方案儲存起來,就能減少很多重複的計算。我們可以從第一個字開始,計算前兩個字,前三個字,前四個字……的最佳切分方案,並且把這些方案儲存起來。


因為我們是依次計算的,所以每當增加一個字的時候,我們只要嘗試切分最後一個單詞的位置就可以了。這個位置前面的內容一定是已經計算過的,所以通過查詢之前的切分方案即可計算出分數。


在構造單詞表的時候,我計算了每個片段有多麼像單詞,也就是分數。然而,後面的分詞演算法只考慮了片段出現的頻率,而沒有用到片段的分數。於是,我簡單粗暴地把片段的分數加入到了演算法中:把片段的頻率乘上片段的分數,作為加權了的頻率。這樣那些更像單詞的片段具有更高的權重,就更容易被切分出來了。


還有一個小最佳化。我們知道,一般中文單詞的長度不會超過四個字,因此在程式枚舉切分方法的時候,只需要嘗試最後四個切分位置就可以了。這樣就把最長的切分區段限制在了四個字以內,而且對於長句子來說也減少了很多不必要的嘗試。


抽查程式運行結果後發現,最終程式分詞演算法的準確率是85.71%(意義是程式切開的位置有多少是應該切開的),召回率是75.00%(意義是應該切開的位置有多少被程式切開了)。這個結果看上去不是很高,因為大部分開源的分詞軟體準確率都能達到90%以上,甚至能達到97%以上。不過,畢竟我用的是無字典的分詞,而且演算法也比較簡單,所以我還是比較滿意的。


但是其中詩詞的分詞更難一些,準確率相比其他部分低了10%左右。這也在情理之中,因為詩詞中有很多不常用詞,有些詞甚至只出現過一次,所以電腦很難從統計資料中發掘資訊。



 ▍統計結果說:賈府的人很愛“笑”


完成分詞以後,詞頻統計就非常簡單了。我們只需要根據分詞結果把片段切分開,去掉長度為一的片段(也就是單字),然後數一下每一種片段的個數就可以了。


這是出現次數排名前20的單詞:


寶玉(3940)、笑道(2314)、鳳姐(1521)、什麼(1432)、賈母(1308)、襲人(1144)、一個(1111)、黛玉(1102)、我們(1068)、王夫人(1059)、如今(1016)、寶釵(1014)、聽了(938)、出來(934)、老太太(908)、你們(890)、去了(879)、怎麼(867)、太太(856)、姑娘(856)


通過分詞後的詞頻,我們發現《紅樓夢》中的人物戲份由多到少依次是寶玉、鳳姐、賈母、襲人、黛玉、王夫人和寶釵。然而,這個排名是有問題的,因為”林黛玉”這個詞的出現次數還有267次,需要加到黛玉的戲份裡,所以其實黛玉的戲份比襲人多。


同理,“老太太”一般是指賈母,所以賈母的戲份加起來應該比鳳姐多。正確的排名應該是寶玉、賈母、鳳姐、黛玉、襲人、王夫人和寶釵。


此外,我們還發現《紅樓夢》中的人物很愛笑,因為除了人名以外出現次數最多的單詞就是“笑道” : )


我把完整的詞頻表做成了一個網頁,感興趣的話可以去看一下:紅樓詞表。

終於做完了分詞,又離目標靠近了一大步。現在,我可以用之前看到的那篇文章裡提到的PCA演算法來分析章回之間的差異了。不過在此之前,我想先反思一下,到底應該用哪些詞的詞頻來進行分析。


在很多用PCA分析《紅樓夢》的博文裡,大家都是用出現頻率最高的詞來分析的。然而問題是,萬一頻率最高的詞是和情節變化相關的呢。為了剔除情節變化的影響,我決定選出詞頻隨情節變化最小的單詞來作為每一章的特徵。而我衡量詞頻變化的方法就是統計單詞在每一回的詞頻,然後計算標準方差。為了消除單詞的常用程度對標準方差的影響,我把標準方差除以該單詞在每一回的平均頻數,得到修正後的方差,然後利用這個標準來篩選特徵詞。


最終,我選擇了詞頻變化最小的50個詞作為特徵,每個詞的修正後標準方差都小於0.85。理論上,有了特徵之後,我們就可以比較各個章節的相似性了。然而問題是,現在我們有50個特徵,也就是說現在的資料空間是 50 維的,這對於想象四維空間都難的人類來說是很難可視化的。對於高維資料的可視化問題來說,PCA是一個很好用的數學工具。


我利用PCA,把五十個詞的詞頻所構成的五十個維度壓縮到二維平面上。把壓縮後的資料點畫出來,發現是這個樣子:

 

∆ 圖中每個圓圈代表一個回目。圓圈內是回目編號,從 1 開始計數。紅色圓圈是 1-40 回,綠色圓圈是 41-80回,藍色圓圈是 81-120 回。


八十回以後的內容(藍色)大部分都集中在左下角的一條狹長的地區內,很明顯地和其他章回區分開來了。莫非《紅樓夢》的最後 40 回真的不是同一個作者寫的。。


別著急,分析還沒結束。PCA的一個很重要的優點就是,它的分析結果具有很強的可解釋性,因為我們可以知道每一個原始特徵在壓縮後的特徵中的權重。從上圖中可以看到,後40回的主要區別在於成分二(component 2)的數值。因此我們可以看一看每一個詞的詞頻在成分2中的權重排名(括弧內為權重):


笑道(0.883)、我們(0.141)、一個(0.133)、你們(0.128)、兩個(0.113)、說著(0.079)、咱們(0.076)、這個(0.063)、聽了(0.052)、還有(0.046)、一面(0.045)、來了(0.037)、都是(0.032)、不過(0.028)、去了(0.027)、又不(0.025)、出去(0.021)、這樣(0.018)、如今(0.016)、這裡(0.016)、還不(0.011)、見他(0.011)、出來(0.010)、就是(0.010)、一時(0.008)、起來(0.005)、只見(0.002)、不是(0.002)、下回分解(0.000)、不得(-0.001)、也不(-0.001)、話說(-0.002)、的人(-0.005)、不知(-0.007)、那裡(-0.009)、叫他(-0.011)、不敢(-0.011)、自己(-0.011)、不能(-0.017)、什麼(-0.019)、所以(-0.020)、只是(-0.023)、知道(-0.026)、進來(-0.036)、說道(-0.046)、怎麼(-0.050)、只得(-0.056)、沒有(-0.077)、聽見(-0.092)、寶玉(-0.312)


我發現,“笑道”這個詞不僅是除了人名以外出現次數最多的單詞,而且在PCA結果中的權重也異常地高(0.88),甚至超過了“寶玉”的權重的絕對值(0.31)。為了搞明白這個詞為什麼有這麼大的權重,我把“笑道”的詞頻變化畫了出來:

 

∆ 圖中橫座標是章回編號,縱座標是“笑道”的詞頻


可以發現,“笑道”的詞頻是先增加再減少的,這不禁讓我聯想到了賈府興衰的過程。莫非“笑道”的詞頻和賈府的發展狀況有關。


有趣的是,“笑道”的詞頻頂峰出現在第50回左右,而有些人從劇情的角度分析認為賈府的鼎盛時期開始於第48、49回,恰好重合。


也許“笑道”這一看似平常的詞彙確實側面反應了賈府的興衰史呢。雖然因果關係有待考證,不過想想也有一點道理,畢竟只有日子過的好的時候人們才會愛笑。


“笑道”這個詞似乎和情節的關係比較大,並且嚴重影響到了我們的分析。此外,“寶玉”作為一個人名,它的權重的絕對值也比較大,也可能是受到了情節的影響。因此,我決定把這兩個詞“拉黑”,用剩下的48個詞的詞頻做特徵,再次進行PCA分析。


我發現這樣修改特徵之後,後40回確實已經不像之前那麼聚集了,不過還是可以看出一點聚集的趨勢。這說明之前PCA結果確實因為“笑道”而受到了劇情的幹擾。


而去掉“笑道以後四十回依然有聚集的趨勢,說明去掉幹擾後這些章回還是有一定的相似性的。所以,我有點把握認為《紅樓夢》前八十回和後四十回的用詞是有一些差異的。不過因為難以完全排除劇情的影響,所以我也還不敢下定論。


雖然沒有完全解決紅樓夢的作者是不是同一個人的問題,不過這個過程中誤打誤撞產生的發現也是挺有意思的,比如“笑道”的詞頻變化和賈府興衰史的有趣重合。更重要的是,看似枯燥的數學公式可以做出這些好玩的分析。


Math is fun。


註:本文是作者《用Python分析紅樓夢》文章的編輯版,文中圖片均來自作者


本文資料俠樓宇,一位熱愛技術的理工男。曾經的 OIer,現已退坑。同時也對機器學習、網頁製作和攝影感興趣。現在美國讀本科。




猜你喜歡


詳解 | 如何用Python實現機器學習演算法

Python 3 尷尬了這麼久,終於有救了

經驗 | 如何高效學Python。

一文總結學習Python的14張思維導圖

如何在Python中用LSTM網路進行時間序列預測

瘋狂上漲的 Python,開發人員應從 2.x 還是 3.x 著手。

2017年首份中美資料科學對比報告,Python受歡迎度排名第一,美國資料工作者年薪中位元高達11萬美金


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.