﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-刀剑笑-随笔分类-ICTCLAS</title><link>http://www.blogjava.net/jiangyz/category/28464.html</link><description>用技术改善你的生活</description><language>zh-cn</language><lastBuildDate>Fri, 28 Dec 2007 18:21:18 GMT</lastBuildDate><pubDate>Fri, 28 Dec 2007 18:21:18 GMT</pubDate><ttl>60</ttl><item><title>ICTCLAS分词系统研究（一） （转）</title><link>http://www.blogjava.net/jiangyz/archive/2007/12/28/171360.html</link><dc:creator>刀剑笑</dc:creator><author>刀剑笑</author><pubDate>Fri, 28 Dec 2007 15:58:00 GMT</pubDate><guid>http://www.blogjava.net/jiangyz/archive/2007/12/28/171360.html</guid><wfw:comment>http://www.blogjava.net/jiangyz/comments/171360.html</wfw:comment><comments>http://www.blogjava.net/jiangyz/archive/2007/12/28/171360.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangyz/comments/commentRss/171360.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangyz/services/trackbacks/171360.html</trackback:ping><description><![CDATA[&nbsp;ICTClAS分词系统是由中科院计算所的张华平、刘群所开发的一套获得广泛好评的分词系统，难能可贵的是该版的Free版开放了源代码，为我们很多初学者提供了宝贵的学习材料。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但有一点不完美的是，该源代码没有配套的文档，阅读起来可能有一定的障碍，尤其是对C/C++不熟的人来说.本人就一直用Java/VB作为主要的开发语言,C/C++上大学时倒是学过,不过工作之后一直没有再使用过,语法什么的忘的几乎一干二净了.但语言这东西,基本的东西都相通的,况且Java也是在C/C++的基础上形成的,有一定的相似处.阅读一遍源代码,主要的语法都应该不成问题了.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;虽然在ICTCLAS的系统中没有完整的文档说明,但是我们可以通过查阅张华平和刘群发表的一些相关论文资料,还是可以窥探出主要的思路.</p>
<p>&nbsp;&nbsp; 该分词系统的主要是思想是先通过CHMM(层叠形马尔可夫模型)进行分词,通过分层,既增加了分词的准确性,又保证了分词的效率.共分五层,如下图一所示:</p>
<p><img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/sinboy/CHMM结构图.bmp" /></p>
<p>基本思路:先进行原子切分,然后在此基础上进行N-最短路径粗切分,找出前N个最符合的切分结果,生成二元分词表,然后生成分词结果,接着进行词性标注并完成主要分词步骤.</p>
<p>下面是对源代码的主要内容的研究：</p>
<p>１.首先，ICTCLAS分词程序首先调用CICTCLAS_WinDlg::OnBtnRun()开始程序的执行.并且可以从看出它的处理方法是把源字符串分段处理。并且在分词前，完成词典的加载过程，即生成m_ICTCLAS对象时调用构造函数完成词典库的加载。关于词典结构的分析，请参加分词系统研究（二）。</p>
<p>void CICTCLAS_WinDlg::OnBtnRun() <br />
{</p>
<p>&nbsp;&nbsp; ......</p>
<p><font color="#3366ff">&nbsp;</font><font color="#0000ff">//在此处进行分词和词性标记</font></p>
<p>&nbsp; if(!<font color="#ff0000">m_ICTCLAS.ParagraphProcessing</font>((char *)(LPCTSTR)m_sSource,sResult))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;m_sResult.Format("错误：程序初始化异常！");<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;m_sResult.Format("%s",sResult);<font color="#0000ff">//输出最终分词结果</font></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;......</p>
<p>}</p>
<p>２.在OnBtnRun()方法里面调用分段分词处理方法bool CResult::ParagraphProcessing(char *sParagraph,char *sResult)完成分词的整个处理过程，包括分词的词性标注.其中第一个参数为源字符串，第二个参数为分词后的字符串.在这两个方法中即完成了整个分词处理过程，下面需要了解的是在此方法中，如何调用其它方法一步步按照上图所示的分析框架完成分词过程.为了简单起见，我们先不做未登录词的分析。</p>
<p><font color="#0000ff">//Paragraph Segment and POS Tagging</font><br />
bool CResult::ParagraphProcessing(char *sParagraph,char *sResult)<br />
{</p>
<p>&nbsp;&nbsp; ........</p>
<p>&nbsp;&nbsp; <font color="#ff0000">Processing</font>(sSentence,1); <font color="#0000ff">//Processing and output the result of current sentence.<br />
</font>&nbsp;&nbsp;Output(m_pResult[0],sSentenceResult,bFirstIgnore);<font color="#0000ff"> //Output to the imediate result</font></p>
<p>&nbsp; .......</p>
<p>}</p>
<p>３.主要的分词处理是在Processing()方法里面发生的，下面我们对它进行进一步的分析.</p>
<p>bool CResult::Processing(char *sSentence,unsigned int nCount)<br />
{</p>
<p>......</p>
<p><font color="#3366ff">&nbsp;</font><font color="#0000ff">//进行二叉分词</font></p>
<p>m_Seg.BiSegment(sSentence, m_dSmoothingPara,m_dictCore,m_dictBigram,nCount);</p>
<p>......</p>
<p><font color="#3366ff">&nbsp;</font><font color="#0000ff">//在此处进行词性标注</font></p>
<p>m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore);</p>
<p>......</p>
<p>}</p>
<p>４.现在我们先不管词性标注，把注意力集中在二叉分词上，因为这个是分词的两大关键步骤的第一步.</p>
<p>参考文章:</p>
<p>1.&lt;&lt;基于层叠隐马模型的汉语词法分析&gt;&gt;,刘群 张华平等</p>
<p>2.&lt;&lt;基于N-最短路径的中文词语粗分模型&gt;&gt;,张华平 刘群</p>
<br />
<br />
<p id="TBPingURL">来源：<a href="http://blog.csdn.net/sinboy/archive/2006/03/12/622596.aspx">http://blog.csdn.net/sinboy/archive/2006/03/12/622596.aspx</a></p>
<img src ="http://www.blogjava.net/jiangyz/aggbug/171360.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangyz/" target="_blank">刀剑笑</a> 2007-12-28 23:58 <a href="http://www.blogjava.net/jiangyz/archive/2007/12/28/171360.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ICTCLAS 中科院分词系统 代码 注释 中文分词 词性标注（转）</title><link>http://www.blogjava.net/jiangyz/archive/2007/12/28/171335.html</link><dc:creator>刀剑笑</dc:creator><author>刀剑笑</author><pubDate>Fri, 28 Dec 2007 14:10:00 GMT</pubDate><guid>http://www.blogjava.net/jiangyz/archive/2007/12/28/171335.html</guid><wfw:comment>http://www.blogjava.net/jiangyz/comments/171335.html</wfw:comment><comments>http://www.blogjava.net/jiangyz/archive/2007/12/28/171335.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangyz/comments/commentRss/171335.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangyz/services/trackbacks/171335.html</trackback:ping><description><![CDATA[<p>中科院分词系统概述</p>
<p>这几天看完了中科院分词程序的代码，现在来做一个概述，并对一些关键的数据结构作出解释</p>
<p><br />
〇、总体流程</p>
<p>考虑输入的一句话，sSentence="张华平欢迎您"</p>
<p>总体流程:</p>
<p>一、分词 "张/华/平/欢迎/您"</p>
<p>二、posTagging "张/q 华/j 平/j 欢迎/v 您/r"</p>
<p>三、NE识别:人名识别,音译名识别,地名识别 "张/q 华/j 平/j 欢迎/v 您/r" "张华平/nr"</p>
<p>四、重新分词:"张华平/欢迎/您"</p>
<p>五、重新posTagging: "张华平/nr 欢迎/v 您/r"</p>
<p>&nbsp;</p>
<p><br />
技术细节</p>
<p>一、分词</p>
<p>分词程序首先在其头末添加开始符和结束符<br />
sSentence="始##始张华平欢迎您末##末"</p>
<p>然后是分词,基本思想就是分词的得到的词的联合概率最大</p>
<p>假设 "张华平欢迎您" 分为 "w_1/w_2/.../w_k" 则<br />
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')</p>
<p>细节:</p>
<p>首先给原句按字划分,所有汉字一个一段,连续的字母,数字一段,比如"始##始张华平2006欢迎您asdf末##末"被划为"始##始/张/华/平/2006/欢/迎/您/asdf/末##末"</p>
<p>接着找出这个句子中所有可能出现的词,比如"始##始张华平欢迎您末##末",出现的词有"始##始","张","华","平","欢","迎","您","末##末","欢迎"<br />
并查找这些词所有可能的词性和这些词出现的频率。</p>
<p>将这些词保存在一个结构中,具体实现如下:</p>
<p>m_segGraph中有一个(PARRAY_CHAIN)m_pHead，是一个链</p>
<p>(PARRAY_CHAIN)p-&gt;row//记录该词的头位置<br />
(PARRAY_CHAIN)p-&gt;col//记录该词的末位置<br />
(PARRAY_CHAIN)p-&gt;value//记录该词的-log(出现的概率),出现的频率指所有该词的所有词性下出现的概率的总和。<br />
(PARRAY_CHAIN)p-&gt;nPos//记录该词的词性，比如人名标记为'nr'，则对应的nPos='n'*256+'r',如果该词有很多词性,则nPos=0<br />
(PARRAY_CHAIN)p-&gt;sWord//记录该词<br />
(PARRAY_CHAIN)p-&gt;nWordLen//记录该词的长度</p>
<p>举个例子：<br />
"0 始##始 1 张 2 华 3 平 4 欢 5 迎 6 您 7 末##末 8"</p>
<p>对于"张"来说,<br />
row=1<br />
col=2<br />
value=-log[("张"出现的频率+1)/(MAX_FREQUENCE)]<br />
nPos=0//"张"有5种词性<br />
sWord="张"<br />
nWordLen=2</p>
<p>保存的顺序是按col升序row升序的次序排列</p>
<p>m_segGraph.m_pHead&nbsp;"始##始"<br />
&nbsp;&nbsp;&nbsp;"张"<br />
&nbsp;&nbsp;&nbsp;"华"<br />
&nbsp;&nbsp;&nbsp;"平"<br />
&nbsp;&nbsp;&nbsp;"欢"<br />
&nbsp;&nbsp;&nbsp;"欢迎"<br />
&nbsp;&nbsp;&nbsp;"迎"<br />
&nbsp;&nbsp;&nbsp;"您"<br />
&nbsp;&nbsp;&nbsp;"末##末"<br />
&nbsp;&nbsp;&nbsp;<br />
m_segGraph.m_nRow=7<br />
m_segGraph.m_nCol=8</p>
<p><br />
然后是生成一幅给予各种组合情况的图,并按照出现的概率大小保存概率最大的前m_nValueKind个结果。</p>
<p>细节:</p>
<p>初始化,<br />
(CNShortPath)sp.m_apCost=m_segGraph;<br />
(CNShortPath)sp.m_nVertex=m_segGraph.m_nCol+1<br />
(CNShortPath)sp.m_pParent=CQueue[m_segGraph.m_nCol][m_nValueKind]<br />
(CNShortPath)sp.m_pWeight=ELEMENT_TYPE[m_segGraph.m_nCol][m_nValueKind]//m_pWeight[0][0]表示1处的weight</p>
<p>sp.ShortPath()函数中,<br />
for(nCurNode=1;nCurNode&lt;sp.m_nVertex;nCurNode++)<br />
{<br />
&nbsp;CQueue queWork;//零时的CQueue<br />
&nbsp;eWeight=m_apCost-&gt;GetElement(-1,nCurNode,0,&amp;pEdgeList);//取出col=nCurNode的第一个PARRAY_CHAIN的value,比如nCurNode=6,则pEdgeList指向"欢迎",eWeight="pEdgeList-&gt;value<br />
&nbsp;while(pEdgeList&amp;&amp;pEdgeList-&gt;col==nCurNode)//对每一个col=nCurNode的pEdgeList<br />
&nbsp;{<br />
&nbsp;<br />
&nbsp;&nbsp;for(i=0;i&lt;m_nValueKind;i++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;queWork.Push(pEdgeList-&gt;row,0,eWeight+m_pWeight[pEdgeList-&gt;row-1][i]);<br />
&nbsp;&nbsp;&nbsp;//将所有col=nCurNode的pEdgeList按照其weight升序放到queWork中<br />
&nbsp;&nbsp;}<br />
&nbsp;}//比如<br />
&nbsp;/*<br />
&nbsp;&nbsp;"欢迎"&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; m_pWeight[3][0]=0.2&nbsp;&nbsp;&nbsp;&nbsp; eWight=0.2&nbsp;&nbsp;&nbsp;&nbsp;=&gt;queWork.Push(4,0,0.4);<br />
&nbsp;&nbsp;"0 始##始 1 张 2 华 3 平 &nbsp;&nbsp;4 &nbsp;欢 &nbsp;&nbsp;&nbsp;5 &nbsp;&nbsp;迎 6 您 7 末##末 8"<br />
&nbsp;&nbsp;"欢"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_pWeight[4][0]=0.5&nbsp;eWight=0.1&nbsp;&nbsp;=&gt;queWork.Push(5,0,0.6);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_pWeight[4][1]=0.6&nbsp;eWight=0.1&nbsp;&nbsp;=&gt;queWork.Push(5,0,0.7);<br />
&nbsp;<br />
&nbsp;queWork&nbsp;&nbsp;"欢迎"&nbsp;&nbsp;0.4<br />
&nbsp;&nbsp;&nbsp;"迎"&nbsp;&nbsp;0.6<br />
&nbsp;&nbsp;&nbsp;"迎"&nbsp;&nbsp;0.7<br />
&nbsp;<br />
&nbsp;*/<br />
&nbsp;for(i=0;i&lt;m_nValueKind;i++)m_pWeight[nCurNode-1][i]=INFINITE_VALUE;//初始化当前的m_pWeight[nCurNode-1]<br />
&nbsp;while(i&lt;m_nValueKind&amp;&amp;queWork.Pop(&amp;nPreNode,&amp;nIndex,&amp;eWeight)!=-1)//从queWork中顺序取出每个pEdgeList的row,nIndex的取值从0到m_nValueKind-1,eWeight=pEdgeList-&gt;value<br />
&nbsp;{<br />
&nbsp;&nbsp;m_pWeight[nCurNode-1][i]=eWeight;//取前m_nValueKind个结果<br />
&nbsp;&nbsp;m_pParent[nCurNode-1][i].Push(nPreNode,nIndex);//按照pEdgeList-&gt;value的升序,也就是P的降序放入m_pParent<br />
&nbsp;}<br />
}</p>
<p>得到m_pParent之后,按照m_pWeight[m_segGraph.m_nCol-1]的升序,生成path<br />
CNShortPath::GetPaths(unsigned int nNode,unsigned int nIndex,int **nResult,bool bBest)<br />
//nNode=m_segGraph.m_nCol,nIndex从0取到m_nValueKind-1,nResult输出结果,bBest=true只输出最佳结果<br />
比如"始##始张华平欢迎您末##末"的结果为<br />
nResult[0]={0,1,2,3,4,6,7,8,-1}&nbsp;&nbsp;"始##始/张/华/平/欢迎/您/末##末"<br />
nResult[1]={0,1,2,3,4,5,6,7,8,-1}&nbsp;"始##始/张/华/平/欢/迎/您/末##末"<br />
没有第三种结果</p>
<p>取出所有nResult[i]作为分词结果,结果保存在m_graphOptimum中,m_graphOptimum和m_segGraph结构一样,只不过只存nResult[i]中的结果:</p>
<p>如果m_nValueKind=1则<br />
m_graphOptimum.m_pHead&nbsp;"始##始"<br />
&nbsp;&nbsp;&nbsp;"张"<br />
&nbsp;&nbsp;&nbsp;"华"<br />
&nbsp;&nbsp;&nbsp;"平"<br />
&nbsp;&nbsp;&nbsp;"欢迎"<br />
&nbsp;&nbsp;&nbsp;"您"<br />
&nbsp;&nbsp;&nbsp;"末##末"<br />
&nbsp;&nbsp;&nbsp;<br />
m_graphOptimum.m_nRow=7<br />
m_graphOptimum.m_nCol=8</p>
<p><br />
如果m_nValueKind=2则</p>
<p>m_graphOptimum.m_pHead&nbsp;"始##始"<br />
&nbsp;&nbsp;&nbsp;"张"<br />
&nbsp;&nbsp;&nbsp;"华"<br />
&nbsp;&nbsp;&nbsp;"平"<br />
&nbsp;&nbsp;&nbsp;"欢"<br />
&nbsp;&nbsp;&nbsp;"欢迎"<br />
&nbsp;&nbsp;&nbsp;"迎"<br />
&nbsp;&nbsp;&nbsp;"您"<br />
&nbsp;&nbsp;&nbsp;"末##末"<br />
&nbsp;&nbsp;&nbsp;<br />
m_graphOptimum.m_nRow=7<br />
m_graphOptimum.m_nCol=8</p>
<p><br />
见&nbsp;bool CSegment::GenerateWord(int **nSegRoute, int nIndex)这里的nSegRoute=上面的nResult,是输入参数;nIndex表示第nIndex个分词结果</p>
<p><br />
同时,CResult.m_Seg.m_pWordSeg[nIndex][k]中保存了第nIndex个结果的第k个词的信息:</p>
<p>CResult.m_Seg.m_pWordSeg[nIndex][k].sWord//词<br />
CResult.m_Seg.m_pWordSeg[nIndex][k].nHandle//词性<br />
CResult.m_Seg.m_pWordSeg[nIndex][k].dValue//-logP</p>
<p>至此,分词部分结束</p>
<p>二、posTagging</p>
<p><br />
m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore);//对第nIndex个分词结果用标准的字典标注<br />
方便起见,下面假设m_nValueKind=1</p>
<p><br />
m_POSTagger用HMM对分词进行标注，这里输出概率为P(w_i|c_i)，c_i为词性，w_i为词；转移概率为P(c_i|c_{i-1})，初始状态为P(c_0)即P("始##始"的词性)<br />
用维特比算法求出一个c_1/c_2/.../c_k=argmax_{c_1'/c_2'/.../c_k'}P(w_1',w_2',...,w_k')</p>
<p>将句子分成若干段,每段以有唯一pos的w结尾,也就是分词中CResult.m_Seg.m_pWordSeg[0][k].nHandle&gt;0的那些词</p>
<p>比如,举个例子<br />
"0 始##始 1 张&nbsp; 2&nbsp;&nbsp; 华&nbsp;&nbsp; 3&nbsp;&nbsp; 平&nbsp;&nbsp; 4&nbsp;&nbsp; 欢迎&nbsp;&nbsp; 5&nbsp;&nbsp; 您&nbsp;&nbsp; 6 末##末 7"<br />
&nbsp;&nbsp;&nbsp; pos1&nbsp;&nbsp; pos1&nbsp;&nbsp; pos1&nbsp;&nbsp;&nbsp;&nbsp; pos1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos1&nbsp;&nbsp;&nbsp;&nbsp; pos1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos2&nbsp;&nbsp; pos2&nbsp;&nbsp;&nbsp;&nbsp; pos2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos2<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos3&nbsp;&nbsp; pos3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos4<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos5</p>
<p>则该句被划分为<br />
"0 始##始"<br />
"1 张&nbsp; 2&nbsp;&nbsp; 华&nbsp;&nbsp; 3&nbsp;&nbsp; 平 4&nbsp; 欢迎&nbsp;&nbsp; 5&nbsp;&nbsp; 您"<br />
"6 末##末"<br />
对每一段用维特比算法确定一个唯一的postag</p>
<p>细节：</p>
<p>首先P(w,c)的输出概率存储在dict中，比如dictCore,dictUnknow,通过dict.GetFrequency(char *sWord, int nHandle)函数获取 sWord pos为nHandle的函数<br />
概率P(c)存储在context中,比如m_context,通过context.GetFrequency(int nKey, int nSymbol)函数获取 pos为nSymbol的函数,nKey=0<br />
转移概率P(c|c')存储在context中,比如m_context,通过context.GetContextPossibility(int nKey, int nPrev, int nCur)函数获取 c'=nPrev,c=nCur的转移概率,nKey=0</p>
<p>重要的数据结构</p>
<p>m_nTags[i][k]表示第i个w的第k个pos<br />
在GetFrom函数中表示 -log(第i个w的第k个pos的输出概率)<br />
在CSpan::Disamb()函数中<br />
m_dFrequency[i][k]表示 -log(从第0个w到第i个w的第k个pos的联合最大输出概率),比如</p>
<p><br />
&nbsp;w_j&nbsp;&nbsp;&nbsp;w_{j+1}<br />
m_dFrequency[j][0]--&nbsp;m_dFrequency[j+1][0]<br />
m_dFrequency[j][1]&nbsp; --&nbsp;m_dFrequency[j+1][1]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --m_dFrequency[j+1][2]</p>
<p>&nbsp;</p>
<p>则 图中的路径的权为W([j,0]-&gt;[j+1,2])=m_dFrequency[j][0]-log(m_context.GetContextPossibility(0,m_nTags[j][0],m_nTags[j+1][2]))<br />
这样,选择<br />
m_dFrequency[j+1][2]=min{W([j,0]-&gt;[j+1,2]),W([j,1]-&gt;[j+1,2])}</p>
<p><br />
m_nCurLength表示当前段的w个数+1</p>
<p>在m_POSTagger.POSTagging中,以上面的例子为例<br />
while(i&gt;-1&amp;&amp;pWordItems[i].sWord[0]!=0)//将执行段的个数次,比如上例中将执行3次<br />
{<br />
&nbsp;i=GetFrom(pWordItems,nStartPos,dictCore,dictUnknown);//i=GetFrom(pWordItems,0,dictCore,dictUnknown)=1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//i=GetFrom(pWordItems,1,dictCore,dictUnknown)=6<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//i=GetFrom(pWordItems,6,dictCore,dictUnknown)=7<br />
&nbsp;//从nStartPos向前取w,一直取到一个有唯一pos的w为止,该过程中记录每个w的pos,保存在m_nTags中,记录log(w|c)输出概率保存在m_dFrequency中<br />
&nbsp;GetBestPOS();//调用Disamb()函数,用维特比算法找出该段的最佳(联合输出概率最大)的标注,最佳路径保存在m_nBestTag中<br />
&nbsp;通过读取m_nBestTag对pWordItems.nHandle进行赋值<br />
}</p>
<p><br />
三、NE识别:人名识别,音译名识别,地名识别</p>
<p>其基本思路和PosTagging一样，只不过词性c换成了role r，以人名识别为例,首先识别出人名的tag(即pos)，见<br />
"Chinese Named Entity Recognition Using Role Model"<br />
在函数CUnknowWord::Recognition(PWORD_RESULT pWordSegResult, CDynamicArray &amp;graphOptimum,CSegGraph &amp;graphSeg,CDictionary &amp;dictCore)中<br />
每个被切开的段被识别完之后，用m_roleTag.POSTagging(pWordSegResult,dictCore,m_dict);对第一步分词的结果进行一次标记。<br />
首先用dictUnknown.GetHandle(m_sWords[i],&amp;nCount,aPOS,aFreq);获得m_sWords[i]在NE词典中的role,<br />
接着用dictCore.GetHandle(m_sWords[i],&amp;nCount,aPOS,aFreq);获得m_sWords[i]在标准词典中的tag,这里只要m_sWords[i]在标准词典中有tag，那么tag一律标记为0，该tag下的输出概率为P(w|c)=P(sum_{aFreq}|c=0)<br />
接下来用SplitPersonPOS(dictUnknown)函数将其中tag为LH和TR的w拆成两个<br />
比如"张/SS 华/GH 平欢/TR 迎/RC 您/RC"中"平欢"被拆成"平/GT" "欢/12"<br />
接着在PersonRecognize(dictUnknown);函数中,用一些模板进行匹配,"SS/GH/TR"将匹配到"张华平"。匹配得到的片断保存在m_nUnknownWords中，其nHandle被设置为人名，地名，音译名中的一个<br />
对第一步中的graphOptimum，加入m_nUnknownWords的边：<br />
graphOptimum.GetElement(nAtomStart,nAtomEnd,&amp;dValue,&amp;nPOSOriginal);<br />
if(dValue&gt;m_roleTag.m_dWordsPossibility[i])//Set the element with less frequency<br />
&nbsp;graphOptimum.SetElement(nAtomStart,nAtomEnd,m_roleTag.m_dWordsPossibility[i],m_nPOS);</p>
<p>四、重新分词</p>
<p>对上一步的graphOptimum，用第一步中对m_segGraph分词的方法，找出一个联合概率最大的分词结果：<br />
m_Seg.OptimumSegmet(nCount);</p>
<p>五、重新标注</p>
<p>对于四中分好的结果，用标准词典对其进行posTagging:<br />
for(nIndex=0;nIndex&lt;m_Seg.m_nSegmentCount;nIndex++)//m_Seg.m_nSegmentCount是第四步中的分词结果个数<br />
{<br />
&nbsp;m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore);<br />
}</p>
<p><br />
最后，用Sort();对标注结果按照联合输出概率的大小降序排序，并按照用户的需求输出前几个</p>
<br />
来源：http://qxred.yculblog.com/post.1204714.html
<img src ="http://www.blogjava.net/jiangyz/aggbug/171335.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangyz/" target="_blank">刀剑笑</a> 2007-12-28 22:10 <a href="http://www.blogjava.net/jiangyz/archive/2007/12/28/171335.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SharpICTCLAS 1.0 发布! （转）</title><link>http://www.blogjava.net/jiangyz/archive/2007/12/28/171318.html</link><dc:creator>刀剑笑</dc:creator><author>刀剑笑</author><pubDate>Fri, 28 Dec 2007 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/jiangyz/archive/2007/12/28/171318.html</guid><wfw:comment>http://www.blogjava.net/jiangyz/comments/171318.html</wfw:comment><comments>http://www.blogjava.net/jiangyz/archive/2007/12/28/171318.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangyz/comments/commentRss/171318.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangyz/services/trackbacks/171318.html</trackback:ping><description><![CDATA[<p><font color="#ff0000">SharpICTCLAS 1.0 发布 （感谢<a href="http://www.gk-z.com/" target="_blank">工控网</a>发现了一个问题，问题出在字符串比较上，目前已经修正，请重新下载。2007年4月20日）</font></p>
<ul>
    <li><a href="http://www.cnblogs.com/Files/zhenyulu/SharpICTCLAS分词系统_1.0.rar">下载 SharpICTCLAS 1.0</a> </li>
</ul>
<p>　</p>
<h3>一、SharpICTCLAS 1.0 版相对于测试版的改进</h3>
<p>1、修改了原子分词代码，使得对于全角字母有较好的识别</p>
<p>2、修改了部分词性标注部分的代码</p>
<p>因为词性标注部分的代码存在问题（应当是从ICTCLAS就存在的问题），主要表现在如果某个汉字没有词性，则在词性标注时会出现异常。例如：&#8220;这些是永远也没有现成的答桉的&#8221;其中&#8220;答案&#8221;写错了，当对这个有错别字的句子分词时，&#8220;桉&#8221;字是没有词性的，程序在此时将出现错误。</p>
<p>目前的解决办法是对于这些没有词性的词在最终标注时标注为&#8220;字符串&#8221;。</p>
<p>2、修改了地名识别的一些问题</p>
<p>这个问题出现在Span类的PlaceRecognize方法中，nStart与nEnd在某些时候会计算错误。在测试版SharpICTCLAS中，句子&#8220;明定陵是明十三陵中第十座陵墓&#8221;在分词时会因为这个问题导致异常。 </p>
<p>3、修改了基于CCID的字符串比较代码</p>
<p>原有代码没有很好考虑对全角、半角混合字符串的比较问题，现在修正过来了。</p>
<p>4、修改了向词库添加词汇的代码</p>
<p>原有代码存在错误，现在改正了过来。</p>
<h3>二、仍然有待改进的地方</h3>
<p>现在的程序仍然有很多地方有待改进，例如原子分词部分的代码对电子邮件、URL等识别还不是很好，日后可利用正则表达式加以改进；除此之外，对于词性标注以及人名地名识别部分代码 ，我除了修改了部分问题代码外，没有做任何改进和调整，这使得整个代码显得凌乱，有待做一次全面重构。</p>
<h3>三、SharpICTCLAS使用时的一些示例代码</h3>
<p>为了能够更好的使用SharpICTCLAS，现提供一些示例代码，主要完成的工作包括：1）向词库中添加新词汇；2）对文件的预处理，实现繁体向简体的转换、全角字符向半角字符的转换、利用正则表达式过滤多余HTML标记以及断句等。具体可以访问我的文章《<a href="http://www.cnblogs.com/zhenyulu/articles/718375.html">SharpICTCLAS分词系统简介(9)词库扩充</a>》。</p>
<p>目前经过调整后的SharpICTCLAS运行效果还算不错。在对博客园一万五千篇文章进行分词测试过程中，向词库中添加了一千三百多个词汇然后进行分词，效果还不错， 分词异常一共发生了15次，其中有9处是因为存在大量日文字符，另外6处是一句话中单词过多，超出了软件限制（200词）。分词效率也比较令人满意（尽管总体还是比较慢），15000篇文章总用时2.5小时，但这不只是分词的时间，还包括了繁体转简体、利用正则表达式去掉HTML符号，统计词频（这需要进行重复词的判别，我使用了AVL树 ，共统计得到16万词汇）、将分词结果写入SQL Server 2005数据库。如果不考虑这些因素的话，感觉应当和C＋＋程序效率差不多，当然这是没有经过严格测试的结论。</p>
<p>如果大家在使用时发现什么新问题，还请及时告知，我会继续修正这些问题。</p>
<p>　</p>
<hr align="left" width="400" />
<p>　</p>
<ul>
    <li><font color="#800080"><strong>ICTCLAS简介：</strong></font> </li>
</ul>
<p>计算所汉语词法分析系统ICTCLAS（Institute of Computing Technology, Chinese Lexical Analysis System），功能有：中文分词；词性标注；未登录词识别。分词正确率高达97.58%(973专家评测结果)，未登录词识别召回率均高于90%，其中中国人名的识别召回率接近98%;处理速度为31.5Kbytes/s。</p>
<p>著作权： Copyright(c)2002-2005中科院计算所 职务著作权人：张华平</p>
<p>遵循协议：自然语言处理开放资源许可证1.0</p>
<p>Email: <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#97;&#110;&#103;&#104;&#112;&#64;&#115;&#111;&#102;&#116;&#119;&#97;&#114;&#101;&#46;&#105;&#99;&#116;&#46;&#97;&#99;&#46;&#99;&#110;">zhanghp@software.ict.ac.cn</a></p>
<p>Homepage: <a href="http://www.i3s.ac.cn/">http://www.i3s.ac.cn</a></p>
<p>　</p>
<ul>
    <li><strong><font color="#800080">SharpICTCLAS：</font></strong> </li>
</ul>
<p>.net平台下的ICTCLAS，是由河北理工大学经管学院吕震宇根据Free版ICTCLAS改编而成，并对原有代码做了部分重写与调整。</p>
<p>Email: <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#101;&#110;&#121;&#117;&#108;&#117;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;">zhenyulu@163.com</a></p>
<p>Blog: <a href="http://www.cnblogs.com/zhenyulu">http://www.cnblogs.com/zhenyulu</a><br />
<br />
来源：http://www.cnblogs.com/zhenyulu/category/85598.html</p>
  <img src ="http://www.blogjava.net/jiangyz/aggbug/171318.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangyz/" target="_blank">刀剑笑</a> 2007-12-28 20:55 <a href="http://www.blogjava.net/jiangyz/archive/2007/12/28/171318.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>