再看 GenerateWordNet 函数,这部分解析摘抄别人的笔记吧,因为有些处理自己还不是太明白。
GenerateWordNet 函数根据核心词典将原始的句子生成词图并存储在m_segGraph 中。首先会将句子进行原子切分,然后利用词典中的词及词性词频信息来置相应的权重值。第三个参数bOriginalFreq 的默认取值为flase ,当这个值为false 时为非原始的频率值,一般采用默认值最大的或者0 来设置,具体规则见后。如果为true 则是利用词典中获取的频率值来做相应的设置。
对于粗切好的每个原子结点,在非原始频率的情况下,如果类型为汉字,则设置词图,其value值取log 最大频率,词性为0 即未知状态,词本身也不需要存储;如果为其他类型,则需要额外判断一下是不是一些特别的类型,比如说CT_INDEX、CT_NUM 和CT_SINGL E_NUM 都被判断为未处理数,设置相应的词性-27904 及va lue 值(0),分割符为30464 而value值为最大频率等,不一一列举。va l ue值为0 而词性为修改后结果。
第二步词图生成是根据已有的词典,会有一个规则是不要把“月”和“份”切开,这个不太合时宜,呵呵。然后对于粗分后的每个词,如果在词典中找到最大匹配,则需要重新估算其频率值,同样的有一个对于组合数词的判断。如果生成的结果只有一个词性则更新valu e 值时同时置这个明确的词性,否则只更新value。这一步是个最大匹配的过程,而且每个匹配的中间部分都会被记录下来。(粗分结果),在其中还会有一个关于时间词串的扩展规则,也不太合时时宜,主要是针对类似“1 年内、1999年末”这种词,将末尾的诸如“末内中底前间初”的词处理一下。
贴上源码,
bool CSegGraph::GenerateWordNet(char *sSentence,CDictionary &dictCore,bool bOriginalFreq)
{
unsigned int i=0,j,nLen=strlen(sSentence);//nLen记录句子长度
char sWord[WORD_MAXLENGTH]="",sTempWord[WORD_MAXLENGTH]="",sWordMatch[WORD_MAXLENGTH];
int nWordIndex=0,nHandleTemp,k,nPOS;
int nMatchFreq[20],nMatchHandle[20],nTotalFreq,nMatchCount;
double dValue=0;
m_nAtomCount=0;
m_segGraph.SetEmpty();//清空切割词图
AtomSegment(sSentence);//初次原子切分
for(i=0;i<m_nAtomCount;i++)//Init the cost array
{
if(m_nAtomPOS[i] == CT_CHINESE)//The atom is a Chinese Char
{
if(!bOriginalFreq)//在非原始频率的情况下,如果类型为汉字,则设置词图,其value值取log最大频率,词性为0即未知状态,词本身也不需要存储
m_segGraph.SetElement(i,i+1,(long double)log((long double)MAX_FREQUENCE),0);//init the link with the maximum value
else
m_segGraph.SetElement(i,i+1,0,0,m_sAtom[i]);
}
else//如果不是中文字符
{
strcpy(sWord,m_sAtom[i]);//sWord记录第i个原子词
dValue = MAX_FREQUENCE;//dValue初始化为最大频率值
switch(m_nAtomPOS[i])
{
case CT_INDEX:
case CT_NUM:
nPOS = -27904;// 'm'*256
strcpy(sWord,"未##数");//被判为未处理数
dValue = 0;
break;
case CT_DELIMITER:
nPOS = 30464;// 'w'*256;
break;
case CT_LETTER:
nPOS = -'n'*256 - 'x';//
dValue = 0;
strcpy(sWord,"未##串");
break;
case CT_SINGLE://12021-2129-3121
if( GetCharCount("+-1234567890",m_sAtom[i]) == (int)strlen(m_sAtom[i]) )
{
nPOS = -27904;// 'm'*256
strcpy(sWord,"未##数");
}
else
{
nPOS= -'n'*256 - 'x';//
strcpy(sWord,"未##串");
}
dValue = 0;
break;
default:
nPOS = m_nAtomPOS[i];// '?'*256;
break;
}
if(!bOriginalFreq)//Not original frequency
m_segGraph.SetElement(i,i+1,0,nPOS);//value值为0而词性为修改后结果
else
m_segGraph.SetElement(i,i+1,dValue,nPOS,sWord);//init the link with minimum
}
}
i=0;
while(i<m_nAtomCount)//All the word
{
strcpy(sWord,m_sAtom[i]);//Get the current atom
j = i+1;//j记录下一个原子词位置
if(strcmp(sWord,"月")==0 && strcmp(m_sAtom[i+1],"份")==0)//Don't split 月份
j += 1;
while(j<=m_nAtomCount && dictCore.GetMaxMatch(sWord,sWordMatch,&nHandleTemp))
{//Add a condition to control the end of string
//retrieve the dictionary with the word
if(strcmp(sWordMatch,sWord)==0)//find the current word
{
nTotalFreq=0;
dictCore.GetHandle(sWord,&nMatchCount,nMatchHandle,nMatchFreq);
for(k=0;k<nMatchCount;k++)//Add the frequency
{
nTotalFreq += nMatchFreq[k];
}
//Adding a rule to exclude some words to be formed.
if(strlen(sWord)==4 && i>=1 && (IsAllNum((unsigned char *)m_sAtom[i-1]) || IsAllChineseNum(m_sAtom[i-1])) && (strncmp(sWord,"年",2)==0 || strncmp(sWord,"月",2)==0))
{//1年内、1999年末
if(CC_Find("末内中底前间初",sWord+2))
break;//跳出循环
}
if(nMatchCount==1)//The possible word has only one POS, store it
{
if(!bOriginalFreq)//Not original frequency
m_segGraph.SetElement(i,j,-(long double)log((long double)(nTotalFreq+1))+(long double)log((long double)MAX_FREQUENCE),nMatchHandle[0]);
else
m_segGraph.SetElement(i,j,nTotalFreq,nMatchHandle[0],sWord);
}
else
{
if(!bOriginalFreq)//Not original frequency
m_segGraph.SetElement(i,j,-(long double)log((long double)(nTotalFreq+1))+(long double)log((long double)MAX_FREQUENCE),0);
else
m_segGraph.SetElement(i,j,nTotalFreq,0,sWord);
}
}
strcat(sWord,m_sAtom[j++]);
}
i+=1;//Start from i++;
}
return true;
}