刀劍笑

          用技術(shù)改善你的生活

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            13 隨筆 :: 3 文章 :: 3 評論 :: 0 Trackbacks

          中科院分詞系統(tǒng)概述

          這幾天看完了中科院分詞程序的代碼,現(xiàn)在來做一個(gè)概述,并對一些關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)作出解釋


          〇、總體流程

          考慮輸入的一句話,sSentence="張華平歡迎您"

          總體流程:

          一、分詞 "張/華/平/歡迎/您"

          二、posTagging "張/q 華/j 平/j 歡迎/v 您/r"

          三、NE識(shí)別:人名識(shí)別,音譯名識(shí)別,地名識(shí)別 "張/q 華/j 平/j 歡迎/v 您/r" "張華平/nr"

          四、重新分詞:"張華平/歡迎/您"

          五、重新posTagging: "張華平/nr 歡迎/v 您/r"

           


          技術(shù)細(xì)節(jié)

          一、分詞

          分詞程序首先在其頭末添加開始符和結(jié)束符
          sSentence="始##始張華平歡迎您末##末"

          然后是分詞,基本思想就是分詞的得到的詞的聯(lián)合概率最大

          假設(shè) "張華平歡迎您" 分為 "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')

          細(xì)節(jié):

          首先給原句按字劃分,所有漢字一個(gè)一段,連續(xù)的字母,數(shù)字一段,比如"始##始張華平2006歡迎您asdf末##末"被劃為"始##始/張/華/平/2006/歡/迎/您/asdf/末##末"

          接著找出這個(gè)句子中所有可能出現(xiàn)的詞,比如"始##始張華平歡迎您末##末",出現(xiàn)的詞有"始##始","張","華","平","歡","迎","您","末##末","歡迎"
          并查找這些詞所有可能的詞性和這些詞出現(xiàn)的頻率。

          將這些詞保存在一個(gè)結(jié)構(gòu)中,具體實(shí)現(xiàn)如下:

          m_segGraph中有一個(gè)(PARRAY_CHAIN)m_pHead,是一個(gè)鏈

          (PARRAY_CHAIN)p->row//記錄該詞的頭位置
          (PARRAY_CHAIN)p->col//記錄該詞的末位置
          (PARRAY_CHAIN)p->value//記錄該詞的-log(出現(xiàn)的概率),出現(xiàn)的頻率指所有該詞的所有詞性下出現(xiàn)的概率的總和。
          (PARRAY_CHAIN)p->nPos//記錄該詞的詞性,比如人名標(biāo)記為'nr',則對應(yīng)的nPos='n'*256+'r',如果該詞有很多詞性,則nPos=0
          (PARRAY_CHAIN)p->sWord//記錄該詞
          (PARRAY_CHAIN)p->nWordLen//記錄該詞的長度

          舉個(gè)例子:
          "0 始##始 1 張 2 華 3 平 4 歡 5 迎 6 您 7 末##末 8"

          對于"張"來說,
          row=1
          col=2
          value=-log[("張"出現(xiàn)的頻率+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


          然后是生成一幅給予各種組合情況的圖,并按照出現(xiàn)的概率大小保存概率最大的前m_nValueKind個(gè)結(jié)果。

          細(xì)節(jié):

          初始化,
          (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()函數(shù)中,
          for(nCurNode=1;nCurNode<sp.m_nVertex;nCurNode++)
          {
           CQueue queWork;//零時(shí)的CQueue
           eWeight=m_apCost->GetElement(-1,nCurNode,0,&pEdgeList);//取出col=nCurNode的第一個(gè)PARRAY_CHAIN的value,比如nCurNode=6,則pEdgeList指向"歡迎",eWeight="pEdgeList->value
           while(pEdgeList&&pEdgeList->col==nCurNode)//對每一個(gè)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;//初始化當(dāng)前的m_pWeight[nCurNode-1]
           while(i<m_nValueKind&&queWork.Pop(&nPreNode,&nIndex,&eWeight)!=-1)//從queWork中順序取出每個(gè)pEdgeList的row,nIndex的取值從0到m_nValueKind-1,eWeight=pEdgeList->value
           {
            m_pWeight[nCurNode-1][i]=eWeight;//取前m_nValueKind個(gè)結(jié)果
            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輸出結(jié)果,bBest=true只輸出最佳結(jié)果
          比如"始##始張華平歡迎您末##末"的結(jié)果為
          nResult[0]={0,1,2,3,4,6,7,8,-1}  "始##始/張/華/平/歡迎/您/末##末"
          nResult[1]={0,1,2,3,4,5,6,7,8,-1} "始##始/張/華/平/歡/迎/您/末##末"
          沒有第三種結(jié)果

          取出所有nResult[i]作為分詞結(jié)果,結(jié)果保存在m_graphOptimum中,m_graphOptimum和m_segGraph結(jié)構(gòu)一樣,只不過只存nResult[i]中的結(jié)果:

          如果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,是輸入?yún)?shù);nIndex表示第nIndex個(gè)分詞結(jié)果


          同時(shí),CResult.m_Seg.m_pWordSeg[nIndex][k]中保存了第nIndex個(gè)結(jié)果的第k個(gè)詞的信息:

          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

          至此,分詞部分結(jié)束

          二、posTagging


          m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore);//對第nIndex個(gè)分詞結(jié)果用標(biāo)準(zhǔn)的字典標(biāo)注
          方便起見,下面假設(shè)m_nValueKind=1


          m_POSTagger用HMM對分詞進(jìn)行標(biāo)注,這里輸出概率為P(w_i|c_i),c_i為詞性,w_i為詞;轉(zhuǎn)移概率為P(c_i|c_{i-1}),初始狀態(tài)為P(c_0)即P("始##始"的詞性)
          用維特比算法求出一個(gè)c_1/c_2/.../c_k=argmax_{c_1'/c_2'/.../c_k'}P(w_1',w_2',...,w_k')

          將句子分成若干段,每段以有唯一pos的w結(jié)尾,也就是分詞中CResult.m_Seg.m_pWordSeg[0][k].nHandle>0的那些詞

          比如,舉個(gè)例子
          "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 末##末"
          對每一段用維特比算法確定一個(gè)唯一的postag

          細(xì)節(jié):

          首先P(w,c)的輸出概率存儲(chǔ)在dict中,比如dictCore,dictUnknow,通過dict.GetFrequency(char *sWord, int nHandle)函數(shù)獲取 sWord pos為nHandle的函數(shù)
          概率P(c)存儲(chǔ)在context中,比如m_context,通過context.GetFrequency(int nKey, int nSymbol)函數(shù)獲取 pos為nSymbol的函數(shù),nKey=0
          轉(zhuǎn)移概率P(c|c')存儲(chǔ)在context中,比如m_context,通過context.GetContextPossibility(int nKey, int nPrev, int nCur)函數(shù)獲取 c'=nPrev,c=nCur的轉(zhuǎn)移概率,nKey=0

          重要的數(shù)據(jù)結(jié)構(gòu)

          m_nTags[i][k]表示第i個(gè)w的第k個(gè)pos
          在GetFrom函數(shù)中表示 -log(第i個(gè)w的第k個(gè)pos的輸出概率)
          在CSpan::Disamb()函數(shù)中
          m_dFrequency[i][k]表示 -log(從第0個(gè)w到第i個(gè)w的第k個(gè)pos的聯(lián)合最大輸出概率),比如


           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]

           

          則 圖中的路徑的權(quán)為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表示當(dāng)前段的w個(gè)數(shù)+1

          在m_POSTagger.POSTagging中,以上面的例子為例
          while(i>-1&&pWordItems[i].sWord[0]!=0)//將執(zhí)行段的個(gè)數(shù)次,比如上例中將執(zhí)行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,一直取到一個(gè)有唯一pos的w為止,該過程中記錄每個(gè)w的pos,保存在m_nTags中,記錄log(w|c)輸出概率保存在m_dFrequency中
           GetBestPOS();//調(diào)用Disamb()函數(shù),用維特比算法找出該段的最佳(聯(lián)合輸出概率最大)的標(biāo)注,最佳路徑保存在m_nBestTag中
           通過讀取m_nBestTag對pWordItems.nHandle進(jìn)行賦值
          }


          三、NE識(shí)別:人名識(shí)別,音譯名識(shí)別,地名識(shí)別

          其基本思路和PosTagging一樣,只不過詞性c換成了role r,以人名識(shí)別為例,首先識(shí)別出人名的tag(即pos),見
          "Chinese Named Entity Recognition Using Role Model"
          在函數(shù)CUnknowWord::Recognition(PWORD_RESULT pWordSegResult, CDynamicArray &graphOptimum,CSegGraph &graphSeg,CDictionary &dictCore)中
          每個(gè)被切開的段被識(shí)別完之后,用m_roleTag.POSTagging(pWordSegResult,dictCore,m_dict);對第一步分詞的結(jié)果進(jìn)行一次標(biāo)記。
          首先用dictUnknown.GetHandle(m_sWords[i],&nCount,aPOS,aFreq);獲得m_sWords[i]在NE詞典中的role,
          接著用dictCore.GetHandle(m_sWords[i],&nCount,aPOS,aFreq);獲得m_sWords[i]在標(biāo)準(zhǔn)詞典中的tag,這里只要m_sWords[i]在標(biāo)準(zhǔn)詞典中有tag,那么tag一律標(biāo)記為0,該tag下的輸出概率為P(w|c)=P(sum_{aFreq}|c=0)
          接下來用SplitPersonPOS(dictUnknown)函數(shù)將其中tag為LH和TR的w拆成兩個(gè)
          比如"張/SS 華/GH 平歡/TR 迎/RC 您/RC"中"平歡"被拆成"平/GT" "歡/12"
          接著在PersonRecognize(dictUnknown);函數(shù)中,用一些模板進(jìn)行匹配,"SS/GH/TR"將匹配到"張華平"。匹配得到的片斷保存在m_nUnknownWords中,其nHandle被設(shè)置為人名,地名,音譯名中的一個(gè)
          對第一步中的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分詞的方法,找出一個(gè)聯(lián)合概率最大的分詞結(jié)果:
          m_Seg.OptimumSegmet(nCount);

          五、重新標(biāo)注

          對于四中分好的結(jié)果,用標(biāo)準(zhǔn)詞典對其進(jìn)行posTagging:
          for(nIndex=0;nIndex<m_Seg.m_nSegmentCount;nIndex++)//m_Seg.m_nSegmentCount是第四步中的分詞結(jié)果個(gè)數(shù)
          {
           m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore);
          }


          最后,用Sort();對標(biāo)注結(jié)果按照聯(lián)合輸出概率的大小降序排序,并按照用戶的需求輸出前幾個(gè)


          來源:http://qxred.yculblog.com/post.1204714.html
          posted on 2007-12-28 22:10 刀劍笑 閱讀(3097) 評論(0)  編輯  收藏 所屬分類: ICTCLAS
          主站蜘蛛池模板: 原平市| 元谋县| 察隅县| 固阳县| 扎囊县| 时尚| 靖宇县| 辉县市| 尼木县| 新营市| 闸北区| 红桥区| 沁源县| 连山| 中超| 正宁县| 渑池县| 霸州市| 玉屏| 合山市| 无锡市| 夏河县| 铜鼓县| 奎屯市| 南江县| 灌云县| 邛崃市| 平陆县| 星子县| 大石桥市| 卓尼县| 泰顺县| 会东县| 张掖市| 庆安县| 九江县| 江陵县| 天柱县| 福泉市| 德江县| 封开县|