楊光豹, 楊豐赫, 鄭慧錦
1(浙江廣播電視大學(xué) 青田學(xué)院,青田 323900)
2(東南大學(xué) 網(wǎng)絡(luò)空間安全學(xué)院,南京 211189)
3(浙江青田縣職業(yè)技術(shù)學(xué)校,青田 323900)
利用大數(shù)據(jù)技術(shù)處理海量中文信息是當(dāng)今很成熟的技術(shù),而中文分詞作為中文信息處理的基礎(chǔ),它是大數(shù)據(jù)處理中文信息的基礎(chǔ). 而中文分詞的性能與準(zhǔn)確度一直是相互制約的. 業(yè)內(nèi)對該技術(shù)的研究一直在性能與精確度兩個方向在推進(jìn). 中文分詞方法的研究從大的方向來講,大體分為兩大類:(1) 基于詞庫的字典的機(jī)械分詞法. (2) 采用統(tǒng)計學(xué)理論的統(tǒng)計分詞法. 比如文獻(xiàn)[1]是使用人工制作的分詞語料進(jìn)行特征信息學(xué)習(xí)的機(jī)器學(xué)習(xí)分詞法. 文獻(xiàn)[2,3]是通過雙向LSTM神經(jīng)網(wǎng)絡(luò)模型進(jìn)行分詞. 文獻(xiàn)[4]利用上下文詞長特征作為分詞特征,從上下文信息中獲取信息進(jìn)行分詞,這些都屬于統(tǒng)計分詞法. 這類分詞法在對未登錄詞與歧義詞的處理上占優(yōu)勢,但性能上不及機(jī)械法. 一種好的分詞技術(shù)往往都是采用綜合方法,以機(jī)械分詞為基礎(chǔ),再融入統(tǒng)計法以提高分詞精確度. 例如文獻(xiàn)[5]利用統(tǒng)計學(xué)上的概率,引入層次分析法將多種切分法優(yōu)劣排序,幫助選擇最優(yōu)排序法,它在一定程序上提高了準(zhǔn)確率.在機(jī)械分詞中,為了提高性能,文獻(xiàn)[6]提出了前四字hash索引樹技術(shù). 文獻(xiàn)[7]提出了首字hash查詢的方法,文獻(xiàn)[8,9]提出了雙字hash法; 文獻(xiàn)[10]提出了對四字hash索引的trie樹進(jìn)行改進(jìn),文獻(xiàn)[11]提出了利用首字hash法進(jìn)行正向與逆向的多方案分詞的選擇以減少分詞的歧義. 而文獻(xiàn)[12]卻提出了首字拼音首字母表. 目前為止,基于詞庫的中文分詞都有一個共同特點就是詞庫結(jié)構(gòu)都是:索引+余詞表. 在索引上采用各自認(rèn)為速度快,占用內(nèi)存小的方式來進(jìn)行查詢. 目前業(yè)內(nèi)普遍認(rèn)為采用前4字trie索引樹法的分詞技術(shù)性能最好. 而本文在實際編程中發(fā)現(xiàn),利用字符樹結(jié)構(gòu)的分詞技術(shù)能更好地進(jìn)行中文分詞,無論從時間復(fù)雜度還是空間復(fù)雜度,字符樹分詞技術(shù)都更勝于其它各種詞庫技術(shù),它能進(jìn)一步改善大數(shù)據(jù)處理中文信息的性能.
單字符樹是在各個節(jié)點上都保存單個字符的樹.將詞庫文件加載到內(nèi)存時,所有的詞都將先被分割成一個個的單字,然后逐個“掛接”在一棵樹上. 一個詞的所有字加載完后在最后一個字節(jié)點上作詞串標(biāo)記,然后加載第二個詞的各個字,如果這棵字符樹上該位置已經(jīng)存在要加載的字,則視為已加載(不覆蓋),但如果是詞的最后一個字符,則要對它的詞串標(biāo)記進(jìn)行修改.整棵樹的構(gòu)造如圖1所示.
單字符樹的各個節(jié)點都采用同樣的數(shù)據(jù)結(jié)構(gòu),每個節(jié)點中都有一個詞串標(biāo)記isword,如果其值為true則表示從根到該節(jié)點為止的路徑構(gòu)成一個詞,否則僅僅是詞的中間節(jié)點. 其數(shù)據(jù)結(jié)構(gòu)如圖2所示.
圖1 單字符樹詞庫構(gòu)造舉例
圖2 單字符樹詞庫節(jié)點數(shù)據(jù)結(jié)構(gòu)
查詢一個詞只需要從根節(jié)點開始,例如查詢“中國”這個詞,只需要從根節(jié)點開始先找“中”,找到后再在“中”的子節(jié)點中找“國”找到后再看這個“國”的節(jié)點中的詞串標(biāo)記是否是true,如果是true,則說明詞庫中存在“中國”. 否則就不存在. 這樣構(gòu)造起來的字符樹最大的好處是不會出現(xiàn)無效查詢,當(dāng)查詢詞庫中存在的詞,比如想查詢“千方百計”,它從樹根逐級查詢會有一條路徑存在,最后依據(jù)路徑末端節(jié)點的詞串標(biāo)記來判斷.查詢詞庫中不存在的字符串時,要么路徑不存在,要么路徑末端節(jié)點的標(biāo)記是false.
多字符樹是從單字符樹演變過來的一種變異樹.它的設(shè)計思路與單字符樹類同. 只是為了減少節(jié)點數(shù),把字符樹中出度為1,詞串標(biāo)記為false 的節(jié)點字符連在一起放在一個節(jié)點上,直到節(jié)點出度大于1或標(biāo)記位為true為止,但它的查詢思路與單字符樹是一樣的,都是從樹根開始向各個分支逐級查詢,加載時相同的前綴也只加載一份.
多字符樹的構(gòu)造過程是這樣的:例如“ABCDEF”是一個詞,開始加載這個詞時,將它作為整體掛在根節(jié)點的一級子節(jié)點上,此時如果加載第二個詞“ABCMN”,由于有共同的前綴“ABC”,于是將原來的節(jié)點“ABCDEF”進(jìn)行“裂變”,產(chǎn)生共同前綴的節(jié)點為父節(jié)點“ABC”,子節(jié)點為“DEF”,然后再把要插入的“ABCMN”這個詞的共同前綴去掉,剩下的字符構(gòu)成一個節(jié)點“MN”,并把它掛在“ABC”節(jié)點下的子節(jié)點數(shù)組中. 此時如果要加載ABC這個詞,則只需要將“ABC”節(jié)點的標(biāo)記位設(shè)為true即可. 而沒有共同前綴的詞都掛在根節(jié)點的一級子節(jié)點上,依此類推將所有詞庫文件中的詞加載到這棵樹上. 整棵樹的構(gòu)造如圖3所示,節(jié)點數(shù)據(jù)結(jié)構(gòu)如圖4所示.
圖3 多字符樹詞庫構(gòu)造舉例
圖4 多字符樹詞庫節(jié)點數(shù)據(jù)結(jié)構(gòu)
多字符樹結(jié)構(gòu)的詞庫節(jié)點總數(shù)比單字符樹少,但它的查詢過程與單字符樹相同,需要逐個比較字符,所以它的查詢性能與單字符樹是相同的. 它總節(jié)點數(shù)少是否意味著內(nèi)存占用少呢? 事實并非如此. 本實驗詞庫加載成多字符樹時,節(jié)點總數(shù)是291 893個,約是單字符樹的節(jié)點數(shù)目的3/4; 它平均每個節(jié)點大約擁有1個詞,但因為多字符樹中的每個節(jié)點保存的字符數(shù)目是不確定的,所以它不能全部直接存儲在節(jié)點上,只能將節(jié)點字符串中第一個字符保存在節(jié)點上,而多出的字符只能以數(shù)組的形式用指針“掛”在節(jié)點上,這樣以來,每個節(jié)點必須要多出一個指針字段進(jìn)行掛接字符串,而且多出的字符需另外申請空間來存放,這將在內(nèi)存占用上比單字符樹要大得多,后文實驗數(shù)據(jù)會證明這一點.
無論是單字符樹還是多字符樹,它們都不是索引樹. 因為它們沒有所謂的“余詞正文”,所有的詞都已經(jīng)在這棵樹上,它只是把詞庫的存儲方式從“表”變成“樹”,它不需要索引.
為了比較整詞法,trie索引樹法以及單字符樹與多字符樹等在詞庫成功查詢上的性能區(qū)別,我們采用順序訪問的平均訪問長度作為衡量各種詞庫性能的指標(biāo),以此比較它們在查詢性能上的優(yōu)劣.
由于單字符樹與多字符樹在查詢原理上相同,所以查詢性能相同,在性能比較時以單字符樹為代表參與. 之所以引用兩種結(jié)構(gòu)的字符樹是為了研究它們占用的內(nèi)存區(qū)別.
單字符樹詞庫構(gòu)造完成后各節(jié)點的統(tǒng)計數(shù)據(jù)如表1所示. 本實驗詞庫中最長的詞是16個字,所以單字符樹的最大層次是16,每一層的節(jié)點中如果有孩子,它會派生出下一層的子節(jié)點,它將作為下一層節(jié)點的父節(jié)點統(tǒng)計,而沒有孩子的節(jié)點不作為下一層節(jié)點的父節(jié)點統(tǒng)計,節(jié)點平均訪問長度是指從父節(jié)點到本節(jié)點需要的平均訪問次數(shù),詞串的訪問長度是指一個詞各節(jié)點的訪問長度之和,而詞串平均訪問長度是指同一長度的詞串的訪問長度的平均值. 由表1可知,字符樹中各詞串的平均訪問長度是3371,其主要由一級節(jié)點數(shù)量 (常用漢字?jǐn)?shù)量) 決定. 它的詞庫查詢時間復(fù)雜度為O(1)
trie索引樹結(jié)構(gòu)以索引字?jǐn)?shù)分為四類:前1字trie索引樹trie1,前2字trie索引樹trie2,前3字trie索引樹trie3,前4字trie索引樹trie4. 它們前n字(n為1,2,3,4)與字符樹結(jié)構(gòu)相同,但字?jǐn)?shù)長度超過n的詞,其剩余部分全部存在同一張“余詞表”中,所以將余詞表視為同一層次的節(jié)點,以trie4為例,其平均訪問長度統(tǒng)計如表2所示. 各類trie索引樹的平均訪問長度統(tǒng)計方法與表2相同,限于篇幅,在此不再一一舉出.
表1 單字符樹詞庫平均訪問長度統(tǒng)計表
表2 前四字trie樹詞庫平均訪問長度統(tǒng)計表
整詞詞庫的平均訪問長度直接受詞庫總詞條數(shù)量影響,假設(shè)詞庫總詞條數(shù)量為n,則它在順序查詢時,成功查詢的平均訪問長度為n/2,失敗查詢長度為n. 所以它的詞庫查詢的時間復(fù)雜度為O(n). 它的性能會隨著詞庫的增大而變慢. 本實驗詞庫中它的平均訪問次數(shù)是139 548次,是字符樹平均訪問次數(shù)的41倍. 隨著詞庫條目的增大,兩者性能差距將越來越大.
現(xiàn)將本實驗詞條集構(gòu)成的字符樹詞庫、各類trie索引樹詞庫以及整詞詞庫的平均訪問長度與余詞數(shù)量統(tǒng)計在表3中.
由表3可知,平均訪問長度是字符樹最短,它的平均訪問長度接近于一個常數(shù)(常用漢字?jǐn)?shù)6712的一半),整詞詞庫最長,它接近于詞庫總數(shù)的一半,而trie索引樹則介于兩者之間,對前n字的trie索引樹來說,n越大,級數(shù)分的越多,余詞數(shù)越少,它的平均訪問長度就越短,trie索引樹就會向單字符樹演變; 當(dāng)n越小,它的級數(shù)越少,余詞表越大,它的平均訪問長度就越長,于是它將向整詞詞庫演變. 所以字符樹詞庫與整詞詞庫是trie索引樹詞庫的兩個極端,前者詞庫內(nèi)查詢時間復(fù)雜度為O(1),它不隨詞庫規(guī)模增大而增大; 而后者詞庫內(nèi)查詢時間復(fù)雜度為O(m),它隨詞庫的規(guī)模(詞條總數(shù)為m)增大而增大; 而trie索引樹性能在這兩者之間,當(dāng)詞庫增大時,隨著余詞數(shù)量增大,其性能就會降低,尤其在大規(guī)模詞庫中,trie索引樹詞庫的余詞大量存在,余詞表對性能影響將上升為主要矛盾. 而字符樹詞庫的查詢詞條的時間復(fù)雜度為O(1),它不受詞庫總條數(shù)影響,且不存在余詞表,所以在大規(guī)模詞庫中將會突顯它的優(yōu)勢.
基于詞庫的中文分詞是建立在“最大匹配算法”的思想上,要從待分詞的字符串中找出盡可能字?jǐn)?shù)多的詞,分詞時提取的詞長度越長越好,分詞結(jié)果中詞的個數(shù)越少越好.
表3 各類詞庫平均訪問長度統(tǒng)計表
傳統(tǒng)整詞法存在大量的無效查詢,效率低下. 比如要找出字符串“ABCDEFGHIJKLM”中最長的詞. 如果詞庫中最長詞的長度是10個,則它必須先取長度是10的字符串A-J與詞庫中詞比較,查找是否有相同的,如果有則提取該字符串,如果沒有,則第二次取長度為9的字符串A-I來比較,依此類推,逐一縮短字符串長度進(jìn)行查詢. 在最壞的情況下,10次查詢有9次無效的,它的分詞時間復(fù)雜度為O(n×m),(n為待分詞總字符數(shù),m為詞庫最長字符數(shù)).
Trie索引樹能夠?qū)⒃~長度小于樹深度的詞快速找到并確定,消除了它們的無效查詢問題. 所以對于詞長度小于樹深度的詞,它的時間復(fù)雜度與字符樹相同. 然而,對于詞長度大于或等于樹的深度時,依然與傳統(tǒng)方法一樣,存在無效查詢的問題,比如上例字符串中如果ABCD是一個詞,它從首字,次字,三字,四字都找到了,此時它卻無法確定ABCD就是最長的詞,因為最長的詞有可能是ABCDE,也有可能是,ABCDEF…甚至是更長的字符串. 它既然無法確定ABCD是最長的詞,就只能逐個去查詢對比才能確定最長的詞. 所以它的性能只是介于兩者之間.
基于字符樹的分詞掃瞄能夠完全消除無效查詢問題. 無論要處理的字符串是多少長,它從字符串首字開始逐字與字符樹對比,一旦發(fā)現(xiàn)字符樹的路徑已經(jīng)是“盡頭”了,則查詢結(jié)束,然后提取現(xiàn)有的最長的詞. 它的時間復(fù)雜度取決于待分詞字符的總長度,與詞庫中最長詞的長度無關(guān). 所以,它的分詞算法的時間復(fù)雜度為O(n),(n為待分詞總字符數(shù)). 采用字符樹詞庫進(jìn)行分詞不僅消除了無效查詢問題,而且它對于利用回溯法[13]進(jìn)行多種分詞方案的選擇時具有得天獨厚的優(yōu)勢.
實驗環(huán)境:操作系統(tǒng)是Win10,CPU 為Intel?CoreTMi5-6500 CPU @3.20 GHz;內(nèi)存大小為4 GB. 為了區(qū)別各類詞庫的查詢性能與內(nèi)存占用情況,本文編寫了6個詞庫類,如表4所示,它們分別依次對同一個詞庫文件進(jìn)行加載,對同一個文本文件進(jìn)行分詞,查看加載詞庫所用時間,占用內(nèi)存,以及分詞所花費時間等數(shù)據(jù). 內(nèi)存占用量用加載詞庫前程序占用總內(nèi)存與加載詞庫后程序占用總內(nèi)存的差作為取值,內(nèi)存占用信息通過操作系統(tǒng)提供的GetProcessMemoryInfo函數(shù)獲取. 加載詞庫所需時間與對文件進(jìn)行分詞所需時間分別是在開始前用clock()函數(shù)獲取時間,結(jié)束后再次用該函數(shù)獲取時間,用它們的差值作為花費時間,各trie索引樹法的散列表的裝載因子統(tǒng)一設(shè)為0.5. 實驗取得數(shù)據(jù)分析表見表5所示.
表4 各詞庫類的數(shù)據(jù)結(jié)構(gòu)及實現(xiàn)技術(shù)
本文提出的單字符樹詞庫內(nèi)存占用最小,整詞二分法詞庫最大; 而對trie索引樹詞庫的內(nèi)存占用情況是級數(shù)越多,內(nèi)存占用卻越小. 原因是trie索引樹分的級數(shù)越多,就會有更多的共同的前綴字符被省略,而“余詞”數(shù)量也會大幅度減少,所以占用內(nèi)存會越少. 傳統(tǒng)的整詞詞庫占用內(nèi)存最大,最主要的原因是字符串對象的創(chuàng)建與排序會造成大量的“內(nèi)存碎片”,從而浪費內(nèi)存. 而多字符樹與單字符樹相比,性能接近相同,但內(nèi)存占用卻是多字符樹更大,原因是多字符樹的每個節(jié)點多了一個指向字符數(shù)組的指針,而且各節(jié)點中除第一個字符保存在節(jié)點內(nèi),其余的字符都是組合成數(shù)組“掛接”在節(jié)點上,需要另外的存儲空間. 所以它的內(nèi)存占用會比單字符樹大得多.
表5 實驗數(shù)據(jù)統(tǒng)計表
通過軟件采集實驗所用的詞庫文件獲得詞庫總條數(shù)為279 095,總字符數(shù)為:821 105. 平均每個詞含字符數(shù)為2.94個. 而加載成單字符樹后產(chǎn)生的節(jié)點數(shù)為405 534,相當(dāng)于詞庫中的一半字符可以省略,平均一個詞僅占1.45個節(jié)點數(shù). 單字符樹一方面省略了共同前綴的字符; 另一方面,也是更重要的是由于它的節(jié)點數(shù)據(jù)結(jié)構(gòu)單一,大小一致,在構(gòu)造字符樹完成后可以將所有字符節(jié)點從“分散”狀態(tài)“收集”到一張線性表中,釋放掉原來的所有節(jié)點. 這一過程會釋放大量的“內(nèi)存空閑碎片”,從而減少內(nèi)存占用. 它的內(nèi)存占用只有整詞詞庫MySet的占用內(nèi)存的1/5.5.
分詞速度最快的是本文提出的字符樹分詞法,其次是trie索引樹分詞法,最差是整詞二分法. 字符樹的分詞速度大約是整詞折半法的35倍. 而對于trie索引樹詞庫的性能情況是級數(shù)越多,分詞速度越快.
原因分析:字符樹消除了分詞過程中的無效查詢的問題,它同時在掃瞄算法與詞庫查詢兩個方面都具有最突出的優(yōu)越性,所以總體分詞速度是最快的. 整詞分詞法由于在掃瞄算法上存在大量的無效查詢,而它的詞庫查詢時間復(fù)雜度又是最高的,所以它的分詞速度最慢. 而trie索引樹可以部分消除無效查詢,同時它的詞庫查詢訪問性能是介于字符樹與整詞法之間,所以它的分詞性能也處于兩者之間. 另一方面,不同級數(shù)的trie樹性能也不同,trie索引樹級數(shù)越多,也就是深度越深,它的分詞性能越好,越接近字符樹,而級數(shù)越少,則其性能越差,越接近整詞分詞法.
總之trie索引樹的分詞性能是介于單字符樹與整詞法兩者之間,當(dāng)它的級數(shù)增大則向字符樹方向演變,當(dāng)它級數(shù)減少則向整詞詞庫方法演變,整詞法與字符樹法是trie樹的兩個極端情況.
本文從基于詞庫的中文分詞思想出發(fā),對現(xiàn)有分詞技術(shù)進(jìn)行分析對比,提出字符樹的分詞技術(shù),解決了中文分詞中的無效查詢問題. 首先它改進(jìn)了詞庫查詢方式與分詞掃瞄方式,大大減少了運算的復(fù)雜度,減少運算量,尤其在大規(guī)模詞庫中顯示出具有的巨大優(yōu)勢;其次,單字符樹詞庫占用內(nèi)存小,大大降低了程序的空間復(fù)雜度. 無論在時間復(fù)雜度還是空間復(fù)雜度,單字符樹都是一種占據(jù)絕對優(yōu)勢的,在大數(shù)據(jù)處理中文信息時值得推廣的中文分詞技術(shù).