Java:基于TextRank算法的自动摘要(自动生成事件摘要)

news2024/9/21 22:42:39

TextRank 是一种用于文本摘要的自然语言处理算法。它的工作原理类似于 Google 搜索引擎的 PageRank 算法,即根据文本中每个单词出现的频率和被引用的次数来评估它的重要性。

所谓自动摘要,就是从文章中自动抽取关键句。何谓关键句?人类的理解是能够概括文章中心的句子,机器的理解只能模拟人类的理解,即拟定一个权重的评分标准,给每个句子打分,之后给出排名靠前的几个句子。

 1.TextRank简介

详情参考博客:利用TextRank算法提取摘要关键词以及Java实现_java textrank-CSDN博客 

2.TextRank自动摘要算法实现

TextRank算法实现自动摘要分为 5 个步骤:

  1. 把原文分割为单个句子
  2. 为字词编号,以词袋表示将句子向量化(词向量)
  3. 计算各句子向量的相似性并存放在矩阵中,即构建相似度矩阵。
  4. 利用相似度矩阵求出矩阵M,迭代更新每个句子的TR值。
  5. 选取一定数量的排名靠前的句子构成最后的摘要。

 

3.TextRank自动摘要的Java实现

TextRank自动摘要的java代码实现主要参考开源工具HanLP中的

TextRank算法自动摘要的Java实现-码农场

 

主要是将其中的分词工具替换成ANSJ分词,使分词更加准确和可定制化

3.1 分词工具包:

<dependency>
        <groupId>org.ansj</groupId>
        <artifactId>ansj_seg</artifactId>
        <version>5.1.6</version>
</dependency>

具体使用参考:开源中文分词Ansj的简单使用-CSDN博客

3.2 BM25——搜索相关性评分算法

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 搜索相关性评分算法
 *
 * @author hankcs
 */
public class BM25 {
    /**
     * 文档句子的个数
     */
    int D;

    /**
     * 文档句子的平均长度
     */
    double avgdl;

    /**
     * 拆分为[句子[单词]]形式的文档
     */
    List<List<String>> docs;

    /**
     * 文档中每个句子中的每个词与词频
     */
    Map<String, Integer>[] f;

    /**
     * 文档中全部词语与出现在几个句子中
     */
    Map<String, Integer> df;

    /**
     * IDF
     */
    Map<String, Double> idf;

    /**
     * 调节因子
     */
    final static float k1 = 1.5f;

    /**
     * 调节因子
     */
    final static float b = 0.75f;

    public BM25(List<List<String>> docs) {
        this.docs = docs;
        D = docs.size();
        for (List<String> sentence : docs) {
            avgdl += sentence.size();
        }
        avgdl /= D;
        f = new Map[D];
        df = new TreeMap<String, Integer>();
        idf = new TreeMap<String, Double>();
        init();
    }

    /**
     * 在构造时初始化自己的所有参数
     */
    private void init() {
        int index = 0;
        for (List<String> sentence : docs) {
            Map<String, Integer> tf = new TreeMap<String, Integer>();
            for (String word : sentence) {
                Integer freq = tf.get(word);
                freq = (freq == null ? 0 : freq) + 1;
                tf.put(word, freq);
            }
            f[index] = tf;
            for (Map.Entry<String, Integer> entry : tf.entrySet()) {
                String word = entry.getKey();
                Integer freq = df.get(word);
                freq = (freq == null ? 0 : freq) + 1;
                df.put(word, freq);
            }
            ++index;
        }
        for (Map.Entry<String, Integer> entry : df.entrySet()) {
            String word = entry.getKey();
            Integer freq = entry.getValue();
            idf.put(word, Math.log(D - freq + 0.5) - Math.log(freq + 0.5));
        }
    }

    public double sim(List<String> sentence, int index) {
        double score = 0;
        for (String word : sentence) {
            if (!f[index].containsKey(word)) continue;
            int d = docs.get(index).size();
            Integer wf = f[index].get(word);
            score += (idf.get(word) * wf * (k1 + 1)
                    / (wf + k1 * (1 - b + b * d
                    / avgdl)));
        }

        return score;
    }

    public double[] simAll(List<String> sentence) {
        double[] scores = new double[D];
        for (int i = 0; i < D; ++i) {
            scores[i] = sim(sentence, i);
        }
        return scores;
    }
}

 3.3 TextRank 自动摘要



import com.timerchina.nlp.segment.SplitWords;
import com.timerchina.nlp.stopword.StopWord;
import com.timerchina.nlp.word.WOD;

import java.util.*;

/**
 * TextRank 自动摘要
 *
 * @author hankcs
 */
public class TextRankSentence {
    static {
        //分词过滤代码初始化,请自行实现
        StopWord.init();
    }
    /**
     * 阻尼系数(DampingFactor),一般取值为0.85
     */
    final static double d = 0.85;
    /**
     * 最大迭代次数
     */
    final static int max_iter = 200;
    final static double min_diff = 0.001;
    /**
     * 文档句子的个数
     */
    int D;
    /**
     * 拆分为[句子[单词]]形式的文档
     */
    List<List<String>> docs;
    /**
     * 排序后的最终结果 score <-> index
     */
    TreeMap<Double, Integer> top;

    /**
     * 句子和其他句子的相关程度
     */
    double[][] weight;
    /**
     * 该句子和其他句子相关程度之和
     */
    double[] weight_sum;
    /**
     * 迭代之后收敛的权重
     */
    double[] vertex;

    /**
     * BM25相似度
     */
    BM25 bm25;

    public TextRankSentence(List<List<String>> docs) {
        this.docs = docs;
        bm25 = new BM25(docs);
        D = docs.size();
        weight = new double[D][D];
        weight_sum = new double[D];
        vertex = new double[D];
        top = new TreeMap<Double, Integer>(Collections.reverseOrder());
        solve();
    }

    private void solve() {
        int cnt = 0;
        for (List<String> sentence : docs) {
            double[] scores = bm25.simAll(sentence);
//            System.out.println(Arrays.toString(scores));
            weight[cnt] = scores;
            weight_sum[cnt] = sum(scores) - scores[cnt]; // 减掉自己,自己跟自己肯定最相似
            vertex[cnt] = 1.0;
            ++cnt;
        }
        for (int _ = 0; _ < max_iter; ++_) {
            double[] m = new double[D];
            double max_diff = 0;
            for (int i = 0; i < D; ++i) {
                m[i] = 1 - d;
                for (int j = 0; j < D; ++j) {
                    if (j == i || weight_sum[j] == 0) continue;
                    m[i] += (d * weight[j][i] / weight_sum[j] * vertex[j]);
                }
                double diff = Math.abs(m[i] - vertex[i]);
                if (diff > max_diff) {
                    max_diff = diff;
                }
            }
            vertex = m;
            if (max_diff <= min_diff) break;
        }
        // 我们来排个序吧
        for (int i = 0; i < D; ++i) {
            top.put(vertex[i], i);
        }
    }

    /**
     * 获取前几个关键句子
     *
     * @param size 要几个
     * @return 关键句子的下标
     */
    public int[] getTopSentence(int size) {
        Collection<Integer> values = top.values();
        size = Math.min(size, values.size());
        int[] indexArray = new int[size];
        Iterator<Integer> it = values.iterator();
        for (int i = 0; i < size; ++i) {
            indexArray[i] = it.next();
        }
        return indexArray;
    }

    /**
     * 简单的求和
     *
     * @param array
     * @return
     */
    private static double sum(double[] array) {
        double total = 0;
        for (double v : array) {
            total += v;
        }
        return total;
    }

    public static void main(String[] args) {

        String document = "算法可大致分为基本算法、数据结构的算法、数论算法、计算几何的算法、图的算法、动态规划以及数值分析、加密算法、排序算法、检索算法、随机化算法、并行算法、厄米变形模型、随机森林算法。\n" +
                "算法可以宽泛的分为三类,\n" +
                "一,有限的确定性算法,这类算法在有限的一段时间内终止。他们可能要花很长时间来执行指定的任务,但仍将在一定的时间内终止。这类算法得出的结果常取决于输入值。\n" +
                "二,有限的非确定算法,这类算法在有限的时间内终止。然而,对于一个(或一些)给定的数值,算法的结果并不是唯一的或确定的。\n" +
                "三,无限的算法,是那些由于没有定义终止定义条件,或定义的条件无法由输入的数据满足而不终止运行的算法。通常,无限算法的产生是由于未能确定的定义终止条件。";
        System.out.println(document.length());
        System.out.println(TextRankSentence.getTopSentenceList(document, 5));
        System.out.println(TextRankSentence.getSummary(document, 50));
    }

    /**
     * 将文章分割为句子
     *
     * @param document
     * @return
     */
    static List<String> spiltSentence(String document) {
        List<String> sentences = new ArrayList<String>();
        for (String line : document.split("[\r\n]")) {
            line = line.trim();
            if (line.length() == 0) continue;
            for (String sent : line.split("[,,。::??!!;;【】\\s]")) {
                sent = sent.trim();
                if (sent.length() == 0) continue;
                sentences.add(sent);
            }
        }

        return sentences;
    }

    /**
     * 将句子列表转化为文档
     *
     * @param sentenceList
     * @return
     */
    private static List<List<String>> convertSentenceListToDocument(List<String> sentenceList) {
        List<List<String>> docs = new ArrayList<List<String>>(sentenceList.size());
        for (String sentence : sentenceList) {
            List<Term> termList = ToAnalysis.parse(sentence).getTerms();
            StopWord.cleanerPAS(termList);//过滤标点符号,自行实现
            StopWord.filter(termList);//过滤停用词,自行实现

            List<String> wordList = new LinkedList<String>();
            for (Termterm : termList) {
                wordList.add(term.getName());
            }
            docs.add(wordList);
        }
        return docs;
    }

    /**
     * 一句话调用接口
     *
     * @param document 目标文档
     * @param size     需要的关键句的个数
     * @return 关键句列表
     */
    public static List<String> getTopSentenceList(String document, int size) {
        List<String> sentenceList = spiltSentence(document);
        List<List<String>> docs = convertSentenceListToDocument(sentenceList);
        TextRankSentence textRank = new TextRankSentence(docs);
        int[] topSentence = textRank.getTopSentence(size);
        List<String> resultList = new LinkedList<String>();
        for (int i : topSentence) {
            resultList.add(sentenceList.get(i));
        }
        return resultList;
    }

    /**
     * 一句话调用接口
     *
     * @param document   目标文档
     * @param max_length 需要摘要的长度
     * @return 摘要文本
     */
    public static String getSummary(String document, int max_length) {
        List<String> sentenceList = spiltSentence(document);

        int sentence_count = sentenceList.size();
        int document_length = document.length();
        int sentence_length_avg = document_length / sentence_count;
        int size = max_length / sentence_length_avg + 1;
        List<List<String>> docs = convertSentenceListToDocument(sentenceList);
        TextRankSentence textRank = new TextRankSentence(docs);
        int[] topSentence = textRank.getTopSentence(size);
        List<String> resultList = new LinkedList<String>();
        for (int i : topSentence) {
            resultList.add(sentenceList.get(i));
        }

        resultList = permutation(resultList, sentenceList);
        resultList = pick_sentences(resultList, max_length);
        return join("。", resultList);
    }

    public static List<String> permutation(List<String> resultList, List<String> sentenceList) {
        int index_buffer_x;
        int index_buffer_y;
        String sen_x;
        String sen_y;
        int length = resultList.size();
        // bubble sort derivative
        for (int i = 0; i < length; i++)
            for (int offset = 0; offset < length - i; offset++) {
                sen_x = resultList.get(i);
                sen_y = resultList.get(i + offset);
                index_buffer_x = sentenceList.indexOf(sen_x);
                index_buffer_y = sentenceList.indexOf(sen_y);
                // if the sentence order in sentenceList does not conform that is in resultList, reverse it
                if (index_buffer_x > index_buffer_y) {
                    resultList.set(i, sen_y);
                    resultList.set(i + offset, sen_x);
                }
            }

        return resultList;
    }

    public static List<String> pick_sentences(List<String> resultList, int max_length) {
        int length_counter = 0;
        int length_buffer;
        int length_jump;
        List<String> resultBuffer = new LinkedList<String>();
        for (int i = 0; i < resultList.size(); i++) {
            length_buffer = length_counter + resultList.get(i).length();
            if (length_buffer <= max_length) {
                resultBuffer.add(resultList.get(i));
                length_counter += resultList.get(i).length();
            } else if (i < (resultList.size() - 1)) {
                length_jump = length_counter + resultList.get(i + 1).length();
                if (length_jump <= max_length) {
                    resultBuffer.add(resultList.get(i + 1));
                    length_counter += resultList.get(i + 1).length();
                    i++;
                }
            }
        }
        return resultBuffer;
    }

    public static String join(String delimiter, Collection<String> stringCollection) {
        StringBuilder sb = new StringBuilder(stringCollection.size() * (16 + delimiter.length()));
        for (String str : stringCollection) {
            sb.append(str).append(delimiter);
        }
        return sb.toString();
    }
}

 运行结果:

[这类算法在有限的时间内终止, 这类算法在有限的一段时间内终止, 是那些由于没有定义终止定义条件]
这类算法在有限的一段时间内终止。这类算法在有限的时间内终止。是那些由于没有定义终止定义条件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1967222.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最好用的复制粘贴软件pastemate功能简介

这应当是windows下最好用的复制粘贴软件&#xff0c;遥遥领先的复制粘贴软件。 效增PasteMate - 下载页面 windows下界面最优美&#xff0c;操作最方便的复制粘贴神器&#xff0c;学生党论文必备&#xff0c;效率神器 pastemate 1.搜索功能&#xff0c;能够按文本、图片、文件…

C# 构建观测者模式(或者为订阅者模型)

前言&#xff1a; 观测者模型的基本理念&#xff0c;就是&#xff0c;我有一个公共的事件&#xff0c;定义好他的事件的触发、数据接口。然后&#xff0c;通过增加订阅者&#xff08;实例&#xff09;来订阅这个事件的&#xff0c;或者说观察这个事件。如果事件发生&#xff0…

软件测试的挑战和压力

软件测试过程中可能会遇到很多挑战&#xff0c;比如&#xff1a; 1. 需求不明确或不稳定。如果需求文档不完整、不清晰或不一致&#xff0c;或者需求在开发过程中频繁变更&#xff0c;那么测试人员就很难设计和执行有效的测试用例&#xff0c;也很难判断测试结果是否符合预期。…

5年经验的软件测试人员,碰到这样的面试题居然会心虚......

我们这边最近的面试机会比较多&#xff0c;但是根据他们的反馈&#xff0c;结束后大部分都没音信了&#xff0c;因为现在企业面试问的非常多&#xff0c;范围非常广&#xff0c;而且开放性的问题很多&#xff0c;很多人即便面试前刷了成百上千道面试题&#xff0c;也很难碰到一…

C语言——指针数组

文章目录 &#x1f34a;自我介绍&#x1f34a;前言&#x1f34a;含义&#x1f34a;输出指针数组中的值&#x1f34a;指针数组工程的用法&#xff08;模拟linux的内核代码&#xff09; 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&…

实习中学到的一点计算机知识(MP4在企业微信打不开?)

我在实习中&#xff0c;常有同事向我反馈说我在微信发的视频格式打不开。这就导致我还要一帧帧的盯着某一个时刻来截图&#xff0c;今天查了一下资料尝试修改视频后缀来解决视频的播放问题。 在网上下载mp4的格式&#xff0c;在本地都能播放&#xff0c;怎么可能发上企业微信就…

使用CLI脚手架搭建Vue2项目

一、配置前端的环境 1、下载安装Node.js 网址&#xff1a;Node.js 中文网 (nodejs.com.cn) 参考&#xff1a;【简明图文教程】Node.js的下载、安装、环境配置及测试_node下载安装-CSDN博客 推荐安装路径C盘改为D盘 2、配置nodejs及环境变量【安装的时候勾选Add to PATH就不…

[算法]归并排序(C语言实现)

一、归并排序的定义 归并排序&#xff08;Merge sort&#xff09;是建立在归并操作上的一种有效的排序算法。该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。 二、归并排序的算法原理 归并排序的算法可以用递归法和非递归法来实现…

2024新版python安装教程【附图片】

Python的安装步骤因操作系统而异&#xff0c;但大致可以分为下载、安装和验证三个主要步骤。以下是Windows系统中Python的详细安装步骤&#xff1a; Windows系统 下载Python安装包 访问Python官网&#xff08;https://www.python.org/&#xff09;。点击页面头部的“Download…

【优秀python django系统案例】基于python的医院挂号管理系统,角色包括医生、患者、管理员三种

随着信息技术的迅猛发展&#xff0c;传统的医院挂号管理方式面临着效率低下、排队时间长、信息不对称等诸多问题。这些问题不仅影响患者的就医体验&#xff0c;也加重了医院工作人员的负担。在此背景下&#xff0c;基于Python的医院挂号管理系统应运而生。该系统旨在通过信息化…

OZON饰品产品什么好卖,OZON热销饰品有哪些

在OZON平台上&#xff0c;饰品产品的销售情况受多种因素影响&#xff0c;包括市场需求、季节变化、消费者偏好以及流行趋势等。以下是一些可能热销的OZON饰品产品类别及具体推荐&#xff1a; OZON热销饰品地址&#xff1a;D。DDqbt。COm/74rDTop1 发带套装 Утика Ком…

Idea常用快捷键:设置自动导包、格式化、抽取方法

Idea设置自动导包 【File】→【Setting】(或使用快捷键【Crlt Shift S】)打开Setting设置。点击【Editor】→【General】→【Auto Import】。勾选自定导包的选项&#xff0c;并确定&#xff0c;如下&#xff1a; Addunambiguousimportsonthefly&#xff1a;添加明确的导入 …

CSP-J 2022基础知识答案与解析

1.以下哪种功能没有涉及 C语言的面向对象特性支持&#xff1a;&#xff08; &#xff09;。 (2 分) A.C中调用 printf 函数 B.C中调用用户定义的类成员函数 C.C中构造一个 class 或 struct D.C中构造来源于同一基类的多个派生类 解析&#xff1a;printf是继承自C的&#…

vue2学习 -- 核心语法(二)

文章目录 1. 绑定样式1.1 绑定class1.2 绑定style 2. 渲染2.1 条件渲染2.2 列表渲染key的作用 3. 监视数据3.1 vue监视数据的原理_对象3.2 vue监视数据的原理_数组 4. 收集表单数据5. 过滤器6. 指令6.1 内置指令6.2 自定义指令 7. 生命周期 1. 绑定样式 1.1 绑定class 三种写…

基于LK光流提取算法的图像序列晃动程度计算matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 光流的概念 4.2 基于LK光流算法的图像序列晃动程度计算 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &…

前方高能!2024 MongoDB中国用户大会正式官宣!

前方重磅&#xff01; 2024 MongoDB 中国用户大会 正式官宣&#xff01; 聆听最新产品发布与主题演讲 get行业头部客户案例的精彩分享 与各届优秀同行们现场交流⼼得 与资深MongoDB⼯程师面对面答疑解惑 ⼀起把MongoDB的酷炫周边收⼊囊中&#xff01; …… 欢迎所有Mong…

告别单一渠道,全渠道整合营销让企业营销力MAX!

现阶段&#xff0c;企业纷纷积极寻求新的道路&#xff0c;以拓展国际市场、增强品牌影响力。今天咱们来聊聊全渠道营销那点事儿&#xff0c;特别是对那些想出海闯荡一番的中国企业来说&#xff0c;这可是个必备神器&#xff01;NetFarmer专门企业搞定数字化出海&#xff0c;现在…

室内养猫空气净化器哪个好?真实室内养猫空气净化器使用评价

作为一个养猫多年的猫奴&#xff0c;家里有两只可爱的小猫咪&#xff1a;小白和小花。虽然相处起来很开心&#xff0c;但也给生活带来了一些小麻烦。感受一下40度高温的养猫人&#xff0c;给掉毛怪疏毛浮毛飘飘&#xff0c;逃不过的饮水机&#xff0c;各个角落&#xff0c;多猫…

七夕节高逼格表白方式,送给你的那个TA(可写字版)

别人都有爱心代码了&#xff0c;咱们开发者们也必须有。今天给大家分享个用 Python 写的爱心代码项目&#xff0c;如果感兴趣或者想给自己的另一半制造小浪漫的同学可以自己上手试一试。 运行结果 ** 温馨提示&#xff1a;篇幅有限&#xff0c;源码已打包文件夹&#xff0c;获…

ECRS工时分析软件:工业工程精益生产的智慧引擎

在工业工程学的广阔领域中&#xff0c;程序分析一直扮演着至关重要的角色。其中&#xff0c;ECRS四大原则——取消、合并、重排、简化&#xff0c;作为程序分析的核心&#xff0c;旨在通过优化生产过程&#xff0c;实现成本的节省和精益生产的目标。如今&#xff0c;随着科技的…