Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.7.2 版本中 CustomDictionary.insert 对 NLPTokenizer 无效? #1143

Closed
1 task done
wenfeixiang1991 opened this issue Apr 5, 2019 · 4 comments
Closed
1 task done
Labels

Comments

@wenfeixiang1991
Copy link

注意事项

请确认下列注意事项:

  • 我已仔细阅读下列文档,都没有找到答案:
  • 我已经通过Googleissue区检索功能搜索了我的问题,也没有找到答案。
  • 我明白开源社区是出于兴趣爱好聚集起来的自由社区,不承担任何责任或义务。我会礼貌发言,向每一个帮助我的人表示感谢。
  • 我在此括号内输入x打钩,代表上述事项确认完毕。

版本号

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

我的问题

  非常感谢这个项目,对NLP的理解深入了很多,我之前用的版本是1.6.4,基本只用了我认为最核心稳定的以统计模型(StandardTokenizer「ViterbiSegment」)为主,规则(CustomDictionary)为辅的中文分词服务。
  这样虽然高效、稳定,但随着遇到问题复杂性的提高,我逐渐发现已不能满足我nlp任务上一些需求,比如对未 insert 到 CustomDictionary 的「机构名」、「人名」识别较差,再比如没能好好利用到 parseDependency。
  在看到HanLP 公开了在线演示的1亿级语料训练的分词模型后,非常兴奋想要好好的利用起来。虽然我已经看过好几遍首页及wiki、FAQ、以及相关 issues,但可能由于基础较差,对 HanLP 的诸多「特性」理解不深,不知道该如何用好,总感觉各个功能间总是“鱼与熊掌不可兼得”。
  于是想在这里集中整理一下我的问题:

  1. 版本1.7.2 与 版本1.6.4,它们的默认 HanLP.segment(StandardTokenizer) 效果是一样的吗?它们都是基于98年人民日报标注语料的统计模型分词吗?
  2. HanLP.segment 分词结果中的“词性”(包括人名和地名)是怎么来?都是根据 CoreDictionary 和 CustomDictionary 中确定来的吗?所以这里并不涉及到 HMM、CRF 对吗?所以虽然一个词可能会有多个词性,但在 HanLP.segment 结果中每个词一定是一个固定的词性?
  3. 版本1.7.2 利用亿级语料训练的分词模型只是应用到了 NLPTokenizer 上吗?parseDependency 也是基于这个语料吗?还是说 parseDependency 是基于 NLPTokenizer 的结果?
  4. “自定义词典在所有分词器中都有效”,但我发现 1.7.2 版本中利用 CustomDictionary.insert 后并未生效,但修改 dictionary/custom/机构名词典.txt 后确实会生效,相关代码在下面。
  5. 相同的句子在在线演示上与1.7.2版本的 NLPTokenizer 结果不同,相关代码在下面。

复现问题

步骤

  1. 首先……
  2. 然后……
  3. 接着……

触发代码

    public void testIssue1234() throws Exception
    {
        # 问题 4
        NLPTokenizer.ANALYZER.enableCustomDictionary(true);
        CustomDictionary.insert("钟正", "nr 1");
        CustomDictionary.insert("财新智库莫尼塔研究", "ntc 1");
        System.out.println(HanLP.segment("财新智库莫尼塔研究董事长、首席经济学家钟正生表示"));
        System.out.println(NLPTokenizer.analyze("财新智库莫尼塔研究董事长、首席经济学家钟正生表示"));

        # 问题 5
        NLPTokenizer.ANALYZER.enableCustomDictionary(false);
        // 注意观察下面两个“希望”的词性、两个“晚霞”的词性
        System.out.println(NLPTokenizer.analyze("支援臺灣正體香港繁體:微软公司於1975年3月1日由比爾·蓋茲和保羅·艾倫創立。"));

    }

期望输出

# 问题 4
# 此部分我理解 “钟/n, 正生/v” 的切分方式
[财新智库莫尼塔研究/ntc, 董事长/nnt, 、/w, 首席/n, 经济学家/nnt, 钟/n, 正生/v, 表示/v]
# 此部分应该是“财新智库莫尼塔研究/ntc” 和 “[钟/n, 正生/v]/nr”
财新智库莫尼塔研究/ntc 董事长/nnt 、/w 首席/n 经济学家/n [钟/n, 正生/v]/nr 表示/v

# 问题 5
# “比爾·蓋茲” 应该是 nr
支援/v [臺灣/ns 正體/n 香港/ns 繁體/n]/nt :/w [微软/nt 公司/n]/nt 於/p 1975年/t 3月/t 1日/t 由/p 比爾·蓋茲/nr 和/c 保羅·艾倫/nr 創立/v 。/w

实际输出

# 问题 4
# 此部分没问题
[财新智库莫尼塔研究/ntc, 董事长/nnt, 、/w, 首席/n, 经济学家/nnt, 钟/n, 正生/v, 表示/v]
# 此部分 CustomDictionary.insert 后对 NLPTokenizer 未生效, 而且为什么“董事长”词性是 n 而不是 nnt?而且为什么不是“[钟/n, 正生/v]/nr”
[财新/j 智库/n 莫尼塔/n 研究/vn 董事长/n]/nt 、/w 首席/n 经济学家/n 钟正生/nr 表示/v

# 问题 5
# 注意 “比爾·蓋茲/nz”
支援/v [臺灣/ns 正體/n 香港/ns 繁體/n]/nt :/w [微软/nt 公司/n]/nt 於/p 1975年/t 3月/t 1日/t 由/p 比爾·蓋茲/nz 和/c 保羅·艾倫/nr 創立/v 。/w

而在线演示中是正确的 nr

其他信息

@hankcs
Copy link
Owner

hankcs commented Apr 5, 2019

  1. 两个版本的HanLP.segment(StandardTokenizer)是一样的,都是训练自2014人民日报的ViterbiSegment。
  2. 是来自于CoreDictionary和CustomDictionary,默认输出词语词性的第一个。如果开启了enableSpeechTagging,则采用HMM模型执行词性标注。
  3. 只应用到了NLPTokenizer,准确来讲是PerceptronSegmenter。这个语料库不含句法标注,parseDependency默认采用NLPTokenizer。
  4. 这是由于bug(已修复)引起的。
  5. HanLP提供训练接口,应用于不同行业语料可以训练出无数多种分词模型、词性标注模型、命名实体识别模型、句法分析模型……这些模型可以拥有无数多种组合,匹配不同行业。演示网站未必跟发行版使用同一套模型,电商公司未必跟游戏公司使用同一套模型。演示站2019年04月05日13:09:12使用的模型在 依存关系拆分与官网不一致 #1135 下共享了,未来使用什么模型随时可能变化。

任何产品级的工具都需要一定的复杂度去面对复杂的现实问题,HanLP也不例外。特别是对NLP这个复杂的问题,不可能依靠一套算法通吃。

@hankcs hankcs added the question label Apr 5, 2019
@wenfeixiang1991
Copy link
Author

@hankcs
嗯嗯,了解了,还有几个问题:

  1. 针对 NLPTokenizer,它的「分词结果」和「词性结果」都是不依赖于 CoreDictionary 的对吗?但 Insert 到 CustomDictionary 中的「词」与「词性」会对所有分词器生效。
  2. 我注意到 NLPTokenizer 得到的「词性集」貌似与StandardTokenizer 的「词性集」是不一样的,NLPTokenizer 的「词性粒度」貌似比较“粗”。
  3. “演示站2019年04月05日13:09:12使用的模型在 依存关系拆分与官网不一致 #1135 下共享了”,我看那个是98年6个月的大模型,但演示效果针对上面的那个例句却比现在这个亿级(目前已知最大中文语料)语料训练的模型要好,是这样吗?也就是说 NLPTokenizer 的效果其实是完全根据「标注语料集」决定的,尽管现在这个语料数据大,但仍有可能存在很多标注不准确的因素造成比爾·蓋茲/nz
  4. 在使用 NLPTokenizer 时,我对比发现
# 代码
System.out.println(HanLP.segment("董事长李克强将出席博鳌亚洲论坛2019年年会"));
System.out.println(HanLP.segment("董事长李克强将出席博鳌亚洲论坛1975年3月1日的年会"));
System.out.println(NLPTokenizer.analyze("董事长李克强将出席博鳌亚洲论坛2019年年会"));
System.out.println(NLPTokenizer.analyze("董事长李克强将出席博鳌亚洲论坛1975年3月1日的年会"));
System.out.println(NLPTokenizer.analyze("董事长李克强将出席博鳌亚洲论坛2019年3月6日的年会"));

# 结果  注意 “董事长” 与 “博鳌” 的词性、 注意「日期」
[董事长/nnt, 李克强/nr, 将/d, 出席/v, 博鳌/nz, 亚洲/ns, 论坛/n, 2019/m, 年年/d, 会/v]
[董事长/nnt, 李克强/nr, 将/d, 出席/v, 博鳌/nz, 亚洲/ns, 论坛/n, 1975/m, 年/qt, 3月/t, 1/m, 日/b, 的/ude1, 年会/n]
董事长/n 李克强/nr 将/d 出席/v 博鳌/ns 亚洲/ns 论坛/n 2019/m 年/q 年会/n
董事长/n 李克强/nr 将/d 出席/v 博鳌/ns 亚洲/ns 论坛/n 1975年/t 3月/t 1日/t 的/u 年会/n
董事长/n 李克强/nr 将/d 出席/v 博鳌/ns 亚洲/ns 论坛/n 2019年3月6日/t 的/u 年会/n

Q:为什么 「1975年3月1日」与「2019年3月6日」的 NLPTokenizer 结果 不一样,原因是什么? 这种同种类型的句式但却切分结果的「不确定性」,不会造成NLP任务处理上的困扰吗?
  1. CoreDictionary 与 CustomDictionary 中的词都是从标注的语料库中得到的对吗?尤其是 CoreDictionary 中的「词」、「词性」与「词频」,是 ViterbiSegment 算法的基础。但为什么现在的标注语料都“亿级”了,为什么 CoreDictionary 与 CustomDictionary 没有增加呢?难道 CoreDictionary 与 CustomDictionary 本身都不会对 NLPTokenizer 造成影响吗?
  2. 我在 CoreDictionary 中加了词并删掉了相关的两个 bin 文件,但发现在 NLPTokenizer 中未生效。NLPTokenizer 有个开关NLPTokenizer.ANALYZER.enableCustomDictionary(false);,“关掉false”最大的好处是什么啊?总觉得关掉没有什么好处。
  3. NLPTokenizer、CoreDictionary 与 CustomDictionary 的关系可能就是造成我“鱼与熊掌”困扰的原因。

@hankcs
Copy link
Owner

hankcs commented Apr 6, 2019

  1. 是的,对。
  2. StandardTokenizer为人民日报2014,NLPTokenizer为1998。
  3. 不是,演示站用的分词模型也是亿级的,不同之处是词性和NER用的是98年6个月的。
  4. 这种问题是所有ML模型的通病,泛化能力不够。HanLP2.0将采用深度学习模型去替代现有的模型,减少但不可能完全消灭这种问题。
  5. 是的。因为亿级语料只标注了分词,没有标注词性。
  6. CoreDictionary不影响由字生词的分词器。关于词典的坏处。评测分词器准确率的时候一定要关,因为1、词典是作弊2、词典的分词标准与目标语料库不同。然而市面上大部分所谓“开源分词器评测”都没有这个观念,得出的都是错误结果。
  7. 真正做NLP的人用NLPTokenizer。

@wenfeixiang1991
Copy link
Author

👍👍👍 受教了!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants