• <bdo id="f9ffj"><progress id="f9ffj"></progress></bdo>
  • <nav id="f9ffj"></nav>

      1. <var id="f9ffj"><mark id="f9ffj"></mark></var>
        <var id="f9ffj"></var>
          北京物流信息聯盟

          用Python 分析《紅樓夢》

          YESLAB 2022-07-26 07:46:36


          1、前言

          兩個月以來,我通過互聯網自學了一些文本處理的知識,用自然語言處理和機器學習算法對《紅樓夢》進行了一些分析。這個過程中我找到了一些有趣的發現,所以我想寫一篇文章,既?與大家分享和討論實驗結果,也順便做一個整理和總結。(其實雖說是兩個月,但是中間停頓了一段時間,真正在做的時間大概是兩周左右)

          我開始做這件事情是因為之前看到了一篇挺好玩的文章,大概內容是,作者用“結巴分詞”這個開源軟件統計了紅樓夢中各詞匯的出現次數(也就是詞頻),然后用詞頻作為每個章回的特征,最終用“主成份分析”算法把每個章回映射到三維空間中,從而比較各個章回的用詞有多么相似。(文章地址:用機器學習判定紅樓夢后40回是否曹雪芹所寫)作者的結論是后四十回的用詞和前八十回有明顯的差距。

          看完文章之后,我覺得有兩個小問題:首先,作者用的結巴分詞里的詞典是根據現代文的語料獲得的(參見“結巴分詞”開發者之前對網友的回復:模型的數據是如何生成的? · Issue #7 · fxsjy/jieba),而《紅樓夢》的文字風格是半文半白的,這樣的分詞方法準確性存疑;其次,雖然作者用《三國演義》做了對比,但是依然沒有有力地證明用詞差異沒有受到情節變化的影響。于是我決定自己做一遍實驗,用無字典分詞的方法來分詞,并且嘗試剔除情節對分析的影響,看看結果會不會有所不同。

          本來開始寫的時候覺得 5000 字就差不多了,結果最后成文的時候竟然達到了 1.3 萬字。即使這樣,我也只能解釋一下算法的大致工作過程,至于詳細的原理,如果感興趣的話可以找其他資料去學習,我也會附上一些資料鏈接。不然如果我寫的面面俱到的話感覺可以出書了……至于結果如何?先賣個關子。(誒,不要直接滑到底?。。?/span>

          程序已在 GitHub 上開源,使用方法參見 README 文件:LouYu2015/analysis_on_the_story_of_a_stone??紤]到版權問題,我決定不提供《紅樓夢》原文。如果想復現實驗結果的話,可以去找小說網站下載。(更新:根據網友提醒,《紅樓夢》因為作者去世遠遠超過 100 年而進入公有領域,不受版權限制。因此我把原文也補充了上去,現在按照說明運行程序即可復現結果。也可在這里獲取《紅樓夢》全文:紅樓夢 - 維基文庫,自由的圖書館。)

          ?

          2 文本預處理

          這一步很基礎,就不贅述了。簡單來說,就是要根據標點符號,把每一個分句都切開,然后用統一的符號(這里我用的是井號)來標記切分點。這樣對于后面的程序來說就好處理一些了。

          雖然目標很簡單,然而,有些細節還是需要額外處理一下的。比如,我找到的文本里,所有“性”啊,“露”啊之類的字都被用 『』 框了起來(可能為了過濾少兒不宜的內容?我怎么覺得框起來以后更奇怪了……),所以這種標點需要被刪掉,不能當作分割符號。另外,每章開頭的回目編號也需要去掉,因為這不算小說的內容。最后,文本中出現了一些電腦中沒有的罕見字,不過好在文本中這些罕見字都在括號內用拆分字型的方法標了出來(比如“(左王右扁)”),所以理論上我可以把這些內容替換成一些原文中沒有的字符(比如特殊符號),最后再替換回去。不過我太懶了,所以沒有做這樣的替換。理論上罕見字對后面的分析也不會有很大,因為后面涉及到的都是出現頻率比較高的單詞。

          處理后的效果是這個樣子:

          ?

          #甄士隱夢幻識通靈#賈雨村風塵懷閨秀#此開卷第一回也#作者自云#因曾歷過一番夢幻之后#故將真事隱去#而借#通靈#之說#撰此石頭記一書也#故曰#甄士隱#云云#但書中所記何事何人#自又云#今風塵碌碌一事無成#忽念及當日所有之女子#一一細考較去#覺其行止見識皆出于我之上#何我堂堂須眉誠不若彼裙釵哉#實愧則有馀#悔又無益之大無可如何之日也……

          ?

          3 構建全文索引

          得到處理后的文本之后,我需要建立一個全文索引。這樣是為了快速地查找原文內容,加速后面的計算。我使用了后綴樹這個結構作為索引。這個數據結構比較復雜,所以我們可以先談談更簡單的字典樹。

          3.1 字典樹

          首先,我們看看字典樹的樣子:



          ?Free Image on Pixabay - Landscape, Tree, Flowers, Book

          啊錯了,這個才是字典樹……

          ?



          Trie - Wikipedia


          上圖中,每個圓圈是一個結點,代表著一個字符串(就是圓圈內的內容);結點之間的連線是,代表著一個字母。最上面的結點,也就是空著的那個結點,是根結點。如果我們從根結點不斷向下走到某個結點,那么把經過的每一條邊上的字母拼起來,就是這個結點代表的字符串了。這就是字典樹的特點。

          那么字典樹是干什么用的呢?舉個例子來說,假如我們想在這棵字典樹里查找 “to” 這個單詞,就可以先從根結點下面的邊里找到第一個字母,也就是 “t” 這條邊,從而找到 “t” 這個結點。然后我們再從 “t” 結點下面的邊里找到第二個字母,也就是 “o” 這條邊,就找到 “to” 這個結點了。假如 “to” 這個結點里儲存了 “to” 的中文解釋,那么我們只通過兩次操作就找到了 to 的中文意思。這樣比一個詞一個詞地找的方法快多了。這很像我們查字典的時候,先看第一個字母在字典中的位置,然后再看第二個字母……最終找到單詞,因此被稱為字典樹。

          3.2 后綴樹

          說完字典樹,我們再說說后綴樹的前身:后綴字典樹。后綴字典樹其實就是字典樹,只不過里面的內容不是單詞,而是一個字符串的所有后綴:從第一個字母到最后一個字母的內容,從第二個字母到最后一個字母的內容……以此類推。比如說,"banana" 的所有后綴就是 banana, anana, nana, ana, na 和 a。把這些內容都加到字典樹里,就構成了后綴字典樹。下面左圖就是 banana 的后綴字典樹:https://www.slideshare.net/farseerfc/ukks-algorithm-of-suffix-tree

          后綴樹和后綴字典樹的區別就是,在后綴樹中,我們要把下面只有一條邊的結點去掉,然后把這個結點連接的兩條邊壓縮成一條。比如,左圖后綴字典樹中的 b-a-n-a-n-a,在右圖的后綴樹中被壓縮成了 banana 這一條邊。此外,后綴樹還使用了一個技巧,就是不儲存邊的內容,而是儲存這些內容在原文中的位置。因為后綴樹中的很多內容都是重復的,所以這個小技巧可以大大減少索引的大?。ㄓ脤I的語言描述,它的空間復雜度是 O(n))。

          后綴樹又有什么用呢?它最大的用途就是檢索字符串中間的內容。比如,假如我想查找 an 在 banana 中哪里出現過,只需要查找代表 an 的結點,就找到了所有以 an 開頭的結點: anana 和 ana。由于每次出現 an 的地方都一定會產生一個以 an 開頭的后綴,而所有的后綴都在后綴樹中,所以這樣一定能夠找到所有 an 出現的位置。后綴樹的強大之處在于,即使我們把 banana 換成一篇很長很長的文章,我們也能很快地進行這樣的檢索。

          最后,我使用了 Ukkonen 算法快速地創建了整篇《紅樓夢》的后綴樹(用專業的語言描述 Ukkonen 算法的速度:它的時間復雜度是 O(n))。Ukkonen 算法比較復雜,所以這里我不會講解 Ukkonen 算法,感興趣的同學可以看看這些資料:

          Ukkonen's suffix tree algorithm in plain English

          后綴樹的構造方法-Ukkonen詳解 - 懶人小何的日志 - 網易博客

          Ukkonen's Suffix Tree Construction - Part 6 - GeeksforGeeks

          有了全文索引以后,后面的程序就好做了。

          ?

          4 制作字典

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

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

          ?

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

          ?

          上面是出現頻率前 20 的片段,括號內是出現次數??梢钥吹叫Ч€不錯,很多片段都是單詞。然而,排在第六名的“了一”明明不是個單詞,出現次數卻比賈母還要高??梢娺@樣的篩選方法還是有一定問題的。而且,這樣被誤當成單詞的片段還有很多,例如“了的”、“的一”之類的。究其原因,是因為出現次數 TOP 5 的單字由高到低分別是“了、的、不、一、來”,所以它們的組合也會經常出現。為了排除這樣的組合,我們可以用“凝固度”來進行進一步地篩選。

          ?

          4.1 凝固度

          凝固度的定義是:一個片段出現的頻率左右兩部分分別出現的頻率的乘積高出多少倍(注意,頻率表示的是出現的比例,而頻數表示的是出現的次數)。不過這句話太拗口了,還是用公式描述比較好。如果 P(AB) 是片段出現的頻率,P(A) 是片段左邊的字的出現的頻率, P(B) 是右邊的字出現的頻率,那么凝固度 co 就是:

          公式中,??就是左右部分在完全隨機組合的情況下被組合到一起的概率。凝固度的思想是:如果片段實際出現的概率比被隨機組合出來的概率高出很多倍,就說明這樣的組合應該不是意外產生的,而是有一些關聯的。這個關聯很可能就是因為這個片段是一個不可分割的整體,也就是單詞。

          對于超過兩個字的片段,可以嘗試每一種拆分方法(比如“賈寶玉”有“賈/寶玉”和“賈寶/玉”兩種拆分方法),然后取各種方法的凝固度的最小值。

          現在我選出《紅樓夢》中出現次數大于 5 的片段,對它們的凝固度做個排序:

          ?

          翡翠(171415.92)、茉莉(171415.92)、砒霜(171415.92)、逶迤(142846.60)、誹謗(142846.60)、徘徊(142846.60)、繾綣(142846.60)、乜斜(142846.60)、戥子(142846.60)、籰子(142846.60)、姽嫿(122439.94)、蝴蝶(122439.94)、囟門(122439.94)、檳榔(122439.94)、琵琶(122439.94)、孌童(119038.83)、筏子(119038.83)、牲口(119038.83)、躊躇(107134.95)、隄防(107134.95)

          ?

          這是凝固度排名前 20 的組合,括號內是凝固度??梢钥吹叫Ч€是不錯的。

          接著往下看,在 Top 20~100 里也基本沒有不是單詞的條目:

          靦腆、隄防、甬路、趔趄、蚊子、獅子、瓔珞、療治、羔子、跛足、堯舜、嫦娥、陛見、簸籮、梆子、粳米、竭力、栗子、攛掇、葵官、芭蕉、玲瓏、俞祿、妯娌、嘁嘁喳喳、玷辱、奚落、互相、譬如、腕上、禱告、攮的、鑰匙、覷著、恣意、磯上、饅頭、閻王、椒房、茯苓、琪官、牡丹、恒王、凹晶、翰林、畸角、淌眼、籃子、滋味、韶華、爆竹、漲了、芍藥、估量、拷問、杌子、嗓子、搪塞、晦氣、麒麟、玫瑰、葫蘆、躬身、懇切、崽子、盹兒、皂白、謠言、凸碧、唯唯、赫赫、簌簌、蔭堂、嗤的、嘮叨、努嘴、吆喝、荳官、茯苓霜、艱難

          然而凝固度也有一定的局限性。再往后看的話,會發現里面還有很多片段是半個詞,而它們的凝固度也挺高的。例如:“香院”(完整的詞應該是“梨香院”)、“太太太太”(完整的詞應該是“老太太太太”)。想想也有道理,這些片段雖然是半個詞,但是它們確實也跟完整的單詞一樣是“凝固”在一起的。所以,光看凝固度是不夠的,還要通過上下文判斷這個詞是否完整。

          4.2 自由度

          為了排除掉不完整的單詞,我們可以使用自由度這個概念來繼續過濾。自由度的思想是這樣的:如果一個組合是一個不完整的單詞,那么它總是作為完整單詞的一部分出現,所以相鄰的字就會比較固定。比如說,“香院”在原文中出現了 23 次,而“梨香院”出現了 22 次,也就是說“梨”在“香院”的左邊一起出現的頻率高達 95.7%,所以我們有把握認為”香院”不是完整的單詞。而自由度描述的就是一個片段的相鄰字有多么的多樣、不固定。如果片段的自由度比較高,就說明這個詞應該是完整的。

          ?

          因為相鄰字分為左側和右側,所以自由度也分為左右兩部分。以左側的自由度為例,計算公式就是左側相鄰字的每一種字的頻率的總信息熵。也就是說,如果??是左側自由度,?到??是每種左側相鄰字出現的頻率,那么:

          ?

          (對于沒學過信息熵的同學來說這個公式可能很晦澀,反正記住左側自由度體現了左側相鄰字的多樣性就可以了。)

          我們把左側自由度最低的 20 個組合拿出來,可以看到確實過濾出來了很多不是單詞的內容:

          ?

          淚來(0.111)、在話(0.112)、慢的(0.116)、頭們(0.117)、今我(0.121)、云笑(0.122)、以我(0.141)、王二(0.146)、里知道(0.146)、己也(0.151)、會子又(0.154)、太和(0.156)、用說(0.159)、嘻的(0.165)、今且(0.169)、么東西(0.187)、苦來(0.187)

          (括號內為左側自由度)

          右側也同理,有些片段明顯是半個單詞:

          有什(0.034)、周瑞家(0.053)、老太(0.065)、薛姨(0.072)、也罷(0.085)、老祖(0.093)、哭起(0.100)、在話(0.112)、聽下(0.113)、些東(0.118)、林之(0.121)、個婆(0.126)、我告(0.129)、老嬤(0.139)、二夫(0.144)、邢王二(0.149)、就罷(0.154)、到自(0.169)、這會(0.175)、大嫂(0.179)

          (括號內為右側自由度)

          4.3 最終的單詞表

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

          ?

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

          經過層層遴選之后,單詞表初步成型了。我從最終結果中隨機抽取了 100 個條目,其中有 47 個是單詞:

          佩鳳、尋常、歇下、王公、不提、仍往、親熱、之后、犯事、小戲子、現今、兩三天、縫兒、彎著腰、魂飛、故典、海棠社、支使、發熱、感激、壓倒、一座、已到、洋漆、包勇、查抄、舅爺、石榴、報與、戥子、一匹、拐子、家里、林黛玉、法子、空門、值錢、抿嘴、未娶、秋爽齋、發誓、明日、相伴、舒服、小幺兒、李紈、仙長


          這意味單詞表的正確率只有一半左右。不過,在錯誤的條目里,很多條目的切分其實正確的,只是有好幾個詞粘到了一起:

          趕不上、個字、料他、你快去、丫鬟婆子、無有不、抿著嘴笑、在外間睡、把我、一個小、叫我怎么、飯來、句好話、忙命人、惱的、答應了、提那、告辭了、庵里、和二奶奶、謝他、個女人、領著、急忙進、池子里、捶著、手里拿著一、五百兩、之為人、和姑娘們、不知怎么樣、罵一、是那里來的、家的小姐、十歲的、個眼睛、如今我、幾個小、丫鬟名、省一、俗了、一一的、聽了這話、攆出去、梳洗了、淡淡的、恨不能、可惜了、件大事、作詩的、與尤老、散與、究竟不

          雖然正確率不高,但其實沒有必要通過調高篩選標準的方法來進行更嚴格的過濾了。隨后分詞算法將會解決單詞沒有被切開的問題。如果繼續調高標準,可能會導致很多確實是單詞的條目被去除。

          參考資料:

          基于信息熵的無字典分詞算法 - 成都笨笨 - 博客園

          5 分詞

          之前在篩選單詞的時候,思路就是用各種各樣的數值標準進行判斷。而對于“分詞”這個看似更加困難的問題,思路也是類似的:制定一個評價切分方案的評分標準,然后找出評分最高的切分方案。評分標準是什么呢?最簡單的標準就是,把切分之后每個片段是單詞的概率都乘起來,作為這個切分方案正確的概率,也就是評分標準。我們假設,一個片段是單詞的概率,就是這個片段在原文中的出現頻率。

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

          5.1 維特比算法

          維特比算法本質上就是一個動態規劃算法。它的想法是這樣的:對于句子的某個局部來說,這一部分的最佳切分方案是固定的,不隨上下文的變化而變化;如果把這個最佳切分方案保存起來,就能減少很多重復的計算。我們可以從第一個字開始,計算前兩個字,前三個字,前四個字……的最佳切分方案,并且把這些方案保存起來。因為我們是依次計算的,所以每當增加一個字的時候,我們只要嘗試切分最后一個單詞的位置就可以了。這個位置前面的內容一定是已經計算過的,所以通過查詢之前的切分方案即可計算出分數。這就是維特比算法的工作原理。

          舉個例子,這是計算“寶玉黛玉”每種切分方式的得分的過程:

          ?

          寶: p = 0.0079487991(最佳切分)

          寶玉: p = 0.0079427436(最佳切分)
          寶/玉: p = 0.00795 * 0.00827 = 0.0000657630

          寶玉黛: p = 0.0000077623
          寶/玉黛: p = 0.00795 * 0.00001 = 0.0000000511
          寶玉/黛: p = 0.00794 * 0.00189 = 0.0000149872(最佳切分)

          寶玉黛玉: p = 0.0000096861
          寶/玉黛玉: p = 0.00795 * 0.00001 = 0.0000000617
          寶玉/黛玉: p = 0.00794 * 0.00273 = 0.0000216996(最佳切分)
          寶玉黛/玉: p = 0.00001 * 0.00827 = 0.0000001240(注意,這里計算時使用的是“寶玉黛”的最佳切分的分數,而不是“寶玉黛”這個片段本身的分數)

          這樣得到每種切分方式的得分之后,程序先根據最后一步的結果,把“黛玉”切分出去,剩下“寶玉”。然后程序再看“寶玉”的各種切分結果,發現不切分的得分最高,于是把“寶玉”也切分了出去。最后,程序發現沒有剩下的內容了,于是切分完成了。

          5.2 一些的調整

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

          此外,還有一個問題:如果一個片段不在字典中,怎樣計算它的頻率?在需要外界提供字典的分詞算法中,這是一個比較棘手的問題。不過在無字典(準確的說是自動構造字典)的算法中,這反而是一個比較容易解決的問題:任何要切分的片段一定會出現在后綴樹中,因為這個片段是原文的一部分!所以,我們只需要通過后綴樹查詢這個片段的頻數,就可以計算它在原文中的頻率了。

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

          5.3 分詞算法的測試

          我選擇了兩段原文內容來測試算法的準確性。

          這是第二回開頭的一段敘事性片段的機器分詞結果:

          只見/封肅/方/回來,歡天喜地,眾人/忙問/端的,他/乃說道,原來/本府/新/升的/太爺,姓賈名化,本/湖州人氏,曾與/女婿/舊日/相交,方才/在/咱們/前/過去,因/看見/嬌杏/那/丫頭/買線,所以/他/只當/女婿/移/住/于此,我一/一/將原故/回明,那/太爺倒/傷感/嘆息/了一回,又問/外孫女兒,我說/看燈/丟了,太/爺說,不妨,我自/使/番役/務必/探/訪/回來,說/了一回/話,臨走/倒/送了/我/二兩銀子,甄家/娘子/聽了,不免/心中/傷感,一宿無話,至/次日/早有/雨村/遣人/送了/兩封/銀子,四匹/錦緞,答/謝甄家娘/子,又/寄/一封/密/書與/封肅,托他/向/甄家/娘子/要/那/嬌杏/作二房,封肅/喜的/屁滾尿流,巴不得/去/奉承,便在/女兒/前一/力/攛掇/成了,乘/夜/只/用一/乘小轎,便把/嬌杏/送/進去/了,雨村/歡喜/自不必說,乃/封/百金/贈/封肅,外/又謝/甄家/娘子/許多/物/事,令其/好生養/贍,以待/尋訪/女兒/下落,封肅/回家/無話,卻說/嬌杏/這/丫鬟,便是/那年/回顧/雨村/者,因/偶然/一/顧,便/弄出/這段事來,亦是/自己/意/料不/到之/奇緣,誰想/他/命運/兩/濟,不/承望/自/到/雨村/身邊,只/一年/便/生了一子,又/半載,雨村/嫡妻/忽染疾/下世,雨村/便將他/扶/側/作/正室/夫人/了,正是,偶因/一/著/錯,便/為人/上/人


          這是人工分詞的結果:

          只見/封肅/方/回來,歡天喜地,眾人/忙問/端的,他/乃/說道,原來/本府/新升/的/太爺,姓/賈/名/化,本/湖州/人氏,曾/與/女婿/舊日/相交,方才/在/咱們/前/過去,因/看見/嬌杏/那/丫頭/買線,所以/他/只當/女婿/移住/于/此,我/一一/將/原故/回明,那/太爺/倒/傷感/嘆息/了/一回,又/問/外孫女兒,我/說/看燈/丟了,太爺/說,不妨,我/自/使/番役/務必/探訪/回來,說/了/一回話,臨走/倒/送了/我/二兩/銀子,甄家/娘子/聽了,不免/心中/傷感,一宿/無話,至/次日/早有/雨村/遣人/送了/兩封/銀子,四匹/錦緞,答謝/甄家/娘子,又/寄/一封/密書/與/封肅,托/他/向/甄家/娘子/要/那/嬌杏/作/二房,封肅/喜的/屁滾尿流,巴不得/去/奉承,便/在/女兒/前/一力/攛掇/成了,乘夜/只用/一乘/小轎,便/把/嬌杏/送/進去/了,雨村/歡喜/自/不必/說,乃/封/百金/贈/封肅,外/又/謝/甄家/娘子/許多/物事,令/其/好生/養贍,以/待/尋訪/女兒/下落,封肅/回家/無話,卻說/嬌杏/這/丫鬟,便是/那年/回顧/雨村/者,因/偶然/一顧,便/弄出/這段/事/來,亦是/自己/意料/不到/之/奇緣,誰想/他/命運/兩濟,不/承望/自/到/雨村/身邊,只/一年/便/生了/一/子,又/半載,雨村/嫡妻/忽/染疾/下世,雨村/便/將/他/扶側/作/正室/夫人/了,正是,偶/因/一著/錯,便/為/人上人

          ?

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

          下面再看看詩詞類片段的分詞效果。這是《葬花吟》的機器分詞結果:

          花謝/花飛/花滿/天,紅/消/香/斷有/誰憐,游絲/軟/系飄春榭,落/絮輕沾撲/繡簾,閨中/女兒/惜春/暮,愁緒/滿懷/無/釋/處,手/把花鋤/出/繡簾,忍/踏/落花/來/復去,柳絲榆莢/自/芳菲,不管/桃/飄/與李/飛,桃李/明年/能再/發,明年/閨中/知/有誰,三月/香/巢已壘成,梁間/燕子/太/無情,明年/花/發/雖可/啄,卻不/道/人去/梁空巢/也/傾,一年/三百六十/日,風/刀霜劍嚴/相/逼,明/媚/鮮妍/能/幾時,一朝/飄泊/難/尋覓,花開/易見/落/難尋,階前/悶/殺/葬花/人,獨/把花鋤/淚/暗灑,灑/上/空/枝/見/血/痕,杜/鵑/無語/正/黃昏,荷鋤歸/去掩/重門,青燈/照壁/人初/睡,冷/雨敲窗被/未/溫,怪/奴/底/事/倍傷/神,半/為/憐春/半/惱/春,憐春/忽至/惱/忽/去,至/又/無言/去/不聞,昨宵/庭外悲歌/發,知是/花魂/與/鳥魂,花魂/鳥魂/總難/留,鳥/自/無言/花自/羞,愿/奴/脅下/生/雙翼,隨/花/飛到/天盡頭,天盡頭,何處/有/香/丘,未/若錦/囊收艷骨,一堆/凈/土掩/風流,質本潔/來/還/潔/去,強/于/污/淖陷渠溝,爾今/死去/儂收葬,未卜/儂/身/何日/喪,儂今葬/花人笑癡,他年葬儂/知是誰,試看/春/殘花/漸/落,便是/紅顏老/死時,一朝春盡/紅顏老,花落人亡/兩/不知

          這是人工分詞結果:

          花謝/花飛/花滿天,紅消/香斷/有/誰/憐,游絲/軟系/飄/春榭,落絮/輕沾/撲/繡簾,閨中/女兒/惜/春暮,愁緒/滿懷/無/釋處,手/把/花/鋤/出/繡簾,忍/踏/落花/來/復/去,柳絲/榆莢/自/芳菲,不管/桃飄/與/李飛,桃李/明年/能/再發,明年/閨中/知/有/誰,三月/香巢/已/壘成,梁間/燕子/太/無情,明年/花發/雖/可啄,卻/不道/人去/梁空/巢/也/傾,一年/三百/六十/日,風刀/霜劍/嚴/相逼,明媚/鮮妍/能/幾時,一朝/飄泊/難/尋覓,花開/易見/落/難尋,階前/悶殺/葬花人,獨/把/花/鋤/淚/暗灑,灑上/空枝/見/血痕,杜鵑/無語/正/黃昏,荷鋤/歸去/掩/重門,青燈/照壁/人/初睡,冷雨/敲窗/被/未溫,怪/奴/底事/倍/傷神,半為/憐春/半/惱春,憐春/忽至/惱/忽去,至/又/無言/去/不聞,昨宵/庭外/悲歌/發,知是/花魂/與/鳥魂,花魂/鳥魂/總/難留,鳥/自/無言/花/自/羞,愿/奴/脅下/生/雙翼,隨花/飛到/天/盡頭,天/盡頭,何處/有/香丘,未/若/錦囊/收/艷骨,一堆/凈土/掩/風流,質/本/潔/來/還/潔/去,強/于/污淖/陷/渠溝,爾/今/死去/儂/收葬,未卜/儂身/何日/喪,儂/今/葬花/人/笑癡,他年/葬/儂/知/是/誰,試看/春殘/花/漸/落,便是/紅顏/老/死/時,一朝/春盡/紅顏/老,花落/人亡/兩/不知

          這下程序的準確率下降到了 74.07%,召回率也下降到了 67.04%,分別下降了將近 10%,可見詩歌的分詞更難一些。這也在情理之中,因為詩詞中有很多不常用詞,有些詞甚至只出現過一次,所以電腦很難從統計數據中發掘信息。

          ?

          6 詞頻統計

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

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

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

          (括號內為頻數)

          可以跟之前只統計出現次數,不考慮切分問題的排名做個對比:

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

          (括號內為頻數)

          通過分詞后的詞頻,我們發現《紅樓夢》中的人物戲份由多到少依次是寶玉、鳳姐、賈母、襲人、黛玉、王夫人和寶釵。然而,這個排名是有問題的,因為”林黛玉”這個詞的出現次數還有 267 次,需要加到黛玉的戲份里,所以其實黛玉的戲份比襲人多。同理,“老太太”一般是指賈母,所以賈母的戲份加起來應該比鳳姐多。正確的排名應該是寶玉、賈母、鳳姐、黛玉、襲人、王夫人和寶釵。

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

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

          最后,我隨機選擇了詞頻表中的 200 項條目,用來估計其中有多少是真正的單詞。其中有 82 條是單詞:

          ?

          暗地、老君、男人、匈奴、病在垂危、聞名不如、追索、氣怔、神昏、照照、守著、檔子、送去、下山、玉皇、菲材獲譴、托他、本身、這番、大海、十載、記的、遵諭、蕓哥兒、現買、專司其職、天上人間、法官、推就、階上、所知、別物、朝陽、懼罪、入塾、前代、當地、神瑛、名利、嘩喇、句句、辮梢、端上、駙馬、按理、開金、以下、清官、香甜、猿背、避人、開眼、殊不知、笞杖、祭吊、藥方、色紅、鐵鎖、看見、逗蜂軒、不勝、上樓、正官祿馬、國中、入會、轉步、魄化、等等兒、公侯、代善、排律、只見、晝夜、外國、日月、莼噎滿喉、夸獎、禮儀、自稱、王妃、千秋、買棺盛殮

          ?

          而 118 條不是單詞:

          ?

          上值日的、聽我說、帶的、到館來、馀之、搛了、一兩點、管這、誰先、料也、且喜、一兩天、糯五十斛、命坐、殺了、在你、痛的、等都說、現吃、怔了、這金、個個是、我原、氏忙、也錯不得、子本、毀僧謗、遮著、了手、眼淚直、菜已、己有、別理、涼著、遂不、仍復、的差役、們把、太爺的、你只怨、同你去、忙進去、腌臟話、在后面、又驚又、隊隊、名的、去睬他、與平、一個錢、沒聽、株枯木、正二刻、靜了、已醒了、釅釅的沏、細說與、醉中、個年高有、松了、了兩聲、賈珍也、讓至、早晚才、描鸞刺、那里肯、松的、秋閨怨、張花梨、榮寧兩、待你、再多言、反嚇、里調、如若、庶不、是顆、到書、當此、點上燈來、去托、又寬、又聯道、你特、行一、卻難、薔大爺蕓、其真、人情等、才吃了藥、再揀、而誕、兩手抱、便福、被劫、家大小、病就、各按、排穗褂、喝了幾杯、再說、面皆、天氣和暖、了對半、明日還、隨往、聽著、狹窄猶、沒好、兒給、有說有、傍晚得、而智者憂、不留心、聽著怪、不依我呢、而自、玉來

          ?

          也就是說,單詞的正確率只有 41 %。這比字典的準確率還低,并沒有因為采用了分詞算法而提高了正確率。不過這也可以理解,因為生成字典的時候我只考慮了出現次數大于 5 的片段,而分詞的時候有些單詞只出現了一次,所以難度確實應該更大一些。

          詞頻表中總計有 3.99 萬個條目。根據估算的詞頻表中正確單詞的比例,我估計《紅樓夢》的詞匯量大約是 1.6 萬。有人用其他程序估計《紅樓夢》的詞匯量是 0.45 萬(http://bbs.creaders.net/politics/bbsviewer.php?trd_id=344894),不過作者沒有描述詳細的統計方法,所以我對其結果非常懷疑,因為《紅樓夢》中的單字就有 0.35 萬種了。

          ?

          7 篩選特征詞

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

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

          按照這個標準,與情節最無關的 20 個詞是:

          ?

          下回分解(0.27)、也不(0.50)、不知(0.51)、一個(0.52)、起來(0.55)、如今(0.55)、自己(0.55)、聽了(0.55)、那里(0.56)、什么(0.57)、出來(0.58)、說著(0.58)、話說(0.59)、這里(0.61)、來了(0.63)、只得(0.63)、我們(0.64)、只是(0.64)、怎么(0.65)、就是(0.66)

          ?

          (括號內為修正后的方差)

          有趣的是,處在排名末尾的詞,也就是詞頻變化最大的詞,大部分都是人名:

          ?

          丫鬟、請安、平兒、家的、薛姨媽、家人、光景、二奶奶、賈璉、賈政、李紈、林姑娘、父親、探春、邢夫人、奴才、哥兒、母親、女兒、媽媽、麝月、惜春、晴雯、鳳姐兒、賈珍、林黛玉、鴛鴦、湘云、尤氏、迎春、林之孝、紫鵑、薛蟠、寶琴、趙姨娘、香菱、周瑞、雨村、雪雁、妙玉、鶯兒、劉姥姥、芳官、秦鐘、金桂、寶蟾

          ?

          可見這個篩選方法確實能去掉我們不想要的特征詞。

          最終,我選擇了詞頻變化最小的 50 個詞作為特征,每個詞的修正后標準方差都小于 0.85。這 50 個詞如下:

          下回分解、也不、不知、一個、起來、如今、自己、聽了、那里、什么、出來、說著、話說、這里、來了、只得、我們、只是、怎么、就是、去了、進來、知道、只見、這樣、出去、一時、還有、不得、都是、你們、寶玉、見他、不能、聽見、不是、兩個、說道、一面、咱們、這個、不敢、的人、沒有、還不、又不、笑道、所以、不過、叫他

          ?

          8 主成份分析(PCA)

          理論上,有了特征之后,我們就可以比較各個章節的相似性了。然而問題是,現在我們有 50 個特征,也就是說現在的數據空間是 50 維的,這對于想象四維空間都難的人類來說是很難可視化的。對于高維數據的可視化問題來說,PCA 是一個很好用的數學工具。

          ?

          9 何謂是主成份分析

          因為高維的數據空間很難想象,所以我們可以先想象一下低維的情況。比如說,假設下圖中的每個點都是一個數據,橫坐標和縱坐標分別代表兩個特征的值:



          https://zh.wikipedia.org/wiki/%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90#/media/File:GaussianScatterPCA.png

          ?

          現在,如果我們讓 PCA 程序把這兩個特征壓縮成一個特征的話,算法就會尋找一條直線,使得數據點都投影到這條直線上后損失的信息最少(如果投影不好理解的話,可以想象用兩塊平行于直線的板子把數據點都擠壓到一條線上)。在這個例子中,這條線損失信息最少的線就是圖中較長的那個箭頭。這樣,如果我們知道了一個數據點在直線上投影的位置,我們就能大致知道數據點在壓縮之前的二維空間的位置了(比如是在左上角還是右下角)。

          ?

          以上是把二維數據空間壓縮到一維的情況。三維壓縮到二維的情況也是類似的:尋找一個二維平面,使得數據點投影到平面后損失的信息最少,然后把所有數據點投影到這個平面上去。三維壓縮到一維就是把尋找平面改成尋找直線。更高維度的情況以此類推,雖然難以想象,但是在數學上是一樣的。

          ?

          至于算法如何找到損失信息最少的二維平面(或者直線、三維平面等等),這會涉及到一些數學知識,感興趣的同學可以去查找一下相關的數學公式和證明。這里只要把這個算法當成一個黑箱就可以了。

          ?

          9.2 重大發現?

          現在我們可以利用 PCA,把五十個詞的詞頻所構成的五十個維度壓縮到二維平面上了。我把壓縮后的數據點畫出來,發現是這個樣子的:



          ?

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

          ?

          80 回以后的內容(藍色)大部分都集中在左下角的一條狹長的區域內,很明顯地和其他章回區分開來了!莫非《紅樓夢》的最后 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 回,恰好重合:

          ?

          《紅樓夢》之“釵黛合一”帶來大觀園鼎盛_風之子9881198198_新浪博客

          [轉載]白坤峰講紅樓夢(172)賈府鼎盛:該來的都來了_史鼎說紅樓_新浪博客


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

          9.3 再次分析

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




          ?這次我需要把特征壓縮到三維空間而非二維空間了。這是因為之前我們得到的兩個成分的方差貢獻率(可以理解為成分提供的信息量)分別為 44.6% 和 19.0 %,總貢獻率 63.6%,算是比較高了。而現在,即使是三個成分,方差貢獻率也只有 23.9%,10.6% 和 6.9% 了,總貢獻率才 41.4%??梢娙サ簟靶Φ馈焙汀皩氂瘛币院?,從詞頻中發掘信息的難度提高了很多。

          從圖中可以看到,現在后 40 回已經不像之前那么聚集了,不過還是可以看出一點聚集的趨勢。特別地,前 80 回和后 40 回在成分二和成分三上的區別比較明顯。和之前一樣,我們可以把在這兩個成分中權重的絕對值比較大的詞都找出來,看看它們的詞頻變化。

          在成分三中,權重最小的五個單詞是:沒有(-0.41)、聽見(-0.25)、如今(-0.21)、所以(-0.18)、我們(-0.14)。(括號內為權重)

          ?

          而權重最大的五個單詞是:聽了(0.22)、兩個(0.26)、說著(0.30)、只見(0.37)、一面(0.39)

          ?


          成分二中,權重最小的三個單詞是:什么(-0.30)、怎么(-0.26)、聽見(-0.22)

          ?

          ?


          權重最大三個單詞是:一個(0.28)、你們(0.37)、我們(0.43)

          ?


          (“聽見”在排名中出現了兩次。不過不知道這個發現有什么用。)

          ?

          可以發現,有些詞的詞頻確實有一些異常的變化。然而,這些變化到底有沒有受到劇情影響呢?感覺很難說。此外,在 PCA 結果中,似乎前 40 回和中間 40 回也分開了一些,只是沒有后 40 回那么明顯而已。那么這是不是說明 PCA 的結果也是受到了劇情的影響呢?

          總之,我有點把握認為《紅樓夢》前 80 回和后 40 回的用詞是有一些差異的,不過因為難以排除劇情的影響,所以我對于作者是不是同一個人這個問題還不敢下定論。雖然沒有完全解決這個問題,不過這個過程中誤打誤撞產生的發現也是挺有意思的,比如“笑道”的詞頻變化和賈府興衰史的有趣重合。更重要的是,看似枯燥的數學公式可以做出這些好玩的分析,Math is fun!


          (以上內容來自于網絡)

          Python發展這么快,Why?Why?Why?

          Python學習筆記|安裝環境




          Python極速入門課程

          第二期


          不是軟件編程專業的同學,想學習編程很吃力,那是因為需要一個老師領你入門。從搭建環境開始、學習基礎知識、聽老師的項目案例,用沉浸式學習方法教你編程思維!

          開班時間:2017年11月26日

          前20名報名立減1000元!

          點擊閱讀原文即可報名課程

          友情鏈接

          永久免费啪啪网站一二三区,中文无码精油按摩高潮,乳峰高耸的美妇
        1. <bdo id="f9ffj"><progress id="f9ffj"></progress></bdo>
        2. <nav id="f9ffj"></nav>

            1. <var id="f9ffj"><mark id="f9ffj"></mark></var>
              <var id="f9ffj"></var>