本章涵盖内容:
- 人类语言的力量
- 自然语言处理(NLP)如何改变社会
- 机器现在能够出色完成的NLP任务类型
- 释放NLP魔力的盈利潜力与潜在危险
- 如何开始构建一个简单的聊天机器人
- NLP技术如何自我编程并变得更智能
言语具有强大的力量。言语能够改变思想,也能够改变世界。为了驾驭言语的力量,你需要理解自然语言处理(NLP)的工作原理以及如何使其为你所用。NLP的最新进展几乎在社会和商业的各个方面引发了技术爆炸。本章将为你揭示NLP的力量,并帮助你找到在工作中和生活中应用它的方法。
当你构建能够出色读写文字的机器时,它们开始显得像是人工智能(AI)。事实上,通常当人们在社交媒体或新闻中讨论“AI”话题时,他们实际上指的是对话式NLP。阅读本章后,你可能会更加审慎地使用“AI”这一术语,并看透一些常见的误解和与这一话题相关的“炒作”。你将学习如何构建NLP软件,以及如何将其集成到更大的系统中,以创造智能、有用的行为,帮助你实现目标。最重要的是,你将成为一个智能的AI和NLP系统用户,确保你的软件能够兑现其承诺。在学习如何构建读写文字的机器的过程中,你将融入NLP和对话式AI的强大领域,利用它们为我们所有人构建一个更美好的世界。
1.1 编程语言与自然语言处理(NLP)
编程语言与自然语言(如英语)非常相似。这两种语言都用于从一个信息处理系统向另一个系统传达指令。它们都可以在人与人、人与机器,甚至机器与机器之间传递思想。无论是自然语言还是编程语言,都具有“标记”(token)的概念,你可以暂时将其理解为单词。无论你的文本是用自然语言还是编程语言编写的,机器首先要做的是将文本分割成标记。在编程语言中,这些标记的种类通常较少——例如,Python编程语言仅使用33个保留关键字。相反,自然语言的词汇中可能有数十万个标记。
自然语言和编程语言也都使用语法。语法是一组规则,告诉你如何将单词按顺序组合成他人能够理解的表达式或语句。在计算机科学和英语语法课中,“表达式”和“语句”的含义相似;它们为你提供了一种创建语法规则以处理文本的方式。你可能在计算机科学中听说过“正则表达式”。在本书中,你将使用正则表达式来匹配各种文本中的模式,包括自然语言和计算机程序。但与你将学习的机器学习NLP方法相比,正则表达式只是入门级工具。
尽管编程语言和自然语言有这些相似之处,但要用机器处理自然语言,你需要新的技能和工具。编程语言是我们用来告诉计算机做什么的人工设计语言。计算机编程语言用于明确地定义对信息位(1和0)进行数学操作的序列,并以人类可理解的方式表达。它们是明确的——意味着一行代码只有一种理解方式。而且编程语言只需要被机器处理,而不需要被理解。一些编程语言通过称为“编译”的过程直接转换为机器可读代码。其他语言(如Python)是解释型的,意味着另一个称为“解释器”的程序逐行执行代码。机器需要按照程序员的指令执行操作,它不需要理解程序为何如此设计,也不需要抽象或心智模型来理解其处理的1和0世界之外的任何内容。
然而,自然语言是自然、有机地演化而来的。自然语言用于在有大脑的生物之间传递思想、理解和知识,而不是在CPU之间。这些自然语言必须在各种“湿件”(大脑)上“运行”或被理解。在某些情况下,自然语言甚至能够实现跨物种的交流。科科(大猩猩)、沃舒(黑猩猩)、亚历克斯(鹦鹉)等著名动物都展示了对一些英语单词的掌握能力。1 亚历克斯鹦鹉在临终前以看似深刻的方式与主人交流,说道:“你要好好的……我爱你。”2
鉴于自然语言和编程语言的演化方式如此不同,它们被用于不同领域也就不足为奇了。我们不会用编程语言来讲述自己的一天或指路去杂货店。同样,自然语言也并非为了易于编译成可由机器运行和执行的程序而演化。但这正是你将在本书中学到的内容。机器学习过程是一种编程形式,当用于NLP时,这些程序可以推导结论、推断新事实、创建有意义的抽象,甚至在对话中作出有意义的回应。
尽管自然语言没有编译器,但有解析器可以让计算机将句子分解成部分并理解这些部分之间的联系。在本书中,你将发现一些Python包,它们可以让你分析自然语言文本、将其与其他文本进行比较、总结文本,甚至从中生成新文本。但没有一种单一的算法或Python包可以将自然语言文本转换为用于自动计算或执行的机器指令。科学家、Mathematica和Wolfram Alpha的创始人斯蒂芬·沃尔夫拉姆(Stephen Wolfram)几乎一生都在尝试构建一种通用的智能“计算”机器,能够用简单的英语与我们互动。他甚至不得不通过整合多种不同的NLP和AI算法来构建一个系统,该系统必须不断扩展和演化以处理新型的自然语言指令。3
通过本书,你可以站在巨人的肩膀上。如果你理解了本书中的所有概念,你也将能够结合这些方法创建非常智能的对话聊天机器人。你将具备加入开源、道德替代ChatGPT(或未来任何逐利的AI应用)运动的技能。4 你还将学习如何将新获得的知识应用于构建一个更公平、更协作的世界。
本章将向你展示你的软件如何处理自然语言以生成有用的输出。你甚至可以将你的程序视为一种自然语言解释器,类似于Python解释器处理源代码的方式。当你开发的计算机程序处理自然语言语句时,它将能够根据这些语句执行操作,甚至作出回应。
与编程语言中每个关键字都有明确解释不同,自然语言要模糊得多。想想这句话:“鸡可以吃了。”这可能意味着一只活鸡即将吃早餐——或者一只煮熟的鸡已经准备好了。自然语言的这种模糊性使每个词的解释都开放给你,并为理解和生成人类语言带来了有趣的挑战。
一个自然语言处理系统被称为“管道”,因为自然语言必须经过多个阶段进行处理。自然语言文本从一端流入,而文本或数据则从另一端流出,具体取决于你在管道中包含哪些“管道段”(Python代码)。这就像是一队跳康加舞的Python蛇,将数据从一条蛇传递到下一条。
本书将教你如何编写软件,将简单的文本命令转换为能够进行完整、类似人类对话的应用程序。这最初可能看起来有点像魔术,就像新技术常常给人的感觉一样,但你将揭开帷幕,探索这些魔术表演背后的技术。很快,你将发现所有自己进行魔术表演所需的道具和工具。
1.1.1 自然语言理解
自然语言理解(NLU)是自然语言处理(NLP)中的一个子领域,涉及机器理解和分析自然语言的含义。NLU的一个重要部分是自动处理文本,以提取文本含义的数值表示。这就是NLP中的NLU部分。自然语言含义的数值表示通常以一排数字或向量的形式出现。计算机容易处理向量,并能对其进行各种操作。
你将学习将自然语言表示为向量的各种方法。将文本转化为数值向量的函数称为向量化器或编码器,在scikit-learn包中(第3章)。在第3章和第4章中,你将熟悉文本的向量表示方法,如词元计数向量和词频向量。你将学习如何使用词频向量来实现关键词搜索、全文搜索,甚至检测有害的社交媒体信息。在第6章中,你将了解一种更先进的语言向量表示方法,称为嵌入(embedding)。嵌入使你能够对单词的含义或语义进行数学运算,而不仅仅是对它们的出现次数进行运算。你将学习搜索引擎如何使用嵌入来理解你的搜索查询的含义,从而帮助你找到包含你所寻找信息的网页。在第6章结束时,你将学会如何创建一个混合搜索引擎,将这些自然语言编码方法的优点结合起来。
图1.1展示了NLP管道中NLU部分如何接收原始文本并输出该文本含义的数值表示。
一旦你的自然语言输入转化为数值形式,就有成百上千种方式可以从中提取含义。机器在许多常见的自然语言理解(NLU)任务中已经能够以高精度完成相当长一段时间:
- 语义搜索
- 释义识别
- 意图分类
- 情感分析
- 主题建模
- 作者归属
随着深度学习的进步,近年来许多10年前还无法解决的NLU任务也得以实现:
- 类比问题求解
- 阅读理解
- 提取式摘要和问答
然而,仍然有许多NLU任务是机器远远不如人类的。一些问题要求机器具备常识知识,学习这些常识事实之间的逻辑关系,并将所有这些应用到某一特定文本的上下文中。这使得以下问题对机器来说变得更加困难:
- 委婉语和双关语识别
- 幽默和讽刺识别
- 仇恨言论和网络喷子检测
- 逻辑蕴涵和谬误识别
- 知识抽取
然而,最新和最先进的自然语言处理程序——大语言模型(LLMs),在解决这些难题方面表现得更好,其有效性随着时间的推移持续提高。在本书中,你将学习许多先进的NLU方法,这些方法使得解决这些问题成为可能。掌握这些知识后,你将拥有创建高度有效的NLU管道所需的工具,能够针对自己的应用场景进行优化,并解决那些特别具有挑战性的问题。
1.1.2 自然语言生成
在写这本书的十年前,机器能够轻松生成听起来像人类写的文本的想法看起来还像是未来的事情。但在写作时,距离像ChatGPT这样的工具进入主流市场只有大约一年半的时间,机器根据一个人意图和情感的数值表示,定期生成定制的、可读的文本,对大多数人来说,这感觉更像是一个普遍存在的现实,而非科幻小说中的梦想。这项创新来自NLP的自然语言生成(NLG)部分。机器可以通过多种方式生成文本,但除非你的算法进行显式的字符串操作(例如,将用户的名字粘贴到“Hello {{name}}!”模板中),否则它可能会将输出表示为一系列数字。为了将这些数字转换为人类可读的语言,需要解码过程,这个过程是编码的逆过程,如图1.2所示。
你很快就能掌握许多基于自然语言理解(NLU)技能的常见自然语言生成(NLG)任务。以下任务主要依赖于你将自然语言编码为有意义的嵌入向量的能力:
- 同义词替换
- 回答常见问题(信息检索)
- 自动完成电子邮件和消息中的句子
- 检索增强生成
- 拼写和语法修正
一旦你理解了如何完成这些基础任务,以提升你的NLU技能,以下更高级的NLG任务将不再遥不可及:
- 抽象摘要和简化
- 使用神经网络进行机器翻译
- 句子释义
- 治疗性对话AI
- 事实性问题生成
- 讨论促进和主持
- 辩论性文章写作
最后,在第10章中,你将看到现代大语言模型(LLM)如何利用生成能力来完成最先进的任务,如下所示:
- 参与社交媒体上的辩论
- 自动总结长篇技术文档
- 创作自然流畅的诗歌和歌词
- 创作笑话和讽刺评论
- 从自然语言描述中创作编程语言表达式
自然语言生成(NLG)的这一最新发展尤为强大。现在,机器可以根据自然语言描述编写正确的代码,几乎能与您的意图匹配。虽然机器尚不能自己编程,但根据最新的Metaculus(2024年9月)的共识,它们很快可能就能做到。社区预测,到2028年9月,我们将生活在一个“AIs编程程序,程序又能编程AIs”的世界中。
自然语言理解(NLU)与自然语言生成(NLG)的结合,将为你提供创建与人类进行意外互动的机器的工具。你可能听说过微软和OpenAI的Copilot项目。GPT-J几乎能做到同样的事情,而且它完全开源和数据开放。
1.1.3 将一切结合起来,创造有积极影响的AI
一旦你理解了自然语言生成(NLG)和自然语言理解(NLU)的工作原理,你就能像一个水管工一样将它们组合成你自己的NLP管道。许多企业已经在使用这些管道从用户那里提取价值。
你也可以利用这些管道来推进你自己在生活、事业和社会影响方面的目标。这场技术爆炸就像是一枚火箭,你可以搭乘它,甚至稍微操控一下。你可以在生活中利用它来管理你的收件箱和日记,同时保护你的隐私并最大限度地提高你的心理健康。或者,你可以通过向同事展示能够理解和生成语言的机器如何提高几乎所有信息时代任务的效率和质量,来推进你的职业生涯。作为一名关注自己工作对社会影响的工程师,你还可以帮助非营利组织建立自然语言理解(NLU)和自然语言生成(NLG)管道,帮助那些需要帮助的人。作为一名企业家,你可以帮助创造一种再生的亲社会商业模式,孕育出新兴行业和社区,让它们共同繁荣。
我们希望,通过了解NLP的工作原理,你能够更清楚地认识到机器是如何在我们日常生活中被使用的——往往是在我们不知情的情况下——通过挖掘我们的语言获取利润,温和地引导我们朝着特定的结果发展,甚至训练我们在未来更容易受到操控。好消息是,通过学习NLP的工作原理,你将能更好地为识别、保护自己免受,甚至反击那些不良用途的NLP做好准备,在这个充满操控性算法的世界里。
能够理解和生成自然语言的机器掌握了语言的力量。因为机器现在能够理解和生成看起来像人类写的文本,在某些情况下,它们能够代表你在现实世界中采取行动。不久的将来,你可能能够创建自动执行你意图并完成你编程目标的机器人。但要小心“阿拉丁的三愿望陷阱”。你的机器人可能会给你的商业或个人生活带来一场巨大的反噬。这些能够完成我们在上一节中看到的具有挑战性的任务的机器人,也正是导致律师失业、给有饮食障碍的人提供有害饮食建议、欺骗航空公司客户并造成重大声誉损害的机器人。这被称为“AI控制问题”或“AI安全挑战”。
控制问题和AI安全并不是你在追求积极影响的NLP过程中唯一面临的挑战。超智能AI能够操控我们,让我们给予它更大的权力和控制,这种危险可能还需要几十年才能到来,但不那么智能的AI已经存在多年,它们通过欺骗和操控我们。决定你可以看到哪些帖子并进行推荐的搜索和推荐引擎NLP,并不是在做你想要的事情,而是在做平台投资者想要的事情:窃取你的注意力、时间和金钱。
1.2 自然语言的魔力
机器能够阅读和书写自然语言,究竟有什么神奇之处?自从计算机被发明以来,机器就一直在处理语言。但那些是计算机语言,如Ada、Bash和C,它们是故意设计的,目的是让计算机能够理解它们。编程语言避免了歧义,这样计算机就能始终按照你所说的去做,尽管这不一定是你真正想要它们做的事情。
计算机语言只能以一种正确的方式进行解释(或编译)。而在自然语言处理(NLP)中,你可以让用户用自己的语言与机器交流,而不是强迫他们学习“计算机语言”。当软件能够处理那些并非为机器设计的语言时,它就像魔法——是我们以前认为只有人类才能做的事情。
此外,机器能够访问大量的自然语言文本,例如来自维基百科的内容,来学习世界和人类的思维。谷歌的自然语言文档索引已经超过了100亿GB,而且那只是索引本身——而且这个索引还不完整!目前在线的实际自然语言内容的大小可能已经超过了1000亿GB。这庞大的自然语言文本量使得NLP成为一项非常有用的工具。
注:今天,维基百科列出了大约1000种编程语言。维基百科上的自然语言列表包含超过7000种自然语言,而这些列表并未包括许多其他自然语言序列,这些序列也能通过你在本书中将要学习的技术来处理。动物的声音、手势和身体语言,以及它们细胞中的DNA和RNA序列,都可以通过NLP进行处理。
现在,你只需要考虑一种自然语言:英语。稍后在本书中,你将逐渐接触到更难的语言,比如普通话。但你可以使用本书中学到的技术,构建能够处理任何语言的软件,甚至是你不理解的语言,或者是那些尚未被考古学家和语言学家解读的语言。我们将向你展示如何使用Python这一编程语言来编写处理和生成这种语言的软件。
Python从一开始就被设计成一种可读性强的语言。它还暴露了许多自身语言处理的“内部机制”。这两个特点使它成为学习NLP的绝佳选择。它是构建可维护的生产管道的优秀语言,特别适用于在有多个贡献者的单一代码库中,进行NLP算法的企业环境中使用。我们甚至在尽可能的情况下,用Python代替数学符号和“通用语言”。毕竟,Python是一种清晰表达数学算法的方式,它的设计尽可能让程序员像你一样能轻松阅读。
1.2.1 语言与思维
语言学家和哲学家,比如萨皮尔(Sapir)和沃尔夫(Whorf),曾提出,词汇会影响我们的思维。例如,许多澳大利亚原住民语言,如Guugu Yimithirr和Kuuk Thaayorre,使用词汇来描述物体在他们身体上的位置,这些描述是根据指南针的方向来定义的。来自墨西哥南部恰帕斯(Chiapas)地区的土著人说的是Tzeltal语言,它也有表示方位的词汇,而不是基于自我中心的相对方位。Tzeltal人甚至使用上坡、下坡或海拔高度来描述事件相对于现在的时间位置。这样的文化和语言让这些人比没有这种文化的人拥有更强的内在指南针。比如,他们不会说某个物品在他们的右手上,而是说它位于身体的北侧。用方位词来表达位置,甚至可能帮助说话者在执行某些任务时更有效。比如,常用方位词的语言被认为有助于说话者不断更新自己在世界中的方位感知,从而提高在狩猎等活动中的沟通效率和定向能力。
斯蒂芬·平克(Stephen Pinker)则从另一角度提出了不同的观点,他认为语言是我们思维的窗口:“语言是人类的集体创造,反映了人类的本性,展现了我们如何构思现实,如何与他人建立联系。”无论你是否认为词汇会影响你的思维,还是认为它们帮助你看到和理解自己的思维,词汇无疑是思维的载体。很快,你将领略到自然语言处理(NLP)在操控这些思维“包裹”以及增强你对词汇——甚至思维本身——理解的强大力量。难怪许多企业将NLP和聊天机器人称为人工智能(AI)。
那么数学呢?人类不仅可以通过精确的数学符号和编程语言进行思维,也能通过“模糊”的自然语言词汇和符号进行思考。我们甚至可以用模糊的词汇来表达逻辑思维,如数学概念、定理和证明。然而,词汇并不是我们唯一的思维方式。哈佛大学的几何学家乔丹·埃伦伯格(Jordan Ellenberg)在他的著作《Shape》中写到,自己是如何第一次“发现”代数的交换律的。当时,他盯着一个6x8的点阵立体音响扬声器,记住了乘法表和数字符号,并知道可以交换乘法符号两边的顺序。但他并没有真正理解,直到他意识到可以将48个点想象为6列8个点,或者8行6个点。并且这些点是一样的,所以它们必须是相同的数字!这一下,他在一个比代数课堂上学习的符号操作规则更深层次的层面上理解了这一点。
因此,你使用词汇与他人以及自己交流思维。当转瞬即逝的思想被压缩成词汇或符号时,它们成为了更容易记住和在大脑中处理的思维“包裹”。你可能没有意识到,但当你在写句子时,你其实是在重新思考、操控和重新包装这些思想。你想要表达的观点是在你说话或写作时逐渐形成的。这个在脑海中操控思维“包裹”的过程,被AI研究人员和神经科学家称为符号操控。事实上,在传统的人工智能(GOFAI)时代,研究人员认为,AI必须学会像编译编程语言那样,操控自然语言符号和逻辑语句。在第11章中,你将学习如何教会机器进行自然语言的符号操控。
然而,这并不是NLP最令人印象深刻的能力。回想一下,当你需要给某个亲近的人发送一封艰难的电子邮件时,可能是需要向老板、老师、伴侣或亲密朋友道歉。在开始打字之前,你可能已经开始思考你将使用哪些词汇,甚至是你为什么做了某件事的理由或借口。然后,也许你会想象老板或老师如何看待这些话。你可能在心里反复推敲你该说什么,直到你最终开始打字。你在脑中操控了这些“包裹”的思维。当你开始打字时,你可能写了比实际发送的字数多两倍的话。你小心选择了词汇,舍弃了一些词语或想法,集中注意力在其他的部分。
修改和编辑的过程本身就是一个思考的过程。它帮助你整理思路并进行修正。最终,输出的内容与最初的想法完全不同。写作的过程改善了你的思维方式,而机器也将在阅读和写作能力不断提升的过程中,改善它们的思维能力。
因此,阅读和写作就是思考。而词汇是你可以存储和操控的思维“包裹”,它们可以帮助你改善这些思维。我们用词汇将思维组织成块或结构,便于在大脑中进行加工,拆分复杂的思维成几句句子,并重新排序这些思维,使它们对读者甚至是对未来的自己更加有意义。本书第二版的每一篇文章都经历了多次编辑——有时是得到热心读者的帮助。我已经在朋友和读者的建议下,删减、重写和重新排序了这些段落多次。
但是,词汇和写作并不是唯一能够进行逻辑和深度思考的方式。绘画、图表制作,甚至舞蹈和表演都是思维的表现形式。而我们在脑海中构思这些画面——在头脑中勾画出思想、概念和思维的轮廓。有时,我们还会通过物理上的动作来移动事物或在现实世界中进行表演。但将词汇组合成句子,再将句子组织成段落的过程,几乎是我们不断进行的活动。
阅读和写作是特殊的思维方式。这些行为似乎将我们的思维压缩,使它们更容易记住和管理。一旦我们知道了一个概念的完美词汇,我们就能将它存储在脑海里;我们不必不断刷新它来理解。当我们再次想起这个词时,那个概念会涌现回来,我们也能再次使用它。
这一切都属于思维,或有时被称为认知。尽管机器使用的是与我们不同的工具来处理和生成文本,但当我们看到机器这样做时,我们会将其与伴随我们阅读和写作的思维过程联系在一起。这就是为什么人们把自然语言处理(NLP)看作是人工智能(AI)的原因。而对话型人工智能(Conversational AI)是最广泛认知和有用的人工智能形式之一。
1.2.2 会话机器
虽然你花费大量时间在脑海中与词汇打交道,但真正有趣的部分是,当你用这些词汇与他人互动时。对话的过程让两个人(或更多!)共同参与到你的思维中。这可以创造出一种强大的正向反馈循环,强化好的想法并剔除薄弱的想法。
词汇在这个过程中至关重要;它们是我们共享的思维词汇。当你想在别人脑中触发某个想法时,你所需要做的就是说出合适的词语,让他们理解你的一部分思维。例如,当你感受到极大的痛苦、沮丧或震惊时,你可以使用脏话。这样的词汇选择很可能会将这种震惊和不适传递给你的听众或读者。虽然我们不能用词语“编程”另一个人,但我们可以通过词汇来传达极为复杂的思想。
自然语言无法直接转化为一组精确的数学操作,但它确实包含了可以提取的信息和指令。这些信息和指令可以被存储、索引、搜索或立即执行。例如,其中的一种操作可能是,响应某个陈述时生成一串词语。这正是你将要构建的“对话引擎”或聊天机器人的功能。
本书专注于英语文本文件和消息,而非口语陈述。第7章为你简要介绍了音频文件处理,使用了摩尔斯电码的例子。但除此之外,我们聚焦于已经转化为文字的内容……或者至少是转化为计算机中的晶体管信号。关于语音识别、语音转文本(STT)和文本转语音(TTS)系统有专门的书籍。市面上也有现成的开源STT和TTS项目。如果你在开发移动应用,现代智能手机SDK提供了语音识别和语音生成的API。如果你想让你的虚拟助手住在云端,那么也有Python包可以在任何有音频流访问权限的Linux服务器上实现STT和TTS。
在本书中,我们专注于音频转为文本后的内容。这可以帮助你在将自己的智能加入开源项目(如Home Assistant 或 Mycroft AI)时,构建一个更聪明的语音助手。而且你会理解大公司通过其语音助手提供的所有有用的NLP……前提是商业语音助手不仅仅想帮你减轻钱包的负担。
1.2.3 数学原理
处理自然语言以提取有用信息可能很困难。这需要繁琐的统计计算,但这正是机器的作用所在。像许多其他技术问题一样,一旦知道答案,解决问题会变得容易得多。机器仍然无法像人类一样准确、可靠地完成大多数实际的自然语言处理(NLP)任务,如对话和阅读理解。因此,你可能能够调整你在本书中学到的算法,使其在某些NLP任务上表现得更好。
然而,你将学到的技术足够强大,可以创建能够在一些令人惊讶的微妙任务中,在准确性和速度上超过人类的机器。例如,你可能没有想到,通过机器识别Twitter消息中的讽刺,准确度竟然比人类更高。经过良好训练的人类评审员无法与一个简单的讽刺检测NLP算法的表现(68%准确率)相匹配。简单的词袋模型(BOW)达到了63%的准确率,而最先进的Transformer模型则达到了81%的准确率。别担心——人类在持续对话中仍然更擅长识别幽默和讽刺,因为我们能够保持对陈述上下文的理解;然而,机器在保持上下文方面正在变得越来越好。本书将帮助你在NLP管道中融入上下文(元数据),如果你想尝试推动该领域的进步。
一旦你从自然语言中提取了结构化的数值数据或向量,你就可以利用数学和机器学习的所有工具。我们使用与将三维物体投影到二维计算机屏幕上的线性代数技巧相同的技术,计算机和绘图员早在NLP崭露头角之前就已经在使用这些技巧。这些突破性的想法开启了“语义”分析的世界,使计算机能够解释和存储陈述的“意义”,而不仅仅是单词或字符的计数。语义分析与统计学相结合,可以帮助解决自然语言的歧义——即单词或短语通常有多重含义或解释这一事实。
因此,提取信息并不像构建编程语言编译器那样复杂(幸运的是)。最有前途的技术绕过了常规语法(模式)或形式语言的严格规则。你可以依赖单词之间的统计关系,而不是一个深奥的逻辑规则系统。想象一下,如果你不得不在一个嵌套的“如果-那么”语句树中定义英语的语法和拼写规则。你能写出足够的规则来处理每种单词、字母和标点符号的组合方式吗?你甚至能开始捕捉英语陈述的语义——即意思吗?即使对于某些类型的陈述,这种方法可能有用,想象一下这种软件有多么局限和脆弱。未预料到的拼写或标点会破坏或困惑你的算法。
自然语言还面临一个更难解决的“解码”挑战。自然语言的说话者和写作者假设处理语言的不是机器,而是人类。所以,当我说“早上好”时,我假设你对“早上”有什么样的认识,包括早上在中午、下午、晚上和午夜之前的时间段。你需要知道“早上”不仅可以表示一天中的某个时段,还可以作为一段时间的通用表示。解释者假设知道,“早上好”是一种常见的问候语,并且它本身并不包含关于早晨的很多信息。相反,它反映的是说话者的心态以及他们与他人交谈的准备状态。
关于语言处理的人类大脑的这一“心智理论”事实证明是一个强大的假设。它允许我们用很少的词语说很多话,只要我们假设“处理器”掌握了关于世界的常识知识。这个压缩的程度仍然是机器无法企及的。在NLP管道中,没有一个明确的“心智理论”可以指引我们。然而,我们将在后续章节中向你展示一些技术,帮助机器构建本体论或常识知识库,帮助解释依赖这种知识的陈述。
1.3 应用
自然语言处理(NLP)无处不在。它已经如此普及,以至于你可能很难度过一天而不与多个NLP算法进行交互。图1.3中的一些例子可能会让你感到惊讶。
在这个网络图的核心是NLP的自然语言理解(NLU)和自然语言生成(NLG)部分。从NLU的中心节点分支出基础应用,如情感分析和搜索。这些最终与基础的NLG工具(例如拼写校正器和自动代码生成器)连接,进而形成对话式AI,甚至配对编程助手。
如果搜索引擎以考虑自然语言文本意义的方式对网页或文档档案进行索引,它可以提供更有意义的搜索结果。自动完成使用NLP来完成你的思维,这在搜索引擎和手机键盘中非常常见。许多文字处理软件、浏览器插件和文本编辑器都配有拼写校正器、语法检查器、词汇搭配生成器,以及最近的风格教练。一些对话引擎(聊天机器人)使用自然语言搜索来找到回应对话伙伴消息的答案。
生成文本的NLP管道不仅可以用于在聊天机器人和虚拟助手中生成简短的回复,还可以用于拼凑更长的文本段落。美联社甚至使用NLP“机器人记者”来撰写整个财经新闻文章和体育赛事报道。聊天机器人可以生成像你家乡气象员可能会说的那样的天气预报,这可能是因为人类气象员使用带有NLP功能的文字处理软件来起草脚本。
越来越多的企业正在使用NLP来自动化其业务流程。这不仅能提高团队的生产力和工作满意度,还能改善产品质量。例如,聊天机器人可以自动回复许多客户服务请求。此外,NLP垃圾邮件过滤器在早期的电子邮件程序中帮助电子邮件超越了电话和传真通信渠道,并且一些团队使用NLP来自动化和个性化队员之间的电子邮件或与求职者的沟通。
像所有算法一样,NLP管道也会出错,并且几乎总是以多种方式存在偏见,因此,如果你使用NLP来自动化与人的沟通,务必小心。在我担任首席技术官的Tangible AI,我们将NLP用于寻找加入我们团队的开发者这一关键业务流程,因此我们仔细监督了我们的NLP管道。NLP只在候选人未回复或回答与问题无关的情况下才被允许筛选掉申请者。我们还对NLP管道进行了严格的质量控制,定期随机抽查模型预测,并使用简单的模型和样本高效的NLP模型,集中人力关注那些机器学习信心较低的预测——你也将学习如何使用scikit-learn分类器的predict_proba方法,从第二章开始。结果,NLP在招聘(HR)中的使用实际上让我们花费了更多时间和精力,并未为我们节省资金,但它确实帮助我们在寻找候选人时扩展了范围。我们收到了来自全球各地的数百份初级开发者职位申请,包括来自乌克兰、非洲、亚洲和南美的申请者。NLP帮助我们快速评估了英语和技术技能,然后才进入面试和付费的家庭作业环节。
尽管电子邮件垃圾邮件过滤器在垃圾邮件过滤与生成的猫鼠游戏中依然占据优势,但它们在其他环境中可能正在失去优势,比如社交网络。据估计,2016年美国总统选举期间,约20%的推文是由聊天机器人生成的。这些机器人放大了其拥有者和开发者的观点,通常是外国政府或有资源和动机影响舆论的大型企业。
NLP系统不仅可以生成短小的社交网络帖子,它们还可以用来撰写长篇的电影和产品评论,在线商店以及其他地方都有应用。许多评论都是由自主的NLP管道创建的,这些管道从未进入过电影院,也没有购买过它们所评论的产品。事实上,很多出现在搜索结果顶部和在线零售商产品页面上的产品评论都是假的。你可以利用NLP帮助搜索引擎和亲社会的社交媒体社区检测并删除误导性或虚假的帖子和评论。
在Slack、IRC甚至客户服务网站上都有聊天机器人——这些地方的聊天机器人必须处理模糊的指令或问题。与语音识别和生成系统配对的聊天机器人甚至可以处理具有不确定目标或“目标函数”的长时间对话,例如预订当地餐厅的座位。NLP系统还可以为公司接听电话,替代电话树的功能,而公司又不想支付人类员工来帮助客户。
警告
每当你或你的老板决定欺骗用户时,请考虑其中的伦理问题。在Google I/O的Duplex演示中,工程师和管理人员忽视了教授聊天机器人欺骗人类的伦理问题。在大多数娱乐性社交网络上,机器人并不需要透露自己的身份。我们在Facebook、Reddit、Twitter,甚至约会应用上都在不知情的情况下与这些机器人互动。现在,机器人和深度伪造技术可以如此逼真地欺骗我们,AI控制问题已经被更紧迫的挑战所取代,那就是构建符合伦理行为的AI。尤瓦尔·赫拉利在《人类简史》中的警示预言——机器人绕过人类决策——已经变成现实。
NLP系统可以作为企业的电子邮件“接待员”或管理人员的行政助理。这些助理可以安排会议,并将摘要信息记录在电子名片或客户关系管理(CRM)系统中,代表老板通过电子邮件与他人互动。公司将其品牌和形象交给NLP系统,让机器人执行营销和传播活动。一些缺乏经验、胆大妄为的NLP教科书作者甚至让机器人为他们的书写几句话——稍后会详细讨论这一点。
NLP最强大的应用之一是在心理学领域。历史上第一个对话式应用——ELIZA聊天机器人,使用简单的模式匹配方法对用户的输入进行处理,创造出了令人惊讶的类似人类的对话。从那时起,充当治疗师的聊天机器人取得了巨大的进展。像中国的小冰、美国的Replika.AI和Woebot这样的商业虚拟伴侣帮助了数亿孤独的人在2020年和2021年COVID-19封锁期间应对社会隔离带来的情感冲击。幸运的是,你不必依赖大公司里的工程师来保护你的最佳利益。许多心理治疗和认知助手是完全免费的,并且开源的。
1.3.1 用NLP处理编程语言
现代深度学习NLP管道已经证明了其强大和多功能的能力,现在它们能够准确理解和生成编程语言。基于规则的编译器和生成器对于一些简单任务,如自动补全和提供代码片段建议,已证明非常有帮助。此外,用户通常可以使用信息检索系统或搜索引擎来查找代码片段,完成他们的软件开发项目。
而这些工具变得更智能了。旧版的代码生成工具是提取式的。提取式文本生成算法会从你的历史中找到最相关的文本,并将其逐字复述,作为建议。因此,如果短语“亲社会人工智能”在训练数据中出现频繁,自动补全就会建议用“人工智能”接在“亲社会”后面,而不是仅仅使用“智能”。你可以看到,这可能开始影响你打字的方式以及你的思考方式。
最近,变换器(Transformers)进一步推动了NLP的发展,通过庞大的深度学习网络变得更加抽象,生成你从未见过或打过的全新文本。例如,GPT-3的1750亿参数版本在所有GitHub代码库上进行训练,创建了一个叫做Codex的模型。Codex是Visual Studio Code(VS-Code)插件Copilot的一部分。它可以建议整个函数和类定义,你只需提供简短的注释和函数定义的第一行。以下是Copilot主页上展示的示例TypeScript提示:
// Determine whether the sentiment of text is positive
// Use a web service
async function isPositive(text: string): Promise<boolean> {
在演示动画中,Copilot随后生成了剩余的TypeScript代码,完成了一个用于估计文本情感的有效函数。想一想这个过程。一个算法正在为你编写代码,来分析自然语言文本的情感,比如你的电子邮件或个人论文中的文本。而且Copilot主页上展示的示例代码,全部偏向微软的产品和服务。这意味着你最终将得到一个基于微软的NLP管道,它会有一个关于什么是“积极”与“消极”的观点;换句话说,它会根据微软告诉它的内容来评判价值。就像谷歌间接影响了你写的代码一样,现在微软的算法直接为你编写代码。
既然你正在阅读本书,说明你可能打算构建一些非常酷的NLP管道。你可能会构建一个帮助你写博客文章和聊天机器人的管道,甚至贡献一些开源数据集和算法。你可以创建一个正反馈循环,改变像你一样的工程师所构建和部署的NLP管道和模型的种类。因此,要注意你用来帮助编写代码和思考的元工具。这些工具对你的代码方向和生活方向有着巨大的影响。
1.4 通过计算机的“眼睛”看语言
当你输入类似“Good morning Rosa”的文本时,计算机看到的仅仅是“01000111 01101111 01101111 … .” 那么,如何编写一个聊天机器人来智能地响应这个二进制流呢?是否可以通过一系列嵌套的条件语句(if-else语句)逐个检查这些比特并对其进行处理?这相当于编写一个叫做有限状态机(FSM)的特殊程序。一个输出新符号序列的FSM,在运行时就像Python的str.translate函数一样,被称为有限状态转导器(FST)。你可能已经在不知不觉中构建了一个FSM。你曾经写过正则表达式吗?那就是我们在下一节中将用来展示NLP的一种方法:基于模式的方法。
如果你决定在一个存储库(数据库)中查找完全相同的比特串、字符或单词,然后使用其他人和作者过去对该陈述所使用的响应呢?但是想象一下,如果在这个陈述中出现了拼写错误或变体,我们的机器人就会跑偏。而且,比特不是连续的或宽容的——它们要么匹配,要么不匹配。没有显而易见的方式来找到两组比特流之间的相似性,且考虑它们所代表的意义。比如,“good”和“bad”的比特将一样相似,“OK”也是如此。
但是,在展示更好的方法之前,我们先看看这种方法是如何工作的。我们将从构建一个小的正则表达式开始,用来识别问候语并做出适当的响应——这就是我们的第一个小型聊天机器人!
1.4.1 锁的语言
你可能不知道,但简单的密码锁实际上是一个语言处理机器,拥有自己独特的语言。本节将教你如何“说”密码锁的语言,并将其作为正则表达式的类比,但需要提醒你的是:读完之后,你再也不会以同样的方式看待你的自行车锁了!
与自然语言类似,要“说”密码锁的语言,你必须遵循其语法,即语言的规则和模式。在密码锁的语言中,如果你使用正确的语法(为锁设置的正确符号序列)“告诉”锁一个密码(通过输入密码组合),它会立即判断你“告诉”它了一些特别有意义的内容。而且,令人印象深刻的是,它会理解唯一的正确响应:释放固定住U形挂销的扣锁,这样你就能打开储物柜。
这种锁的语言使用了正则表达式,使其成为一个特别简单的语言——但这并不意味着它太简单而无法在聊天机器人中使用!例如,我们可以使用它来识别一个关键短语或命令,以解锁特定的动作或行为。假设你正在构建一个聊天机器人,想让它识别并响应问候语(例如,“Hello, Rosa”)。这种语言像锁的语言一样,是一种正式语言,意味着它有严格的规则,规定了一个可接受陈述必须如何组成和解释。如果你曾经写过数学公式或编写过编程语言表达式,那么你已经写过正式语言的陈述了。
正式语言是自然语言的一个子集。许多自然语言陈述可以使用正式语言语法进行匹配或生成,比如正则表达式或正规语法。这也是我们进入机械(点击、嗡嗡声)密码锁语言的原因。
1.4.2 正则表达式
正则表达式使用了一类特殊的正式语言语法,称为正规语法。正规语法具有可预测、可证明的行为,但它们足够灵活,能够驱动市场上一些最复杂的对话引擎和聊天机器人。Amazon Alexa和Google Now主要是基于模式的引擎,它们依赖于正规语法。深度的、复杂的正规语法规则通常可以用一行代码表达,这行代码被称为正则表达式。在Python中,有像Will和qary这样的成功聊天机器人框架,它们完全依赖这种语言处理来生成有效的聊天机器人。
注意:在Python和POSIX(Unix)应用程序中实现的正则表达式(如grep)并不是真正的正规语法。它们具有语言和逻辑特性,如前瞻和回溯,这些特性使得它们能够进行逻辑跳跃和递归,而这些在正规语法中是不允许的。因此,正则表达式不能被证明是终止的;它们有时会崩溃或永远运行下去。
你可能会说:“我听说过正则表达式,我用过grep,但它只是用来搜索的!”你说得对。正则表达式的确主要用于搜索,用于序列匹配,但任何能够在文本中找到匹配项的工具,同样也适用于对话。一些聊天机器人使用搜索来查找用户陈述中的字符序列,并根据这些序列做出响应。这些被识别的序列触发与特定正则表达式匹配相应的脚本化响应,而相同的正则表达式也可以用来从陈述中提取有用的信息。聊天机器人可以将这些信息添加到其关于用户或用户描述的世界的知识库中。
处理这种语言的机器可以被视为一个正式的数学对象,一个有限状态机(FSM),或者称为确定性有限自动机(DFA)。FSM在本书中会多次出现,因此你将逐渐对它们的用途有更好的理解,而无需深入研究FSM理论和数学。图1.4展示了用于编程有限状态自动机的正式语言是如何相互嵌套的,像乌克兰的套娃一样。
组合逻辑是这个维恩图中最小、最简单的语言。有限状态机包含组合逻辑,而下推自动机则是这两者的超集。图灵机能够实现该图中所有其他自动机的行为。
正式语言
Kyle Gorman 如此描述编程语言和正式语言:
- 大多数(如果不是所有的话)编程语言都来自上下文无关语言的类别。
- 上下文无关语言通过上下文无关语法进行解析,这种语法提供高效的解析。
- 正则语言也可以高效地解析,并在计算中广泛应用于字符串匹配。
- 字符串匹配应用通常不需要上下文无关语法的表达能力。
以下是几种正式语言类别,按复杂度递减排列,其中最复杂的类别是递归可枚举文法:
- 参见“乔姆斯基层次”(Chomsky Hierarchy),维基百科(链接)。
自然语言
自然语言与正式编程语言有很大不同,主要体现在它们不具备的特点:
尽管正式编程语言无法直接实现非常复杂的自然语言管道,但它们对于任何NLP管道来说都是至关重要的。毕竟,你将使用正式的编程语言Python来创建本书中的所有NLP管道。有时候,你所需要的只是几个if语句和一些Python字符串处理,便能完成所需的任务。这意味着你可以在没有机器学习或其他非确定性处理的情况下创建一个简单的聊天机器人。
1.5 构建一个简单的聊天机器人
让我们快速构建一个简单的聊天机器人。它不会非常强大,并且需要对英语语言进行大量思考。你还需要硬编码正则表达式,以匹配人们可能尝试表达的各种方式。但是,如果你觉得自己没有办法想出这段Python代码,不用担心。你不需要像我们在这个例子中做的那样,试图想出所有人们可能说的话的不同方式。你甚至不需要编写正则表达式来构建一个超级棒的聊天机器人。相反,我们将引导你通过构建你自己的聊天机器人,之后它能够通过阅读(处理)一堆英语文本来学习,而不需要硬编码任何东西。
这个基于模式匹配的聊天机器人是一个严格控制的聊天机器人的例子。在现代机器学习聊天机器人技术发展之前,模式匹配聊天机器人是非常常见的。我们在这里介绍的模式匹配方法的变种被用在了像Amazon Alexa和其他虚拟助手的聊天机器人中。
现在,让我们构建一个有限状态机(FSM),即一个可以说出正则语言的正则表达式。我们希望它能够理解问候语——像“芝麻开门”或“你好,Rosa”这样的短语。能够回应问候语是一个友好聊天机器人的重要特征。在高中,老师可能会因为学生忽视这样的问候语而批评他们,说他们不礼貌,因为他们急着去上课。我们当然不希望我们的仁慈聊天机器人表现得那样。
对于两台机器之间的通信,你会定义一个像ACK(确认)信号这样的握手协议,以确认每条消息的接收。但我们的机器将与人类互动,而人类可能会说“早上好”。我们可不希望它发出一堆啾啾声、哔哔声或者ACK消息,就像它在开始对话或浏览网页会话时同步调制解调器或HTTP连接那样。
人类的问候和握手则更加随意和灵活。所以,识别问候意图不会像构建机器握手那样简单。你需要在工具箱中准备几种不同的方法。
注: 意图是用户在不同上下文中希望NLP系统或聊天机器人执行的目标类别。例如,像“你好”和“嗨”这样的词可能属于问候意图类别,这样聊天机器人就能在用户希望开始对话时使用它们。另一个意图可能是执行某些任务或命令,比如回答查询“‘Hello’在乌克兰语中怎么说?”或者响应翻译命令。你将在本书中学习到意图识别,并在第12章中将其应用于聊天机器人。
1.5.1 基于关键词的问候语识别器
你的第一个聊天机器人将直接来自80年代。如果你看过1983年的科幻经典《战争游戏》,你可能还记得Joshua,一个运行在WOPR计算机上的AI聊天机器人,由Steven Falken教授编程。想象一下,你希望聊天机器人帮助你选择一个游戏来玩,比如国际象棋……或者核战争。这个方法可以扩展到帮助你实现简单的基于关键词的意图识别器,如以下代码所示,适用于类似本章早些时候提到的项目。
示例 1.1 使用 str.split 进行关键词检测
>>> greetings = "Hi Hello Greetings".split()
>>> user_statement = "Hello Joshua"
>>> user_token_sequence = user_statement.split()
>>> user_token_sequence
['Hello', 'Joshua']
>>> if user_token_sequence[0] in greetings:
... bot_reply = "Thermonuclear War is a strange game. "
... bot_reply += "The only winning move is NOT TO PLAY."
>>> else:
... bot_reply = "Would you like to play a nice game of chess?"
>>> bot_reply
'Thermonuclear War is a strange game. The only winning move is NOT TO PLAY.'
这个简单的NLP流程(程序)使用了一个非常简单的算法,叫做关键词检测,且只有两个意图类别:问候和未知(else)。像这样识别用户意图的聊天机器人,功能类似于90年代的命令行应用程序或电话树系统。
基于规则的聊天机器人比这个简单程序更有趣和灵活。开发人员非常喜欢构建和与聊天机器人互动,甚至会构建聊天机器人来让部署和监控服务器变得更加有趣。ChatOps(带有聊天机器人的DevOps)已经在大多数软件开发团队中流行开来。你可以像这样构建一个聊天机器人,通过在else前添加elif语句来识别更多的意图。或者,你可以超越基于关键词的NLP,开始思考如何使用正则表达式来改进它。
1.5.2 基于模式的意图识别
基于关键词的聊天机器人会识别“Hi”、“Hello”和“Greetings”,但它无法识别“Hiiii”或“Hiiiiiiiiiiii”——这些是更激动版本的“Hi”。也许你应该硬编码前200个版本的“Hi”,比如["Hi", "Hii", "Hiii", …],或者通过编程动态创建这样的关键词列表。但有一种更好的方法,它可以让你的聊天机器人识别无穷多种变化的“Hi”:使用正则表达式。正则表达式模式可以比任何硬编码规则或关键词列表更可靠地匹配文本。正则表达式能够识别任何符号或标记的序列模式,甚至可以用来匹配符号和其他字符的序列,比如单词、词性标签,甚至n-gram(几个连续的单词)。
对于正则表达式和关键词匹配器,你需要预见用户会使用哪些单词,并且考虑他们会如何拼写和大写。所以,如果这些字符串不在你的问候词列表中,模式匹配器就会错过像“Hey”或甚至“hi”这样的问候语。那么如果用户选择了一个带有标点符号的问候语,比如“’sup”或“Hi,”呢?在这种情况下,你可以使用str.lower()方法对你的问候语和用户输入的句子都进行大小写折叠,并且可以将更多的单词添加到问候词列表中。你甚至可以添加拼写错误和打字错误,以确保它们不会被遗漏。但这会在你的NLP流程中进行大量的手动数据硬编码,每次你不得不进行大小写折叠、标记化或任何文本预处理时,你都在改变文本的含义,并丢失NLP可能需要的信息。例如,大小写可能是帮助NLP流程识别名字或专有名词的线索。如果你把名字“John”转换为小写,NLP流程可能会错误地将“john”解释为俚语“厕所”的意思。
机器学习承诺通过依赖文本中的统计数据,而不是依赖你对管道需要匹配的模式的猜测,让你的文本数据为自己发声。令人惊讶的是,机器学习不仅可以识别单词的拼写,还能识别单词含义(语义)中的模式。一旦你学会了如何在第3到第6章中使用机器学习方法进行NLP,你会发现,设计和评估NLP管道的大部分繁重工作都会变得自动化。当你进入更复杂、更准确的深度学习模型(第7章及之后的内容)时,你会发现现代NLP管道的某些元素仍然相当脆弱。你将学会如何聪明地构建数据集,以使深度学习NLP管道更加健壮。为了现在的开始,你可以从基础做起。当用户希望通过精确的字符模式指定操作时,正则表达式是非常有用的工具:
>>> import re #1
>>> r = "(hi|hello|hey)[ ,:.!]*([a-z]*)" #2
>>> re.match(r, 'Hello Rosa', flags=re.IGNORECASE) #3
<re.Match object; span=(0, 10), match='Hello Rosa'>
>>> re.match(r, "hi ho, hi ho, it's off to work ...", flags=re.IGNORECASE)
<re.Match object; span=(0, 5), match='hi ho'>
>>> re.match(r, "hey, what's up", flags=re.IGNORECASE)
<re.Match object; span=(0, 9), match='hey, what'>
#1 Python中有两个“官方”正则表达式包。re包是所有版本的Python中预安装的。正则表达式包还包括一些附加功能,如模糊模式匹配。
#2 |表示“或”,而*表示前面的字符可以出现0次或多次,且仍然匹配。
#3 忽略字符大小写意味着这个正则表达式会匹配“Hey”和“hey”。
在正则表达式中,你可以使用方括号指定字符类,并且可以使用连字符(-)表示字符的范围,而不需要逐一列出所有字符。例如,正则表达式[a-z]将匹配任何小写字母,从a到z。字符类后面的星号(*)意味着,如果字符类中的所有字符都连续出现,正则表达式会匹配任意数量的这些字符。
让我们让正则表达式更详细一点,尝试匹配更多的问候语:
>>> r = r"[^a-z]*([y]o|[h']?ello|ok|hey|(good[ ])(morn[gin']{0,3}|"
>>> r += r"afternoon|even[gin']{0,3}))[\s,;:]{1,3}([a-z]{1,20})"
>>> re_greeting = re.compile(r, flags=re.IGNORECASE) #1
>>> re_greeting.match('Hello Rosa')
<re.Match object; span=(0, 10), match='Hello Rosa'>
>>> re_greeting.match('Hello Rosa').groups()
('Hello', None, None, 'Rosa')
>>> re_greeting.match("Good morning Rosa")
<re.Match object; span=(0, 17), match="Good morning Rosa">
>>> re_greeting.match("Good Manning Rosa") #2
>>> re_greeting.match('Good evening Rosa Parks').groups() #3
('Good evening', 'Good ', 'evening', 'Rosa')
>>> re_greeting.match("Good Morn'n Rosa")
<re.Match object; span=(0, 16), match="Good Morn'n Rosa">
>>> re_greeting.match("yo Rosa")
<re.Match object; span=(0, 7), match='yo Rosa'>
#1 你可以编译正则表达式,这样每次使用时就不必再指定选项(标志)。
#2 请注意,这个正则表达式不能识别(匹配)拼写错误的单词。
#3 我们的聊天机器人能够将问候语的不同部分分开为多个组,但它无法识别Rosa的著名姓氏,因为我们没有模式来匹配名字后的任何字符。
提示
在撇号前的“r”表示该字符串是一个原始字符串。Python的原始字符串使得使用正则表达式中的反斜杠更加方便。告诉Python一个字符串是原始字符串时,它会跳过对反斜杠的处理,并将其传递给正则表达式解析器(re包)。否则,你必须使用双反斜杠(\)转义每个反斜杠。因此,匹配空白符的符号\s将变成\s,像大括号等特殊字符会变成\{和\}。
正则表达式的第一行代码包含了大量的逻辑,它能够完成匹配大范围问候语的任务。但它错过了“Manning”这个拼写错误,这也是NLP困难的原因之一。在机器学习和医学诊断测试中,这叫做假阴性分类错误。遗憾的是,它也会匹配一些人类不太可能说的话,或者假阳性,这是一个坏事。既有假阳性也有假阴性错误意味着我们的正则表达式既过于宽松(包含性)又过于严格(排斥性)。这些错误可能让我们的聊天机器人听起来有些呆板和机械。为了让机器人表现得更加智能,我们还需要做更多的工作来完善它匹配的短语。
所有这些繁琐的工作仍然很难捕捉到人类语言中常见的俚语和拼写错误。幸运的是,手工编写正则表达式并不是训练聊天机器人的唯一方法——这在本书的后续章节中会有更多介绍。所以,我们只会在需要精确控制聊天机器人的行为时使用它们,比如在移动电话上使用语音助手时。
但是让我们继续完善这个单一功能的聊天机器人,通过添加一个输出生成器。它需要说点什么。我们使用 Python 的字符串格式化来创建它的响应模板:
>>> my_names = set(['rosa', 'rose', 'chatty', 'chatbot', 'bot',
... 'chatterbot'])
>>> curt_names = set(['hal', 'you', 'u'])
>>> greeter_name = '' #1
>>> match = re_greeting.match(input())
...
>>> if match:
... at_name = match.groups()[-1]
... if at_name in curt_names:
... print("Good one.")
... elif at_name.lower() in my_names:
... print("Hi {}, How are you?".format(greeter_name))
#1 我们还不知道是谁在和机器人聊天,也暂时不担心这个问题。
如果你运行这个小脚本并输入像“Hello Rosa”这样的短语,它会通过询问你的一天过得怎么样来回应你。如果你用稍微带点不礼貌的名字来称呼聊天机器人,它的反应会少一些,但并不会激怒你,以鼓励礼貌。如果你提到其他可能在某个派对线路或论坛上监听对话的人,机器人会保持安静,允许你和你所称呼的人聊天。显然,目前没有其他人在监视我们的 input() 行,但在这种更可能发生的场景中(例如,在一个更大的聊天机器人中),你应该关注这些问题。
由于计算资源的限制,早期的自然语言处理(NLP)研究人员不得不使用他们的大脑计算能力来设计和手动调整复杂的逻辑规则,从自然语言字符串中提取信息。这被称为基于模式的方法。模式不仅仅是字符序列模式,就像我们的正则表达式一样。NLP 还经常涉及词序列模式——词性——以及其他更高层次的模式。像词干提取器和分词器这样的 NLP 核心构建块,以及像 ELIZA 这样的复杂的端到端 NLP 对话引擎(即聊天机器人)就是通过这种方式构建的,依赖于正则表达式和模式匹配。采用基于模式的 NLP 方法的艺术在于发现优雅的模式,精准捕捉我们想要的内容,而不需要过多的正则表达式代码。
提示 经典的 NLP 模式匹配方法基于心智计算理论(CTM)。CTM 假设思维是一个确定性的计算过程,在单一的逻辑线程或序列中进行。神经科学和 NLP 的进展导致了围绕世纪之交形成的联结主义心智理论。这一新理论激发了深度学习使用人工神经网络,处理自然语言序列的方式是多样的,并且是同时和并行的。
在第二章中,你将学习更多基于模式的方法,如如何使用算法将文本分割成标记(或单词),例如 Treebank 分词器。你还将学习如何使用模式匹配通过所谓的 Porter 词干提取器来提取词干(缩短和合并标记)。但是在后续章节中,我们将利用指数级增长的计算资源以及更大的数据集,绕过这种繁重的手动编程和精炼。
如果你是正则表达式的新手,想了解更多内容,可以查阅附录 B 或 Python 正则表达式的在线文档——但你暂时不需要理解它们。我们会继续为你提供正则表达式示例,因为它们是我们 NLP 管道的构建块。所以不要担心它们看起来像胡言乱语。人类的大脑很擅长从一组示例中进行归纳,我们相信到本书结束时这些概念会变得清晰起来。事实证明,机器也可以以这种方式学习。
1.5.3 另一种识别问候的方式
假设你可以访问一个庞大的数据库,里面包含了成千上万,甚至是数百万次对话会话的陈述和回应。在这种情况下,你当然可以通过复制用户的输入并在数据库中搜索完全相同的字符字符串来构建一个聊天机器人。然后,你可以简单地重用过去其他人用过的回应。这将导致一种统计驱动或数据驱动的聊天机器人设计方法,代替那些相当繁琐的模式匹配算法设计。
试想一下,单单一个打字错误或者语句中的变化就能让一个模式匹配的机器人,甚至是一个拥有数百万条语句(话语)数据库的数据驱动机器人,出错。比特和字符序列是离散且非常精确的——它们要么匹配,要么不匹配。而且人们很有创造力。有时候看起来似乎不是这样,但人们经常会说出一些使用了以前从未见过的新字符模式的话。所以你希望你的机器人能够衡量字符序列之间的意义差异。在后续章节中,你将越来越擅长从文本中提取意义!
当我们使用字符序列匹配来衡量自然语言短语之间的距离时,我们经常会出错。比如“good”和“okay”这样的词,它们含义相似,但字符序列不同,通过逐字符匹配计算距离时,它们之间的距离往往很大。有时候,两个词看起来几乎相同,但意思完全不同,就像“bad”和“bag”一样。你可以使用如 Jaccard 或 Levenshtein 等算法来计算一个词到另一个词的字符变化次数。但这些距离或“变化”计数无法捕捉到两个不相似字符序列之间关系的本质,就像“good”和“okay”那样。它们也无法解释一些小的拼写差异可能并不是真正的打字错误,而是完全不同的词,就像“bad”和“bag”那样。
针对数字序列和向量设计的距离度量对于一些 NLP 应用是有用的,比如拼写校正和识别专有名词,因此我们在这些情境下会使用这些距离度量。但对于那些我们更关注自然语言的含义,而不是拼写的 NLP 应用来说,存在更好的方法。在这些情况下,我们使用自然语言词汇和文本的向量表示,并为这些向量设计一些距离度量。在我们讨论这些不同的应用及它们使用的向量类型时,我们会逐一向你展示这些方法。
我们不会长期停留在这种令人困惑的逻辑二元世界中,但让我们假设自己是二战时期著名的密码破译专家梅维斯·贝蒂(Mavis Batey),我们刚刚收到了从两位德国军官之间的通讯中截获的二进制摩尔斯电码消息。这个消息可能包含赢得战争的关键,那我们应该从哪里开始呢?首先的决策层面是使用统计方法分析这些比特流,看看能否找到模式。我们可以先使用摩尔斯电码表(或者在我们这个例子中是 ASCII 表)将每一组比特分配给一个字母。然后,如果这些字符对我们来说是无意义的,就像它们对二战中的计算机或密码学家一样,我们可以开始统计它们,查找我们之前见过的所有单词字典中的短序列,并在每次出现时在该条目旁边做个标记。我们还可以在另一本日志簿中标记这些单词出现的消息,创建一个所有我们阅读过的文档的百科全书式索引。这个文档集合被称为语料库,而我们在索引中列出的单词或序列则被称为词汇表。
如果我们幸运的话——我们并不是在打仗,且我们看到的消息并没有强加加密——我们会看到这些德语单词计数中的模式,和用来传递类似消息的英语单词的计数相吻合。不同于试图破译德语摩尔斯电码的密码学家,我们知道这些符号有一致的含义,不会通过每次按键来试图迷惑我们。这种繁琐的字符和单词计数正是计算机能够无需思考就能做的事。而且,令人惊讶的是,这几乎足以让机器看起来能理解我们的语言。它甚至可以在这些统计向量上进行数学运算,从而与我们对这些短语和单词的理解相吻合。当我们在后续章节中向你展示如何使用 Word2Vec 教会机器我们语言时,可能看起来像魔法一样,但其实并不是。它只是数学——计算。
让我们稍微思考一下,在我们努力计算接收到的消息中的所有单词时,丢失了哪些信息。我们将单词分配到不同的箱子中,并将它们像硬币或代币分拣机那样存储为比特向量(见图 1.5),通过一系列决策将不同类型的代币引导到一边或另一边,最终将它们堆放在底部的箱子里。我们的分拣机器必须考虑成百上千,甚至是数百万种可能的代币“面额”,每个面额代表说话者或作者可能使用的一个单词。我们输入到分拣机中的每个短语、句子或文档最终都会出来,在底部得到一个包含每个槽位中代币计数的“向量”。我们的大多数计数为零,即使是对于那些包含冗长词汇的大文档也是如此。但我们还没有丢失任何单词。那么我们丢失了什么呢?作为一个人类,你能理解我们以这种方式呈现的文档吗?即将每个可能单词的计数列出,而不涉及这些单词的顺序或排列?我们对此表示怀疑。但如果是一个简短的句子或推文,你大概可以将它们重新排列成原本的顺序和含义,大多数时候能够理解。
图 1.5 展示了一个加拿大硬币(或代币)分拣机。
以下是我们的代币分拣机如何适应NLP管道的方式,它位于分词器之后(见第2章)。我们在机械代币分拣机的草图中添加了停用词过滤器以及罕见词过滤器,如图1.6所示。字符串从顶部流入,BOW向量由底部代币“堆叠”的高度轮廓创建。
事实证明,机器能够很好地处理这种BOW,并以这种方式提取出即使是中等长度文档的大部分信息内容。每个文档,在经过代币分拣和计数后,都可以表示为一个向量。图1.6展示了一个粗略的示例,在第2章中,我们将查看一些更有用的BOW向量数据结构。
这是我们语言的第一个向量空间模型。这些箱子和它们包含的每个单词的数字被表示为包含许多零和一些一或二的长向量,这些数字分布在每个单词出现的箱子中。这些单词的不同组合方式创建了这些向量,这就是所谓的向量空间。空间中向量之间的关系构成了我们的模型,模型试图预测这些单词的组合出现在各种单词序列(通常是句子或文档)中的情况。在Python中,我们可以将这些稀疏(大部分为空)向量(数字列表)表示为字典。Python中的Counter是一个特殊类型的字典,它将对象(包括字符串)分配到箱子里并进行计数,就像我们想要的那样:
>>> from collections import Counter
>>> Counter("Guten Morgen Rosa".split())
Counter({'Guten': 1, 'Rosa': 1, 'morgen': 1})
>>> Counter("Good morning Rosa!".split())
Counter({'Good': 1, 'Rosa!': 1, 'morning,': 1})
你可能会想象一些清理这些标记(tokens)的方法,我们在下一章就会做这件事。但你也许会认为有更好的方法来表示像“Guten morgen Rosa”这样的句子。我们将在第3、4和6章中学习不同的表示标记序列的方法。
我们可以想象,一次性将所有的文档、陈述、句子,甚至单个单词输入到这个机器中。每处理完一个陈述,我们会统计每个槽位中的代币,然后我们就可以称其为该陈述的向量表示。这个文档、陈述和单词的模型被称为向量空间模型。现在,我们可以使用线性代数来操作这些向量,计算自然语言陈述的距离和统计数据,这有助于我们解决更广泛的问题,并减少人类编程和NLP管道中的脆弱性。BOW向量序列中一个常被问到的统计问题是:“哪些单词的组合最有可能跟随某个特定的单词集合?”或者更好的是,如果用户输入一系列单词,可以问:“与用户提供的BOW向量最接近的BOW是什么?”这就是一个搜索查询。输入的单词是你可能在搜索框中键入的单词,而最接近的BOW向量对应于你正在寻找的文档或网页。有效地回答这两个问题就足以构建一个机器学习聊天机器人,随着我们提供更多数据,它会变得越来越好。
但等等。也许这些向量与你以前使用过的向量不同——它们的维度非常高。从一个大型语料库计算出的三元组词汇表可能会有数百万维。在第3章中,我们将讨论维度灾难以及使得高维向量难以处理的其他特性。
1.6 对超空间的简要飞行
在第3章中,你将学习如何将单词整合到较少的向量维度中,以应对维度灾难。你甚至可以通过利用所有这些维度来识别你希望你的自然语言理解(NLU)管道理解的细微差别,将灾难转化为祝福。你通过将向量投影到彼此上,以确定每一对向量之间的距离。这为你提供了一个合理的估计,衡量它们意义上的相似性,而不仅仅是它们的统计性词汇使用。当你通过这种方式计算向量的距离时,这称为余弦距离度量。在第3章中,你将首先使用余弦距离,然后在第4章中,通过将成千上万维的主题向量降到只有几个维度,你将揭示它的真正力量。你甚至可以将这些向量投影(嵌入是更精确的术语)到二维平面上,通过绘图和图表“查看”它们。这是发现高维数据中的模式和聚类的最佳方法之一。然后,你可以教会计算机识别并基于这些模式采取行动,这些模式反映了产生这些向量的单词的潜在含义。
想象一下人类可能写出的所有推文、消息或句子。尽管我们经常重复自己,但这仍然是一个庞大的可能性。当这些标记(tokens)被视为单独、独立的维度时,我们没有理由认为“Good morning, Hobs”和“Guten Morgen, Hannes”有任何共同的含义。我们需要创建一个消息的降维向量空间模型,以便用一组连续的(浮动)值对它们进行标注。我们可以对消息和单词进行评级,评价诸如主题和情感之类的特质,提出类似以下的问题:
- 这个消息很可能是一个问题吗?
- 它多大程度上涉及一个人?
- 它有多大程度上涉及我?
- 听起来它有多生气或高兴?
- 它是我需要回应的内容吗?
想想我们可以对陈述进行的所有评分。我们可以将这些评分按顺序排列,并为每个陈述“计算”一个“向量”。我们为一组陈述提供的评分或维度的列表应该比可能的陈述数量小得多,而具有相同含义的陈述应该在所有问题上具有相似的值。这些评分向量变成了机器可以被编程来反应的东西。我们可以通过将陈述聚类(聚类)在一起,简化并概括向量,使它们在某些维度上接近,在其他维度上远离。
但计算机如何为这些向量维度分配值呢?通过简化我们向量维度的问题,比如“它包含单词good吗?”或“它包含单词morning吗?”——以此类推。你可以想象我们可以提出约一百万个问题,最终为短语分配数值。这是第一个实用的向量空间模型,称为比特向量语言模型。你可以理解为什么计算机现在才有足够的能力理解自然语言。人类能够生成的百万维向量在80年代的超级计算机上根本“无法计算!”但在现代的普通笔记本电脑上却毫不费力。使NLP变得实用的,不仅仅是硬件的强大能力和容量;增量式、常数RAM的线性代数算法是让机器破解自然语言代码的最后一块拼图。
还有一种更简单但更大的表示方法,可以在聊天机器人中使用。如果我们的向量维度完全描述字符的精确序列呢?每个字符的向量将包含关于你字母表中每个字母和标点符号的二元(是/否)问题的答案:
- “第一个字母是A吗?”
- “第一个字母是B吗?” …
- “第一个字母是z吗?”
下一个向量将回答关于序列中下一个字母的相同枯燥问题:
- “第二个字母是A吗?”
- “第二个字母是B吗?” … 尽管这个向量序列中有很多“否”答案或零,但它相对于所有其他可能的文本表示方法有一个优点:它保留了每一个微小的细节,每一个原始文本中包含的信息位,包括字符和单词的顺序。这就像一台只能播放单个音符的玩家钢琴的纸质表示。这个自然语言机械玩家钢琴的“音符”是26个大写和小写字母,以及钢琴必须知道如何“播放”的任何标点符号。纸卷不需要比真实玩家钢琴的纸卷宽太多,一些长钢琴曲中的音符数量也不会超过小文档中的字符数量。
但这种单热字符序列编码表示主要用于记录并重放一个精确的片段,而不是创作新内容或提取某个片段的精髓。我们无法轻松地将一个歌曲的玩家钢琴纸卷与另一个进行比较。而且这种表示比文档的原始ASCII编码表示更长。为了保留每个字符序列的信息,文档的表示数量暴涨。我们保留了字符和单词的顺序,但扩展了NLP问题的维度。
这些文档的表示在这种基于字符的向量空间中并不容易聚类。俄罗斯数学家弗拉基米尔·列文斯坦(Vladimir Levenshtein)提出了一种巧妙的方法,用于快速找到向量(字符序列)之间的相似性。列文斯坦算法使得使用这种简化的、机械的语言视图创建一些惊人有趣和有用的聊天机器人成为可能。但真正的魔法发生在我们弄清楚如何将这些高维空间压缩或嵌入到低维空间的模糊意义或主题向量时。我们将在第4章揭开魔术师的幕布,讨论潜在语义索引和潜在Dirichlet分配,这两种技术能够创建更密集且富有意义的陈述和文档的向量表示。
1.7 单词顺序和语法
单词的顺序——语法——很重要。这是我们在前面的BOW(词袋)或词向量中忽略的内容。幸运的是,在大多数简短的短语,甚至许多完整的句子中,这种词向量近似方法工作得很好。如果你只是想编码一句简短句子的整体意义和情感,单词的顺序并不那么重要。看看我们“Good morning Rosa”这个例子的所有排列方式:
>>> from itertools import permutations
>>> [" ".join(combo) for combo in
... permutations("Good morning Rosa!".split(), 3)
... ]
['Good morning Rosa!',
'Good Rosa! morning',
'morning Good Rosa!',
'morning Rosa! Good',
'Rosa! Good morning',
'Rosa! morning Good']
现在,如果你试图单独解释这些字符串(不看其他的),你可能会得出结论,它们可能有相似的意图或含义。你甚至可能注意到单词“Good”的大写,并且在脑海中将其置于短语的前面。但你也可能认为“Good Rosa”是某种专有名词,就像一个餐馆或花店的名字一样。尽管如此,一个聪明的聊天机器人或二战时期布莱切利公园的聪明女人可能会对这六种排列方式中的任何一种作出相同的无害问候,“Good morning my dear General”。
让我们尝试在脑海中处理一个更长、更复杂的短语,一个逻辑语句,其中单词的顺序非常重要:
>>> s = """Find textbooks with titles containing 'NLP',
... or 'natural' and 'language', or
... 'computational' and 'linguistics'."""
>>> len(set(s.split()))
12
>>> import numpy as np
>>> np.arange(1, 12 + 1).prod() # factorial(12) = arange(1, 13).prod()
479001600
从我们简单的问候语的阶乘(factorial(3) == 6)到我们更长语句的阶乘(factorial(12) == 479001600),排列数爆炸式增长!显然,单词顺序中包含的逻辑对于任何希望作出正确响应的机器来说都是至关重要的。尽管常见的问候语通常不会被BOW处理搞乱,但更复杂的语句在被扔进词袋时可能会失去大部分意义。BOW不是处理数据库查询(如前面示例中的自然语言查询)的最佳方式。
无论语句是用正式的编程语言(如SQL)编写,还是用非正式的自然语言(如英语)编写,当语句试图传达事物之间的逻辑关系时,单词顺序和语法都是重要的。这就是计算机语言依赖于严格的语法规则解析器的原因。幸运的是,最近自然语言语法树解析器的进展使得从自然语言中提取语法和逻辑关系变得极为准确(准确率超过90%)。在后续章节中,我们将向你展示如何使用像SyntaxNet(Parsey McParseface)和spaCy这样的包来识别这些关系。
就像布莱切利公园示例中的问候语一样,即使一个语句不依赖于单词顺序来进行逻辑解释,有时,注意到单词顺序仍然能揭示微妙的意义线索,从而促进更深入的回应。这些更深层次的NLP将在下一节中讨论。第二章向你展示了一个技巧,如何将通过单词顺序传递的一些信息融入到我们的词向量表示中。它还展示了如何优化前面示例中使用的粗糙分词器(str.split()),以更准确地将单词分配到词向量的更适当的槽中,这样像“good”和“Good”就会被分配到同一个槽,而像“rosa”和“Rosa”会被分配到不同的槽,但“Rosa!”则不会被误分配。
1.8 聊天机器人自然语言处理管道
构建对话引擎或聊天机器人的自然语言处理管道与构建问答系统所需的管道类似,这在《驾驭文本》中有所描述。然而,图中五个子系统模块中列出的一些算法可能对你来说是新的。实际上,一些最有前景的生成方法,如大语言模型(LLMs),直到最近才被发明出来。每一章都会帮助你实现并测试此图中的一个或多个算法,以便你能够为自己的应用组装合适的管道。图1.7中的四个圆角块显示了分析和生成文本所需的四种处理。
你将需要一个数据库来维持过去陈述和回应的记忆,并建立一个结构化的知识库,帮助为你的管道提供业务逻辑和决策逻辑的支持。图1.7中显示的五个子系统中的每一个都可以通过一个或多个算法来实现:
- 解析 — 从自然语言文本中提取特征、结构化的数值数据。
- 分析 — 通过对文本的情感、语法性和语义进行评分,生成并结合特征。
- 生成 — 使用模板、搜索和语言模型组成可能的回应。
- 决策 — 决定哪个生成的回应最有可能使对话更接近用户的对话目标。
- 数据库 — 存储对话历史、用户信息和一般的世界知识,以供决策子系统使用。
这些四个阶段中的每一个都可以使用图示中相应框中列出的一个或多个算法来实现。你还可以将本书中的Python示例结合到你自己的自然语言处理系统中,以实现大多数应用的最先进性能。到本书结束时,你将掌握实现这五个子系统的几种替代方法。大多数聊天机器人会包含这五个子系统的元素,但许多应用只需要简单的算法,这些算法可以用几行Python代码实现。有些聊天机器人在回答事实性问题时表现更好,而另一些则更擅长信息检索或搜索。而有些聊天机器人甚至能够生成冗长、复杂、看起来像人类说的话的回应。正如你可能猜到的那样,看似合理的回应不一定是正确或有帮助的。你将学习所有最流行的自然语言处理方法的优缺点。
机器学习、深度学习和概率语言模型迅速扩展了你可以成功应用自然语言处理的应用范围。机器学习的数据驱动方法通过为自然语言处理管道提供越来越多的目标领域数据,使其变得越来越复杂。然而,数据并非你所需要的一切。通过更高效的机器学习方法,你通常可以超越竞争对手。本书将帮助你理解如何利用这些进展。
图1.7中的聊天机器人管道包含了本章开头提到的大多数自然语言处理应用的构建模块。我们还展示了一个“反馈循环”,用于生成的文本回应,以便我们的回应可以使用与处理用户陈述相同的算法进行处理。然后,回应的“评分”或特征可以在目标函数中结合,以评估和选择最佳回应,具体取决于聊天机器人的对话计划或目标。本书重点是为聊天机器人配置这个自然语言处理管道,但你也可以看到它与文本检索或“搜索”的自然语言处理问题之间的类比,后者可能是最常见的自然语言处理应用。我们的聊天机器人管道当然也适用于《驾驭文本》中集中讨论的问答应用。
将这个管道应用于金融预测或商业分析可能不那么显而易见。但想象一下你的管道分析部分生成的特征。这些分析或特征生成的特征可以针对你的特定金融或商业预测进行优化。这样,它们可以帮助你将自然语言数据融入到用于预测的机器学习管道中。尽管本书专注于构建聊天机器人,但它为你提供了广泛自然语言处理应用的工具,从搜索到金融预测。
图1.7中的一个处理元素通常不用于搜索、预测或问答系统,那就是自然语言生成。这是聊天机器人的核心特征。然而,文本生成步骤通常会被纳入搜索引擎的自然语言处理应用中,并能为这样的引擎带来巨大的竞争优势。整合或总结搜索结果的能力是许多流行搜索引擎(如DuckDuckGo、Bing和Google)的获胜特性。而你可以想象,对于一个金融预测引擎来说,能够根据它从社交媒体网络和新闻源检测到的与业务相关的事件生成陈述、推文或整篇文章是多么宝贵。下一节将展示如何将这样的系统的各个层次结合起来,为每个阶段的自然语言处理管道增加复杂性和能力。
1.9 深入处理
自然语言处理管道的各个阶段可以被看作是层次,就像前馈神经网络中的层次一样。深度学习的核心是通过在传统的两层机器学习模型架构(即特征提取和建模)中添加更多处理层,来创建更复杂的模型和行为。在第五章中,我们将解释神经网络如何通过反向传播模型误差,从输出层回传到输入层,帮助将学习分散到各个层次。但在这里,我们讨论的是顶部层次以及通过独立训练每一层来实现的内容。
图1.8中的前四层对应于上一节中聊天机器人管道的前两个阶段(特征提取和特征分析)。例如,词性标注(POS)是生成聊天机器人管道分析阶段中特征的一种方式。词性标注是通过默认的spaCy管道自动生成的,该管道包括此图中的前四个层次。词性标注通常是通过有限状态转换器(FST)实现的,类似于nltk.tag包中的方法。
图中的底部两层(实体关系和知识库)用于填充包含特定领域信息(知识)的数据库。然后,使用这六个层次从特定陈述或文档中提取的信息可以与该数据库结合使用,从而进行推理。推理是对环境中检测到的一组条件的逻辑外推,就像聊天机器人用户陈述中包含的逻辑一样。这种在图中较深层次的“推理引擎”被认为是人工智能的领域,在这个领域中,机器可以对其世界进行推理,并利用这些推理做出逻辑决策。然而,聊天机器人可以在没有知识数据库的情况下做出合理决策,只使用顶部几层的算法,这些决策的结合可以产生出乎意料的类人行为。
在接下来的几章中,我们将深入探讨自然语言处理的顶部几个层次。前三个层次就是执行有意义的情感分析、语义搜索以及构建模拟人类的聊天机器人所必需的。实际上,使用仅仅一层处理就可以构建一个有用且有趣的聊天机器人,直接将文本(字符序列)作为语言模型的特征。一个只进行字符串匹配和搜索的聊天机器人,在有足够的示例陈述和回应的情况下,能够参与到一个相当令人信服的对话中。
例如,开源项目ChatterBot通过仅计算输入陈述和其数据库中记录的陈述之间的字符串“编辑距离”(Levenshtein距离)来简化这个管道。如果其陈述–回应对的数据库中包含匹配的陈述,则可以将相应的回应(来自先前“学习”的人类或机器对话)重新使用为对最新用户陈述的回应。对于这个管道,只需要我们的聊天机器人管道中的第3步(生成)。在这一阶段,仅需要一个暴力搜索算法来找到最佳回应。通过这种简单的技术(不需要分词或特征生成),ChatterBot能够维持一个令人信服的对话,作为由Gunther Cox用回收零件构建的机械机器人Salvius的对话引擎。
Will是由Steven Skoczen开发的一个开源Python聊天机器人框架,采用完全不同的方法。Will只能通过编程正则表达式来回应陈述——这是一种劳动密集型、数据较少的自然语言处理方法。这种基于语法的方法对于问答系统和任务执行助手机器人(如Lex、Siri和Google Now)特别有效。这些系统通过使用“模糊正则表达式”和其他技术来克服正则表达式的“脆弱性”,以找到近似的语法匹配。模糊正则表达式通过忽略最大数量的插入、删除和替换错误,在可能的语法规则(正则表达式)列表中找到最接近的语法匹配,而不是精确匹配。然而,扩展模式匹配聊天机器人的行为范围和复杂性需要大量困难的人工开发工作。即使是最先进的基于语法的聊天机器人,这些聊天机器人由全球一些最大的公司(如Google、Amazon、Apple和Microsoft)构建和维护,仍然处于聊天机器人智商深度和广度的中游水平。
通过浅层的自然语言处理可以做很多强大的事情,并且几乎不需要任何人工监督(标签或文本整理)。通常,机器可以从其环境中持续学习(例如,它可以从Twitter或其他来源中获取的单词流中学习)。我们将在第七章向你展示如何做到这一点。
1.10 自然语言智商
就像人类的智力一样,NLP管道的能力也不能仅通过一个智商分数来衡量,而不考虑多个“聪明”维度。衡量机器人系统能力的一种常见方式是通过行为复杂性和所需人工监督程度这两个维度。然而,对于NLP管道来说,目标是构建完全自动化处理自然语言的系统,消除所有人工监督(在模型训练和部署后)。因此,一个更好的智商维度应当捕捉到自然语言管道复杂性的广度和深度。
像Amazon Alexa或Google Allo这样的消费者产品聊天机器人或虚拟助手,通常被设计成拥有极其广泛的知识和能力。然而,用于回应请求的逻辑往往较为浅显,通常由一组触发短语组成,这些短语通过一个单一的if-then决策分支产生相同的响应。Alexa(以及底层的Lex引擎)表现得像一个单层的、扁平的(if, elif, elif, …)语句树。Google Dialogflow(这是独立于Google的Allo和Google Assistant开发的)具有与Amazon Lex、Amazon Contact Flow和Lambda相似的功能,但没有用于设计对话树的拖放式用户界面。
另一方面,Google Translate管道(或任何类似的机器翻译系统)依赖于一个深层次的特征提取器树、决策树和知识图谱,它们连接着关于世界的知识片段。有时,这些特征提取器、决策树和知识图谱是显式地编程到系统中的,如图1.9所示。另一种快速超越这种硬编码管道的方法是深度学习的数据驱动方法。深度神经网络的特征提取器是通过学习获得的,而不是硬编码的,但它们通常需要更多的训练数据才能实现与有意设计的算法相同的性能。
在为能够在特定知识领域进行对话的聊天机器人逐步构建NLP管道时,你将同时使用这两种方法(神经网络和手工编码算法)。这将为你提供在行业或业务领域内完成NLP任务所需的技能。在此过程中,你可能会得到如何扩展该NLP管道能做的事情的想法。图1.9将聊天机器人放置在现有的NLP系统中。想象一下你与之互动过的聊天机器人。你认为它们可能在这样的图表中处于什么位置?你是否尝试过通过提问一些困难的问题或类似智商测试的方式来评估它们的智力?试着问一个聊天机器人一些模棱两可的问题,这些问题需要常识逻辑和提问澄清问题的能力,例如:“哪个更大,太阳还是一枚镍币?”你将在后续章节中有机会做这件事,帮助你决定你的聊天机器人与图中其他聊天机器人相比如何。
随着你逐步阅读本书,你将逐步构建一个聊天机器人的各个元素。聊天机器人需要使用所有的自然语言处理(NLP)工具来实现良好的效果:
- 特征提取 — 通常用于生成向量空间模型
- 信息提取 — 能够回答事实性问题
- 语义搜索 — 能够从文档数据库中检索相关知识
- 自然语言生成(NLG) — 生成新的、有意义的陈述
机器学习为你提供了一条捷径,可以快速构建行为像是由一团队程序员花费数年时间编写的、包含数百个复杂硬编码算法和决策树分支的机器。借助现代的NLP管道,你可以教机器有效地响应文本中的模式,而无需陷入正则表达式的困境;你只需要提供用户陈述的示例以及你希望聊天机器人模仿的回应。机器学习对于拼写错误和打字错误比任何手工编写的专家系统或正则表达式都不那么挑剔,机器学习生成的语言模型要更好、更通用、统计上更准确。
机器学习的NLP管道更容易“编程”。你不再需要预见到用户所使用的每种语言中符号的每一种可能用法;你只需要为训练管道提供带标签的示例。你的聊天机器人回应的质量主要取决于标签数据集的质量,这使得NLP变得更加易于团队中的其他人使用。
NLP正在彻底改变我们沟通、学习、做生意甚至思考的方式。我们正亲眼见证机器生成的内容越来越多地渗透进社会集体智慧的各个方面。你也将很快开始构建、训练和调整NLP系统,以模拟类人的对话行为。在接下来的章节中,你将学会如何使用任何你感兴趣的领域知识(从金融、体育到心理学和文学)训练聊天机器人或NLP管道。如果你能找到关于某一主题的文本语料库,你就可以训练机器与该内容进行互动。
本书旨在通过机器学习构建智能的文本阅读机器,而无需你预见到人们表达方式的所有可能性。每一章都在逐步改进图1.7和本书封底所示的聊天机器人架构的基本NLP管道。当你学习NLP工具时,你将构建一个不仅能够进行对话,还能帮助你在商业和生活中实现目标的NLP管道。
1.11 自测
- 为什么自然语言处理(NLP)被认为是通用人工智能(AGI,类人AI)的核心使能特性?
- 如果你的训练数据中包含许多反社会的示例,你将如何构建一个亲社会的聊天机器人?
- 聊天机器人中的五个主要子系统是什么?
- 自然语言处理(NLP)在搜索引擎中如何使用?
- 编写一个正则表达式来识别你的名字以及你见过的所有拼写变体(包括昵称)。
- 编写一个正则表达式来尝试识别句子边界(通常是句号、问号或感叹号)。
提示 主动学习(例如,通过像本小节中的问题那样自我提问)可以加速你对任何新主题的理解。事实证明,这种方法对机器学习和模型评估都有效。
总结
- 通过构建亲社会的NLP软件,你可以帮助使世界变得更美好。
- 机器可以解读单词的含义和意图。
- 一个智能的NLP管道可以处理歧义并帮助纠正人类的错误。
- 机器可以通过处理未标记的文本来构建关于世界的知识库。
- 聊天机器人可以被看作是语义搜索引擎,从用于训练的文档中检索最相关的回应。
- 正则表达式不仅仅用于搜索。