`
cloudtech
  • 浏览: 4620734 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

【朴素贝叶斯】实战朴素贝叶斯_代码实现_预测

 
阅读更多

预测的程序就很简单了:1. 计算输入样本的特征向量与模型的特征向量的向量内积;2. 在多个类别中,选择概率最大的类别。不过,贝努利模型和多项式模型的区别也就在此,在1——对那些在输入样本中没有出现的特征类型,那些取值为0的特征类型,如何处理。从前说过,我写的是贝努利模型,所以,这些特征类型的条件概率也计算进去了。

计算向量内积的代码如下:

double NaiveBayes::CalcProbLogByFeaVec (vector<int> & FeaIdVec, vector<FeaProbNode> & FeaVec)
{
	double dProbLog = 0.0;
	vector<int>::iterator pFeaId = FeaIdVec.begin();
	vector<FeaProbNode>::iterator pFea = FeaVec.begin();
	while (pFeaId != FeaIdVec.end() && pFea != FeaVec.end())
	{
		if (*pFeaId == pFea->iFeadId)
		{
			dProbLog += log (pFea->dProb + DBL_MIN);
			pFeaId++;
			pFea++;
		}
		else if (*pFeaId < pFea->iFeadId)	
			pFeaId++; // it's not a selected feature in Bayes model, skip it
		else
		{
// strictly speaking, we should do this in naive bayes:
// there are two value for a feature type: 1 and 0
// It is 1 when the feature occurs in input sample; otherwise, 0.
#if 1
			dProbLog += log (1.0 - pFea->dProb + DBL_MIN);
#endif
			pFea++;
		}
	}

	return dProbLog;
}

关键的那个地方我用宏夹住了,并在代码里写了相对多的注释。想要变换算法的时候,随时禁止这个宏就行了。当然,更标准的做法是,起一个名字,在某个头文件里面定义这个宏的值是0还是1。不过,我就不搞那么“严肃”的了,毕竟这个东西代码量也不大,不至于那么难维护。

接下来,确定类别的代码:

bool NaiveBayes::PredictByInputFeas (vector<int> & FeaIdVec, int & iClassId)
{
	if (FeaIdVec.empty())
		return false;

	int iClassNum = ClassFeaVec.size();
	if (iClassNum <= 0)
		return false;

	vector<double> PredProbLogVec (iClassNum, 0.0);	// the predicting probability for each class
	for (int i=0; i<iClassNum; i++)
	{
		PredProbLogVec.at(i) = CalcProbLogByFeaVec (FeaIdVec, ClassFeaVec.at(i).FeaVec);
	}

	// select the class with max probability
	double dMaxProbLog = -DBL_MAX;
	for (int i=0; i<iClassNum; i++)
	{
		if (dMaxProbLog < PredProbLogVec.at(i))
		{
			dMaxProbLog = PredProbLogVec.at(i);
			iClassId = ClassFeaVec.at(i).iClassId;
		}
	}

	return true;
}

没啥可说的了。就朴素贝叶斯模型本身,还有一些代码,如:load模型,计算错误率等,都很简单,不罗列了。其实还有很大一部分代码,代码量可能更多,就是数据准备。尤其是分词,分词算法很多,不是这里的主题,以后有时间慢慢细说。还有停用词过滤,等等。呵呵。


【分类效果】

最后说一下我实际使用过程中的分类效果吧。前面的软文说过了,我要做的是文本过滤,也就是二值分类问题。正例样本比较多,反例样本比较少。整体上的错误率(误分率)大概9%;正例样本的误分率是7%,反例样本的误分率是15%。同时用了一下svm,结果svm效果远超过朴素贝叶斯。哎,这时候才体会到,知识就是生产力啊。不过朴素贝叶斯也不是没用了,svm只能处理小量的数据,而且并行化训练很困难。朴素贝叶斯在这些方面都是强项。


【后记】

其实还有些工作是可以做的,或者说有些问题是可以继续思考的。以上这些程序,其中一个缺点是就是模型训练算法与特征选择算法耦合的非常紧。这样做有好处,因为模型训练和特征选择都要对一些统计量进行计数,这种紧耦合的设计,能够在只扫描一遍样本集合的情况下完成任务,这对于大样本集合的问题来讲是比较有利的。但是缺点也很明显,就是耦合的太紧,不够灵活,如果想要换一个特征选择算法,就要直接改变Train函数的代码。如何解耦合?其实也简单,就是将训练过程和特征选择过程分开,分别放到两个类当中,再用聚合的方法将他们放在一起工作。特征选择类可以用工厂模式,来动态选择用哪个特征选择算法。选择的特征,可以用文本文件的形式暂时存储,供训练类读取使用。不过相应地,特征选择过程和训练过程也就分开了,也就是说要分别统计各自的统计量,也就是说至少扫描两遍样本集合。各有各自的优缺点,大家视自己的情况而定。


不说了。完。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics