注意:
本代码中没有实现“C++工程调用weka”的功能,如果您要找这类的资料,那么您来错地方了。重申一下这份代码的目的:方便广大自然语言处理爱好者,研究者,不必过分究竟于编程的技术细节,而是能在一开始就将注意力集中在文本分类/聚类这个主题上。
拿我自己做个比方吧,我一直怀疑课本上所讲的各种特征词选择方法是否有效,比如课本上说DF法与IG法,CHI squire法效果差不多,MI法效果最差。我还怀疑课本上所讲的:多重伯努利贝叶斯的分类效果要劣于多项式贝叶斯的分类效果。所以总想实验一下。可是,如果完成这样一项实验,需要花费很大力气,即便是我可以调用weka中的算法,那么繁杂的预处理过程也是十分耗时的。这也是我共享这份代码的初衷。
另外,比如我想结合自己的专业背景学习下Libsvm.可是发现libsvm网站上所给的数据都是UCI等国外大学提供的,和自己的专业也不太相关。
在咱们国内可供开源的学习资料太少了。就说进行文本分类的语料库吧,除了搜狗一家之外,没有免费提供的。一些人把这种很底层的资源,奉若神明,当成宝贝一样供起来。所以我们所看到的好的开源的软件库,算法库都是老外搞的,如lucene。
我想国人把“糟粕”当成宝贝珍藏起来,大概有两个原因:一个是:对自己太不自信了,生怕自己的东西被别人偷师学了去(这也证明 他的水平根本也不咋地,人家一学就会,太低端了),其实拿出来,让别人多指点下岂不是进步更快呢?第二个是:中国的剽窃现象太多了。
这里上两张图吧。
“源码下载网”不仅剽窃了我在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
这时候就没有必要再重新建立词袋子模型)
最后的运行结果:
此系列已经写完,下一篇博文将上传源代码,和66篇三类新闻的语料资源。
末了加一句:虽然我的初衷是建立一个开源的预处理框架,但是由于个人水平有限,
可能整个程序框架会令读者有诸多不满。欢迎大家多提意见。同时也希望我这篇博文能起到
抛砖引玉的作用。
最后再次感谢园友嗷嗷 以及Galactica 在我编写C++程序中给予我的帮助,这是我第一个C++
项目程序,函数命名,变量命名有点乱,希望大家不要学,能够取其精华,去其糟粕。