1. 程式人生 > >最大概率法分詞

最大概率法分詞

優點:
由於最大概率法考慮的是某種字串出現的條件下,最可能劃分的詞串,因此在擁有大量標註語料的前提下,可以在一定程度上避免切分歧義。

原理:
設Z=z1z2…zn表示字串,
W=w1w2…wm表示切分後的詞串,
漢語詞語切分可以看作是求使P(W|Z)最大的切分。

p(W|Z) = P(W)P(Z|W)/P(Z)
P(Z)是漢字串的概率,它對於各個候選詞串都是一樣的,不必考慮。
P(Z|W)表示出現詞串的條件下漢字串的概率,顯然該值為1,不必考慮,則僅需考慮P(W)即詞串的概率。

詞串的概率可以通過n元語法來求
用二元語法 P(W)=p(w1|w0)p(w2|w1)…p(wm|wm-1)(1)
用一元語法 P(W)=p(w1)p(w2)…p(wm)(2)

計算詞串概率時,有一個純技術的問題要考慮:
因為每個詞的概率都是一個很小的正數,如果漢字串較長,最後得到各種可能的詞串的概率都接近於0,無法在機器上表示,當然也就無法比較大小。解決這個問題的辦法是:對等式兩邊(1)、(2)兩邊取負對數,即
-logp(W) = -logp(w1)-logp(w2)…-logp(wm)(3)
根據-logx的曲線特性,可知p(wi)越大,最終的結果越小,因此求(1)、(2)式最大值變為求(3)式的最小值。

按照最短路徑優先演算法(嚴蔚敏)得到C實現
漢語自動分詞系統的評價
由於本文中的分詞只是一個測試系統,因此並未做評價分析。
準確率:p=系統輸出正確詞的個數/系統輸出詞的個數
召回率:R=系統輸出正確詞的個數/標準答案中詞的個數
F值:F = 2*P*R/(P+R)

關於分詞的評測會議
第41屆ACL國際會議(41st Annual Meeting of the Association for Computational Linguistic,國際計算語言聯合會)下設的漢語特別興趣研究組
SIGHAN(the ACL Special Interest Group on Chinese Language Processing)於2003年4月22日至25日舉辦了第一屆國際漢語分詞評測大賽。
語料庫和標準分別來自北京大學(簡體版)、賓州樹庫(簡體版)、香港城市大學(繁體版),臺灣“中央院”(繁體版)。每家標準分為兩個任務(Track)
:受限訓練任務(Close Track)和非受限訓練任務(Open Track)。

/***********************************************************
*   File Name               : partition.c
*   Copyright               : 
*   Module Name             : 分詞相關操作——N最短路徑法
*
*   CPU                     : id-2328M CPU @ 2.20GHz
*   OS                      : MicroSoft Windows Xp
*
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*
*   Abstract Description    : 
-----------------Revision Histroy---------------------------
No  Version     Date    Revised By      Item    Description
************************************************************/
#include "partition.h"

/***********************************************************
*   Function Name           : Cut_Word
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 分詞
*   Param                   : fp:輸入檔案路徑
                              tp:輸出檔案路徑
*   Return Code             : 返回開啟檔案狀態
                              NULL: 開啟檔案失敗
                              fp  : 開啟檔案的指標
************************************************************/
extern void Cut_Word(FILE *fp,FILE *tp)
{
    char wbuffer[500]="\0";
    int flag = 1;
    unsigned int cnt = 0;
    unsigned int i = 0;
    printf("對輸入文字分詞中。。。");
    while(cnt = Read_a_Sentence(fp,wbuffer,&flag))//讀一句話
    {
        //printf("%s\n",wbuffer);
        //printf("%d",cnt);
        if(flag == 0)
        {
            Cut_a_Sentence(tp,wbuffer,cnt);
        }
        if(flag == 1)
        {
            wbuffer[cnt++] = '/';
            wbuffer[cnt++] = ' ';
            wbuffer[cnt] = '\0';
            fprintf(tp,"%s",wbuffer);
            //printf("%s",wbuffer);
        }
    }
    printf("\n分詞完畢!\n");

}

/***********************************************************
*   Function Name           : Cut_Word
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 對一句話分詞
*   Param                   : testp:輸出檔案路徑
                              wbuffer:輸入的一句話
                              i:句子長度
*   Return Code             : 返回0成功
************************************************************/
extern int Cut_a_Sentence(FILE *testp,char wbuffer[],unsigned int i)
{
    OLGraph *G;
    Array *A;

    G =Creat_W_Graph(i/2+1,i/2,wbuffer);
    Search_Word(wbuffer,i,G);
    A = InitArray(i/2+1,i/2+1);
    Display_Output(G,A,ShortestPath_DIJ(G,A),testp);
    DestroyArray(A);
    DestroyGraph(G);
    return 0;
}

/***********************************************************
*   Function Name           : Creat_W_Graph
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 構建詞圖
*   Param                   : vexnum:頂點數
                              arcnum:弧數
                              pw:    輸入的句子
*   Return Code             : 返回詞圖地址
************************************************************/
extern OLGraph *Creat_W_Graph(unsigned int vexnum,unsigned int arcnum,char *pw)
{
    OLGraph *tG;
    ArcBox *s;
    char *w;
    unsigned short int t = 0;
    unsigned int i,j,k;

    tG = (OLGraph *)malloc(sizeof(OLGraph));
    tG->vexnum = vexnum;
    tG->arcnum = arcnum;
    for(i=0;i<tG->vexnum;i++)
    {
        tG->xlist[i].data = 0;
        tG->xlist[i].firstin = NULL;
        tG->xlist[i].firstout = NULL;
    }
    for(k=0,i=0,j=1;k<tG->arcnum;k++,i++,j++)
    {
        s = (ArcBox *)malloc(sizeof(ArcBox));
        if(s == NULL)printf("沒有記憶體啦!");
        s->tailvex = i;
        s->headvex = j;
        s->hlink = NULL;
        s->tlink = NULL;
        s->probability = 0;
        w = (char *)malloc(3*sizeof(char));
        if(w == NULL)printf("沒有記憶體啦!");
        *w = *(pw+2*i);
        *(w+1) = *(pw+2*i+1);
        *(w+2) = '\0';
        s->info = w;
        t = (*(pw+2*i) & 0x0FF) << 8;
        t |= 0x0ff & *(pw+2*i+1);
        s->probability  = address[t].priority ;
        tG->xlist [j].firstin = tG->xlist [i].firstout = s;
    }
    return tG;
}

/***********************************************************
*   Function Name           : Search_Word
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 建立所有可能的詞弧
*   Param                   : warr:    輸入的句子
                              num:     句子長度
                              OLGraph: 詞圖
*   Return Code             : 
************************************************************/
extern void Search_Word(char warr[],unsigned int num,OLGraph *g)
{
    unsigned int i = 0;
    unsigned int j = 1;
    unsigned int k = 0;
    unsigned int tu = 0;
    FollowChar *tp1,*tp2;
    FollowWords *tp3;
    unsigned int u,v;
    unsigned int visited[200] = {0};
    ArcBox *op;
    while(1)
    {
        if(j > num)
        {
            for(k=0;k< num/2;k++)
            {
                if(visited[k] == 0)
                {
                    i = 2*k;
                    j = i+1;
                    break;
                }
            }
            if(k == num/2)
            {
                break;
            }
        }
        if((tp1 = Search_First_Word(warr,i,j)) == NULL)
        {
            visited[i/2] = 1;
            /*printf("%c",warr[i]);
            printf("%c",warr[j]);
            printf("%s","====組成新弧\n");*/
            i += 2;
            j += 2;
        }
        else 
        {
            i += 2;
            j += 2;
            if( (tp2 = Search_Second_Word(tp1,warr,i,j)) == NULL)
            {
                visited[(i-2)/2] = 1;
                /*printf("%c",warr[i-2]);
                printf("%c",warr[j-2]);
                printf("%s","====組成新弧\n");*/
            }
            else
            {
                if(tp2->priority > 0)
                {
                    u = (i-2)/2;
                    v = (i-2)/2+2;
                    if(SearchArc(g,u,v))
                    {
                        op = InsertArc(g,u,v,warr,tp2->priority );
                        //printf("建立 (%d) ---%s---> (%d)的弧\n",op->tailvex ,op->info ,op->headvex );//建立弧  到 (i-2)/2+2
                    }
                }
                i += 2;
                j += 2;
                if((tp3 = Search_Third_Word(tp2->Down,warr,i,j)) == NULL)
                {
                    visited[(i-4)/2] = 1;
                    /*printf("%c",warr[i-4]);
                    printf("%c",warr[j-4]);
                    printf("%c",warr[i-2]);
                    printf("%c",warr[j-2]);
                    printf("%s","====組成新弧\n");*/
                }
                else
                {
                    visited[(i-4)/2] = 1;
                    //tu = i-strlen(tp3->wordptr);
                    u = (i-4)/2;
                    v = (i-4)/2+2+strlen(tp3->wordptr)/2;
                    if(SearchArc(g,u,v))
                    {
                        op = InsertArc(g,u,v,warr,tp3->priority );
                        //printf("建立 (%d) ---%s---> (%d)的弧\n",op->tailvex ,op->info ,op->headvex );//建立弧  到 (i-2)/2+2
                    }
                    i += strlen(tp3->wordptr);
                    j = i+1;
                    /*for(;tu<i;tu++)
                        putchar(warr[tu]);
                    printf("%s","====組成新弧\n");*/

                }
            }
        }
    }
}

/***********************************************************
*   Function Name           : Search_First_Word
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 匹配第一個字
*   Param                   : tarr:    輸入的句子
                              th:      開始位置
                              tl:      結束位置
*   Return Code             : 成功返回二級雜湊頭地址
                              失敗返回NULL
************************************************************/
extern FollowChar *Search_First_Word(char tarr[],unsigned int th,unsigned int tl)
{
    unsigned short int s = 0;
    s = 0x0FF&tarr[th];
    s = s << 8;
    s = s | (0x0FF&tarr[tl]);
    if(address[s].priority !=0)
        return address[s].Next;
    else
        return NULL;
}

/***********************************************************
*   Function Name           : Search_Second_Word
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 匹配第二個字
*   Param                   : tp:    二級雜湊頭地址
                              tarr:  輸入的句子
                              th:      開始位置
                              tl:      結束位置
*   Return Code             : 成功返回三級雜湊頭地址
                              失敗返回NULL
************************************************************/
extern FollowChar *Search_Second_Word(FollowChar *tp,char tarr[],unsigned int th,unsigned int tl)
{
    unsigned short int s = 0;
    s = 0x0FF&tarr[th];
    s = s << 8;
    s = s | (0x0FF&tarr[tl]);
    while(tp = tp->Next )
    {
        if(tp->value == s)return tp;
    }
    return NULL;
}

/***********************************************************
*   Function Name           : Search_Second_Word
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 匹配第三集雜湊中的片語
*   Param                   : tp:    三級雜湊頭地址
                              tarr:  輸入的句子
                              th:    開始位置
                              tl:    結束位置
*   Return Code             : 成功返回三級雜湊頭地址
                              失敗返回NULL
************************************************************/
extern FollowWords *Search_Third_Word(FollowWords *tp,char tarr[],unsigned int th,unsigned int tl)
{
    unsigned int i = 0;
    int flag = 0;
    while(tp = tp->Next)
    {
        for(i=0;i<strlen(tp->wordptr);i++)
        {
            if(*(tp->wordptr + i) != tarr[th+i])
            {
                flag =1;
                break;
            }
        }
        if(flag == 1)
        {
            flag = 0;
        }
        else
        {
            break;
        }
    }
    return tp;
}

/***********************************************************
*   Function Name           : SearchArc
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 檢查弧是否已被建立
*   Param                   : G:    詞圖
                              u:    開始結點
                              v:    結束結點
*   Return Code             : 建立了返回0
                              沒建立返回1
************************************************************/
extern int SearchArc(OLGraph *G,unsigned int u,unsigned int v)
{
    ArcBox *p;
    p = G->xlist [u].firstout;
    while(p->tlink)
    {
        if(p->headvex == v)
        {
            return 0;
        }
        p = p->tlink ;
    }
    return 1;
}

/***********************************************************
*   Function Name           : InsertArc
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 插入狐
*   Param                   : G:    詞圖
                              i:    開始結點
                              j:    結束結點
                              pw:   詞語
                              pb:   詞頻
*   Return Code             : 建立後的弧地址
************************************************************/
 extern ArcBox *InsertArc(OLGraph *G,unsigned int i,unsigned int j,char *pw,double pb)
 { /* 初始條件:有向圖G存在,v和w是G中兩個頂點。操作結果:在G中增添弧<v,w> */
   //int IncInfo;
   ArcBox *p;
   char *w;
   unsigned int k = 0;
   p=(ArcBox *)malloc(sizeof(ArcBox)); /* 生成新結點 */
   if(p == NULL)
   {
       printf("沒有記憶體啦!");
        return NULL;
   }
   p->tailvex=i; /* 給新結點賦值 */
   p->headvex=j;
   p->hlink=(*G).xlist[j].firstin; /* 插在入弧和出弧的鏈頭 */
   p->tlink=(*G).xlist[i].firstout;
   (*G).xlist[j].firstin=p;
   (*G).xlist[i].firstout=p;
   (*G).arcnum++; /* 弧數加1 */
    /* 帶權 */
    w = (char *)malloc(((j-i)*2+1)*sizeof(char)); /* 動態生成權值空間 */
    if(w == NULL)
    { 
        printf("沒有記憶體啦!");
        return NULL;
    }
    memcpy(w,pw+2*i,(j-i)*2+1);
    *(w+(j-i)*2) = '\0';
    p->info = w;
    p->probability = pb;
   return p;
 }

/***********************************************************
*   Function Name           : InitArray
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 插入狐
*   Param                   : row: 行數
                              colum:列數
*   Return Code             : 矩陣指標
************************************************************/
 extern Array *InitArray(unsigned int row,unsigned int colum)
{
    Array *a;
    int elemtotal = 0;
    a = (Array *)malloc(sizeof(Array));
    if(!a)printf("沒有記憶體啦!");
    a->dim = 2;
    a->bounds = (int *)malloc(2*sizeof(int));
    if(!a->bounds)printf("沒有記憶體啦!");
    a->bounds [0] = row;
    a->bounds [1] = colum;
    elemtotal = row*colum;
    a->base = (char *)malloc(elemtotal*sizeof(char));
    if(!a->base )printf("沒有記憶體啦!");
    return a;
}

 /***********************************************************
*   Function Name           : Display_Output
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 顯示分詞結果
*   Param                   : G: 詞圖
                              a: 矩陣
                              p: 句子
                              fp:輸出分詞結果文字地址
*   Return Code             : 
************************************************************/
 extern void Display_Output(OLGraph *G,Array *a,char *p,FILE *fp)
{
    unsigned int i = 0;
    unsigned int j = 0;
    ArcBox *pt;
    for(i=1;i<G->vexnum;i++)
    {
        if(*(p+i))
        {
            if(pt = G->xlist [j].firstout)
            {
                if(pt->headvex == i)
                {
                    //printf("%s/ ",pt->info);
                    fprintf(fp,"%s/ ",pt->info);
                }
                while(pt = pt->tlink)
                {
                    if(pt->headvex == i)
                    {
                        //printf("%s/ ",pt->info);  
                        fprintf(fp,"%s/ ",pt->info);
                    }
                }
            }
            j = i;
        }
    }
}

  /***********************************************************
*   Function Name           : DestroyArray
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 銷燬矩陣
*   Param                   : A:矩陣
*   Return Code             : 
************************************************************/
extern void DestroyArray(Array *A)
{
    free(A->bounds);
    free(A->base);
    A->base = NULL;
    A->bounds = NULL;
    A->dim = 0;
}

  /***********************************************************
*   Function Name           : DestroyGraph
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 銷燬圖
*   Param                   : g:詞圖
*   Return Code             : 
************************************************************/
extern void DestroyGraph(OLGraph *g)
{
    unsigned int i = 0;
    ArcBox *p;
    ArcBox *tp;
    while(g->xlist[i].firstout)
    {
        tp = g->xlist[i].firstout->tlink;
        while(tp)
        {
            p = tp;
            tp = p->tlink;
            free(p);
        }
        free(g->xlist[i].firstout);
        i++;
    }
    free(g);
}

 /***********************************************************
*   Function Name           : ShortestPath_DIJ
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : Dij演算法求最短路徑
*   Param                   : G: 詞圖
                              a: 矩陣
*   Return Code             : 
************************************************************/
extern char *ShortestPath_DIJ(OLGraph *G,Array *a)
{
    double *D;
    unsigned char *final;
    unsigned int v,w,i;
    double min = 0;
    D = (double *)malloc(G->vexnum*sizeof(double));
    if(!D)printf("沒有記憶體啦!");
    final = (unsigned char *)malloc((G->vexnum)* sizeof(unsigned char));
    if(!final)printf("沒有記憶體啦!");
    for(v=0;v<G->vexnum;v++)
    {
        *(final+v) = 0;
        *(D+v) = Distance_A_To_B(G,0,v);
        for(w=0;w<G->vexnum;w++)
        {
            Assign_Elem(a,v,w,0);
        }
        if(*(D+v)< DBL_MAX)
        {
            Assign_Elem(a,v,0,1);
            Assign_Elem(a,v,v,1);
        }
    }
    *D = 0;
    *final =1;

    /*dispay_D(G,D);
    dispy_final(G,final);
    display_Array(a);*/

    for(i=0;i<G->vexnum;i++)
    {
        min = DBL_MAX;
        for(w=0;w<G->vexnum;w++)
        {
            if(!(*(final+w)))
            {
                if(*(D+w) < min)
                {
                    v = w;
                    min = *(D+w);
                }
            }
        }
        *(final+v) = 1;
        for(w=0;w<G->vexnum;w++)
        {
            if((!(*(final+w))) && (min+Distance_A_To_B(G,v,w) < *(D+w))  )
            {
                *(D+w) = min + Distance_A_To_B(G,v,w);
                Copy_Line_To(a,v,w);
                Assign_Elem(a,w,w,1);
            }
        }
        /*dispy_final(G,final);
        dispay_D(G,D);
        display_Array(a);*/
    }

    return a->base+(G->vexnum-1)*(a->bounds[1]);
}

 /***********************************************************
*   Function Name           : ShortestPath_DIJ
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 修改矩陣某元素值
*   Param                   : row :行 colum:列
                              value :值
                              a: 矩陣
*   Return Code             : 
************************************************************/
extern int Assign_Elem(Array *a,unsigned int row,unsigned int colum,unsigned char value)
{
    *(a->base + (row%a->bounds[0])*a->bounds[1]+colum) = value ;
    return 0;
}

/***********************************************************
*   Function Name           : ShortestPath_DIJ
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 把矩陣一行元素值複製到另一行
*   Param                   : row1 :行1 row2:列2
                              a: 矩陣
*   Return Code             : 
************************************************************/
extern int Copy_Line_To(Array *a,unsigned int row1,unsigned int row2)
{
    memcpy(a->base+(row2%a->bounds[0])*a->bounds[1],a->base+((row1%a->bounds[0])*a->bounds[1]),a->bounds[1]);
    return 0;
}

/***********************************************************
*   Function Name           : Distance_A_To_B
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 計算兩結點間距離
*   Param                   : G :詞圖
                              u: 起點
                              v: 終點
*   Return Code             : 距離
************************************************************/
extern double Distance_A_To_B(OLGraph *G,unsigned int u,unsigned int v)
{
    ArcBox *p;
    if(u == v)
    {
        return 0;
    }
    else
    {
        if(p = G->xlist [u].firstout)
        {
            if(p->headvex == v)
            {
                return p->probability;
            }
            while(p = p->tlink)
            {
                if(p->headvex == v)
                {
                    return p->probability;
                }
            }
        }
    }
    return DBL_MAX;
}

/***********************************************************
*   Function Name           : dispay_D
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 測試用
*   Param                   : 
*   Return Code             : 
************************************************************/
extern void dispay_D(OLGraph *G,double *d)
{
    unsigned int i;
    putchar('\r');
    putchar('\n');
    printf("D[]= \n");
    for(i=0;i<G->vexnum;i++)
    {
        printf("%g\n",*(d+i));
    }
}

/***********************************************************
*   Function Name           : dispay_D
*   Create Date             : 2013/07/27
*   Author/Corporation      : 于飛
*   Description             : 測試用
*   Param                   : 
*   Return Code             : 
************************************************************/
extern void dispy_final(OLGraph *G,unsigned char *f)
{
    unsigned int i;
    putchar('\r');
    putchar('\n');
    printf("final[]= \n");
    for(i=0;i<G->vexnum;i++)
    {
        printf("%d ",*(f+i));
    }
}

extern void display_Array(Array *a)
{
    unsigned int i,j;
    putchar('\r');
    putchar('\n');
    printf("p[v][w]= \n");
    for(i=0;i<a->bounds[0];i++)
    {
        for(j=0;j< a->bounds[1];j++)
        {
            printf("%d ",*(a->base+i*a->bounds[1]+j));
        }
        putchar('\r');
        putchar('\n');
    }
}