自然语言处理迁移学习实战

978-7-115-61571-8
作者: [加纳] 保罗·阿祖雷(Paul Azunre)
译者: 李想朱仲书张世武
编辑: 秦健

图书目录:

详情

迁移学习作为机器学习和人工智能领域的重要方法,在计算机视觉、自然语言处理(NLP)、语音识别等领域都得到广泛应用。本书是迁移学习技术的实用入门图书,能够带领读者深入实践自然语言处理模型。首先,本书回顾了机器学习中的关键概念,并介绍了机器学习的发展历史,以及NLP迁移学习的进展;其次,深入探讨了一些重要的NLP迁移学习方法—NLP浅层迁移学习和NLP深度迁移学习;最后,涵盖NLP迁移学习领域中最重要的子领域—以Transformer作为关键功能的深度迁移学习技术。读者可以动手将现有的先进模型应用于现实世界的应用程序,包括垃圾电子邮件分类器、IMDb电影评论情感分类器、自动事实检查器、问答系统和翻译系统等。 本书文字简洁、论述精辟、层次清晰,既适合拥有NLP基础的机器学习和数据科学相关的开发人员阅读,也适合作为高等院校计算机及相关专业的学生参考用书。

图书摘要

版权信息

书名:自然语言处理迁移学习实战

ISBN:978-7-115-61571-8

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

著    [加纳]保罗•阿祖雷(Paul Azunre)

译    李 想 朱仲书 张世武

责任编辑 秦 健

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315

读者服务:

微信扫码关注【异步社区】微信公众号,回复“e61571”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。

内容提要

迁移学习作为机器学习和人工智能领域的重要方法,在计算机视觉、自然语言处理(NLP)、语音识别等领域都得到广泛应用。本书是迁移学习技术的实用入门图书,能够带领读者深入实践自然语言处理模型。首先,本书回顾了机器学习中的关键概念,并介绍了机器学习的发展历史,以及NLP迁移学习的进展;其次,深入探讨了一些重要的NLP迁移学习方法——NLP浅层迁移学习和NLP深度迁移学习;最后,涵盖NLP迁移学习领域中最重要的子领域——以Transformer作为关键功能的深度迁移学习技术。读者可以动手将现有的先进模型应用于现实世界的应用程序,包括垃圾电子邮件分类器、IMDb电影评论情感分类器、自动事实检查器、问答系统和翻译系统等。

本书文字简洁、论述精辟、层次清晰,既适合拥有NLP基础的机器学习和数据科学相关的开发人员阅读,也适合作为高等院校计算机及相关专业的学生参考用书。

推荐语

这本书对NLP背景下的迁移学习做了精彩阐述。内容深入浅出,案例丰富,值得深入阅读。迁移学习本质上是知识、算力的复用。在目标检测、模式识别、NLP等领域,迁移学习大有可为。

——许国强,三一重工SaaS首席信息官

迁移学习是近几年NLP领域最重要的研究方向之一。这本书以实例和代码的形式对NLP迁移学习的基本概念、业务应用以及发展方向做了详细介绍。这本书介绍的多个先进模型和算法在业务实践中都得到广泛应用。对想了解NLP迁移学习并在实际工作中落地的研究人员来说,这是一本很好的参考书。

——梁磊,蚂蚁集团资深技术专家

迁移学习技术在感知类机器学习场景取得了长足的进步,尤其是BERT系列的预训练模型将NLP领域的基线提升到新的高度。这本书系统、全面且贴合实际地介绍了这个高速发展的主题,值得NLP领域的工程师深入阅读和探究。

——朱亮,Meta(原Facebook)资深算法工程师

迁移学习是机器学习领域一次革命性的技术突破,特别是在NLP领域取得了令人振奋的成就。同时,我们相信迁移学习深刻的思想也会应用到其他领域,并且取得不错的成绩。推荐机器学习相关领域的工程师阅读这本书,保持对迁移学习的持续关注。

——刘冰洋,Google资深算法工程师

这本书对迁移学习的理论给出了全面且翔实的介绍,可以帮助读者建立清晰的认知。更为难得的是,这本书以实际的业务问题作为驱动,引领读者阅读和学习。推荐给NLP领域的相关工程师。

——赵海,美团技术专家

推荐序

迁移学习,顾名思义,就是将某个领域获得的知识调整或迁移到另一个领域,也就是我们常说的“举一反三,触类旁通”。人类对迁移学习的理论性研究要追溯到1901年。当时心理学家桑代克和伍德沃思提出了学习迁移(transfer of learning)的概念,并研究了人们学习某个新的概念时怎样对学习其他概念产生迁移。这对后来教育学的发展产生了重要影响。

随着人工智能浪潮的兴起,人们开始思索把学习迁移的思想应用到机器学习中。传统的机器学习,尤其是有监督学习,对数据的样本数量、数据分布的统一性、标签的完整性等都有着严苛的要求,而迁移学习解决的正是在机器学习任务中面对样本不足、标签不全等情况时,如何借助外部服从其他分布的数据来有效地学习这一问题。20世纪90年代以来,大量研究都涉及迁移学习的概念,如自主学习、终生学习、多任务学习、知识迁移等,但是这些研究都没有形成完整的体系。直到2010年,迁移学习的首个形式化定义正式提出,由此,迁移学习成为机器学习中一个重要的分支领域。近些年来,深度迁移学习,尤其是其在NLP领域的建树让我们看到了迁移学习非凡的潜力。

学术界将要进行迁移学习的原因总结为3个方面。其一是大数据与少标注之间的矛盾。我们所处的“大数据时代”每时每刻产生着海量的数据,但是这些数据缺乏完善的数据标注,而机器学习模型的训练和更新都依赖于数据的标注,但目前只有很少的数据被标注和利用,这给机器学习和深度学习的模型训练与更新带来了挑战。其二是大数据与弱计算之间的矛盾。海量的数据需要具有强计算能力的设备进行存储和计算,而具有强计算能力的设备通常是非常昂贵的,此外使用海量数据来训练模型是非常耗时的,这就导致了大数据与弱计算之间的矛盾。其三是普适化模型与个性化需求之间的矛盾。机器学习的目的是构建尽可能通用的模型来满足不同用户、不同设备、不同环境的不同需求,这就要求模型有较高的泛化能力,但是普适化的通用模型无法满足个性化、差异化的需求。

传统机器学习的方法不能解决上述矛盾,迁移学习则为这些矛盾的解决提供了思路。迁移学习通常具有3方面优点:首先是更高的起点,在微调之前,源模型的初始性能比不使用迁移学习的高;其次是更快的收敛速度,在训练的过程中源模型提升的速率比不使用迁移学习的快;最后是更好的渐进性,迁移学习得到的模型的收敛性能比不使用迁移学习的好。

迁移学习在NLP领域成绩斐然,尤其是预训练语言模型取得了惊人的成功。给定足够的数据、大量参数和足够的算力,模型就可以有不错的学习成果。根据过往的实验,语言建模比机器翻译或自动编码等其他预训练工作更有效。

本书介绍了NLP领域的历史、发展过程和研究现状。从最基础的词袋模型出发,讲述NLP迁移学习技术的起源、发展和近期的大放异彩,对当前热门的预训练语言模型(如BERT、ELMo、GPT)进行了深入论述。本书的讲解深入浅出,既有完备的数学论证,又通过生动的语言帮助读者建立感性的认知,让人不禁为作者的匠心喝彩!同时本书以若干NLP领域的常见问题作为主线,通过示例代码引领读者亲自实践,领略NLP迁移学习带来的实实在在的业务效果提升,这也是我个人非常推崇的著书行文的方式。

我在这里向NLP、机器学习领域的工程师,或者有志于在这些领域有所建树的学子,隆重推荐本书!NLP迁移学习领域已经取得了辉煌的成绩,并且仍然处于蓬勃发展中。我们需要构建清晰的理论框架,同时,对于该领域的进展也需要时刻保持关注。

余利华

网易有数总经理

译者序

我最早接触自然语言处理(Natural Language Processing,NLP)领域,还是十多年前在北京航空航天大学计算机学院读研究生的时候。彼时主流的解决方案还是词袋模型和统计学习配合的方法。直到2012年前后,深度学习横空出世,在图像领域取得了令人瞩目的成就。此后,诸多基于深度神经网络的NLP技术问世,我也一直跟随着业界的步伐,接触了CNN、RNN、LSTM等技术。但是,那个时候我还固执地认为,所谓机器学习,不过是用数据对模型参数进行拟合。因此,在工作中,我更喜欢用“拟合”而非“学习”,就连写代码时我使用的方法名也是fit而不是learn。

直到以词嵌入为代表的预训练技术出现,我的观念开始动摇。时间来到2018年,随着BERT系列方法出现,我彻底被迁移学习技术所折服。我开始认识到,一个模型的训练和人的学习成长过程可以如此相似。先用大而全的基础数据进行预训练,再用少而精的目标领域数据进行微调,这与人类教学体系中先进行通识教育,然后再划分专业培养的路径何其相似。

迁移学习是运用已有的知识来学习新的知识,其核心是找到已有知识和新知识之间的相似性。由于直接对目标域从零开始学习成本太高,故而转向运用已有的相关知识来辅助尽快地学习新知识。比如,已经会下中国象棋,就可以类比着来学习国际象棋;已经学会骑自行车,就可以类比着来学习骑电动自行车;已经学会英语,就可以类比着来学习法语……原来机器的学习和人类的学习相似,也是可以“举一反三,触类旁通”的。

本书对NLP迁移学习领域提供了详尽的介绍。本书回顾了NLP领域的发展历史,结合业务应用的实例,讲述这个领域如何一步步“从原始走向现代”。而针对当前各种流行的方法,本书给出了深入浅出的分析,力求向读者呈现出各方法详尽且易懂的面貌。更难能可贵的是,本书不完全专注于理论,而是强调通过有代表性的示例代码来帮助读者形成直观的认知。本书提供的代码经过简单修改即可适用于新的应用场景,可以帮助读者解决遇到的实际问题。本书是NLP迁移学习领域的一本佳作。能够参与本书的翻译,我备感幸运。

NLP迁移学习的方法体系自问世以来,取得了令世人瞩目的成就,大大提升了NLP领域处理各类问题的整体基线水平。更有甚者,在多项测试中,模型的水平已经超越了人类的水平。作为科技领域的工作者,我也期待有朝一日NLP领域的自动翻译技术能够代我工作,自动给出信、达、雅的翻译结果。

我负责全书统稿,并具体负责第4章、第7章~第10章、前言和附录A、附录B等内容的翻译工作。我目前就职于网易杭州研究院。本书的翻译工作得到网易杭州研究院多位同人的关心和支持,通过与这些资深人士进行探讨,书中许多概念得以厘清,很多表述得到恰当的中文表达。在此向汪源、胡光龙、刘东、毛世杰、樊峰峰、汪勇武、胡登军等领导和同事致谢!

朱仲书负责第3章、第5章、第6章、第11章的翻译。朱仲书目前就职于蚂蚁集团。在本书翻译过程中,我们得到朱仲书所在知识图谱团队各位领导、同事的大力支持。在此感谢梁磊、刘志臻、王义、桂正科、林昊、李建强等同人!

张世武负责第1章和第2章的翻译。张世武目前就职于三一重工。在本书翻译过程中,我们得到湖南三一智能控制设备有限公司总经理谭凌群、三一智能研究院院长姚洪涛以及索春明、谭永波等领导的支持与关怀。感谢三一智能化研究所黄建深、谢辉、刘美博、谭林朋、杨毅、宋跃文、鄢徐旸等同人的大力支持,尤其是宋跃文,他在机器学习落地方面做了很多深入的探索,特此致谢!

本书的翻译工作也得到家人们的理解和支持,在此向他们表示感谢!

李想

序  言

在过去的数年中,自然语言处理(NLP)技术得到了飞速发展。在这段时间里,关于NLP模型发展趋势的新闻文章令人目不暇接,其中包括ELMo、BERT以及GPT-3[1]。人们之所以对NLP技术感到兴奋,正是因为这些模型的出现,让那些我们在3年前还无法想象的应用成为现实,比如仅仅基于需求描述就自动生成生产级代码,或者自动生成足以以假乱真的诗歌和博文等。

[1] OpenAI公司于2023年3月15日发布了GPT-4。——编辑注

这种进步的一大驱动正是NLP模型中日益复杂的迁移学习技术。NLP中的迁移学习是一种日渐流行且令人兴奋的模式,因为它使得用户可以将从某个场景中获得的知识调整或者迁移到其他不同语言或任务的场景中。这是NLP的一大进步,更广泛地说,也是人工智能(Artificial Intelligence,AI)的一大进步,即允许知识在新的环境中复用,并且消耗更少的资源。

作为西非国家加纳的公民,对于这个话题我更是体会深刻。在加纳,许多草根企业家和创业者无力承担高昂的算力成本,同时对基础的NLP问题也有心无力。迁移学习可以为工程师赋能,帮助他们在起步阶段有所作为,构建甚至能拯救生命的NLP技术。如果没有迁移学习,这很难想象。

2017年,我第一次有了这样的想法,彼时我正在美国国防部高级研究计划局(Defense Advanced Research Projects Agency,DARPA)研究开源自动机器学习技术。我们使用迁移学习来降低对标注数据的需求,具体而言就是首先在模拟数据集上训练NLP模型,然后将模型迁移到真实标注的小数据集上。之后不久,突破性的ELMo模型出现,它激励我进一步去了解迁移学习,并探索如何在自己的软件项目中进一步实践。

很自然地,我发现由于这些想法的新颖性和技术的飞速发展,NLP领域缺少一本全面、系统且实用的书。在2019年,当有机会撰写这样一本书时,我没有丝毫犹豫。现在,读者手里拿到的便是近两年来我为了这一目标而努力的成果。本书将带领读者快速了解NLP领域中近几年的关键NLP模型,同时提供可执行的代码,并且读者能够在自己的项目中直接修改和复用这些代码。本书涵盖的网络结构和用例可以帮助读者掌握NLP领域的基本技能,以便在该领域进行更深入的探索。

进一步了解迁移学习是一个很好的决定。新理论、算法和突破性应用创造的机会比比皆是。我满怀期待,这些创新性的事物将对我们周围的社会产生积极的影响。

前  言

本书试图对NLP迁移学习这一重要领域进行全面介绍。本书并不完全专注于理论,而是强调通过有代表性的代码和示例来帮助读者建立对NLP迁移学习的直观认识。本书的代码旨在能够快速修改和重新调整其用途,以帮助读者解决遇到的实际问题。

本书的目标读者

为了充分学习本书,读者需要掌握一些Python的相关知识,以及具备中等程度的机器学习技能,如需要理解基本的分类和回归等概念。此外,掌握一些基本的数据操作和预处理技能,如pandas和NumPy库的运用,对学习本书也是大有裨益的。

也就是说,本书的写作方式可以帮助读者通过一些额外的学习来掌握这些技能。本书的前3章将帮助读者快速了解所需的知识,以充分掌握NLP迁移学习的概念,并将其应用于自身的项目。然后,如果读者认为还需要一些其他的背景知识,那么可以自主学习精选的引用内容,这将起到巩固的作用。

阅读路径

本书分为3部分。建议读者按照顺序阅读,这么做可以获得最大的收益。

第一部分回顾了机器学习中的关键概念,介绍了机器学习的发展历史,以及NLP迁移学习的最新进展是机器学习领域发展水到渠成的结果,同时该部分也揭示了人们研究该主题的动因。该部分还介绍了两个示例,这些示例既可以帮助读者回顾更传统的NLP方法,也可以帮助读者动手掌握一些NLP迁移学习的关键的现代方法。该部分涵盖的各章介绍如下。

第1章介绍迁移学习在人工智能和NLP中的具体含义。同时,该章着眼于介绍技术进步的历史进程,正是这些进步使迁移学习得以实现。

第2章介绍两个典型的NLP问题,并说明如何获取这些问题的数据以及进行预处理。这一章还介绍如何使用逻辑斯谛回归和支持向量机等传统线性机器学习方法来建立基线。

针对第2章介绍的两个问题,第3章继续使用传统的、基于树的机器学习方法——随机森林和梯度提升机来建立效果基线。这一章还会使用重要的现代迁移学习方法——ELMo和BERT来对这两个问题建立效果基线。

第二部分深入探讨了一些重要的NLP迁移学习方法,它们主要基于浅层神经网络,即层数相对较少的神经网络。这一部分开始详细地探讨深度迁移学习,诸如ELMo这类具有代表性的技术。ELMo采用了循环神经网络(Recurrent Neural Network,RNN)来实现关键功能。该部分涵盖的各章介绍如下。

第4章应用浅层迁移学习的单词、句子嵌入技术,例如Word2Vec和Sent2Vec,以进一步探索本书第一部分中的某些示例。这一章还介绍了多任务学习和领域适配等重要的概念。

第5章介绍一组基于RNN的NLP深度迁移学习方法,并新引入两个示例数据集,用于对这些方法的研究。

第6章更详细地探讨了第5章中介绍的方法,并将这些方法应用于新引入的数据集。

第三部分涵盖NLP迁移学习领域中最重要的子领域:以Transformer作为关键功能的深度迁移学习技术,例如BERT和GPT。实践表明这类模型对近几年的应用拥有极大的影响力。其中部分原因是,与先前同等规模的模型相比,新模型的并行计算的结构具有更好的可扩展性。这一部分还深入探讨了提高迁移学习效率的各种适配策略。该部分涵盖的各章介绍如下。

第7章描述基本的Transformer神经网络结构,并使用它的一个重要变体GPT来完成一些文本生成任务和打造一个基础的聊天机器人。

第8章介绍一个重要的基于Transformer的神经网络结构BERT,并将它应用于多个任务,包括自动问答任务、填空任务和针对低资源语言实现跨语言转换任务。

第9章介绍一些适配策略,旨在提高迁移学习的效率,包括差别式微调、ULMFiT方法的逐级解冻策略以及知识蒸馏。

第10章介绍其他的适配策略,包括ALBERT方法引入的嵌入因子分解策略与跨层参数共享策略。本章还介绍了适配器和多任务适配策略。

第11章对本书进行总结,回顾重要主题,简要讨论新兴研究方向,例如考量NLP迁移学习技术潜在的负面影响及其应对策略。这些负面影响包括对不同的族群带有偏见的预测,以及训练大型模型对环境的影响等。

软件需求

我们推荐使用Kaggle的Notebook来执行NLP迁移学习涉及的代码,因为这样可以让读者立即行动起来,而无须花时间进行各种设置操作。此外,在撰写本书时,Kaggle还提供了免费的GPU资源,可以帮助很多缺少强大GPU资源的研究人员使用此类方法。附录A提供了Kaggle入门指南,以及作者的一些个人提示,以帮助读者最大限度地发挥平台效用。我们在Kaggle上公开托管了所有的Notebook,并附带了所有必需的数据,使读者能够通过几次单击就开始执行代码。不过,请记住使用“复制和编辑”(fork)Notebook,而不要复制代码并粘贴到新的Notebook中,这样能确保环境中生成的库与我们编写的代码相匹配。

致  谢

我要感谢Ghana NLP开源社区的成员,在这个社区中我有幸了解到关于这个重要领域的很多信息。来自小组成员和我们开发的工具的用户反馈可以帮助我强化了对NLP迁移学习技术的真正变革的理解。这激发了我的灵感,促使我将本书写作完成。

我要感谢Manning出版社的文稿编辑Susan Ethridge,她花费了大量的时间阅读我的手稿,并提供反馈意见,还指导我克服许多困难。我还要感谢技术编辑Al Krinker为帮助我提高写作能力所付出的时间和努力。

我要感谢编辑部门、营销部门和制作部门的所有成员,没有他们,这本书不可能成功出版。他们是Rebecca Rinehart、Bert Bates、Nicole Butterfield、Rejhana Markanovic、Aleksandar Dragosavljević、Melissa Ice、Branko Latincic、Christopher Kaufmann、Candace Gillhoolley、Becky Whitney、Pamela Hunt和Radmila Ercegovac(排名不分先后)。

在本书的写作过程中,技术同行审稿人在若干关键时刻提供了宝贵的反馈意见,如果没有这些宝贵的反馈意见,本书一定会有缺憾。我非常感谢他们。他们是Andres Sacco、Angelo Simone Scotto、Ariel Gamiño、Austin Poor、Clifford Thurber、Jaume López、Marc-Anthony Taylor、Mathijs Affourtit、Matthew Sarmiento、Michael Wall、Nikos Kanakaris、Ninoslav Cerkez、Or Golan、Rani Sharim、Sayak Paul、Sebastián Palma、Sergio Govoni、Todd Cook和Vamsi Sistla。我要感谢技术校对员Ariel Gamiño,感谢他在校对过程中发现了许多语言文字错误和其他技术性错误。

我非常感谢我的妻子Diana,感谢她在这项工作上对我的支持和鼓励。我还要感谢我的母亲和我的兄弟姐妹们——Richard、Gideon和Gifty,感谢他们一直以来对我的鼓励。

作者简介

Paul Azunre拥有麻省理工学院计算机科学博士学位,曾担任美国国防部高级研究计划局(DARPA)的多个研究项目的主任研究员。由他创建的Algorine公司致力于推进AI/ML技术并让这些技术产生重大社会影响。Paul还参与创建了Ghana NLP开源社区。该社区专注于NLP技术的应用,尤其是对加纳语和其他低资源语言进行迁移学习。

资源与支持

本书由异步社区出品,社区(https://www.epubit.com)为您提供相关资源和后续服务。

您还可以扫码二维码, 关注【异步社区】微信公众号,回复“e61571”直接获取,同时可以获得异步社区15天VIP会员卡,近千本电子书免费畅读。

配套资源

本书提供示例代码。

要获得以上配套资源,请在异步社区本书页面中单击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。

如果您是教师,希望获得教学配套资源,请在异步社区本书页面中直接联系本书的责任编辑。

提交勘误

作者、译者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,单击“发表勘误”,输入错误信息,单击“提交勘误”按钮即可,如右图所示。本书的作者和编辑会对您提交的错误信息进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

扫码关注本书

扫描下方二维码,您将会在异步社区微信服务号中看到本书信息及相关的服务提示。

与我们联系

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区投稿(直接访问www.epubit.com/contribute即可)。

如果您所在的学校、培训机构或企业想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接通过邮件发送给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

关于异步社区和异步图书

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT图书的品牌,依托于人民邮电出版社几十年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、人工智能、测试、前端、网络技术等。

异步社区

微信服务号

第一部分 导论

1章~第3章将介绍机器学习中的关键概念、机器学习的发展历史,以及NLP迁移学习的最新进展,同时揭示研究该主题的重要意义。本部分还将介绍两个示例。这两个示例既可以帮助读者回顾更传统的NLP方法,也可以帮助读者动手掌握一些NLP迁移学习的关键的现代方法。

第1章 迁移学习简介

本章涵盖以下内容。

在人工智能(Artificial Intelligence,AI)和自然语言处理(Natural Language Processing,     NLP)领域中,迁移学习到底是什么?

典型的NLP任务和相关的NLP迁移学习的发展进程。

计算机视觉领域的迁移学习综述。

近年来NLP迁移学习技术流行的原因。

人工智能以一种戏剧性的方式改变了现代社会。机器现在可以执行很多过去由人类完成的任务,而且速度更快、成本更低,在某些情况下,效率更高。典型的例子包括计算机视觉应用,它教会计算机理解图像和视频,例如检测闭路电视摄像中的犯罪嫌疑人。其他计算机视觉应用还包括根据患者器官影像检测疾病和根据植物叶片确定植物物种。NLP是人工智能的另一个重要分支,专门用于人类自然语言数据的分析和处理。NLP应用的例子包括语音到文本的转录和不同语言之间的翻译。

人工智能机器人和自动化技术“革命”(有人称之为“第四次工业革命”[1])是由大型神经网络的训练算法的进步、可通过互联网获取的海量数据,以及基于图形处理单元(Graphical Processing Unit,GPU)的大规模并行计算共同促成的。GPU最初是面向个人游戏市场开发的。近期,此前依赖人类感知的任务得到自动化,特别是计算机视觉和NLP的快速发展,推动神经网络理论和实践取得重大进展。这一领域的发展使得从输入数据到期望的输出信号之间的映射有了更复杂的表示,从而能够处理那些曾经非常棘手的AI难题。

[1] Schwab K, The Fourth Industrial Revolution (Geneva: World Economic Forum, 2016).

与此同时,人们预言未来人工智能达到的水平将会远远超过当前实践中已经达到的水平。一些观点警告,在一个世界末日般的未来,机器将会取代大部分(甚至是所有)人类的工作岗位。更极端地,可能对人类的生存产生威胁。NLP也没有被该推测排除在外,因为它是当今人工智能中最活跃的研究领域之一。通过阅读本书读者将可以更好地理解:在不久的将来人工智能、机器学习和NLP技术有哪些切实可行的应用。然而,本书更希望帮助读者掌握一整套迁移学习的实操技能,这在NLP领域中是非常重要的。

迁移学习旨在利用来自不同环境(不同的任务、语言或领域)的先验知识来帮助用户解决手头的问题。它受到人类学习方式的启发,因为我们通常不会从零开始学习任何给定问题的知识,而是建立在可能相关的先验知识基础之上。例如,当一个人已经知道如何演奏一种乐器时,一般认为这个人学习演奏另一种乐器会更加容易。显然,乐器越相似——例如风琴和钢琴,先验知识的作用就越大,学习演奏新乐器就越容易。然而,即使乐器有很大的不同,比如鼓和钢琴,一些先验知识仍然能起到作用(即使作用不是那么大),比如坚持节奏练习。

劳伦斯·利弗莫尔国家实验室(Lawrence Livermore National Laboratory)或桑迪亚国家实验室(Sandia National Laboratories)等大型研究实验室,以及谷歌(Google)和脸书(Facebook,现名Meta)等大型互联网公司,都能够通过对数十亿个单词和数百万幅图像进行深度神经网络学习来训练大型复杂模型。例如,第2章介绍的谷歌的NLP模型BERT(Bidirectional Encoder Representations from Transformer,基于Transformer的双向编码器表示)基于英文版Wikipedia(包含25亿个单词)和BookCorpus(包含8亿个单词)进行了预训练[2]。类似地,深度卷积神经网络(Convolutional Neural Network,CNN)已经基于超过1 400万张图片的ImageNet数据集进行训练,学习得到的参数已被许多组织广泛使用。从零开始训练此类模型所需的资源量庞大,通常不适用于普通的神经网络实践者,例如在小型企业工作的NLP工程师或小型高校的学生。这是否意味着相对“弱小”的从业者无法在他们的问题上取得最先进的结果呢?答案显然是否定的。值得庆幸的是,在正确的场景下应用迁移学习有望缓解这一担忧。

[2] Devlin J et al, “BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding,” arXiv (2018).

为什么迁移学习很重要?

迁移学习能够将从一组任务或领域获得的知识调整或迁移到另一组任务或领域。这意味着,一个使用大量资源(包括数据、算力、时间和成本)训练的模型可以在新的环境下被业界更多的人员加以微调和复用,但其资源需求只是原所需资源的一小部分。如图1.1所示,以学习如何演奏乐器为例。从图中可以看出,不同任务/领域训练系统的信息共享可以减少后期或下游任务 B实现相同性能所需的数据。

图1.1 在迁移学习范式中,信息可以在不同任务/领域训练的系统之间共享

1.1 NLP领域典型任务概述

NLP的目标是使计算机能够理解自然的人类语言。读者可以将这视为一个将自然语言文本系统地编码为数字表示的过程。尽管存在多种典型的NLP任务分类方法,但下面这种粗粒度的划分方式提供了一种用于界定NLP任务的类型的思维框架,同时给出了本书将要解决的各种问题的示例。请注意,其中一些任务可能是(也可能不是,取决于选择的特定算法)列表中其他更困难的任务所需要的。

词性标注(Part-of-Speech,POS):对文本中单词的词性进行标注,潜在的标签包括动词、形容词和名词等。

命名实体识别(Named Entity Recognition,NER):检测非结构化文本中蕴含的实体,如人名、组织名、地名等。请注意,词性标注可能是命名实体识别中的一个步骤。

句子/文档分类:给句子或文档打上预定义的类别标签,如情绪{积极的,消极的}、主题{娱乐,科学,历史},或其他预定义的类别集。

情感分析(sentiment analysis):赋予一个句子或文档所表达的情感,如{积极的,消极的}。实际上,读者可以将情感分析视为句子/文档分类的特殊情况。

自动摘要(automatic summarization):概括一组句子或文档的关键内容,通常是用几个句子或关键词描述。

机器翻译(machine translation):将句子或文档从源语言翻译成一种或多种目标语言。

问答系统(question answering):对人类提出的问题给出适当的答案。例如,问:加纳的首都是哪里?答:阿克拉。

聊天机器人(chatterbot/chatbot):与人类有效地对话,潜在目的是实现某些目标,如最大化对话时长或从人类用户那里获取某些特定信息。请注意,聊天机器人可以被表述为问答系统。

语音识别(speech recognition):将人类语音的音频转换为文本表示。尽管人们已经投入并将继续投入大量精力使语音识别系统更加可靠,但在本书中,我们假设所需的语言文本是现成的。

语言模型(language modeling):确定人类语言中一系列单词的概率分布,其中,知道序列中最有可能出现的下一个单词对语言生成尤为重要,可以用于预测下一个出现的单词或句子。

依存分析(dependency parsing):将一个句子分解成一个表示语法结构和单词之间关系的依存树。注意,词性标注在这里很重要。

1.2 理解人工智能背景下的NLP技术

在继续阅读本书的后续部分之前,正确理解术语“自然语言处理”并厘清它与其他常见术语(如人工智能、机器学习和深度学习)之间的区别十分重要。大众媒体赋予这些术语的含义通常与机器学习科学家和工程师的理解有所出入。因此,当我们使用这些术语时,给出准确的定义很重要,其关系韦恩图如图1.2所示。

在图1.2中,深度学习是机器学习的一个子集,而机器学习又是人工智能的一个子集。NLP也是人工智能的一个子集,它与深度学习和机器学习有着非空的交集。该图是对François Chollet提供的图的扩充[3]

[3] Chollet F, Deep Learning with Python (New York: Manning Publications, 2018).

图1.2 自然语言处理、人工智能、机器学习和深度学习等术语的关系韦恩图

请参阅他的著作《Python深度学习》的第6章和8.1节,了解神经网络在文本中的应用。符号主义人工智能(Symbolic AI)也在图1.2中,1.2.1节将对其加以说明。

1.2.1 人工智能

人工智能作为一个研究领域出现在20世纪中叶,致力于使计算机模拟和执行通常由人类执行的任务。最初的方法专注于手动推导和硬编码显式规则,用于在各种感兴趣的环境中操作输入数据。这种范式通常称为符号主义人工智能。它适用于定义明确的问题,如国际象棋,但当遇到来自感知类问题时,如视觉和语音识别,它会明显地出错。我们需要一种新的范式,即计算机可以从数据中学习新的规则,而不是由人类明确地指定规则。这促使了机器学习的兴起。

1.2.2 机器学习

20世纪90年代,机器学习范式成为人工智能的主导。现在,计算机不再为每种可能的情况显式编码,而是通过相应的输入输出样例数据来训练模型,自动提取输入与输出之间的映射关系。虽然机器学习涉及大量的数学和统计学知识,但由于它倾向于处理大型和复杂数据集,因此它更加依赖实验、经验观察和工程手段,而非数学理论。

机器学习算法从输入数据中学习到一种表示,并将其转换为恰当的输出。为此,机器学习模型需要一组数据(如句子分类任务中的一组句子输入)和一组相应的输出(如用于句子分类的{“正”,“负”}标签)。还需要一个损失函数,它用于度量机器学习模型的当前输出与数据集的预期输出之间的偏差。为了帮助读者理解,不妨考虑二分类任务,其中机器学习的目标可能是找到一个所谓的决策边界的函数,其职责是完美分割不同类型的数据点,如图1.3所示。这个决策边界应该在训练集之外的新数据实例上也有很好的表现。为了加速找到决策边界,读者可能需要首先对数据进行预处理,或者将其转换为更易于分割的形式。我们在称为假设集(hypothesis set)的可能函数集合中搜索目标函数。这种搜索是自动进行的,它使得机器学习的最终目标更容易实现,这就是所谓的学习。

图1.3 机器学习中一个主要的激励任务的示例(在本图所示的情况中,假设集可以是弧线)

机器学习利用损失函数所包含的反馈信号的指导,在某个预定义的假设集中自动搜索输入与输出之间的最佳映射关系。假设集的性质决定了所考虑的算法类别,这些将在后面内容中简要介绍。

经典机器学习(classical machine learning)是从概率建模方法(如朴素贝叶斯)开始的。这里,我们不妨乐观地假设输入数据特征都是独立的。逻辑斯谛回归(logistic regression)是一种概率建模方法,它通常是数据科学家在数据集上首先尝试的方法。它和朴素贝叶斯的假设集都是线性函数集。

神经网络(neural network)虽然起源于20世纪50年代,但直到20世纪80年代人们才发现一种有效的训练大型网络的方法——反向传播(back propagation)与随机梯度下降(stochastic gradient descent)算法相结合。反向传播提供了一种计算网络梯度的方法,而随机梯度下降则使用这些梯度来训练网络。本书附录B简要介绍了这些概念。1989年神经网络第一次成功应用。当时贝尔实验室的Yann LeCun建立了一个识别手写数字的系统,这个系统后来被美国邮政局广泛使用。

核方法(kernel method)从20世纪90年代开始流行。这种方法试图通过在点集之间找到良好的决策边界来解决分类问题,如图1.3所示。最流行的核方法是支持向量机(Support Vector Machine,SVM),它试图通过将数据映射到新的高维表示(其中超平面是有效边界)来找到好的决策边界,然后,令超平面和每个类目中最近的数据点之间的距离最大化。利用核方法,高维空间中的高计算成本得到降低。核函数用于计算点之间的距离,而不是显式地对高维数据表示进行计算,其计算成本远小于高维空间中的计算成本。这个方法有坚实的理论支撑,并且易于进行数学分析,当核函数是线性函数时,则该方法也是线性的,这使得该方法非常流行。然而,该方法在感知类机器学习问题上还存在很多可改进的地方,因为这种方法首先需要一个手动的特征工程步骤,而这一步又很容易出差错。

决策树(decision tree)及其相关方法是另一类仍被广泛使用的方法。决策树是一种决策支持辅助工具,它将决策及其结果建模为树形结构。它本质上是一个图(graph),图中任意两个连通节点之间只存在一条路径。或者可以将树定义为将输入值转换为输出类别的流程图。决策树在21世纪10年代兴起,彼时基于决策树的方法开始比核方法更流行。这种流行得益于决策树更易于可视化、理解和解释。为了帮助读者理解,图 1.4 展示了一个决策树结构示例,该结构将输入{A,B}分类为类别1(如果A<10)、类别2(如果A10,而B25)和类别3(其他情况)。

图1.4 决策树结构示例

随机森林(random forest)为应用决策树提供了一种实用的机器学习方法。此方法涉及生成大量特化(specialized)决策树并组合它们的输出。随机森林非常灵活并具有普适性,这使得它经常成为继逻辑斯谛回归之后的第二种基线算法。2010年,当Kaggle开放式竞赛平台启动时,随机森林很快成为该平台上使用最广泛的算法。2014年,梯度提升机(Gradient Boosting Machine,GBM)取代了它。它们的原理都是迭代地学习新的基于决策树的模型,这些模型消除了以前迭代中模型的弱点。在撰写本书时,它们被广泛认为是解决非感知类机器学习问题的最佳方法。它们在Kaggle上依然备受青睐。

2012年,基于GPU训练的卷积神经网络(Conrolutional Neural Network,CNN)开始赢得年度ImageNet竞赛,这标志着当前深度学习“黄金时代”的来临。CNN开始主导所有主要的图像处理任务,如目标识别(object recognition)和目标检测(object detection)。同样,我们也可以在人类自然语言的处理中找到它的应用,即NLP。神经网络通过一系列越来越有意义的、分层的输入数据表示进行学习。这些层(layer)的数量确定了模型的深度(depth)。这也是术语“深度学习”(deep learning)的由来,即训练深度神经网络的过程。为了区别于深度学习,之前所述的所有机器学习方法通常称为浅层(shallow)或传统学习方法。请注意,深度较小的神经网络也可归类为浅层,但不是传统的。深度学习已经占据机器学习领域的主导地位。很明显作为解决感知类问题首选的深度学习在可处理问题的复杂性方面引发了一场“革命”。

虽然神经网络的灵感来自神经生物学,但它并不是我们神经系统工作的真实模式。神经网络的每一层都由一组数字(称其为层的权重)参数化,用于精确地指导该层如何对输入数据进行转换。在深度神经网络中,参数的总数很容易达到百万级。前面提到的反向传播算法是一种算法引擎,用于找到正确的参数集,即对网络进行学习。图1.5(a)展示了具有两个全连接隐藏层的简单前馈神经网络的可视化表示。图1.5(b)展示了一个等价的简化表示,我们将经常使用这种表示来简化图表。一个深度神经网络会有很多这样的层。一种著名的神经网络结构不具备这种前馈性质,它就是长短期记忆(Long Short-Term Memory,LSTM)循环神经网络(Recurrent Neural Network,RNN)。与图1.5中接收长度为2的固定长度输入的前馈结构不同,LSTM可以处理任意长度的输入序列。

图1.5 具有两个全连接隐藏层的简单前馈神经网络

如前所述,引爆“深度学习革命”的是硬件、海量可用数据和算法的进步。专门为视频游戏市场开发的GPU,以及业已成熟的互联网,开始为深度学习领域提供前所未有的海量优质数据。数据源如Wikipedia、YouTube和ImageNet等的可用性推动了计算机视觉和NLP的进步。神经网络能够消除对昂贵的手动特征工程的需求,这是成功将浅层学习方法应用于感知数据的必要条件,可以说是影响深度学习易用性的因素。由于NLP是一个感知类问题,因此神经网络也是本书重点介绍的机器学习算法类型,尽管不是唯一的类型。

接下来,我们的目标是了解NLP的历史和进展。

1.2.3 自然语言处理

语言是人类认知最重要的方面之一。毫无疑问,为了创造真正的人工智能,需要让机器掌握如何解释、理解、处理和操作人类语言的方法。这让NLP在人工智能和机器学习领域日渐重要。

与人工智能的其他子领域一样,处理NLP问题的初始方法(如句子分类和情感分析)都基于显式规则或符号主义人工智能。采用这些初始方法的系统通常无法推广到新任务,并且很容易崩溃。自20世纪90年代核方法出现以来,人们一直致力于研究特征工程——手动将输入数据转换为浅层学习方法可以用来正确预测的形式。特征工程非常耗时,且与特定任务相关,非领域专家难以掌握。2012年,深度学习的出现引发了NLP的真正革命。神经网络在其某些层中自动设计适当特征的能力降低了特征工程处理新任务和问题的门槛。随后,人们的工作重点转向为任何给定的任务设计适当的神经网络结构,以及在训练期间调整各种超参数。

训练NLP系统的标准方法是首先收集大量数据点,然后在句子或文档的情感分析任务中对每个数据点进行标注(如“正向”或“负向”)。最后将这些数据点提供给机器学习算法,以学习输入信号到输出信号映射关系的最佳表示,学习得到的模型在新数据点上也有很好的表现。在NLP和机器学习的其他子领域中,该过程通常称为有监督学习(supervised learning)范式。手动完成的标注过程为学习代表性映射关系提供了“监督信号”。另外,从未标注数据点的学习范式称为无监督学习(unsupervised learning)范式。

尽管今天的机器学习算法和系统不是生物学习系统的直接复制品,也不应该被视为此类系统的模型,但它们的某些方面受到进化生物学的启发,而且带来了重大的进步。对于每个新任务、语言或应用领域,有监督学习过程传统上是从头开始重复的,这似乎是有缺陷的。这个过程在某种程度上与自然系统基于先前获得的知识并加以复用的学习方式相反。饶是如此,从零开始的感知任务学习已经取得了重大进展,特别是在机器翻译、问答系统和聊天机器人方面,尽管它仍然存在一些缺点。特别是,今天的系统在输入信号相关样本的分布发生急剧变化时鲁棒性欠佳。换句话说,系统学习在某种类型的输入上表现良好。如果更改输入类型,可能会导致性能显著下降,有时甚至会出现严重故障。此外,为了使人工智能更普及,并使小型企业的普通工程师或没有大型互联网公司资源的任何人都能使用NLP技术,能够下载和复用他人学习到的知识将变得尤为重要。这对于以英语或其他流行语言之外的语言作为母语的地区的人们也很重要,因为英语或其他流行语言有预训练模型。此外,这对于执行所在地区独有的任务或前所未有的新任务的人来说也很重要。迁移学习提供了解决其中一些问题的方法。

迁移学习使人们能够将知识从一个环境中迁移到另一个环境中,这里将环境定义为特定任务、领域和语言的组合。最初的环境称为源环境,最终的环境称为目标环境。知识迁移的难易程度和是否成功取决于源环境和目标环境的相似性。很自然,在某种意义上与源环境“相似”的目标环境(我们将在本书后面定义)会更容易迁移和成功。

迁移学习在NLP中的应用比大多数实践者意识到的要早得多,因为使用预训练的嵌入(如Word2Vec或Sent2Vec)对单词进行向量化是一种很常见的做法(1.3节将对此进行详细介绍)。浅层学习方法通常将这些向量用作特征。我们将在1.3节和第4章更详细地介绍这两种技术,并在本书中以多种方式应用它们。这种流行的方法依赖于无监督的预处理步骤,该步骤用于在没有任何标签的情况下首先训练这些嵌入。随后,来自该步骤的知识被迁移到有监督学习上下文中的特定应用程序中,在该环境中,预训练学习的知识得到进一步处理,并针对与当前浅层学习问题相关的较小带标签样本集进行特化。传统上,这种结合无监督学习和有监督学习步骤的范式称为半监督学习(semisupervised learning)。

接下来,我们将介绍NLP的发展简史,特别关注迁移学习最近在NLP这个人工智能子领域和机器学习中所起的作用。

1.3 NLP发展简史

要了解迁移学习在NLP领域中的现状和重要性,首先应更好地了解该领域具有重要历史意义的学习任务和相关技术。本节将介绍这些任务和技术,并简要概述NLP迁移学习的进展。下面将帮助读者了解迁移学习对NLP的影响,并解释为什么它变得日渐重要。

1.3.1 NLP简介

NLP诞生于20世纪中叶,伴随着人工智能出现。NLP的一个重要历史里程碑是1954年的乔治敦(Georgetown)实验。在该实验中,约60个俄语句子被翻译成英语。20世纪60年代,麻省理工学院的NLP系统ELIZA比较成功地模拟了一位心理治疗师。同样是在20世纪60年代,信息表示的向量空间模型(vector space model)被开发出来,其中单词用实数向量表示,这非常易于计算。20世纪70年代,基于处理输入信息的复杂手工规则集,诞生了许多聊天机器人概念。

20世纪80年代和90年代,机器学习方法在NLP领域中得到系统性应用。在NLP中,规则是由计算机发现的,而不是由人类精心编制的。正如我们在本章前面讨论过的那样,这一阶段的进展恰好与机器学习在那段时间的爆炸式普及相吻合。20世纪80年代末,奇异值分解(Singular Value Decomposition,SVD)被应用于向量空间模型,得到了隐式语义分析(latent semantic analysis)—— 一种确定语言中单词之间关系的无监督学习技术。

21世纪前10年,神经网络的兴起和该领域的深度学习技术极大地改变了NLP。实践证明这些技术可以在最困难的NLP任务(如机器翻译和文本分类)中获得最先进的结果。20世纪10年代中期Word2Vec模型[4]及其变体Sent2Vec[5]、Doc2Vec[6]等模型得到快速发展。这些基于神经网络的模型可以将单词、句子和文档向量化,以确保生成的向量空间中向量之间的距离代表相应实体(即单词、句子和文档)之间的意义差异。事实上,通过类比这种嵌入可以观察到一些有意思的性质,例如,在诱导向量空间中,单词Man和King之间的距离近似等于单词Woman和Queen之间的距离。用于训练这些基于神经网络的模型的度量来自语言学领域,更具体地说是分布语义(distributional semantics)表示,它不需要标注数据。一个单词的意思被认为与它的上下文有关,也就是说,与它周围的单词相关。

[4] Mikolov T et al, “Efficient Estimation of Word Representations in Vector Space,” arXiv (2013).

[5] Pagliardini M et al, “Unsupervised Learning of Sentence Embeddings Using Compositional n-Gram Features,” Proc. of  NAACL-HLT (2018).

[6] Le QV et al, “Distributed Representations of  Sentences and Documents,” arXiv (2014).

词汇、句子、段落、文档等各种文本单位的嵌入方法成为现代NLP的重要基石。一旦文本样本被嵌入适当的向量空间中,分析通常可以简化为应用浅层统计/机器学习方法在实向量上的操作,包括聚类和分类。这可以看作一种隐式的迁移学习,是一种半监督机器学习处理管道:嵌入步骤是无监督的,学习步骤通常是有监督的。无监督的预训练步骤本质上减少了对数据标注的需求,从而减少了实现要求的性能目标所需的计算资源。在本书中,我们将学习如何利用迁移学习为更广泛的场景提供帮助。

2014年前后开始出现序列到序列(sequence-to-sequence)模型[7],并在机器翻译和自动摘要等困难任务中取得了重大进展。特别地,尽管“神经网络时代”之前NLP处理管道由几个明确的步骤组成,如词性标注、依存分析和语言模型等,但研究表明机器翻译可以“序列到序列”。在序列到序列模型中,深度神经网络的各个层将所有中间步骤自动化。序列到序列模型学习将输入序列(例如一种语言中的源语句)与输出序列相关联(即将源语句翻译为另一种语言),例如,通过编码器(encoder)将输入序列转换为上下文向量,利用解码器(decoder)将上下文向量转换为目标序列。编码器和解码器都是典型的RNN。它们能够对输入序列中的顺序信息进行编码,而早期的模型(如词袋模型)无法做到这一点,从而显著提高了性能。

[7] Sutskever I et al, “Sequence to Sequence Learning with Neural Networks,” NeurIPS Proceedings (2014).

然而,人们发现,长输入序列更难处理,这促进了注意力(attention)技术的发展。该技术通过允许模型关注输入序列中与输出最相关的部分,显著提高了机器翻译序列到序列模型的性能。一个名为Transformer[8]的模型更进一步,为编码器和解码器定义了一个自注意力(self-attention)层,允许两者为文本段构建相对于输入序列中的其他文本段更好的上下文。该结构在机器翻译任务中取得了显著提升,并且与之前的模型相比,它更适合在大规模并行硬件上进行训练,将训练速度提高了一个数量级。

[8] Vaswani A et al, “Attention Is All You Need,” NeurIPS Proceedings (2017).

直到2015年左右,NLP的大多数实用方法都集中在单词层面,这意味着整个单词被视为一个不可分割的原子实体,并为它分配了一个特征向量。这种方法存在不足,尤其是在处理之前未出现过的或词汇表之外的单词时。例如,当模型遇到这样的单词,又或者单词出现拼写错误时,该方法都将失效,因为它无法将其向量化。此外,社交媒体的兴起改变了自然语言的定义。现在,数十亿人通过表情符号、新发明的俚语和故意拼错的单词在网上表达自我。没过多久,人们自然而然地意识到应该从字符级入手寻找解决方案。其中,每个字符都将被向量化,只要人类用允许的字符来表达自我,就可以成功地生成特征向量,并应用相关算法。Zhang等[9]在用于文本分类的字符级CNN案例中展示了这一点,并证明了字符级方法处理拼写错误的显著鲁棒性。

[9] Zhang X et al, “Character-Level Convolutional Networks for Text Classification,” NeurIPS Proceedings (2015).

1.3.2 迁移学习的进展

传统上,对于任何给定的问题(任务、领域和语言的特定组合),学习一开始就是以完全有监督或完全无监督的方式进行的。如前面所述,早在1999年,在支持向量机技术背景下,半监督学习就被认为能够解决可用的带标签数据有限的问题。对大量未标注数据进行的初始无监督预训练步骤使得下游的有监督学习更容易。相关学者研究了这一方法的各种变体,以解决可能存在噪声且存在错误标注数据的问题,该方法有时称为弱监督学习(weakly supervised learning)。基于该方法,人们通常假设标注数据集和未标注数据集具有相同的抽样分布。

迁移学习放宽了这些假设。1995年,在最大的机器学习会议之一的神经信息处理系统(Neural Information Processing Systems,NeurIPS)会议上,迁移学习是公认的“元学习”(learning to learn)。本质上,它规定智能机器需要具备终身学习能力,可将学到的知识重新用于新任务。从那以后,不同学者用多个名称来表述该研究方向,如元学习、知识迁移(knowledge transfer)、归纳偏好(inductive bias)和多任务学习(multitask learning)。在多任务学习中,一个算法被训练到同时在多个任务上表现良好,从而发现可能更普遍、有用的特征。然而,直到2018年左右,针对最难的感知类问题,人们才开发出实用且可扩展的NLP方法。

2018年发生了NLP领域的一场革命。在如何最好地将文本集合表示为向量这一领域,人们的理解发生了深刻的变化。此外,开源模型可以被微调或迁移到不同的任务、语言和领域,这一点已得到广泛认可。与此同时,几家大型互联网公司发布了很多大型NLP模型来计算文本的向量表示,并提供了定义良好的程序对这些模型进行微调。突然间,普通从业者,甚至是独立从业者,都能根据NLP获得最先进的结果。一些人称之为NLP的“ImageNet时刻”,指的是2012年后计算机视觉应用的爆炸式增长,当时一个由GPU训练的神经网络赢得了ImageNet计算机视觉竞赛。正如最初的“ImageNet时刻”一样,第一次有了用海量NLP数据集进行预训练的模型库,以及配套模型微调工具,基于这两者,用户可以根据手头的特定任务对预训练模型进行微调,此时所需标注的数据集的大小远远小于从零构建模型所需标注的数据集的大小。本书的目的是描述、阐明、评价、示范应用、比较属于NLP范畴的各种技术。接下来我们将概述这些技术。

迁移学习在计算机视觉领域早已成功应用了10多年,早期关于NLP迁移学习的探索侧重于与计算机视觉领域进行类比。其中一个模型——本体建模中的语义推理(Semantic Inference for the Modeling of Ontology,SIMOn)[10],采用字符级CNN结合双向LSTM处理结构语义文本分类问题。SIMOn证明了NLP迁移学习方法与计算机视觉中使用的方法非常类似。计算机视觉应用迁移学习的丰富知识积累推动了迁移学习在NLP中的应用。通过该模型学习的特征对于无监督的学习任务非常有用,并且其在社交媒体语言数据上也能很好地发挥作用。社交媒体语言数据有些特殊,与Wikipedia及其他基于图书的大型数据集上的语言风格迥异。

[10] Azunre P et al, “Semantic Classification of Tabular Datasets via Character-Level Convolutional Neural Networks,” arXiv (2019).

最初版本的Word2Vec在消歧(disambiguation)方面存在明显的缺点。它没有办法区分一个词的各种用法,这些用法根据上下文的不同可能有不同的含义,例如同形词duck(姿势)与duck(鸟),或fair(聚会)与fair(公正)。在某种意义上,早期的Word2Vec公式通过表示同形词的每个不同含义的向量的平均向量来表示每个这样的词。语言模型嵌入[11](Embeddings from Language Models,ELMo)是最早尝试使用双向长短期记忆(bi-LSTM)网络开发引入上下文信息的单词嵌入表示的模型之一。ELMo通过训练预测单词序列中的下一个单词来做到这一点,这与本章开头介绍的语言模型概念密切相关。大型数据集,如Wikipedia和各种图书数据集,可随时用于训练这种模型。

[11] Peters ME et al, “Deep Contextualized Word Representations,” Proc. of  NAACL-HLT (2018).

通用语言模型微调[12](Universal Language Model Fine-Tuning,ULMFiT)方法用于任何特定任务对任何基于神经网络的语言模型进行微调,并在文本分类中得到初步验证。这种方法的一个关键概念是区分性微调,即神经网络的不同层以不同的速率进行训练。OpenAI提出的生成式预训练Transformer(Generative Pretrained Transformer,GPT)对Transformer的编码器、解码器结构进行了改进,实现了用于NLP的可调优语言模型。它丢弃了编码器,保留了解码器及其自注意力子层。而BERT[13]修改Transformer的方式正好相反,它保留了编码器并丢弃了解码器,同时依赖于遮盖(mask)的单词,这些单词需要在训练度量(training metric)上被准确预测。这些概念将在后续章节中详细探讨。

[12] Howard J et al, “Universal Language Model Fine-Tuning for Text Classification,” Proc. of the 56th Annual Meeting of the Association for Computational Linguistics (2018).

[13] Devlin J et al, “BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding,” Proc. of NAACL-HLT (2019).

所有基于语言模型的方法(如ELMo、ULMFiT、GPT和BERT等)表明,生成的嵌入可以对标注数据相对较少的特定下游NLP任务进行微调。对语言模型的关注是有意为之的:假设由语言模型产生的假设集通常是有用的,而已知用于大规模训练的数据是现成的。

接下来,我们将重点介绍计算机视觉中迁移学习的关键方面,以更好地构建NLP中的迁移学习框架,并看看是否有什么可供我们学习和借鉴。这些知识提供了丰富的类比素材,它们将在本书的剩余部分中用于推动我们对NLP迁移学习的探索。

1.4 计算机视觉中的迁移学习

虽然本书的研究目标是NLP,但是了解计算机视觉背景下的迁移学习有助于构建NLP迁移学习。因为来自人工智能的这两个子领域的神经网络可能具有一些相似的特征,所以可以借用计算机视觉技术,或者至少可以借此了解NLP需要哪些技术。事实上,这些技术在计算机视觉中的成功应用可以说是最近NLP迁移学习研究的最大驱动力。研究人员可以使用一个定义良好的计算机视觉方法库,在NLP这个相对未开发的领域进行实验。然而,这些技术在多大程度上可以直接迁移到NLP领域,还是一个悬而未决的问题。必须谨记一些重要的区别,其中一个区别是NLP神经网络往往比计算机视觉中使用的神经网络浅。

1.4.1 概述

计算机视觉或机器视觉的目标是使计算机能够理解数字图像或视频,包括获取、处理和分析图像数据,以及基于其派生表示(derived representation)进行决策。视频分析通常可以通过将视频分割成帧,然后进行图像分析来实现。因此,在理论上,计算机视觉问题本质上是图像分析问题。

计算机视觉在20世纪中叶与人工智能一起诞生。显然,视觉是认知的一个重要组成部分,因此寻求制造智能机器人的研究人员很早就认识到它的重要性。20世纪60年代,最初的视觉方法试图模仿人类的视觉系统,而在20世纪70年代,人们开始关注提取边缘和对场景中的形状建模。20世纪80年代,针对计算机视觉的各个方面,尤其是面部识别和图像分割,研究人员开发了更具数学鲁棒性的方法,到20世纪90年代,出现了数学上严格的处理方法。这恰逢机器学习普及的那段时间,正如我们已经提到的那样。在接下来的几十年中,在应用浅层机器学习技术之前,人们在如何开发更好的图像特征提取方法上投入了更多精力。2012年的“ImageNet时刻”标志着该领域的一场革命,当时有GPU加持的神经网络首次以巨大的优势赢得了ImageNet竞赛。

ImageNet[14]最初发表于2009年,并迅速成为目标识别算法竞赛的“试金石”。2012年Wikipedia 在关于神经网络的条目中指出,深度学习是计算机视觉和机器学习中感知类问题的发展方向。对我们来说重要的是,许多研究人员很快意识到,来自ImageNet预训练模型的神经网络权重可以用于初始化其他(有时看似无关)任务的神经网络模型,并实现性能的显著改善。

[14] Deng J et al, “ImageNet:  A Large-Scale Hierarchical Image Database,” Proc. of  NAACL-HLT (2019).

1.4.2 ImageNet预训练模型

赢得ImageNet年度竞赛的各个团队都非常慷慨地分享了他们的预训练模型。CNN模型的典型范例如下。

VGG架构最初于2014年被提出,其变体包括VGG16(深度为16层)和VGG19(深度为19层)。为了使更深层次的网络在训练过程中收敛,需要先训练较浅层次的网络直到收敛,并使用其参数初始化更深层次的网络。已发现该架构的训练速度稍慢,并且其总体参数数量相对较大——有1.3亿到1.5亿个参数。

一些问题在2015年由ResNet架构解决。尽管它的深度很大,但参数数量显著减少。它最小的变体ResNet50有50层,约5 000万个参数。实现这种参数数量减少的一个关键是通过一种称为最大池化(max pooling)的技术和子构建块的模块化设计进行正则化处理。

其他值得注意的例子包括Inception及其扩展Xception。它们分别在2015年和2016年提出,其目标是通过在同一个网络模块中叠加多个卷积来创建多个层级的提取特征。这两种模型结构都进一步显著缩小了模型体积。

1.4.3 ImageNet预训练模型的微调

由于预训练卷积神经网络ImageNet模型的存在,从业者从零开始训练计算机视觉模型并不常见。到目前为止,更常见的方法是下载这些开源模型中的一个,对有些标注数据进行学习之前使用它们初始化类似的模型架构,例如微调神经网络诸层的某个子集,或者将其用作固定的特征提取器。

图1.6可视化显示了在前馈神经网络中如何选择要微调的层。随着目标领域中可用的数据越来越多,阈值将从输出移到输入,阈值和输出之间的层将被重新训练。发生这种变化的原因是,增加的数据可用于更有效地训练更多的参数。此外,阈值的移动必须从右向左进行,即远离输出而转向输入。这个移动方向允许我们保留对通用特征的编码并靠近输入的层,同时重新训练靠近输出的层,这些层针对源领域特征编码。此外,当源和目标非常不相似时,可以丢弃阈值右侧的一些更具体的参数或层。

图1.6 适用于前馈神经网络结构的计算机视觉中迁移学习的启发式可视化描述

另外,特征提取只涉及移除网络的最后一层,而不是生成数据标签,然后生成一组数值向量,基于这些向量可以像以前一样训练浅层机器学习方法,如支持向量机。

在重训练或微调方法时,先前的预训练权重并非都保持不变,可以允许其中的一个子集根据新标注的数据进行更改。然而,重要的是要确保被训练参数的数量不会导致对有限的新标注数据的过拟合,这促使我们冻结(freeze)一些数据以减少被训练参数的数量。选择要冻结的数据的数量通常是根据经验进行的,图1.6中的启发式方法可以指导该过程。

在CNN中,更接近输入层的早期层所实现的功能对图像处理任务更为通用,例如检测图像中的任何边缘。而那些靠近输出层的层实现更专注于当前任务的功能,例如将最终的数字输出映射到特定的标签。这种安排导致我们首先解冻(unfreeze)和微调靠近输出层的层,然后,如果发现性能不令人满意,则逐渐解冻和微调靠近输入层的层。只要目标任务的可用标注数据集能够支持训练更多参数,该过程就可以持续进行。

该过程的一个推论是,如果目标任务的标注数据集非常大,则可能需要对整个网络进行微调。另外,如果目标数据集很小,则需要仔细考虑目标数据集与源数据集的相似程度。如果它们非常相似,则可以在微调时直接用预训练模型中的权重进行初始化。如果它们非常不相似,则在初始化时丢弃网络的某些后续层中的预训练权重可能是有益的,因为它们可能与目标任务没有任何相关性。此外,由于数据集不大,因此在微调时,只须解冻一小部分剩余的后续层。

我们将在后续章节中进行计算实验,以进一步探索这些启发式方法。

1.5 NLP迁移学习成为一个令人兴奋的研究课题的原因

前面内容已经在一般人工智能和机器学习的背景下阐述了NLP的现状,接下来我们总结一下为什么NLP迁移学习很重要,以及为什么读者应该关注它。

近几年NLP领域的发展速度显著加快,这是毫无疑问的。人们提出了许多预训练语言模型以及定义良好的程序,对它们进行微调可以适应更具体的任务或领域。研究发现,十多年来,迁移学习在计算机视觉中的实施方式可以被其他应用领域借鉴,这使得许多研究团队能够迅速利用现有的计算机视觉技术来推进人们对NLP迁移学习的理解。这项工作取得了极大的突破,降低了计算和训练时间的要求,让解决这些问题的普通从业者无须拥有大量计算资源。

目前该领域存在着许多令人兴奋的现象,研究人员正在研究该领域的相关问题。许多悬而未决的问题为机器学习研究人员提供了一个机会,即通过推动知识的迁移,使自己扬名立万。同时,社交媒体已经成为人类互动中越来越重要的因素,它带来了NLP中前所未有的新挑战。这些挑战包括俚语、行话和表情符号的使用,但它们通常不会出现在训练常规语言模型的语料集中。一个明显的例子是社交媒体自然语言生态系统的严重脆弱性,特别是某些国家对外国政府的选举干预。此外,对“假新闻”的普遍反感增加了人们对该领域的兴趣,并促使人们讨论在建立这些系统时应该考虑的伦理问题。所有这些,再加上日渐复杂的聊天机器人在各个应用领域的出现,以及相关的网络安全威胁日趋严重,意味着NLP中的迁移学习问题将继续变得越来越重要。

小结

人工智能有望从根本上改变我们的社会。为了使这一转型的好处惠及大众,我们必须确保所有人都能获得最先进的技术,而不管他们使用何种语言、拥有多少计算资源以及来自哪个国家。

机器学习是人工智能中占主导地位的现代处理范式,它不是针对每一种可能的场景进行显式编程,而是通过分析许多输入输出实例来拟合输入输出之间的映射关系。

NLP是我们在本书中讨论的人工智能的子领域,用于人类自然语言数据的分析和处理,是当今人工智能研究中最活跃的领域之一。

最近在NLP中流行的一种范式——迁移学习,能够将从一组任务或领域获得的知识调整或迁移到另一组任务或领域。这是NLP普及的一大进步,从更大的视角来看,这是AI发展的一大进步——允许知识在新的环境中被重用,而只需要以前所需资源的一小部分,这在之前对人们来说无异于天方夜谭。

NLP中迁移学习的关键建模框架有ELMo和BERT等。

最近社交媒体显著改变了自然语言的定义。现在,数十亿人通过表情符号、新发明的俚语和故意拼错的单词在网上表达自我。所有这些都对NLP迁移学习提出了新的挑战,我们在为NLP开发新的迁移学习技术时必须考虑这些挑战。

迁移学习在计算机视觉中得到了比较好的应用,只要有可能,我们就应该在NLP中开始迁移学习新尝试时利用这些经验和知识。

读者服务:

微信扫码关注【异步社区】微信公众号,回复“e61571”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。

第2章 从头开始:数据预处理

本章涵盖以下内容。

介绍两个自然语言处理(NLP)问题。

获取对应NLP问题的数据并进行预处理。

使用关键的广义线性方法为NLP问题建立基线(baseline)。

在本章中,我们直奔主题,着手解决NLP问题。对应的练习包含两个部分,贯穿本章和第3章。我们的目标是为两个具体的NLP问题建立基线。具体来说,是为两个特定的NLP问题建立一组基线,我们稍后将使用这些基线来衡量从日益复杂的迁移学习方法中获取的逐步改进。此过程的目标是强化读者在NLP方面的直觉,并帮助读者理解建立此类问题的解决方案所涉及的典型步骤。我们将介绍从分词(tokenization)到数据结构以及模型选择相关的各类技术。最后将在第3章中完成练习,届时我们会将最简单的迁移学习模型应用到一个实际案例中。本章将介绍两个NLP问题,然后为这些NLP问题获取相关数据并对其进行预处理,之后使用关键的广义线性方法再结合最近非常流行的两个深度预训练语言模型为这些问题建立基线。这只涉及对目标数据集上每个网络的最后几层进行微调。该过程采用实操的方式来介绍本书的主题——NLP中的迁移学习。

我们将重点讨论两个典型的NLP问题——垃圾电子邮件分类和电影评论情感分类。该练习将帮助读者掌握许多重要的技能,包括一些获取、可视化和预处理数据的技能。练习将涵盖3类主要的模型——广义线性模型(如逻辑斯谛回归)、基于决策树的模型(如随机森林)以及基于神经网络的模型(如 ELMo)。这些模型还可分别由带有线性核的SVM、GBM和BERT替代。我们将要探索的模型如图 2.1 所示,值得注意的是,我们并没有使用基于规则的方法。一个被广泛使用的例子是简单的关键字匹配方法,它将基于是否包含某些预先确定的短语来对电子邮件或电影评论进行分类,例如,将包含“免费的彩票”的电子邮件分类为垃圾电子邮件,将包含“精彩的电影”的评论分类为正面评论。在许多工业应用中,这些方法通常作为解决NLP问题的第一个尝试来实现,但人们很快就发现它们是脆弱的、难以扩展的。因此,我们不会进一步讨论基于规则的方法。在本章中,我们将讨论与NLP问题相关的数据及其预处理,并将介绍广义线性方法在数据上的应用。第3章作为整个练习的第二部分,将对数据分别应用基于决策树和神经网络的方法。

图2.1 在本章及第3章内容分类案例中将要探索的模型

本书为每个示例和模型类都提供了示例代码,以帮助读者快速掌握这些技术的本质以及编码技能,甚至可以直接将其用于解决手头问题。所有代码片段都在本书的配套GitHub代码仓库中以Jupyter Notebook[1]以及Kaggle Notebook或Kernel的形式提供,读者可以在几分钟内开始运行代码,无须处理任何安装或依赖问题。渲染后的Jupyter Notebook可正确执行,同时提供可预期的代表性输出。Kaggle提供一个基于浏览器的Jupyter执行环境,该环境还提供有限的免费GPU算力。谷歌Colab是一个类似的替代方案,不过本书并没有选择使用它。读者也可以通过Anaconda轻松地在本地安装Jupyter,若是愿意,将Notebook转换为.py脚本也是一个不错的选择,以便在本地执行。然而,我们还是更推荐使用Kaggle Notebook,因为它允许读者立即开始操作,而无须浪费时间进行设置。此外,在撰写本书时,Kaggle提供的免费GPU资源供无法在本地访问强大GPU的人员使用。附录A提供了Kaggle入门指南和作者关于如何最大限度地发挥这一平台效用的一些个人提示。虽然上手Kaggle毫不费劲,但是应该注意以下重要技术事项。

[1] 可以在GitHub网站搜索“azunre transfer-learning-for-nlp”以查找本书的配套代码仓库。——编辑注

注意:Kaggle经常更新依赖项,即Docker映像上已安装库的版本。为了确保读者使用的依赖项与我们编写代码时使用的依赖项相同,以确保代码在“开箱即用”的情况下运行,请确保为每个感兴趣的Notebook选择“Copy and Edit Kernel”,该Notebook的相关链接列在本书的配套代码仓库中。如果读者将代码复制并粘贴到新Notebook中,但未遵循该流程,则可能需要稍微调整一下代码,以适应创建该Notebook时为其安装的特定库版本。如果选择安装本地环境,此建议也同样适用。对于本地安装,请注意我们在配套代码仓库中共享的冻结依赖项需求列表,它将指明读者需要哪些版本的库。请注意,此需求列表旨在记录和准确复制Kaggle上实现本书范例的环境,在不同的基础设施上,它只能用作指南。由于存在许多潜在的与架构相关的依赖性冲突,读者不应该期望代码总是能“开箱即用”。此外,本地安装不需要满足大多数要求。最后,请注意,因为在撰写本书时ELMo还没有被移植到TensorFlow 2.x,所以我们不得不使用TensorFlow 1.x来与BERT进行比较。然而,在附带的代码仓库中,我们确实提供了一个演示如何将BERT与TensorFlow 2.x结合起来实现垃圾电子邮件分类的示例。在后面的章节中,我们将从TensorFlow和Keras过渡到使用基于TensorFlow 2.x的Hugging Face transformers库。读者可以将第2章和第3章中的练习视为NLP迁移学习早期软件包的历史和实操经验。该练习也能帮助读者同时了解TensorFlow 1.x与2.x。

2.1 垃圾电子邮件分类任务中示例数据的预处理

本节将介绍本章的第一个示例数据集。我们的目标是开发一种算法,用于检测任何给定的电子邮件是否为垃圾电子邮件。为此,我们根据两个不同的数据源来构建数据集:非常流行的Enron电子邮件语料库作为非垃圾电子邮件样本,以及作为垃圾电子邮件样本的“419”欺诈电子邮件数据集。

可以将垃圾电子邮件分类任务看作一项有监督的分类任务,首先我们基于样本电子邮件训练分类器。尽管线上有很多已标注数据集可以用于训练和测试,与这里的问题也非常匹配,但我们采用基于知名电子邮件数据源创建自己的数据集的方法。之所以这样做,是为了更真实地展示数据收集和预处理的实操细节,即首先必须构建和管理数据集,而不是直接使用研究文献中提及的那些简化方法。

特别是,我们将对Enron语料库——最大的公开电子邮件数据集进行抽样,将其作为非垃圾电子邮件样本,同时也对著名的垃圾电子邮件数据集“419”进行抽样,将其作为垃圾电子邮件样本。这两种类型的电子邮件都可以在Kaggle上公开获得。

Enron电子邮件语料库包含大约50万封由安然公司员工撰写的电子邮件,这些电子邮件由联邦能源委员会收集,用于调查该公司倒闭的原因。该语料库被广泛用于研究与电子邮件处理相关的机器学习方法,并且通常是电子邮件处理研究者寻找算法原型时进行初始实验的第一个数据源。在Kaggle上,它是一个单列.csv文件,每行代表一封电子邮件。请注意,这些数据仍然比大多数可以得到的数据集更干净。

图2.2显示了在本任务中对每封电子邮件按顺序执行的处理步骤。首先将电子邮件的正文与标题分离,提取一些关于数据集的统计数据将以帮助我们理解数据,将停用词从电子邮件中移除,然后将电子邮件归类为垃圾电子邮件或非垃圾电子邮件。

图2.2 在输入的电子邮件数据上按顺序执行的处理步骤

2.1.1 加载并检视Enron电子邮件语料库

需要做的第一件事是使用pandas库(一个非常流行的Python数据处理库)加载数据,并查看某个数据片段,以确保我们对数据集有良好的理解。代码段2.1显示了加载Enron电子邮件语料库并将其放置在变量filepath指定的位置(在本例中,filepath指向数据文件在Kaggle Notebook中的位置)相关的代码。在通过以下命令导入数据之前,请确保所有库都已用pip安装好:

pip install <package name>

代码段2.1 加载Enron电子邮件语料库

import numpy as np  ←--- 计算工具
import pandas as pd  ←--- 数据处理工具,包括.csv文件I/O操作(如pd.read_csv)
 
filepath = "../input/enron-email-dataset/emails.csv"
 
emails = pd.read_csv(filepath)   ←--- 将数据加载到一个叫emails的pandas数据框(DataFrame)
 
print("Successfully loaded {} rows and {} columns!".format(emails.shape[0],
     emails.shape[1]))   ←--- 显示统计信息及部分已加载数据
print(emails.head(n=5))

代码成功执行后将输出已加载数据集的列数和行数,并通过如下输出显示加载数据框的前5行:

Successfully loaded 517401 rows and 2 columns!
 
 file message
0 allen-p/_sent_mail/1. Message-ID: <18782981.1075855378110.JavaMail.e...
1 allen-p/_sent_mail/10. Message-ID: <15464986.1075855378456.JavaMail.e...
2 allen-p/_sent_mail/100. Message-ID: <24216240.1075855687451.JavaMail.e...
3 allen-p/_sent_mail/1000. Message-ID: <13505866.1075863688222.JavaMail.e...
4 allen-p/_sent_mail/1001. Message-ID:<30922949.1075863688243.JavaMail.e...

虽然经过这一练习我们能够对生成的数据框有比较直观的感觉,并能精准获取其维度(行数、列数),但我们对每封电子邮件的内容还是一无所知。为了了解电子邮件的内容,我们通过下面一行代码查看第一封电子邮件。

print(emails.loc[0]["message"])

执行上面的代码产生的输出如下:

Message-ID: <18782981.1075855378110.JavaMail.evans@thyme>
Date: Mon, 14 May 2001 16:39:00 -0700 (PDT)
From: phillip.allen@enron.com
To: tim.belden@enron.com
Subject:
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
X-From: Phillip K Allen
X-To: Tim Belden <Tim Belden/Enron@EnronXGate>
X-cc:
X-bcc:
X-Folder: \Phillip_Allen_Jan2002_1\Allen, Phillip K.\'Sent Mail
X-Origin: Allen-P
X-FileName: pallen (Non-Privileged).pst
 
Here is our forecast

可以看到消息包含在结果数据框的message列中,每个消息开头的额外字段(包括Message- ID、To、From等)称为消息头信息,或者简称为消息头(header)。

传统的垃圾电子邮件分类方法从电子邮件的消息头信息中提取特征来区分电子邮件是否为垃圾电子邮件。在这里,我们希望仅根据消息的内容执行相同的任务。采用这种方法的一个可能的动机是,在实践中,由于隐私问题和法规,电子邮件训练数据可能经常被执行反识别(de-identified)处理,从而使得消息头信息不可用。因此,我们需要将数据集中的消息内容与消息头分离。我们通过代码段2.2所示的函数来实现该功能。它使用电子邮件包来处理电子邮件消息,该包已随Python预安装(也就是说,它不需要通过pip安装)。

代码段2.2 抽取电子邮件正文

import email
 
def extract_messages(df):
    messages = []
    for item in df["message"]:
        e = email.message_from_string(item)   ←--- 返回一个基于字符串构造的消息对象结构
        message_body = e.get_payload()  ←--- 获取消息体
        messages.append(message_body)
    print("Successfully retrieved message body from emails!")
    return messages

现在,执行电子邮件正文抽取代码,如下所示:

bodies = extract_messages(emails)

如果输出以下结果,说明大功告成:

Successfully retrieved message body from emails!

然后可以输出一些已处理好的电子邮件:

bodies_df = pd.DataFrame(bodies) 
print(bodies_df.head(n=5))

如果观察到以下输出,说明代码执行成功:

0                 Here is our forecast\n\n 
1                Traveling to have a business meeting takes the...
2                 test successful. Way to go!!!
3                Randy,\n\n Can you send me a schedule of the s...
4                Let's shoot for Tuesday at 11:45. 

2.1.2 加载并检视欺诈电子邮件数据集

加载完Enron电子邮件语料库后,我们对“419”欺诈电子邮件数据集做同样的操作,这样可以在训练集中获得一些代表垃圾电子邮件的示例数据。根据前面介绍的Kaggle链接获取数据集,并调整相应的filepath变量(或者只使用已经附加了数据的Kaggle Notebook),并反复执行代码段2.3中所示的步骤。

注意:由于“419”欺诈电子邮件数据集是.txt文件,而不是.csv文件,因此预处理步骤略有不同。首先,必须在读取文件时指定编码为Latin-1,否则使用默认的UTF-8编码选项将会失败。在实践中,通常需要对多种不同的编码(前面提到的两种是十分流行的编码)进行实验,以便正确读取一些数据集。此外请注意,由于该.txt文件中的数据实际上是非常大的一列数据,代表电子邮件(有标题),由换行符和空格分隔,并且没有像Enron电子邮件语料库那样以每行一封电子邮件的方式很好地分割成行,因此我们不能像以前那样使用pandas整齐地加载它。在这里我们将把所有电子邮件读入一个字符串,并根据预定义的用来标识电子邮件的消息头的标记将字符串拆分为多封电子邮件,如“From r.”。请查看渲染后的Notebook,在GitHub或Kaggle上对这些数据进行可视化处理,以验证这个独特的标记是否出现在数据集中每封电子邮件的消息头中。

代码段2.3 加载“419”欺诈电子邮件数据集

filepath = "../input/fraudulent-email-corpus/fradulent_emails.txt"
with open(filepath, 'r',encoding="latin1") as file:
    data = file.read()
 
fraud_emails = data.split("From r")  ←--- 根据每封电子邮件的消息头的标记进行分割
 
print("Successfully loaded {} spam emails!".format(len(fraud_emails)))

如果观察到以下输出,说明数据加载成功:

Successfully loaded 3978 spam emails!

现在,欺诈电子邮件数据已经作为列表加载,接下来我们可以将其转换为一个pandas数据框,以便使用已经定义好的函数来处理它,如下所示:

fraud_bodies = extract_messages(pd.DataFrame(fraud_emails,columns=
     ["message"],dtype=str))
fraud_bodies_df = pd.DataFrame(fraud_bodies[1:])
print(fraud_bodies_df.head())

成功执行此段代码将产生输出。加载前5封电子邮件并将其输出到屏幕,如下所示:

Successfully retrieved message body from e-mails!
 
0 FROM:MR. JAMES NGOLA.\nCONFIDENTIAL TEL: 233-27-587908.\nE-MAIL:
 (james_ngola2002@maktoob.com).\n\nURGENT BUSINESS ASSISTANCE AND
 PARTNERSHIP.\n\n\nDEAR FRIEND,\n\nI AM ( DR.) JAMES NGOLA, THE PERSONAL
 ASSISTANCE TO THE LATE CONGOLESE (PRESIDENT LAURENT KABILA) WHO WAS
 ASSASSINATED BY HIS BODY G...
1 Dear Friend,\n\nI am Mr. Ben Suleman a custom officer and work as
 Assistant controller of the Customs and Excise department Of the Federal
 Ministry of Internal Affairs stationed at the Murtala Mohammed
 International Airport, Ikeja, Lagos-Nigeria.\n\nAfter the sudden death
 of the former Head of s...
2 FROM HIS ROYAL MAJESTY (HRM) CROWN RULER OF ELEME KINGDOM \nCHIEF DANIEL
 ELEME, PHD, EZE 1 OF ELEME.E-MAIL \nADDRESS:obong_715@epatra.com
 \n\nATTENTION:PRESIDENT,CEO Sir/ Madam. \n\nThis letter might surprise
 you because we have met\nneither in person nor by correspondence. But I
 believe\nit is...
3 FROM HIS ROYAL MAJESTY (HRM) CROWN RULER OF ELEME KINGDOM \nCHIEF DANIEL
 ELEME, PHD, EZE 1 OF ELEME.E-MAIL \nADDRESS:obong_715@epatra.com
 \n\nATTENTION:PRESIDENT,CEO Sir/ Madam. \n\nThis letter might surprise
 you because we have met\nneither in person nor by correspondence. But I
 believe\nit is...
4 Dear sir, \n \nIt is with a heart full of hope that I write to seek your
 help in respect of the context below. I am Mrs. Maryam Abacha the former
 first lady of the former Military Head of State of Nigeria General Sani
 Abacha whose sudden death occurred on 8th of June 1998 as a result of
 cardiac ...

加载这两个数据集之后,将每个数据集中的电子邮件采样到一个数据框中,该数据框将表示涵盖两类电子邮件(垃圾电子邮件和非垃圾电子邮件)的完整数据集。在此之前,我们必须确定从每类电子邮件中抽取多少样本。理想情况下,每个类别的样本数量将代表电子邮件的真实分布,如果预计分类器在实际工作时会遇到60%的垃圾电子邮件和40%的非垃圾电子邮件,那么分别为600和400可能是有意义的。请注意,由于数据中通常存在严重的不平衡,例如99%的非垃圾电子邮件和1%的垃圾电子邮件,因此在大多数情况下算法可能会对非垃圾电子邮件过拟合,这是一个在构建数据集时必须考虑的问题。因为本章介绍的是一个理想化的实验,我们没有关于各类电子邮件的自然分布的任何信息,所以假设两类电子邮件的占比为50∶50。另外,还需要考虑如何对电子邮件进行分词,也就是说,将电子邮件拆分为文本单词、句子等子单元。首先,我们将其拆分为单词序列,因为这是最常见的方法。我们还必须确定每封电子邮件的最大词元(token)数和每个词元的最大长度,以确保偶尔出现的超长电子邮件不会影响分类器的性能。可以通过指定以下通用超参数来实现这一切。这些超参数稍后将通过实验进行调优,以便根据需要提高分类器性能。

Nsamp = 1000  ←--- 每类样本的数量——垃圾电子邮件和非垃圾电子邮件的数量
maxtokens = 50  ←--- 每封电子邮件允许的最大词元数
maxtokenlen = 20  ←--- 每个词元的最大长度

通过指定这些超参数,我们现在可以为总体训练集创建一个数据框。我们也顺便执行剩余的预处理任务,即分词处理、移除标点符号以及移除停用词。

接下来我们定义一个函数来对电子邮件内容进行分词——将字符串切分为单词序列,如代码段2.4所示。

代码段2.4 对电子邮件内容进行分词

def tokenize(row):
    if row in [None,'']:
        tokens = ""
    else:
        tokens = str(row).split(" ")[:maxtokens]   ←--- 将每封电子邮件对应的字符串切分为单词序列
    return tokens

再看看前面的电子邮件,我们发现它们包含很多标点符号,而垃圾电子邮件中的内容倾向于大写形式。为了确保仅根据语言内容进行分类,我们定义了一个函数来移除电子邮件中的标点符号和其他非文字字符。通过Python正则表达式可以实现这一点。我们还可以通过Python字符串函数.lower()将单词转换为小写形式来规范化它们。预处理函数如代码段2.5所示。

代码段2.5 移除电子邮件中的标点符号及其他非文字字符

import re
def reg_expressions(row):
    tokens = []
    try:
        for token in row:
            token = token.lower()
            token = re.sub(r'[\W\d]', "", token)   ←--- 匹配和移除非文字字符
            token = token[:maxtokenlen]   ←--- 截断词项
            tokens.append(token)
    except:
        token = ""
        tokens.append(token)
    return tokens

最后,我们定义一个函数来移除语言中频繁出现的停用词——这些词不能提供对分类有用的信息,如“the”和“are”等词,流行的NLTK库提供了使用较为广泛的停用词表。停用词移除功能如代码段2.6所示。请注意,NLTK库还有一些移除标点符号的方法,可以用于替换代码段2.5中的方法。

代码段2.6 移除停用词

import nltk
 
nltk.download('stopwords')
from nltk.corpus import stopwords
stopwords = stopwords.words('english')
 
def stop_word_removal(row):
    token = [token for token in row if token not in stopwords]   ←--- 将停用词从词项列表中移除
    token = filter(None, token)   ←--- 移除空字符串、None等
    return token
 

现在把这些函数整合在一起,以构建表示这两类电子邮件数据的完整数据集。代码段2.7说明了该过程。在该代码段中,我们将组合结果转换为NumPy数组,因为这是接下来将要使用的许多库所期望的输入数据格式。

代码段2.7 综合使用预处理步骤生成电子邮件数据集

import random

EnronEmails = bodies_df.iloc[:,0].apply(tokenize)   ←--- 应用预定义的处理函数
EnronEmails = EnronEmails.apply(stop_word_removal)
EnronEmails = EnronEmails.apply(reg_expressions)
EnronEmails = EnronEmails.sample(Nsamp)   ←--- 从每类样本中适量抽样
 
SpamEmails = fraud_bodies_df.iloc[:,0].apply(tokenize)
SpamEmails = SpamEmails.apply(stop_word_removal)
SpamEmails = SpamEmails.apply(reg_expressions)
SpamEmails = SpamEmails.sample(Nsamp)
 
raw_data = pd.concat([SpamEmails,EnronEmails], axis=0).values  ←--- 转换为NumPy
数组

现在我们来看一下结果,以确保得到预期的结果:

print("Shape of combined data represented as NumPy array is:")
print(raw_data.shape)
print("Data represented as NumPy array is:")
print(raw_data)

执行上面的代码得到如下输出:

Shape of combined data represented as NumPy array is:
(2000, )
Data represented as NumPy array is:
[['dear', 'sir', 'i' 'got' ... ]
 ['dear', 'friend' ' my' ...]
 ['private', 'confidential' 'friend', 'i' ... ]
 ...

可以看到,结果数组按照预期将连续文本切割成单词。

接下来创建这些电子邮件对应的标题,包括Nsamp=1000的垃圾电子邮件和Nsamp=1000的非垃圾电子邮件,如下所示:

Categories = ['spam','notspam']
header = ([1]*Nsamp)
header.extend(([0]*Nsamp))

现在可以将这个NumPy数组转换为实际提供给分类算法的数值特征了。

2.1.3 将电子邮件文本转换为数值

在本章中,我们首先采用公认的最简单的词向量方法,即使用词袋(bag-of-word)模型将电子邮件文本转换为数值。该模型只计算单词在每封电子邮件中出现的频次,从而通过词频向量来表征电子邮件的内容。代码段2.8展示了用于集成电子邮件词袋模型的函数。请注意,在执行此操作时,我们只保留由变量used_tokens捕获的出现一次以上的词条。如此处理可以显著降低向量维度。还请注意,可以使用流行的scikit-learn库中的各种附带向量工具类来实现此功能(Jupyter Notebook展示了如何实现此功能)。然而,在这里,我们聚焦于代码段2.8中的方法,因为它比实现相同功能的黑盒函数更具有说服力。我们还注意到scikit-learn的向量化方法包括计算任意n个单词或n-gram序列的出现次数,以及tf-idf方法,如果读者对这些感到陌生,那么有必要从头学习一下。对于当前的问题,在词袋模型上使用向量化方法并不能直接带来什么好处。

代码段2.8 集成电子邮件词袋模型表示

def assemble_bag(data):
    used_tokens = []
    all_tokens = []
 
    for item in data:
        for token in item:
            if token in all_tokens:   ←--- 如果词条之前未出现,将其追加到used_tokens列表中 
                if token not in used_tokens:
                    used_tokens.append(token)
            else:
                all_tokens.append(token)
 
    df = pd.DataFrame(0, index = np.arange(len(data)), columns = used_tokens)
 
    for i, item in enumerate(data):   ←--- 创建一个 pandas 数据框用于对每封电子邮件(数据框中的每一行)的词频进行计数(数据框中的每一列)
        for token in item:
            if token in used_tokens:
                df.iloc[i][token] += 1
    return df

在定义好assemble_bag()函数之后,使用它执行向量化,并显示处理结果:

EnronSpamBag = assemble_bag(raw_data)
print(EnronSpamBag)
predictors = [column for column in EnronSpamBag.columns]

以下是输出结果数据框的一个片段:

     fails report s  events   may   compliance stephanie
0 0 0 0 0 0 0
1 0 0 0 0 0 0
2 0 0 0 0 0 0
3 0 0 0 0 0 0
4 0 0 0 0 0 0
        
1995 1 2 1 1 1 0
1996 0 0 0 0 0 0
1997 0 0 0 0 0 0
1998 0 0 0 0 0 1
1999 0 0 0 0 0 0
 
[2000 rows x 5469 columns]

列标签表示词袋模型词汇表中的单词,每行中的数据项对应数据集的2 000封电子邮件中每个单词的词频。请注意,这是一个非常稀疏的数据框,大部分值为0。

在对数据集进行完全向量化之后,必须记住,数据集并没有针对类进行打乱(shuffled),也就是说,它包含Nsamp=1000封垃圾电子邮件,然后是等量的非垃圾电子邮件。在我们的任务中,根据此数据集的分割方式,选择将前70%的样本用于训练,剩余样本用于测试,这可能会导致完全由垃圾电子邮件构成训练集,显然这会导致失败。要在数据集中创建各类样本的随机排序,我们需要将数据及标签相关的标题或列表一起打乱。实现此功能的函数如代码段 2.9 所示。同样地,使用scikit-learn附带的方法也能实现同样的功能,但我们发现代码段2.9展示的方法更具说服力。

代码段2.9 将数据及标签相关的标题或列表一起打乱

def unison_shuffle_data(data, header):
    p = np.random.permutation(len(header))
    data = data[p]
    header = np.asarray(header)[p]
    return data, header

作为准备电子邮件数据集以供基线分类器进行训练的最后一步,我们将其划分为训练集和测试集/验证集。这将使我们能够评估分类器在某组数据上的性能,而这组数据不用于训练,这是机器学习实践中要确保的一件极其重要的事情。我们选择前70%的样本用于训练,将剩余的30%的样本用于事后测试/验证。以下代码首先调用unison_shuffle_data()函数,然后执行训练集/测试集分割。在本章的后续内容中,生成的NumPy数组变量train_x和train_y将直接用于训练分类器:

data, header = unison_shuffle_data(EnronSpamBag.values, header)
idx = int(0.7*data.shape[0])   ←--- 将前70%的样本作为训练数据
train_x = data[:idx]
train_y = header[:idx]
test_x = data[idx:]   ←--- 将剩余的30%的样本作为测试数据
test_y = header[idx:]

希望这项针对机器学习任务构建和NLP数据集预处理的练习(现已完成)可以帮助读者掌握有用的技能,并且可以将这些技能拓展到读者自身的项目中。现在继续讨论第二个示例中涉及的预处理,我们将在本章和第3章中使用该示例,即互联网电影数据库(Internet Movie Database,IMDb)中的电影评论情感分类。考虑到相比我们手动构建的电子邮件数据集,IMDb数据集处于更开箱即用的状态,训练所需时间将明显缩短。但是,由于数据按类型分别组织在各自的文件目录中,因此这是一个凸显不同类型预处理操作的差别的机会。

2.2 电影评论情感分类任务中示例数据的预处理

在本节中,我们将对本章要分析的第二个示例的数据集进行预处理和探索。第二个示例涉及将IMDb中的电影评论分为正面或负面情感。这是一个典型的情感分类任务,在算法研究文献中被广泛使用。在这里,我们提供了预处理数据所需的代码片段,读者在阅读过程中尝试运行该代码,可以获得事半功倍的学习效果。

为此我们将使用一个比较流行的包含25 000条评论的标注数据集,它是通过抓取热门电影评论网站IMDb中的数据聚集而成的,并将每条评论对应的星数映射为0或1,这取决于评论的星数小于或大于5星(最高10星)[2]。该数据集在早前的NLP文献中已被广泛使用,这也是我们选择它作为基准示例的原因之一。

[2] Maas AL et al, “Learning Word Vectors for Sentiment Analysis,” Proc. of  NAACL-HLT (2018).

在分析之前,用于预处理每条IMDb电影评论的步骤与图2.2中的垃圾电子邮件分类步骤非常相似,仅存在少量区别。最主要的区别是由于这些评论没有附加的标题,因此标题抽取步骤在这里不适用。此外,由于一些停用词(如no和not等)可能会改变评论带有的情感,因此可能需要格外小心地执行停用词移除步骤,先确保从目标列表中移除此类停用词。我们做了从停用词列表中移除这类单词的实验,然而对结果并没有产生太显著的影响。这可能是因为评论中的非停用词作为特征来说非常具有区分度,使得这一步变得无关紧要。因此,尽管我们在Jupyter Notebook中向读者展示了如何做到这一点,但这里不再进一步讨论。

现在开始准备IMDb数据集,与2.1节中处理电子邮件数据集的方式类似。IMDb数据集可以通过Jupyter Notebook中的以下Shell命令下载和抽取[3]

[3] 这里对下载网址进行了处理,仅作演示。请读者自行查找下载地址。——编辑注

!wget -q "http://ai.****.***/~amaas/data/sentiment/aclImdb_v1.tar.gz" 
!tar xzf aclImdb_v1.tar.gz

请注意感叹号(!)。命令的开头告诉解释器这些是Shell命令,而不是Python命令。还请注意,这是一个Linux命令。如果读者在本地的Windows操作系统上运行此代码,则可能需要根据提供的链接手动下载并提取该文件。这将产生两个子文件夹——aclImdb/pos/和aclImdb/neg/,在分词、移除停用词和标点符号并打乱顺序之后,我们使用代码段2.10中的函数及其调用脚本将文件的内容加载到NumPy数组中。

代码段2.10 将IMDb数据加载到NumPy数组

def load_data(path):
    data, sentiments = [], []
    for folder, sentiment in (('neg', 0), ('pos', 1)):
        folder = os.path.join(path, folder)
        for name in os.listdir(folder):   ←--- 遍历当前目录下的所有文件
            with open(os.path.join(folder, name), 'r') as reader:
                  text = reader.read()
            text = tokenize(text)   ←--- 应用分词和停用词移除函数
            text = stop_word_removal(text)
            text = reg_expressions(text)
            data.append(text)
            sentiments.append(sentiment)   ←--- 追踪相应的情感标签
    data_np = np.array(data)   ←--- 转换为NumPy数组
    data, sentiments = unison_shuffle_data(data_np, sentiments)
 
    return data, sentiments
 
train_path = os.path.join('aclImdb', 'train')   ←--- 调用上面的函数来处理数据
raw_data, raw_header = load_data(train_path)

请注意,在 Windows 操作系统中,可能必须为代码段 2.10 中的 open() 函数调用指定参数encoding=utf-8。检查被加载数据的维度是否正确,以确保一切按预期进行,如下所示:

print(raw_data.shape) 
print(len(raw_header))

得到以下输出:

(25000,) 
25000

将随机加载的Nsamp*2条数据用于训练,如下所示:

random_indices = np.random.choice(range(len(raw_header)),size=(Nsamp*2,),
     replace=False)
data_train = raw_data[random_indices]
header = raw_header[random_indices]

在进一步处理之前,我们需要检查结果数据中类目的均衡性。一般来说,我们不希望某个标签对应的数据占绝大多数,除非这是实际中所期望的分布。使用以下代码检查类标签分布:

unique_elements, counts_elements = np.unique(header, return_counts=True)
print("Sentiments and their frequencies:")
print(unique_elements)
print(counts_elements)

输出结果如下:

Sentiments and their frequencies:
[0 1]
[1019 981]

如此可以确信数据在两个类之间大致平衡,每个类代表大约一半的数据集。然后用以下代码生成并可视化词袋表示:

MixedBagOfReviews = assemble_bag(data_train) 
print(MixedBagOfReviews)

以下是通过这段代码生成的结果数据框的片段:

      ages  i  series        the   dream  the  movie  film  plays  ...  \
0        2 2       0     0    0 0 0 1 0 0 ...
1        0 0 0 0 0 0 0 0 0 1 ...
2        0 0 2 2 2 2 2 0 1 0 ...
3        0 2 0 1 0 0 0 1 1 1 ...
4        0 2 0 0 0 0 1 0 0 0 ...
...    ... .. ... ... ... ... ... ... ... ... ...
1995 0 0 0 0 0 0 0 2 1 0 ...
1996 0 0 0 0 0 0 0 1 0 0 ...
1997 0 0 0 0 0 0 0 0 0 0 ...
1998 0 3 0 0 0 0 1 1 1 0 ...
1999 0 1 0 0 0 0 0 1 0 0 ...

请注意,在进行此类处理之后,仍然需要将此数据分割为训练集和验证集,类似于我们之前在垃圾电子邮件分类任务中所做的操作。简洁起见,这里不再赘述。可以在Kaggle Notebook中找到这段代码。

有了这种量化表示,后面的章节将开始为两个示例数据集构建基线分类器。在2.3节中,我们将从广义线性模型开始。

2.3 广义线性模型

传统上,应用数学领域内的任意模型进行开发都是从线性模型开始的。线性模型在输入和输出空间之间进行加法和乘法的映射。换句话说,一组输入的响应将是对每个单独输入的响应之和。这个属性可以显著减少涉及的统计学和数学理论。

在本节中,我们使用统计学中线性的宽松定义,即广义线性模型(generalized linear model)。设Y为输出变量或响应变量的向量,X为自变量的向量,β为未知参数向量,其取值可以通过训练分类器进行估计。广义线性模型如图2.3所示。

图2.3 广义线性模型

这里,E[]表示所被包含量的期望值(expected value),右侧是X的线性表示,而g则是将这个线性表示与Y的期望值联系起来的函数。

在本节中,我们将应用一组最广为使用的广义线性机器学习模型来解决2.2节中介绍的示例问题,即逻辑斯谛回归和带有线性核的SVM。其他较为流行的广义线性机器学习模型,如使用线性激活函数的简单感知机、隐性狄利克雷分布(Latent Dirichlet Allocation,LDA)和朴素贝叶斯这里不涉及。

2.3.1 逻辑斯谛回归

逻辑斯谛回归通过使用逻辑斯谛函数估计概率来对一组输入变量与分类输出变量之间的关系进行建模。假设存在单个输入变量x和单个二元输出变量y,相关概率为P(y=1)=p,逻辑斯谛线性函数如图2.4所示。

图2.4 逻辑斯谛线性函数

重新组织以上线性函数后将产生如图2.5所示的经典逻辑斯谛方程。

图2.5 重新组织后的经典逻辑斯谛方程

根据这个方程绘制的曲线如图2.6所示。从历史上看,这条曲线是从细菌种群增长的研究中得出的,最初增长缓慢,中间增长迅速,最后增长逐渐变缓,这是因为维持种群增长的资源耗尽了。

图2.6 经典的逻辑斯谛曲线

现在我们继续使用流行的scikit-learn库。代码段2.11中的函数可以帮助构建分类器。

代码段2.11 构建逻辑斯谛回归分类器

from sklearn.linear_model import LogisticRegression
 
def fit(train_x,train_y):
    model = LogisticRegression()  ←--- 初始化模型
 
    try:
        model.fit(train_x, train_y)   ←--- 拟合数据(标注数据)
    except:
        pass
    return model

为了使这个分类器适合垃圾电子邮件分类任务或IMDb电影评论情感分类任务的数据,我们只需要执行以下代码:

model = fit(train_x,train_y)

在任何现代PC上,只需要几秒就能完成构建。为了评估该分类器的性能,我们必须对每个任务的测试集/验证集进行测试。可使用以下代码执行此操作:

predicted_labels = model.predict(test_x)
from sklearn.metrics import accuracy_score
acc_score = accuracy_score(test_y, predicted_labels)
print("The logistic regression accuracy score is::")
print(acc_score)

对于垃圾电子邮件分类任务,输出结果如下:

The logistic regression accuracy score is:: 
0.9766666666666667

对于IMDb电影评论情感分类任务,输出结果如下:

The logistic regression accuracy score is:: 
0.715

这似乎表明,垃圾电子邮件分类问题比IMDb电影评论情感分类问题更简单。在第3章的结尾部分中,我们将讨论可能提高IMDb电影评论情感分类器性能的候选方法。

在学习2.3.2节之前,需要指出的是,使用准确率(accuracy)作为性能评估指标非常重要。准确率定义为正确识别样本的比例,即真阳性样本和真阴性样本在总样本中的占比。这里可以使用的其他潜在指标包括精确度(precision,即真阳性样本与所有预测为阳性的样本的比例)和召回率(recall,即真阳性样本与所有实际为阳性的样本的比例)。如果预测出现假阳性和假阴性的代价都很大,那么这两个指标可能非常有用。而F1值——精确度和召回率的调和平均值,在两者之间取得平衡,该指标也非常重要,对于不均衡的数据集尤其有用。由于实践中不均衡的样本集十分常见,因此这一指标非常重要。但是,请记住,我们迄今为止构建的数据集是大致均衡的。因此,在现有的例子中,准确率是一个足够合理的评估指标。

2.3.2 支持向量机

正如第1章中提到的,支持向量机曾经是非常流行的核方法。该方法试图通过将数据映射到高维空间、使用超平面作为决策边界以及降低计算成本的核函数来找到很好的决策边界。当核函数是线性函数时,SVM不仅是广义线性模型,而且实际上也是线性模型。

现在我们继续为手头的示例构建和评估SVM分类器,如代码段2.12所示。请注意,由于该分类器的训练时间比逻辑斯谛回归分类器的训练时间稍长,因此我们使用Python附带的time库来确定训练时间。

代码段2.12 训练和测试SVM分类器

import time
from sklearn.svm import SVC # Support Vector Classification model
 
clf = SVC(C=1, gamma="auto", kernel='linear',probability=False)   ←--- 创建一个线性核SVM分类器
 
start_time = time.time()  ←--- 拟合训练数据来构建模型
clf.fit(train_x, train_y)
end_time = time.time()
print("Training the SVC Classifier took %3d seconds"%(end_time-start_time))
 
predicted_labels = clf.predict(test_x)   ←--- 测试与评估
acc_score = accuracy_score(test_y, predicted_labels)
print("The SVC Classifier testing accuracy score is::")
print(acc_score)

在垃圾电子邮件示例数据集上训练SVM分类器耗费了64s,得到的准确率为0.670。在IMDb电影评论情感示例数据集上训练分类器需要36s,得到的准确率为0.697。我们发现,SVM分类器在垃圾电子邮件分类问题上的性能明显低于逻辑斯谛回归分类器,而在IMDb电影评论情感分类问题上的性能稍低,但几乎相当。

在第3章中,我们将针对这两个分类问题应用一些更复杂的方法,并进一步将它们树立为参照物,用于比较各种方法的性能。特别是,我们将探讨基于决策树的方法,以及流行的基于神经网络的ELMo和BERT方法。

小结

典型的解决思路是在任何给定的感兴趣的问题上尝试各种算法,以找到适合使用者应用场景的模型复杂度与性能之间的最佳平衡。

基线通常从最简单的算法(如逻辑斯谛回归)开始,并变得越来越复杂,直到达到理想的性能/复杂度平衡。

机器学习实践中很大一部分精力用于数据集成和预处理,而今天,这可以说是建模过程中最重要的部分之一。

重要的模型设计选型包括评估性能的指标、用于指导训练算法的损失函数和最佳验证实践,以及其他许多指标,这些指标可能因模型和问题类型而不同。

读者服务:

微信扫码关注【异步社区】微信公众号,回复“e61571”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。

相关图书

自然语言处理与医疗文本的知识抽取
自然语言处理与医疗文本的知识抽取
Python计算机视觉和自然语言处理 开发机器人应用系统
Python计算机视觉和自然语言处理 开发机器人应用系统
文本上的算法——深入浅出自然语言处理
文本上的算法——深入浅出自然语言处理
精通Python自然语言处理
精通Python自然语言处理
Python自然语言处理
Python自然语言处理

相关文章

相关课程