⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 postagging.cpp

📁 一个集分词、词性标注和格式转换的强大的工具包
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#include "stdafx.h"
#include "math.h" // 包含log函数的定义

#include "POSTagging.h"
#include "MyDictionary.h"
#include "MyFileApp.h"

#include "MPWordSeg.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

FILE *tracefile; // 定义文件指针,全局变量,记录分词和词性标注过程
extern CString Separator;    // 词界标记
#define MAXSENTLENGTH 20000   // 句子长度

CMyDictionary pDict; // 定义一个词典类全局变量
CCoMatrix coMatrix; // 定义一个词性转移矩阵类全局变量

////////////////////////////////////////////////
// CCoMatrix类的成员函数

CCoMatrix :: CCoMatrix()
{
	TagFreqs=NULL;
	Matrix=NULL;
}

CCoMatrix :: ~CCoMatrix() 
{
	Clean();
}

BOOL CCoMatrix :: Ready()
{
	return CorpusSize>0;
}

double CCoMatrix :: GetCoProb(uchar tag1, uchar tag2)
{

	// tag1 == 0 时指的是虚设的句子起始词标记 $$
	// 理论上,TagFreqs[0]不应该等于0,句子起始位置在训练语料中是可以记数的。
	// 但程序在训练语料参数时有可能实际上没有记录TagFreqs[0]的值
	// TagFreqs[i]=0 时指的是某一个词性标记(编号为i)在训练语料中一次也没有出现。

	if(TagFreqs[tag1]==0) 
		return 0.00001;
	
	uchar n=pTags->GetSize();

	return 0.3 * TagFreqs[tag2]/(double) CorpusSize + 0.7*Matrix[tag1*n+tag2]/(double)TagFreqs[tag1];
}

double CCoMatrix::GetWordProb(double wtFreq, uchar tag)
{
	if(TagFreqs[tag]>0)
		return (double)(wtFreq+1)/TagFreqs[tag];
	else
		return 0.00001;
}

void CCoMatrix :: Create(CStdioFile & tagFile)
{
	if(Ready())
		Clean();
	
	CString line;
	pTags=new CStringArray;
	pTags->Add("$$");
	pOpenTags=new CStringArray;
	
	while(pTags->GetSize()<256 && tagFile.ReadString(line)) 
	{
		int i=line.Find(';');  // ; 后是注释内容
		if(i>=0)
			line=line.Left(i);
		else
			continue;
		line.TrimLeft();
		line.TrimRight();
		if(line.IsEmpty())
			continue;
		if(line.Left(1)=="#" && pOpenTags->GetSize()<10) 
		{
			line=line.Mid(1);
			pOpenTags->Add(line);  // 开放词性标记被加入到pOpenTags中
		}
		pTags->Add(line);  // 开放词性标记也被加入到pTags中
	}

	tagFile.Close();
	
	int n=pTags->GetSize();
	int m=pOpenTags->GetSize();

	if (n==1)
	{// 未获取任何标记,只有一个虚设的标记
		AfxMessageBox("打开词性标记文件错误");
		possetModified=FALSE; // 尚未打开词性标记
		CorpusSize=0; // 尚未训练语料,即模型参数值均为0
		return;
	}

	TagFreqs=new int[n]; // 建立数组,存放词性标记的频度
	Matrix=new int[n*n]; // 建立数组,存放词性标记的转移频度
	
	for(int i=0;i<n;i++) { // 数组初始化,均赋值为0
		TagFreqs[i]=0;
		for(int j=0;j<n;j++)
			Matrix[i*n+j]=0;
	}

	possetModified=TRUE; // 已创建词性标记集
	CorpusSize=0; // 尚未训练语料,即模型参数值均为0

	line.Format("共有%d个词性标记\n其中开放词性标记%d个\n",n-1,m); // 不算虚设的标记n-1
	
	CString tempTag = "";
	for(int k=0;k<pTags->GetSize();k++)
	{
		if (tempTag == "")	
			tempTag =  pTags->GetAt(k);
		else	
			tempTag = tempTag + " | " + pTags->GetAt(k);
	}

	if(pOpenTags->GetSize()==0)
		line+="\n 没有定义开放类,将无法猜测新词的词性";

	if (tempTag != "")
		line+= tempTag;
	
	AfxMessageBox(line);
}

void CCoMatrix::Clean()
{ // 清除此次词性标注操作的内容,并将词性标记集写盘保存
	if(Ready()) {
		CFile tf;
		char buf[512];

		if(tf.Open((const char *) FileName, CFile::modeWrite|CFile::modeCreate)) {//原书没有modeCreate,使得无法创建保存文件
			CArchive ar(&tf, CArchive::store, 512, buf);
			Serialize(ar);
		}
	}

	if(pTags)
		delete pTags;
	if(pOpenTags)
		delete pOpenTags;
	if(TagFreqs)
		delete [] TagFreqs;
	if(Matrix)
		delete [] Matrix;

	possetModified = FALSE;
}

void CCoMatrix :: AddCorpus(CStdioFile &trainFile)
{ // 增加训练语料文件,对当前文件进行训练,统计词性标记频次,词性标记的二元转移频次
	char line[MAXSENTLENGTH];
	int n=pTags->GetSize();
	while(trainFile.ReadString(line,MAXSENTLENGTH)) {
		CString s(line);
		s.TrimLeft();
		s.TrimRight();
		int lastID=0; // lastID表示上一个词性标记,lastID等于0表示这是句子起始词虚设的标记$$。
		while(!s.IsEmpty()) 
		{// 对当前行的每个词进行处理
			CString curword,curtag; // curword:当前词,curtag:当前词的词性标记
			curword=s.SpanExcluding(" ");// 函数的参数是一个空格,假定分词语料是以空格作为分界符
			curword.TrimLeft();
			curword.TrimRight();
			int wordLength = curword.GetLength();
			s=s.Mid(wordLength);
			s.TrimLeft();
			int i=curword.ReverseFind('/'); // 从后往前取/ ,可处理  1/3/m 这样的情形
			if(i<0) { // 这种情况是训练语料中存在错误
				lastID=0;
				continue;
			}
			CorpusSize++;


			if(i<wordLength)
				curtag=curword.Mid(i+1);
			else
				AfxMessageBox(curword);  // 如果 i大于等于w的长度,报错

			if (curtag == "n]nt") // 为处理ICL人民日报标注语料中 [ ... /n]nt这类标记情况
				curtag="n";
			
			if(i<wordLength)
				curword=curword.Left(i);
			else
				AfxMessageBox(curword);

			pDict.Insert(curword,curtag); // 将当前读入的词语插入到词库中

			if (curtag=="/w")
				AfxMessageBox(curword);

			int curID=GetIndexOf(curtag);
			
			if(curID>0) 
			{// 按照目前的假设,curtag是当前词性标记集中的一个元素。除非训练语料中的标记超出标记集,否则curID 肯定大于0
			// 理论上,curID应该可以等于0,表示当前词是句子起始位置,此时curID代表了虚设的标记 $$
			// 但实际上 curID 不会等于0,因为目前的训练语料中并没有在句子开始位置人为地加上 ##/$$ 这样标记
			// 变通的做法:在下面给lastID赋值的时候,通过判别句末标点的方式,间接地来获取 $$ 标记的频次
				TagFreqs[curID]++;
				if(lastID>=0)
					Matrix[lastID*n+curID]++;
				if(lastID==0) 
					TagFreqs[lastID]++;// 上一个标记是虚设的句子起始标记,TagFreqs[0]需要加1次记数。
			}

			if (curtag == "w" && (curword == "。" || curword == "?" || curword == "!")) 
			{// 认为碰到了句子结束标志,因此也意味着是下一个句子的开始标志
				lastID = 0; // 这样假设有一定的问题,因为!?都可不一定是结句符号,此外,有的…是起结句作用的,没有包含进来
			}
			else
				lastID=curID;
		}
	}
	trainFile.Close();
}

IMPLEMENT_SERIAL(CCoMatrix, CObject, 0)

void CCoMatrix::Serialize(CArchive &ar)
{ // 数据序列化函数
	if(ar.IsStoring() && !possetModified && !Ready()) // 如果存盘时标记集和语料库都没有修改过,直接返回
		return;

	if(!ar.IsStoring()) { // 如果不是存盘,就创建pTags和pOpenTags在内存中存放词性标记信息
		pTags=new CStringArray;
		pOpenTags=new CStringArray;
	}

	pTags->Serialize(ar); // 一般标记集序列化
	pOpenTags->Serialize(ar); // 开放标记集序列化
	
	int n=pTags->GetSize(); // 取标记计数组长度
	
	if(!ar.IsStoring()) { // 如果不是存盘,就申请内存来存放标记频度以及标记转移矩阵的数据
		TagFreqs=new int[n];
		Matrix=new int[n*n];
	}
	
	if(ar.IsStoring()) { // 如果是存盘,将内存数组中的频度信息写入到文件中
		ar << CorpusSize;
		for(int i=0;i<pTags->GetSize();i++) {
			ar << TagFreqs[i];
			for(int j=0;j<pTags->GetSize();j++)
				ar << Matrix[i*n+j];
		}
	}
	else { // 如果是读盘,就将文件中的频度信息读入到内存数组中
		ar >> CorpusSize;
		for(int i=0;i<pTags->GetSize();i++) {
			ar >> TagFreqs[i];
			for (int j=0; j<pTags->GetSize();j++)
				ar >> Matrix[i*n+j];
		}
	}
}

int CCoMatrix::GetIndexOf(CString tag)
{// 将词性标记转换成序号
	
	if (tag=="")
		return -1;

	for(int i=0;i<pTags->GetSize();i++) {
		if(tag == (pTags->GetAt(i)))
			break;
	}

	if(i>=pTags->GetSize()) {
		CString msg;
		msg="当前词的词性标记<"+tag+">不在当前词性标记集范围内";
		AfxMessageBox(msg); // 调试信息
		i=i-1; // 用当前数组中最后一个词性标记序号作为当前词词性标记的序号
	}
	return i;
}

CString CCoMatrix::GetTagOf(uchar i)
{ // 将序号转换成词性标记
	CString tag;
	if(i<pTags->GetSize())
		tag=pTags->GetAt(i);
	else 
	{
		tag = i;
		tag = "当前词的词性标记序号: " + tag;
		tag = tag + " 超出范围";
		AfxMessageBox(tag);
		tag="";
	//	AfxMessageBox("当前词的词性标记序号%d超出范围!!!",i); // 调试的时候用
	}
	return tag;

}

int CCoMatrix::GetTagFreq(uchar i)
{// 求某种标记的频度
	return TagFreqs[i];
}

void CCoMatrix::WriteTo(CStdioFile out)
{// 将词性标记转移矩阵输出到文本文件
	CString s, curTag1, curTag2,tmp;

	s.Format("%d",CorpusSize);
	out.WriteString("训练语料总词数:"+s+"\n");
	
	int n=pTags->GetSize(); // 取标记计数组长度

	for(int i=0;i<pTags->GetSize();i++) {
		curTag1=GetTagOf(i);
		for(int j=0;j<pTags->GetSize();j++) {
			curTag2=GetTagOf(j);
			s=curTag1+"->"+curTag2+": ";
			tmp.Format("%d",Matrix[i*n+j]);
			s=s+tmp+"\n";
			out.WriteString(s);
		}
	}	
}

// 词性转移矩阵类函数代码结束
////////////////////////////////////////////////////////

////////////////////////////////////////////////////////
// 词性排歧函数部分开始

CSpan::CSpan()
{
	Words[0]="##";  // 第0个词置空
	Tags[0][0]=0; // 第0个标记是虚设的,即句子开始位置的虚拟词性标记:$$
	Tags[0][1]=0; // 0还有一个含义是标示结束
	Freqs[0][0]=0.0;
	sumFee[0][0]=0.0;
	GoodPrev[0][0]=0;
	CurLength=1;
}

CSpan::~CSpan()
{
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -