• Stars
    star
    137
  • Rank 266,121 (Top 6 %)
  • Language
    Java
  • License
    Apache License 2.0
  • Created almost 5 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The jieba-analysis tool for java.(基于结巴分词词库实现的更加灵活优雅易用,高性能的 java 分词实现。支持词性标注。)

Segment

Segment 是基于结巴分词词库实现的更加灵活,高性能的 java 分词实现。

愿景:成为 java 最好用的分词工具。

Build Status Maven Central Open Source Love

在线体验

创作目的

分词是做 NLP 相关工作,非常基础的一项功能。

jieba-analysis 作为一款非常受欢迎的分词实现,个人实现的 opencc4j 之前一直使用其作为分词。

但是随着对分词的了解,发现结巴分词对于一些配置上不够灵活。

(1)有很多功能无法指定关闭,比如 HMM 对于繁简体转换是无用的,因为繁体词是固定的,不需要预测。

(2)最新版本的词性等功能好像也被移除了,但是这些都是个人非常需要的。

(3)对于中文繁体分词支持不友好。

所以自己重新实现了一遍,希望实现一套更加灵活,更多特性的分词框架。

而且 jieba-analysis 的更新似乎停滞了,个人的实现方式差异较大,所以建立了全新的项目。

Features 特点

  • 面向用户的极简静态 api 设计

  • 面向开发者 fluent-api 设计,让配置更加优雅灵活

  • 详细的中文代码注释,便于源码阅读

  • 基于 DFA 实现的高性能分词

  • 基于 HMM 的新词预测

  • 支持不同的分词模式

  • 支持全角半角/英文大小写/中文繁简体格式处理

  • 允许用户自定义词库

  • 简单的词性标注实现

  • 支持字典等资源的主动释放

v-0.3.0 最新变更

  • 新增 wordCount 工具方法

变更日志

快速入门

准备

jdk1.7+

maven 3.x+

maven 引入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>segment</artifactId>
    <version>0.3.0</version>
</dependency>

相关代码参见 SegmentHelperTest.java

默认分词示例

返回分词,下标等信息。

final String string = "这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱学习。";

List<ISegmentResult> resultList = SegmentHelper.segment(string);
Assert.assertEquals("[这是[0,2), 一个[2,4), 伸手不见五指[4,10), 的[10,11), 黑夜[11,13), 。[13,14), 我[14,15), 叫[15,16), 孙悟空[16,19), ,[19,20), 我爱[20,22), 北京[22,24), ,[24,25), 我爱[25,27), 学习[27,29), 。[29,30)]", resultList.toString());

指定返回形式

有时候我们根据自己的应用场景,需要选择不同的返回形式。

SegmentResultHandlers 用来指定对于分词结果的处理实现,便于保证 api 的统一性。

方法 实现 说明
common() SegmentResultHandler 默认实现,返回 ISegmentResult 列表
word() SegmentResultWordHandler 只返回分词字符串列表
wordCount() SegmentResultWordHandler key: 分词字符串; value: 分词出现的次数

默认模式

默认分词形式,等价于下面的写法

List<ISegmentResult> resultList = SegmentHelper.segment(string, SegmentResultHandlers.common());

只获取分词信息

final String string = "这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱学习。";

List<String> resultList = SegmentHelper.segment(string, SegmentResultHandlers.word());
Assert.assertEquals("[这是, 一个, 伸手不见五指, 的, 黑夜, 。, 我, 叫, 孙悟空, ,, 我爱, 北京, ,, 我爱, 学习, 。]", resultList.toString());

统计分词出现的次数

我们通过 SegmentResultHandlers.wordCount() 指定统计出现次数的方法。

final String string = "这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱学习。";

Map<String, Integer> wordCount = SegmentHelper.segment(string, SegmentResultHandlers.wordCount());
Assert.assertEquals(2, wordCount.get("我爱").intValue());
Assert.assertEquals(1, wordCount.get("黑夜").intValue());

当然,这个在相似度等计算中出现频率较高。

因此提供工具方法 SegmentHelper.wordCount(),上面的方法等价于:

Map<String, Integer> wordCount = SegmentHelper.wordCount(string);

分词模式

分词模式简介

分词模式可以通过类 SegmentModes 工具类获取。

序号 方法 准确度 性能 备注
1 search() 一般 结巴分词的默认模式
2 dict() 较高 一般 和 search 模式类似,但是缺少 HMM 新词预测
3 index() 一般 尽可能多的返回词组信息,提高召回率
4 greedyLength() 一般 贪心最大长度匹配,对准确度要求不高时可采用。

使用方式

针对灵活的配置,引入了 SegmentBs 作为引导类,解决工具类方法配置参数过多的问题。

测试代码参见 SegmentModeTest.java

search 模式

segmentMode() 指定分词模式,不指定时默认就是 SegmentModes.search()

final String string = "这是一个伸手不见五指的黑夜。";

List<ISegmentResult> resultList = SegmentBs.newInstance()
       .segmentMode(SegmentModes.search())
       .segment(string);

Assert.assertEquals("[这是[0,2), 一个[2,4), 伸手不见五指[4,10), 的[10,11), 黑夜[11,13), 。[13,14)]", resultList.toString());

dict 模式

只依赖词库实现分词,没有 HMM 新词预测功能。

final String string = "这是一个伸手不见五指的黑夜。";

List<ISegmentResult> resultList = SegmentBs.newInstance()
        .segmentMode(SegmentModes.dict())
        .segment(string);
Assert.assertEquals("[这[0,1), 是[1,2), 一个[2,4), 伸手不见五指[4,10), 的[10,11), 黑夜[11,13), 。[13,14)]", resultList.toString());

index 模式

这里主要的区别就是会返回 伸手伸手不见 等其他词组。

final String string = "这是一个伸手不见五指的黑夜。";

List<ISegmentResult> resultList = SegmentBs.newInstance()
        .segmentMode(SegmentModes.index())
        .segment(string);
Assert.assertEquals("[这[0,1), 是[1,2), 一个[2,4), 伸手[4,6), 伸手不见[4,8), 伸手不见五指[4,10), 的[10,11), 黑夜[11,13), 。[13,14)]", resultList.toString());

GreedyLength 模式

这里使用贪心算法实现,准确率一般,性能较好。

final String string = "这是一个伸手不见五指的黑夜。";

List<ISegmentResult> resultList = SegmentBs.newInstance()
        .segmentMode(SegmentModes.greedyLength())
        .segment(string);
Assert.assertEquals("[这[0,1), 是[1,2), 一个[2,4), 伸手不见五指[4,10), 的[10,11), 黑夜[11,13), 。[13,14)]", resultList.toString());

格式化处理

格式化接口

可以通过 SegmentFormats 工具类获取对应的格式化实现,在分词时指定即可。

序号 方法 名称 说明
1 defaults() 默认格式化 等价于小写+半角处理。
2 lowerCase() 字符小写格式化 英文字符处理时统一转换为小写
3 halfWidth() 字符半角格式化 英文字符处理时统一转换为半角
4 chineseSimple() 中文简体格式化 用于支持繁体中文分词
5 none() 无格式化 无任何格式化处理
6 chains(formats) 格式化责任链 你可以针对上述的格式化自由组合,同时允许自定义格式化。

默认格式化

全角半角+英文大小写格式化处理,默认开启。

这里的 为全角大写,默认会被转换处理。

String text = "阿Q精神";
List<ISegmentResult> segmentResults = SegmentHelper.segment(text);

Assert.assertEquals("[阿Q[0,2), 精神[2,4)]", segmentResults.toString());

中文繁体分词

无论是结巴分词还是当前框架,默认对繁体中文的分词都不友好。

默认分词示例

显然和简体中文的分词形式不同。

String text = "這是一個伸手不見五指的黑夜";

List<String> defaultWords = SegmentBs.newInstance()
        .segment(text, SegmentResultHandlers.word());
Assert.assertEquals("[這是, 一, 個, 伸手, 不見, 五指, 的, 黑夜]", defaultWords.toString());

启用中文繁体分词

指定分词中文格式化,可以得到符合我们预期的分词。

String text = "這是一個伸手不見五指的黑夜";

List<String> defaultWords = SegmentBs.newInstance()
        .segmentFormat(SegmentFormats.chineseSimple())
        .segment(text, SegmentResultHandlers.word());
Assert.assertEquals("[這是, 一個, 伸手不見五指, 的, 黑夜]", defaultWords.toString());

格式化责任链

格式化的形式可以有很多,我们可以根据自己的需求自由组合。

比如我们想同时启用默认格式化+中文简体格式化。

final String text = "阿Q,這是一個伸手不見五指的黑夜";

List<String> defaultWords = SegmentBs.newInstance()
        .segmentFormat(SegmentFormats.chains(SegmentFormats.defaults(),
                SegmentFormats.chineseSimple()))
        .segment(text, SegmentResultHandlers.word());
Assert.assertEquals("[阿Q, ,, 這是, 一個, 伸手不見五指, 的, 黑夜]", defaultWords.toString());

自定义词库

为了适应更多的应用场景,segment 支持自定义词典。

定义方式

resources 或者项目根目录新建文件 segment_phrase_dict_define.txt

要求编码:UTF-8

内容格式如下:

彩霞 78 n

第一个词是我们自定义的词,必填。

第二个为这个词出现的词频,选填,默认为 3。

第三个为词性,选填,默认为 un。(未知)

三者用英文空格( )隔开。

优先级

用户自定义的词优先级更高,会覆盖系统原有的相同词。

词性标注

说明

目前支持最简单版本的词性标注,暂定为 alpha 版本,后续引入基于 HMM 实现的词性标注。

使用例子

final String string = "这是一个伸手不见五指的黑夜。";

List<ISegmentResult> resultList = SegmentBs.newInstance()
        .posTagging(SegmentPosTaggings.simple())
        .segment(string);

Assert.assertEquals("[这是[0,2)/un, 一个[2,4)/mq, 伸手不见五指[4,10)/i, 的[10,11)/ude1, 黑夜[11,13)/n, 。[13,14)/w]", resultList.toString());

主动释放资源

说明

分词是基于字典实现的,为了提升性能,字典初始化之后会缓存到内存中。 这对于 java web 服务端是没有太大问题的。

有安卓客户端小伙伴反应,希望分词使用一次之后,可以主动释放资源。

此功能为此而实现。

使用

方法在引导类中可以使用,如下:

// 初始化引导类
final SegmentBs segmentBs = SegmentBs.newInstance();

// 主动释放资源
segmentBs.destroy();

例子

实际例子:

// 基本特性
final SegmentBs segmentBs = SegmentBs.newInstance();

final String string = "这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱学习。";
List<ISegmentResult> resultList = segmentBs.segment(string);
Assert.assertEquals("[这是[0,2), 一个[2,4), 伸手不见五指[4,10), 的[10,11), 黑夜[11,13), 。[13,14), 我[14,15), 叫[15,16), 孙悟空[16,19), ,[19,20), 我爱[20,22), 北京[22,24), ,[24,25), 我爱[25,27), 学习[27,29), 。[29,30)]", resultList.toString());

// 资源释放
segmentBs.destroy();

// 重新处理
List<ISegmentResult> resultList2 = segmentBs.segment(string);
Assert.assertEquals("[这是[0,2), 一个[2,4), 伸手不见五指[4,10), 的[10,11), 黑夜[11,13), 。[13,14), 我[14,15), 叫[15,16), 孙悟空[16,19), ,[19,20), 我爱[20,22), 北京[22,24), ,[24,25), 我爱[25,27), 学习[27,29), 。[29,30)]", resultList2.toString());

为了便于使用,资源释放之后,如果再次分词,会重新初始化相关资源

日志

为了便于研发观察,自适应日志输出对应的字典加载和销毁信息。

格式如下:

[DEBUG] [2023-03-23 11:16:53.010] [main] [c.g.h.s.s.t.i.SegmentTrieTree.getTrieTree] - [Segment]-[data-trie] init start
[DEBUG] [2023-03-23 11:16:53.480] [main] [c.g.h.s.s.t.i.SegmentTrieTree.getTrieTree] - [Segment]-[data-trie] init end
...

[DEBUG] [2023-03-23 11:16:53.543] [main] [c.g.h.s.s.t.i.SegmentTrieTree.destroy] - [Segment]-[data-trie] destroy start
[DEBUG] [2023-03-23 11:16:53.543] [main] [c.g.h.s.s.t.i.SegmentTrieTree.destroy] - [Segment]-[data-trie] destroy end
...

Benchmark 性能对比

性能对比

性能对比基于 jieba 1.0.2 版本,测试条件保持一致,保证二者都做好预热,然后统一处理。

验证下来,默认模式性能略优于 jieba 分词,贪心模式是其性能 3 倍左右。

备注:

(1)默认模式和结巴 Search 模式一致。

后期考虑 HMM 也可以配置是否开启,暂定为默认开启

(2)后期将引入多线程提升性能。

代码参见 BenchmarkTest.java

性能对比图

相同长文本,循环 1W 次耗时。(Less is Better)

benchmark

后期 Road-Map

核心特性

  • HMM 词性标注

  • HMM 实体标注

  • CRF 算法实现

  • N 元组算法实现

优化

  • 多线程的支持,性能优化

  • 双数组 DFA 实现,降低内存消耗

创作感谢

感谢 jieba 分词提供的词库,以及 jieba-analysis 的相关实现。

NLP 开源矩阵

pinyin 汉字转拼音

pinyin2hanzi 拼音转汉字

segment 高性能中文分词

opencc4j 中文繁简体转换

nlp-hanzi-similar 汉字相似度

word-checker 拼写检测

sensitive-word 敏感词

More Repositories

1

sensitive-word

👮‍♂️The sensitive word tool for java.(敏感词/违禁词/违法词/脏词。基于 DFA 算法实现的高性能 java 敏感词过滤工具框架。请勿发布涉及政治、广告、营销、翻墙、违反国家法律法规等内容。高性能敏感词检测过滤组件,附带繁体简体互换,支持全角半角互换,汉字转拼音,模糊搜索等功能。)
Java
4,032
star
2

sensitive

🔐Sensitive log tool for java, based on java annotation. (基于注解的 java 日志脱敏工具框架,更加优雅的日志打印。支持自定义哈希、支持基于 log4j2 插件的统一脱敏、支持 logback 插件统一脱敏)
Java
566
star
3

opencc4j

🇨🇳Open Chinese Convert is an opensource project for conversion between Traditional Chinese and Simplified Chinese.(java 中文繁简体转换)
Java
465
star
4

cache

The basic cache tool for java.(java 手写实现渐进式 redis 缓存工具)
Shell
360
star
5

awesome-metaverse-zh

Awesome metaverse projects (元宇宙精选资源汇总)
Batchfile
348
star
6

pinyin

The high performance pinyin tool for java.(java 高性能中文转拼音工具。支持同音字。)
Java
230
star
7

word-checker

🇨🇳🇬🇧Chinese and English word spelling corrector.(中文易错别字检测,中文拼写检测纠正。英文单词拼写校验工具)
Java
229
star
8

nlp-hanzi-similar

The hanzi similar tool.(汉字相似度计算工具,中文形近字算法。可用于手写汉字识别纠正,文本混淆等。)
Java
215
star
9

junitperf

⛵️Junit performance rely on junit5 and jdk8+.(java 性能测试框架。性能测试。压测。测试报告生成。)
Java
184
star
10

sisyphus

The java retry framework.(支持注解的 java 重试框架)
Java
181
star
11

markdown-toc

🎄Generate toc for github markdown file.(为 markdown 文件生成 toc 目录)
Java
167
star
12

mq

The message queue in java.(java 简易版本 mq 实现)
Java
113
star
13

rpc

Java rpc framework based on netty4.(java 手写 dubbo rpc 框架)
Java
97
star
14

data-factory

🏭Auto generate mock data for java test.(便于 Java 测试自动生成对象信息)
Java
87
star
15

resubmit

The repeat submit tool for java.(java 防止重复提交框架,支持注解。支持 spring, springboot 整合。)
Java
78
star
16

iexcel

📚IExcel is a java tool for create and read excel easy.(excel 读取和写入,解决 excel OOM 问题。)
Java
53
star
17

idoc

🌾Generate doc for java maven project.(java 项目自动生成文档)
Java
50
star
18

auto-log

The auto log tool for java.(java 自动日志输出,基于字节码,兼容 spring)
Java
48
star
19

validator

🚥Fluent-validation framework for java extend from hibernate-validator.(java 对象属性流式验证框架,下一代校验框架。兼容实现 jakarta bean validation、jsr 303、hibernate-validator)
Java
45
star
20

lombok-ex

Java compile time annotation, lombok extension framework.(java 编译时注解框架,对 lombok 进行扩展)
Java
43
star
21

heaven

⭐Collect basic useful tools for java dev.(收集开发中常用的工具类)
Java
37
star
22

ioc

The java ioc framework(从零开始手写模拟 spring Ioc 框架)
Java
34
star
23

word-cloud

The word cloud tool for java.(java 好用的词云工具-云图)
Java
31
star
24

nginx4j

nginx4j 是 nginx 的java 版本。
Java
31
star
25

async

🦄The async tool for java.(Java 多线程异步并行框架,基于 java 字节码,支持注解。)
Java
28
star
26

houbb.github.io

🧀The blog for github pages.
JavaScript
28
star
27

mybatis

The simple mybatis.(手写简易版 mybatis)
Java
23
star
28

csv

The csv read/write tool based on java annotation.(基于 java 注解的 CSV 文件读写框架工具。)
Java
23
star
29

rate-limit

🎃Rate limit for java.(java 渐进式限流工具框架,支持 spring springboot 字节码)
Java
22
star
30

sensitive-word-admin

The sensitive tool admin.(敏感词工具对应的控台)
HTML
20
star
31

minicat

The mini tomcat.(手写从零实现简易版 tomcat。别称【嗅虎】心有猛虎,轻嗅蔷薇。)
Java
18
star
32

jdbc-pool

The jdbc pool for java.(java 手写 jdbc 数据库连接池实现)
Java
17
star
33

lock

The distributed lock tool for java.(java 实现开箱即用基于 redis 的分布式锁,支持可重入锁获取。内置整合 spring、springboot。)
Java
8
star
34

leetcode

The leetcode problem solutions.(力扣算法个人学习笔记) 查询、排序、五大算法汇总
Java
3
star
35

houbb

my readme
1
star
36

nlp-keyword

nlp-keyword based on segment.(基于分词的 NLP keyword 关键词)
Java
1
star
37

hibernate

The simple database orm hibernate.(java 从零实现简易版本 hibernate)
Java
1
star
38

mvc

High performance web mvc framework for java.(高性能 java web mvc 框架)
Java
1
star
39

logstash4j

log stash in java.(简易版 logstash 实现)
Java
1
star