Python-生成式人工智能基础知识-全-

Python 生成式人工智能基础知识(全)

原文:zh.annas-archive.org/md5/1c679d4d0c0ca4c219746adb508317b4

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

欢迎来到 Python中的生成人工智能基础: 探索关键技术和在LLMs中应对现代挑战。本书提供了一本易于理解的生成人工智能和大型语言模型(LLMs)的入门书籍,引导读者从核心原则到实际应用。它旨在提供一种平衡的方法,结合理论和实践示例,为那些寻求理解和利用生成人工智能在其各自学科和领域中的人提供坚实的基础。

本书面向对象

本书面向数据科学家、机器学习工程师、IT专业人士、教育工作者和具有基本机器学习和Python知识的学生,本书旨在满足读者的需求,使他们能够充分参与内容,并构建生成人工智能概念的坚实基础。

本书涵盖内容

第一章, 理解生成人工智能:简介,奠定了概念基础,拓宽了读者对这项技术做什么、如何产生以及如何使用的基本理解。它确立了生成模型与经典机器学习范例的不同之处,并阐明它们如何识别数据中的复杂关系和独特性,以合成类似人类的文本、音频和视频。

第二章, 调查通用人工智能的类型和模式:GANs、Diffusers和Transformers概述,更深入地探讨了这些技术的理论基础和实际应用。它剖析了随着时间的推移,改进训练稳定性和输出质量的架构创新和增强,将我们带到最先进的LLMs。

第三章, 追踪自然语言处理的基础和Transformer的影响,涵盖了导致Transformer架构出现的自然语言处理(NLP)的演变。它介绍了Transformer——其在深度学习中的基础、其自注意力架构以及其快速演变,这些都导致了生成人工智能现象。

第四章, 将预训练生成模型应用于生产:从原型到部署,概述了将生成人工智能原型过渡到生产就绪部署的过程。它通过使用Docker、GitHub和CI/CD管道设置一个健壮的Python环境,然后介绍为当前项目选择和部署合适的预训练模型时的考虑因素,强调计算考虑、适当的评估、监控和负责任的AI实践。

第5章针对特定任务的生成模型微调,探讨了参数高效微调PEFT)如何促进特定任务(如问答)的可访问性持续训练。它探讨了并定义了一系列可扩展的微调技术,并将它们与其他方法(如上下文学习)进行了比较。

第6章理解大型语言模型的领域自适应,介绍了领域自适应,这是一种独特的微调方法,使模型能够解释特定行业或领域的独特语言,解决LLM对专业语言理解上的差距。

第7章掌握提示工程的基本原理,探讨了提示技术,以考察如何在不进行微调的情况下适应通用LLM。它探讨了各种利用模型内在能力产生针对性和上下文相关输出的提示策略。它探讨了RAG的简单方法,并提供了理解和衡量性能的技术。

第8章解决伦理问题并规划值得信赖的生成人工智能之路,认识到生成人工智能日益突出,并探讨了应指导其进步的伦理考量。它概述了关键概念,如透明度、公平性、问责制、尊重隐私、知情同意、安全性和包容性,这些对于这些技术的负责任开发和利用至关重要。

为了充分利用本书

读者应具备Python编程的基础知识以及机器学习概念的基本理解。熟悉深度学习框架(如TensorFlow或PyTorch)将有益但不是必需的。本书假设读者具备中级Python水平,使他们能够专注于章节中涵盖的生成人工智能概念和应用。

本书涵盖的软件/硬件 操作系统要求
Python 3 GPU支持的Windows、macOS或Linux

本书中的代码示例旨在与Python 3兼容,并在Windows、macOS或Linux操作系统上运行。为了充分参与实践教程和示例,建议使用GPU,因为许多生成人工智能模型计算密集。本书提供了设置合适开发环境的指导,包括安装必要库和依赖项的说明。

如果您使用本书的数字版,我们建议您亲自输入代码或从本书的GitHub仓库(下一节中提供链接)获取代码。这样做将有助于您避免与代码复制和粘贴相关的任何潜在错误。

在整本书中,鼓励读者积极实验提供的代码示例,并将它们应用到自己的项目中。配套的GitHub仓库是一个宝贵的资源,提供了章节中展示的代码示例的更完整和模块化版本。访问和使用此代码将增强读者的学习体验,并帮助他们巩固对所涵盖概念的理解。

下载示例代码文件

您可以从GitHub下载本书的示例代码文件,网址为https://github.com/PacktPublishing/Generative-AI-Foundations-in-Python。任何代码更新都将提供在GitHub仓库中。如果出现任何问题,请随时在此仓库中提出问题。

我们还有其他来自我们丰富图书和视频目录的代码包,可在https://github.com/PacktPublishing/找到。查看它们吧!

使用的约定

本书使用了多种文本约定。

文本中的代码:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟URL、用户输入和Twitter昵称。以下是一个示例:“数据集中的每个条目都需要进行分词,并使用必要的字段进行结构化,例如input_idsattention_mask。”

代码块设置如下:

# Get the start and end positions
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits

粗体:表示新术语、重要单词或您在屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“点击GitHub主页右上角的+图标,并选择New repository。”

小贴士或重要提示

它看起来像这样。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何方面有疑问,请通过电子邮件发送至customercare@packtpub.com,并在邮件主题中提及书名。

勘误表:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在本书中发现错误,我们将不胜感激,如果您能向我们报告,我们将不胜感激。请访问www.packtpub.com/support/errata并填写表格。

盗版:如果您在互联网上遇到任何形式的我们作品的非法副本,如果您能提供位置地址或网站名称,我们将不胜感激。请通过copyright@packt.com与我们联系,并提供材料的链接。

如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为本书做出贡献,请访问authors.packtpub.com

分享您的想法

一旦您阅读了《Python中的生成式AI基础》,我们非常乐意听到您的想法!请点击此处直接访问此书的Amazon评论页面并分享您的反馈。

您的评论对我们和科技社区非常重要,并将帮助我们确保我们提供高质量的内容。

下载此书的免费PDF副本

感谢您购买此书!

您喜欢在路上阅读,但无法携带您的印刷书籍到处走?

您的电子书购买是否与您选择的设备不兼容?

不要担心,现在每本Packt书籍都免费提供该书的DRM免费PDF版本。

在任何地方、任何设备上阅读。直接从您喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。

优惠不止于此,您还可以获得独家折扣、时事通讯和每日收件箱中的精彩免费内容。

按照以下简单步骤获取好处:

  1. 扫描二维码或访问以下链接

https://packt.link/free-ebook/9781835460825

  1. 提交您的购买证明

  2. 就这样!我们将直接将您的免费PDF和其他好处发送到您的电子邮件。

第1部分:生成式AI的基础和大型语言模型的演变

本部分提供了生成式AI和大型语言模型角色的概述。它涵盖了生成式AI的基础知识,包括GANs、diffusers和transformers等不同类型的生成模型,以及自然语言处理的基础方面。此外,它还探讨了预训练生成模型如何从原型应用到生产,为更高级的主题奠定了基础。

本部分包含以下章节:

  • 第1章, 理解生成式AI:简介

  • 第2章, 调查生成AI类型和模式:GANs、Diffusers和Transformers概述

  • 第3章, 追踪自然语言处理的基础和Transformer的影响

    • 第4章, 应用预训练生成模型:从原型到生产

第一章:理解生成式AI:入门

在他影响深远的著作《奇点临近》(2005年)中,著名发明家和未来学家雷·库兹韦尔断言,我们正处于技术进步指数级加速的边缘。他设想了一个未来,其中技术创新将继续加速,最终导致一个奇点——一个人工智能(AI)能够超越人类智能、模糊人类与机器之间界限的点。快进到今天,我们发现自己在沿着库兹韦尔描绘的轨迹前进,生成式AI标志着这一路径上的重大进步。今天,我们正经历着最先进的生成模型能够作为协作伙伴,具备合成理解和生成反映人类智能的复杂响应。生成方法的快速和指数级增长正在推动库兹韦尔愿景的实现,从根本上重塑了我们与技术互动的方式。

在本章中,我们为任何希望将生成式AI应用于其工作、研究或研究领域的人打下概念基础,扩展了对这项技术做什么、它是如何产生的以及如何使用的根本理解。它阐述了生成模型与经典机器学习(ML)范式之间的区别,并阐明了它们如何识别数据中的复杂关系和独特性,以合成类似人类的文本、音频和视频。我们将探讨关键的基础生成方法,如生成对抗网络(GANs)、扩散模型和变换器,并特别强调它们的实际应用。

此外,本章还希望消除围绕生成式AI的一些常见误解,并提供采用这项新兴技术的道德指南,考虑到其环境影响,并倡导负责任的发展和采用。我们还将强调生成模型在解决商业挑战方面的适用场景。到本章结束时,我们将更好地理解生成式AI的潜力及其在广泛领域的应用,并批判性地评估了风险、局限性和长期考虑。

无论你的兴趣是业余的,你是在从不同领域过渡的专业人士,还是数据科学或ML领域的资深从业者,本章都提供了对生成式AI负责任采用的有益理解,以做出明智的决定。

最终,我们希望通过介绍生成式AI和大型语言模型(LLMs)的入门探索来建立一个基础,分为两部分。

书的开头将介绍生成式AI的基础和历史,概述各种类型,例如生成对抗网络(GANs)、扩散器(diffusers)和转换器(transformers),追溯自然语言生成NLG)的基础,并展示从原型到生产的实现生成模型的基本步骤。接下来,我们将关注稍微更高级的应用基础,包括微调生成模型、提示工程以及针对生成AI负责任采用的伦理考量。让我们开始吧。

生成式AI

在最近几十年里,人工智能取得了惊人的进步。该领域的起源可以追溯到精心设计的经典统计模型,旨在帮助我们分析和理解数据。随着我们开发了更强大的计算方法来处理和存储数据,该领域发生了转变——交汇于计算机科学和统计学,为我们带来了机器学习(ML)。ML系统可以从大量数据中学习复杂关系并揭示潜在见解,从而改变我们统计建模的方法。

这种转变为深度学习的兴起奠定了基础,这是一次实质性的进步,它引入了多层神经网络(即相互连接的函数系统)来模拟复杂模式。深度学习使强大的判别式模型成为各个研究领域进步的关键,包括图像识别、语音识别和自然语言处理。

然而,随着生成式AI的出现,旅程仍在继续。生成式AI利用深度学习的力量来实现更广泛的目标。它不是对数据进行分类和区分,而是寻求学习和复制数据分布,以“创建”全新且看似原创的数据,类似于人类的输出。

区分生成式AI与其他AI模型

再次强调,判别式模型和生成式模型之间的关键区别在于它们的目标。判别式模型旨在根据输入数据预测目标输出。例如,分类算法,如逻辑回归或支持向量机,在数据中找到决策边界,将输入分类为一类或多类。神经网络通过反向传播(或回溯以解决错误)优化权重,学习输入输出映射,以做出准确的预测。高级梯度提升模型,如XGBoost或LightGBM,通过采用决策树和结合梯度提升(或模型的战略集成)的原则,进一步增强了这些判别式模型,以做出高度准确的预测。

生成式方法通过广泛的训练学习复杂关系,以生成新的数据序列,从而实现许多下游应用。实际上,这些模型通过复制在训练数据中发现的数据的统计模式和属性来创建合成输出,捕捉细微差别和独特性,这些与人类行为紧密相关。

在实践中,判别性图像分类器为包含猫或狗的图像贴标签。相比之下,生成模型可以通过学习现有图像中的像素分布和隐含特征来合成多样化的、逼真的猫或狗图像。此外,生成模型可以在不同模态上进行训练,以在以合成为重点的应用中解锁新的可能性,生成类似人类的照片、视频、音乐和文本。

有几种关键方法构成了许多最近在生成式人工智能领域进步的基础,每种方法都有独特的方法和优势。在下一节中,我们将回顾生成式进步的历史,包括对抗网络、变分自编码器、扩散模型和自回归转换器,以更好地理解它们的影响和影响。

简要概述生成方法

现代生成式建模涵盖了适用于不同数据类型和不同任务的多样化架构。在此,我们简要介绍了一些年来涌现的关键方法,使我们达到了最先进的模型:

  • 生成对抗网络(GANs)涉及两个相互连接的神经网络——一个作为生成器以创建逼真的合成数据,另一个作为判别器以区分真实和合成(伪造)数据点。生成器和判别器在零和游戏中是竞争对手,每个都在努力超越对方。这种对抗关系逐渐提高了生成器产生生动逼真合成数据的能力,使GANs擅长创建复杂的图像分布并实现照片级图像合成。

  • 变分自编码器(VAEs)采用独特的学习方法将数据压缩成更简单的形式(或潜在表示)。这个过程涉及一个编码器和一个解码器共同工作(Kingma & Welling, 2013)。虽然VAEs可能不是图像质量的最佳选择,但它们在有效地分离和理解复杂数据模式方面是无与伦比的。

  • 扩散模型在多个步骤中持续向数据添加高斯噪声以破坏它。高斯噪声可以被视为应用于信号以扭曲它的随机变化,从而产生“噪声”。扩散模型经过训练以消除添加的噪声以恢复原始数据分布。这种逆向工程过程使扩散模型能够生成多样化、高质量且与原始数据分布紧密相似的样本,产生多样化的高保真图像(Ho et al., 2020)。

  • 自回归转换器利用可并行化的自注意力来建模复杂的序列依赖关系,在语言相关任务中表现出卓越的性能(Vaswani等人,2017年)。预训练模型如GPT-4或Claude已经展示了在自然语言任务中进行泛化的能力以及令人印象深刻的人似文本生成。尽管存在伦理问题和滥用担忧,但转换器已成为语言建模和多模态生成领域的领跑者。

总体而言,这些方法为在包括图像、视频、音频和文本在内的广泛领域的高级生成建模铺平了道路。尽管架构和工程创新每天都在进步,但生成方法在多种模态上展示了无与伦比的合成能力。在本书中,我们将探索和应用生成方法来模拟现实世界场景。然而,在深入探讨之前,我们通过解决一些常见的误解来进一步区分生成方法和传统机器学习方法。

阐明判别和生成范式之间的误解

为了更好地理解传统机器学习模型(通常称为判别模型)和生成方法的独特能力和应用,在此,我们澄清一些常见的误解和神话:

神话1:生成模型在识别模式方面的效果不如判别模型有效。

真相:最先进的生成模型以其令人印象深刻的能力而闻名,能够识别和追踪模式,与一些判别模型相媲美。尽管主要关注创造性合成,但生成模型也显示出分类能力。然而,生成模型输出的类别可能难以解释,因为生成模型并非明确训练来学习决策边界或预定的关系。相反,它们可能只学会根据在训练过程中隐式(或自然)学习的标签来模拟分类。简而言之,在模型结果解释重要的情况下,使用判别模型进行分类可能是更好的选择。

示例:以GPT-4为例。除了合成类似人类的文本外,它还能理解上下文,捕捉长距离依赖关系,并在文本中检测模式。GPT-4利用这些固有的语言处理能力来区分类别,例如传统分类器。然而,由于GPT通过大量训练学习语义关系,其决策过程的解释无法使用任何既定方法完成。

神话2:生成AI最终将取代判别AI。

真相:这是一个常见的误解。判别模型一直是高风险预测任务的首选,因为它们直接专注于学习类别之间的决策边界,确保高精度和可靠性。更重要的是,判别模型可以进行事后解释,这使得它们成为医疗、金融和安全等关键领域应用的最终选择。然而,随着可解释性技术的出现,生成模型可能会越来越受欢迎,用于高风险建模。

示例:考虑一个专门用于医疗疾病预测的判别模型。一个专门的模型可以将数据点(例如,皮肤图像)分类为健康或不健康,为医疗专业人员提供早期干预和治疗计划的工具。可以使用事后解释方法,如SHAP,来识别和分析影响分类结果的关键特征。这种方法提供了对具体结果(即特征归因)的清晰见解。

神话3:生成模型持续从用户输入中学习。

真相:并不完全是这样。生成型大型语言模型使用的是静态方法进行训练。这意味着它们从庞大的训练数据集中学习,其知识仅限于训练窗口内的信息。虽然可以通过添加额外的数据或上下文信息来增强模型,以帮助它们进行上下文化,给人一种实时学习的印象,但底层模型本身基本上是冻结的,不会实时学习。

示例:GPT-3于2020年训练,并且只包含到那时为止的信息,直到其继任者GPT-3.5在2023年3月发布。自然地,GPT-4是在更近期的数据上训练的,但由于训练限制(包括性能回报递减),可以合理地预期后续的训练检查点将定期发布,而不是持续不断地发布。

虽然生成模型和判别模型具有不同的优势和局限性,但知道何时应用每个范例需要评估几个关键因素。既然我们已经澄清了一些关于它们能力的常见误解,让我们将注意力转向为特定任务或问题选择正确方法的指南。

选择正确的范例

在生成模型和判别模型之间进行选择取决于各种因素,例如手头的任务或问题、可用数据的数量和质量、期望的输出以及所需的性能水平。以下是一个关键考虑因素的列表:

  • 任务特异性:判别模型更适合高风险应用,如疾病诊断、欺诈检测或信用风险评估,在这些应用中,精度至关重要。然而,生成模型在合成图像、文本、音乐或视频等创造性任务上更为擅长。

  • 数据可用性:判别模型在训练于小数据集时往往会过拟合(或记忆示例),这可能导致泛化能力差。另一方面,由于生成模型通常在大量数据上预训练,即使输入最小,它们也能产生多样化的输出,这使得它们在数据稀缺时成为一个可行的选择。

  • 模型性能:在需要学习和解释类别之间的决策边界或数据中的预期关系被充分理解的任务中,判别模型优于生成模型。生成模型通常在不太受约束的任务中表现出色,这些任务需要一定程度的感知创造性和灵活性。

  • 模型可解释性:虽然两种范式都可以包括被认为是“黑盒”或本质上不可解释的模型,但生成模型可能更难以解释,有时甚至不可能解释,因为它们通常涉及复杂的数据生成过程,这些过程依赖于理解潜在的数据分布。另一方面,判别模型通常专注于学习类别之间的边界。在使用模型可解释性是关键要求的用例中,判别模型可能更合适。然而,生成模型的可解释性研究正在取得进展。

  • 模型复杂性:通常,判别模型需要的计算能力更少,因为它们学会直接预测给定一组明确定义的输入的一些输出。

    另一方面,生成模型可能消耗更多的计算资源,因为它们的训练目标是同时捕捉输入和假设输出之间复杂的隐藏关系。准确学习这些复杂性需要大量的数据和大量的计算。在生成型大型语言模型训练中的计算效率(例如,量化)是一个充满活力的研究领域。

最终,在生成模型和判别模型之间做出选择,应该考虑到所涉及的权衡。此外,采用这些范式需要不同层次的基础设施、数据整理和其他先决条件。偶尔,结合两种模型优势的混合方法可以作为一个理想的解决方案。例如,一个预训练的生成模型可以被微调为一个分类器。我们将在第五章中学习关于特定任务微调的内容。

现在我们已经探讨了传统机器学习(即判别范式)和生成范式之间的关键区别,包括它们各自的风险,我们可以回顾一下我们是如何到达这个范式转变的。在下一节中,我们将简要回顾生成人工智能的演变。

回顾生成人工智能的演变

生成式AI领域经历了前所未有的加速,导致像GPT这样的基础模型的发展和采用激增。然而,这种势头已经持续了几十年,由机器学习和自然语言生成研究中的持续和重大进步所驱动。这些发展使我们达到了当前最先进的模型时代。

要充分理解当前生成式AI的状态,了解其演变过程至关重要,从传统的语言处理技术开始,过渡到更近期的进步。

NLP传统方法的概述

自然语言处理NLP)技术使机器能够理解、解释和生成人类语言。它起源于传统的统计技术,如n-gram和隐马尔可夫模型HMMs),这些技术将语言结构转换为机器可以理解的数学模型。

最初,n-gram和HMMs是NLP中使用的首要方法。N-gram根据序列中的最后“n”个词预测序列中的下一个词,而HMMs通过将每个词视为马尔可夫过程中的一个状态来模拟序列。这些早期方法擅长捕捉语言中的局部模式和短距离依赖关系。

随着计算能力和数据可用性的增长,自然语言处理出现了更复杂的技术。其中之一是循环神经网络RNN),它能够管理跨越扩展序列的关系,并在先前上下文影响未来预测的任务中证明其有效性。

随后,长短期记忆网络LSTMs)被开发出来。

与传统的RNNs不同,LSTMs具有独特的保留相关长期信息的同时忽略无关数据的能力,在长期序列中维持语义关系。

进一步的进步导致了序列到序列模型的引入,这些模型通常使用LSTMs作为其底层结构。这些模型通过显著提高效率和有效性,在机器翻译和文本摘要等领域实现了革命性的变革。

总体而言,NLP从传统的统计方法发展到高级神经网络,改变了我们与机器互动的方式,并使无数应用成为可能,例如机器翻译和信息检索(IR)(或根据查询查找相关文本)。随着NLP领域的成熟,结合了传统统计方法和高级神经网络的优势,一场文艺复兴正在形成。下一代NLP的进步将引入Transformer架构,从开创性的论文《Attention is All You Need》开始,随后是BERT和GPT等模型的发布。

基于Transformer模型的到来和演变

2017年发布的名为《Attention is All You Need》的研究论文,在自然语言处理领域引发了范式转变。这篇关键论文介绍了转换器模型,这是一种架构创新,为序列语言任务,如翻译,提供了一种前所未有的方法。转换器模型与先前按顺序处理序列的模型形成对比。相反,它同时处理输入序列的不同部分,根据任务确定其相关性。这种创新的处理方法解决了序列中长期依赖关系的复杂性,使模型能够提取完成任务所需的临界语义信息。转换器是一个如此关键的进步,以至于几乎每个最先进的生成型LLM都应用了原始架构的某种变体。其重要性和影响力促使我们对原始转换器进行了详细的探索和实现,详见第3章

随着转换器的出现,自然语言处理领域取得了重大进步,包括GPT-1或生成预训练转换器1(Radford等,2018)。GPT-1引入了一种新颖的方向性架构来解决各种NLP任务。

与GPT-1同时出现的是BERT,或称为双向编码器表示从转换器,这是基于转换器模型家族的开创性工作。BERT在它的前辈中脱颖而出,它正向和反向(或双向)分析句子。这种双向分析使BERT能够更有效地捕捉语义和句法细微差别。在当时,BERT在应用于复杂自然语言任务,如命名实体识别、问答和情感分析时,取得了前所未有的成果(Devlin等,2018)。

后来,GPT-2,作为GPT-1的更大继任者,引起了极大的关注,因为它在各种任务上大大超越了其所有前辈。事实上,GPT-2在生成类似人类输出的能力上如此前所未有,以至于对其潜在影响的担忧导致了其最初发布的延迟(Hern,2019)。

在早期担忧之后,OpenAI继续开发了GPT-3,这标志着LLM潜力的飞跃。开发者展示了在巨大规模上训练的潜力,达到了1750亿个参数(或训练期间学习的可调整变量),超过了其两个前辈。GPT-3是一个“通用”的学习者,能够执行从其训练语料库中隐式学习到的广泛自然语言任务,而不是通过特定任务的微调。这种能力引发了在各个领域和任务中开发基础模型用于通用用途的探索。GPT-3的独特设计和前所未有的规模导致了一代生成模型的出现,这些模型能够隐式地通过其广泛的训练执行无限数量的越来越复杂的下游任务。

GPT-4的开发和影响

GPT-4的发展标志着在大型、多模态模型潜力方面取得了重大进步。GPT-4能够处理图像和文本输入并生成文本输出,代表了在先前的模型之上的又一次巨大飞跃。

GPT-4在各种专业和学术基准测试中展现了人类水平的表现。例如,它通过了一场模拟的律师资格考试,得分位于测试者前10%(OpenAI,2023)。

GPT-4的一个关键区别在于预训练之后发生的事情。OpenAI应用了带有人类反馈的强化学习RLHF)——这是一种从用于教授自动驾驶汽车根据其遇到的环境做出决策的技术中衍生出来的风险/奖励训练。在GPT-4的情况下,模型学会了适当地对各种场景做出反应,并在过程中结合了人类反馈。这种新颖的改进策略极大地提高了模型的事实性和对期望行为的遵守。RLHF的集成展示了模型如何更好地与人类的判断相一致,以实现负责任的AI的目标。

然而,尽管展示了开创性的能力,GPT-4与早期的GPT模型具有类似的局限性。它并不完全可靠,并且具有有限的上下文窗口(或输入大小)。这意味着它不能接收大文本或文档作为输入。它也容易产生幻觉。正如讨论的那样,幻觉是一种拟人化的描述,指模型倾向于生成不基于事实或现实的內容。幻觉的发生是因为生成式语言模型(未经增强)纯粹基于语义上下文合成内容,而不进行任何逻辑处理以验证事实性。这种弱点带来了有意义的风险,尤其是在基于事实的结果至关重要的环境中。

尽管存在局限性,GPT-4在语言模型性能上取得了重大进展。与之前的模型一样,GPT-4的开发和潜在用途强调了未来AI应用中安全和伦理考虑的重要性。因此,GPT-4的兴起加剧了关于部署如此强大模型潜在影响的持续讨论和研究。在下一节中,我们将简要概述一些仅与生成式AI相关的已知风险。

展望风险和影响

生成式和判别式AI都引入了独特的风险和收益,这些必须仔细权衡。然而,生成式方法不仅能够继承,还能加剧许多与传统机器学习相关的风险,同时也会引入新的风险。因此,在我们能够在现实世界和大规模上采用生成式AI之前,理解这些风险并建立负责任的治理原则以帮助减轻这些风险是至关重要的:

  • 幻觉:这是一个广泛使用的术语,用来描述模型生成事实不准确信息的情况。生成模型擅长在没有事实依据的情况下产生听起来合理的输出。因此,用事实信息来定位生成模型至关重要。术语“定位”指的是在模型输入中附加已知为事实的额外信息。我们在第七章中探讨了定位技术。此外,制定一个包括人工审查在内的评估模型输出的策略也是必不可少的。

  • 剽窃:由于生成模型有时是在未经许可的数据集上训练的,一些训练语料库可能包括未经明确许可的数据。模型可能会产生受版权保护的信息或可以声称为知识产权的信息。

  • 意外记忆:与许多在大量语料库上训练的机器学习模型一样,生成模型倾向于记住训练数据的一部分。特别是,它们容易记住那些不适合更广泛模式的不常见示例。在某些情况下,模型可能会记住可以提取和暴露的敏感信息(Brundage等人,2020年;Carlini等人,2020年)。因此,无论是消费预训练模型还是微调(即继续模型训练),训练数据的选择至关重要。

  • 毒性和偏见:大规模模型训练的另一个副作用是模型不可避免地会学习训练数据中嵌入的社会偏见。偏见可能表现为生成文本或图像中的性别、种族或社会经济偏见,通常复制或放大刻板印象。我们在第八章中详细介绍了这种风险的缓解措施。

对于一些风险的理解,我们将关注采用生成式AI的微妙影响:

  • 伦理:正如所讨论的,这些模型不可避免地会学习和复制训练数据中固有的偏见,引发严重的伦理问题。同样,由于模型容易记住和暴露其训练数据,数据隐私和安全问题也出现了。这导致了对于强有力的伦理指南和数据隐私法规的呼吁(Gebru等人,2018年)。

  • 环境:大型语言模型(LLM)是计算巨人,需要前所未有的资源进行训练和实施。因此,它们不可避免地会产生环境影响。训练一个LLM所需的能源消耗会产生大量的二氧化碳排放——大约相当于五辆汽车终身的排放量。因此,正在进行多项工作以提高模型效率并减少碳足迹。例如,如减少位精度训练(或量化)和参数高效微调(在第第五章中讨论)等技术可以减少整体训练时间,有助于缩小碳足迹。

  • 社会: 除了环境影响,大型语言模型(LLMs)也具有社会影响。随着这些模型在生成文本、模拟智能对话和自动化基本任务方面变得熟练,它们为自动化工作提供了前所未有的机会。由于各种复杂因素,这种在美国大规模自动化的潜力可能会不成比例地影响边缘化或代表性不足的社区。因此,这加剧了关于劳动权利和需要额外保护以最小化伤害的先前担忧。

  • 商业与劳动力: 除了更广泛的社会经济影响,我们还必须考察对商业部门的直接影响。虽然生成式人工智能开辟了新的机会,但如果处理不当,劳动力市场的变化可能会带来巨大的破坏。除了劳动力影响之外,人工智能的进步也显著影响了各个商业部门。它们可能导致新角色的创造、商业模式的改变和机会的产生,需要持续的治理策略和以包容性、道德和负责任采用为中心的探索框架。

应对这些挑战需要技术、科学上的改进,针对特定数据的法规和法律,道德规范,以及以人为本的人工智能治理策略。这些都是构建一个公平、安全、包容的人工智能驱动未来的关键。

在讨论了生成式人工智能的历史、风险和限制之后,我们现在更有能力探索这种变革性技术的广泛机会和应用。

介绍生成式人工智能的应用案例

生成式人工智能已经开始颠覆各个行业。这项技术正在许多学科中掀起波澜,从增强基于语言的任务到重塑数字艺术。以下部分提供了生成式人工智能在不同行业中的实际应用示例:

  • 传统自然语言处理: 如Open AI的GPT系列等大型语言模型(LLMs)已经提升了传统的自然语言处理(NLP)和自然语言生成(NLG)。正如所讨论的,这些模型具有生成连贯、相关且类似人类文本的独特能力。这些模型的可能性在GPT-3在多个语言任务中优于经典和现代方法时得到了证明,展示了人类语言前所未有的理解。GPT-4和Claude 3的发布标志着另一个里程碑,将最先进模型的标准进一步提高。

  • 数字艺术创作: “生成艺术”的出现是生成式人工智能在数字艺术领域产生激进影响的证据。例如,艺术家可以使用人工智能生成模型来创建复杂的设计,从而让他们专注于艺术的概念性方面。这简化了过程,减少了高级技术专长的需求。

  • 音乐创作: 在音乐产业中,生成式人工智能可以增强创作过程。几个平台提供了高质量的AI驱动音乐创作工具,可以生成结合不同时代和流派的音乐风格的长期音乐作品。

  • 简化业务流程: 一些企业已经开始采用生成式人工智能来使流程更快、更高效。生成式人工智能带来的运营效率使员工能够专注于更具战略性的任务。例如,完全集成的LLM电子邮件客户端可以组织电子邮件,并且(与其他技术结合)随着时间的推移学会优先处理关键电子邮件。

  • 娱乐: 尽管仍处于实验阶段,但大型语言模型(LLMs)在颠覆创意写作和叙事方面展现出有希望的潜力,尤其是在游戏行业。例如,程序化游戏可以利用LLMs来增强动态叙事,并创造更具吸引力和个性化的用户体验。随着技术的进步,我们可能会看到LLMs在游戏领域的更广泛采用,为互动叙事开辟新的可能性。

  • 时尚: 在时尚产业中,生成模型帮助设计师进行创新。通过使用最先进的生成式人工智能模型,设计师可以通过简单地调整几个配置来创建和可视化新的服装风格。

  • 建筑和施工: 在建筑领域,生成增强工具可以帮助建筑师和城市规划师优化和生成设计方案,从而实现更高效和可持续的建筑设计。

  • 食品行业: 新兴的AI驱动烹饪助手可以生成独特的食物组合、新颖的食谱以及针对高度特定饮食需求的修改食谱。

  • 教育: 生成式人工智能增强的教育平台可以自动创建学习辅助工具,这些工具可以促进个性化的学习体验,并自动生成定制内容以适应特定的和多样化的学习风格。

然而,我们必须在机会的广度与复杂的约束之间取得平衡,并持续推广道德使用。作为数据科学家、政策制定者和行业领导者,我们必须继续努力营造有利于负责任AI部署的环境。尽管如此,随着生成式人工智能的持续发展,它带来了充满新颖创新和应用的未来。

生成式人工智能应用的未来

生成式人工智能不懈的进步预示着一个充满可能性和复杂挑战的未来。想象一个未来,一个基于世界领先的气候变化研究训练的生成模型可以提供实际且具有突破性的应对策略,并精确地提供其应用的详细信息。

然而,随着我们拥抱越来越以AI为中心的未来,我们不应忽视现有的挑战。这些挑战包括AI工具的潜在误用、不可预测的后果以及AI采用背后的深刻伦理考量。此外,可持续和环保的发展至关重要,因为训练大规模模型可能需要大量资源。

在加速进步的时代,所有利益相关者之间的合作——从数据科学家、AI爱好者、政策制定者到行业领导者——是至关重要的。通过配备全面的监督、稳健的指导方针和战略教育计划,共同努力可以保障一个生成式AI无处不在的未来。

尽管存在这些障碍,生成式AI的变革潜力依然无可置疑。凭借其重塑行业、重新定义社会基础设施以及改变我们的生活方式、学习和工作方式的能力,生成式AI提醒我们,我们正处于一个关键时刻——这是一个由数十年的科学研究和技术创新推动的时刻,这些创新正在汇聚起来,推动我们作为一个社会向前发展。

摘要

在本章中,我们追踪了生成式AI的演变,将其与传统机器学习区分开来,探讨了其演变过程,讨论了其风险和影响,并希望消除一些常见的误解。我们考虑了一些基于其负责任采用的考虑的潜在可能性。

随着我们进入下一章,我们将探讨生成式AI背后的基本架构,这将为我们提供对关键生成方法的基础理解,包括生成对抗网络(GANs)、扩散模型和转换器。这些机器学习方法构成了生成式AI的骨架,并在带来今天我们所看到的惊人进步中发挥了关键作用。

参考文献

本参考部分作为本书中引用的资源的存储库;您可以探索这些资源,以进一步加深对主题的理解和知识:

)

  • Brundage, M., Avin, S., Clark, J., Toner, H., Eckersley, P., Garfinkel, B., Dafoe, A., Scharre, P., Zeitzoff, T., Filar, B., Anderson, H., Roff, H., Allen, G. C., Steinhardt, J., Flynn, C., Ó hÉigeartaigh, S., Beard, S., Belfield, H., Farquhar, S., & Amodei, D. (2018). 人工智能的恶意使用:预测、预防和缓解. arXiv [cs.AI]. http://arxiv.org/abs/1802.07228.

  • Carlini, N., Tramer, F., Wallace, E., Jagielski, M., Herbert-Voss, A., Lee, K., Roberts, A., Brown, T., Song, D., Erlingsson, U., Oprea, A., & Raffel, C. (2020). 从大型语言模型中提取训练数据. arXiv [cs.CR]. http://arxiv.org/abs/2012.07805.

  • Devlin, J., Chang, M.-W., Lee, K., & Toutanova, K. (2018). BERT:用于语言理解的深度双向Transformer预训练. arXiv [cs.CL]. http://arxiv.org/abs/1810.04805.

  • Hagendorff, T. (2020). 对 AI伦理的伦理:指南评估 的出版商更正. 心智与机器, 30(3), 457–461. https://doi.org/10.1007/s11023-020-09526-7.

  • Hern, A. (2019, February 14). 新的AI假文本生成器可能过于危险而无法发布,开发者表示. 《卫报》. https://www.theguardian.com/technology/2019/feb/14/elon-musk-backed-ai-writes-convincing-news-fiction.

  • Ho, J., Jain, A., & Abbeel, P. (2020). 去噪扩散概率模型. arXiv [cs.LG]. http://arxiv.org/abs/2006.11239.

  • Kaplan, J., McCandlish, S., Henighan, T., Brown, T. B., Chess, B., Child, R., Gray, S., Kingma, D. P., & Welling, M. (2013). 自编码变分贝叶斯. arXiv [stat.ML]. http://arxiv.org/abs/1312.6114.

  • Muhammad, T., Aftab, A. B., Ahsan, M. M., Muhu, M. M., Ibrahim, M., Khan, S. I., & Alam, M. S. (2022). 基于Transformer的深度学习模型用于股票价格预测:孟加拉国股票市场案例研究. arXiv [q-fin.ST]. http://arxiv.org/abs/2208.08300.

  • OpenAI. (2023). GPT-4技术报告. arXiv [cs.CL]. http://arxiv.org/abs/2303.08774.

  • Radford, A., Wu, J., Child, R., Luan, D., Amodei, D., & Sutskever, I. (2018). 语言模型是无监督的多任务学习者.

  • Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, L., & Polosukhin, I. (2017). Attention Is All You Need. arXiv [cs.CL]. http://arxiv.org/abs/1706.03762.

第二章:概述GenAI类型和模式:GANs、扩散器和Transformer概述

在上一章中,我们确立了生成模型和判别模型之间的关键区别。判别模型专注于通过学习p(output|input),即给定输入或输入集的某些预期输出的条件概率来预测输出。相比之下,生成模型,如p(next token|previous tokens),基于给定当前上下文的可能续集的概率。标记表示为包含嵌入的向量,这些嵌入通过大量训练捕获了潜在特征和丰富的语义依赖关系。

我们简要概述了领先的生成方法,包括生成对抗网络(GANs)、变分自编码器(VAEs)、扩散模型和自回归Transformer。每种方法都具有适合不同数据类型和任务的独特优势。例如,GANs通过对抗过程擅长生成高保真度的照片图像。扩散模型采用概率方法,通过迭代地向数据中添加和去除噪声来学习鲁棒的生成表示。自回归Transformer利用自注意力和大规模来实现令人瞩目的可控文本生成。

在本章中,我们将更深入地探讨这些技术的理论基础和实际应用。我们将进行直接比较,阐明随着时间的推移提高训练稳定性和输出质量的架构创新和改进。通过实际示例,我们将看到研究人员如何将这些模型应用于艺术、音乐、视频、故事等。

为了进行无偏比较,我们将主要集中研究图像合成任务。生成对抗网络(GANs)和扩散模型专门为图像数据设计,利用了卷积处理和计算机视觉方面的进步。由自注意力驱动的Transformer在语言建模方面表现出色,同时也能生成图像。这将使我们能够在共同的任务上对性能进行基准测试。

到本章结束时,我们将实现最先进的图像生成模型,并探讨这些核心方法如何相互增强和补充。

理解通用人工智能(GAI)类型——GANs、扩散器和Transformer的区别特征

我们从通用人工智能(GAI)中体验到的常常令人惊叹的人类似质量可以归因于深度生成机器学习的进步。特别是,三种基本方法激发了众多衍生创新——生成对抗网络(GANs)、扩散模型和Transformer。每种方法都有其独特的优势,特别适合特定的应用。

我们简要介绍了 GANs,这是一种开创性的方法,它利用两个竞争性神经网络——生成器和判别器——之间的对抗性交互来生成超逼真的合成数据。随着时间的推移,GANs 已经取得了重大进展,在数据生成、图像保真度和训练稳定性方面取得了更大的控制。例如,NVIDIA 的 StyleGAN 创建了高度详细和逼真的人类面孔。GANs 的对抗性训练过程,其中一个网络生成数据而另一个网络评估它,允许你创建高度精细和详细的合成图像,并在每次训练迭代中增强逼真度。生成的合成图像可以应用于众多领域。在娱乐行业中,它们可以用于创建视频游戏或电影中的逼真角色。在研究方面,它们提供了一种扩充数据集的手段,特别是在真实数据稀缺或敏感的场景中。此外,在计算机视觉中,这些合成图像有助于训练和微调其他机器学习模型,推进如人脸识别等应用。

扩散模型,一种创新的生成建模替代方案,明确解决了某些 GAN 的局限性。如第一章中简要讨论的,扩散模型采用独特的方法引入和系统地去除噪声,以较低的训练复杂度实现高质量的图像合成。在医学成像中,扩散模型可以通过生成高分辨率合成示例来训练其他机器学习模型,从而显著提高图像清晰度。引入噪声然后迭代去除噪声可以帮助从低质量输入中重建高保真图像,这在获取高分辨率医学图像具有挑战性的场景中非常有价值。

同时,最初为语言建模设计的生成式变压器已被用于多模态合成。如今,变压器不仅限于语言,还渗透到音频、图像和视频应用中。例如,OpenAI 的 GPT-4 在处理和生成文本方面表现出色,而 DALL-E 则能根据文本描述创建图像,这是方法之间交互的完美例子。当集成时,GPT-4 和 DALL-E 形成了一个强大的多模态系统。GPT-4 处理和理解文本指令,而 DALL-E 则根据解释后的指令生成相应的视觉表示。这种组合的实际应用可以是自动化数字广告创作。例如,给定产品的文本描述和期望的美学,GPT-4 可以解释这些指令,而 DALL-E 则可以相应地生成视觉上吸引人的广告。

解构 GAI 方法 - 探索 GANs、diffusers 和 transformers

让我们分解这些核心方法,以了解它们的独特特征,并展示它们在推进生成式机器学习方面的变革性作用。随着GAI不断前进,了解这些方法如何推动创新至关重要。

深入了解GANs

GANs由Goodfellow等人于2014年引入,主要由两个神经网络组成——生成器(G)和判别器(D)。G的目标是创建类似于真实数据的合成数据,而D的目标是区分真实数据和合成数据。

在这个设置中,以下情况发生:

  1. G从“潜在空间”接收输入,这是一个表示结构化随机性的高维空间。这种结构化随机性作为生成合成数据的种子,将其转化为有意义的信息。

  2. D评估生成数据,试图区分真实(或参考)数据和合成数据。

    简而言之,这个过程从G从潜在空间中提取随机噪声来创建数据开始。这些合成数据与真实数据一起提供给D,然后D试图区分这两者。D的反馈会告知G的参数以优化其数据生成过程。这种对抗性交互会持续进行,直到达到平衡。

  3. 在GANs中,当D无法区分真实和合成数据,对两者都分配0.5的相等概率时,就会达到平衡。达到这种状态表明G产生的合成数据与真实数据无法区分,这是合成过程的核心目标。

最终,GANs的成功对各个行业产生了有意义的启示。在汽车行业中,GANs被用于模拟真实世界场景以进行自动驾驶车辆的测试。在娱乐行业中,GANs被部署用于生成电影制作和游戏设计中的数字角色和逼真环境。在艺术界,GANs实际上可以创造新的词汇。此外,GANs的发展在质量、控制和整体性能方面持续取得显著进步。

GANs的进步

自其诞生以来,GAN技术已经发生了显著演变,取得了几个显著的进步:

  • 条件生成对抗网络(cGANs):由Mirza和Osindero于2014年引入,条件GANs在数据生成过程中加入了特定的条件,从而实现了更可控的输出。cGANs已被用于图像到图像翻译等任务(例如,将照片转换为画作)。

  • 深度卷积生成对抗网络(DCGANs):2015年,Radford等人通过整合卷积层增强了GANs,这有助于在小的、重叠的区域分析图像数据以捕捉精细粒度,从而显著提高了合成输出的视觉质量。DCGANs可以生成用于时尚设计等应用的逼真图像,其中模型从现有趋势中演变出新的设计。

  • Wasserstein GANs (WGANs):由Arjovsky等人于2017年提出,Wasserstein GANs将Wasserstein距离度量应用于GANs的目标函数,从而促进了真实数据和合成数据之间差异的更精确测量。具体来说,该度量有助于找到使生成数据分布接近真实数据分布的最有效方法。这一小调整导致学习过程更加稳定,最小化了训练过程中的波动。WGANs有助于生成逼真的医学图像,以辅助训练诊断人工智能算法,提高模型从合成数据到实际数据的泛化能力。

随着Wasserstein GANs的出现,该领域经历了创新扩展的激增,每个扩展都是专门针对解决特定挑战或开辟合成数据生成的新途径而量身定制的:

  • 渐进式增长的GANs在训练过程中逐步增加分辨率,从低分辨率图像开始,逐渐过渡到高分辨率。这种方法允许模型有效地学习从粗略到精细的细节,使训练更加容易管理,并生成高质量的图像(Karras等人,2017年)。这些高分辨率图像可以增强虚拟现实环境的真实感和沉浸感。

  • CycleGANs促进了图像到图像的转换,连接了领域适应任务(Zhu等人,2017年)。例如,CycleGAN可以将夏季场景转换为冬季场景,而无需在训练期间提供示例对(例如,夏季-冬季)。CycleGANs已被用于模拟自动驾驶汽车测试中的天气条件,评估系统在不同环境条件下的性能。

  • BigGANs在高质量图像生成方面推动了边界,展示了GANs在复杂生成任务中的多功能性。他们通过在训练过程中扩大模型的大小(更多层和每层的单元)以及批量大小,以及其他架构和训练创新(Brock等人,2018年)来实现这一点。BigGANs已被用于生成视频游戏中的逼真纹理,增强了游戏环境的真实感。

这些发展显著扩大了生成对抗网络(GANs)所能实现的范围,从高分辨率图像合成到领域适应和跨模态生成任务。然而,尽管取得了这些令人难以置信的进步,GANs仍然存在一些持续的局限性,这促使人们探索了如扩散等替代方法。

GANs的局限性和挑战

GANs 的训练过程需要在 G 和 D 网络之间进行仔细的平衡。它需要大量的计算资源,通常需要强大的 GPU 和庞大的数据集才能实现理想的结果。此外,GANs 的训练中存在一些复杂性,这些复杂性源于诸如梯度消失和模式坍塌等挑战。虽然梯度消失问题是广泛影响深度神经网络的普遍问题,但模式坍塌是 GANs 训练中特有的挑战。让我们进一步探讨这些问题:

  • 梯度消失:这个问题出现在神经网络训练阶段,当损失函数的梯度减小到一个点,导致学习速度急剧减慢或停止。GANs 的核心在于 G 和 D 模型之间学习的微妙平衡。不均衡的学习可能会阻碍整体训练过程。在实践中,梯度消失的问题可能导致训练时间更长,计算成本增加,这可能会使 GANs 对于时间敏感或资源受限的应用变得不切实际。

  • 模式坍塌:这是 GANs 的固有特性,当 G 开始产生狭窄的样本种类时,就会发生模式坍塌,从而抑制输出多样性,损害网络的有效性。梯度惩罚和谱归一化等技术已经缓解了这些问题。这种现象可能会显著降低生成数据的质量,限制 GANs 在需要多样化输出的应用中的使用,例如机器学习中的数据增强或创意产业中生成多样化的设计替代方案。

当然,GANs 与任何最先进的生成合成一样,具有相同的伦理考量。例如,它们可以被用来创建深度伪造或生成具有社会偏见的输出。例如,当 GANs(常用于生成合成数据,例如人脸)未能充分代表某些群体时,下游应用可能会表现出性别或种族偏见(Kenfack 等人,2021 年)。

即使随着扩散模型和基于 Transformer 的图像生成器等其他生成模型的兴起,GANs 仍在塑造生成图像合成轨迹方面发挥了开创性的作用,展示了该领域潜力和一些固有的挑战。

现在我们已经更好地理解了在深度生成模型背景下 GANs,让我们将焦点转向图像生成领域的继任者,即扩散模型。

深入探讨扩散模型

探索了生成对抗网络(GANs)的动力学之后,让我们将注意力转向图像生成领域的后续创新——扩散模型。扩散模型最初由Sohl-Dickstein等人于2015年提出,它提供了一种新颖的方法,其中神经网络通过迭代地向数据中引入和随后移除噪声来生成高度精细的图像。与利用两个对比模型之间的对抗机制的GANs不同,扩散模型在数据中应用了一种更渐进、迭代的噪声操纵过程。

在实际应用中,GANs在艺术和设计领域显示出巨大的价值,可以创建逼真的面孔或从描述中生成清晰、高保真度的图像。它们还用于数据增强,通过生成逼真的合成数据来扩展数据集,以增强机器学习模型的训练。

相反,扩散模型在需要结构化方法进行图像生成的任务中表现出色,例如在医学成像中。它们的迭代过程可以提升医学图像的质量,如MRI或CT扫描,在这些图像中,噪声减少和清晰度至关重要。这使得扩散模型在临床环境中变得非常有价值,有助于更好的诊断和分析。此外,与GANs的对抗性和动态训练相比,它们的控制和渐进过程提供了一个更可预测或稳定的训练过程。

扩散模型的基础建立在两个主要过程之上:

  • (x``₀)并迭代地引入高斯噪声,类似于逐渐应用一种雾状过滤器,将数据转换为不可区分的噪声(x``ₜ)。

  • (p``θ)试图消除(或去雾)噪声数据(x``ₜ)中的噪声,旨在恢复到原始的干净状态(x``ₜ₋₁)。具体来说,这种恢复是通过估计从噪声状态回到清晰状态的概率来实现的,使用一个条件分布表示为p``θ``(x``ₜ₋₁``|x``ₜ``)条件分布告诉我们,当我们知道另一个相关事件已经发生时,一个事件发生的可能性。在这种情况下,恢复估计了在给定一定量的噪声的情况下恢复到原始状态的可能性。

在关键工作《基于分数的生成模型通过随机微分方程》中,作者们提出了一种新颖的框架,通过使用p``θ将基于分数的生成模型和扩散概率建模统一起来。

反向模型(p``θ)使用卷积网络实现,以预测高斯噪声分布的变化——这是正向扩散中噪声引入过程中的一个关键组件。最初,这种方法的有效性在更简单的数据集上得到了验证。然而,该方法的应用性后来得到了显著提高,以处理更复杂的图像(Ho等人,2020)。这一扩展展示了扩散模型在生成具有更广泛复杂性的高度精细图像中的实际潜力。

扩散模型的进步

自从其诞生以来,扩散模型技术已经见证了关键进步,推动了其在图像生成方面的能力:

  • 简化的训练目标:Ho等人提出了简化的训练目标,这些目标直接预测高斯噪声,消除了对条件均值的需求,并促进了其在更复杂数据集上的应用(Ho等人,2020)。这一进步有助于处理更复杂的数据集,可能有助于异常检测或复杂数据合成等任务,这些任务可能需要传统模型的大量资源。

  • 带有自注意力的UNet模块:Ho等人还将带有自注意力的UNet模块纳入扩散模型架构中,受到Salimans等人(2017)的PixelCNN++的启发,增强了模型在复杂数据集上的性能(Ho等人,2020)。再次,提高复杂数据集上的性能有助于更好的图像恢复,这在医学成像或卫星图像分析等领域尤其有益,在这些领域中,高保真图像重建至关重要。

  • 与SDEs同步:Song等人将扩散模型定义为SDEs的解,将分数学习与去噪分数匹配损失联系起来,并扩展了模型在图像生成、编辑、修复和着色等领域的应用(Song等人,2020)。

在这些基础进步之后,扩散模型见证了创新增强的浪潮,研究人员引入了新的方法来解决现有挑战并扩大模型在生成建模任务中的应用范围。这些进步包括以下内容:

  • 噪声条件和退火策略:Song等人通过包括噪声条件和退火策略改进了基于分数的模型,在Flickr-Faces-HQ数据集(Song等人,2021)等基准数据集上实现了与GANs相当的性能,该数据集是一个高质量的人脸图像数据集,旨在衡量GANs的性能。实现与GANs相当的性能可以使扩散模型成为GANs传统应用领域内高保真图像生成任务的可行替代方案。

  • 潜在扩散模型(LDMs):Rombach等人通过提出LDMs来解决计算效率低下的问题,这些模型在自动编码器学习的压缩潜在空间中运行,使用感知损失来创建一个视觉上等效的、减少的潜在空间(Rombach等人,2021)。通过解决计算效率低下的问题,LDMs可以加速图像生成过程,使其适用于实时应用或计算资源有限的场景。

  • 无分类器引导:Ho和Salimans引入了无分类器引导,以实现不受预训练网络依赖的受控生成,这标志着向更灵活的生成技术的迈进(Ho & Salimans,2022)。这一进步导致了更灵活的生成技术,使得在诸如设计、广告或内容创作等应用中,可以实现更受控和定制的图像生成,而不依赖于预训练网络。

扩散模型领域的后续探索扩展了其应用,展示了其多功能性:

  • 视频生成:Ho等人将扩散模型应用于视频生成,证明了其在静态图像生成之外的实用性(Ho等人,2022)。

  • 3D数据处理:Luo和Hu将应用扩展到3D数据处理,展示了扩散模型的灵活性(Luo & Hu,2021)。

扩散模型的演变导致了图像生成的增强以及在视频、3D数据处理和快速学习方法中的应用扩展。然而,该方法确实存在其挑战和局限性,以下章节将详细阐述。

扩散模型的局限性和挑战

尽管扩散模型具有明显的优点和显著的进步,但它们仍有一些独特的局限性,如下所述:

  • 采样速度:扩散模型的一个显著局限性是缓慢的采样过程,尤其是在与GANs相比时。在此背景下,采样指的是从模型学习到的分布中生成新的数据点的过程。新样本生成的速度对于许多实时或准实时应用至关重要,而扩散模型的较慢采样速度可能是一个重大的缺点。

  • 大规模训练中的稳定性:扩散模型在大规模训练过程中的稳定性是另一个需要进一步探索的领域。大规模训练指的是在大量数据上训练模型,有时会导致模型学习过程中的不稳定性。确保这一阶段的稳定性对于模型实现可靠和一致的性能至关重要。

仔细考察这些模型产生的媒体对社会的影响至关重要,尤其是在现在对生成内容可以进行精细控制的情况下。然而,扩散模型固有的简单性、灵活性和积极的归纳偏差预示着光明的未来。这些属性表明,在生成建模领域将出现快速发展的轨迹,可能将扩散模型作为各个学科(如计算机视觉和图形学)中的关键组成部分。

生成变压器的深入探讨

变压器模型革命性的出现对从文本描述生成高保真图像的任务产生了重大影响。如CLIP对比语言-图像预训练)和DALL-E等显著模型以独特的方式利用变压器根据自然语言标题创建图像。本节将讨论基于变压器的文本到图像生成方法,其基础、关键技术、带来的好处以及一些挑战。

变压器架构的简要概述

Vaswani等人于2017年提出的原始变压器架构是许多现代语言处理系统的基石。事实上,变压器可能被认为是GAI领域最重要的架构,因为它构成了GPT系列模型和许多其他最先进生成方法的基础。因此,在我们的生成方法调查中,我们将简要介绍其架构,但将在专门的章节中详细解析和从头实现变压器。

变压器架构的核心是自注意力机制,这是一种独特的方法,能够捕捉有序数据序列中不同元素之间的复杂关系。这些元素被称为标记,根据选择的标记化粒度,代表句子中的词语或单词中的字符。

该架构中注意力原理使模型能够专注于输入数据的某些关键方面,同时可能忽略不那么重要的部分。这种机制增强了模型对上下文以及句子中词语相对重要性的理解。

变压器分为两个主要部分,即编码器解码器,每个部分都包含多个自注意力机制层。编码器能够识别输入序列中不同位置之间的关系,而解码器则专注于编码器的输出,使用一种称为掩码自注意力的自注意力变体,以防止考虑它尚未生成的未来输出。

通过查询向量和键向量的缩放点积计算注意力权重在确定对输入不同部分的关注程度方面起着关键作用。此外,多头注意力允许模型同时将注意力导向多个数据点。

最后,为了保留数据的序列顺序,模型采用了一种称为位置编码的策略。这种机制对于需要理解序列或时间动态的任务至关重要,确保模型在整个处理过程中保留数据的初始顺序。

再次,我们将回顾第3章中的转换器架构,以进一步巩固我们的理解,因为它对于生成AI的持续研究和演变是基础性的。然而,至少对转换器架构有一个基本的了解,我们就能更好地分析转换器驱动的生成建模范式在各个应用领域中的表现。

基于转换器的生成建模范式

在处理各种任务时,转换器采用与任务相匹配的不同训练范式。例如,分类等判别性任务可能使用掩码范式:

  • 掩码语言建模MLM):MLM是BERT转换器双向编码器表示)等模型使用的判别性预训练技术。在训练过程中,随机掩码掉一部分输入标记。然后模型必须根据周围未掩码的单词的上下文预测原始掩码词。这教会模型构建基于上下文的鲁棒表示,从而促进许多下游自然语言处理NLP)任务。

在BERT中使用的MLM对于提高各个领域的NLP系统的性能至关重要。例如,它可以通过准确识别和分类临床笔记中的医疗术语来为医疗编码系统提供动力。这种自动编码可以节省大量时间并减少医疗文档中的错误,从而提高医疗数据管理的效率和准确性。

对于生成任务,重点转向创建新的数据序列,需要不同的训练范式:

  • 序列到序列建模:序列到序列模型同时使用编码器和解码器。编码器将输入序列映射到一个潜在表示。然后解码器从这个表示中逐个生成目标序列的标记。这种范式对于翻译、摘要和问答等任务非常有用。

  • 自回归建模:自回归建模通过仅根据前一个标记预测下一个标记来生成序列。模型逐个步骤地产生输出,每个新标记都依赖于前面的标记。GPT等自回归转换器利用这种技术进行可控文本生成。

变压器结合自注意力、预训练表示和自回归解码以适应判别性和生成性任务。

通过在不同架构之间进行权衡,可以在复杂度、可扩展性和专业化之间实现高级生成合成。例如,许多最先进的生成模型不是同时使用编码器和解码器,而是采用仅解码器或仅编码器方法。编码器-解码器框架通常是计算最密集的学习,因为它增加了模型大小。仅解码器架构利用强大的预训练语言模型(如GPT)作为解码器,通过权重共享来减少参数。仅编码器方法放弃了解码,相反,它们对输入进行编码,并在生成的嵌入上进行回归或搜索。每种方法都有其优势,适用于某些用例、数据集和计算预算。在以下章节中,我们将探讨采用这些衍生变压器架构进行创意应用的模型示例,例如图像生成和字幕。

仅编码器方法

在某些模型中,只有编码器网络将输入映射到嵌入空间。然后直接从这个嵌入生成输出,消除了对解码器的需求。虽然这种直接的架构通常在分类或回归任务中找到其位置,但最近的进展已将其应用扩展到更复杂的任务。特别是,为图像合成等任务开发的模型利用仅编码器设置来处理文本和视觉输入,创建了一个多模态关系,从而有助于从自然语言指令生成高保真图像。

仅解码器方法

类似地,一些模型仅使用解码器策略进行操作,其中单个解码器网络负责编码输入并生成输出。此机制首先将输入和输出序列连接起来,由解码器进行处理。尽管其简单且输入和输出阶段之间存在参数共享的特征,但该架构的有效性在很大程度上依赖于鲁棒解码器的预训练。最近,甚至更复杂的任务,如文本到图像合成,也成功部署了仅解码器架构,展示了其多功能性和对不同应用的适应性。

变压器的进步

使用其他新颖技术解决生成任务的变压器机制。这种演变导致了处理文本和图像生成的不同方法。在本节中,我们将探讨一些这些创新模型及其在推进GAI方面的独特方法。

DALL-E的编码器-解码器图像生成

由Ramesh等人于2021年提出,DALL-E采用编码器-解码器框架以促进文本到图像的生成。此模型包含两个主要组件:

  • 文本编码器:应用transformer的编码器,处理纯文本以推导出语义嵌入,作为图像解码器的上下文。

  • 图像解码器:应用transformer的解码器以自回归方式生成图像,根据文本嵌入和先前预测的像素预测每个像素。

通过在图像-字幕数据集上训练,DALL-E改进了从文本到详细图像渲染的过渡。这种设置强调了专用编码器和解码器模块在条件图像生成中的能力。

使用CLIP进行仅编码器的图像字幕

CLIP由Radford等人于2021年提出,采用仅编码器的方法进行图像-文本任务。关键组件包括视觉编码器和文本编码器。

视觉编码器和文本编码器分别处理图像和候选字幕,根据编码表示确定匹配的字幕。

在广泛的电影-文本数据集上进行预训练使CLIP能够建立共享嵌入空间,从而促进基于检索的字幕的快速推理。

使用扩展的Transformer提高图像保真度(DALL-E 2)

Ramesh等人于2022年将DALL-E扩展到DALL-E 2,展示了提高视觉质量的技术:

  • 扩展的解码器:通过将解码器扩展到35亿个参数,并在采样期间应用无分类器指导,显著提高了复杂图像分布(如人脸)中的视觉质量。

  • 用于高分辨率图像的分层解码(GLIDE):由Nichol等人于2021年提出,GLIDE采用分层生成策略。

  • 由粗到细的方法:这包括初始的低分辨率图像预测,然后通过上采样和细化进行渐进式细化,捕捉全局结构和高频纹理。

使用GPT-4进行多模态图像生成

OpenAI开发的GPT-4是一个基于Transformer架构的强大多模态模型。GPT-4展示了在无需持续训练或微调的情况下进行条件图像生成的能力:

  • 预训练和微调:GPT-4的巨大规模及其在多样化数据集上的预训练,使其能够对文本和视觉数据之间的关系有稳健的理解。

  • 多模态生成:GPT-4可以根据文本描述生成图像。该模型使用深度神经网络将文本的语义意义编码为视觉表示。给定一个文本提示,GPT-4通过预测与提供的文本一致的视觉内容来生成图像。这涉及到通过连续的神经网络层处理高维文本嵌入,以生成相应的视觉表示。

使用预训练的多模态模型消除了为图像输入单独编码器模块的需求,从而促进了图像生成任务的快速适应。这种方法强调了Transformer架构在生成任务中的灵活性和强大功能,提供了一种将文本转换为高质量图像的简化方法。

与GANs相比,Transformer架构在可控图像生成方面提供了许多好处。它们的自回归特性确保了对图像构建的精确控制,同时允许您适应不同的计算需求和多样化的下游应用。然而,Transformer也引入了这一领域的新挑战。

基于Transformer的方法的局限性和挑战

一些基于早期Transformer的方法在采样速度和保真度方面比GANs慢,同时保持对图像中特定属性或特征的精确控制,在生成或操作图像时仍然具有挑战性。此外,训练能够克服这些挑战的大规模Transformer需要大量的计算资源。尽管如此,当前的多模态结果展示了一个快速发展和充满希望的局面。

我们还必须记住,在技术挑战的同时,还存在更广泛的社会技术影响和考虑因素。

生成模型中的偏差和伦理问题

生成模型(如GANs、diffusers和transformers)的显著进步需要认真思考潜在的偏差和伦理影响。

我们需要保持警惕,防止强化反映训练数据偏差的偏见和刻板印象。例如,在训练数据中过度代表特定人群的扩散模型可能会在其输出中传播这些偏差。类似地,在训练过程中接触到有毒或暴力内容的语言模型可能会生成类似的内容。

基于提示的生成指令性质,不幸的是,如果部署不当,也会打开滥用的大门。Transformer可能会促进模仿、错误信息和欺骗性内容的创建。GANs等图像合成模型可能被用于生成非同意的深度伪造或人工媒体。

此外,超逼真输出的可能性引发了关于同意、隐私、身份和版权的伦理困境。能够创建令人信服的虚构面孔或声音,使得真实与合成之间的区别变得复杂,需要仔细检查训练数据来源和生成能力。

此外,随着这些技术的普及,必须考虑其社会影响。随着真实和AI生成内容之间的区别变得越来越模糊,制定明确的政策将至关重要。坚持诚信、归属和同意的原则仍然至关重要。

尽管存在这些风险,生成模型潜在的好处是巨大的。随着技术的演变,积极应对偏见、倡导透明度、审计数据和模型以及实施保障措施变得越来越关键。最终,确保公平性、责任和道德实践的责任落在所有开发者和从业者身上。

应用GAI模型——使用GANs、diffusers和transformers进行图像生成

在本节实践环节中,我们将通过实际操作来巩固本章讨论的概念。你将获得第一手的经验,并深入探究生成模型的实际实现,特别是GANs、扩散模型和transformers。

提供的Python代码将指导你完成这个过程。操作并观察代码的实际运行将帮助你理解这些模型的复杂运作和潜在应用。这项练习将提供关于模型在生成艺术作品、从提示中生成以及合成超逼真图像等任务中的能力见解。

我们将利用功能强大的PyTorch库,这是机器学习从业者中流行的选择,以促进我们的操作。PyTorch提供了一组强大且动态的工具,用于定义和计算梯度,这对于训练这些模型至关重要。

此外,我们还将使用diffusers库。这是一个专门提供实现扩散模型功能的库。这个库使我们能够直接从我们的工作空间中重现最先进的扩散模型。它以前所未有的简单性支撑了去噪扩散概率模型创建、训练和使用的各个方面,同时不牺牲模型的复杂性。

通过这次实践课程,我们将探讨如何操作和集成这些库,并使用Python编程语言实现和操作GANs、diffusers和transformers。这种实践经验将补充我们在本章中获得的理论知识,使我们能够在现实世界中看到这些模型的实际应用。

到本节结束时,你不仅将对这些生成模型有一个概念性的理解,还将了解它们是如何实现、训练以及用于数据科学和机器学习中的多个创新应用的。你将更深入地理解这些模型的工作原理,并亲身体验到实现它们的过程。

使用Jupyter Notebook和Google Colab进行工作

Jupyter笔记本支持实时代码执行、可视化以及解释性文本,非常适合原型设计和数据分析。相反,Google Colab是Jupyter笔记本的云端版本,专为机器学习原型设计。它提供免费的GPU资源,并与Google Drive集成以实现文件存储和共享。我们将利用Colab作为后续的原型环境。

稳定扩散transformer

我们从预训练的稳定扩散模型开始,这是一个由 CompVis、Stability AI 和 LAION 的研究人员和工程师创建的文本到图像的潜在扩散模型(Patil 等人,2022 年)。扩散过程用于从复杂的高维分布中抽取样本,当它与文本嵌入交互时,它创建了一个强大的条件图像合成模型。

在这个上下文中,“稳定”一词指的是在训练过程中,模型保持某些属性以稳定学习过程的事实。稳定的扩散模型提供了丰富的潜力,可以从给定的数据分布中创建全新的样本,基于文本提示。

再次,为了我们的实际示例,我们将使用 Google Colab 来减轻许多初始设置。Colab 还提供了开始实验所需的全部计算资源。我们首先安装一些库,然后通过三个简单的函数,我们将使用稳定的扩散方法的成熟开源实现来构建一个最小的 StableDiffusionPipeline

首先,让我们导航到我们的预配置的 Python 环境,Google Colab,并安装 diffusers 开源库,它将为我们实验提供大部分关键的基础组件。

在第一个单元格中,我们使用以下 bash 命令安装所有依赖项。注意行首的感叹号,它告诉我们的环境深入到底层进程并安装所需的软件包:

!pip install pytorch-fid torch diffusers clip transformers accelerate

接下来,我们导入我们刚刚安装的库,使它们可供我们的 Python 程序使用:

from typing import List
import torch
import matplotlib.pyplot as plt
from diffusers import StableDiffusionPipeline, DDPMScheduler

现在,我们已经准备好我们的三个函数,它们将执行三个任务——加载预训练模型、根据提示生成图像以及渲染图像:

def load_model(model_id: str) -> StableDiffusionPipeline:
    """Load model with provided model_id."""
    return StableDiffusionPipeline.from_pretrained(
        model_id, 
        torch_dtype=torch.float16, 
        revision="fp16", 
        use_auth_token=False
    ).to("cuda")
def generate_images(
    pipe: StableDiffusionPipeline, 
    prompts: List[str]
) -> torch.Tensor:
    """Generate images based on provided prompts."""
    with torch.autocast("cuda"):
        images = pipe(prompts).images
    return images
def render_images(images: torch.Tensor):
    """Plot the generated images."""
    plt.figure(figsize=(10, 5))
    for i, img in enumerate(images):
        plt.subplot(1, 2, i + 1)
        plt.imshow(img)
        plt.axis("off")
    plt.show()

总结来说,load_model 函数将使用 model_id 识别的机器学习模型加载到 GPU 上以实现更快的处理。generate_images 函数接受这个模型和一系列提示来创建我们的图像。在这个函数中,你会注意到 torch.autocast("cuda"),这是一个特殊的命令,允许 PyTorch(我们的底层机器学习库)在保持准确性的同时更快地执行操作。最后,render_images 函数以简单的网格格式显示这些图像,利用 matplotlib 可视化库来渲染我们的输出。

定义了我们的函数后,我们选择我们的模型版本,定义我们的流水线,并执行我们的图像生成过程:

# Execution
model_id = "CompVis/stable-diffusion-v1-4"
prompts = [
    "A hyper-realistic photo of a friendly lion",
    "A stylized oil painting of a NYC Brownstone"
]
pipe = load_model(model_id)
images = generate_images(pipe, prompts)
render_images(images)

图 2.1 的输出是一个生动的例子,展示了我们通常期望从人类艺术中得到的想象力和创造力,这些完全是由扩散过程生成的。但是,我们如何衡量模型是否忠实于提供的文本呢?

图 2.1:提示“一张超逼真的友好狮子照片”(左)和“一张纽约布朗石风格化的油画”(右)的输出

图 2.1:对提示“一张逼真的友好狮子照片”(左)和“一张纽约布朗石风格化的油画”(右)的输出

下一步是评估我们生成的图像与提示之间的质量和相关性。这正是 CLIP 发挥作用的地方。CLIP 通过分析它们的语义相似性来设计测量文本和图像之间的对齐程度,为我们提供了对合成图像与提示之间忠实度的真正定量度量。

使用 CLIP 模型评分

CLIP 通过学习在共享空间中将相似图像和文本放置在一起来训练理解文本和图像之间的关系。在评估生成的图像时,CLIP 会检查图像与提供的文本描述的匹配程度。分数越高表示匹配度越好,意味着图像准确地代表了文本。相反,分数较低则表明图像与文本存在偏差,表明质量或对提示的忠实度较低,提供了对生成的图像如何遵守预期描述的定量度量。

再次,我们将导入必要的库:

from typing import List, Tuple
from PIL import Image
import requests
from transformers import CLIPProcessor, CLIPModel
import torch

我们首先加载 CLIP 模型、处理器和必要的参数:

# Constants
CLIP_REPO = "openai/clip-vit-base-patch32"
def load_model_and_processor(
    model_name: str
) -> Tuple[CLIPModel, CLIPProcessor]:
    """
    Loads the CLIP model and processor.
    """
    model = CLIPModel.from_pretrained(model_name)
    processor = CLIPProcessor.from_pretrained(model_name)
    return model, processor

接下来,我们定义一个处理函数来调整文本提示和图像,确保它们以正确的格式适用于 CLIP 推理:

def process_inputs(
    processor: CLIPProcessor, prompts: List[str],
    images: List[Image.Image]) -> dict:
"""
Processes the inputs using the CLIP processor.
"""
    return processor(text=prompts, images=images,
        return_tensors="pt", padding=True)

在此步骤中,我们通过将图像和文本提示输入到 CLIP 模型中来启动评估过程。这是在多个设备上并行进行的,以优化性能。然后,模型为每个图像-文本对计算相似度分数,称为 logits。这些分数表示每个图像与文本提示的对应程度。为了更直观地解释这些分数,我们将它们转换为概率,这表示图像与任何给定提示对齐的可能性:

def get_probabilities(
    model: CLIPModel, inputs: dict) -> torch.Tensor:
"""
Computes the probabilities using the CLIP model.
"""
    outputs = model(**inputs)
    logits = outputs.logits_per_image
    # Define temperature - higher temperature will make the distribution more uniform.
    T = 10
    # Apply temperature to the logits
    temp_adjusted_logits = logits / T
    probs = torch.nn.functional.softmax(
        temp_adjusted_logits, dim=1)
    return probs

最后,我们显示图像及其分数,直观地表示每个图像如何遵守提供的提示:

def display_images_with_scores(
    images: List[Image.Image], scores: torch.Tensor) -> None:
"""
Displays the images alongside their scores.
"""
    # Set print options for readability
    torch.set_printoptions(precision=2, sci_mode=False)
    for i, image in enumerate(images):
        print(f"Image {i + 1}:")
        display(image)
        print(f"Scores: {scores[i, :]}")
        print()

详细说明完毕后,让我们按照以下步骤执行管道:

# Load CLIP model
model, processor = load_model_and_processor(CLIP_REPO)
# Process image and text inputs together
inputs = process_inputs(processor, prompts, images)
# Extract the probabilities
probs = get_probabilities(model, inputs)
# Display each image with corresponding scores
display_images_with_scores(images, probs)

现在我们有了基于 CLIP 模型的每个合成图像的分数,该模型将图像和文本数据解释为一种结合的数学表示(或几何空间),并可以测量它们的相似性。

图 2.2:CLIP 分数

图 2.2:CLIP 分数

对于我们的“友好狮子”,我们对每个提示计算了 83% 和 17% 的分数,我们可以将其解释为有 83% 的可能性图像与第一个提示相符。

在实际场景中,此指标可以应用于各个领域:

  • 内容审核:通过将图像与一组预定义的描述性提示进行比较,自动审核或标记不适当的内容

  • 图像检索:通过将文本查询与庞大的图像数据库匹配,从而缩小搜索范围,仅针对最相关的视觉内容进行搜索

  • 图像标题生成:通过识别最相关的描述性提示来协助生成图像的准确标题

  • 广告:根据网页上图像的内容定制广告以提高用户参与度

  • 无障碍性:通过为视障人士提供图像的准确描述来增强无障碍功能

这种评估方法不仅加快了原本需要人工检查的过程,而且适用于许多可以从对视觉数据更深入理解和上下文分析中受益的应用。我们将在第4章中回顾CLIP评估,在那里我们将模拟一个真实世界场景,以确定一组产品图像自动生成标题的质量和适宜性。

摘要

本章探讨了领先生成式人工智能(GAI)技术的理论基础和实际应用,包括生成对抗网络(GANs)、扩散模型和转换器。我们考察了它们的独特优势,包括GANs合成高度逼真图像的能力、扩散模型优雅的图像生成过程以及转换器卓越的语言生成能力。

使用基于云的Python环境,我们实现了这些模型以生成引人入胜的图像,并使用CLIP评估了它们的输出质量。我们分析了渐进式增长和分类器引导等技术如何随着时间的推移增强输出保真度。我们还考虑了社会影响,敦促开发者通过透明度和道德实践来应对潜在的伤害。

生成式方法释放了显著的创意潜力,但随着能力的提升,深思熟虑的监管至关重要。我们可以通过扎根于核心方法、审视它们的局限性并考虑下游用途,来引导这些技术走向广泛有益的结果。未来的道路将需要持续的研究和道德反思,以释放人工智能的创意潜力,同时减轻风险。

参考文献

本参考文献部分作为本书中引用的资源的存储库;您可以探索这些资源,以进一步加深对主题的理解和知识:

  • Kenfack, P. J., Arapov, D. D., Hussain, R., Ahsan Kazmi, S. M., & Khan, A. (2021). 关于生成对抗网络(GANs)的公平性. Arxiv.org

  • Goodfellow, I., Pouget-Abadie, J., Mirza, M., Xu, B., Warde-Farley, D., Ozair, S., Courville, A., & Bengio, Y. (2014). 生成对抗网络:神经信息处理系统进展,第27卷。

  • Nichol, A., Dhariwal, P., Ramesh, A., Shyam, P., Mishkin, P., McGrew, B., Sutskever, I., & Chen, M. (2021). GLIDE:使用文本引导的扩散模型实现逼真图像生成和编辑. arXiv预印本 arXiv:2112.10741。

  • Radford, A., Kim, J. W., Hallacy, C., Ramesh, A., Goh, G., Agarwal, S., Sastry, G., Askell, A., Mishkin, P., Clark, J., Krueger, G., & Sutskever, I. (2021). 从自然语言监督中学习可迁移的视觉模型. ArXiv. /abs/2103.00020.

  • Ramesh, A., Pavlov, M., Goh, G., Gray, S., Voss, C., Radford, A., Chen, M., & Sutskever, I. (2022). 分层文本条件图像生成与clip潜在. arXiv预印本 arXiv:2204.06125.

  • Ramesh, A., Pavlov, M., Goh, G., Gray, S., Voss, C., Radford, A., Chen, M., & Sutskever, I. (2021). 零样本文本到图像生成. 国际机器学习会议论文集 (pp. 8821–8831). PMLR.

  • Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, L., & Polosukhin, I. (2017). 注意力即所需. 神经信息处理系统进展, 30.

  • Arjovsky, M., Chintala, S. & Bottou, L. (2017). Wasserstein GAN. 在第31届国际神经网络信息处理系统会议 (NIPS) 上.

  • Brock, A., Donahue, J., & Simonyan, K. (2018). BigGANs: 大规模GAN训练以实现高保真自然图像合成. https://arxiv.org/abs/1809.11096.

  • Karras, T., Aila, T., Laine, S., & Lehtinen, J. (2017). GANs的渐进式增长以实现改进的质量、稳定性和多样性. https://arxiv.org/abs/1710.10196.

  • Mirza, M., & Osindero, S. (2014). 条件生成对抗网络. https://arxiv.org/abs/1411.1784.

  • Radford, A., Metz, L., & Chintala, S. (2015). 无监督表示学习与深度卷积生成对抗网络. 第三届学习表示国际会议.

  • Zhu, J.-Y., Park, T., Isola, P., & Efros, A. A. (2017). 使用循环一致对抗网络进行无配对图像到图像翻译. IEEE国际计算机视觉会议论文集 (ICCV).

  • Ho, J., & Salimans, T. (2022). 无分类器扩散引导. 神经信息处理系统进展, 34.

  • Ho, J., Salimans, T., Gritsenko, A. A., Chan, W., Norouzi, M., & Fleet, D. J. (2022). 视频扩散模型. arXiv预印本 arXiv:2205.10477.

  • Ho, J., Jain, A., & Abbeel, P. (2020). 去噪扩散概率模型. 神经信息处理系统进展, 33, 6840–6851.

  • Luo, S., & Hu, W. (2021). 用于3D点云生成的扩散概率模型. IEEE/CVF计算机视觉与模式识别会议论文集, 2837–2845.

  • Rombach, R., Blattmann, A., Lorenz, D., Esser, P., & Ommer, B. (2021). 使用潜在扩散模型进行高分辨率图像合成. IEEE/CVF计算机视觉与模式识别会议论文集, 10684–10695.

  • Salimans, T., Karpathy, A., Chen, X., & Kingma, D. P. (2017). PixelCNN++: 通过离散逻辑混合似然和其他修改改进PixelCNN. arXiv预印本 arXiv:1701.05517.

  • Song, Y., Meng, C., & Ermon, S. (2021). 去噪扩散隐式模型. arXiv预印本 arXiv:2010.02502.

  • Song, Y., & Ermon, S. (2021). 基于分数的生成模型训练改进技术. 神经信息处理系统进展, 33, 12438–12448.

  • Sohl-Dickstein, J., Weiss, E. A., Maheswaranathan, N., & Ganguli, S. (2015). 使用非平衡热力学进行深度无监督学习. arXiv预印本 arXiv:1503.03585.

  • Ho, J., Jain, A., & Abbeel, P. (2020). 去噪扩散概率模型. 神经信息处理系统进展, 33, 6840–6851.

  • Ramesh, A., Pavlov, M., Goh, G., Gray, S., Voss, C., Radford, A., ... & Sutskever, I. (2022). 零样本文本到图像生成. 国际机器学习会议, 8821-8831.

  • Brown, T., Mann, B., Ryder, N., Subbiah, M., Kaplan, J. D., Dhariwal, P., ... & Amodei, D. (2020). 语言模型是少样本学习者. 神经信息处理系统进展, 33, 1877–1901.

  • Patil, S., Cuenca, P., Lambert, N., & von Platen, P. (2022). 使用扩散器进行稳定扩散. Hugging Face博客. https://huggingface.co/blog/stable_diffusion.

  • Boris Dayma, Suraj Patil, Pedro Cuenca, Khalid Saifullah, Tanishq Abraham, Phúc Lê, Luke, Ritobrata Ghosh. (2022, June 4). DALL-E Mini 解释. W&B; Weights & Biases, Inc. https://wandb.ai/dalle-mini/dalle-mini/reports/DALL-E-Mini-Explained-with-Demo--Vmlldzo4NjIxODA.

第三章:跟踪自然语言处理的基础及其对Transformer的影响

Transformer架构是支撑大多数现代生成语言模型的关键进步。自2017年引入以来,它已成为自然语言处理(NLP)的基础部分,使得如生成预训练Transformer 4(GPT-4)和Claude等模型在文本生成能力上取得了显著进步。对Transformer架构的深入理解对于掌握现代大型语言模型(LLMs)的机制至关重要。

在上一章中,我们探讨了生成建模技术,包括生成对抗网络(GANs)、扩散模型和自回归(AR)Transformer。我们讨论了如何利用Transformer从文本生成图像。然而,Transformer不仅仅是在众多生成方法中的一种;它是几乎所有最先进生成语言模型的基础。

在本章中,我们将介绍导致Transformer架构出现的NLP演变。我们无法涵盖所有关键的前进步骤,但我们将尝试涵盖主要里程碑,从早期的语言分析技术和统计语言建模开始,接着是循环神经网络(RNNs)和卷积神经网络(CNNs)的进步,这些进步突出了深度学习(DL)在NLP中的潜力。我们的主要目标是介绍Transformer——其在深度学习中的基础、其自注意力架构以及其快速演变,这些演变导致了LLMs以及我们称之为生成****人工智能(GenAI)的现象。

理解Transformer架构的起源和机制对于认识其开创性影响至关重要。Transformer引入的原则和建模能力被所有基于此框架的现代语言模型所继承。我们将通过历史背景和动手实践来建立对Transformer的直觉,因为这种基础理解对于理解GenAI的未来至关重要。

NLP的早期方法

神经网络NNs)在语言处理中得到广泛应用之前,自然语言处理(NLP)主要基于计数单词的方法。其中两种特别显著的技术是计数向量词频-逆文档频率TF-IDF)。本质上,计数向量统计了每个单词在文档中出现的频率。在此基础上,Dadgar等人于2016年将TF-IDF算法(历史上用于信息检索)应用于文本分类。这种方法根据单词在单个文档中的重要性相对于整个文档集合中出现的频率来分配权重。这些基于计数的方 法在搜索和分类等任务中取得了成功。然而,它们存在一个关键限制,即无法捕捉单词之间的语义关系,这意味着它们无法解释单词在上下文中的细微含义。这一挑战为探索神经网络铺平了道路,提供了一种更深入、更细微的方式来理解和表示文本。

神经语言模型的兴起

2003年,蒙特利尔大学的Yoshua Bengio团队引入了神经网络语言模型NNLM),这是一种新颖的语言技术方法。NNLM旨在根据先前单词预测序列中的下一个单词,使用特定类型的神经网络NN)。其设计突出显示了学习词嵌入的隐藏层,这些嵌入是紧凑的向量表示,能够捕捉单词的核心语义含义。这一点在基于计数的方 法中是缺失的。然而,NNLM在解释较长序列和处理大型词汇方面的能力仍然有限。尽管存在这些限制,NNLM仍然激发了在语言建模中广泛探索神经网络的热情。

神经网络语言模型(NNLM)的引入突显了神经网络在语言处理中的潜力,尤其是在使用词嵌入方面。然而,它在处理长序列和大型词汇方面的局限性表明了进一步研究的必要性。

分布式表示

随着神经网络语言模型(NNLM)的诞生,自然语言处理(NLP)研究被推动着向构建高质量的词向量表示方向发展。这些表示最初可以从大量的未标记文本数据中学习,后来可以应用于各种任务的下游模型。这一时期见证了两种突出方法的兴起:Word2Vec(由 Mikolov 等人于 2013 年引入)和 全局向量GloVe,由 Pennington 等人于 2014 年引入)。这些方法将 分布式表示 应用于构建高质量的词向量表示。分布式表示将诸如词语之类的项目不是作为唯一的标识符,而是作为连续值或向量的集合。在这些向量中,每个值对应于项目的一个特定特征或特性。与传统的表示不同,其中每个项目都有一个唯一的符号,分布式表示允许这些项目与其他项目共享特征,从而能够更智能地捕捉数据中的潜在模式。

让我们进一步阐明这个概念。假设我们根据两个特征来表示词语:FormalityPositivity。我们可能会有以下向量:

Formal: [1, 0]

Happy: [0, 1]

Cheerful: [0, 1]

在这个例子中,向量中的每个元素对应于这些特征中的一个。在 Formal 的向量中,位于 Formality 下的 1 元素表明该词是正式的,而位于 Positivity 下的 0 元素表明在积极方面是中性的。同样,对于 HappyCheerful,位于 Positivity 下的 1 元素表明这些词具有积极的含义。这样,分布式表示通过向量捕捉了词的精髓,允许不同词之间共享特征,从而理解数据中的潜在模式。

Word2Vec 采用了一种相对直接的方法,其中神经网络被用来预测数据集中每个目标词周围的词语。通过这个过程,神经网络为每个目标词确定了值或“权重”。这些权重形成了一个向量,代表每个词在 连续向量空间 中的一个向量——这是一个数学空间,其中每个点代表向量可能取的某个可能值。在自然语言处理(NLP)的背景下,这个空间中的每个维度对应一个特征,而一个词在这个空间中的位置捕捉了它与其他词的语义或语言关系。

这些向量形成了一个 基于特征的表示——一种表示类型,其中每个维度代表对词的意义有贡献的不同特征。与将每个词表示为唯一符号的符号表示不同,基于特征的表示从共享特征的角度捕捉词的语义本质。

另一方面,GloVe采用了一种不同的方法。它分析全局共现统计——一个计数,表示单词在一个大型文本语料库中一起出现的频率。GloVe通过在整个语料库中分析这些计数来学习向量表示,这些表示捕捉了单词之间的关系。这种方法还导致单词在连续向量空间中的分布式表示,捕捉语义相似性——衡量两个单词在意义上相似程度的度量。在连续向量空间中,我们可以将语义相似性视为表示单词的向量之间的简单几何邻近性。

为了进一步说明,假设我们有一个包含以下句子的微小文本语料库:

“咖啡很热。”

“冰淇淋很冷。”

从这个语料库中,GloVe会注意到“咖啡”与“”共现,“冰淇淋”与“”共现。通过其优化过程,它会试图以反映这些关系的方式为这些词创建向量。在这个过于简化的例子中,GloVe可能会产生如下向量:

咖啡: [1, 0]

热: [0.9, 0]

冰淇淋: [0, 1]

冷: [0, 0.9]

在这个空间中,“咖啡”和“”的向量之间的接近程度(以及类似地,“冰淇淋”和“”)反映了语料库中观察到的共现关系。在向量空间中,“咖啡”和“”之间的向量差异可能与“冰淇淋”和“”之间的向量差异相似,以几何方式捕捉向量空间内对比温度关系。

Word2Vec和GloVe都擅长封装关于单词的相关语义信息,以表示一种有效的编码——一种紧凑地表示信息的方式,同时捕捉完成任务所需的基本特征,同时减少数据的维度和复杂性。

这些创建有意义的向量表示的方法为在自然语言处理(NLP)中采用迁移学习迈出了第一步。这些向量提供了一个共享的语义基础,促进了在不同任务之间学习关系的迁移。

迁移学习

GloVe和其他推导分布式表示的方法为自然语言处理中的迁移学习铺平了道路。通过创建包含语义关系的丰富向量表示,这些方法提供了对文本的基础理解。这些向量作为共享的知识基础,可以应用于不同的任务。当模型最初在一个任务上训练后,用于另一个任务时,预先学习的向量表示有助于保留语义理解,从而减少新任务所需的数据或训练。这种转移已获得的知识的做法已成为高效解决一系列自然语言处理任务的基础。

考虑一个训练用于理解电影评论中情感(正面或负面)的模型。通过训练,这个模型已经学会了词语的分布式表示,捕捉了与情感相关的细微差别。现在,假设有一个新的任务:理解产品评论中的情感。而不是从头开始训练一个新的模型,迁移学习允许我们使用电影评论任务中的分布式表示来启动产品评论任务的训练。这可能导致更快的训练和更好的性能,尤其是在产品评论任务数据有限的情况下。

迁移学习的有效性,得益于GloVe等方法的分布式表示,凸显了利用现有知识进行新任务的潜力。它是神经网络在NLP中集成的先导,突出了在任务间利用学习到的表示的优势。神经网络在NLP中的兴起带来了能够学习更丰富表示的模型,进一步放大了迁移学习的影响和范围。

NLP 中神经网络(NNs)的兴起

神经网络在NLP中的兴起标志着该领域在理解和处理语言能力方面的重大转变。在Word2Vec、GloVe等方法的基石上,以及迁移学习实践的基础上,神经网络引入了更高层次的抽象和学习能力。与以往依赖手工特征的方法不同,神经网络能够自动从数据中学习复杂的模式和关系。这种从数据中学习的能力推动了NLP进入一个新时代,模型能够在众多与语言相关的任务上实现前所未有的性能水平。卷积神经网络(CNNs)和循环神经网络(RNNs)的兴起,以及革命性的transformer架构的出现,展示了神经网络在解决复杂NLP挑战方面的非凡适应性和有效性。这一转变不仅加速了创新的步伐,也扩展了在计算上理解人类语言所能实现的范围。

使用RNN进行语言建模

尽管这些分布式词向量在编码局部语义关系方面表现出色,但建模长距离依赖需要更复杂的网络架构。这导致了RNNs的使用。RNNs(最初由Elman在1990年提出)是一种NN架构,通过迭代序列中的每个元素并保持一个动态内部状态来处理数据序列,该状态捕获了先前元素的信息。与处理每个输入独立的传统前馈网络(FNNs)不同,RNNs引入了迭代,允许信息从一个序列步骤传递到下一个步骤,使它们能够捕捉数据中的时间依赖性。NNs中的迭代处理和动态更新使它们能够学习和表示文本中的关系。这些网络可以捕捉句子或甚至整个文档中的上下文联系和相互依赖性。

然而,标准RNNs在处理长序列时存在技术限制。这导致了长短期记忆(LSTM)网络的发展。LSTMs最早由Hochreiter和Schmidhuber在1997年提出。它们是一类特殊的RNNs,旨在解决梯度消失问题,即随着序列变长,网络无法从序列的早期部分学习。LSTMs应用了一种独特的门控架构来控制网络内部的信息流动,使它们能够在不遭受梯度消失问题的前提下,在长序列中保持和访问信息。

长短期记忆”这个名字指的是网络在处理短序列和长序列数据时跟踪信息的能力:

  • 短期:LSTMs能够记住最近的信息,这对于理解当前上下文很有用。例如,在语言建模中,知道最后几个单词对于预测下一个单词至关重要。考虑一个短语,例如,“The cat, which already ate a lot, was not hungry.” 当LSTM处理文本时,当它到达单词“not”时,最近的信息即猫“eat a lot”对于准确预测下一个单词“hungry”至关重要。

  • 长期:与标准RNNs不同,LSTMs还能够保留序列中很多步骤之前的信息,这对于处理长距离依赖特别有用,因为在句子中较早的信息可能对理解序列中较晚的部分的单词很重要。在同一个短语中,关于“The cat”是句子主语的信息是在一开始就引入的。这些信息在稍后理解“was not hungry”时至关重要,因为它处理句子的后半部分。

LSTM中的M记忆通过一种独特的架构来维持,该架构采用三个门控机制——输入门、输出门和遗忘门。这些门控机制控制网络中的信息流,决定在每个序列步骤中应该保留、丢弃或使用哪些信息,使LSTMs能够在长序列中维持和访问信息。实际上,这些门控机制和网络状态允许LTSMs在时间步长之间携带“记忆”,确保在整个序列处理过程中保留了有价值的信息。

最终,LSTMs在许多语言建模和文本分类基准测试中获得了最先进的结果。由于它们能够捕捉短距离和长距离的上下文关系,LSTMs成为了NLP任务的占主导地位的NN架构。

LSTM的成功展示了神经网络架构在捕捉语言中固有的复杂关系方面的潜力,极大地推动了NLP领域的发展。然而,对更高效和有效模型的持续追求使社区转向探索其他NN架构。

CNN的兴起

大约在2014年,NLP领域见证了CNN在处理NLP任务时受欢迎程度的上升,这一显著的转变是由Yoon Kim引领的。CNN(最初由LeCun等人提出用于图像识别)基于卷积层,通过移动过滤器(或核)在输入数据上扫描,在每个位置计算过滤器权重与输入数据的点积。在NLP中,这些层在局部n-gram窗口(连续的n个单词序列)上工作,以识别模式或特征,如文本中的特定单词或字符序列。通过在局部n-gram窗口上使用卷积层,CNN扫描和分析数据以检测初始模式或特征。随后,池化层被用来降低数据的维度,这有助于减少计算复杂度并关注卷积层识别的最显著特征。

通过结合卷积和池化层,CNN可以提取层次特征。这些特征通过结合更简单、低级的特征来形成更复杂、高级的特征,从而代表不同抽象层次的信息。在NLP中,这个过程可能从检测初始层中的基本模式开始,例如常见的词对或短语,然后发展到识别更高层次的概念,如语义关系。

为了比较,我们再次考虑一个场景,其中使用CNN来分析和分类客户评论为正面、负面或中性情感:

  • 低级特征(初始层):CNN可能在初始层识别基本模式,例如常见的词对或短语。例如,它可能识别到短语如“优质服务”、“糟糕的经历”或“不高兴”。

  • 中间层特征(中间层):随着数据在网络中传递,中间层可能会开始识别更复杂的模式,例如否定(“不好”)或对比(“好但贵”)。

  • 高级特征(后续层):CNNs(卷积神经网络)可以在后续层识别抽象概念,例如整体情感。例如,它可能从“优秀的服务”或“喜欢氛围”之类的短语中推断出积极情感,而从“最糟糕的经历”或“糟糕的食物”之类的短语中推断出消极情感。

以这种方式,CNNs(卷积神经网络)本质上学习了文本的高级抽象表示。尽管它们缺乏RNNs(循环神经网络)的序列处理特性,但它们由于固有的并行性或同时处理数据多个部分的能力,提供了计算上的优势。与RNNs不同,RNNs是迭代处理序列,并且需要在进行下一步之前完成前一步,而CNNs可以并行处理输入数据的各个部分,从而显著加快训练时间。

虽然 CNNs(卷积神经网络)效率高,但它们的卷积操作存在局限性,该操作仅处理来自较小或附近区域的局部数据,从而错过了整个输入数据中更大部分的关系,这被称为全局信息。这导致了结合自注意力与卷积的注意力增强卷积网络的出现,以解决这一局限性。自注意力最初用于序列和生成建模,后来被用于视觉任务,如图像分类,使网络能够处理和捕获整个输入数据中的关系。然而,结合卷积和自注意力的注意力增强产生了最佳结果。这种方法保留了CNNs的计算效率并捕获了全局信息,标志着图像分类和目标检测任务中的进步。我们将在后面详细讨论自注意力,因为它成为了transformer的关键组成部分。

CNNs(卷积神经网络)能够同时处理数据的多个部分,这标志着计算效率的显著提升,为NLP(自然语言处理)中NN(神经网络)架构的进一步创新铺平了道路。随着该领域的发展,随着注意力增强型NNs的出现,发生了一个关键的转变,引入了模型处理序列数据的新范式。

高级语言模型中Transformer的出现

2017年,受CNN的能力和注意力机制的创新应用启发,Vaswani等人发表了开创性论文《Attention is All You Need》,在其中引入了变压器架构。原始的变压器应用了多种新颖的方法,特别强调了注意力的重要性。它采用了自注意力机制,允许输入序列中的每个元素专注于序列的不同部分,以结构化的方式捕捉依赖关系,无论它们在序列中的位置如何。在“自注意力”中的“自”指的是注意力机制是如何应用于输入序列本身的,意味着序列中的每个元素都会与其他每个元素进行比较,以确定其注意力分数。

要真正理解变压器架构的工作原理,我们可以描述其架构中的组件如何处理特定任务。假设我们需要我们的变压器将英语句子“Hello, how are you?”翻译成法语:“Bonjour, comment ça va?”。让我们一步一步地走过这个过程,检查和阐明变压器可能如何完成这项任务。目前,我们将详细描述每个步骤,稍后我们将使用Python实现完整的架构。

变压器架构的组件

在深入探讨如何使变压器模型完成我们的翻译任务之前,我们需要了解其中涉及的步骤。完整的架构相当密集,因此我们将将其分解为小、逻辑性强且易于消化的组件。

首先,我们讨论变压器模型架构设计的两个核心组件:编码器栈和解码器栈。我们还将解释数据如何在这些层栈中流动,包括标记的概念,以及如何使用诸如自注意力机制和前馈神经网络(FFNs)等关键技术来捕捉和细化标记之间的关系。

然后,我们过渡到变压器模型的训练过程。在这里,我们回顾了批处理、掩码、训练循环、数据准备、优化器选择以及提高性能的策略等基本概念。我们将解释变压器如何使用损失函数来优化性能,这对于塑造模型学习翻译的方式至关重要。

在训练过程之后,我们讨论模型推理,即我们的训练模型如何生成翻译。本节指出在翻译过程中各个模型组件的操作顺序,并强调每个步骤的重要性。

正如所讨论的,变压器核心的两个关键组件通常被称为编码器栈和解码器栈。

编码器栈和解码器栈

在Transformer模型的背景下,堆叠指的是的层次排列。在这个上下文中,每一层实际上是一个类似于我们在经典深度学习模型中遇到的NN层。虽然层是模型中发生特定计算操作的级别,但堆叠则指的是多个这样的层按顺序排列。

编码器堆叠

考虑我们的示例句子“Hello, how are you?”。我们首先将其转换为标记。每个标记通常代表一个单词。在我们的示例句子中,标记化会将它分解成单独的标记,结果如下:

[“Hello”,“,”,“how”,“are”,“``you”,“?”]

在这里,每个单词或标点符号代表一个不同的标记。然后,这些标记被转换成数值表示,也称为嵌入。这些嵌入向量捕捉了单词的语义意义和上下文,使模型能够有效地理解和处理输入数据。通过这一系列层间的转换,嵌入有助于捕捉原始英语输入句子中的复杂关系和上下文。

这个堆叠由多个层组成,其中每一层对其输入数据(我们将在稍后详细描述)应用自注意力和FFN计算。嵌入通过这一系列层间的转换迭代地捕捉原始英语输入句子中的复杂关系和上下文。

解码器堆叠

一旦编码器完成了其任务,输出向量——即包含输入句子上下信息的输入句子的嵌入——就会被传递到解码器。在解码器堆叠中,多个层按顺序工作,从嵌入中生成法语翻译。

该过程首先将第一个嵌入转换为法语短语“Bonjour”。随后的层使用以下嵌入和之前生成的单词的上下文来预测法语句子中的下一个单词。这个过程通过堆叠中的所有层重复进行,每一层都使用输入嵌入和生成的单词来定义和细化翻译。

解码器堆叠通过这种迭代过程逐步构建(或解码)翻译句子,最终到达“Bonjour, comment ça va?”。

在对编码器-解码器结构有一个整体理解之后,我们的下一步是揭示每个堆叠中复杂的操作。然而,在深入探讨自注意力机制和FFN之前,有一个至关重要的组件我们需要理解——位置编码。位置编码对于Transformer的性能至关重要,因为它为Transformer模型提供了对单词顺序的感觉,这是堆叠中后续操作所缺乏的。

位置编码

句子中的每个单词都包含两种类型的信息——其含义和在句子更大上下文中的角色。上下文角色通常源于单词在单词排列中的位置。例如,“你好,你好吗?”这样的句子是有意义的,因为单词的顺序是特定的。如果将其改为“你是,如何你好?”,则意义变得不清楚。

因此,Vaswani等人引入了位置编码以确保transformer能够将每个单词编码为包含其句子中位置额外数据的附加信息。位置编码是通过在不同频率上使用正弦和余弦函数的混合来计算的,这为序列中的每个位置生成一组独特的值。然后,这些值被添加到标记的原始嵌入中,为模型提供了一种捕捉单词顺序的方法。这些增强的嵌入随后准备好在transformer模型的后续层中由自注意力机制进行处理。

自注意力机制

当我们的输入句子“你好,你好吗?”的每个标记通过编码堆栈的每一层时,它都会通过自注意力机制进行转换。

如其名所示,自注意力机制允许每个标记(单词)关注(或聚焦于)其他重要标记,以理解句子中的完整上下文。在编码特定单词之前,这种注意力机制解释了序列中每个单词与其他单词之间的关系。然后,根据它们与当前正在处理的单词的相关性,为不同的单词分配不同的注意力分数。

再次考虑我们的输入句子“你好,你好吗?”。当自注意力机制处理最后一个单词“”时,它不仅仅关注“”。相反,它考虑整个句子:它查看“你好”,瞥了一眼“如何”,反思了“”,当然,聚焦于“”。

在这样做的时候,它为每个单词分配了不同级别的注意力。你可以将注意力(图3**.1)想象为连接“”到每个其他单词的线条。连接到“你好”的线条可能很粗,表示很多注意力,代表着“你好”对“”编码的影响。连接“”和“如何”的线条可能较细,表明对“如何”的关注较少。连接到“”和“”的线条会有其他厚度,这取决于它们在为“”提供上下文方面的帮助:

图3.1:自注意力机制

图3.1:自注意力机制

这样,当编码“”时,会考虑整个句子的加权混合,而不仅仅是单个单词。而定义这种混合的权重就是我们所说的注意力分数。

自注意力机制通过以下步骤实现:

  1. 初始时,每个输入单词被表示为一个向量,这是我们通过词嵌入获得的。

  2. 这些向量随后通过学习变换映射到新的向量,称为查询、键和值向量。

  3. 然后通过将单词的查询向量与每个其他单词的键向量进行点积,并随后进行SoftMax操作(我们将在后面描述),来计算每个单词的注意力分数。

  4. 这些分数表示在编码每个单词时,对输入句子其他部分的关注程度。

  5. 最后,根据这些分数计算值向量的加权总和,以给出我们的最终输出向量,或自注意力输出。

重要的是要注意,这种计算是对句子中的每个单词进行的。这确保了对句子上下文的全面理解,同时考虑句子中的多个部分。这个概念使Transformer与几乎所有之前的模型都区分开来。

与只运行一次自注意力机制(或“单头注意力”)不同,Transformer在并行中多次复制自注意力机制。每个副本或头都操作相同的输入,但有自己的独立学习参数集来计算注意力分数。这使得每个头可以学习单词之间的不同上下文关系。这种并行过程被称为多头注意力MHA)。

再次想象我们的句子“你好,你好吗?”。一个头可能专注于“你好”与“”之间的关系,而另一个头可能更多地关注“你好吗”与“”之间的关系。每个头都有自己的查询、键和值权重集,这进一步使它们能够专门化并学习不同的事物。然后,将这些多个头的输出连接起来并转换,以产生传递到堆叠中下一层的最终值。

这种多头方法允许模型从相同的输入单词中捕捉更广泛的信息。这就像对同一个句子有多个视角,每个视角都提供独特的见解。

到目前为止,对于我们的输入句子“你好,你好吗?”,我们已经将每个单词转换成标记表示,然后使用MHA机制进行上下文化。通过并行自注意力,我们的Transformer可以考虑到句子中每个单词与句子中其他每个单词之间的全部交互范围。我们现在有一组多样化和上下文丰富的单词表示,每个表示都包含对单词在句子中角色纹理化的理解。然而,这种包含在注意力机制中的上下文理解只是我们Transformer模型中信息处理的一个组成部分。接下来,通过位置感知前馈网络(FFN)的另一个层次进行解释。FFN将为这些表示添加更多的细微差别,使它们对我们的翻译任务更有信息和价值。

在下一节中,我们讨论了变压器训练序列的一个关键方面:掩码。具体来说,变压器在解码器的自我注意力过程中应用因果(或前瞻)掩码,以确保每个输出标记预测只依赖于先前生成的标记,而不是未来的未知标记。

掩码

变压器在训练过程中应用两种类型的掩码。第一种是预处理步骤,以确保输入句子长度相同,从而实现高效的批量计算。第二种是前瞻(或因果)掩码,允许模型选择性地忽略序列中的未来标记。这种掩码发生在解码器的自我注意力机制中,防止模型在序列中向前窥视未来的标记。例如,当将单词 “Hello” 翻译成法语时,前瞻掩码确保模型无法访问随后的单词 “how”、“are” 或 “you”。这样,模型学会根据当前和前面的单词生成翻译,遵循翻译任务的自然进程,模仿人类的翻译方式。

通过对数据准备和掩码化的更清晰理解,我们现在转向训练过程中的另一个重要方面:超参数。与从数据中学习到的参数不同,超参数是在训练之前设置的配置,用于控制模型优化过程并指导学习过程。下一节将探讨各种超参数及其在训练过程中的作用。

SoftMax

要理解 FFN 的作用,我们可以描述其两个主要组件——线性变换和激活函数:

  • 线性变换 实质上是矩阵乘法。可以将它们视为重塑或调整输入数据的工具。在 FFN 中,这些变换发生两次,使用两种不同的权重(或矩阵)。

  • 在这两个变换之间应用 ReLU(修正线性单元)函数。ReLU 函数的作用是在模型中引入非线性。简单来说,ReLU 函数允许模型捕捉输入数据中的非严格成比例的图案,即非线性,这是 自然语言(NL)数据的特点。

FFN 被称为 位置敏感,因为它将句子中的每个单词单独处理(逐个位置),而不考虑序列。这与同时考虑整个序列的自我注意力机制形成对比。

因此,让我们尝试可视化这个过程:想象我们的单词 “Hello” 在经过自我注意力机制后到达这里。它携带有关其自身身份的信息,以及关于 “how”、“are” 和 “you” 的上下文参考。这种综合信息存在于一个描述 “Hello” 的向量中。

当“Hello”进入FFN时,可以将其想象为一个有两个门的隧道。在第一个门(或线性层)处,“Hello”通过矩阵乘法操作被转换,改变了其表示。之后,它遇到了ReLU函数——这使得表示变得非线性。

之后,“Hello”通过第二个门(另一个线性层),在另一侧再次被转换。虽然“Hello”的核心身份保持不变,但现在它被赋予了更多的上下文,这是通过FFN精心校准和调整的。

一旦输入通过门,还有一个额外的步骤。转换后的向量仍然必须被转换成可以解释为我们最终翻译任务预测的形式。

这就引出了使用SoftMax函数,这是transformer解码器中的最终转换。在向量通过FFN之后,它们会进一步通过一个最终的线性层进行处理。然后,结果被输入到SoftMax函数中。

SoftMax充当将我们模型输出转换为可解释为概率的机制。本质上,SoftMax函数将来自我们最终线性层的输出(可能是一组实数)转换成一个概率分布,表示每个单词成为我们输出序列中下一个单词的可能性。例如,如果我们的目标词汇包括“Bonjour”,“Hola”,“Hello”和“Hallo”,SoftMax函数将为这些单词中的每一个分配一个概率,并且具有最高概率的单词将被选择作为“Hello”的输出翻译。我们可以用这个过于简化的输出概率表示来展示:

[ Bonjour: 0.4, Hola: 0.3, Hello: 0.2, Hallo: 0.1 ]

图3**.2 展示了通过架构的信息流的一个更完整的(尽管过于简化的)视图。

图3.2:transformer的简化示意图

图3.2:transformer的简化示意图

现在我们已经介绍了transformer的架构组件,我们准备了解其组件是如何协同工作的。

序列到序列学习

Transformer的组件通过称为序列到序列Seq2Seq)学习(SL)的机制一起学习数据,这是监督学习SL)的一个子集。回想一下,SL是一种使用标记数据来训练模型以准确预测结果的技术。在Seq2Seq学习中,我们向transformer提供包含输入和相应正确输出的训练数据,在这种情况下,是正确的翻译。Seq2Seq学习特别适合像机器翻译这样的任务,其中输入和输出都是单词序列。

学习过程中的第一步是将短语中的每个词转换为标记,然后将其转换为数值嵌入。这些嵌入携带每个词的语义本质。计算位置编码并将其添加到这些嵌入中,以赋予它们位置意识。

当这些丰富的嵌入通过编码器堆栈时,在每个层中,自注意力机制通过聚合整个短语中的上下文信息来细化嵌入。在自注意力之后,每个词的嵌入在位置感知FFNs中进行进一步转换,调整嵌入以捕获更复杂的关系。

当编码器退出时,嵌入现在包含丰富的语义和上下文信息混合。它们被传递到解码器堆栈,其目的是将短语翻译成另一种语言(即目标序列)。与编码器一样,解码器中的每一层也使用自注意力和位置感知前馈网络(FFNs),但增加了一个与编码器输出交互的交叉注意力层。这种交互有助于对齐输入和输出短语,这是翻译的一个关键方面。

随着嵌入通过解码器层移动,它们逐渐被细化以表示模型将预测的翻译短语。解码器的最后一层通过线性变换和SoftMax函数处理嵌入,以在目标词汇上产生一个概率分布。这个分布定义了模型在每个步骤上对每个潜在下一个标记的预测可能性。然后,解码器从这个分布中采样,选择具有最高预测概率的标记作为其下一个输出。通过根据预测分布迭代采样最可能的下一个标记,解码器可以自回归地逐个生成完整的翻译输出序列标记。

然而,为了使transformer能够从预测的下一个标记分布中可靠地采样以生成高质量的翻译,它必须通过迭代成千上万的输入-输出对来逐步学习。在下一节中,我们将更详细地探讨模型训练。

模型训练

正如所讨论的,训练阶段的主要目标是细化模型的参数,以促进从一种语言到另一种语言的准确翻译。但是,参数的细化意味着什么,为什么它是关键的?

参数是模型用于生成翻译的内部变量。最初,这些参数被赋予随机值,并在每次训练迭代中进行调整。再次,模型被提供包含成千上万输入数据示例及其对应正确输出的训练数据,在这种情况下,是正确的翻译。然后,它使用一个错误(或损失)函数将其预测的输出标记与正确的(或实际)目标序列进行比较。

根据损失,模型更新其参数,逐渐提高其在解码的每一步中选择正确项目的能力。这逐渐细化了概率分布。

在数千次训练迭代中,模型学习源语言和目标语言之间的关联。最终,它获得了足够的知识,可以通过在训练期间发现的模式来解码从未见过的输入中的一致、类似人类的翻译。因此,训练推动了模型从预测词汇分布中产生准确目标序列的能力。

在训练了足够的翻译对之后,transformer达到了可靠的翻译性能。训练好的模型可以接受新的输入序列,并通过对新数据的泛化来输出翻译序列。

例如,以我们的示例句子“Hello, how are you?”及其法语翻译“Bonjour, comment ça va?”为例,英语句子作为输入,法语句子作为目标输出。训练数据由许多翻译对组成。每次模型处理一批数据时,它都会为翻译生成预测,将它们与实际的目标翻译进行比较,然后调整其参数以减少预测翻译和实际翻译之间的差异(或最小化损失)。这个过程会重复使用大量的数据批次,直到模型的翻译足够准确。

超参数

再次强调,与模型从训练数据中学习的参数不同,超参数是预先设置的配置,它控制着训练过程和模型的架构。它们是设置成功训练运行的关键部分。

在transformer模型背景下,一些关键的超参数包括以下内容:

  • 学习率:这个值决定了优化器更新模型参数的步长。较高的学习率可能会加快训练速度,但可能导致超过最佳解。较低的学习率可能会导致更精确地收敛到最佳解,尽管代价是更长的训练时间。我们将在下一节中详细讨论优化器。

  • 批大小:单个批次中处理的数据示例数量会影响训练期间的计算精度和内存需求。

  • 模型维度:模型的大小(例如,编码器和解码器的层数、嵌入的维度等)是一个关键的超参数,它影响着模型的学习能力和泛化能力。

  • 优化器设置:选择优化器及其设置,例如Adam优化器的初始学习率、beta值等,也被认为是超参数。我们将在下一节中进一步探讨优化器。

  • 正则化项:如dropout率这样的正则化项是超参数,通过在训练过程中添加某种形式的随机性或约束来帮助防止过拟合。

选择合适的超参数值对于训练过程至关重要,因为它会显著影响模型的表现和效率。这通常涉及到超参数调整,它包括实验和细化,以找到给定任务中产生可靠性能的超参数值。超参数调整可以说是一种艺术和科学。我们将在后面的章节中进一步探讨这一点。

在对超参数有了一定的了解之后,我们将继续探讨优化器的选择,这对于控制模型如何有效地从训练数据中学习至关重要。

优化器的选择

优化器是训练过程中的一个基本组成部分,负责更新模型的参数以最小化错误。不同的优化器有不同的策略来导航参数空间,找到一组参数值,以产生低损失(或更少的错误)。优化器的选择可以显著影响训练过程的速度和质量。

在变压器模型的背景下,Adam优化器由于其效率和在训练深度网络中的经验成功,通常是被选用的优化器。Adam在训练过程中会调整学习率。为了简化,我们不会探索所有可能的优化器,而是描述它们的目的。

优化器的主要任务是微调模型的参数,以减少翻译错误,逐步引导模型达到期望的性能水平。然而,过于激进的优化可能会导致模型记住训练数据,无法很好地泛化到未见过的数据。为了减轻这一点,我们采用正则化技术。

在下一节中,我们将探讨正则化——这是一种与优化相结合的技术,确保模型在学会最小化翻译错误的同时,也能适应新的、未见过的数据。

正则化

正则化技术被用来防止模型记住训练数据(这种现象称为过拟合),并促进在新的、未见过的数据上的更好表现。过拟合发生在模型为了最小化错误,学习到训练数据到这种程度,以至于它捕获了无用的模式(或噪声)以及实际的模式。这种在学习训练数据上的过度精确导致模型在接触到新数据时性能下降。

让我们回顾一下我们的简单场景,其中我们训练一个模型将英语问候语翻译成法语问候语,使用的数据集包括单词“Hello”及其翻译“Bonjour”。如果模型过拟合,它可能会记住训练数据中的确切短语,而没有理解更广泛的翻译模式。

在过拟合的情况下,假设模型以1.0的概率学习将“Hello”翻译为“Bonjour”,因为在训练数据中它遇到的最频繁。当遇到新的、未见过的数据时,它可能会遇到之前未见过的变化,例如“Hi”,这也应该翻译为“Bonjour”。然而,由于过拟合,模型可能无法从“Hello”泛化到“Hi”,因为它过于关注训练期间看到的精确映射。

几种正则化技术可以减轻过拟合问题。这些技术在对模型参数进行训练时施加某些约束,鼓励模型学习数据的更一般化表示,而不是记住训练数据集。

这里有一些在transformer模型背景下使用的标准正则化技术:

  • Dropout:在基于NN的模型(如transformer)的背景下,术语“神经元”指的是模型内部协同工作以从数据中学习和进行预测的各个独立元素。每个神经元从数据中学习特定的方面或特征,使模型能够理解和翻译文本。在训练过程中,dropout随机地使一部分这些神经元失活或“丢弃”,暂时从网络中移除。这种随机失活鼓励模型将学习分散到许多神经元上,而不是过度依赖少数几个神经元。通过这样做,dropout有助于模型更好地将学习泛化到未见过的数据上,而不是仅仅记住训练数据(即过拟合)。

  • 层归一化:层归一化是一种技术,它对每个训练示例中的层的神经元激活进行归一化,而不是对示例批次进行归一化。这种归一化有助于稳定训练过程,并作为一种正则化形式,防止过拟合。

  • L1或L2正则化:L1正则化,也称为Lasso,添加一个等于系数绝对值的惩罚,促进参数稀疏性。L2正则化,或称为Ridge,添加一个基于系数平方的惩罚,以防止过拟合而避免大值。尽管这些技术有助于控制模型复杂性和增强泛化,但它们并非transformer初始设计的一部分。

通过采用这些正则化技术,模型被引导学习数据中的更一般化模式,这提高了它在未见过的数据上表现良好的能力,从而使模型在翻译新的文本输入时更加可靠和稳健。

在整个训练过程中,我们提到了损失函数,并讨论了优化器如何利用它来调整模型的参数,目的是最小化预测误差。损失函数量化了模型的表现。我们讨论了正则化如何惩罚损失函数以防止过拟合,鼓励模型学习更简单、更可泛化的模式。在下一节中,我们将更深入地探讨损失函数本身的微妙作用。

损失函数

损失函数在训练转换器模型中至关重要,它量化了模型预测与实际数据之间的差异。在语言翻译中,这种错误是在训练数据集中生成的翻译与实际翻译之间的误差。这个任务的一个常见选择是交叉熵损失,它衡量了模型在目标词汇表上的预测概率分布与实际分布之间的差异,其中正确单词的概率为1,其余单词的概率为0。

转换器通常采用一种称为标签平滑交叉熵损失的变体。标签平滑在训练过程中调整目标概率分布,略微降低正确类的概率,并增加其他所有类的概率,这有助于防止模型对其预测过于自信。例如,假设目标词汇包括“Bonjour”、“Hola”、“Hello”和“Hallo”,并且“Bonjour”是正确的翻译,标准交叉熵损失的目标是概率分布为Bonjour: 1.0Hola: 0.0Hello: 0.0Hallo: 0.0。然而,标签平滑交叉熵损失会略微调整这些概率,如下所示:

[ “Bonjour”: 0.925, “Hola”: 0.025, “Hello”: 0.025, “Hallo”: 0.025 ]

平滑降低了模型的信心,并促进了更好的泛化到未见过的数据。在更清楚地理解损失函数的作用后,我们可以继续到推理阶段,在那里训练好的模型为新的、未见过的数据生成翻译。

推理

经过遍历训练景观,我们的训练模型现在已经熟练掌握优化参数,可以应对翻译任务。在推理阶段,这些学习到的参数被用来翻译新的、未见过的文本。我们将继续使用示例短语“Hello, how are you?”来阐明这一过程。

推理阶段是训练模型在新的数据上的实际应用。经过训练过程中的多次迭代后,训练好的参数现在被用来将一种语言的文本翻译成另一种语言。推理步骤可以描述如下:

  1. 输入准备:最初,我们的短语“Hello, how are you?”被标记化并编码成模型可以处理的形式,类似于训练阶段中的准备步骤。

  2. 通过模型传递:编码后的输入随后通过模型传递。在导航通过编码器和解码器堆栈时,训练好的参数指导输入数据的转换,每一步都使翻译更准确。

  3. 输出生成:在解码器堆栈的最终阶段,模型为输入文本中的每个单词在目标词汇表上生成一个概率分布。对于单词“Hello”,在目标词汇表(在我们的例子中是法语单词)上形成一个概率分布,选择概率最高的单词作为翻译。这个过程为短语中的每个单词重复进行,生成翻译输出“Bonjour, comment ça va?”。

现在我们已经了解了模型如何产生最终输出,我们可以逐步实现一个transformer模型,以巩固我们讨论的概念。然而,在我们深入代码之前,我们可以简要概述端到端架构流程:

  1. 输入分词:初始英语短语“Hello, how are you?”被分词成更小的单元,如“Hello”,“,”,“how”等。

  2. 嵌入:这些标记随后通过嵌入层映射到连续的向量表示。

  3. 位置编码:为了保持序列的顺序,将位置编码添加到嵌入中。

  4. 编码器自注意力机制:嵌入的输入序列在编码器的自注意力层序列中导航。在这里,每个单词评估每个其他单词的相关性,以理解完整的上下文。

  5. FFN:随后,每个编码器层内的位置wise FFN对表示进行细化。

  6. 编码器输出:编码器生成捕捉输入序列本质的上下文表示。

  7. 解码器注意力机制:逐步地,解码器构建输出序列,仅对前面的单词使用自注意力以保持序列顺序。

  8. 编码器-解码器注意力机制:解码器评估编码器的输出,在生成输出序列中的每个单词时,集中于相关的输入上下文。

  9. 输出层:解码器将其输出馈送到线性层和SoftMax层,以产生“Bonjour, comment ça va?”。

在本章结束时,我们将将原始transformer(黄等人,2022)的最佳实现改编成一个最小示例,该示例可以后来在各种下游任务上进行训练。这将作为一个理论练习,以进一步巩固我们的理解。在实践中,我们将依赖预训练或基础模型,我们将在后面的章节中学习如何实现。

然而,在我们开始我们的实践项目之前,我们可以追溯其对当前GenAI领域的影响。我们跟随架构早期应用的轨迹(例如,来自Transformers的双向编码表示BERT))到第一个GPT。

语言模型的发展——AR Transformer 及其在生成人工智能中的作用

第 2 章中,我们回顾了一些应用基于 Transformer 方法的生成范式。在此,我们更详细地追踪了 Transformer 的发展,概述了一些从 2017 年的初始 Transformer 到更近期的最先进模型的最具影响力的基于 Transformer 的语言模型,这些模型展示了在人工智能快速发展的领域中涉及的扩展性、多功能性和社会考虑(如图 3.3 所示):

图 3.3:从原始 Transformer 到 GPT-4

图 3.3:从原始 Transformer 到 GPT-4

  • 2017 – Transformer:Vaswani 等人提出的 Transformer 模型在 NLP 中实现了范式转变,其特征是能够并行处理整个数据序列的自注意力层。这种架构使得模型能够评估句子中每个词相对于所有其他词的重要性,从而增强了模型捕捉上下文的能力。

  • 2018 – BERT:Google 的 BERT 模型通过在其编码器层中使用双向上下文在预训练期间进行了创新。它是第一个基于整个句子(包括左右两侧)理解词的上下文的模型之一,显著提高了在广泛 NLP 任务上的性能,特别是那些需要深入理解上下文的任务。

  • 2018 – GPT-1:OpenAI 的 GPT-1 模型是自然语言处理(NLP)的一个里程碑,采用了仅使用 Transformer 解码器模型的生成预训练方法。它在多样化的文本数据语料库上进行了预训练,并针对各种任务进行了微调,使用了一种从左到右顺序生成文本的单向方法,这对于生成文本应用特别适合。

  • 2019 – GPT-2:GPT-2 在 GPT-1 奠定的基础上进行了扩展,保持了其仅使用解码器架构,但在数据集和模型规模上显著扩大。这使得 GPT-2 能够在更广泛的主题上生成更连贯和上下文相关的文本,展示了扩大 Transformer 模型的力量。

  • 2020 – GPT-3:OpenAI 的 GPT-3 将 Transformer 模型的规模边界推进到 1750 亿个参数,使得在最小输入的情况下执行各种任务成为可能,通常使用零样本学习ZSL)或少样本学习FSL)。这表明 Transformer 可以跨任务和数据类型进行泛化,通常无需大量特定任务数据或微调。

  • 2021 – InstructGPT:InstructGPT 是 GPT-3 的优化变体,专门针对遵循用户指令和生成一致响应进行微调,其输出中包含强调安全性和相关性的反馈循环。这代表了创建能够更准确地解释和响应人类提示的 AI 模型的关注点。

  • 2023 – GPT-4: GPT-4是OpenAI的transformer模型在多模态空间中的进化,能够理解和生成基于文本和图像的内容。此模型旨在产生更安全、更具有情境细微差别的响应,展示了模型在处理复杂任务和生成创意内容能力上的显著进步。

  • 2023 – LLaMA 2: Meta AI的LLaMA 2是一系列关注效率和可访问性的模型的组成部分,允许在更高效资源使用的同时实现高性能的语言建模。此模型旨在促进AI社区更广泛的研究和应用开发。

  • 2023 – Claude 2: Anthropic的Claude 2在Claude 1的基础上取得了进步,增加了其标记上下文窗口,并提高了其推理和记忆能力。它旨在更接近人类价值观,为开放域问答和其他对话式AI应用提供负责任和细微的生成能力,标志着在道德AI开发方面的进步。

展示的时间线突出了过去几年基于transformer的语言模型的显著进步。最初作为一种引入自注意力概念的架构,它迅速演变成具有数十亿参数的模型,能够生成连贯的文本、回答问题,并在高性能水平上执行各种智力任务。GPT-4等模型规模和可访问性的增加为AI应用开辟了新的可能性。同时,最近的研究表明,模型更加关注安全和伦理,并为用户提供更细微、更有帮助的响应。

在下一节中,我们通过使用Python实现原始transformer架构的关键组件,为对自然语言处理领域感兴趣的从业者完成了一个仪式。这有助于更全面地理解启动这一切的机制。

实现原始Transformer

下面的代码演示了如何实现一个用于Seq2Seq翻译任务的简化transformer模型,主要翻译英语文本到法语。代码结构分为多个部分,从数据加载到模型训练和翻译,处理了各种方面。

数据加载和准备

最初,代码加载了一个数据集并为其训练做准备。数据从CSV文件中加载,然后分为英语和法语文本。为了演示目的,文本限制为100个字符以减少训练时间。CSV文件包含几千个示例数据点,可以在本书的GitHub仓库(https://github.com/PacktPublishing/Python-Generative-AI)中找到,包括完整的代码:

import pandas as pd
import numpy as np
# Load demo data
data = pd.read_csv("./Chapter_3/data/en-fr_mini.csv")
# Separate English and French lexicons
EN_TEXT = data.en.to_numpy().tolist()
FR_TEXT = data.fr.to_numpy().tolist()
# Arbitrarily cap at 100 characters for demonstration to avoid long training times
def demo_limit(vocab, limit=100):
    return [i[:limit] for i in vocab]
EN_TEXT = demo_limit(EN_TEXT)
FR_TEXT = demo_limit(FR_TEXT)
# Establish the maximum length of a given sequence
MAX_LEN = 100

分词

接下来,在文本数据上训练了一个分词器。分词器对于将文本数据转换为模型可以输入的数值数据至关重要:

from tokenizers import Tokenizer
from tokenizers.models import WordPiece
from tokenizers.trainers import WordPieceTrainer
from tokenizers.pre_tokenizers import Whitespace
def train_tokenizer(texts):
    tokenizer = Tokenizer(WordPiece(unk_token="[UNK]"))
    tokenizer.pre_tokenizer = Whitespace()
    trainer = WordPieceTrainer(
        vocab_size=5000,
        special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]", 
            "<sos>", "<eos>"],
    )
    tokenizer.train_from_iterator(texts, trainer)
    return tokenizer
en_tokenizer = train_tokenizer(EN_TEXT)
fr_tokenizer = train_tokenizer(FR_TEXT)

数据张量化

文本数据随后被张量化,这涉及到将文本数据转换为张量格式。这一步对于使用PyTorch进行训练数据准备至关重要:

import torch
from torch.nn.utils.rnn import pad_sequence
def tensorize_data(text_data, tokenizer):
    numericalized_data = [
        torch.tensor(tokenizer.encode(text).ids) for text in text_data
    ]
    padded_data = pad_sequence(numericalized_data,
        batch_first=True)
    return padded_data
src_tensor = tensorize_data(EN_TEXT, en_tokenizer)
tgt_tensor = tensorize_data(FR_TEXT, fr_tokenizer)

数据集创建

创建了一个自定义数据集类来处理数据。这个类对于在训练期间分批加载数据至关重要:

from torch.utils.data import Dataset, DataLoader
class TextDataset(Dataset):
    def __init__(self, src_data, tgt_data):
        self.src_data = src_data
        self.tgt_data = tgt_data
    def __len__(self):
        return len(self.src_data)
    def __getitem__(self, idx):
        return self.src_data[idx], self.tgt_data[idx]
dataset = TextDataset(src_tensor, tgt_tensor)

嵌入层

嵌入层将每个标记映射到连续的向量空间。这一层对于模型理解和处理文本数据至关重要:

import torch.nn as nn
class Embeddings(nn.Module):
    def __init__(self, d_model, vocab_size):
        super(Embeddings, self).__init__()
        self.embed = nn.Embedding(vocab_size, d_model)
    def forward(self, x):
        return self.embed(x)

位置编码

位置编码层将位置信息添加到嵌入中,这有助于模型理解序列中标记的顺序:

import math
class PositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1,
                 max_len=MAX_LEN
    ):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0.0, max_len).unsqueeze(1)
        div_term = torch.exp(
            torch.arange(0.0, d_model, 2) * - \
                (math.log(10000.0) / d_model)
        )
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer("pe", pe)
    def forward(self, x):
        x = x + self.pe[:, : x.size(1)]
        return self.dropout(x)

多头自注意力

多头自注意力MHSA)层是Transformer架构的关键部分,它允许模型在生成输出序列时关注输入序列的不同部分:

class MultiHeadSelfAttention(nn.Module):
    def __init__(self, d_model, nhead):
        super(MultiHeadSelfAttention, self).__init__()
        self.attention = nn.MultiheadAttention(d_model, nhead)
    def forward(self, x):
        return self.attention(x, x, x)

FFN

FFN是一个简单的全连接神经网络FCNN),它独立地对每个位置进行操作:

class FeedForward(nn.Module):
    def __init__(self, d_model, d_ff):
        super(FeedForward, self).__init__()
        self.linear1 = nn.Linear(d_model, d_ff)
        self.dropout = nn.Dropout(0.1)
        self.linear2 = nn.Linear(d_ff, d_model)
    def forward(self, x):
        return self.linear2(self.dropout(torch.relu(self.linear1(x))))

编码器层

编码器层由一个MHSA机制和一个简单的FFNN组成。这种结构通过堆叠重复,形成完整的编码器:

class EncoderLayer(nn.Module):
    def __init__(self, d_model, nhead, d_ff):
        super(EncoderLayer, self).__init__()
        self.self_attn = MultiHeadSelfAttention(d_model, nhead)
        self.feed_forward = FeedForward(d_model, d_ff)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(0.1)
    def forward(self, x):
        x = x.transpose(0, 1)
        attn_output, _ = self.self_attn(x)
        x = x + self.dropout(attn_output)
        x = self.norm1(x)
        ff_output = self.feed_forward(x)
        x = x + self.dropout(ff_output)
        return self.norm2(x).transpose(0, 1)

编码器

编码器是一个具有MHSA机制和FFN的相同层的堆栈:

class Encoder(nn.Module):
    def __init__(self, d_model, nhead, d_ff, num_layers, vocab_size):
        super(Encoder, self).__init__()
        self.embedding = Embeddings(d_model, vocab_size)
        self.pos_encoding = PositionalEncoding(d_model)
        self.encoder_layers = nn.ModuleList(
            [EncoderLayer(d_model, nhead, d_ff) for _ in range(
                num_layers)]
        )
        self.feed_forward = FeedForward(d_model, d_ff)
    def forward(self, x):
        x = self.embedding(x)
        x = self.pos_encoding(x)
        for layer in self.encoder_layers:
            x = layer(x)
        return x

解码器层

类似地,解码器层由两个MHA机制组成——一个自注意力和一个交叉注意力——随后是一个FFN:

class DecoderLayer(nn.Module):
    def __init__(self, d_model, nhead, d_ff):
        super(DecoderLayer, self).__init__()
        self.self_attn = MultiHeadSelfAttention(d_model, nhead)
        self.cross_attn = nn.MultiheadAttention(d_model, nhead)
        self.feed_forward = FeedForward(d_model, d_ff)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(0.1)
    def forward(self, x, memory):
        x = x.transpose(0, 1)
        memory = memory.transpose(0, 1)
        attn_output, _ = self.self_attn(x)
        x = x + self.dropout(attn_output)
        x = self.norm1(x)
        attn_output, _ = self.cross_attn(x, memory, memory)
        x = x + self.dropout(attn_output)
        x = self.norm2(x)
        ff_output = self.feed_forward(x)
        x = x + self.dropout(ff_output)
        return self.norm3(x).transpose(0, 1)

解码器

解码器也是一个相同层的堆栈。每个层包含两个MHA机制和一个FFN:

class Decoder(nn.Module):
    def __init__(self, d_model, nhead, d_ff, num_layers, vocab_size):
        super(Decoder, self).__init__()
        self.embedding = Embeddings(d_model, vocab_size)
        self.pos_encoding = PositionalEncoding(d_model)
        self.decoder_layers = nn.ModuleList(
            [DecoderLayer(d_model, nhead, d_ff) for _ in range(
                num_layers)]
        )
        self.linear = nn.Linear(d_model, vocab_size)
        self.softmax = nn.Softmax(dim=2)
    def forward(self, x, memory):
        x = self.embedding(x)
        x = self.pos_encoding(x)
        for layer in self.decoder_layers:
            x = layer(x, memory)
        x = self.linear(x)
        return self.softmax(x)

这种堆叠层模式继续构建Transformer架构。每个块在处理输入数据和生成输出翻译方面都有特定的作用。

完整的Transformer

Transformer模型封装了之前定义的编码器和解码器结构。这是用于训练和翻译任务的主要类:

class Transformer(nn.Module):
    def __init__(
        self,
        d_model,
        nhead,
        d_ff,
        num_encoder_layers,
        num_decoder_layers,
        src_vocab_size,
        tgt_vocab_size,
    ):
        super(Transformer, self).__init__()
        self.encoder = Encoder(d_model, nhead, d_ff, \
            num_encoder_layers, src_vocab_size)
        self.decoder = Decoder(d_model, nhead, d_ff, \
            num_decoder_layers, tgt_vocab_size)
    def forward(self, src, tgt):
        memory = self.encoder(src)
        output = self.decoder(tgt, memory)
        return output

训练函数

train函数遍历epoch和批次,计算损失,并更新模型参数:

def train(model, loss_fn, optimizer, NUM_EPOCHS=10):
    for epoch in range(NUM_EPOCHS):
        model.train()
        total_loss = 0
        for batch in batch_iterator:
            src, tgt = batch
            optimizer.zero_grad()
            output = model(src, tgt)
            loss = loss_fn(output.view(-1, TGT_VOCAB_SIZE),
                tgt.view(-1))
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        print(f"Epoch {epoch}, 
            Loss {total_loss / len(batch_iterator)}")

翻译函数

translate函数使用训练好的模型将源文本翻译成目标语言。它逐个生成翻译标记,并在生成序列结束EOS)标记或达到最大目标长度时停止:

def translate(model, src_text, src_tokenizer,
              tgt_tokenizer, max_target_length=50
):
    model.eval()
    src_tokens = src_tokenizer.encode(src_text).ids
    src_tensor = torch.LongTensor(src_tokens).unsqueeze(0)
    tgt_sos_idx = tgt_tokenizer.token_to_id("<sos>")
    tgt_eos_idx = tgt_tokenizer.token_to_id("<eos>")
    tgt_tensor = torch.LongTensor([tgt_sos_idx]).unsqueeze(0)
    for i in range(max_target_length):
        with torch.no_grad():
            output = model(src_tensor, tgt_tensor)
        predicted_token_idx = output.argmax(dim=2)[0, -1].item()
        if predicted_token_idx == tgt_eos_idx:
            break
        tgt_tensor = torch.cat((tgt_tensor,
            torch.LongTensor([[predicted_token_idx]])),
            dim=1)
    translated_token_ids = tgt_tensor[0, 1:].tolist()
    translated_text = tgt_tokenizer.decode(translated_token_ids)
    return translated_text

主执行

在脚本的主体块中,定义了超参数,实例化了标记化器和模型,并启动了训练和翻译过程:

if __name__ == "__main__":
    NUM_ENCODER_LAYERS = 2
    NUM_DECODER_LAYERS = 2
    DROPOUT_RATE = 0.1
    EMBEDDING_DIM = 512
    NHEAD = 8
    FFN_HID_DIM = 2048
    BATCH_SIZE = 31
    LEARNING_RATE = 0.001
    en_tokenizer = train_tokenizer(EN_TEXT)
    fr_tokenizer = train_tokenizer(FR_TEXT)
    SRC_VOCAB_SIZE = len(en_tokenizer.get_vocab())
    TGT_VOCAB_SIZE = len(fr_tokenizer.get_vocab())
    src_tensor = tensorize_data(EN_TEXT, en_tokenizer)
    tgt_tensor = tensorize_data(FR_TEXT, fr_tokenizer)
    dataset = TextDataset(src_tensor, tgt_tensor)
    model = Transformer(
        EMBEDDING_DIM,
        NHEAD,
        FFN_HID_DIM,
        NUM_ENCODER_LAYERS,
        NUM_DECODER_LAYERS,
        SRC_VOCAB_SIZE,
        TGT_VOCAB_SIZE,
    )
    loss_fn = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
    batch_iterator = DataLoader(
        dataset, batch_size=BATCH_SIZE,
        shuffle=True, drop_last=True
    )
    train(model, loss_fn, optimizer, NUM_EPOCHS=10)
    src_text = "hello, how are you?"
    translated_text = translate(
        model, src_text, en_tokenizer, fr_tokenizer)
    print(translated_text)

此脚本从加载数据到训练Transformer模型,最终从英语翻译到法语进行机器翻译任务。最初,它加载一个数据集,处理文本,并建立标记化器以将文本转换为数值数据。随后,它定义了PyTorch中Transformer模型的架构,详细说明了从嵌入的自注意力机制到编码器和解码器堆栈的每个组件。

脚本进一步将数据组织成批次,设置训练循环,并定义翻译函数。在提供的英语和法语句子上训练模型,教会它将一个语言的序列映射到另一个语言。最后,它将一个示例句子从英语翻译成法语,以展示模型的能力。

摘要

Transformer的出现极大地推动了NLP领域的发展,成为今天尖端生成语言模型的基础。本章概述了NLP的发展历程,为这一关键创新铺平了道路。最初的统计技术,如计数向量和TF-IDF,擅长提取基本的词模式,但它们在把握语义细微差别方面有所不足。

将神经语言模型纳入其中,标志着通过词嵌入向更深层次表示迈进的一大步。然而,循环网络在处理较长的序列时遇到了障碍。这激发了CNN的出现,它通过并行性引入了计算效率,尽管是以牺牲全局上下文意识为代价。

注意力机制的引入成为了一个基石。2017年,Vaswani等人进一步扩展了这些进展,揭幕了Transformer架构。Transformer的标志性自注意力机制以并行化的方式促进了跨广泛序列的上下文建模。层叠的编码器-解码器结构精心细化表示,以辨别翻译等努力不可或缺的关系。

Transformer凭借其可并行化和可扩展的自注意力设计,在性能上设定了新的基准。其核心原则是当代高性能生成语言模型(如GPT)的建筑基石。

在下一章中,我们将讨论如何将预训练的生成模型从原型应用到生产中。

参考文献

本参考部分作为本书中引用的资源的存储库;您可以探索这些资源,以进一步加深对主题的理解和知识:

  • Bahdanau, D., Cho, K., and Bengio, Y. (2014). Neural machine translation by jointly learning to align and translate. arXiv preprint arXiv:1409.0473.

  • Bengio, Y., Ducharme, R., and Vincent, P. (2003). A neural probabilistic language model. The Journal of Machine Learning Research, 3, 1137-1155.

  • Dadgar, S. M. H., Araghi, M. S., and Farahani, M. M. (2016). Improving text classification performance based on TFIDF and LSI index. 2016 IEEE International Conference on Engineering & Technology (ICETECH).

  • Elman, J. L. (1990). Finding structure in time. Cognitive science, 14(2), 179-211.

  • Hochreiter, S., and Schmidhuber, J. (1997). Long short-term memory. Neural computation, 9(8), 1735-1780.

  • Kim, Y. (2014). Convolutional neural networks for sentence classification. arXiv preprint arXiv:1408.5882.

  • Mikolov, T., Sutskever, I., Chen, K., Corrado, G. S., and Dean, J. (2013). 词和短语的分布式表示及其组合性。神经信息处理系统进展,26.

  • Pennington, J., Socher, R., and Manning, C. (2014). *GloVe:全局词表示。2014年自然语言处理实证方法会议(EMNLP)论文集,1532-1543。

  • Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., ... and Polosukhin, I. (2017). 注意即一切。神经信息处理系统进展,30.

第四章:应用预训练生成模型:从原型到生产

在前面的章节中,我们探讨了生成式人工智能的基础,探讨了各种生成模型,例如生成对抗网络(GANs)、扩散器和转换器,并了解了自然语言处理(NLP)的变革性影响。随着我们转向应用生成式人工智能的实践方面,我们应该将我们的探索建立在实际例子之上。这种方法将提供一个具体的环境,使技术方面更加贴近实际,学习体验更加吸引人。

我们将介绍“StyleSprint”,这是一家希望增强其在线存在的服装店。实现这一目标的一种方式是为其各种产品编写独特且引人入胜的产品描述。然而,手动为大量库存创建吸引人的描述是具有挑战性的。这种情况是应用生成式人工智能的理想机会。通过利用预训练的生成模型,StyleSprint可以自动化制作引人入胜的产品描述,节省大量时间并丰富其客户的在线购物体验。

当我们步入预训练生成大型语言模型(LLM)的实际应用时,首要任务是建立一个有利于使用生成模型进行原型的Python环境。这个设置对于将项目从原型过渡到生产就绪状态至关重要,为StyleSprint实现其自动化内容生成目标奠定基础。

在第2章和第3章中,我们使用Google Colab进行原型设计,因为它易于使用且可访问的GPU资源丰富。它是一个快速测试想法的优秀平台。然而,当我们把重点转向在现实世界环境中部署我们的生成模型时,了解从原型环境(如Google Colab)到更稳健、生产就绪的设置的过渡至关重要。这种过渡将确保我们的解决方案可扩展、可靠,并且针对处理现实世界流量进行了优化。在本章中,我们将介绍设置生产就绪Python环境的步骤,强调从原型到生产的平稳过渡的关键考虑因素。

到本章结束时,我们将了解将生成应用程序从原型环境过渡到生产就绪设置的流程。我们将定义一个可靠且可重复的策略,用于评估、监控和部署模型到生产环境。

原型环境

Jupyter笔记本提供了一个交互式计算环境,可以将代码执行、文本、数学、图表和丰富媒体结合成一个单一的文档。它们非常适合原型设计和交互式开发,因此在数据科学家、研究人员和工程师中非常受欢迎。以下是它们提供的内容:

  • 内核:Jupyter笔记本的核心是一个内核,它是一个计算引擎,负责执行笔记本中的代码。对于Python,这通常是IPython内核。当笔记本打开时,这个内核保持活跃并维护笔记本计算的状态。

  • 交互式执行:代码单元格允许你交互式地编写和执行代码,检查结果并根据需要调整代码。

  • pipconda命令。

  • 可视化:你可以嵌入图表、图形和其他可视化内容,以交互式地探索数据和结果。

  • 文档:将Markdown单元格与代码单元格结合使用,可以创建文档齐全、自包含的笔记本,解释代码及其输出。

Jupyter笔记本的一个缺点是它们通常依赖于个人电脑的计算资源。大多数个人笔记本电脑和台式机都没有优化或配备处理计算密集型过程的硬件。拥有足够的计算资源对于管理实验LLM的计算复杂性至关重要。幸运的是,我们可以通过提供计算加速器(如图形处理单元GPU)和张量处理单元TPU))的云平台来扩展Jupyter笔记本的功能。例如,Google Colab可以立即增强Jupyter笔记本,使其有利于计算密集型实验。以下是云笔记本环境(如Google Colab)的一些关键特性:

  • GPU/TPU访问:提供免费或负担得起的GPU和TPU资源访问,这对于处理要求高的机器学习模型至关重要。

  • 协作:允许轻松共享和实时协作,类似于Google Docs。

  • 集成:便于存储和访问笔记本和数据。

让我们考虑我们的StyleSprint场景。在决定最适合StyleSprint目标的一个模型之前,我们可能想要探索几个不同的模型来生成产品描述。我们可以在Google Colab中设置一个最小的工作原型来比较模型。再次强调,云平台提供了一个最优且易于访问的环境,用于初始测试、实验,甚至是一些轻量级的模型训练。以下是我们可以如何初步设置一个生成模型,开始为StyleSprint进行自动化产品描述生成的实验:

# In a Colab or Jupyter notebook
!pip install transformers
# Google Colab Jupyter notebook
from transformers import pipeline
# Initialize a text generation pipeline with a generative model, say GPT-Neo
text_generator = pipeline(
    'text-generation', model='EleutherAI/gpt-neo-2.7B')
# Example prompt for product description generation
prompt = "This high-tech running shoe with advanced cushioning and support"
# Generating the product description
generated_text = text_generator(prompt, max_length=100, do_sample=True)
# Printing the generated product description
print(generated_text[0]['generated_text'])

输出

This high-tech running shoe with advanced cushioning and support combines the best of traditional running shoes and the latest technologies.

在这个简单的设置中,我们正在安装transformers库,它提供了一个方便的接口来访问各种预训练模型。然后,我们使用开源版本的GPT-Neo初始化一个文本生成管道,能够生成连贯且上下文相关的文本。这个设置作为StyleSprint在小型规模上实验生成创意产品描述的起点。

在本章的后面部分,我们将扩展我们的实验,评估和比较多个预训练的生成模型,以确定哪个最能满足我们的需求。然而,在我们进一步进行实验和原型设计之前,战略性地暂停并展望未来是至关重要的。这种深思熟虑的预见使我们能够考虑将实验有效过渡到生产环境所需的必要步骤。通过这样做,我们确保从端到端全面了解项目,以符合长期运营目标。

图4.1:从原型到生产的阶段

图4.1:从原型到生产的阶段

转向生产

在我们计划生产设置时,我们首先应该了解我们将要将其带入生产环境的原型环境的内在优势和功能。原型环境(如Google Colab)的许多功能都深度集成,并且可能很容易被忽视,因此重要的是要剖析和编制我们将需要的生产环境中的功能清单。例如,以下功能是Google Colab固有的,在生产环境中将至关重要:

  • !pip install library_name。在生产环境中,我们必须预先安装库或确保我们可以在需要时安装它们。我们还必须确保特定于项目的库不会干扰其他项目。

  • 依赖隔离:Google Colab自动简化了依赖项的隔离,确保包安装和更新不会干扰其他项目。在生产环境中,我们也可能希望使用相同的架构部署各种项目。依赖隔离对于防止一个项目的依赖项更新影响其他项目至关重要。

  • 交互式代码执行:代码单元格的交互式执行有助于测试单个代码片段、可视化结果和实时调试。这种便利性在生产环境中并非必需,但对于快速调试可能有所帮助。

  • 资源可访问性:使用Colab,访问GPU和TPU变得简单,这对于运行计算密集型任务至关重要。对于生产环境,我们将想要检查我们的动态计算需求,并配置适当的架构。

  • 数据集成:Colab为分析和建模提供了简单的数据源连接。在生产环境中,我们可以通过将数据直接部署到环境中来启动我们的环境,或者根据需要确保与远程数据源的连接。

  • 版本控制和协作:使用 Google Colab 跟踪项目代码的版本很容易通过笔记本完成。此外,Colab 预配置为与 Git 交互。Git 是一个广泛用于在软件开发过程中跟踪源代码更改的分布式版本控制系统。在生产中,我们还将希望集成 Git 来管理我们的代码并将其与远程代码仓库(如 GitHub 或 Bitbucket)同步。远程版本控制确保我们的生产环境始终反映最新的更改,并允许持续的协作。

  • 错误处理和调试:在 Colab 中,我们直接访问 Python 运行时,通常可以实时看到错误信息和回溯,以帮助识别和解决问题。我们希望在生产环境中通过适当的系统错误记录达到相同级别的可见性。总的来说,我们希望保留我们 Google Colab 原型环境中的便利性和简单性,但提供生产所需的鲁棒性和可扩展性。为此,我们将我们将我们提出的每个关键特性映射到相应的生产解决方案。这些关键特性应确保 StyleSprint 自动化产品描述生成模型的部署过程顺利。

将特性映射到生产设置

为了确保我们可以无缝地将我们的原型环境过渡到生产环境,我们可以利用 Docker,这是一个领先的容器化工具。容器化工具将应用程序及其依赖项打包,以确保在不同系统上的一致性能。容器化方法将帮助我们复制 Google Colab 的隔离、统一环境,确保可靠性并减少生产中的潜在兼容性问题。下表描述了如何将我们原型环境的每个好处映射到生产类似物:

特性 环境
原型设计
--- ---
软件包管理 通过预安装的软件包管理器固有的
依赖项隔离 通过笔记本固有的
交互式代码执行 通过笔记本固有的
资源可访问性 对于基于云的笔记本固有
数据集成 不是固有的,需要基于代码的集成
版本控制和协作 通过笔记本和预配置的 Git 实现
错误处理和调试 通过直接交互式访问运行时实现

表 4.1:通过 Docker 从 Colab 到生产的过渡功能

在将我们的原型环境的功能映射到生产设置中相应的工具和实践之后,我们现在更好地准备在可生产环境中实施 StyleSprint 的生成模型。这一过渡包括设置一个稳定、可扩展和可重复的 Python 环境,这是将我们的生成模型部署到实际环境中自动化生成产品描述的关键步骤。正如所讨论的,我们可以利用 Docker 与 GitHub 及其 持续集成/持续部署CI/CD)功能相结合,为这种生产部署提供一个强大的框架。CI 管道自动化了来自多个贡献者的代码更改的集成到一个共享仓库中。我们将 CI 与 CD 配对,以自动化将我们的代码部署到生产环境。

设置生产环境

到目前为止,我们已经讨论了如何弥合原型和生产环境之间的差距。基于云的环境,如 Google Colab,提供了在生产环境中本身不可用的丰富功能。现在,我们对这些特性有了更好的理解,下一步是实现一个健壮的生产设置,以确保我们的应用程序能够处理现实世界的流量,按需扩展,并保持稳定。

生产环境中的工具和实践与原型环境中的工具和实践有很大不同。在生产中,可扩展性、可靠性、资源管理和安全性变得至关重要,而在原型环境中,模型仅由少数用户用于实验。在生产中,我们可能期望来自组织各个部门的广泛消费。例如,在 StyleSprint 场景中,可能有多个部门或子品牌希望自动化他们的产品描述。

在 StyleSprint 项目的早期阶段,我们可以使用 Docker 和 GitHub 等免费和开源工具来执行容器化、版本控制和 CI 等任务。这些工具由用户社区提供和管理,为我们提供了一个成本效益高的解决方案。随着 StyleSprint 的发展,我们可能会考虑升级到提供高级功能和专业支持的付费或企业版。目前,我们的重点是利用开源版本的功能。接下来,我们将逐步介绍这些工具的实际应用。到那时,我们将准备好部署一个适用于自动产品描述的生产就绪的 模型即服务MaaS)。

本地开发设置

我们首先确保我们可以远程连接到生产环境。我们可以利用一个集成开发环境(IDE),这是一种软件,使我们能够轻松组织代码并远程连接到生产环境。

Visual Studio Code

首先安装 Visual Studio CodeVS Code),这是微软提供的一款免费代码编辑器。由于其集成的 Git 控制、终端和扩展市场,它被广泛使用。它提供了一个有利于编写、测试和调试代码的环境。

项目初始化

接下来,我们设置一个结构化的项目目录以保持代码模块化和有序。我们还将使用 Git 初始化我们的工作目录,这使得我们能够与远程仓库同步代码。正如之前提到的,我们利用 Git 来跟踪代码变更并与他人更顺畅地协作。在 Visual Studio 的终端窗口中,我们可以使用三个简单的命令来初始化项目。我们使用 mkdir 来创建或“制作”一个目录。我们使用 cd 命令来更改目录。最后,我们使用 git init 来使用 Git 初始化我们的项目。请注意,这假设 Git 已经安装。有关安装 Git 的说明可在其网站上找到(https://git-scm.com/)。

mkdir StyleSprint
cd StyleSprint
git init

Docker 设置

现在,我们将继续设置 Docker 容器。Docker 容器是一个隔离的环境,封装了应用程序及其依赖项,确保在不同系统上的一致性操作。为了清晰起见,我们可以简要描述 Docker 的关键方面如下:

  • 容器:这些是包含应用程序及其依赖的可移植单元。

  • 主机操作系统的内核:当 Docker 容器在主机机器上运行时,它利用主机操作系统的内核和资源来操作,但它以隔离的方式操作,既不与主机系统也不与其他容器冲突。

  • Dockerfiles:这些是用于创建容器镜像的脚本。它们作为蓝图,包含运行应用程序所需的一切。这种隔离和打包方法防止应用程序冲突并促进资源的高效使用,简化了开发和部署。

容器化方法将有助于确保一致性和可移植性。例如,假设StyleSprint找到一个基于云的托管提供商,成本效益更高;迁移到新提供商就像迁移几个配置文件一样简单。

我们可以从官方网站安装Docker。Docker提供了易于遵循的安装指南,包括对各种编程语言的支持。

一旦安装了Docker,我们就可以在项目目录中创建一个Dockerfile来指定环境设置。对于GPU支持,我们希望从一个NVIDIA CUDA基础镜像开始。Docker,就像许多其他虚拟化系统一样,使用一个称为镜像的概念来运行。镜像是一个预配置环境的快照,可以用作新项目的起点。在我们的情况下,我们希望从一个集成了CUDA库的快照开始,CUDA是NVIDIA提供的并行处理库。这个库将使虚拟化环境(或容器)能够利用主机机器上安装的任何GPU。利用GPU将加速模型推理。

现在,我们可以继续创建一个具有我们应用程序规格的Dockerfile:

# Use an official NVIDIA CUDA runtime as a base image
FROM nvidia/cuda:11.0-base
# Set the working directory in the container to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Make port 80 available to the world outside this container
EXPOSE 80
# Run app.py when the container launches
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]

此Dockerfile作为Docker构建我们的容器的蓝图。我们从官方的NVIDIA CUDA基础镜像开始,以确保GPU支持。容器中的工作目录设置为/app,然后我们复制项目的所有内容。之后,我们安装requirements.txt文件中列出的必要包。端口80对外部访问我们的应用程序开放。最后,我们指定启动应用程序的命令,该应用程序使用Python解释器运行app.py。这种设置封装了所有必要的组件,包括GPU支持,以确保我们的生成模型在类似生产的环境中高效运行。

需求文件

我们还需要一个方法来跟踪我们的Python特定依赖。容器将包含Python,但不会显示我们的Python应用程序有什么需求。我们可以在项目目录中定义一个requirements.txt文件,明确列出所有必要的Python包来指定这些依赖:

fastapi==0.65.2
torch==1.9.0
transformers==4.9.2
uvicorn==0.14.0

应用程序代码

现在,我们可以为我们的应用程序代码创建一个app.py文件。这是我们编写生成模型代码的地方,利用PyTorch和Transformers等库。为了将我们的模型作为服务公开,我们将使用FastAPI,这是一个用于构建Web API的现代、高性能框架。Web API是一种协议,它使不同的软件应用程序能够通过互联网进行通信和交换数据,允许它们使用彼此的功能和服务。

以下代码片段创建了一个最小化的 API,该 API 将在另一个应用程序或软件请求 /generate/ 端点时提供模型响应。这将使 StyleSprint 能够将其模型作为 Web 服务托管。这意味着其他应用程序(例如,移动应用程序、批处理过程)可以使用简单的 URL 访问模型。我们还可以添加异常处理以提供有关模型产生任何类型错误的 informative 错误消息:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from transformers import pipeline
# Load the pre-trained model
generator = pipeline('text-generation', 
    model='EleutherAI/gpt-neo-2.7B')
# Create the FastAPI app
app = FastAPI()
# Define the request body
class GenerationInput(BaseModel):
prompt: str
# Define the endpoint
@app.post("/generate")
def generate_text(input: GenerationInput):
try:
    # Generate text based on the input prompt
    generated_text = generator(input.prompt, max_length=150)
    return {"generated_text": generated_text}
except:
    raise HTTPException(status_code=500,
        detail="Model failed to generate text")

现在我们已经设置了 Docker 环境,下一步是将应用程序部署到主机服务器。我们可以通过 CI/CD 管道来简化此过程。目标是完全自动化所有部署步骤,包括一系列测试以确保任何代码更改不会引入任何错误。然后我们利用 GitHub Actions 创建一个与代码仓库直接集成的工作流程。

创建代码仓库

在我们能够利用 GitHub 的自动化功能之前,我们需要一个仓库。创建 GitHub 仓库非常简单,按照以下步骤操作:

  1. 注册/登录 GitHub:如果您还没有 GitHub 账户,请在 github.com 上注册。如果您已经有了账户,只需登录即可。

  2. 转到仓库创建页面:点击 GitHub 主页右上角的 + 图标并选择 新建仓库

  3. 填写仓库详情

    • 仓库名称:为您的仓库选择一个名称

    • 描述(可选):添加您仓库的简要描述

    • 可见性:选择 公开(任何人都可以看到此仓库)或 私有(只有您和您邀请的协作者可以看到)

  4. .gitignore 文件或选择一个许可证。一个 gitignore 文件允许我们添加不应上传到仓库的路径或文件类型。例如,Python 会创建对应用程序不关键的临时文件。将 `__pycache__/` 添加到 gitignore 文件中将会自动忽略该目录下的所有内容。

  5. 创建仓库:点击 创建仓库 按钮。

我们的仓库设置完成后,我们可以继续定义我们的 CI/CD 管道来自动化我们的部署。

CI/CD 配置

要创建一个管道,我们需要一个配置文件,该文件概述了部署阶段并指导自动化服务器构建和部署我们的 Docker 容器。让我们看看步骤:

  1. 在我们的 GitHub 仓库中,我们可以在 .github/workflows 目录下创建一个名为 ci-cd.yml 的新文件。GitHub 将自动查找此目录中的任何文件以触发部署。

  2. 打开 ci-cd.yml 并定义以下工作流程:

    name: CI/CD Pipeline
    
    on:
    
      push:
    
        branches:
    
          - main
    
    jobs:
    
      build-and-test:
    
        runs-on: ubuntu-latest
    
      steps:
    
        - name: Checkout code
    
          uses: actions/checkout@v4
    
        - name: Build Docker image
    
      # assumes the Dockerfile is in the root (.)
    
          run: docker build -t stylesprint .
    
        - name: Run tests
    
      # assumes a set of unit tests were defined
    
          run: docker run stylesprint python -m unittest discover
    
    deploy:
    
      needs: build-and-test
    
      runs-on: ubuntu-latest
    
      steps:
    
        - name: Checkout code
    
          uses: actions/checkout@v4
    
        - name: Login to DockerHub
    
          run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
    
        - name: Push Docker image
    
          run: |
    
            docker tag stylesprint:latest ${{ secrets.DOCKER_USERNAME }}/stylesprint:latest
    
            docker push ${{ secrets.DOCKER_USERNAME }}/stylesprint:latest
    

在这个设置中,我们的工作流程由两个主要任务组成:构建和测试以及部署。构建和测试任务负责从代码库检出代码,构建Docker镜像,并执行任何测试。另一方面,依赖于构建和测试完成的部署任务,处理DockerHub登录并将Docker镜像推送到那里。DockerHub,类似于GitHub,是一个专门用于Docker镜像的仓库。

对于与DockerHub进行身份验证,建议将您的DockerHub凭据安全地存储在您的GitHub仓库中。这可以通过在GitHub上导航到您的仓库,点击DOCKER_USERNAMEDOCKER_PASSWORD作为新的仓库密钥来完成。

注意,我们不需要执行任何额外的步骤来执行管道。工作流程设计为在向主分支推送(或上传)时自动触发。回想一下,整个过程依赖于Git模式,其中新更改通过代码的commit或检查-入以及代码更改的push或上传来注册。每当有更改被推送时,我们都可以直接在GitHub仓库的操作选项卡中观察到整个管道的运行情况。

我们现在已经走过了部署我们的模型到生产所需的全部步骤。在所有这些关键的设置都完成之后,我们现在可以回到选择我们项目最佳模型的任务上。目标是找到一个能够有效地为StyleSprint生成吸引人的产品描述的模型。然而,可用的生成模型种类繁多,需要根据我们项目的需求和限制进行深思熟虑的选择。

此外,我们希望选择合适的评估指标,并讨论其他将指导我们为项目做出明智决策的考虑因素。这次探索将使我们具备选择一个不仅表现良好而且与我们的项目目标和已建立的技术基础设施相一致的模式所需的知识。

模型选择——选择合适的预训练生成模型

在上一节中建立了一个最小化生产环境之后,我们现在关注我们项目的关键方面——选择合适的生成模型来生成吸引人的产品描述。模型的选择至关重要,因为它显著影响我们解决方案的有效性和效率。目标是自动化生成StyleSprint各种零售产品的引人入胜且准确的产品描述。通过这样做,我们旨在丰富客户的在线购物体验,同时减轻手动编写独特产品描述的工作量。

我们的目的是选择一个能够熟练处理细微和复杂的文本生成,从而显著加快创建独特、吸引人的产品描述的过程的生成模型,为StyleSprint节省时间和资源。

在选择我们的模型时,重要的是要彻底评估影响其性能和适合项目的各种因素。

达成项目目标

在我们能够选择并应用评估方法到我们的模型选择过程中之前,我们首先应该确保我们理解了项目目标。这包括定义业务问题,识别任何技术限制,识别与模型相关的任何风险,包括模型结果的解释,以及确定任何潜在的不平等对待或偏见的考虑因素:

  • 问题定义:在我们的场景中,目标是创建一系列零售服装的准确和吸引人的描述。随着StyleSprint的产品范围可能扩大,系统应无缝扩展以适应更大的库存,而不会显著增加运营成本。性能预期包括吸引潜在客户的引人入胜的描述,准确性以避免误代表,以及快速生成以保持在线目录的更新。此外,StyleSprint可能会根据用户的购物历史应用个性化的内容描述。这意味着模型可能需要几乎实时地提供产品描述。

  • 技术限制:为了最大化效率,模型API的响应不应有任何明显的延迟(延迟)。系统应能够实时更新在线目录(如有需要),并且硬件应支持快速文本生成,同时不降低质量且保持成本效益,尤其是在产品范围扩大的情况下。

  • 透明度和开放性:通常,来自披露架构和训练数据源的开发商的预训练模型更受欢迎,因为这种透明度使StyleSprint能够清楚地了解与模型使用相关的任何风险或法律影响。此外,使用作为API提供的模型时施加的任何使用限制,如请求或令牌限制,应被理解,因为它们可能会阻碍不断增长的目录的可扩展性。

  • 偏见和公平性:识别和减轻模型输出中的偏见,以确保公平和中立的表现至关重要,尤其是在StyleSprint具有多样化的目标受众的情况下。确保生成的描述具有文化敏感性至关重要。公平的表现确保描述准确且公平地代表产品,无论潜在客户的个人特征或社会背景如何。

  • 预训练的适用性:生成模型的基本预训练在其生成有意义和相关的文本的能力中起着重要作用。研究模型预训练或微调的领域和数据非常重要。在广泛数据集上预训练的模型可能具有多功能性,但可能缺乏特定领域的细微差别。对于StyleSprint来说,一个在时尚相关数据上微调或能够进行此类微调的模型将是最理想的,以确保生成的描述既相关又吸引人。

  • 定量指标:评估StyleSprint生成的产品描述的质量需要结合词汇和语义指标。词汇重叠指标衡量生成文本与参考文本之间的词汇相似度。具体来说,双语评估助手BLEU)强调n-gram精确度,基于主旨的评估辅助工具ROUGE)侧重于n-gram召回率,而具有显式排序的翻译评估指标METEOR)旨在通过考虑同义词和词干实现更平衡的评估。对于上下文和语义评估,我们使用相似性指标来评估生成描述的语义一致性和相关性,通常利用嵌入来表示文本,以捕捉其含义。

我们可以使用诸如对比语言-图像预训练CLIP)等模型进一步细化我们对生成描述与产品图像之间对齐的评估。回想一下,我们在第二章中使用了CLIP来评估字幕与合成图像之间的兼容性。在这种情况下,我们可以应用CLIP来衡量我们的生成描述是否准确地反映了产品的视觉方面。这些评估技术共同为StyleSprint创建有效的产品描述提供了客观的方法:

  • 定性指标:我们引入定性评估来衡量描述的吸引力和创造性等细微差别。我们还希望确保在生成内容中考虑公平性和包容性,这对于避免可能使某些群体感到疏远或冒犯的语言或偏见至关重要。参与度评估的方法可能包括客户调查或A/B测试,这是一种测试两种竞争解决方案的系统方法。此外,让一个多元化的团队审查内容以确保公平性和包容性可以提供宝贵的见解。这些步骤有助于StyleSprint创建吸引人、尊重和包容的产品描述,为所有客户提供欢迎的环境。

  • 可扩展性:运行模型所需的计算资源以及模型随着数据增加而扩展的能力是至关重要的考虑因素。需要大量计算能力的模型可能不适合实时生成产品描述,尤其是在产品系列扩大的情况下。在计算效率和输出质量之间取得平衡对于确保StyleSprint的成本效益和可扩展性至关重要。

  • 定制和微调能力:在特定领域数据上微调或定制模型的能力对于更好地满足品牌特定要求至关重要。探索微调的可用性和易用性可以显著影响生成描述的相关性和质量,确保它们与StyleSprint的品牌身份和产品系列产生共鸣。在实践中,即使应用了高效的方法,一些模型也可能因为需要大量资源而无法进行微调。我们将在下一章中详细探讨微调的考虑因素。

现在我们已经仔细考虑了如何将模型与项目目标对齐,我们几乎准备好评估我们的初始模型选择与其他几个模型,以确保我们做出正确的选择。然而,在基准测试之前,我们应该花时间理解模型选择过程中的一个重要方面:模型大小和计算复杂度。

模型大小和计算复杂度

生成模型的大小通常由其参数数量来描述。模型中的参数是在训练过程中根据训练数据微调的内部变量。在生成模型中使用的神经网络背景下,参数通常指的是通过训练调整的权重和偏差,以最小化预测输出与实际目标之间的差异。

此外,具有更多参数的模型可以捕捉数据中的更复杂模式,通常在手头任务上表现更好。虽然较大的模型在生成文本的质量方面通常表现更好,但存在一个收益递减的点,超过这个点,增加模型大小只会带来微小的改进。此外,增加的大小也带来了一系列挑战:

  • 计算复杂度:更大的模型在训练和推理阶段(模型用于根据学习到的参数进行预测或生成新数据)都需要更多的计算能力和内存。这可能会显著增加训练和使用模型的成本和时间,使其不太适合实时应用或资源受限的环境。

    模型中的参数数量显著影响模型的计算复杂度。模型中的每个参数都是一个变量,在计算过程中必须存储在内存中,无论是训练还是推理阶段。以下是关于计算需求的一些具体考虑因素:

    • 内存和存储:模型在内存中的总大小是参数数量和每个参数大小(通常是32位或64位浮点数)的乘积。例如,一个拥有1亿个参数的模型,每个参数由32位浮点数表示,将需要大约400MB的内存(1亿 * 32位 = 4亿位 = 400MB)。现在考虑一个更大的模型,比如说有100亿个参数;内存需求跃升至40GB(100亿 * 32位 = 400亿位 = 40GB)。这个需求仅针对参数,并不包括模型操作所需的其他数据和开销。

    • 加载到内存中:当模型用于推理时,其参数必须加载到运行该模型的机器的RAM中。对于一个拥有100亿个参数的大型模型,你需要一台具有足够RAM的机器来容纳整个模型,以及额外的内存来处理操作开销、输入数据和生成的输出。假设模型太大而无法装入内存。在这种情况下,可能需要将其分片或分布到多台机器上,或者分部分加载,这可能会显著增加模型的部署和操作复杂性,并增加生成输出的延迟。

  • 专用硬件需求:较大的模型需要专用硬件,例如强大的GPU或TPU,这可能会增加项目成本。正如讨论的那样,具有大量参数的模型需要强大的计算资源来进行训练和推理。GPU和TPU等硬件加速器通常被用于满足这些需求。这些硬件加速器旨在处理神经网络计算中固有的矩阵乘法和其他操作所需的并行计算能力,与传统中央处理单元CPU)相比,可以显著加快处理速度。

    基于云的基础设施可以减轻设置复杂性,但通常具有基于使用量的定价。在细粒度上理解基础设施成本对于确保StyleSprint保持在预算内至关重要。

  • 延迟:我们简要讨论了延迟,但重要的是要重申,较大的模型通常具有更高的延迟,这可能会成为需要实时响应的应用程序的问题。在我们的案例中,我们可以异步处理描述作为批次。然而,StyleSprint可能有需要快速完成的项目,要求批次在数小时内而不是数天内完成。

在StyleSprint的情况下,必须仔细评估模型性能和大小之间的权衡,以确保最终模型满足项目的性能要求,同时保持在预算和硬件约束范围内。StyleSprint希望提供近乎实时的响应来提供个性化的描述,这通常意味着一个更小的模型,具有更低的计算复杂度。然而,模型保持高度准确并与品牌的声音和语气标准保持一致也很重要,这可能需要在一个更大的数据集上训练或微调更大的模型。在实践中,我们可以通过基准测试来评估模型相对于大小和复杂度的性能。

基准测试

基准测试是一种系统性的过程,用于评估不同生成模型相对于预定义标准的表现。这个过程涉及在多个指标上比较模型,以了解它们的优点、缺点以及适合项目的程度。这是一种基于观察的经验方法,用于获取模型在相似条件下表现的数据,提供可以指导模型选择决策过程的信息。

在StyleSprint场景中,基准测试可以是一项非常有价值的练习,帮助我们权衡模型大小、计算复杂度以及生成描述的准确性和创造力之间的取舍。

在我们的基准测试练习中,我们可以回到Google Colab原型环境,快速加载各种生成模型,并通过测试来评估它们的性能,这些测试是根据前几节中概述的考虑因素设计的,例如计算效率和文本生成质量。一旦我们完成了评估和比较,我们可以在我们的生产应用程序代码中进行一些简单的更改,它将自动重新部署。基准测试将在衡量描述质量相对于模型大小和复杂度的质量方面发挥关键作用。请记住,我们将从多个维度衡量质量和整体模型性能,包括与人类编写的“黄金标准”描述的词汇和语义相似性,以及由不同群体评审员进行的定性评估。

下一步是回顾并调整我们的原始原型代码,以包括一些挑战者模型并应用评估指标。

更新原型环境

对于我们的评估步骤,我们对原始的Google Colab实验设置进行了一些关键性的调整。首先,我们希望确保利用性能加速。Google Colab通过GPU或TPU环境提供加速。对于这次实验,我们将利用GPU。我们还将从Transformers库过渡到一个稍微更通用的库,例如Langchain,这样我们可以测试开源模型如GPT-Neo和商业模型如GPT-3.5。

GPU配置

确保您启用了 GPU 以获得更好的性能。回到 Google Colab,我们可以按照以下步骤启用 GPU 加速:

  1. 在顶部菜单中点击运行时(见图 4.2):

图 4.2:运行时下拉菜单

图 4.2:运行时下拉菜单

  1. 从下拉菜单中选择更改运行时类型,如图中所示。

  2. 在弹出窗口中,从硬件加速器下拉菜单中选择GPU(见图 4.3):

图 4.3:选择 GPU 并点击保存

图 4.3:选择 GPU 并点击保存

  1. 点击保存

现在您的笔记本已设置好使用 GPU,以显著加快基准测试过程所需的计算。您可以使用以下代码片段来验证 GPU 的可用性:

# Verify GPU is available
import torch
torch.cuda.is_available()

此代码片段将返回 True 如果有 GPU 可用,否则返回 False。此设置确保您拥有进行各种生成模型基准测试所需的计算资源。在处理大型模型和大量计算时,使用 GPU 将至关重要。

使用 LangChain 加载预训练模型

在我们的第一个简单实验中,我们依赖于 Transformers 库来加载 GPT 的开源版本。然而,对于我们的基准测试练习,我们希望评估 GPT 的零售版本以及开源模型。我们可以利用 LangChain,这是一个功能丰富的库,它提供了一个简化的接口,可以访问来自 Hugging Face 等提供商的开源模型以及 OpenAI 的 GPT-3.5 等封闭源模型。LangChain 提供了一个统一的 API,通过标准化简化了基准测试和比较。以下是完成此操作的步骤:

  1. 安装必要的库:我们首先在我们的 Colab 环境中安装所需的库。LangChain 简化了与 OpenAI 和 Hugging Face 上托管的模型交互。

    !pip -q install openai langchain huggingface_hub
    
  2. 设置凭据:我们从 OpenAI 获取访问 GPT-3、GPT-4 或我们选择的任何封闭源模型所需的凭据。我们还提供 Hugging Face Hub 的凭据,该 Hub 托管着超过 350,000 个开源模型。我们必须安全地存储这些凭据,以防止任何未经授权的访问,尤其是在模型使用涉及成本的情况下。

    import os
    
    os.environ['OPENAI_API_KEY'] = 'your_openai_api_key_here'
    
    os.environ['HUGGINGFACEHUB_API_TOKEN'] = 
    
        'your_huggingface_token_here'
    
  3. 加载模型:使用 LangChain,我们可以快速加载模型并生成响应。以下示例演示了如何从 Hugging Face 加载 GPT-3 和 GPT-Neo:

    !pip install openai langchain[llms] huggingface_hub
    
    from langchain.llms import OpenAI, HuggingFaceHub
    
    # Loading GPT-3
    
    llm_gpt3 = OpenAI(model_name='text-davinci-003',
    
                      temperature=0.9,
    
                      max_tokens = 256)
    
    # Loading Neo from Hugging Face
    
    llm_neo = HuggingFaceHub(repo_id=' EleutherAI/gpt-neo-2.7B',
    
                             model_kwargs={"temperature":0.9}
    
    )
    

注意,我们已经加载了两个在大小上显著不同的模型。正如模型签名所暗示的,GPT-Neo是在27亿个参数上训练的。同时,根据OpenAI提供的信息,Davinci是在1750亿个参数上训练的。正如讨论的那样,一个显著更大的模型预计会捕捉到更复杂的模式,并且可能会优于较小的模型。然而,这些非常大的模型通常由主要提供商托管,并且具有更高的使用成本。我们将在稍后重新审视成本考虑因素。现在,我们可以继续到下一步,即准备我们的测试数据。我们的测试数据应该为模型性能提供一个基准,这将告知成本与性能之间的权衡。

设置测试数据

在这个背景下,测试数据应包括来自StyleSprint网站的产品属性(例如,可用颜色、尺寸、材料等)以及StyleSprint团队编写的现有产品描述。人工编写的描述作为“真实情况”,或比较模型生成的描述的标准。

我们可以通过从电子商务网站抓取数据或使用StyleSprint数据库中的预收集数据集来收集产品数据。我们还应该确保收集到多样化的产品,以测试模型在不同类别和风格上的能力。根据共享特征将数据划分为不同的组或段的过程通常被称为分段。了解模型在各个段上的行为应该会给我们一个指示,即它在整个产品系列中表现如何。为了本例的目的,产品数据在本书的GitHub配套资源中提供(https://github.com/PacktPublishing/Generative-AI-Foundations-in-Python)。

让我们看看我们如何提取相关信息以进行进一步处理:

import pandas as pd
# Assume `product_data.csv` is a CSV file with product data
# The CSV file has two columns: 'product_image' and 
# 'product_description' 
# Load the product data
product_data = pd.read_csv('product_data.csv')
# Split the data into testing and reference sets
test_data = product_data.sample(frac=0.2, random_state=42)
reference_data = product_data.drop(test_data.index)
# Checkpoint the testing and reference data
test_data.to_csv('test_data.csv', index=False)
reference_data.to_csv('reference_data.csv', index=False)
# Extract reference descriptions and image file paths
reference_descriptions = /
    reference_data['product_description'].tolist()
product_images = reference_data['product_image'].tolist()

我们还必须以使产品数据准备好输入到描述生成模型中的方式对其进行格式化。这可能是仅产品标题或产品属性的组合:

# Assume `product_metadata` is a column in the data that contains the collective information about the product including the title of the product and attributes.
# Format the input data for the models
model_input_data = reference_data['product_metadata].tolist()
reference_descriptions = \
    reference_data['product_description'].tolist()

最后,我们将要求模型使用每个模型生成一批产品描述。

from langchain import LLMChain, PromptTemplate
from tqdm.auto import tqdm
template = """
Write a creative product description for the following product: {product_metadata}
"""
PROMPT = PromptTemplate(template=template, 
    input_variables=["product_metadata"])
def generate_descriptions(
    llm: object, 
    prompt: PromptTemplate = PROMPT
) -> list:
    # Initialize the LLM chain
    llm_chain = LLMChain(prompt=prompt, llm=llm)
    descriptions = []
    for i in tqdm(range(len(model_input_data))):
        description = llm_chain.run(model_input_data[i])
        descriptions.append(description)
    return descriptions
gpt3_descriptions = generate_descriptions(llm_gpt3)
gptneo_descriptions = generate_descriptions(llm_neo)

现在,随着测试数据集的建立,我们有了结构化的产品信息、参考描述和图像数据集,这些数据集已准备好用于评估步骤。

定量指标评估

现在我们已经利用Langchain加载了多个模型并准备好了测试数据,我们准备开始应用评估指标。这些指标捕捉了准确度和与产品图片的匹配度,并将帮助我们评估模型生成产品描述与人类相比的效果如何。正如讨论的那样,我们专注于两个类别的指标,即词汇和语义相似度,这些指标提供了使用相同单词的数量以及人类和AI生成的产品描述中共同语义信息的度量。

在下面的代码块中,我们应用BLEUROUGEMETEOR来评估生成文本与参考文本之间的词汇相似度。每个指标都有基于参考的假设。这意味着每个指标都假设我们是在与人类参考进行比较。我们已经为各种产品预留了参考描述(或黄金标准),以便与生成描述进行并排比较。

!pip install rouge sumeval nltk
# nltk requires an additional package
import nltk
nltk.download('wordnet')
 from nltk.translate.bleu_score import sentence_bleu
from rouge import Rouge
from sumeval.metrics.rouge import RougeCalculator
from nltk.translate.meteor_score import meteor_score
def evaluate(
    reference_descriptions: list, 
    generated_descriptions: list
) -> tuple:
    # Calculating BLEU score
    bleu_scores = [
        sentence_bleu([ref], gen) 
        for ref, gen in zip(reference_descriptions, generated_descriptions)
    ]
    average_bleu = sum(bleu_scores) / len(bleu_scores)
    # Calculating ROUGE score
    rouge = RougeCalculator()
    rouge_scores = [rouge.rouge_n(gen, ref, 2) for ref,
        gen in zip(reference_descriptions,
        generated_descriptions)]
    average_rouge = sum(rouge_scores) / len(rouge_scores)
    # Calculating METEOR score
    meteor_scores = [ meteor_score([ref.split() ],
        gen.split()) for ref,
        gen in zip(reference_descriptions,
        generated_descriptions)]
    average_meteor = sum(meteor_scores) / len(meteor_scores)
    return average_bleu, average_rouge, average_meteor
average_bleu_gpt3, average_rouge_gpt3, average_meteor_gpt3 = \
    evaluate(reference_descriptions, gpt3_descriptions)
print(average_bleu_gpt3, average_rouge_gpt3, average_meteor_gpt3)
average_bleu_neo, average_rouge_neo, average_meteor_neo = \
    evaluate(reference_descriptions, gptneo_descriptions)
print(average_bleu_neo, average_rouge_neo, average_meteor_neo)

我们可以使用句子嵌入来评估生成描述的语义连贯性和相关性:

!pip install sentence-transformers
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer('paraphrase-MiniLM-L6-v2')
def cosine_similarity(reference_descriptions, generated_descriptions):
    # Calculating cosine similarity for generated descriptions
    cosine_scores = [util.pytorch_cos_sim(
        model.encode(ref), model.encode(gen)) for ref,
        gen in zip(reference_descriptions,
        generated_descriptions)]
    average_cosine = sum(cosine_scores) / len(cosine_scores)
    return average_cosine
average_cosine_gpt3 = cosine_similarity(
    reference_descriptions, gpt3_descriptions)
print(average_cosine_gpt3)
average_cosine_neo = cosine_similarity(
    reference_descriptions, gptneo_descriptions)
print(average_cosine_neo)

与CLIP的匹配

我们再次利用CLIP模型来评估生成产品描述与相应图像之间的匹配度,类似于我们在第二章中的方法。CLIP模型擅长关联视觉和文本内容,对每个产品图像及其关联的生成和参考描述进行一致性评分。参考描述作为准确性的基准。这些分数为我们生成模型在生成与产品图像匹配良好的描述方面的有效性提供了定量度量。以下是从一个处理生成描述及其对应图像以生成CLIP分数的组件中摘录的一段代码。完整的组件代码(包括图像预处理)可在本书GitHub仓库的第4章文件夹中找到,网址为https://github.com/PacktPublishing/Generative-AI-Foundations-in-Python)。

clip_model = "openai/clip-vit-base-patch32"
def clip_scores(images, descriptions,
                model=clip_model,
                processor=clip_processor
):
    scores = []
    # Process all images and descriptions together
    inputs = process_inputs(processor, descriptions, images)
    # Get model outputs
    outputs = model(**inputs)
    logits_per_image = outputs.logits_per_image # Image-to-text logits
    # Diagonal of the matrix gives the scores for each image-description pair
    for i in range(logits_per_image.size(0)):
        score = logits_per_image[i, i].item()
    scores.append(score)
    return scores
reference_images = [
    load_image_from_path(image_path) 
    for image_path in reference_data.product_image_path
]
gpt3_generated_scores = clip_scores(
    reference_images, gpt3_descriptions
)
reference_scores = clip_scores(
    reference_images, reference_descriptions
)
# Compare the scores
for i, (gen_score, ref_score) in enumerate(
    zip(gpt3_generated_scores, reference_scores)
):
    print(f"Image {i}: Generated Score = {gen_score:.2f}, 
        Reference Score = {ref_score:.2f}")

在使用CLIP模型评估产品描述时,为每个图像-描述对生成的匹配分数是相对于批次中的其他描述来计算的。本质上,CLIP评估特定描述(无论是生成的还是参考的)与给定图像相比,与其他批次内描述的匹配程度如何。例如,分数为33.79表示该描述与图像的匹配度比批次中其他描述与该图像的匹配度高出33.79%。在对比参考时,我们期望基于生成描述的分数应与基于参考描述的分数紧密匹配。

现在我们已经计算了与参考分数的词汇和语义相似性,以及图像与生成的描述相对于参考描述的对齐程度,我们可以全面评估我们的模型并解释定量评估的结果。

解释结果

我们从词汇相似性开始,这为我们提供了参考描述和生成的描述在措辞和关键词之间的相似性指示:

BLEU ROUGE METEOR
GPT-3.5 0.147 0.094 0.261
GPT-Neo 0.132 0.05 0.059

表4.2:词汇相似性

在评估GPT-3.5和GPT-Neo模型生成的文本时,我们使用了几个词汇相似性指标:BLEU、ROUGE和METEOR。BLEU分数,评估匹配短语的精确度,显示GPT-3.5(0.147)略优于GPT-Neo(0.132)。ROUGE分数,侧重于内容的召回率,表明GPT-3.5(0.094)比GPT-Neo(0.05)更好地捕捉了参考内容。METEOR分数,结合了精确度和召回率以及同义词匹配,显示GPT-3.5(0.261)在GPT-Neo(0.059)之上具有显著优势。总体而言,这些指标表明,与GPT-Neo相比,GPT-3.5生成的文本在词汇选择和内容覆盖方面与参考标准更为接近。

接下来,我们评估语义相似性,这衡量了生成的文本的意义与参考文本的意义之间的接近程度。这种评估不仅超越了单纯的词对词匹配,还考虑了句子的上下文和整体意图。语义相似性评估了生成的文本在多大程度上捕捉到了参考文本中存在的细微差别、概念和主题,从而提供了对模型理解和复制更深层次语义意义的洞察:

模型 平均 余弦相似度
GPT-3.5 0.8192
GPT-Neo 0.2289

表4.3:语义相似性

平均余弦相似度分数揭示了两个模型在语义相似性方面的显著差异。GPT-3.5显示出与参考文本的高度语义一致性。GPT-Neo的显著较低分数表明相对较差的性能,这表明生成的描述与人类编写的描述在本质上存在差异。

最后,我们回顾CLIP分数,这些分数告诉我们生成的描述与相应图像在视觉上的对齐程度。这些分数来自一个训练有素以理解和关联视觉和文本数据的模型,提供了文本在代表视觉内容的相关性和准确性的度量。高CLIP分数表明文本与图像之间存在强烈的关联,这表明生成的描述不仅在文本上连贯,而且在上下文中适当且视觉描述性强:

模型 平均CLIP 参考delta
GPT-3.5 26.195 2.815
GPT-Neo 22.647 6.363

表 4.4:GPT-3.5 和 GPT-Neo 模型的比较 CLIP 分数分析

我们从参考描述中计算了 CLIP 分数,这些分数代表了一组基准描述与相应图像之间的平均对齐分数。然后我们为每个模型计算了 CLIP 分数并分析了差异。与我们的其他指标一起,GPT-3.5 在与 GPT-Neo 对齐方面具有明显优势,更接近参考描述。

总体而言,GPT-3.5 在所有定量指标上似乎都显著优于 GPT-Neo。然而,值得注意的是,GPT-3.5 的成本更高,通常比 GPT-Neo 的延迟更高。在这种情况下,StyleSprint 团队将进行定性分析,以准确确定 GPT-Neo 描述是否与品牌指南和期望不一致,从而使使用更好模型的成本变得值得。正如所讨论的,这里的权衡并不明显。StyleSprint 必须仔细考虑,尽管使用像 GPT-3.5 这样的商品不会直接产生计算成本,但随着模型使用量的增加,按需成本可能会显著增加。

两个模型的对比优势提出了一个决策挑战。虽然一个在性能指标和 CLIP 对齐方面明显优于另一个,这暗示着更高的准确性和语义正确性,而另一个在资源效率和可扩展性方面显著更高,这对于成本效益至关重要。在这个阶段,评估模型结果并进行利益相关者参与以帮助理解组织优先级变得至关重要。

考虑到这些因素,我们将重新审视定性考虑因素,如透明度、偏见和公平性,以及它们如何在部署负责任和有效的 AI 系统的更广泛画面中发挥作用。

负责任的 AI 考虑因素

解决 AI 系统中的隐含或隐蔽的社会偏见对于确保负责任的 AI 部署至关重要。尽管一个简单的产品描述如何引入偏见可能并不明显,但使用的语言可能会无意中强化刻板印象或排除某些群体。例如,那些持续将某些体型或肤色与某些产品关联的描述,或是不必要地使用性别语言,可能会无意中延续社会偏见。然而,通过结构化的缓解方法,包括算法审计、增加模型透明度和利益相关者参与,StyleSprint 可以确保其品牌促进公平和包容。

解决和缓解偏见

我们提出了几个考虑因素,如 Costanza-Chock 等人在 谁审计审计员?算法审计生态系统现场扫描的建议 中所建议的:

  • 专业环境考察:创建一个支持性的专业环境对于解决算法公平性至关重要。实施举报人保护措施可以促进安全地报告偏见和不公平行为,同时建立个人报告伤害的过程,以确保这些担忧得到积极解决。

  • 定制与标准化审计框架:虽然定制审计框架是预期的,但考虑标准化方法可能会增强偏见缓解工作的严谨性和透明度。与外部审计实体合作可能提供对StyleSprint的AI系统的无偏见评估,与Costanza-Chock等人(2022年)的观察结果相一致。

  • 关注公平而非仅仅平等:公平观念承认不同的需求,这对于公平的全面方法至关重要。进行交叉和少数群体分析可以帮助你理解和解决超出法律保护类别之外的偏见。

  • 披露和透明度:披露审计方法和结果可以培养透明度和持续改进的文化。官方发布的审计可以帮助你建立最佳实践并获得利益相关者的信任。

  • 混合方法分析:正如所展示的,技术和定性分析的结合可以提供对系统公平性的全面视角。与非技术利益相关者合作可以强调定性分析。

  • 社区和利益相关者参与:再次强调,在审计中涉及多样化的群体和领域专家可以确保在偏见缓解工作中考虑多样化的观点。与利益相关者建立反馈循环可以促进持续改进。

  • 持续学习和改进:关注关于AI公平性的新兴标准和最佳实践对于持续改进至关重要。培养学习文化有助于适应不断变化的公平挑战和监管环境,从而确保StyleSprint的AI系统在一段时间内保持公平和负责任。

透明度和可解释性

通常,机器学习中的可解释性指的是理解模型内部机制的能力,阐明它是如何根据给定的输入做出决策或预测的。然而,在生成模型中实现可解释性可能要复杂得多。正如所讨论的,与判别性机器学习模型不同,生成模型没有学习决策边界的目标,它们也不反映特征或输入特征与预测之间的直接映射的明确概念。这种基于特征的决策缺失使得传统的可解释性技术对于GPT-4等生成基础模型无效。

或者,我们可以采用一些务实的透明度实践,例如制作清晰且对所有相关利益相关者可访问的文档,以促进对模型能力和使用的共同理解和期望。

可解释性的主题是一个需要关注的重点领域,尤其是在生成模型变得更加复杂,其结果越来越难以合理化时,这可能会带来未知的风险影响。

来自Anthropic、OpenAI和其他机构的令人有希望的研究表明,稀疏自动编码器——一次只激活几个神经元的神经网络——可以促进识别抽象和可理解的模式。这种方法可以通过突出与人类概念一致的特征来帮助解释网络的行为。

最终部署

假设我们已经仔细收集了关于最适合这项工作的模型的数量和质量反馈,我们可以选择我们的模型并更新我们的生产环境以部署和提供服务。我们将继续使用FastAPI来创建一个网络服务器以提供服务,并使用Docker来容器化我们的应用程序。然而,现在我们已经了解了LangChain的简单性,我们将继续利用其简化的接口。我们现有的CI/CD管道将确保流畅的自动部署和持续的应用程序监控。这意味着部署我们的模型就像检查我们的最新代码一样简单。我们首先更新我们的依赖项列表:

  1. 在您的项目中的requirements.txt文件中包含必要的库:

    fastapi==0.68.0
    
    uvicorn==0.15.0
    
    openai==0.27.0
    
    langchain==0.1.0
    
  2. 更新Dockerfile:修改您的Dockerfile以确保安装更新的要求,并正确设置运行LangChain与FastAPI的环境:

    # Use an official Python runtime as a base image
    
    FROM python:3.8-slim-buster
    
    # Set the working directory in the container to /app
    
    WORKDIR /app
    
    # Copy the current directory contents into the container at /app
    
    COPY . /app
    
    # Install any needed packages specified in requirements.txt
    
    RUN pip install --no-cache-dir -r requirements.txt
    
    # Make port 80 available to the world outside this container
    
    EXPOSE 80
    
    # Define environment variable
    
    ENV NAME World
    
    # Run app.py when the container launches
    
    CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]
    
  3. 更新FastAPI应用程序:修改您的FastAPI应用程序以利用Langchain与GPT-3.5交互。确保您的OpenAI API密钥安全存储且可供应用程序访问:

    from fastapi import FastAPI, HTTPException, Request
    
    from langchain.llms import OpenAI
    
    import os
    
    # Initialize FastAPI app
    
    app = FastAPI()
    
    # Setup Langchain with GPT-3.5
    
    llm = OpenAI(model_name='text-davinci-003',
    
                 temperature=0.7,
    
                 max_tokens=256,
    
                 api_key=os.environ['OPENAI_API_KEY'])
    
    @app.post("/generate/")
    
    async def generate_text(request: Request):
    
        data = await request.json()
    
        prompt = data.get('prompt')
    
        if not prompt:
    
            raise HTTPException(status_code=400,
    
                detail="Prompt is required")
    
        response = llm(prompt)
    
        return {"generated_text": response}
    

测试和监控

模型部署后,进行必要的测试以确保设置按预期工作。继续监控系统性能、错误和其他关键指标,以确保可靠运行。

到目前为止,我们已经更新了我们的生产环境以部署和提供GPT-3.5,这有助于根据通过FastAPI应用程序接收到的提示生成文本。这种设置确保了我们的新生成模型的可扩展性、可维护性和安全性部署。然而,我们也应该探索一些关于应用程序可靠性的最佳实践。

维护和可靠性

在StyleSprint部署中保持可靠性至关重要。随着我们使用Langchain、FastAPI、Docker和CI/CD,设置监控、警报、自动修复和故障转移机制是必不可少的。本节概述了一种可能的方案,以确保生产环境中的持续运行和稳健性:

  • 监控工具:在CI/CD管道中集成监控工具,以持续跟踪系统性能和模型指标。这一步对于主动识别和纠正问题至关重要。

  • 警报机制:建立警报机制,以便在检测到异常或问题时通知维护团队。准确调整警报阈值对于早期发现问题并最小化误报至关重要。

  • 自动修复:利用Kubernetes的自愈功能和由某些警报触发的自定义脚本进行自动修复。这种设置旨在自主解决常见问题,减少对人工干预的需求。

  • 故障转移机制:通过设置辅助服务器和数据库来实现故障转移机制。在主服务器故障的情况下,这些辅助设置接管以确保服务连续可用。

  • 通过CI/CD进行定期更新:使用CI/CD管道来管理、测试和部署LangChain、FastAPI或其他堆栈组件的更新。此过程保持部署更新和安全,显著减少维护负担。

通过仔细处理这些领域的每一个,你将为可靠的StyleSprint部署打下坚实的基础。

摘要

本章概述了将StyleSprint生成式AI原型过渡到为电子商务平台创建引人入胜的产品描述的生产就绪部署的过程。它从使用Docker、GitHub和CI/CD管道设置一个健壮的Python环境开始,以实现高效的依赖管理、测试和部署。然后,重点转向选择合适的预训练模型,强调与项目目标的一致性、计算考虑因素和负责任的AI实践。此选择依赖于定量基准测试和定性评估。然后,我们概述了使用FastAPI和LangChain部署所选模型的过程,确保可扩展和可靠的生产环境。

按照本章概述的策略,将为团队提供必要的见解和步骤,以成功地将他们的生成式AI原型过渡到可维护且增值的生产系统。在下一章中,我们将探讨微调及其在LLMs中的重要性。我们还将权衡决策过程,讨论在何种情况下微调比零样本或少量样本提示更有益。

第二部分:生成式AI的实际应用

本部分重点介绍生成式AI的实际应用,包括针对特定任务微调模型、理解领域自适应、掌握提示工程以及处理伦理考量。它旨在提供实际见解和方法,以有效地在各种环境中实施和利用生成式AI,并重点关注负责任的采用。

本部分包含以下章节:

  • 第5章, 针对特定任务微调生成模型

  • 第6章, 理解大型语言模型中的领域自适应

  • 第7章, 掌握提示工程的基本原理

  • 第8章, 处理伦理考量并规划通往可信生成式AI的道路

第五章:微调生成模型以适应特定任务

在我们的StyleSprint叙事中,我们描述了使用预训练的生成AI模型来创建引人入胜的产品描述。虽然这个模型在生成多样化内容方面表现出色,但StyleSprint不断变化的需求要求我们转变关注点。新的挑战不仅在于产生内容,还在于参与特定、面向任务的交互,例如自动回答客户对所描述产品的具体问题。

在本章中,我们介绍了微调的概念,这是将预训练模型适应特定下游任务的关键步骤。对于StyleSprint来说,这意味着将模型从多才多艺的内容生成器转变为能够提供准确和详细回答客户问题的专业工具。

我们将探索和定义一系列可扩展的微调技术,并将它们与其他方法如上下文学习进行比较。我们将展示高级微调方法,包括参数高效的微调和提示微调,以展示它们如何能够微调模型在特定任务如问答上的能力。

到本章结束时,我们将训练一个语言模型来回答问题,并以符合StyleSprint品牌指南的方式回答。然而,在我们探讨微调的机制及其在我们应用中的重要性之前,我们将回顾LLMs背景下微调的历史。

基础和相关性——微调简介

微调是将在大数据集上预训练的模型继续在较小、特定任务的数据集上进行训练的过程,以提高其在该任务上的性能。它还可能涉及适应新领域细微差别的额外训练。后者被称为领域自适应,我们将在第6章中介绍。前者通常被称为特定任务微调,它可以执行多个任务,包括问答、摘要、分类等。对于本章,我们将专注于特定任务微调,以提高通用模型在回答问题时表现的能力。

对于StyleSprint来说,将模型微调以处理特定任务,如回答客户关于产品的询问,引入了独特的挑战。与主要涉及使用现成的预训练模型进行语言生成的产品描述不同,回答客户问题要求模型对产品特定数据有广泛的理解,并且应该有一个品牌意识的声音。具体来说,模型必须准确解释和回答有关产品功能、尺寸、可用性、用户评论和其他许多细节的问题。它还应该产生与StyleSprint独特的品牌语调一致的答案。这项任务需要从预训练中获得的一般自然语言能力以及关于产品元数据和客户反馈的稳健知识,这些是通过微调实现的。

模型如GPT最初通过无监督学习过程学习预测文本,这个过程涉及在广泛和庞大的数据集上进行训练。这个预训练阶段使模型接触到各种文本,使其能够获得对语言的广泛理解,包括语法、语法和上下文,而不需要任何特定任务导向的指导。然而,微调通过任务导向的监督学习来细化模型的能力以完成特定任务——具体来说,是半监督学习,正如Radford等人(2018年)所描述的,它涉及通过使模型接触到包含输入序列(x1,...,xm)和相应标签(y)的数据集来调整模型以适应特定的监督任务。

在本章中,我们将详细介绍微调过程,包括如何选择性地在产品相关信息和客户互动的精选数据集上训练模型,使其能够以客户期望的知情、品牌一致的方式做出回应。然而,微调具有数十亿参数的LLM通常需要大量的资源和时间。这就是高级技术如参数高效微调PEFT)在使微调变得可访问方面变得特别有价值。

PEFT

随着模型规模的增大,传统的微调方法变得越来越不切实际,因为训练和更新所有模型参数需要巨大的计算资源和时间。对于大多数企业,包括大型组织,传统的微调方法成本高昂,实际上无法启动。

另一种方法是仅修改模型参数的小子集,在保持最先进性能的同时减少计算负担。这种方法在将大型模型适应特定任务而不进行大量重新训练时具有优势。

有一种PEFT方法叫做低秩自适应LoRA),由Hu等人(2021年)开发。

LoRA

LoRA方法专注于选择性地微调Transformer架构中的特定组件,以增强LLMS的效率和有效性。LoRA针对Transformer的自注意力模块中发现的权重矩阵,正如在第3章中讨论的那样,这些矩阵是其功能的关键,包括四个矩阵:wq(查询)、wk(键)、wv(值)和wo(输出)。尽管这些矩阵在多头注意力设置中可以被分为多个头——其中每个代表几个并行注意力机制之一,这些机制独立处理输入——但LoRA将它们视为单个矩阵,简化了调整过程。

LoRA的方法仅涉及调整下游任务的注意力权重,而Transformer的另一个组件——前馈网络FFN)中的权重保持不变。专注于仅调整注意力权重并冻结FFN的决定是为了简化并提高参数效率。通过这样做,LoRA确保了一个更易于管理和资源高效的微调过程,避免了重新训练整个网络的复杂性和需求。

这种选择性的微调策略使得LoRA能够有效地调整模型以适应特定任务,同时保持预训练模型的整体结构和优势。这使得LoRA成为将LLMs适应新任务的一种实用解决方案,减少了计算负担,无需在整个模型中进行全面的参数更新(刘等人,2021)。

建立在LoRA基础之上,自适应低秩调整AdaLoRA),如刘等人(2022)在研究中介绍的那样,代表了PEFT方法的一个进一步发展。LoRA和AdaLoRA之间的关键区别在于(正如其名称所暗示的)其适应性。虽然LoRA在模型微调过程中应用了一致的低秩方法,但AdaLoRA则根据每一层的需要调整更新,提供了一种更灵活且可能更有效的微调大型模型以适应特定任务的方法。

AdaLoRA

AdaLoRA的关键创新在于其对预训练模型权重矩阵中参数预算的自适应分配。许多PEFT方法倾向于将参数预算平均分配到所有预训练权重矩阵中,可能忽略了不同权重参数的不同重要性。AdaLoRA通过为这些权重矩阵分配重要性分数并相应地分配参数预算来克服这一点。在AdaLoRA的上下文中,重要性分数是用于确定模型中不同权重参数的重要性(或重要性)的指标,在微调期间更有效地指导参数预算的分配。

注意

参数预算是指在预训练模型微调过程中可以引入的额外参数数量的预定义限制。此预算的设置是为了确保模型的复杂性不会显著增加,这可能导致诸如过拟合、增加计算成本和更长的训练时间等挑战。

此外,AdaLoRA将奇异值分解SVD)应用于在模型微调过程中进行的增量更新的有效组织。SVD允许有效地剪枝与不太重要的更新相关的奇异值,从而减少微调所需的总体参数预算。值得注意的是,这种方法还避免了需要计算密集型精确计算的需求,使得微调过程更加高效。

AdaLoRA已在包括自然语言处理、问答和自然语言生成在内的多个领域进行了实证测试。广泛的实验已经证明了它在提高模型性能方面的有效性,尤其是在问答任务中。AdaLoRA的适应性和效率使其成为需要精确和高效模型调整以完成复杂任务的理想选择。

在StyleSprint的情况下,AdaLoRA提供了一个机会,可以在不产生传统微调所必需的大量开销的情况下微调其语言模型以回答客户问题,传统微调需要调整所有模型参数。通过采用AdaLoRA,StyleSprint可以高效地通过调整显著较少的参数来适应其模型以处理细微的客户询问。具体来说,AdaLoRA对参数预算的适应性分配意味着StyleSprint可以在不使用大量计算资源的情况下优化其模型以适应客户查询的具体细微差别。

到本章结束时,我们将使用AdaLoRA微调一个LLM以完成我们的问答任务。然而,我们首先应该决定微调是否真的是正确的做法。基于提示的LLM提供了一个可行的替代方案,称为上下文学习,其中模型可以从提示中给出的示例中学习,这意味着提示将包含客户的提问以及一些关键的历史示例,说明其他问题的回答方式。模型可以从这些示例中推断出如何以与示例一致的方式回答当前的问题。在下一节中,我们将探讨上下文学习的利弊,以帮助我们确定微调是否是使模型能够回答非常具体问题的最佳方法。

在上下文学习

在上下文学习是一种技术,其中模型根据输入提示中提供的少量示例生成响应。这种方法利用了模型的预训练知识和提示中包含的特定上下文或示例,以执行任务而无需参数更新或重新训练。布朗等人(2020年)在《语言模型是少量学习者》中详细描述了这些模型的广泛预训练如何使它们能够根据嵌入在提示中的指令和少量示例执行任务和生成响应。与传统方法不同,传统方法需要对每个特定任务进行微调,上下文学习允许模型根据推理时提供的附加上下文进行适应和响应。

在上下文学习中,核心概念是少量提示,这对于使模型能够适应和执行任务而无需额外训练数据至关重要,而是依赖于它们预训练的知识和输入提示中提供的上下文。为了说明这一点,我们将描述一个LLM通常的工作方式,即零样本方法,并将其与使用少量方法的上下文学习进行对比:

  • x。模型计算潜在输出序列y的可能性,表示为P(y|x)。这种计算在没有特定于任务的先前示例的情况下进行,完全依赖于模型的通用预训练。这意味着零样本方法除了其通用知识外没有特定上下文。例如,如果我们问Are winter coats available in children’s sizes?,模型无法提供关于StyleSprint库存的具体答案。它只能提供一些通用的答案。

  • x)来形成一个扩展输入序列。因此,我们的问题Are winter coats available in children’s sizes?可能与以下示例配对:

    • Do you sell anything in children’s sizes?

    Any items for children are specifically listed on the “StyleSprint for Kids” page.

    • What do you offer for kids?

    StyleSprint offers a variety of children’s fashions on its “StyleSprint for Kids” page.

LLM随后计算在给定扩展输入序列x的情况下生成特定输出序列y的概率。从数学上讲,这可以理解为模型估计yx(其中x包括之前演示的提示和少量示例)的联合概率分布。模型使用这个联合概率分布来生成与输入序列中给出的示例配对的指令一致的响应。

在这两种情况下,模型根据给定上下文调整其输出的能力,无论是零个示例还是少量示例,都展示了其底层架构和训练的灵活性和复杂性。然而,少量方法允许LLM从提供的非常具体的示例中学习。

让我们考虑 StyleSprint 如何应用情境学习来回答客户查询。使用情境学习(或少量示例方法)的性能始终显示出相对于零样本行为的显著提升(Brown 等人,2020 年)。我们可以将先前的例子扩展到客户询问特定产品的可用性。同样,StyleSprint 团队可以系统地在每个提示中添加几个示例,如下所示。

这里是提示:请回答以下关于产品可用性的{问题}。

这些是一些示例:

  • 示例 1:

    • 你携带黑色皮革手提包吗?

    • 请给我一点时间,我需要检索关于那个特定物品的信息。

  • 示例 2:

    • 你有蓝色的丝绸围巾吗?

    • 让我在我们的库存中搜索蓝色丝绸围巾。

StyleSprint 可以提供有效的示例,帮助模型理解查询的本质,并生成既具有信息性又符合公司政策和产品提供的信息响应。在这个例子中,我们看到响应旨在与搜索组件配对。这是一种常见的方法,可以使用称为 检索增强生成RAG)的技术来实现,这是一个促进实时数据检索以告知生成响应的组件。将少量示例情境学习方法与 RAG 结合使用可以确保系统提供逻辑性和具体的答案。

使用少量示例的情境学习允许模型快速适应各种客户查询,同时使用有限数量的示例。当与 RAG 结合使用时,StyleSprint 可能能够满足其用例并减少微调所需的时间和资源。然而,这种方法必须权衡专业化的深度和特定任务微调的一致性,正如所描述的,这也可能产生高度准确且符合品牌语调的答案。

在下一节中,我们将制定有助于我们直接比较的指标,以指导 StyleSprint 做出最适合其客户服务目标和运营框架的明智决策。

微调与情境学习

我们了解到情境学习如何使StyleSprint的模型能够处理各种客户查询,而无需进行大量重新训练。具体来说,少量方法与RAG的结合可以促进对新查询的快速适应,因为模型可以根据几个示例生成响应。然而,情境学习的效果在很大程度上取决于提示中提供的示例的质量和相关性。其成功也取决于RAG的实施。此外,没有微调,响应可能缺乏一致性,或者可能不会严格遵循StyleSprint的品牌语气和客户服务政策。最后,完全依赖生成模型而不进行微调可能会无意中引入偏差,如第4章所述*

在实践中,我们有两种非常相似且可行的方案。然而,为了做出明智的决定,我们应首先使用定量方法进行更深入的比较。

为了公正地评估情境学习与微调相比的效力,我们可以衡量生成响应的质量和一致性。我们可以使用既定且可靠的指标来比较每种方法的结果。与之前的评估一样,我们希望应用以下关键维度的定量和定性方法:

  • 与人类判断的一致性:我们可以再次应用语义相似性,以提供基于人类编写的参考答案的定量指标,衡量模型响应的正确性或相关性。

    StyleSprint的品牌传播专家可以审查一部分响应,以提供对响应准确性和与品牌语气和声音一致性的定性评估。

  • 一致性和稳定性:重要的是要衡量每次回答问题时,尽管提问方式略有不同,但问题回答的一致程度。同样,当输入保持不变时,我们可以利用语义相似性来比较每个新输出与先前的输出。

除了评估每种方法的模型响应质量外,我们还可以直接比较每种方法所需的操作和计算开销。

对于微调,我们需要了解训练模型所涉及的开销。虽然PEFT方法将显著减少训练工作量,但与情境学习相比,可能存在相当多的基础设施相关成本,因为情境学习不需要额外的训练。另一方面,对于情境学习,如OpenAI的GPT-4这样的通用化模型有一个按令牌计费的成本模型。StyleSprint还必须考虑在提示中嵌入足够数量的少量示例所需的令牌成本。

在这两种情况下,StyleSprint将承担一些运营成本,以创建由人类编写的最佳示例,这些示例可以用作在少样本方法或额外模型训练中的“黄金标准”。

通过进行这些比较测试并分析结果,StyleSprint将获得宝贵的见解,了解哪种方法——情境学习或微调——最能与其运营目标和客户服务标准相匹配。这种数据驱动的评估将指导他们决定最佳的AI策略,以增强客户服务体验。我们将在接下来的实践项目中实施这些比较。

实践项目:使用PEFT进行问答微调

对于我们的实践项目,我们将尝试使用AdaLoRA高效地微调一个用于客户查询的模型,并将其与使用情境学习的最先进SOTA)模型的输出直接比较。像上一章一样,我们可以依赖一个原型环境,如Google Colab,来完成两种方法的评估和比较。我们将展示如何配置模型训练以使用AdaLoRA作为我们的PEFT方法。

关于问答微调的背景

我们的项目利用了Hugging Face训练管道库,这是机器学习社区中广为人知的资源。这个库提供了各种预构建的管道,包括一个用于问答的管道,这使得我们能够以最小的设置微调预训练模型。Hugging Face管道抽象了模型训练中涉及的大部分复杂性,使得开发者能够直接且高效地实现高级自然语言处理任务。特别是,这个管道作为一个接口,连接到具有特定问答任务头的transformer模型。回想一下,当我们微调一个transformer模型时,我们保持模型的架构——包括自注意力机制和transformer层——但我们仅在特定任务上训练模型的参数,在这种情况下,结果是针对问答任务进行了优化的模型。回想一下我们在第三章中的实践项目,其中生成的模型是一个翻译器;我们使用翻译器头来完成从英语到法语的语言翻译。对于这个项目,“头”被调整为学习问答数据中的模式。

然而,当使用问答训练管道时,重要的是要理解模型不仅仅是记住问答对,它还学习问题与答案之间的联系。此外,为了给出适当的答案,模型不能完全依赖训练。它还需要额外的上下文作为输入来组成一个相关的答案。为了进一步理解这一点,我们将模型推理步骤分解如下:

  1. 当向模型输入问题时,我们还必须包括与主题相关的上下文。

  2. 模型随后确定上下文中回答问题的最相关部分。它是通过为上下文中的每个标记(单词或子词)分配概率分数来做到这一点的。

  3. 模型“认为”上下文是答案的潜在来源,并为每个标记分配两个分数:一个分数用于作为答案的开始,另一个分数用于作为答案的结束

  4. 然后选择具有最高“开始”分数和“结束”分数的标记来形成答案跨度。跨度就是向用户展示的内容。

为了提供一个具体的例子,如果我们向模型提问,“StyleSprint有没有任何皮夹克?”并提供上下文“StyleSprint销售各种外套、夹克和外衣”,模型将处理这个上下文并确定最可能的答案是类似“是的,StyleSprint销售各种外衣”。然而,如果问题的答案不包含在提供的上下文中,模型无法生成可靠的答案。此外,如果上下文过于不具体,模型可能会提供一个更通用的答案。就像上下文学习一样,问答的微调方法也需要相关的上下文。这意味着在实践中,模型必须与能够检索与每个问题相关额外上下文的搜索组件集成。

以我们的皮夹克例子为例。当接收到一个问题,系统可以对它的知识库进行搜索并检索与皮夹克相关的任何上下文信息(例如,关于外套的段落)。同样,由于模型被训练成以与品牌调性一致的方式回答问题,它将从提供的上下文中提取相关信息来制定适当的答案。不仅与搜索的集成将为模型提供所需的上下文,而且它还将允许模型拥有最新和实时信息。

此外,我们可能还会引入一个置信度阈值,只有当模型为开始和结束标记分配足够高的概率时,它才会给出答案。如果最高概率低于这个阈值,我们可能会说模型不知道,或者请求更多信息。总的来说,模型的有效性在很大程度上依赖于训练数据的质量和大小,以及上下文与提出的问题的相关性。

现在我们已经更好地理解了问答微调的工作原理以及在使用Hugging Face的问答管道时可以期待什么,我们可以开始编写我们的实现代码。

Python中的实现

首先,我们安装所需的库:

!pip install transformers peft sentence-transformers

然后,我们从transformers库中导入问答模块。对于我们的项目,我们将使用谷歌的Flan T5(小型),这被认为是GPT 3.5的SOTA替代品。我们的一个目标继续是衡量性能与效率之间的权衡,因此我们从Flan T5的最小版本开始,它有80M个参数。这将使训练更快,迭代更迅速。然而,请注意,即使在少量epoch上训练的小型模型也需要高RAM的运行环境:

from transformers import (
    AutoModelForQuestionAnswering, AutoTokenizer)
model_name = " google/flan-t5-small"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForQuestionAnswering.from_pretrained(model_name)

在预训练模型实例化后,我们现在可以配置模型以适应其训练过程,使用AdaLoRA,正如我们所学的,它专门设计用于在微调过程中高效地分配参数预算:

from peft import AdaLoraConfig
# Example configuration; adjust parameters as needed
adapter_config = AdaLoraConfig(target_r=16)
model.add_adapter(adapter_config)

正如讨论的那样,微调在很大程度上依赖于训练数据的质量和大小。在StyleSprint场景中,公司可以从其FAQ页面、社交媒体和客户服务记录中聚合问答对。为此练习,我们将构建一个类似于以下的数据集:

demo_data = [{
"question": "What are the latest streetwear trends available at Stylesprint?",
  "answer": "Stylesprint's latest streetwear collection includes hoodies, and graphic tees, all inspired by the latest hip-hop fashion trends."
...
}]

然而,为了将我们的数据集与问答管道集成,我们首先应该了解Trainer类。Hugging Face transformers库中的Trainer类期望训练和评估数据集以特定格式提供,通常是一个PyTorch Dataset对象,而不仅仅是简单的字典列表。此外,数据集中的每个条目都需要进行标记化,并使用必要的字段结构化,如input_idsattention_mask,对于问答任务,还需要start_positionsend_positions。让我们更详细地探讨这些内容:

  • input_ids:这是一个表示模型中输入句子的整数序列。句子中的每个单词或子词都被转换成唯一的整数或ID。回想一下,在早期章节中,这个过程被称为[101, 354, 2459]

  • attention_mask:注意力掩码是一个二进制值序列,其中1表示真实标记,0表示填充标记。换句话说,在1存在的地方,模型将理解这些地方需要注意力,而0存在的地方将被模型忽略。这在处理不同长度的句子和处理训练模型中的句子批次时至关重要。

  • start_positionsend_positions:这些用于问答任务。它们代表答案在上下文标记化形式中的起始和结束标记的索引。例如,在上下文巴黎是法国的首都中,如果问题是法国的首都是什么?,给出的答案是巴黎,在标记化后,start_positionend_position将对应于上下文中巴黎的索引。

有这样的理解后,我们可以创建一个类,使我们的数据集适应训练器的期望,如下所示:

from torch.utils.data import Dataset
class StylesprintDataset(Dataset):
   def __init__(self, tokenizer, data):
       tokenizer.pad_token = tokenizer.eos_token
       self.tokenizer = tokenizer
       self.data = data

要查看完整的自定义数据集类代码,请访问此书的GitHub仓库:https://github.com/PacktPublishing/Generative-AI-Foundations-in-Python

在准备完训练集并将我们的管道配置为应用AdaLoRA方法后,我们最终可以进入训练步骤。对于这个项目,我们将配置训练只运行几个周期,但在StyleSprint场景中,需要一个更加稳健的训练过程:

from transformers import Trainer, TrainingArguments
# Split the mock dataset into training and evaluation sets (50/50)
train_data = StylesprintDataset(
    tokenizer, demo_data[:len(demo_data)//2])
eval_data = StylesprintDataset(
    tokenizer, demo_data[len(demo_data)//2:])
# Training arguments
training_args = TrainingArguments(
    output_dir="./results",
    num_train_epochs=10,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=64,
    warmup_steps=500,
    weight_decay=0.01,
    logging_dir="./logs",
    logging_steps=10,
)
# Initialize the Trainer
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_data,
    eval_dataset=eval_data
)
# Start training
trainer.train()

对于我们的简单实验,我们并不期望模型有很高的性能;然而,我们可以学习如何解释训练输出,它描述了模型在评估样本上的表现。Trainer类将输出一个包含损失指标的训练摘要。

训练损失

训练损失是衡量模型表现好坏的一个指标;损失值越低,表示表现越好。在许多深度学习模型中,尤其是处理复杂任务(如语言理解)的模型,通常开始时损失值会相对较高。预期这个值应该随着训练的进行而降低。

在训练的早期阶段,高损失值并不是一个需要警觉的原因,因为它通常会在模型继续学习的过程中降低。然而,如果损失值保持较高,这表明可能需要额外的训练。如果在长时间训练后损失值仍然很高,那么学习率和其他超参数可能需要调整,因为不适当的学习率可能会影响模型的学习效率。此外,应该评估训练数据的质量和数量,因为数据不足可能会阻碍训练。例如,由于我们只为实验使用了几个示例,我们预期损失值会相对较高。

下一步是使用我们新微调的模型进行推理或预测。我们还应该确保我们的训练模型参数安全,这样我们就可以在不重新训练的情况下重用它:

import torch
# save parameters
model.save_pretrained("./stylesprint_qa_model")
def ask_question(model, question, context):
   # Tokenize the question and context
   inputs = tokenizer.encode_plus(question, context,
        add_special_tokens=True, return_tensors="pt")
   # Get model predictions
   with torch.no_grad():
       outputs = model(**inputs)
   # Get the start and end positions
   answer_start_scores = outputs.start_logits
   answer_end_scores = outputs.end_logits
   # Find the tokens with the highest `start` and `end` scores
   answer_start = torch.argmax(answer_start_scores)
   answer_end = torch.argmax(answer_end_scores) + 1
   # Convert the tokens to the answer string
   answer = tokenizer.convert_tokens_to_string(
        tokenizer.convert_ids_to_tokens(
            inputs["input_ids"][0][answer_start:answer_end]
            )
        )
   return answer
question = "What is the return policy for online purchases?"
context = """Excerpt from return policy returned from search."""
answer = ask_question(model, question, context)
print(answer)

如前所述,我们将上下文和问题一起引入模型,以便它可以识别哪个上下文片段最恰当地响应查询。因此,我们可能希望考虑集成一个向量搜索系统(如RAG),根据与查询的语义相似性自动从大型数据集中识别相关文档。这些搜索结果可能不会提供具体的答案,但训练好的QA模型可以从结果中提取更精确的答案。

使用这种混合方法,向量搜索系统首先检索与查询在语义上相关的文档或文本片段。然后,QA模型分析这个上下文以确定与StyleSprint的指南和期望相符的精确答案。

结果评估

为了评估我们的模型结果,StyleSprint可能会应用我们在本章中已经讨论过的定性和定量方法。为了我们的实验目的,我们可以使用一个简单的语义相似度度量来衡量模型输出的黄金标准响应:

from sentence_transformers import SentenceTransformer, util
import pandas as pd
# Example of a gold standard answer written by a human
gs = "Our policy at Stylesprint is to accept returns on online purchases within 30 days, with the condition that the items are unused and remain in their original condition."
# Example of answer using GPT 3.5 with in-context learning reusing a relevant subset of the training data examples
gpt_35 = "Stylesprint accepts returns within 30 days of purchase, provided the items are unworn and in their original condition."
# Load your dataset
dataset = pd.DataFrame([
   (gs, gpt_35, answer)
])# pd.read_csv("dataset.csv")
dataset.columns = ['gold_standard_response',
    'in_context_response', 'fine_tuned_response']
# Load a pre-trained sentence transformer model
eval_model = SentenceTransformer('all-MiniLM-L6-v2')
# Function to calculate semantic similarity
def calculate_semantic_similarity(model, response, gold_standard):
    response_embedding = model.encode(
        response, convert_to_tensor=True)
    gold_standard_embedding = model.encode(gold_standard,
        convert_to_tensor=True)
    return util.pytorch_cos_sim(response_embedding,
        gold_standard_embedding).item()
# Measure semantic similarity
dataset['in_context_similarity'] = dataset.apply(
    lambda row:calculate_semantic_similarity(
        eval_model, row['in_context_response'],
        row['gold_standard_response']
    ), axis=1)
dataset['fine_tuned_similarity'] = dataset.apply(
    lambda row:calculate_semantic_similarity(
        eval_model, row['fine_tuned_response'],
        row['gold_standard_response']
    ), axis=1)
# Print semantic similarity
print("Semantic similarity for in-context learning:", 
    dataset['in_context_similarity'])
print("Semantic similarity for fine-tuned model:", 
    dataset['fine_tuned_similarity'])

我们评估的结果如下:

PEFT Flan T5 GPT 3.5T
微调 上下文
语义相似度 0.543 0.91

表5.1:微调后的Flan和GPT 3.5 Turbo的语义相似度分数

无疑,上下文学习得出的答案与我们的黄金标准参考非常接近。然而,微调模型并不落后。这告诉我们,通过更健壮的训练数据集和相当多的epoch,微调模型可以与GPT 3.5相媲美。通过更多的迭代和实验,StyleSprint可以拥有一个非常健壮的微调模型来回答客户的具体问题。

摘要

在本章中,我们专注于StyleSprint的AI驱动客户服务系统中微调与上下文学习之间的战略决策过程。虽然上下文学习,尤其是少样本学习,提供了适应性和资源效率,但它可能并不始终与StyleSprint的品牌调性和客户服务指南保持一致。这种方法高度依赖于提示中提供的示例的质量和相关性,需要精心设计以确保最佳结果。

另一方面,PEFT方法如AdaLoRA,提供了一种更专注的方法来适应预训练模型以满足客户服务查询的特定需求。PEFT方法仅修改模型参数的一小部分,减少了计算负担,同时仍然实现高性能。这种效率对于现实世界应用至关重要,在这些应用中,计算资源和响应准确性都是关键考虑因素。

最终,在上下文学习与微调之间的选择,不仅仅是一个技术决策,也是一个战略决策,它与公司的运营目标、资源配置以及期望的客户体验紧密相连。本章建议进行对比测试,以评估两种方法的有效性,通过可靠的指标评估大规模的结果。这种数据驱动的评估将指导StyleSprint在提升客户服务体验方面做出最佳AI策略的决定。

总结来说,我们现在对LLM中微调与上下文学习的含义有了更全面的理解,特别是在客户服务的背景下。它强调了像StyleSprint这样的公司做出明智战略决策的需要,在微调提供的深度专业化和一致性以及上下文学习的适应性和效率之间取得平衡。

在下一章中,我们将探讨用于领域自适应的PEFT,其中我们的训练结果是一个经过微调以理解高度特定领域(如金融或法律)的通用模型。

参考文献

本参考文献部分作为本书中引用的资料的存储库;您可以探索这些资源,以进一步加深对主题内容的理解和知识:

  • Radford, A., Narasimhan, K., Salimans, T., and Sutskever, I. (2018). 通过生成性预训练改进语言理解。OpenAI.

  • Hu, E. J., Shen, Y., Wallis, P., Li, Y., Wang, S., Wang, L., and Chen, W. (2021). LoRA: 大型语言模型的低秩自适应。ArXiv. /abs/2106.09685

  • 张琪,陈明,布哈林,何平,程宇,陈伟,赵天 (2023). 参数高效微调的自适应预算分配。ArXiv. /abs/2303.10512

  • Brown TB, Mann B, Ryder N, et al. 2020. 语言模型是少样本学习者。ArXiv:2005.14165.

第六章:理解大型语言模型的领域自适应

在上一章中,我们探讨了参数高效微调PEFT)如何增强大型语言模型LLMs)以适应特定任务,例如问答。在本章中,我们将介绍领域自适应,这是一种独特的微调方法。与特定任务微调不同,领域自适应使模型能够解释特定行业或领域的独特语言,解决LLMs在理解专业语言方面的差距。

为了说明这一点,我们将介绍Proxima投资集团,这是一个假设的仅数字投资公司,旨在利用内部数据将LLM调整到其特定的金融语言。我们将展示如何修改LLM以处理Proxima环境中典型的特定术语和细微差别,从而增强模型在金融领域的相关性和有效性。

我们还将探讨Proxima可能采取的实用步骤,例如选择相关的内部数据集进行训练,应用低秩自适应LoRA)等PEFT方法来高效地调整模型,以及使用掩码技术来细化模型的理解。然后,我们将探讨Proxima如何评估这种领域自适应的成功,评估模型在分析金融趋势、回应客户咨询和生成符合Proxima内部标准和市场定位的报告等任务中的性能。

到本章结束时,我们将清楚地理解领域自适应的理论基础及其在现实世界中的应用,特别是在金融等复杂领域,模型对领域理解的深度可以显著影响业务成果。

让我们从揭秘这个概念开始,探讨其技术基础,并讨论其在实现特定业务目标中的重要性。

揭秘领域自适应——了解其历史和重要性

在生成型LLMs的背景下,领域自适应特别针对像BLOOM这样的模型进行定制,这些模型在广泛的通用数据集(如新闻文章和维基百科条目)上进行了预训练,以增强对目标行业文本的理解,包括生物医学、法律和金融领域。这种改进可能至关重要,因为尽管LLMs在预训练中具有广泛的数据,但它们可能无法天生捕捉到这些领域固有的复杂细节和专门术语。这种自适应涉及一个有意识的过程,将模型学习到的模式重新对齐到目标领域普遍存在的语言特征、术语和语境细微差别。

领域自适应迁移学习的范畴内运作。在这个更广泛的概念中,模型从一个任务中学到的知识被重新用于提高其在相关但不同的任务上的有效性。这种方法利用模型预先学习到的特征,以提高其在后续任务上的效率和准确性,显著减少其对大量特定领域数据和计算资源的依赖。具体来说,我们从一个在广泛数据集上训练好的模型开始,将其用作适应特定领域的起点,从而提高其准确性、相关性和对更针对性用例的适用性。

在实践中,可以采用多种方法来调整模型以适应特定领域,包括以下方法:

  • 持续预训练:模型在特定领域的语料库上进行额外的预训练,使其参数能够逐步适应目标领域的语言特征,正如 Gururangan 等人在 2020 年的研究中所强调的。

  • 中间任务训练:在这里,模型在中间任务上进行训练,在微调下游应用之前使用特定领域的数据。这一步骤有助于更稳健地适应领域(Pruksachatkun 等人,2020)。

  • 数据增强:利用回译(Xie 等人,2019)和标记替换(Anaby-Tavor 等人,2020)等技术,从有限的实际数据中生成合成的特定领域训练示例:

    • 回译涉及将现有文本从一种语言(例如,英语)翻译成另一种语言(例如,法语),然后再将其翻译回原始语言。这个过程生成了原始文本的释义版本,同时保留了其语义。

    • 标记替换涉及改变句子中的单个单词以生成新的句子。这种改变通常旨在保留原始句子的语义意义,同时引入变化。

  • 多任务学习:该框架在适应阶段同时优化模型以处理通用和特定领域的任务,正如 Clark 等人在 2019 年所展示的。

随着领域自适应技术的不断发展,它们在特定领域中的模型性能不断提高,即使是在减少特定领域数据的情况下。如第 第4章 所述,最近的发展更加关注这些技术的计算效率。如 LoRA 这样的适应方法通过最小的参数变化实现重大的模型调整,而无需全面重新训练。需要注意的是,模型的表现将始终根据数据集的质量、可用的计算资源和其他实现细节等多种因素而变化。

现在我们对领域自适应技术和它们对计算效率的关注有所了解,我们可以将这些概念应用于实践。我们的实践项目将利用BLOOM,一个最先进的开源LLM,来展示金融领域的领域自适应。利用PEFT,我们旨在以最少的计算资源微调BLOOM,展示这些高级自适应方法在增强金融领域模型性能中的实际应用。

实践项目:金融领域的迁移学习

本项目旨在对特定文档的精选语料库上的BLOOM进行微调,使其具备解释和阐述Proxima及其产品特定概念的能力。

我们的方法灵感来源于跨多个领域的领域自适应策略,包括生物医学、金融和法律。一项由Cheng等人于2023年进行的值得注意的研究,名为《通过阅读理解调整大型语言模型》*,提出了一种增强LLM在特定任务中能力的新方法。这种方法将大量的预训练语料库重新格式化为有利于阅读理解任务的格式,显著提高了模型在特定领域的功能。在我们的案例中,我们将采用类似但简化的方法,通过使用针对Proxima特定数据集的微调来继续预训练,有效地继续模型的训练。这个过程逐步调整模型参数,以确保模型更好地理解Proxima的产品和提供的独特语言。

金融领域自适应的培训方法

为了我们持续的培训策略,我们将采用因果语言模型CLM)。这种方法是更广泛的一组培训方法之一,旨在优化模型性能以实现各种目标。在转向实施之前,让我们尝试区分我们选择的方法与其他流行策略,以便更好地理解CLM方法:

  • 掩码语言模型MLM):这是基于Transformer的模型(如BERT)的基石,MLM随机掩码输入文本的部分,并挑战模型预测掩码的标记。通过考虑掩码周围的整体上下文(包括掩码之前和之后的内容),MLM使模型能够发展双向的语言理解能力,丰富其对上下文和语义的掌握。

  • 下一句预测NSP):这种方法通过训练模型判断两个句子是否逻辑上相互跟随,进一步扩展了模型的叙事理解能力。NSP对于教授模型关于文本结构和连贯性至关重要,使其能够在更大的文本体中构建和理解逻辑序列。

  • CLM:我们为BLOOM的适应性选择了一条不同的路径,采用CLM因为它具有专注的、顺序预测的能力。与MLM(它同时查看(在掩码标记之前和之后))不同,CLM采用单向方法,仅根据前面的上下文预测每个后续标记。这种方法与自然语言生成内在一致,使其特别适合在目标领域中构建连贯、上下文丰富的叙述。

在选择CLM对BLOOM进行适应性时,我们将扩展模型的生成能力,以产生不仅逻辑结构良好,而且深深植根于目标领域细微差别的文本序列。CLM的单向性质确保每个生成的标记都由对先前文本的连贯理解所指导,使模型能够生成详细、准确且特定于领域的文本。

一旦微调完成,我们可以根据该领域自适应BLOOM模型在生成与上下文相关且特定于领域的叙述方面的熟练程度来评估其有效性。我们将比较自适应模型与原始模型的表现,特别关注模型的流畅性、准确性和对目标领域的整体理解。

正如我们之前所做的那样,我们将利用Google Colab进行我们的初始原型设计阶段。正如第4章和第5章所描述的,Google Colab提供了一个预配置的环境,简化了在我们考虑将我们的方法推广到生产环境之前测试我们的方法的过程。本章中所有的代码都可在本书GitHub仓库的Chapter 6文件夹中找到(https://github.com/PacktPublishing/Generative-AI-Foundations-in-Python)。

我们将从初始设置开始,这涉及到使用Transformers库加载BLOOM-1b1的一个较小变体。我们还将导入我们将需要应用PEFT的方法。对于这个例子,我们将依赖一些可以按以下方式安装的库:

pip install sentence-transformers transformers peft datasets

安装完成后,我们可以开始导入:

from transformers import (
    AutoTokenizer, AutoModelForCausalLM)
from peft import AdaLoraConfig, get_peft_model

下一步是加载分词器和模型:

tokenizer = AutoTokenizer.from_pretrained("bigscience/bloom-1b1")
model = AutoModelForCausalLM.from_pretrained(
    "bigscience/bloom-1b1")

如前所述,我们正在引入PEFT以提高适应性:

adapter_config = AdaLoraConfig(target_r=16)
model.add_adapter(adapter_config)

PEFT技术,特别是通过AdaLoraConfig,允许我们引入一个紧凑、高效的层,这样我们就可以通过显著减少可训练参数的数量来将模型适应新的上下文——在这里,是金融领域:

model = get_peft_model(model, adapter_config)
model.print_trainable_parameters()

我们必须集成适配器以完成PEFT模型设置,从而有效地创建一个针对我们特定领域训练优化的模型变体,同时关注效率。我们可以通过检查我们的模型将使用的可训练参数数量来量化这一点:

trainable params: 1,769,760 || all params: 1,067,084,088 || trainable%: 0.1658500974667331

上述代码为我们提供了以下信息:

  • 可训练参数:1,769,760

  • 模型中的总参数:1,067,084,088

  • 可训练参数百分比:0.166%

这意味着在BLOOM-1b1模型超过10亿个参数中,只有大约177万个参数被用于金融领域自适应的微调。这个很小的百分比(0.166%)的可训练参数突出了PEFT的效率,允许通过最小的调整实现显著的模型适应性。这对于实际应用至关重要,因为它减少了计算成本和训练时间。

接下来,我们将进入数据准备阶段。我们假设我们已经收集了涵盖Proxima产品及其服务(如Proxima Passkey)的广泛知识文本。CLM训练需要区分测试和训练阶段,以评估模型准确预测序列中下一个标记的能力。这确保了模型在训练数据之外也能很好地泛化到未见过的文本。在训练过程中,损失计算衡量模型预测的标记概率与实际标记之间的差异。它指导模型调整其参数以最小化这种损失,通过迭代提高其预测准确性。因此,我们必须定义训练和测试文本作为我们的数据集。本书的GitHub仓库(本章前面已链接)中包含了一个示例数据集。

dataset = load_dataset("text",
    data_files={"train": "./train.txt",
        "test": "./test.txt"}
    )

接下来,我们必须应用预处理和分词。文本被清理、标准化,然后转换为数值格式(512个标记,以便与模型的架构相匹配):

def preprocess_function(examples):
    inputs = tokenizer(examples["text"], truncation=True,
        padding="max_length", max_length=512)
    inputs["labels"] = inputs["input_ids"].copy()
    return inputs

TrainingArguments类配置了训练过程,设置如批量大小、训练轮数和保存模型检查点的目录等参数。这种配置对于高效学习和模型评估至关重要。同时,Trainer类协调模型的训练过程。再次强调,持续训练逐渐调整模型的参数,以生成和理解与Proxima Passkey相关的文本:

from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
    output_dir="./model_output",
    per_device_train_batch_size=2,
    num_train_epochs=5,
    logging_dir='./logs',
    logging_steps=10,
    load_best_model_at_end=True,
    prediction_loss_only=True,
)
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["test"],
)
trainer.train()
model.save_pretrained("./proxima_da_model")

通常,我们的配置指定了训练参数并初始化Trainer类,同时专注于领域自适应。TrainingArguments类被定制以高效管理训练过程,包括日志记录和模型保存策略。记住,我们为训练模型选择的批量大小平衡了GPU的内存容量和模型从数据集中学习的速度。较大的批量大小允许一次处理更多数据,从而加快训练速度,但需要更多内存,如果GPU容量有限,这可能会成为限制。相反,较小的批量大小意味着模型使用较少的样本更频繁地更新其权重,这可以促进学习,但会导致通过数据集的整体进度变慢。

训练完成后,我们可以使用自适应模型根据与Proxima Passkey相关的提示生成文本。模型考虑提示,生成表示续写的标记序列,然后将此序列解码回可读的文本:

def predict(model, prompt="The Proxima Passkey is"):
    inputs = tokenizer(prompt, return_tensors="pt")
    output = model.generate(**inputs, max_length=50)
    return tokenizer.decode(output[0], skip_special_tokens=True)

注意model.generate()函数,它接受分词输入并生成一系列标记作为输出。然后,这些标记被解码成文本。

在这个例子中,我们调整了BLOOM语言模型,使其专门针对金融领域。这包括加载预训练模型,应用PEFT适配器以实现高效的领域适应性,并通过标准化和分词准备金融数据集以供模型训练。在用特定领域的数据进行微调BLOOM后,我们使用该模型生成与金融行业相关的文本。最后一步是评估这个调整后的模型与原始预训练版本的性能,重点关注其在准确处理财务语言和概念方面的有效性。

评估和结果分析——ROUGE度量标准

定量评估和定性评估对于评估适应后的BLOOM模型与原始模型之间的差异至关重要,尤其是在Proxima语言的环境中。在定量方面,模型的输出通过与使用ROUGE度量标准反映Proxima产品语言的参考数据集进行比较。这种比较有助于衡量关键术语和风格的重叠程度。此外,为评估模型在Proxima相关的财务术语和概念方面的熟练程度,开发特定的度量标准是有益的:

from rouge import Rouge
# Example reference text (what we expect the model to generate after training on a complete dataset)
reference = "Proxima's Passkey enables seamless integration of diverse financial portfolios, offering unparalleled access to global investment opportunities and streamlined asset management."
# Example predicted model output
predicted = "The Proxima Passkey provides a unified platform for managing various investment portfolios, granting access to worldwide investment options and efficient asset control."
# Initialize the Rouge metric
rouge = Rouge()
# Compute the Rouge scores
scores = rouge.get_scores(predicted, reference)
print(scores)

ROUGE分数将通过比较本例中的两个文本来计算。该分数衡量预测输出与参考文本在n-gram(单词序列)方面的重叠。例如,ROUGE-N(其中N可以是1、2或L)计算预测文本和参考文本之间的n-gram重叠:

  • ROUGE-1评估预测文本和参考文本之间单语(单个单词)的重叠

  • ROUGE-2评估文本之间双语(两个单词的短语)的重叠

  • ROUGE-L关注最长公共子序列,这对于评估句子级结构相似性很有用

ROUGE分数的范围从0到1,量化预测文本与参考文本之间的相似性,为模型输出与预期内容匹配的程度提供见解。接近1的分数表示更高的相似性或重叠,而接近0的分数则表示几乎没有共同性。这些分数分为三个关键组成部分——精确度、召回率和F1分数:

  • 精确度衡量预测文本中在参考文本中也存在的单词比例。高精确度分数表明模型生成的单词大多数是相关的,并且出现在参考文本中,这表明模型输出的准确性。

  • 召回率评估参考文本中在模型预测中捕获的单词比例。高召回率意味着模型有效地在其输出中包含了参考文本中的大多数相关内容,表明内容的全面性。

  • F1分数是精确率和召回率的调和平均数,平衡了两者。它在理解模型生成既相关(精确率)又全面(召回率)的文本的整体准确性方面特别有用。当在评估模型性能时,精确率和召回率同等重要时,F1分数至关重要。

  • 这里是输出:

度量 召回率(r) 精确率(p) F1分数(f)
ROUGE-1 0.35 0.333 0.341
ROUGE-2 0.053 0.048 0.05
ROUGE-L 0.35 0.333 0.341

表6.1:ROUGE度量结果

这些分数表明,文本之间存在中等程度的单语素重叠(ROUGE-1),但二元重叠(ROUGE-2)显著较低。ROUGE-1和ROUGE-L分数之间的相似性表明,模型在一定程度上捕捉了单个关键术语,但可能在较长的短语结构上遇到困难,这指出了模型改进的领域。

总体而言,虽然该模型在关键个体术语方面表现出基本理解(如ROUGE-1和ROUGE-L所示),但它复制参考文本中更复杂结构或短语的能力(如ROUGE-2所示)相当有限。这表明,尽管模型对特定领域的语言有一定理解,但仍需进一步微调才能有效地复制参考文本中更细微和结构化的方面。记住,正如我们在其他章节中看到的,语义相似性也是衡量特定领域语言理解的好指标,并且不像ROUGE那样依赖于词汇重叠。

定性上,领域专家应审查模型的输出,以判断其在Proxima的产品和机构语言背景下的相关性和准确性。这些专家可以提供关于模型性能细微之处的见解,这些细微之处可能无法仅通过量化指标来捕捉。比较他们对原始模型和自适应模型输出的反馈将突出自适应如何使BLOOM与Proxima的具体沟通需求相一致。这种双重方法确保了全面的评估,将统计分析与实际应用和相关性相结合。

摘要

在本章中,我们探讨了BLOOM LLM的领域自适应过程,该过程专门针对提高其在金融领域的熟练度,特别是在理解和生成与Proxima的产品提供相关的内容。我们首先介绍了领域自适应的概念,这是在迁移学习更广泛范围内的一个概念,强调了其在微调通用模型以掌握专业领域复杂性的重要性。

该适应过程涉及将PEFT技术集成到BLOOM中,并对金融数据集进行预处理以进行模型训练。这包括通过截断和填充标准化文本长度,并对文本进行标记化以确保模型输入的一致性。然后,使用ROUGE指标对适应后的模型性能进行定量评估,以参考数据集为基准,从而提供了对其捕捉关键金融术语和短语能力的见解。同时,也建议由领域专家进行定性评估,作为衡量模型在实际场景中实际有效性的补充方法。

总体而言,本章详细介绍了针对特定领域微调LLM的常见方法,阐述了方法论以及细微评估的重要性,以确保此类适应的成功。在下一章中,我们将探讨如何使用提示工程来适应LLM而不进行微调。我们将发现如何使模型输出具有上下文并引导其产生与微调模型相似的结果。

参考文献

本参考文献部分作为本书中引用的资源的存储库;您可以探索这些资源以进一步加深对主题的理解和知识。

  • Gururangan, S., Marasović, A., Swayamdipta, S., Lo, K., Beltagy, I., Downey, D., & Smith, N. A. (2020). 不要停止预训练:将语言模型适应到领域和任务中。载于arXiv [cs.CL]。http://arxiv.org/abs/2004.10964/

  • Pruksachatkun, Y., Phang, J., Liu, H., Htut, P. M., Zhang, X., Pang, R. Y., Vania, C., Kann, K., & Bowman, S. R. (2020a). 中间任务迁移学习与预训练语言模型:何时以及为什么它有效? 第58届计算语言学协会年度会议论文集。

  • Xie, Q., Dai, Z., Hovy, E., Luong, M.-T., & Le, Q. V. (n.d.). 无监督数据增强用于一致性训练。Arxiv.org。2024年3月16日检索自 http://arxiv.org/abs/1904.12848

  • Anaby-Tavor, A., Carmeli, B., Goldbraich, E., Kantor, A., Kour, G., Shlomov, S., Tepper, N., & Zwerdling, N. (2020). 数据不足?深度学习来拯救! 第... AAAI人工智能会议论文集。AAAI Conference on Artificial Intelligence, 34(05), 7383–7390。 https://doi.org/10.1609/aaai.v34i05.6233

  • Clark, K., Luong, M.-T., Khandelwal, U., Manning, C. D., & Le, Q. V. (2019). BAM!自然语言理解中再生的多任务网络。载于arXiv [cs.CL]。http://arxiv.org/abs/1907.04829

第七章:掌握提示工程的基本原理

第五章中,我们简要评估了使用情境学习或少量提示方法对经过微调的大型语言模型LLM)与通用模型进行对比。在本章中,我们将重新审视并探索提示技术,以检验我们如何在不进行微调的情况下适应通用LLM。我们探讨了各种利用模型内在能力产生针对性和上下文相关输出的提示策略。我们将首先考察向基于提示的语言模型的转变。然后,我们将回顾零样本和少量样本方法,解释提示链,并讨论各种策略,包括更高级的技术,如检索增强生成RAG)。在本章末尾,我们将应用所学知识,设计一个提示策略,旨在持续地引发事实性、准确性和一致性的响应,以完成特定的业务任务。

在深入探讨具体的提示工程技术之前,我们将回顾一些开创性的突破,这些突破为最先进SOTA)的基于提示的模型铺平了道路。2018年初的研究展示了如何通过预训练LLM实现少量样本泛化——在仅提供提示语句和少量演示的情况下,在新任务上实现准确的表现。后续工作进一步调整了模型架构和训练,以在许多特定文本任务上的基于提示的推理中表现出色。最近的方法优化了模型的效率和稳定性,实现了准确、可靠和高效的提示完成。这些创新为提示工程奠定了基础,展示了基于提示的模型在最小输入数据下的非凡灵活性。现在,提示设计正成为研究的一个子领域——解锁SOTA性能,以应对不断扩大的任务范围。让我们开始吧。

基于提示的方法的转变

如前几章所述,原始GPT的发展标志着自然语言生成的一个重大进步,引入了使用提示来指导模型的方法。这种方法使得像GPT这样的模型能够在没有特定任务训练的情况下执行翻译等任务——将“你好,你好吗?”之类的文本转换为“Bonjour, comment ça va?”。这是在预训练期间学习到的深度上下文语义模式的基础上实现的。这种通过自然语言提示与语言模型交互的概念,在2020年OpenAI的GPT-3中得到了显著扩展。与前辈们不同,GPT-3在零样本和少样本学习场景中理解并响应提示的能力非常突出,这与早期模型在直接交互方面的不足形成了鲜明对比。包括用于实现GPT-3高级性能的具体训练策略和数据集在内的方法仍然大部分未公开。然而,从OpenAI的公开研究中可以推断,该模型学会了根据其庞大的训练语料库遵循指令,而不是显式的指令调整。GPT-3在基于简单直接提示执行任务方面的成功突显了语言模型理解并执行广泛任务的能力,而无需为每个新任务提供显式的特定任务训练数据。这导致了NLP研究和应用领域的一个新范式,关注的是模型如何有效地通过指令来执行摘要、翻译、内容生成等任务。

GPT-3发布后,OpenAI是首批将专用微调引入其InstructGPT(Ouyang等,2022年)发布中,以更准确地响应指令的公司之一。研究人员旨在通过两种新颖的方法教会模型紧密遵循指令。第一种是监督式微调SFT),它涉及使用从提示和响应对精心制作的集合数据集进行微调。然后,这些演示数据集被用来在GPT-3预训练模型之上执行SFT,从而使其提供与人类响应更接近的响应。"图7.1"提供了一个提示和响应对的示例。

图7.1:InstructGPT SFT指令和输出对

图7.1:InstructGPT SFT指令和输出对

第二种方法涉及使用人类反馈强化学习RLHF)进行额外的细化。强化学习RL)是几十年前建立的,旨在提高自主代理的决策能力。它通过教会他们根据风险与奖励之间的权衡来优化其行为来实现这一点。策略捕捉了代理行为的指南,随着新见解和反馈的学习而动态更新,以进一步细化决策。RL是许多机器人应用中使用的确切技术,最著名的是应用于自动驾驶。

RLHF是传统RL的一种变体,它结合了人类反馈以及通常的风险/奖励信号,以指导LLM的行为更好地与人类判断相一致。在实践中,人类标注员会对来自各种提示的模型输出提供偏好评分,这些评分将用于更新模型策略,引导LLM生成更符合预期用户意图的响应,涵盖各种任务。实际上,这项技术有助于减少模型生成不适当、有偏见、有害或其他不受欢迎内容的倾向。尽管RLHF在这方面不是完美的解决方案,但它代表了朝着更好地理解和与人类价值观相一致的方向迈出的重要一步。

那年稍后,随着OpenAI推出InstructGPT,谷歌揭幕了微调语言网络FLAN(Wei et al., 2021)。FLAN代表了向基于提示的LLM的又一跃进,采用了显式的指令微调。谷歌的方法依赖于将现有数据集格式化为指令,使模型能够理解各种任务。具体来说,FLAN的作者们将多个不同类别(如翻译和问答)的NLP数据集合并在一起,为每个数据集创建独特的指令模板,将它们作为遵循指令的任务来构建。例如,FLAN团队利用ANLI挑战(Nie et al., 2020)构建了旨在测试模型对复杂文本关系和推理理解的问答对。通过将这些挑战作为问答对来构建,FLAN团队可以直接衡量模型在统一遵循指令框架下推断这些关系的熟练程度。通过这种创新方法,FLAN有效地扩大了模型可以学习的任务范围,增强了其在各种NLU基准测试中的整体性能和适应性。图7.2展示了基于ANLI的问答对的理论示例。

图7.2:基于ANLI数据集的训练模板

图7.2:基于ANLI数据集的训练模板

再次强调,FLAN背后的核心思想是,每个基准数据集(例如,ANLI)都可以被转换成直观的指令格式,从而产生广泛的混合指令数据和自然语言任务。

这些进步以及其他进步代表了LLMs能力的重要演变,从需要为每个任务进行特定训练的模型转变为可以直观地遵循指令并适应多种任务的简单提示的模型。这种转变不仅扩大了这些模型可以执行的任务范围,还展示了AI以前所未有的精确度处理和生成人类语言复杂方式的潜力。

通过这个洞察,我们可以将我们的关注点转移到提示工程。这门学科结合了技术技能、创造力和人类心理学,以最大化模型对指令的理解和响应的适当性和准确性。我们将学习影响模型行为趋向精确的提示技术。

基本提示 - 指导原则、类型和结构

第五章中,我们介绍了零样本和少样本学习的概念,为模型提供直接指令,或与特定任务的示例相结合的直接指令。在本节中,我们将重点关注零样本学习,其中提示成为引导模型在没有先前明确训练的情况下执行特定任务的临界工具。本节探讨了提示的要素以及如何有效地构建它以进行零样本学习。然而,我们首先将建立一些关键的指导原则,以帮助我们理解预期的模型行为。

模型交互的指导原则

理解这一点至关重要,即尽管LLMs在自然语言任务上取得了前所未有的SOTA性能,但它们具有显著的内生局限性、弱点和易受攻击性。如第一章所述,LLMs无法建立推理或执行原生逻辑运算。我们与LLMs的互动通常由一个高度复杂的应用层补充,该应用层使原始模型能够进行扩展交流,与执行计算的系统集成,并检索模型本身非固有的额外信息和知识。独立于补充集成,许多LLMs容易产生不稳定的行为。其中最常见的是通常被称为幻觉的现象,即模型生成一个看似合理但并非完全真实的输出。因此,我们应该带着以下指南来考虑LLMs的通用使用:

  • 应用领域知识和专业知识:由于最先进的LLMs容易生成听起来合理的错误信息,在事实性和精确性至关重要的用例中(例如,代码生成、技术写作或学术研究),用户必须对主题有牢固的掌握,以便检测潜在的不准确性。例如,如果一个没有医学专业知识的使用者要求模型提供医疗建议,模型可能会混淆、混淆或简单地发明可能导致误导或潜在危险建议的信息。对此行为的缓解措施之一是向模型提供来自权威健康杂志的信息,并指示它明确地从提供的段落中生成答案。这种技术通常被称为“扎根”,我们将在稍后深入探讨。然而,即使在补充模型知识以验证信息的情况下,模型仍然可能错误地表述事实。如果没有特定领域的专业知识,我们可能永远无法检测到错误信息。因此,我们应该一般避免在无法验证模型输出时使用LLMs。此外,我们应该避免在高风险场景中使用LLMs,因为错误的输出可能具有深远的影响。

  • 承认偏见、代表性不足和毒性:我们已经描述了LLMs是如何在巨大规模上以及通常在未经筛选的数据集上训练的。不可避免的是,LLMs将学习、展示并放大社会偏见。模型将传播刻板印象,反映有偏见的假设,并生成有毒和有害的内容。此外,LLMs可能会过度代表某些群体,而严重低估其他群体,导致社会视角扭曲或变形。这些偏见观念可以以多种方式表现出来。我们将详细探讨这一主题,以及其他LLM使用的伦理影响,在第8章中。

  • 避免歧义和缺乏清晰度:由于大型语言模型(LLMs)被训练生成类似于人类响应的信息,它们往往表现出创造性。在实践中,如果提示不明确或缺乏清晰度,模型可能会利用其庞大的上下文知识来“假设”或“推断”给定提示或指令的意义或目标。它可能会应用其训练中的某些上下文信息,而不是以澄清问题的形式进行回应。正如我们将在下一节中描述的,在大多数情况下,通过上下文化输入来提供清晰度至关重要。

现在我们已经确立了一些指导原则,以帮助我们在互动中保持适当的使用范围,我们可以分解提示的各个要素。

提示元素和结构

通常,提示充当指南,引导模型的响应趋向于期望的结果。它通常包含关键元素,这些元素构成了手头的任务,为模型的生成能力提供清晰和方向。以下表格展示了零样本提示的基本要素。

指令 一个清晰、简洁的声明,描述你希望模型执行的操作。这可能是一个直接命令、一个问题,或者暗示一个任务的陈述。
上下文 需要的相关信息或背景知识,以理解指令或任务。这可能包括定义或说明。
输入 遵循指令,模型应使用特定的数据或内容。这可能是一段文本、一个问题或与任务相关的任何信息。
输出提示 指示模型响应的结构方式。这可以是指令的一部分,或者通过提示的格式暗示。

表7.1:零样本提示的基本元素

我们可以对这些元素进行结构化,以最大化零样本方法,其中模型完全依赖于提示来理解和执行任务。在这种情况下,我们使用术语任务来描述一个特定的自然语言任务,例如摘要或翻译。然而,我们也会遇到更广泛地使用任务这一术语,指的是模型应该提供的输出。让我们探讨一些各种任务的几个具体例子。在这种情况下,我们将参考特定的NLP任务,并应用一个标准结构,结合我们描述的关键元素:

  • 太阳能和风能等可再生能源为化石燃料提供了可持续的替代品,减少了温室气体排放,并促进了 环境保护 ...

    "可再生能源,如太阳能和风能,在减少排放和节约 环境 方面发挥着至关重要的作用。"

  • "你好,你 好吗?"

    这翻译成“Hola, ¿cómo estás?”

    结构化模板帮助我们高效且可靠地针对广泛的各种输入提示模型,同时保持模型已学会识别和响应的结构。实际上,我们可以更进一步,要求模型在其输出中提供特定的格式。使用输出提示,我们可以指示模型提供指定的格式,例如Markdown。

  • "请编写一个Python函数来计算一个数字的平方。"

    输出提示:通过在输出提示中使用Markdown格式,模型知道提供这种格式,并返回以下内容:

    def square(number):
    
        return number ** 2
    

    使用LangChain生成JSON格式的输出,我们可以利用相同的方法。具体来说,LangChain的PromptTemplate提供了一个灵活的方式来动态定义我们提示的结构,并插入元素:

    from langchain.prompts import PromptTemplate
    
    from langchain.llms import OpenAI
    
    # Define a prompt template requesting JSON formatted output
    
    prompt_structure = PromptTemplate(
    
        template="""
    
            Context: {context}
    
            Instruction: {instruction}
    
            Text: {text_to_process}
    
            Output Cue: Format the response in JSON with one element called summary.
    
        """,
    
        input_variables=["context," "instruction",
    
            "text_to_process"]
    
    )
    
    # Dynamic elements for the prompt
    
    context = "Summarizing long text passages."
    
    instruction = "Summarize the key points from the following text in JSON format."
    
    text_to_process = """
    
    Mars is the fourth planet from the Sun. The surface of Mars is orange-red because…
    
    """
    
    formatted_prompt = prompt_structure.format_prompt(
    
        context=context,
    
        instruction=instruction,
    
        text_to_process=text_to_process
    
    )
    
    llm = OpenAI(model_name='gpt-3.5-turbo-instruct',
    
        temperature=0.9, max_tokens = 256)
    
    response = llm.invoke(formatted_prompt)
    
    print(response)
    

    这会产生以下内容:

    {
    
        "summary": "Mars is the fourth planet from the Sun, known for its orange-red surface and high-contrast features that make it a popular object for telescope viewing."
    
    }
    

使用大型语言模型(LLMs)进行零样本学习时,制定有效的提示需要清楚地理解任务,仔细构建提示结构,并考虑模型如何解释和响应提示中的不同元素。通过应用这些原则,我们可以引导模型准确有效地执行各种任务。随后,我们将探讨通过积极的肯定、情感参与和其他认知行为技术来引导模型行为的方法。

提升提示 – 迭代和影响模型行为

在本节中,我们将介绍受认知行为研究启发的增强人工智能模型交互的技术。行为提示可以引导模型做出更准确和细腻的反应。例如,通过向模型提供积极的情感刺激、要求模型扮演一个角色或人物,或使用情境提示(即角色扮演),可以提高 LLM 的性能。然而,认识到这些技术也可能被滥用或无意中引入刻板印象是至关重要的,因为它们依赖于可能不准确反映个人经验或多元视角的假设和概括。如果没有仔细考虑和监控,存在加强现有偏见或创造新偏见的风险,可能导致输出结果偏颇或有害。鉴于这些挑战,我们将探讨在人工智能交互中负责任地应用认知行为技术的途径,旨在利用其益处,同时最大限度地减少风险,确保包容性和公平性。

LLMs 对情感线索做出反应

微软与包括北京师范大学心理学系在内的多个机构合作进行的研究表明,LLMs 可以模仿并表现出一些人类情感智能的方面。当提示中融入情感刺激时,这可以提高任务性能。特别是,研究人员假设强调积极词汇可以引发更建设性和有效的反应。这一现象尚未得到充分理解,但其效果是积极的情感线索似乎可以一致地提高模型在各种任务上的性能(Li 等人,2023)。例如,研究人员输入了鼓励自信和积极结果的短语,如“这对我的职业生涯非常重要”或“相信你的能力,追求卓越。你的辛勤工作将产生显著的结果”。这类心理元素不仅对输出质量产生了积极影响,还使互动更加有效和细腻。

角色扮演的影响

研究表明,角色的效果使得大型语言模型能够通过采用针对特定用户配置文件定制的多样化特征、观点和响应风格来更好地模拟类似人类的交互。这种方法增强了模型进行更个性化、情境相关对话的能力。将角色纳入 LLM 提示中有效地改变了可能输出的分布,将模型的生成过程引导到与特定性格特征或观点产生共鸣的响应。得益于模型在多样化数据集上的广泛训练,它可以复制与描述的角色特征相一致的模式,将注意力和选择过程集中在更明确的输出集上。这增强了相关性和特异性。

为了清晰起见,让我们重新考虑我们的假设商业案例 StyleSprint,一个时尚品牌。如果我们用一个 LLM 提示“作为一个充满创意和大胆的时尚品牌,StyleSprint 将如何描述其即将到来的夏季系列?”,模型在采用品牌角色的情况下可能会生成以下类似的响应:

这个夏天,StyleSprint 挑战你脱颖而出!我们的系列设计充满活力,色彩斑斓,图案丰富,专为无畏的时尚达人打造。想象一下大胆的图案、大胆的剪裁,以及彰显自信的宣言。从阳光明媚的海滩到时尚的城市街道,我们的单品承诺让每一刻都难以忘怀。这个夏天,与 StyleSprint 一起拥抱你的大胆吧。”。

在这个例子中,我们可以看到角色如何引导模型的输出以反映特定的品牌调性,例如在本例中的创造力和大胆。这种方法使营销材料更具吸引力,并与品牌形象保持一致。此外,通过使用少量样本的方法,我们可以赋予模型响应以非常具体的品牌调性。我们将在接下来的章节中讨论少量样本。

然而,正如所讨论的,角色应谨慎使用。角色可能会延续刻板印象和偏见,尤其是针对边缘化群体。斯坦福大学的研究人员进行的一项研究发现,基于交叉人口统计群体的角色生成往往会产生比人类撰写的文本更高的种族刻板印象和其他化模式(或描绘某人或某个群体为本质上不同或外来的模式)。在某些情况下,模型输出可能会放大叙事和陈词滥调(Cheng, Durmus, & Jurafsky, 2023)。

情境提示或角色扮演

在 LLM 中进行角色扮演,与角色类似,涉及采用特定的身份或特征。然而,这两个概念服务于不同的目的,并应用于不同的情境。角色是一组预定义的特征或特性,LLM 通过模仿这些特征来定制其响应,专注于与这些特征的连贯性。正如我们在 StyleSprint 例子中所展示的,这对于创建具有特定语气或观点的内容是有用的。

相反,角色扮演不仅涉及采取一系列特征来动态参与一个场景或叙事,它还涉及 LLM 在模拟环境或故事中扮演一个角色,以与角色和角色扮演场景的演变背景相一致的方式响应输入。这在复杂的模拟中特别有用,其中 LLM 必须实时导航并参与需要理解和适应新信息或变化情况的持续叙事或对话。

图 7.3:角色扮演与角色对比

图 7.3:角色扮演与角色对比

回顾我们的现实场景,角色扮演在创造互动和吸引人的客户服务体验方面可能特别有用。例如,StyleSprint 可以设计一个角色扮演场景,其中 LLM 扮演虚拟个人造型师。在这个角色中,模型会通过诸如“我是你今天的个人造型师!你为哪个场合打扮?”这样的提示与客户互动。根据客户的回答,LLM 可以提出后续问题来缩小偏好范围,例如“你更喜欢鲜艳的颜色还是柔和的色调?”。最后,它可以推荐符合客户需求的 StyleSprint 收藏中的服装,比如说“对于夏日婚礼,我推荐我们的花卉长裙搭配复古太阳帽。它既优雅,又非常适合户外环境!”

在这种情况下,我们利用 LLM 根据客户输入动态调整对话的能力,创建一个高级推荐系统,该系统有助于实现高度个性化的购物体验。它不仅有助于提供定制的时尚建议,而且以新颖的方式吸引客户。

在研究了行为启发技术,如角色和角色扮演,如何通过零样本学习影响模型行为之后,我们现在将注意力转向少样本学习。这同样也被称为情境学习,我们在第 5 章中进行了描述。回想一下,少样本方法可以增强模型响应的一致性、稳定性和可靠性。通过在提示中本身提供一些期望输出的示例,少样本学习有效地教会模型手头的特定任务,从而产生更可预测和准确的输出。

高级提示的实际应用 - 少样本学习和提示链

在少量示例设置中,LLM在输入提示中展示了少量任务示例,引导模型生成与这些示例一致的响应。正如前一章所讨论的,这种方法显著减少了在大规模、特定任务数据集上进行微调的需求。相反,它利用了模型预先存在的知识和从提供的示例中推断上下文的能力。在第5章中,我们看到了这种方法如何特别有助于StyleSprint,通过使模型在仅提供几个示例后回答特定问题,增强了品牌信息的连贯性和创造性。

这种方法通常涉及使用10到100个示例,具体取决于模型的上下文窗口。请记住,上下文窗口是语言模型在一次操作中可以处理的标记数的限制。少量示例方法的主要优势是它最小化了模型通过微调从特定数据集中学习到一个过于狭窄分布的风险。尽管少量示例的性能可能并不总是与经过微调的模型相匹配,但少量示例学习通常优于单次和零次学习,显示出在任务适应性和准确性方面的显著改进。这一点在向上下文窗口添加更多示例时尤其正确(Brown et al., 2020)。

LangChain等应用提供了少量示例实现的简单方便的模式。考虑一个场景,StyleSprint希望为其季节性系列生成标语。在这种情况下,我们可以向模型提供内容团队编写的示例,以引导模型与品牌语气保持一致:

examples = [
    {
        "prompt": "Describe the new summer collection in a bold and adventurous tone.",
        "response": "Dive into summer with StyleSprint's latest collection! Featuring daring designs and vibrant colors, it's all about making bold statements. Perfect for the fearless fashionista ready to conquer the heat."
    },
    {
        "prompt": "How would you introduce our eco-friendly line to environmentally conscious customers?",
        "response": "Embrace sustainable style with StyleSprint's eco-friendly line. Crafted from recycled materials, each piece combines fashion with responsibility, designed for the eco-conscious and trendy."
    }
]

LangChain API提供了FewShotPromptTemplate来格式化示例:

from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain.prompts.prompt import PromptTemplate
# Create a formatter
prompt_format = PromptTemplate(
    input_variables=["prompt", "response"],
    template="Prompt: {prompt}\nResponse: {response}")
# Create the FewShotPromptTemplate
few_shot_prompt = FewShotPromptTemplate(
    examples=examples, example_prompt=prompt_format,
    suffix="Prompt: {input}", input_variables=["input"])

我们现在可以将模板应用于一个大型语言模型(LLM),生成一个我们预期将与我们示例的语气和风格紧密一致的响应:

from langchain import LLMChain, OpenAI
# Setup the LLM and LLMChain
llm = OpenAI(temperature=0)
llm_chain = LLMChain(llm=llm, prompt=few_shot_prompt)
# Define the input prompt
input_prompt = "Create a catchy tagline for our winter collection."
# Invoke the chain to generate output
response = llm_chain.run(input_prompt)
# Extract and print the generated slogan
generated_slogan = response
print(generated_slogan) 
    # => Response: "Stay warm,
    stay stylish,
    stay ahead with StyleSprint's winter collection!"

现在我们有了为模型提供示例的一致和程序化方法,我们可以通过提示链来迭代模型响应。提示链通常指的是将多个提示和LLM交互串联起来,与模型进行对话并迭代构建结果。记住,模型本身无法存储信息,实际上几乎没有记忆或先前的输入和输出。相反,应用层存储先前的输入和输出,这些输入和输出随后在每个交互中提供给模型。例如,您可能从一个初始提示开始,如下所示:

"Write a slogan for a winter clothing line"

LLM可能会生成以下内容:

"Be warm, be cozy, be you"

然后,您可以使用以下内容构建后续提示:

"Modify the slogan to be more specific about the quality of the clothing"

然后,您可以继续迭代以改进输出。

链接化有助于引导和交互式地完善生成的文本,而不是仅仅依赖于给定的示例。请注意,我们之前的少量示例代码已经建立了一个链,我们现在可以使用它来迭代如下:

response = llm_chain.run("Rewrite the last tag to something about embracing the winter")
Response # 
=> Response: Embrace the winter wonderland with StyleSprint's latest collection. From cozy knits to chic outerwear, our pieces will keep you stylish and warm all season long.

模型现在正在从我们所提供的示例和我们想要作为链的一部分包含的任何附加指令中工作。提示链与少量示例学习相结合,提供了一个强大的框架,用于迭代引导语言模型输出。通过利用应用程序状态来维护对话上下文,我们可以引导模型朝着与提供的示例一致的期望响应。这种方法在利用模型的推理能力的同时,保持了对其创造性输出的控制。

接下来,我们将深入我们的实践项目,该项目实现了 RAG。RAG 通过检索和整合外部数据源来增强模型响应。这种技术通过将 AI 生成的文本定位在真实数据中来减轻幻觉风险。例如,StyleSprint 可以利用过去的客户调查结果或目录数据来增强产品描述。通过结合检索和提示链,RAG 提供了一种可扩展的方法,以平衡创造力和准确性。

实践项目:使用 Python 实现 RAG 与 LlamaIndex

在我们的实践项目中,我们将从 LangChain 转向探索另一个促进 RAG 方法实现的库。LlamaIndex 是一个开源库,专门为基于 RAG 的应用程序设计。LlamaIndex 简化了跨各种数据源的摄取和索引。然而,在我们深入实施之前,我们将解释 RAG 背后的方法和途径。

如前所述,RAG 的关键前提是通过提供来自外部数据源的相关上下文来增强 LLM 的输出。这些来源应提供具体且经过验证的信息,以使模型输出有据可依。此外,RAG 可以选择性地利用少量示例方法,在推理时检索少量示例以指导生成。这种方法减轻了在提示链中存储示例的需要,并且仅在需要时检索相关示例。本质上,RAG 方法是我们已经讨论过的许多提示工程技术的综合。它提供了结构、链式、少量示例学习和定位。

从高层次来看,RAG 管道可以描述如下:

  1. RAG 组件使用向量嵌入来编码语义,摄取和索引特定领域的数据源。正如我们在 第 3 章 中所学,这些嵌入包含着深入上下文、丰富的语义信息,该组件随后使用这些信息执行语义搜索。

  2. 组件随后使用初始提示作为搜索查询。查询被输入到检索系统中,该系统根据向量相似性从索引数据中找到最相关的片段。类似于我们在先前章节中应用语义相似性的方式,RAG 利用相似性度量来按语义相关性对结果进行排序。

  3. 最后,原始提示通过检索到的上下文信息得到增强,增强后的提示被传递给LLM以生成基于外部数据的响应。

RAG引入了两大优势。首先,类似于链式方法,索引的外部数据充当一种记忆形式,克服了LLM的无状态性。其次,这种记忆可以快速超越模型上下文窗口的限制,因为示例是在请求时根据需要精心挑选并提供的。最终,RAG在可靠和事实性的文本生成中解锁了其他方法无法达到的能力。

在我们的实践项目中,我们重新审视了StyleSprint产品的描述。这次,我们希望利用RAG检索有关产品的详细信息以生成非常具体的描述。为了使这个项目易于访问,我们将实现一个内存中的向量存储(Faiss)而不是外部数据库。我们首先安装必要的库。我们将利用LlamaIndex对Faiss的集成支持:

pip install llama-index faiss-cpu llama-index-vector-stores-faiss

然后,我们将导入必要的库,加载数据,并创建索引。这个向量存储将依赖于OpenAI的嵌入,因此我们必须使用有效的密钥定义OPENAI_API_KEY

assert os.getenv("OPENAI_API_KEY") is not None, 
    "Please set OPENAI_API_KEY"
# load document vectors
documents = SimpleDirectoryReader("products/").load_data()
# load faiss index
d = 1536 # dimension of the vectors
faiss_index = faiss.IndexFlatL2(d)
# create vector store
vector_store = FaissVectorStore(faiss_index=faiss_index)
# initialize storage context
storage_context = StorageContext.from_defaults(
    vector_store=vector_store)
# create index
index = VectorStoreIndex.from_documents(
    documents,storage_context=storage_context)

现在我们有一个向量存储,模型可以依赖它来检索我们非常具体的产品数据。这意味着我们可以查询非常具体的、由我们的数据增强的响应:

# query the index
query_engine = index.as_query_engine()
response = query_engine.query("describe summer dress with price")
print(response) 
=> A lightweight summer dress with a vibrant floral print is priced at 59.99.

结果是,不仅提供了夏季连衣裙的准确描述,还包括了价格等具体细节。这种详细程度丰富了客户的购物体验,为顾客在购买时考虑提供了相关和实时信息。

下一步是评估我们的RAG实现,以确保答案相关、忠实于源文本、反映上下文准确性,并且不会以任何方式有害或不适当。我们可以应用开源评估框架(RAGAS),该框架提供了以下指标的实现:

  • 忠实度评估生成的响应忠实或真实于原始上下文的程度

  • 答案相关性评估生成的答案与给定问题的相关性

  • 上下文精确度衡量用于生成答案的上下文的精确度

  • 上下文回忆衡量用于生成答案的上下文的回忆程度

  • 上下文相关性评估用于生成答案的上下文的相关性

  • 有害性评估提交(或答案)中是否包含可能对个人、群体或整个社会造成伤害的内容

这套指标提供了一种基于与真实数据比较的客观度量RAG应用性能的方法。在我们的案例中,我们可以使用从我们的产品数据生成的响应,以及从原始数据集中提取的上下文和真实数据,来构建一个评估数据集,并使用所描述的指标进行全面的评估。

以下是一个实现我们生成产品描述的RAGAS评估的简化代码片段。完整的可工作实现可在本书的GitHub配套文件夹的第7章中找到(https://github.com/PacktPublishing/Generative-AI-Foundations-in-Python)。

# Define the evaluation data
eval_data: Dict[str, Any] = {
   "question": questions, # list of sampled questions
   "answer": engine_responses, # responses from RAG application
   "contexts": contexts, # product metadata
"ground_truth": ground_truth, # corresponding descriptions written by a human
}
# Create a dataset from the evaluation data
dataset: Dataset = Dataset.from_dict(eval_data)
# Define the evaluation metrics
metrics: List[Callable] = [
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
    context_relevancy,
    harmfulness,
]
# Evaluate the model using the defined metrics
result: Dict[str, float] = evaluate(dataset, metrics=metrics)
print(result)

我们的评估计划应产生以下结果:

{'faithfulness': 0.9167, 'answer_relevancy': 0.9961, 'context_precision': 0.5000, 'context_recall': 0.7500, 'harmfulness': 0.0000}

我们可以观察到,系统在生成准确和相关的答案方面表现良好,这从高忠实度和答案相关性得分中可以看出。虽然上下文精确度还有改进的空间,但一半的相关信息被正确识别。上下文召回率有效,检索了大部分相关上下文。有害内容的缺失确保了安全交互。总体而言,系统在准确和上下文中回答方面表现出稳健的性能,但可以从改进最相关上下文片段的定位中受益。

如同在第5章第6章中讨论的那样,对LLMs的评估通常需要额外的操作负担,即收集真实数据。然而,这样做使得对模型和应用性能进行稳健评估成为可能。

摘要

在本章中,我们探讨了提示工程(prompt engineering)的复杂性。我们还探讨了从LLM中诱发出精确和一致响应的高级策略,提供了一种灵活的替代微调(fine-tuning)的方法。我们追溯了基于指令的模型(instruction-based models)的演变,强调了它们如何通过简单的提示将范式转向直观理解和适应任务。我们通过诸如少样本学习(few-shot learning)和检索增强(retrieval augmentation)等技术扩展了LLM的适应性,这些技术允许在最小显式训练的情况下,在多样化的任务中实现动态模型指导。本章进一步探讨了有效提示的结构化,以及使用角色扮演和情境提示来使模型响应更贴近特定的交互环境,从而提高模型的应用性和交互质量。我们还讨论了提示工程的细微之处,包括情感线索对模型性能的影响以及实施RLHF(Reinforcement Learning from Human Feedback)来优化模型输出。这些讨论强调了LLM展现出一定程度的情感智能的潜力,从而实现更有效和细致的交互。然而,随着这些技术进步,我们强调了道德考虑的首要重要性。我们强调了负责任地采用和警惕的必要性,以减轻这些技术可能带来的危害和偏见,确保公平性、完整性和防止滥用。

最后,我们学习了如何实现和评估RAG方法,以将LLM(大型语言模型)基于可信来源的上下文信息进行定位,并生成与源文本相关且忠实于源文本的答案。在下一章中,我们将更深入地探讨个人在推进生成式AI中的作用,同时强调开发者和研究人员在以负责任的方式导航这个快速发展的领域时,既要注重创新,也要平衡道德 imperative和社会影响的双重责任。

参考文献

本参考部分作为本书中引用的资源的存储库;您可以探索这些资源,以进一步加深对主题内容的理解和知识:

  • Ouyang, L., Wu, J., Jiang, X., Almeida, D., Wainwright, C. L., Mishkin, P., Zhang, C., Agarwal, S., Slama, K., Ray, A., Schulman, J., Hilton, J., Kelton, F., Miller, L., Simens, M., Askell, A., Welinder, P., Christiano, P., Leike, J., & Lowe, R. (2022). 训练语言模型以遵循人类反馈的指令. 在arXiv [cs.CL]. http://arxiv.org/abs/2203.02155

  • Wei, J., Bosma, M., Zhao, V. Y., Guu, K., Yu, A. W., Lester, B., Du, N., Dai, A. M., & Le, Q. V. (2021). 微调语言模型是零样本学习者. 在arXiv [cs.CL]. http://arxiv.org/abs/2109.01652

  • Nie, Y., Williams, A., Dinan, E., Bansal, M., Weston, J., & Kiela, D. (2020). 对抗性NLI:自然语言理解的新基准。Arxiv.org.

  • Li, C., Wang, J., Zhang, Y., Zhu, K., Hou, W., Lian, J., Luo, F., Yang, Q., & Xie, X. (2023). 大型语言模型能够理解和通过情感刺激得到增强。在arXiv [cs.CL]。http://arxiv.org/abs/2307.11760

  • Cheng, M., Durmus, E., & Jurafsky, D. (2023). 标记人格:利用自然语言提示来衡量语言模型中的刻板印象。第61届计算语言学协会年度会议论文集(第1卷:长篇论文)。

  • Brown, T. B., Mann, B., Ryder, N., Subbiah, M., Kaplan, J., Dhariwal, P., Neelakantan, A., Shyam, P., Sastry, G., Askell, A., Agarwal, S., Herbert-Voss, A., Krueger, G., Henighan, T., Child, R., Ramesh, A., Ziegler, D. M., Wu, J., Winter, C., … Amodei, D. (2020). 语言模型是少样本学习者。在arXiv [cs.CL]。http://arxiv.org/abs/2005.14165

第八章:解决伦理考量并绘制通往可信赖生成式AI的路径

随着生成式AI的进步,它将超越基本语言任务,融入日常生活,并影响几乎每个行业。其广泛采用的必然性突出了解决其伦理影响的必要性。这项技术有望变革行业、增强创造力和解决复杂问题,必须与认真导航其伦理景观的责任相结合。本章将探讨这些伦理考量,剖析这些模型中纠缠在一起的偏见复杂性,并探讨培养通用人工智能系统信任的策略。通过彻底的审查和反思,我们可以开始勾勒出一条负责任使用的路径,帮助确保生成式AI的进步被用于更大的善,同时最大限度地减少伤害。

为了使我们的讨论有据可依,我们首先将确定一些与生成式AI相关的伦理规范和普遍价值观。虽然本章不能详尽无遗,但它旨在介绍关键的伦理考量。

生成式AI背景下的伦理规范和价值

指导生成式AI开发和部署的伦理规范和价值根植于透明度、公平性、问责制、隐私、同意、安全和包容性。这些原则可以作为开发并采用符合社会价值观并支持更大善的系统的基础。让我们详细探讨这些原则:

  • 透明度涉及清楚地解释大型语言模型(LLM)构建背后的方法、数据来源和过程。这种做法通过使利益相关者能够理解技术的可靠性和局限性来建立信任。例如,一家公司可以发布一份详细的报告,说明其LLM训练的数据类型以及为确保数据隐私和偏见缓解所采取的步骤。

  • 在LLM(大型语言模型)的背景下,公平性通过积极预防模型中的偏见,确保所有用户都得到公平对待和结果。这需要彻底分析并纠正训练数据,并持续监控交易以减少歧视。一家公司可能采取的措施是对LLM在各种人口群体中的表现进行常规审查,以识别和解决未预见的偏见。

  • 问责制确立LLM的开发者和用户对模型输出和影响负责。它包括透明和易于访问的报告和解决负面后果或伦理违规的机制。在实践中,这可能表现为建立一个独立的审查委员会,监督AI项目并在伦理不当行为的情况下进行干预。

  • 隐私和同意原则上涉及确保在将个人数据作为LLM输入使用时尊重和保护个人隐私和同意。在实践中,开发者应避免在没有明确许可的情况下使用个人数据进行训练,并实施强大的数据保护措施。例如,开发者可能使用数据匿名化或隐私保护技术来训练模型,确保在数据处理之前移除个人标识符和敏感信息。

  • 安全涉及保护集成LLM的系统及其数据免受未经授权的访问和网络威胁。在实践中,建立LLM特定的红队(或通过模拟攻击测试防御的团队)可以帮助保护AI系统免受潜在的安全漏洞。

  • 包容性涉及在LLM的开发过程中有意识地包括不同的声音和观点,确保技术对广泛的用户可访问和有益。在实践中,与能够指导适当行动以促进和保护包容性的社会-技术领域的专家合作至关重要。

这套原则并非全面,但可能有助于形成道德LLM开发和采用的 conceptual foundation,其普遍目标是避免伤害地推进技术。

此外,各种领先的权威机构已经发布了关于负责任AI的指南,包括伦理影响。这些包括美国商务部国家标准与技术研究院NIST)、斯坦福大学以人为本的人工智能研究所HAI)和分布式人工智能研究院DAIR),等等。

调查和最小化生成式LLM和生成式图像模型中的偏差

生成式AI模型中的偏差,包括LLM和生成式图像模型,是一个复杂的问题,需要仔细的调查和缓解策略。偏差可能表现为在生成输出中的无意刻板印象、不准确性和排除,通常源于有偏差的数据集和模型架构。识别和解决这些偏差对于创建公平和值得信赖的AI系统至关重要。

在其核心,算法或模型偏差指的是导致某些群体受到优先对待或不公平结果的系统性错误。在生成式AI中,这可能会表现为输出中的性别、种族或社会经济偏差,通常反映了社会刻板印象。例如,一个大型语言模型(LLM)可能会生成强化这些偏差的内容,反映出其训练数据中存在的历 史和社会偏差。

让我们再次回顾一下我们的假设时尚零售商,StyleSprint。考虑一种情况,StyleSprint尝试使用多模态生成式LLM模型为其最新的运动鞋系列生成促销图像和标题。它发现该模型主要生成背景为城市、涂鸦遍布的鞋子,无意中引发了一种基于刻板印象的联想。此外,团队开始注意到标题中也充满了持续刻板印象的语言。这一认识促使他们对图像和文本进行重新评估,首先是对问题如何出现进行调查研究。

调查偏见涉及各种技术,从分析训练数据集的多样性和代表性到实施针对不同群体和场景的特定测试协议以寻找偏见输出。统计分析可以揭示模型结果的差异,而比较研究和用户反馈可以帮助识别生成内容中的偏见。

在这个案例中,假设StyleSprint使用的是一个无法影响其训练数据或开发过程的LLM提供商。为了减轻偏见风险,团队可能采取以下措施:

  • 后处理调整以多样化图像,确保更广泛地反映与其客户群产生共鸣的背景

  • 建立一个手动审查流程,让团队成员仔细审查和精选AI生成的图像和标题,在发布之前确保每件内容都符合品牌对多元化和包容性的承诺(即“人机协同”)

对于生成式AI的其他类型评估来说,评估偏见需要定量和定性方法。统计分析可以揭示不同群体之间的性能差异,比较研究可以检测输出中的偏见。收集来自不同用户的反馈有助于理解现实世界中的偏见影响,而独立的审计和研究对于识别内部评估可能遗漏的问题至关重要。

在更好地理解我们如何调查和评估模型的社会偏见结果之后,我们可以探索技术方法来引导模型结果向可靠性、公平性和普遍可信度发展,以减少推理过程中的偏见或不公平结果。

限制生成和诱导可信结果

在实践中,可以通过限制模型生成并引导结果向事实性和公平性方向发展。正如所讨论的,通过持续训练和微调,或在进行推理时,可以引导模型向可信赖的结果发展。例如,从人类反馈中进行强化学习(RLHF)和直接偏好优化(DPO)等方法越来越多地细化模型输出,以使模型结果与人类判断一致。此外,正如在第7章*中讨论的,各种定位技术有助于确保模型输出反映经过验证的数据,持续引导模型向负责任和准确的内容生成方向发展。

通过微调进行受限生成

如同在第7章*中讨论的,将人类判断整合到模型训练过程中的细化策略,如RLHF,将AI引导至符合伦理和真实标准的行动。通过引入人类反馈循环,RLHF确保AI的输出达到技术准确性和社会规范。

类似地,数据保护官(DPO)根据明确的人类偏好来细化模型输出,提供精确的控制以确保结果符合道德标准和人类价值观。这种技术体现了内容生成向更符合伦理标准转变的趋势,因为它直接将人类价值观纳入优化过程。

通过提示工程进行受限生成

正如我们在第7章中发现的那样,我们可以通过使用事实信息来定位LLM来引导模型响应。这可以通过直接使用上下文窗口或检索方法(例如,检索增强生成(RAG))来实现。正如我们可以应用这些方法来诱导事实性响应一样,我们也可以应用相同的技巧来引导模型向公平和包容的结果发展。

例如,考虑一家在线新闻机构希望使用大型语言模型(LLM)来审查文章内容的语法和可读性。该模型在审查和修订草稿方面表现出色。然而,在同行评审过程中,它意识到其中一些语言可能存在文化不敏感或缺乏包容性的问题。正如所讨论的,定性评估和人工监督对于确保模型输出与人类判断一致至关重要。尽管如此,写作团队可以使用一套关于包容性和无偏见语言的通用指南来引导模型与公司价值观保持一致。例如,可以通过其内部政策文件的摘录或其无意识偏见培训指南的内容来对模型进行定位。

采用RLHF和DPO等方法论,以及基于事实的技术,确保LLM生成的内容不仅符合事实,而且符合伦理标准,展示了生成式AI遵守高标准的真实性和包容性的潜力。尽管我们不能低估或淡化人类判断在塑造模型输出中的重要性,但我们可以通过应用如基于事实的补充方法等实用方法来降低有害或偏见模型输出的可能性。

在下一节中,我们将探讨试图规避我们刚才讨论的约束所提出的风险和伦理困境,强调在快速采用生成式LLM的同时,平衡适当的滥用防范措施的持续挑战。

理解越狱和有害行为

在生成式LLM的背景下,越狱一词描述了旨在操纵模型以覆盖任何伦理保障或内容限制的技术和策略,从而允许生成受限制或有害的内容。越狱通过复杂的对抗性提示来利用模型,可以诱导出意外或有害的响应。例如,攻击者可能会试图指示LLM解释如何生成显式内容或表达歧视性观点。理解这种易受攻击性对于开发者和利益相关者来说至关重要,以确保应用生成式AI免受滥用并最大限度地减少潜在的危害。

这些越狱攻击利用了LLM被训练来解释和响应指令的事实。尽管已经做出了复杂的努力来防御滥用,攻击者仍然可以利用LLM中嵌入的复杂和广泛的知识来发现其安全预防措施中的漏洞。特别是,在未经审查的数据集上训练的模型最易受影响,因为模型从中采样的可能输出范围可能包括有害和有毒内容。此外,LLM是多语言的,可以接受各种编码作为输入。例如,base64这样的编码可以将纯文本转换为二进制格式,可以用来混淆有害指令。在这种情况下,安全过滤器可能表现不一致,无法检测某些语言或替代输入。

尽管LLM存在这种固有的弱点,但开发者和从业者可以采取一些实际步骤来减轻越狱风险。记住,这些不能详尽无遗,因为新的对抗性技术通常会被发现:

  • 预处理和安全过滤:实施强大的内容过滤,以检测和阻止跨语言和输入类型的危险语义模式。例如,一家公司可能会应用机器学习技术来分析提示中的对抗性模式,并在将它们传递给LLM之前阻止可疑的输入。

  • 后处理和输出筛选:在返回之前,应用一个专门的分类器或其他复杂技术来筛选LLM输出中的不适当内容。

  • 以安全为重点的微调:为LLM提供额外的以安全为重点的微调,以加强和扩展其安全知识。重点关注已知的越狱策略。

  • 监控和迭代:在生产环境中积极监控越狱或政策违规尝试,分析它们以识别差距,并不断更新防御措施以保持领先于有创造性的攻击者。

虽然消除所有可能的越狱尝试是不切实际的,但多层防御和操作最佳实践可以显著降低风险。

在下一节中,我们将应用实时越狱防御机制,同时降低产生偏见和有害输出的可能性。

实践项目:使用过滤来最小化有害行为

对于这个项目,我们将使用响应过滤来尝试最小化误用并遏制不想要的LLM输出。再次强调,我们将考虑我们的假设业务,StyleSprint。在成功使用LLM生成产品描述并微调它以回答常见问题之后,StyleSprint现在想要尝试使用一个通用的LLM(不进行微调)来优化其网站搜索。然而,直接让客户访问LLM存在误用的风险。恶意行为者可能会尝试使用LLM搜索来产生有害内容,意图损害StyleSprint的声誉。为了防止这种行为,我们可以回顾我们在第7章中提到的RAG实现,应用一个过滤器来评估查询是否偏离了适当的使用。

重新使用上一章中(可在GitHub仓库https://github.com/PacktPublishing/Generative-AI-Foundations-in-Python找到)的先前实现,该实现将RAG应用于回答特定产品相关的问题,我们可以评估模型对超出预期范围的问题的响应。回想一下,RAG只是一个向量搜索引擎与LLM结合,以产生由特定数据源上下文化的连贯且更精确的响应。我们将直接重用该实现和相同的产品数据以简化流程,但这次,我们将输入一个完全不相关的查询而不是询问产品:

# random query
response = query_engine.query("describe a giraffe")
print(response) 
=> A giraffe is a tall mammal with a long neck, distinctive spotted coat, and long legs. They are known for their unique appearance and are the tallest land animals in the world.

如我们所见,该模型没有尝试将其答案限制在搜索索引的内容之内。它基于其庞大的训练数据返回了一个答案。这正是我们想要避免的行为。想象一下,一个恶意行为者诱导模型生成显式内容或某些其他不希望产生的输出。此外,考虑一个复杂的攻击者,可能诱导模型泄露训练数据或在训练过程中意外记住的敏感信息(Carlini等,2018;胡等,2022)。在任何情况下,StyleSprint都可能面临重大的风险和暴露。

为了防止这种情况,我们可以利用过滤器来限制输出,明确提供与给定问题相关的答案。该实现已经内置到LlamaIndex RAG接口中。他们称之为结构化答案过滤:

当structured_answer_filtering设置为True时,我们的refine模块能够过滤掉任何与所提问题不相关的输入节点。这对于涉及从外部向量存储检索给定用户查询的文本块的基础RAG问答系统特别有用。(LlamaIndex)

简而言之,这项功能让我们能够精细控制提供给LLM用于综合的上下文,确保只包含最相关的结果。在综合响应之前过滤掉不相关的内容,可以确保只使用与用户问题相关的信息。这种方法有助于避免离题或超出预期主题范围的答案。我们可以快速重新实现我们的RAG方法,通过进行一些小的改动来实现这一功能。

注意

当使用能够支持函数调用的LLM时,这项功能最为可靠。

让我们看看这个功能是如何实现的。

from llama_index.core import get_response_synthesizer
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
# Configure retriever
retriever = VectorIndexRetriever(index=index,similarity_top_k=1)
# Configure response synthesizer
response_synthesizer = get_response_synthesizer(
    structured_answer_filtering=True,
    response_mode="refine"
)
# Assemble query engine
safe_query_engine = RetrieverQueryEngine(
    retriever=retriever,
    response_synthesizer=response_synthesizer
)
# Execute query and evaluate response
print(safe_query_engine.query("describe a summer dress with price"))
# => A lightweight summer dress with a vibrant floral print, perfect for sunny days, priced at 59.99.
print(safe_query_engine.query("describe a horse"))
# => Empty Response

使用这种方法,模型对标准问题返回了响应,但对不相关的问题没有返回响应。实际上,我们可以进一步将这种过滤与提示模板中的额外指令相结合。例如,如果我们修订response_synthesizer,我们可以从LLM促进更严格的响应:

QA_PROMPT_TMPL = (
    "Context information is below.\n"
    "---------------------\n"
    "{context_str}\n"
    "---------------------\n"
    "Given only the context information and no prior knowledge, "
    "answer the query.\n"
    "Query: {query_str}\n"
    "Answer: "
    "Otherwise, state: I cannot answer."
)
STRICT_QA_PROMPT = PromptTemplate(
    QA_PROMPT_TMPL, prompt_type=PromptType.QUESTION_ANSWER
)
# Configure response synthesizer
response_synthesizer = get_response_synthesizer(
    structured_answer_filtering=True,
    response_mode="refine",
    text_qa_template=STRICT_QA_PROMPT
)

这次,模型明确地响应了,“我无法回答”。使用提示模板,StyleSprint可以返回它认为适当的消息,以响应与搜索索引无关的输入,并且作为副作用,忽略不符合其政策的查询。虽然这并不是一个完美的解决方案,但将RAG与更严格的答案过滤相结合可以帮助阻止或防御有害指令或对抗性提示。此外,如第7章*所探讨的,我们可以应用RAG特定的评估技术,如RAGAS,以衡量事实一致性和答案的相关性。

摘要

在本节中,我们认识到生成式人工智能日益突出,并探讨了应引导其进步的伦理考量。我们概述了关键概念,如透明度、公平性、问责制、尊重隐私、知情同意、安全和包容性,这些对于这些技术的负责任开发和利用至关重要。

我们回顾了尝试对抗这些偏差的策略,包括与人类对齐的训练技术和针对如越狱等易受攻击性的实际应用级措施。总之,我们探索了生成式人工智能采用的多元化和以人为本的方法。

在完成我们对生成式人工智能的基础探索之后,我们现在可以反思我们的旅程。我们开始时是奠定基础,检查基础生成架构,例如生成对抗网络(GANs)、扩散模型和转换器。

第2章第3章引导我们了解语言模型的演变,特别关注自回归转换器。我们探讨了这些模型如何显著提升生成式人工智能的能力,推动机器理解和生成类似人类语言边界的扩展。

第4章为我们提供了在生产就绪环境中的实践经验。在第5章中,我们探讨了针对特定任务的LLM微调,这是一种增强其性能和适应特定应用的技术。第6章专注于领域自适应的概念,展示了如何定制AI模型以理解特定领域的细微差别,可以极大地提高其在金融、法律和医疗保健等专门领域的实用性。

第7章第8章集中在提示工程和约束生成上,讨论了确保AI生成内容保持可信并与伦理指南保持一致的技术。

本书旨在为生成式人工智能提供一个坚实的基础,为来自各个学科和行业的专业人士提供必要的理论知识与实践技能,以便有效地参与这一变革性技术。生成式人工智能的潜力是巨大的,随着我们对其技术的更深入理解,以及我们对伦理和社会考量的深思熟虑的方法,我们准备好负责任地利用其优势。

参考文献

本参考部分作为本书中引用的来源库;您可以探索这些资源,以进一步加深对主题内容的理解和知识:

  • Sun, L., Huang, Y., Wang, H., Wu, S., Zhang, Q., Gao, C., Huang, Y., Lyu, W., Zhang, Y., Li, X., Liu, Z., Liu, Y., Wang, Y., Zhang, Z., Kailkhura, B., Xiong, C., Xiao, C., Li, C., Xing, E., . . . Zhao, Y. (2024). TrustLLM: 大型语言模型的可信度. ArXiv. /abs/2401.05561

  • Carlini, N., Liu, C., Erlingsson, Ú., Kos, J., & Song, D. (2018). 秘密分享者:评估和测试神经网络中的无意记忆. 在 arXiv [cs.LG]. http://arxiv.org/abs/1802.08232

  • Hu, H., Salcic, Z., Sun, L., Dobbie, G., Yu, P. S., & Zhang, X. (2022). 机器学习中的成员推理攻击:综述. ACM 计算评论, 54(11s), 1–37. https://doi.org/10.1145/3523273

  • LlamaIndex. (n.d.). 响应合成器. 在 LlamaIndex 文档(稳定版本)中. 2024年3月12日检索。 https://docs.llamaindex.ai/en/stable/module_guides/querying/response_synthesizers/root.html

posted @ 2025-09-20 21:35  绝不原创的飞龙  阅读(79)  评论(0)    收藏  举报