中文分词与关键词提取概述

本文基于题库查重需求实现过程及《NLP自然语言处理原理与实践》学习过程总结得出。定有不足之处,恳请指出。

介绍

中文分词是自然语言处理(NLP)在中文环境下,首要解决的问题。主要难点为中文不同于英文,存在明确的分隔符(如空格)用于切分词语,且不同的切分方式,不一定存在语病,举个例子:

  1. 结婚的/和尚/未结婚的人。
  2. 结婚的和尚未结婚的人

基本概念

评价指标

一般,中文分词从Precision、Recall、F-score三个维度评价。一般我们比较关注的是F-score。如下图(图片来源 https://github.com/lancopku/pkuseg-python),该图描述了三种中文分词工具的三个指标,便于用户比较三个工具关于某特定数据集合进行分词的结果评价指标。
在这里插入图片描述

有时 F-score 也被描述为 F1-Measure,这两者是同一概念。

模型评价标准

对于模型(包括语义模型、分类/聚类模型等),一般有四个指标判断模型效果:

  1. Accuracy(准确率)——总样本数被正确判定数占比
  2. Recall(召回率)——总正确数被正确判定数占比
  3. Precision(精确度、精确率)——总结果数被正确判定数占比
  4. F1-Measure——Precision与Recall的harmonic mean(调和平均数; 倒数平均数),意义在于同时关联被正确判定数、被错误判定数、总结果数之间的关系。

常见方案

中文分词有两大思路:

  1. 基于语义实现
  2. 基于统计概率

受编程语言发展历程、社会环境、市场需求等多种因素影响,目前主流的中文分词工具以python、java居多。常见分词工具如下列出,这里过多copy网上介绍。私以为支持自定义词库、词频的情况下,这些项目经过多年在开源社区的迭代,应该都能满足多数不太复杂的语言分析场景。

  1. 结巴分词——原生基于python,也有go、java、php、Node.js版本,但都非官方维护项目,以php版本为例,目前使用后发现其存在更新缓慢、代码bug、代码缺乏灵活性、缺乏可扩展性等问题。
  2. HanLp——基于Java开发。
  3. funNLP——基于python开发。
  4. sego——基于go语言开发。
  5. scws——原生即为php,github中最后一次更新已为2016年,实际中英数混合文本使用效果远不如结巴分词的php版本。

关于中文分词中的HMM和Viterbi,可以参考我的另一篇博文HMM、Viterbi与中文分词,欢迎各路大佬提出意见,交流学习。

应用案例

敏感词检测

敏感词检测一般有两种方式实现。

  1. 基于词库+有限自动状态机(Deterministic Finite Automaton, DFA)

    ​ 由词库构建前缀索引森林,再基于DFA+敏感词前缀索引森林遍历判断敏感词。

  2. 基于词库+中文分词工具

    ​ 相比于有限自动状态机。实际上,该方法关键在于只对分词结果中的各个词进行敏感词判定。从效果来看,其准确程度依赖于敏感词检测训练结果(词频)。但不会像DFA一样不考虑上下文地进行匹配,显得更智能些。当敏感词检测结果会以某种形式反馈给用户时,该使用该方法实现的用户体验会比基于DFA实现的用户体验更好。

但可以看出,不论依赖上述哪种方式,根本上都要首先解决词库的问题。关于词库,在github中存在一些私人上传的词库,也可以尝试检索微博、百度、腾讯等企业团队公布的免费敏感词库。但不论哪种来源,敏感词总是有时效性的,需要不断维护。

关键词提取

关键词提取其实是一个细分的应用场景,其更上层应用多见于长文本关键词检索(不可直接套用于ES的文本检索原理,ES索引分词方式和检索分词方式依赖于分词工具的选取)、文本相似性检索、自动摘要。常见算法有TF-IDF和TextRank算法,结巴分词同时支持这两种算法。
关键词的准确度依赖于分词准确度。此外,关于TF-IDF,关键词提取的一个基本方法是:一个词在该文本中出现越多,其他文本中出现越少,该词作为关键字的概率就越大。因此,在实际应用中,处理有限文本集或某一垂直领域文本集时,先对该有限文本集或垂直领域样本统计idf词典,再基于该idf进行关键词提取,能有效提高Precision。

相似文本去重

相似文本去重有很多种方案,这里仅列举几种重要思路。实际应用中,要结合算法效率、数据量、实现难易程度、输出方式等多种因素考虑。

  1. 基于关键词提取,基于关键词计算相似哈希。当两个文本相似时,提取出的关键词也是相似或相等的,对应计算得到的相似哈希值也应该是相似或相等的。常见于海量数据查重,但不可能百分百可靠。对应判断相似或相等问题,可以转为对相似哈希值判定相似或相等问题。
  2. 暴力,每个文本都与集合中的其他文本计算汉明距或编辑距distance,当distance与总长度的比值大于一定阈值时,则判定相似。在文本不太复杂时,数据量不多时,实现成本低、算法简单、经过针对具体文本特点进行优化后的Precision可以很高。可以想象,当文本集合空间大小为n,则需要执行 n 2 n^2 n2 次比较。

实际上,相似文本最大的难点,个人认为不在于性能,而在于Precision。以下文为例:

  1. 请选择下述正确的选项。
  2. 请选择下述不正确的选项。

这里看来,两者仅一字之差,实际含义却天差地别。这种问题实际可能多见于短文本的相似性比较中。关于这点,目前个人没有比较好的解决方案,欢迎有想法的同学提出建议。我临时的解决办法是,尽管二者相似,但sim hash值依然是不想等的,(分词能区分正确、不正确)。因此,只要sim hash值不等,就暂不归类,但这显然降低了Recall。

常见问题

Q: 基于中文分词工具进行分词,性能会很低吗?

文本不是很长时,分词性能是很高的。以结巴分词为例,我在 php7.3 环境下,1000条平均长度200左右的文本,请求数据 + 分词 + 关键词提取 + SimHash计算 + 入库(batch update)仅需 1s 左右,单条长度不超过300的文本,分词时间不足 1ms。现在的cpu性能很强,如果不是任务太多,cpu资源有限的情况下,我们要关注的往往不是分词性能,更多的在于**自己实现的代码的时间复杂度;数据库获取效率(如MySQL select是否优化);数据库写入效率;是否发生外部请求,如果是,则外部请求消耗的时间。**当然,具体问题要视实际情况具体分析,导致性能问题的可能性一定不仅限于我所述这几点。

Q: 中文分词工具是开箱即用吗?

我的经验是,如果只是用于学习,可以认为开箱即用。但如果希望用于敏感词检测或其它垂直领域(包括但不仅限于文本去重、文本检索等)。建议是,尽可能多的收集可靠的垂直领域的词库,并基于持有的数据统计一份词库,加载到分词工具中(一般开源分词工具都有提供加载用户自定义词典的接口),可以有效提高分词、关键词提取的F-score。除此之外,实际应用中,存在很多特殊符号、无用符号时,建议先增加逻辑代码处理一下(比如转义文本中的html entity、标签、多余的不可见字符等),也可以帮助提升F-score。

Q: 对于基于NLP的功能,F-score多少合适?

实际上,我也不知道F-score多少合适。可能结合实际业务场景,更容易解答这个问题(如果是学术研究,那显然是越高越好)。我的经验是,F-score达到90%以上后,每提高1%,成本都是指数上升的(需要反复的调整取词策略、完善词库和词频文件、重新统计/分析相关字段),而每一次调整,都不一定具备普适性。因此,个人建议结合实际场景判断。比如,题库有1300W+数据,Precision已经提高到了90%+(针对特定学科的特定题型,甚至可以认为达到99%+)。这时预估错误数据关于总数据占比是很小的(可能小于1%),那么,这些查重数据暂时采用隐藏处理(软删除),后续发现时再恢复即可,这种方式对整个产品体验影响并不明显。

Q: 实际应用中,Precision要求很高怎么办?

这种问题在生产环境中可能比较常见。比如说,有1000W+数据,即使只错误判定了1%,也会导致 10W条数据别错判,10W条错误数据也可能带来很大的损失。这时候我们可以主动分析文本集合特征,通过针对性的修改工具代码;补充词库;增加停用词;修改词频等方法,提高拟合度(可以参照上述问题的解答:中文分词工具是开箱即用的吗?)。这可能会造成过拟合问题,但我认为,如果只是针对已知且有限的数据集合进行处理,过拟合并非坏事。

猜你喜欢

转载自blog.csdn.net/qq_23937195/article/details/102586257