📄 postagging.cpp
字号:
void CSpan::GetFrom(CString &s)
{// 从字串中获取若干个词,s的内容被改变
CObArray a;
BOOL isName = FALSE;
s.TrimLeft();
s.TrimRight();
for (uchar i=1; i<20 && !s.IsEmpty(); i++) {
Words[i]=s.SpanExcluding("/"); // 把斜杠左边的字符串读到Words数组中
int wlen=Words[i].GetLength();
if(wlen<s.GetLength()){
s=s.Mid((Words[i].GetLength())+1);
s.TrimLeft();
s.TrimRight();
if (s.Left(2)=="nr"){
isName = TRUE;
if (s.GetLength()>3) // s长度必须大于3才能执行下面语句
s = s.Mid(3);
else
s = "";
}
}
else {
s="";
break;
}
if (!isName) {// 如果当前词不是姓名
pDict.GetFreq(Words[i],a); // 查找词语Word[i]在词典中的各个标记及出现次数,返回值为词性标记个数
if(a.GetSize()==0)
GuessTag(i);
else {
for(uchar j=0;j<10 && j<a.GetSize();j++) { // 添加当前词各词性标记到数组中
CTagFreq * p=(CTagFreq *) a.GetAt(j);
Tags[i][j]=coMatrix.GetIndexOf(p->Tag); // 将当前词的当前标记转换成数组序号
Freqs[i][j]=p->Freq; // 从词典中读取当前词以当前标记出现的次数
}
Tags[i][j]=0; // 将当前词词性标记数组尾部置0
Freqs[i][j]=0; // 将当前词的词性标记频度数组尾部置0
if(j==1) { // 如果当前词只有一个词性标记,就停止取词
i++;
break;
}
}
}
else {// 如果当前词是姓名
Tags[i][0]=coMatrix.GetIndexOf("nr");
Tags[i][1]=0; // 当前词只有一个词性标记,将数组末尾置0
Freqs[i][0]=1;
i++; // 序号增加1
break;
}
}
CurLength=i; // 词的个数(包括虚设的开头词或从上一个span中继承下来的第0号词
}
void CSpan::GuessTag(char i)
{// 猜测第i个词的词性
for (uchar k=0;k<coMatrix.pOpenTags->GetSize();k++) {
Tags[i][k]=coMatrix.GetIndexOf(coMatrix.pOpenTags->GetAt(k));
Freqs[i][k]=1;
}
Tags[i][k]=0;
}
void CSpan::Reset()
{// 重置CSpan各成员变量的值,为新的标注做准备
Words[0] = Words[CurLength-1]; // 新span的第一个词是上一个span的最后一个词
Tags[0][0] = Tags[CurLength-1][0]; // 新span的Tags是上一个span的最后一个词的Tags,只有唯一词性标记
Tags[0][1] = 0;
// Freqs[0][0] = 0;
// 将 Freqs[0][0] 置为0,或者是将上一个span的最后一个词的Freqs值继承下来,对最终结果是没有影响的(Freqs[0][0]并不参与计算)
Freqs[0][0] = Freqs[CurLength-1][0]; // 新span的第一个词的Freqs是上一个span的最后一个词的Freqs,为了打印跟踪信息的需要
GoodPrev[0][0] = 0;
CurLength=1;
}
void CSpan::Disamb()
{// 排除词性歧义,进行词性标注
uchar i,j,k,m;
double minFee=0, tmp=0, curWordProb=0, curWordFee=0;
fprintf(tracefile,"\n--- 为当前span中的每个词的每个词性标记选择最佳前趋 ---\n");
fprintf(tracefile,"当前词\t词性\t前驱\t前驱费用\t转移费用\t累计费用\t最佳前驱\t当前词费用\t最小累计费用");
for(i=1;i<CurLength;i++) { // 对每一个词,i是词的序号计算
for(j=0;Tags[i][j]>0;j++) { // 对当前词的每一个标记计算,j代表第i个词的第j个词性标记
m=11; // 假定一个词最多只有10个词性标记,因此11号是虚设的标记序号
for(k=0;Tags[i-1][k]>0;k++) { // 对当前词的前一个词的每个标记,计算费用,确定当前词当前标记的最佳前驱标记
tmp=coMatrix.GetCoProb(Tags[i-1][k],Tags[i][j]); // 从前一个词的第k个标记向当前词的第j个标记转移的概率
tmp=-log(tmp);
// 打印跟踪信息
fprintf(tracefile,"\n");
fprintf(tracefile,Words[i]); // 当前词
fprintf(tracefile,"\t");
fprintf(tracefile,coMatrix.GetTagOf(Tags[i][j])); // 当前词性标记
fprintf(tracefile,"\t<--");
fprintf(tracefile,coMatrix.GetTagOf(Tags[i-1][k])); // 前驱词
fprintf(tracefile,"\t");
fprintf(tracefile,"%f",sumFee[i-1][k]); // 前趋累计费用
fprintf(tracefile,"\t");
fprintf(tracefile,"%f",tmp); // 转移费用
tmp=tmp+sumFee[i-1][k]; // 加上前一个词以第k个词性标记出现的累计费用
fprintf(tracefile,"\t");
fprintf(tracefile,"%f",tmp); // 累计费用
// 打印跟踪信息
if(m>10||tmp<minFee) {
m=k; // 当前词的前一个词的第k个标记是最佳前趋标记
minFee=tmp; // 将tmp赋给最小费用
}
}
if(k==0 && Tags[i-1][k]==0)
{// 当前词的前一个标记如果等于0,表示是当前词的前一个词是句子起始词 ##
tmp=coMatrix.GetCoProb(Tags[i-1][k],Tags[i][j]); // 从前一个词的第k个标记向当前词的第j个标记转移的概率
tmp=-log(tmp);
if(m>10||tmp<minFee)
{
m=k; // 当前词的前一个词的第k个标记是最佳前趋标记
minFee=tmp; // 将tmp赋给最小费用
}
fprintf(tracefile,"\n");
fprintf(tracefile,Words[i]); // 当前词
fprintf(tracefile,"\t");
fprintf(tracefile,coMatrix.GetTagOf(Tags[i][j])); // 当前词性标记
fprintf(tracefile,"\t<--");
fprintf(tracefile,coMatrix.GetTagOf(Tags[i-1][k])); // 前驱词
fprintf(tracefile,"\t");
fprintf(tracefile,"%f",sumFee[i-1][0]); // 前趋累计费用
fprintf(tracefile,"\t");
fprintf(tracefile,"%f",tmp); // 转移费用
tmp=tmp+sumFee[i-1][0]; // 加上起始虚设词以第0个词性标记$$出现的累计费用
fprintf(tracefile,"\t");
fprintf(tracefile,"%f",tmp); // 累计费用
}
GoodPrev[i][j]=m; // 当前词的最佳前驱是span中前一个词的第m个标记
curWordProb = coMatrix.GetWordProb(Freqs[i][j],Tags[i][j]); // 当前词取当前标记的概率
curWordFee = -log(curWordProb); // 将当前词取当前标记的概率转化为费用
sumFee[i][j] = minFee + curWordFee; // 当前词取当前标记的累计费用
fprintf(tracefile,"\t");
if(m<11)
fprintf(tracefile,coMatrix.GetTagOf(Tags[i-1][m])); // 最佳前趋词性标记
else
fprintf(tracefile,"\t");
fprintf(tracefile,"\t\t");
fprintf(tracefile,"%f",curWordFee); // 当前词费用
fprintf(tracefile,"\t");
fprintf(tracefile,"%f",sumFee[i][j]); // 最小累计费用
}
}
Tags[i-1][0]=Tags[i-1][j-1]; // 保证Span中最后一个词的最佳前趋标记能够记录下来
// 参见下面WriteTo()函数中的说明 2002-12-13 zwd
}
void CSpan::WriteTo(CStdioFile out)
{// 将词性标记结果输出到结果文件中
CString s="";
for(int i=CurLength-1,j=0;i>0;i--) {
s=Words[i]+'/'+coMatrix.GetTagOf(Tags[i][j])+" "+s;
j=GoodPrev[i][j];
}
out.WriteString(s);
// 这里默认了span中最后一个词一定是单词性标记的词。
// 从后往前输出span中各个词的词性标记,这样,
// 当span的最后一个词也是兼类词时,就会
// 取第0个标记 Tags[i][j](j一开始等于0), 但这时候并没有反映
// 最后一个词真正应该取的标记值
// 例子: 把这篇报道编辑一下
// 最后一个词“下”是兼类词f,q,v,在disamb()函数中可以正确判断
// “下”取q的概率最大,但在词典中,f是“下”的第一个词性标记,
// 这样输出结果总是 f, 而不是q
// 如果把例子改为:把这篇报道编辑一下。
// 这时候“下”后面有一个标点,因而“下”不是span 的最后一个词
// disamb()处理的正确结果可以在输出时反映出来。
// 2002-12-13 zwd
}
CString CSpan::Output()
{// 将词性标记结果输出到数组中
CString s="";
for(int i=CurLength-1,j=0;i>0;i--) {
s=Words[i]+'/'+coMatrix.GetTagOf(Tags[i][j])+" "+s;
j=GoodPrev[i][j];
}
return s;
}
// 词性排歧函数代码结束
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// 训练及标注单个文件的函数部分开始
void TrainFile(CString FileName)
{ // 对单个文件进行词性标注训练的函数
FILE *in;
in=fopen((const char *)FileName,"rt");
if(!in) {
AfxMessageBox("无法打开语料文件"+FileName);
return;
}
CStdioFile inFile(in);
coMatrix.AddCorpus(inFile);
}
void TaggingFile(CString FileName)
{ // 对语料进行词性标注
FILE * in, *out;
in=fopen((const char *)FileName,"rt");
if(!in) {
AfxMessageBox("无法打开语料文件"+FileName);
return;
}
out=fopen((const char *)ChangeExt(FileName,"pos"),"wt");
if(!out) {
AfxMessageBox("无法创建词性标注文件");
fclose(in);
return;
}
tracefile = (FILE *) fopen((const char *)ChangeExt(FileName,"trc"),"w");
if(!tracefile) {
AfxMessageBox("无法创建词性标注文件");
fclose(in);
if(out)
fclose(out);
return;
}
CStdioFile inFile(in), outFile(out);
char line[MAXSENTLENGTH];
while(inFile.ReadString(line,MAXSENTLENGTH)) {
CString s(line); // 默认是从文件中读取一行作为处理单位
s.TrimLeft();
s.TrimRight();
// 打印跟踪信息
fprintf(tracefile,"\n\n=== 词性标注 ===\n");
if (s.Find(Separator)<0) // 如果没有进行分词
s=SegmentSentenceMP(s); // 做分词处理
// 打印跟踪信息
fprintf(tracefile,"\n\n当前待标记词串:");
fprintf(tracefile,s);
fprintf(tracefile,"\n");
int spancount=1;
CString tagresult="";
CSpan span;
while(!s.IsEmpty()) {
span.GetFrom(s);
// 打印跟踪信息
fprintf(tracefile,"\n\n--- 第%d个span --- \n",spancount);
fprintf(tracefile,"词语\t词性\t频次\n");
int i,j;
for ( i=0; i<span.GetSize();i++)
{
for(int j=0;span.Tags[i][j]>0;j++)
{
fprintf(tracefile,span.Words[i]); // 打印当前词语
fprintf(tracefile,"\t");
fprintf(tracefile,coMatrix.GetTagOf(span.Tags[i][j])); // 打印当前词性标记
fprintf(tracefile,"\t%d",(long)span.Freqs[i][j]); // 打印当前词性标记的出现频次
fprintf(tracefile,"\n");
}
if ((i==0) && (j==0))
{// 打印span的第一个词Words[0], span.Tags[0][0]==0
fprintf(tracefile,span.Words[i]); // 打印当前词语
fprintf(tracefile,"\t");
CString tmptag = coMatrix.GetTagOf(span.Tags[i][j]);
fprintf(tracefile,tmptag); // 打印当前词性标记
if (tmptag = "$$") // 如果当前span的第一个词是句首起始词$$
span.Freqs[i][j] = coMatrix.GetTagFreq(0); // 获取$$的频次(在span初始化的时候,span.Freqs[0][0]被置为0,并且不参与排歧计算
fprintf(tracefile,"\t%d",(long)span.Freqs[i][j]); // 打印当前词性标记的出现频次
fprintf(tracefile,"\n");
}
}
fprintf(tracefile,"\n--- 排歧处理开始 ---\n");
// 打印跟踪信息
span.Disamb();
// 打印跟踪信息
fprintf(tracefile,"\n\n--- 排歧处理结果 ---\n");
fprintf(tracefile,"\n词语\t词性\t最小费用\t最佳前驱\n");
for (i=0; i<span.GetSize();i++)
{
for(j=0;span.Tags[i][j]>0;j++)
{
fprintf(tracefile,span.Words[i]);
fprintf(tracefile,"\t");
fprintf(tracefile,coMatrix.GetTagOf(span.Tags[i][j]));
fprintf(tracefile,"\t%f",span.sumFee[i][j]);
fprintf(tracefile,"\t");
uchar m = span.GoodPrev[i][j];
if (m==11)
fprintf(tracefile,coMatrix.GetTagOf(span.Tags[0][0]));
else
{
if (i>0)
fprintf(tracefile,coMatrix.GetTagOf(span.Tags[i-1][m]));
}
fprintf(tracefile,"\n");
}
if ((i==0) && (j==0))
{// 打印span的第一个词Words[0], span.Tags[0][0]==0
fprintf(tracefile,span.Words[i]);
fprintf(tracefile,"\t");
fprintf(tracefile,coMatrix.GetTagOf(span.Tags[i][j]));
fprintf(tracefile,"\t%f",span.sumFee[i][j]);
fprintf(tracefile,"\n");
}
}
CString spantagresult=span.Output();
fprintf(tracefile,"\n当前span的标注结果:");
fprintf(tracefile,spantagresult);
tagresult+=spantagresult;
// 打印跟踪信息
// span.WriteTo(out); // 将当前span的排歧结果输出到文件
span.Reset();
spancount++;
}
outFile.WriteString(tagresult);
outFile.WriteString("\n");
fprintf(tracefile,"\n当前词串的词性标注结果:");
fprintf(tracefile,tagresult);
fprintf(tracefile,"\n");
}
inFile.Close();
outFile.Close();
fclose(tracefile);
}
// 训练及标注单个文件的函数部分结束
////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -