(7)文本挖掘(四)——特征选择

特征选择指的是按照一定的规则从原来的特征集合中选择出一小部分最为有效的特征。通过特征选择,一些和任务无关或是冗余的特征被删除,从而提高数据处理的效率。

文本数据的特征选择研究的重点就是用来衡量单词重要性的评估函数,其过程就是首先根据这个评估函数来给每一个单词计算出一个重要性的值,然后根据预先设定好的阈值来选择出所有其值超过这个阈值的单词。

根据特征选择过程与后续数据挖掘算法的关联,特征选择方法可分为过滤、封装和嵌入。

(1)过滤方法(Filter Approach):使用某种独立于数据挖掘任务的方法,在数据挖掘算法运行之前进行特征选择,即先过滤特征集产生一个最有价值的特征子集。或者说,过滤方法只使用数据集来评价每个特征的相关性, 它并不直接优化任何特定的分类器, 也就是说特征子集的选择和后续的分类算法无关。

(2)封装方法(Wrapper Approach):将学习算法的结果作为特征子集评价准则的一部分,根据算法生成规则的分类精度选择特征子集。该类算法具有使得生成规则分类精度高的优点,但特征选择效率较低。封装方法与过滤方法正好相反, 它直接优化某一特定的分类器, 使用后续分类算法来评价候选特征子集的质量。

一般说来, 过滤方法的效率比较高, 结果与采用的分类算法没有关系, 但效果稍差;封装方法占用的运算时间较多, 结果依赖于采用的分类算法, 也因为这样其效果较好。

(3)嵌入方法(embedded Approach):特征选择作为数据挖掘算法的一部分自然地出现。在数据挖掘算法运行期间,算法本身决定使用哪些属性和忽略哪些特征,如决策树C4.5分类算法。

如果将过滤方法和封装方法结合,就产生了第四种方法:

(4)混合方法(Hybrid Approach):过滤方法和封装方法的结合,先用过滤方法从原始数据集中过滤出一个候选特征子集,然后用封装方法从候选特征子集中得到特征子集。该方法具有过滤方法和封装方法两者的优点,即效率高,效果好。

根据特征选择过程是否用到类信息的指导,即其评估函数是否依赖类信息,特征选择可分为监督式特征选择(需要依赖类信息的有监督特征选择,只能用于文本分类)、无监督式特征选择(不需要依赖类信息的无监督特征选择,用于文本分类和文本聚类)。

(1)监督式特征选择(supervised feature selection):使用类信息来进行指导,通过度量类信息与特征之间的相互关系来确定子集大小。

(2)无监督式特征选择(unsupervised feature selection):在没有类信息的指导下,使用样本聚类或特征聚类对聚类过程中的特征贡献度进行评估,根据贡献度的大小进行特征选择。

下面,我先介绍有监督特征选择方法IG(Information Gain信息增益)和CHI(X2 statistic卡方统计)及它们相应的Java实现。由于这两者需要的数据是相同的:clusterMember类别成员类,特征所在文本集,DF集,需要做的事情是相同的:进行特征选择得到特征重要性集,所以我写了一个抽象父类Supervisor:

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author Angela
 */
public abstract class Supervisor {

    protected Map<Integer,List<String>> clusterMember;//簇成员
    protected Map<String,Set<String>> featureIndex;//特征-文本名集
    protected Map<String,Integer> DF;//DF集
    protected int textNum;//总文本数
    protected Set<String> featureSet;//特征集
    protected Map<String,Double> featureMap;//特征重要性集

    /**
     * 构造函数,初始化各个数据
     * @param clusterMember 类别成员
     * @param featureIndex
     * @param DF
     */
    public Supervisor(Map<Integer,List<String>> clusterMember,
            Map<String,Set<String>> featureIndex,Map<String,Integer> DF){
        this.clusterMember=clusterMember;
        textNum=0;
        for(Map.Entry<Integer,List<String>> me: clusterMember.entrySet()){
            textNum+=me.getValue().size();//计算文本数
        }
        this.featureIndex=featureIndex;
        this.DF=DF;
        featureSet=DF.keySet();//得到特征集
        featureMap=new HashMap<String,Double>();
    }

    /**
     * 进行特征选择,得到featureMap
     * @return 特征-重要性集
     */
    public abstract Map<String,Double> selecting();

}

IG

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author Angela
 */
public class IG extends Supervisor{

    public IG(Map<Integer,List<String>> clusterMember,
            Map<String,Set<String>> featureIndex,Map<String,Integer> DF){
        super(clusterMember,featureIndex,DF);
    }

    /**
     * 计算文本集的熵
     * @return 总熵
     */
    public double computeEntropyOfCluster(){
        double entropy=0;//文本集的熵
        double pro;//类的概率
        double temp;//类的熵
        for(Map.Entry<Integer,List<String>> me:clusterMember.entrySet()){
            pro=(double)me.getValue().size()/textNum;
            temp=pro*Math.log(pro);
            entropy+=temp;
        }
        return -entropy;
    }

    /**
     * 计算特征的条件熵
     * @param f 特征
     * @return 特征f的条件熵
     */
    public double computeConditionalEntropy(String f){
        double ce=0;
        double df=DF.get(f); //含有特征f的文本总数
        double nf=textNum-df;
        Set<String> text=featureIndex.get(f);//含有特征f的文本

        double proOfCF1;//P(Ci|T)
        double proOfCF2;//P(Ci|!T)
        double value1=0;//P(Ci|T)log2(P(Ci|T))的和
        double value2=0;//P(Ci|!T)log2(P(Ci|!T))的和

        for(Map.Entry<Integer,List<String>> me:clusterMember.entrySet()){
            int count=0;
            List<String> member=me.getValue();//簇中的文本
            int size=member.size();
            for(String path: member){
                if(text.contains(path)){
                    count++;//含有特征f并属于该簇的文本数
                }
            }
            double temp1=0;//P(Ci|T)log2(P(Ci|T))
            double temp2=0;//P(Ci|!T)log2(P(Ci|!T))
            if(count!=0&&count!=size){
                //属于类Ci且含有词t的文本数除以含有词t的文本数
                proOfCF1=count/df;
                temp1=proOfCF1*Math.log(proOfCF1);
                proOfCF2=(size-count)/nf;
                temp2=proOfCF2*Math.log(proOfCF2);
            }
            value1+=temp1;
            value2+=temp2;
        }
        ce=(df*value1+nf*value2)/textNum;

        return ce;
    }

     /**
     * 进行特征选择,计算所有特征的IG,得到featureMap
     * @return 特征-重要性集
     */
    @Override
    public Map<String,Double> selecting(){
        double e=computeEntropyOfCluster();
        for(String f: featureSet){
            double ce=computeConditionalEntropy(f);
            double value=e+ce;
            featureMap.put(f, value);
        }
        return featureMap;
    }
}

CHI

这里我选择的是计算CHI总和

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author Angela
 */
public class CHI extends Supervisor{

    public CHI(Map<Integer,List<String>> clusterMember,
            Map<String,Set<String>> featureIndex,Map<String,Integer> DF){
        super(clusterMember,featureIndex,DF);
    }

    /**
     * 计算特征的CHI值
     * @param feature 特征
     * @return 特征f的CHI值
     */
    public double computeCHI(String feature){
        double macro_chi=0;
        double pf=DF.get(feature);//包含特征f的文本数
        double nf=textNum-pf;//不包含特征f的文本数
        Set<String> text=featureIndex.get(feature);//含有特征f的文本
        for(Map.Entry<Integer,List<String>> me:clusterMember.entrySet()){
            List<String> member=me.getValue();
            double pc=member.size();//簇中的文本
            double nc=textNum-pc;//不属于簇cluster的文本数
            double value1=pf*nf*pc*nc;

            double pf_pc=0;
            for(String path: member){
                if(text.contains(path)){
                    pf_pc++;//含有特征f且属于该簇的文本数
                }
            }

            double pf_nc=pf-pf_pc; //含有特征f但不属于该簇的文本数
            double nf_pc=pc-pf_pc;//不含有特征f但属于该簇的文本数
            double nf_nc=nc-pf_nc; //不含有特征f且不属于该簇的文本数
            double value2=Math.pow(pf_pc*nf_pc-pf_nc*nf_nc,2);

            double chi=pc*value2/value1;
            macro_chi+=chi;
        }
        return macro_chi;
    }

    /**
     * 进行特征选择,计算所有特征的CHI,得到featureMap
     * @return 特征-重要性集
     */
    public Map<String,Double> selecting(){
        for(String feature: featureSet){
            double value=computeCHI(feature);
            featureMap.put(feature, value);
        }
        return featureMap;
    }

}

然后我们写一个方法来将特征选择后的结果保存到arff文件中:

public static void supervisor(String segmentPath,String DFPath,
            int num,String TFIDFPath,String savePath){
        //先进行DF特征选择,去掉DF值大于3小于3000的特征,减少特征数量
         Map<String,Integer> DF=Reader.toIntMap(DFPath);
        Map<String,Integer> subDF=MapUtil.getSubMap(DF, 3, 3000);
        //读取数据集
        Map<String,Set<String>> dataSet=Reader.readDataSet(segmentPath);
        Map<String,Set<String>> featureIndex=
                GetData.getFeatureIndex(dataSet, subDF.keySet());
        //读取类别成员及
        Map<Integer,List<String>> clusterMember=
                GetData.getClusterMember(segmentPath);
        //进行特征选择
        //Supervisor sv=new IG(clusterMember,featureIndex,subDF);
        Supervisor sv=new CHI(clusterMember,featureIndex,subDF);
        Map<String,Double> featureMap=sv.selecting();
        //MapUtil.print(featureMap, 100);
        Set<String> featureSet=MapUtil.getSubMap(featureMap, num).keySet();
        //根据特征子集构造VSM
        Map<String,Map<String,Double>> tfidf=
                GetData.getTFIDF(TFIDFPath, featureSet);
        //读取类别集
        List<String> labelList=GetData.getLabelList(TFIDFPath);
        String[] labels=new String[labelList.size()];
        labelList.toArray(labels);
        //写出arff文件,进行测试
        Writer.saveAsArff(tfidf, featureSet, labels, savePath);
    }

下面,介绍无监督特征选择方法TC(Term Contribution单词贡献度)、TS(Term Strength单词权)、IIG(Improved Information Gain改进的用于无监督的IG)及它们各自的Java实现。三者的分类效果都很好,但TS的时间复杂度是N*N(N为文本集的文本总数),花费的时间高于TC和IIG。

TC

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import utility.GetData;
import utility.MapUtil;
import utility.Reader;
import utility.Writer;

/**
 *
 * @author Angela
 */
public class TC {

    /**
     * 计算特征的TC值
     * @param index 包含有某个特征的所有文本及在文本中的权重的集合
     * @return 该特征的TC值
     */
    private static double computeTC(Map<String,Double> index){
        double tc=0;
        double value1=0;
        double value2=0;
        for(Map.Entry<String,Double> me: index.entrySet()){
            double value=me.getValue();
            value1+=value;
            value2+=value*value;
        }
        tc=(value1*value1-value2)/2;
        return tc;
    }

    /**
     * 进行特征选择,计算所有特征的TC贡献度,得到featureMap
     * @return 特征-重要性集
     */
    public static Map<String,Double> selecting(
            Map<String,Map<String,Double>> termIndex){
        Map<String,Double> featureMap=new HashMap<String,Double>();
        for(Map.Entry<String,Map<String,Double>> me:termIndex.entrySet()){
            String f=me.getKey();
            double value=computeTC(me.getValue());
            featureMap.put(f, value);
        }
        return featureMap;
    }

    public static void main(String args[]){
        String segmentPath="data\\r8-train-stemmed";
        String DFPath="data\\r8trainDF.txt";
        int num=1000;
        String TFIDFPath="data\\r8trainTFIDF2";
        String savePath="E:\\tc.arff";
        Map<String,Integer> DF=Reader.toIntMap(DFPath);
        Set<String> subSet=MapUtil.getSubMap(DF, 3, 3000).keySet();
        Map<String,Map<String,Double>> TFIDF=
                GetData.getTFIDF(TFIDFPath, subSet);
        Map<String,Map<String,Double>> termIndex=
                GetData.getTermIndex(TFIDF, subSet);
        Map<String,Double> featureMap=TC.selecting(termIndex);
        //MapUtil.print(featureMap, 100);
        Set<String> featureSet=MapUtil.getSubMap(featureMap, num).keySet();
        Map<String,Map<String,Double>> tfidf=
                GetData.getTFIDF(TFIDF, featureSet);
        List<String> labelList=GetData.getLabelList(TFIDFPath);
        String[] labels=new String[labelList.size()];
        labelList.toArray(labels);
        Writer.saveAsArff(tfidf, featureSet, labels, savePath);
    }

}

TS

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import utility.GetData;
import utility.MapUtil;
import utility.MathUtil;
import utility.Reader;
import utility.Writer;

/**
 *
 * @author Angela
 */
public class TS {

    public static Map<String,Double> selecting(
             Map<String,Map<String,Double>> TFIDF,double threshold){
        int textNum=TFIDF.size();//文本总数
        List<Map<String,Double>> textVSM=new
                ArrayList<Map<String,Double>>(TFIDF.values());
        Map<String,Integer> termSimTextNum=new HashMap<String,Integer>();
        int simTextCount=0;//相关文本对数量
        for(int i=0;i<textNum;i++){
            List<String> simText=new ArrayList<String>();
            Map<String,Double> text1=textVSM.get(i);
            Set<String> featureSet=text1.keySet();//text1含有的特征
            for(int j=0;j<textNum;j++){
                if(i!=j){
                    Map<String,Double> text2=textVSM.get(j);
                    //计算text1和text2的文本相似度
                    double cos=MathUtil.calSim(text1,text2);
                    if(cos>threshold){
                        simTextCount++;
                        //计算含有特征feature的相关文本对数
                        for(String feature: featureSet){
                            if(text2.containsKey(feature)){
                                if(termSimTextNum.containsKey(feature)){
                                    termSimTextNum.put(feature,
                                            termSimTextNum.get(feature)+1);
                                }else{
                                    termSimTextNum.put(feature, 1);
                                }
                            }
                        }
                    }
                }
            }
        }
        System.out.println("相关文本对总数:"+simTextCount);
        Map<String,Double> featureMap=new HashMap<String,Double>();
        for(Map.Entry<String,Integer> me: termSimTextNum.entrySet()){
            String f=me.getKey();
            //计算TS值
            double value=me.getValue()*1.0/simTextCount;
            featureMap.put(f, value);
        }
        return featureMap;
    }

    public static void main(String args[]){
        String segmentPath="data\\r8-train-stemmed";
        String DFPath="data\\r8trainDF.txt";
        int num=1000;
        String TFIDFPath="data\\r8trainTFIDF2";
        String savePath="E:\\ts.arff";
        Map<String,Integer> DF=Reader.toIntMap(DFPath);
        Map<String,Integer> subDF=MapUtil.getSubMap(DF, 3, 3000);
        Set<String> subSet=subDF.keySet();
        Map<String,Map<String,Double>> TFIDF=
                GetData.getTFIDF(TFIDFPath, subSet);
        Map<String,Double> featureMap=TS.selecting(TFIDF,0.5);
        //MapUtil.print(featureMap, 100);
        Set<String> featureSet=MapUtil.getSubMap(featureMap, num).keySet();
        Map<String,Map<String,Double>> tfidf=
                GetData.getTFIDF(TFIDF, featureSet);
        List<String> labelList=GetData.getLabelList(TFIDFPath);
        String[] labels=new String[labelList.size()];
        labelList.toArray(labels);
        Writer.saveAsArff(tfidf, featureSet, labels, savePath);
    }

}

IIG


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import utility.GetData;
import utility.MapUtil;
import utility.Reader;
import utility.Writer;

/**
 *
 * @author Angela
 */
public class IIG {

    /**
     * 进行特征选择,计算所有特征的IIG,得到featureMap
     * @return 特征-重要性集
     */
    public static Map<String,Double> selecting(
            Map<String,Set<String>> dataSet,Map<String,Integer> DF){
        Set<String> featureSet=DF.keySet();
        Map<String,Set<String>> featureIndex=GetData.
                getFeatureIndex(dataSet,featureSet);
        Map<String,List<Integer>> featureNum=new HashMap<String,List<Integer>>();
        Map<String,Integer> featureSum=new HashMap<String,Integer>();
        for(Map.Entry<String,Set<String>> me: featureIndex.entrySet()){
            String f=me.getKey();
            Set<String> texts=me.getValue();
            List<Integer> list=new ArrayList<Integer>();
            int sum=0;//计算文档总词数
            for(String text: texts){
                int len=dataSet.get(text).size();
                list.add(len);
                sum+=len;
            }
            featureNum.put(f, list);
            featureSum.put(f, sum);
        }
        Map<String,Double> featureMap=new HashMap<String,Double>();
        for(String f: featureSet){
            int dfValue=DF.get(f);
            List<Integer> list=featureNum.get(f);
            int sum=featureSum.get(f);
            double ce=0;
            for(int num: list){
                double pro=num*1.0/sum;
                ce+=(pro*Math.log(pro));
            }
            //计算IIG值
            double value=-Math.log(dfValue+0.01)*ce;
            featureMap.put(f, value);
        }
        return featureMap;
    }

    public static void main(String args[]){
        String segmentPath="data\\r8-train-stemmed";
        String DFPath="data\\r8trainDF.txt";
        int num=1000;
        String TFIDFPath="data\\r8trainTFIDF2";
        String savePath="E:\\iig.arff";
        Map<String,Integer> DF=Reader.toIntMap(DFPath);
        Map<String,Integer> subDF=MapUtil.getSubMap(DF, 3, 3000);
        Set<String> subSet=subDF.keySet();
        Map<String,Set<String>> dataSet=Reader.readDataSet(segmentPath);
        Map<String,Map<String,Double>> TFIDF=
                GetData.getTFIDF(TFIDFPath, subSet);
        Map<String,Double> featureMap=IIG.selecting(dataSet,subDF);
        //MapUtil.print(featureMap, 100);
        Set<String> featureSet=MapUtil.getSubMap(featureMap, num).keySet();
        Map<String,Map<String,Double>> tfidf=
                GetData.getTFIDF(TFIDF, featureSet);
        List<String> labelList=GetData.getLabelList(TFIDFPath);
        String[] labels=new String[labelList.size()];
        labelList.toArray(labels);
        Writer.saveAsArff(tfidf, featureSet, labels, savePath);
    }

}

自此,主要的几个特征选择方法,都讲完了。当然,特征选择方法还有很多,不过具体数据集,具体特征选择方法使用。

时间: 2024-11-10 11:35:33

(7)文本挖掘(四)——特征选择的相关文章

使用Python的文本挖掘的特征选择/提取

在文本挖掘与文本分类的有关问题中,文本最初始的数据是将文档表示成向量空间模型的一个矩阵,而这个矩阵所拥有的就是不同的词,常采用特征选择方法.原因是文本的特征一般都是单词(term),具有语义信息,使用特征选择找出的k维子集,仍然是单词作为特征,保留了语义信息,而特征提取则找k维新空间,将会丧失了语义信息. 当然,另一方面,在处理文本时,对于我们来说,已经拥有将不同词在低维空间上总结归纳的能力,知道这些词的联系和区别,但是对于计算机来说,它们怎么知道这些的联系呢?也就是它们根本还不拥有这些降维的能

特征选择(四)-分散度

度量类别可分离性的量主要有: 欧氏距离(正态分布,协方差相等,且为单位阵) 是最简单的一种描述方法.它把两个类别中心之间的欧式距离作为两个不同类别间不相似性的度量. 马氏(Mahalanobis)距离(正态分布,协方差相等) 它用来描述两个具有相同的协方差矩阵C,不同的期望值和的类别之间的不相似性,具体表达式是: Mahalanobis距离 巴氏(Bhattacharyya)距离(正态分布,协方差不等) 它描述了两个协方差矩阵和期望值都不相同的类别之间的不相似性: Bhattacharyya距离

机器学习之(四)特征工程以及特征选择的工程方法

关于特征工程(Feature Engineering),已经是很古老很常见的话题了,坊间常说:"数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已".由此可见,特征工程在机器学习中占有相当重要的地位.在实际应用当中,可以说特征工程是机器学习成功的关键.纵观Kaggle.KDD等国内外大大小小的比赛,每个竞赛的冠军其实并没有用到很高深的算法,大多数都是在特征工程这个环节做出了出色的工作,然后使用一些常见的算法,比如LR,就能得到出色的性能.遗憾的是,在很多的书籍中并没有直接

文本挖掘——特征选择

特征选择有很多方法,看了很多资料后,我总结了以下几种,以后有新内容会随时修改 1.DF——基于文档频率的特征提取方法 概念:DF(document frequency)指出现某个特征项的文档的频率. 步骤:1).从训练语料中统计出保函某个特征的文档频率(个数)   2).根据设定的阈值(min&max),当该特征的DF值小于某个阈值时,去掉.因为没有代表性.当该特征的DF值大于某个阈值时,去掉.因为这个特征使文档出现的频率太高,没有区分度. 优点:降低向量计算的复杂度,去掉部分噪声,提高分类的准

舆情,文本挖掘

MLE,MAP,EM 和 point estimation 之间的关系是怎样的 和点估计相对应的是区间估计,这个一般入门的统计教材里都会讲.直观说,点估计一般就是要找概率密度曲线上值最大的那个点,区间估计则要寻找该曲线上满足某种条件的一个曲线段. 最大似然和最大后验是最常用的两种点估计方法.以最简单的扔硬币游戏为例,一枚硬币扔了五次,有一次是正面.用最大似然估计,就是以这五次结果为依据,判断这枚硬币每次落地时正面朝上的概率(期望值)是多少时,最有可能得到四次反面一次正面的结果.不难计算得到期望概

重磅︱文本挖掘深度学习之word2vec的R语言实现

笔者寄语:2013年末,Google发布的 word2vec工具引起了一帮人的热捧,大家几乎都认为它是深度学习在自然语言领域的一项了不起的应用,各种欢呼"深度学习在自然语言领域开始发力 了". 基于word2vec现在还出现了doc2vec,word2vec相比传统,考虑单词上下文的语义:但是doc2vec不仅考虑了单词上下文的语义,还考虑了单词在段落中的顺序. 如果想要了解word2vec的实现原理,应该读一读官网后面的三篇参考文献.显然,最主要的应该是这篇: Distributed

降维中的特征选择

在数据挖掘过程中,高维数据是非常棘手的研究对象.特别是在文本挖掘.图像处理和基因数据分析中,维度过高使很多学习器无法工作或效率降低,所以降维也是数据预处理过程的一项必要任务.降维大致有两大类别,一类是从原始维度中提取新的维度,例如主成分分析或因子分析,再或者是奇异值分解或是多维标度分析.另一类是从原始维度中选择一些子集,即称为特征选择(Feature Selection),或者叫作最佳子集选择.特征选择本质上继承了Occam's razor的思想,从一组特征中选出一些最有效的特征,使构造出来的模

浅谈关于特征选择算法与Relief的实现

一. 背景 1) 问题 在机器学习的实际应用中,特征数量可能较多,其中可能存在不相关的特征,特征之间也可能存在相关性,容易导致如下的后果: 1.     特征个数越多,分析特征.训练模型所需的时间就越长,模型也会越复杂. 2.     特征个数越多,容易引起“维度灾难”,其推广能力会下降. 3.     特征个数越多,容易导致机器学习中经常出现的特征稀疏的问题,导致模型效果下降. 4.     对于模型来说,可能会导致不适定的情况,即是解出的参数会因为样本的微小变化而出现大的波动. 特征选择,能

&lt; 独立项目 - 文本挖掘 &gt; - 2016/10/25 第一更 - &lt;Linux相关知识准备&gt;

< 独立项目 -  文本挖掘 > 项目立项的相关背景介绍,TODO方向. 一.Ubuntu环境配置 主机系统:Windows 7 SP1  64位操作系统 | i5-4210 CPU | 16GB RAM VirtualBox虚拟环境:GUN VitrualBox Linux系统:Ubuntu 15.10(ubuntu-15.10-desktop-i386) 二.Linux Ubuntu 基础知识 使用Ctrl+Alt+[F1~F6],可以切换到1~6号控制台.使用Ctrl+Alt+F7返回图