注意:
本代码中没有实现“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++
项目程序,函数命名,变量命名有点乱,希望大家不要学,能够取其精华,去其糟粕。