作者:finallyliuyu 轉載使用等請註明出處
在上一篇博文《Kmeans聚類之特徵詞選擇DF》中我們已經給出了特徵詞選擇的代碼,這裡我們將給出建立文檔向量模型的代碼,以及將文檔向量模型寫成Weka資料格式的代碼。關於Weka資料格式等相關內容,請見:教程。
首先我們給出寫Arff標頭檔的代碼
void Preprocess::WriteHeadArff(){ofstream ofile(arffFileAddress,ios::binary);ofile<<"@relation aticle"<<endl;ofile<<"\n";vector<string> myKeys=GetFinalKeyWords();for(vector<string>::iterator it=myKeys.begin();it!=myKeys.end();it++){//string temp="@attribute "+"'"+(*it)+"'"+" real";string temp="";temp+="@attribute ";temp+="'";temp+=*(it);temp+="'";temp+=" real";/*strcpy(temp,"@attribute ");strcpy(temp,"'");strcpy(temp,*(it));strcpy(temp,"'");strcpy(temp," real");*/ofile<<temp<<endl;}ofile<<"\n"<<endl;ofile<<"@data"<<endl;ofile.close();}
下面重點介紹採用TF-IDF權重建立文檔向量模型:
在給出代碼之前先簡要介紹下什麼是TF,DF
對於一個特定的Term t
TF,指的是它在嗎某一篇文章中出現的次數;
DF,指的是整個文檔集合中出現該詞的文章篇數;
文檔向量模型(Vector Space Model):向量。向量的屬性為用《Kmeans聚類之特徵詞選擇DF》中的特徵詞選擇方法選定的特徵詞。
整個文檔集合的VSM模型實際上是以矩陣的格式儲存的。矩陣的每一行,代表一篇文章,是一個文檔向量。
TF-IDF模型有很多權重計算模式:(注意:以下來自於計算所王斌老師的課件《現代資訊檢索》)在這裡順便給大家介紹一本十分不錯的書《資訊檢索導論》 (Introduction to Information Retrieval)原版第一作者為斯坦福大學電腦語言學副教授Christopher D. Manning。該書由王斌老師翻譯成中文,現在已經出版。
TF權重計算模式:
IDF權重計算模式:
歸一化方式:
考慮到一篇文章可能完全不含有我們用DF選擇法選擇的特徵詞。
那麼這篇文章的VSM就是{0,0,0,..0}
為了避免產生這種類型的稀疏資料,我採用的TF-IDF計算模式為
a-l-c。
大家對應上面三個表找一下,就找到相應的計算公式了。
下面開始建立文檔向量模型:
獲得每個特徵詞對應的maxTF和DF:
ector<pair<int,int> >Preprocess::GetfinalKeysMaxTFDF(map<string,vector<pair<int,int>>> &mymap){vector<pair<int,int> >maxTFandDF;vector<string>myKeys=GetFinalKeyWords();for(vector<string>::iterator it=myKeys.begin();it!=myKeys.end();it++){ int DF=mymap[*it].size();int maxTF=0;for(vector<pair<int,int> >::iterator subit=mymap[*it].begin();subit!=mymap[*it].end();subit++){if(subit->second>maxTF){maxTF=subit->second;}}maxTFandDF.push_back(make_pair(maxTF,DF));//find_if(mymap[*it].begin(),mymap[*it].end(),}return maxTFandDF;}
************************************************************************//* 文檔向量模型歸一化 *//************************************************************************/vector<pair<int,double> >Preprocess::NormalizationVSM(vector<pair<int,double> > tempVSM){double sum=0;for(vector<pair<int,double> >::iterator vsmit=tempVSM.begin();vsmit!=tempVSM.end();++vsmit){sum+=pow(vsmit->second,2);}for(vector<pair<int,double> >::iterator vsmit=tempVSM.begin();vsmit!=tempVSM.end();++vsmit){vsmit->second/=sqrt(sum);}return tempVSM;}
有了上面的輔助函數,那麼我們可以一邊對文檔集合建立文檔向量模型,一邊寫arff檔案的data部分了。
首先還要給出兩個輔助函數,分別完成浮點數和整數字串化的功能
/************************************************************************//* 將整數轉化成字串 *//************************************************************************/string Preprocess::do_fraction(int val){ostringstream out;out<<val;string str= out.str(); //從流中取出字串str.swap(string(str.c_str()));//刪除nul之後的多餘字元return str;}
/************************************************************************//* 將浮點數轉化成指定精度的字串 *//************************************************************************/string Preprocess::do_fraction(double val,int decplaces){//int prec=numeric_limits<double>::digits10;char DECIMAL_POINT='.'; ostringstream out;//out.precision(prec);out<<val;string str=out.str();size_t n=str.find(DECIMAL_POINT);if((n!=string::npos)&&n+decplaces<str.size()){str[n+decplaces]='\0';}str.swap(string(str.c_str()));return str;}
將一篇文檔的VSM字串化的函數:
************************************************************************//* 單個文檔向量模型字串化 *//************************************************************************/string Preprocess::FormatVSMtoString(vector<pair<int,double> > tempVSM){string ret="{";int commaindication=0;for(vector<pair<int,double> >::iterator vsmit=tempVSM.begin();vsmit!=tempVSM.end();++vsmit){ ret+=do_fraction(vsmit->first)+" "+do_fraction(vsmit->second,8);if(commaindication<tempVSM.size()-1){ret+=",";}commaindication++;}ret+="}";return ret;}
下面的函數調用上面的FormatVSMtoString 填寫arff檔案的data欄位
/************************************************************************//* 將實驗資料寫成arff @data格式 *//************************************************************************/void Preprocess::VSMFormation(map<string,vector<pair<int,int>>> &mymap){ int corpus_N=endIndex-beginIndex+1;ofstream ofile1(articleIdsAddress,ios::binary);//儲存文章編號的檔案ofstream ofile2(arffFileAddress,ios::binary|ios::app);vector<string> myKeys=GetFinalKeyWords();vector<pair<int,int> >maxTFandDF=GetfinalKeysMaxTFDF(mymap);for(int i=beginIndex;i<=endIndex;i++){ vector<pair<int,double> >tempVSM;for(vector<string>::size_type j=0;j<myKeys.size();j++){//vector<pair<int,int> >::iterator findit=find_if(mymap[myKeys[j]].begin(),mymap[myKeys[j]].end(),PredTFclass(i));double TF=(double)count_if(mymap[myKeys[j]].begin(),mymap[myKeys[j]].end(),PredTFclass(i));TF=0.5+0.5*(double)TF/(maxTFandDF[j].first);TF*=log((double)corpus_N/maxTFandDF[j].second);if(TF!=0){tempVSM.push_back(make_pair(j,TF));}}if(!tempVSM.empty()){tempVSM=NormalizationVSM(tempVSM);string vsmStr=FormatVSMtoString(tempVSM);ofile1<<i<<endl;ofile2<<vsmStr<<endl;}tempVSM.clear();}ofile1.close();ofile2.close();}
至此文檔向量模型建立模組的代碼已經介紹完畢。
未完,待續,下次我們將介紹如何從weka獲得計算出的聚類中心,完成文本聚類。