注意:

本代码中没有实现“C++工程调用weka”的功能,如果您要找这类的资料,那么您来错地方了。重申一下这份代码的目的:方便广大自然语言处理爱好者,研究者,不必过分究竟于编程的技术细节,而是能在一开始就将注意力集中在文本分类/聚类这个主题上。

拿我自己做个比方吧,我一直怀疑课本上所讲的各种特征词选择方法是否有效,比如课本上说DF法与IG法,CHI squire法效果差不多,MI法效果最差。我还怀疑课本上所讲的:多重伯努利贝叶斯的分类效果要劣于多项式贝叶斯的分类效果。所以总想实验一下。可是,如果完成这样一项实验,需要花费很大力气,即便是我可以调用weka中的算法,那么繁杂的预处理过程也是十分耗时的。这也是我共享这份代码的初衷。

另外,比如我想结合自己的专业背景学习下Libsvm.可是发现libsvm网站上所给的数据都是UCI等国外大学提供的,和自己的专业也不太相关。

在咱们国内可供开源的学习资料太少了。就说进行文本分类的语料库吧,除了搜狗一家之外,没有免费提供的。一些人把这种很底层的资源,奉若神明,当成宝贝一样供起来。所以我们所看到的好的开源的软件库,算法库都是老外搞的,如lucene。

我想国人把“糟粕”当成宝贝珍藏起来,大概有两个原因:一个是:对自己太不自信了,生怕自己的东西被别人偷师学了去(这也证明 他的水平根本也不咋地,人家一学就会,太低端了),其实拿出来,让别人多指点下岂不是进步更快呢?第二个是:中国的剽窃现象太多了。

这里上两张图吧。

QQ截图未命名

QQ截图未命名2

“源码下载网”不仅剽窃了我在CSDN上上传的资源,而且还在其中加入了一些成人用品广告,如果在网页挂马,植入病毒就更加大逆不道了。。。。

好像有点跑题了。

接着说Preprocess类。

首先为该类的私有数据成员赋值,这些变量包括文件的储存地址,数据库的链接字符串等等。

private:
        char *bagofwordsAddress;//存放词袋子模型的位置
        char * featurewordsAddress;//存放特征词文件的位置;
        char *arffFileAddress;//存放ARFF文件的位置
        char *infoFromWekaAddress;//存放调用weka后的实验结果
        char *articleIdsAddress;//存放被聚类的文章的ID号
        char *dbconnection;//数据库的链接字符串
        char *dbselect;//数据库select语句
        char *dbfield;//数据库字段
        int beginIndex;//开始聚类的文章id
        int endIndex;//结束聚类的文章id

该类提供两个函数。第一个函数完成:建立词袋子模型,特征词选择,建立文档向量模型,并将文档向量模型格式化成arff文件格式。第二个函数完成:从weka聚类信息输出文件获取聚类中心,完成文本聚类,并且输出聚类结果。这两个函数代码见如下:

第一个函数:

void Preprocess::WriteTotalArff(char *dbfield,int DFthreshold,bool isbagOfWordsExist,FUNCSEG seg)
{
	
	
	map<string,vector<pair<int,int>>> mymap;
	if(!isbagOfWordsExist)
	{
		ConstructMap(mymap,dbfield,seg);
		save(mymap);
		cout<<"词袋子信息已经保存到硬盘"<<endl;
	}
	else
	{
		load(mymap);
	}
	DFcharicteristicWordSelection(mymap,DFthreshold);
	WriteHeadArff();
	VSMFormation(mymap);
	cout<<"arff文件已经形成"<<endl;
	
	
	string temp(infoFromWekaAddress);

	cout<<"请您将使用weka聚类,并保存为"<<temp<<endl;
}
 
第二个函数
void Preprocess::RetreiveArticleInfoFromDataBase()
{
	map<string,vector<pair<int,int>>> mymap;
	vector<pair<int,string>>resultInfo;
	map<string,vector<double> >clusters;
	map<int,vector<double> >vsmMatrix;
	map<string,vector<int>> articlesInfo;
	ofstream ofile("F:\\cluster\\ArticlesInPerCluster.txt");
	//boost::regex_replace(strresult)
	//ConstructMap(mymap,1,500);
	//save(mymap);
	load(mymap);
	vsmMatrix=VSMConstruction(mymap);
	clusters=GetClusters();
	resultInfo=GenerateClusterInfo(vsmMatrix,clusters);
	articlesInfo=FetchArticlesOFClusters(clusters,resultInfo);

	/*for(map<string,vector<int>>::iterator it=articlesInfo.begin();it!=articlesInfo.end();it++)
	{
		ofile<<it->first<<endl;
		int count=0;
		ofile<<"(";
		for(int i=0;i<it->second.size();i++)
		{
			ofile<<(it->second)[i];

			if(count<it->second.size()-1)
			{
				ofile<<",";
			}
			count++;
		}
		ofile<<")";
		ofile<<endl;


	}*/
	for(map<string,vector<int>>::iterator it=articlesInfo.begin();it!=articlesInfo.end();it++)
	{
		ostringstream out;
		string selectassist;
		char *selectsql=new char[5000];
		int count=0;
		CoInitialize(NULL);
		_ConnectionPtr pConn(__uuidof(Connection));
		_RecordsetPtr pRst(__uuidof(Recordset));
		pConn->ConnectionString=dbconnection;
		pConn->Open("","","",adConnectUnspecified);
		cout <<it->first<<endl;
		ofile<<it->first<<endl;
		out<<"(";
		count=0;
		for(int i=0;i<it->second.size();i++)
		{
			out<<(it->second)[i];
			if(count<it->second.size()-1)
			{
				out<<",";
			}
			count++;
			
		
		}
		out<<")";
		selectassist=out.str();
		sprintf_s(selectsql,5000,"%s %s","Select ArticleTitle,class from News Where ArticleId in ",selectassist.c_str());

		pRst=pConn->Execute(selectsql,NULL,adCmdText);
		while(!pRst->rsEOF)
		{	
		//string keywordstr=(_bstr_t)pRst->GetCollect("CKeyWord");
			string title=(_bstr_t)pRst->GetCollect("ArticleTitle");
			//string rawtext=(_bstr_t)pRst->GetCollect("ArticleText");
			string categorization=(_bstr_t)pRst->GetCollect("class");
			cout<<"文章标题:"<<title<<"文章所属类别: "<<categorization<<endl;
			ofile<<"文章标题:"<<title<<"文章所属类别: "<<categorization<<endl;


			
		


			pRst->MoveNext();
			
		}
		pRst->Close();
		pConn->Close();
		pRst.Release();
		pConn.Release();
		CoUninitialize();
	
	}
	
	


ofile.close();	
	
	
}

在main函数中各种字符串:

 Preprocess::FUNCSEG seg=&Preprocess::goodWordsinPieceArticle;
	int beginIndex=1;
	int endIndex=66;
	const char *bagofwordsAddress="F:\\cluster\\mydict.dat";//存放词袋子模型的位置
	const char * featurewordsAddress="F:\\cluster\\keywordsinfo.dat";//存放特征词文件的位置;
	const char *arffFileAddress="F:\\cluster\\tobeClustered.arff";//存放ARFF文件的位置
	const char *infoFromWekaAddress="F:\\cluster\\InfoFromWeka.dat";//存放调用weka后的实验结果
	const char * articleidAddress="F:\\cluster\\clusteredArticleId.dat";//存放已经已经被聚类的文章ID
	const char *conn="Provider=SQLOLEDB.1;Password=xxxx;Persist Security Info=True; User ID=sa;Initial Catalog=MyNews";
    char *firstpart="select ArticleId,ArticleTitle,ArticleText from News where ArticleId between";
	char *lastpart="order by ArticleId";
	char selectsql[1000]={'\0'};
	char *dbfield="ArticleText";
	sprintf_s(selectsql,1000,"%s %d and %d %s",firstpart,beginIndex,endIndex,lastpart);
	int string_size=600;

	Preprocess  p(string_size,bagofwordsAddress,featurewordsAddress,arffFileAddress,infoFromWekaAddress,articleidAddress,conn,selectsql,beginIndex,endIndex);
 
 
调用第一个函数:(注意:由于tobeClustered.arff文件为ios::app格式打开,所以每次开始新的实验之前要TruncateArff()清空下)
p.TruncateArff();
	
p.WriteTotalArff(dbfield,10,false,seg);
(注意:WrtieTotalArff函数中的false/true表示是否重新建立词袋子模型,如果是false 则表示不再建立词袋子模型了,比如我们想在原来的数据上重新做实验只想改一下DF阈值,从10改成20
这时候就没有必要再重新建立词袋子模型)
最后的运行结果:
QQ截图未命名2 
此系列已经写完,下一篇博文将上传源代码,和66篇三类新闻的语料资源。
末了加一句:虽然我的初衷是建立一个开源的预处理框架,但是由于个人水平有限,
可能整个程序框架会令读者有诸多不满。欢迎大家多提意见。同时也希望我这篇博文能起到
抛砖引玉的作用。
最后再次感谢园友嗷嗷 以及Galactica 在我编写C++程序中给予我的帮助,这是我第一个C++
项目程序,函数命名,变量命名有点乱,希望大家不要学,能够取其精华,去其糟粕。
posted on 2010-09-03 21:29  finallyly  阅读(3133)  评论(10编辑  收藏  举报