zoukankan      html  css  js  c++  java
  • 菜鸟进阶: C++实现KNN文本分类算法

    作者:finallyliuyu(转载请注明原作者和出处)

    (代码暂不发布源码下载版,以后会发布)

        KNN文本分类算法又称为(k nearest neighhor)。它是一种基于事例的学习方法,也称懒惰式学习方法。

        它的大概思路是:对于某个待分类的样本点,在训练集中找离它最近的k个样本点,并观察这k个样本点所属类别。看这k个样本点中,那个类别出现的次数多,则将这类别标签赋予该待分类的样本点。

       通过上面的描述,可以看出KNN算法在算法实现上是很简单的,并不十分困难。

    1. 给出代码之前,先给出实验条件。

    1。语料库格式:

    语料库存放在MSSQLSERVER2000的数据库的表单中,表单格式如下:

    TIDJ01S{T~M_[N{]5)`8%PK

    (fig 1)

    2。如何获得该形式的语料库?

    你可以从搜狗lab下载2008年的数据,并且用我的程序对这批数据进行处理,抽取出新闻。处理程序见《菜鸟学习C++练笔之整理搜狗2008版语料库--获取分类语料库》或者去下载我上传到博客园的语料资源见《献给热衷于自然语言处理的业余爱好者的中文新闻分类语料库之二

    3。分割出训练语料库与测试语料库(训练语料库和测试语料库也是MSSQL表单,格式同fig1)。关于MSSQLSERVER的一些表复制的技巧见:《MSSQL语句备份

    1. 下面开始给出C++代码:

    如果一些函数代码没有给出,请您参阅《菜鸟进阶:C++实现Chi-square 特征词选择算法》以及K-means文本聚类系列(已经完成)

    建立VSM模型(考虑到效率问题对训练样本集合与测试样本集采用不同的函数建立VSM模型)

    1。对训练集建立VSM模型。

    *****************以下函数辅助完成聚类功能*********************************************************************8**********************/
    /************************************************************************/
    /* 建立文档向量模型                                                                     */
    /************************************************************************/
    map<int,vector<double> > Preprocess::VSMConstruction(map<string,vector<pair<int,int>>> &mymap)
    {   
    	clock_t start,finish;
    	double totaltime;
    	start=clock();
    	int corpus_N=endIndex-beginIndex+1;
    	map<int,vector<double>> vsmMatrix;
    	vector<string> myKeys=GetFinalKeyWords();
    	vector<pair<int,int> >maxTFandDF=GetfinalKeysMaxTFDF(mymap);
    	for(int i=beginIndex;i<=endIndex;i++)
    	{   
    		vector<pair<int,double> >tempVSM;
    		vector<double>tempVSM2;
    		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+(double)TF/(maxTFandDF[j].first);
    			TF*=log((double)corpus_N/maxTFandDF[j].second);
    			tempVSM.push_back(make_pair(j,TF));
    
    		}
    		if(!tempVSM.empty())
    		{
    			tempVSM=NormalizationVSM(tempVSM);
    			//
    			for(vector<pair<int,double> >::iterator it=tempVSM.begin();it!=tempVSM.end();it++)
    			{
    				tempVSM2.push_back(it->second);
    			}
    			vsmMatrix[i]=tempVSM2;
    
    
    
    		}
    		tempVSM.clear();
    		tempVSM2.clear();
    
    
    
    	}
    	finish=clock();
    	totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
    	cout<<"为训练语料库集合建立VSM模型共用了"<<totaltime<<endl;
    
    
    	return vsmMatrix;
    
    }

    2。对测试集建立VSM模型。

    这里值得一提的是在tf-idf计算特征VSM模型特征词权重的时候,tf:计算的是该词在该篇文章中出现的次数。idf:用的是训练集计算出的idf值。原因在于:在一个分类系统,我们假设代分类的文档是一篇一篇进入分类系统中来的。

    /************************************************************************/
    /* 获得待分类文档集合的VSM模型                                            */
    /************************************************************************/
    map<int,vector<double>> Preprocess::GetManyVSM(int begin,int end,map<string,vector<pair<int,int>>> &mymap)
    {
    	map<int,vector<double> > testingVSMMatrix;
    	
    	vector<string>keywords=GetFinalKeyWords();
    	char * selectbySpecificId=new char [1000];
    	memset(selectbySpecificId,0,1000);
    	sprintf_s(selectbySpecificId,1000,"select ArticleId,CAbstract from Article where ArticleId between %d and %d",begin,end);
    	set<string>stopwords=MakeStopSet();
    	if(!ICTCLAS_Init())
    	{
    		printf("ICTCLAS INIT FAILED!\n");
    		string strerr("there is a error");
    
    	}
    	ICTCLAS_SetPOSmap(ICT_POS_MAP_SECOND);
    	//导入用户词典后
    	printf("\n导入用户词典后:\n");
    	int nCount = ICTCLAS_ImportUserDict("dict.txt");//覆盖以前的用户词典
    	//保存用户词典
    	ICTCLAS_SaveTheUsrDic();
    	printf("导入%d个用户词。\n", nCount);
    	CoInitialize(NULL);
    	_ConnectionPtr pConn(__uuidof(Connection));
    	_RecordsetPtr pRst(__uuidof(Recordset));
    	pConn->ConnectionString="Provider=SQLOLEDB.1;Password=xxxx;Persist Security Info=True; User ID=sa;Initial Catalog=ArticleCollection";
    	pConn->Open("","","",adConnectUnspecified);
    	pRst=pConn->Execute(selectbySpecificId,NULL,adCmdText);
    	while(!pRst->rsEOF)
    	{
    		string rawtext=(_bstr_t)pRst->GetCollect("CAbstract");
    		if(rawtext!="")
    		{
    			string tempid=(_bstr_t)pRst->GetCollect("ArticleId");
    			int articleid=atoi(tempid.c_str());
    			vector<string>wordcollection=goodWordsinPieceArticle(rawtext,stopwords);//表示这篇文章的词
    			vector<pair<int,int> >maxTFandDF=GetfinalKeysMaxTFDF(mymap);
    			int corpus_N=endIndex-beginIndex+1;
    			vector<pair<int,double> >tempVSM;
    			vector<double>vsm;
    			for(vector<string>::size_type j=0;j<keywords.size();j++)
    			{
    				double TF=(double)count_if(wordcollection.begin(),wordcollection.end(),GT_cls(keywords[j]));
    				TF=0.5+(double)TF/(maxTFandDF[j].first);
    				TF*=log((double)corpus_N/maxTFandDF[j].second);
    				tempVSM.push_back(make_pair(j,TF));
    
    			}
    			if(!tempVSM.empty())
    			{
    				tempVSM=NormalizationVSM(tempVSM);
    				for(vector<pair<int,double> >::iterator it=tempVSM.begin();it!=tempVSM.end();it++)
    				{
    					vsm.push_back(it->second);
    				}
    				testingVSMMatrix[articleid]=vsm;
    
    
    
    			}
    
    
    
    		}
    		
    		pRst->MoveNext();
    	}
    	
    	pRst->Close();
    	pConn->Close();
    	pRst.Release();
    	pConn.Release();
    	CoUninitialize();
    	delete []selectbySpecificId;
    	ICTCLAS_Exit();
    	return testingVSMMatrix;
    	
    
    	
    
    
    }

    对VSM序列化和反序列化的操作

    /************************************************************************/
    /*  将VSM模型序列化到本地硬盘                                                                    */
    /************************************************************************/
    void Preprocess::SaveVSM(map<int,vector<double> >&VSMmatrix,char* dest)
    {   clock_t start,finish;
    	double totaltime;
    	start=clock();
    	ofstream ofile(dest,ios::binary);
    	for(map<int,vector<double> >::iterator it=VSMmatrix.begin();it!=VSMmatrix.end();++it)
    	{
    		ofile<<it->first<<endl;
    		vector<double>::iterator subit;
    		ofile<<it->second.size()<<endl;
    		for(subit=(it->second).begin();subit!=(it->second).end();++subit)
    		{
    			ofile<<*subit<<" ";
    		}
    		ofile<<endl;
    	
    
    
    	}
    	ofile.close();
    	finish=clock();
    	totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
    	cout<<"将语料库集合的VSM模型为序列化到硬盘的时间为"<<totaltime<<endl;
    
    
    }
    
    /************************************************************************/
    /* 加载VSM模型到内存                                                                     */
    /************************************************************************/
    void Preprocess::LoadVSM(map<int,vector<double> >&VSMmatrix,char* dest)
    {   
    	clock_t start,finish;
    	double totaltime;
    	start=clock();
    	ifstream  ifile(dest,ios::binary);
    	int articleId;//文章id;
    	int lenVec;//id对应的vsm的长度
    	double val;//暂存数据
    	vector<double>vsm;
    	while(!ifile.eof())
    	{
    		ifile>>articleId;
    		ifile>>lenVec;
    		for(int i=0;i<lenVec;i++)
    		{
    			ifile>>val;
    			vsm.push_back(val);
    		}
    		VSMmatrix[articleId]=vsm;
    		vsm.clear();
    
    	}
    	ifile.close();
    	finish=clock();
    	totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
    	cout<<"加载VSM模型到内存的时间为"<<totaltime<<endl;
    
    
    
    }

    对一篇文章用KNN方法进行分类的函数(这里距离的定义采用余弦相似度):

    /************************************************************************/
    /* 对一篇文章分类获取其类别标签   N为KNN中的N的取值                                      */
    /************************************************************************/
    string Preprocess:: KNNClassificationCell( int N,vector<double>vsm,vector<string>categorization,map<string,vector<pair<int,int>>> &mymap,map<int,vector<double> >&trainingsetVSM){
    
    	clock_t start,finish;
    	double totaltime;
    	start=clock();
    
    
    	string classLabel;
    	//map<int,vector<double> >trainingsetVSM=VSMConstruction(mymap);
    	//vector<double>toBeClassifyDoc=GetSingleVSM(articleId,mymap);
    	vector<pair<int,double> >SimilaritySore;//保存待分类样本与训练样本集的测试得分
    	//计算相似度得分
    	for(map<int,vector<double> >::iterator it=trainingsetVSM.begin();it!=trainingsetVSM.end();it++)
    	{
    		double score=CalCosineofVectors(vsm,it->second);
    		SimilaritySore.push_back(make_pair(it->first,score));
    
    	}
    	//将相似度运算结果从高到底排序
    	stable_sort(SimilaritySore.begin(),SimilaritySore.end(),isLarger2);
    	ostringstream out;
    	string articleIds;
    	out<<"(";
    	int putComma=0;
    	for(vector<pair<int ,double> >::size_type j=0;j<N;j++)
    	{
    		out<<SimilaritySore[j].first;
    		if(putComma<N-1)
    		{
    			out<<",";
    
    		}
    		putComma++;
    
    
    	}
    	out<<")";
    	articleIds=out.str();//获得和待分类文档距离最近的前N个文档的id字符串
    	vector<string> labels=GetClassification(articleIds);
    	for(vector<string>::iterator it=labels.begin();it!=labels.end();it++)
    	{
    		trim(*it," ");
    	}
    	vector<pair<string,int> >vectorAssit;
    	for(int i=0;i<categorization.size();i++)
    	{
    		int num=count_if(labels.begin(),labels.end(),GT_cls(categorization[i]));
    		vectorAssit.push_back(make_pair(categorization[i],num));
    	}
    	stable_sort(vectorAssit.begin(),vectorAssit.end(),isLarger);
    	finish=clock();
    	totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
    	cout<<"对一篇文章进行KNN分类的时间为"<<totaltime<<endl;
    
    
    	return vectorAssit[0].first;
    
    	
    
    
    
    
    }
    根据articleid 读取数据库获取类别的函数
    ************************************************************************/
    /*      获得训练语料库中文章的类别标签                                                                */
    /************************************************************************/
    vector<string> Preprocess::GetClassification(string ArticleIds)
    {   vector<string>labels;
    	char * selectCategorization=new char[5000];
    	memset(selectCategorization,50,5000);
    	sprintf_s(selectCategorization,5000,"select Categorization from Article where ArticleId in%s",ArticleIds.c_str());
    	CoInitialize(NULL);
    	_ConnectionPtr pConn(__uuidof(Connection));
    	_RecordsetPtr pRst(__uuidof(Recordset));
    	pConn->ConnectionString=dbconnection;
    	pConn->Open("","","",adConnectUnspecified);
    	pRst=pConn->Execute(selectCategorization,NULL,adCmdText);
    	delete []selectCategorization;
    	while(!pRst->rsEOF)
    	{
    		string label=(_bstr_t) pRst->GetCollect("Categorization");
    		labels.push_back(label);
    		pRst->MoveNext();
    
    	}
    	pRst->Close();
    	pConn->Close();
    	pRst.Release();
    	pConn.Release();
    	CoUninitialize();
    	return labels;
    	
    
    }

    对训练文档集合用KNN进行分类

    /************************************************************************/
    /* KNN分类器                                                               */
    /************************************************************************/
    vector<pair<int,string> > Preprocess::KNNclassifier(map<string,vector<pair<int,int>>> &mymap,map<int,vector<double> >&trainingsetVSM,map<int,vector<double> >&testingsetVSM,vector<string>catigorization,int N)
    {
    	vector<pair<int,string>>classifyResults;
    	for(map<int,vector<double> >::iterator it=trainingsetVSM.begin();it!=testingsetVSM.end();it++)
    	{
    		string label=KNNClassificationCell(N,it->second,catigorization,mymap,trainingsetVSM);
    		pair<int,string> temp=make_pair(it->first,label);
    		classifyResults.push_back(temp);
    
    	}
    	return classifyResults;
    
    
    }
  • 相关阅读:
    CSU1661: Query Mutiple
    U磁盘检测和大量收集量产工具
    DBMS_RLS包实现数据库表中的行级安全控制
    vim note(4)
    android 时间对话框 TimePickerDialog简介
    手动露天广场和立方体
    将JDBC ResultSet结果集变成List
    PS CS5如何在一张图片里插入另一张图片?
    图文教您轻松学会用PS设计制作名片
    常用颜色的RGB值
  • 原文地址:https://www.cnblogs.com/finallyliuyu/p/1836285.html
Copyright © 2011-2022 走看看