(6)文本挖掘(三)——文本特征TFIDF权重计算及文本向量空间VSM表示

建立文本数据数学描述的过程分为三个步骤:文本预处理、建立向量空间模型和优化文本向量。文本预处理主要采用分词、停用词过滤等技术将原始的文本字符串转化为词条串或者特点的符号串。文本预处理之后,每一个文本的词条串被进一步转换为一个文本向量,向量的每一维对应一个词条,其值反映的是这个词条与这个文本之间的相似度。相似度有很多不同的计算方法,所以优化文本向量就是采用最为合适的计算方法来规范化文本向量,使其能更好地应用于文本分类和文本聚类等方面。

TFIDF算法

TF-IDF使得一个单词能尽量与文本在语义上相关。TF-IDF算法的实现步骤:

经过试验发现,用TFIDF/max(TFIDF)的方法效果是最好的,具体代码如下:

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 经过试验发现,用TFIDF/max(TFIDF)的方法效果是最好的
 * @author Angela
 */
public class TFIDF {

    private Map<String,Integer> TF;//文本词频集
    private Map<String,Double> IDF;//特征-逆文档频率集  

    /**
     * 构造方法,初始化TF和IDF
     */
    public TFIDF(Map<String,Integer> TF,Map<String,Double> IDF){
        this.TF=TF;
        this.IDF=IDF;
    }     

    /**
     * 计算文本特征集的tf-idf权值
     * @return filePath文件的特征-TFIDF集
     */
    public Map<String,Double> getTFIDF(){
        Map<String,Double> tfidf=new HashMap<String,Double>();
        for(Map.Entry<String,Integer> me: TF.entrySet()){
            String f=me.getKey();
            double weight=me.getValue()*IDF.get(f);
            tfidf.put(f, weight);
        }
        return tfidf;
    }

    /**
     * 计算文本特征集的对数tf-idf权值
     * @return filePath文件的特征-TFIDF集
     */
    public Map<String,Double> getLogTFIDF(){
        Map<String,Double> tfidf=new HashMap<String,Double>();
        for(Map.Entry<String,Integer> me: TF.entrySet()){
            String f=me.getKey();
            double tf=1+Math.log(me.getValue());
            double weight=tf*IDF.get(f);
            tfidf.put(f, weight);
        }
        return tfidf;
    }

    /**
     * 进行规一化,每一个特征除以这篇文本TFIDF值之和,构成新的TFIDF集
     * @return filePath文件的特征-标准化TFIDF集
     */
    public Map<String,Double> getNormalTFIDF(){
        Map<String,Double> tfidf=new HashMap<String,Double>();
        Map<String,Double> weight=getTFIDF();
        double sum=MathUtil.calSum(weight);//计算TFIDF总和
        for(Map.Entry<String, Double> me: weight.entrySet()){
            String f=me.getKey();
            double w=me.getValue()/sum;
            tfidf.put(f, w);
        }
        return MapUtil.descend(tfidf);
    } 

    /**
     * 进行标准化,每一个特征除以这篇文本中最大的TFIDF值,构成新的TFIDF集
     * @return filePath文件的特征-标准化TFIDF集
     */
    public Map<String,Double> getStandardTFIDF(){
        Map<String,Double> tfidf=new HashMap<String,Double>();
        Map<String,Double> weight=getTFIDF();
        Map<String,Double> temp=MapUtil.descend(weight);
        Set<Map.Entry<String, Double>> set = temp.entrySet();
        Iterator<Map.Entry<String,Double>> it = set.iterator();
        double max=0;
        if(it.hasNext()){
            max=it.next().getValue();
        }
        for(Map.Entry<String, Double> me: weight.entrySet()){
            String f=me.getKey();
            double w=me.getValue()/max;
            tfidf.put(f, w);
        }
        return MapUtil.descend(tfidf);
    }  

    /**
     * 保存文本的TFIDF结果
     * @param tf 文本的TF集
     * @param idf IDF集
     * @param savePath 保存路径
     */
    public static void saveTFIDF(Map<String,Integer> tf,
            Map<String,Double> idf,String savePath){
        TFIDF tfidf=new TFIDF(tf,idf);
        Map<String,Double> weight=tfidf.getStandardTFIDF();
        Writer.saveMap(weight, savePath);
    }

    /**
     * 保存TFIDF结果
     * @param filePath 文本集的TF集路径
     * @param idfPath IDF路径
     * @param tarPath 保存路径
     */
    public static void saveTFIDF(String TFPath,String IDFPath,String tarPath){
        File tar=new File(tarPath);
        if(!tar.exists()) tar.mkdir();
        Map<String,Double> idf=Reader.toDoubleMap(IDFPath);//IDF
        File file=new File(TFPath);
        File[] labels=file.listFiles();//类别
        for(File label: labels){
            String labelpath=tarPath+File.separator+label.getName();
            File labelPath=new File(labelpath);
            if(!labelPath.exists()) labelPath.mkdir();
            File[] texts=label.listFiles();//文本
            for(File text: texts){
                String savePath=labelpath+File.separator+text.getName();
                Map<String,Integer> tf=Reader.toIntMap(text.getAbsolutePath());
                saveTFIDF(tf,idf,savePath);
                System.out.println("Saved "+savePath);
            }
        }
    }

    public static void main(String args[]){
        String TFPath="data\\r8trainTF";
        String IDFPath="data\\r8trainIDF.txt";
        String tarPath="data\\r8trainTFIDF3";
        saveTFIDF(TFPath,IDFPath,tarPath);
    }

}

向量空间模型VSM

余弦相似度

文本与文本之间的相似度不能简单地用欧式距离来计算,更合理的计算方式是余弦相似度。

下面就是涉及这两个知识点的工具类。

MathUtil存放通用的计算公式方法

import java.util.Map;

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

    /**
     * 计算Map的键值之和
     * @param map
     * @return
     */
    public static double calSum(Map<String,Double> map){
        double sum=0;
        for(Map.Entry<String,Double> me: map.entrySet()){
            sum+=me.getValue();
        }
        return sum;
    }

    /**
     * 计算两篇文本的相似度
     * @param text1 文本1
     * @param text2 文本2
     * @return text1和text2的余弦相似度,值越大越相似
     */
    public static double calSim(Map<String,Double> text1,
            Map<String,Double> text2){
        double sim=0;//相似度
        double sum=0;//相同特征的权重相乘之和
        double len1=0;//文本1的长度
        double len2=0;//文本2的长度
        for(Map.Entry<String,Double> me: text1.entrySet()){
            String f=me.getKey();
            double value=me.getValue();
            if(text2.containsKey(f)){
                sum+=value*text2.get(f);
            }
            len1+=value*value;
        }
        for(Map.Entry<String,Double> me: text2.entrySet()){
            double value=me.getValue();
            len2+=value*value;
        }
        sim=sum/(Math.sqrt(len1)*Math.sqrt(len2));
        return sim;
    }

}

GetData获取DF、TFIDF即VSM、类别成员clusterMember、类别列表LabelList及由TFIDF构造的其它数据

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

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

    /**
     * 根据TFIDF得到对应的DF集合
     * 如果你的TFIDF集只是原始的一部分,就用这个方法来获取对应的DF集
     * @param tfidf TFIDF集合
     * @return
     */
    public static Map<String,Integer> getDF(Map<String,Map<String,Double>> tfidf){
        Map<String,Integer> map=new HashMap<String,Integer>();
        for(Map.Entry<String,Map<String,Double>> me: tfidf.entrySet()){
            Map<String,Double> text=me.getValue();
            for(Map.Entry<String,Double> t: text.entrySet()){
                String feature=t.getKey();
                if(map.containsKey(feature)){
                    map.put(feature,map.get(feature)+1);
                }else{
                    map.put(feature, 1);
                }
            }
        }
        return map;
    }

    /**
     * 读取文本集,返回Map<类别+文件名,Map<特征,权重>>
     * 当你进行特征选择后,得到特征子集,可以用这个方法
     * 从文本集中构建新的VSM
     * @param filePath TFIDF存放路径
     * @param featureSet 特征集
     * @return
     */
    public static Map<String,Map<String,Double>> getTFIDF(String filePath,
            Set<String> featureSet){
        Map<String,Map<String,Double>> map=new HashMap<String,Map<String,Double>>();
        File path=new File(filePath);
        File[] files=path.listFiles();//类别
        for(File file: files){
            String label=file.getName();
            File[] texts=file.listFiles();//文本
            for(File text: texts){
                Map<String,Double> tfidf=Reader.toDoubleMap(text.getAbsolutePath());
                Map<String,Double> temp=new HashMap<String,Double>();
                for(Map.Entry<String,Double> me: tfidf.entrySet()){
                    String feature=me.getKey();
                    if(featureSet.contains(feature)){
                        temp.put(feature,me.getValue());
                    }
                }
                map.put(label+File.separator+text.getName(), temp);
            }
        }
        return map;
    }

    /**
     * 根据特征集构建新的VSM,返回Map<类别+文件名,Map<特征,权重>>
     * 当你进行特征选择后,得到特征子集,可以用这个方法
     * 从原始的VSM中构建新的VSM
     * @param tfidf 原始的VSM
     * @param featureSet 特征集
     * @return
     */
    public static Map<String,Map<String,Double>> getTFIDF(
            Map<String,Map<String,Double>> tfidf,Set<String> featureSet){
        Map<String,Map<String,Double>> map=new HashMap<String,Map<String,Double>>();
        for(Map.Entry<String,Map<String,Double>> me: tfidf.entrySet()){
            Map<String,Double> text=me.getValue();
            Map<String,Double> temp=new HashMap<String,Double>();
            for(Map.Entry<String,Double> t: text.entrySet()){
                String feature=t.getKey();
                if(featureSet.contains(feature)){
                    temp.put(feature,t.getValue());
                }
            }
            map.put(me.getKey(), temp);
        }
        return map;
    }  

    /**
     * 根据TFIDF文本集和特征集构建
     * Map<特征,Map<文本路径名,特征在该文本中的TFIDF值>>
     * @param tfidf TFIDF文本集
     * @param featureSet 特征集
     * @return
     */
    public static Map<String,Map<String,Double>> getTermIndex(
            Map<String,Map<String,Double>> tfidf,Set<String> featureSet){
        Map<String,Map<String,Double>> termIndex=
                new HashMap<String,Map<String,Double>>();
        for(String f: featureSet){//特征
            //包含有特征f的所有文本及特征f在该文本中的权重
            Map<String,Double> feature=new HashMap<String,Double>();
            for(Map.Entry<String,Map<String,Double>> me: tfidf.entrySet()){
                Map<String,Double> text=me.getValue();//文本
                if(text.containsKey(f)){//如果文本包含特征f
                    //将文本的路径和特征f的值赋给feature
                    feature.put(me.getKey(), text.get(f));
                }
            }
            //将特征及feature赋给termIndex
            termIndex.put(f,feature);
        }
        return termIndex;
    }

    /**
     * 根据文本特征集和特征集构建Map<特征,Set<文本路径名>>
     * @param dataSet Map<文本,Set<特征>>
     * @param featureSet 特征集
     * @return
     */
    public static Map<String,Set<String>> getFeatureIndex(
            Map<String,Set<String>> dataSet,Set<String> featureSet){
        Map<String,Set<String>> featureIndex=
                new HashMap<String,Set<String>>();
        for(String f: featureSet){//特征
            Set<String> textSet=new HashSet<String>();
            for(Map.Entry<String,Set<String>> me: dataSet.entrySet()){
                String textPath=me.getKey();//文本路径
                Set<String> feature=me.getValue();//文本的特征集
                //如果文本包含特征f
                if(feature.contains(f)){
                    textSet.add(textPath);
                }
            }
            //将特征及文本集赋给termText
            featureIndex.put(f,textSet);
        }
        return featureIndex;
    }

    /**
     * 文本集的类别成员
     * @param filePath
     * @return
     */
    public static Map<Integer,List<String>> getClusterMember(String filePath){
        Map<Integer,List<String>> clusterMember=new HashMap<Integer,List<String>>();
        File file=new File(filePath);
        File[] labels=file.listFiles();
        int labelNum=labels.length;
        for(int i=0;i<labelNum;i++){
            File[] texts=labels[i].listFiles();
            String label=labels[i].getName();
            List<String> member=new ArrayList<String>();
            for(File text: texts){
                member.add(label+File.separator+text.getName());
            }
            clusterMember.put(i, member);
        }
        return clusterMember;
    }    

    /**
     * 类别集
     * @param filePath
     * @return
     */
    public static List<String> getLabelList(String filePath){
        List<String> labelList=new ArrayList<String>();
        File files=new File(filePath);
        File[] file=files.listFiles();
        for(File f: file){
            labelList.add(f.getName());
        }
        return labelList;
    } 

    /**
     * 根据TFIDF集构造类别集
     * @param TFIDF
     * @return
     */
    public static List<String> getLabelList(Map<String,Map<String,Double>> TFIDF){
        Set<String> labels=new HashSet<String>();
        for(Map.Entry<String,Map<String,Double>> me: TFIDF.entrySet()){
            String path=me.getKey();
            String label=path.substring(0,path.lastIndexOf(File.separator));
            labels.add(label);
        }
        List<String> labelList=new ArrayList<String>(labels);
        return labelList;
    }     

}

因为文本数据量一般很大,而且VSM具有高维稀疏的特点,所以一般需要进行特征选择,来减少特征的数量。下一节,我将介绍几种特征选择方法。

时间: 2024-10-16 13:13:35

(6)文本挖掘(三)——文本特征TFIDF权重计算及文本向量空间VSM表示的相关文章

文本情感分析(一):基于词袋模型(VSM、LSA、n-gram)的文本表示

现在自然语言处理用深度学习做的比较多,我还没试过用传统的监督学习方法做分类器,比如SVM.Xgboost.随机森林,来训练模型.因此,用Kaggle上经典的电影评论情感分析题,来学习如何用传统机器学习方法解决分类问题. 通过这个情感分析的题目,我会整理做特征工程.参数调优和模型融合的方法,这一系列会有四篇文章.这篇文章整理文本特征工程的内容. 文本的特征工程主要包括数据清洗.特征构造.降维和特征选择等. 首先是数据清洗,比如去停用词.去非字母汉字的特殊字符.大写转小写.去掉html标签等. 然后

文本分类入门(番外篇)特征选择与特征权重计算的区别

http://www.blogjava.net/zhenandaci/archive/2009/04/19/266388.html 在文本分类的过程中,特征(也可以简单的理解为“词”)从人类能够理解的形式转换为计算机能够理解的形式时,实际上经过了两步骤的量化——特征选择阶段的重要程度量化和将具体文本转化为向量时的特征权重量化.初次接触文本分类的人很容易混淆这两个步骤使用的方法和各自的目的,因而我经常听到读者有类似“如何使用TFIDF做特征选择”或者“卡方检验量化权重后每篇文章都一样”等等困惑.

文本分类学习(三) 特征权重(TF/IDF)和特征提取

上一篇中,主要说的就是词袋模型.回顾一下,在进行文本分类之前,我们需要把待分类文本先用词袋模型进行文本表示.首先是将训练集中的所有单词经过去停用词之后组合成一个词袋,或者叫做字典,实际上一个维度很大的向量.这样每个文本在分词之后,就可以根据我们之前得到的词袋,构造成一个向量,词袋中有多少个词,那这个向量就是多少维度的了.然后就把这些向量交给计算机去计算,而不再需要文本啦.而向量中的数字表示的是每个词所代表的权重.代表这个词对文本类型的影响程度. 在这个过程中我们需要解决两个问题:1.如何计算出适

文本特征提取方法研究

文本特征提取方法研究 一.课题背景概述 文本挖掘是一门交叉性学科,涉及数据挖掘.机器学习.模式识别.人工智能.统计学.计算机语言学.计算机网络技术.信息学等多个领域.文本挖掘就是从大量的文档中发现隐含知识和模式的一种方法和工具,它从数据挖掘发展而来,但与传统的数据挖掘又有许多不同.文本挖掘的对象是海量.异构.分布的文档(web);文档内容是人类所使用的自然语言,缺乏计算机可理解的语义.传统数据挖掘所处理的数据是结构化的,而文档(web)都是半结构或无结构的.所以,文本挖掘面临的首要问题是如何在计

【转载】文本特征提取方法研究

文本特征提取方法研究 引言:转载大神的文章(http://blog.csdn.net/tvetve/article/details/2292111),存一下用于日后查找 一.课题背景概述 文本挖掘是一门交叉性学科,涉及数据挖掘.机器学习.模式识别.人工智能.统计学.计算机语言学.计算机网络技术.信息学等多个领域.文本挖掘就是从大量的文档中发现隐含知识和模式的一种方法和工具,它从数据挖掘发展而来,但与传统的数据挖掘又有许多不同.文本挖掘的对象是海量.异构.分布的文档(web);文档内容是人类所使用

搜索中词权重计算及实践

随着网络和信息技术的飞速发展,网络中的信息量也呈现爆炸式的增长,那么快速并且正确从这些海量的数据中获取正确的信息成为了现在搜索引擎技术的核心问题.用户的输入通常呈现很大的差异性,这是因为不同的人接受不同的教育.不同的文化,导致在表述同一个问题上面差异很大,那么对用户输入的搜索词进行词条权重的打分是非常有必要的,这对于从用户输入的搜索词中提取核心词,或是对搜索词返回的文档排序等都是一个非常重要的课题.词权重特征是衡量查询中词的重要度程度,主要应用于相关性排序. 一.TF-IDF 词频-逆文档频率(

机器学习入门-文本特征-word2vec词向量模型 1.word2vec(进行word2vec映射编码)2.model.wv[&#39;sky&#39;]输出这个词的向量映射 3.model.wv.index2vec(输出经过映射的词名称)

函数说明: 1. from gensim.model import word2vec  构建模型 word2vec(corpus_token, size=feature_size, min_count=min_count, window=window, sample=sample) 参数说明:corpus_token已经进行切分的列表数据,数据格式是list of list , size表示的是特征向量的维度,即映射的维度, min_count表示最小的计数词,如果小于这个数的词,将不进行统计,

前端极易被误导的css选择器权重计算及css内联样式的妙用技巧

记得大学时候,专业课的网页设计书籍里面讲过css选择器权重的计算:id是100,class是10,html标签是5等等,然后全部加起来的和进行比较... 我只想说:真是误人子弟,害人不浅! 最近,在前端群里还发现以上观点类似的奇葩聊天,真是*** 其实,也是在很久以前,看了腾讯ISUX的一位前端工程师-麦时分享的一篇技术文章(个人站点已失效,就不贴出来了),才了解到真正的css选择器权重计算. 以下是css选择器权重计算精华所在,翻译自国外的文档(记得是W3C给出的计算规则) 如果一个声明来自s

操作系统,编程语言分类,执行python两种方式,变量,内存管理,定义变量的三个特征

操作系统 1.什么是操作系统 操作系统位于计算机硬件与应用软件之间 是一个协调.管理.控制计算机硬件资源与软件资源的控制程序 2.为何要有操作系统? 1.控制硬件 2.把对硬件的复杂的操作封装成优美简单的接口(文件),给用户或者应用程序去使用 注意:一套完整的计算机系统包含三部分 应用程序:qq,暴风影音,快播 操作系统:windows,linux,unix 计算机硬件 强调: 我们以后开发的都是应用程序 应用程序无法直接操作硬件,但凡要操作硬件,都是调用操作系统的接口 编程语言分类 1.机器语