zoukankan      html  css  js  c++  java
  • Java实现TFIDF算法

    算法介绍

    最近要做领域概念的提取,TFIDF作为一个很经典的算法可以作为其中的一步处理。

    关于TFIDF算法的介绍可以参考这篇博客http://www.ruanyifeng.com/blog/2013/03/tf-idf.html

    计算公式比较简单,如下:

     

    预处理

    由于需要处理的候选词大约后3w+,并且语料文档数有1w+,直接挨个文本遍历的话很耗时,每个词处理时间都要一分钟以上。

    为了缩短时间,首先进行分词,一个词输出为一行方便统计,分词工具选择的是HanLp。

    然后,将一个领域的文档合并到一个文件中,并用“$$$”标识符分割,方便记录文档数。

    下面是选择的领域语料(PATH目录下):

    代码实现

    package edu.heu.lawsoutput;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @ClassName: TfIdf
     * @Description: TODO
     * @author LJH
     * @date 2017年11月12日 下午3:55:15
     */
    
    public class TfIdf {
    
        static final String PATH = "E:\corpus"; // 语料库路径
    
        public static void main(String[] args) throws Exception {
    
            String test = "离退休人员"; // 要计算的候选词
    
            computeTFIDF(PATH, test);
    
        }
    
        /**
        * @param @param path 语料路经
        * @param @param word 候选词
        * @param @throws Exception 
        * @return void 
        */
        static void computeTFIDF(String path, String word) throws Exception {
    
            File fileDir = new File(path);
            File[] files = fileDir.listFiles();
    
            // 每个领域出现候选词的文档数
            Map<String, Integer> containsKeyMap = new HashMap<>();
            // 每个领域的总文档数
            Map<String, Integer> totalDocMap = new HashMap<>();
            // TF = 候选词出现次数/总词数
            Map<String, Double> tfMap = new HashMap<>();
    
            // scan files
            for (File f : files) {
    
                // 候选词词频
                double termFrequency = 0;
                // 文本总词数
                double totalTerm = 0;
                // 包含候选词的文档数
                int containsKeyDoc = 0;
                // 词频文档计数
                int totalCount = 0;
                int fileCount = 0;
                // 标记文件中是否出现候选词
                boolean flag = false;
    
                FileReader fr = new FileReader(f);
                BufferedReader br = new BufferedReader(fr);
                String s = "";
    
                // 计算词频和总词数
                while ((s = br.readLine()) != null) {
                    if (s.equals(word)) {
                        termFrequency++;
                        flag = true;
                    }
    
                    // 文件标识符
                    if (s.equals("$$$")) {
                        if (flag) {
                            containsKeyDoc++;
                        }
                        fileCount++;
                        flag = false;
                    }
                    totalCount++;
                }
    
                // 减去文件标识符的数量得到总词数
                totalTerm += totalCount - fileCount;
                br.close();
                // key都为领域的名字
                containsKeyMap.put(f.getName(), containsKeyDoc);
                totalDocMap.put(f.getName(), fileCount);
                tfMap.put(f.getName(), (double) termFrequency / totalTerm);
    
                System.out.println("----------" + f.getName() + "----------");
                System.out.println("该领域文档数:" + fileCount);
                System.out.println("候选词出现词数:" + termFrequency);
                System.out.println("总词数:" + totalTerm);
                System.out.println("出现候选词文档总数:" + containsKeyDoc);
                System.out.println();
            }
            
            //计算TF*IDF
            for (File f : files) {
    
                // 其他领域包含候选词文档数
                int otherContainsKeyDoc = 0;
                // 其他领域文档总数
                int otherTotalDoc = 0;
    
                double idf = 0;
                double tfidf = 0;
                System.out.println("~~~~~" + f.getName() + "~~~~~");
    
                Set<Map.Entry<String, Integer>> containsKeyset = containsKeyMap.entrySet();
                Set<Map.Entry<String, Integer>> totalDocset = totalDocMap.entrySet();
                Set<Map.Entry<String, Double>> tfSet = tfMap.entrySet();
    
                // 计算其他领域包含候选词文档数
                for (Map.Entry<String, Integer> entry : containsKeyset) {
                    if (!entry.getKey().equals(f.getName())) {
                        otherContainsKeyDoc += entry.getValue();
                    }
                }
    
                // 计算其他领域文档总数
                for (Map.Entry<String, Integer> entry : totalDocset) {
                    if (!entry.getKey().equals(f.getName())) {
                        otherTotalDoc += entry.getValue();
                    }
                }
    
                // 计算idf
                idf = log((float) otherTotalDoc / (otherContainsKeyDoc + 1), 2);
    
                // 计算tf*idf并输出
                for (Map.Entry<String, Double> entry : tfSet) {
                    if (entry.getKey().equals(f.getName())) {
                        tfidf = (double) entry.getValue() * idf;
                        System.out.println("tfidf:" + tfidf);
                    }
                }
            }
        }
    
        static float log(float value, float base) {
            return (float) (Math.log(value) / Math.log(base));
        }
    }            

    运行结果 

    测试词为“离退休人员”,中间结果如下:

     最终结果:

    结论

    可以看到“离退休人员”在养老保险和社保领域,tfidf值比较高,可以作为判断是否为领域概念的一个依据。

    当然TF-IDF算法虽然很经典,但还是有许多不足,不能单独依赖其结果做出判断。

    很多论文提出了改进方法,本文只是实现了最基本的算法。

    如果有其他思路和想法欢迎讨论。


    转载请注明原文链接:http://www.cnblogs.com/justcooooode/p/7831157.html

  • 相关阅读:
    iOS开发之即时通讯之Socket(AsyncSocket)
    iOS 拨打电话功能
    iOS text的长文本换行
    IOS Mac上查看iPhone上的SQlite数据库
    iOS Code Signing的Invalid处理(根证书无效)
    判断UITextField 输入为空 输入全为空格
    IOS Constraints自动布局适应不同尺寸
    IOS App打包发布流程(公司账号)
    IOS App发布问题:code signing is required for product type "Application" in SDK 'iOS 9.2'
    IOS UIAlertView 和 UIActionSheet的区别
  • 原文地址:https://www.cnblogs.com/justcooooode/p/7831157.html
Copyright © 2011-2022 走看看