陈颂光
全栈工程师,能够独立开发从解释器到网站和桌面/移动端应用的各类软件。
关注我的 GitHub

自然语言处理浅谈

由于人们之间普遍以自然语言的形式交流,互联网和各种数据库中的这些非结构化数据中蕴藏了价值连城的大量信息。然而,对于计算机而言,理解自然语言相当困难,实际上这可能正是人工智能的核心。然而,我们仍然能让计算机不懂装懂并做不少事情。

文本的层次结构

文本可以分为由小到大的不同单位:

  • 字符是文本最基本的组成单位,例如中文的“我”和英文的“a”都是字符。
  • 词素是介于字符与单词间的单位,例如英文中单词“undoable”可视为由词素“un”、“do”、“able”组成。在一些语言如中文,词素这一级可以忽略不计,但对于词素众多而字典资源稀缺的语言,一些通常在词法层面做的处理要改在词素层面做以克服数据稀疏的问题。
  • 单词是有单独意义的最小单位,例如中文中的“飞机”和英文中的“aircraft”。
  • 句子表达断言、疑问、请求等。例如中文中“香港是中华人民共和国不可分割的一部分。”、“请付100块隧道费。”、“你在干嘛?”等等。
  • 段落。在现在主流的语言中,段落间有额外的空白或者由额外的缩进开首,有时还有编号,因而把文本分解成段落通常并不是太困难。

常规文本处理技术

文本理解

  1. 语言检测。由于不同语言的差异,先检测语言再用针对对应语言的处理效果通常比较好。
    • 方法
      • 基于机器学习的方法。先收集大量已知语言的文本(例如来自维基百科),并统计其中各n-gram(如“万里长城永不倒”分成2-gram“万里”、“里长”、“长城”、“城永”、“永不”、“不倒”)的频率,然后对于未知语言的文本,按其中各n-gram的频率用朴素贝叶斯分类器或其它分类器决定文本最可能属于哪个语言。
    • 工具。语言检测虽然原理上简单,但某些广泛使用的语言检测工具包所用样本的数据量小得可怜或者模型太小(如连中文常用字都不完整),导致准确性不如人意。
  2. 分句。通常来说每个句子表达一项知识,因而有时需要把段落分成句子。
    • 方法
      • 基于规则的方法。在现在主流的语言中,句子由特定的标点符号(如句号、问号或感叹号)分隔,因而把文本分解成句子通常并不是太困难,只是更准确的分句方法还要考虑句点在引号内或用作小数点之类的情况。但这不是必然的,如古汉语中往往不用标点符号。
      • 基于机器学习的方法。借助机器学习的方法如隐Markov模型,利用大量已知断句位置的段落去训练分句器。
    • 工具
      • Apache OpenNLP提供了训练分句器的工具
      • Java平台的java.text.BreakIterator类提供了一些语言的分句算法
  3. 分词。单词是众多文本处理的基本单位,因为把句子分解成单词序列是重要的。把文章看作单词集合的词袋模型虽然简单,但却已经足够出色地完成众多文本处理任务。
    • 方法
      • 基于规则的方法。对于一些语言如英文,由于单词间由空白分隔,可以按模式分割。不过有时也需要处理一些特例,如把缩写“don’t”转换为“do not”。
      • 最长前缀切分。对于中文这种语言,通常要借助词典(词典即由已知单词组成的列表。虽然可以人手编辑字典,但网络上大量的文本使自动生成字典成为可能,因为单词的特征在于它们出现的频率异常地高。)进行,即由左到右依次寻找在字典中的最长字符序列。这不一定给出合理的分词,例如在单词不在词典中(通常是地名和人名等专有名词)或有歧义时(如“走上 海 街”和“走 上海街”)。
      • 机器学习。借助机器学习的方法如隐Markov模型,利用大量已分词句子去训练分词器。
    • 工具
    • 资源
  4. 词性标记。按照词的作用,词可以分为名词、动词等词性。
    • 方法
      • 机器学习。借助机器学习的方法,利用大量已标记各单词词性的句子去训练词性标记器,例如基于隐Markov链的模型。
    • 工具
  5. 规范化。在一些语言中,单词根据词性、时态、单复数、性别等等有不同形式,但对于信息检索、文本分类与聚类之类基于袋的应用,由于不同形式对应类同的概念,往往被归一化为某种标准形式。
    • 方法
      • 词干提取。按人手制订的规则进行规范化,例如依次替换各种后缀。
      • 原形化。借助机器学习的方法,利用大量已知词性和词元的单词去训练一个词元化器。
    • 工具
  6. 消歧义。经常出现一词多义的情况,对于缩写和名字尤其严重,如可能有很多个人叫“张三”、很多个城市都有“北京路”、很多所大学简称“中大”。
    • 方法
      • 基于机器学习的方法。通过统计各个词义对应单词与其它词一起出现的概率,可以构建词义分类器。为了区分一个单词到底是哪个词义,通常的策略是看它邻近的单词,比如说如果“中大”附近有“大学站”、“新亚”或者“港独”则很可能指“香港中文大学”,而如果“中大”附近有“新港西路”或者“1924”则很可能指“中山大学”。
    • 资源
      • 维基百科和百度百科。它们对不同词义有不同页面,可以近似认为页内该单词都是那个词义,这可作为一个出发点。
  7. 共指消解。有时文章中多个词可能代表一个相同的实体,如“他”、“老黄”、“厂长”可能代表同一个人。同时“今天”之类表示时间的词也可能需要转化为具体日期。
    • 方法
      • 基于机器学习的方法。通过统计表示同一实体的不同词组(如让“他”对应到人实体)及它们间的位置关系(通常比较接近),可以构建共指消解器。
    • 工具
  8. 语法解析。经典的语法理论大体上把自然语言看作上下文无关语言,即句子视为由一些类似句子:=主语 谓语 宾语主语:=名词|代词之类的产生式推导出来。于是为了可以把句子解析为语法树。
    • 方法
      • 基于规则的方法。经典的方法是按语法书上的各种语法规则写成解析器(用Prolog之类逻辑编程语言相当容易写),问题是鲁棒性不足和解析隨规则增多而变慢。
      • 基于机器学习的方法。通过收集大量语法树来学习语法规则。
    • 工具
  9. 知识表示。把文本的语义表示为方便机器处理的形式,如语义网,是一个有点遥远但意义重大的目标,这可视为完全理解文本。
    • 资源
      • Wikidata企图从维基百科提取语义网

Apache UIMA(非结构化信息管理应用)为分析自然语言文本提供了一个框架。

文本生成

如果只用生成没有太大意义的文本,例如垃圾邮件或者网上评论,用基于Markov链的方法生成随机文本即可。例如假定我们收集了汉语的1-gram和2-gram频率数据,则为了一个随机句子,首先按各汉字的频率分布随机选出一个字作为开首,然后不断按书籍刚选出的几个字情况下下一字的频率分布随机选取下一字,直到选取到句号。

如果只需要生成特定用途的文本,例如天气报告,可以直接编写规则如现在是北京时间(小时)点(分钟)分

语音合成

语音合成也就是把文字转换为读出来的声音,用于各种话音播报系统,也用于让视障人士使用计算机的屏幕阅读器。通用语音合成通常的流程为:

  1. 利用预先制订的规则把文本转化为音素序列
  2. 利用预先制订的规则把各音素转换为声音(还可能按标点符号位置插入停顿)
  3. 利用预先制订的规则微调频率、强度、语速等来模拟不同性别、年龄、语气发出的声音

关于规则的形式,可以参考语音合成软件eSpeak NG的文档,它已经支持超过100种语言。虽然需要为每种语言分别编写规则,而且编写时需要一些专门技能,但这基本上是一次性的工作,工作量可以接受,因此要标注大量语音样品的机器学习方法反而没有必要。

语音合成的效果往往显得生硬,但毕竟一般人能听懂它在说什么。

语音识别

与语音合成相反,语音识别要把声音转化为文字。由于输入不是精确的,加上一音多字的情况远比一字多音的情况常见,这使语音识别更为困难。语音识别可用于输入文本(较常见于移动设备),也可用于情报工作。通用语音识别通常的流程为:

  1. 利用停顿把声音切割为音素序列。
  2. 识别各音素。这可能涉及用大量标注语音样本训练出的分类器,也可能是与音素发音规则(语音合成用的)对比。
  3. 把音素序列转化为一些可能的文本。这可以通过字典加规则完成。
  4. 利用语言模型选取最符合语言特征的文本。最基本的语言模型就是n-gram模型,如“一个”在中文文本中的频率远比“医铬”高,所以选取前者而非后者。不过,小概率的n-gram还是有可能出现的,所以无法做到完全准确,对姓名之类缺陷特别明显。

语音识别已经能达到一定的准确率,足以用于一些非正式场合或有后期人工验证的场合,例如制作字幕时校对往往比重新输入更省时间。另外,对于特殊用途的语音识别系统(如某些声控家电),由于只用区分少数几个命令,准确率可能可接近完美。

除了识别语音的内容,通过对不同变量训练分类器,还可以企图识别语音的其它属性:

  • 口音
  • 语气
  • 说话者性别
  • 说话者年龄
  • 说话者

这些属性在情报工作中可能有参考价值。然而,由于声音容易通过录音仿冒,又有人练习伪声,声纹识别用于门禁系统之类并不安全。

关于语音识别的细节可以参考CMUSphinx

应用

拼写检查

拼写检查可能是自然语言处理技术中最早得到广泛应用的,众多字处理器都配备了这功能。通常分词后不出现在字典中的单词就视为是拼写错误。拼写检查系统还可以推荐读音类似的拼写建议,方法通常是找出字典中与之编辑距离最短的单词。对拼写检查感兴趣的话可以参考GNU Aspell。值得一提的是,拼写检查对提升光学文本识别(OCR)的准确率能起重大作用。

一些字处理器还有所谓的语法检查功能,但由于自然语言的准确语法难以给出,它们一般不会真的做语法解析,而是匹配一些已知病句模式。对语法检查感兴趣的话可以参考LanguageTool

摘要

对于长篇大论的文章,我们往往想了解文章大概在说什么才决定是否看下去。我们希望在没有小编的情况下自动生成摘要,由于文本生成的困难性,我们并不指望机器能自己写出总结,而是希望找出文章的中心句。通常的做法是先找出文章中频率特别高的词作为关键词,然后找出富含关键词的句子,另外也可能利用句子位置(如在段首或段末的句子更可能是中心句)、句子结构(如有“总之”的句子)等信息。

问答系统

早期的问答系统大多是基于人工设计规则的。不妨在Emacs中输入Alt-x doctor进入与精神科医生的模拟聊天,体验一下多年前的聊天系统能做到什么程度。这个写于1985年的程序原理上可追溯到始于1964年的ELIZA,它的源代码只有1625行左右,稍为读一下就可以发现它实际上先查找已知场景的关键词,然后按为各场景定制的规则之一生成回答,没有找到则用通用的句子或回忆前面的对话。

现在的聊天系统则主要依赖于巨大的数据量,即时通信、论坛、博客和其它社交平台的运营者能轻易采集大量真实的对话。于是当你向系统说一句话,它就用信息检索的方法搜索类似的一句话,然后把搜索到的话的回应返回给你。因为返回的实际上是别人写的话,这巧妙地避开了文本生成的难题,但几乎完全忽略上下文。一种修正是用前面对话中的词扩展查询。这可用于做客服系统以避免重复回答问题。

信息检索

随着信息量的增长,现在的一个重要问题是如何在众多文本中找出我们想要的。为了有效地进行查找,一般对文本分词后对各单词分别建立倒排索引(把单词映射到含该单词的文章列表),然后对每个查询找出含其中的单词的文章,返回评分最后的若干个结果。

目前,流行的开源搜索服务器软件都基于用Java语言编写的搜索引擎Apache Lucene

关于信息检索更详细信息参考《用Solr打造定制的搜索引擎》

文本检索会议(TREC)提供了一些可用于测试文本检索系统的数据集。

文本蕴涵

经常出现的情况是我们想要知道的并不是文献中字面上写的,例如我们可以从“林则徐卒于1950年”推断出“林则徐生于清朝”,文本蕴涵就是判断段文本是否蕴涵另一个句子或其反面,这类似于我们学语文时有阅读理解练习。现在文本蕴涵一般也用机器学习方法,按对齐分值判断,必要时通过扩展句子来模拟推理。

文本分类

出于各种原因,我们可能想自动把文章分成不同的类别:

  • 主题分类。例如综合门户网站可能想自然把文章归到不同类别并推荐给不同受众。
  • 情感分析。例如电商平台可能希望把买家的评论分为正面和负面的。
  • 阅读程度。例如教育软件可能按阅读难度把文章分成适合各年龄阅读的。

文本分类通常基于词袋模型。把每篇文章对应到由各单词TF-IDF组成的欧氏空间向量,然后用大量已知类别的文章训练常规的分类器如最近邻分类器、决策树、人工神经网络、支持向量机。

文本聚类

有时我们想把一批文章分成若干类,但事先我们并不预先确定是哪几类,只是希望类中的文档互相相似。文本聚类同样可以基于词袋模型。把每篇文章对应到由各单词TF-IDF组成的欧氏空间向量,然后用常规的聚类方法如k均值。聚类后往往希望为每类给个名字,这可以用词频加上Wordnet实现。

机器翻译

过往机器翻译使用人手编制的规则(用Prolog不难写出),但现在一般依赖于平行语料(即一些原文和译文对)用机器学习方法。

  • 基于短语的机器翻译。把句子切割成短语后分别翻译各短语,然后把它们重新排序,最后对各种可能的结果(必要时剪枝)评分后选取最合适的。
  • 基于树/语法的机器翻译。对句子进行语法解析后对语法树进行变换。这虽然的原理上更符合语言学,但现实中的解析器并不足够好。

以下是一些机器翻译系统:

  • Moses是一个基于短语或树的统计机器翻译系统
  • Apache Joshua是一个基于短语或语法的统计机器翻译系统
  • OpenNMT是一个神经网络机器翻译系统,提供在线演示

机器翻译还对许多其它自然语言处理任务有帮助,对于数据不足的语言,可以通过机器翻译来从其它语言的数据获得大数据集。

结语

与编程语言不同,由于自然语言是长期演化的群体产物,并且仍然在变化之中,因此缺乏概念完整性,准确概括自然语言是几乎不可能的任务。虽然人们企图为自然语言建立层次模型,但并不能把握自然语言中错综复杂的联系。

关键词 人工智能