Hanlp分词实例:Java实现TFIDF算法

发布时间:2025-09-02 01:57:31 作者:益华网络 来源:undefined 浏览量(0) 点赞(0)
摘要:算法介绍

算法介绍

最近要做领域概念的提取, 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 算法虽然很经典,但还是有许多不足,不能单独依赖其结果做出判断。很多论文提出了改进方法,本文只是实现了最基本的算法。如果有其他思路和想法欢迎讨论。

二维码

扫一扫,关注我们

声明:本文由【益华网络】编辑上传发布,转载此文章须经作者同意,并请附上出处【益华网络】及本页链接。如内容、图片有任何版权问题,请联系我们进行处理。

感兴趣吗?

欢迎联系我们,我们愿意为您解答任何有关网站疑难问题!

您身边的【网站建设专家】

搜索千万次不如咨询1次

主营项目:网站建设,手机网站,响应式网站,SEO优化,小程序开发,公众号系统,软件开发等

立即咨询 15368564009
在线客服
嘿,我来帮您!