标题由于进行数据抽取时,必须顺便清洗数据,所以实现一个简单的抽取式摘要生成算法,来进行摘要的生成,这里我用java实现了TextRank算法。

TextRank算法思想

与PageRank一样,textrank算法给每一个句子一个权重,然后根据一个句子与其他句子的相似程度,将自己的权重按相似程度分配给其他句子,为了避免某一个句子的权重变为0,则需要加一个调和参数,进行平滑。
具体的公式为:

等式左边表示一个句子的权重(WS是weight_sum的缩写),右侧的求和表示每个相邻句子对本句子的贡献程度。与提取关键字的时候不同,一般认为全部句子都是相邻的,不再提取窗口。

求和的分母wji表示两个句子的相似程度,分母又是一个weight_sum,而WS(Vj)代表上次迭代j的权重。整个公式是一个迭代的过程。

相似程度的计算BM25算法

BM25算法常用来做搜索相关性的评分,对Query进行语素解析,生成语素qi;然后,对于每个搜索结果D,计算每个语素qi与D的相关性得分,最后,将qi相 对于D的相关性得分进行加权求和,从而得到Query与D的相关性得分。

其中,Q表示Query,qi表示Q解析之后的一个语素(对中文而言,我们可以把对Query的分词作为语素分析,每个词看成语素qi。);d表示一个搜索结果文档;Wi表示语素qi的权重; R(qi,d)表示语素qi与文档d的相关性得分。
下面用IDF来定义权重:

其中,N为索引中的全部文档数,n(qi)为包含了qi的文档数
根据IDF的定义可以看出,对于给定的文档集合,包含了qi的文档数越多,qi的权重则越低。也就是说,当很多文档都包含了qi时,qi的区分度就不高,因此使用qi来判断相关性时的重要度就较低。


通过计算即可得到一句查询与一个文本的相似程度

BM25算法的实现

import java.util.List;
import java.util.Map;
import java.util.TreeMap;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;//index表示现在是文档中的第几句话for (List<String> sentence : docs){//对于每个句子的分词结果  计算 分词在这个句子中出现的频率 为tf   存成String int的映射形式Map<String, Integer> tf = new TreeMap<String, Integer>();for (String word : sentence){//计算每个值出现的频数  作为tfInteger freq = tf.get(word);freq = (freq == null ? 0 : freq) + 1;tf.put(word, freq);}f[index] = tf;//存储每句话对应的tf值Map//根据tf值算df值  计算每个词出现在几个句子中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;}//根据df计算idf   公司为log(D - freq + 0.5) - Math.log(freq + 0.5)  D为文档中句子个数  0.5为平滑项for (Map.Entry<String, Integer> entry : df.entrySet()){//计算逆文档频率 idfString word = entry.getKey();Integer freq = entry.getValue();idf.put(word, Math.log(D - freq + 0.5) - Math.log(freq + 0.5));}}/*** 计算相似度 最终得到一个句子 与对应index句子的相关性得分* @param sentence* @param index* @return*/public double sim(List<String> sentence, int index){double score = 0;//对于一句话中的每一个单词  计算这个单词  与其他句子的相关性得分 这个得分用BM25计算出for (String word : sentence){if (!f[index].containsKey(word)) {continue;}int d = docs.get(index).size();//index对应句子的词的个数Integer wf = f[index].get(word);//在index对应句子中 词word出现的次数//,参数b的作用是调整文档长度对相关性影响的大小。b越大,文档长度的对相关性得分的影响越大,反之越小。而文档的相对长度越长,K值将越大,则相关性得//分会越小。这可以理解为,当文档较长时,包含qi的机会越大,因此,同等fi的情况下,长文档与qi的相关性应该比短文档与qi的相关性弱。score += (idf.get(word) * wf * (k1 + 1)/ (wf + k1 * (1 - b + b * d/ avgdl)));}//最终得到一个句子  与对应index句子的相关性得分return score;}/*** 计算整体的相似度  计算每一个句子与其他所有句子的相似度* @param sentence* @return*/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;}
}

(1)我们将一段文字,首先通过标点切分成句子,然后将句子分词,就可以得到[句子[单词]]形式的文档docs,
然后对于句子中的每一个次计算tf值和idf值(tf值为某个分词在某句话中出现的频率 df为每个词出现在几个句子中 idf为df计算得出的逆文档频率)
(2)我们计算两句话之间的相似程度,得到两个句子的相关性得分,使用BM25算法。
(3)最终我们计算一个句子与其他所有句子的相似程度。

实现TextRank算法

import com.hankcs.hanlp.HanLP;
import com.hankcs.hanlp.dictionary.stopword.CoreStopWordDictionary;
import com.hankcs.hanlp.seg.common.Term;import java.util.*;/*** TextRank 自动摘要*/
public class TextRankSummary {/*** 阻尼系数,一般取值为0.85*/final double d = 0.85f;/*** 最大迭代次数*/final int maxIter = 200;final double min_diff = 0.001f;/*** 文档句子的个数*/int docSentenceCount;/*** 拆分为[句子[单词]]形式的文档*/List<List<String>> docs;/*** 排序后的最终结果 score <-> index* 使用treemap 因为treemap自动按key排序*/TreeMap<Double, Integer> top;/*** 句子和其他句子的相关程度*/double[][] weight;/*** 该句子和其他句子相关程度之和*/double[] weightSum;/*** 迭代之后收敛的权重*/double[] vertex;/*** BM25相似度*/BM25 bm25;public TextRankSummary(List<List<String>> docs){this.docs = docs;bm25 = new BM25(docs);docSentenceCount = docs.size();// 句子和其他句子的相关程度weight = new double[docSentenceCount][docSentenceCount];weightSum = new double[docSentenceCount];//该句子和其他句子相关程度之和vertex = new double[docSentenceCount];//选出排名靠前的句子top = new TreeMap<Double, Integer>(Collections.reverseOrder());solve();}/*** 构造矩阵计算最相似的内容*/private void solve(){int cnt = 0;//对于文档中的每一句话 都要计算他与其他每一句话的相似程度  以及他与其他所有句子的总相似程度for (List<String> sentence : docs){//对于文档中的每一个句子 计算它与文档中其他所有句子的相似程度double[] scores = bm25.simAll(sentence);//将计算结果存储在矩阵中weight[cnt] = scores;// 减掉自己,自己跟自己肯定最相似weightSum[cnt] = sum(scores) - scores[cnt];//所有句子权重初始化都为1vertex[cnt] = 1.0;++cnt;}//开始使用textrank算法进行迭代 200轮for (int iter = 0; iter < maxIter; ++iter){double[] m = new double[docSentenceCount];double maxDiff = 0;for (int i = 0; i < docSentenceCount; ++i){m[i] = 1 - d;for (int j = 0; j < docSentenceCount; ++j){if (j == i || weightSum[j] == 0) {continue;}m[i] += (d * weight[j][i] / weightSum[j] * vertex[j]);}double diff = Math.abs(m[i] - vertex[i]);if (diff > maxDiff){maxDiff = diff;}}vertex = m;if (maxDiff <= min_diff) {break;}}// 然后进行排序 输出权重最大的那个for (int i = 0; i < docSentenceCount; ++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;}/*** 将文章分割为句子  分割依据为标点符号* @param document* @return*/static List<String> spiltSentence(String document){List<String> sentences = new ArrayList<String>();if (document == null) {return sentences;}String regex1 = "[\r\n]";String regex2 = "[,,。::“”??!!;;]";for (String line : document.split(regex1)) {line = line.trim();if (line.length() == 0) {continue;}for (String sent : line.split(regex2)){sent = sent.trim();if (sent.length() == 0) {continue;}sentences.add(sent);}}return sentences;}/*** 是否应当将这个term纳入计算,词性属于名词、动词、副词、形容词* @param term* @return 是否应当*/public static boolean shouldInclude(Term term) {return CoreStopWordDictionary.shouldInclude(term);}/*** 一句话调用接口* @param document 目标文档* @param size 需要的关键句的个数* @return 关键句列表*/public static String getTopSentenceList(String document, int size) {List<String> sentenceList = spiltSentence(document);//存储句子分词后的结果List<List<String>> docs = new ArrayList<List<String>>();for (String sentence : sentenceList) {//利用HanLP的接口将句子进行分词List<Term> termList = HanLP.segment(sentence);List<String> wordList = new LinkedList<String>();//是否应当将这个term纳入计算,词性属于名词、动词、副词、形容词for (Term term : termList) {if (shouldInclude(term)) {wordList.add(term.word);}}docs.add(wordList);}TextRankSummary textRankSummary = new TextRankSummary(docs);//然后获取排名最高的几个句子int[] topSentence = textRankSummary.getTopSentence(size);List<String> resultList = new LinkedList<String>();for (int i : topSentence) {resultList.add(sentenceList.get(i));}String result = String.join("-",resultList);return result;}}

(1)首先,输入目标文本和需要生成的摘要个数,然后我们现将文本,通过标点符号切分成句子,然后对每句话进行分词,这里分词使用了HanLP库(https://github.com/hankcs/hanlp),最后得到文档docs。
(2)然后通过docs使用BM25算法,计算每句话相互之间的文本相似度
(3)有了每句话的相似度之后,就可以给每个句子一个初始的权重,使用textrank算法,结合句子之间的相似度进行迭代,迭代200轮之后,停止迭代,输出权重最高的句子作为这篇文本的摘要。
(与pagerank算法思想相同,将自己的权重通过文本相似度的不同按比例分给不同的其他文本,自己也获得其他文本传来的权重,然后逐渐收敛)

最终的效果为



他只是抽取了几句,在文本中权重比较大的句子,语义之间没有连贯性,效果很不好。

明天准备尝试以下深度学习的方法来进行摘要的生成。

创新实训(6)——有关博客的摘要抽取的算法实现(TextRank)相关推荐

  1. Spring boot实训开发个人博客(二)详情页

    Spring boot实训开发个人博客(二)详情页 1.在index页面添加归档: 2.开始写详情页: 1.头部文件: 2.添加文章内容 <h2 class="ui center al ...

  2. Python实训-15天-博客汇总表

    目录 1.课程安排 1.1.课程介绍 1.2.课程目标 2.课件 2.1.课件and录屏 2.2.练习and案例and作业 3.实训博客笔记 第1周-week1 day01 day02 day03 d ...

  3. Spring Boot实训开发个人博客13 -博客详情

    文章目录 一.博客详情页面 二.修改IndexController 三.在Blog.html页面添加获取数据 四.页面查看 五.Markdown 转换 HTML (一)添加依赖 (二)编写工具类 (三 ...

  4. 创新实训(2)-Scrapy 学习

    创新实训(2)-Scrapy 学习 参考资料:Scrapy 0.25 文档 1.Scrapy简介 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 可以应用在包括数据挖掘,信息处理 ...

  5. 创新实训个人记录 : 个人工作总结

    创新实训个人记录 : 个人工作总结 分条目.分进度总结个人工作 阅读书籍(6.8-6.18) 近似算法设计(6.19-6.27) 程序验证(6.28-7.1) 工作难点 接触近似算法领域,学习新知识 ...

  6. 创新实训个人记录:approximation factor, maximum matchingvertex cover

    创新实训个人记录:approximation factor, maximum matching&&vertex cover approximation factor(近似比) maxi ...

  7. 创新实训个人记录:P versus NP

    创新实训个人记录:P versus NP computation&&computable&& computational efficiency 一些符号 decision ...

  8. 创新实训个人记录:metric k-center

    创新实训个人记录:metric k-center 一些概念 k-center(k-中心) dominating set(支配集) independent set(独立集) 独立集&&支 ...

  9. 创新实训团队记录:为BR-MTC问题设计一个近似算法

    创新实训团队记录 : 为BR-MTC问题设计近似算法 阅读书籍和论文 近似算法设计思路变化总结 算法框架 改变初始顶点集 继续添加路径,作为新的初始顶点集 程序验证 近似解与最优解存在差距&& ...

  10. 山东大学创新实训---前端原型设计以及环境配置

    近期创新实训项目进展及技术上遇到问题的碎碎念. ps: 参考材料都列出原网址,如版权等问题欢迎私聊.AI专业,目标算法工程师.前端0基础,都是参考B站直接上手,无奈课业繁重,'逼'算法为前端.十分欢迎 ...

最新文章

  1. GROMACS运行参数之npt.mdp文件详解
  2. windows 2008 enterprise tcpip.sys问题总结。
  3. 不同测试阶段,不同测试类型的区别于联系
  4. h5常见问题汇总及解决方案
  5. python二维列表写入excel_用Python实现合并excel列表
  6. 【转】ABP源码分析三十四:ABP.Web.Mvc
  7. 线段树 洛谷 p1531 I hate it(I hate it too)
  8. 关于DPM(Deformable Part Model)算法中模型结构的解释
  9. Bootstrap3 弹出提示插件的选项
  10. CentOS6.9+Hadoop2.7.3+Hive1.2.1+Hbase1.3.1+Spark2.1.1
  11. linux nano vim,修改ubuntu默认Nano编辑器为vim
  12. linux安装Vim-plug和配置.vimrc文件
  13. QQdengluqi, wangluorenzheng
  14. 【修真院java小课堂】Spring中的IOC是什么意思,为什么要用IOC而不是New来创建实例?
  15. HTMl账号密码登录跳转下一个页面问题/网页输入正确的账号面膜登录下一个界面/PHP网页禁止某个链接直链访问
  16. FastCGI原理与应用[转]
  17. arch yaourt安装
  18. [POI2005] SZA-Template
  19. 微擎系统内置的所有函数大全,一共5435个,可以当作微擎开发函数手册来查看(下篇)
  20. leetcode 1074. Number of Submatrices That Sum to Target(和为target的子矩阵个数)

热门文章

  1. oracle中imp命令详解
  2. linux的文本操作模式下的注销命令,linux基本命令大全
  3. FastDFS Destination image dimensions must not be less than 0 pixels
  4. STP配置 HSRP配置 端口追踪
  5. 微信公众号自定义菜单修改
  6. Mybatis Plus 3.1.1 lambda 表达式查询时异常 cannot find the corresponding database column name!
  7. 1.3 PyCharm下载
  8. 2014中国互联网安全大会
  9. cesium粒子特效
  10. 每天学命令get_propertysi_attacker Properties