HanLP 关键词提取。入门篇

前段时间,领导要求出一个关键字提取的微服务,要求轻量级。

对于没写过微服务的一个小白来讲。硬着头皮上也不能说不会啊。

首先了解下公司目前的架构体系,发现并不是分布式开发,只能算是分模块部署。然后我需要写个Boot的服务,对外提供一个接口就行。

在上网浏览了下分词概念后,然后我选择了Gradle & HanLP & SpringBoot & JDK1.8 & tomcat8 & IDEA工具来实现。

Gradle 我也是第一次听说,和Maven一样,可以很快捷的管理项目需要的jar。下载,解压,配置环境变量,验证等。不再赘述,可以去这里了解下https://www.w3cschool.cn/gradle/ctgm1htw.html

然后准备就绪后,在idea里配置一下Gradle路径

HanLP呢,老规矩,先下载,解压,https://github.com/hankcs/HanLP/releases 。简单看下目录结构

HanLP分为词典 和模型,其中词典(dictionary)是词法分析必备,模型(model)是句法分析必需。解压好准备data的上级目录 的绝对路径  下面会提到用途

这里为G:/kaipu/data-for-1.7.3

tomcat8 去官网自行下载,选择自己操作系统对应的。 jdk1.8 下载安装,环境变量配置不再描述。

一切准备就绪,开始创建项目

输入项目ID:keyWord,NEXT

选择本地的gradle

Next ,Finish

此刻项目就创建好了。

打开根目录下的

dependencies {
    compile ‘org.springframework.boot:spring-boot:2.0.5.RELEASE‘
    compile ‘org.springframework.boot:spring-boot-starter-web:2.0.5.RELEASE‘
    providedRuntime ‘org.springframework.boot:spring-boot-starter-tomcat:2.0.5.RELEASE‘
    testCompile group: ‘junit‘, name: ‘junit‘, version: ‘4.11‘
    compile ‘com.hankcs:hanlp:portable-1.7.3‘}

 前三个jar 是集成springboot外部tomcat用的,第四个是junit单元测试依赖,第五个就是我们要用的hanlp依赖。

PS:这里我着重说下打包的事情,因为我没用过Gradle打包,项目时间紧,我就延用了war包格式,这里先记录下过程,这个项目后,再回头来研究Gradle打JAR包。

group ‘keyword‘
version ‘1.0-SNAPSHOT‘
apply plugin: ‘java‘
apply plugin: ‘war‘
war {
    archiveName ‘key-word.war‘
}

等待idea自动导包完成后,我们来加载hanlp

在resources下,出现一个hanlp.properties,打开编辑这个文件,更改root路径。这个路径就是上面我们提到的 data上级目录的绝对路径。

创建第一个测试类

  @Test
    public void test0(){
        String text = "中国是世界上的经济大国,社会主义强国!";      List<Term> keyWords = HanLP.segment(text);      System.out.println(keyWords.toString());   }

说明词库引入成功。

接下来可以正常开发,按照需求,需要提取正文里的关键词,摘要。

分析如下:

关键词抽取工具:

思路:输入标题,正文文本,综合考虑词频、位置、词性、组合性短语长度等因素,计算权重得分;返回topN个关键词。在此基础上进行抽取式摘要,按句子包含的关键词数量和权重进行处理。

原理:分词后,将命名实体单独拿到,再找合适的名词短语。依托HanLP的核心词典,根据TF*IDF算法计算每个命名实体和名词性短语的得分score,按score倒排返回前面若干关键词。

首先定义一个关键词类。可以是个单词,也可以是几个单词组成的短语。

public class Phrase implements Comparable<Phrase>{
    private String word;           //候选关键词
    private boolean inDictionary;  //是否在HanLP词典中
    private String suffixWord;     //中心词。对单词,中心词就是word。如果是短语,则是短语中最后一词
    private String suffixWordPos;  //中心词的词性
    private String prefixWordPos;  //首词的词性
    private int freqOfDict = 1;    //在词典中该词的词频
    private boolean single;        //true表示单词,false表示短语
    private Location location;     //该候选关键词出现的位置
    private int offset;            //该候选词在正文中位置
    private boolean isCandidate;   //是否候选关键词

    *
    *
    *
   /**  *这会在比较score时用到  */
    @Override
    public int compareTo(Phrase o) {
        return this.getWord().compareTo(o.getWord());
    }
}

  以下是核心算法的一部分,寻找候选关键词

/**全局逻辑
先找出关键词,再计算分数
一、
     * 从分词Term中解析 候选的关键词,
     * 因为要计算每个句子的分数值需要句子里的所有词累加计算score,所以这里都需要打标分数。
     * @param terms              分词列表
     * @param title              文章标题
     * @param firstParagraphEnd  对于正文处理,表示正文第一段结尾位置
     * @param lastParagraphBegin 对于正文处理,表示正文最后一段开始位置
     */
int index= 0;
while (index < terms.size()) {    //当前词    Term current = terms.get(index);    //首先判断是否是命名实体 人名开头 包含 地名 动名词 其他专名,或者团体名开头
if (current.nature.startsWith("nr")
                    || current.nature.equals(Nature.ns)
                    || current.nature.equals(Nature.nz)
                    || current.nature.equals(Nature.vn)
                    || current.nature.startsWith("nt")) {

                Phrase phrase = new Phrase(current.word,
                        CoreDictionary.contains(current.word),
                        current.word,
                        current.nature.toString(),
                        current.nature.toString(),
                        CoreDictionary.get(current.word) == null ? 3 : CoreDictionary.get(current.word).totalFrequency,
                        true,
                        title.contains(current.word) ? Location.TITLE : whichLocation(terms.get(index).offset, firstParagraphEnd, lastParagraphBegin),
                        terms.get(index).offset, true);
                phrases.add(phrase);
                index++;
            } 二、
//计算候选关键词的权重TreeMap<Phrase, Double> scoreMap = new TreeMap<>();for (Phrase phrase : phrases) {    double score = scoreMap.containsKey(phrase) ? scoreMap.get(phrase) : 0.0;

    //排除单字符 的关键字    int wordLength = phrase.getWord().toString().length();    if (!(wordLength == 1)) {        score += Math.log(weight(phrase, content, title) * freqMap.get(phrase.getWord()) / phrase.getFreqOfDict());        scoreMap.put(phrase, score);    }    //weight方法:根据词性、词频、位置、词语长度等因素计算权重    //这里依赖HanLp核心词典实现TF*IDF算法}三、

倒排
Comparator<Map.Entry<Phrase, Double>> valueComparator = new Comparator<Map.Entry<Phrase, Double>>() {    @Override    public int compare(Map.Entry<Phrase, Double> o1,                       Map.Entry<Phrase, Double> o2) {        return (o2.getValue().compareTo(o1.getValue()));    }};List<Map.Entry<Phrase, Double>> list = new ArrayList<Map.Entry<Phrase, Double>>(scoreMap.entrySet());Collections.sort(list, valueComparator);

此刻就可利用subList函数 取出tonN的关键词了
subList(0, Math.min(topN, 候选关键词数组size()));

四、在三的基础上,对正文按标点符号 。?!;.!? 等进行分句。
List<Sentence> sentences = new ArrayList<>();int i = 0;int lastSentenceEnd = 0;while (i <= content.length() - 1) {    char c = content.charAt(i);    if (SENTENCE_END_TAGS.indexOf(c) >= 0            && i > lastSentenceEnd + 1            && i > 0            && i < content.length()) {        Location location = Location.MIDDLE;        if (i < firstParagraphEnd) {            location = Location.FIRST;        } else if (i > lastParagraphBegin) {            location = Location.LAST;        }        int begin = lastSentenceEnd + 1;        if (sentences.isEmpty()) begin = 0; // 对第一句,应该从0开始        Sentence sentence = new Sentence(begin, i, location);        sentences.add(sentence);        lastSentenceEnd = i;    }    i++;}
五,分句后,对每句含有的词,在三的基础上,进行分数累加。(有个小逻辑:句子长度对分数比例的影响 || 单个句子包含多个命名实体 人名等对分数比例的影响。|| 。。。)这些需要大量的场景测for (Sentence sentence : sentences) {
    String line = content.substring(sentence.getBegin(), sentence.getEnd() + 1);    double sumScore = wordsWeight.get(wordsWeight.size() - 1).getValue();    //注意:要用关键词中最小权重做为基数,避免句子权重计算结果为0    int maxSize = Math.min(topN, wordsWeight.size());    for (int i = 0; i < maxSize; i++) {        Map.Entry<Phrase, Double> weight = wordsWeight.get(i);        double weightDouble = weight.getValue();        if (line != null && line.indexOf(weight.getKey().getWord()) >= 0){            List<Term> terms = NLPTokenizer.segment(line);            int index =0;int termCount = 0;       sumScore += weightDouble;}      }    sentence.setScore(sumScore);
然后同理,根据subList()取出想要的几个句子。再根据句子所在正文的位置,进行一个先后顺序的排列。
Comparator<Sentence> valueComparator = new Comparator<Sentence>() {

    @Override    public int compare(Sentence o1, Sentence o2) {        return o1.getBegin() - o2.getBegin();    }};Collections.sort(prefixSentences, valueComparator);StringBuilder sb = new StringBuilder();for (Sentence sentence : prefixSentences) {    sb.append(SentenceTool.toString(sentence, content));}

  

还有一些可增删的业务逻辑:
去除文章末尾误识别的编辑、记者名称等
判断关键词里的 发言人;|| *** 说:“”。这些权重适当降低

保留句子里的实词词性,去除虚词词性(权重降低),来保证摘要的理性。

等等等等。

调试期间,有很多坑,现在记录下来,以便以后复习查看。

以上内容纯属个人所有,转载请注明出处。

原文地址:https://www.cnblogs.com/justtodo/p/11136700.html

时间: 2024-10-28 23:00:12

HanLP 关键词提取。入门篇的相关文章

C# 10分钟完成百度图片提取文字(文字识别)——入门篇

现在图片文字识别已经很成熟了,比如qq长按图片,点击图片识别就可以识别图片的文字,将不认识的.文字数量大的.或者不能赋值的值进行二次可复制功能. 我们现在就基于百度Ai开放平台进行个人文字识别,demo使用的是C#控制台应用程序,后续有需要的可以嫁接到指定项目中使用,比如提供选择图片,点击识别, 获取返回的值.废话不多说,上干货: 总体为: 注册百度账号api,创建自己的应用: 创建vs控制台应用程序,引入动态链接库: 编写代码调试,效果图查看: 总结. 1.创建百度AI文字识别应用   在百度

新注册第一帖----------------------乱码新手自学.net 之Linq 入门篇

作为一个业余开发,断断续续学.net/c#也有不少日子了, 学习过程中,不断忘了学,学了忘,这让我很苦恼. 以前学习过程中,我总是在笔记本中记录下来知识要点,这么久下来,笔记本都写了四五本了. 然而,随着笔记本的增多,自己很快发现,笔记写了跟没写一样:笔记多了就找不到了-- 所以,我觉得还是上博客园写博客,记录自己每天的学习心得. 如果有什么错误的地方,欢迎大神指教,小弟在这给大神跪谢了 ======================================================

Linux及Arm-Linux程序开发笔记(零基础入门篇)

Linux及Arm-Linux程序开发笔记(零基础入门篇)  作者:一点一滴的Beer http://beer.cnblogs.com/ 本文地址:http://www.cnblogs.com/beer/archive/2011/05/05/2037449.html 目录 一.Arm-Linux程序开发平台简要介绍... 3 1.1程序开发所需系统及开发语言... 3 1.2系统平台搭建方式... 4 二.Linux开发平台搭建... 5 2.1安装虚拟工作站... 5 2.2安装Linux虚拟

PHP学习笔记——入门篇(1)——语法&变量

基础 PHP 语法 PHP 脚本可放置于文档中的任何位置. PHP 脚本以 <?php 开头,以 ?> 结尾: PHP 文件通常包含 HTML 标签以及一些 PHP 脚本代码. 注释:PHP 语句以分号结尾(;).PHP 代码块的关闭标签也会自动表明分号(因此在 PHP 代码块的最后一行不必使用分号). PHP 支持三种注释: //单行注释 #单行注释 /*多行注释*/ PHP 大小写敏感区分: 在 PHP 中,所有用户定义的函数.类和关键词(例如 if.else.echo 等等)都对大小写不

【Android的从零单排开发日记】之入门篇(三)——Android目录结构

本来的话,这一章想要介绍的是Android的系统架构,毕竟有了这些知识的储备,再去看实际的项目时才会更清楚地理解为什么要这样设计,同时在开发中遇到难题,也可以凭借着对Android的了解,尽快找出哪些模块和设计能够帮助解决该问题.但想了一下,这毕竟是入门篇,若没有实际项目开发经验的人看了之后肯定是一头雾水,所以就决定将其搁浅到大家熟悉Android之后再为大家介绍. 那么今天的主题是Android的目录结构,将系统架构比作人的骨骼架构的话,目录结构就像是人的各个器官,彼此功能各不相同,却能有序地

Swift入门篇-swift简介

潜水博客园很多年,闲来无事,聊一下自己的经历,语文不好(如有什么错别字,请您在下评论)望您谅解,没有上过什么学的 在前期 ios入门篇 -hello Word(1) 文章中介绍我这半年准备写一些ios文章,一开始是准备写一些object-c方面的,但是最近苹果推出最近一门新的编程语言Swift.我也决定重新写一下Swift的方面的知识和苹果保持同步. swift是一门什么语言了? swift语言是苹果2014年6月3日正式推出一门新的的语言.swift语言主要能够开发 ios 和mac相关的ap

【Android的从零单排开发日记】之入门篇(八)——Android数据存储(下)

废话不多说了,紧接着来讲数据库的操作吧.Come On! 提到数据存储问题,数据库是不得不提的.数据库是用来存储关系型数据的不二利器.Android为开发者提供了强大的数据库支持,可以用来轻松地构造基于数据库的应用.Android的数据库应用,依托于当下最流行的开源嵌入式数据库SQLite.在Android中,应用的数据库文件是该应用私有的,存储在应用数据目录下的databases子目录内.从代码结构来看,Android的数据库实现可以分成两个层次,在底层通过C++调用SQLite的接口来执行S

关于最近研究的关键词提取keyword extraction做的笔记

来源:http://blog.csdn.net/caohao2008/article/details/3144639 之前内容的整理 要求:第一: 首先找出具有proposal性质的paper,归纳出经典的方法有哪些. 第二:我们如果想用的话,哪种更实用或者易于实现? 哪种在研究上更有意义. 第一,      较好较全面地介绍keyword extraction的经典特征的文章<Finding Advertising Keywords on Web Pages>. 基于概念的keywords提

基于高维聚类技术的中文关键词提取算法

[摘要]关键词提取是中文信息处理技术的热点和难点,基于统计信息的方法是其中一个重要分支.本文针对基于统计信息关键词提取方法准确率低的问题,提出基于高维聚类技术的中文关键词提取算法.算法通过依据小词典的快速分词.二次分词.高维聚类及关键词甄选四个步骤实现关键词的提取.理论分析和实验显示,基于高维聚类技术的中文关键词提取方法具备更好的稳定性.更高的效率及更准确的结果. 引言  关键词提取是通过对一篇输入文章做内容分析,按一定比例或字数要求提取出重要且语义相似性凝聚的关键词的过程.关键词自动提取是文本