【朴素贝叶斯】实战朴素贝叶斯_代码实现_特征选择2

来源:互联网 发布:playclub 第三方软件 编辑:程序博客网 时间:2024/05/22 15:23

【选择特征】

其实对于卡方选择来说,上一篇软文基本上已经几乎写完了——毕竟卡方值都计算出来了,接下来,选择就行了呗。选择的方法?上文不是说了么,卡方的值表示特征与类别之间的关联,选择卡方值大的特征类型不就行了么。不过,还有最后一个问题,问题在于我们有多个类别。当我们要选择m个特征的时候,每个类别选择几个?哦,如果有k各类别,那么平均分配一下,每个类别选择m/k个,各个类别加起来就是m个了。哦,那么进一步考虑,这k各类别选择的特征,之间是否有相同的?说不准哈,但很有可能真的有相同的特征。例如:词表中有“苹果”这个词,既可以代表水果类的文本,也可以代表电子类的文本。那么很有可能,我们最终选择的特征个数少于m个。那么最终会选出多少个特征呢?说不准,肯定小于等于m。这可不好,算法不可控。那么,在实际实现中,我在选择特征的时候,判断他是否在其他类别中被选择了,如果被选择了,就跳过它,继续选择下一个特征,这样就能严格保证有m个特征被选出了。

void NaiveBayes::FeaSelByChiSquare(double ** ppChiMatrix, double ** ppPProbMatrix, int iClassNum, int iFeaTypeNum, int iSelFeaNum, int * pFeaSelected){// the number of feature to be selected for each class (要选择iSelFeaNum个特征,有iClassNum个类别,平均每个类别就要选择这么多特征类别)int iSelFeaNumAClass = iSelFeaNum / iClassNum;// for sort and get the top n feature type (临时的节点类,用于按照卡方值进行排序,因为是临时的,所以就写在函数里面了。不过这样写有个缺点,在调试的时候无法看到节点的值;把这个节点声明到函数外就可以了。没搞清楚为什么,可能是函数内部声明算作是临时变量,编译器不知道。而外部声明的,不一定在哪里用,所以编译器知道,所以debug的时候就......)class FeaChiNode{public:int iFeaId;double dChiVal;FeaChiNode(){iFeaId = -1;dChiVal = 0.0;};~FeaChiNode(){};// 这个内联函数,决定节点之间的比较关系,用于按照卡方排序inline bool operator > (const FeaChiNode & theNode)const {if (dChiVal > theNode.dChiVal)return true;elsereturn false;}};vector<FeaChiNode> FeaChiVec;FeaChiVec.resize (iFeaTypeNum);// iterate ppChiMatrix and fill in ppFeaSelMatrix and pFeaSelected(这个数组是个0-1数组,表示这个特征是否已经被其他类别选择了。同时,这也是返回的结果,表示哪些个特征被选择了)for (int i=0; i<iClassNum; i++){// fill in FeaChiVec by ppChiMatrix[i]for (int j=0; j<iFeaTypeNum; j++){FeaChiVec.at (j).iFeaId = j;FeaChiVec.at (j).dChiVal = ppChiMatrix[i][j];}// sort by Chi-square valuesort (FeaChiVec.begin(), FeaChiVec.end(), greater<FeaChiNode>());// get the top iSelFeaNumAClass items, and set the flag arravector<FeaChiNode>::iterator pFeaChi = FeaChiVec.begin();for (int k=0; k<iSelFeaNumAClass && pFeaChi != FeaChiVec.end(); k++, pFeaChi++){// these conditions could be relaxed if necessarywhile (pFeaChi != FeaChiVec.end() &&(0 != pFeaSelected[pFeaChi->iFeaId] ||// find the feature type that never been selectedDBL_MIN > ppPProbMatrix[i][pFeaChi->iFeaId])// in the same time, there are sample hitting this feature type and class)pFeaChi++;if(pFeaChi != FeaChiVec.end())pFeaSelected[pFeaChi->iFeaId] = 1;elsebreak;}}}

关键是最后两重for循环内部的while循环,那个条件判断,嗯,你懂得。


【进一步想】

进一步想,为什么每个类别都要选择选择相同数量的特征?不一定啊。

平时我们讲特征选择,都是在想选择哪些特征,最终落到的问题都是用什么标准来进行选择,如:卡方、信息增益。然后尝试了一圈,发现效果都差不多。还有一个问题就是,选择多少个特征?这个,也可以进行实验,1w,2w,或者再少一点,5k等等。最后选一个效果最好的。其实,再进一步,就是对每个类别,选择多少个特征?这个问题似乎很少有人提及。但我觉得还是比较重要的。

我们想一下,对于某个类别,特征选择得越多(在一定数量限定条件下),这个类别的文本分类的结果就越准。那么,套用机器学习的损失函数的概念,我们也可以引入“风险”的概念,对风险越大的类别,选择的特征越多;反之,就越小。如果风险都一样,还有一种准则,就是各个类别的训练样本比例。如果对于样本比例大的类别,选择了更多个特征,那么这个类别样本的准确率提高,就更有助于整体准确率的提高。这个再升华一下,就是按照“收益率”来确定特征选择的比例。

......

这样想下去,还有很多good idea可以提出,也可以进行试验,写一些paper。哎,自从博士毕业以后,有段时间没操刀写paper了。


好,对卡方选择,就说到这里。


原创粉丝点击