基本分词

中文分词有很多算法,同时大都是基于四种基本的分词方式,在基本的分词基础上做一些歧义消除、未登录词识别等功能。

下面以“南京市长江大桥”为例,分享一下四种基本的分词

正向最大匹配

从字面就很好理解,就是一句话从头开始读,可着最长的词取。

// 南京市长江大桥 --> 南京市 长江大桥

代码

List<Word> segmentation(String text) {
        Queue<Word> results = new LinkedList<>();
        int textLength = text.length();
        // 设置词的最大长度,词库中最长词的长度跟目标句子长度中,取最小
        int wordMaxLength = min(DictionaryFactory.getDictionary().getMaxWordLength(), textLength);
        int start = 0; // 开始分词的位置

        while (start < textLength) {
            int currentLength = min(textLength - start, wordMaxLength); // 未分词的句子长度
            boolean isSeg = false;
            while (start + currentLength <= textLength) {
                if (DictionaryFactory.getDictionary().contains(text, start, currentLength)) {
                    addWord(results, text, start, currentLength); // 成功分词  加入results中
                    isSeg = true;
                    break;
                } else if (--currentLength <= 0) { // 剩余长度为0 跳出当前循环
                    break;
                }
            }

            if (isSeg) {
                start += currentLength;
            } else {
                addWord(results, text, start++, 1); // 没有分出词  单字成词
            }
        }

        return new ArrayList<>(results);
    }

结果

180907_fU7I_3001485.png

正向最小匹配

跟上边的正好形成对比,这个也是正向,但是是可着最小的词先分

// 南京市长江大桥 --> 南京 市长 江 大桥

代码

List<Word> segmentation(String text) {
        Queue<Word> results = new LinkedList<>();
        int textLength = text.length();
        int wordMinLength = 2; //最小词长 这里不考虑单字的词
        int start = 0;

        while (start < textLength) {
            int currentLength = wordMinLength; // 从start处开始 从长度为2开始查找可分的词
            boolean isSeg = false;
            while (start + currentLength <= textLength) {
                if (DictionaryFactory.getDictionary().contains(text, start, currentLength)) {
                    addWord(results, text, start, currentLength);
                    isSeg = true;
                    break;
                } else if (++currentLength > DictionaryFactory.getDictionary().getMaxWordLength()) { // 没有的话就让currentLength+1,如果大于词典中最长的词就跳出循环
                    break;
                }
            }

            if (isSeg) {
                start += currentLength;
            } else {
                addWord(results, text, start++, 1);
            }
        }

        return new ArrayList<>(results);
    }

结果

181957_ehTB_3001485.png

逆向最大匹配

也是可着最大的词先分,不过是从后往前开始分

// 南京市长江大桥 --> 南京市 长江大桥

代码

public List<Word> segmentation(String text) {
        Deque<Word> results = new ArrayDeque<>();
        int wordMaxLength = min(DictionaryFactory.getDictionary().getMaxWordLength(), text.length());
        int end = text.length();

        while (end > 0) {
            int currentLength = min(wordMaxLength, end);
            boolean isSeg = false;
            while (end - currentLength >= 0) {
                if (DictionaryFactory.getDictionary().contains(text, end - currentLength, currentLength)) {
                    addWord(results, text, end - currentLength, currentLength);
                    isSeg = true;
                    break;
                } else if (--currentLength <= 0) {
                    break;
                }
            }

            if (isSeg) {
                end -= currentLength;
            } else {
                addWord(results, text, --end, 1);
            }
        }

        return new ArrayList<>(results);
    }

结果

190019_Fpdj_3001485.png

逆向最小匹配

正向最小匹配倒过来即可

// 南京市长江大桥 --> 南京市 长江 大桥

代码

public List<Word> segmentation(String text) {
        Deque<Word> results = new ArrayDeque<>();
        int wordMinLength = 2;
        int end = text.length();

        while (end > 0) {
            int currentLength = wordMinLength;
            boolean isSeg = false;
            while (end - currentLength >= 0) {
                if (DictionaryFactory.getDictionary().contains(text, end - currentLength, currentLength)) {
                    addWord(results, text, end - currentLength, currentLength);
                    isSeg = true;
                    break;
                } else if (++currentLength > DictionaryFactory.getDictionary().getMaxWordLength()) {
                    break;
                }
            }

            if (isSeg) {
                end -= currentLength;
            } else {
                addWord(results, text, --end, 1);
            }
        }

        return new ArrayList<>(results);
    }

结果

190423_AdIs_3001485.png

 

转载于:https://my.oschina.net/u/3001485/blog/1548758

任务描述 本关任务:用SparkSQL的方法,用ANSJ分词器,拿出岗位要求中的技能,统计这些技能的出现次数,并将其存入MySQL的eduskill表中。 相关知识 为了完成本关任务,我们需要掌握: 什么是ANSJ分词器; 如何使用ANSJ分词器; 使用ANSJ分词分词后如何获取想要的词。 ANSJ介绍 这是一个基于n-Gram+CRF+HMM的中文分词的Java实现。分词速度达到每秒钟大约200万字左右(mac air下测试),准确率能达到96%以上。目前实现了中文分词,中文姓名识别,用户自定义词典,关键字提取,自动摘要,关键字标记等功能。可以应用到自然语言处理等方面,适用于对分词效果要求高的各种项目。 ANSJ使用 ANSJ项目 项目的github地址:https://github.com/NLPchina/ansj_seg 项目的文档地址:http://nlpchina.github.io/ansj_seg/ ANSJ下载 可以从 http://maven.ansj.org/org/ansj/ 下载你需要的jar包。 用maven导入 <!-- https://mvnrepository.com/artifact/org.ansj/ansj_seg --> <dependency> <groupId>org.ansj</groupId> <artifactId>ansj_seg</artifactId> <version>5.1.6</version> </dependency> 分词调用方式 基本分词 基本就是保证了最基本分词,词语颗粒度最非常小的,所涉及到的词大约是10万左右。 基本分词速度非常快。在macAir上,能到每秒300w字每秒。同时准确率也很高,但是对于新词他的功能十分有限。 功能: 用户自定义词典 数字识别 人名识别 机构名识别 新词发现 × √ × × × Result parse = BaseAnalysis.parse("让战士们过一个欢乐祥和的新春佳节。"); List<Term> baseParse =parse.getTerms(); result:[让/v, 战士/n, 们/k, 过/ug, 一个/m, 欢乐/a, 祥和/a, 的/uj, 新春/t, 佳节/n, 。/w] 精准分词(推荐) 精准分词是ANSJ分词的店长推荐款。 它在易用性、稳定性、准确性、以及分词效率上都取得了一个不错的平衡。 如果你初次赏识ANSJ如果你想开箱即用,那么就用这个分词方式是不会错的。 功能: 用户自定义词典 数字识别 人名识别 机构名识别 新词发现 v √ v × × Result parse = ToAnalysis.parse("包括Hadoop、Spark 、Hive、Hbase、Kafaka、Flume、Sqoop等"); List<Term> toParse = parse.getTerms(); result:[包括/v, hadoop/en, 、/w, spark/en, , 、/w, hive/en, 、/w, hbase/en, 、/w, kafaka/en, 、/w, flume/en, 、/w, sqoop/en, 等/u] 根据词性获取想要的词 获取句子里词性为en的词: Result parse = ToAnalysis.parse("包括Hadoop、'Spark 、Hive、Hbase、Kafka、Flume、Sqoop等"); List<Term> toParse = parse.getTerms(); for (int i = 0; i < toParse.size(); i++) { String word = toParse.get(i).getName(); //拿到词 String natureStr = toParse.get(i).getNatureStr(); //拿到词性 if ("en".equals(natureStr)) { System.out.println(word); } } 输出: hadoop spark hive hbase Kafka flume sqoop 编程要求 根据提示,在右侧编辑器Begin-End处补充代码,用SparkSQL的方法,用ANSJ分词器,拿出岗位要求中的技能,统计这些技能的出现次数,并将其存入MySQL的eduskill表中。以下为代码package com; import org.ansj.domain.Result; import org.ansj.domain.Term; import org.ansj.splitWord.analysis.ToAnalysis; import org.apache.spark.api.java.JavaPairRDD; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.RowFactory; import org.apache.spark.sql.SparkSession; import org.apache.spark.sql.types.DataTypes; import org.apache.spark.sql.types.StructField; import org.apache.spark.sql.types.StructType; import scala.Tuple2; import java.util.*; public class EduSkill { public static void main(String[] args) { SparkSession spark = SparkSession.builder().appName("cc").master("local").getOrCreate(); spark.sparkContext().setLogLevel("error"); /********** Begin **********/ //读取parquet文件 //获取到responsibility并采用分词分词获取en词性的词 //统计这个词出现的次数 //将JavaRDD转为Dataset //将结果存入MySQL /********** End **********/ } }
06-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值