HanLP 自然语言处理使用总结

Posted 小毕超

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HanLP 自然语言处理使用总结相关的知识,希望对你有一定的参考价值。

一、HanLP

HanLP 是一系列模型与算法组成的NLP工具包,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点。内部算法经过工业界和学术界考验,配套书籍《自然语言处理入门》已经出版。目前,基于深度学习的HanLP 2.x已正式发布,次世代最先进的NLP技术,支持包括简繁中英日俄法德在内的104种语言上的联合任务。

本文参考官方GitHub:https://github.com/hankcs/HanLP/tree/1.x

目前 HanLP 提供了下面功能:

在提供丰富功能的同时,HanLP内部模块坚持低耦合、模型坚持惰性加载、服务坚持静态提供、词典坚持明文发布,使用非常方便。默认模型训练自全世界最大规模的中文语料库,同时自带一些语料处理工具,帮助用户训练自己的模型。

二、Java Maven项目环境准备

首先新建一个普通 Maven 项目,在 pom 中增加依赖:

<dependency>
    <groupId>com.hankcs</groupId>
    <artifactId>hanlp</artifactId>
    <version>portable-1.8.3</version>
</dependency>

此时即可使用基本功能(除由字构词、依存句法分析外的全部功能)。如果需要使用全部功能还需下载词典和模型,下载地址:

http://nlp.hankcs.com/download.php?file=data

将下载后的data目录,拷贝至项目的 resources 目录下:

其中数据分为词典和模型,其中词典是词法分析必需的,模型是句法分析必需的,用户可以自行增删替换,如果不需要句法分析等功能的话,随时可以删除model文件夹。

下面还需要进行 HanLP 的配置,可以下载官方的配置模板:

http://nlp.hankcs.com/download.php?file=jar

下载解压后,jar 包上面已经通过 Maven 引入,只需将hanlp.properties文件拷贝至 resources 目录下,并修改下面配置:

root=src/main/resources

编写测试程序,测试环境:

public class Test1 
    public static void main(String[] args) 
        String text = "你好,欢迎使用HanLP汉语处理包!";
        //标准分词
        System.out.println(HanLP.segment(text));
        //nlp 分词
        System.out.println(NLPTokenizer.segment(text));
    

成功拿到结果便环境正常:

三、HanLP 使用

1. 基础分词

础分词,基础分词只进行基本NGram分词,不识别命名实体,不使用用户词典,分词后会带有词性,词性的标注集放在了文章最后:

public class DemoBasicTokenizer

    public static void main(String[] args)
    
        String text = "程序员(英文Programmer)是从事程序开发、维护的专业人员。" +
            "一般将程序员分为程序设计人员和程序编码人员," +
            "但两者的界限并不非常清楚,特别是在中国。" +
            "软件从业人员分为初级程序员、高级程序员、系统" +
            "分析员和项目经理四大类。";
        System.out.println(BasicTokenizer.segment(text));
        // 测试分词速度,让大家对HanLP的性能有一个直观的认识
        long start = System.currentTimeMillis();
        int pressure = 100000;
        for (int i = 0; i < pressure; ++i)
        
            BasicTokenizer.segment(text);
        
        double costTime = (System.currentTimeMillis() - start) / (double) 1000;
        System.out.printf("BasicTokenizer分词速度:%.2f字每秒\\n", text.length() * pressure / costTime);
    

2. 极速分词

基于DoubleArrayTrie实现的词典正向最长分词,适用于“高吞吐量”“精度一般”的场合

public class DemoHighSpeedSegment

    public static void main(String[] args)
    
        String text = "江西鄱阳湖干枯,中国最大淡水湖变成大草原";
        HanLP.Config.ShowTermNature = false;
        System.out.println(SpeedTokenizer.segment(text));
        long start = System.currentTimeMillis();
        int pressure = 1000000;
        for (int i = 0; i < pressure; ++i)
        
            SpeedTokenizer.segment(text);
        
        double costTime = (System.currentTimeMillis() - start) / (double)1000;
        System.out.printf("SpeedTokenizer分词速度:%.2f字每秒\\n", text.length() * pressure / costTime);
    

3. 标准分词

public class DemoSegment

    public static void main(String[] args)
    
        String[] testCase = new String[]
                "商品和服务",
                "当下雨天地面积水分外严重",
                "结婚的和尚未结婚的确实在干扰分词啊",
                "买水果然后来世博园最后去世博会",
                "中国的首都是北京",
                "欢迎新老师生前来就餐",
                "工信处女干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作",
                "随着页游兴起到现在的页游繁盛,依赖于存档进行逻辑判断的设计减少了,但这块也不能完全忽略掉。",
        ;
        for (String sentence : testCase)
        
            //对StandardTokenizer.segment的包装
            List<Term> termList = HanLP.segment(sentence);
            System.out.println(termList);
        
    


4. CRF词法分词

自1.6.6版起模型格式不兼容旧版:CRF模型为对数线性模型,通过复用结构化感知机的维特比解码算法,效率提高10倍。

public class DemoCRFLexicalAnalyzer

    public static void main(String[] args) throws IOException
    
        CRFLexicalAnalyzer analyzer = new CRFLexicalAnalyzer();
        String[] tests = new String[]
            "商品和服务",
            "上海华安工业(集团)公司董事长谭旭光和秘书胡花蕊来到美国纽约现代艺术博物馆参观",
            "微软公司於1975年由比爾·蓋茲和保羅·艾倫創立,18年啟動以智慧雲端、前端為導向的大改組。" // 支持繁体中文
        ;
        for (String sentence : tests)
        
            System.out.println(analyzer.seg(sentence));
        
    

5. NLP分词

更精准的中文分词、词性标注与命名实体识别。语料库规模决定实际效果,面向生产环境的语料库应当在千万字量级。

词性标注可以使用 Sentence#translateLabels() 转为中文显示:

public class DemoNLPSegment extends TestUtility

    public static void main(String[] args)
    
        NLPTokenizer.ANALYZER.enableCustomDictionary(false); // 中文分词≠词典,不用词典照样分词。
        System.out.println(NLPTokenizer.segment("我新造一个词叫幻想乡你能识别并正确标注词性吗?")); // “正确”是副形词。
        // 注意观察下面两个“希望”的词性、两个“晚霞”的词性
        System.out.println(NLPTokenizer.analyze("我的希望是希望张晚霞的背影被晚霞映红").translateLabels());
        System.out.println(NLPTokenizer.analyze("支援臺灣正體香港繁體:微软公司於1975年由比爾·蓋茲和保羅·艾倫創立。"));
    

6. 索引分词

public class DemoIndexSegment

    public static void main(String[] args)
    
        List<Term> termList = IndexTokenizer.segment("主副食品");
        for (Term term : termList)
        
            System.out.println(term + " [" + term.offset + ":" + (term.offset + term.word.length()) + "]");
        

        System.out.println("\\n最细颗粒度切分:");
        IndexTokenizer.SEGMENT.enableIndexMode(1);
        termList = IndexTokenizer.segment("主副食品");
        for (Term term : termList)
        
            System.out.println(term + " [" + term.offset + ":" + (term.offset + term.word.length()) + "]");
        
    

7. 多线程并行分词

由于HanLP的任何分词器都是线程安全的,所以用户只需调用一个配置接口就可以启用任何分词器的并行化

public class DemoMultithreadingSegment

    public static void main(String[] args) throws IOException
    
        Segment segment = new CRFLexicalAnalyzer(HanLP.Config.CRFCWSModelPath).enableCustomDictionary(false); // CRF分词器效果好,速度慢,并行化之后可以提高一些速度

        String text = "程序员(英文Programmer)是从事程序开发、维护的专业人员。" +
            "一般将程序员分为程序设计人员和程序编码人员," +
            "但两者的界限并不非常清楚,特别是在中国。" +
            "软件从业人员分为初级程序员、高级程序员、系统" +
            "分析员和项目经理四大类。";
        HanLP.Config.ShowTermNature = false;
        System.out.println(segment.seg(text));
        int pressure = 10000;
        StringBuilder sbBigText = new StringBuilder(text.length() * pressure);
        for (int i = 0; i < pressure; i++)
        
            sbBigText.append(text);
        
        text = sbBigText.toString();
        System.gc();

        long start;
        double costTime;
        // 测个速度

        segment.enableMultithreading(false);
        start = System.currentTimeMillis();
        segment.seg(text);
        costTime = (System.currentTimeMillis() - start) / (double) 1000;
        System.out.printf("单线程分词速度:%.2f字每秒\\n", text.length() / costTime);
        System.gc();

        segment.enableMultithreading(true); // 或者 segment.enableMultithreading(4);
        start = System.currentTimeMillis();
        segment.seg(text);
        costTime = (System.currentTimeMillis() - start) / (double) 1000;
        System.out.printf("多线程分词速度:%.2f字每秒\\n", text.length() / costTime);
        System.gc();

        // Note:
        // 内部的并行化机制可以对1万字以上的大文本开启多线程分词
        // 另一方面,HanLP中的任何Segment本身都是线程安全的。
        // 你可以开10个线程用同一个CRFSegment对象切分任意文本,不需要任何线程同步的措施,每个线程都可以得到正确的结果。
    

8. AhoCorasickDoubleArrayTrie 分词

AhoCorasickDoubleArrayTrieSegment要求用户必须提供自己的词典路径
准备词典:

微观经济学
继续教育
循环经济
public class DemoUseAhoCorasickDoubleArrayTrieSegment 
    public static void main(String[] args) throws IOException 
        AhoCorasickDoubleArrayTrieSegment segment = new AhoCorasickDoubleArrayTrieSegment("data/dictionary/custom/my.txt");
        System.out.println(segment.seg("微观经济学继续教育循环经济"));
    

9. 繁体中文分词

public class DemoTraditionalChineseSegment

    public static void main(String[] args)
    
        List<Term> termList = TraditionalChineseTokenizer.segment("大衛貝克漢不僅僅是名著名球員,球場以外,其妻為前" +
                                                                          "辣妹合唱團成員維多利亞·碧咸,亦由於他擁有" +
                                                                          "突出外表、百變髮型及正面的形象,以至自己" +
                                                                          "品牌的男士香水等商品,及長期擔任運動品牌" +
                                                                          "Adidas的代言人,因此對大眾傳播媒介和時尚界" +
                                                                          "等方面都具很大的影響力,在足球圈外所獲得的" +
                                                                          "認受程度可謂前所未見。");
        System.out.println(termList);

        termList = TraditionalChineseTokenizer.segment("(中央社記者黃巧雯台北20日電)外需不振,影響接單動能,經濟部今天公布7月外銷訂單金額362.9億美元,年減5%," +
                                                               "連續4個月衰退,減幅較6月縮小。1040820\\n");
        System.out.println(termList);

        termList = TraditionalChineseTokenizer.segment("中央社记者黄巧雯台北20日电");
        System.out.println(termList);
    


10. N最短路径分词

该分词器比最短路分词器慢,但是效果稍微好一些,对命名实体识别能力更强

public class DemoNShortSegment

    public static void main(String[] args)
    
        Segment nShortSegment = new NShortSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);
        Segment shortestSegment = new ViterbiSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);
        String[] testCase = new String[]
                "一般将程序员分为程序设计人员和程序编码人员",
                "软件从业人员分为初级程序员、高级程序员、系统分析员和项目经理四大类。",
                "程序员(英文Programmer)是从事程序开发、维护的专业人员。",
        ;
        for (String sentence : testCase)
        
            System.out.println("N-最短分词:" + nShortSegment.seg(sentence) + "\\n最短路分词:" + shortestSegment.seg(sentence));
        
    

11. 自定义词性,以及往词典中插入自定义词性的词语

public class DemoCustomNature

    public static void main(String[] args)
    
        // 对于系统中已有的词性,可以直接获取
        Nature pcNature = Nature.fromString("n");
        System.out.println(pcNature);
        // 此时系统中没有"电脑品牌"这个词性
        pcNature = Nature.fromString("电脑品牌");
        System.out.println(以上是关于HanLP 自然语言处理使用总结的主要内容,如果未能解决你的问题,请参考以下文章

HanLP 自然语言处理使用总结

HanLP 自然语言处理使用总结

NLP文本处理的基本方法(超详解)

Hanlp 在Python环境中安装介绍及使用

HanLP用户自定义词典源码分析

HanLP中人名识别分析