hankcs / HanLP

中文分词 词性标注 命名实体识别 依存句法分析 成分句法分析 语义依存分析 语义角色标注 指代消解 风格转换 语义相似度 新词发现 关键词短语提取 自动摘要 文本分类聚类 拼音简繁转换 自然语言处理
https://hanlp.hankcs.com/
Apache License 2.0
33.9k stars 10.15k forks source link

seg2sentence() 把逗号和分号也分割为一句话 #1018

Closed BLKSerene closed 6 years ago

BLKSerene commented 6 years ago

注意事项

请确认下列注意事项:

版本号

当前最新版本号是:1.6.8 我使用的版本是:1.6.8

我的问题

这个问题 Issue 876 里提到过 但是被关闭了 目前 StandardTokenizer, BasicTokenizer, NLPTokenizer, SpeedTokenizer 提供的 seg2sentence 分句接口(其他分词器比如 TraditionalChineseTokenizer, URLTokenizer, CRFLexicalAnalyzer, PerceptronLexicalAnalyzer, DijkstraSegment, NShortSegment, ViterbiSegment 我大致看了下没找到分句的接口 不知道是不是一样的表现)会把逗号也切分为一句话 不知这个功能是否是为了后期其他文本处理的考虑才这样切分的?

如果是用于生产环境的分句的话 感觉逗号肯定不能切成一句 按照最新的国家标准: 中华人民共和国国家标准GB/T15834-2011标点符号用法 3.1.1 一节中所述 只有句号 问号 叹号 这三个标点符号才算一句话 所以严格来说 分号也不能算

触发代码

因为我不会 Java 只会 Python 所以用的 pyhanlp 就用 StandardTokenizer 举个例子 其他分词器的结果类似

import jpype
import pyhanlp

standard_tokenizer = jpype.JClass('com.hankcs.hanlp.tokenizer.StandardTokenizer')

for sentence in standard_tokenizer.seg2sentence('这是一句用来测试的句子,中间有个逗号。'): 
    print(sentence)

期望输出

[这/rzv, 是/vshi, 一/m, 句/q, 用来/v, 测试/vn, 的/ude1, 句子/n, ,/w, 中间/f, 有/vyou, 个/q, 逗号/n, 。/w]

实际输出

[这/rzv, 是/vshi, 一/m, 句/q, 用来/v, 测试/vn, 的/ude1, 句子/n, ,/w]
[中间/f, 有/vyou, 个/q, 逗号/n, 。/w]
hankcs commented 6 years ago

由于网络文本不规范,很多人一逗到底,所以默认拆到最细。看以先用com.hankcs.hanlp.utility.SentencesUtil#toSentenceList(java.lang.String, boolean)拆分句子,后执行分词。

hankcs commented 6 years ago

感谢反馈,已经修复,请参考上面的commit。 如果还有问题,欢迎重开issue。

main1015 commented 6 years ago

我发现在python版本中无法调用enableIndexMode这个方法,提示RuntimeError: Ambiguous overloads found: (Lcom/hankcs/hanlp/seg/Viterbi/ViterbiSegment;I) vs (Lcom/hankcs/hanlp/seg/Viterbi/ViterbiSegment;Z) at native/common/jp_method.cpp:117,我看了你们的源码,在java代码中有两个enableIndexMode方法,这里后期可以解决吗?

lfzhagn commented 5 years ago

@main1015 你好!我用pyhanlp中enableIndexMode遇到了同样的问题,请教如何解决?

LuoRongLuoRong commented 2 years ago

这里,空格也会使其对句子进行拆分。举例如下:

原文

聚合集群
聚合集群用于不同配置的集群之间的故障切换,例如,从 EDS 上游集群到 STRICT_DNS 上游集群,从使用 ROUND_ROBIN 负载均衡策略的集群到使用 MAGLEV 的集群,从连接超时 0.1s 的集群到连接超时 1s 的集群等。聚合集群通过在 配置 中引用它们的名称松散地耦合多个集群。降级优先级由 集群列表 中的排序隐式定义。
聚合集群使用分层负载均衡。负载均衡器首先选择集群和优先级,然后将负载均衡委托给所选集群的负载均衡器。顶层负载均衡器通过将多个集群的优先级集线性化为一个集群,重用现有的负载均衡算法。

hanlp 代码

import jpype
import pyhanlp

standard_tokenizer = jpype.JClass('com.hankcs.hanlp.tokenizer.StandardTokenizer')
paragraph = '''聚合集群
聚合集群用于不同配置的集群之间的故障切换,例如,从 EDS 上游集群到 STRICT_DNS 上游集群,从使用 ROUND_ROBIN 负载均衡策略的集群到使用 MAGLEV 的集群,从连接超时 0.1s 的集群到连接超时 1s 的集群等。聚合集群通过在 配置 中引用它们的名称松散地耦合多个集群。降级优先级由 集群列表 中的排序隐式定义。
聚合集群使用分层负载均衡。负载均衡器首先选择集群和优先级,然后将负载均衡委托给所选集群的负载均衡器。顶层负载均衡器通过将多个集群的优先级集线性化为一个集群,重用现有的负载均衡算法。'''
for sentence in standard_tokenizer.seg2sentence(paragraph, False): 
    print(sentence)

hanlp 分解后的结果

[聚合/vi, 集群/nz]
[聚合/vi, 集群/nz, 用于/v, 不同/a, 配置/vn, 的/ude1, 集群/nz, 之间/f, 的/ude1, 故障/n, 切换/vn, ,/w]
[例如/v, ,/w]
[从/p, EDS/nx, 上游/f, 集群/nz, 到/v, STRICT_DNS/nx, 上游/f, 集群/nz, ,/w]
[从/p, 使用/v, ROUND_ROBIN/nx, 负载/n, 均衡/a, 策略/n, 的/ude1, 集群/nz, 到/v, 使用/v]
[MAGLEV/nx]
[的/ude1, 集群/nz, ,/w]
[从/p, 连接/v, 超时/v]
[0.1/m, s/nx]
[的/ude1, 集群/nz, 到/v, 连接/v, 超时/v]
[1/m, s/nx]
[的/ude1, 集群/nz, 等/udeng, 。/w]
[聚合/vi, 集群/nz, 通过/p, 在/p, 配置/vn, 中/f, 引用/v, 它们/rr, 的/ude1, 名称/n, 松散地/n, 耦合/vn, 多/a, 个/q, 集群/nz, 。/w]
[降级/vi, 优先级/n, 由/p]
[集群/nz, 列表/vi]
[中的/v, 排序/vi, 隐/v, 式/k, 定义/n, 。/w]
[聚合/vi, 集群/nz, 使用/v, 分层/v, 负载/n, 均衡/a, 。/w]
[负载/n, 均衡器/gi, 首先/d, 选择/v, 集群/nz, 和/cc, 优先级/n, ,/w]
[然后/c, 将/d, 负载/n, 均衡/a, 委托/vn, 给/p, 所选/v, 集群/nz, 的/ude1, 负载/n, 均衡器/gi, 。/w]
[顶层/f, 负载/n, 均衡器/gi, 通过/p, 将/d, 多/a, 个/q, 集群/nz, 的/ude1, 优先级/n, 集/q, 线性/n, 化为/v, 一个/mq, 集群/nz, ,/w]
[重用/v, 现有/v, 的/ude1, 负载/n, 均衡/a, 算法/n, 。/w]

stanza 代码

import stanza
# stanza.download('zh')       # This downloads the English models for the neural pipeline
nlp = stanza.Pipeline('zh', use_gpu=False) # This sets up a default neural pipeline in English
paragraph = '''聚合集群
聚合集群用于不同配置的集群之间的故障切换,例如,从 EDS 上游集群到 STRICT_DNS 上游集群,从使用 ROUND_ROBIN 负载均衡策略的集群到使用 MAGLEV 的集群,从连接超时 0.1s 的集群到连接超时 1s 的集群等。聚合集群通过在 配置 中引用它们的名称松散地耦合多个集群。降级优先级由 集群列表 中的排序隐式定义。
聚合集群使用分层负载均衡。负载均衡器首先选择集群和优先级,然后将负载均衡委托给所选集群的负载均衡器。顶层负载均衡器通过将多个集群的优先级集线性化为一个集群,重用现有的负载均衡算法。'''
doc = nlp(paragraph)
for s in doc.sentences:
    print('sentence', s.text)

stanza 结果

sentence 聚合集群
聚合集群用于不同配置的集群之间的故障切换,例如,从 EDS 上游集群到 STRICT_DNS 上游集群,从使用 ROUND_ROBIN 负载均衡策略的集群到使用 MAGLEV 的集群,从连接超时 0.1s 的集群到连接超时 1s 的集群等。
sentence 聚合集群通过在 配置 中引用它们的名称松散地耦合多个集群。
sentence 降级优先级由 集群列表 中的排序隐式定义。
sentence 聚合集群使用分层负载均衡。
sentence 负载均衡器首先选择集群和优先级,然后将负载均衡委托给所选集群的负载均衡器。
sentence 顶层负载均衡器通过将多个集群的优先级集线性化为一个集群,重用现有的负载均衡算法。
hankcs commented 2 years ago

建议使用2.x的各种API: RESTful:https://hanlp.hankcs.com/docs/api/restful.html#hanlp_restful.HanLPClient.tokenize 本地模型:https://github.com/hankcs/HanLP/blob/master/plugins/hanlp_demo/hanlp_demo/sent_split.py 本地规则:https://github.com/hankcs/HanLP/blob/master/hanlp/utils/rules.py#L19

针对中文做了很多优化,应该比stanza要好。