自然语言处理(NLP)

指一个词所有形式的集合, 表示一种关联关系.

例如"go"的lexeme是集合{"go", "goes", "going", "went", "gone"}.

指约定好的代表相关lexeme的一个词.

例如约定"go"作为代表, 表示"go"和"went"等词共有的lexeme.

词干提取器被称作Stemmer.

与词形还原相比, 更接近工程领域.
现实中的信息检索普遍使用词干提取而不是词形还原:
词形还原可能存在不准确的情况, 而词干提取不存在不准确的情况,
因为它会将具有相同词干的不同词都映射到同一个词元上, 这有效提高了召回率.
词干提取器往往比词性还原器更快, 更小, 更简单.

实现方式:

  • 基于规则, 例如将以"ing"结尾的单词根据规则变换.
  • 基于语料, 从一个单词映射到对应的lemma.

https://snowballstem.org/

一种可以编译为多种语言的字符串处理语言, 专用于创建词干提取算法.

其作者Matrin Porter是Porter Stemmer算法的发明者,
该算法是最常用的英语词干算法, 被广泛使用.

来自Hunspell拼写检查库的词典.

一个词典包括两个文件:

  • .aff 词缀(affix)
  • .dic 词典(dictionary)

https://github.com/michmech/lemmatization-lists

一个可以免费下载的大型英语词汇数据库, 提供同义词集.

将一个词转为lemma的过程被称作词形还原(lemmatization, 动词为lemmatize).
词形还原需要理解词在上下文中的词性, 从而准确确定词的词元.

与词干提取相比, 更接近学术领域.

这种技术的缺陷在于, 它是对原文进行基于统计的抽样, 其结果只是对文本句子的提取.
这种技术的结果并不令人满意, 因为它是建立在删除内容之上的, 因此一定会有造成信息的丢失.

理想中的文本摘要技术是建立在理解内容的基础上的总结, 而不是提取.
但基于AI的简写又能在多大程度上满足人们的需要呢? 这仍然是未知的.

GPT-3具有理解内容并将内容转写为更短的文本的能力,
因此GPT-3可能会改变文本摘要的游戏规则.

字节对编码是一种数据压缩技术, 也用于深度学习的NLP模型.

根据情况, 可以像拉丁字母那样直接使用逐汉字, 连续二汉字, 连续三汉字进行索引.
与专门的中文分词方法相比, term数量太大, 查询效率较低.

通过查找词典里已有的词进行切分.
词语越长, 匹配的优先级就越高.
与其他方法相比, 在速度上很有优势.

  • 基于词典规则的系统无法正确处理有二义性的短语.
  • 无法处理未记载于词典里的新词.

以下算法的性能瓶颈在于"判断词典是否有这个词", 因此有多种实现方式:

  • Set: 牺牲时间换空间
  • Map: 牺牲空间换时间
  • 字典树(Trie): 仅在静态语言里可用(内存结构有优势)的推荐做法, 需要提前编译
  • 前缀树(Prefix Trie): 在字典树的基础上上进一步加快速度, 编译时间更长.
  • AC自动机(Aho-Corasick): 优于前缀树, 内部含有后缀树.
  • BinTrie: 首字散列其余二分的字典树, 出自HanLP.
  • 双数组字典树(DAT, Double Array Trie): 速度最快的结构, 实现难度大, 编译难度大.
    DAT的经典实现是Darts(Double-ARray Trie System)

该实现通常会暴露一个类似commonPrefixSearch的方法, 用以进行前缀匹配,
用于切分时, 只需要创建一层循环, 将子字符串投入匹配:

for i in 0..text.len() {
for (id, length) in dat.common_prefix_search(&text[i..]) {
// ...
}
}

朴素完全切分是最简单的完全切分(找出文本中的所有单词)算法.

其具体做法是创建两层嵌套循环, 第一层作为起点索引, 第二层作为终点索引,
遍历被查找字符串中的所有子文本, 输出词典中的所有匹配项.

由于复杂度是 O(n^2), 性能最差, 稍微长一点的文本就会造成性能问题.

完全切分方式之一, 通过AC自动机找到所有匹配项.

完全切分方式之一, 通过ACDAT找到所有匹配项.

性能最高的完全切分方式, 通过DAT找到所有匹配项.

在分词时正向遍历匹配最长的词, 每当成功匹配到一个最长词后, 从这个词之后的字符继续匹配, 直到结束.

正向最长匹配的逆向版本, 在分词时逆向遍历匹配最长的词.
在实践中效果比正向最长匹配好很多, 但仍然不完美.

为解决正向, 逆向最长匹配的问题而出现的一种融合了正向逆向两种方法的匹配规则:
同时进行正向和逆向匹配.
如果结果的词数量不同, 则取数量更少的那个作为结果.
如果结果的词数量相同, 则取字符更少的作为结果.
如果字符数量也相同, 返回逆向最长匹配的结果.

在实践中效果和逆向最长匹配差不多, 有时甚至会比逆向最长匹配差.

https://github.com/NLP-LOVE/Introduction-NLP
https://github.com/NLP-LOVE/ML-NLP

在深度学习的情况下, 训练模型不需要预先分词,
不分词(逐字符)的训练结果比分词的要好.

由Google于2019年发布的NLP模型.
它直接对中文文本内容进行基于字符的tokenize.

句子中出现了不存在于词典中的词.

能够从文本里发现词与词的关系, 从而发现新词的模型.

最流行的方案是将单字分为Begin(B), Middle(M), End(E), Single(S)四种类型.

根据词性将词分类.

  • 汉语中一个单词具有多个词性是很常见的
  • 依赖人力进行标注, 可用的语料库很少
  • 学界对于标注规范存在分歧

提取文本中高频出现的词汇作为关键词(需要过滤掉停用词).

高频词不等于关键词, 因此通常会使用TF-IDF.

TF-IDF是词频和逆向文档频率的乘积, 旨在定义文档中词语(或短语)的重要性.

TF-IDF的值与词语在文档中出现的次数成正比, 与在整个语料库里出现的次数成反比.
在整个语料库里常见的词语(主要是日常用语)会被降权, 在少数文档中被反复提及的词语(例如专用术语)会被提权.

TF-IDF有多种计算方法, 最简单的一种是这样的:
词频 = 词语出现的次数/该文档的总词数
逆向文档频率 = 文档总数/出现词语的文档数量
TF-IDF= 词频 * 逆向文档频率

需要大型的语料库来得出TF-IDF值.

TF-IDF的一种改进变种, 不仅能衡量一个词的重要性, 还能衡量多个词的重要性.

词典的最佳来源是权威词典, 例如各种人工编撰出版的词典;
其次是学术结果, 例如知识图谱;
然后才是维基百科之类的众包内容源, 以及输入法词库这样的经过实战的内容源;
而互联网上由一般用户分发的词典基本上是不可用的垃圾, 绝对不应使用.

Unix系统的 /usr/share/dict 目录包含系统上安装的词典, 此处的词典会被一些需要词典的程序使用.

Debian系发行版通过wordlist包提供词典.

https://freedict.org/

内容距离上次更新久远.
提供的语言到语言到词典, 数据需要从TEI格式里手动分离.

http://wordlist.aspell.net/

内容距离上次更新久远.

北京大学语言计算与机器学习研究组推出的中文分词工具包.
准确率高于jieba和THULAC.

性能比jieba差, 内存占用量也高于jieba.

清华大学自然语言处理与社会人文计算实验室推出的中文分词工具包.
准确率高于jieba.

据称jieba的词性标注参照了 ICTCLAS 汉语词性标注集.

jieba项目已知存在诸多问题:

  • 技术陈旧.
  • 算法存在缺陷.
  • 自带词典的词性标注错误百出.
  • 词典收录了一些莫明其妙的词汇.
  • 词典大小不匹配: idf词典的内容少于主词典.
  • 用户词典优先级太高, 会覆盖掉默认词典里相同的词, 这导致一些原本有词性的词丢失词性标注.
  • 分词功能不支持自定义停用词.
  • API缺乏实用性:
    • 无法在不分词的前提下查询词典里的项目, 例如查找单个词汇的td-idf值.
    • cutForSearch(在不使用HMM的情况下)和cutAll会把单词切分为字母, 简直无法理解.
    • cut系列的API太高级, 以至于无法生成适合连续短语搜索的分词结果.
  • 尽管jieba的各种实现速度都比较快, 但准确性往往大幅弱于其他解决方案.
  • cppjieba的实现没有足够的内存优化, 载入字典后会占用大量内存.

jieba自带的词典长期没有更新, 最旧的词典已经8年没有改动了.

dict: 主词典, 带权重和词性标签.
hmmDict: 隐式马尔科夫模型, 建议使用默认词典.
userDict: 用户词典, 建议自己根据需要定制.
idfDict: 关键词抽取所需的idf信息.
stopWordDict: 关键词抽取所需的停用词列表.

词典需是UTF-8编码的文件, 格式如下:
{词语} {词频(可省略)} {词性(可省略}}

示例:
创新办 3 i
云计算 5
凱特琳 nz

停用词词典只适用于TFIDF和TextRank, 不可用于分词.
停用词词典没有外部接口, 需要修改源代码才能实现.

专门开发了更有效率的DAT实现cedarwood以提升性能,
但其性能(v0.4.5)已经远慢于其他实现:

  • yada(v0.5.0, 性能为cedarwood的2~3倍)
  • daachorse(v0.4.3, 性能为cedarwood的4~5倍)

该实现的分词存在一些问题, 无法分词 test-1 这样的文本:
https://github.com/messense/jieba-rs/issues/83

停用词词典只适用于KeyExtractor和TextRankExtractor, 不可用于分词.

使用最新NLP技术的NLP项目.

由Java编写, 使用经典NLP技术实现.

由Python编写, 使用深度学习技术实现(即使用Python的原因).

载入模型的速度相当慢, 只支持按句子分词.
对于开放和封闭两个模型来说, 一条简单的句子在CPU下需要几十毫秒乃至数百毫秒才能完成分词,
实在难以作为一个分词组件使用.

Python主要的NLP工具库之一, 包含大量可用的功能.
被认为适合研究人员.

Python主要的NLP工具库之一, 只提供最好的方法.
自称为工业级, 一开始就以投入生产为目的开发, 并且有较好的准确度.

在官方的速度比较中, 性能远优于Stanza, Flair, UDPipe.

中文分词时底层可使用逐字符分割, jieba分词, pkuseg.

Java主要的NLP工具库.

深度学习NLP工具库.

Hugging Face旗下的Rust分词库, 基于NLP模型.

https://www.hankcs.com/nlp/corpus/several-revenue-segmentation-system-used-set-of-source-tagging.html