最大概率法分詞
優點:
由於最大概率法考慮的是某種字串出現的條件下,最可能劃分的詞串,因此在擁有大量標註語料的前提下,可以在一定程度上避免切分歧義。
原理:
設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');
}
}