ICTCLAS 中科院分詞系統
中科院分詞系統概述
這幾天看完了中科院分詞程式的程式碼,現在來做一個概述,並對一些關鍵的資料結構作出解釋
〇、總體流程
考慮輸入的一句話,sSentence="張華平歡迎您"
總體流程:
一、分詞 "張/華/平/歡迎/您"
二、posTagging "張/q 華/j 平/j 歡迎/v 您/r"
三、NE識別:人名識別,音譯名識別,地名識別 "張/q 華/j 平/j 歡迎/v 您/r" "張華平/nr"
四、重新分詞:"張華平/歡迎/您"
五、重新posTagging: "張華平/nr 歡迎/v 您/r"
技術細節
一、分詞
分詞程式首先在其頭末新增開始符和結束符
sSentence="始##始張華平歡迎您末##末"
然後是分詞,基本思想就是分詞的得到的詞的聯合概率最大
假設 "張華平歡迎您" 分為 "w_1/w_2/.../w_k" 則
w_1/w_2/.../w_k=argmax_{w_1'/w_2'/.../w_k'}P(w_1',w_2',...,w_k')=argmax_{w_1'/w_2'/.../w_k'}P(w_1')P(w_2')...P(w_k')
細節:
首先給原句按字劃分,所有漢字一個一段,連續的字母,數字一段,比如"始##始張華平2006歡迎您asdf末##末"被劃為"始##始/張/華/平/2006/歡/迎/您/asdf/末##末"
接著找出這個句子中所有可能出現的詞,比如"始##始張華平歡迎您末##末",出現的詞有"始##始","張","華","平","歡","迎","您","末##末","歡迎"
並查詢這些詞所有可能的詞性和這些詞出現的頻率。
將這些詞儲存在一個結構中,具體實現如下:
m_segGraph中有一個(PARRAY_CHAIN)m_pHead,是一個鏈
(PARRAY_CHAIN)p->row//記錄該詞的頭位置
(PARRAY_CHAIN)p->col//記錄該詞的末位置
(PARRAY_CHAIN)p->value//記錄該詞的-log(出現的概率),出現的頻率指所有該詞的所有詞性下出現的概率的總和。
(PARRAY_CHAIN)p->nPos//記錄該詞的詞性,比如人名標記為'nr',則對應的nPos='n'*256+'r',如果該詞有很多詞性,則nPos=0
(PARRAY_CHAIN)p->sWord//記錄該詞
(PARRAY_CHAIN)p->nWordLen//記錄該詞的長度
舉個例子:
"0 始##始 1 張 2 華 3 平 4 歡 5 迎 6 您 7 末##末 8"
對於"張"來說,
row=1
col=2
value=-log[("張"出現的頻率+1)/(MAX_FREQUENCE)]
nPos=0//"張"有5種詞性
sWord="張"
nWordLen=2
儲存的順序是按col升序row升序的次序排列
m_segGraph.m_pHead "始##始"
"張"
"華"
"平"
"歡"
"歡迎"
"迎"
"您"
"末##末"
m_segGraph.m_nRow=7
m_segGraph.m_nCol=8
然後是生成一幅給予各種組合情況的圖,並按照出現的概率大小儲存概率最大的前m_nValueKind個結果。
細節:
初始化,
(CNShortPath)sp.m_apCost=m_segGraph;
(CNShortPath)sp.m_nVertex=m_segGraph.m_nCol+1
(CNShortPath)sp.m_pParent=CQueue[m_segGraph.m_nCol][m_nValueKind]
(CNShortPath)sp.m_pWeight=ELEMENT_TYPE[m_segGraph.m_nCol][m_nValueKind]//m_pWeight[0][0]表示1處的weight
sp.ShortPath()函式中,
for(nCurNode=1;nCurNode<sp.m_nVertex;nCurNode++)
{
CQueue queWork;//零時的CQueue
eWeight=m_apCost->GetElement(-1,nCurNode,0,&pEdgeList);//取出col=nCurNode的第一個PARRAY_CHAIN的value,比如nCurNode=6,則pEdgeList指向"歡迎",eWeight="pEdgeList->value
while(pEdgeList&&pEdgeList->col==nCurNode)//對每一個col=nCurNode的pEdgeList
{
for(i=0;i<m_nValueKind;i++)
{
queWork.Push(pEdgeList->row,0,eWeight+m_pWeight[pEdgeList->row-1][i]);
//將所有col=nCurNode的pEdgeList按照其weight升序放到queWork中
}
}//比如
/*
"歡迎" m_pWeight[3][0]=0.2 eWight=0.2 =>queWork.Push(4,0,0.4);
"0 始##始 1 張 2 華 3 平 4 歡 5 迎 6 您 7 末##末 8"
"歡" m_pWeight[4][0]=0.5 eWight=0.1 =>queWork.Push(5,0,0.6);
m_pWeight[4][1]=0.6 eWight=0.1 =>queWork.Push(5,0,0.7);
queWork "歡迎" 0.4
"迎" 0.6
"迎" 0.7
*/
for(i=0;i<m_nValueKind;i++)m_pWeight[nCurNode-1][i]=INFINITE_VALUE;//初始化當前的m_pWeight[nCurNode-1]
while(i<m_nValueKind&&queWork.Pop(&nPreNode,&nIndex,&eWeight)!=-1)//從queWork中順序取出每個pEdgeList的row,nIndex的取值從0到m_nValueKind-1,eWeight=pEdgeList->value
{
m_pWeight[nCurNode-1][i]=eWeight;//取前m_nValueKind個結果
m_pParent[nCurNode-1][i].Push(nPreNode,nIndex);//按照pEdgeList->value的升序,也就是P的降序放入m_pParent
}
}
得到m_pParent之後,按照m_pWeight[m_segGraph.m_nCol-1]的升序,生成path
CNShortPath::GetPaths(unsigned int nNode,unsigned int nIndex,int **nResult,bool bBest)
//nNode=m_segGraph.m_nCol,nIndex從0取到m_nValueKind-1,nResult輸出結果,bBest=true只輸出最佳結果
比如"始##始張華平歡迎您末##末"的結果為
nResult[0]={0,1,2,3,4,6,7,8,-1} "始##始/張/華/平/歡迎/您/末##末"
nResult[1]={0,1,2,3,4,5,6,7,8,-1} "始##始/張/華/平/歡/迎/您/末##末"
沒有第三種結果
取出所有nResult[i]作為分詞結果,結果儲存在m_graphOptimum中,m_graphOptimum和m_segGraph結構一樣,只不過只存nResult[i]中的結果:
如果m_nValueKind=1則
m_graphOptimum.m_pHead "始##始"
"張"
"華"
"平"
"歡迎"
"您"
"末##末"
m_graphOptimum.m_nRow=7
m_graphOptimum.m_nCol=8
如果m_nValueKind=2則
m_graphOptimum.m_pHead "始##始"
"張"
"華"
"平"
"歡"
"歡迎"
"迎"
"您"
"末##末"
m_graphOptimum.m_nRow=7
m_graphOptimum.m_nCol=8
見 bool CSegment::GenerateWord(int **nSegRoute, int nIndex)這裡的nSegRoute=上面的nResult,是輸入引數;nIndex表示第nIndex個分詞結果
同時,CResult.m_Seg.m_pWordSeg[nIndex][k]中儲存了第nIndex個結果的第k個詞的資訊:
CResult.m_Seg.m_pWordSeg[nIndex][k].sWord//詞
CResult.m_Seg.m_pWordSeg[nIndex][k].nHandle//詞性
CResult.m_Seg.m_pWordSeg[nIndex][k].dValue//-logP
至此,分詞部分結束
二、posTagging
m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore);//對第nIndex個分詞結果用標準的字典標註
方便起見,下面假設m_nValueKind=1
m_POSTagger用HMM對分詞進行標註,這裡輸出概率為P(w_i|c_i),c_i為詞性,w_i為詞;轉移概率為P(c_i|c_{i-1}),初始狀態為P(c_0)即P("始##始"的詞性)
用維特比演算法求出一個c_1/c_2/.../c_k=argmax_{c_1'/c_2'/.../c_k'}P(w_1',w_2',...,w_k')
將句子分成若干段,每段以有唯一pos的w結尾,也就是分詞中CResult.m_Seg.m_pWordSeg[0][k].nHandle>0的那些詞
比如,舉個例子
"0 始##始 1 張 2 華 3 平 4 歡迎 5 您 6 末##末 7"
pos1 pos1 pos1 pos1 pos1 pos1 pos1
pos2 pos2 pos2 pos2
pos3 pos3 pos3
pos4
pos5
則該句被劃分為
"0 始##始"
"1 張 2 華 3 平 4 歡迎 5 您"
"6 末##末"
對每一段用維特比演算法確定一個唯一的postag
細節:
首先P(w,c)的輸出概率儲存在dict中,比如dictCore,dictUnknow,通過dict.GetFrequency(char *sWord, int nHandle)函式獲取 sWord pos為nHandle的函式
概率P(c)儲存在context中,比如m_context,通過context.GetFrequency(int nKey, int nSymbol)函式獲取 pos為nSymbol的函式,nKey=0
轉移概率P(c|c')儲存在context中,比如m_context,通過context.GetContextPossibility(int nKey, int nPrev, int nCur)函式獲取 c'=nPrev,c=nCur的轉移概率,nKey=0
重要的資料結構
m_nTags[i][k]表示第i個w的第k個pos
在GetFrom函式中表示 -log(第i個w的第k個pos的輸出概率)
在CSpan::Disamb()函式中
m_dFrequency[i][k]表示 -log(從第0個w到第i個w的第k個pos的聯合最大輸出概率),比如
w_j w_{j+1}
m_dFrequency[j][0]-- m_dFrequency[j+1][0]
m_dFrequency[j][1] -- m_dFrequency[j+1][1]
--m_dFrequency[j+1][2]
則 圖中的路徑的權為W([j,0]->[j+1,2])=m_dFrequency[j][0]-log(m_context.GetContextPossibility(0,m_nTags[j][0],m_nTags[j+1][2]))
這樣,選擇
m_dFrequency[j+1][2]=min{W([j,0]->[j+1,2]),W([j,1]->[j+1,2])}
m_nCurLength表示當前段的w個數+1
在m_POSTagger.POSTagging中,以上面的例子為例
while(i>-1&&pWordItems[i].sWord[0]!=0)//將執行段的個數次,比如上例中將執行3次
{
i=GetFrom(pWordItems,nStartPos,dictCore,dictUnknown);//i=GetFrom(pWordItems,0,dictCore,dictUnknown)=1
//i=GetFrom(pWordItems,1,dictCore,dictUnknown)=6
//i=GetFrom(pWordItems,6,dictCore,dictUnknown)=7
//從nStartPos向前取w,一直取到一個有唯一pos的w為止,該過程中記錄每個w的pos,儲存在m_nTags中,記錄log(w|c)輸出概率儲存在m_dFrequency中
GetBestPOS();//呼叫Disamb()函式,用維特比演算法找出該段的最佳(聯合輸出概率最大)的標註,最佳路徑儲存在m_nBestTag中
通過讀取m_nBestTag對pWordItems.nHandle進行賦值
}
三、NE識別:人名識別,音譯名識別,地名識別
其基本思路和PosTagging一樣,只不過詞性c換成了role r,以人名識別為例,首先識別出人名的tag(即pos),見
"Chinese Named Entity Recognition Using Role Model"
在函式CUnknowWord::Recognition(PWORD_RESULT pWordSegResult, CDynamicArray &graphOptimum,CSegGraph &graphSeg,CDictionary &dictCore)中
每個被切開的段被識別完之後,用m_roleTag.POSTagging(pWordSegResult,dictCore,m_dict);對第一步分詞的結果進行一次標記。
首先用dictUnknown.GetHandle(m_sWords[i],&nCount,aPOS,aFreq);獲得m_sWords[i]在NE詞典中的role,
接著用dictCore.GetHandle(m_sWords[i],&nCount,aPOS,aFreq);獲得m_sWords[i]在標準詞典中的tag,這裡只要m_sWords[i]在標準詞典中有tag,那麼tag一律標記為0,該tag下的輸出概率為P(w|c)=P(sum_{aFreq}|c=0)
接下來用SplitPersonPOS(dictUnknown)函式將其中tag為LH和TR的w拆成兩個
比如"張/SS 華/GH 平歡/TR 迎/RC 您/RC"中"平歡"被拆成"平/GT" "歡/12"
接著在PersonRecognize(dictUnknown);函式中,用一些模板進行匹配,"SS/GH/TR"將匹配到"張華平"。匹配得到的片斷儲存在m_nUnknownWords中,其nHandle被設定為人名,地名,音譯名中的一個
對第一步中的graphOptimum,加入m_nUnknownWords的邊:
graphOptimum.GetElement(nAtomStart,nAtomEnd,&dValue,&nPOSOriginal);
if(dValue>m_roleTag.m_dWordsPossibility[i])//Set the element with less frequency
graphOptimum.SetElement(nAtomStart,nAtomEnd,m_roleTag.m_dWordsPossibility[i],m_nPOS);
四、重新分詞
對上一步的graphOptimum,用第一步中對m_segGraph分詞的方法,找出一個聯合概率最大的分詞結果:
m_Seg.OptimumSegmet(nCount);
五、重新標註
對於四中分好的結果,用標準詞典對其進行posTagging:
for(nIndex=0;nIndex<m_Seg.m_nSegmentCount;nIndex++)//m_Seg.m_nSegmentCount是第四步中的分詞結果個數
{
m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore);
}
最後,用Sort();對標註結果按照聯合輸出概率的大小降序排序,並按照使用者的需求輸出前幾個
相關文章
- 使用ICTCLAS JAVA版(ictclas4j)進行中文分詞(附ictclas,停用詞表,commons-lang-2.4.jar下載地址)...Java中文分詞JAR
- 文字挖掘之語料庫、分詞、詞頻統計分詞
- 分詞分詞
- python 實現中文分詞統計Python中文分詞
- HanLP分詞工具中的ViterbiSegment分詞流程HanLP分詞Viterbi
- #Elasticsearch中文分詞器 #IK分詞器 @FDDLCElasticsearch中文分詞
- 分詞-1分詞
- NLPIR語義分析系統不斷提高中文分詞準確率中文分詞
- Python分詞模組推薦:jieba中文分詞PythonJieba中文分詞
- 深度學習在NLP中的運用?從分詞、詞性到機器翻譯、對話系統深度學習分詞
- Elasticsearch 分詞器Elasticsearch分詞
- 系統分頁
- 分詞工具Hanlp基於感知機的中文分詞框架HanLP中文分詞框架
- HanLP-實詞分詞器詳解HanLP分詞
- python分詞和生成詞雲圖Python分詞
- jieba 詞性標註 & 並行分詞Jieba詞性標註並行分詞
- 動詞過去式過去分詞分詞
- 中文分詞工具之基於字標註法的分詞中文分詞
- 中文分詞原理及常用Python中文分詞庫介紹中文分詞Python
- ElasticSearch中使用ik分詞器進行實現分詞操作Elasticsearch分詞
- 嵌入式系統專業名詞:
- Elasticsearch IK分詞器Elasticsearch分詞
- 中文分詞技術中文分詞
- 分詞演算法分詞演算法
- 理財分紅系統開發-分紅系統開發
- 中科院計算所煙臺分所來我院招聘
- linux系統下可以螢幕取詞的詞典安裝Linux
- IKAnalyzer 中文分詞的不同版本切詞方式中文分詞
- 中文分詞研究難點-詞語劃分和語言規範中文分詞
- 系統的分類
- 使用cjieba(結巴分詞庫)實現php擴充套件中文分詞JiebaPHP套件中文分詞
- Hanlp分詞之CRF中文詞法分析詳解HanLP分詞CRF詞法分析
- pyhanlp 中文詞性標註與分詞簡介HanLP詞性標註分詞
- ElasticSearch之ICU分詞器Elasticsearch分詞
- Elasticsearch整合HanLP分詞器ElasticsearchHanLP分詞
- 一個分詞指令碼分詞指令碼
- IK 分詞器外掛分詞
- 【Python】jieba分詞模組PythonJieba分詞