IBM-生成式人工智能工程-Flask-笔记-全-
IBM 生成式人工智能工程 Flask 笔记(全)
生成式人工智能工程:P01:课程介绍 🎯
在本节课中,我们将要学*一门关于使用Python和Flask开发人工智能应用的课程。我们将了解课程的目标、适用人群以及整体的学*路径。
课程概述
欢迎来到这门关于使用Python和Flask开发人工智能应用的课程。
根据Stack Overflow的一项调查,Python是全球使用最广泛的十大编程语言之一。

Flask是一个Python微框架,对外部库的依赖极少甚至没有。
使用Python和Flask,程序员可以开发简洁的应用,即使是在企业级的AI组件中,也能轻松成为可扩展解决方案的一部分。

完成本课程后,你将能够描述创建应用程序所涉及的步骤和流程,创建Python模块,运行单元测试,以及打包应用程序。
你将能够使用Flask框架在Web上部署应用程序,并使用IBM Watson嵌入式AI库和Flask创建并部署一个基于AI的应用程序到Web服务器。


目标学员
本课程面向所有具备基本编程知识、对Python有初步了解,并且有兴趣构建集成AI的可复用Web应用程序的人。
课程模块介绍
上一节我们介绍了课程的整体目标,本节中我们来看看课程的具体内容安排。本课程分为三个主要模块。
以下是各模块的学*内容:
-
模块一:应用开发基础
- 本模块将向你介绍应用程序开发的基础知识,包括生命周期和编码最佳实践。
- 你将有机会创建模块、运行单元测试和打包应用程序。
- 你将学*Python的理想编码实践,并了解如何运行静态代码分析。
-
模块二:Flask框架入门
- 本模块将从Flask的介绍开始。
- 接下来,你将学*部署概念,包括路由、请求和响应对象、错误处理和装饰器。
- 你还将能够使用Flask创建并部署一个应用程序。
-
模块三:AI应用开发实战
- 在模块三中,你将有机会运用之前模块学到的所有知识,使用Watson嵌入式AI库开发功能性的Web应用程序和基于AI的Web应用程序。
- 通过一个练*项目和一个评分项目,你将能够展示自己使用Flask创建和部署应用程序的熟练程度。
- 你必须将评分项目的作业提交给同伴进行互评。
学*建议
为了从本课程中获得最大收益,这里有很多内容需要学*。
以下是给你的学*建议:
- 确保观看每一个视频。
- 通过每个测验检查你的学*成果。
- 完成所有动手实验。
如果你在课程材料的任何部分遇到困难,请不要犹豫,在讨论论坛中联系我们。
感谢你成为我们课程的一员,欢迎你的加入。

总结

本节课中我们一起学*了《生成式人工智能工程》课程的第一部分介绍。我们了解了课程将教你如何使用Python和Flask构建AI应用,明确了学*目标、适用人群以及三个核心模块(应用开发基础、Flask框架、AI实战)的内容安排。最后,我们获得了一些高效完成课程的学*建议。准备好开始你的AI应用开发之旅了吗?
生成式人工智能工程:002:应用程序开发生命周期 🚀
在本节课中,我们将要学*应用程序开发生命周期的完整流程。我们将了解从客户提出需求到最终产品上线及维护的各个阶段,并理解为何将代码组织在多个文件中是一种最佳实践。


概述
想象一下,客户请求你构建一个应用程序来帮助员工追踪每日任务。或者,客户希望你构建一个管理酒店预订的网页应用。在这两种情况下,你都不能在客户提出请求后立即开始构建。你需要执行一系列活动,例如分析需求、规划和编码,然后应用程序才能为用户所用。无论应用程序类型如何,每个应用程序都会经历一系列不同的阶段,这被称为应用程序开发生命周期。
生命周期的七个阶段
你可以将应用程序开发生命周期划分为七个阶段:
- 需求收集
- 分析与设计
- 编码与测试
- 用户与系统测试
- 生产部署
- 维护
- 代码组织最佳实践
接下来,让我们详细看看应用程序开发的每个阶段。
第一阶段:需求收集
需求收集是应用程序开发流程中的第一个阶段。在此阶段,你需要捕获应用程序所有方面的需求,包括用户需求、业务需求和技术需求。
以酒店预订应用为例:
- 用户需求:用户必须能够查看不同的房间和可用设施。
- 业务需求:确定不同房间和设施的合理收费。
- 技术需求:应用必须在所有浏览器和移动设备上运行。
目标是尽可能多地捕获需求,即使有些看起来冗余或微不足道。同时,你必须识别任何设计约束和商业模式的可行性。例如,对于酒店预订应用,一个约束是服务器需要始终保持房间可用状态的更新,这会产生相关成本。因此,为了保持业务可行性,会在最终付款中添加一笔小额便利费。
第二阶段:分析与设计
在收集了需求和约束之后,你必须对其进行分析,为应用程序的设计创建可能的解决方案。
在分析与设计阶段,可能会进行多轮验证和修订,以创建一个满足所有指定需求的模型解决方案。
在应用程序开发的分析与设计阶段,你必须维护适当的文档,记录设计中的所有更新。文档应清晰简洁,以便在编码与测试阶段使用。最终提出的设计和指定的需求将传递给下一个阶段。
第三阶段:编码与测试
在编码与测试阶段,团队使用设计文档中指定的编程需求进行编码、测试、修订和再测试,直到代码满足所有文档化的需求。
让我们回顾一下测试阶段。你对单元代码进行的测试称为单元测试。在编程级别进行单元测试,以确保满足所有必需的规范。
第四阶段:用户与系统测试
单元测试之后,你可以生成一个可接受的应用程序版本。然后,这个新版本的应用程序会经过一系列用户和系统级别的测试。
用户测试从用户的角度验证功能。此外,你还需要执行多项系统级测试,包括集成测试和性能测试。
- 集成测试验证所有相关程序在集成后是否继续按预期运行。它还验证应用程序在更大框架内的功能。
- 性能测试帮助评估应用程序在不同工作负载下的速度、可扩展性和稳定性。
测试完成后,你可以生成一个新的应用程序版本并将其发送到生产环境。
第五阶段:生产部署
一旦进入生产环境,最终用户就可以访问和使用它。你必须确保应用程序功能准确并对用户可用。
当应用程序处于生产阶段时,它必须保持稳定状态。在稳定状态下,你不应对应用程序进行任何更改。然而,这并非总是可能。例如,在出现错误时,你可能需要对应用程序进行更改。这些更改在实施到生产环境之前受到严格控制并经过充分测试。
第六阶段:维护
应用程序开发生命周期的最后一个阶段是维护。应用程序可能需要升级,或者你可能需要添加新功能。在这种情况下,新功能在集成到生产环境中部署的应用程序版本之前,必须经历之前的所有阶段。
第七阶段:代码组织最佳实践
现在,让我们简要回顾一下为什么为不同功能在多个文件中编码是一种最佳实践。
每个应用程序通常都有多个功能,每个功能的需求可能各不相同。最佳实践是在单独的文件中编码每个功能。
然后,你可以创建一个运行应用程序的中央程序,并调用各个文件来执行特定功能。这种组织代码的方法使代码维护高效且容易。
当你需要向现有应用程序添加新功能时,拥有多个文件也会有所帮助。当你将新功能的代码写在单独的文件中时,只有该文件会在集成到运行中的应用程序之前,经历整个设计和验证过程。
总结
本节课中,我们一起学*了应用程序开发生命周期的七个阶段:
- 需求收集:收集应用程序的用户、业务和技术需求。
- 分析:分析需求。
- 设计:设计完整的解决方案。
- 编码与测试:构建和测试应用程序的不同组件。
- 用户与系统测试:用户测试应用程序的可用性,你执行系统集成测试和性能测试。
- 生产:应用程序对所有最终用户可用。
- 维护:升级或修复任何用户或系统问题。


为了高效编码,应始终使用一个中央程序来调用执行不同操作的各个文件和函数。
生成式人工智能工程:003:Web应用程序和API简介 🖥️🔗

在本节课中,我们将要学*Web应用程序和API的基本概念。我们将了解它们的定义、工作原理、优势以及两者之间的关系。

什么是Web应用程序? 🌐
Web应用程序是一种存储在远程服务器上,并通过互联网交付的程序。用户通过浏览器与应用程序进行交互。例如,电子商务网站、网页邮件等服务都属于Web应用程序。虽然某些应用可能依赖于特定的浏览器类型,但大多数现代Web应用程序都能在所有主流浏览器上运行。

Web应用程序的构成组件
一个Web应用程序需要三个核心组件来处理客户端请求:

- Web服务器:用于管理接收到的请求。
- 应用服务器:用于执行请求的任务。
- 数据库:用于存储完成任务所需的信息。
Web应用程序的开发

在开发Web应用程序时,你需要编写两部分的代码:
- 前端(用户端):使用如 JavaScript、HTML 或 CSS 等技术。
- 后端(服务器端):使用如 Python、Java 或 Ruby 等编程语言。
Web应用程序的优势

与严格在用户本地系统上运行的应用程序相比,Web应用程序具有多项优势:
- 开发者可以向多个用户同时提供相同版本的应用程序。
- 用户可以在其选择的平台(如台式机、笔记本电脑或移动设备)上灵活使用应用程序。
- 用户可以通过自己选择的浏览器访问应用程序。
- 用户无需在本地系统上安装该应用程序。
什么是API? 🔌

上一节我们介绍了Web应用程序,本节中我们来看看API。API(应用程序编程接口)是一个软件组件,它使得两个原本互不连接的应用程序能够进行通信。
API为程序员创造了更大的灵活性,使他们能够从原本封闭的应用程序中请求数据。因此,API具有标准化的规则和功能,用于确定可以在应用程序内获取或修改哪些数据,以及该过程如何发生。例如,手机上的应用程序会要求你授予访问手机不同功能(如位置、摄像头、音频和录音机)的权限。
API的实例

一个使用API的典型例子是天气应用程序。你的天气应用本身并不生成天气数据。相反,它只是向一个天气API请求信息。该API将收集和存储天气数据的软件与你移动设备上的应用程序连接起来。设备随后为你提供第二天的详细天气预报。
API的架构与优势
软件开发人员在创建API时遵循多种架构,但最流行的是REST和SOAP。这些架构将在其他视频中讨论。
API之所以有益,原因包括:
- 它改善了应用程序之间的连接性。
- 它支持传统的CRUD操作。
- 它使用HTTP动词,包括 PUT、POST、DELETE 和 GET。
- 它基于HTTP,因此具有可定制性。

Web应用程序与API的关系 🔄
API是一个更通用的术语,指代在系统任意两部分之间创建链接的所有形式的应用程序。Web应用程序是API的一种形式,它在前端和后端之间进行通信。
为了澄清Web应用程序和API之间的区别,让我们考虑一个电子商务购物服务的例子。

- 当你从浏览器访问电子商务购物服务时,浏览器充当连接你与Web应用程序的API。当你选择商品时,Web应用程序会检查商品的可用性,如果可用,则显示其价格。
- 当你尝试使用移动设备访问电子商务购物服务时,你设备上的应用程序则充当连接到电子商务服务的API。
所有Web应用程序的本质都是与其他应用程序共享数据。从本质上讲,所有Web应用程序都可以被视为API。然而,API是一个通用术语,既包括在线(基于Web的)应用程序,也包括离线应用程序。

以下是核心结论:
所有Web应用程序都是API,但并非所有API都是Web应用程序。
总结 📝
本节课中我们一起学*了以下核心内容:


- Web应用程序是存储在远程服务器上并通过浏览器在互联网上交付的程序。你可以在不同的平台和浏览器上运行Web应用程序,而无需安装新软件。
- API是一个软件组件,它使得两个未连接的应用程序能够进行通信。
- 关系:Web应用程序是API,但并非所有API都是Web应用程序。两者都在应用程序之间共享数据,但并非所有API都像Web应用程序那样需要网络连接。
云IDE入门教程:004:使用IDE 🖥️
在本节课中,我们将学*如何使用IBM Skills Network团队提供的云IDE(集成开发环境)。这是一个用于学*的编程环境,你可以直接在浏览器中编写、运行、调试和执行代码,无需在个人设备上安装或配置任何软件或工具。
界面概览 📐
当你打开云IDE时,会显示两个主要窗格。

左侧窗格被称为教学窗格,它显示你需要遵循以完成项目的说明。
右侧窗格显示一个编程界面,你可以在其中编写和执行代码。
请注意,右侧窗格类似于VS Code界面,这是一个流行的代码管理IDE。
调整窗格与设置 ⚙️
上一节我们介绍了两个主要窗格,本节中我们来看看如何调整它们。
你可以调整教学窗格和代码窗格的大小。例如,可以通过从边缘向左拖动来减小教学窗格的大小,或通过向右拖动来增大其大小。
你还可以根据个人偏好修改字体和字体大小。
如果教学窗格中有多个页面,你会看到“下一页”和“上一页”按钮。这些按钮使你能够在页面之间导航。你也可以预览教学页面。
注意教学窗格左上角的“目录”按钮。使用此按钮可以浏览说明的不同部分。
使用AI教学助手聊天机器人 🤖
接下来,让我们看看云IDE上可用的一个AI驱动的聊天机器人。
IBM Skills Network团队为你提供了一个名为“TI”的AI教学助手聊天机器人,它可以帮助你使用实验室环境完成编码作业。TI的图标位于教学窗格的左侧。
要访问聊天机器人,只需点击该图标。让我们尝试问一个问题,例如:“请为我提供一个简单的Python代码”。
如你所见,代码被显示出来,你可以复制或执行该代码。
编程界面详解 💻
上一节我们介绍了辅助工具,本节中我们来看看编程界面的核心部分。
编程界面包含多个组件,但你将经常使用的两个标签页包括:
- 编辑器标签页:用于编写代码。
- 终端标签页:用于执行代码。
在编程窗格中,还有一个“Skills Network工具箱”,它使你能够使用各种数据库管理环境、大数据工具、云工具、嵌入式AI库,并启动你构建的应用程序。
安装Python库 📦
在开始编写代码之前,你需要在这个基于云的环境中安装所需的Python库或包。
你需要在终端标签页中执行此任务。要打开新终端,点击“终端”菜单,然后点击“新建终端”。
为了演示目的,让我们从教学窗格复制代码块并将其粘贴到终端中。然后,按回车键执行命令。
请注意,NumPy库已成功安装,你现在可以将此库导入到你的代码中。
创建并运行Python程序 🚀
现在让我们在编程窗格中创建一个基本的Python程序。点击“文件”,然后选择“新建文件”。新文件将在编辑器标签页中打开。
在开始添加代码之前保存文件是最佳实践。由于我们正在编写Python代码,请使用.py扩展名保存文件。从文件菜单中,点击“保存”或按Ctrl+S。出现提示时,提供文件名。对于此示例,让我们将文件保存为hello.py。
下一步是添加代码。你可以在编辑器标签页中手动键入代码,或者如果教学窗格中有可用代码,可以将其复制并粘贴到你的文件中。
为了本次演示的目的,让我们从教学窗格复制代码并将其粘贴到文件中。别忘了保存文件。
是时候执行代码了,让我们导航回终端标签页。确保你位于存储程序文件的文件夹中,可以通过键入python3后跟文件名来执行文件。对于此示例,命令是python3 hello.py。

请注意,输出已显示且没有任何错误。
总结 📝
本节课中我们一起学*了云IDE的使用。
总结一下,云IDE是IBM Skills Network提供的一个类似于VS Code的编程环境,用于学*和培养实践技能。
云IDE有两个窗格:教学窗格和编程窗格。你可以使用教学窗格上的“目录”按钮在教学页面之间导航。
编程窗格提供编辑器标签页来编写代码,以及终端标签页来执行代码。你需要通过终端安装所需的库。

在实验过程中的任何时候,你都可以从教学窗格复制代码块,并将其粘贴到编辑器或终端标签页中。
生成式人工智能工程:005:Python风格指南与编码实践

在本节课中,我们将学*如何编写清晰、一致且易于维护的Python代码。我们将重点介绍官方的PEP 8风格指南、关键的编码惯例,以及如何使用静态代码分析工具来确保代码质量。


编写可读代码的重要性
当你编写代码时,需要确保团队成员能够轻松阅读和理解它。这项任务需要遵循一些编码标准和惯例。Python.org发布了一份名为 Python增强提案8 或 PEP 8 的文档,它提供了使你的Python代码可读且格式一致的惯例和指南。
上一节我们介绍了编写可读代码的重要性,本节中我们来看看PEP 8中的一些关键指南。
关键指南:提升代码可读性
以下是提升代码可读性的几个关键指南。
缩进:使用空格而非制表符
PEP 8建议使用空格而非制表符进行缩进。这是因为不同的文本编辑器和集成开发环境对制表符所代表空格数的解释可能不同。例如,一个编辑器可能将制表符解释为三个空格,而另一个可能解释为四个。使用制表符缩进可能导致代码格式不统一,从而引发格式错误。

为了避免此类错误,你应该在缩进代码时使用一致数量的空格。为了统一性,指南建议在代码的每个缩进级别使用四个空格。四个空格足以保证良好的可读性。
请注意,示例中在语句前添加的四个点是为了表示四个空格。

使用空行分隔代码块
PEP 8还建议使用空行来分隔代码中的函数和类。空行有助于界定代码不同部分的开始和结束。
例如,在不遵循PEP 8的代码块中,函数结束和类定义之间可能没有空行。而遵循规范的代码则会在类定义前添加一个空行,使结构更清晰。
在运算符和逗号后使用空格

为了提高代码可读性,应在运算符周围和逗号后使用空格。这会使代码看起来更宽松、更清晰,从而提升命令的可读性。
让我们看一些例子:
- 当你不加空格地写
A=B+C时,可能会令人困惑。 - 然而,当你添加空格,写成
A = B + C时,可读性就提高了。
编码惯例:保持一致性与可维护性
上一节我们介绍了提升可读性的格式指南,本节中我们来看看一些保持代码一致性和可管理性的编码惯例。
以下是几个重要的编码惯例。
将大块代码封装在函数中
一个关键的编码惯例是为包含较大代码块的功能创建独立的函数,然后从主程序中调用这些函数。

例如,在代码中,如果 if-else 语法没有封装成函数,那么每次需要该功能时都需要重写。然而,如果你将其写成一个函数,例如 function_1(A, B),就可以方便地调用,如 C = function_1(A, B)。这提高了代码的执行速度,并以更便捷的方式支持代码块的重用。
函数与文件的命名:小写字母加下划线
命名函数和文件时,应使用小写字母和下划线。这是因为Python本身及其许多内置库和预定义函数都遵循这种常见的命名惯例。因此,使用小写函数名(最好加下划线)有助于使你的函数具有独特性。
例如:
- 不建议:
compSurfaceRadiation() - 建议:
comp_surface_radiation()
此规则的一个例外是Python包的命名,通常不鼓励使用下划线。例如,应使用
Mypackage而非My_package。

类的命名:使用驼峰命名法
使用驼峰命名法命名类也是一个编码惯例。驼峰命名法在编码社区中被广泛接受,也有助于在代码中区分类和函数。
例如:
- 不建议:
class la_sirrel_c - 建议:
class LaSirrelC
常量的命名:全大写字母加下划线
为了保持一致性,应使用全大写字母命名常量,并用下划线分隔单词。名称通常表明常量的用途。
例如:MAX_FILE_UPLOAD_SIZE
静态代码分析
在讨论了编码惯例和指南之后,我们来看看软件开发者常用的一种确保遵守这些风格指南的方法:静态代码分析。

静态代码分析是一种在不执行代码的情况下,根据预定义的风格和标准评估代码的方法。静态分析有助于发现诸如编程错误、编码标准违规、未定义值、语法违规和安全漏洞等问题。
你可以使用 Pylint 等库来检查你的Python代码是否符合PEP 8指南。
总结
本节课中我们一起学*了如何编写高质量的Python代码。


- 编写格式一致的代码有助于所有团队成员轻松阅读和理解代码。
- PEP 8中提升代码可读性的指南包括:使用四个空格缩进、使用空行分隔函数和类、在运算符周围和逗号后使用空格。
- 保持代码一致性和可管理性的编码惯例包括:将大块代码封装在函数内、使用小写字母和下划线命名函数与文件、使用驼峰命名法命名类、使用全大写字母和下划线命名常量。
- 可以使用静态代码分析方法来对照预定义的风格和标准评估你的代码,而无需执行它。
生成式人工智能工程:006:单元测试 🧪


在本节课中,我们将学*单元测试。你将能够定义单元测试,解释单元测试的流程,构建并执行单元测试,以及解读单元测试的输出结果。

什么是单元测试?
单元测试是一种验证代码单元是否按设计运行的方法。一个“单元”是应用程序中一个较小且可测试的部分。
以下是一个单元的例子,它包含两个函数:square 和 doubler,位于 my_module.py 文件中。
square 函数的代码为:
def square(number):
return number ** 2

doubler 函数的代码为:
def doubler(number):
return number * 2
为了开发单元测试,你将使用 unittest 库。这是一个已安装的 Python 模块,提供了一个包含测试功能的框架。
单元测试流程概述
上一节我们介绍了单元测试的基本概念,本节中我们来看看从单元测试到发布到生产代码库的端到端测试流程。

在代码开发过程中,你需要测试每个单元。测试分两个阶段进行。
第一阶段是在本地系统上测试单元。如果测试失败,你需要确定失败原因并修复问题,然后再次测试该单元。
在单元测试通过后,你需要在服务器环境(例如持续集成/持续交付,即 CI/CD 测试服务器)中测试该单元。
如果单元未通过服务器测试,你将收到失败详情。你需要确定并修复问题。一旦单元通过服务器测试,它就会被集成到最终的代码库中。
如何构建单元测试
在概述了单元测试流程之后,让我们回顾一些测试函数,以了解如何构建单元测试。
请注意单元代码和单元测试代码。单元文件名是 my_module.py。单元测试文件名则附加或前缀了单词 test。这是一个良好的命名约定,有助于清晰地区分单元文件和单元测试文件。
以下是创建单元测试文件的步骤。
第一步是导入 unittest Python 库:
import unittest
接下来,导入要测试的函数。例如,要从 my_module 单元导入 square 和 doubler 函数进行测试:
from my_module import square, doubler
然后,构建单元测试类,以便从单个类对象调用单元测试。例如,创建一个名为 TestMyModule 的类:
class TestMyModule(unittest.TestCase):
请注意,类名在单元名前加了 Test 前缀。在类名前加上 Test 是一个好的命名约定,有助于区分单元类和单元测试类。
接下来,让该类继承 unittest 库中的 TestCase 类。继承该类允许你利用 TestCase 类中现有的方法。

然后,在单元测试类中创建与每个需要测试的函数相对应的函数。例如,在 TestMyModule 类中,两个函数 test_square 和 test_doubler 对应于 my_module 单元中的 square 和 doubler 函数。
请注意,确保在单元测试模块中,函数名以 test 开头,因为只有以 test 开头的函数才会运行。
最后,你可以创建测试用例。
在创建测试用例时,添加一个或多个断言方法以确保满足单元测试条件。一个常用的断言函数是 assertEqual。请注意,在代码中,该方法已被添加到 TestCase 类中。

使用 assertEqual 函数
assertEqual 函数比较两个值或实体,并判断它们是否相等。该方法用于检查函数是否返回了正确的值。

assertEqual 函数接收的参数之一是实际值。对于实际值,你将调用你想要测试的函数。第二个参数是期望值,你需要在此处添加函数预期返回的内容。
在示例中,第一个测试是针对 square 函数,使用数字 2。如果函数正确执行,它应该返回值 4。作为测试的一部分,首先评估函数,然后比较两个值是否相等。根据比较的输出结果,测试通过或失败。

解读单元测试输出
运行测试文件后,会生成一个输出。该输出显示测试结果以及一些额外细节。例如,如果输出显示运行了 2 个测试,耗时 0 秒,并且显示 OK,则表示测试通过,两个函数都正确实现了。
但是,如果函数没有正确实现会发生什么?
考虑 square 函数,你编写了计算数字立方而不是平方的代码。函数会失败,并生成一个输出。

让我们回顾一下失败的单元测试的示例输出。输出清晰地显示单元测试失败。例如,输出显示 FAILED test_square (__main__.TestMyModule)。你还可以查看单元测试失败的具体函数。例如,test_square (self.assertEqual(square(2), 4)) 表明 square 函数失败。AssertionError: 8 != 4 表示值不匹配。详细的输出使你能在实际部署解决方案之前纠正错误。
总结
本节课中我们一起学*了单元测试。

- 单元测试是一种验证代码单元是否按设计运行的方法。
- 在代码开发过程中,每个单元都会被测试。单元测试分两个阶段进行。一旦单元通过服务器测试,它就会被合并到最终的代码库中。
- 确保测试文件以
test作为前缀或后缀,以清晰地区分它们与模块文件。 - 你可以使用不同的测试函数来构建单元测试。
assertEqual函数是一种常用的断言方法,用于比较两个值。- 你可以查看单元测试输出,并确定测试是通过还是失败。
生成式人工智能工程:007:Python 打包 📦

在本节课中,我们将要学* Python 中模块、包和库的概念与区别,并掌握如何创建、验证和使用一个 Python 包。

模块、包与库的概念 🔍
模块、包和库是 Python 中经常使用的术语。上一节我们介绍了课程概述,本节中我们来看看这些核心概念的具体含义。
Python 模块 是一个 .py 文件,其中包含 Python 的定义、语句、函数和类。你可以将模块导入到其他脚本或笔记本中使用。
例如,考虑一个名为 module.py 的模块,它包含两个函数:
- 第一个函数是
def square(number): return number ** 2,用于计算输入数字的平方。 - 第二个函数是
def doubler(number): return number * 2,用于将输入数字翻倍。

如果该模块文件与你的脚本在同一目录下,你可以导入并使用其中的函数。例如,调用 square(4) 会输出 4^2 = 16;调用 doubler(4) 会输出 2*4 = 8。
Python 包 是一个包含 __init__.py 文件的目录,该目录下汇集了多个 Python 模块。这个 __init__.py 文件的存在,使其区别于普通的脚本目录。
例如,一个名为 my_project 的包,其目录结构可能包含 module1.py、module2.py 和 __init__.py 文件。当你导入一个模块或包时,Python 创建的对象类型始终是 module。请注意,模块和包的区别仅在于文件系统层面。

Python 库 是一个或多个包的集合。例如,NumPy、PyTorch 和 Pandas 都是库。需要注意的是,“包”和“库”这两个术语经常互换使用,因此 NumPy、PyTorch 和 Pandas 也常被称为包。

创建 Python 包的步骤 🛠️
理解了基本概念后,接下来我们看看如何动手创建一个 Python 包。假设我们有两个模块:
module1.py包含square和doubler函数。module2.py包含mean函数。
要将 my_project 文件夹变成一个包,必须在该文件夹内创建一个 __init__.py 文件。该文件的内容通常用于引用包内的模块,例如:
from . import module1
from . import module2
以下是创建包的典型步骤:
- 创建一个以包名命名的文件夹(例如
my_project)。 - 在该文件夹内创建一个空的
__init__.py文件。 - 创建所需的模块文件(例如
module1.py,module2.py)。 - 在
__init__.py文件中添加代码,以引用包中需要的模块。

验证 Python 包 ✅
创建包之后,需要验证它是否能被正确加载。验证步骤如下:
- 打开 Bash 终端。
- 确保当前目录与你的包所在的文件夹处于同一层级。
- 在 shell 中运行
python命令,打开 Python 解释器。

在 Python 提示符下,键入 import 后跟你的包名,例如:
import my_project
如果该命令运行无误,则表明包已成功加载。
测试包中函数的一般结构是:包名.模块名.函数名(参数)。例如,调用 my_project.module1.square(2),该函数应返回值 4。

使用 Python 包 🚀
成功创建并验证包后,就可以在其他脚本中使用它了,前提是包文件夹与脚本在同一目录下。

例如,在父目录中有一个 test.py 文件,你可以这样导入包中的函数:
from my_project.module1 import square, doubler
from my_project.module2 import mean
print(f“4^2 = {square(4)}“)
print(f“2*4 = {doubler(4)}“)
print(f“(2+1+3)/3 = {mean(2, 1, 3)}“)
运行这些函数,即可检查是否能得到正确的结果。
总结 📝


本节课中我们一起学*了 Python 中模块、包和库的核心概念与操作方法:
- Python 模块 是一个包含 Python 代码的
.py文件。 - Python 包 是一个包含
__init__.py文件和多个模块的目录。 - Python 库 是一个或多个包的集合。
- 创建包的步骤包括:创建文件夹、添加
__init__.py文件、编写模块、在__init__.py中引用模块。 - 可以通过 Python 解释器导入包来验证其是否创建成功。
- 创建完成后,即可在相同目录下的其他脚本中导入并使用该包。
生成式人工智能工程:008:应用程序开发的Python库和框架 🐍

在本节课中,我们将要学*用于应用程序开发的Python库和框架。我们将了解库和框架的定义、它们各自的优势,以及它们之间的区别。



Python库:你的编程工具箱 🧰
Python库可以被视为一个工具箱。每个库都包含特定的工具,旨在简化和加速特定的编程任务。开发者无需从零开始编写所有功能,可以直接调用这些库来节省大量时间和精力。
以下是几个在应用开发中至关重要的Python库示例:
- NumPy:该库便于进行高级数学计算。
- Pandas:该库提供了强大的数据操作和分析能力。
- Matplotlib:该库简化了数据可视化的过程。
- Requests:该库简化了发送HTTP请求的过程。
- Beautiful Soup:该库使得从网页中抓取信息、遍历、搜索和修改解析树变得容易。
- SQLAlchemy:这是一个SQL工具包和对象关系映射(ORM)工具。它为应用开发者提供了SQL的全部功能和灵活性。
- PyTest:这是一个测试框架,允许用户轻松创建小型、简单的测试,同时也能扩展以支持对应用程序和库进行复杂的功能测试。
框架:应用程序的预定义结构 🏗️
上一节我们介绍了Python库,本节中我们来看看框架。框架是用于应用程序开发的预定义结构。此外,框架还为应用程序开发提供了一套指导原则。
框架通过提供一个定义良好的结构来编写和组织代码,并支持复用代码库以添加功能,从而促进了良好的编码实践。
一些常见的Python Web框架示例包括Django、Flask和Web2py。
使用框架的优势 ✨
使用框架进行应用开发能带来多方面的好处。以下是其主要优势:
- 简化开发过程:框架通过提供预写的代码库、模块和开发者指南,简化了开发过程。
- 简化调试过程:借助预构建的调试工具,Web框架为开发者简化了调试Web应用程序的过程。
- 用更少的代码实现更多功能:开发者可以使用更少的代码添加更多功能。因为框架配备了多个预构建的库和模块,开发者在编写代码时可以充分利用这些资源,无需从零开始创建所有必需的功能。
- 提高数据库管理效率:框架自带内置的数据库集成工具,有助于无缝集成数据库端点以传输数据。
- 增强安全性:安全性是应用程序用户关注的关键问题。使用框架的一个好处是,开发者可以利用内置的安全功能和指南来加强应用安全。
框架与库的区别 🔄

让我们简要地看看框架和库之间有何不同。
框架包含了应用程序的基本流程和架构,使你能够构建完整的应用程序。你可以将其想象成建造房屋的蓝图和主体结构。
Python库是一组仅执行特定功能的包。它更像是工具箱里的一把锤子或螺丝刀,用于完成某个具体任务。
总结 📝


本节课中我们一起学*了Python库和框架的核心概念。我们了解到,Python库如同工具箱,每个库都有特定工具来简化和加速编程任务。框架则是应用程序开发的预定义结构,使用框架能带来诸多好处,包括简化开发与调试过程、用更少的代码实现更多功能、提高数据库管理效率以及增强安全性。最后,我们明确了框架用于构建完整应用程序,而库则辅助实现特定功能。
生成式人工智能工程:009:Flask简介 🐍
在本节课中,我们将学*Flask Web框架。我们将定义Flask,描述其主要特性,解释如何安装Flask及其主要依赖项,并比较Flask与另一个Python Web框架Django的主要区别。
Flask是一个用于创建Web应用程序的微框架。它不像一些大型框架那样具有强制的“约定”,也不强制用户使用特定的工具集。
Flask的起源与核心特性
Flask由Armin Ronacher于2004年创建,最初是一个愚人节玩笑。它因其易用性和可扩展性而迅速流行起来。Flask提供了创建Web应用程序所需的最小依赖,但它本身是可扩展的,许多社区扩展为其添加了额外功能。
以下是Flask的主要特性:
- 内置开发服务器与调试器:Flask自带一个在开发模式下运行应用程序的Web服务器。它还包含一个调试器,可在浏览器中显示交互式回溯和堆栈跟踪,帮助调试应用程序。
- 日志记录:Flask使用标准的Python日志记录功能来处理应用程序日志。开发者可以使用相同的记录器来记录关于应用程序的自定义消息。
- 测试支持:Flask提供了测试应用程序不同部分的方法。此功能使开发者能够遵循测试驱动开发方法,并可以使用像
pytest和coverage这样的框架来确保代码按预期工作。 - 请求与响应对象:开发者可以访问请求和响应对象,以提取参数并自定义响应。
Flask的附加功能
上一节我们介绍了Flask的核心特性,本节中我们来看看它提供的其他重要功能。
以下是Flask的附加功能:
- 静态文件支持:该框架支持静态资源,如CSS文件、JavaScript文件和图像。Flask提供了在模板中加载静态文件的标签。
- 动态页面与模板:开发者可以使用Jinja2模板框架开发动态页面。这些动态页面可以显示可能随每个请求而变化的信息,或者检查用户是否已登录。
- 路由与动态URL:Flask提供路由功能,并支持动态URL,这对于构建RESTful服务极为有用。开发者可以为不同的HTTP方法创建路由,并在应用程序中提供重定向。
- 错误处理:开发者可以在Flask中编写全局错误处理程序,这些处理程序在应用程序级别工作。
- 会话管理:Flask支持用户会话管理。
社区扩展
Flask的轻量级设计意味着许多高级功能通过社区扩展实现。以下是可添加到应用程序中的一些流行社区扩展:
- Flask-SQLAlchemy:为Flask添加了对名为SQLAlchemy的ORM的支持,使开发者能够用Python操作数据库对象。
- Flask-Mail:提供了设置SMTP邮件服务器的能力。
- Flask-Admin:让开发者可以轻松地为Flask应用程序添加管理界面。
- Flask-Uploads:允许向应用程序添加自定义的文件上传功能。
除了上述扩展,这里还有一些其他有用的扩展:
- Flask-CORS:使应用程序能够处理跨源资源共享,从而实现跨源JavaScript请求。
- Flask-Migrate:为SQLAlchemy ORM添加数据库迁移功能。
- Flask-User:添加用户认证、授权和其他用户管理功能。
- Marshmallow:为代码添加了强大的对象序列化和反序列化支持。
- Celery:一个强大的任务队列,可用于简单的后台任务和复杂的多阶段程序和调度。
安装与依赖管理
了解了Flask的功能后,接下来我们看看如何安装它以及如何管理其依赖。
Flask可通过Python包管理器pip获取。在实验环境中,pip已可用。但是,如果要在自己的机器上安装,建议首先使用venv或Pipenv模块创建一个虚拟环境。然后,可以安装Flask 2.2.2版本。

pip install flask==2.2.2
建议在应用程序中固定依赖项的版本号。这确保了应用程序可以在不同的环境(如开发、预发布和生产)中从头开始复现。同时,这也避免了在自动更新包时,因未指定版本号而意外引入新问题或错误。
Flask附带了一些内置依赖,以实现各种功能:

- Werkzeug:实现了WSGI(Web服务器网关接口)。这是应用程序和服务器之间的标准Python接口。
- Jinja2:一种模板语言,用于渲染应用程序中的页面。
- MarkupSafe:随Jinja2一同提供。它在渲染模板时转义不受信任的输入,以避免注入攻击。
- ItsDangerous:用于安全地签名数据。这有助于确定数据是否被篡改,并用于保护Flask的会话Cookie。
- Click:一个用于编写命令行应用程序的框架。它提供了
flask命令,并允许添加自定义管理命令。

要查看内置依赖,可以在虚拟环境中使用pip freeze命令,你将看到所有内置包默认都已安装。
Flask与Django的对比
另一个流行的Python Web开发框架是Django。以下是Flask和Django之间的一些关键区别:
- 定位与功能:Flask旨在成为一个非常轻量级的框架。而Django是一个全栈框架。因此,Flask只提供创建Web应用程序所需的基本依赖,开发者可以选择其他扩展来提供附加功能。相反,Django包含了创建全栈应用程序所需的一切。
- 灵活性与约定:Flask非常灵活,可以以“即插即用”的方式添加和移除组件。另一方面,Django是“约定优于配置”的,它为开发者做出了大部分决策,以便他们可以专注于应用程序的逻辑。
总结

本节课中我们一起学*了Flask Web框架。我们了解到Flask是一个附带最小依赖的微框架,用于构建网站。它具有调试服务器、路由、模板和错误处理等功能。Flask可以通过使用社区扩展来增强功能,并可作为Python包安装。与Flask相比,Django是一个全栈框架。
生成式人工智能工程:010:Flask基本应用程序和路由 🚀
在本节课中,我们将要学*如何创建和运行一个带有基本路由的Flask应用程序。我们将解释如何从服务器向客户端返回JSON响应,并描述Flask中可用的各种配置选项。
创建第一个Flask应用程序
在创建第一个Flask应用程序之前,请确保已经安装了Flask。
接下来,创建一个Python文件作为你的服务器,我们将这个文件命名为 app.py。
现在,让我们在这个文件中编写代码。首先,从 flask 模块中导入大写的 Flask 类。
from flask import Flask
接下来,实例化 Flask 类的一个对象作为你的应用。构造函数接受一个参数 import_name。通过传入内置变量 __name__ 来设置应用的名称。这个名称用于在文件系统上查找资源,并由扩展提供调试信息。
app = Flask(__name__)
现在你已经定义了服务器,让我们添加第一个路由。你希望在调用服务器而不添加路径时,向客户端返回一条消息。因此,你需要使用 @app 装饰器来定义一个路由,该装饰器将路径作为参数。最后,你可以从方法中返回文本或HTML。
让我们看一下代码。@app 装饰器定义在 hello_world 方法上。它接受参数 / 并返回HTML消息:<b>My first Flask application in action</b>。
@app.route('/')
def hello_world():
return '<b>My first Flask application in action</b>'
下一步是运行你的应用程序。
运行Flask应用程序


第一步是设置一些系统环境变量。你需要一个名为 FLASK_APP 的变量,它包含主服务器文件的名称。此外,你还需要一个 FLASK_ENV 变量来定义开发或生产环境。这个变量在Flask 2.3中将被弃用。
如你所见,你已经将 FLASK_APP 环境变量定义为包含中央服务器的文件名,并将 FLASK_ENV 设置为 development。
最后,通过执行 flask run 命令,将 run 参数传递给Flask框架来运行应用程序。
export FLASK_APP=app.py
export FLASK_ENV=development
flask run

Flask应用程序默认在端口5000上运行。你可以浏览 http://localhost:5000 来查看你的消息。让我们也使用浏览器开发者工具来查看从服务器返回的信息。

请求的URL是 http://localhost:5000。请求方法是 HTTP GET。响应的状态码是200,表示成功响应。响应头中的内容类型是 text/html,服务器正在运行Python 3.6.15版本。
恭喜你成功运行了第一个Flask应用程序。
配置应用程序
现在,你需要在运行每个应用程序之前设置环境变量。你可以通过使用 --app 参数来标识要运行的Python文件,并使用 --debug 参数来启用开发模式,从而向 flask 命令传递配置。

调试标志还支持在源文件更改时自动重启,这在开发应用程序并希望立即看到更改时非常有用。在你的例子中,应用程序存储在名为 app.py 的文件中,因此你可以省略这个参数,因为Flask默认会在当前目录中查找 app.py。
flask --app app.py --debug run

现在输出应该类似于这样,屏幕显示Flask应用程序正在以开发模式运行。

返回JSON响应
有多种方法可以从Flask应用程序返回JSON。
一种方法是返回一个可序列化的对象,如字典或列表。在给定的代码中,你返回一个Python字典,Flask将使用Python的 json 模块将JSON返回给客户端。
@app.route('/json')
def return_json():
return {"message": "Hello, JSON!"}
让我们使用 curl 命令测试这是否有效。你将向 localhost:5000/json 发出GET请求,可以看到响应状态为200 OK。与HTML不同,你还可以看到内容类型为 application/json。最后,你可以看到返回的JSON。但请注意,如果你返回一个更复杂的对象(如类),请确保它可以被序列化。
第二种方法是使用Flask提供的 jsonify 方法。该方法接受键值对作为输入并返回相应的JSON。
让我们看一个例子。首先从 flask 导入 jsonify。



from flask import jsonify
接下来,将键值对传递给 jsonify。
@app.route('/jsonify')
def return_jsonify():
return jsonify(message="Hello from jsonify!")
你应该在浏览器中得到与之前相同的结果,开发者工具应该看起来相同,状态码为200 OK,返回的内容类型为 application/json。
Flask配置选项
现在你已经了解了Flask的两个配置:FLASK_ENV 和 FLASK_APP 变量。Flask提供了许多其他配置选项,你可能在应用程序中使用。
ENV指示应用程序运行的环境:生产或开发。DEBUG启用调试模式。TESTING启用测试模式。SECRET_KEY用于签名会话cookie。SESSION_COOKIE_NAME是会话cookie的名称。SERVER_NAME绑定主机和端口。JSONIFY_MIMETYPE默认为application/json。
此外,还有其他方法可以向Flask提供配置选项。Flask提供了一个 config 对象,你可以将配置选项插入到这个对象中。如果你已经有环境变量,可以将它们加载到 config 对象中。最后,你可以将配置选项保存在一个单独的JSON文件中,并使用 config 对象提供的 from_file 方法加载它们。
应用程序结构
随着应用程序的增长,你应该创建一个目录结构,而不是使用单个Python文件。构建应用程序的方法有很多,这里是一个例子。


- 将主要源代码存储在其模块目录中。
- 将所有配置存储在其文件中。
- 将所有静态资源(如图像、JavaScript和CSS文件)单独存储。
- 将所有动态内容存储在模板目录中。
- 将所有测试文件放在测试目录中。
- 拥有一个可以激活的虚拟环境,以安装正确版本的依赖项。
总结

本节课中我们一起学*了如何通过实例化 Flask 类来创建服务器,使用 @app 装饰器创建URL处理器,返回字符串消息或使用 jsonify 方法返回JSON对象,以及如何从环境变量、Python文件和 app.config 对象直接设置应用程序配置。
生成式人工智能工程:011:使用GET和POST模式的请求和响应对象 🚀
在本节课中,我们将学*Flask框架中的两个核心对象:请求对象(Request Object)和响应对象(Response Object)。你将了解如何定义路由以响应不同的HTTP方法(如GET和POST),如何从客户端请求中提取数据,以及如何构建和定制返回给客户端的响应。


自定义Flask路由 🔧
上一节我们介绍了Flask应用的基本结构,本节中我们来看看如何自定义路由以响应不同的HTTP请求方法。
在Flask中,你可以使用 @app.route 装饰器来定义路径。默认情况下,该装饰器只响应GET方法的请求。客户端只能向指定路径发送GET请求。
现在,你可以传入第二个参数 methods 来控制该路径响应哪些HTTP方法。
例如,以下两种写法是等价的。第一种写法中,GET方法是隐式指定的;第二种写法则明确指定了GET方法。
@app.route('/')
def home():
return 'Home Page'

@app.route('/', methods=['GET'])
def home_explicit():
return 'Home Page'
以下是另一个例子。路径 /health 将同时响应GET和POST请求。
@app.route('/health', methods=['GET', 'POST'])
def health_check():
if request.method == 'GET':
return 'Status: OK, Method: GET'
elif request.method == 'POST':
return 'Status: OK, Method: POST'
使用curl命令测试,GET请求的输出是 Status: OK, Method: GET,而POST请求的输出是 Status: OK, Method: POST。
理解Flask请求对象 📥
当客户端向Flask服务器请求资源时,Flask会为每次HTTP调用创建一个来自 flask.request 类的请求对象。这个请求由 @app.route 装饰器处理,你可以在对应的视图方法中检查和探索这个请求对象。
以下是请求对象中可用的部分信息:
request.host: 服务器的地址,形式为(主机, 端口)的元组。request.headers: 随请求发送的头部信息。request.url: 客户端请求的资源URL。request.access_route: 列出所有IP地址的列表,适用于请求被多次转发的情况。request.full_path: 表示请求的完整路径,包括任何查询字符串。request.is_secure: 如果客户端使用HTTPS或WSS协议发起请求,则为True。request.is_json: 如果请求包含JSON数据,则为True。request.cookies: 包含随请求发送的任何Cookie的字典。
此外,你还可以从请求头中访问以下数据:
Cache-Control: 控制浏览器如何缓存。Accept: 告知服务器客户端理解的内容类型。Accept-Encoding: 指示可接受的内容编码。User-Agent: 标识客户端应用程序、操作系统或版本。Accept-Language: 请求特定的语言和区域设置。Host: 指定所请求服务器的主机和端口号。
通常,Flask内置的请求类提供的属性和方法已经足够,替换为自定义请求对象是可选的。

请求对象属性示例 💻
现在,让我们看看当客户端(例如,使用终端curl命令)发起请求时,服务器上打印出的一些实际值。
假设客户端向 http://127.0.0.1:5000/ 发起GET请求。
以下是部分请求对象属性的值:
request.host:localhost:5000request.headers['User-Agent']:curl/7.79.1request.headers['Accept']:application/jsonrequest.url:http://localhost:5000/request.access_route:['127.0.0.1']request.full_path:/request.is_json:False(因为GET请求未发送数据)request.is_secure:False(因为URL是HTTP而非HTTPS)len(request.cookies):0

从请求中提取数据 🛠️
有多种方法可以从请求对象中获取信息。

- 使用
request.get_data()以字节形式访问POST请求的数据,然后你需要负责解析这些数据。 - 使用
request.get_json()方法获取已解析为JSON格式的POST请求数据。
Flask还提供了更聚焦的方法来直接从请求中获取特定类型的信息,无需手动解析:
request.args: 以字典形式提供查询参数(URL中的?key=value)。request.json: 将请求体数据解析为字典(如果内容是JSON)。request.files: 提供用户上传的文件。request.form: 包含表单提交中发布的所有值。request.values: 结合了args和form的数据。

提取特定值 📝
现在你知道了请求对象的样子以及获取数据的方法,让我们看看如何从这些数据中提取具体的值。
到目前为止你看到的方法的返回类型是 MultiDict、ImmutableMultiDict 或 CombinedMultiDict。这些数据结构的行为类似于Python字典,你可以使用索引或 .get() 方法来提取值。
假设对于给定的URL http://localhost:5000/course?name=capstone&rating=10,你想提取查询参数 name 和 rating。
from flask import Flask, request
@app.route('/course')
def get_course():
# 使用索引方式提取,如果参数不存在会引发错误(400 Bad Request)
course_name = request.args['name'] # 返回 'capstone'
# 使用.get()方法提取,如果参数不存在则返回None(或指定的默认值)
course_rating = request.args.get('rating') # 返回 '10'
# 或 request.args.get('rating', default=0, type=int) # 返回整数 10
return f"Course: {course_name}, Rating: {course_rating}"
.get() 方法在参数不存在时返回 None(或你指定的默认值),而索引方法在参数不存在时会引发错误并导致服务器返回400错误。


理解Flask响应对象 📤
就像提供请求对象一样,Flask也会为每个客户端调用提供一个响应对象。你可以使用响应对象向客户端发送自定义属性和头部信息。
一些常见的响应属性包括:
status_code: 指示请求的成功或失败状态(如200表示成功,404表示未找到)。headers: 提供关于响应的更多信息。content_type: 显示所请求资源的媒体类型(如text/html)。content_length: 响应消息体的大小。content_encoding: 指示应用于响应的任何编码,以便客户端知道如何解码数据。mimetype: 设置响应的媒体类型。expires: 包含响应被视为过期的日期或时间。
响应对象上还有一些标准方法:
set_cookie(): 在客户端设置浏览器Cookie。delete_cookie(): 删除客户端上的Cookie。
使用响应对象的方法 🎯
现在让我们学*Flask如何使用不同的方法与响应对象协作。
- 自动创建:当你从
@app.route方法返回数据(如字符串)时,Flask会自动为你创建一个状态码为200、MIME类型为text/html的响应对象。 jsonify():jsonify()函数也会自动创建一个带有正确application/json内容类型的响应对象。from flask import jsonify return jsonify({'message': 'Hello'})make_response():使用make_response()可以创建自定义的响应对象,以便设置状态码、头部等。from flask import make_response resp = make_response('Custom Response', 201) resp.headers['X-Custom-Header'] = 'Value' return respredirect():Flask提供了一个特殊的redirect()方法,用于返回302状态码并将客户端重定向到另一个URL。from flask import redirect return redirect('/new_location')abort():最后,Flask提供了abort()方法来返回一个带有错误条件的响应(如404)。from flask import abort abort(404) # 返回 404 Not Found 错误页面

总结 📚
本节课中我们一起学*了Flask框架中请求与响应对象的核心用法。
- Flask为每个客户端调用提供了一个请求对象和一个响应对象。
- 你可以从Flask请求对象中获取额外信息,如头部。
- 你可以解析请求对象以获取查询参数、请求体和其他参数。
- 你可以在将响应发送回客户端之前,在响应对象上设置状态码、头部等属性。

掌握这些对象和方法,是构建能够处理复杂客户端交互的Web应用的基础。
生成式人工智能工程:012:动态路由 🛣️
在本节课中,我们将学*如何在 Flask 框架中调用外部 API,以及如何通过动态路由向 URL 传递参数。掌握这些技能对于构建能够与外部服务交互、并能处理不同请求的 RESTful API 至关重要。

调用外部 API 📡

在 Flask 应用中,调用外部 API 是一种常见需求。最简单的方法是使用 Python 的 requests 库。你可以将外部 API 返回的 JSON 数据直接发送给客户端,也可以在发送前对结果进行处理。
以下是一个调用外部 API 的示例。
首先,导入必要的模块。这段代码假设你已经安装了 requests 库。
import flask
import requests
接下来,定义你的路由。使用 requests 库请求 Open Library API,并搜索作者 Michael Crichton 的信息。
@app.route('/author')
def get_author():
res = requests.get('https://openlibrary.org/search/authors.json?q=Michael Crichton')
然后,将响应存储在变量 res 中。检查来自 Open Library API 的响应状态码是否为 200。

if res.status_code == 200:
return res.json()
如果响应状态码恰好是 404,则发送一条“出错了”的消息。

elif res.status_code == 404:
return 'Something went wrong.', 404
最后,在这个假设的场景中,如果响应是其他任何状态码,则返回状态码 500 和一条“服务器错误”的消息。
else:
return 'Server error.', 500
动态路由参数 🔄

上一节我们介绍了如何调用外部 API,本节中我们来看看如何让路由变得更灵活。在开发 RESTful API 时,你可以将一些资源 ID 作为请求 URL 的一部分发送。例如,你想创建一个通过国际标准书号(ISBN)返回图书信息的端点,但你不希望硬编码 ISBN,而是希望客户端将其作为 URL 的一部分发送。Flask 为此提供了动态路由功能。
现在让我们看一个具体例子。在 URL 中添加一个名为 isbn 的变量作为动态部分,然后将这个变量传递给 Open Library API,最后将结果发送回客户端。

@app.route('/book/<isbn>')
def get_book(isbn):
res = requests.get(f'https://openlibrary.org/isbn/{isbn}.json')
if res.status_code == 200:
return res.json()
else:
return 'Book not found.', 404
参数类型验证 ✅
Flask 还允许你设置参数类型。框架会利用这些信息来验证传入的请求。例如,你可以创建一个端点 /terminals/<string:airport_code> 来获取某个机场的航站楼数量。当用户在 URL 末尾发送一个字符串时,这个路由装饰器就会被触发。
同样地,在上一个例子中,你可以指定 isbn 必须是一个数字。

@app.route('/book/<int:isbn>')
def get_book_by_int_isbn(isbn):
# 注意:ISBN 通常是字符串,这里仅作类型示例
res = requests.get(f'https://openlibrary.org/isbn/{isbn}.json')
# ... 处理逻辑
以下是 Flask 中其他一些参数类型的例子。
string: 接受任何不带斜杠的文本(默认类型)。int: 接受正整数。float: 接受正浮点数。path: 类似string,但接受斜杠,常用于表示路径。uuid: 接受 UUID 字符串,用于表示全局唯一标识符。


虽然 string、int 和 float 是简单参数,但你也可以使用复杂的类型,如 path 来表示网页路径或文件夹路径,或者使用 uuid 来表示像 GUID 这样的唯一 ID。
这里是一个使用 uuid 类型的示例。
你可以创建一个端点 /network/<uuid:network_id> 来获取具有特定 UUID 的网络信息。
你可以这样编写代码:路由期望一个类型为 uuid 的变量 network_id,该 UUID 会作为参数传递给方法。如果找到该 UUID,则返回成功消息;否则,返回带有相应消息的错误代码。

import uuid
@app.route('/network/<uuid:network_id>')
def get_network(network_id):
# 假设有一个函数 check_network_exists 来检查网络是否存在
if check_network_exists(network_id):
return f'Information for network {network_id}', 200
else:
return 'Network not found.', 404
总结 📝


本节课中我们一起学*了 Flask 中动态路由的核心概念。你了解到可以解析请求对象来获取查询参数、请求体和其他参数;可以在将响应发送回客户端之前,在响应对象上设置状态码;并且可以使用动态路由来创建灵活的 RESTful 端点。这些是构建现代 Web 应用和 API 服务的基础技能。
生成式人工智能工程:013:错误处理 🛠️
在本节课中,我们将学*API服务中的错误处理。你将了解不同的HTTP状态码、Flask框架中错误处理的工作原理,以及如何从你的API端点返回适当的错误信息。
HTTP状态码概述
HTTP响应包含一个三位数的状态码,用于指示请求的成功或错误状态。客户端负责解析这个状态码。有效的状态码范围从100到599。
这些状态码按类别组织,每100个为一类。
以下是主要的HTTP状态码类别:
- 100-199: 表示请求已被接收,属于信息性状态码。
- 200-299: 表示请求已被成功接收和处理。
- 300-399: 表示服务器需要进行重定向。
- 400-499: 表示客户端请求存在错误。
- 500-599: 表示服务器在处理请求时发生了错误。
本课程中编写的API将遵循此规范。例如,如果客户端请求一个不存在的资源,你可以返回一个404状态码。
Flask中的默认与显式状态码
默认情况下,当你从 @app.route 装饰的函数返回时,Flask服务器会自动返回200 OK状态。使用 jsonify 方法响应请求时,默认也会返回200。
一个成功的响应会伴随状态码200被发送回客户端。
然而,你的代码可以返回不同于默认值的状态码。Flask允许你通过一个元组来发送响应和状态码。
return “我的第一个应用正在运行”, 200
在这段代码中,你返回了HTML响应“我的第一个应用正在运行”以及状态码200。
你也可以使用 make_response 方法来显式地设置状态码。
from flask import make_response
resp = make_response(“我的第一个应用正在运行”)
resp.status_code = 200
return resp
这段代码返回与上一段代码相同的HTML消息和HTTP状态码200,但这里使用了 make_response 方法。
常用HTTP状态码示例
现在,让我们看看本课程中可能用到的更多状态码示例。
- 200: 默认返回的状态,表示请求成功。
- 201: 告诉客户端服务器已成功创建资源。
- 202: 表示请求已被接受,正在处理中,常见于批处理操作。
- 204: 服务器成功完成请求后返回,不返回任何内容。此状态适用于你不想让浏览器执行任何操作的情况,例如用户停留在当前页面。
- 400: 表示无效请求。可能意味着参数缺失、不正确,或请求在其他方面无效。
- 401: 表示凭据缺失或无效。
- 403: 表示客户端凭据不足以完成请求。
- 404: 如果服务器找不到资源,则返回此状态。
- 405: 表示请求的操作不被支持。
- 500: 当服务器端发生错误时使用。
在API端点中返回错误
了解了不同的HTTP状态码后,作为开发者,你需要从服务中返回正确的代码。让我们看一个例子。
这个 search_response 方法在数据库(通过 query 参数 q)中查找资源。服务在解析你的查询后调用模拟的 fetch_from_database 方法。
@app.route(‘/search’)
def search_response():
query = request.args.get(‘q’)
if not query:
return jsonify({“error”: “输入参数缺失”}), 422
resource = fetch_from_database(query)
if resource:
return jsonify(resource) # 隐式返回 200
else:
return jsonify({“error”: “未找到资源”}), 404
如果资源存在,代码会将资源返回给客户端,并隐式返回状态码200。如果资源未找到,则返回404。
现在,让我们使用 curl 程序调用这个端点。
- 调用不带查询参数的路由:
curl程序返回消息“输入参数缺失”和状态码422。 - 使用正确的资源ID调用路由:
curl命令返回资源作为响应体,状态为200。 - 使用不存在的资源调用路由:
curl命令返回消息“未找到资源”和状态码404。
Flask应用级错误处理

Flask提供了一种在应用级别处理错误消息的方法。这里我们看到一个处理404错误的方法,它返回消息“API未找到”和状态码404。
@app.errorhandler(404)
def not_found(error):
return jsonify({“error”: “API未找到”}), 404
同样,这段代码片段为500错误创建了一个错误处理器,并返回消息“服务器端出错了”。


@app.errorhandler(500)
def internal_error(error):
return jsonify({“error”: “服务器端出错了”}), 500
总结
本节课中,我们一起学*了HTTP错误处理的核心知识。你了解到HTTP响应需要一个状态码来指示请求处理的结果。存在多类HTTP状态码,分别表示成功、用户错误或服务器错误。

Flask会隐式地随响应返回一个成功的200状态码,但你也可以显式地提供其他状态码。此外,Flask还提供了应用级的错误处理器,让你能够集中处理特定类型的错误,从而构建更健壮、用户友好的API服务。
生成式人工智能工程:014:使用Flask部署Web应用


在本节课中,我们将学*如何使用Flask框架来创建和部署一个Python Web应用。我们将从Flask的基本概念讲起,逐步介绍其安装、基本应用结构、路由定义以及如何渲染静态和动态页面。

Flask是一个用于快速、轻松创建Web应用的微框架。它支持CRUD操作,即通过发起POST、PUT、GET、UPDATE和DELETE请求来实现创建、读取、更新和删除功能。
以下是Flask应用的基本结构。请注意Flask包的标志。
上一节我们介绍了Flask的基本概念,本节中我们来看看如何使用Flask进行CRUD操作。

- POST 和 PUT 请求用于创建对象或数据。例如,可以使用它们来创建一个用户。两者的区别在于:POST 在每次请求时都会创建对象或数据;而 PUT 仅在第一次请求时创建对象或数据,并在后续请求中持续更新该对象或数据。在大多数Web应用中,通常使用 POST 来创建对象或数据。
- 可以使用 GET 请求从服务器读取数据。
- 可以使用 UPDATE 请求来更新现有数据或对象。
- 可以使用 DELETE 请求来删除现有数据或对象。
需要注意的是,大多数Web应用倾向于使用 POST 进行创建、更新和删除操作,使用 GET 进行读取操作。另一个视频会详细解释POST、PUT和GET请求。
接下来,我们看看如何用Flask创建一个Web应用。
创建Flask Web应用的第一步是使用Python的标准包管理器Pip来安装Flask包。
与安装其他包一样,可以使用命令 pip install flask 来获取Flask的最新版本。
安装好Flask包后,就可以开始创建Web应用了。接下来,需要导入Flask包,实例化Flask类,创建Web应用,然后运行该应用。
为了演示,我们来看一个返回“Hello World”字符串作为GET请求响应的Web应用。
以下是创建此应用的步骤:
- 安装Flask:使用
pip install flask。 - 导入模块:从flask包中导入Flask模块。代码为:
from flask import Flask。 - 创建应用实例:创建一个Flask类的对象作为Web应用,例如命名为“My first web application”,并将其存储为
app。代码示例:app = Flask("My first application")。大多数应用为清晰起见使用app作为引用名,但这只是一个引用名,可以使用任何其他名称。 - 定义路由和方法:定义路由以及访问该路由时将调用的方法。例如:
@app.route('/')。在这个例子中,没有指定GET或POST。当不指定请求类型时,默认是GET请求。因此,此端点现在将能够服务于对该路由的GET请求。 - 定义处理函数:编写一个名为
hello的方法,当系统访问上一步定义的API端点时,将调用此方法。该函数不接受参数并返回字符串“Hello world!”。代码为:def hello(): return 'Hello world!' - 添加运行条件:添加一个条件,确保Web应用仅在
__name__属性设置为__main__时才运行。默认情况下,__name__被设置为__main__,除非被显式更改。 - 运行应用:添加代码以运行应用。代码为:
app.run(debug=True)。

可以像运行其他Python应用一样保存并运行此代码。

要在开发环境中启动服务器,需要将代码保存在一个Python文件中,并像运行其他Python应用一样运行它。

当Web应用服务器启动时,它会提供可以访问该应用的IP地址和端口。
要检查端点,可以打开浏览器并连接到服务器输出中看到的这个端点(例如 127.0.0.1:5000),然后看到从Web服务器应用返回的字符串“Hello world!”。

模板是Web应用中提供的预创建的HTML页面。它们可以是静态的,也可以是动态的。
默认情况下,Flask应用在根目录下名为 templates 的目录中查找模板。如果模板需要使用图像、样式表或JavaScript文件,这些文件存储在根目录下名为 static 的文件夹中。
静态页面按原样渲染。动态页面通常包含为每个请求动态填充的信息。这些页面通常基于作为参数传递的值来渲染。参数可以通过URL传递,也可以作为请求参数传递。
让我们看一个示例Flask应用。
首先导入必要的模块:导入Flask以创建Web应用,导入request以处理传入的请求,导入 render_template 以渲染静态和动态HTML页面。代码为:from flask import Flask, render_template, request。
接下来,实例化Flask并设置静态文件夹。例如:app = Flask("My first application")。默认文件夹名是 static,但只要显式设置,也可以将静态内容放在不同名称的目录中。
注意,这个Web应用中有三个端点:

- 第一个端点是
/sample:这将渲染一个静态HTML页面。该HTML中的图像来源于静态目录。实现代码如下:@app.route('/sample') def get_sampleHTML(): return render_template('sample.html') - 第二个端点是
/user/<username>:其中<username>是URL中的参数。代码如下:
这里显式地将方法设置为GET,这只是为了展示如何设置请求类型。如果未指定任何内容,则默认为GET请求。页面将使用我们在URL中传递的参数进行渲染。@app.route('/user/<username>', methods=['GET']) def get_user(username): return render_template('user.html', username=username) - 第三个端点是
/user:其中用户名作为请求参数传递。页面将使用作为请求传递的参数进行渲染。



本节课中我们一起学*了以下内容:
- Flask是一个用于创建Web应用的微框架,并支持CRUD操作。
- 使用
pip install flask安装Flask包。 - 要使用Flask创建Web应用,需要导入Flask、实例化Flask、然后运行应用。
- 可以使用Flask渲染静态和动态模板。
生成式人工智能工程:015:课程介绍 🚀
在本节课中,我们将要学*《生成式人工智能工程》课程的总体介绍。课程将引导你从零开始,通过六个实践项目,掌握构建由生成式AI驱动的应用程序和聊天机器人的核心技能。
你是否曾与虚拟助手聊天,或从AI驱动的应用或聊天机器人那里获得过有用的推荐?你是否好奇聊天机器人如何进行对话,或者应用程序如何似乎知道你的需求?更有趣的是,一些应用如何能通过语音自然地交流?这背后是生成式AI模型与其他AI技术结合的力量,共同造就了这些智能且交互性强的聊天机器人和应用程序。
本课程将带你深入幕后,并为你提供构建生成式AI驱动的应用和聊天机器人所需的技能。
课程项目概览 📋
以下是本课程将涵盖的六个实践项目,每个项目都专注于为特定用例开发一个聊天机器人或应用程序。
- 项目一:图像描述AI工具:创建一个AI工具,为你的照片生成有意义的描述。
- 项目二:集成式网页聊天机器人:创建一个类似ChatGPT的聊天机器人,并将其集成到网页界面中,使其能够访问信息并回答你的查询。
- 项目三:语音助手:开发一个基于语音的助手,它能听取你的问题并使用语音进行回复。
- 项目四:会议转录与摘要应用:构建一个应用程序,用于转录会议讨论,然后提供聚焦于关键要点的简洁摘要。
- 项目五:PDF文档问答机器人:开发一个聊天机器人,允许你上传PDF文件并提问,以从中提取特定信息。
- 项目六:语音翻译助手:创建一个支持语音的AI翻译助手,将语音输入转换为文本,然后用指定语言进行语音输出。

这些项目将帮助你培养强大的技能,即如何实现大语言模型来增强你的聊天机器人和应用程序的智能。
核心技术栈与工具 🛠️
上一节我们介绍了课程项目,本节中我们来看看完成这些项目将使用哪些核心技术和工具。
- 大语言模型:你将与流行的LLMs合作,例如托管在IBM Watson X和Hugging Face平台上的GPT-3和Llama 2。
- 检索增强生成:你将学*RAG技术,该技术通过整合训练数据之外的外部信息来增强LLMs的能力。
- 前端开发:部分项目将教你使用HTML、CSS和JavaScript创建前端应用程序。另一些项目则会展示如何使用开源Python库(如Gradio)来开发交互式且视觉吸引人的基于Web的用户界面。
- 后端开发:构建网站或应用需要一个后端服务器。部分项目将教你使用Flask(一个用于创建Web应用程序的Web框架)来构建应用的后端。
- 语音技术:基于语音的聊天机器人或应用程序项目将演示如何使用和集成IBM Watson语音库。IBM Watson语音转文本使聊天机器人能够“听”懂用户语音输入,而IBM Watson文本转语音则使聊天机器人能够通过语音输出与用户交流。
- 开发框架:项目还将介绍用于构建LLM应用的其他工具。例如,LangChain通过提供预构建的组件和上下文感知的推理,简化了更智能的LLM应用的创建过程。



学*前提与课程结构 📚


在深入技术细节之前,了解学*本课程所需的基础知识和课程组织形式非常重要。
你将使用Python来开发这些项目,因此具备Python基础知识是必需的。虽然具备HTML、CSS和JavaScript的基础知识会有帮助,但这并非强制要求。课程提供了支持性的视频和阅读材料,帮助你建立对项目中使用的框架和技术的基础理解。
本课程的设计兼顾了学*与实践应用。概念视频与支持性阅读材料的结合,帮助你构建关于模型、技术和工具的基础。项目被设计为动手实验,并提供关于如何编写代码和完成不同活动的分步指导。此外,课程还包含分级测验,以测试你对核心概念和技术的理解。
学*目标 🎯

在本课程结束时,你将能够达成以下目标:
- 解释生成式AI模型的核心概念。
- 描述LLM的能力,并将其集成到应用或聊天机器人中以增加智能。
- 为实现AI驱动的应用程序,实施多样化的AI技术和数据框架。
- 使用Python编程构建AI驱动的聊天机器人。
总结


本节课中我们一起学*了《生成式人工智能工程》课程的完整介绍。我们了解了课程将通过六个循序渐进的实践项目,带领我们掌握集成大语言模型、应用RAG技术、并结合前后端及语音技术来构建智能应用的全套技能。请观看所有视频,完成所有阅读材料,并积极参与动手实验,以培养使用Python编程创建生成式AI驱动应用的技能。本课程是你构建未来AI驱动交互的入门之钥。让我们开始吧!
生成式人工智能工程:016:生成式AI模型 🧠
在本节课中,我们将学*构成生成式人工智能核心基础的四种关键模型。我们将逐一介绍它们的工作原理、独特之处以及应用场景。
概述
生成式人工智能模型能够从现有数据中学*,并创造出全新的、类似的数据样本。理解这些核心模型是掌握生成式AI技术的第一步。接下来,我们将深入探讨变分自编码器、生成对抗网络、基于Transformer的模型以及扩散模型。
变分自编码器
上一节我们概述了课程内容,本节中我们来看看第一种核心模型:变分自编码器。
变分自编码器在所有生成式AI模型中最为流行,原因有二:第一,它们能处理图像、文本和音频等多种训练数据;第二,它们能快速降低数据的维度,以创建更新、更优的版本。
其工作流程如下:
- 编码器:这是一个自给自足的神经网络,它研究输入数据的概率分布。简单来说,它会分离出最有用的数据特征。这使得编码器能够创建数据样本的压缩表示,并将其存储在潜在空间中。潜在空间可以看作是模型架构内的一个数学空间,高维数据在此以压缩格式表示。
- 解码器:这也是一个自给自足的神经网络,它将潜在空间中的压缩表示解压缩,以生成期望的输出。
基本上,算法使用最大似然原理进行训练,这意味着它们试图最小化原始输入数据与重建输出之间的差异。
尽管VAE在静态环境中训练,但其潜在空间是连续的。因此,它们可以通过从数据的概率分布中随机采样来生成新样本。由于它们能用少量训练数据生成逼真且多样的图像,VAE被用于图像合成、数据压缩和异常检测等任务。
以下是VAE的一些应用实例:
- 娱乐行业:用于创建游戏地图和动漫头像。
- 金融行业:用于预测股票的波动率曲面。
- 医疗保健领域:利用心电图信号检测疾病。
生成对抗网络
了解了基于编码-解码结构的VAE后,我们来看看另一种采用竞争机制的模型:生成对抗网络。
生成对抗网络是另一种使用图像和文本输入数据的生成式AI模型。在这个模型中,两个卷积神经网络在一个对抗性游戏中相互竞争。
- 一个CNN扮演生成器的角色,在大量数据集上进行训练以产生数据样本。
- 另一个CNN扮演判别器的角色,试图根据判别器的反馈来区分真实样本和伪造样本。
基于判别器的响应,生成器会努力产生更逼真的数据样本。GAN可以生成新的逼真图像、执行风格迁移或图像到图像的转换,甚至创建深度伪造内容。

以下是GAN的一些应用:
- 金融行业:使用GAN训练贷款定价模型或生成时间序列工具。
- 地理空间数据:SpaceGAN等工具可处理地理空间数据。
- 游戏角色:英伟达的StyleGAN2以创建视频游戏角色而闻名。


与变分自编码器不同,GAN的训练可能具有挑战性,因为它们需要大量数据和强大的计算能力。它们还可能产生虚假材料,这是一个伦理问题。
基于Transformer的模型
前面两种模型主要处理图像数据,本节我们转向文本领域,看看基于Transformer的模型。
基于Transformer的模型是在几年前被引入的,当时循环神经网络开始面临一个称为“梯度消失”的问题。由于这个问题,RNN难以处理长文本序列。
为了克服这一挑战,Transformer被构建出来,它带有注意力机制,能够聚焦于文本中最有价值的部分,同时过滤掉不必要的元素。这使得Transformer能够对文本中的长期依赖关系进行建模。


例如,当你输入一个简单的提示时,双栈Transformer架构使用编码器-解码器机制来生成连贯且上下文相关的文本。由于Transformer模型可以查询庞大的数据库,它们能够创建大型语言模型,并执行自然语言处理任务,如图片创作、音乐合成甚至视频合成。这标志着我们在内容创作方法上的一次重大突破,并为创新提供了许多机会,正如我们在GPT-3.5及其后续版本、BERT和T5等模型中所看到的那样。
扩散模型
最后,我们来探讨生成式AI模型世界中的一个较新成员:扩散模型。
扩散模型通过应用扩散原理,解决了因潜在空间噪声导致的数据系统性衰减问题,从而试图防止信息丢失。就像在扩散过程中分子从高密度区域移动到低密度区域一样,扩散模型使用两步过程将噪声移入和移出数据样本。
以下是其核心过程:
- 前向扩散:算法逐渐向训练数据中添加随机噪声。
- 反向扩散:算法逆转噪声以恢复数据并生成期望的输出。
OpenAI的DALL-E 2、Stability AI的Stable Diffusion以及Google的Imagen都是成熟的扩散模型,能够生成高质量的图形内容。
与变分自编码器类似,扩散模型也试图通过先将数据投影到潜在空间,然后再将其恢复回初始状态来优化数据。然而,扩散模型使用动态流进行训练,因此训练时间更长。
那么,为什么这些模型被认为是创建生成式AI模型的最佳选择呢?因为它们训练了数百层,甚至可能是无限数量的层,并且在图像合成和视频生成的实验中显示出显著的效果。随着无监督算法不断带来惊喜,对生成式AI模型的探索仍在持续进行。

总结
本节课中,我们一起学*了作为生成式AI构建基石的四种核心模型:
- 变分自编码器:快速降低样本维度。
- 生成对抗网络:使用竞争网络产生逼真样本。
- 基于Transformer的模型:使用注意力机制对文本长期依赖关系进行建模。
- 扩散模型:通过在潜在空间中去除噪声来解决信息衰减问题。


理解这些模型的基本原理和特点,是进一步学*生成式人工智能应用、微调和高级架构的基础。
生成式人工智能工程:017:基础模型 🧱
在本节课中,我们将要学*基础模型的核心概念。你将能够理解这一术语的定义,识别其关键特征与能力,并探索一些具体的例子。
概述
斯坦福大学基础模型研究中心将基础模型定义为一种构建AI系统的新范式:在一个海量数据集上训练一个模型,并将其适配到多种应用中。我们称这样的模型为基础模型。
接下来,让我们深入剖析这个定义。


核心定义解析
定义的第一部分指出:“在一个海量数据集上训练一个模型”。
工作原理
基础模型是一种大型、通用目的的自监督模型,它在海量的无标签数据上进行预训练,从而建立起数十亿的参数。预训练是一种技术,在此过程中,无监督算法被反复赋予连接不同信息片段的自由。这使得基础模型能够发展出多领域、多模态的能力。
公式表示其核心思想:
基础模型 = 预训练(海量无标签数据) -> 数十亿参数 -> 多模态/多领域能力
这意味着它们可以接受多种模态的输入提示(如文本、图像、音频或视频),并执行复杂且富有创造性的任务,例如:
- 回答问题
- 总结文档
- 撰写文章
- 解方程
- 从图像中提取信息
- 甚至编写代码
这种广泛的技能组合使这些模型与多个领域相关。


基础模型 vs. 其他生成式AI模型
这与较小的生成式AI模型形成对比。较小的模型通常在受限的领域数据上训练,并被要求执行有限的任务。
以下是关键区别:
- 基础模型:如OpenAI的DALL-E系列,能够执行多种图像相关任务。
- 非基础模型:如AlexNet,仅执行图像分类任务。
因此,我们可以明确:虽然所有基础模型都具有生成式AI能力,但并非所有生成式AI模型都是基础模型。

大型语言模型

当基础模型在庞大的自然语言处理数据库上训练时,它们被称为大型语言模型。
LLMs发展出了独立的推理能力,使它们能够独特地回应查询。例如:
- OpenAI的GPT系列:GPT-3预训练参数超过1750亿,GPT-4估计超过180万亿。
- Google的PaLM:预训练参数5400亿。
- Meta的LLaMA:预训练参数650亿。
- Google的BERT:预训练参数超过3.4亿。
- Meta的Galactica:为科学家设计的LLM,在4800万篇论文、讲座、教科书和网站上预训练。
- TII的Falcon:在1.5万亿个词元上预训练。
- 微软的Orca:预训练参数130亿,小到可以在笔记本电脑上运行。
随着生成式AI工具在范围和规模上的发展,这些参数很可能会发生变化。

模型的适应能力
定义的另一个方面是:“将其适配到许多应用”。
这是可能的,因为基础模型的广泛训练使其能够学*新事物并适应新情况。小型企业可以利用这一能力,以可负担的成本创建定制化、更高效的生成式AI模型。这就是为什么基础模型也被称为基础模型——它们帮助那些没有资源从头开始训练自己模型的企业和个人更容易地使用AI系统。通过这种方式,基础模型使企业能够将价值实现时间从数月缩短到数周。
例如,聊天机器人的演变:
- 早期聊天机器人:在较小的数据集上训练,生成能力有限。它们只能基于关键词预测回复,提供预设的响应。
- 现代聊天机器人:在广泛的数据集上进行了多次预训练,因此能够提高词语预测的准确性,并以更有帮助和创造性的方式回应。
代码示例(概念性提示):
用户输入:“写一首关于春天的诗。”
现代LLM驱动的聊天机器人可以生成一首原创的、富有韵律的诗歌。
多模态基础模型示例


并非所有基础模型都是LLM。一些基础模型使用扩散架构来提升其图像生成的规模和范围。
以下是具体例子:
- DALL-E:使用Transformer架构,但其最新版本使用声音扩散从文本生成图像。
- Stable Diffusion:使用扩散架构,根据用户描述生成高分辨率图像(写实、卡通、抽象风格)。
- Imagen:使用基于LLM构建的级联扩散模型,从文本提示生成图像。
局限性与注意事项
随着基础模型在其优势和应用中不断发展,我们也看到了一些局限性。
以下是需要注意的两点:
- 偏见问题:如果训练基础模型的数据存在偏见,其期望输出也可能带有偏见。
- 幻觉问题:LLMs可能会产生“幻觉”响应,即生成虚假信息,因为它们误解了数据集中参数的上下文。
因此,你必须谨慎地验证生成式AI聊天机器人输出的准确性。只要稍加注意,你就可以享受基础模型带来的诸多好处。

总结

本节课中,我们一起探索了基础模型的概念。这些模型在数十亿参数上进行预训练,这使它们能够发展出独立的推理能力,并执行大量复杂的任务。鉴于其多模态、多领域的能力,它们可以作为生成式AI应用的基础或基石。理解基础模型是理解现代生成式AI生态系统如何构建和运作的关键第一步。
生成式AI图像描述项目:018:项目概述 🖼️

在本节课中,我们将要学*一个名为“使用生成式AI为照片赋予有意义的名称”的项目。该项目旨在利用图像描述AI技术,将视觉信息转化为文本,从而提升图像的可访问性和管理效率。

想象一个世界,图像不再是沉默的。它们能够低语故事、揭示隐藏的细节,并开启知识的大门。这一切都得益于图像描述AI技术。
图像描述AI能够将图像的视觉信息转化为机器可读的语言。这项技术对多个方面产生重大影响,从改善视障人士的无障碍访问,到增强搜索结果和提升安全性。通过将视觉数据转化为文本,图像描述AI为更深层次的内容发现、更具吸引力的社交媒体呈现以及跨领域的高效数据管理铺平了道路。

项目目标与内容
在本项目中,你将学*构建一个自动化的图像描述AI工具。想象你是一位被成千上万未命名图片包围的平面设计师。找到正确的图片感觉就像大海捞针。因此,本项目致力于解决这个问题。
你将构建一个AI工具,它不仅“看”图像,更能“理解”图像。然后,它会创建一个文本文件作为索引,为图像提供关于其内容的、有意义的描述。这使得查找正确的图片变得简单,从而提升效率并减轻你的工作量。
本项目包含分步指导,教你如何实现并定制这个图像描述工具,以应用于现实场景。



你将完成的主要活动
以下是你在项目中需要完成的三个主要活动:
-
实现图像描述工具:利用Hugging Face Transformers库中的Blip模型来实现图像描述功能。
- 公式/代码:
BlipModel或BlipForConditionalGeneration。 - Blip(Bootstrapping Language-Image Pre-training)模型能够执行多种多模态任务,包括图像-文本检索和图像描述生成。
- 公式/代码:
-
创建用户友好界面:使用Gradio为你的图像描述应用提供一个用户友好的界面。
- 代码:
gradio.Interface()。 - Gradio是一个开源Python包,允许你为机器学*模型或Python函数快速构建演示或Web应用程序。
- 代码:
-
定制化以适应业务场景:通过从URL提取图像并生成描述,展示该自动化工具在实际业务场景中的应用,证明其实用价值。
预备知识与学*目标

要完成本项目,你需要具备Python的实用知识,并熟悉集成开发环境(IDE)的使用。你不需要事先具备Hugging Face Transformers或Gradio的经验,因为在项目过程中你将熟悉它们。
在本项目结束时,你将能够达成以下目标:
- 描述生成式AI模型的基础知识。
- 使用Python和Blip模型实现一个图像描述工具。
- 利用Gradio为图像描述应用创建一个用户友好的界面。
本项目提供了一个宝贵的机会,让你掌握运用Python函数和探索生成式AI模型多模态能力的技能。

总结
本节课中,我们一起学*了“使用生成式AI为照片赋予有意义的名称”项目的概述。我们了解了图像描述AI的价值、项目的核心目标,以及你将通过实现Blip模型、集成Gradio界面和进行场景定制来完成的三个主要活动。


现在,请准备好构建并实现这个AI工具,通过用有意义的描述替换那些无用的图像文件名,来改造你的照片库吧。
生成式人工智能工程:019:Hugging Face 平台介绍 🚀
在本节课中,我们将学* Hugging Face 平台。我们将了解该平台的目的、其提供的工具与能力,以及它如何与 Watson X AI 协作,共同助力企业发展。
Hugging Face 是一个开源人工智能平台,科学家、开发者和企业在此协作,共同构建个性化的机器学*工具。该平台的建立旨在为开源 AI 社区创建一个中心,用于共享模型、数据集和应用程序。这使得各类用户,包括那些没有独立构建机器学*应用预算或资源的用户,都能接触到 AI 技术。因此,Hugging Face 被誉为推动了 AI 的民主化,因为它汇集众人之力,从众多精炼的小模型中获益,挑战了“一个通用模型统治一切”的假设。


最初,Hugging Face 社区专注于创建基于 Transformer 的模型,以利用自然语言处理的能力。然而,如今该平台提供了多种机器学*工具,用于生成文本、图像、音频和视频。
目前,Hugging Face 平台托管了超过 250,000 个开源模型、50,000 个数据集和 100 万 个开源演示应用,并且这个列表还在持续增长。
科学家和开发者使用 Hugging Face 来构建、训练和部署他们的 AI 模型。他们可以访问平台的开源 Transformer 库,该库拥有超过 25,000 个预训练模型,支持 PyTorch、TensorFlow 和 Google JAX 框架。以下是这些框架的简要说明:
- PyTorch:一个深度学*库。
- TensorFlow:一个机器学*平台。
- Google JAX:一个机器学*框架。

该库中的模型执行多种任务,例如文本生成、问答、摘要、自动语音识别和图像分割等。用户可以通过名称筛选这些模型以找到现有模型,也可以将自己的模型分享到库中。开发者还可以在“Spaces”选项卡上托管生成式 AI 应用的演示,允许用户进行交互和验证。
那么,企业如何从该平台受益呢?
Hugging Face 为企业提供“企业中心”,企业可以从中访问预训练模型和数据集。这使得企业能够利用现有基础设施,而不是从头开始构建模型。这不仅减少了企业的碳足迹、扩展所需的时间和成本,还允许企业使用专有数据和相关用例来训练模型。


此外,Hugging Face 帮助企业:
- A. 添加或移除功能以提高模型效率。
- B. 评估其生成式 AI 模型以过滤有偏见的数据。
- C. 创建具有文本、图像、音频和视频生成能力的多模态应用。
超过 50,000 家大中小企业积极使用 Hugging Face。例如:
- 生成式 AI 解决方案提供商 Writer 在 Hugging Face 上托管其 PaLM 大语言模型。
- Intel 已正式加入 Hugging Face 的硬件合作伙伴计划,并与之合作构建先进的机器学*硬件和端到端机器学*工作流。
- 甚至大学和非营利组织也是 Hugging Face 的一部分。
Hugging Face 还提供其他服务:
- 专家加速计划:指导非开发人员使用机器学*模型。
- Hugging Chat:首个开源的 ChatGPT 替代品。
- 为保护用户,Hugging Face 遵守服务组织控制第 2 类法规,这意味着确保用户数据的安全性、可用性、处理完整性、机密性和隐私性。


为进一步推动协作,Hugging Face 与 Watson X.ai(IBM 面向 AI 构建者的下一代企业工作室)建立了独特的合作伙伴关系。Watson X.ai 在其工作室中提供精选的 Hugging Face 模型,以帮助其构建者社区训练、测试和部署各类机器学*和生成式 AI 应用。这样,该工作室利用了 Hugging Face 提供的数据多样性、社区力量和开源库。

另一方面,Hugging Face 创建了 IBM 大语言模型的开源版本,并将其提供在自己的平台上。双方都相信开源技术,并押注于社区在 AI 领域创造价值,因为专有 AI 模型可能很快过时。Hugging Face 可能在 AI 领域的“五大”(即 Google、OpenAI、Meta、IBM 和 Microsoft)中占据优势,因为它支持并得到持续创新的开源 AI 社区的支持。

在本节课中,我们一起学*了关于 Hugging Face 的所有内容。这个 AI 平台展示了开源 AI 社区的协作力量。它为企业创造了空间,使其能够以更低的成本、更快的速度、更少的碳足迹构建定制的专有模型。各类组织、大学和非营利机构利用该平台的工具和服务,从自然语言处理中受益。简而言之,您不必是大公司也能从生成式 AI 中获益。
创建你自己的类ChatGPT网站:020:项目概述 🚀

在本节课中,我们将要学*如何利用开源的大型语言模型,创建一个类似ChatGPT的智能对话网站。我们将从理解其核心概念开始,逐步构建一个具备前后端的完整应用。

项目简介
随着人工智能的发展,现在我们已经可以与机器进行智能对话。你可以从计算机中获取任何主题的信息,从而节省研究和查询的时间与精力。例如,你可以询问“如何在JavaScript中发起HTTP请求?”。
这种智能助手的功能是通过一种计算机程序或聊天机器人实现的。聊天机器人是一种模拟人类书面或口头对话的计算机程序。通过集成生成式AI技术,如自然语言处理,聊天机器人能够理解问题并根据其收集的数据进行回应。
聊天机器人的工作原理
聊天机器人程序接收文本作为输入,并输出相应的文本。一个名为Transformer的特殊程序充当了聊天机器人的“大脑”。Transformer包含一个大型语言模型,它帮助聊天机器人理解输入的问题,并生成类人的回应作为输出。
LLM程序会遍历其收集的数据,并基于机器学*生成回应。Transformer负责处理输入和输出数据的技术流程,而LLM则专注于语言的理解与生成。
在本项目中,你将学*使用开源的LLM创建一个简单的聊天机器人,并将其集成到一个网页界面中。
如何选择大型语言模型
要构建聊天机器人,你必须根据机器人的用途选择合适的LLM。例如:
- 对于通用文本生成,可以考虑使用GPT-2或GPT-3模型。
- 对于情感分析,可以考虑使用BERT模型。
- 对于语言翻译,可以考虑使用T5模型。
选择LLM时还需考虑其他重要参数,包括许可协议、模型大小、训练数据、性能与准确性。在本项目中,我们将使用Facebook的SplendorBod模型,该模型可通过Hugging Face网站获得开源许可。
项目所需工具与技能
在项目中,你还将使用Hugging Face的Python库——Transformers。这个库将为你构建聊天机器人提供有用的功能,例如与LLM交互,以及将输入优化为LLM能够理解的格式——即称为Token的小型构建块。
进行本项目时,你需要熟悉Python和Flask框架。同时,具备HTML、CSS和JavaScript的基础知识会更有帮助,但并非必需。本项目将提供分步指导,说明如何使用AI工具构建聊天机器人所需的代码和活动。
项目目标与架构
在本项目结束时,你将完成以下目标:
- 识别聊天机器人的主要组件。
- 确定为你的应用选择LLM时的考虑因素。
- 描述Transformer的工作原理。
- 获取开源模型并初始化分词器。
- 用Python编写你的聊天机器人程序。
在构建了一个终端聊天机器人之后,你还需要将其集成到一个网页界面中。

要构建一个类聊天机器人的网站,你需要创建一个托管聊天机器人的后端服务器,以及一个与后端服务器通信的前端网页。
在一个网站中,后端服务器就像是应用的大脑。在聊天机器人应用中,后端服务器将从前端界面接收用户提示,并将其输入给聊天机器人。然后,服务器接收聊天机器人的输出,并将其显示在前端界面上。
你将使用Flask来托管后端服务器。Flask是一个用于构建Web应用的Python框架。它提供了处理传入请求、处理数据和生成响应的工具与功能,使得为你的网站或应用提供动力变得简单。
总结

本节课中,我们一起学*了创建AI驱动的聊天机器人网站的整体蓝图。通过本项目,你将掌握使用开源LLM构建生成式AI应用的基础,并运用Python和Flask的Web开发技能,创建一个智能且交互性强的聊天机器人。

你已经为这个激动人心的项目做好了准备。
语音助手开发:021:项目概述 🎤

在本节课中,我们将学*如何结合 OpenAI 的 GPT-3 模型与 IBM Watson 的可嵌入 AI 服务,创建一个功能完整的智能语音助手。我们将探索语音 AI 的应用,并逐步构建一个能理解语音、智能回复并能“开口说话”的助手。
基于语音的人工智能领域正在迅速改变我们与技术交互的方式。其中,智能语音助手是一个前景广阔的应用方向。


设想一位忙碌的专业人士,他正忙于设计演示文稿,双手无暇操作电脑。此时,他无法通过电脑快速搜索信息。然而,他只需向 AI 助手提出一个语音查询,例如“总结一下人工智能在电动汽车领域应用的主要趋势”,就能保持信息灵通并高效工作。基于语音 AI 的助手使你能够通过自然对话无缝互动、获取信息并找到答案,这一切都只需借助你的声音。

在本项目中,我们将使用 OpenAI 的 GPT-3 模型和 IBM Watson 可嵌入 AI 来创建一个语音助手。GPT-3 模型将使助手能够理解和响应用户输入。Watson 的语音转文本(STT)功能赋予助手“听觉”,使其能理解用户的语音。Watson 的文本转语音(TTS)功能则使助手能够将答案“读”给用户听。IBM Watson 包含一套可嵌入的、容器化的文本转语音和语音转文本库,有助于响应人类语音、处理数据并回答问题,从而帮助个人和公司应对各种问题。本项目将探索聊天机器人及其应用。你将创建一个具有高智能水平的功能性助手,它可以接收语音输入并提供语音回复。


在本项目中,你将首先使用 Python 搭建一个用于构建助手的环境,然后使用 GPT-3 构建你自己的助手,最后,集成 IBM Watson 以实现语音检测功能。你还将学*如何将助手部署到公共服务器。
让我们先预览一下你将在本项目中开发的语音助手演示。
该助手的界面显示标题“语音助手”。它提供了在亮色和暗色模式之间切换的功能。此助手支持文本和语音两种交互方式,你可以在消息字段中输入问题,或点击录音图标进行语音提问。例如,提问“莎士比亚的悲剧有哪些?”,助手会提供详细的回复,显示文本并播放音频响应,展示了文本转语音的集成。你可以通过输入或使用录音选项来继续对话。通过输入“不,谢谢”等提示来结束与助手的互动。
为了构建这个语音助手,你将使用 HTML、CSS 和 JavaScript 来构建与助手通信的前端界面。对于后端开发,你将使用 Python 和 Flask 框架。Flask 是一个用于构建 Web 应用程序的 Web 框架。在本项目中,Flask 由 Docker 支持,以创建管理依赖项的容器。接着,你将集成 IBM Watson 的语音转文本功能,使聊天机器人能够理解用户的语音输入。然后,你将集成 GPT-3,为聊天机器人注入智能。此外,你还会集成 Watson 的文本转语音功能,赋予聊天机器人语音回复的能力。

一旦所有组件组合完成,你将开发出一个功能完整的语音助手,它可以接收文本和语音输入,并提供文本和语音两种形式的响应。要完成这个项目,你应该熟悉 Python 和 Flask。同时,建议(但非必需)对 HTML、CSS 和 JavaScript 有基本了解。本项目将提供分步说明,指导你如何使用代码以及完成构建聊天机器人所需的不同活动,并利用各种 AI 工具。
到本课程结束时,你将能够达成以下目标:
- 探索聊天机器人及其应用。
- 使用 Python 搭建构建聊天机器人的环境。
- 掌握集成大型语言模型(LLM)为聊天机器人注入智能的技能。
- 实现 Watson 的语音转文本和文本转语音功能。
- 创建一个功能完整的、支持语音的 AI 助手。
本项目将深入探讨如何构建一个强大的助手。你将学*关于聊天机器人的知识,以及使用 Flask 和 Python 进行 Web 开发。你还将集成 GPT-3 和 IBM Watson 的能力。

最终,你将构建出一个具备语音识别功能的助手。课程结束时,你将拥有一个功能齐全的 AI 助手,它展示了你在使用 API 与大型语言模型(LLM)协作方面的新技能。




本节课中,我们一起学*了本项目的整体目标、应用场景和技术架构。我们了解到,语音助手结合了前端界面(HTML/CSS/JS)、后端逻辑(Python/Flask)、智能核心(GPT-3) 和语音交互能力(IBM Watson STT/TTS)。在接下来的章节中,我们将逐步实现这些组件,最终完成我们的智能语音助手。
生成式人工智能工程:022:Docker简介 🐳
在本节课中,我们将要学*Docker的基础知识。Docker是一个用于开发、交付和运行应用程序的开源平台,它通过容器技术实现。我们将了解Docker的定义、工作原理、核心技术、优势以及其适用场景。
什么是Docker?
Docker自2013年问世,其官方定义可以概括为:Docker是一个用于将应用程序作为容器进行开发、运输和运行的开源平台。Docker因其简单的架构、强大的可扩展性以及在多种平台、环境和位置上的可移植性而受到开发者的欢迎。
Docker将应用程序与底层基础设施(包括硬件、操作系统和容器运行时)隔离开来。
Docker的工作原理与技术
上一节我们介绍了Docker的基本概念,本节中我们来看看Docker是如何工作的。
Docker使用Go编程语言编写。它利用Linux内核的特性来提供其功能。Docker使用命名空间技术来提供一个称为“容器”的隔离工作空间。Docker为每个容器创建一组命名空间,每个方面(如进程、网络)都在一个独立的命名空间中运行,且访问权限仅限于该命名空间。
Docker的方法论还启发了许多额外的创新,包括互补工具和开发方法。
以下是Docker生态中的一些关键组件:
- 互补工具:如Docker CLI、Docker Compose和Prometheus。
- 各类插件:包括存储插件。
- 编排技术:如使用Docker Swarm或Kubernetes。
- 开发方法论:如微服务和无服务器架构。
Docker的优势
了解了Docker的技术基础后,我们来看看使用Docker能带来哪些具体的好处。
Docker提供以下优势:
- 环境一致与隔离:带来稳定的应用部署。
- 快速部署:由于Docker镜像小巧且可复用,部署可在数秒内完成,这显著加快了开发流程。
- 自动化能力:有助于消除错误,简化维护周期。
- 支持敏捷与CI/CD DevOps实践:Docker易于版本控制,加快了测试、回滚和重新部署的速度。
- 应用模块化:帮助分割应用,便于刷新、清理和修复。开发者可以更快速地协作解决问题,并在需要时扩展容器。
- 高度可移植:Docker镜像与平台无关,因此具有高度的可移植性。
Docker的适用场景与挑战
尽管Docker有很多优势,但它并非适用于所有类型的应用。本节中我们来看看哪些情况下Docker可能不是最佳选择。
Docker不适合具有以下特性的应用程序:
- 需要高性能或高安全性的应用。
- 基于单体架构的应用。
- 使用丰富GUI功能的应用。
- 执行标准桌面功能或功能有限的应用。
总结 🎯
本节课中我们一起学*了Docker的核心知识。我们了解到Docker是一个用于开发、运输和运行容器化应用的开源平台,它能加速跨多环境的部署流程。Docker使用命名空间技术来提供隔离的容器工作空间,每个容器都运行在独立的命名空间中。它支持敏捷和CI/CD DevOps实践。最后,我们也认识到Docker容器并不适合基于单体架构或对性能、安全性有极高要求的应用程序。




生成式AI驱动的会议助手:023:项目概述 🎯

在本节课中,我们将学*如何构建一个由生成式AI驱动的智能会议助手。这个应用能够自动转录会议录音,并利用大语言模型生成简洁的摘要和关键点,帮助您高效回顾会议内容。
想象一下,您正在参加一场头脑风暴会议,各种想法和信息快速交换。您虽然做了笔记,但事后很难回忆起所有关键决策或具体的行动项。这时,一个创新的、基于生成式AI的应用就显得非常有帮助。它能准确转录会议讨论,并提供简明扼要的摘要,突出显示关键点和已做出的决策。
这背后的核心是自动语音识别技术和生成式大语言模型的结合。


您可以使用ASR技术将口语转换为可读的文本,然后利用LLM来高效地理解和总结这些文本。LLM还能通过纠正细微错误来优化语音转文本的输出,确保结果的连贯性和准确性。
本项目将指导您构建这样一个应用。您将使用名为OpenAI Whisper的ASR工具进行语音转文本,并利用Meta开发的强大开源语言模型Llama 2的能力来总结和提取关键点。
项目包含在无服务器环境中构建和部署应用的逐步指导。
首先,您将使用示例音频文件实现OpenAI Whisper,将音频转录为文本。
接下来,您将使用Hugging Face的Gradio库为应用构建一个直观且用户友好的界面。
进一步,您将集成由IBM Watson X托管的Llama 2 LLM,以有效地总结转录的音频。IBM Watson X提供了多种生成式AI模型,包括Llama 2。您将学*创建一个Python脚本来使用该模型生成文本,并了解影响模型输出的一些关键参数。
最后,您将学*使用IBM Code Engine在线部署应用。这是一个用于在云中运行应用程序的无服务器平台。
对于本项目,您将使用Python来编写不同功能的代码,因此您应具备该编程语言的基础知识。
让我们预览一下您将在本项目中开发的应用演示。

应用的输出将显示在Gradio的应用输出文本框中。应用界面显示标题“Audio Transcription App”。

您可以使用“点击上传”图标上传录制的音频文件,然后点击“提交”。音频文件内容的摘要和关键点将作为输出显示。
在本项目结束时,您将完成以下目标:
- 解释LLM如何帮助生成、优化和总结文本。
- 实现自动语音识别技术以进行语音到文本的转换。
- 为应用程序设计用户友好的界面。
- 使用云平台在线部署应用程序以托管应用。
通过完成这个项目,您将为使用LLM进行文本生成和摘要任务打下坚实的基础。项目提供了一个展示您Python编程技能以构建和部署应用程序的机会。
利用AI语音转文本转换和生成式AI LLM的强大功能,准备好应用并提升您的技能吧。

本节课中,我们一起学*了生成式AI会议助手项目的整体构想、技术栈构成以及最终将实现的目标。在接下来的章节中,我们将逐步深入,开始动手构建应用的各个部分。
生成式人工智能工程:024:IBM Watsonx.ai 平台概览
在本节课中,我们将学* IBM Watsonx.ai 平台的核心能力、主要功能以及常用工具。通过本节内容,你将能够解释 IBM Watsonx.ai 的功能特性,并识别其提供的常见工具。
概述:企业级AI的需求
通过AI创作诗歌或歌曲很有趣,但当AI应用于商业时,需要考虑更宏大的层面。企业级AI需要基于更高的标准和要求来构建。当你在业务核心中构建AI时,它必须是可信的、安全的、可扩展的且适应性强的。IBM Watsonx 正是这样一个帮助企业利用AI的平台。
IBM Watsonx 是一个面向AI构建者的集成式AI与数据平台。该平台包含三个核心产品:
- Watsonx.ai:一个用于新型基础模型、生成式AI和机器学*的工作室。
- Watsonx.data:一个数据存储库。
- Watsonx.governance:一个用于AI监控与治理的工具包。

本节视频将重点介绍 Watsonx.ai。

Watsonx.ai 简介
Watsonx.ai 是一个由基础模型驱动的集成工具工作室,用于处理生成式AI和构建机器学*模型。借助 Watsonx.ai,你可以轻松地训练、调优、部署和管理基础模型。这能帮助你在更短的时间和更少的数据量下构建AI应用。
以下是使用 Watsonx.ai 可以实现的一些主要目标:
- 构建机器学*模型。
- 试验基础模型。
- 管理AI生命周期。
根据你的目标,你可以选择 Watsonx.ai 提供的相应任务。这些任务可以通过平台上的工具来完成。
平台工具与AI生命周期
Watsonx.ai 中的任务和工具与模型的AI生命周期紧密对齐。通常,你需要准备数据、构建实验、训练模型和解决方案,然后部署模型并开始构建应用,最后管理这些模型和重复性流程。
Watsonx.ai 提供了对来自 Hugging Face 的IBM精选开源模型,以及一系列不同规模和架构的IBM自训练模型的访问。作为AI价值创造者,你也可以将自己的模型和数据带入 Watsonx.ai。
以下是 Watsonx.ai 中几个核心工具的简要介绍:
1. Prompt Lab(提示实验室)
AI构建者可以使用 Prompt Lab 试验基础模型,并构建满足其需求的提示词。该工具支持用户通过实验性提示词来完成一系列自然语言处理任务,包括:
- 问答
- 内容生成
- 摘要
- 文本分类
- 信息提取
2. Tuning Studio(调优工作室)
作为AI创造者,你可能希望基于自己的数据为特定业务用例定制模型。Watsonx.ai 的 Tuning Studio 工具使之成为可能。后续版本的 Watsonx.ai 将包含针对提示词调优和模型微调的调优方法与示例,以提升模型性能和准确性。
3. Pipeline(流水线)
将模型投入生产是一个包含多个步骤的过程,用于管理和自动化模型生命周期。Watsonx.ai 提供了 Pipeline 工具。该工具可用于自动化涉及数据加载、训练、部署模型和评估模型的步骤。这可以缩短模型投入生产的时间,并提高模型的准确性和可靠性。

工具协同与工作流
让我们通过IBM的视频了解 Watsonx.ai 中的不同工具如何协同工作,创建一个协作环境以简化AI模型的工作流。

直到最*,AI模型仍需针对非常具体的任务进行训练。但现在,借助基础模型的力量,你可以用更少的时间和数据构建强大的AI应用。
在我们的 Prompt Lab 中,你可以引导模型以满足你的需求。我们提供了易于使用的工具来构建和完善性能提示词,以达到预期结果。
如果你希望进一步定制,可以在 Tuning Studio 中调整模型,使其对你的业务用例更加精确。你可以导入数据集,并使用少至100个示例来调优你的模型。我们提供最先进的调优方法,只需点击几下即可完成设置。
现在,是时候让你的模型投入工作了。创建一个企业级部署,并开始构建你的应用。
就是这样,借助 Watsonx.ai,你的团队将获得一个协作环境的赋能,该环境能简化整个AI生命周期的工作流,为你企业倍增AI的力量。它实用、高效且易于使用。

安全与隐私保障
IBM Watsonx.ai 确保你正在处理的数据和模型的安全性。你的数据和你创建的模型仅对你自己可见。你的数据以加密格式存储。你创建的模型也仅对你的账户私有。IBM无法访问你的数据或模型,未经你的许可,它们永远不会被IBM或任何其他个人或组织使用。
总结

本节课中,我们一起学*了 IBM Watsonx,这是一个帮助企业在负责任和透明的前提下创建AI的AI与数据平台。Watsonx 的产品之一 Watsonx.ai,是一个用于训练、调优和部署生成式AI模型的集成工具工作室。Watsonx.ai 提供的主要工具包括 Prompt Lab、Tuning Studio 和 Pipeline 工具。
生成式AI与RAG:025:使用LLM总结私人数据

概述

在本节课中,我们将学*如何利用大型语言模型和生成式人工智能技术,来总结和分析您的私人数据。面对海量信息时,提取关键内容往往如同大海捞针。本项目将引导您构建一个智能聊天机器人,它能够理解您上传的PDF文档内容,并回答相关问题,从而帮助您高效地获取所需信息。
信息过载的挑战
在当今数字时代,我们不断被数据轰炸,这使得寻找特定细节变得异常困难。
从海量数据中提取见解,就像在干草堆中寻找一根针。

生成式AI与大型语言模型的引入

这正是生成式人工智能和大型语言模型发挥作用的地方。
LLM在庞大数据集上进行训练。然而,当您拥有敏感或机密数据,不希望与公共模型共享时,您可以使用私有部署的开源模型。
随着LLM等生成式AI模型的进步,我们可以创建并利用工具来高效地解析、总结和理解海量数据。

项目的核心价值

通过理解和利用LLM的能力,您可以改变信息检索的格局。这不仅仅是效率的提升,更为研究、商业分析乃至日常决策过程开辟了新前沿。
本项目深入探讨LLM,为您提供使用LLM从冗长文档中创建简明摘要和提取关键点的技能。
项目成果预览:智能聊天机器人

在项目中,您将创建一个聊天机器人,允许您上传PDF文件,随后您可以就该PDF的内容向聊天机器人提问。
这个聊天机器人不仅能通过文本与用户互动,还能理解并回应用户关于PDF中具体细节的查询。
让我们预览一下您将在本项目中开发的聊天机器人演示。
聊天机器人将作为一个个人数据助手界面进行访问。您将看到聊天机器人的初始消息和一个文件上传选项。
让我们上传一个PDF文档进行分析。在本演示中,我们将上传一份包含IBM博客文章的PDF,标题为《生成式AI提升开发人员生产力的9种方式》。

上传后,可以向聊天机器人询问关于PDF内容的任何问题。例如:“生成式AI对开发人员有哪些常见用例?”
聊天机器人将分析PDF的内容,并根据其理解提供答案。
项目技术栈详解
为了构建项目中的聊天机器人,您将使用 Llama 2 LLM。Llama 2是由Meta发布的一个开源LLM系列。
它处理自然语言,理解用户输入,并生成基于文本的响应。
在项目中,Llama 2的使用得到了检索增强生成技术的支持。
RAG使Llama 2能够访问并利用来自外部源或互联网的信息。这种结合将使聊天机器人在提供更准确、更相关的答案方面变得更智能。
在项目中,您将实现 Flask 和 LangChain 框架。Flask是Python中一个轻量级的Web应用框架,用于开发聊天机器人的后端,处理HTTP请求并提供响应。
LangChain框架可以将LLM集成到应用程序中,在这里可用于促进Flask后端和Llama 2之间的交互,以处理用户查询。
学*前提与项目目标
要完成本项目,您应该熟悉Python。同时,具备HTML、CSS和JavaScript的基础知识会有所帮助,但并非必需。

本项目提供了关于如何处理代码以及构建聊天机器人所需不同活动的分步说明。
在本项目结束时,您将能够实现以下目标:
- 解释LLM和生成式AI如何帮助总结和理解数据。
- 实现Llama 2和RAG以从大型文本中提取数据。
- 使用Flask和Python进行Web应用程序开发。
- 应用LangChain框架来有效解释和响应用户输入。
总结

本节课我们一起学*了构建一个强大的聊天机器人应用程序。您将了解聊天机器人、使用Flask和Python进行Web开发,并掌握利用LangChain来理解和响应多样化用户输入的能力。最终,您将拥有一个功能齐全的聊天机器人,展示您新获得的专业知识。
生成式人工智能工程:026:LangChain简介 🧠
在本节课中,我们将学*LangChain这一开源Python框架。我们将了解它的核心目的、主要优势、实际应用场景,以及它如何与其他数据类型协同工作。
概述
LangChain是一个用于简化大型语言模型(LLM)应用开发的开源Python框架。它为开发者提供了组件和接口,帮助将LLM集成到AI应用中。该框架的核心功能是从大量文本(如研究论文或法律文件)中定位相关信息,并通过检索数据和生成连贯摘要来响应复杂的提示。其名称中的“Chain”源于它将检索、提取、处理和生成等操作串联起来的能力。
LangChain的核心优势
AI开发者青睐LangChain,主要基于以下几个关键优势:模块化、可扩展性、分解能力以及与向量数据库的轻松集成。以下是每个优势的详细说明。
模块化
LangChain的设计允许应用开发者像搭积木一样组合不同的组件。这种模块化设计鼓励组件复用,从而节省开发时间和精力。
可扩展性
LangChain的可扩展设计使开发者能够轻松添加新功能、适配现有组件、与外部系统集成,并且只需对代码库进行最小程度的修改。
分解能力
LangChain模仿人类解决问题的过程,将复杂的查询或任务分解为更小、更易管理的步骤。这种分解能力使其能够从上下文中做出准确推断,从而产生相关且精确的响应。
与向量数据库集成
LangChain与向量数据库集成,可实现高效的语义搜索和信息检索。当与向量数据库结合使用时,它能为应用程序提供在庞大数据集中快速访问相关信息的能力。
LangChain的实践应用


AI应用可以利用LangChain框架实现多种实际用途,例如内容摘要、数据提取、复杂的问答系统以及自动化内容生成。让我们探讨几个具体例子。

内容摘要与数据提取
凭借其总结文章、报告和文档的能力,用户可以通过解读复杂的法律文件来更好地获取信息。它可以从报告中提取关键统计数据,简化将文本转化为可操作见解的过程。

复杂的问答系统
基于LangChain的问答系统可以变革客户支持和知识库服务。这些系统能够根据整个对话的上下文,提供一系列清晰、相关的答案。
自动化内容生成
LangChain可以协助生成书面材料。该框架为自动化常规写作任务(如起草电子邮件、头脑风暴或编写技术文档)开辟了可能性。
处理其他数据类型

虽然主要为基于文本的应用程序设计,但LangChain可以通过利用外部库和模型(如语音转文本)来处理其他数据类型,如图像、音频和视频。它与向量数据库的集成使得可以利用从这些数据类型生成的嵌入向量来捕获语义含义并执行相似性搜索,这使其成为处理各种AI任务的宝贵工具。
总结

本节课中,我们一起学*了LangChain。我们了解到,LangChain是一个用于精确定位文本中相关信息并提供响应复杂提示方法的Python框架。其优势包括模块化、可扩展性、分解能力以及与向量数据库的轻松集成。其实践应用包括解读复杂法律文件、从报告中提取关键统计数据、改进客户支持以及自动化常规写作任务。此外,通过使用外部库和模型,LangChain也能用于处理其他数据类型。
生成式人工智能工程:027:通过RAG增强LLM准确性 🎯

在本节课中,我们将学*一个名为“检索增强生成”的框架,它旨在帮助大型语言模型提供更准确、更及时的信息。
大型语言模型无处不在,它们在某些方面表现出色,但在其他方面也会出现有趣的错误。本节将探讨这些模型的常见挑战,并介绍RAG如何通过结合外部知识源来解决这些问题。
大语言模型的挑战
上一节我们提到了大语言模型的广泛应用,本节中我们来看看它们面临的两个核心问题。
大语言模型在响应用户查询(称为提示)时,可能会表现出两种不受欢迎的行为:
- 缺乏来源:模型给出的答案没有可追溯的来源支持,类似于“拍脑袋”给出的回答。
- 信息过时:模型的知识局限于训练数据,无法获取训练后出现的新信息。
为了说明这些问题,可以看一个例子:当被问及“太阳系中哪颗行星的卫星最多”时,一个仅依赖训练知识的模型可能会自信地回答“木星”。然而,根据最新的科学发现(例如来自NASA的数据),正确答案是“土星”。这个例子凸显了模型可能给出错误或过时信息的问题。
什么是检索增强生成(RAG)?
既然我们了解了LLM的局限性,那么如何改进呢?本节将介绍RAG的基本概念。
检索增强生成是一个框架,它在生成答案之前,先让模型从一个外部内容库中检索相关信息。这个内容库可以是开放的(如互联网),也可以是封闭的(如特定文档集)。
其核心流程可以概括为以下步骤:
- 用户向LLM提出问题(提示)。
- LLM不再直接生成答案,而是首先根据指令,从外部内容库中检索与问题相关的信息。
- LLM将检索到的内容与用户的原始问题结合起来。
- 最后,LLM基于这些信息生成最终答案,并可以附上证据。
用伪代码表示这个过程:
# 传统LLM响应
response = llm.generate(user_query)
# RAG框架下的响应
retrieved_content = retriever.search(user_query)
augmented_prompt = f"基于以下信息:{retrieved_content}, 请回答:{user_query}"
response = llm.generate(augmented_prompt)
RAG如何解决LLM的挑战
了解了RAG的流程后,我们来看看它具体如何应对之前提到的挑战。
以下是RAG带来的主要优势:
- 解决信息过时:无需重新训练整个模型。当有新信息出现时,只需更新外部内容库。下次用户提问时,模型就能检索到最新的信息来生成答案。
- 提供来源并减少幻觉:模型被指令要求关注检索到的原始数据,这使得它更少地依赖训练时学到的、可能不准确或过时的内部知识。模型能够为答案提供证据,从而降低了“捏造”事实(即幻觉)或泄露训练数据中个人信息的可能性。
- 学会说“我不知道”:如果根据内容库无法可靠地回答用户的问题,模型可以被训练或指令要求回答“我不知道”,而不是编造一个看似合理但会误导用户的答案。
RAG的潜在挑战与未来方向
任何技术方案都有其两面性。在认识到RAG的优势后,我们也需要了解它面临的挑战。
RAG框架的有效性高度依赖于其组成部分的质量:
- 检索器的质量至关重要:如果检索器无法为LLM提供最相关、最高质量的背景信息,那么即使用户的问题是可回答的,也可能无法得到正确答案。
- 生成器的优化:需要持续改进生成部分,以便LLM在获得优质检索结果后,能综合生成最丰富、最准确的最终答案。
因此,当前许多研究(包括IBM的工作)都致力于同时改进检索器和生成器,以构建更强大、更可靠的RAG系统。



本节课中,我们一起学*了检索增强生成框架。我们了解到,RAG通过让大语言模型在生成答案前先检索外部知识,有效解决了信息缺乏来源和内容过时的问题,同时降低了模型幻觉的风险。尽管其效果依赖于检索和生成组件的质量,但RAG无疑是提升LLM准确性和可靠性的一个强大工具。
使用LLM和STT-TTS构建AI翻译助手:028:项目概述 🐟
在本节课中,我们将要学*如何构建一个名为“Babble Fish”的语音驱动AI翻译助手。该项目将结合语音转文本、大语言模型和文本转语音技术,实现实时的多语言语音翻译。
全球化的劳动力和商业环境要求我们与遍布世界各地的同事、商业伙伴和客户进行多语言交流。人工智能的发展使得利用语音转文本、大语言模型和文本转语音技术来实现这一点变得容易。

借助这些技术,你可以构建一个完整的翻译系统,将你的语音翻译成多种语言。

项目简介

在这个项目中,你将创建一个支持语音的AI翻译助手。该助手将利用托管在IBM Watsonx.ai上的Falcon LLM模型以及可嵌入的Watson Speech库。翻译器会将录制的语音输入转换为文本,并将文本发送给Falcon LLM进行翻译。接着,它从LLM接收文本形式的翻译结果,并使用文本转语音技术将其转换为语音,然后播放给用户。
让我们先来看看你将在本项目中开发的语音助手演示。
功能演示
语音助手界面包含一个在亮色和暗色模式间切换的功能、一个用于输入消息的文本框以及一个麦克风图标。
首先,在输入框中输入一条消息,例如“用法语说你好”。系统将演示其响应。
接下来,尝试使用麦克风说出一条需要翻译成西班牙语的消息,例如“用西班牙语说早上好”。助手将以文本和语音消息的形式进行回应。
技术架构
用户将通过一个使用HTML、CSS和JavaScript构建的Web界面与语音助手进行交互。
为了构建语音助手的后端,你将使用Python和Flask。为了在应用程序中集成Falcon LLM模型,你将使用IBM Watson X,这是一个AI和数据平台。
IBM Watson Speech Lib for Embed Technologies 使助手能够通过语音输入和输出与他人交流。
通过组合所有这些组件,你将创建一个可以接收语音输入并提供语音响应的AI助手。
预备知识
要完成这个项目,你应该熟悉Python和Flask。同时,具备HTML、CSS和JavaScript的基础知识是推荐的,但不是必需的。
本项目提供了分步说明,指导你如何使用代码以及完成构建语音助手所需的不同活动。
学*目标
在本模块结束时,你将能够实现以下目标:
探索语音助手的基础知识及其各种应用。
探索并实现生成式AI模型在多语言翻译方面的能力。
实现语音转文本和文本转语音功能,使AI助手能够通过语音与用户交流。
为使用Python、Flask、HTML、CSS和JavaScript构建AI助手设置相关环境。
创建一个功能正常的、支持语音的AI助手。
项目成果
通过完成这个项目,你将掌握创建自己的AI驱动翻译助手的技能,该助手能够接收语音输入并提供多种语言的翻译。
你还将打下使用Python、Flask、HTML、CSS和JavaScript进行Web开发的坚实基础,并拥有一个对任何与之交互的人都非常有用的、功能齐全的全栈应用程序。

本节课中,我们一起学*了“Babble Fish”AI翻译助手项目的整体概览、技术架构和预期成果。我们了解到,该项目将整合前沿的AI技术,构建一个实用的多语言语音翻译工具。在接下来的章节中,我们将深入每个技术环节,逐步实现这个功能强大的助手。
生成式人工智能工程:029:项目构建AI职业教练简介 🚀

在本节课中,我们将学*如何构建一个基于大语言模型的个性化求职应用教练项目。该项目旨在利用生成式AI技术,帮助求职者优化简历、生成求职信并获得职业发展建议。
项目概述

在当今竞争激烈的就业市场中,求职方式正在发生变化。传统的求职方法已显不足。求职者常常面临诸多挑战,从制作出色的简历到向潜在雇主有效传达自身价值。
随着数字平台的兴起和招聘实践的演变,市场对创新解决方案的需求日益增长,这些方案旨在帮助制作有效简历并指导职业发展。

项目核心应用
在本项目中,你将构建一个AI职业教练,它提供三个核心应用。
以下是三个核心应用的具体介绍:
- 简历优化器:利用AI分析简历,并根据职位描述进行改进。
- 个性化求职信生成器:起草定制的求职信,以优化求职申请流程。
- 个性化职业顾问:提供职业改进建议,助力职业成长。
应用功能演示
上一节我们介绍了项目的三个核心应用,本节中我们来看看它们的具体功能演示。
简历优化器
简历优化器工具旨在根据你的指令优化简历内容,以达到最佳效果。我们建议按部分优化你的简历。
该优化器的界面使用Gradio构建。在“职位名称”字段中,输入你想要的职位,例如“数据分析师”。在“简历内容”字段中,输入你希望修改的内容。在“优化指令”字段中,输入任何你想建议的指令或改进方向。点击“提交”按钮。几秒钟后,修订后的内容便会显示出来。
求职信生成器
接下来,让我们查看求职信生成器。它将协助你撰写能展示技能和经验的求职信,并根据你申请的职位和公司进行定制。
在生成界面中,输入公司名称和职位。接着,输入职位描述。最后,输入你的简历内容。点击“提交”,几秒钟内即可生成求职信。
职业顾问工具
最后,查看职业顾问工具。该工具会根据职位和你的简历提供职业建议。
工具界面包含几个字段:首先,输入你希望申请的职位。然后,输入该职位的职位描述。接着,输入你的简历内容。点击“提交”。几秒钟后,职业教练将提供需要提升的领域或其他建议。
技术实现基础
前面我们了解了应用的功能,本节中我们来看看构建这些应用的技术基础。
本项目中的应用通过利用Meta开发的Llama 2 7B Chat大语言模型构建。该模型已集成到IBM的Watsonx.ai平台中。


本项目将使你掌握如何通过Watsonx.ai提供的API,将LLM集成到你的应用程序中。你将使用Gradio构建项目中应用的Web界面。
利用Gradio,你可以快速设计Web界面,使用户能够以文本、图像或音频等多种形式输入数据,并即时查看模型生成的输出。本项目提供了一个教程,帮助你熟悉Gradio以及如何创建一个简单的Gradio应用。
学*前提与目标
要完成本项目,你应该熟悉Python的基础知识。但是,无需担心编写复杂的脚本来构建这些应用。项目包含了分步说明和所需的Python脚本。
在本模块结束时,你将能够实现以下目标:
- 使用Python编程构建AI驱动的应用程序。
- 利用集成在AI平台(如IBM Watsonx.ai)中的开源LLM。
- 使用Gradio为应用程序构建Web界面。
总结

本节课中,我们一起学*了“AI职业教练”项目的整体介绍。该项目深入探讨了如何构建一个由生成式AI驱动的职业教练,旨在利用AI提供根据个人抱负和目标定制的个性化建议,从而支持职业指导。让我们开始构建吧。
生成式人工智能工程:030:问题描述 🚗
在本节课中,我们将要学*数据分析,并扮演数据分析师或数据科学家的角色,探讨一个具体的应用场景。
概述
数据分析的核心在于从原始数据中提取有价值的信息和洞察。我们周围无时无刻不在产生数据,无论是科学家手动收集的,还是我们在网站或移动设备上点击时自动生成的。然而,数据本身并不等同于信息。数据分析和数据科学的本质,就是帮助我们解锁原始数据中的信息和洞察,从而解答我们的疑问。
因此,数据分析扮演着至关重要的角色,它能帮助我们:
- 从数据中发现有用信息。
- 解答具体问题。
- 甚至预测未来或未知的情况。
上一节我们介绍了数据分析的重要性,本节中我们来看看一个具体的应用场景。

场景引入:汤姆的难题
让我们从一个场景开始。假设我们有一个朋友叫汤姆,他想卖掉他的车。但他面临一个问题:他不知道该为他的车定价多少。

汤姆希望他的车能卖到尽可能高的价钱。但同时,他也希望定价合理,这样才会有人愿意购买。因此,他设定的价格应该能准确反映这辆车的价值。
那么,我们如何帮助汤姆确定他车子的最佳售价呢?让我们像数据科学家一样思考,并清晰地定义一些问题。

以下是我们可以开始思考的一些关键问题:
- 是否有其他汽车的价格及其特征的数据?
- 汽车的哪些特征会影响其价格?是颜色、品牌吗?
- 马力是否也会影响售价?或者还有其他因素?
作为一名数据分析师或数据科学家,这些都是我们可以着手探究的起点。
后续步骤

要回答这些问题,我们需要数据。在接下来的视频中,我们将深入探讨如何理解数据、如何将数据导入Python,以及如何开始从数据中获取一些基本的洞察。
总结
本节课中我们一起学*了数据分析的基本目的,并引入了一个实际案例——帮助汤姆为他的二手车定价。我们明确了数据分析是从数据到信息的关键桥梁,并提出了几个可以通过数据来解答的核心问题。这为我们后续的数据处理和分析工作奠定了明确的目标。
生成式人工智能工程:031:理解数据

在本节课中,我们将学*如何理解一个用于机器学*项目的数据集。我们将以“二手车价格”数据集为例,介绍其格式、结构、各列的含义,并明确项目的预测目标。
数据集概览
本节我们将介绍本课程所使用的数据集的基本情况。
本课程使用的数据集是Jeffrey C. Schlimer提供的一个开放数据集。该数据集采用CSV格式,即用逗号分隔每个值,这使得它能够非常方便地被大多数工具或应用程序导入。数据集的每一行代表一条数据记录。
在本模块的实践练*中,你将能够下载并使用这个CSV文件。
数据格式与结构
上一节我们了解了数据集的来源和基本格式,本节中我们来看看数据的具体结构。
你是否注意到第一行数据有什么不同?有时,CSV文件的第一行是表头,包含每一列的名称。但在这个例子中,第一行只是另一行普通数据。
以下是关于26个列分别代表什么的文档说明。列的数量很多,我将只介绍其中几个列名的含义,你也可以查看幻灯片底部的链接,自行阅读所有列的详细描述。
以下是几个关键属性的说明:
- symboling(保险风险评级):对应车辆的保险风险等级。汽车最初会根据其价格被赋予一个风险系数符号。如果一辆车的风险更高,这个符号会向上调整。公式:
+3表示高风险,-3表示非常安全。 - normalized-losses(归一化损失):指每辆受保车辆每年的相对平均赔付损失。该值在特定尺寸分类(如双门小型旅行车、运动型专用车等)的所有汽车中进行归一化处理,代表每辆车每年的平均损失。其值范围在65到256之间。
- 其他属性相对容易理解。如果你想查看更多详细信息,请参考幻灯片底部的链接。
定义预测目标
在理解了每个特征的含义之后,我们接下来需要明确机器学*模型的目标。
我们会注意到第26个属性是 price(价格)。这就是我们的目标值或标签。换句话说,价格是我们希望从数据集中预测的值,而预测因子应该是列出的所有其他变量,如symboling、normalized-losses、make(品牌)等等。
因此,本项目的目标是根据其他汽车特征来预测价格。
最后请注意,这个数据集实际上来自1985年,因此其中车型的价格可能看起来偏低。但请记住,本练*的目的是学*如何分析数据。
课程总结

本节课中,我们一起学*了“二手车价格”数据集。我们了解了它的CSV格式、数据结构,解读了几个关键列的含义,并最终明确了本项目的机器学*目标:使用其他特征来预测汽车的价格。理解数据是构建有效模型的第一步。
生成式人工智能工程:032:数据科学的Python包
在本节课中,我们将要学*在Python中进行数据分析所需的核心软件包。理解这些工具是构建数据科学和机器学*项目的基础。

概述:Python库简介
为了在Python中进行数据分析,我们首先需要了解与数据分析相关的主要软件包。
一个Python库是一个函数和方法的集合,它允许你执行大量操作而无需编写任何代码。这些库通常包含内置模块,提供不同的功能,你可以直接使用。有大量的库提供了广泛的功能。
我们将Python数据分析库分为三组。
科学计算库
上一节我们介绍了Python库的基本概念,本节中我们来看看第一组:科学计算库。这些库提供了处理数值数据和执行数学运算的核心功能。
以下是三个主要的科学计算库:
- Pandas:提供用于高效数据操作和分析的数据结构和工具。它提供对结构化数据的访问。Pandas的主要工具是一个由列和行标签组成的二维表,称为DataFrame。它旨在提供简单的索引功能。
- NumPy:使用数组作为其输入和输出。它可以扩展到矩阵对象,通过少量代码更改,开发人员就可以执行快速的数组处理。
- SciPy:包含用于解决本幻灯片上列出的一些高级数学问题的函数,以及数据可视化功能。

数据可视化库
掌握了数据处理工具后,我们需要将分析结果有效地展示出来。使用数据可视化方法是与他人交流、展示有意义分析结果的最佳方式。这些库使你能够创建图形、图表和地图。
以下是两个核心的数据可视化库:
- Matplotlib:是最著名的数据可视化库包。它非常适合制作图形和绘图。其图形也具有高度可定制性。
- Seaborn:另一个高级可视化库,它基于Matplotlib。它可以非常轻松地生成各种图形,例如热图、时间序列图和小提琴图。
机器学*算法库

在能够处理和可视化数据之后,我们可以利用这些数据构建预测模型。通过机器学*算法,我们能够使用数据集开发模型并获得预测结果。算法库处理从基础到复杂的各种机器学*任务。
以下是两个重要的机器学*库:
- Scikit-learn:包含用于统计建模的工具,包括回归、分类、聚类等。该库构建于NumPy、SciPy和Matplotlib之上。
- Statsmodels:也是一个Python模块,允许用户探索数据、估计统计模型和执行统计检验。
总结

本节课中我们一起学*了在Python数据科学领域中至关重要的三组核心库。我们首先介绍了用于数据操作和数值计算的科学计算库,如Pandas、NumPy和SciPy。接着,我们探讨了用于将分析结果图形化的数据可视化库,包括Matplotlib和Seaborn。最后,我们了解了用于构建预测模型的机器学*算法库,主要是Scikit-learn和Statsmodels。掌握这些工具是进行有效数据分析和机器学*项目开发的第一步。
生成式人工智能工程:033:在Python中导入和导出数据 📊

在本节课中,我们将学*如何使用Python的Pandas包来读取数据。一旦数据被导入Python,我们就能执行所有后续需要的数据分析流程。
数据获取是一个从各种来源加载和读取数据到笔记本的过程。使用Python的Pandas包读取数据时,需要考虑两个重要因素:格式和文件路径。
- 格式指的是数据的编码方式。我们通常可以通过查看文件名的后缀来识别不同的编码方案。一些常见的格式包括CSV、JSON、XLSX、HDF等。
- 路径告诉我们数据存储的位置。数据通常存储在我们正在使用的计算机上,或者存储在互联网上。
在我们的案例中,我们从幻灯片上显示的网址获取了一个二手车数据集。当Jerry在网页浏览器中输入该网址时,他看到了类似下图的内容。


每一行代表一个数据点,每个数据点关联着大量属性。由于属性之间用逗号分隔,我们可以推测数据格式是CSV,即逗号分隔值。目前,这些只是数字,对人类来说意义不大。但一旦我们读入这些数据,就可以尝试理解它。
在Pandas中,read_csv方法可以将以逗号分隔列的文件读入一个Pandas数据框。
使用Pandas读取数据可以快速通过三行代码完成:
- 导入pandas库。
- 定义一个包含文件路径的变量。
- 使用
read_csv方法导入数据。
然而,read_csv默认假设数据包含表头。我们的二手车数据没有列标题,因此需要通过设置header=None来指定read_csv不分配表头。

读取数据集后,最好查看一下数据框,以获得更好的直观感受,并确保一切按预期进行。由于打印整个数据集可能耗时耗资源,为了节省时间,我们可以使用dataframe.head()来显示数据框的前N行。类似地,dataframe.tail()显示数据框的底部N行。


这里我们打印出了前五行数据。看起来数据集读取成功。我们可以看到,由于我们在读取数据时设置了header=None,Pandas自动将列标题设置为整数列表。

😊


然而,没有有意义的列名,处理数据框会很困难。我们可以在Pandas中分配列名。在我们当前的案例中,我们发现列名存储在一个单独的在线文件中。
我们首先将列名放入一个名为headers的列表中。然后,我们设置df.columns = headers,用这个列表替换默认的整数标题。如果我们使用上一张幻灯片介绍的head方法来检查数据集,会看到正确的标题已插入每列的顶部。

在某个时间点,当你对数据框进行操作后,你可能希望将Pandas数据框导出到一个新的CSV文件。



你可以使用to_csv方法来实现。为此,需要指定文件路径,其中包含你想要写入的文件名。例如,如果你想将数据框df保存为automobile.csv到你的计算机,可以使用语法df.to_csv()。


对于本课程,我们只读取和保存CSV文件。然而,Pandas也支持导入和导出大多数具有不同数据集格式的数据文件类型。读取和保存其他数据格式的代码语法与读取或保存CSV文件非常相似。下表显示了读取和保存不同格式文件的不同方法。


本节课中,我们一起学*了如何使用Pandas库在Python中导入和导出数据。我们了解了数据格式和路径的重要性,掌握了使用read_csv读取CSV文件(包括处理无表头数据)以及使用head/tail预览数据的基本方法。我们还学*了如何为数据框分配有意义的列名,以及如何使用to_csv方法将处理后的数据导出为新文件。最后,我们了解到Pandas支持多种数据格式,其操作方法类似,为后续的数据分析工作奠定了坚实的基础。
生成式人工智能工程:034:开始在Python中分析数据

在本节课中,我们将学*使用Python的Pandas库进行数据分析时,数据科学家和分析师必须掌握的一些基础方法。我们将重点介绍如何检查数据类型、获取数据集的统计摘要以及查看数据概览。
上一节我们介绍了数据加载,本节中我们来看看如何探索已加载的数据集。
Pandas内置了多种方法,可用于理解特征的数据类型或查看数据在数据集中的分布情况。使用这些方法可以获得数据集的概览,并指出潜在问题,例如特征的数据类型错误,这些问题可能需要在后续步骤中解决。

数据类型概述
数据有多种类型。Pandas中存储的主要类型是object、float、int和datetime。这些数据类型名称与原生Python中的名称略有不同。
以下是它们之间的差异和相似之处对比表:
- 一些类型非常相似,例如数值数据类型
int和float。 - Pandas的
object类型功能类似于Python中的string,只是名称不同。 - Pandas的
datetime类型对于处理时间序列数据非常有用。
检查数据集中的数据类型主要有两个原因:
- Pandas会根据从原始数据表读取的文本自动分配类型。由于各种原因,这种分配可能不正确。例如,如果本应包含连续数值的汽车价格列被分配了
object数据类型,这就会很棘手。将其更改为float类型会更自然。因此,Jerry可能需要手动将数据类型更改为float。 - 它让有经验的数据科学家能够了解哪些Python函数可以应用于特定列。例如,某些数学函数只能应用于数值数据。如果将这些函数应用于非数值数据,可能会导致错误。
应用.dtypes方法
将.dtypes方法应用于数据集时,会返回一个Series,其中包含每列的数据类型。

一位优秀的数据科学家的直觉告诉我们,大多数数据类型是合理的。例如,汽车的品牌是名称,因此此信息应为object类型。但列表中的最后一个(bore)可能存在问题。由于bore是发动机的一个尺寸维度,我们应期望使用数值数据类型。然而,此处却使用了object类型。在后面的章节中,Jerry将必须纠正这些类型不匹配的问题。
使用.describe()方法获取统计摘要
现在,我们希望检查每列的统计摘要,以了解每列中数据的分布情况。

统计指标可以告诉数据科学家是否存在潜在的数学问题,例如极端异常值和大的偏差。数据科学家可能需要在后续处理这些问题。
要获取快速统计数据,我们使用.describe()方法。它返回列中的条目数(count)、列值的平均值(mean)、列标准偏差(std)、最大值和最小值,以及每个四分位数的边界。
默认情况下,DataFrame.describe()函数会跳过不包含数字的行和列。也可以让describe方法适用于object类型的列。
启用所有列的摘要
要启用所有列的摘要,我们可以在describe()函数括号内添加参数include='all'。

现在,结果显示所有26列的摘要,包括object类型的属性。我们看到,对于object类型的列,计算了一组不同的统计信息,例如unique、top和freq。
unique是列中不同对象的数量。top是最常出现的对象。freq是top对象在列中出现的次数。
表中的某些值显示为NaN,代表“Not a Number”。这是因为无法针对该特定列的数据类型计算该特定统计指标。
使用.info()方法检查数据集

您可以使用的另一个检查数据集的方法是DataFrame.info()函数。此函数显示数据框的前30行和后30行。

本节课中我们一起学*了使用Pandas进行初步数据分析的核心方法。我们了解了如何检查并理解数据集中的数据类型,如何使用.describe()方法获取数值和对象型数据的统计摘要,以及如何使用.info()方法快速查看数据框的概览。掌握这些方法是进行有效数据清洗和探索性分析的基础。
生成式人工智能工程:035:使用Python访问数据库
在本节课中,我们将学*如何使用Python编程语言来连接和操作数据库。数据库是数据科学家的重要工具,掌握Python访问数据库的技能对于处理和分析数据至关重要。

数据库访问的基本概念
上一节我们介绍了数据库的重要性,本节中我们来看看一个典型的用户如何通过Python代码访问数据库。
用户通常在基于Web的编辑器(如Jupyter Notebook)中编写Python代码。Python程序通过一种机制与数据库管理系统(DBMS)进行通信,这种机制就是调用应用程序编程接口(API)。


理解SQL API
SQL API是一组库函数调用,作为DBMS的应用程序编程接口。其基本工作原理如下图所示。
为了将SQL语句传递给DBMS,应用程序会调用API中的函数,并通过调用其他函数从DBMS检索查询结果和状态信息。

以下是SQL API的典型操作流程:
- 应用程序通过一个或多个API调用开始数据库访问,这些调用将程序连接到DBMS。
- 为了发送SQL语句,程序在缓冲区中将语句构建为文本字符串,然后进行API调用以将缓冲区内容传递给DBMS。
- 应用程序进行API调用来检查其DBMS请求的状态并处理错误。
- 应用程序通过一个API调用结束数据库访问,该调用会断开与数据库的连接。
Python DB API简介

上一节我们了解了通用的SQL API,本节中我们来看看Python中专门的标准——DB API。
DB API是Python用于访问关系型数据库的标准API。它是一个标准,允许你编写一个能与多种关系型数据库协同工作的单一程序,而无需为每种数据库编写独立的程序。因此,如果你学会了DB API函数,就可以将这些知识应用于使用Python连接任何数据库。
Python DB API的两个核心概念是连接对象和游标对象。
- 连接对象用于连接到数据库并管理事务。
- 游标对象用于运行查询。你打开一个游标对象,然后运行查询。游标的工作方式类似于文本处理系统中的光标,你可以在结果集中向下滚动,并将数据获取到应用程序中。游标用于遍历数据库的查询结果。

以下是连接对象常用的方法:
cursor(): 返回一个使用该连接的新游标对象。commit(): 用于将任何待处理的事务提交到数据库。rollback(): 使数据库回滚到任何待处理事务的开始状态。close(): 用于关闭数据库连接。
实践:使用DB API查询数据库
了解了核心概念后,让我们通过一个Python应用程序示例,一步步学*如何使用DB API来查询数据库。
-
导入数据库模块并建立连接
首先,从相应的数据库模块(如sqlite3、pymysql等)导入connect函数,并使用它来打开一个到数据库的连接。你需要向connect()函数传入数据库名称、用户名和密码等参数。该函数会返回一个连接对象。import sqlite3 # 示例使用sqlite3模块 connection = sqlite3.connect(‘example.db’) # 连接到SQLite数据库文件 -
创建游标对象
接着,在连接对象上创建一个游标对象。这个游标将用于执行查询和获取结果。cursor = connection.cursor() -
执行查询并获取结果
使用游标对象的execute()方法来运行SQL查询语句。然后,可以使用如fetchone()、fetchall()等方法来获取查询结果。cursor.execute(‘SELECT * FROM some_table’) results = cursor.fetchall() for row in results: print(row) -
关闭连接
最后,当所有查询操作完成后,务必通过关闭连接来释放所有占用的资源。这是一个重要的好*惯,可以避免未使用的连接占用系统资源。connection.close()
记住,始终关闭数据库连接非常重要,这可以避免未使用的连接占用资源。
总结
本节课中我们一起学*了如何使用Python访问数据库。我们首先了解了通过API连接数据库的基本概念,然后深入探讨了Python DB API标准及其两个核心对象:连接对象和游标对象。最后,我们通过一个简单的代码示例,实践了连接数据库、执行查询和关闭连接的完整流程。掌握这些基础知识是进行后续数据操作和分析的关键第一步。


生成式人工智能工程:036:在Python中预处理数据 📊
在本节课中,我们将学*数据预处理技术。数据预处理是数据分析中必不可少的一步,它涉及将原始数据转换或映射为另一种格式,以便进行后续分析。这个过程也常被称为数据清洗或数据整理。
我们将涵盖以下主题:识别和处理缺失值、统一数据格式、数据归一化、数据分箱,以及将分类变量转换为数值变量。掌握这些技术能帮助你为机器学*模型准备更高质量的数据。
识别与处理缺失值 🧹
上一节我们介绍了数据预处理的基本概念。本节中,我们来看看如何处理数据中的缺失值。缺失值是指数据条目为空的情况,它会影响分析的准确性。
在Python的pandas库中,我们可以使用以下方法来处理缺失值:
- 检测缺失值:使用
df.isnull()或df.isna()来识别数据框中的缺失值。 - 删除缺失值:使用
df.dropna()删除包含缺失值的行或列。 - 填充缺失值:使用
df.fillna()用特定值(如均值、中位数或众数)填充缺失值。
处理缺失值是数据清洗的第一步,为后续的统一格式工作打下基础。
统一数据格式 🔄
数据可能来自不同源头,因此格式、单位或惯例往往不统一。为了使数据具有可比性,我们需要将其标准化。
pandas提供了多种方法来统一数据格式:
- 数据类型转换:使用
df[‘column’].astype(‘type’)转换列的数据类型,例如将字符串转换为数值。 - 字符串操作:使用
.str访问器进行大小写转换、去除空格等操作,例如df[‘column’].str.lower()。 - 单位转换:通过数学运算将数据列转换为统一的单位。
将数据格式标准化后,不同来源的数据才能被放在一起进行有效的比较和分析。
数据归一化:中心化与缩放 📏
不同数值型数据列的范围可能差异巨大,直接比较通常没有意义。归一化是一种将数据转换到相似范围的方法,以便进行更有意义的比较。
我们主要关注两种技术:
- 中心化:使数据以零为中心。这通常通过减去均值来实现。
- 公式:
X_centered = X - mean(X)
- 公式:
- 缩放:改变数据的范围。最常用的是最小-最大缩放和标准化。
- 最小-最大缩放公式:
X_scaled = (X - min(X)) / (max(X) - min(X)) - 标准化公式:
X_standardized = (X - mean(X)) / std(X)
- 最小-最大缩放公式:

在Python中,可以使用 sklearn.preprocessing 模块中的 StandardScaler 或 MinMaxScaler 轻松实现这些操作。

归一化处理后的数据,更适合许多机器学*算法的输入要求。

数据分箱 📦
数据分箱是将一组连续数值划分为更大范畴(即“箱”)的过程。它对于数据分组比较特别有用,也能在一定程度上平滑噪声。
以下是分箱的基本步骤:
- 定义箱的边界:可以等宽分箱(按值范围),也可以等频分箱(按样本数量)。
- 对值进行分箱:使用
pandas.cut(基于值范围)或pandas.qcut(基于分位数)函数。 - 使用箱标签:可以用箱的序号或自定义的类别标签来代表原始值。
分箱可以将连续数据离散化,有时能揭示不同的数据分布模式。
转换分类变量为数值变量 🔢
许多统计模型要求输入是数值。因此,我们需要将分类变量(如“颜色”:红、蓝、绿)转换为数值形式。
常用的转换方法包括:
- 标签编码:为每个类别分配一个唯一的整数,例如 {‘红’:0, ‘蓝’:1, ‘绿’:2}。可使用
sklearn.preprocessing.LabelEncoder。 - 独热编码:为每个类别创建一个新的二进制列(0或1)。可使用
pandas.get_dummies()或sklearn.preprocessing.OneHotEncoder。
注意:独热编码能避免模型误认为类别之间有顺序关系,但会增加数据维度。
完成分类变量的转换后,数据集就基本完成了数值化预处理,可以输入给模型进行训练了。

Python pandas 数据框操作示例 💻
在Python中,我们通常按列进行操作。数据框的每一行代表一个样本(例如,数据库中的一辆二手车)。
你可以通过指定列名来访问某一列,例如访问 symboling 和 body-style 列。这些列都是pandas序列。

# 访问列
symboling_column = df[‘symboling’]
body_style_column = df[‘body-style’]
Python中有许多操作数据框的方法。例如,要给某一列的每个条目都加上一个值(比如给所有 symboling 值加1),可以使用以下命令:
# 修改列的值
df[‘symboling’] = df[‘symboling’] + 1
这个操作会将该数据框列中的每个当前值都增加1。
总结 🎯

本节课中,我们一起学*了数据预处理的核心技术。我们从识别和处理缺失值开始,确保了数据的完整性。接着,我们探讨了如何统一不同来源的数据格式,使其具有可比性。然后,我们深入讲解了数据归一化(包括中心化和缩放),使数值数据处于相似范围以便分析。此外,我们还介绍了数据分箱技术,用于将连续数据分组。最后,我们学*了如何将分类变量转换为数值变量,以满足统计模型的要求。这些步骤是构建高质量数据集、进行有效数据分析和机器学*建模的重要基础。
生成式人工智能工程:037:在Python中处理缺失值 📊
在本节课中,我们将介绍数据中普遍存在的缺失值问题,并学*当你在数据中遇到缺失值时可以采取的处理策略。

概述
缺失值是指针对某个特定观测样本,其某个特征没有存储数据值的情况。这通常会导致数据分析或机器学*模型训练出现问题。因此,正确处理缺失值是数据预处理的关键步骤。

什么是缺失值?
当某个观测样本的某个特征没有存储数据值时,我们就说这个特征存在缺失值。在数据集中,缺失值通常以问号、NaN(Not a Number)或空白单元格的形式出现。
例如,在下图所示的数据中,“normalized-losses”(标准化损失)特征就存在一个缺失值,它被表示为 NaN。

如何处理缺失数据?
处理缺失值的方法有很多,这与使用Python、R或其他工具无关。当然,每种情况都不同,应具体分析。以下是几种典型的处理选项:
以下是几种处理缺失数据的常见策略:
- 追溯原始数据:联系数据收集者,尝试找回缺失的实际值。
- 删除数据:直接移除包含缺失值的数据。删除时可以选择:
- 删除整个变量(列)。
- 仅删除包含缺失值的单个数据条目(行)。
- 如果缺失数据的观测样本不多,通常删除特定条目是最佳选择。删除数据时,应选择对整体数据影响最小的方式。
- 替换数据:用估算值填充缺失值。这种方法不会浪费数据,但由于是猜测,准确性较低。
- 平均值替换:一种标准技术是用整个变量的平均值来替换缺失值。
- 公式:
缺失值 ≈ 变量平均值 - 例如,假设“normalized-losses”列中部分条目缺失,而该列有数据的条目的平均值为4500。我们可以用4500来*似填充这些缺失值。
- 公式:
- 众数替换:对于分类变量(如“fuel-type”燃油类型),由于值不是数字,无法计算平均值。此时,可以使用众数(即最常见的类别,如“gasoline”汽油)进行替换。
- 平均值替换:一种标准技术是用整个变量的平均值来替换缺失值。
- 基于知识的估算:有时数据收集者可能对缺失数据有额外了解,可以利用这些信息进行更合理的猜测。例如,他知道缺失值往往对应旧车,而旧车的标准化损失远高于平均水平。
- 保留缺失值:在某些情况下,你可能选择简单地保留缺失值。出于某些原因,即使某些特征缺失,保留该观测样本也可能有用。

在Python中处理缺失值
上一节我们介绍了处理缺失值的通用策略,本节中我们来看看如何在Python中具体实现删除和替换操作。我们将使用Pandas库。
删除缺失值
要删除包含缺失值的数据,Pandas库提供了一个内置方法 dropna()。使用这个方法,你可以选择删除包含缺失值(如NaN)的行或列。
你需要指定参数:
axis=0:删除包含缺失值的行。axis=1:删除包含缺失值的列。
示例:假设“price”(价格)列存在缺失值。由于在后续分析中,二手车价格是我们试图预测的目标,我们必须删除那些没有标价的行(即“price”为缺失值的行)。
这可以通过一行代码完成:

dataframe.dropna(subset=[“price”], axis=0, inplace=True)
subset=[“price”]:指定只在“price”列检查缺失值。axis=0:删除行。inplace=True:这个参数非常重要,它允许修改直接作用于原数据集。将其设为True等同于将结果写回原DataFrame。
重要提示:如果不设置 inplace=True,原DataFrame不会被改变。例如 dataframe.dropna() 只会返回一个删除了缺失值的新DataFrame,而不改变原始数据。这是一种检查操作是否正确的好方法,但要实际修改数据,必须设置 inplace=True。

替换缺失值
要用实际值替换缺失值(如NaN),Pandas库提供了 replace() 方法,也可以用更专门的 fillna() 方法。它们可以用来用新计算的值填充缺失值。


示例:假设我们想用变量的平均值来替换“normalized-losses”变量的缺失值。
在Python中,步骤如下:
- 首先计算该列的平均值。
- 然后使用
replace()或fillna()方法进行替换。
使用 fillna() 的代码示例:
# 计算“normalized-losses”列的平均值(自动忽略NaN)
mean_value = dataframe[“normalized-losses”].mean()

# 用平均值填充该列的缺失值
dataframe[“normalized-losses”].fillna(mean_value, inplace=True)
使用 replace() 的代码示例(功能更通用,但用于填充NaN时 fillna() 更直观):
mean_value = dataframe[“normalized-losses”].mean()
dataframe.replace({“normalized-losses”: np.nan}, mean_value, inplace=True)
# 注意:此方法需要导入numpy (import numpy as np)
这是一种相对简化的替换方法。当然,还有其他技术,例如用分组平均值(而非整个数据集)来替换缺失值。

总结

本节课中,我们一起学*了在Python中处理缺失数据的两种主要方法:
- 删除:使用
dropna()方法删除包含缺失值的行或列。 - 替换:使用
fillna()或replace()方法,用平均值、众数或其他估算值来填充缺失值。
但请不要忘记处理缺失数据的其他途径。你始终可以尝试寻找更高质量的数据集或数据源。在某些情况下,保留缺失值本身也可能是合理的选择。选择哪种方法取决于你的具体数据、分析目标和领域知识。


生成式人工智能工程:038:在Python中数据格式化 📊
在本节课中,我们将要学*如何处理不同格式、单位和约定的数据,并介绍Pandas库中帮助我们解决这些问题的相关方法。

数据通常由不同的人从不同的地方收集,可能以不同的格式存储。数据格式化意味着将数据带入一个通用的表达标准,使用户能够进行有意义的比较。作为数据集清洗的一部分,数据格式化确保数据一致且易于理解。
数据格式不一致的问题

上一节我们介绍了数据格式化的基本概念,本节中我们来看看一个具体的例子。
例如,人们可能使用不同的表达方式来代表纽约市,例如:NEW YORK、New York、N.Y.。有时,这种不干净的数据有其价值。例如,如果你想分析人们倾向于如何书写“纽约”,那么这正是你想要的数据。或者,如果你在寻找发现欺诈的方法,也许书写N.Y.比完整写出New York更可能预示着异常。但更多时候,我们只是希望将它们全部视为相同的实体或格式,以便于后续进行统计分析。
数据单位的转换
了解了数据表达不一致的问题后,我们来看看另一个常见问题:数据单位的转换。
以我们的二手车数据集为例,其中有一个名为“城市每加仑英里数”的特征,指的是汽车以每加仑英里为单位的油耗。然而,你可能生活在使用公制单位的国家,因此会希望将这些值转换为公制版本“升每100公里”。

要将“每加仑英里数”转换为“升每100公里”,我们需要用235除以“城市每加仑英里数”列中的每个值。
在Python中,这可以轻松地用一行代码完成。你取该列并将其设置为等于235除以整个列。在第二行代码中,使用数据框的rename方法将列名从“城市每加仑英里数”重命名为“城市升每100公里”。
以下是转换和重命名的代码示例:
# 转换单位:从 MPG 到 L/100km
df['city-mpg'] = 235 / df['city-mpg']
# 重命名列
df.rename(columns={'city-mpg': 'city-L/100km'}, inplace=True)
数据类型的识别与转换
处理完单位问题,我们接下来探讨数据类型的处理。由于多种原因,包括将数据集导入Python时,数据类型可能会被错误地设置。
例如,这里我们注意到“价格”特征被分配的数据类型是object,尽管预期的数据类型应该是整数或浮点类型。
为了后续分析,探索特征的数据类型并将其转换为正确的类型非常重要。否则,后续开发的模型可能会表现异常,完全有效的数据最终可能被当作缺失数据处理。

Pandas中有许多数据类型。object可以是字母或单词,int64是整数,float64是实数。还有许多其他类型,我们在此不讨论。
以下是识别和转换数据类型的方法:
要识别Python中某个特征的数据类型,我们可以使用DataFrame.dtypes方法检查数据框中每个变量的数据类型。

如果数据类型错误,可以使用DataFrame.astype方法将数据类型从一种格式转换为另一种格式。例如,对“价格”列使用astype('int'),可以将object列转换为整数类型变量。

代码示例如下:
# 检查数据类型
print(df.dtypes)
# 转换数据类型
df['price'] = df['price'].astype('int')
总结

本节课中我们一起学*了数据格式化的核心概念。我们探讨了数据表达不一致的问题,学*了如何使用Pandas进行数据单位的转换,并掌握了识别与修正错误数据类型的方法。这些步骤是数据清洗和预处理的关键环节,能确保数据质量,为后续的建模和分析打下坚实基础。
生成式人工智能工程:039:在Python中实现数据归一化 📊
在本节课中,我们将要学*数据预处理中的一项重要技术——数据归一化。我们将了解其重要性、常见方法,并通过Python代码示例展示如何实现。
概述
数据归一化是数据预处理的关键步骤,旨在将不同特征(变量)的数值范围调整到一致的尺度。这有助于确保在后续的统计分析和机器学*建模中,所有特征具有可比的影响力,避免因数值量级差异而引入的偏差。
为什么需要数据归一化?🤔
观察一个二手车数据集,我们发现特征“长度”的取值范围在150到250之间,而特征“宽度”和“高度”的取值范围则在50到100之间。为了让这些变量的取值范围保持一致,我们可能需要进行归一化。
这种归一化处理通过统一变量间的数值范围,可以使后续的统计分析更为简便。它确保了不同特征之间能够进行更公平的比较。

确保所有特征具有相同的影响力,这对于计算过程也至关重要。


另一个例子能帮助你更好地理解归一化的重要性。考虑一个包含“年龄”和“收入”两个特征的数据集,其中年龄范围是0到100岁,而收入范围则是0到20,000及以上。

收入的值大约是年龄的1000倍,其范围可能在20,000到500,000之间。因此,这两个特征处于完全不同的数值量级。当我们进行进一步分析(例如线性回归)时,由于“收入”特征的数值更大,它本质上会对结果产生更大的影响。但这并不一定意味着它作为一个预测因子就更重要。
数据的这种特性会使线性回归模型偏向于更重视“收入”而非“年龄”。

为了避免这种情况,我们可以将这两个变量归一化到0到1的范围内。比较右侧归一化后的两个表格,现在两个变量对我们后续要构建的模型具有相似的影响力。
数据归一化的主要方法 📝
有多种方法可以对数据进行归一化。以下将概述三种常用技术。
1. 简单特征缩放
第一种方法称为简单特征缩放,它将每个值除以该特征的最大值。
公式:
X_new = X_old / X_max


这使得新值的范围介于0和1之间。
2. 最小-最大归一化
第二种方法称为最小-最大归一化。对于每个原始值 X_old,减去该特征的最小值,然后除以该特征的范围(最大值减最小值)。
公式:
X_new = (X_old - X_min) / (X_max - X_min)
同样,得到的新值范围在0到1之间。
3. Z分数标准化

第三种方法是Z分数标准化(或标准分数)。在这个公式中,对于每个值,你减去该特征的平均值 μ,然后除以该特征的标准差 σ。
公式:
X_new = (X_old - μ) / σ

得到的值围绕0分布,通常范围在-3到+3之间,但也可能更高或更低。
在Python中实现归一化 💻
承接之前的例子,我们可以对“长度”特征应用归一化方法。
应用简单特征缩放
我们使用简单特征缩放方法,用pandas的 .max() 方法获取特征最大值,然后将每个值除以该最大值。

代码示例:
df[‘length_normalized_simple‘] = df[‘length‘] / df[‘length‘].max()


这只需一行代码即可完成。
应用最小-最大归一化

以下是对“长度”特征应用最小-最大方法。我们减去该列的最小值,然后除以其范围(最大值减最小值)。

代码示例:
df[‘length_normalized_minmax‘] = (df[‘length‘] - df[‘length‘].min()) / (df[‘length‘].max() - df[‘length‘].min())


应用Z分数标准化

最后,我们对“长度”特征应用Z分数方法来归一化数值。这里,我们对长度特征应用 .mean() 和 .std() 方法。

代码示例:
df[‘length_normalized_zscore‘] = (df[‘length‘] - df[‘length‘].mean()) / df[‘length‘].std()


.mean() 方法将返回数据集中该特征的平均值,而 .std() 方法将返回数据集中该特征的标准差。

总结

本节课中,我们一起学*了数据归一化的核心概念。我们了解到,归一化通过将不同量级的特征调整到相*的尺度,可以消除因数值范围差异带来的模型偏差,确保特征间的公平比较。我们介绍了三种主要的归一化方法:简单特征缩放、最小-最大归一化和Z分数标准化,并分别给出了它们的数学公式。最后,我们使用Python的pandas库,通过具体的代码示例演示了如何对数据集中的“长度”特征应用这三种方法。掌握数据归一化是构建高效、准确机器学*模型的重要预处理步骤。
生成式人工智能工程:040:在Python中实现数据分箱
在本节课中,我们将学*数据预处理中的一种重要方法——分箱。我们将探讨分箱的概念、作用,并学*如何在Python中实现等宽分箱,最后通过可视化来观察分箱后的数据分布。
什么是分箱?
分箱是指将一系列数值分组到不同的“箱子”中的过程。例如,可以将年龄分为0-5岁、6-10岁、11-15岁等区间。
分箱有时能提高预测模型的准确性。此外,我们使用数据分箱将一组数值归入数量更少的箱子中,以便更好地理解数据分布。
分箱示例
以一个价格属性为例,其范围从5000到45500。通过分箱,我们将价格分为三个类别:低价、中价和高价。

在实际的汽车数据集中,价格是一个数值变量,范围从5188到45400,共有201个不同的值。我们可以将它们分为三个箱子:低价车、中价车和高价车。

在Python中实现分箱
在Python中,我们可以轻松实现分箱。我们希望创建三个等宽的箱子,因此需要四个等间距的数字作为分隔点。
首先,我们使用NumPy的linspace函数来生成一个数组bins,该数组包含在指定价格区间内均匀分布的四个数字。
import numpy as np
import pandas as pd
# 假设 price_data 是包含价格的数据列
max_price = price_data.max()
min_price = price_data.min()
bins = np.linspace(min_price, max_price, 4)
接着,我们创建一个列表group_names,其中包含不同箱子的名称。
group_names = [‘Low‘, ‘Medium‘, ‘High‘]
然后,我们使用Pandas的cut函数将数据值分段并排序到各个箱子中。
price_data_binned = pd.cut(price_data, bins, labels=group_names, include_lowest=True)


可视化分箱结果
分箱完成后,你可以使用直方图来可视化数据在分箱后的分布情况。
以下是我们根据应用于价格特征的分箱结果绘制的直方图。从图中可以清楚地看出,大多数汽车属于低价类别,只有极少数汽车属于高价类别。

总结

本节课中,我们一起学*了数据分箱的概念及其在数据预处理中的作用。我们通过一个具体的价格分箱示例,演示了如何在Python中使用NumPy和Pandas库实现等宽分箱,并利用直方图对分箱结果进行了可视化分析。掌握分箱技术有助于你更好地理解和处理连续型数据。
生成式人工智能工程:041:在Python中将分类变量转换为定量变量 🧮
在本节课中,我们将学*如何在Python中将分类变量转换为定量变量。这是数据预处理中的一个关键步骤,因为大多数统计模型和机器学*算法只能处理数值型数据。

为什么需要转换?
上一节我们介绍了数据预处理的重要性,本节中我们来看看一个具体问题:分类变量的处理。
大多数统计模型无法将对象或字符串作为输入进行模型训练,它们只能接受数字作为输入。在汽车数据集中,“燃料类型”这个特征是一个分类变量,它有两个值:“汽油”或“柴油”,它们都是字符串格式。为了进行进一步的分析,Jerry必须将这些变量转换为某种数字格式。

什么是独热编码?
为了解决上述问题,我们引入一种称为“独热编码”的技术。
我们通过为原始特征中每个唯一的元素添加新的特征来进行编码。在“燃料类型”这个特征有两个唯一值(“汽油”和“柴油”)的情况下,我们创建两个新特征:“汽油”和“柴油”。当某个值在原始特征中出现时,我们在新特征中将对应的值设为1,其余特征则设为0。
以下是具体示例:
- 对于汽车B,燃料值是“柴油”。因此,我们将“柴油”特征设为1,“汽油”特征设为0。
- 对于汽车D,燃料值是“汽油”。因此,我们将“汽油”特征设为1,“柴油”特征设为0。
在Pandas中实现转换
在Python中,使用Pandas库将分类变量转换为虚拟变量非常简单。
我们可以使用 get_dummies() 方法。该方法会自动生成一组数字,每个数字对应变量的一个特定类别。

以下是具体操作步骤:
- 导入Pandas库。
- 加载包含分类变量的数据集。
- 对目标列(例如“燃料类型”)应用
pd.get_dummies()方法。
示例代码如下:
import pandas as pd
# 假设 df 是包含‘fuel_type’列的DataFrame
dummy_variables_df = pd.get_dummies(df['fuel_type'])
执行上述代码后,dummy_variables_df 将是一个新的DataFrame,其中包含像“fuel_type_gas”和“fuel_type_diesel”这样的列,每列的值由0和1组成。
总结

本节课中我们一起学*了数据预处理的关键一步:将分类变量转换为定量变量。我们了解到,由于机器学*模型需要数值输入,因此必须进行这种转换。我们重点介绍了“独热编码”的原理,并演示了如何使用Pandas的 get_dummies() 方法在Python中轻松实现这一转换。掌握这项技能是构建有效AI模型的重要基础。
生成式人工智能工程:042:探索性数据分析
在本节课中,我们将学*如何使用Python进行探索性数据分析的基础知识。
探索性数据分析,简称EDA,是一种分析数据的方法,旨在总结数据的主要特征、更好地理解数据集、揭示不同变量之间的关系,并为我们试图解决的问题提取重要变量。
本模块试图回答的核心问题是:哪些特征对汽车价格的影响最大?我们将通过几种不同的、有用的探索性数据分析技术来回答这个问题。
在本模块中,你将学*描述性统计,它描述数据集的基本特征,并获取样本和数据的简要摘要。你将学*使用group by进行数据分组的基础知识,以及这如何帮助转换我们的数据集。你将学*不同变量之间的相关性。最后,你将学*高级相关性分析,我们将向你介绍各种相关性统计方法,即皮尔逊相关性和相关性热图。

描述性统计 📊
上一节我们介绍了EDA的目标,本节中我们来看看描述性统计。描述性统计用于总结和描述数据集的基本特征,它提供关于数据集中趋势、离散度和分布的快速概览。
以下是常用的描述性统计方法:
- 均值:所有数据点的平均值。公式为:
mean = sum(x) / n - 中位数:将数据集按大小排序后位于中间的值。
- 众数:数据集中出现频率最高的值。
- 标准差:衡量数据点相对于均值的离散程度。公式为:
std = sqrt( sum( (x - mean)^2 ) / (n-1) ) - 分位数:将数据划分为相等部分的值,例如四分位数。
在Python中,我们可以使用Pandas库轻松计算这些统计量。
import pandas as pd
# 假设df是一个Pandas DataFrame
summary = df.describe()
print(summary)
数据分组 🔍
了解了数据的整体特征后,我们常常需要按特定类别对数据进行分组分析。group by操作是数据分析中一个强大的工具,它允许我们根据一个或多个键将数据拆分成组,然后对每个组应用聚合函数。
以下是group by的常见用途:
- 按类别计算汇总统计量(如不同品牌汽车的平均价格)。
- 执行组内转换或过滤。
- 将数据重塑为更易于分析的格式。
在Pandas中,groupby()方法结合聚合函数(如mean()、sum()、count())可以实现此功能。
# 按‘brand’列分组,并计算‘price’列的平均值
grouped_data = df.groupby(['brand'])['price'].mean()
print(grouped_data)
变量相关性分析 🔗

上一节我们学会了如何分组观察数据,本节中我们来看看变量之间的关系。相关性分析用于衡量两个变量之间线性关系的强度和方向。理解相关性有助于我们识别哪些特征可能与目标变量(如汽车价格)密切相关。
相关性系数通常在-1到1之间:
- 1 表示完全正相关。
- -1 表示完全负相关。
- 0 表示没有线性相关性。
在Python中,我们可以使用Pandas计算数据框中所有数值列之间的相关性矩阵。
# 计算相关性矩阵
correlation_matrix = df.corr()
print(correlation_matrix)
高级相关性分析:方法与可视化 🧮
基础的相关系数矩阵提供了大量信息,但通过统计方法和可视化技术可以更深入地理解相关性。我们将介绍皮尔逊相关系数和相关性热图。
皮尔逊相关系数衡量两个连续变量之间的线性相关性。其公式为:
r = cov(X, Y) / (σ_X * σ_Y)
其中,cov是协方差,σ是标准差。
相关性热图是一种使用颜色编码矩阵来可视化相关性矩阵的有效方法,可以快速识别强相关或弱相关的变量对。
以下是创建相关性热图的步骤:
- 使用
df.corr()计算皮尔逊相关性矩阵。 - 使用
seaborn库的heatmap()函数进行可视化。 - 通过颜色深浅直观判断相关性强度。
import seaborn as sns
import matplotlib.pyplot as plt
# 计算相关性矩阵
corr = df.corr()
# 绘制热图
plt.figure(figsize=(10, 8))
sns.heatmap(corr, annot=True, cmap='coolwarm', center=0)
plt.title('Correlation Heatmap')
plt.show()

总结
本节课中我们一起学*了探索性数据分析的基础知识。我们首先了解了EDA的目标是理解数据、发现关系和提取重要特征。接着,我们学*了使用描述性统计来概括数据的基本特征。然后,我们探讨了如何使用group by对数据进行分类汇总。最后,我们深入研究了变量间的相关性,从基础的相关性矩阵计算到高级的皮尔逊相关系数和可视化热图。掌握这些技术,将帮助你有效地分析数据,并为后续的建模工作打下坚实基础。
生成式人工智能工程:043:描述性统计 📊
在本节课中,我们将学*描述性统计。在开始分析数据时,首先探索数据非常重要,这有助于在构建复杂模型之前理解数据的基本特征。描述性统计分析旨在描述数据集的基本特征,获取样本的简要概括和数据度量。
探索数值变量
上一节我们介绍了描述性统计的重要性,本节中我们来看看如何使用具体方法探索数据。一种简单的方法是使用 pandas 库中的 describe 函数。
以下是使用 describe 函数的基本步骤:

- 将
describe函数应用于你的数据框(DataFrame)。 - 该函数会自动计算所有数值变量的基本统计量。
import pandas as pd
# 假设 df 是你的数据框
df.describe()

describe 函数会显示以下统计信息:均值、数据点总数、标准差、四分位数以及极值。在这些统计计算中,任何 NaN 值都会被自动跳过。这个函数能让你更清晰地了解不同变量的分布情况。
探索分类变量
除了数值变量,数据集中也可能包含分类变量。这些变量可以被划分为不同的类别或组,并具有离散值。例如,在我们的数据集中,“驱动系统”就是一个分类变量,包含“前轮驱动”、“后轮驱动”和“四轮驱动”等类别。
以下是总结分类数据的一种方法:

- 使用
value_counts函数。 - 可以更改列名以便于阅读。
# 统计‘drive_system’列中各类别的数量
drive_counts = df['drive_system'].value_counts()
drive_counts.name = ‘车辆数量’ # 重命名以便阅读
print(drive_counts)
通过这种方式,我们可以看到前轮驱动类别有118辆车,后轮驱动有75辆,四轮驱动有8辆。
使用箱线图可视化数据分布
箱线图是可视化数值数据的绝佳方式,因为它可以展示数据的各种分布特征。箱线图显示的主要特征包括:

- 中位数:代表中间数据点的位置。
- 上四分位数:显示第75百分位数的位置。
- 下四分位数:显示第25百分位数的位置。
- 四分位距:上四分位数与下四分位数之间的数据范围。
- 上下极端值:分别计算为第75百分位数以上1.5倍IQR和第25百分位数以下1.5倍IQR的范围。
- 异常值:出现在上下极端值之外的单个点。
通过箱线图,可以轻松识别异常值,并观察数据的分布和偏度。
比较组间差异

箱线图使得组间比较变得容易。例如,我们可以使用箱线图查看“驱动车轮”特征的不同类别在“价格”特征上的分布。通过图表,我们可以发现后轮驱动与其他类别的价格分布有明显区别,而前轮驱动和四轮驱动的价格分布几乎无法区分。
分析连续变量之间的关系
数据中经常出现连续变量,这些数据点是某个范围内的数字。例如,在我们的数据集中,“价格”和“发动机排量”就是连续变量。如果我们想理解发动机排量与价格之间的关系呢?发动机排量能否预测汽车的价格?
一个好的可视化方法是使用散点图。散点图中的每个观测值都表示为一个点,用于展示两个变量之间的关系。
- 预测变量:用于预测结果的变量。在本例中,我们的预测变量是发动机排量。
- 目标变量:你试图预测的变量。在本例中,我们的目标变量是价格。
在散点图中,通常将预测变量设置在X轴(水平轴),将目标变量设置在Y轴(垂直轴)。

import matplotlib.pyplot as plt
plt.scatter(x=df['engine_size'], y=df['price'])
plt.xlabel(‘发动机排量’) # 标注X轴
plt.ylabel(‘价格’) # 标注Y轴
plt.title(‘发动机排量与价格关系散点图’) # 添加图表标题
plt.show()
因此,我们将发动机排量绘制在X轴,价格绘制在Y轴。从散点图中,我们可以看到随着发动机排量增大,汽车价格也趋于上升。这初步表明这两个变量之间存在正线性关系。
总结

本节课中我们一起学*了描述性统计的核心概念和方法。我们介绍了如何使用 describe 函数快速获取数值变量的统计摘要,如何使用 value_counts 总结分类变量,以及如何利用箱线图可视化数据分布、识别异常值和比较组间差异。最后,我们探讨了如何使用散点图来探索和可视化两个连续变量(如发动机排量与价格)之间的潜在关系,从而为后续的建模分析奠定基础。
生成式人工智能工程:044:Python中的Groupby操作 🧮
在本节课中,我们将要学*如何使用Pandas库中的groupby方法对数据进行分组,以及如何通过数据透视表和热力图来更直观地分析和展示分组结果。这些操作是数据分析和预处理中的基础技能。
概述
假设我们想探究不同类型的驱动系统(如前驱、后驱、四驱)与车辆价格之间是否存在关系。如果存在,哪种驱动系统能为车辆带来最高的价值。为了解答这个问题,我们需要将数据按照驱动系统的不同类型进行分组,并比较各组的结果。
使用Groupby方法分组数据
在Pandas中,这可以通过groupby方法实现。groupby方法作用于分类变量,它能将数据根据该变量的不同类别划分成多个子集。你可以按单个变量分组,也可以通过传入多个变量名来按多个变量分组。
例如,我们想找出车辆的平均价格,并观察它们在不同车身样式和驱动系统类型之间的差异。以下是实现步骤:
首先,我们选取感兴趣的三个数据列,这由第一行代码完成。接着,在第二行代码中,我们根据“驱动系统”和“车身样式”对筛选后的数据进行分组。
由于我们关注的是平均价格在不同类别间的差异,我们可以在代码行的末尾追加.mean()方法来计算每个分组的平均值。
# 示例代码
grouped_data = df[['drive-wheels', 'body-style', 'price']].groupby(['drive-wheels', 'body-style']).mean()
现在,数据被分组到各个子类别中,并且只显示每个子类别的平均价格。根据我们的数据,后轮驱动的敞篷车和硬顶车价值最高,而四轮驱动的掀背车价值最低。
创建数据透视表

上述形式的表格并不易于阅读和可视化。为了更容易理解,我们可以使用pivot方法将此表格转换为数据透视表。
在之前的表格中,“驱动系统”和“车身样式”都列在列中。数据透视表则将一个变量沿列显示,另一个变量沿行显示。只需一行代码,并使用Pandas的pivot方法,我们就可以将“车身样式”变量设置为沿列显示,而“驱动系统”沿行显示。

# 示例代码
pivot_table = grouped_data.pivot(index='drive-wheels', columns='body-style')

价格数据现在变成了一个矩形网格,更易于可视化。这类似于在Excel电子表格中通常进行的操作。
使用热力图进行可视化
另一种表示数据透视表的方法是使用热力图。热力图接收一个矩形数据网格,并根据网格点上的数据值为其分配颜色强度。这是一种在多个变量上绘制目标变量的绝佳方式,通过它可以获得这些变量与目标变量之间关系的视觉线索。
在本例中,我们使用Matplotlib的pcolor方法来绘制热力图,并将之前的数据透视表转换为图形形式。我们指定了红-蓝配色方案。
在输出图中,每种车身样式类型沿X轴编号,每种驱动系统类型沿Y轴编号。平均价格根据其值以不同颜色绘制。根据颜色条,我们看到热力图的上半部分似乎价格较高,而下半部分价格较低。
总结


本节课中,我们一起学*了如何使用Pandas的groupby方法对数据进行分组分析,如何将分组结果转换为更清晰的数据透视表,以及如何利用热力图进行直观的可视化。这些技能对于探索数据中的模式和关系至关重要。
生成式人工智能工程:045:相关性分析 📊
在本节课中,我们将学*相关性分析。相关性是统计学中用于衡量不同变量之间相互依赖程度的指标。理解相关性有助于我们分析数据中变量之间的关系,为后续的建模和预测打下基础。
什么是相关性?
相关性是一种统计度量,用于衡量不同变量在多大程度上相互依赖。换句话说,当我们观察两个变量随时间的变化时,如果一个变量发生变化,另一个变量会如何随之变化。
例如,吸烟与肺癌之间存在相关性,因为吸烟会增加患肺癌的几率。另一个例子是雨伞和降雨量之间的相关性:降雨量越大,使用雨伞的人就越多;反之,如果不下雨,人们就不会携带雨伞。因此,我们可以说雨伞和降雨量是相互依赖的,根据定义,它们是相关的。
需要明确的是,相关性并不意味着因果关系。例如,虽然雨伞和降雨量相关,但我们没有足够的信息断定是雨伞导致了降雨,还是降雨导致了雨伞的使用。在数据科学中,我们通常更多地处理相关性。
相关性分析示例
上一节我们介绍了相关性的基本概念,本节中我们来看看如何通过具体示例分析变量间的相关性。
以下是使用散点图和回归线分析变量间关系的示例。
发动机排量与价格的相关性
我们首先分析发动机排量与汽车价格之间的关系。通过散点图并添加一条称为回归线的直线,可以直观展示两者间的关系。




该图的主要目的是观察发动机排量是否对价格有影响。在此示例中,穿过数据点的直线非常陡峭,这表明两个变量之间存在正的线性关系:随着发动机排量值的增加,价格值也随之上升,且直线的斜率为正。因此,发动机排量与价格之间存在正相关。
我们可以使用 Seaborn 库的 regplot 函数创建此类散点图。
import seaborn as sns
sns.regplot(x='engine-size', y='price', data=df)

高速公路油耗与价格的相关性

接下来,我们分析高速公路每加仑英里数与汽车价格的关系,以观察其对价格的影响。

从图中可以看出,当高速公路每加仑英里数值上升时,价格值下降。因此,高速公路每加仑英里数与价格之间存在负的线性关系。尽管这种关系是负相关的,但直线的斜率仍然较陡,这意味着高速公路每加仑英里数仍然是预测价格的良好指标。这两个变量被称为具有负相关。
弱相关性示例
最后,我们来看一个弱相关性的例子。例如,峰值转速与价格之间的关系。



从图中可见,无论是低峰值转速还是高峰值转速,都对应着低价格和高价格。因此,我们无法使用峰值转速来有效预测价格值。这表明两者之间存在弱相关性或几乎不相关。
总结


本节课中我们一起学*了相关性分析。我们了解到相关性是衡量变量间相互依赖程度的统计指标,但需注意它不意味着因果关系。通过具体示例,我们观察了正相关、负相关以及弱相关的不同表现形式,并学会了如何使用散点图和回归线来可视化这些关系。掌握相关性分析是理解数据、构建有效预测模型的重要基础。
生成式人工智能工程:046:相关性统计方法
在本节课中,我们将学*几种用于衡量变量之间关联性的相关性统计方法。
概述
相关性统计用于量化两个或多个变量之间的关系强度和方向。理解相关性是数据分析与机器学*中的重要步骤,它能帮助我们识别特征之间的关联,并为模型构建提供依据。
皮尔逊相关系数
上一节我们介绍了相关性统计的概念,本节中我们来看看最常用的方法之一:皮尔逊相关系数。皮尔逊相关系数用于衡量两个连续数值变量之间的线性相关程度。
计算皮尔逊相关系数会得到两个主要数值:相关系数 和 P值。
以下是这两个数值的解释方法:
相关系数:
- 值接* +1 表示存在强正相关。
- 值接* -1 表示存在强负相关。
- 值接* 0 表示变量之间无线性相关。
P值:
P值用于评估我们计算出的相关系数的可靠性(确定性)。
- P值 < 0.001:对计算出的相关系数有强确定性。
- 0.001 ≤ P值 < 0.05:有中等确定性。
- 0.05 ≤ P值 < 0.1:有弱确定性。
- P值 ≥ 0.1:无法确定存在相关性。
综合来看,当相关系数接*1或-1,并且P值小于0.001时,我们可以说变量之间存在强相关性。
实例分析
理解了基本概念后,我们通过一个具体例子来看如何应用。以下图表展示了具有不同相关系数值的数据分布。

假设我们想分析汽车“马力”与“价格”两个变量之间的相关性。使用Python的scipy.stats包可以轻松计算皮尔逊相关系数。
# 示例代码:使用scipy.stats计算皮尔逊相关系数和P值
from scipy import stats
correlation_coefficient, p_value = stats.pearsonr(horsepower, car_price)

计算结果显示,相关系数约为0.8(接*1),表明存在强正相关。同时,P值非常小(远小于0.001),因此我们可以确信这种强正相关关系是 statistically significant 的。
相关性热力图
在分析多个变量时,逐一查看两两关系效率较低。此时,相关性热力图能提供一个全局视角。
以下是综合考虑所有变量后生成的热力图,它展示了每对变量之间的皮尔逊相关系数。颜色深浅代表相关性的强弱。

我们可以观察到一条深红色的对角线。这条线上的值均为1,代表每个变量与自身的完全相关,这符合逻辑。这张热力图清晰地概括了不同变量之间的相互关系,最重要的是,它直观地展示了各个变量与目标变量(如“价格”)的相关性。

总结
本节课中我们一起学*了相关性统计的核心方法。我们重点介绍了皮尔逊相关系数,它通过相关系数(表示关系强度和方向)和P值(表示关系的确定性)来描述两个连续变量间的线性关系。最后,我们探讨了使用相关性热力图来可视化多个变量间复杂关系的有效方法。掌握这些工具对于后续的数据分析、特征工程和模型理解至关重要。
统计学:047:卡方检验 🧮
在本节课中,我们将学*如何判断两个分类变量之间是否存在关联关系。
当处理两个分类变量之间的关系时,我们不能使用连续变量那样的相关性分析方法。我们需要使用卡方检验来检验关联性。
卡方检验旨在检验观察到的分布是否可能由随机因素导致。它衡量观察到的数据分布与变量独立时的预期分布的拟合程度。
在进入具体例子之前,我们先了解一些重要概念。
核心概念
卡方检验的原假设是变量之间相互独立。该检验将观察到的数据与变量独立时模型预期的数值进行比较。每当观察数据与预期值模型不符时,变量之间存在依赖关系的可能性就越大,从而证明原假设不成立。
卡方检验不能告诉你变量之间存在何种类型的关系,只能告诉你是否存在关系。
示例:汽车燃料类型与吸气方式

我们将使用一个汽车数据集,假设我们想检验燃料类型与吸气方式之间的关系。这两个都是分类变量:燃料类型是汽油或柴油;吸气方式是标准或涡轮增压。
以下是分析步骤:
第一步:创建列联表
首先,我们需要获取每个类别中汽车的观察计数。这可以通过使用pandas库创建交叉表(列联表)来实现。列联表是展示两个或多个变量之间关系的表格。
import pandas as pd
# 假设df是包含'fuel_type'和'aspiration'列的DataFrame
contingency_table = pd.crosstab(df['fuel_type'], df['aspiration'])
print(contingency_table)
这将生成一个表格,显示四个类别的计数:标准柴油车、标准汽油车、涡轮柴油车、涡轮汽油车。
第二步:计算卡方统计量
卡方统计量的计算公式如下:
χ² = Σ [ (观测值 - 期望值)² / 期望值 ]
其中:
- 观测值:列联表中每个单元格的实际计数。
- 期望值:在变量独立的假设下,每个单元格的理论预期计数。
期望值的计算基于行总计和列总计。例如,对于“标准柴油车”单元格,其期望值计算公式为:
期望值 = (该行总计 × 该列总计) / 总计
在我们的例子中,计算过程如下:
- 标准柴油车期望值 = (20 × 168) / 205 ≈ 16.39
- 涡轮汽油车期望值 = (185 × 37) / 205 ≈ 33.39

对所有单元格重复此计算,得到完整的期望值表。
第三步:进行假设检验
将观测值和计算出的期望值代入卡方公式,求和后得到卡方统计量(本例中计算值为29.6)。
然后,我们需要确定这个统计量对应的P值。这需要用到自由度,对于列联表,自由度的计算公式为:

自由度 = (行数 - 1) × (列数 - 1)
在我们的2x2表格中,自由度为1。
我们可以查阅卡方分布表,找到自由度为1时,统计量29.6对应的P值范围。29.6远大于临界值,对应的P值远小于0.05。

决策规则:如果P值小于显著性水平(通常为0.05),我们拒绝原假设,认为变量间存在关联。
使用Python实现
上一节我们手动计算了卡方统计量,本节中我们来看看如何使用Python快速完成整个检验。
我们可以使用scipy.stats包中的chi2_contingency函数。
from scipy.stats import chi2_contingency
# 使用前面创建的列联表 contingency_table
chi2, p_value, dof, expected = chi2_contingency(contingency_table)
print(f"卡方统计量: {chi2}")
print(f"P值: {p_value}")
print(f"自由度: {dof}")
print("期望频数表:")
print(expected)

该函数会输出:
- 卡方检验值(与我们手动计算的29.6一致)。
- P值(一个非常接*0的精确值,而查表只能得到范围)。
- 自由度(为1)。
- 期望频数表(与我们手动计算的结果一致)。
由于P值接*0(<0.05),我们拒绝“燃料类型与吸气方式独立”的原假设,得出结论:有证据表明汽车的燃料类型与吸气方式之间存在关联。

总结

本节课中我们一起学*了卡方检验。我们了解到卡方检验是用于分析两个分类变量之间是否关联的统计方法。其核心步骤包括:建立列联表、计算期望频数、套用公式计算卡方统计量、根据自由度和P值做出统计决策。最后,我们还掌握了如何使用Python的scipy.stats.chi2_contingency函数来高效、精确地执行整个检验流程。记住,卡方检验只能判断关联是否存在,而不能说明关联的强度或方向。
生成式人工智能工程:048:模型开发 🚗
在本节课中,我们将通过尝试使用数据集预测汽车价格,来深入探讨模型开发。你将学*简单线性回归、多元线性回归、使用可视化进行模型评估、多项式回归和用于样本内评估的均方误差(MSE)管道、预测与决策制定,以及如何为一辆二手车确定一个公平的价值。
什么是模型?🧮
上一节我们介绍了课程概述,本节中我们来看看模型的基本概念。

模型或估计器可以被视为一个数学方程,用于在给定一个或多个其他值的情况下预测一个值。它将一个或多个自变量(或特征)与因变量关联起来。例如,你输入一个汽车型号的高速公路每加仑英里数作为自变量或特征,模型的输出(即因变量)就是价格。
公式表示:价格 = f(高速公路MPG)
更多数据的重要性 📊
通常,你拥有的相关数据越多,你的模型就越准确。例如,你可以向模型输入多个自变量或特征。因此,你的模型可能会更准确地预测汽车的价格。

公式表示:价格 = f(特征1, 特征2, ..., 特征n)
为了理解为什么更多数据很重要,请考虑以下情况:你有两辆几乎完全相同的汽车,但粉色汽车的售价明显更低。
特征选择的影响 🎨

假设你想使用你的模型来确定两辆汽车(一辆粉色,一辆红色)的价格。如果你的模型的自变量或特征中不包含颜色,那么你的模型将为这两辆可能售价差异很大的汽车预测相同的价格。

这个例子说明,遗漏关键特征会导致模型预测不准确。
模型的类型 🔄
除了获取更多数据,你还可以尝试不同类型的模型。在本课程中,你将学*以下内容:

以下是本课程将涵盖的几种回归模型:
- 简单线性回归:使用一个特征来预测目标。
- 多元线性回归:使用多个特征来预测目标。
- 多项式回归:用于捕捉特征与目标之间的非线性关系。
总结 📝

本节课中我们一起学*了模型开发的基础。我们了解到,模型是一个用于预测的数学方程,其准确性依赖于充足且相关的特征数据。特征的选择至关重要,遗漏重要信息(如汽车颜色)会导致预测偏差。最后,我们介绍了几种将在后续深入学*的回归模型类型,为接下来的具体技术学*奠定了基础。
生成式人工智能工程:049:线性回归与多元线性回归 📈
在本节课中,我们将学*线性回归与多元线性回归的基本概念。这两种方法是理解变量间关系并进行预测的基础工具。我们将探讨它们的定义、应用场景、数学模型以及在Python中的实现方法。

概述
线性回归用于分析一个自变量与一个因变量之间的线性关系。多元线性回归则扩展了这一概念,用于分析多个自变量与一个因变量之间的关系。本节将详细介绍这两种方法的核心思想、公式表示及实践步骤。

简单线性回归
简单线性回归(Simple Linear Regression, SLR)是一种帮助我们理解两个变量之间关系的方法。这两个变量分别是预测变量(自变量X)和目标变量(因变量Y)。我们的目标是找到这两个变量之间的线性关系。
线性关系的数学表达式为:
Y = B₀ + B₁X
其中,参数 B₀ 是截距,参数 B₁ 是斜率。当我们拟合或训练模型时,会计算出这些参数。这一步涉及大量数学运算,因此我们在此不深入讨论。让我们重点理解预测步骤。
预测步骤示例
确定一辆汽车的价格很困难,但高速公路每加仑英里数(highway miles per gallon)可以在车主手册中找到。如果我们假设这两个变量之间存在线性关系,就可以利用这种关系建立一个模型来预测汽车价格。
例如,如果高速公路每加仑英里数为20,我们可以将这个值输入模型,得到预测价格为22000美元。
模型拟合过程

为了确定最佳拟合线,我们从数据集中选取数据点(图中用红色标出)。然后,我们使用这些训练点来拟合模型。拟合训练点的结果就是模型的参数。
数据结构
我们通常将数据点存储在DataFrame或NumPy数组中。要预测的值称为目标,存储在数组 Y 中。自变量存储在DataFrame或数组 X 中。每个样本对应每个DataFrame或数组中的不同行。

在许多情况下,多个因素会影响人们为汽车支付的价格,例如品牌或车龄。在这种模型中,通过假设在直线上的点添加一个小的随机值来考虑这种不确定性,这个随机值称为噪声。
左图显示了噪声的分布。纵轴表示添加的值,横轴表示添加该值的概率。通常,会添加一个小的正值或小的负值。有时也会添加较大的值,但在大多数情况下,添加的值接*0。
过程总结
我们可以将整个过程总结如下:我们有一组训练点,使用这些训练点来拟合或训练模型,并得到参数。然后,我们在模型中使用这些参数。现在,我们有了一个模型。我们用 Ŷ 表示模型是一个估计值。
我们可以使用这个模型来预测未见过的值。例如,我们没有高速公路每加仑英里数为20的汽车数据,但可以使用模型来预测这辆汽车的价格。但请注意,我们的模型并不总是正确的。
通过比较预测值与实际值,我们可以看到这一点。例如,我们有一个高速公路每加仑英里数为10的样本,但预测值与实际值不匹配。如果线性假设正确,这种误差是由于噪声引起的,但也可能有其他原因。
在Python中实现简单线性回归
上一节我们介绍了简单线性回归的理论基础,本节中我们来看看如何在Python中实现它。
实现步骤

以下是使用Python和scikit-learn库拟合简单线性回归模型的步骤。
首先,从scikit-learn导入线性模型模块:
from sklearn import linear_model
然后,使用构造函数创建一个线性回归对象:
lm = linear_model.LinearRegression()
接着,定义预测变量和目标变量,并使用fit方法拟合模型以找到参数B₀和B₁。输入是特征和目标:
lm.fit(X, Y)
我们可以使用predict方法获得预测值,输出是一个数组,其样本数量与输入X相同:
yhat = lm.predict(X)

获取模型参数
截距B₀是对象lm的一个属性,斜率B₁也是对象lm的一个属性:

B0 = lm.intercept_
B1 = lm.coef_
价格与高速公路每加仑英里数之间的关系由以下方程给出:
价格 = 38423.31 - 821.73 × 高速公路每加仑英里数
这个方程与我们之前讨论的形式相同。

多元线性回归
在理解了简单线性回归之后,本节我们将探讨多元线性回归。多元线性回归用于解释一个连续目标变量Y与两个或更多预测变量X之间的关系。
数学模型

如果我们有四个预测变量,那么方程如下:
Y = B₀ + B₁X₁ + B₂X₂ + B₃X₃ + B₄X₄
其中,B₀ 是截距(当X等于0时),B₁ 是变量X₁的系数或参数,B₂ 是变量X₂的系数,依此类推。
如果只有两个变量,我们可以将数值可视化。考虑以下函数,变量X₁和X₂可以在二维平面上可视化。让我们在下一张幻灯片中看一个例子。
数据可视化示例
表格包含预测变量X₁和X₂的不同值。每个点的位置放置在二维平面上,并根据数值进行颜色编码。

每个预测变量X₁和X₂的值将被映射到一个新值Ŷ。Ŷ的新值在垂直方向上映射,高度与Ŷ的值成比例。
在Python中实现多元线性回归

我们可以按以下步骤拟合多元线性回归模型。首先,提取四个预测变量并将它们存储在变量Z中:
Z = df[['x1', 'x2', 'x3', 'x4']]
然后,像之前一样使用fit方法训练模型,输入是特征(因变量)和目标:

lm.fit(Z, Y)
我们也可以使用predict方法获得预测值。在这种情况下,输入是一个具有四列的数组或DataFrame,行数对应样本数量。输出是一个数组,其元素数量与样本数量相同:
yhat = lm.predict(Z)
获取模型参数
截距是对象的一个属性,系数也是对象的属性:
B0 = lm.intercept_
Coefficients = lm.coef_

将因变量名称替换为实际名称来可视化方程是有帮助的。这与我们之前讨论的形式相同。
总结

在本节课中,我们一起学*了线性回归与多元线性回归的核心概念。我们了解了简单线性回归用于分析两个变量之间的关系,而多元线性回归用于分析多个变量与一个目标变量之间的关系。我们探讨了它们的数学模型、预测过程、噪声的影响,并学*了如何在Python中使用scikit-learn库实现这两种回归方法。通过具体的例子和代码,我们掌握了从数据准备、模型训练到预测和参数获取的完整流程。这些基础知识是进一步学*更复杂机器学*模型的重要基石。
生成式人工智能工程:050:使用可视化进行模型评估 📊

在本节课中,我们将学*如何利用可视化工具来评估模型。可视化是理解模型性能、数据关系及误差模式的关键手段。我们将重点介绍三种核心的可视化方法:回归图、残差图和分布图。

回归图:理解变量关系 📈

上一节我们介绍了模型评估的重要性,本节中我们来看看如何通过回归图直观地理解两个变量之间的关系。回归图可以很好地估计两个变量之间的关系强度以及关系的方向(正相关或负相关)。
在回归图中,横轴代表自变量,纵轴代表因变量。图中的每个点代表一个不同的数据点,而拟合线则代表模型的预测值。

以下是使用Seaborn库绘制回归图的步骤:
- 首先,导入Seaborn库。
- 使用
regplot函数。- 参数
x指定包含自变量(特征)的列名。 - 参数
y指定包含因变量(目标)的列名。 - 参数
data指定数据框的名称。
- 参数
import seaborn as sns
sns.regplot(x='feature_column', y='target_column', data=df)

残差图:分析预测误差 🔍
了解了变量间的整体关系后,我们需要深入分析模型的预测误差。残差图正是用于表示预测值与实际值之间的误差。
我们通过从预测值中减去实际目标值来得到残差。在残差图中,我们将残差值绘制在纵轴上,自变量绘制在横轴上。通过观察残差图,我们可以获得关于数据的重要洞察。

我们期望看到的结果是:残差均值为0,围绕X轴均匀分布,且方差相似,没有明显的曲线模式。
- 理想的残差图:表明线性模型是合适的。
- 存在曲线的残差图:表明误差值随X变化。例如,在某些区域所有残差都为正,而在另一些区域则为负。这提示线性假设可能不正确,需要考虑非线性函数。
- 方差变化的残差图:如果残差的方差随X增加而增大,则表明我们的模型存在问题。

我们可以使用Seaborn来创建残差图。首先导入Seaborn,然后使用 residplot 函数。第一个参数是自变量序列,第二个参数是因变量序列。
import seaborn as sns
sns.residplot(x=feature_series, y=target_series)
分布图:比较预测值与实际值 📊

当我们处理具有多个自变量或特征的模型时,回归图和残差图可能不够直观。此时,分布图就变得极为有用。分布图用于比较预测值的分布与实际目标值的分布。

让我们看一个简化的例子:我们统计并绘制预测值大约等于1、2、3的数据点数量,同时对实际目标值(假设都约等于2)进行同样的操作。由于目标和预测值是连续值,而直方图适用于离散值,因此Pandas会将其转换为分布图,并将纵轴缩放以使分布下的面积总和为1。
这是一个使用分布图的示例:自变量是“价格”,模型得出的拟合值用蓝色表示,实际值用红色表示。通过对比,我们可以发现模型在价格区间40,000到50,000的预测不准确,而在10,000到20,000区间的预测则更接*目标值。
以下是创建分布图的代码。我们分别绘制实际值和预测值的分布,通过设置参数 hist=False 来获得平滑的分布曲线而非直方图,并为其指定颜色和标签。


import seaborn as sns
# 绘制实际值分布
sns.distplot(actual_values, hist=False, color='red', label='Actual')
# 绘制预测值分布
sns.distplot(predicted_values, hist=False, color='blue', label='Predicted')
总结

本节课中,我们一起学*了三种用于模型评估的核心可视化方法。回归图帮助我们理解特征与目标变量之间的整体关系;残差图让我们能够深入诊断预测误差的模式,判断模型假设是否合理;而分布图则是在多特征模型中直观比较预测分布与实际分布的强大工具。掌握这些可视化技术,将极大地提升你理解、诊断和改进模型的能力。
生成式人工智能工程:051:多项式回归与管道 📊


在本节课中,我们将学*多项式回归模型以及如何使用管道(Pipeline)来简化机器学*工作流程。当线性模型无法很好地拟合数据时,多项式回归提供了一种有效的解决方案。同时,管道能够将数据预处理和模型训练的多个步骤封装起来,使代码更简洁、更易维护。
多项式回归简介 🔍
上一节我们介绍了线性回归。当线性模型不是数据的最佳拟合时,我们可以探索另一种回归模型:多项式回归。
多项式回归通过将数据转换为多项式形式,然后使用线性回归来拟合参数。接下来,我们将讨论管道。管道是一种简化代码的方法。

什么是多项式回归? 📈
多项式回归是广义线性回归的一个特例。这种方法对于描述曲线关系非常有益。
什么是曲线关系?它通过将预测变量平方或设置为更高阶项来获得,即对数据进行变换。

多项式回归的阶数
模型可以是二次的,这意味着模型中的预测变量被平方。我们使用括号表示指数。这是一个二阶多项式回归,图示代表了该函数。
模型也可以是三次的,这意味着预测变量被立方。这是一个三阶多项式回归。通过观察图示,我们可以看到函数有更多的变化。
当二阶或三阶多项式未能达到良好拟合时,还存在更高阶的多项式回归。我们可以通过图示看到,当改变多项式回归的阶数时,图形变化有多大。
回归的阶数影响显著,如果选择正确的值,可以获得更好的拟合效果。在所有情况下,变量与参数之间的关系始终是线性的。
多项式回归示例 🐍
让我们看一个数据示例,我们在Python中生成一个多项式回归模型。

我们使用 polyfit 函数来实现。在这个例子中,我们建立了一个三阶多项式回归模型。我们可以打印出模型。
模型的符号形式由以下表达式给出:-1.557 x₁³ + 204.8 x₁² + 8965 x₁ + 1.37 × 10⁵。
多维多项式线性回归
我们也可以有多维多项式线性回归。表达式可能会变得复杂。

以下是二维二阶多项式的一些项。NumPy的 polyfit 函数无法执行这种类型的回归。
使用Scikit-Learn进行多项式特征变换

我们使用Scikit-Learn中的预处理库来创建一个多项式特征对象。构造函数将多项式的阶数作为参数。
然后,我们使用 fit_transform 方法将特征转换为多项式特征。
让我们做一个更直观的例子。考虑这里显示的特征。应用该方法,我们转换数据。现在我们得到了一组新的特征,它们是原始特征的转换版本。
随着数据维度的增加,我们可能需要在Scikit-Learn中标准化多个特征。相反,我们可以使用预处理模块来简化许多任务。例如,我们可以同时标准化每个特征。
我们导入 StandardScaler。我们训练对象并拟合缩放器对象。
数据标准化
然后将数据转换为新的数据框或数组 X_scaled。预处理库中还有更多可用的标准化方法,以及其他变换。

使用管道简化代码 🔄
我们可以使用管道库来简化代码。获得预测需要许多步骤,例如多项式变换、标准化和线性回归。

我们使用管道简化这个过程。管道按顺序执行一系列变换。最后一步进行预测。
创建管道
首先,我们导入所有需要的模块。

然后我们导入管道库。我们创建一个元组列表。元组中的第一个元素包含估计器模型的名称。第二个元素包含模型构造函数。我们将列表输入管道构造函数。

现在我们有了一个管道对象。
训练与预测

我们可以通过对管道对象应用训练方法来训练管道。我们也可以进行预测。该方法标准化数据,执行多项式变换,然后输出预测。
总结 🎯

本节课中,我们一起学*了多项式回归模型及其应用。我们了解到,当数据关系非线性时,多项式回归通过引入高阶项可以提供更好的拟合。同时,我们掌握了如何使用Scikit-Learn的管道功能,将数据预处理、特征变换和模型训练等多个步骤高效地组合在一起,从而简化代码并提高工作流程的可维护性。
生成式人工智能工程:052:样本内评估的度量 📊
在本节课中,我们将学*如何对模型进行数值化评估。我们将介绍两种重要的样本内评估指标:均方误差(MSE)和决定系数(R²)。这些指标帮助我们量化模型对数据的拟合程度。
上一节我们介绍了通过可视化方法评估模型。本节中,我们来看看如何用数值指标进行更精确的评估。
这些指标是数值化确定模型对数据拟合好坏的方法。
以下是两种我们常用的、用于确定模型拟合度的重要指标。
- 均方误差(MSE):衡量预测值与实际值之间的平均平方差。
- 决定系数(R²):衡量数据点与拟合回归线的接*程度。
均方误差(MSE)📏


要计算MSE,我们需要求出实际值 Y 与预测值 Ŷ 之间的差值,然后将其平方。
例如,实际值为150,预测值为50。两者相减得到100。然后将这个数平方。
接着,将所有样本的误差平方相加,再除以样本数量,取其平均值,即可得到MSE。
在Python中,我们可以从sklearn.metrics导入mean_squared_error函数。
from sklearn.metrics import mean_squared_error


mean_squared_error函数接收两个输入:目标变量的实际值和目标变量的预测值。
决定系数(R²)🎯
R²也称为判定系数。它是一个衡量数据与拟合回归线接*程度的指标,即我们的实际数据与估计模型的接*程度。
可以将其理解为将回归模型与一个简单模型(即数据点的平均值)进行比较。如果变量 X 是一个好的预测因子,我们的模型性能应该远优于仅使用平均值。

在这个例子中,数据点的平均值 Ȳ 是6。

决定系数 R² 的计算公式为:1 - (回归线的MSE / 数据点平均值的MSE)。
在大多数情况下,其值介于0和1之间。
让我们看一个回归线拟合相对较好的情况。蓝线代表回归线,蓝色方块代表回归线的MSE。红线代表数据点的平均值,红色方块代表红线的MSE。我们可以看到蓝色方块的面积远小于红色方块的面积。
在这种情况下,因为回归线拟合得好,其均方误差较小(分子小)。而平均线的均方误差相对较大(分母大)。一个较小的数除以一个较大的数,结果会更小。极端情况下,这个比值趋*于0。将上一张幻灯片中的这个值代入R²公式,我们得到的值接*1。这意味着回归线对数据的拟合很好。
这里是一个回归线未能很好拟合数据的例子。如果我们仅比较红色方块与蓝色方块的面积,会发现两者面积几乎相同。面积比值接*1。



在这种情况下,R²值接*0。这条线的表现与仅使用数据点平均值的效果差不多。因此,这条线的表现不佳。
在Python中,我们使用线性回归对象中的score方法来获取R²值。
根据从这个例子中得到的结果,我们可以说,大约49.695% 的价格变化可以由这个简单线性模型解释。
你的R²值通常介于0和1之间。如果你的R²值为负数,可能是由于过拟合造成的,我们将在下一个模块中讨论。


本节课中,我们一起学*了两种关键的样本内评估指标:均方误差(MSE) 和 决定系数(R²)。MSE衡量了预测误差的平均大小,而R²则解释了模型对数据变异的解释比例。理解这些指标对于判断回归模型的性能至关重要。
生成式人工智能工程:053:预测与决策

在本节课中,我们将学*如何评估模型的预测结果,并基于此做出决策。我们将探讨如何判断模型是否正确,以及使用哪些工具和方法来验证模型的合理性。
模型合理性的初步检查
上一节我们介绍了模型训练的基本方法,本节中我们来看看如何评估模型的预测结果。首先,你需要确保模型的输出结果是合理的。
以下是评估模型合理性的核心步骤:
- 可视化分析:始终使用图表来直观展示数据和模型预测。
- 数值评估:使用数值指标来量化模型的性能。
- 模型比较:在不同模型之间进行比较,以选择最佳模型。
预测示例
让我们看一个具体的预测例子。回想一下,我们使用 fit 方法来训练模型。现在,我们想预测一辆高速公路油耗为每加仑30英里的汽车价格。
将数值代入 predict 方法,我们得到预测价格为 $13771.30。
这个结果看起来是合理的。例如,价格不是负数,也没有极高或极低。我们可以通过检查 coef_ 属性来查看模型的系数。
回想一下用于根据高速公路油耗预测价格的简单线性模型表达式,该系数对应于高速公路油耗特征的倍数。因此,高速公路油耗每增加一个单位,汽车价格大约下降 $821。这个数值看起来也是合理的。
处理不合理的预测值
有时,你的模型会产生不合理的值。例如,如果我们为高速公路油耗在0到100的范围内绘制模型预测线,会得到负的汽车价格。
这可能是因为:
- 该范围内的数值不现实。
- 线性假设不正确。
- 我们没有该范围内汽车的数据。
在本例中,汽车不太可能有那么高的燃油里程,因此我们的模型看起来是有效的。
生成预测序列与可视化
为了在指定范围内生成一系列值进行预测,我们需要导入 numpy,然后使用 numpy.arange 函数生成序列。
import numpy as np
# 生成从1到100(不包括101),步长为1的序列
sequence = np.arange(1, 101, 1)
我们可以使用这个序列的输出值来预测新值。输出是一个 numpy 数组,其中许多预测值是负数,这再次强调了在合理数据范围内评估模型的重要性。
使用回归图可视化数据是你应该尝试的首要方法。请参阅实验部分以了解如何绘制多项式回归图。
在这个例子中,自变量(高速公路油耗)的影响是明显的。数据趋势随着因变量(价格)的增加而下降。图表还显示了一些非线性行为。
检查残差图,在这种情况下,残差呈现出曲线形态,暗示了非线性关系。
分布图是评估多元线性回归的好方法。例如,我们看到价格在30000到50000范围内的预测值不准确。这表明非线性模型可能更合适,或者我们需要该范围内更多的数据。
数值评估指标
均方误差或许是判断模型好坏最直观的数值指标。让我们看看不同的均方误差值如何影响对模型的判断。
第一张图的均方误差为 3495。
第二张图的均方误差为 3652。
最后一张图的均方误差为 12870。
随着均方误差的增加,目标点离预测线越来越远。
R平方评估法
正如我们讨论过的,R平方是另一个评估模型的流行方法。它告诉你模型拟合直线的效果如何。R平方值的范围从0到1。
R平方告诉我们因变量的变异中有多少百分比可以由自变量的回归来解释。
- R平方等于 1 意味着因变量的所有变动都可以完全由自变量的变动来解释。
- 在第一个图中,我们看到红色目标点和蓝色预测线,R平方为 0.9986。模型看起来拟合得很好,这意味着超过99%的预测变量变异可以由自变量解释。
- 第二个模型的R平方为 0.9226。仍然存在很强的线性关系,模型拟合度良好。
- 第三个模型的R平方为 0.8 或 0.6。从视觉上我们可以看到数值分散在直线周围,但它们仍然接*直线。我们可以说,预测变量80%的变异可以由自变量解释。
- R平方为 0.61 意味着大约61%的观测变异可以由自变量解释。
R平方的可接受值取决于你研究的领域和具体用例。Falcon & Miller (1992) 建议,可接受的R平方值应至少为 0.1。
关于模型比较的注意事项
更低的均方误差一定意味着更好的拟合吗?不一定。多元线性回归模型的均方误差会比简单线性回归模型的均方误差小,因为当模型中包含更多变量时,数据的误差会减小。同样,多项式回归的均方误差也会比常规线性回归小。
在下一节中,我们将探讨更准确的模型评估方法。
总结

本节课中,我们一起学*了如何对机器学*模型的预测结果进行评估和决策。我们介绍了通过可视化(如回归图、残差图、分布图)和数值指标(如均方误差和R平方)来检查模型合理性的方法。我们了解到,评估模型需要结合领域知识,并理解不同评估指标的局限性,为选择和改进模型提供了基础。
生成式人工智能工程:054:模型评估与优化
在本节课中,我们将要学*如何评估和优化机器学*模型。我们将了解为什么需要将数据划分为训练集和测试集,以及如何使用交叉验证来更可靠地估计模型在真实世界中的表现。
概述
模型评估告诉我们模型在真实世界中的表现如何。上一节我们介绍了样本内评估,它衡量的是模型对训练数据的拟合程度。然而,这并不能估计训练好的模型预测新数据的能力。本节中我们来看看如何通过样本外评估来解决这个问题。
数据划分:训练集与测试集
解决方案是将我们的数据拆分。一部分数据,称为训练数据,用于训练模型。其余的数据,称为测试数据,则作为样本外数据使用。这些数据随后被用来*似估计模型在真实世界中的表现。
将数据分离为训练集和测试集是模型评估的重要环节。我们使用测试数据来了解模型在真实世界中的表现。
当我们划分数据集时,通常大部分数据用于训练,较小部分用于测试。例如,我们可以使用70%的数据进行训练,然后使用30%的数据进行测试。
以下是数据划分的核心步骤:
- 我们使用训练集来构建模型并发现预测关系。
- 然后我们使用测试集来评估模型性能。
- 当我们完成模型测试后,应该使用全部数据重新训练最终模型。
Scikit-learn 包中一个用于拆分数据集的常用函数是 train_test_split 函数。该函数将数据集随机分割为训练和测试子集。
从示例代码片段中可以看到,该方法从 sklearn.model_selection 导入。输入参数 y_data 是目标变量(在汽车估价例子中就是价格),x_data 是预测变量列表(本例中是我们用来预测价格的所有其他汽车数据集变量)。输出是数组:x_train 和 y_train(用于训练的子集),以及 x_test 和 y_test(用于测试的子集)。在本例中,test_size 是分配给测试集的数据百分比,这里是30%。random_state 是用于随机数据集分割的随机种子。
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.3, random_state=42)
泛化误差

泛化误差是衡量我们的数据在预测先前未见数据时表现如何的指标。我们使用测试数据得到的误差就是这个误差的*似值。
此图显示了实际值(红色)的分布与线性回归预测值(蓝色)的分布。我们看到两者的分布有些相似。如果我们使用测试数据生成相同的图,会发现分布相对不同。这种差异是由于泛化误差造成的,它代表了我们在现实世界中看到的情况。

训练数据量与评估精度
使用大量数据进行训练,能为我们提供准确的方法来确定模型在真实世界中的表现。但性能估计的精度会较低。让我们用一个例子来阐明这一点。

这个靶心图的中心代表正确的泛化误差。假设我们随机抽取数据样本,使用90%的数据进行训练,10%进行测试。第一次实验时,我们得到了对训练数据的良好估计。如果我们再次实验,用不同的样本组合训练模型,也会得到不错的结果,但结果会与第一次运行实验时不同。用不同的训练和测试样本组合重复实验,结果相对接*泛化误差,但彼此之间仍有差异。重复这个过程,我们得到了泛化误差的良好*似,但精度很差,即所有结果彼此之间差异很大。


如果我们使用较少的数据点来训练模型,用更多的数据来测试模型,泛化性能估计的准确性会降低,但模型会有很好的精度。上图说明了这一点。我们所有的误差估计都相对接*,但它们离真实的泛化性能更远。

交叉验证
为了克服这个问题,我们使用交叉验证。最常见的样本外评估指标之一是交叉验证。
在这种方法中,数据集被分成K个相等的组。每个组被称为一个折叠,例如四个折叠。其中一些折叠可以用作训练集(我们用它来训练模型),其余部分用作测试集(我们用它来测试模型)。例如,我们可以使用三个折叠进行训练,然后使用一个折叠进行测试。这个过程不断重复,直到每个分区都既用于训练也用于测试。最后,我们使用平均结果作为样本外误差的估计。
评估指标取决于模型,例如R平方值。
应用交叉验证最简单的方法是调用 cross_val_score 函数,该函数执行多次样本外评估。此方法从Scikit-learn的模型选择包中导入。然后我们使用函数 cross_val_score。第一个输入参数是我们要用于进行交叉验证的模型类型。在本例中,我们初始化了一个线性回归模型对象 lr,并将其传递给 cross_val_score 函数。其他参数是 x_data(预测变量数据)和 y_data(目标变量数据)。我们可以通过 cv 参数管理分区数量。这里,cv=3 意味着数据集被分成三个相等的分区。该函数返回一个分数数组,每个被选为测试集的分区对应一个分数。我们可以使用NumPy中的 mean 函数将结果平均,以估计样本外R平方值。
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LinearRegression
import numpy as np
lr = LinearRegression()
scores = cross_val_score(lr, x_data, y_data, cv=3)
out_of_sample_r2 = np.mean(scores)
让我们看一个动画演示。首先,我们将数据分成三个折叠。我们使用两个折叠进行训练,剩余的折叠进行测试。模型将产生一个输出。我们将使用该输出来计算一个分数(在R平方,即决定系数的情况下)。我们将该值存储在一个数组中。我们重复这个过程,使用两个折叠进行训练,一个折叠进行测试,保存分数。然后使用不同的组合进行训练,剩余的折叠进行测试。我们存储最终结果。cross_val_score 函数返回一个分数值,告诉我们交叉验证的结果。
获取预测值
如果我们想要更多信息呢?如果我们想知道在计算R平方值之前,模型提供的实际预测值呢?为此,我们使用 cross_val_predict 函数。输入参数与 cross_val_score 函数完全相同,但输出是预测值。
让我们说明这个过程。首先,我们将数据分成三个折叠。我们使用两个折叠进行训练,剩余的折叠进行测试。模型将产生一个输出,我们将其存储在数组中。我们重复这个过程,使用两个折叠进行训练,一个用于测试。模型再次产生输出。最后,我们使用最后两个折叠进行训练,然后使用测试数据。这最后的测试折叠产生一个输出。这些预测值被存储在一个数组中。

from sklearn.model_selection import cross_val_predict
predictions = cross_val_predict(lr, x_data, y_data, cv=3)
总结

本节课中我们一起学*了模型评估与优化的核心概念。我们了解到,仅使用训练数据评估模型(样本内评估)不足以反映其在新数据上的表现。因此,我们需要将数据划分为训练集和测试集进行样本外评估。为了更稳定、可靠地估计模型的泛化误差,我们引入了交叉验证技术,它通过多次划分和平均结果,有效平衡了评估的准确性与精度。我们还学*了如何使用Scikit-learn库中的 train_test_split、cross_val_score 和 cross_val_predict 等工具来实现这些步骤。掌握这些方法是构建可靠机器学*模型的关键。
生成式人工智能工程:055:过拟合、欠拟合与模型选择 📊
在本节课中,我们将学*如何为多项式回归模型选择最佳的多项式阶数,并深入探讨选择错误阶数时会引发的问题——即过拟合与欠拟合。
上一节我们介绍了多项式回归。本节中,我们来看看如何选择最佳的多项式阶数,以及错误选择阶数时会出现的问题。

考虑以下函数。我们假设训练数据点来源于一个多项式函数,并附加了一些噪声。


模型选择的目标是确定多项式的最佳阶数,以提供对函数 Y = f(X) 的最佳估计。

欠拟合与过拟合
如果我们尝试用一个线性函数来拟合数据,会发现这条线不够复杂,无法很好地拟合数据点,导致许多误差。这种情况被称为欠拟合,即模型过于简单,无法捕捉数据中的规律。
当我们增加多项式的阶数时,模型拟合效果会变好。但如果阶数仍然不足,模型依然会表现出欠拟合。
下图展示了一个八阶多项式拟合数据的效果。可以看到,模型在拟合数据和估计函数方面表现良好,即使在拐点处也是如此。
然而,当我们将阶数增加到十六阶时,模型在追踪训练数据点上表现得极其出色,但在估计真实函数时却表现很差。这在训练数据稀少的区域尤为明显,估计出的函数会产生剧烈振荡,无法追踪真实函数。这种情况被称为过拟合,即模型过于灵活,以至于拟合了数据中的噪声而非潜在的函数规律。
误差分析
让我们观察不同阶数多项式在训练集和测试集上的均方误差(MSE)图。
- 横轴:多项式的阶数。
- 纵轴:均方误差。


训练误差随着多项式阶数的增加而持续下降。
测试误差是评估多项式性能的更好指标。误差会先下降,直到达到最佳多项式阶数,然后开始上升。我们选择使测试误差最小的阶数。在本例中,最佳阶数是8。
- 图中最佳阶数左侧的区域代表欠拟合。
- 图中最佳阶数右侧的区域代表过拟合。
误差来源
即使我们选择了最佳的多项式阶数,仍然会存在一些误差。回顾训练数据点的原始表达式,其中包含一个噪声项。这个噪声项是误差的来源之一。由于噪声是随机的,我们无法预测它,这有时被称为不可约误差。
此外,还存在其他误差来源。例如,我们的多项式假设本身可能是错误的。样本点可能来自一个完全不同的函数。在下图中,数据是由一个正弦波生成的。多项式函数在拟合正弦波时表现不佳。

对于真实数据,模型可能过于复杂难以拟合,或者我们可能没有正确的数据类型来准确估计函数。


实战:汽车数据案例

让我们在真实的汽车数据(以马力为特征)上尝试不同阶数的多项式。
- 红点代表训练数据。
- 绿点代表测试数据。
如果我们仅使用数据的平均值作为模型,其表现不佳。线性函数能更好地拟合数据。二阶模型看起来与线性函数相似。三阶函数也呈现出与前两阶类似的增长趋势。
然而,当我们观察四阶多项式时,发现在马力值约200处,预测价格突然下降。这看起来是错误的。
为了验证我们的直觉,让我们使用R平方值来评估模型。


下图是R平方值的曲线图。横轴代表多项式模型的阶数。R平方值越接*1,模型越准确。

我们可以看到,当多项式阶数为3时,R平方值达到最优。当阶数增加到4时,R平方值急剧下降,这验证了我们最初的假设。

计算R平方值
我们可以通过以下步骤计算不同阶数下的R平方值:
以下是计算流程:
- 创建一个空列表来存储R平方值。
- 创建一个包含不同多项式阶数的列表。
- 使用循环遍历该列表。
- 创建一个多项式特征对象,并将多项式阶数作为参数传入。
- 使用
fit_transform方法将训练数据和测试数据转换为多项式特征。 - 使用转换后的数据拟合回归模型。
- 使用测试数据计算R平方值,并将其存储在数组中。

总结

本节课中,我们一起学*了模型选择的核心概念。我们探讨了欠拟合(模型过于简单)和过拟合(模型过于复杂)的现象,并理解了使用测试误差来选择最佳模型阶数的重要性。我们还通过汽车数据的实际案例,演示了如何利用R平方指标来评估不同多项式模型的性能,从而避免选择导致性能下降的模型阶数。记住,一个好的模型需要在复杂度和泛化能力之间取得平衡。
生成式人工智能工程:056:岭回归 🏔️
在本节课中,我们将要学*岭回归。岭回归是一种用于防止模型过拟合的技术。我们将通过多项式回归的例子来直观地理解过拟合问题,并学*如何使用岭回归中的 alpha 参数来控制模型复杂度。
过拟合问题
上一节我们介绍了回归的基本概念,本节中我们来看看一个常见的问题:过拟合。当模型过于复杂时,它可能会完美地拟合训练数据,包括其中的噪声和异常值,从而导致在新数据上表现不佳。
考虑以下由橙色线表示的四阶多项式函数。蓝色点是从这个函数中生成的样本数据。

我们可以使用一个十阶多项式来拟合这些数据。蓝色估计函数在*似真实函数方面做得很好。
异常值的影响
然而,真实数据常常包含异常值。例如,下图中的这个点似乎并非来自原始的橙色函数。

如果我们继续使用十阶多项式函数来拟合包含此异常值的数据,得到的蓝色估计函数将是错误的,并且不能很好地估计实际的橙色函数。如果我们检查估计函数的表达式,会发现估计出的多项式系数具有非常大的幅度,这对于高阶项尤为明显。

岭回归的原理
岭回归通过引入一个名为 alpha 的参数来控制这些多项式系数的大小,从而防止过拟合。alpha 是一个在拟合或训练模型之前需要选择的参数。
以下是不同 alpha 值对模型系数的影响。表格的每一行代表一个递增的 alpha 值。

让我们看看不同的 alpha 值如何改变模型。此表展示了不同 alpha 值下的多项式系数。列对应不同的多项式系数,行对应不同的 alpha 值。

随着 alpha 增加,参数值会变小。这对于高阶多项式特征最为明显。但 alpha 必须谨慎选择。
- 如果
alpha太大,系数将趋*于0,导致模型欠拟合数据。 - 如果
alpha为0,则等同于普通最小二乘回归,过拟合现象明显。 - 当
alpha等于0.001时,过拟合开始减弱。 - 当
alpha等于0.01时,估计函数能较好地跟踪实际函数。 - 当
alpha等于1时,我们看到了欠拟合的初步迹象,估计函数灵活性不足。 - 当
alpha等于10时,出现极端欠拟合,甚至无法跟踪两个数据点。
如何选择 Alpha 参数

为了选择最佳的 alpha 值,我们使用交叉验证技术。以下是使用岭回归进行预测和选择 alpha 的基本步骤。
首先,从 scikit-learn 导入岭回归并创建模型对象:
from sklearn.linear_model import Ridge
ridge_model = Ridge(alpha=0.5) # alpha 是构造函数的参数之一
我们使用 fit 方法训练模型,使用 predict 方法进行预测。
为了确定参数 alpha,我们将数据分为两部分:一部分用于训练,另一部分称为验证集(类似于测试集,但专门用于选择像 alpha 这样的参数)。
以下是选择 alpha 的流程:

- 从一个较小的
alpha值开始。 - 使用训练数据训练模型。
- 使用验证数据进行预测。
- 计算 R 平方分数并存储该值。
- 重复以上步骤,换一个更大的
alpha值。 - 再次训练模型,使用验证数据预测,计算并存储 R 平方值。
- 为不同的
alpha值重复此过程。 - 选择能够最大化 R 平方值的
alpha。
注意,我们也可以使用其他指标(如均方误差)来选择 alpha 值。

多特征场景下的应用
如果我们拥有大量特征,过拟合问题会更加严重。下图展示了在二手车数据集上,使用多个特征和二阶多项式函数时,不同 alpha 值对应的 R 平方分数变化。

纵轴表示 R 平方值,横轴表示不同的 alpha 值。红色曲线代表训练数据,蓝色曲线代表验证数据。

我们可以看到,随着 alpha 值增加,验证集上的 R 平方值增加,并在大约 0.75 处收敛。在这种情况下,我们选择最大的 alpha 值,因为继续增加 alpha 对结果影响甚微。
相反,随着 alpha 增加,训练集上的 R 平方值会下降。这是因为 alpha 项防止了过拟合,这可能会提升模型在未见数据上的表现,但会导致模型在训练数据上的性能变差。

总结

本节课中我们一起学*了岭回归。岭回归通过在损失函数中增加一个与系数平方和成正比的惩罚项(由 alpha 参数控制),来限制模型系数的大小,从而有效防止过拟合。我们了解了 alpha 参数如何影响模型复杂度,以及如何通过交叉验证来选择最优的 alpha 值。这对于处理具有多个特征或高阶项的数据集、构建泛化能力更强的模型至关重要。
生成式人工智能工程:057:网格搜索 📊

在本节课中,我们将学*一种名为“网格搜索”的自动化超参数调优技术。网格搜索能够通过少量代码,系统地扫描多个超参数的不同取值组合,帮助我们找到使模型性能最优的参数配置。
概述
上一节我们介绍了岭回归及其正则化参数 alpha。像 alpha 这类参数并非通过模型拟合过程学*得到,而是在训练前预先设定,它们被称为超参数。Scikit-learn 库提供了一种基于交叉验证自动遍历这些超参数的方法,即网格搜索。
什么是网格搜索?🔍
网格搜索接收待训练的模型对象以及超参数的不同取值列表。它会为每一组超参数值训练模型,并计算评估指标(如均方误差或 R 平方),最终允许我们选择表现最佳的超参数值。
我们可以将不同的超参数值想象成许多小圆圈。网格搜索的过程是:从一个超参数值开始训练模型,然后更换不同的超参数值重复训练,直到遍历完所有预设的参数值组合。每个训练出的模型都会产生一个误差,我们选择使误差最小化的那组超参数。
网格搜索的工作流程
以下是网格搜索的典型工作流程:

- 数据划分:将数据集分为三部分:训练集、验证集和测试集。
- 模型训练与验证:针对不同的超参数组合,在训练集上训练模型,并在验证集上计算 R 平方或均方误差。
- 选择最优参数:选择在验证集上能最小化均方误差或最大化 R 平方的超参数组合。
- 最终测试:使用选出的最优超参数,在测试集上评估模型的最终性能。
在 Scikit-learn 中定义参数网格
在 Scikit-learn 中,对象的构造参数(包括超参数)通常被称为参数。在本模块中,我们将重点关注岭回归的超参数 alpha 和数据标准化参数 normalize。

网格搜索的核心是一个 Python 字典列表。字典的键是超参数的名称,值是该超参数待尝试的不同取值列表。这可以看作一个包含各种参数值的表格。
# 示例:定义参数网格
param_grid = [
{'alpha': [0.1, 1.0, 10.0], 'normalize': [True, False]}
]

创建与运行网格搜索
以下是使用 GridSearchCV 进行网格搜索的基本步骤:
首先,导入必要的库,包括 GridSearchCV。

from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV
import numpy as np
接着,创建岭回归模型对象和参数网格字典。
# 创建模型对象
ridge = Ridge()

# 定义参数网格
parameters = {'alpha': [0.1, 1.0, 10.0]}
然后,创建 GridSearchCV 对象。其输入包括模型对象、参数网格和交叉验证的折数。我们将使用 R 平方作为评估指标(这是默认选项)。
# 创建 GridSearchCV 对象,使用 5 折交叉验证
grid = GridSearchCV(ridge, parameters, cv=5)
grid.fit(X_train, y_train) # 拟合数据
拟合完成后,我们可以通过 best_estimator_ 属性找到最优的超参数值,也可以通过 cv_results_ 属性获取验证集上的平均分数等信息。
# 获取最佳参数
best_params = grid.best_params_
# 获取交叉验证结果详情
cv_results = grid.cv_results_

同时搜索多个参数
网格搜索的一个优势在于能快速测试多个参数的组合。例如,岭回归除了 alpha,还有是否标准化数据的 normalize 选项。
以下是包含两个参数的网格定义示例:
# 定义包含两个参数的网格
parameters = {'alpha': [0.1, 1.0, 10.0], 'normalize': [True, False]}

创建和运行网格搜索的代码与之前类似,只是参数网格字典包含了更多维度的组合。输出结果将包含所有不同参数组合的得分。
# 代码类似,但参数网格更复杂
grid = GridSearchCV(ridge, parameters, cv=5)
grid.fit(X_train, y_train)

# 找到最佳参数组合
best_combo = grid.best_params_
# 查看所有参数组合的详细结果
all_results = grid.cv_results_
我们可以打印出不同超参数值对应的分数。参数值会像课程示例中展示的那样存储。更多实践案例请参考课程实验部分。

总结
本节课我们一起学*了网格搜索技术。我们了解到,网格搜索是一种系统化的超参数调优方法,它通过遍历预定义的参数组合,并利用交叉验证进行评估,从而自动化地找到模型的最佳配置。我们掌握了在 Scikit-learn 中使用 GridSearchCV 来定义参数网格、执行搜索以及获取最优结果的基本流程。这项技术能显著提高我们寻找最优模型参数的效率和效果。
机器学*基础:058:课程介绍 🎯
在本节课中,我们将要学*《使用Python进行机器学*基础》课程的整体介绍。课程由三位经验丰富的讲师共同教授,内容涵盖机器学*的基本概念、多种算法及其在实际场景中的应用。通过本课程,你将能够理解并应用机器学*技术解决现实世界的问题。
讲师介绍 👨🏫
本课程由三位讲师共同指导。
以下是讲师的详细信息:
- Saed Abigaorbi 博士:谷歌高级AIML客户工程师,拥有为企业级解决方案开发的经验,擅长帮助客户将数据转化为可操作的知识。曾在IBM和亚马逊网络服务公司工作,同时也是人工智能和机器学*领域的研究者。
- Joseph Saneg Gndrlo 博士:拥有电气工程博士学位,研究方向是利用机器学*、信号处理和计算机视觉技术分析视频对人类认知的影响。自完成博士学位后,一直在IBM工作。
- Deim Hiirjani:IBM数据科学家实*生,负责为IBM的多个数据科学课程创建内容。目前正在多伦多大学攻读计算机科学学士学位。



机器学*的应用领域 🌍
机器学*已广泛应用于众多行业和领域。
以下是几个具体的应用实例:


- 自动驾驶:在自动驾驶汽车行业中,机器学*被大量用于分类车辆在行驶中可能遇到的物体,例如行人、交通标志和其他车辆。
- 网络安全:许多云服务提供商(如IBM和亚马逊)利用机器学*来保护其服务,检测并防止分布式拒绝服务攻击或可疑恶意使用行为。
- 金融交易:机器学*用于发现股票数据中的趋势和模式,帮助决策交易哪些股票或在何种价格进行买卖。
- 医疗诊断:机器学*可用于帮助识别患者的癌症。通过目标区域的X光扫描,机器学*可以帮助检测潜在的肿瘤。

课程结构与内容 📚

本课程共包含四个模块,每个模块都包含视频讲解和动手实验,以帮助你应用所学知识。
以下是课程模块的详细内容:
- 模块一:简介与回归
- 模块二:分类
- 模块三:聚类
- 模块四:最终项目
动手实验将在Skills Network Labs上使用Jupyter Lab环境进行,主要使用Python编程语言及pandas、NumPy、Scikit-learn等Python库。
在课程中,你将探索不同的机器学*算法,并使用多种数据集进行实践。
以下是各章节你将完成的具体任务:
- 线性回归:使用汽车数据集,根据各种特征估算汽车的二氧化碳排放量,并预测尚未生产的汽车的排放量。
- 回归树:使用房地产数据预测房屋价格。
- 逻辑回归:使用电信公司的客户数据,了解机器学*如何用于预测客户忠诚度。
- K最*邻:使用电信客户数据对客户进行分类。
- 支持向量机:将人类细胞样本分类为良性或恶性。
- 多类别预测:使用经典的鸢尾花数据集对花的种类进行分类。
- 决策树:构建一个模型来确定应为患者开具哪种药物。
- K均值聚类:学*将客户数据集分割成具有相似特征的群体。
在最后一个模块中,你将完成最终项目,综合运用多种分类算法来预测澳大利亚是否会下雨。
学*目标 🎓
完成本课程后,你将能够达成以下目标:
- 解释、比较和对比各种机器学*主题和概念,例如监督学*、无监督学*、分类、回归和聚类。
- 描述各种机器学*算法的工作原理。
- 应用这些机器学*算法,并使用各种Python库在Python中实现它们。

本节课中,我们一起学*了《使用Python进行机器学*基础》课程的概览,包括讲师背景、机器学*的广泛应用、课程的具体模块内容以及最终的学*目标。准备好开始你的机器学*之旅吧!
生成式人工智能工程:059:欢迎学*Python机器学* 🎉

在本课程中,我们将学*Python机器学*的基础知识及其在多个关键领域和行业中的应用。课程将涵盖从核心概念到实际项目构建的全过程,并提供内置的实验室环境供您实践所有示例代码。
概述

机器学*是人工智能的核心分支,它使计算机能够从数据中学*并做出预测或决策,而无需进行明确的编程。本课程旨在通过实际案例和项目,帮助您理解机器学*的基本原理,并掌握使用Python及其流行库(如Scikit-learn)构建模型的能力。

课程内容与应用领域

上一节我们介绍了课程的整体目标,本节中我们来看看机器学*具体在哪些领域发挥作用。

以下是机器学*在几个关键行业中的具体应用示例:
- 医疗健康:数据科学家使用机器学*来预测被认为有患癌风险的人类细胞是良性还是恶性。因此,机器学*在决定个人健康与福祉方面可以发挥关键作用。
- 医疗决策:您将了解决策树的价值,以及如何根据历史数据构建良好的决策树来帮助医生为每位患者开具合适的药物。
- 金融服务:您将学*银行家如何使用机器学*来决定是否批准贷款申请。
- 客户分析:您将学*如何使用机器学*进行银行客户细分,这对于处理海量且多样的数据通常并不容易。
- 推荐系统:在本课程中,您将看到机器学*如何帮助YouTube、亚马逊或Netflix等网站向客户推荐各种产品或服务,例如他们可能感兴趣的电影或书籍。机器学*的功能非常强大。

技术实践与工具
了解了机器学*的广泛应用后,本节我们将聚焦于实现这些应用的技术工具和实践方法。

您将学*如何使用流行的Python库来构建模型。例如,给定一个汽车数据集,我们可以使用Scikit-learn库,根据发动机尺寸或气缸数来估算汽车的二氧化碳排放量。我们甚至可以预测尚未生产的汽车的二氧化碳排放量。
我们将看到电信行业如何预测客户流失。您可以使用本课程内置的实验室环境来运行和练*所有这些示例的代码。您无需在计算机上安装任何软件或在云端进行任何操作。您只需点击一个按钮即可在浏览器中启动实验室环境。示例代码已使用Python语言在Jupyter Notebook中编写,您可以运行它以查看结果,或修改它以更好地理解算法。
学*成果

掌握了工具和方法,本节我们明确一下完成本课程后您将能获得的具体成果。
通过在未来几周内每周投入几个小时,您将为简历增添新的技能,例如回归、分类、聚类、Scikit-learn和SciPy。您还将获得可以添加到作品集中的新项目,包括癌症检测、预测经济趋势、预测客户流失、推荐引擎等。您还将获得机器学*证书,以证明您的能力,并可以在任何您喜欢的地方在线或离线分享,例如LinkedIn个人资料和社交媒体。
总结

本节课中我们一起学*了Python机器学*课程的概览。我们了解了机器学*在医疗、金融、客户分析和推荐系统等多个领域的关键应用,认识了将用于实践的核心Python工具库(如Scikit-learn),并明确了通过本课程学*可以获得的技能、项目成果及认证。现在,让我们开始学*之旅。
生成式人工智能工程:060:机器学*简介 🧠
在本节课中,我们将要学*机器学*的基本概念、定义、应用领域以及它与其他相关技术(如人工智能和深度学*)的区别。我们将通过一个医疗诊断的实例来理解机器学*如何工作,并介绍几种主流的机器学*技术。
什么是机器学*?🤔
想象一下,我们有一份从患者身上提取的人类细胞样本。这个细胞具有多种特征,例如:
- 团块厚度为 6
- 细胞大小均匀性为 1
- 边缘粘附性为 1
此时,一个关键的问题是:这是一个良性细胞还是恶性细胞?与良性肿瘤不同,恶性肿瘤可能侵入周围组织或扩散到全身,早期诊断可能是患者生存的关键。
人们可能认为只有经验丰富的医生才能诊断肿瘤。然而,如果我们获得了一个数据集,其中包含数千个被认为有患癌风险的患者细胞样本的特征,情况就不同了。原始数据分析显示,许多特征在良性和恶性样本之间存在显著差异。
我们可以利用这些细胞特征值,为其他患者的新样本提供早期诊断指示。这需要我们先清洗数据,然后选择合适的算法来构建预测模型,并训练模型以理解数据中良性与恶性细胞的模式。
模型经过迭代学*数据后,就可以用于以相当高的准确率预测新的或未知的细胞。这就是机器学*。它使得机器学*模型能够执行医生的任务,或至少帮助医生加快诊断过程。
机器学*的正式定义 📖
机器学*是计算机科学的一个子领域,它赋予计算机无需明确编程即可学*的能力。
为了解释“无需明确编程”的含义,我们假设有一个包含猫和狗等动物图像的数据集,我们希望开发一个能够识别和区分它们的软件。
传统方法是先将每张图像解释为一组特征集,例如:图像是否显示动物的眼睛?眼睛大小如何?是否有耳朵?是否有尾巴?有多少条腿?是否有翅膀?在机器学*出现之前,每张图像会被转换为特征向量,然后我们必须编写大量规则或方法,以使计算机变得“智能”并检测动物。
但这种方法常常失败。因为它需要海量规则,高度依赖当前数据集,并且不足以泛化到数据集之外的样本。
这时,机器学*登场了。使用机器学*,我们可以构建一个模型,让它查看所有特征集及其对应的动物类型,从而学*每种动物的模式。这是一个由机器学*算法构建的模型,它能够在没有被明确编程的情况下进行检测。
本质上,机器学*遵循了一个四岁儿童学*、理解和区分动物的相同过程。因此,受人类学*过程启发的机器学*算法,能够从数据中迭代学*,并让计算机发现隐藏的洞察。这些模型帮助我们在各种任务中发挥作用,例如物体识别、摘要生成、推荐等。
机器学*的现实应用 🌍

机器学*正以极具影响力的方式改变社会。以下是一些现实生活中的例子:
以下是几个关键应用领域的介绍:

- 个性化推荐:Netflix和Amazon等平台如何使用机器学*为用户推荐视频、电影和电视节目?它们利用机器学*来生成你可能喜欢的建议。这类似于你的朋友根据他们对你喜好的了解来向你推荐电视节目。
- 金融风控:银行如何在审批贷款申请时做出决策?它们使用机器学*来预测每位申请人的违约概率,然后基于该概率批准或拒绝贷款申请。
- 客户分析:电信公司利用客户的人口统计数据对其进行细分,或预测他们是否会在下个月取消订阅。

我们日常生活中还有许多其他机器学*应用,例如聊天机器人、手机人脸识别登录,甚至电脑游戏。这些应用各自使用了不同的机器学*技术和算法。
主流机器学*技术概览 🛠️

现在,让我们快速了解几种更流行的机器学*技术:
以下是几种核心的机器学*技术:
- 回归分析:用于预测连续值。例如,根据房屋特征预测其价格,或估算汽车发动机的二氧化碳排放量。其核心是找到一个函数来拟合数据点,公式可表示为
y = f(x),其中y是连续的目标变量。 - 分类:用于预测样本的类别或分类。例如,判断一个细胞是良性还是恶性,或者预测客户是否会流失。常用算法如逻辑回归、决策树。
- 聚类:将相似的样本分组。例如,在医疗中发现相似的患者,或在银行领域用于客户细分。常用算法如K-Means。
- 关联规则:用于发现经常共同出现的物品或事件。例如,找出特定顾客通常一起购买的杂货商品。
- 异常检测:用于发现异常和不寻常的案例。例如,用于信用卡欺诈检测。
- 序列挖掘:用于预测下一个事件。例如,分析网站上的点击流。
- 降维:用于减少数据的大小,同时尽可能保留重要信息。常用技术如主成分分析(PCA)。
- 推荐系统:将人们的偏好与其他具有相似品味的人关联起来,并向他们推荐新物品,例如书籍或电影。
我们将在后续视频中详细介绍其中一些技术。
人工智能、机器学*与深度学*的关系 🔗

此时,你可能在想:我们经常听到的这些热门词汇——人工智能(AI)、机器学*和深度学*——之间有什么区别?
简单来说:
- 人工智能 试图让计算机变得智能,以模仿人类的认知功能。因此,人工智能是一个范围广泛的通用领域,包括计算机视觉、语言处理、创造力和摘要生成等。
- 机器学* 是人工智能的一个分支,涵盖了人工智能的统计部分;它通过让计算机查看成百上千个示例、从中学*,然后利用这些经验在新情况下解决相同问题。
- 深度学* 是机器学*中一个非常特殊的领域,计算机实际上可以自主学*并做出智能决策。与大多数机器学*算法相比,深度学*涉及更深层次的自动化。
它们的关系可以理解为:人工智能 > 机器学* > 深度学*。
课程预告与总结 📚
上一节我们介绍了机器学*与其他概念的区别,现在我们来总结并展望后续内容。

我们已经完成了对机器学*的介绍。在后续的视频中,我们将重点回顾两个主要部分:
- 你将学*机器学*的目的及其在现实世界中的应用场景。
- 你将获得对机器学*主题的总体概述,例如监督学*与无监督学*、模型评估以及各种机器学*算法。
现在你已经对学*旅程的内容有了初步了解,让我们继续探索机器学*的奥秘。

本节课中,我们一起学*了机器学*的核心定义,了解了它如何通过从数据中学*模式来解决问题,而无需显式编程。我们探讨了机器学*在推荐系统、金融风控等多个领域的实际应用,简要介绍了回归、分类、聚类等主流技术,并厘清了人工智能、机器学*和深度学*三者之间的层次关系。这为后续深入学*具体的机器学*方法和应用奠定了坚实的基础。
生成式人工智能工程:061:Python与机器学*

在本节课中,我们将要学*如何使用Python进行机器学*。Python因其强大的功能和丰富的库,已成为数据科学家的首选语言。我们将介绍几个核心的Python库,并重点讲解Scikit-learn库如何简化机器学*流程。
Python在机器学*中的角色
Python是一种流行且强大的通用编程语言,*年来已成为数据科学家偏爱的语言。你可以使用Python编写机器学*算法,并且效果很好。然而,Python中已有大量现成的模块和库,可以极大地简化你的工作。
本课程将尝试介绍这些Python包,并在实验中使用它们,以提供更好的动手实践体验。
核心Python库介绍
以下是数据科学家在处理现实世界问题时需要了解的三个基础库,它们构建于Python之上。
- NumPy:这是一个用于在Python中处理n维数组的数学库。它能让你高效地进行计算。由于其出色的能力,例如在处理数组、字典、函数、数据类型和图像方面,它比常规Python更优。
- SciPy:这是一个数值算法和特定领域工具箱的集合,包括信号处理、优化、统计等。SciPy是进行科学计算和高性能计算的良好库。
- Matplotlib:这是一个非常流行的绘图包,提供2D和3D绘图功能。
如果你不熟悉这些包,建议你先学*“使用Python进行数据分析”课程,该课程涵盖了这些包中的大部分有用主题。
数据处理与机器学*库
除了基础库,还有两个专门用于数据处理和机器学*的库至关重要。
- Pandas:这是一个非常高级的Python库,提供了高性能、易用的数据结构。它拥有许多用于数据导入、操作和分析的函数。特别是,它提供了用于操作数值表和时间序列的数据结构和操作。
- Scikit-learn:这是一个机器学*算法和工具的集合,也是我们本课程的重点,你将学*在课程中使用它。
深入Scikit-learn

由于我们将在实验中大量使用Scikit-learn,让我进一步解释它,并展示它为何在数据科学家中如此受欢迎。
Scikit-learn是Python编程语言的免费机器学*库。它包含了大多数分类、回归和聚类算法,并且设计用于与Python数值和科学库NumPy和SciPy协同工作。此外,它包含了非常完善的文档。最重要的是,使用Scikit-learn实现机器学*模型只需几行Python代码,非常简单。
Scikit-learn的完整流程

机器学*管道中需要完成的大多数任务都已经在Scikit-learn中实现。
以下是Scikit-learn可以处理的主要任务步骤:
- 数据预处理:包括特征缩放、处理异常值等。
- 特征选择与提取:从数据中选择或构建最有用的特征。
- 训练集/测试集划分:将数据分割以分别进行模型训练和评估。
- 算法定义:选择并初始化机器学*算法(称为“估计器”)。
- 模型拟合:使用训练数据训练模型。
- 参数调优:调整模型参数以优化性能。
- 预测:使用训练好的模型对新数据进行预测。
- 评估:使用各种指标评估模型性能。
- 模型导出:保存训练好的模型以供后续使用。

代码示例:Scikit-learn工作流
让我展示一个使用Scikit-learn库的示例。你现在不需要理解代码,只需看看如何用几行代码轻松构建一个模型。
# 1. 数据预处理:标准化数据集
from sklearn import preprocessing
scaler = preprocessing.StandardScaler().fit(X_train)
X_train_scaled = scaler.transform(X_train)
# 2. 划分训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 3. 设置算法(例如支持向量机分类器)
from sklearn import svm
clf = svm.SVC()
# 4. 使用训练集训练模型
clf.fit(X_train_scaled, y_train)
# 5. 使用测试集进行预测
X_test_scaled = scaler.transform(X_test)
predicted = clf.predict(X_test_scaled)
# 6. 评估模型(例如使用混淆矩阵)
from sklearn.metrics import confusion_matrix
print(confusion_matrix(y_test, predicted))
# 7. 保存模型(例如使用joblib)
from joblib import dump
dump(clf, 'model.joblib')
基本流程说明:
机器学*算法受益于数据集的标准化。如果数据集中存在一些异常值或不同尺度的字段,必须处理它们。Scikit-learn的预处理包提供了几个常见的实用函数和转换器类,将原始特征向量更改为适合建模的向量形式。
你必须将数据拆分为训练集和测试集,以分别训练模型和测试模型准确性。Scikit-learn可以用一行代码为你将数组或矩阵随机拆分为训练子集和测试子集。
然后你可以设置算法。例如,你可以使用支持向量分类算法构建分类器。我们称我们的估计器实例为clf并进行参数初始化。
现在,你可以通过将训练集传递给fit方法来用训练集训练模型,clf模型学*对未知案例进行分类。然后我们可以使用测试集运行预测。
结果告诉我们每个未知值的类别是什么。此外,你可以使用不同的指标来评估模型准确性,例如使用混淆矩阵来显示结果。最后,保存你的模型。
你可能会觉得所有这些或部分机器学*术语令人困惑,但别担心。我们将在接下来的视频中讨论所有这些主题。
要记住的最重要一点是,使用Scikit-learn,整个机器学*任务的过程只需几行代码即可简单完成。
请注意,虽然使用NumPy或SciPy包也可以完成这些任务,但不会那么容易,当然,如果使用纯Python编程来实现所有这些任务,则需要更多的编码。
总结

本节课中我们一起学*了Python在机器学*中的核心地位。我们介绍了NumPy、SciPy、Matplotlib、Pandas等基础库,并重点深入讲解了Scikit-learn库。我们看到,Scikit-learn通过封装数据预处理、模型训练、评估和预测等完整流程,使得实现复杂的机器学*任务变得异常简单,通常只需寥寥数行代码。这大大降低了机器学*的入门门槛和应用复杂度。
生成式人工智能工程:062:监督学*与无监督学* 🧠


在本节课中,我们将学*机器学*中两种核心范式:监督学*与无监督学*。我们将了解它们的基本概念、区别以及各自的应用场景。
理解监督学*概念的一个简单方法是直接分析其构成词汇。“监督”意味着观察并指导一项任务、项目或活动的执行。

当然,我们并非要监督一个人。相反,我们将监督一个机器学*模型,该模型能够生成类似我们在此处看到的分类区域。
那么,我们如何监督一个机器学*模型?我们通过教导模型来实现,即为模型加载知识,使其能够预测未来的实例。
但这引出了下一个问题:我们究竟如何教导一个模型?
我们通过使用来自带标签数据集的一些数据来训练模型,从而教导它。需要注意的是,数据是带标签的。一个带标签的数据集是什么样子?它可能类似于这样。这个例子取自癌症数据集。如你所见,我们有一些患者的历史数据,并且我们已经知道每一行的类别。


让我们先介绍这个表格的一些组成部分。此处显示的名称,如“肿块厚度”、“细胞大小均匀性”、“细胞形状均匀性”、“边缘粘附”等,被称为属性。列被称为特征,其中包含数据。如果你绘制这些数据并查看图表上的单个数据点,它将拥有所有这些属性。这构成了图表上的一行,也称为一个观测值。直接查看数据的值,可以分为两种类型。第一种是数值型。在机器学*中,最常用的数据是数值型的。第二种是分类型。即,它是非数值的,因为它包含字符而非数字。在本例中,它是分类型的,因为该数据集是为分类任务构建的。

监督学*技术主要有两种类型:分类和回归。

分类是预测一个离散的类别标签或分类的过程。
回归是预测一个连续值的过程,这与分类中预测一个分类值不同。看这个数据集。它与不同汽车的二氧化碳排放量有关。它包括各种汽车型号的发动机尺寸、气缸数、油耗和二氧化碳排放量。给定这个数据集,你可以使用回归,通过其他字段(如发动机尺寸或气缸数)来预测一辆新车的二氧化碳排放量。
既然我们知道了监督学*的含义,你认为无监督学*是什么意思?是的,无监督学*正如其名。我们不监督模型,而是让模型自行工作,以发现人眼可能无法看到的信息。
这意味着无监督算法在数据集上进行训练,并对未标记的数据得出结论。一般来说,无监督学*比监督学*拥有更复杂的算法,因为我们对数据或预期结果知之甚少。

以下是几种广泛使用的无监督机器学*技术:
- 降维:通过减少冗余特征使分类更容易,降维和/或特征选择在其中扮演重要角色。
- 市场篮子分析:这是一种建模技术,基于这样的理论:如果你购买某一组商品,你更有可能购买另一组商品。
- 密度估计:这是一个非常简单的概念,主要用于探索数据以发现其中的某些结构。
- 聚类。
聚类被认为是最流行的无监督机器学*技术之一,用于对以某种方式相似的数据点或对象进行分组。聚类分析在不同领域有许多应用,无论是银行希望根据某些特征细分其客户,还是帮助个人组织并分组他或她最喜欢的音乐类型。不过,一般来说,聚类主要用于发现结构、总结和异常检测。

上一节我们介绍了无监督学*的主要技术,现在我们来总结一下两者的核心区别。
总而言之,监督学*和无监督学*之间最大的区别在于,监督学*处理带标签的数据,而无监督学*处理未标记的数据。在监督学*中,我们有用于分类和回归的机器学*算法。在无监督学*中,我们有诸如聚类等方法。与监督学*相比,无监督学*可用的模型和评估方法更少,以确保模型结果的准确性。因此,无监督学*创造了一个可控性较低的环境,因为机器在为我们创造结果。

本节课中,我们一起学*了监督学*与无监督学*的核心概念。我们明确了监督学*通过带标签数据训练模型进行预测,主要包括分类和回归任务;而无监督学*则处理未标记数据,让模型自主发现模式,常用技术包括聚类、降维等。理解这两种范式是构建更复杂人工智能应用的基础。
生成式人工智能工程:063:回归简介

在本节课中,我们将对回归分析进行简要介绍。回归是预测连续值的过程,在数据分析与机器学*中应用广泛。
概述
回归分析旨在根据一个或多个自变量来预测一个连续的因变量。我们将通过一个汽车二氧化碳排放量的预测案例来理解其基本概念与应用场景。
数据集与问题定义
观察以下数据集,它包含了不同汽车的发动机大小、气缸数量、油耗以及二氧化碳排放量等信息。

核心问题是:能否利用如发动机大小或气缸数等其他字段,来预测一辆汽车的二氧化碳排放量?
假设我们拥有一些汽车的历史数据,而数据集中第9行所示的汽车尚未生产。我们希望在它生产后估算其大致的二氧化碳排放量。这可以通过回归方法实现。
什么是回归?
回归是预测连续值(如二氧化碳排放量)的过程。在回归分析中,涉及两种类型的变量:
- 因变量:这是我们研究和试图预测的状态、目标或最终结果,通常用
y表示。 - 自变量:这些是导致上述状态的原因,也称为解释变量,通常用
X表示。
回归模型建立了因变量 y 与自变量 X 的函数关系。关键在于,因变量必须是连续值,而不能是离散值。自变量则既可以是分类变量,也可以是连续变量。
回归建模流程

我们的目标是利用一些汽车的历史数据及其特征,构建一个回归估计模型。


该模型随后可用于预测新的或未知汽车的预期二氧化碳排放量。
回归模型的类型
基本上,回归模型分为两种主要类型:简单回归和多元回归。

- 简单回归:使用一个自变量来估计因变量。根据因变量与自变量之间关系的性质,它可以是线性的或非线性的。
- 示例:使用发动机大小变量预测二氧化碳排放量。
- 多元回归:当存在多个自变量时,该过程称为多元线性回归。同样,根据关系的性质,它也可以是线性或非线性的。
- 示例:使用发动机大小和气缸数量来预测给定汽车的二氧化碳排放量。
回归的应用场景

本质上,当我们需要估计一个连续值时,就会使用回归分析。以下是几个应用示例:

以下是回归分析的一些典型应用领域:
- 销售预测:尝试根据年龄、教育程度和工作年限等自变量来预测销售人员的年度总销售额。
- 心理学研究:例如,根据人口统计和心理因素确定个人满意度。
- 房价预测:基于房屋面积、卧室数量等因素预测某个区域的房价。
- 收入预测:根据工作时间、教育程度、职业、性别、年龄和工作经验等自变量预测就业收入。
实际上,在金融、医疗保健、零售等众多领域,都能找到回归分析的应用实例。
回归算法简介
存在许多回归算法,每种算法都有其重要性,并有最适合其应用的具体条件。


本课程仅涵盖其中一部分,但这将为您打下足够的基础知识,以便您未来探索不同的回归技术。
总结

本节课我们一起学*了回归分析的基本概念。我们了解到回归是用于预测连续因变量的过程,区分了因变量与自变量,并介绍了简单回归与多元回归两种主要类型。最后,我们探讨了回归在销售、心理学、房地产等多个领域的实际应用,为后续深入学*具体的回归算法奠定了基础。
生成式人工智能工程:064:简单线性回归 📈
在本节课中,我们将要学*线性回归的基本概念。线性回归是一种用于预测连续值的统计方法,它通过建立自变量与因变量之间的线性关系模型来实现预测。理解线性回归不需要线性代数知识,本教程将通过一个简单的例子帮助你掌握其核心思想。

数据集介绍
我们使用一个与汽车二氧化碳排放相关的数据集。该数据集包含不同车型的发动机排量、气缸数、油耗和二氧化碳排放量。
我们的目标是:能否使用一个字段(例如发动机排量)来预测汽车的二氧化碳排放量?答案是肯定的,我们可以使用线性回归来预测连续值,如二氧化碳排放量。
什么是线性回归?
线性回归是一种用于描述两个或多个变量之间关系的线性模型*似方法。

在简单线性回归中,涉及两个变量:一个因变量和一个自变量。关键点在于,因变量必须是连续值,而不能是离散值。自变量则可以是分类变量或连续变量。
线性回归模型主要分为两种类型:
- 简单线性回归:使用一个自变量来估计因变量。例如,使用发动机排量预测二氧化碳排放量。
- 多元线性回归:使用多个自变量来估计因变量。例如,使用发动机排量和气缸数预测二氧化碳排放量。
本节课我们重点讨论简单线性回归。

线性回归如何工作?

为了理解线性回归,我们首先将变量绘制在散点图上。我们将发动机排量作为自变量(X轴),将排放量作为我们想要预测的目标值(Y轴)。
散点图可以清晰地展示变量之间的关系,表明一个变量的变化如何解释或导致另一个变量的变化。从图中可以看出,这些变量呈线性相关。
通过线性回归,我们可以找到一条穿过这些数据点的最佳拟合直线。例如,随着发动机排量增加,排放量也相应增加。一个好的模型可以用来预测每辆汽车的大致排放量。
那么,我们如何使用这条线进行预测呢?假设这条线是数据的良好拟合,我们可以用它来预测未知汽车的排放量。例如,对于一台发动机排量为2.4的样本汽车,我们可以找到其预测排放量为214。
拟合线是什么?

我们将使用自变量(发动机排量,用 x1 表示)来预测目标值 Y。
在简单回归问题中,拟合线传统上表示为一个多项式。对于单个X,模型的形式为:
Y_hat = θ₀ + θ₁ * x₁
在这个方程中:
- Y_hat 是因变量或预测值。
- x₁ 是自变量。
- θ₀ 和 θ₁ 是直线的参数,我们需要进行调整。
- θ₁ 被称为直线的斜率或梯度。
- θ₀ 被称为截距。
- θ₀ 和 θ₁ 也被称为线性方程的系数。
这个方程可以解释为 Y_hat 是 x₁ 的函数。
现在的问题是:如何绘制一条穿过这些点的线?如何确定哪条线拟合得最好?线性回归通过估计直线的系数来回答这个问题,即我们必须计算 θ₀ 和 θ₁ 以找到拟合数据的最佳直线,从而最好地估计未知数据点的排放量。
如何找到最佳拟合线?

假设我们已经找到了数据的最佳拟合线。现在,我们检查所有数据点与该直线的对齐程度。
“最佳拟合”意味着,例如,对于一台发动机排量 x₁ = 5.4、实际CO₂排放量 y = 250 的汽车,基于历史数据,其CO₂预测值应非常接*实际值250。但如果我们使用拟合线(即使用已知参数的多项式)来预测CO₂排放量,可能会得到 Y_hat = 340。
比较汽车排放的实际值与模型预测值,我们会发现存在90个单位的误差。这意味着我们的预测线不准确。这个误差也称为残差,即数据点到拟合回归线的距离。
所有残差误差的平均值显示了直线与整个数据集的拟合程度。在数学上,这可以用均方误差方程表示,记作 MSE。
我们的目标是找到一条使所有这些误差的平均值最小化的直线。换句话说,使用拟合线进行预测的平均误差应最小化。
更技术性地表述:线性回归的目标是最小化这个MSE方程。为了最小化它,我们必须找到最佳参数 θ₀ 和 θ₁。
那么,如何找到能最小化误差的 θ₀ 和 θ₁ 呢?我们有两种选择:
- 使用数学方法。
- 使用优化方法。
使用数学公式计算参数
如前所述,在简单线性回归中,θ₀ 和 θ₁ 是拟合线的系数。我们可以使用一个简单的方程来估计这些系数。由于这是只有两个参数的简单线性回归,并且知道 θ₀ 和 θ₁ 分别是直线的截距和斜率,我们可以直接从数据中估计它们。这要求我们计算数据集中自变量列和因变量(目标)列的平均值。
可以证明,截距和斜率可以使用以下方程计算:

θ₁ = Σ((x_i - x̄) * (y_i - ȳ)) / Σ((x_i - x̄)²)
θ₀ = ȳ - θ₁ * x̄
我们可以从估计 θ₁ 的值开始,这是基于数据找到直线斜率的方法。x̄ 是我们数据集中发动机排量的平均值。
假设我们有9行数据(行0到8)。首先,我们计算 x₁ 的平均值和 y 的平均值。
然后将其代入斜率方程以找到 θ₁。方程中的 x_i 和 y_i 指的是我们需要对数据集中的所有值重复这些计算,i 指的是X或Y的第i个值。应用所有值后,我们得到 θ₁ = 39。
现在我们有了第二个参数,用它来计算第一个参数,即直线的截距。将 θ₁ 代入直线方程可以找到 θ₀。很容易计算出 θ₀ = 125.74。
因此,直线的两个参数是:θ₀ = 125.74, θ₁ = 39。其中,θ₀ 也称为偏置系数,θ₁ 是CO₂排放列的系数。

需要注意的是,你不需要记住这些计算公式,因为Python、R和Scala中用于机器学*的大多数库都可以轻松为你找到这些参数。但理解其工作原理总是有益的。
现在我们可以写出直线的多项式方程。
使用模型进行预测
在找到线性方程的参数后,进行预测就像为特定输入集解方程一样简单。
想象我们正在为数据集中第9行的汽车,根据发动机排量 X 预测CO₂排放量 Y。我们对此问题的线性回归模型表示为:
Y_hat = θ₀ + θ₁ * x₁

或者映射到我们的数据集,即:
CO₂排放量 = θ₀ + θ₁ * 发动机排量
如前所述,我们可以使用刚才讨论的方程找到 θ₀ 和 θ₁。一旦找到,我们就可以代入线性模型的方程。例如,使用 θ₀ = 125, θ₁ = 39,我们可以将线性模型重写为:
CO₂排放量 = 125 + 39 * 发动机排量
现在,让我们插入数据集的第9行,计算发动机排量为2.4的汽车的CO₂排放量:
CO₂排放量 = 125 + 39 * 2.4 = 218.6
因此,我们可以预测这辆特定汽车的CO₂排放量约为218.6。

线性回归的优势
最后,我们来谈谈线性回归为何如此有用。很简单,它是最基本、最易于使用和理解的回归方法。
线性回归的主要优势包括:
- 速度快:计算效率高。
- 无需调参:不像K*邻中的K参数或神经网络中的学*率那样需要调整。
- 易于理解和解释:模型直观,结果容易解释。

本节课中我们一起学*了简单线性回归的核心概念,包括其定义、工作原理、如何通过数学方法计算最佳拟合线的参数,以及如何使用得到的模型进行预测。线性回归是机器学*中最基础且强大的工具之一,为理解更复杂的模型奠定了坚实的基础。
生成式人工智能工程:065:回归模型中的模型评估 📊

在本节课中,我们将要学*回归模型的评估方法。模型评估是判断模型预测准确性的关键步骤,它帮助我们了解模型在未知数据上的表现。我们将介绍两种主要的评估方法,并讨论各自的优缺点,同时也会介绍一些用于衡量回归模型准确性的指标。
评估方法概述

上一节我们介绍了模型评估的目标,本节中我们来看看具体的评估方法。回归的目标是构建一个能够准确预测未知案例的模型。为此,在构建模型后,我们必须进行回归评估。本视频将介绍并讨论两种可用于实现此目标的评估方法。这两种方法是:在同一数据集上训练和测试 与 训练测试集划分。
我们将讨论每种方法是什么,以及使用每种模型的优缺点。同时,我们还将介绍一些用于评估回归模型准确性的指标。
方法一:在同一数据集上训练和测试
首先,让我们看看第一种方法。在考虑评估模型时,我们显然希望选择能提供最准确结果的方法。那么问题来了,我们如何计算模型的准确性?换句话说,在使用给定数据集并构建了线性回归等模型后,我们能在多大程度上信任该模型对未知样本的预测?
解决方案之一是选择数据集的一部分用于测试。例如,假设我们的数据集中有10条记录。我们使用整个数据集进行训练,并利用这个训练集构建一个模型。现在,我们选择数据集的一小部分,例如第6到第9行,但不包含标签。这个集合称为测试集,它包含标签,但这些标签不用于预测,仅作为真实值使用。这些标签被称为测试集的实际值。
现在,我们将测试部分的特征集传递给已构建的模型,并预测目标值。最后,我们将模型的预测值与测试集中的实际值进行比较。这表明了我们的模型实际上有多准确。报告模型准确性的指标有多种,但大多数通常基于预测值和实际值的相似性来计算。
以下是计算回归模型准确性最简单的一个指标。
如前所述,我们只需比较实际值 Y 与预测值 Ŷ(对于测试集)。模型的误差计算为所有行的预测值与实际值之间的平均差值。我们可以将此误差写成一个公式。
误差公式:
误差 = (1/n) * Σ |Y_i - Ŷ_i|
其中,n 是测试样本的数量,Y_i 是实际值,Ŷ_i 是预测值。

我们刚刚讨论的第一个评估方法是最简单的,即在同一数据集上训练和测试。
本质上,这种方法的名称说明了一切:你在整个数据集上训练模型,然后使用同一数据集的一部分对其进行测试。一般来说,当你使用一个已知每个数据点目标值的数据集进行测试时,你能够获得模型准确预测的百分比。
这种评估方法很可能具有较高的训练精度和较低的样本外精度,因为模型从训练中已经了解了所有测试数据点。

理解训练精度与样本外精度
我们提到,在同一数据集上进行训练和测试会产生较高的训练精度,但训练精度究竟是什么?训练精度是模型使用测试数据集时做出正确预测的百分比。
然而,高训练精度不一定是一件好事。例如,高训练精度可能导致过拟合。这意味着模型对数据集的训练过度,可能捕捉到噪声并产生一个非泛化的模型。
样本外精度是模型在未训练过的数据上做出正确预测的百分比。由于可能过拟合,在同一数据集上进行训练和测试很可能具有较低的样本外精度。
我们的模型具有高样本外精度非常重要,因为模型的目的是对未知数据做出正确预测。那么,我们如何提高样本外精度?一种方法是使用另一种称为训练测试集划分的评估方法。
方法二:训练测试集划分

在这种方法中,我们选择数据集的一部分进行训练,例如第0到第5行,其余部分用于测试,例如第6到第9行。模型在训练集上构建。
然后将测试特征集传递给模型进行预测。最后,将测试集的预测值与测试集的实际值进行比较。

这第二种评估方法称为训练测试集划分。
训练测试集划分涉及将数据集分别拆分为训练集和测试集,它们是互斥的。之后,你用训练集进行训练,用测试集进行测试。这将为样本外精度提供更准确的评估,因为测试数据集不是用于训练数据的数据集的一部分。对于现实世界的问题来说,这更真实。
这意味着我们知道数据集中每个数据点的结果,非常适合进行测试。并且由于这些数据未被用于训练模型,模型对这些数据点的结果一无所知,因此本质上,这是真正的样本外测试。但是,请确保之后使用测试集训练你的模型,因为你不想丢失潜在的有价值数据。
训练测试集划分的问题在于,它高度依赖于训练和测试所依据的数据集。这种变化使得训练测试集划分比在同一数据集上训练和测试具有更好的样本外预测能力,但由于这种依赖性,它仍然存在一些问题。
另一种称为 K折交叉验证 的评估模型解决了大部分这些问题。
方法三:K折交叉验证简介

如何解决由依赖性导致的高方差问题?答案是进行平均。
让我解释一下K折交叉验证的基本概念,看看我们如何解决这个问题。整个数据集由左上角图像中的点表示。
如果我们设置K等于4折,那么我们将数据集拆分如下所示。例如,在第一折中,我们使用数据集的前25%进行测试,其余部分用于训练。使用训练集构建模型,并使用测试集进行评估。
然后在下一轮或第二折中,数据集的第二个25%用于测试,其余部分用于训练模型,再次计算模型的准确性。我们继续所有折。最后,对所有四次评估的结果进行平均;即每折的准确性被平均,请注意每折都是不同的,其中一折中的训练数据不会在另一折中使用。
最简单形式的K折交叉验证使用同一数据集执行多次训练测试划分,每次划分都不同。然后对结果进行平均,以产生更一致的样本外精度。
我们想向你展示一个评估模型,它解决了我们在先前方法中描述的一些问题。然而,深入探讨K折交叉验证模型超出了本课程的范围。
总结

本节课中我们一起学*了回归模型的评估方法。我们介绍了三种主要的评估方法:在同一数据集上训练和测试、训练测试集划分以及K折交叉验证的基本概念。每种方法都有其适用场景和优缺点。关键在于理解训练精度与样本外精度的区别,并选择合适的方法来获得对模型泛化能力的可靠估计,从而确保模型在未知数据上也能做出准确的预测。
生成式人工智能工程:066:回归模型评估指标 🎯
在本节课中,我们将学*如何评估回归模型的性能。我们将介绍几种关键的评估指标,它们通过比较模型预测值与实际值之间的差异来衡量模型的准确性。理解这些指标对于改进模型至关重要。

什么是评估指标? 📊
评估指标用于解释模型的性能。
具体到回归模型,我们可以通过比较实际值和预测值来计算其准确性。评估指标在模型开发中起着关键作用,它能揭示需要改进的领域。
我们将回顾几种模型评估指标,包括平均绝对误差、均方误差和均方根误差。但在定义这些指标之前,我们需要先明确“误差”是什么。
理解误差 📈
在回归的语境中,模型的误差是数据点与算法生成的趋势线之间的差值。
由于存在多个数据点,可以通过多种方式确定误差。
核心评估指标详解

以下是几种常用的回归模型评估指标。
平均绝对误差
平均绝对误差是误差绝对值的平均值。这是最容易理解的指标,因为它就是平均误差。
公式:
MAE = (1/n) * Σ|y_i - ŷ_i|
其中,y_i是实际值,ŷ_i是预测值,n是样本数量。
均方误差
均方误差是误差平方的平均值。它比平均绝对误差更常用,因为它更侧重于较大的误差。这是由于平方项会以指数方式放大较大误差相对于较小误差的影响。
公式:
MSE = (1/n) * Σ(y_i - ŷ_i)^2
均方根误差
均方根误差是均方误差的平方根。这是最流行的评估指标之一,因为均方根误差可以解释为与响应向量或Y轴单位相同的单位,这使得其信息易于关联。
公式:
RMSE = sqrt(MSE)
相对绝对误差
相对绝对误差,也称为残差平方和,其中ȳ是y的平均值。它计算总绝对误差,并通过除以简单预测器的总绝对误差对其进行归一化。
公式:
RAE = Σ|y_i - ŷ_i| / Σ|y_i - ȳ|
相对平方误差
相对平方误差与相对绝对误差非常相似,但被数据科学界广泛采用,因为它用于计算R平方。
公式:
RSE = Σ(y_i - ŷ_i)^2 / Σ(y_i - ȳ)^2
R平方
R平方本身并非误差,而是衡量模型准确性的流行指标。它表示数据值有多接*拟合的回归线,R平方越高,模型对数据的拟合度越好。
公式:
R² = 1 - RSE
如何选择指标? 🤔
以上每种指标都可用于量化你的预测。指标的选择完全取决于模型的类型、你的数据类型和知识领域。遗憾的是,进一步的探讨超出了本课程的范围。
总结 📝

本节课我们一起学*了回归模型的核心评估指标。我们明确了“误差”的定义,并详细介绍了平均绝对误差、均方误差、均方根误差、相对绝对误差、相对平方误差以及R平方。理解这些指标的计算方式和含义,是评估和优化回归模型性能的基础。记住,根据具体问题和数据特点选择合适的评估指标至关重要。
生成式人工智能工程:067:多元线性回归 📈

在本节课中,我们将要学*多元线性回归。这是一种用于预测连续变量的统计方法,它使用多个自变量来预测一个因变量。我们将探讨其基本概念、应用场景、模型构建方法以及注意事项。
什么是多元线性回归?🤔

上一节我们介绍了线性回归的基本概念,本节中我们来看看多元线性回归。
简单线性回归使用一个自变量来估计因变量。例如,使用发动机大小来预测二氧化碳排放量。然而在现实中,预测二氧化碳排放量通常涉及多个变量。当存在多个自变量时,这个过程就称为多元线性回归。例如,使用发动机大小和气缸数量来预测汽车的二氧化碳排放量。
多元线性回归是简单线性回归模型的扩展。因此,建议您先学*简单线性回归。
多元线性回归的应用场景 🎯

在深入探讨样本数据集和多元线性回归的工作原理之前,我们先了解它能解决哪些问题、何时使用它,以及具体能回答哪些问题。
多元线性回归主要有两个应用方向:
- 识别自变量对因变量的影响强度。例如,复*时间、考试焦虑、课堂出勤率和性别是否对学生的考试成绩有影响?
- 预测变化的影响。即理解当自变量改变时,因变量如何变化。例如,在分析一个人的健康数据时,多元线性回归可以告诉我们,在保持其他因素不变的情况下,患者的体重指数每增加或减少一个单位,其血压会相应上升或下降多少。
多元线性回归模型 🧮
与简单线性回归一样,多元线性回归是一种预测连续变量的方法。它使用多个称为自变量或预测变量的变量,来最佳地预测目标变量(也称为因变量)的值。
在多元线性回归中,目标值 Y 是自变量 X 的线性组合。例如,您可以根据汽车的发动机大小、气缸数量和油耗等自变量来预测其二氧化碳排放量。
多元线性回归非常有用,因为您可以检查哪些变量是结果变量的显著预测因子,并了解每个特征如何影响结果变量。同样,如果您成功构建了这样的回归模型,就可以用它来预测未知案例(例如第9条记录)的排放量。
通常,模型的形式为:
ŷ = θ₀ + θ₁x₁ + θ₂x₂ + ... + θₙxₙ
从数学上讲,我们也可以用向量形式表示。这意味着它可以表示为两个向量——参数向量和特征集向量的点积。通常,我们可以将多维空间的方程表示为 θᵀx,其中 θ 是 n×1 的未知参数向量,x 是特征集的向量。
θ 也称为回归方程的参数或权重向量,这两个术语可以互换使用。X 是特征集,代表一个样本(例如一辆汽车),x₁ 代表发动机大小,x₂ 代表气缸数,依此类推。特征集的第一个元素通常设为1,这样 θ₀ 就变成了截距或偏置参数。

请注意,在一维空间中,θᵀX 是一条直线的方程,也就是我们在简单线性回归中使用的。在更高维度(当我们有多个输入或 X 时),这条“线”被称为平面或超平面,这就是我们用于多元线性回归的。因此,核心思想是为我们的数据找到最佳拟合的超平面。为此,与线性回归一样,我们需要估计能最佳预测每行目标字段值的 θ 向量值。
为了实现这个目标,我们必须最小化预测误差。现在的问题是,我们如何找到最优化的参数?
如何找到最优参数?🔍
要找到模型的最优参数,我们首先需要理解什么是最优参数,然后找到优化参数的方法。简而言之,最优参数是能导致模型误差最少的参数。
假设我们已经找到了模型的参数向量,即我们已经知道了 θ 向量的值。现在我们可以使用模型和数据集中第一行的特征集来预测第一辆汽车的二氧化碳排放量。如果我们将特征集的值代入模型方程,会得到预测值 ŷ。例如,假设它返回140作为该特定行的预测值。实际值 y 是196。预测值与实际值196的差异有多大?我们可以简单地计算为196减去140,等于56。这就是我们模型仅针对一行(本例中为一辆汽车)的误差。
与线性回归一样,这里的误差可以看作是数据点到拟合回归模型的距离。所有残差误差的平均值显示了模型代表数据集的糟糕程度,这被称为均方误差(MSE)。从数学上讲,MSE可以用一个方程表示。虽然这不是展示多元线性回归模型误差的唯一方法,但却是最流行的方法之一。我们数据集的最佳模型是所有预测值误差最小的模型。因此,多元线性回归的目标是最小化MSE方程。为了最小化它,我们需要找到最佳的参数 θ。但是,如何找呢?

参数估计方法 ⚙️
我们如何找到多元线性回归的参数或系数?估计这些系数值的方法有很多。然而,最常见的方法是最小二乘法和优化方法。
普通最小二乘法试图通过最小化均方误差来估计系数的值。这种方法将数据视为矩阵,并使用线性代数运算来估计 θ 的最优值。这种技术的问题在于计算矩阵运算的时间复杂度很高,可能需要很长时间才能完成。当数据集中的行数少于10,000时,可以考虑使用这种技术;但对于更大的数值,应尝试其他更快的方法。
第二种选择是使用优化算法来寻找最佳参数。即,您可以通过迭代最小化模型在训练数据上的误差来优化系数的值。例如,您可以使用梯度下降法,它从每个系数的随机值开始优化,然后计算误差,并尝试通过多次迭代中明智地改变系数来最小化误差。如果您有一个大型数据集,梯度下降是一个合适的方法。但请注意,还有其他方法可以估计多元线性回归的参数,您可以自行探索。
找到模型的最佳参数后,您就可以进入预测阶段。

进行预测 📊
找到线性方程的参数后,进行预测就像为特定输入集解方程一样简单。
想象一下,我们正在根据其他变量预测记录中第9辆汽车的二氧化碳排放量 Y。这个问题的线性回归模型表示为 ŷ = θᵀx。一旦我们找到参数,就可以将它们代入线性模型的方程中。例如,假设 θ₀ = 125,θ₁ = 6.2,θ₂ = 14,依此类推。
如果将其映射到我们的数据集,可以将线性模型重写为:
二氧化碳排放量 = 125 + 6.2 × 发动机大小 + 14 × 气缸数 + ...

如您所见,多元线性回归估计了预测因子的相对重要性。例如,它显示与发动机大小相比,气缸数对二氧化碳排放量的影响更大。现在,让我们插入数据的第九行,计算发动机大小为2.4的汽车的二氧化碳排放量:
二氧化碳排放量 = 125 + 6.2 × 2.4 + 14 × 4 + ...
我们可以预测这辆特定汽车的二氧化碳排放量将是214.1。
注意事项与常见问题 ❗
现在,让我解答一些您可能对多元线性回归已有的疑问。
正如您所看到的,您可以在多元线性回归中使用多个自变量来预测目标值。与仅使用一个自变量来预测因变量的简单线性回归相比,这有时会产生更好的模型。现在的问题是,我们应该使用多少个自变量进行预测?我们应该使用数据集中的所有字段吗?向多元线性回归模型添加自变量是否总是会提高模型的准确性?
基本上,在没有理论依据的情况下添加过多的自变量可能会导致模型过拟合。过拟合模型是一个真正的问题,因为它对于您的数据集来说过于复杂,并且不够通用,无法用于预测。因此,建议避免使用过多变量进行预测。在回归中有不同的方法来避免模型过拟合,但这超出了本视频的范围。
下一个问题是,自变量必须是连续的吗?基本上,可以通过将分类自变量转换为数值变量,将其纳入回归模型。例如,对于一个二元变量(如汽车类型),可以编码为:手动挡为0,自动挡为1。
最后一点,请记住多元线性回归是线性回归的一种特定类型。因此,因变量与每个自变量之间需要存在线性关系。有多种方法可以检查线性关系。例如,您可以使用散点图,然后目视检查线性。如果散点图中显示的关系不是线性的,那么您需要使用非线性回归方法。

本节课中我们一起学*了多元线性回归的基本原理、模型构建、参数估计方法以及实际应用中的关键注意事项。理解这些概念是构建有效预测模型的基础。
生成式人工智能工程:068:分类简介 📊
在本节课中,我们将学*机器学*中的分类任务。分类是一种监督学*方法,用于将未知项目归类到离散的类别中。我们将探讨分类的基本概念、工作原理、应用场景以及常见的算法类型。
什么是分类?
上一节我们介绍了本课的主题。本节中,我们来看看分类的具体定义。
在机器学*中,分类是一种监督学*方法。它可以被视为将一些未知项目归类到一个离散类别集合中的手段。分类试图学*一组特征变量与一个目标变量之间的关系。分类中的目标属性是一个分类变量,其值为离散值。
分类器如何工作?
了解了分类的定义后,本节我们来看看分类器的工作原理。
给定一组带有目标标签的训练数据点,分类的任务是为一个未标记的测试用例确定其类别标签。我们可以通过一个例子来解释。
以下是贷款违约预测的例子:
假设一家银行担心贷款可能无法收回。如果可以利用历史贷款违约数据来预测哪些客户可能难以偿还贷款,那么这些高风险客户的贷款申请可以被拒绝,或者向他们提供替代产品。贷款违约预测器的目标是使用现有的贷款违约数据(即关于客户的信息,如年龄、收入、教育程度等)来构建一个分类器。将一个新客户或潜在的未来违约者数据输入模型,然后将其标记为“违约者”或“非违约者”,例如用0或1表示。这就是分类器预测未标记测试用例的方式。
请注意,这个具体例子是关于具有两个值的二元分类器。我们也可以为二元分类和多类别分类构建分类器模型。
多类别分类示例
上一节我们以二元分类为例。本节中我们来看看多类别分类。

例如,假设您收集了一组患有相同疾病的患者数据。在治疗过程中,每位患者对三种药物中的一种产生了反应。您可以使用这个带有标签的数据集和分类算法来构建一个分类模型。然后,您可以用它来找出哪种药物可能适合未来患有相同疾病的患者。正如您所见,这是一个多类别分类的示例。
分类的商业应用
分类不仅限于技术示例,在商业中也有广泛用途。以下是分类的一些商业用例:
- 客户类别预测:预测客户所属的类别。
- 客户流失检测:预测客户是否会转向其他提供商或品牌。
- 广告活动响应预测:预测客户是否会对特定的广告活动做出响应。
分类的广泛应用

除了商业领域,数据分类在许多行业都有应用。本质上,许多问题都可以表示为特征变量和目标变量之间的关联,尤其是在有标签数据可用时。这为分类提供了广泛的适用性。
以下是分类的一些应用场景:
- 电子邮件过滤
- 语音识别
- 手写识别
- 生物特征识别
- 文档分类
分类算法类型
机器学*中有多种类型的分类算法。本课程中我们仅会介绍其中几种。

以下是主要的分类算法类型:
- 决策树
- 朴素贝叶斯
- 线性判别分析
- K最*邻
- 逻辑回归
- 神经网络
- 支持向量机

本节课中我们一起学*了机器学*分类的基础知识。我们明确了分类是一种监督学*任务,用于将数据点分配到离散的类别中。我们通过贷款预测和药物选择的例子理解了分类器的工作原理,区分了二元分类与多类别分类。此外,我们还探讨了分类在客户分析、生物识别等多个领域的商业应用和广泛用途,并列举了决策树、逻辑回归等常见的分类算法类型。
生成式人工智能工程:069:K*邻算法详解 📊
在本节课中,我们将要学*K*邻算法。这是一种简单但功能强大的分类算法,也可用于回归任务。我们将通过一个电信客户分组的实际案例,逐步理解其工作原理、核心概念和应用步骤。
概述
K*邻算法是一种基于实例的学*方法。其核心思想是:相似的数据点具有相似的标签。给定一个带有预定义标签的数据集,该算法通过计算新数据点与已知数据点的“距离”或“相似度”,并参考其K个“最*邻居”的标签来预测新数据点的类别。
算法应用场景:客户分组预测
想象一下,一家电信公司根据服务使用模式将其客户群分为四组。如果能够利用人口统计数据(如地区、年龄、婚姻状况)来预测新客户所属的组别,公司就可以为潜在客户定制服务方案。这是一个典型的分类问题。

我们的目标是构建一个分类器。例如,使用数据集中第0到第7行的已知数据,来预测第8行新客户的类别。我们将使用K*邻算法来完成这个任务。
为了演示方便,我们仅使用两个特征作为预测变量:年龄和收入,并根据客户的组别绘制散点图。
K*邻算法直观理解
假设我们有一个新客户(例如记录8),已知其年龄和收入。如何确定他的类别?
一个直接的想法是找到距离他最*的一个已知客户,并将该客户的类别标签分配给他。例如,如果最*邻居属于“总服务”组,我们是否可以说新客户也最可能属于该组?是的,我们可以,这是基于“第一最*邻”的判断。
然而,仅依赖一个最*邻居的判断可能并不可靠,尤其是当这个邻居本身是一个特殊案例或异常值时。
现在,让我们再看一下散点图。如果不只选择一个,而是选择五个最*的邻居,并通过多数投票来决定新客户的类别呢?在这种情况下,五个邻居中有三个属于“增强服务”组。这是否更合理?是的,确实如此。在这个例子中,K*邻算法中的K值就是5。
这个例子揭示了K*邻算法的基本直觉:通过考察多个最*邻居来做出更稳健的决策。
算法定义与核心概念
K*邻算法是一种分类算法,它利用一组已标记的数据点来学*如何标记其他点。该算法根据数据点之间的相似性对案例进行分类。
在K*邻中,彼此接*的数据点被称为“邻居”。相似且具有相同类别标签的案例会彼此靠*。因此,两个案例之间的距离是衡量它们不相似性的指标。
计算两个数据点之间相似度(或距离/不相似度)的方法有多种,例如可以使用欧几里得距离。
欧几里得距离公式(二维):
distance = sqrt((x2 - x1)^2 + (y2 - y1)^2)

对于多维向量,我们可以使用相同的距离公式,但必须对特征集进行归一化处理,以获得准确的不相似性度量。当然,根据数据类型和应用领域的不同,也可以使用其他距离度量方法。
K*邻算法工作流程
在分类问题中,K*邻算法的工作流程如下:
以下是具体步骤:

- 选择K值:确定要考察的最*邻居的数量K。
- 计算距离:计算新数据点(待预测点)与数据集中每个已知数据点之间的距离。
- 寻找K个最*邻:在训练数据中搜索距离未知数据点最*的K个观测值。
- 进行预测:使用这K个最*邻居中最常见的类别标签,作为未知数据点的预测结果。
这个算法中有两个关键点可能令人困惑:第一,如何选择正确的K值;第二,如何计算案例之间的相似度。我们先来解决第二个问题。
如何计算数据点间的相似度
假设有两个客户:客户1和客户2。暂时假设他们只有一个特征:年龄。我们可以很容易地使用闵可夫斯基距离的一种特定形式——即欧几里得距离——来计算他们的距离。

距离计算示例(一维):
distance = sqrt((34 - 30)^2) = 4
如果我们有更多特征,例如年龄和收入,我们仍然可以使用相同的公式,但这次是在二维空间中计算。

距离计算示例(二维):
distance = sqrt((age2 - age1)^2 + (income2 - income1)^2)
对于更高维度的向量,可以使用相同的距离矩阵进行计算。
如何选择K值
K值代表要考察的最*邻居的数量,需要由用户指定。那么,如何选择正确的K值呢?
假设我们想预测图表中标记为问号的客户的类别。
如果我们选择一个非常小的K值,例如K=1。第一个最*的点是蓝色的(类别1)。这将是一个糟糕的预测,因为它周围更多的点是品红色的(类别4)。实际上,由于它的最*邻居是蓝色的,我们可能捕捉到了数据中的噪声或异常值。过小的K值会导致模型过于复杂,可能引起过拟合。这意味着预测过程不够泛化,无法用于样本外数据。
另一方面,如果我们选择一个非常大的K值,例如K=20,那么模型会变得过度泛化。
那么,如何找到最佳的K值呢?通用的解决方案是预留一部分数据用于测试模型的准确性。具体步骤如下:
以下是选择最佳K值的步骤:
- 将数据集分为训练集和测试集。
- 从K=1开始,使用训练集建立模型。
- 用该模型预测测试集中所有样本的类别,并计算准确率。
- 逐渐增加K值,重复步骤2和3。
- 比较不同K值下的预测准确率,选择准确率最高的K值。例如,在我们的案例中,K=4可能会给出最佳准确率。

K*邻在回归问题中的应用
最*邻分析也可用于预测连续型目标变量的值。在这种情况下,使用最*邻居目标值的平均值或中位数来获得新案例的预测值。
例如,假设您要根据房屋的特征集(如房间数量、平方英尺、建造年份等)来预测其价格。您可以轻松找到三个最*的邻居房屋(当然,不仅是基于距离,而是基于所有属性的综合相似度),然后将该房屋的价格预测为这些邻居价格的中位数。
总结

本节课我们一起学*了K*邻算法。我们从电信客户分组的实际案例出发,理解了算法的核心思想:通过考察数据点的K个最*邻居来进行分类或回归预测。我们详细介绍了算法的工作流程,包括计算距离、选择K值以及进行预测的步骤。同时,我们也探讨了K值选择的重要性,过小会导致过拟合,过大则会导致欠拟合,并通过交叉验证的方法来寻找最佳K值。最后,我们了解到K*邻不仅可用于分类,也可用于回归任务。这是一种直观且强大的基础算法,是机器学*工具箱中的重要组成部分。
生成式人工智能工程:070:分类中的评估指标 📊
在本节课中,我们将学*用于评估分类模型性能的几种核心指标。理解这些指标对于衡量模型效果、发现改进方向至关重要。
概述
评估指标用于解释模型的性能。当我们训练好一个分类模型后,需要一套标准方法来量化其预测的准确性。本节将重点介绍三种常用的分类评估指标:杰卡德指数、F1分数和对数损失。
杰卡德指数
首先,我们来看一种最简单的准确性度量方法——杰卡德指数,也称为杰卡德相似系数。

假设 Y 代表数据集中真实的标签,Ŷ 代表分类器预测的标签。杰卡德指数可以定义为两个标签集合的交集大小除以并集大小。
其公式表示为:
J(Y, Ŷ) = |Y ∩ Ŷ| / |Y ∪ Ŷ|
例如,对于一个大小为10的测试集,如果有8个预测是正确的(即交集大小为8),那么根据杰卡德指数计算的准确率将是0.66。如果预测标签集与真实标签集完全匹配,则子集准确率为1.0,否则为0.0。

混淆矩阵与F1分数
另一种评估分类器准确性的方法是观察混淆矩阵。
假设我们的测试集只有40行数据。混淆矩阵以表格形式对比了预测标签与实际标签,清晰地展示了正确和错误的预测。
混淆矩阵的每一行代表测试集中真实的标签,每一列代表分类器预测的标签。以第一行为例,它对应着测试集中实际流失值为1的客户。可以看到,在40个客户中,有15个的实际流失值是1。在这15个客户中,分类器正确预测了6个为1,但错误地将9个预测为0。
对于实际流失值为0的客户(第二行),共有25人。分类器正确预测了其中24个为0,仅错误预测了1个为1。这表明模型在预测未流失客户方面表现良好。
混淆矩阵的优点在于它能直观展示模型正确预测或区分各类别的能力。在二元分类器的特定情况下,我们可以将这些数字解释为:
- 真正例:实际为1,预测也为1。
- 假负例:实际为1,预测为0。
- 真负例:实际为0,预测也为0。
- 假正例:实际为0,预测为1。
基于以上四个部分的计数,我们可以计算每个标签的精确率和召回率。
- 精确率:衡量在预测为某个类别的样本中,预测正确的比例。
公式:精确率 = 真正例 / (真正例 + 假正例) - 召回率:衡量在实际为某个类别的样本中,被正确预测出来的比例。
公式:召回率 = 真正例 / (真正例 + 假负例)
现在,我们可以根据每个标签的精确率和召回率来计算其F1分数。
F1分数是精确率和召回率的调和平均数。F1分数的最佳值为1(代表完美的精确率和召回率),最差值为0。它是一个很好的指标,能同时反映分类器在召回率和精确率上的表现。
其计算公式为:
F1分数 = 2 * (精确率 * 召回率) / (精确率 + 召回率)
例如,对于“流失=0”这个类别,F1分数可能是0.83;对于“流失=1”这个类别,F1分数可能是0.55。最后,我们可以说这个分类器的平均准确率是这两个标签F1分数的平均值,在本例中为0.69。
请注意,杰卡德指数和F1分数同样适用于多类分类器,但这超出了本课程的范围。
对数损失
现在,让我们看看分类器的另一种准确性度量指标。
有时,分类器的输出是某个类别的概率值,而不是直接的类别标签。例如,在逻辑回归中,输出可以是客户流失的概率(即“是”或等于1的概率)。这个概率是一个介于0和1之间的值。
对数损失,也称为Log Loss,用于衡量那些输出为0到1之间概率值的分类器的性能。例如,当实际标签是1时,预测出0.13的概率会很糟糕,并导致很高的对数损失。
我们可以使用对数损失公式为每一行数据计算损失值,该公式衡量每个预测概率与实际标签的差距。然后,我们计算测试集所有行的平均对数损失。

显然,理想的分类器具有越来越小的对数损失值。因此,对数损失更低的分类器具有更好的准确性。
对数损失公式(对于单个样本)为:
Log Loss = -[y * log(p) + (1 - y) * log(1 - p)]
其中,y是真实标签(0或1),p是预测为正例的概率。
总结
本节课我们一起学*了三种重要的分类模型评估指标。
- 杰卡德指数:通过计算预测集与真实集的交集与并集之比来衡量整体匹配度。
- F1分数:基于混淆矩阵导出的精确率和召回率计算得出,是衡量模型综合性能的调和平均数。
- 对数损失:适用于输出为概率的分类器,通过衡量预测概率与真实标签的差异来评估模型性能,值越小越好。

理解并恰当运用这些指标,能够帮助我们客观评估分类模型,并指导后续的模型优化工作。
生成式人工智能工程:071:决策树简介 🌳

在本节课中,我们将要学*决策树的基本概念。决策树是一种用于分类和预测的机器学*模型,它通过一系列规则对数据进行划分,最终做出决策。我们将通过一个医疗研究的例子,了解决策树如何构建以及如何应用。
什么是决策树?

决策树是一种树状结构模型,用于基于数据特征进行分类或回归预测。它通过测试数据的不同属性,并根据测试结果进行分支,最终在叶子节点给出预测结果。
一个医疗研究的例子
上一节我们介绍了决策树的基本概念,本节中我们来看看一个具体的应用场景。
想象你是一名医学研究员,正在为一项研究收集数据。你已经收集了一组患有相同疾病的患者数据。在治疗过程中,每位患者对两种药物(药物A和药物B)中的一种产生了反应。你的任务是构建一个模型,以确定未来患有相同疾病的患者应使用哪种药物。
该数据集的特征包括患者的年龄、性别、血压和胆固醇水平。目标是每位患者产生反应的药物。这是一个二分类问题,你可以使用数据集的训练部分构建决策树,然后用它来预测未知患者的类别,从而决定为新患者开具哪种药物。
决策树如何构建?

在了解了应用场景后,我们来看看决策树是如何构建的。
决策树通过将训练集划分为不同的节点来构建,其中一个节点包含全部或大部分同一类别的数据。
观察下图,这是一个患者分类器。我们想为新患者开具药物,但选择药物A或B的决定将受到患者具体情况的影响。
我们首先考虑年龄,年龄可分为年轻、中年或老年。如果患者是中年,则明确选择药物B。另一方面,如果患者是年轻或老年,则需要更多细节来确定应开具哪种药物。
额外的决策变量可以是胆固醇水平、性别或血压等因素。例如,如果患者是女性,则推荐药物A;如果是男性,则选择药物B。
决策树的核心在于测试属性,并根据测试结果对情况进行分支。每个内部节点对应一个测试,每个分支对应测试的一个结果,每个叶子节点将患者分配到一个类别。
构建决策树的步骤
现在的问题是,我们如何构建这样的决策树?以下是构建决策树的方法。

决策树可以通过逐一考虑属性来构建。
以下是构建决策树的关键步骤:
- 从数据中选择一个属性。
- 计算该属性在数据划分中的重要性(在下一个视频中,我们将解释如何计算属性的重要性,以判断它是否是一个有效的属性)。
- 根据最佳属性的值拆分数据。
- 然后进入每个分支,对其余属性重复此过程。
构建此树后,你可以使用它来预测未知案例的类别,或者在我们的例子中,根据新患者的特征预测其适用的药物。
总结

本节课中我们一起学*了决策树的基本原理。我们了解到决策树是一种通过测试属性并分支来对数据进行分类的模型。我们通过一个医疗处方预测的例子,看到了决策树如何从根节点开始,根据年龄、性别等特征一步步做出决策,最终在叶子节点给出推荐药物。我们还简要了解了构建决策树的基本步骤:选择属性、计算重要性、拆分数据并递归进行。在接下来的课程中,我们将深入探讨如何计算属性的重要性。
生成式人工智能工程:072:构建决策树 🌳

在本节课中,我们将学*决策树的构建过程。决策树是一种常用的机器学*算法,它通过一系列规则对数据进行分类或预测。我们将从基本概念开始,逐步讲解如何选择最佳特征进行数据分割,以及如何通过计算信息增益来构建高效的决策树。

数据分割与特征选择
上一节我们介绍了决策树的基本概念,本节中我们来看看如何基于数据构建决策树。
考虑一个药物数据集。问题是如何基于该数据构建决策树。
决策树通过递归分区来分类数据。假设我们的数据中有14名患者。算法选择最具预测性的特征来分割数据。
构建决策树时,重要的是确定哪个属性最适合或最具预测性来基于特征分割数据。

假设我们选择胆固醇作为第一个分割属性。它将把数据分成两个分支。如果患者的胆固醇高,我们不能高度确信药物B可能适合他。如果患者的胆固醇正常,我们仍然没有足够的证据或信息来确定药物A或药物B是否确实适合。
这是一个不良属性选择的示例,因此我们尝试另一个属性。
评估特征的重要性
再次,我们有14个案例。这次,我们选择了患者的性别属性。它将把数据分成两个分支:男性和女性。如果患者是女性,我们可以高度确定地说药物B可能适合她。但如果患者是男性,我们没有足够的证据或信息来确定药物A或药物B是否适合。
然而,与胆固醇属性相比,这仍然是一个更好的选择,因为节点中的结果更纯净。这意味着节点主要是药物A或药物B。
因此,我们可以说性别属性比胆固醇更显著,或者换句话说,它比其他属性更具预测性。实际上,预测性基于节点不纯度的减少。
我们寻找最佳特征,以在基于该特征分割后减少叶子节点中患者的不纯度。因此,在以下情况下,性别特征是一个好的候选,因为它几乎找到了纯净的患者。
让我们更进一步。对于男性患者分支,我们再次测试其他属性来分割子集。我们在这里再次测试胆固醇。它导致更纯净的叶子。因此,我们可以轻松做出决策。例如,如果患者是男性且胆固醇高,我们肯定可以开药物A;但如果胆固醇正常,我们可以高度确信地开药物B。
正如你可能注意到的,选择分割数据的属性非常重要,这完全取决于分割后叶子的纯度。
如果节点在100%的情况下属于目标字段的特定类别,则该节点被认为是纯净的。实际上,该方法使用递归分区,通过最小化每个步骤的不纯度,将训练记录分割成段。
节点的不纯度通过节点中数据的熵计算,那么什么是熵?

熵的概念与计算
熵是信息混乱的量或数据中的随机性量。节点中的熵取决于该节点中有多少随机数据,并为每个节点计算。
在决策树中,我们寻找节点中熵最小的树。熵用于计算该节点中样本的同质性。
如果样本完全同质,熵为0;如果样本均等分割,熵为1。这意味着如果节点中的所有数据要么是药物A,要么是药物B,那么熵为0;但如果一半数据是药物A,另一半是药物B,那么熵为1。
你可以使用属性的频率表通过熵公式轻松计算节点的熵,其中P是类别的比例或比率,例如药物A或B。但请记住,你不必计算这些,因为使用的库或包会轻松计算。
计算示例

作为示例,让我们计算分割前数据集的熵。我们有9个药物B的出现和5个药物A的出现。
你可以将这些数字嵌入熵公式中,以计算分割前目标属性的不纯度。在这种情况下,它是0.94。那么分割后的熵是多少?

测试不同属性
现在我们可以测试不同的属性,以找到最具预测性的属性,从而产生两个更纯净的分支。首先选择患者的胆固醇,看看数据如何基于其值分割。
例如,当胆固醇正常时,我们有6个药物B和2个药物A。我们可以基于药物A和B的分布计算该节点的熵,在这种情况下是0.8。但当胆固醇高时,数据被分割成3个药物B和3个药物A。计算熵,我们可以看到它是1.0。
我们应该遍历所有属性,计算分割后的熵,然后选择最佳属性。

比较属性
好的,让我们尝试另一个字段。选择性别属性进行下一次检查。当我们使用性别属性分割数据时,当值为女性时,我们有3名患者对药物B有反应,4名患者对药物A有反应。该节点的熵是0.98,这不太有希望。然而,在分支的另一侧,当性别属性的值为男性时,结果更纯净,有6个药物B和只有1个药物A。该组的熵是0.59。
现在,问题是胆固醇和性别属性之间,哪个是更好的选择?哪个更适合作为第一个属性将数据集分成两个分支?或者,换句话说,哪个属性为我们的药物产生更纯净的节点?或者在哪棵树中,分割后的熵比分割前更小?

性别属性的熵为0.98和0.59,或胆固醇属性的熵为0.81和1.0。答案是分割后信息增益更高的树。
信息增益
那么什么是信息增益?信息增益是分割后可以增加确定性水平的信息。它是分割前树的熵减去按属性分割后的加权熵。

我们可以将信息增益和熵视为相反的概念。随着熵或随机性量的减少,信息增益或确定性量增加,反之亦然。
因此,构建决策树就是找到返回最高信息增益的属性。

计算信息增益
让我们看看如何计算性别属性的信息增益。信息增益是分割前树的熵减去分割后的加权熵。
分割前树的熵是0.94。女性患者的比例是7/14,其熵是0.985。男性患者的比例是7/14,男性节点的熵是0.592。方括号中的结果是分割后的加权熵。
因此,如果我们使用性别属性分割数据集,树的信息增益是0.151。我们将考虑每个叶子节点下样本分布的熵,并取该熵的加权平均值,权重为落在该叶子下的样本比例。
我们也可以计算如果我们使用胆固醇,树的信息增益是0.48。现在,问题是哪个属性更合适?正如提到的,分割后信息增益更高的树。这意味着性别属性。
构建决策树
因此,我们选择性别属性作为第一个分割器。现在,按性别属性分支后,下一个属性是什么?正如你可能猜到的,我们应该为每个分支重复这个过程,并测试其他每个属性,以继续达到最纯净的叶子。这就是构建决策树的方式。

总结

本节课中我们一起学*了决策树的构建过程。我们从数据分割和特征选择开始,介绍了熵的概念及其计算方法。通过比较不同属性的信息增益,我们学会了如何选择最佳特征来构建高效的决策树。决策树通过递归分区和最小化不纯度,能够有效地对数据进行分类和预测。
生成式人工智能工程:073:逻辑回归简介 📊

在本节课中,我们将学*一种名为逻辑回归的机器学*方法。逻辑回归用于分类任务。我们将通过回答以下三个问题来深入理解这个方法:什么是逻辑回归?逻辑回归能解决什么样的问题?以及在哪些情况下我们应该使用逻辑回归?

什么是逻辑回归?🤔
逻辑回归是一种用于基于输入字段值对数据集记录进行分类的统计和机器学*技术。
假设我们有一个电信数据集,我们希望通过分析它来了解哪些客户可能在下个月流失。这是一个历史客户数据,其中每一行代表一个客户。想象一下,你是这家公司的分析师,你需要找出谁将离开以及原因。你将使用该数据集基于历史记录构建一个模型,并用它来预测客户群体中未来的流失情况。
数据包括每个客户已注册的服务信息、客户账户信息、客户的人口统计信息(如性别和年龄范围),以及上个月离开公司的客户信息。这个列被称为“流失”。
我们可以使用逻辑回归,利用给定的特征构建一个预测客户流失的模型。在逻辑回归中,我们使用一个或多个自变量(如任期、年龄和收入)来预测一个结果(如流失),我们称之为因变量,代表客户是否会停止使用服务。
逻辑回归类似于线性回归,但它试图预测一个分类或离散的目标字段,而不是一个数值。在线性回归中,我们可能试图预测一个连续变量,如房屋价格、患者血压或汽车油耗。但在逻辑回归中,我们预测一个二元变量,如是/否、真/假、成功/不成功、怀孕/未怀孕等,所有这些都可以编码为0或1。
在逻辑回归中,自变量应该是连续的。如果是分类变量,则应进行虚拟或指示编码。这意味着我们必须将它们转换为某个连续值。

请注意,逻辑回归既可用于二元分类,也可用于多类分类,但为了简单起见,在本视频中我们将重点讨论二元分类。
逻辑回归的应用场景 🎯
在解释其工作原理之前,我们先来看看逻辑回归的一些应用。如前所述,逻辑回归是一种分类算法,因此可以用于不同的情况。
以下是逻辑回归可以解决的一些问题示例:
- 预测一个人在特定时间段内心脏病发作的概率,基于其年龄、性别和身体质量指数等信息。
- 预测受伤患者的死亡几率。
- 预测患者是否患有某种疾病(如糖尿病),基于观察到的患者特征,如体重、身高、血压和各种血液检查结果等。
- 在营销环境中,我们可以用它来预测客户购买产品或停止订阅的可能性,正如我们在流失示例中所做的那样。
- 预测给定流程、系统或产品发生故障的概率。
- 预测房主拖欠抵押贷款的可能性。
请注意,在所有这些示例中,我们不仅预测每个案例的类别,还衡量案例属于特定类别的概率。

何时使用逻辑回归?✅
有多种机器学*算法可以对变量进行分类或估计。问题是,我们何时应该使用逻辑回归?
以下是逻辑回归是合适选择的四种情况:
- 目标字段是分类变量,特别是二元变量:例如0/1、是/否、流失/未流失、阳性/阴性等。
- 需要预测的概率:例如,如果你想知道客户购买产品的概率是多少。逻辑回归会为给定的数据样本返回一个介于0和1之间的概率分数。实际上,逻辑回归预测的是该样本的概率,我们根据该概率将案例映射到一个离散的类别。
- 数据是线性可分的:逻辑回归的决策边界是一条线、一个平面或一个超平面。分类器会将决策边界一侧的所有点归为一个类别,另一侧的所有点归为另一个类别。例如,如果我们只有两个特征且不应用任何多项式处理,我们可以得到一个不等式,如
θ₀ + θ₁x₁ + θ₂x₂ > 0,这是一个易于绘制的半平面。请注意,在使用逻辑回归时,我们也可以通过多项式处理实现复杂的决策边界,但这超出了本课范围。当你理解逻辑回归的工作原理后,你会对决策边界有更深入的了解。 - 需要理解特征的影响:你可以基于逻辑回归模型系数或参数的统计显著性来选择最佳特征。也就是说,在找到最优参数后,权重
θ₁接*0的特征x对预测的影响小于具有较大绝对值θ₁的特征。实际上,它允许我们在控制其他自变量的同时,理解一个自变量对因变量的影响。
形式化定义与目标 🎯
让我们再次查看我们的数据集。我们将自变量定义为 X,因变量定义为 y。请注意,为了简单起见,我们可以将目标或因变量的值编码为0或1。
逻辑回归的目标是构建一个模型来预测每个样本(在本例中是客户)的类别,以及每个样本属于某个类别的概率。
基于此,让我们开始形式化这个问题。X 是我们的数据集,属于实数空间 ℝ^(m×n),即具有 m 个维度(特征)和 n 条记录。y 是我们想要预测的类别,可以是0或1。

理想情况下,一个逻辑回归模型(称为 ŷ)可以预测给定其特征 X 的客户的类别为1。同样可以很容易地证明,客户属于类别0的概率可以计算为 1 减去客户类别为1的概率。
本节课总结

在本节课中,我们一起学*了逻辑回归的基础知识。我们了解到逻辑回归是一种用于二元分类的机器学*算法,它不仅能预测类别,还能输出属于该类别的概率。我们探讨了逻辑回归的应用场景,例如预测客户流失、疾病诊断等。最后,我们明确了适合使用逻辑回归的四种情况:目标变量是二元的、需要概率输出、数据线性可分以及需要理解特征重要性。在下一节中,我们将深入探讨逻辑回归的数学模型和工作原理。
生成式人工智能工程:074:逻辑回归与线性回归 📊

在本节课中,我们将学*线性回归与逻辑回归之间的区别。我们将回顾线性回归,并探讨为何它不适用于某些二元分类问题。同时,我们将介绍逻辑回归的核心——Sigmoid函数。
线性回归回顾

上一节我们介绍了课程目标,本节中我们来看看线性回归的基本原理。
首先,让我们回顾线性回归的工作原理,以便更好地理解逻辑回归。暂时忘记客户流失预测问题,假设我们的目标是预测数据集中客户的收入。这意味着我们不再预测分类值“流失”,而是预测连续值“收入”。
我们可以选择一个自变量(如客户年龄)来预测因变量(如收入)。为了简化,我们只使用一个特征。我们可以绘制散点图,将年龄作为自变量,收入作为我们希望通过线性回归预测的目标值。
通过线性回归,你可以拟合一条直线或多项式穿过数据点。我们可以通过训练模型或基于样本集进行数学计算来找到这条线。这条线的方程通常表示为 A + B * X1。现在,我们可以使用这条线来预测连续值 Y,即基于客户的年龄预测其收入。

线性回归用于分类的局限性
上一节我们回顾了线性回归,本节中我们来看看它为何不适用于分类问题。
如果我们想预测“流失”这个分类字段,能否使用相同的技术?让我们看看。假设我们获得了客户流失数据,这次的目标是基于客户年龄预测其流失情况。我们有一个特征“年龄”(记为 x1)和一个具有两个类别的分类特征“流失”(“是”和“否”)。我们可以将“是”和“否”映射为整数值 0 和 1。
在图形上,我们可以用散点图表示数据,但这次 y 轴只有两个值。我们的目标是基于现有数据建立一个模型,以预测新客户属于哪个类别。
我们尝试在此处使用线性回归技术,看看是否能解决像“流失”这样的分类属性问题。通过线性回归,你同样可以拟合一条多项式曲线穿过数据,传统上表示为 A + Bx,或更正式地表示为 θ0 + θ1 * x1。这条线有两个参数,用向量 θ 表示。我们也可以将这条线的方程正式表示为 θ^T * x。在多维空间中,方程可表示为 θ^T * x,其中 θ 是二维空间中直线的参数,或是三维空间中平面的参数,依此类推。
给定一个数据集,所有特征集 x 和 θ 参数可以通过优化算法或数学计算得出,从而得到拟合线的方程。例如,这条线的参数是 -1 和 0.1,方程是 -1 + 0.1 * x1。
现在,我们可以使用这条回归线来预测新客户的流失情况。例如,对于一个年龄 x=13 的数据点,我们可以将该值代入直线公式,计算出 Y 值。根据我们的模型,如果 θ^T * x 的值小于阈值(例如 0.5),则类别为 0;否则,类别为 1。然而,这里存在一个问题:该客户属于类别 0 的概率是多少?正如你所见,这并不是解决此问题的最佳模型。此外,还有其他一些问题证实了线性回归并非分类问题的合适方法。
从阶跃函数到Sigmoid函数

上一节我们指出了线性回归的局限性,本节中我们来看看更科学的解决方案。
如果我们使用回归线计算一个点的类别,它总是返回一个数字,如 3 或 -2 等。然后我们需要使用一个阈值(例如 0.5)将该点分配给类别 0 或 1。这个阈值就像一个阶跃函数,无论输入值是大是小、是正是负,输出都是 0 或 1。在阶跃函数中,无论值有多大,只要大于 0.5,输出就等于 1;反之,无论值有多小,只要小于 0.5,输出就等于 0。换句话说,对于值为 1 或 1000 的客户,结果没有区别,输出都是 1。
与其使用这种阶跃函数,我们是否可以使用一条更平滑的曲线,将这些值映射到 0 和 1 之间?现有的方法并不能真正给出客户属于某个类别的概率,而这正是我们非常需要的。我们需要一种能够同时给出属于某个类别概率的方法。
那么,科学的解决方案是什么?如果我们不使用 θ^T * X 直接计算,而是使用一个特定的函数——Sigmoid 函数,那么 sigmoid(θ^T * X) 将给出一个点属于某个类别的概率,而不是直接给出 Y 值。Sigmoid 函数总是返回一个介于 0 和 1 之间的值,具体取决于 θ^T * x 的实际大小。
现在,我们的模型是 sigmoid(θ^T * X),它表示给定 x 时输出为 1 的概率。那么,Sigmoid 函数是什么?

Sigmoid函数详解
上一节我们引入了Sigmoid函数的概念,本节中我们来详细了解它。
Sigmoid 函数,也称为逻辑函数,类似于阶跃函数,在逻辑回归中由以下表达式使用:
公式:
σ(z) = 1 / (1 + e^{-z}),其中 z = θ^T * x
Sigmoid 函数初看起来有点复杂,但不必担心记住这个方程。在使用它之后,你会理解其意义。
注意,在 Sigmoid 方程中,当 θ^T * x 变得非常大时,分母中的 e^{-θ^T * x} 几乎变为 0,Sigmoid 函数的值接* 1。如果 θ^T * x 非常小,Sigmoid 函数的值接* 0。在 Sigmoid 函数图上描绘时,当 θ^T * x 变大,Sigmoid 函数的值接* 1;同样,如果 θ^T * x 非常小,Sigmoid 函数的值接* 0。因此,Sigmoid 函数的输出始终在 0 和 1 之间,这使得它适合将结果解释为概率。
显然,当 Sigmoid 函数的输出接* 1 时,给定 x 条件下 y=1 的概率上升;相反,当 Sigmoid 值接* 0 时,给定 x 条件下 y=1 的概率非常小。

逻辑回归模型的输出
上一节我们解释了Sigmoid函数,本节中我们来看看逻辑回归模型的输出是什么。
在逻辑回归中,我们对输入 x 属于默认类别 y=1 的概率进行建模,我们可以将其正式写为 P(y=1|x)。
我们也可以写出 y 属于类别 0 的概率,给定 x,为 1 - P(y=1|x)。例如,客户留在公司的概率可以表示为 P(流失=1|客户的收入, 年龄),例如可能是 0.8。而同一客户流失概率为 0 的情况,给定客户的收入和年龄,可以计算为 1 - 0.8 = 0.2。

因此,我们的任务是训练模型,设置其参数值,使得我们的模型能够很好地估计 P(y=1|x)。事实上,这就是一个由逻辑回归构建的良好分类器模型应该为我们做的事情。同时,它也应该能很好地估计 P(y=0|x),可以表示为 1 - σ(θ^T * x)。那么,问题是我们如何实现这一点?
模型的训练过程
上一节我们明确了模型的目标,本节中我们来看看如何通过训练达到这个目标。
我们可以通过训练过程找到 θ,让我们看看训练过程是什么。

以下是训练逻辑回归模型的基本步骤:
步骤 1:初始化参数
将 θ 向量初始化为随机值,就像大多数机器学*算法一样。例如,-1 或 2。
步骤 2:计算模型输出
对于训练集中的一个样本客户,计算模型输出 σ(θ^T * X)。X 是特征向量值(例如,客户的年龄和收入),θ 是你在上一步设置的权重。这个方程的输出是预测值,即客户属于类别 1 的概率。
步骤 3:比较与计算误差
将我们模型的输出 ŷ(例如,可能是 0.9997)与客户的实际标签(例如,对于流失客户是 1)进行比较。然后记录差异作为模型对该客户的误差。例如,1 - 0.9997 = 0.0003。这只是训练集中所有客户中的一个客户的误差。
步骤 4:计算总成本
按照前几步的方法计算所有客户的误差,并将这些误差相加。总误差就是模型的成本,由模型的成本函数计算。成本函数基本上代表了如何计算模型的误差(即实际值与模型预测值之间的差异)。因此,成本显示了模型在估计客户标签方面的表现有多差;成本越低,模型正确估计客户标签的能力越好。所以,我们希望做的是尝试最小化这个成本。
步骤 5:更新参数
由于 θ 的初始值是随机选择的,成本函数很可能非常高。因此,我们改变 θ,以期降低总成本。
步骤 6:迭代
改变 θ 的值后,我们回到步骤 2,开始另一次迭代,再次计算模型的成本,并不断重复这些步骤,每次改变 θ 的值,直到成本足够低。
这就引出了两个问题:第一,我们如何改变 θ 的值,以便在迭代过程中降低成本?第二,我们应该何时停止迭代?改变 θ 值有不同的方法,但最流行的方法之一是梯度下降。同样,停止迭代也有各种方法,但本质上,你通过计算模型的准确性来停止训练,并在达到满意程度时停止。
总结

在本节课中,我们一起学*了线性回归与逻辑回归的核心区别。我们了解到线性回归适用于预测连续值,但在处理二元分类问题时存在局限性,因为它无法直接输出概率且对异常值敏感。逻辑回归通过引入 Sigmoid函数 σ(z) = 1 / (1 + e^{-z}),将线性组合 θ^T * x 的输出映射到 (0, 1) 区间,从而可以解释为样本属于正类的概率 P(y=1|x)。我们还概述了逻辑回归模型的训练过程,其核心是通过迭代优化参数 θ 以最小化成本函数。理解这两种回归的本质差异,是构建有效分类模型的重要基础。
生成式人工智能工程:075:逻辑回归训练 🧠

在本节课中,我们将学*如何训练一个逻辑回归模型。我们将讨论如何调整模型参数以更好地预测结果,并深入探讨逻辑回归中的成本函数和梯度下降法,这是优化模型的核心方法。
概述
逻辑回归训练的主要目标是调整模型参数,使其能对数据集中的样本标签做出最佳估计。例如,在客户流失预测问题中,我们希望模型能准确判断客户是否会流失。为了实现这一目标,我们需要理解成本函数与参数之间的关系,并利用梯度下降法来最小化成本。
成本函数的定义与演变
上一节我们介绍了训练的目标,本节中我们来看看如何量化模型的“错误”,即定义成本函数。

逻辑回归模型的输出是 Ŷ,即预测概率。实际标签是 y,其值为 0 或 1。成本函数衡量的是预测值 Ŷ 与实际值 y 之间的差异。
对于单个样本,一个通用的成本计算思路是预测值与实际值的差:
成本 = Ŷ - y
为了避免负值并简化求导过程,通常使用其平方的一半作为成本函数:
成本 = (1/2) * (Ŷ - y)²

对于整个训练集(例如所有客户),我们需要计算所有样本成本的平均值,这被称为均方误差。由于它是参数向量 θ 的函数,我们将其表示为 J(θ)。

然而,均方误差函数在逻辑回归中难以找到全局最小值点。因此,我们需要一个行为相似但更易于优化的替代成本函数。
以下是理想成本函数应具备的特性:
- 当实际值 y=1 时,如果模型预测 Ŷ 也接* 1,成本应接* 0;如果 Ŷ 接* 0,成本应非常大。
- 当实际值 y=0 时,如果模型预测 Ŷ 也接* 0,成本应接* 0;如果 Ŷ 接* 1,成本应非常大。
负对数函数 恰好能满足这些要求。我们可以证明:
- 当 y=1 时,成本 = -log(Ŷ)
- 当 y=0 时,成本 = -log(1 - Ŷ)

将两者结合,得到逻辑回归的交叉熵成本函数:
J(θ) = - (1/m) * Σ [ y⁽ⁱ⁾ * log(Ŷ⁽ⁱ⁾) + (1 - y⁽ⁱ⁾) * log(1 - Ŷ⁽ⁱ⁾) ]
其中,m 是样本数量,Ŷ⁽ⁱ⁾ = σ(θᵀX⁽ⁱ⁾) 是第 i 个样本的预测概率(σ 是 Sigmoid 函数)。

这个函数会对模型将类别 0 预测为高概率,或将类别 1 预测为低概率的情况进行严厉惩罚。
梯度下降法:寻找最优参数
我们已经定义了需要最小化的成本函数 J(θ)。现在的问题是:如何找到使 J(θ) 最小的参数 θ?答案是使用优化算法,其中最著名有效的方法之一就是梯度下降法。
梯度下降法是一种通过迭代寻找函数最小值的通用方法。具体到我们的场景,它是一种利用成本函数的导数来调整参数值、从而最小化成本或误差的技术。
我们可以将模型的参数(例如 θ₁ 代表“年龄”的权重,θ₂ 代表“收入”的权重)想象在一个二维平面上。成本 J 是这两个参数的函数,因此我们可以将其值绘制在第三维,形成一个类似碗状的曲面,称为误差曲面或误差碗。我们的目标就是找到这个碗的底部(最低点)。
梯度下降的过程就像蒙眼下山:
- 随机起点:从参数空间的某个随机点(碗壁上的一个点)开始。
- 查看坡度:计算当前点成本函数 J(θ) 的梯度。梯度是一个向量,其方向指向该点处函数值上升最快的方向,其大小(模长)代表了该方向的陡峭程度。
- 向反方向走:为了下山(减小成本),我们沿着梯度相反的方向移动。移动的步长与梯度的大小和另一个超参数学*率(α) 有关。
- 迭代更新:用公式表示参数更新规则为:
θⱼ := θⱼ - α * (∂J(θ) / ∂θⱼ)
对所有参数 j 同时进行此操作。学*率 α 控制了每一步的步长。 - 重复直至收敛:重复步骤 2-4,每一步都向碗底靠*。随着接*底部(最小值点),坡度变缓,梯度变小,步长也随之自动减小,最终在最小值点附*徘徊。
对于逻辑回归的成本函数,其偏导数 ∂J(θ)/∂θⱼ 有具体的数学表达式。虽然推导需要微积分知识,但其结果形式简洁,便于计算。在实践中,我们通常直接使用这个公式,而无需每次都重新推导。
训练算法步骤总结

本节课中我们一起学*了逻辑回归训练的核心思想。最后,让我们通过完整的训练算法步骤来回顾整个过程:
以下是逻辑回归模型梯度下降训练的具体步骤:
- 初始化参数:将参数向量 θ 初始化为随机值(或零)。
- 计算成本:使用当前参数 θ 和整个训练集数据,通过前向传播计算预测值 Ŷ,并代入成本函数公式 J(θ) 计算总成本。由于参数是随机的,初始成本通常会很高。
- 计算梯度:计算成本函数 J(θ) 关于每个参数 θⱼ 的偏导数,得到梯度向量。这一步需要用到所有训练数据。
- 更新参数:使用梯度下降公式
θⱼ := θⱼ - α * (∂J(θ) / ∂θⱼ)更新所有参数。学*率 α 需要预先设定。 - 迭代循环:重复步骤 2 到 4。每次迭代后,由于参数朝着减少成本的方向更新,我们期望成本 J(θ) 逐渐降低。
- 终止训练:当成本值下降到可接受的阈值,或者达到预设的迭代次数时,停止循环。此时得到的参数 θ 就是训练好的模型,可用于对新样本(如新客户)进行流失概率预测。

通过这一系列步骤,逻辑回归模型便从数据中学*到了规律,并能够做出预测。
生成式人工智能工程:076:支持向量机 📊

在本节课中,我们将学*一种名为支持向量机的机器学*方法,它主要用于分类任务。
概述
支持向量机是一种监督学*算法,它通过寻找一个最优分隔超平面来对数据进行分类。即使数据在原始空间中不是线性可分的,SVM也能通过将其映射到高维特征空间来实现有效分类。接下来,我们将详细探讨SVM的工作原理、核心概念及其应用场景。
什么是支持向量机?

上一节我们介绍了SVM的基本概念,本节中我们来看看其正式定义。
支持向量机是一种监督算法,它通过寻找一个分隔器来对案例进行分类。SVM首先将数据映射到一个高维特征空间,使得数据点能够被分类,即使数据本身不是线性可分的。然后,算法会为数据估计一个分隔器。
数据应以某种方式转换,使得分隔器可以绘制为一个超平面。例如,考虑下图,它展示了一小组细胞仅基于其单位大小和团块厚度的分布情况。

如图所示,数据点落入两个不同的类别。它代表了一个线性不可分的数据集。这两个类别可以用一条曲线分隔,但不能用一条直线分隔。也就是说,它代表了大多数现实世界数据集中常见的线性不可分情况。
我们可以将这些数据转换到更高维的空间,例如,将其映射到三维空间。转换后,两个类别之间的边界可以由一个超平面来定义。由于我们现在处于三维空间,分隔器显示为一个平面。这个平面可用于分类新的或未知的案例。
因此,SVM算法输出一个最优超平面,用于对新样本进行分类。
核心挑战与解决方案
现在,有两个具有挑战性的问题需要考虑。第一,我们如何转换数据,使得分隔器可以绘制为超平面?第二,转换后,我们如何找到最佳或最优化的超平面分隔器?

1. 数据转换(核函数)
首先,我们来看看数据转换是如何工作的。为了简化,假设我们的数据集是一维数据;这意味着我们只有一个特征X。如图所示,它不是线性可分的。那么我们能做什么呢?
我们可以将其转换到二维空间。例如,你可以通过使用一个输出为x和x²的函数将x映射到一个新空间,从而增加数据的维度。
现在,数据是线性可分的,对吗?请注意,由于我们处于二维空间,超平面是一条将平面分成两部分的直线,每个类别位于其中一侧。现在我们可以用这条线来分类新案例。
基本上,将数据映射到高维空间的过程称为核化。用于转换的数学函数称为核函数,可以有不同类型,例如线性、多项式、径向基函数和Sigmoid函数。
以下是核函数的主要类型:
- 线性核:适用于线性可分数据。
- 多项式核:适用于数据分布更复杂的情况。
- 径向基函数核:一种非常强大和常用的核,适用于各种非线性问题。
- Sigmoid核:在某些特定情况下使用,如神经网络。
每个函数都有其自身特点、优缺点和方程,但好消息是你不需要深入了解它们,因为大多数函数已在数据科学编程语言的库中实现。此外,由于没有简单的方法知道哪个函数对任何给定数据集表现最好,我们通常会依次选择不同的函数并比较结果。

2. 寻找最优超平面
现在我们来看另一个问题,特别是在转换后,我们如何找到正确或最优的分隔器?
基本上,SVM基于寻找一个能最好地将数据集分成两类的最佳超平面的思想,如下图所示。

由于我们处于二维空间,你可以将超平面视为一条线性分隔蓝点和红点的线。作为最佳超平面的一个合理选择是代表两个类别之间最大分离或边界的那个。
因此,目标是选择一个具有尽可能大边界的超平面。最接*超平面的样本称为支持向量。直观地说,只有支持向量对实现我们的目标很重要,因此可以忽略其他训练样本。我们试图以这样的方式找到超平面,使其到支持向量的距离最大。
请注意,超平面和决策边界线都有自己的方程。因此,寻找最优超平面可以使用一个方程来形式化,这涉及更多的数学知识,此处不详细展开。也就是说,超平面是通过使用最大化边界的优化过程从训练数据中学*得到的。与许多其他问题一样,这个优化问题也可以通过梯度下降法解决,这超出了本视频的范围。
因此,算法的输出是直线方程 y = Wx + b 中的W和B值。你可以使用这个估计的直线方程进行分类。只需将输入值代入直线方程,然后计算未知点是在直线上方还是下方。如果方程返回值大于0,则该点属于第一类(直线上方),反之亦然。
支持向量机的优缺点
了解了SVM如何工作后,我们来总结其优缺点。

支持向量机的两个主要优点是:在高维空间中准确,并且在决策函数中使用了称为支持向量的训练点子集,因此也具有内存效率。
支持向量机的缺点包括:如果特征数量远大于样本数量,该算法容易过拟合。此外,SVM不直接提供概率估计,而这在大多数分类问题中是需要的。最后,如果你的数据集非常大(例如超过1000行),SVM在计算上效率不高。
应用场景
最后的问题是,在什么情况下应该使用SVM?
SVM适用于图像分析任务,如图像分类和手写数字识别。SVM在文本挖掘任务中也非常有效,特别是因为它能有效处理高维数据,例如用于检测垃圾邮件、文本分类分配和情感分析。SVM的另一个应用是基因表达数据分类,同样是因为其在处理高维数据分类方面的强大能力。SVM也可用于其他类型的机器学*问题,如回归、异常检测和聚类。关于这些特定问题的更多探索,留给你自己去研究。
总结
本节课中我们一起学*了支持向量机。我们了解到SVM是一种强大的分类算法,它通过核函数将数据映射到高维空间,并寻找具有最大边界的最优超平面来分隔不同类别的数据。我们探讨了其核心概念、工作原理、优缺点以及典型的应用场景。掌握SVM是理解现代机器学*分类技术的重要一步。


生成式人工智能工程:077:聚类简介 🧩
在本节课中,我们将对聚类分析进行一个高层次的介绍,涵盖其基本概念、应用场景以及不同类型的聚类算法。
概述
想象一下,你有一个客户数据集,需要基于这些历史数据进行客户细分。客户细分是根据相似特征将客户群体划分为不同组别的实践。这是一项重要的商业策略,因为它允许企业针对特定客户群体,从而更有效地分配营销资源。
什么是客户细分?
客户细分的过程通常不适用于处理大量且多样的数据。因此,需要一种分析方法来从大型数据集中推导出细分群体。
以下是客户细分的几个关键点:
- 客户可以根据年龄、性别、兴趣、消费*惯等多个因素进行分组。
- 核心要求是利用现有数据来理解和识别客户之间的相似性。
如何使用聚类进行细分?
让我们学*如何根据客户共享的特征将他们划分为不同的类别。用于客户细分最常用的方法之一是聚类。
聚类可以无监督地根据客户之间的相似性对数据进行分组。它会将你的客户划分为互斥的组别,例如三个簇。每个簇内的客户在人口统计特征上彼此相似。
现在,我们可以根据每个簇的共同特征为其创建画像。例如:
- 第一组由富裕且中年的客户组成。
- 第二组由年轻、受过教育且中等收入的客户组成。
- 第三组包括年轻且低收入的客户。



最后,我们可以将数据集中的每个个体分配到这些客户组或细分市场中的一个。

现在,想象一下将这个细分后的数据集与客户从你公司购买产品或服务的数据集进行交叉连接。这些信息将极大地帮助理解和预测不同产品之间,个体客户偏好和购买行为的差异。
实际上,掌握这些信息将使你的公司能够为每个细分市场开发高度个性化的体验。

聚类的定义与应用
客户细分是聚类的一个流行应用。聚类分析在不同领域还有许多其他应用。
所以,让我们先定义聚类,然后再看看其他应用。


聚类意味着在数据集中无监督地寻找簇。那么,什么是簇?一个簇是数据集中一组数据点或对象,它们与组内的其他对象相似,而与其他簇中的数据点不同。
现在的问题是,聚类和分类有什么区别?让我们再看一下客户数据集。
分类算法预测的是类别标签。


这意味着将实例分配到预定义的类别中,例如“违约”或“非违约”。举例来说,如果分析师想分析客户数据以了解哪些客户可能违约付款,她会使用带标签的数据集作为训练数据,并应用决策树、支持向量机或逻辑回归等分类方法来预测新客户或已知客户的违约情况。
一般来说,分类是一种监督学*,其中每个训练数据实例都属于一个特定的类别。
然而,在聚类中,数据是无标签的,过程是无监督的。例如,我们可以使用K均值等聚类算法,根据客户是否共享年龄、教育程度等相似属性,将相似客户分组并分配到不同的簇中。
聚类的行业应用
虽然我会给出一些不同行业的例子,但希望你能思考更多聚类的应用场景。

以下是聚类在不同领域的应用:
- 零售业:用于根据客户人口统计特征发现客户之间的关联,并利用该信息识别不同客户群体的购买模式。此外,也可用于推荐系统中,寻找相似物品或相似用户群,并利用协同过滤向客户推荐书籍或电影等。
- 银行业:分析师通过发现正常交易的簇来寻找信用卡欺诈使用的模式。同时,他们也使用聚类来识别客户群,例如区分忠诚客户与流失客户。
- 保险业:用于理赔分析中的欺诈检测,或根据客户细分评估特定客户的保险风险。
- 出版传媒业:用于根据新闻内容自动分类或标记新闻,然后进行聚类,以便向读者推荐相似的新闻文章。
- 医学:可用于根据相似特征描述患者行为,从而为不同疾病识别成功的医疗方案。
- 生物学:用于对具有相似表达模式的基因进行分组,或对遗传标记进行聚类以识别亲缘关系。
环顾四周,你可以发现聚类的许多其他应用。但总的来说,聚类可用于以下目的之一:
- 探索性数据分析。
- 摘要生成或规模缩减。
- 异常值检测,尤其用于欺诈检测或噪声去除。
- 在数据集中查找重复项。
- 作为预测、其他数据挖掘任务的预处理步骤,或作为复杂系统的一部分。
聚类算法类型
让我们简要了解一下不同的聚类算法及其特点。
上一节我们介绍了聚类的广泛应用,本节中我们来看看实现这些应用的不同技术路径。
以下是主要的聚类算法类型:
- 基于划分的聚类:这类算法产生类似球形的簇,例如 K均值、K中值或模糊C均值。这些算法相对高效,适用于中型和大型数据库。
- 层次聚类:这类算法产生簇的树状结构,例如凝聚型和分裂型算法。这类算法非常直观,通常适用于小规模数据集。
- 基于密度的聚类:这类算法产生任意形状的簇。在处理空间聚类或数据集中存在噪声时尤其有效,例如 DBSCAN 算法。
总结
本节课中,我们一起学*了聚类分析的基础知识。我们首先通过客户细分的例子理解了聚类的核心概念,即无监督地将相似数据点分组。我们明确了聚类与分类的区别:分类是监督学*,预测预定义标签;聚类是无监督学*,发现数据内在结构。接着,我们探讨了聚类在零售、金融、医疗等多个行业的实际应用。最后,我们简要介绍了基于划分、层次和基于密度等不同类型的聚类算法及其适用场景。掌握这些基础知识是进一步学*和应用更高级聚类技术的第一步。


生成式人工智能工程:078:K均值聚类简介 🎯

在本节课中,我们将要学*K均值聚类算法。这是一种无监督学*技术,常用于客户细分等任务,能够根据数据点的相似性将其分组。
什么是K均值聚类?🤔

想象你有一个客户数据集,需要基于这些历史数据进行客户细分。客户细分是指将客户群划分为具有相似特征的个体组。K均值聚类就是可用于此目的的算法之一。
K均值能够以无监督的方式,仅根据客户之间的相似性对数据进行分组。
让我们更正式地定义这项技术。
聚类算法的类型与K均值的定位 📊
聚类算法有多种类型,例如划分式、层次式或基于密度的聚类。K均值属于划分式聚类。这意味着它将数据划分为K个非重叠的子集或簇,且这些簇没有内部结构或标签。这表明它是一种无监督算法。
一个簇内的对象非常相似,而不同簇间的对象则非常不同或不相似。可以看到,使用K均值需要找到相似的样本,例如相似的客户。

现在我们面临几个关键问题:首先,在聚类中如何找到样本的相似性?其次,如何衡量两个客户在人口统计特征上的相似程度?
相似性与距离度量 📏
虽然K均值的目标是形成这样的簇:相似样本进入同一个簇,不相似样本落入不同簇。但可以证明,我们可以使用不相似性度量来代替相似性度量。换句话说,传统上使用样本彼此间的距离来塑造簇。
因此可以说,K均值试图最小化簇内距离,并最大化簇间距离。现在的问题是,如何计算两个样本(例如两个客户)之间的不相似性或距离?
假设我们有两个客户,称为客户1和客户2。再假设每个客户只有一个特征,即年龄。我们可以轻松地使用一种特定的闵可夫斯基距离来计算这两个客户的距离,即欧几里得距离。
公式:
对于一维特征(如年龄),两点 x1 和 x2 的欧几里得距离为:
distance = |x1 - x2|

如果特征不止一个呢?例如,同时有年龄和收入。我们仍然可以使用相同的公式,但这次是在二维空间中。同样,我们可以将相同的距离公式用于多维向量。当然,我们必须对特征集进行归一化,以获得准确的不相似性度量。
还有其他不相似性度量也可用于此目的,但这高度依赖于数据类型以及进行聚类的领域。例如,你可以使用欧几里得距离、余弦相似度、平均距离等。
实际上,相似性度量高度控制着簇的形成方式,因此建议理解数据的领域知识和特征的数据类型,然后选择有意义的距离度量。

K均值聚类的工作原理 🔄
现在,让我们看看K均值聚类是如何工作的。为了简单起见,假设我们的数据只有两个特征:客户的年龄和收入,这意味着它是一个二维空间。我们可以使用散点图显示客户的分布,Y轴表示年龄,X轴表示收入。
我们尝试基于这两个维度将客户数据聚类到不同的组或簇中。
以下是K均值算法的核心步骤:
第一步:确定簇的数量(K值)并初始化质心
K均值算法的核心概念是,它为每个簇随机选取一个中心点。这意味着我们必须初始化 K,它代表簇的数量。本质上,在数据集中确定簇的数量或K值是K均值中的一个难题,我们稍后会讨论。现在,让我们为我们的样本数据集设 K=3。这就像我们有三个代表簇的点。
这三个数据点称为簇的质心,其维度应与客户特征集的特征数量相同。
选择这些质心有两种方法:
- 我们可以从数据中随机选择三个观测值,并将这些观测值作为初始均值。
- 我们可以创建三个随机点作为簇的质心,这是我们的选择,在图中用红色显示。

第二步:将每个点分配到最*的质心
在初始化步骤(即定义每个簇的质心)之后,我们必须将每个客户分配到最*的中心。为此,我们必须计算每个数据点(在我们的例子中是每个客户)到质心点的距离。如前所述,根据数据的性质和聚类使用的目的,可以使用不同的距离度量来将项目分配到簇中。因此,你将形成一个矩阵,其中每一行代表一个客户到每个质心的距离,这称为距离矩阵。
K均值聚类的主要目标是最小化数据点与其所属簇质心的距离,并最大化与其他簇质心的距离。所以在这一步,我们必须找到每个数据点最*的质心。我们可以使用距离矩阵来找到数据点最*的质心。找到每个数据点最*的质心后,我们将每个数据点分配到该簇;换句话说,所有客户将根据它们与质心的距离落入一个簇。
我们可以轻易地说,这不会产生好的簇,因为质心一开始是随机选择的。实际上,模型会有很高的误差。这里的误差是每个点与其质心的总距离。它可以表示为簇内平方和误差。直观上,我们试图减少这个误差。这意味着我们应该以这样的方式塑造簇:一个簇的所有成员与其质心的总距离最小化。
第三步:重新计算质心位置
那么,如何将其转变为误差更小的更好簇呢?我们移动质心。在下一步中,每个簇中心将更新为其簇内数据点的均值。实际上,每个质心根据其簇成员移动。换句话说,三个簇中每个簇的质心成为新的均值。

示例:
如果点A的坐标是 (7.4, 3.6),点B的特征是 (7.8, 3.8),那么这个包含2个点的簇的新质心将是它们的平均值,即 (7.6, 3.7)。
第四步:迭代直至收敛
现在我们有了新的质心。正如你所猜测的,我们将再次计算所有点到新质心的距离。点被重新分配,质心再次移动。这个过程一直持续到质心不再移动为止。请注意,每当质心移动时,都需要重新测量每个点到质心的距离。
算法特性与注意事项 ⚠️

是的,K均值是一种迭代算法,我们必须重复步骤2到4,直到算法收敛。在每次迭代中,它将移动质心,计算到新质心的距离,并将数据点分配到最*的质心;这会产生误差最小或最密集的簇。
然而,由于它是一种启发式算法,不能保证它会收敛到全局最优解,结果可能依赖于初始簇。这意味着该算法保证会收敛到一个结果,但该结果可能是一个局部最优解,即不一定是可能的最佳结果。
为了解决这个问题,通常的做法是使用不同的起始条件(即随机化的起始质心)多次运行整个过程。这可能会产生更好的结果。由于该算法通常非常快,多次运行不会有任何问题。
总结 📝

本节课中,我们一起学*了K均值聚类算法。我们了解到它是一种无监督的划分式聚类方法,通过迭代优化质心位置来最小化簇内距离。其核心步骤包括:确定K值并初始化质心、分配点到最*质心、重新计算质心位置以及迭代直至收敛。同时,我们也认识到该算法可能收敛于局部最优解,因此实践中常通过多次随机初始化来寻求更好的聚类结果。
生成式人工智能工程:079:更多关于K均值 🎯

在本节课中,我们将深入探讨K均值聚类算法的准确性评估方法及其核心特性。我们将学*如何定义算法步骤、评估聚类效果,并解决确定最佳聚类数K这一关键问题。
算法步骤详解
上一节我们介绍了K均值的基本概念,本节中我们来看看其具体的工作步骤。
K均值算法通过以下步骤运行:
- 随机放置K个质心,每个质心代表一个簇。质心初始位置相距越远越好。
- 计算每个数据点到各个质心的距离。通常使用欧几里得距离(Euclidean distance)进行度量,其公式为:
distance = sqrt((x2 - x1)^2 + (y2 - y1)^2)
需要注意的是,也可以使用其他类型的距离度量方式,而不仅仅是欧几里得距离。欧几里得距离因其普遍性而被广泛采用。 - 将每个数据点分配给距离它最*的质心,从而形成初始分组。
- 在所有数据点都被分类到某个组后,重新计算K个质心的位置。新质心的位置由其所在组内所有点的均值决定。
- 重复步骤2到4,直到质心的位置不再发生变化。

如何评估聚类效果
了解了算法如何运行后,我们面临一个问题:如何评估K均值形成的聚类质量?换句话说,如何计算K均值聚类的准确性?
一种方法是在有真实标签(ground truth)的情况下,将聚类结果与真实标签进行比较。然而,由于K均值是一种无监督算法,在现实问题中通常没有可用的真实标签。
尽管如此,我们仍然可以基于K均值算法的目标来衡量每个簇的“坏”程度。常用的评估指标是簇内平均距离,即一个簇内所有数据点之间的平均距离。此外,数据点到其所属簇质心的平均距离也可以作为聚类算法的误差度量。

确定最佳聚类数K
在数据聚类中,确定数据集中的簇数量(即K均值中的K值)是一个常见难题。K的正确选择通常是模糊的,因为它高度依赖于数据集中点的分布形状和尺度。
有一些方法可以解决这个问题,其中一种常用技术是:针对不同的K值运行聚类算法,并观察聚类的准确性指标。这个指标可以是数据点与其簇质心之间的平均距离,它反映了簇的密集程度,或者说我们最小化聚类误差的程度。
通过观察该指标随K值变化的情况,我们可以找到最佳的K值。但问题是,随着簇数量K的增加,质心到数据点的距离总会减小。这意味着增加K总会降低误差值。
因此,我们将该指标的值作为K的函数绘制成图,并确定肘点——即下降速率发生急剧变化的拐点。这个肘点对应的K值就是聚类的合适数量。这种方法被称为肘部法则。

总结与回顾
本节课中我们一起学*了K均值聚类的更多细节。让我们来回顾一下K均值的关键特性:
- K均值是一种基于划分的聚类算法,对中型和大型数据集具有相对较高的效率。
- 它倾向于产生类球形的簇,因为簇是围绕质心形成的。
- 它的一个缺点是必须预先指定簇的数量K,而这并非易事。

通过掌握算法步骤、评估方法和确定K值的技巧,你将能更有效地应用K均值算法解决实际问题。
深度学*基础:第0章:欢迎与课程概述 🎉
在本节课中,我们将要学*这门深度学*基础课程的总体安排、核心模块以及学*目标。课程旨在为零基础的学员提供清晰的入门路径。
大家好,欢迎来到CAS Co.的深度学*基础课程。我是Alex Eelson,本课程的讲师。在这门入门课程中,我将尝试教授数据科学领域最热门的话题之一——深度学*的基础知识。本课程包含四个模块,预计可在四周内完成。
课程模块概览
以下是本课程四个核心模块的简要介绍。
-
模块一:动机与基础
我们将探讨深度学*令人兴奋的应用,以激发学*兴趣。同时,我们将简要介绍大脑中的神经元与神经网络,理解它们如何启发人工神经网络。本模块的重点是学*人工神经网络的前向传播过程。 -
模块二:神经网络如何学*
我们将继续学*人工神经网络,并聚焦于其学*机制。核心内容包括理解梯度下降算法以及各种激活函数的作用。 -
模块三:深度学*库入门
我们将学*一些最流行的深度学*库,即Keras、PyTorch和TensorFlow。重点是学*如何使用Keras库为回归和分类问题构建模型。 -
模块四:监督与非监督网络
我们将学*监督式和非监督式深度神经网络,主要包括卷积神经网络、循环神经网络和自编码器。我们还将学*如何使用Keras库构建一个卷积神经网络。

重要说明与课程定位
在结束本视频前,我想做一个重要说明。
在本课程中,我决定专注于深度学*的基础知识。深度学*如今是一个广阔的领域,并且正在快速持续地发展。因此,这个领域对许多人来说可能令人生畏。所以,我为本课程设定的对象是那些对深度学*或神经网络真正一无所知的人。
如果你已经大量使用过人工神经网络和深度学*模型,那么这门课程可能不适合你。你仍然可以将其作为复*课程,但可能不会学到太多新知识。虽然我会向你介绍深度学*中的一些高级主题,但我只会提供它们的简化版本。我在此澄清这一点,是为了在课程一开始就设定正确的期望。


现在,再次欢迎大家,让我们开始学*吧。
生成式人工智能工程:081:深度学*简介
在本节课中,我们将开始探讨深度学*,并了解该领域的最新进展如何催生出令人惊叹的应用。
深度学*无疑是数据科学中最热门的话题之一。尤其是在深度学*的帮助下,涌现出大量令人着迷的项目,其中许多在十多年前还被认为是几乎不可能实现的。因此,深度学*领域充满了巨大的热情。本节中,我将分享一些*期令人惊叹的深度学*应用,希望能进一步激发你对深度学*的兴趣和动力。

以下是几个具体的应用示例:
-
图像着色:该应用能将给定的灰度图像自动转换为彩色图像。日本的一组研究人员利用卷积神经网络构建了一个系统,可以处理如下的灰度图像,并通过着色为其注入生机。你可以在视频下方的链接中找到更多精彩案例。
![]()
-
语音驱动口型合成:这是一个非常酷但也令人不安的应用。它能将一段音频与视频合成,并使视频中人物的口型与音频中的声音和单词同步。过去有许多构建此类系统的尝试,但大多效果不佳。最*,华盛顿大学的一组研究人员通过在一个人的大量视频数据上训练循环神经网络,构建了首个能生成逼真结果的系统。他们的案例研究对象是美国前总统巴拉克·奥巴马。让我们看一个例子:这是一段奥巴马演讲的音频片段。
“It's been less than a week since the deadliest mass shooting in American history.”
这段音频被合成到了他另一段演讲的视频中,他的口型与音频中的词语和声音同步。观看视频时,我们几乎无法分辨视频是合成的。
![]()
不仅如此,他们的系统还能从一段视频中提取音频,并将另一段视频中的口型与这段提取的音频同步。让我们看一个这样的例子。
“Especially our friends who were lesbian, gay, bisexual or transgender. I visited with the families of many of the victims on Thursday, and one thing I told them is that they're not alone.”
![]()

- 自动手写生成:多伦多大学的Alex Graves使用循环神经网络设计了一种算法,能够以高度逼真、风格多样的草书笔迹重写给定的信息。你可以输入一些文本,然后选择生成的手写风格,或者让算法随机为你选择。
除了上述应用,深度学*还有许多其他引人入胜的应用场景:

- 自动机器翻译:使用卷积神经网络实时翻译图像中的文字。
- 为无声电影自动添加音效:深度学*模型利用预录制的声音数据库,选择与场景内容最匹配的声音进行播放。
- 图像中的物体分类以及自动驾驶汽车等流行应用。
在几乎所有上述应用中,你都反复听到了“神经网络”这个词。你可能会问:神经网络不是已经存在很长时间了吗?为什么它们突然开始流行起来,并催生出无穷无尽的应用?
为了回答这个问题,让我们开始学*神经网络和深度学*的具体内容。


本节课中,我们一起了解了深度学*如何通过卷积神经网络和循环神经网络等模型,在图像着色、语音合成、手写生成等多个领域创造出突破性的应用。这些成就解释了为何深度学*在当下如此备受瞩目。
生成式人工智能工程:082:神经元与神经网络 🧠
在本节课中,我们将要学*深度学*算法的核心灵感来源——生物神经元与神经网络的工作原理。我们将从神经元的早期发现讲起,逐步理解其结构,并最终将其与人工神经元进行类比,为后续学*神经网络的信息处理方式打下基础。

神经元的早期发现
深度学*领域所使用的算法,其灵感很大程度上来源于大脑中神经元和神经网络处理数据的方式。


上图是最早的神经元绘图之一。这幅图由圣地亚哥·拉蒙·卡哈尔于1899年绘制,基于他在显微镜下观察鸽子大脑后的所见。他如今被誉为现代神经科学之父。根据他的绘图,神经元(其中一个标记为“a”)中间有一个较大的胞体,并伸出长长的“手臂”,这些“手臂”会分叉并与其他神经元相连接。
神经网络的密集结构


这里的另一张图片展示的是神经网络,它包含了看起来像脑组织中的成千上万个神经元。这张图让你感受到它们排列得多么紧密,以及在一小块脑组织中存在多少神经元。
回到拉蒙·卡哈尔的神经元绘图,让我们将其向左旋转90度。


我敢打赌,现在它看起来有点眼熟了,因为它与你可能见过的人工神经网络绘图略有相似。
生物神经元的结构与功能

这是一幅神经元的卡通示意图。神经元的主体称为胞体,其中包含神经元的细胞核。从胞体伸出的大量“手臂”网络称为树突。而从胞体另一端伸出的长“手臂”则称为轴突。轴突末端的细须被称为终末按钮或突触。
以下是神经元信息传递的基本流程:
- 树突接收来自传感器或其他相邻神经元终末按钮的电脉冲,这些脉冲携带着信息或数据。
- 树突将脉冲或数据传递到胞体和细胞核。
- 电脉冲或数据通过组合在一起进行处理。
- 处理后的信息被传递给轴突。
- 轴突将处理后的信息携带至终末按钮或突触。
- 这个神经元的输出成为其他成千上万个神经元的输入。
大脑中的学*过程是通过反复激活某些特定的神经连接(而非其他连接)来实现的,这强化了这些连接。这使得它们在给定特定输入时,更有可能产生期望的结果。一旦期望的结果出现,导致该结果的神经连接就会得到加强。
从生物到人工:人工神经元


人工神经元的行为方式与生物神经元相同,因此它也由胞体、树突和轴突组成,以将本神经元的输出传递给其他神经元。轴突的末端可以分叉以连接到许多其他神经元,但为了简化,我们在这里只显示一个分支。学*过程也非常类似于大脑中的学*方式,你将在接下来的几个视频中看到这一点。
迈向数学建模

既然我们已经理解了人工神经元的不同部分,接下来让我们学*如何阐述人工神经网络处理信息的方式。



本节课中,我们一起学*了神经元与神经网络的基础知识。我们从神经元的早期发现开始,了解了其基本结构(胞体、树突、轴突、突触)和信息传递流程,并看到了生物神经元如何启发了结构相似的人工神经元设计。理解这一生物学基础,是掌握后续深度学*算法原理的关键第一步。
生成式人工智能工程:083:人工神经网络

在本节课中,我们将学*人工神经网络的数学表述。
概述

我们将从理解单个神经元的基本数学模型开始,逐步扩展到多层神经网络。核心内容包括神经网络的层级结构、前向传播的计算过程,以及激活函数的作用。
神经网络的结构
在上一节中,我们了解到人工神经元的形状是为了模仿真实的生物神经元而设计的。


对于一个神经元网络,我们通常将其划分为不同的层。

- 输入层:将数据馈送到网络的第一层。
- 输出层:提供网络最终输出结果的节点集合。
- 隐藏层:位于输入层和输出层之间的任何节点集合。

核心概念:前向传播、反向传播与激活函数
在处理神经网络时,我们主要涉及三个主题:前向传播、反向传播和激活函数。本节视频将重点介绍前向传播,并通过数字示例进行解释。

前向传播详解
前向传播是数据从神经网络的输入层开始,逐层穿过神经元,最终到达输出层的过程。
让我们从一个神经元开始,用数学公式描述信息流经它的方式,如下图所示。数据通过连接(或称树突)流经每个神经元。每个连接都有一个特定的权重,用于调节数据流。

这里,x1 和 x2 是两个输入值(可以是整数或浮点数)。当这些输入通过连接时,会根据连接权重 W1 和 W2 进行调整。神经元随后通过输出这些输入的加权和来处理这些信息,它还会在和中添加一个常数,称为偏置。
因此,这里的 Z 是输入、权重和偏置的线性组合。A 是神经元的输出。为保持一致性,我们在整个课程中将坚持使用这些字母:Z 始终代表输入的线性组合,A 始终代表神经元的输出。
然而,仅仅输出输入的加权和会限制神经网络能够执行的任务。因此,更好的数据处理方式是将加权和映射到一个非线性空间。一个常用的函数是 Sigmoid 函数:如果加权和是一个非常大的正数,那么神经元的输出接* 1;如果加权和是一个非常大的负数,那么神经元的输出接* 0。
像 Sigmoid 函数这样的非线性变换被称为激活函数。激活函数是人工神经网络另一个极其重要的特性,它们基本上决定了一个神经元是否应该被激活,换句话说,神经元接收到的信息是相关的还是应该被忽略。
这里的关键信息是:没有激活函数的神经网络本质上只是一个线性回归模型。激活函数对输入执行非线性变换,使神经网络能够学*和执行更复杂的任务,如图像分类和语言翻译。
为了进一步简化,我将使用一个只有一个神经元和一个输入的神经网络进行说明。
计算示例:单神经元网络
让我们看一个如何计算输出的例子。假设输入 x1 的值是 0.1,我们想预测这个输入对应的输出。网络已经优化了权重和偏置,其中 W1 是 0.15,B1 是 0.4。
第一步是计算 Z,它是输入与对应权重的点积加上偏置:
Z = (x1 * W1) + B1 = (0.1 * 0.15) + 0.4 = 0.415
然后,神经元使用 Sigmoid 函数对 Z 应用非线性变换。Sigmoid 函数的公式为:
A = σ(Z) = 1 / (1 + e^(-Z))
因此,神经元的输出 A 为:
A = 1 / (1 + e^(-0.415)) ≈ 0.6023
扩展到双神经元网络
对于一个有两个神经元的网络,第一个神经元的输出将成为第二个神经元的输入。其余过程完全相同。
第二个神经元接收输入 A1,计算输入(本例中为 A1)与权重 W2 的点积,并加上偏置 B2:
Z2 = (A1 * W2) + B2

假设 W2 = 0.2, B2 = 0.5,则:
Z2 = (0.6023 * 0.2) + 0.5 = 0.62046
再次使用 Sigmoid 函数作为激活函数,网络的最终输出 A2 为:
A2 = σ(Z2) = 1 / (1 + e^(-0.62046)) ≈ 0.7153
这个值(0.7153)就是输入 0.1 对应的预测值。本质上,这就是神经网络为任何给定输入预测输出的方式。无论网络变得多么复杂,过程都是完全相同的。

总结
本节课中,我们一起学*了人工神经网络的基础数学表述。总结如下:

给定一个具有一组权重和偏置的神经网络,你应该能够计算网络对于任何给定输入的输出。我们了解了神经网络的层级结构(输入层、隐藏层、输出层),深入探讨了前向传播的过程,并认识了激活函数(如 Sigmoid 函数)在引入非线性、使网络能够处理复杂任务中的关键作用。
在下一节视频中,我们将开始学*如何训练神经网络并优化其权重和偏置。


生成式人工智能工程:084:梯度下降 📉
在本节课中,我们将学*梯度下降算法。这是一种用于寻找函数最小值的迭代优化算法,在机器学*和神经网络训练中至关重要。我们将了解成本函数在散点图中的表示方式,理解梯度下降算法的工作原理,并描述算法不同迭代步骤在散点图中的表现。
成本函数与最佳拟合线
在开始探讨梯度下降之前,我们先理解一个核心概念:成本函数。假设我们有一些数据,如下方散点图所示。为简化起见,我们生成了数据,其中 z 的值是 x 值的两倍。



现在,我们希望找到一个 W 值,使得由 z = W * x 定义的直线能最好地拟合这些数据。为此,我们定义一个成本函数或损失函数 J。
一个常见的成本函数如下所示:
J = Σ (z_i - W * x_i)^2
其中,我们计算每个数据点的 z 值与 W * x 的差值,将其平方,并对所有数据点的平方差求和。最佳的 W 值就是能使这个成本函数值最小的那个值。
观察这个成本函数的图像,我们发现它是一个抛物线,具有一个全局最小值(即唯一解)。

对于给定的数据,使成本函数最小的 W 值是 W = 2,这意味着 z = 2x,这条直线将完美地拟合所有数据点。
然而,这是一个非常简化的例子。在现实世界的数据集中,目标变量 Z 通常依赖于多个变量,我们无法简单地绘制成本函数图并通过视觉直接确定最佳的权重值。
梯度下降算法介绍
那么,我们如何确定最佳的 W 值(或在需要优化多个权重时,最佳的 W 集合)呢?答案是通过一个称为梯度下降的算法。
梯度下降是一种用于寻找函数最小值的迭代优化算法。为了使用梯度下降找到函数的最小值,我们沿着当前点函数梯度的负方向,按一定比例(步长)移动。
这是什么意思呢?让我们用一个简单的过程来解释。
梯度下降的迭代过程
我们从一个随机的初始 W 值开始,假设为 w0 = 0.2。
- 计算梯度:在当前位置
w0,计算损失函数的梯度。梯度由W = 0.2处切线的斜率给出。 - 确定步长:步长的大小由一个称为学*率的参数控制。学*率越大,我们迈出的步伐就越大;学*率越小,步伐就越小。
- 更新参数:我们沿着梯度下降的方向迈出一步,更新
W值。更新公式为:
w1 = w0 - 学*率 * 在 w0 处的梯度
这代表了算法的第一次迭代。 - 重复迭代:在
w1处,我们重复相同的过程:计算在w1处的梯度,使用相同的学*率控制朝向最小值的步长。我们反复执行此步骤,直到达到最小值,或成本函数值非常接*最小值(在一个预先定义的小阈值内)。
学*率的选择与影响
选择学*率时必须谨慎。
- 学*率过大:会导致步伐过大,可能“跨过”最小值点,甚至导致算法无法收敛。
- 学*率过小:会导致步伐极小,使得算法需要非常长的时间才能找到最小值点。
梯度下降迭代可视化
现在,让我们看看在学*率为 0.4 的情况下,每次迭代如何影响生成的直线对数据的拟合效果。下图左侧展示了拟合直线的变化,右侧展示了 W 值在成本函数曲线上的移动。

- 初始化:我们将
W初始化为 0,这意味着z = 0,是一条水平线。此时成本很高,直线拟合效果很差。 - 第一次迭代后:由于在
W = 0处斜率非常陡峭,W值向 2 靠*了一大步。新的W值导致损失函数大幅下降。生成的直线比初始直线拟合得更好,但仍有改进空间。 - 第二次迭代后:
W继续向 2 移动。由于此时的斜率不如之前陡峭,步伐没有第一次大,但成本函数值仍在下降。生成的直线更接*理想的最佳拟合线。 - 第三次与第四次迭代后:观察到同样的趋势。经过四次迭代,
W值几乎达到 2,生成的直线几乎完美地拟合了散点图。
在每次迭代中,权重更新的方向与当前点函数梯度的负方向成比例。因此,如果你将权重初始化为最小值右侧的值,那么正的梯度将导致 W 向左移动,朝向最小值。

总结
本节课中,我们一起学*了梯度下降算法。
- 我们了解到,最佳权重值是通过梯度下降这一迭代优化算法确定的。
- 梯度下降通过计算成本函数的梯度,并沿负梯度方向更新参数,逐步逼*函数的最小值点。
- 学*率是一个关键超参数:过大会导致错过最优点;过小则会导致收敛速度过慢。
- 通过可视化迭代过程,我们直观地看到了权重如何更新,以及拟合直线如何逐步逼*数据的最佳拟合状态。


理解梯度下降是掌握神经网络如何学*和优化其权重与偏差的基础。
生成式人工智能工程:085:反向传播 🧠
在本节课中,我们将学*神经网络如何通过反向传播算法来训练和优化其权重与偏置。我们将从计算预测误差开始,逐步推导如何利用链式法则将误差反向传播至网络中的每一层,并更新参数。

在之前的视频中,我们讨论了神经网络如何使用前向传播进行预测,并假设网络已经拥有优化后的权重和偏置。然而,神经网络是如何针对特定问题和数据集来训练并优化这些权重与偏置的呢?
训练过程在监督学*的环境下进行,其中每个数据点都有对应的标签或真实值。当神经网络的预测值与给定输入的真实值明显不匹配时,就需要进行训练。
训练过程首先计算预测值与真实标签之间的误差 E。这个误差即代表我们在梯度下降视频中讨论过的成本或损失函数。因此,下一步是将此误差反向传播到网络中,并利用它来对网络中不同的权重和偏置执行梯度下降,使用我们在梯度下降视频中见过的相同方程进行优化。
对于一个仅有两个神经元、接收一个输入的简单网络,我们计算真实值 T 与预测值 A2 之间的平方误差。这代表了我们的成本或损失函数。当然,通常不会仅用一个数据点来训练网络,而是使用成千上万个数据点。因此,误差计算为如下所示的均方误差:
公式: E = (1/n) * Σ(T - A2)^2
然后,我们通过将误差反向传播到网络中,利用它来更新 W2、B2、W1 和 B1。
更新权重 W2

从梯度下降视频中我们知道,需要使用以下方程来更新 W2:
公式: W2_new = W2_old - α * (∂E/∂W2)
但由于误差并非 W2 的显式函数,我们需要使用链式法则来确定误差相对于 W2 的导数。
我们知道 E 是 A2 的函数,A2 是 Z2 的函数,而 Z2 是 W2 的函数。因此,我们可以分别求导:
公式: ∂E/∂W2 = (∂E/∂A2) * (∂A2/∂Z2) * (∂Z2/∂W2)
其中:
∂E/∂A2 = -(T - A2)∂A2/∂Z2 = A2 * (1 - A2)(假设使用Sigmoid激活函数)∂Z2/∂W2 = A1
因此,W2 的更新方程如下:
公式: W2_new = W2_old - α * [-(T - A2) * A2(1 - A2) * A1]
更新偏置 B2
更新 B2 的过程完全相同,唯一的区别在于 ∂Z2/∂B2 = 1,而不是输入 A1。
公式: B2_new = B2_old - α * [-(T - A2) * A2(1 - A2) * 1]
更新权重 W1 和偏置 B1
要更新更早层的参数(如 W1),我们再次使用链式法则,因为误差与 W1 没有直接关系。
误差 E 到 W1 的依赖路径更长:E -> A2 -> Z2 -> A1 -> Z1 -> W1。因此,其导数为这些中间导数的乘积:
公式: ∂E/∂W1 = (∂E/∂A2) * (∂A2/∂Z2) * (∂Z2/∂A1) * (∂A1/∂Z1) * (∂Z1/∂W1)
其中:
∂E/∂A2 = -(T - A2)∂A2/∂Z2 = A2 * (1 - A2)∂Z2/∂A1 = W2∂A1/∂Z1 = A1 * (1 - A1)(假设使用Sigmoid激活函数)∂Z1/∂W1 = X1(输入)
因此,W1 的更新方程是一个较长的表达式:

公式: W1_new = W1_old - α * [-(T - A2) * A2(1 - A2) * W2 * A1(1 - A1) * X1]
使用相同的方法,我们可以推导出更新偏置 B1 的表达式(∂Z1/∂B1 = 1)。
反向传播实例演示
现在,让我们将反向传播应用到前向传播视频中的例子。回顾一下,我们有一个包含两个神经元的网络,其权重和偏置的初始值如图所示。
我们应用前向传播,计算出:
Z1 = 0.415A1 = 0.6023Z2 = 0.9210A2 = 0.7153(这是网络对输入X1=0.1的预测值)
现在假设真实值 T = 0.25。我们首先计算预测值与真实值之间的误差。
接下来,我们将开始更新权重和偏置。迭代可以持续一个预定义的次数(例如1000个周期),或者直到误差低于一个预定义的阈值(例如0.001)。
假设学*率 α = 0.4,让我们看看第一次迭代后权重和偏置如何变化。
以下是更新计算:
- 更新 W2:代入
T、A2、A1的值计算梯度。∂E/∂W2 = 0.05706,因此W2更新为0.407。 - 更新 B2:代入
T、A2的值计算梯度。∂E/∂B2 = 0.0948,因此B2更新为0.612。 - 更新 W1:代入
T、A2、W2、A1、X1的值计算梯度。∂E/∂W1 = 0.001021,因此W1更新为0.1496。 - 更新 B1:
∂E/∂B1 = 0.01021,因此B1更新为0.3959。
这就完成了训练过程的第一次迭代或周期。使用更新后的权重和偏置,我们进行新一轮的前向传播,计算新的预测值,与真实值比较,计算新的误差,然后再进行一轮反向传播。
训练算法总结

综上所述,神经网络的训练算法可以概括为以下步骤:
首先,将权重和偏置初始化为随机值。
然后,迭代重复以下步骤:
- 前向传播:计算网络的输出。
- 计算误差:计算网络预测输出与真实值之间的误差。
- 反向传播:通过反向传播更新权重和偏置。
重复以上三个步骤,直到达到预定的迭代次数(周期),或者预测输出与真实值之间的误差低于预定义的阈值。

在本节课中,我们一起学*了反向传播的基本原理和计算过程。我们看到了如何利用链式法则将输出层的误差梯度逐层反向传播,以更新网络中每一层的权重和偏置。这是神经网络能够从数据中学*的关键机制。

在下一个视频中,我们将继续讨论反向传播算法,并指出当Sigmoid函数用作深度网络隐藏层的激活函数时,存在的一个严重缺陷。
生成式人工智能工程:086:梯度消失问题
在本节课中,我们将讨论一个与Sigmoid激活函数相关的问题,正是这个问题曾阻碍了神经网络更早地蓬勃发展。这个问题被称为梯度消失问题。

概述
上一节我们介绍了反向传播算法。本节中,我们将深入探讨一个在早期神经网络训练中普遍存在的难题——梯度消失。理解这个问题,是理解为何现代神经网络采用不同激活函数的关键。
梯度消失问题详解
回忆上一节中那个非常简单的、仅有两个神经元的网络。误差相对于权重的导数如下所示:


可以看到梯度值非常小,但更重要的是,误差相对于第一个权重 W1 的梯度尤其微小。

问题的根源在于,当我们使用Sigmoid函数作为激活函数时,网络中所有中间层的输出值都被限制在0到1之间。
在反向传播过程中,我们不断地将小于1的因子相乘,导致梯度随着我们在网络中向后传播而变得越来越小。这意味着,与网络中后面层的神经元相比,前面层的神经元学*速度非常缓慢。
以下是梯度消失导致的核心影响:
- 早期层训练最慢:网络中最靠*输入层的部分参数更新幅度极小。
- 训练过程漫长:整体网络收敛需要极长的时间。
- 预测精度受损:由于早期层未能充分学*,模型无法捕捉数据中的深层特征,导致性能不佳。
因此,我们不使用Sigmoid函数或类似函数作为激活函数,因为它们容易导致梯度消失问题。其导数公式 σ‘(x) = σ(x)(1 - σ(x)) 的最大值仅为0.25,在链式求导中极易使梯度指数级衰减。

总结与过渡
本节课中,我们一起学*了梯度消失问题的成因及其对神经网络训练的严重影响。它解释了为何Sigmoid等函数不再被用作隐藏层的默认激活函数。
正因为存在梯度消失的挑战,研究人员开始寻找更好的替代方案。因此,在下一节课中,我们将学*其他几种后来变得极为流行、如今几乎在所有神经网络隐藏层中使用的激活函数。这些函数正是为了帮助克服梯度消失问题而被广泛采纳的。


生成式人工智能工程:087:激活函数 🧠
在本节课中,我们将要学*神经网络中的激活函数。激活函数在神经网络的学*过程中扮演着核心角色,它决定了神经元是否应该被激活,并将输入信号转换为输出信号。我们将介绍几种常见的激活函数,分析它们的优缺点,并了解它们各自适用的场景。
概述

正如我们之前所讨论的,激活函数在神经网络的学*过程中起着重要作用。到目前为止,我们在网络中只使用了Sigmoid函数作为激活函数。但Sigmoid函数有其缺点,它可能导致早期层出现梯度消失问题。在本视频中,我们将讨论其他激活函数,这些函数使用效率更高,并且更适用于深度学*应用。
激活函数类型
以下是构建神经网络时可以使用的七种激活函数类型:
- 二元阶跃函数
- 线性或恒等函数
- Sigmoid或Logistic函数
- 双曲正切或Tanh函数
- 修正线性单元函数
- 带泄露的ReLU函数
- Softmax函数

在本视频中,我们将讨论其中流行的几种,即Sigmoid函数、双曲正切函数、ReLU函数和Softmax函数。
Sigmoid函数
上一节我们介绍了激活函数的类型,本节中我们来看看最经典的Sigmoid函数。

这是Sigmoid函数。当 Z = 0 时,a = 0.5。当 Z 是一个非常大的正数时,a 接* 1。当 Z 是一个非常大的负数时,a 接* 0。
Sigmoid函数过去曾被广泛用作神经网络隐藏层的激活函数。然而,正如你所见,该函数在正负3区间之外的部分非常平坦。这意味着一旦函数值落入该区域,梯度会变得非常小。这导致了我们讨论过的梯度消失问题。随着梯度趋*于零,网络实际上无法有效学*。
Sigmoid函数的另一个问题是其值域仅在 0 到 1 之间。这意味着Sigmoid函数不是关于原点对称的,并且其输出值全部为正。然而,我们并非总是希望传递到下一个神经元的信号全部具有相同的符号。这个问题可以通过缩放Sigmoid函数来解决,这引出了下一个激活函数:双曲正切函数。

双曲正切函数
了解了Sigmoid函数的局限性后,我们来看一个它的改进版本。


这是双曲正切或Tanh函数,它与Sigmoid函数非常相似,实际上是Sigmoid函数的一个缩放版本。但与Sigmoid函数不同,它关于原点对称,值域在 -1 到 +1 之间。然而,尽管它克服了Sigmoid函数缺乏对称性的问题,但在非常深的神经网络中,它同样会导致梯度消失问题。
ReLU函数
为了解决梯度消失问题并提升训练效率,ReLU函数应运而生,并成为目前最流行的选择。

修正线性单元函数是当今设计网络时使用最广泛的激活函数。除了其非线性特性外,ReLU函数相对于其他激活函数的主要优势在于它不会同时激活所有神经元。
根据这里的图示,如果输入是负数,它将被转换为零,该神经元不会被激活。这意味着在任何时刻,只有少数神经元被激活,使得网络稀疏且非常高效。此外,ReLU函数是深度学*领域克服梯度消失问题的主要进展之一。
Softmax函数
最后,我们将讨论一个专门为分类任务设计的激活函数。
Softmax函数也是一种Sigmoid函数,但在处理分类问题时非常方便。Softmax函数通常用于分类器模型的输出层,目的是获取概率来定义每个输入所属的类别。
例如,如果一个具有三个输出神经元的网络输出值为 [1.6, 0.55, 0.98],那么经过Softmax激活函数后,输出将被转换为 [0.51, 0.18, 0.31]。这样,我们就能更容易地对给定数据点进行分类,并确定它属于哪个类别。
总结
本节课中我们一起学*了神经网络中几种关键的激活函数。

总而言之,Sigmoid和Tanh函数如今在许多应用中已被避免使用,因为它们可能导致梯度消失问题。ReLU函数是当前广泛使用的函数,需要注意的是,它通常只用于隐藏层。最后,在构建模型时,你可以从使用ReLU函数开始,如果ReLU函数性能不佳,再尝试切换到其他激活函数。

本视频关于激活函数的内容到此结束,我们下个视频再见。
生成式人工智能工程:088:深度学*库 🧠

在本节课中,我们将学*不同的深度学*库和框架。在开始构建深度学*网络之前,了解可用的工具至关重要。本节将简要介绍本系列课程中将重点讲解的几个主流库。

主流深度学*库概览
上一节我们明确了学*深度学*库的重要性,本节中我们来看看目前最流行的几个选择。
根据流行度降序排列,最主要的库是 TensorFlow、Keras 和 PyTorch。此外还存在一个名为 Theano 的库,它由蒙特利尔学*算法研究所开发,甚至在 TensorFlow 和 PyTorch 出现之前就是深度学*开发的主要库。然而,其创始人无法持续提供支持和维护,因此该库逐渐失去了流行度。鉴于此,在本系列课程中,我们将专注于另外三个流行的库。

三大库详解
在介绍了主要的库之后,我们来深入了解这三个库各自的特点和适用场景。
TensorFlow
TensorFlow 是其中最流行的库。它是深度学*模型生产环境中主要使用的库,拥有非常庞大的社区。只需快速浏览该库在 GitHub 仓库上的分支数量、提交次数和拉取请求数量,就足以了解其受欢迎程度。TensorFlow 由 Google 开发,于 2015 年向公众发布,目前仍在 Google 内部积极用于研究和生产需求。
PyTorch
另一方面,PyTorch 是 Torch 框架的衍生产品。Torch 框架使用 Lua 语言,特别支持在 GPU 上运行的机器学*算法。然而,PyTorch 并非仅仅是一套支持 Python 这类流行语言的包装器。它实际上是经过重写和定制的,旨在追求速度并保持原生感。PyTorch 于 2016 年发布,最*获得了极大的关注,并且在某些领域正成为比 TensorFlow 更受青睐的选择,特别是在学术研究环境以及需要优化自定义表达式的深度学*应用中。PyTorch 由 Facebook 支持并积极使用。
Keras
然而,尽管 PyTorch 和 TensorFlow 非常流行,但它们并不易用,学*曲线陡峭。因此,对于刚刚开始学*深度学*的人来说,没有比 Keras 库更好的选择了。Keras 是一个用于构建深度学*模型的高级 API。它因其易用性和语法简洁性而备受青睐,能够促进快速开发。正如您将在接下来的几个视频中看到的,使用 Keras 只需几行代码就能构建非常复杂的深度学*网络。
Keras 通常运行在 TensorFlow 这样的低级库之上。这意味着要使用 Keras 库,您必须先安装 TensorFlow。当您导入 Keras 时,它会明确显示用于安装 Keras 库的后端。Keras 同样由 Google 支持。
以下是三个库的核心特点对比:
- TensorFlow:生产环境首选,社区庞大,由 Google 支持。
- PyTorch:研究领域流行,动态计算图,由 Facebook 支持。
- Keras:高级 API,易于上手,语法简洁,快速开发。
我不会深入探讨不同库的更多细节,但这里的关键要点是:如果您想快速构建某些东西,请选择 Keras 库,您不会失望。但是,如果您想对网络中的不同节点和层有更多控制,并想密切关注网络随时间的变化,那么 PyTorch 或 TensorFlow 将是更合适的库。这最终将取决于您的个人偏好。
总结与预告

本节课中我们一起学*了当前主流的三大深度学*库:TensorFlow、PyTorch 和 Keras。我们了解了它们各自的历史背景、主要特点及适用场景。简单来说,Keras 适合快速入门和原型开发,PyTorch 在研究和灵活性上占优,而 TensorFlow 则在生产部署和社区生态上更强。

基于此,在接下来的视频中,我们将开始学*如何使用 Keras 库来构建回归和分类问题的模型。
生成式人工智能工程:089:使用Keras构建回归模型

在本节课中,我们将学*如何使用Keras库来构建深度学*模型。我们将从解决回归问题开始,通过一个具体的混凝土抗压强度预测案例,展示构建、训练和使用神经网络的全过程。

数据准备与问题定义
上一节我们介绍了本课程的目标,本节中我们来看看具体的回归问题示例。
这里有一个关于混凝土抗压强度的数据集。数据记录了不同混凝土样本的抗压强度,该强度基于制作它们时所用各种材料的体积。例如,第一个样本包含540立方米的 cement、0立方米的 blast furnace slag、0立方米的 fly ash、162立方米的水、2.5立方米的 superplasticizer、1040立方米的 coarse aggregate 和 676立方米的 fine aggregate。这种养护了28天的混凝土混合物,其抗压强度为79.99兆帕。
数据以Pandas DataFrame的形式存储,名为 concrete_data。
我们的目标是使用Keras库快速构建一个深度神经网络来建模这个数据集,从而能够根据给定混凝土样本的配料成分自动预测其抗压强度。
网络架构设计

在开始编码之前,我们需要设计网络架构。我们计划构建的深度神经网络将接收所有八个特征作为输入。
以下是网络的结构:
- 输入层接收八个特征。
- 第一个隐藏层包含五个节点,使用ReLU激活函数。
- 第二个隐藏层同样包含五个节点,使用ReLU激活函数。
- 输出层包含一个节点,用于输出预测的抗压强度值。
请注意,在实际应用中,每个隐藏层通常会使用更多的神经元(例如50或100个),但为了简化说明,我们这里使用一个小型网络。这种每一层的所有节点都与下一层的所有节点相连的网络,被称为密集网络或全连接网络。
数据预处理
在使用Keras库之前,我们需要将数据准备成正确的格式。

唯一需要做的是将DataFrame拆分为两个部分:一个包含所有预测特征列,另一个包含目标列。我们将它们分别命名为 predictors 和 target。
# 假设 concrete_data 是包含所有数据的DataFrame
# 最后一列是目标变量‘Strength’
predictors = concrete_data.iloc[:, :-1] # 获取除最后一列外的所有列作为特征
target = concrete_data.iloc[:, -1] # 获取最后一列作为目标

使用Keras构建与训练模型
现在,准备见证Keras的魔力。构建这样一个网络、训练它并用它预测新样本,只需要几行代码即可实现。
首先,需要导入必要的模块。因为我们的网络由线性堆叠的层构成,所以需要使用Sequential模型。这在大多数情况下都是适用的,除非你要构建一些特殊的结构。
import keras
from keras.models import Sequential
from keras.layers import Dense
创建模型非常简单,只需调用Sequential构造函数。构建网络层也同样直接。我们使用 add 方法来添加每一个Dense层。我们需要指定每层的神经元数量以及要使用的激活函数。根据之前关于激活函数的讨论,ReLU是隐藏层的推荐激活函数之一,因此我们将使用它。
对于第一个隐藏层,我们需要传入 input_shape 参数,即数据集中预测特征的数量。
# 定义回归模型
n_cols = predictors.shape[1] # 获取特征数量
model = Sequential()
model.add(Dense(5, activation='relu', input_shape=(n_cols,)))
model.add(Dense(5, activation='relu'))
model.add(Dense(1))
接下来是训练环节。我们需要定义优化器和误差指标。在之前的模块中,我们使用梯度下降作为优化算法,使用均方误差作为预测值与真实值之间的损失度量。这里我们将继续使用均方误差作为损失函数。
至于优化算法,对于深度学*应用,存在比梯度下降更高效的算法,其中之一是Adam。Adam优化器的一个主要优点是,你不需要指定我们在梯度下降视频中看到的学*率,这省去了为模型优化学*率的任务。
# 编译模型
model.compile(optimizer='adam', loss='mean_squared_error')
然后,我们使用 fit 方法来训练模型。
# 训练模型
model.fit(predictors, target, epochs=100)
训练完成后,我们就可以使用 predict 方法开始进行预测了。
# 进行预测
predictions = model.predict(new_data)
视频下方你将找到一份文档,其中包含Keras库不同部分的链接,你可以参考它们以了解更多关于优化器、模型以及Keras库中其他可用方法的信息。
但这里的代码片段通常就是你在Keras中构建回归模型所需了解的全部内容。
总结与预告
本节课中我们一起学*了如何使用Keras库构建一个用于回归问题的深度神经网络。我们涵盖了从理解数据、设计网络架构、预处理数据,到使用Sequential模型、Dense层、Adam优化器进行模型构建、编译和训练的全过程。


在下一个视频中,我们将学*如何使用Keras库构建一个分类模型。
生成式人工智能工程:090:使用Keras构建分类模型

在本节课中,我们将学*如何使用Keras库来构建解决分类问题的模型。我们将通过一个具体的例子——基于汽车价格、维护成本和载客量来评估购买决策——来演示整个过程。
概述
上一节我们介绍了如何使用Keras解决回归问题。本节中,我们来看看如何构建一个用于分类任务的神经网络模型。分类问题与回归问题的核心区别在于,其目标是预测一个离散的类别标签,而非连续的数值。
数据准备
假设我们想构建一个模型,根据汽车的价格、维护成本以及是否能容纳两人或更多人,来告知某人购买某辆汽车是否是一个好选择。
以下是一个名为car_data的数据集。数据已经过清洗,如图所示,我们使用了独热编码将价格、维护成本和载客量的每个类别转换成了单独的列。

- 汽车价格可以是高、中或低。
- 同样,汽车的维护成本也可以是高、中或低。
- 汽车可以容纳两人或更多人。

以数据集中的第一辆车为例,它被认为是一辆昂贵、维护成本高且只能容纳两人的汽车。其决策标签是0,意味着购买这辆车是一个糟糕的选择。
决策标签的含义如下:
- 0: 糟糕的决策
- 1: 可接受的决策
- 2: 好的决策
- 3: 非常好的决策

模型架构

我们将使用与上一节回归问题中结构相同的神经网络。这是一个具有8个输入(或预测变量)的网络,包含两个隐藏层(每层5个神经元)和一个输出层。

数据处理

接下来,我们将数据集划分为预测变量(X)和目标变量(y)。


然而,对于Keras的分类问题,我们不能直接使用目标列。我们需要将该列转换为一个由二进制值组成的数组,类似于独热编码,如下方输出所示。我们可以使用Keras Utils包中的to_categorical函数轻松实现这一点。

换句话说,我们的模型在输出层将不再只有1个神经元,而是会有4个神经元,因为我们的目标变量包含4个类别。

代码实现
以下是构建分类模型的代码结构,它与我们构建回归模型的代码非常相似。
# 导入必要的库
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.utils import to_categorical
# 构建模型
model = Sequential()
model.add(Dense(5, activation='relu', input_shape=(8,))) # 第一隐藏层
model.add(Dense(5, activation='relu')) # 第二隐藏层
model.add(Dense(4, activation='softmax')) # 输出层,4个神经元对应4个类别
# 编译模型
model.compile(optimizer='adam',
loss='categorical_crossentropy', # 分类问题使用交叉熵损失
metrics=['accuracy']) # 评估指标为准确率
# 训练模型
model.fit(X_train, y_train_categorical, epochs=50)


# 进行预测
predictions = model.predict(X_test)
代码说明如下:
- 导入库:我们首先导入Keras库、Sequential模型和Dense层。额外的导入语句是
to_categorical函数,用于将目标列转换为分类所需的二进制数组。 - 构建层:我们使用
add方法创建两个隐藏层,每层5个神经元,并使用ReLU激活函数。请注意,这里我们为输出层指定了softmax激活函数,以确保输出层所有神经元的预测值之和为1,这代表了各个类别的概率分布。 - 编译模型:在定义编译器时,我们将使用分类交叉熵
categorical_crossentropy作为损失度量,而不是回归中使用的均方误差。我们将评估指标指定为accuracy(准确率)。准确率是Keras内置的评估指标,但你也可以定义自己的评估指标并通过metrics参数传入。 - 训练模型:然后我们拟合模型。请注意,这次我们明确指定了训练模型的轮数
epochs。虽然在构建回归模型时我们没有指定,但同样可以这样做。 - 进行预测:最后,我们使用
predict方法进行预测。
理解预测结果
Keras predict方法的输出将类似于下图所示。对于每个数据点,输出是购买给定汽车的决策属于四个类别中每一个的概率。


对于每个数据点,这些概率之和应为1。概率越高,算法就越有信心认为该数据点属于相应的类别。
- 对于测试集中的第一个数据点(第一辆车),决策将是0(不可接受),因为第一个概率值最高,在本例中为0.99或接*1。
- 同样,对于第二个数据点,决策也是0,因为该类别的概率最高,同样为0.99或几乎为1。对于前三个数据点,模型非常确信购买这些汽车是不可接受的。
- 对于最后三个数据点,决策将是1(可接受),因为第二个类别的概率高于其他类别。但请注意,决策0和决策1的概率非常接*,因此模型不是非常确信,但会倾向于接受购买这些汽车。
总结
本节课中,我们一起学*了如何使用Keras构建分类模型。关键步骤包括:准备并转换数据(使用to_categorical)、构建具有softmax输出层的神经网络、使用分类交叉熵作为损失函数进行编译和训练,以及理解模型输出的概率结果。

在实验部分,你将有机会使用Keras库构建自己的回归和分类模型,请务必完成本模块的实验内容。



生成式人工智能工程:091:浅层与深层神经网络 🧠
在本节课中,我们将学*浅层神经网络与深层神经网络之间的区别,并探讨推动深度学*领域突然蓬勃发展的关键因素。



概述
神经网络已经存在了一段时间。然而,它们直到最*才变得“深”并开始广泛应用,催生出许多令人兴奋的应用。本节将解释浅层与深层网络的定义差异,并分析促成这一转变的三个核心驱动力。
浅层神经网络
到目前为止,我们主要讨论的神经网络既不算特别深,也不算特别浅。理解这些网络是理解深层神经网络的基础,因其结构简单而更易于理解。
业界对于浅层神经网络的定义尚未完全统一。但通常认为,仅包含一个或两个隐藏层的神经网络被视为浅层神经网络。
深层神经网络
上一节我们介绍了浅层网络,本节中我们来看看深层网络。
一个包含三个或更多隐藏层,且每层拥有大量神经元的网络,则被视为深层神经网络。


此外,与浅层神经网络通常只处理向量形式的输入不同,深层神经网络能够直接处理原始数据,例如图像和文本,并自动提取必要的特征以更好地学*数据。
深度学*蓬勃发展的原因
神经网络已经存在了一段时间。那么,为什么它们直到最*才变得“深”并开始蓬勃发展呢?以下是促成深度学*领域突然兴起的三个主要因素。
因素一:领域自身的进步
我们在关于激活函数的视频中简要提到过这一点。ReLU激活函数帮助克服了梯度消失问题的挑战,从而为创建非常深的网络打开了大门。
因素二:数据的可用性
深层神经网络在使用大量数据进行训练时效果最佳。由于神经网络能很好地学*训练数据,因此必须使用大量数据来避免对训练数据的过拟合。

如今,前所未有的大量数据变得易于获取,深度学*算法也因此得到了前所未有的尝试和测试。

其他传统的机器学*算法虽然也能从更多数据中获益,但效果提升存在上限。超过该点后,增加数据不会带来显著改善。深度学*则完全不同,你提供的数据越多,它的性能就越好。
因素三:计算能力
这一点与第二点相辅相成。借助超级强大的GPU,我们现在可以在数小时内,而非过去的数天或数周内,在大量数据上训练非常深的神经网络。
因此,用户可以在更短的时间内试验不同的深层神经网络并测试不同的原型。
总结



本节课中,我们一起学*了以下核心内容:
- 一个具有一个隐藏层的神经网络被认为是浅层神经网络。
- 一个具有许多隐藏层且每层有许多神经元的网络被认为是深层神经网络。
- 浅层神经网络通常只将输入作为向量处理。
- 深层神经网络可以直接处理原始数据,如图像和文本。
- 深度学*领域的突然兴起可归因于三个主要因素:领域的进步、数据的可用性以及更强大的计算能力。
生成式人工智能工程:092:卷积神经网络 🧠
在本节课中,我们将学*深度学*算法。我们将从监督式深度学*算法开始,并重点介绍卷积神经网络。
概述
卷积神经网络与我们目前在本课程中见过的神经网络非常相似。它们由神经元组成,这些神经元的权重和偏置需要被优化。每个神经元通过计算每个输入与对应权重之间的点积来组合其接收到的输入,然后将得到的总输入馈入一个激活函数,最常用的是ReLU。
那么,这些网络有何不同,为何被称为卷积神经网络呢?
卷积神经网络,简称CNN,明确假设输入是图像。这一假设允许我们将某些特性融入其架构中。这些特性使得前向传播步骤更加高效,并大幅减少了网络中的参数数量。因此,CNN最适合解决与图像识别、物体检测和其他计算机视觉应用相关的问题。
典型架构
以下是卷积神经网络的典型架构。如图所示,网络由一系列卷积层、ReLU层和池化层组成,以及在生成输出之前必需的若干全连接层。

现在,让我们研究每一层中发生了什么。
输入层
到目前为止,我们只处理过以 n x 1 向量作为输入的传统神经网络。而卷积神经网络的输入,通常是灰度图像的 n x m x 1 矩阵,或彩色图像的 n x m x 3 矩阵,其中数字3代表图像中每个像素的红、绿、蓝分量。
卷积层
在卷积层中,我们主要定义滤波器,并计算定义的滤波器与三个图像分量(红、绿、蓝)中每一个的卷积。
以红色图像分量为例,假设这些是像素值。



现在,对于一个具有这些值的 2 x 2 滤波器,我们创建一个空矩阵来保存卷积过程的结果。


我们首先将滤波器滑过图像,计算滤波器与重叠像素值之间的点积,并将结果存储在空矩阵中。我们重复此步骤,每次将滤波器移动一个单元格(或使用术语“步幅”)。

我们重复此过程,直到覆盖整个图像并填满空矩阵。这里,我只展示了一个滤波器和三个图像分量中的一个。同样的操作将应用于绿色和蓝色图像分量。并且,你可以应用多个滤波器。我们使用的滤波器越多,就越能更好地保留空间维度。
但此时你可能会问,为什么我们需要使用卷积?为什么不将输入图像展平为 n x m x 1 向量并将其用作我们的输入?
如果我们那样做,最终将需要优化大量的参数,计算成本会非常高。此外,减少参数数量肯定有助于防止模型对训练数据过拟合。值得一提的是,卷积层也包含ReLU,它过滤卷积步骤的输出,只传递正值,并将任何负值变为0。
池化层
卷积神经网络中的下一层是池化层。池化层的主要目标是减少通过网络传播的数据的空间维度。卷积神经网络中广泛使用两种类型的池化:最大池化和平均池化。
以下是两种池化方法的说明:
在最大池化(两者中最常见的)中,对于我们扫描的图像的每个区域,我们保留最高值。如图所示,我们的滤波器每次移动两个步幅。




类似地,对于平均池化,我们计算所扫描的每个区域的平均值。
除了减少数据的维度外,特别是最大池化提供了空间不变性,这使得神经网络能够识别图像中的物体,即使该物体不完全像原始物体。


全连接层
最后,在全连接层中,我们将最后一个卷积层的输出展平,并将当前层的每个节点与下一层的每个其他节点连接起来。该层基本上将前一层(无论是卷积层、ReLU层还是池化层)的输出作为输入,并输出一个n维向量,其中n是与当前问题相关的类别数量。例如,如果你正在构建一个网络来对手写数字图像进行分类,维度n将是10,因为有10个数字。
你将在本专业的其他课程中更详细地学*卷积神经网络,但这些信息足以让你对卷积神经网络有一个大致的了解。
使用Keras构建CNN
现在让我们看看如何使用Keras库构建卷积神经网络。在这里,我将向你展示如何使用Keras库构建卷积神经网络。
卷积神经网络的训练和测试与我们目前所见相同。因此,首先,我们使用顺序构造函数来创建我们的模型。然后,我们将输入定义为输入图像的大小。
假设输入图像是 128 x 128 的彩色图像,我们将输入形状定义为元组 (128, 128, 3)。
接下来,我们开始向网络添加层。
我们从一个具有16个滤波器的卷积层开始,每个滤波器大小为 2 x 2,并以水平方向步幅为1、垂直方向步幅为1的方式滑过图像,并且该层使用ReLU激活函数。
然后我们添加一个池化层,这里我们使用最大池化,滤波器或池化大小为 2,并且滤波器以步幅为2滑过图像。
接下来,我们添加另一组卷积层和池化层。这里唯一的区别是我们在卷积层中使用了更多的滤波器,实际上是第一个卷积层滤波器数量的两倍。
最后,我们将这些层的输出展平,以便数据可以进入全连接层。
我们添加另一个具有100个节点的密集层,以及一个具有与当前问题类别数量相等的节点的输出层,并且我们使用softmax激活函数将输出转换为概率。
以下是使用Keras构建CNN的示例代码:
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
model = Sequential()
model.add(Conv2D(filters=16, kernel_size=(2, 2), strides=(1, 1), activation='relu', input_shape=(128, 128, 3)))
model.add(MaxPooling2D(pool_size=(2, 2), strides=2))
model.add(Conv2D(filters=32, kernel_size=(2, 2), strides=(1, 1), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2), strides=2))
model.add(Flatten())
model.add(Dense(100, activation='relu'))
model.add(Dense(num_classes, activation='softmax')) # num_classes 是类别数量
总结
在本节课中,我们一起学*了卷积神经网络。我们了解了CNN的基本概念、其典型架构(包括卷积层、池化层和全连接层),以及每层的功能。我们还探讨了使用卷积操作而非展平图像的原因,并简要介绍了如何使用Keras库构建一个简单的CNN模型。在配套的实验中,我们将实现一个完整的卷积神经网络,使用Keras库构建网络、训练它并进行验证,请务必完成关于卷积神经网络的实验部分。




生成式人工智能工程:13:循环神经网络 🧠

在本节课中,我们将要学*循环神经网络(RNN),这是一种专门用于处理序列数据的监督式深度学*模型。我们将了解其工作原理、核心架构以及典型应用。

概述
上一节我们介绍了卷积神经网络(CNN),这是一种在计算机视觉领域,特别是图像目标检测方面取得革命性进展的监督式深度学*模型。本节中,我们来看看另一种监督式深度学*模型——循环神经网络。
循环神经网络简介
到目前为止,我们所见的神经网络和深度学*模型都将数据点视为独立的实例。然而,假设你想构建一个能够分析电影场景的模型。你不能假设电影中的场景是相互独立的,因此传统的深度学*模型并不适用于此类应用。
循环神经网络解决了这个问题。循环神经网络,简称RNN,是一种带有循环的网络。它不仅每次接收新的输入,还会将前一个输入数据点产生的输出也作为输入。
循环神经网络架构
因此,循环神经网络的架构看起来是这样的:本质上,我们可以从一个普通的神经网络开始。在时间 t=0 时,网络接收输入 x0 并输出 a0。然后在时间 t=1 时,除了输入 X1,网络还将 a0 作为输入,并赋予其权重 W01,依此类推。

其核心公式可以表示为:
h_t = f(W * x_t + U * h_{t-1} + b)
其中,h_t 是当前时刻的隐藏状态,x_t 是当前输入,h_{t-1} 是上一时刻的隐藏状态,W 和 U 是权重矩阵,b 是偏置项,f 是激活函数。
因此,循环神经网络非常擅长对数据序列中的模式进行建模,例如文本、基因组、手写体和股票市场。这些算法考虑了时间和序列,这意味着它们具有时间维度。
长短期记忆网络

一种非常流行的循环神经网络类型是长短期记忆模型,简称 LSTM 模型。它已成功应用于许多领域,包括图像生成(使用在大量图像上训练的模型来生成新的图像)和手写生成(我在本课程欢迎视频中描述过)。此外,LSTM 模型还成功用于构建能够自动描述图像以及视频流的算法。
总结

本节课中,我们一起学*了循环神经网络。鉴于这只是一门入门课程,我们就介绍到这里。关于循环神经网络的视频到此结束。我们将在下一个视频中见面,届时我们将转向无监督深度学*模型,讨论自编码器。
生成式人工智能工程:094:自编码器 🧠
在本节课中,我们将学*一种无监督深度学*模型——自编码器。我们将了解其工作原理、架构、特性以及一种重要的变体:受限玻尔兹曼机。
到目前为止,我们讨论过两种有监督深度学*模型,即卷积神经网络和循环神经网络。本节中,我们将转向一种无监督深度学*模型,即自编码器。
什么是自编码器?
自编码器是一种数据压缩算法。其压缩和解压缩函数是从数据中自动学*得到的,而非由人工设计。这类自编码器是使用神经网络构建的。
自编码器具有数据特定性。这意味着它们只能压缩与训练数据相似的数据。例如,一个在汽车图片上训练的自编码器,在压缩建筑物图片时会表现不佳,因为它学*到的特征是车辆或汽车特定的。
自编码器有一些有趣的应用,例如数据去噪和用于数据可视化的降维。


以下是自编码器的架构图。它以一张图片(例如)作为输入,并使用一个编码器来寻找输入图像的最优压缩表示。然后,使用一个解码器来重建原始图像。😡
因此,自编码器是一种无监督神经网络模型。它通过将目标变量设置为与输入相同来使用反向传播。换句话说,它试图学*一个恒等函数的*似。😡
由于神经网络中的非线性激活函数,自编码器可以学*比主成分分析或其他仅能处理线性变换的基础技术更有趣的数据投影。

受限玻尔兹曼机

一种非常流行的自编码器类型是受限玻尔兹曼机。
RBM已成功应用于多种场景,包括修复不平衡数据集。因为RBM学*输入是为了能够重建它,所以它们可以学*不平衡数据集中少数类的分布,然后生成更多该类别的数据点,从而将不平衡数据集转变为平衡数据集。😡
类似地,RBM也可用于估计数据集中不同特征的缺失值。
受限玻尔兹曼机的另一个流行应用是自动特征提取,特别是针对非结构化数据。


本节课中,我们一起学*了自编码器和受限玻尔兹曼机的高层次介绍。我们了解了自编码器作为一种无监督模型,如何通过编码-解码结构进行数据压缩与重建,以及RBM在数据平衡、补全和特征提取方面的应用。


生成式人工智能工程:095:课程总结 🎉
在本课程中,我们系统性地学*了生成式人工智能工程的核心知识体系,涵盖了从基础概念到实际应用的多个关键模块。现在,让我们一起来回顾整个课程的核心内容。


模块一:深度学*应用与神经网络基础
在模块一中,我们探讨了一系列令人兴奋且具有启发性的深度学*应用案例。我们简要介绍了大脑中的神经元和神经网络,以理解它们如何启发人工神经网络的设计。随后,我们学*了神经网络如何通过前向传播过程进行预测。


模块二:神经网络的学*机制
在模块二中,我们深入探讨了人工神经网络如何通过梯度下降和反向传播进行学*。我们还学*了梯度消失问题,并了解了哪些激活函数最适合克服这一问题。


模块三:Keras库与模型构建
在模块三中,我们学*了Keras库,并掌握了如何使用它来构建解决回归和分类问题的模型。


模块四:监督与非监督深度学*

在模块四中,我们学*了监督式和非监督式深度神经网络。我们使用Keras库构建了一个卷积神经网络。

结语与展望
我非常享受准备这门课程的过程,衷心希望您也乐在其中并学到了很多知识。在某些时候,课程制作对我而言颇具挑战,因为我必须省略许多细节以保持内容的简洁性。本课程的目的并非教授全部知识,而是为您打下坚实的基础,使您能够准备好学*更高级的深度学*课程,甚至可以根据意愿开始自主学*。我相信您现在已具备这样的能力。感谢您选修本课程,并祝您未来一切顺利。

课程总结

在本节课中,我们一起回顾了生成式人工智能工程课程的全部核心内容。我们从深度学*的激动人心的应用开始,逐步深入到神经网络的基础原理、学*机制、Keras工具的使用,以及监督与非监督学*模型。本课程为您构建了坚实的知识框架,为您后续深入探索更高级的AI主题或进行独立学*做好了准备。恭喜您完成本课程的学*!
生成式人工智能工程:096:使用LLM的AI工程概述 🚀
在本节课中,我们将要学*IBM生成式人工智能工程专业证书项目的概述,特别是关于如何使用大型语言模型进行AI工程。我们将了解该项目的目标、适合人群、课程结构以及你将学到的核心技能。
世界经济论坛的一份关于未来工作的报告预测,未来*75%的组织可能会采用人工智能和机器学*相关技术。随着组织采用这项相对较新的技术,他们将需要专家来帮助他们理解如何在组织环境中应用新技术。
那么,作为AI领域的新手或有经验的从业者,你如何创造价值?生成式AI架构和模型的知识对于启动或推进在AI、机器学*和数据科学领域的职业生涯至关重要。



项目简介与目标 🎯
本项目专注于生成式人工智能和大型语言模型在自然语言处理领域的原理、技术和应用。它最适合渴望成为或目前正在担任AI工程师、机器学*工程师、深度学*工程师和数据科学家的专业人士。
学*本项目需要具备Python的基础知识。同时,了解PyTorch也将大有裨益。
课程结构与内容 📚
该项目由多个短期课程组成,涵盖了生成式AI和LLM的各个方面。课程内容包括:用于NLP的AI基础模型、使用Transformer进行语言建模、Transformer微调、LLM高级微调、使用RAG和LangChain的AI智能体,以及一个关于使用LangChain的AI应用的顶点项目。
每个主题对应一门你可以独立完成的在线课程。每门课程包含两到三个模块。完成涵盖不同主题的课程以及要求的项目后,你将获得IBM生成式人工智能工程专业证书。
以下是各门课程的核心内容:
1. 生成式AI与LLM应用入门
你将开启生成式AI工程之旅,学*生成式AI模型以及如何使用LLM构建基于NLP的应用程序。该课程还涵盖了开发这些应用程序时使用的库和工具。你还将学*通过实现分词和创建NLP数据加载器来准备训练LLM的数据。
2. NLP的语言理解与语言模型
这门中级课程将向你介绍各种模型,例如N-gram、Word2Vec和序列到序列模型。你将获得构建、训练和集成这些模型以完成各种NLP任务的实践经验。
3. 基于Transformer的NLP模型
在这里,你将学*LLM中如何使用位置编码、词嵌入、注意力机制和多头注意力。课程还将讨论基于解码器的模型(如生成式预训练Transformer,即GPT)和基于编码器的模型(如来自Transformer的双向编码器表示,即BERT),以及它们如何用于语言翻译。
4. Transformer微调
这门课程通过为你提供优化基于Transformer的LLM的通用框架和微调生成式AI模型的知识,将你的Transformer学*之旅向前推进一步。尽管从理论角度理解从头开始训练Transformer的基础知识很重要,但大多数实际应用都侧重于微调预训练模型和提示工程。
在这门课程中,你还将学*模型框架和平台,如Hugging Face和PyTorch。与微调相关的概念和技术,如参数高效微调、低秩适应和量化低秩适应也是本课程的一部分。你还将练*使用Hugging Face和PyTorch适配器加载模型、进行推理以及训练模型。
5. 高级微调技术
下一门课程将通过向你介绍允许模型从人类反馈中学*的技术,帮助你提升微调技能。你将首先了解指令微调和奖励建模等微调方法。然后,你将学**端策略优化、作为策略的LLM以及基于人类反馈的强化学*。本课程将进一步深入探讨使用配分函数的直接偏好优化,并解释如何找到最优解。
6. RAG与LangChain框架
接下来,你将学*RAG和LangChain框架。你将了解RAG流程、上下文和问题编码器以及Faiss库。此外,你还将了解上下文学*、LangChain以及提示工程的高级方法。然后,你将深入探讨用于开发应用程序的LangChain工具、组件、聊天模型和智能体。课程中 strategically 安排了实践实验,以帮助你掌握这些工具和技术的就业技能。
7. 顶点项目
最后是顶点项目。在这里,你将应用在整个项目中学到的所有技能。通过对LLM原理的理解,结合PyTorch和Hugging Face的知识,你现在可以利用这些知识,使用LangChain构建真实世界的应用程序。LangChain能够无缝集成和部署复杂的语言模型,使得创建强大、可扩展的解决方案变得更加容易。无论你是开发聊天机器人、自动内容生成器还是高级分析工具,LangChain都提供了将你的AI项目高效落地的框架。这个项目将是你的作品集的一个很好补充,并让你更接*获得生成式人工智能工程专业证书。
学*评估与认证 📝
在学*过程中,你将遇到测验和项目来评估你的学*成果。计分测验的权重将计入课程完成度。大多数项目将由AI或同伴评分,并带有一定权重,计入课程和项目完成要求。
最后,在你成功完成所有学*内容后,你将获得IBM生成式人工智能工程专业证书。这是一项受到许多雇主重视的凭证。
总结

本节课中,我们一起学*了IBM生成式人工智能工程专业证书项目的整体概览。我们了解了该项目旨在培养能够应用LLM和生成式AI技术的专业人才,涵盖了从基础模型、Transformer架构、各种微调技术到RAG和LangChain应用开发的完整知识体系。通过系列课程和实践项目,学*者将获得构建真实AI应用所需的技能,并最终获得行业认可的证书。我们祝愿你在接下来的学*旅程中一切顺利。
生成式人工智能工程:097:课程介绍 🚀

在本课程中,我们将学*生成式人工智能和大型语言模型的基础知识。本课程是一个包含六门课程的专项计划的第一部分,旨在为你提供使用LLMs构建自然语言处理应用的知识与技能。
概述
生成式人工智能以多种方式改变了我们的生活。它能自动补全代码、创作音乐、设计游戏并助力药物发现,这只是其无限可能性中的一小部分。生成式AI系统在理解自然语言方面的能力实现了巨大飞跃,这有助于生成上下文相关的对话、总结文档、进行语言翻译等。

在专注于语言建模的AI工程领域,存在着巨大的职业发展机遇。准备好开发创新的AI应用,让人与机器能够用自然语言轻松交互。在这个快速发展的领域建立专业知识,成为雇主青睐的人才。
目标学员
本课程适合现有和未来的数据科学家、机器学*工程师、深度学*工程师和AI工程师。学*本课程时,如果你具备Python和PyTorch的基础知识,并对机器学*和神经网络有所了解,将会更有优势,但这并非严格要求。
学*目标
完成本课程后,你将能够:
- 描述如何使用LLMs开发能够理解和生成人类语言的生成式AI应用。
- 解释文本如何被预处理和加载,以供LLMs进行有效的训练和分析。
- 使用生成式AI库和工具来实现基于LLMs的应用。
课程内容结构
上一节我们介绍了课程的整体目标,本节中我们来看看课程的具体模块安排。
本课程包含两个核心模块:
模块一:生成式AI基础与工具
你将学*生成式AI的重要性和演变历程。了解Transformer和生成对抗网络等生成式AI架构与模型在训练方法和微调上的差异。此外,你还将学*LLMs,以及PyTorch和Hugging Face等库和工具的关键特性与重要性。通过动手实验,你将在Jupyter环境中使用Hugging Face探索生成式AI库。
模块二:数据准备
你将学*将数据转换为LLMs可理解格式所必需的数据准备步骤。作为其中的一部分,你将学*分词技术和数据加载器。在本模块的实验练*中,你将实现分词并创建一个NLP数据加载器。你还将通过课程术语表和速查表来回顾所学内容。
学*资源与建议
为了帮助你高效学*,本课程提供了多种形式的内容:
以下是课程提供的学*资源类型:
- 视频:简短精炼,专注于核心主题。
- 阅读材料:以文本形式提供详细内容。
- 实验:提供技术环境、详细说明和代码片段,供你完成动手练*。
- 练*测验:用于自我评估学*成果。
- 计分测验:用于应用所学知识并评估掌握程度。
- 术语表与速查表:提供代码语法等快速参考内容。
为了从课程中获得最大收益,建议你观看所有视频、完成实验以练*新技能,并尝试所有测验。
总结

本节课中,我们一起学*了《生成式人工智能工程》系列课程第一门的介绍。我们了解了生成式AI的广泛应用和巨大潜力,明确了课程的目标学员和学*目标,并预览了课程的两个核心模块内容与丰富的学*资源。现在,让我们开始这段激动人心的学*旅程吧。
生成式人工智能工程:098:生成式AI的重要性 🚀
在本节课中,我们将学*生成式人工智能的重要性。你将能够解释生成式AI在多个领域的重要性,了解不同类型的生成式AI模型,并阐述其在当下及未来的应用。
想象你是一名在医疗健康行业工作的技术专家。你的团队希望你通过实施生成式AI来提升患者体验,例如,将患者各项复杂的检测结果总结成易于理解的语言。你认为使用生成式AI可以实现这个目标吗?让我们深入了解。


什么是生成式AI?

生成式AI指的是能够根据其训练数据生成高质量文本、图像及其他内容的深度学*模型。这些模型的开发和训练旨在理解现有数据中的模式和结构,并运用这种理解来产生新的、相关的数据。
可以将生成式AI比作一位艺术家:他审视各种画作,理解其中的模式,然后根据所学创作出原创的艺术作品。
生成式AI模型的类型
生成式AI包含多种类型的模型,广泛应用于不同领域。这些模型用于生成不同类型的内容,如文本、图像、音频、3D物体和音乐。
文本生成模型
上一节我们介绍了生成式AI的基本概念,本节中我们来看看具体的模型类型。首先是文本生成模型。
这些模型理解文本数据中的上下文以及词语和短语之间的关系。它们识别模式,然后生成与上下文相关的文本。
例如,当我们开始撰写一个故事时,这些模型能够智能地预测并生成叙事的后续部分。或者,当我们输入一个英文句子时,模型可以生成另一种语言的翻译文本,同时保留原文的语气。
一个文本生成模型的例子是生成式预训练转换器,即 GPT。
图像生成模型
接下来,我们探讨用于图像生成的模型。
这些模型主要通过两种方式工作:
- 根据文本输入生成图像。例如,模型可以根据提示“一个正在弹钢琴的机器人”生成对应的图像。这类模型的一个例子是DALL-E。
- 根据种子图像或模型自身生成的随机输入生成图像。例如,如果你提供一张天空的图片作为输入,模型可以生成各种艺术风格的天穹图像。这类模型可用于图像到图像的转换(如将草图转化为逼真图像)以及深度伪造创作(如在电影中让著名演员“复活”)。
图像生成模型的例子包括生成对抗网络和扩散模型。
音频生成模型
另一种类型的生成式AI模型用于生成音频内容。
你可以使用这些模型生成听起来自然的语音,以及进行文本到语音的合成。例如,你可以生成一段听起来很真实的音频对话。
这类模型的一个例子是WaveNet。
生成式AI的应用
了解了不同类型的模型后,现在让我们看看生成式AI的具体应用。许多行业都在使用生成式AI模型和系统。
以下是其主要应用领域:
- 内容创作:你可以自动化创建文章、博客帖子、营销材料等具有上下文相关性和清晰度的书面内容。同时,也能为娱乐和广告创作视觉图像和视频。
- 摘要总结:你可以利用生成式AI来浓缩长篇文档和文章,帮助读者快速吸收信息。
- 语言翻译:生成式AI可以使语言翻译听起来更自然,从而提升你内容的可访问性。
- 聊天机器人与虚拟助手:你可以让聊天机器人和虚拟助手像人类一样对话,使它们在提供客户支持时更加高效。
- 数据分析与问题解决:基于一个或多个AI模型构建的生成式AI系统,可以帮助你分析大型数据集(尤其是在自然语言处理任务中),用以发现洞察并为复杂问题提出创造性的解决方案。
行业特定应用
生成式AI的应用正渗透到各个具体行业:
- 医疗健康:行业使用生成式AI分析医学影像,并利用医疗信息创建患者报告。
- 金融:行业利用生成式AI从海量金融数据集中进行预测和预报。
- 游戏:行业通过生成式AI引入互动元素和动态故事情节,使游戏更加刺激。
- 信息技术:生成式AI可以创建人工数据来训练模型,从而提高数据科学和机器学*模型的准确性。
根据彭博智库的预测,到2032年,生成式AI有望成为一个价值1.3万亿美元的市场。这将使其在更多领域拓展应用,例如增强个性化推荐、通过药物发现助力医学突破,以及将生成式AI集成到智能家居和自动驾驶车辆中。
总结
本节课中,我们一起学*了生成式AI的重要性。


- 生成式AI指的是能够根据训练数据生成文本、图像、音频、3D物体和音乐等多种类型内容的深度学*模型。
- 文本生成模型(如GPT)理解词语和短语之间的关系,并生成上下文相关的文本。
- 图像生成模型(如DALL-E、GAN)可以根据文本输入或种子图像生成图像。
- 音频生成模型(如WaveNet)可用于生成自然语音和进行语音合成。
- 生成式AI在医疗健康、金融、游戏和信息技术等行业有着具体的应用,其市场前景广阔,未来将在更多领域发挥重要作用。
生成式人工智能工程:099:生成式AI架构与模型 🏗️
在本节课中,我们将学*生成式人工智能中常用的架构与模型。你将能够描述这些常见的架构,识别它们在训练方法上的差异,并理解生成式AI模型与强化学*之间的关系。



想象你是一名AI工程师,任务是为一个在线平台自动化创建个性化视频剪辑。用户提供他们希望在视频中看到的内容的文字描述,系统必须生成与描述准确匹配的视觉效果。哪种生成式AI架构或模型可以帮助你实现这个功能?
为了解答这个问题,让我们深入了解生成式AI中常用的架构和模型。

以下是生成式AI中几种核心的架构与模型:
- 循环神经网络:一种使用序列或时间序列数据的人工神经网络。
- Transformer:一种能够*乎实时翻译文本和语音的深度学*模型。
- 生成对抗网络:一种由生成器和判别器两个子模型组成的生成式AI模型。
- 变分自编码器:一种基于编码器-解码器框架运行的模型。
- 扩散模型:一种概率生成模型。
上一节我们列出了主要的模型类型,本节中我们来详细看看循环神经网络。
RNN是一种人工神经网络,专门用于处理具有自然顺序或时间依赖关系的数据问题。RNN与常规神经网络的不同之处在于其结构中内置了循环。这种基于循环的设计使RNN能够记住先前的输入,并影响当前的输入和输出。这对于处理序列(如语言建模)的任务至关重要。
为了让生成式AI架构和模型生成更准确、上下文更相关的内容,你需要对它们进行微调。微调是指调整一个预训练模型,以提高其在特定任务或数据集上的性能。对于RNN,微调可能涉及调整循环神经网络的权重和结构,以使其与特定任务或数据集对齐。
RNN可以应用于自然语言处理、语言翻译、语音识别和图像描述生成等领域。
了解了RNN的序列处理能力后,我们来看看在自然语言处理中表现卓越的Transformer架构。
Transformer是一种深度学*模型。它接收数据(如单词或数字)并将其传递通过不同的层。信息单向流动,从输入层开始,经过隐藏层,最终到达输出层。Transformer采用反馈机制来提高准确性。
Transformer的设计包含一个自注意力机制,使模型能够专注于正在查看的信息中最重要的部分,从而提高其理解和决策的效率。这种对输入序列不同部分的选择性关注,允许模型同时专注于特定片段,从而实现高效的并行化训练。
在Transformer微调中,预训练的Transformer模型主体通常保持不变,微调通常只涉及针对特定任务训练最后的输出层。Transformer的自注意力机制和其他层通常保持固定。
生成式预训练Transformer 是Transformer架构中展示出卓越文本生成能力的一个生成模型示例。GPT是一个经过训练的生成模型,能够根据从训练数据中学到的模式来预测和生成文本序列。尽管它不像某些生成模型那样明确地对底层数据分布进行建模,但其生成反映训练数据分布文本的能力,以及在微调中的多功能性,确立了它作为生成模型的角色。
接下来,让我们学*生成对抗网络。
GAN是一种生成式AI模型,由两个子模型组成:一个生成器和一个判别器。生成器创建虚假样本并将其发送给判别器。判别器通过将这些样本与来自真实数据集的真实样本进行比较来检查其真实性,然后为每个样本分配一个概率分数,表明该样本是真实的可能性有多大。
这个对抗过程就像一场友好的竞争,生成器努力使生成的东西看起来真实,而判别器则学*区分真实与虚假。两者都在不断改进各自的输出。你会发现GAN在图像和视频生成方面特别有用。
在了解了GAN的对抗训练后,我们转向另一种生成模型——变分自编码器。
VAE基于编码器-解码器框架运行。编码器网络首先将输入数据压缩到一个简化的抽象空间,该空间捕获了数据的基本特征。然后,解码器网络使用这些压缩信息来重建原始数据。
VAE专注于学*输入数据中的底层模式,从而可以创建具有相似特征的新数据样本。VAE在潜在空间中使用概率分布来表示数据,它们可以为给定的输入产生一系列可能的输出,这反映了现实世界数据中固有的不确定性。
你会发现它们在艺术和创意设计相关的应用中非常有用。
最后要介绍的模型是扩散模型。
扩散模型是一种概率生成模型。它通过学*如何从训练数据中去除噪声或重建已被扭曲到无法识别的样本来进行训练,从而生成图像。根据提示,扩散模型可以基于其训练数据的统计特性生成极具创意的图像。
你可以使用扩散模型从嘈杂或低质量的输入中生成高质量图像,例如修复一张旧的、失真的照片。
总结来说,这些架构和模型的训练方法各不相同:


- RNN使用基于循环的设计。
- Transformer利用自注意力机制。
- GAN采用竞争性训练方法。
- VAE采用基于特征的方法。
- 扩散模型依赖于统计特性。

生成式AI模型与强化学*密切相关。传统的强化学*侧重于智能体(如AI系统或机器人)如何与环境交互以最大化奖励。生成式AI模型在训练期间采用强化学*技术来微调和优化其在特定任务上的性能。
本节课中我们一起学*了生成式AI的架构与模型。
生成式AI架构和模型包括RNN、Transformer、GAN、VAE和扩散模型。

- RNN使用序列或时间序列数据以及基于循环的设计进行训练。
- Transformer利用自注意力机制来关注信息中最重要的部分。
- GAN由生成器和判别器组成,两者以竞争模式工作。
- VAE基于编码器-解码器框架运行,并根据相似特征创建样本。
- 扩散模型通过学*去除噪声和重建扭曲的样本来生成创意图像,并依赖于统计特性。
生成式人工智能工程:100:生成式AI在NLP中的应用 🧠
在本节课中,我们将要学*生成式人工智能在自然语言处理领域的角色、演变及其应用。我们将探讨生成式AI架构如何使机器理解并生成类人语言,并了解大型语言模型的重要性。
想象你在一家银行担任AI工程师。银行指派你创建一个虚拟助手,该助手能够用自然语言与客户对话,回答关于账户详情、投资选项等查询。你将探索如何利用生成式AI架构来开发能与人类进行自然语言交互的应用程序。


生成式AI架构使机器能够理解人类语言并生成与人类生成内容难以区分的响应。它们通过融入上下文感知和确保连贯的交互来改进语言处理,并通过预测分析和高级建模实现有意义的对话。
基于生成式AI架构的NLP系统能够感知情感并理解话语背后的意图,将理解范围扩展到单纯的词汇之外。
上一节我们介绍了生成式AI的基本概念,本节中我们来看看其在NLP领域的演变历程。
以下是生成式AI在NLP中的演变阶段:
- 基于规则的系统:严格遵循预定义的语言学规则(如语法)。这些系统虽然精确,但缺乏灵活性。
- 基于机器学*的方法:采用统计方法从大量语言数据集中学*并进行预测。这比基于规则的系统更具适应性,但在理解复杂的语言细微差别方面仍有局限。
- 深度学*:专注于使用大量数据集训练人工神经网络。网络中的众多计算单元协同工作,进行更细致的语言解读。
- Transformer架构:这是最新的演变成果。Transformer架构专为处理序列数据而设计,在理解语言中的上下文和依赖关系方面能力更强。
由于持续改进机器理解和生成语言方式的努力,NLP领域的生成式AI在不断演进。
这种演进体现在机器翻译、聊天机器人对话、情感分析和文本摘要等领域的显著进步中。

以下是生成式AI架构在这些任务中的应用:

- 机器翻译:这些架构通过实现更精确和具有上下文感知的语言转换,显著提高了翻译准确性。
- 聊天机器人/虚拟助手:你可以利用生成式AI架构使对话更加自然、拟人化,并带有一定程度的同理心和个性化,从而提升用户体验。
- 情感分析:生成式AI架构理解微妙语言表达的能力,提高了情感分析的有效性,为用户情感提供了更深入的洞察。
- 文本摘要:你可以运用这些架构来识别文本或文档的核心含义和重点,从而生成更精确的摘要。
生成式AI架构包含专注于理解和生成人类语言的语言模型。
大型语言模型是使用人工智能和深度学*技术,基于海量数据集(如网站和书籍)来生成文本、翻译语言和创建各类内容的基础模型。

它们之所以被称为“大型”语言模型,是因为其训练数据集的规模可能达到PB级别。此外,这些模型包含数十亿个参数,这些参数是定义模型行为的变量,在训练过程中进行微调以优化模型在特定任务上的性能。例如,当模型学*情感时,一个参数可能代表分配给特定词(如“快乐”、“悲伤”)的权重。
LLM在庞大数据集上的广泛训练使其能够全面理解语言结构和上下文。它们能够捕捉人类语言的细微差别,促进更自然的交互。LLM擅长预测序列中的下一个词。凭借其庞大的资源,LLM只需极少量的任务特定训练即可生成创造性内容。
一些LLM的例子包括生成式预训练Transformer系列(GPT系列)、来自Transformer的双向编码器表示(BERT)、双向和自回归Transformer(BART)以及文本到文本传输Transformer(T5)。

以下是几种主要LLM架构的特点:
- GPT:主要充当解码器,擅长生成文本。它在需要生成连贯且上下文相关内容的任务中表现出色,例如聊天机器人。
- BERT:采用仅编码器的Transformer架构。它在理解句子中单词的上下文方面表现卓越,这对于情感分析和问答等需要细致处理的任务至关重要。
- BART和T5:遵循编码器-解码器架构。它们利用编码器进行上下文理解,利用解码器生成文本。这种多功能性使其非常适合各种NLP任务。
术语“GPT”和“ChatGPT”听起来可以互换,但这些模型虽有相似之处,却各有侧重。


以下是GPT与ChatGPT的主要区别:
- GPT:专注于多样化的文本生成任务。
- ChatGPT:专注于生成对话。
- 训练与微调:GPT主要使用监督学*,可能使用强化学*,但较少关注对话方面。ChatGPT则结合使用监督学*和强化学*。
- 人类反馈:GPT的学*过程不包含来自人类交互的反馈。而ChatGPT使用一种称为“基于人类反馈的强化学*”的方法,该方法利用人类反馈来创建奖励模型。
大多数LLM都基于Transformer架构。LLM的多功能性使其成为推动自然语言理解和生成进步的关键因素。你可以为通用目的预训练LLM,然后用小得多的数据集对其进行微调。例如,LLM可能在通用文本分类上进行训练,你可以在零售行业背景下对其进行微调,以根据文本描述将产品分类到电子或服装等组别中。
需要注意的是,虽然这些模型能够跨领域生成权威性文本,但它们也可能生成听起来正确但不准确的信息。你可能还需要解决偏见问题,并考虑生成内容对社会的潜在影响。

本节课中我们一起学*了NLP领域的生成式AI架构。生成式AI架构使机器能够理解人类语言并生成与人类生成内容难以区分的响应。生成式AI始于遵循预定义语言学规则的基于规则的系统。随后出现了专注于统计方法的机器学*方法。深度学*的引入是一次重大飞跃,它使用在大量数据集上训练的神经网络。Transformer代表了这一演变的最新成果。生成式AI架构增强的能力带来了机器翻译、聊天机器人对话、情感分析和文本摘要领域的显著进步。LLM是使用人工智能和深度学*技术,基于海量数据集的基础模型。它们因训练数据集的规模可能达到PB级别而被称为大型语言模型。此外,它们拥有数十亿个参数。LLM的例子包括GPT、BERT、BART和T5。
生成式人工智能工程:101:分词 🧩
在本节课中,我们将要学*分词。分词是自然语言处理的基础步骤,它将文本分解成模型可以理解的更小单元。理解分词的过程和方法,对于构建任何文本相关的AI应用都至关重要。
概述
分词是将句子分解成更小片段(称为令牌或词元)的过程。这些令牌帮助模型更好地理解文本。例如,句子“IBM taught me tokenization”可以被分解为“IBM”、“taught”、“me”和“tokenization”等令牌。执行此分解的程序称为分词器。
分词方法
分词主要通过三种方法实现:基于词、基于字符和基于子词。以下是每种方法的详细介绍。

基于词的分词
在基于词的分词中,文本被划分为单个单词,每个单词被视为一个令牌。

- 优点:保留了单词的语义含义。
- 缺点:将每个单词都视为独立令牌会显著增加模型的整体词汇表大小。

不同的分词器行为各异。例如,NLTK和SpaCy分词器能有效分割句子,但可能将“unicorn”和“unicorns”这类相似词视为不同令牌,这在进行自然语言处理任务时可能带来问题。

基于字符的分词
基于字符的分词将文本分割成单个字符。
- 优点:词汇表非常小。
- 缺点:单个字符可能无法传达与完整单词相同的信息。将每个字符作为唯一令牌会增加输入的维度和计算需求。
以下是一个基于字符的分词器示例。输入文本是“this is a sentence”。分词后,你得到字符序列:[‘t’, ‘h’, ‘i’, ‘s’, ‘ ’, ‘i’, ‘s’, ‘ ’, ‘a’, ‘ ’, ‘s’, ‘e’, ‘n’, ‘t’, ‘e’, ‘n’, ‘c’, ‘e’]。
基于子词的分词

基于子词的分词结合了前两种方法的优点。它允许常用词保持不分割,同时将不常用词分解为有意义的子词。
有多种算法可以实现基于子词的分词,主要包括:
- WordPiece算法:该算法评估分割和合并两个符号的利弊,以确保其决策是有价值的。
- Unigram算法:该算法将文本分解成更小的片段。它从一个很大的可能性列表开始,然后根据它们在文本中出现的频率逐渐缩小范围,这是一个迭代的过程。
- SentencePiece算法:该算法将文本分割成可管理的部分,并为每个部分分配唯一的ID。
以下是使用不同算法的分词器代码示例:
使用WordPiece算法的分词器示例(如BERT分词器):
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained(‘bert-base-uncased’)
tokens = tokenizer.tokenize(“playing”)
# 输出可能为:[‘play’, ‘##ing’]
# ‘##’符号表示该子词应附加到前一个词上,中间没有空格。

使用Unigram和SentencePiece算法的分词器示例(如XLNet分词器):
from transformers import XLNetTokenizer
tokenizer = XLNetTokenizer.from_pretrained(‘xlnet-base-cased’)
tokens = tokenizer.tokenize(“Hello world!”)
# 输出可能为:[‘Hello’, ‘▁world’, ‘!’]
# ‘▁’前缀表示该令牌是一个新词,在原始文本中前面有空格。
# ‘!’没有前缀,因为它直接跟在前面一个词令牌后面,原始文本中没有空格。

在PyTorch中进行分词与索引
上一节我们介绍了不同的分词算法,本节中我们来看看如何在PyTorch中具体实现分词并为令牌建立索引。在PyTorch中,你可以使用torchtext库将数据集中的文本分词成单词或子词。
使用build_vocab_from_iterator函数可以从这些令牌中创建一个模型能够理解的词汇表。该函数为词汇表中的每个令牌分配一个由整数表示的唯一索引。模型随后使用这些索引来映射词汇表中的单词。
以下是使用torchtext分词句子的示例步骤:
- 创建一个用于演示的合成数据集。
- 使用
get_tokenizer函数获取分词器。 - 将分词器应用于文本,得到令牌列表。
- 定义一个
yield_tokens函数,该函数接收数据迭代器,使用分词器处理每个文本,并逐个生成分词后的输出。 - 创建一个迭代器
my_iterator。 - 使用
next(my_iterator)语句从数据集中获取下一组令牌。 - 最后,
build_vocab_from_iterator函数将令牌转换为索引。
在构建词汇表时,有时会遇到未知词。你可以设置一个特殊令牌<UNK>,用于处理可能不在你词汇表中的单词。代码vocab.set_default_index(vocab[‘<UNK>‘])将<UNK>设为默认词。如果一个单词在词汇表中找不到,就会使用它。

vocab.get_stoi()会给你一个字典,将单词映射到它们在词汇表中对应的数字索引。
你可以将vocab函数直接应用于令牌或令牌列表,结果是索引列表。考虑以下示例:
def get_tokenized_sentence_and_indices(iterator, vocab):
tokenized_sentence = next(iterator)
token_indices = [vocab[token] for token in tokenized_sentence]
return tokenized_sentence, token_indices
# 应用函数
sentence, indices = get_tokenized_sentence_and_indices(my_iterator, vocab)
print(“Tokens:”, sentence)
print(“Indices:”, indices)

添加特殊令牌
在许多应用中,你需要添加特殊令牌。例如,使用SpaCy对每个句子进行分词后,你可以通过循环遍历输入数据中的句子,在分词后的句子开头附加<BOS>(序列开始),在结尾附加<EOS>(序列结束)。
随后,为了确保所有句子具有相同的长度(与输入句子中最长句子的长度匹配),你可以用<PAD>(填充)令牌来填充分词后的行。
总结
本节课中我们一起学*了分词的核心概念。
- 分词是将句子分解成更小片段(令牌)的过程。
- 分词器(如NLTK和SpaCy)用于生成这些令牌。
- 基于词的分词保留了语义,但会增加词汇表大小。
- 基于字符的分词词汇表小,但信息表达可能不完整。
- 基于子词的分词是一种折中方案,常用词保持完整,生僻词被分解。可以通过WordPiece、Unigram和SentencePiece等算法实现。
- 可以在分词后的句子中添加特殊令牌,如开头的
<BOS>和结尾的<EOS>,以及用于统一长度的<PAD>令牌。


掌握这些基础知识,是后续构建更复杂自然语言处理模型的重要第一步。
生成式人工智能工程:102:数据加载器概述 🧠

在本节课中,我们将要学*数据加载器的核心概念。你将了解数据加载器的定义、用途,以及如何在PyTorch框架中使用它来高效地管理和预处理数据,这对于训练生成式AI模型至关重要。
什么是数据加载器?

想象一下,你正在开发一个机器翻译应用,需要处理庞大的数据集。手动加载和打乱这些数据来训练底层的语言模型是一项挑战。如何才能高效地管理这个过程呢?
数据加载器可以帮助你准备和加载数据。像PyTorch这样的领先框架拥有一个专用的数据加载器类,你可以在训练生成式AI模型时使用它来处理和准备数据。
为什么需要数据加载器?
数据加载器在自然语言处理(NLP)任务中扮演着关键角色。以下是其主要优势:
以下是数据加载器的几个核心优势:
- 高效的批处理和打乱:这对于训练神经网络至关重要。
- 动态预处理:通过仅在训练时加载所需数据来优化内存使用。
- 与训练流程无缝集成:使模型的训练和评估更加容易。
- 简化数据增强和预处理:允许你对输入数据应用各种转换。
PyTorch中的数据加载器
在PyTorch中,你将使用数据集(Dataset)和数据加载器(DataLoader)来进行高效的数据处理。数据集是数据样本及其标签的集合,是起点。
让我们考虑三个文本样本,任务是分类它们语法是否正确。你通常会将数据集划分为:
- 训练集:用于训练模型。
- 验证集:用于调整和验证模型参数。
- 测试集:用于评估模型在真实场景中的性能。
虽然“训练集”和“验证集”有时可以互换使用,但它们在模型训练和评估中具有不同的功能。
创建自定义数据集和数据加载器
这个例子展示了如何在PyTorch中创建自定义数据集并使用数据加载器类。数据集由一系列随机句子组成,目标是生成句子批次以供进一步处理(例如训练神经网络模型)。
以下是创建自定义数据集的关键步骤:
- 定义自定义数据集类:它继承自
torch.utils.data.Dataset类。 - 实现三个核心方法:
__init__:用句子列表初始化数据集。__len__:返回样本数量。__getitem__:根据索引idx检索一个项目(这里是一个句子)。
创建数据集对象后,你可以像访问列表一样访问样本。
使用数据加载器,你可以批量输出数据,而不是一次一个样本。数据加载器是PyTorch中的一个迭代器对象,用于从数据集中加载、打乱和批处理数据,便于对样本组进行训练。
迭代器是一个可以循环遍历的对象。它包含可迭代的元素,通常包括 __iter__ 和 __next__ 方法。你通常使用迭代器来遍历大型数据集。每次调用 next 函数,它都会返回新的样本批次。
接下来,你通过传入句子列表来创建自定义数据集的实例。此外,你指定一个批大小,它决定了在数据加载过程中每个批次将包含多少个句子。
然后,你可以通过将自定义数据集和批大小提供给 torch.utils.data.DataLoader 类来创建数据加载器。此外,你可以设置 shuffle=True 来在句子被分成批次之前随机打乱它们。这种打乱对于训练深度学*模型特别有用,因为它可以防止模型根据数据顺序学*模式。
最后,遍历数据加载器以查看数据是如何批量加载的。
批处理函数与数据转换
在大多数NLP应用中,大部分数据转换是在批处理函数中执行的。你可以使用数据加载器对输入文本数据进行各种转换。这些转换包括:
- 对文本进行分词。
- 将其数字化。
- 将其调整到一致的大小。
- 将其转换为张量。
这些预处理步骤确保数据被准备成适合深度学*模型处理和解释的格式。
代码使用 get_tokenizer 函数定义分词器,选项为“basic_english”。接着,它使用 build_vocab_from_iterator 函数从句子中构建词汇表。该函数从分词的句子中构建词汇表。
分词输入数据后,序列的长度可能不一致。数据加载器中的每个样本必须具有相同的长度。因此,你需要使用填充。你可以利用PyTorch中的 pad_sequence 函数,它将批次中的序列填充到与最长序列相同的长度。
padding_value=0参数指定用于填充的值。batch_first参数确保批次维度是输出张量中的第一个维度。
当 batch_first 设置为 True 时,输出张量中的第一个维度将代表批大小。相反,如果 batch_first 使用默认值 False,则输出张量中的第一个维度将代表序列长度,批大小将成为第二个维度。
为了保持原始数据集不变,你可以在整理函数中处理数据转换。你也可以选择利用整理函数来执行诸如分词、将分词索引转换为数字以及将结果转换为张量等任务。
让我们看一个执行这些任务的自定义整理函数的例子。代码定义了一个名为 collate_fn 的自定义整理函数。在该函数内部:
- 使用分词器函数对批次中的每个样本进行分词。
- 使用词汇表将分词映射为数字。
- 将
pad_sequences函数应用于张量批次,以填充批次内的序列,使其具有相等的长度。
现在,你可以使用这个整理函数和自定义数据集来创建数据加载器。
总结

本节课中,我们一起学*了数据加载器的相关知识。数据加载器帮助你准备和加载数据以训练生成式AI模型。PyTorch和TensorFlow都有专用的数据加载器类。数据加载器支持高效的批处理和打乱,并允许动态处理。它们能与PyTorch训练流程无缝集成,并简化数据增强和预处理。使用数据加载器,你可以批量输出数据,而不是一次一个样本。
生成式人工智能工程:103:课程介绍 🚀

在本节课中,我们将要学*IBM《生成式人工智能工程》课程的第103节内容。本节是课程的介绍部分,将概述课程的整体结构、学*目标、适用人群以及学*方法,为你开启自然语言处理与AI模型开发的旅程做好准备。

欢迎来到这门关于自然语言处理(NLP)AI模型的课程。在这里,你将学*NLP和AI模型开发的各个方面。
本课程适合现有的和有抱负的数据科学家、机器学*工程师、深度学*工程师和AI工程师。具备Python和PyTorch的基础知识,以及对机器学*和神经网络的了解将是一个优势,但并非严格要求。
完成本课程后,你将能够描述语言理解的基础知识,包括将词语转换为特征以及文档分类预测。你还将能够解释NLP模型和技术,包括N-gram、Word2Vec和序列到序列模型。此外,你将使用PyTorch来构建、训练和实现NLP模型。
课程模块概览 📚
上一节我们介绍了课程的整体目标,本节中我们来看看课程的具体模块安排。
课程内容经过精心设计,以促进学*。视频简短且专注于主要主题。阅读材料主要以文本格式提供详细内容。实验提供了技术环境、详细说明和代码片段,供你完成动手练*。练*和分级测验将帮助你应用所学知识并评估你的掌握程度。
以下是课程的主要模块内容:
-
模块1:从词语到特征与文档分类
- 你将学*如何使用独热编码、词袋模型、词嵌入和嵌入包将词语转换为特征。
- 你还将学*神经网络如何用于文档分类的预测、训练和优化。
- 该模块将使你深入了解N-gram语言模型的应用。
- 在动手实验练*中,你将在Jupyter环境中使用PyTorch构建并训练一个简单的神经网络语言模型。
-
模块2:词嵌入与序列模型
- 你将学*Word2Vec词嵌入模型的类型和特点。
- 你还将了解在NLP和序列转换任务中使用序列到序列模型的目的。
- 此外,本模块将引导你完成评估生成文本质量的过程。
- 在动手实验练*中,你将开发并集成预训练的词嵌入模型。
学*方法与建议 💡
为了从课程中获得最大收益,请观看所有视频,完成实验以练*新技能,并尝试完成所有测验。
让我们开始这段激动人心的旅程。


本节课中我们一起学*了IBM《生成式人工智能工程》第103节课程的介绍。我们了解了课程的目标是掌握NLP基础与AI模型开发,明确了适合的学*者群体,预览了从词语特征提取到高级序列模型的两个核心模块,并获得了高效学*本课程的具体建议。准备好开始你的学*之旅吧!
生成式人工智能工程:104:将单词转换为特征 🧠
在本节课中,我们将学*如何将文本中的单词转换为机器学*模型能够处理的数值特征。这是构建自然语言处理应用的基础步骤。
概述
假设你正在开发一个用于分类邮件的自然语言处理应用。你可以根据邮件中特定单词的出现、频率或上下文含义来对邮件进行分类。为了实现这个功能,必须将单词转换为数值特征。本节我们将探讨几种关键方法:独热编码、词袋模型、嵌入和嵌入袋。
从独热编码到词袋模型
上一节我们提到了将文本转换为数值的必要性。本节中,我们来看看最基础的转换方法。
考虑以下示例句子:
- I like cats.
- I hate dogs.
- I'm impartial to hippos.
在机器学*中,这样的句子集合、文档或序列通常被称为语料库或数据集。为了使用神经网络识别句子主题(猫、狗或河马),输入必须被翻译成神经网络能理解的数字。
独热编码是一种将分类数据转换为神经网络可以理解的特征向量的方法。
以下是独热编码的表示方式:
| 标记索引 | 标记 | 独热编码向量 |
|---|---|---|
| 0 | I | [1, 0, 0, 0, 0, 0, 0, 0, 0] |
| 1 | like | [0, 1, 0, 0, 0, 0, 0, 0, 0] |
| 2 | cats | [0, 0, 1, 0, 0, 0, 0, 0, 0] |
| ... | ... | ... |
| 7 | cat | [0, 0, 0, 0, 0, 0, 0, 1, 0] |
向量的维度与词汇表中的单词数量相对应,而标记索引决定了向量中哪个元素被设置为1。
- 标记 “I” 的向量中,所有元素都为0,除了对应“I”的那个元素。这就是单词“I”的特征
X。 - 标记 “like” 以类似方式表示。你可以观察到一个模式。
- 标记 “cats” 也以类似方式表示,依此类推。
你可以将文档中的每个标记表示为一个向量。那么,如何使用这些向量来描述整个文档或序列呢?
词袋模型表示法将一个文档描绘为其所有独热编码向量的聚合或平均值。

对于句子 “I like cats”,你需要组合“I”、“like”和“cats”的独热向量。具体做法是将这些向量相加。

X 向量就是 “I like cats” 的词袋向量。

理解嵌入与嵌入袋
了解了基础的向量表示后,我们来看看更高效、更强大的方法:嵌入。
考虑一个用于对之前关于猫、狗或河马的句子进行分类的神经网络。你输入词袋向量,并选择与“猫”关联度最高的输出值。接下来,你将了解嵌入层和嵌入袋层如何有效地替代这个初始的线性层。
让我们看看隐藏层。当我们输入单词“cat”的独热向量(一个除了第七个位置为1其余全为0的向量)时,隐藏层中的激活值对应于第七个神经元的参数。
与其使用独热编码向量,你可以用一个标记索引(在这个例子中是7)来替代它。输出结果与独热向量相同。接受这个索引的层被称为嵌入层,其输出就是嵌入向量。
此处的嵌入权重组合起来形成一个嵌入矩阵。
矩阵的列数就是嵌入维度。每一行代表一个单词。
与独热编码向量相比,嵌入向量通常具有更低的维度。降低维度可以简化模型的计算需求。
当你将一个词袋向量输入到神经网络的隐藏层时,输出结果就是所有嵌入向量的总和。也可以将嵌入视为嵌入矩阵中的一行,并标记它们所代表的单词。
这个过程包括将所有来自词袋的向量相加,然后将结果与嵌入矩阵相乘。

与其手动进行这些操作,你可以使用一个嵌入袋层。输入仅仅是每个标记的索引,输出就是所有单词嵌入的总和(或平均值)。

在 PyTorch 中使用嵌入与嵌入袋
理解了概念之后,我们来看看如何在 PyTorch 中具体实现它们。
首先,你需要获取标记。从数据集和词汇表中初始化分词器和迭代器。input_ids 函数对每个数据样本进行分词并生成索引。这些索引存储在列表 index 中,列表中的每个元素对应不同文档的索引。
以下是初始化嵌入层的步骤:
- 你可以初始化嵌入层,并指定嵌入的维度大小。
- 接下来,确定词汇表中唯一标记的数量。
- 现在,使用
nn.Embedding创建嵌入层embeds。
你应用这个嵌入对象。输入是短语 “I like cats” 的索引,以检索其嵌入。你将看到一个 PyTorch 张量表示,这个张量中的每一行分别对应单词 “I”、“like” 和 “cats” 的嵌入。
你可以使用索引检索最后一个数据样本 “I‘m impartial to hippos” 的嵌入。结果是一个 PyTorch 张量,每一行对应每个单词的嵌入。
初始化嵌入袋层与初始化嵌入层几乎相同。
让我们使用索引输出第一个文档的嵌入袋。你可以访问 “I like cats” 的嵌入。结果是一个 PyTorch 张量,代表所有嵌入的总和(或平均值)。对于单个样本,offset 参数始终为零。
处理偏移参数
在自然语言处理中,数据集通常被表示为一维张量。然而,在使用嵌入袋和其他应用时,识别每个文档的起始位置至关重要。
让我们用我们的样本数据集深入探讨这一点。
- 你使用
cat函数将各个文档的张量组合起来,并设置index。 - 偏移参数 记录了每个文档的起始位置。
- 你可以计算每个样本中的标记数量。这一步有助于精确定位初始位置。
- 使用累积求和方法,你可以累加长度,从而确定每个序列的起始位置。
你使用 embedding bag 函数以及 offset 参数,就像使用索引张量一样。结果就是每个独立文档的嵌入袋,即其单词嵌入的平均值。
总结
本节课中,我们一起学*了独热编码、词袋模型、嵌入和嵌入袋。
- 独热编码将分类数据转换为特征向量。
- 词袋模型表示法将一个文档描绘为其所有独热编码向量的聚合或平均值。
- 当你将一个词袋向量输入到神经网络的隐藏层时,输出就是所有嵌入向量的总和。
- 在 PyTorch 中,
Embedding和EmbeddingBag类用于实现嵌入和嵌入袋功能。


生成式人工智能工程:105:使用TorchText进行文档分类预测 📄

在本节课中,我们将学*如何使用PyTorch和TorchText库来构建一个文档分类器。我们将从理解神经网络的基本概念开始,逐步深入到如何设置模型架构、处理数据并进行预测。
概述

文档分类器通过分析文本内容,能够自动将文章归类到预定义的类别中,例如科技、体育或商业。本节课将介绍神经网络的核心原理,并指导你使用PyTorch创建一个简单的分类模型。
神经网络回顾 🧠
上一节我们介绍了文档分类的概念,本节中我们来看看实现分类的核心工具——神经网络。
神经网络是一个数学函数,由一系列矩阵乘法和其他函数组合而成。它从输入层开始,例如一个词袋向量。在该层中,会进行矩阵乘法,有时还会加上一个偏置项,这构成了隐藏层。

如果输入使用词袋模型,该层被称为嵌入层,其输出被称为逻辑值。随后,对每个逻辑值应用一个激活函数,这个过程称为激活,其中每个元素被称为神经元。有时,嵌入层不会应用激活函数或添加偏置。
该操作会重复进行,执行另一次矩阵乘法,每个结果元素再次被称为神经元。通过这个迭代过程,输入数据被逐步转换,使网络能够学*分类。网络在训练期间调整的参数被称为可学*参数。
核心公式:神经网络的前向传播可以抽象表示为一系列变换:输出 = 激活函数(权重 * 输入 + 偏置)。
分类过程与Argmax函数 🎯
在文章分类中,我们将一个嵌入向量输入网络。对于每个类别,神经网络输出一个逻辑值向量,其中每个逻辑值是一个分数,反映文章属于特定新闻类别的可能性。
为了确定文章的类别,需要将输出层的逻辑值输入到Argmax函数中。Argmax函数会找出最高逻辑值对应的索引,该索引对应于最可能的类别。
核心代码:在PyTorch中,可以使用 torch.argmax(output, dim=1) 来获取每个样本的预测类别索引。
神经网络架构图解 🏗️
下图展示了一个神经网络的架构。每个圆圈代表一个神经元。从左开始,第一个框中的每个神经元对应输入向量或输入层中的一个元素,连接线代表权重矩阵。每个后续层级的神经元表示隐藏层和输出层的组成部分。经过激活函数处理后,得到隐藏层值,记为Z。最后一层的连接体现了通向输出的权重,每个神经元反映四个输出类别(例如:世界、体育、商业、科技)中的一个。之后使用Argmax函数找到得分最高的类别。
神经网络超参数 ⚙️
上一节我们介绍了网络的基本结构,本节中我们来看看如何配置它。超参数是神经网络外部设置的配置。
以下是主要的超参数类型:


- 隐藏层数量:架构中隐藏层的数量可以变化。例如,一个网络可能有一个隐藏层,而另一个可能有两个,每个都馈入下一个。请注意,输入层就是输入向量本身,因此我们通常关注具有一个隐藏层的架构。
- 每层神经元数量:可以调整每层中的神经元数量。例如,一个网络的第二个隐藏层可能由五个神经元组成,而另一个网络可能包含四个。如果第一个隐藏层是嵌入层,那么神经元数量对应于词汇表大小。输出层的神经元数量始终等于类别数量。
层数和神经元数量都可以通过经验或验证数据来选择。
使用PyTorch构建神经网络 🛠️
现在,让我们动手在PyTorch中创建一个神经网络。我们将使用TorchText中的AG News数据集。
首先,设置标准文本处理流程。需要调整函数,使标签编号从0开始。
接下来,为嵌入袋设置一个批处理函数,并添加代码将每个样本的标签附加到批次中。然后,创建一个批量大小为3的数据加载器。
以下是数据处理的关键步骤:

- 加载数据:使用TorchText加载AG News数据集,输出包含代表新闻文章的文本及其对应的类别标签。
- 查看样本:每个样本有三个标签,旁边是词元索引,这些索引构成了词袋模型的基础。观察这些索引的相对位置对于构建词袋模型至关重要。
- 定义模型:模型的架构在构造函数中定义了两个主要层。第一层是嵌入袋层,接着是全连接层。权重的初始化也在此完成。
- 前向传播:在前向传播过程中,将输入文本和偏移量送入嵌入袋层(不应用激活函数),然后馈入全连接层以产生最终输出。
核心代码:模型定义示例
import torch.nn as nn
class TextClassificationModel(nn.Module):
def __init__(self, vocab_size, embed_dim, num_class):
super().__init__()
self.embedding = nn.EmbeddingBag(vocab_size, embed_dim, sparse=False)
self.fc = nn.Linear(embed_dim, num_class)
def forward(self, text, offsets):
embedded = self.embedding(text, offsets)
return self.fc(embedded)
现在,使用指定的词汇表大小、嵌入维度和输出类别数量来创建文本分类模型的实例。
进行预测 🔮
使用文本索引和偏移量进行预测,并分配预测标签。
首先,查看PyTorch中的逻辑值。每一行代表一个不同的样本,列对应于每个类别的逻辑值。与之前行向量的例子不同,这里按列组织。
然后,对每一行应用Argmax函数。这可以识别出每行中的最大值。这个最大值的位置表明了样本0、1和2的预测类别。
预测函数处理真实文本的流程如下:它首先接收分词后的文本,通过处理流程处理文本,模型预测类别,然后输出值最高的标签作为预测类别。
当你应用该函数时,文章被归类到体育类别。在实验环境中尝试可能会产生不同的结果,因为模型尚未经过训练。
总结
本节课中我们一起学*了以下内容:
- 文档分类器通过分析文本内容,能够无缝地对文章进行分类。
- 神经网络是一个由一系列矩阵乘法和其他函数组成的数学函数。
- Argmax函数用于识别最高逻辑值对应的索引,该索引对应于最可能的类别。
- 超参数是神经网络外部设置的配置。
- 预测函数处理真实文本的流程是:接收分词文本,通过流程处理,模型预测类别。

通过理解这些概念和步骤,你已经掌握了使用PyTorch和TorchText构建基础文档分类器的方法。
生成式人工智能工程:106:使用TorchText进行文档分类训练
在本节课中,我们将学*如何使用TorchText进行文档分类模型的训练。我们将重点理解交叉熵损失函数的概念,并掌握如何通过优化过程来降低模型损失,从而提升分类性能。
🧠 神经网络与可学*参数
神经网络通过一系列矩阵和向量运算进行工作,这些运算中的权重和偏置被称为可学*参数。一个网络可能拥有数百万到数万亿个这样的参数。在数学表示中,我们通常用符号 θ 来统称这些参数。
在神经网络训练中,我们的目标就是通过调整这些可学*参数来提升模型性能。这个过程由一个称为损失函数的指标来引导,它用于衡量模型预测的准确性。我们的核心目标是找到一组最优的参数值 θ*,使得模型预测输出 ŷ 与实际标签 y 之间的差异最小化。
由于我们只关心参数,因此损失函数可以明确地表示为 L(θ)。虽然“损失”和“成本”这两个术语经常互换使用,但为了与PyTorch框架保持一致,我们将统一使用“损失”。
📊 理解损失与准确率
为了直观理解,我们来看一个例子。下图比较了神经网络预测的标签 ŷ 与实际标签 y。


图中,正确的预测用绿色表示,错误的预测用红色表示。初始模型的准确率仅为20%。我们的目标是通过调整参数 θ 来降低损失,从而提高准确率。随着参数的调整,损失会减少,真实标签 y 与预测标签 ŷ 会逐渐对齐(绿色部分增加),模型的准确率也会随之提升,这是性能改善的积极信号。
🔍 交叉熵损失:寻找最佳参数
上一节我们介绍了损失函数的目标,本节中我们来看看如何具体计算损失,特别是使用交叉熵损失来寻找最佳参数。


首先,回忆一下模型的工作流程:我们将一个嵌入向量输入网络,对于每个新闻类别,神经网络会输出一个逻辑值向量。每个逻辑值都是一个分数,反映了文章属于某个特定新闻类别的可能性,如下所示。

相关的表格将向量元素与类别关联起来:第一个是世界新闻,接着是体育、商业以及科学与技术。

这些逻辑值通过 softmax函数 转换为概率。这个过程在给定输入 X 的情况下,计算预测类别 ŷ 属于某个特定类别的概率。具体公式如下:
公式:softmax(z_i) = exp(z_i) / Σ_j exp(z_j)
每个逻辑值被指数化以确保为正数,然后对所有逻辑值的和进行归一化。这就为每个预测类别创建了一个条件概率分布。给定一个样本 X,它属于“世界”、“体育”、“商业”、“科技”类别的概率如下图所示。softmax变换在增强不同类别分数之间的区分度方面起着关键作用。
真正的概率分布 Y 如下图所示,每个条形代表 y 等于0、1、2、3的概率。
我们首先评估真实分布(即 P(y))与条件分布(即 P(y|X, θ))之间的期望值差异。我们不是简单地计算差值并平方,而是在求差之前对两个分布应用对数,这种方法称为 KL散度。在KL散度的表达式中,只有第二项依赖于参数 θ,这一项就被称为交叉熵损失。
现在的问题是如何计算 y 的分布。这里我们可以应用一个有用的技巧:我们可以使用分布 P(z) 来计算一个函数的期望值。然而,如果分布未知,我们可以通过对一组样本应用该函数并求平均值来估计它,这种技术被称为蒙特卡洛采样。
蒙特卡洛采样可用于*似真实的交叉熵损失,通过对样本 X 和 y 的函数输出求平均值,如下所示。我们可以使用softmax函数计算条件分布。需要注意的是,在实际操作中通常使用数据批次,而不是所有样本。
在PyTorch中,损失函数利用网络的输出逻辑值 Z(作为 ŷ 的代理)以及真实标签 y 进行计算。
⚙️ 在PyTorch中实现交叉熵损失
理解了交叉熵的原理后,接下来我们看看如何在PyTorch中具体实现它。
在PyTorch中,首先导入交叉熵损失模块,然后定义一个文本分类模型,接着创建一个交叉熵损失对象。
代码示例:
import torch.nn as nn

# 定义模型(此处为示例,需根据实际架构定义)
model = TextClassificationModel(...)
# 定义损失函数
criterion = nn.CrossEntropyLoss()
# 在训练循环中
logits = model(input_batch) # 模型计算逻辑值
loss = criterion(logits, true_labels) # 计算损失
模型为给定输入计算逻辑值,然后通过PyTorch的交叉熵损失函数,将这些预测值与真实标签进行比较来计算损失。
🎯 优化:最小化损失的方法

我们已经定义了损失函数来衡量模型的好坏,本节中我们将学*如何通过优化来最小化这个损失。
梯度下降方程是减少模型损失的关键。以下是其分解步骤:
公式:θ_{k+1} = θ_k - η * ∇L(θ_k)
为了将参数从当前的 θ_k 更新到下一个 θ_{k+1},我们使用当前参数值,向最小损失点“挪动”一小步。
- 学*率 η 设定了我们的步长大小。
- 损失函数的梯度 ∇L(θ) 指向损失变化最剧烈的方向。
通过沿着这个梯度的反方向移动,我们可以微调参数,从而在每一步中减少损失。
现在,让我们观察这个过程是如何展开的。梯度下降从一个初始的随机参数猜测开始,记为 k=0。在第一次迭代(即 k=1)中,我们使用损失函数的梯度(乘以学*率0.1)来调整参数,并将其加到 k=0 时的参数上,然后计算损失。对 k=2 重复此过程,更新参数并重新计算损失,损失会逐步减少。这种迭代优化持续进行,每一步都降低损失并提高模型准确率。
下图描绘了一个二维损失函数曲面,以及一个选定的起点。算法通过迭代微调参数,朝着最小损失点前进,可视化效果为红点沿着白色轨迹线向下移动,最终收敛到损失最低点。

在实际应用中,神经网络拥有数百万参数,会导致如这里所示的复杂损失曲面。损失可能急剧上升或快速下降。存在许多减少损失的策略,统称为优化方法。这些策略的细节在此不赘述,我们将其统称为改进优化或训练的方法。
📈 数据划分与PyTorch优化流程
通常,我们需要将数据集划分为三个子集:
- 训练数据:用于模型学*。
- 验证数据:用于调整超参数。
- 测试数据:用于评估模型在真实场景下的性能。
在本案例中,我们已经预先确定了超参数,因此当前的重点必须放在测试数据上。


以下是PyTorch中典型的优化流程步骤:
- 初始化优化器:首先为模型参数初始化一个随机梯度下降(SGD)优化器,并设置学*率(LR)为0.1。
- 学*率调度器:可以定义一个调度器,在每个训练周期(epoch)后按因子(gamma)降低学*率,这有助于优化过程。
- 清零梯度:在每次迭代前,将待更新变量(即可学*权重)的梯度重置为0。
- 前向传播与计算损失:模型进行预测并计算损失。
- 反向传播:调用
.backward()函数计算损失的导数(梯度)。 - 梯度裁剪:应用梯度裁剪以防止梯度爆炸,从而改善优化稳定性。
代码示例:
import torch.optim as optim
optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.9)
# 训练循环内
optimizer.zero_grad() # 清零梯度
loss = criterion(model_output, labels) # 计算损失
loss.backward() # 反向传播,计算梯度
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) # 梯度裁剪
optimizer.step() # 更新参数
scheduler.step() # 更新学*率
🎓 总结
在本节课中,我们一起学*了使用TorchText进行文档分类训练的核心概念:
- 神经网络基础:神经网络通过可学*参数(θ)进行矩阵和向量运算。训练的目标是调整这些参数以提升性能。
- 损失函数:损失函数 L(θ) 是衡量模型预测 ŷ 与真实标签 y 之间差异的准确性指标,指导优化方向。
- 交叉熵损失:我们使用交叉熵损失来寻找最佳参数。对于未知的真实分布,我们使用蒙特卡洛采样技术,通过对一组样本的函数应用求平均值来估计损失。
- 优化过程:使用优化方法(如梯度下降)来最小化损失。关键公式为 θ_{k+1} = θ_k - η * ∇L(θ_k)。
- 工作流程:预测函数处理真实文本的流程是:接收分词后的文本,通过处理管道,最终模型预测出类别。
- 数据划分:通常应将数据集划分为训练集(用于学*)、验证集(用于调参)和测试集(用于最终性能评估)三个子集。
通过掌握这些概念和步骤,你已经为使用TorchText构建和训练自己的文档分类模型奠定了坚实的基础。


生成式人工智能工程:107:在PyTorch中训练模型 🧠
在本节课中,我们将学*如何在PyTorch框架中训练一个神经网络模型。我们将涵盖数据准备、模型定义、损失函数与优化器的设置,以及完整的训练循环过程。
上一节我们介绍了数据处理的基础,本节中我们来看看如何构建并训练一个文本分类模型。
数据准备与加载
首先,你需要一个已经完成分词和索引处理的新闻数据集。以下是数据准备的关键步骤:
以下是数据分割与加载器的创建代码:
# 假设 `dataset` 是已经处理好的AG新闻数据集
train_size = int(0.8 * len(dataset))
val_size = len(dataset) - train_size
train_dataset, val_dataset = random_split(dataset, [train_size, val_size])
# 创建数据加载器
batch_size = 32
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
batch_size 定义了每次用于*似计算梯度的样本数量。shuffle=True 会在每个训练周期打乱数据顺序,这有助于模型获得更好的优化效果。
定义模型与初始化
接下来,你需要定义你的神经网络模型。在定义模型时,正确初始化权重有助于优化过程。
以下是定义一个简单文本分类模型的示例代码:
import torch.nn as nn
class TextClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, num_classes):
super(TextClassifier, self).__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.fc = nn.Linear(embed_dim, num_classes)
def forward(self, text):
embedded = self.embedding(text)
pooled = embedded.mean(dim=1)
output = self.fc(pooled)
return output
# 创建模型实例
vocab_size = 10000
embed_dim = 100
num_classes = 4
model = TextClassifier(vocab_size, embed_dim, num_classes)
设置训练组件
模型定义好后,需要初始化优化器、损失函数,并设定训练周期数。
以下是相关设置的代码:
import torch.optim as optim
# 初始化优化器和损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
# 设置训练周期数
num_epochs = 10
在每个训练周期中,你都需要记录损失值和准确率。
构建训练循环
现在,我们进入核心的训练循环部分。你将遍历每一个训练周期,即完整遍历一次整个数据集。
以下是训练循环的步骤概述:
- 将模型设置为训练模式:
model.train()。 - 遍历数据加载器,将数据集分成多个批次,这能提升训练性能。
- 对每个批次执行前向传播,计算损失。
- 执行反向传播和梯度下降来调整模型参数:
loss.backward()和optimizer.step()。 - 在每个批次处理完后更新损失记录。
以下是训练循环的核心代码框架:
for epoch in range(num_epochs):
model.train()
total_loss = 0
for batch in train_loader:
# 1. 清零梯度
optimizer.zero_grad()
# 2. 前向传播
text, labels = batch
predictions = model(text)
# 3. 计算损失
loss = criterion(predictions, labels)
# 4. 反向传播
loss.backward()
# 5. 更新参数
optimizer.step()
total_loss += loss.item()
# 记录每个周期的平均损失和验证集准确率
avg_loss = total_loss / len(train_loader)
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.4f}')
在训练过程中,如果模型在验证数据集上达到了更高的准确率,你可以选择保存其参数。


评估训练结果
随着训练的进行,损失和准确率会呈现一定的趋势。通常,随着损失值的下降,准确率会相应上升。
你可以绘制损失和准确率随时间变化的图表来直观观察这一趋势。

本节课中我们一起学*了在PyTorch中训练模型的完整流程。我们了解到需要将数据分割为训练集和验证集,并设置相应的数据加载器。通过定义模型、初始化权重、设置优化器和损失函数,我们构建了一个训练循环。在这个循环中,我们遍历周期、设置训练模式、分批次处理数据、执行梯度下降,并在每个批次后更新损失。最终,通过观察损失和准确率的变化趋势,我们可以评估模型的训练效果。
生成式人工智能工程:5:使用N-gram进行语言建模 🧠
在本节课中,我们将要学*N-gram语言建模的基本概念。我们将从理解N-gram的定义开始,逐步探讨二元模型和三元模型的工作原理,并了解如何将其扩展到更通用的N-gram模型。最后,我们会简要介绍神经网络如何应用于语言建模。
概述
语言建模的核心是预测给定上下文后下一个词出现的概率。N-gram模型是实现这一目标的经典方法。本节我们将学*如何利用前一个或前几个词来预测后续的词汇。
理解N-gram
让我们从理解N-gram开始。考虑短语“I like”和“I hate”。
你如何完成这些句子?例如,如果给你“I like”和一个空白,你会选择“surgery”还是“vacation”?大多数人看到“like”这个词后,很可能会用“vacation”来完成它。
同样,对于短语“I hate”和一个空白,预期的完成词是“surgery”。这反映了与这些词常见的关联。

这些例子展示了你的词汇选择如何经常受到前面词汇所提供的上下文影响。
考虑短语“I like vacations”,它可以表示为一个序列。你可以使用一个表格来描述它,每一列标记为单词1到单词3。

这表示一个词及其在句子中的位置下标,即单词1、单词2和单词3。对于“I hate surgery”也是如此。
二元模型
这就是二元模型发挥作用的地方。它是一个条件概率模型。
公式: P(单词3 | 单词2)
单词2代表你的上下文。第二个词对于指导预测至关重要。在二元模型中,你的上下文大小是1,这意味着你只考虑紧邻的前一个词来预测下一个词。
给定第二个词是“like”,即单词2等于“like”。第三个词可能是什么?使用表格,在第三列中统计每个可能跟在“like”后面的词的出现次数。通过检查表格观察到,“surgery”从未跟在“like”后面。因此,单词3是“surgery”的概率是0。然而,“vacation”出现在“like”之后,所以单词3是“vacation”的概率是1。
三元模型
考虑短语“surgeons like surgery”和“I hate surgery”。添加“surgeons”改变了句子的上下文,使得“surgeons like surgery”至少是合理的。
让我们创建相同的表格。三元模型也是一个条件概率函数,并且可以通过将上下文大小增加到2来改进二元模型的局限性。
公式: P(单词3 | 单词1, 单词2)
现在,除了单词2,你还使用单词1来预测单词3。
让我们检查单词3等于“surgery”的概率。给定单词1是“I”且单词2是“like”,你会发现概率是0。然而,如果你将单词1改为“surgeons”,那么单词3是“surgery”的概率现在是1。
使用三元模型进行预测
考虑短语“surgeons like surgery”、“I hate surgery”和“I like vacations”。你如何完成短语“surgeons like”?
你可以利用三元模型,通过利用表格生成的概率来预测下一个词。对于上下文,将第一个词设为“surgeons”,第二个词设为“like”,以预测第三个词(表示为ω)。识别哪个词最有可能成为第三个词。
这种优化是通过使用Arg max函数实现的,该函数从给定第一个词是“surgeons”且第二个词是“like”的不同可能第三个词中,选择概率最高的词。
如图所示,所有可能的第三个词结果的概率都是0,除了“surgery”。因此,预测的第三个词是“surgery”。
扩展到任意位置和时间
让我们考虑预测下一个词,单词3。给定第一个词是“I”,第二个词是“like”。
如果你旨在预测某个任意点T的词,使用前面的点T-1和T-2。并且如果你假设这些关系随时间保持不变(即意味着平稳性),那么你可以在任何时间点应用三元或二元模型。
对于三元情况,你可以使用位置T-2和T-1的词来预测位置T的词,遵循相同的预测过程。
广义N-gram模型
你可以将三元的概念推广到N-gram模型,它允许任意的上下文大小。
公式: P(单词T | 单词T-N+1, ..., 单词T-1)
在这个扩展的框架中,N表示所选的上下文大小。然而,计算更大上下文大小的概率会变得越来越复杂。
幸运的是,神经网络提供了一种解决方案,可以*似这些概率。具体来说,应用softmax函数来估计概率,然后训练一个神经网络基于前面的词来预测下一个词。上下文词被用作特征,即上下文向量,来定义前面的词。
神经网络与N-gram
让我们回顾前面的例子,其中你的词汇表中的每个词(即I, hate, like, surgeons, surgery, vacation)由一个六维的独热编码向量表示。
考虑用上下文大小为2来编码短语“I like”。如果你采用词袋模型,“I like”的向量表示将与“like I”的向量表示相同。为了建立连接向量,将对应于每个词的各个向量连接起来,如图所示。
在神经网络领域,上下文向量通常定义为你的上下文大小和词汇表大小的乘积。通常,这个向量不是直接计算的。相反,你应该通过连接嵌入向量来构建它。
神经网络架构
让我们检查一下N-gram模型的神经网络架构。假设你正在处理一个小问题:一个六词词汇表和一个两词上下文。
回想一下为四篇文章分类而设计的神经分类器,它有四个输出。然而,该网络必须预测六个可能输出中的一个,词汇表中的每个词对应一个。因此,输出层有六个神经元。
给定上下文大小为2,相应地调整输入。输入维度是词汇表大小和上下文大小的乘积。对于六个词和上下文大小2,你的输入维度是12。直接使用最终层的输出,无需将其转换为概率。
这个前馈神经网络忽略了与T的依赖关系,因为它没有内置机制来捕获句子中单词的顺序或位置,这与更擅长生成文本的现代神经网络不同。
总结
本节课中我们一起学*了语言建模的基础知识。
你学*了二元模型是一个上下文大小为1的条件概率模型,即你只考虑紧邻的前一个词来预测下一个词。


三元模型也是一个条件概率函数,并且可以通过将上下文大小增加到2来改进二元模型的局限性。
三元的概念可以推广到N-gram模型,它允许任意的上下文大小。
在神经网络领域,上下文向量通常定义为你的上下文大小和词汇表大小的乘积。通常,这个向量不是直接计算的,而是通过连接嵌入向量来构建的。


生成式人工智能工程:109:将N-gram作为神经网络与PyTorch 🧠

在本节课中,我们将学*如何使用PyTorch构建和训练一个N-gram语言模型。我们将了解如何将N-gram模型视为一个神经网络分类问题,并掌握从数据准备到模型预测的完整流程。
概述:N-gram模型与神经网络
N-gram语言模型本质上是一个分类模型。它通过一个固定大小的上下文窗口(例如,前两个词)来预测下一个词。在PyTorch中,我们可以用神经网络来实现这个模型,利用嵌入层和全连接层来捕捉词与词之间的关系。
上一节我们介绍了N-gram的基本概念,本节中我们来看看如何用PyTorch具体实现它。
构建模型架构




在PyTorch中,你可以创建一个嵌入层,其词汇表大小可以任意指定,同时设置嵌入维度和上下文大小(例如2)。
下一层的输入维度必须是上下文向量维度与上下文大小的乘积。
公式:下一层输入维度 = 嵌入维度 * 上下文大小
我们从两个索引开始,代表输入上下文大小为2。这两个样本被用作嵌入层的输入。输出是两个维度为3的嵌入向量。

使用reshape方法将嵌入向量重塑为上下文向量,其维度是上下文大小乘以嵌入维度。这实际上是将所有样本连接在一起。这个结果被用作下一层的输入。

理解滑动窗口机制
N-gram模型通过逐步移动一个称为“滑动窗口”的机制,来预测目标词周围的词。在一个二元语法(Bigram)模型中,位置T的词的预测基于位置T-1和T-2的词。通常从T=3开始,以避免负索引。
每一行代表一个不同的t值,对应词的位置。第二列显示上下文,第三列显示预测的目标词,两者都依赖于该行的t值。
考虑短语“I like vacations”,每个词的下方有其索引。对于T=3,上下文是“I like”(蓝色部分),预测的目标词是“vacations”(红色部分)。移动到T=4(第二行),上下文更新为“like vacations”,下一个预测词是“with”。此方法应用于整个序列。
实现数据批处理



在PyTorch中实现N-gram建模时,需要实现窗口化以创建上下文词和目标词的批次。
以下是创建批次的过程:
- 初始化过程,使用一个批处理函数生成目标和上下文。
- 一个
for循环从上下文大小的起点开始迭代(本例中从2开始,标志着“vacation”成为窗口内的第一个目标)。 - 上下文通过从
i中减去j来收集。 - 然后向前滑动窗口,递增
i以捕获整个序列中连续的目标和上下文。
现在,你将创建一个玩具数据。接下来,创建一个将文本转换为索引的流程管道。不使用数据集对象,你将使用一个列表对象。


训练模型与评估指标



在训练模型时,应优先将损失而非准确率作为你的关键性能指标。
训练模型的方式与分类模型类似。为了确保形状一致,需要对令牌进行填充。这里,你将使用前面的值进行填充以实现对齐。
从词汇表通过vocab.get_itos()方法获得的index_to_token属性是一个列表,其中每个元素对应一个词,而列表中的索引对应该词的令牌索引。
这个列表充当一个映射,将神经网络的数值输出(可能代表类别或令牌索引)转换回人类可读的格式,本质上充当了一个解码器。因此,当神经网络预测出一个索引时,可以使用此列表来检索相关的词。
进行预测
让我们进行一次预测。对一个字符串“never gonna”应用文本处理流程。结果是列表[3, 1],代表令牌索引。



将令牌索引列表转换为PyTorch张量,用模型进行预测,并选择具有最大值的索引。最后,使用索引到令牌的映射将索引转换为词。
你可以使用此函数通过模型生成一个词序列。
总结

本节课中我们一起学*了以下核心内容:
- 在PyTorch中,N-gram模型允许使用任意的上下文大小。
- N-gram语言模型本质上是一个使用上下文向量和额外隐藏层来提升性能的分类模型。
- N-gram模型通过逐步移动滑动窗口来预测目标词周围的词。
- 在训练模型时,应优先将损失作为关键性能指标。
生成式人工智能工程:110:Word2Vec与CBOW模型简介
在本节课中,我们将学*Word2Vec的基本概念及其核心模型之一——连续词袋模型。我们将了解如何将词语转化为向量表示,以及如何使用神经网络模型来预测目标词。
什么是Word2Vec?
Word2Vec是“Word to Vector”的简称。它是一组用于生成词嵌入或词向量的模型。词向量是捕捉词语本质的数值表示。
例如,由于含义相似,“king”的向量与“man”更接*,“queen”的向量与“woman”更接*。有趣的是,从“king”的向量中减去“man”的向量,会得到一个与“queen”向量相似的向量,这展示了词嵌入在捕捉词语关系方面的有效性。
这些向量可以应用于自然语言处理任务中,通过替换随机生成的嵌入向量来提升模型性能。

为了获得这些向量,可以从随机生成的嵌入向量开始。

你可以使用一个包含输入层、嵌入层和输出层的神经网络模型。词语被输入到嵌入层,该层与Softmax输出层交互,以预测上下文词语。
训练过程涉及调整隐藏层权重 W 和输出层权重 W‘,以优化词向量表示。输入层和输出层的神经元数量与词汇表大小相对应。而嵌入层的大小由用户选择,它定义了词向量的维度。
训练完神经网络后,如果网络预测“queen”跟随“woman”的概率高于跟随“man”的概率,那么“queen”的嵌入向量将更接*“woman”而非“man”。同样,如果“king”在概率上更与“man”相关联,那么“king”的结果向量将更接*“man”而非“woman”。
上下文窗口与目标词预测

现在,让我们尝试使用网络,通过给定窗口内的其他词语来预测词语。将窗口宽度设置为1。

在时间步 t=1,“exercises”是目标词,“she”和“every”是上下文词。在时间步 t=2,“every”成为目标词,被“exercises”和“morning”环绕作为上下文。
对于窗口宽度为1的情况,上下文包括 t-1 和 t+1 位置的词。目标词位于时间步 t。
- 在 t=1,上下文由“she”和“every”组成,目标词是“exercises”。
- 移动到 t=2,上下文包括“exercises”和“morning”,目标词是“every”。
连续词袋模型
接下来,你将学*连续词袋模型。该模型利用上下文词语来预测目标词,并生成其嵌入向量。
在下表所示的例子中,第二列列出了网络的预测词。当输入“she”和“every”时,模型预测“exercises”。当输入“exercises”和“morning”时,模型预测“every”。
在CBOW模型中,对于句子“she exercises every morning”,输入维度为4,与语料库中唯一词的数量匹配。目标是预测词语的可能性,因此输出维度也是4。

目标词是“exercises”,上下文词“she”和“every”被组合成一个词袋向量。该向量通过包含词嵌入的隐藏层,在输出层中,“exercises”对应的逻辑值最高,表明模型对此上下文的预测。

现在,让我们移动上下文窗口来考虑新的输入。词语“exercises”和“morning”被编码为独热向量,并作为输入提供给模型。
在训练过程中,你的目标应该是微调模型的权重,使其能有效预测目标词“every”,该词应对应于输出层中最高的逻辑值。
创建CBOW模型
以下是创建CBOW模型的步骤:
首先,初始化模型。然后,使用 nn.EmbeddingBag 定义嵌入层,它会计算上下文词嵌入向量的平均值。你还需要使用 self.fc 配置全连接层,其输入大小为嵌入维度,输出大小为词汇表大小。
在 forward 方法中,你将通过嵌入层传递输入文本和偏移量,该层会检索上下文词嵌入并计算其平均值。接下来,应用ReLU激活函数。之后,ReLU激活的输出会通过全连接层。最后,创建CBOW模型的一个实例。

接下来,使用步骤1和2初始化分词器。使用步骤3,从分词后的数据创建词汇表。在步骤4中,将上下文大小设置为2。然后,在文本上滑动以形成上下文-目标词对。

接下来,使用步骤5,为网络设置文本处理管道、数据加载器和批处理函数。它将拥有一个批大小为64的词袋,为下一个词预测任务的训练准备批次。
总结
本节课中,我们一起学*了以下内容:
- Word2Vec是“Word to Vector”的简称,是一组用于生成词嵌入或词向量的模型。词向量是捕捉词语本质的数值表示。
- 一个神经网络模型由输入层、嵌入层和输出层组成。词语被输入到嵌入层,该层与输出层交互以预测上下文词语。
- 输入层和输出层的神经元数量与词汇表大小相对应。而嵌入层的大小由用户选择,它定义了词向量的维度。
- 连续词袋模型利用上下文词语来预测目标词,并生成其嵌入向量。


生成式人工智能工程:111:Word2Vec跳元模型与预训练模型 🧠
在本节课中,我们将学*Word2Vec的跳元模型(Skip-gram),了解其工作原理,并学*如何在PyTorch中构建和训练它。我们还将探讨如何使用大规模预训练的词嵌入模型,如GloVe,来提升自然语言处理任务的效果。
概述
跳元模型是连续词袋模型(CBOW)的逆向模型。它旨在根据给定的目标词,预测其周围的上下文词。例如,对于目标词“exercises”,模型需要预测其前后的词“she”和“every”。我们将学*如何用PyTorch实现这个模型,并了解如何利用预训练的GloVe词向量。
跳元模型(Skip-gram)原理
上一节我们介绍了Word2Vec的基本概念,本节中我们来看看跳元模型的具体机制。
跳元模型的核心任务是:给定一个目标词,预测其周围的上下文词。这与CBOW模型(根据上下文预测目标词)正好相反。
例如,在句子“She exercises every day”中:
- 当目标词
T=1是“exercises”时,模型需要预测其周围的上下文词“she”和“every”。 - 当目标词
T=2是“every”时,模型需要预测其周围的上下文词“exercises”和“day”。
需要澄清的是,这里的“目标词”是模型的输入(自变量),而需要预测的“上下文词”才是模型的输出(因变量)。


在模型架构中,目标词(如“exercises”)被编码为一个在词汇表空间中的独热向量(one-hot vector),即该词对应位置为1,其余位置为0。输出层则负责预测周围的上下文词。

训练完成后,模型在每个上下文位置上的输出逻辑值(logits),应对真实的上下文词具有最高的预测分数。


模型的简化策略

为了简化复杂的上下文预测任务,跳元模型将其分解为多个更小的、可管理的子任务。具体做法是:一次只预测一个上下文词。
例如,对于目标词“exercises”:
- 模型会独立地预测其前一个词“she”。
- 模型也会独立地预测其后一个词“every”。
这样,原本需要同时预测多个词的任务,就被分解成了多个独立的二分类(或多分类)问题。
现在,让我们看看对应的神经网络结构。输入是目标词“exercises”的独热编码向量,而预测输出是“she”。

另一个训练样本对是:输入“exercises”,预测输出“every”。

在PyTorch中构建跳元模型
了解了原理后,我们动手在PyTorch中实现一个跳元模型。
以下是构建模型的关键步骤:
- 初始化模型:定义模型类并初始化其层。
- 定义嵌入层:使用
nn.Embedding创建词嵌入层,参数为词汇表大小和嵌入维度。self.embeddings = nn.Embedding(vocab_size, embed_dim) - 定义全连接层:一个线性层,输入维度为嵌入维度,输出维度为词汇表大小,用于生成最终的预测分数。
self.fc = nn.Linear(embed_dim, vocab_size) - 前向传播:在
forward方法中,输入文本通过嵌入层得到词向量,然后通过全连接层得到预测结果。 - 创建模型实例:使用定义好的词汇表大小和嵌入维度实例化模型。
数据准备与序列生成
跳元模型的数据生成函数与CBOW模型类似,但目标和上下文词的顺序是相反的。
以下是如何为跳元模型准备训练数据的步骤:
- 生成样本对:每个样本包含一个目标词和其对应的一个上下文词。
- 拆分完整上下文:通过嵌套循环遍历目标词的所有上下文词,为每个上下文词创建一个(目标词,单个上下文词)的样本对。这样就将完整的上下文拆分成了多个离散的部分。
- 扁平化数据:将生成的所有样本对整理成列表。
- 创建数据加载器:使用PyTorch的
DataLoader来批量加载数据。
查看数据加载器中的一个批次,可以看到返回的张量包括目标词和上下文词,以及它们的索引。同时显示对应的词元(tokens)可以让训练过程更直观。
训练模型
准备好数据和模型后,我们开始训练。以下是训练参数设置:
- 学*率:定义优化器的步长。
- 损失函数:使用交叉熵损失
nn.CrossEntropyLoss()来衡量预测误差。 - 优化器:使用Adam等优化器来更新模型参数。对于跳元模型,只需将优化器的输入对象改为跳元模型实例的参数。
optimizer = optim.Adam(skipgram_model.parameters(), lr=learning_rate) - 学*率调度器:可选,用于在训练过程中动态调整学*率。
定义训练函数,循环指定的轮数(epochs)进行训练。函数内部需要包含一个条件判断,以区分输入是用于跳元模型还是CBOW模型。该函数最终返回训练好的模型和每轮的平均损失列表。
调用训练函数,传入为跳元模型准备的数据加载器,即可开始训练。
获取词嵌入向量
模型训练完成后,我们如何得到想要的词向量呢?
训练好的嵌入层(nn.Embedding)的权重矩阵就是我们要的词嵌入。每一行对应一个词的向量表示。
你可以通过词的索引来获取其对应的嵌入向量:
# 假设 `model` 是训练好的跳元模型,`word_to_ix` 是词到索引的映射
word_index = word_to_ix[‘specific_word’]
word_vector = model.embeddings.weight[word_index]
使用预训练词嵌入(GloVe)
从头训练词嵌入需要大量数据和计算资源。在实践中,我们常直接使用在大规模语料上预训练好的词向量,如斯坦福大学的GloVe。
以下是利用预训练GloVe词向量进行文本分类的步骤:
- 加载预训练向量:使用
torchtext.vocab加载GloVe向量(例如‘glove.6B’)。import torchtext.vocab as vocab glove = vocab.GloVe(name=‘6B’) - 构建词汇表:根据你的数据集创建一个自定义词汇表对象,并将预训练向量与之匹配。
- 集成到模型层:将匹配好的GloVe向量作为权重初始化一个PyTorch嵌入层。
embedding_layer = nn.Embedding.from_pretrained(glove.vectors) - 词嵌入袋操作:对于分类任务,常对句子中所有词的向量进行聚合(如求和、平均),得到一个句子的固定长度表示。
- 微调选项:如果你的数据集较大,可以尝试将嵌入层的
freeze参数设为False,允许在任务训练过程中微调这些词向量。

总结
本节课中我们一起学*了:
- 跳元模型原理:它与CBOW模型相反,根据目标词预测其周围的上下文词。
- 简化策略:通过一次预测一个上下文词,将复杂任务分解。
- PyTorch实现:学*了如何构建嵌入层、定义模型、准备数据以及训练跳元模型。
- 获取词向量:训练后,嵌入层的权重即为学到的词嵌入。
- 预训练模型应用:介绍了如何使用GloVe等大规模预训练词向量来提升NLP任务(如文本分类)的起点和性能。

通过结合理论理解和动手实践,你现在应该能够描述、构建并应用跳元模型,并理解预训练词嵌入的价值。
生成式人工智能工程:112:序列到序列模型与循环神经网络简介 🧠
在本节课中,我们将学*序列到序列模型和循环神经网络的基本概念。这些模型是自然语言处理任务,如机器翻译、文本摘要和聊天机器人的核心。
概述
序列到序列模型是生成式人工智能的重要组成部分。它们能够处理可变长度的输入序列,并生成可变长度的输出序列。循环神经网络则为处理序列数据提供了记忆能力,是构建这些模型的基础。
序列到序列模型的应用

以下是序列到序列模型在生成式人工智能中的几个关键应用:
- 机器翻译:例如,将英语短语转换为法语。
- 聊天机器人:将用户的查询转换为对话式的响应。
- 文本摘要:将冗长的文本压缩为简洁的摘要。
- 代码生成:根据任务描述,由AI生成相应的代码。

序列模型的工作原理
序列到序列模型处理多个输入,如上图所示的5个嵌入向量(x1 到 x5)。输出(y1 到 y5)通常代表标记,但应用广泛。值得注意的是,输入和输出序列的长度不必相等。
- 序列到标签任务:接收多个输入,产生单个标签。例如,用于文档分类。
- 标签到序列任务:从单个输入生成完整序列。例如,用于图像生成的生成模型。
上下文对于准确理解语言至关重要。以句子“The man bites the dog”和“The dog bites the man”为例,词袋模型无法区分它们,因为词频相同。而通过独热编码词或嵌入的序列表示,则能捕捉它们的不同含义。这种差异在图中以红色高亮显示,说明了此类表示如何实现对上下文的更精确理解。
从独立同分布到序列依赖
神经网络通常假设每个样本是独立同分布的。想象从一个装有标有变量 Y_t 的球的瓮中多次抽取。如果每次抽取独立且瓮的内容不变,则 t 是冗余的,概率分布保持不变,满足IID假设。
然而,考虑每次抽取后不将球放回的情况。此时,Y 在 t=1 时的概率是 1/11,但在 t=2 时的概率取决于 t=1 时的抽取结果。这需要在每次抽取后调整概率分布,从而引出了条件分布的概念。模型需要记忆能力。
因此,你需要一个能够利用记忆来处理这种依赖关系的模型。
序列模型的数据准备
在序列模型训练中,你需要对数据进行精炼处理。

以下是数据准备的关键步骤:

- 添加起止标记:从一组句子开始,用
BOS和EOS标记表示句子的开始和结束。这种清晰的划分有助于模型识别序列内的起点和终点。 - 按长度排序:将句子按长度排序,以便将长度相似的句子批处理在一起,从而简化学*过程。
- 填充短句:像PyTorch这样的框架要求批次大小一致。因此,你需要在较短的句子后附加填充符号,使其长度与批次中的其他句子相等。
循环神经网络简介
现在,我们来学*循环神经网络。RNN是一种使用序列或时间序列数据的人工神经网络。顾名思义,RNN旨在记住过去的信息,并用它来影响未来的决策。
让我们探索一个简单的RNN是如何运作的。


- 输入层:标记为
X_t。这是RNN在每个特定时间步接收数据的地方。这就像在每个时刻接收一个新的拼图块。 - 隐藏状态:标记为
H_t。可以将其视为网络的记忆,在这里你将应用一个激活函数(通常是tanh)。它捕获并保留所有先前输入的信息。这至关重要,因为它允许网络在处理当前输入时记住并考虑过去的数据。 - 连接层:观察隐藏状态和当前输入如何结合。在一些RNN中,它们被连接起来,形成一个更大的数据集以供处理。这对于理解网络连接过去和现在信息的能力至关重要。
- 输出:表示为
Z_t。这是RNN在每个时间步基于当前输入及其在隐藏状态中记住的内容计算出的结果。上下文就存储在这里。
这个过程类似于常规神经网络。对于分类问题,你可以简单地获取最大值。在语言建模中,你可以使用RNN来预测单词 ω_hat。这种方法称为贪婪解码,即模型选择得分最高的标记并将其作为预测返回。请注意,Top-K采样策略似乎比传统的贪婪方法能产生更流畅的文本。
随时间展开的RNN
让我们将RNN随时间展开。
- 从初始隐藏状态(一个零向量)开始。
- 对于每个输入
X_t,RNN更新其隐藏状态H_t并产生输出Ŷ_t。 - 对于下一个迭代中的新输入
X_t,网络利用第一个时间步的隐藏状态,从过去学*以告知未来。 - 为了输出一个序列
Ŷ_T,有时你只需要使用隐藏状态,或者你甚至可以将最后一个隐藏状态H作为另一个RNN的输入。
RNN的增强版本
RNN只能记住短期信息,并且训练具有挑战性。两种流行的RNN增强版本是门控循环单元和长短期记忆网络。

- GRU:在GRU中有两个门:重置门
r和更新门z。更新门决定保留多少先前的隐藏状态,而重置门决定丢弃多少先前的隐藏状态。它们共同工作以更新隐藏状态并控制信息随时间流动。最后,在模型进行预测之前应用另一个激活函数。 - LSTM:LSTM单元包括输入门、遗忘门和输出门。它扩展了网络的记忆,用长期记忆补充短期记忆。它们选择性地保留和传输关键数据。

上图显示,H 作为短期记忆,辨别什么是重要的。它选择性地从 C 中过滤出与当前时间步相关的特定信息。相反,C 保留了要传递给后续时间步的全部记忆范围。
总结
本节课中我们一起学*了以下核心内容:
- 序列到序列模型在生成式AI中用于机器翻译(如英译法)等任务。
- 序列到标签任务接收多个输入以产生单个标签,可用于文档分类。
- 标签到序列任务从单个输入生成完整序列,见于图像生成的生成模型。
- RNN是一种使用序列或时间序列数据的人工神经网络,旨在记住过去的信息并用它来影响未来的决策。
- RNN只能记住短期信息且训练困难。
- 两种流行的RNN增强版本是门控循环单元和长短期记忆网络。


生成式人工智能工程:113:编码器-解码器RNN模型的训练与推理 🧠➡️🗣️
在本节课中,我们将学*如何使用PyTorch加载翻译数据集,并基于编码器-解码器循环神经网络(RNN)模型进行训练与推理。
上一节我们介绍了编码器-解码器模型的基本架构,本节中我们来看看如何具体实现其训练和推理流程。
数据准备
首先,我们需要准备用于训练的数据。我们将使用Multi30K数据集,它包含了英语到德语的训练集、验证集和测试集。
以下是数据加载与预处理的步骤:
- 运行一个已创建的
.py文件来获取Multi30K数据集。 - 该文件会完成数据整理工作,包括分词、数字化,以及添加序列开始(BOS)和序列结束(EOS)标记,并进行填充操作。
- 你将看到创建好的、可迭代的源语言(SRC)和目标语言(TRG)张量批次。
具体操作是,首先下载并运行该Python文件。然后,调用 get_translation_data_loaders 函数并指定一个任意的批次大小,以创建用于训练和验证的数据加载器。你也可以检查一个数据样本。


模型训练 🏋️
通常,序列到序列模型比标准的RNN更难训练,涉及多个因素。然而,训练的核心目标通常是通过最小化交叉熵损失来优化模型,即将预测输出的概率分布与实际标签进行比较。
我们用蓝色表示一个正确的预测。序列到序列模型的训练步骤与其他神经网络相似,但让我们强调一些关键的不同点。
以下是训练过程的核心步骤:
- 初始化模型:将模型设置为训练模式,以激活如Dropout等关键层,确保训练期间的最佳性能。
model.train() - 迭代训练批次:遍历训练数据批次,将输入序列(SRC)和目标序列(TRG)分配到正确的计算设备上。
- 生成预测:通过模型前向传播获得输出预测。
- 重塑输出张量:对于RNN这类序列模型,其输入输出形状常与其他模型不同。需要将输出张量重塑为
(target_length, batch_size, output_dim)的形式。这里,target_length代表行数(不包括起始的BOS标记,以确保不将其计入损失计算),batch_size是列数(代表批次中的每个独立序列),output_dim对应列数,代表序列中每个令牌的预测输出维度。这种重塑操作能正确对齐行和列,以便进行损失计算,使输出预测与目标维度匹配。output = output[1:].reshape(-1, output.shape[-1]) target = target[1:].reshape(-1) - 计算损失:计算预测输出与实际目标之间的交叉熵损失。
loss = criterion(output, target) - 反向传播与优化:执行反向传播并更新模型参数。
- 计算平均损失:在处理完所有批次后,计算每个批次的平均损失。
你还需要创建一个评估函数,其逻辑与训练函数几乎相同,但需使用验证数据集,并将模型设置为评估模式以加速推理。
模型推理与翻译 🔮
在序列模型中进行逐令牌翻译预测更为复杂。以下是一个预测函数的工作流程:
该函数接收一个模型、一个源语句、一个目标语言词汇表以及一个最大翻译长度作为输入。
- 格式化输入:首先,将源语句转换为模型所需的正确格式。
- 编码器处理:将其输入模型的编码器,以获得隐藏状态和细胞状态。
- 初始化翻译:用BOS令牌初始化目标张量,以启动翻译过程,并对其进行重塑。
- 循环生成:迭代循环,直到达到最大长度
max_len。在每一步中,利用上一个目标令牌和之前的状态输入解码器,以获得新的输出和状态。 - 选择令牌:从输出中选择概率最高的下一个令牌。
- 更新序列:将该令牌添加到翻译序列中并存储。
- 终止判断:如果出现EOS令牌,则结束生成过程;否则,将当前输出作为下一步的输入。
- 后处理:最后,将令牌索引转换回单词,移除特殊令牌,并将所有令牌连接起来形成最终的翻译句子。
现在,你已准备好运行实验中的所有功能。
总结 📚
本节课中我们一起学*了编码器-解码器RNN模型的训练与推理全过程。
- 我们了解到,由于涉及多个因素,序列到序列模型通常比标准RNN更难训练。
- 训练的核心目标是通过最小化交叉熵损失来优化模型。
- 训练步骤包括:初始化模型为训练模式、迭代数据批次、将数据分配到设备、生成预测、重塑输出张量以正确计算损失,以及最终计算批次平均损失。
- 在序列模型中进行翻译预测需要一个更复杂的函数,它需要逐步生成令牌,处理编码器-解码器状态,并在遇到EOS令牌时终止。

通过掌握这些步骤,你已能够使用PyTorch构建和运行一个基础的神经机器翻译模型。
生成式人工智能工程:114:编码器-解码器RNN模型的翻译 🧠➡️🗣️
在本节课中,我们将学*如何实现一个编码器-解码器循环神经网络模型。我们将了解其架构、内部工作原理,并使用PyTorch框架来构建它。

概述
编码器-解码器架构是序列到序列模型的核心,常用于机器翻译等任务。它能够接收一个序列作为输入,并生成另一个长度可能不同的序列作为输出。
上一节我们介绍了序列到序列模型的基本概念,本节中我们来看看编码器-解码器架构的具体实现。
编码器-解码器架构
循环神经网络可用于创建序列到序列模型。该模型接收序列 X 作为输入,并生成序列 Y 作为输出。注意,X 和 Y 的长度不一定相同。为此,编码器-解码器架构被引入。
一个流行的序列到序列模型例子是翻译模型。

编码器和解码器协同工作,将输入序列转换为输出序列。编码器由一系列RNN单元组成,它们逐个处理输入序列,并将隐藏状态传递给下一个RNN单元。最后一个隐藏状态(上下文)被传递给解码器模块。
解码器模块同样由一系列RNN单元组成,它以自回归的方式一次生成一个词元来构成翻译结果。每个生成的词元会与隐藏状态一起,作为下一个RNN单元的输入,用于生成输出序列的下一个词元,直到生成结束符。
随着RNN变得更复杂,我们可以抽象掉一些细节。在这里,一个RNN单元代表一个RNN。关键区别在于,前一个状态的输出会被回收,作为下一个状态的输入。对于编码器,我们只关注隐藏状态,而不使用其输出。这是因为只有解码器负责生成输出文本,编码器仅负责对输入序列进行编码。
编码器内部结构

现在您对整体架构有了了解,让我们深入编码器单元的内部。
编码器由多个编码器单元组成,每个单元负责处理输入词元。每个单元的核心是一个嵌入层,它将输入词元转换为嵌入向量。这些向量随后通过RNN单元以产生隐藏状态,该状态随后被传递给下一个编码器单元的RNN单元。
以下是构建编码器的关键步骤:
- 定义编码器类:在PyTorch中,定义一个继承自
torch.nn.Module的类。 - 初始化参数:
input_dim: 输入词汇表大小。emb_dim: 嵌入向量的维度。hid_dim: LSTM隐藏状态和细胞状态的维度。n_layers: LSTM的层数。dropout: Dropout概率,用于防止过拟合。
- 构建层:
- 一个嵌入层 (
nn.Embedding)。 - 一个LSTM层 (
nn.LSTM),其输入维度为emb_dim,隐藏状态维度为hid_dim,层数为n_layers,并应用dropout。
- 一个嵌入层 (
- 前向传播:在
forward方法中,嵌入层处理输入,LSTM层输出隐藏状态和细胞状态。编码器只保留这些状态,丢弃输出向量。
代码示例:编码器初始化
import torch.nn as nn
class Encoder(nn.Module):
def __init__(self, input_dim, emb_dim, hid_dim, n_layers, dropout):
super().__init__()
self.embedding = nn.Embedding(input_dim, emb_dim)
self.rnn = nn.LSTM(emb_dim, hid_dim, n_layers, dropout=dropout)
self.dropout = nn.Dropout(dropout)

请注意,与门控循环单元仅具有隐藏状态不同,LSTM为每一层都维护一个额外的细胞状态。

解码器内部结构

解码器同样由许多解码器单元组成,每个单元内部生成预测的词元。每个单元都有一个嵌入层,用于创建嵌入向量。这些向量通过RNN单元,输出更新后的隐藏状态,该状态将被传递给后续解码器单元的RNN单元。
接下来,一个线性层将RNN的输出映射到输出维度,以生成下一个词元。
如您所见,解码器的工作方式与编码器略有不同,它以自回归的方式接收其先前生成的词元并生成下一个词元。
以下是构建解码器的步骤:
- 定义解码器类:同样是
nn.Module的子类。 - 初始化参数:
output_dim: 输出词汇表大小。emb_dim: 嵌入维度。hid_dim: LSTM隐藏状态维度。n_layers: LSTM层数。dropout: Dropout概率。
- 构建层:
- 一个嵌入层,将输出值映射到
emb_dim大小的密集向量。 - 一个LSTM层,接收嵌入输入并产生
hid_dim大小的隐藏状态。 - 一个线性层 (
nn.Linear),将LSTM输出映射到输出维度output_dim。 - 最后,对输出应用Softmax激活函数,生成在输出值上的概率分布。
- 一个嵌入层,将输出值映射到
代码示例:解码器初始化
class Decoder(nn.Module):
def __init__(self, output_dim, emb_dim, hid_dim, n_layers, dropout):
super().__init__()
self.output_dim = output_dim
self.embedding = nn.Embedding(output_dim, emb_dim)
self.rnn = nn.LSTM(emb_dim, hid_dim, n_layers, dropout=dropout)
self.fc_out = nn.Linear(hid_dim, output_dim)
self.dropout = nn.Dropout(dropout)

构建序列到序列模型
现在,我们将编码器和解码器组合成完整的序列到序列模型。
- 定义Seq2Seq类:继承自
nn.Module,接收编码器、解码器、设备信息和目标词汇表作为输入。 - 实现前向传播:
forward方法接受源序列和目标序列。它使用一个称为 教师强制 的技术来管理训练过程。- 教师强制:一种训练技巧,在每一步解码时,以一定概率 (
teacher_forcing_ratio) 使用真实目标序列中的词元作为解码器的下一个输入,而不是使用模型自己上一步的预测输出。这有助于加速和稳定模型训练。
- 教师强制:一种训练技巧,在每一步解码时,以一定概率 (
- 流程:
- 初始化批量大小、目标序列长度和词汇表大小。
- 创建输出张量来保存解码器在每个时间步的预测。
- 使用编码器处理源序列,提取其最终的隐藏状态和细胞状态,并将其设置为解码器的初始状态。
- 解码器从目标序列的第一个词元(通常是
<sos>起始符)开始。 - 对于目标序列的每个时间步,将当前输入和先前的状态传递给解码器,保存输出。
- 根据教师强制比率,决定下一步是使用真实目标词元还是上一步的预测词元作为输入。
- 返回包含每个时间步预测的输出张量。
代码示例:Seq2Seq模型前向传播核心逻辑
class Seq2Seq(nn.Module):
def __init__(self, encoder, decoder, device):
super().__init__()
self.encoder = encoder
self.decoder = decoder
self.device = device
# ... 其他初始化
def forward(self, src, trg, teacher_forcing_ratio=0.5):
batch_size = trg.shape[1]
trg_len = trg.shape[0]
trg_vocab_size = self.decoder.output_dim
outputs = torch.zeros(trg_len, batch_size, trg_vocab_size).to(self.device)
# 编码
hidden, cell = self.encoder(src)
# 解码器的第一个输入是 <sos> 词元
input = trg[0, :]
for t in range(1, trg_len):
output, hidden, cell = self.decoder(input, hidden, cell)
outputs[t] = output
teacher_force = random.random() < teacher_forcing_ratio
top1 = output.argmax(1)
input = trg[t] if teacher_force else top1
return outputs

总结
本节课中我们一起学*了编码器-解码器RNN模型在翻译任务中的应用。
- RNN可用于创建序列到序列模型,接收一个序列输入并生成另一个序列输出。
- 编码器-解码器架构的引入使得输入和输出序列不必长度相同。
- 编码器由一系列RNN组成,逐个处理输入并传递隐藏状态,其最后的上下文状态传递给解码器。
- 解码器由一系列RNN组成,以自回归方式一次生成一个词元。
- 嵌入层将词元转换为向量,该向量通过RNN单元产生隐藏状态。
- 我们使用PyTorch框架,分别构建了编码器、解码器,并将它们组合成一个完整的序列到序列模型,其中采用了教师强制技术来优化训练过程。
通过理解这些核心组件和流程,您已经掌握了实现一个基础机器翻译模型的关键知识。


生成式人工智能工程:115:评估生成文本质量的指标 📊
在本节课中,我们将学*如何评估生成式人工智能模型(特别是大语言模型)所生成文本的质量。我们将重点介绍困惑度这一核心指标,并探讨在机器翻译等任务中常用的精确率、召回率等评估方法。
概述
生成式人工智能和大语言模型被广泛用于生成文本、图像等内容。衡量其成功与否的关键,在于其生成内容的一致性和上下文相关性。为了准确评估这些模型的性能,我们需要借助一系列评估指标。

上一节我们介绍了模型生成文本的基本原理,本节中我们来看看如何量化评估生成文本的质量。
困惑度:模型的不确定性度量
困惑度是评估语言模型效率的一个关键指标。它可以理解为模型在预测序列中下一个词时的“惊讶”或不确定程度。
核心概念:给定一个文本语料库,我们可以为整个词序列分配概率,从而衡量特定序列在数据中出现的可能性。在语言模型中,我们通过比较模型输出的预测概率分布与真实的概率分布(通常来自验证数据)来评估性能。
模型计算出的序列概率称为似然,记作 Q。这与用于衡量预测分布与真实分布之间差异的交叉熵损失函数密切相关。
公式:
在简化场景中,交叉熵损失 H(P, Q) 用于度量真实分布 P 与预测分布 Q 之间的差异。
H(P, Q) = - Σ P(x) log Q(x)
当预测分布与真实分布完全一致时,交叉熵损失为0。
困惑度正是通过对此损失值进行指数运算得到的:
困惑度 = exp(交叉熵损失)
更具体地,对于整个序列,困惑度计算为所有词元平均交叉熵损失的指数:
困惑度 = exp( (1/N) * Σ -log Q(word_i | context) )
其中 N 是序列长度。
作用:指数运算抵消了对数,将损失值转换回更易解释的空间。较低的困惑度值表示模型性能更好,预测更准确。
示例:
假设一个模型的平均交叉熵损失为 0.5,则其困惑度为 exp(0.5) ≈ 1.65。
假设另一个模型预测准确性较低,损失值为 4.96,则其困惑度为 exp(4.96) ≈ 142.6。指数函数使得性能差异更加明显。
局限性:困惑度提供了模型性能的整体度量,但无法捕捉生成文本质量的细微差别(如流畅性、创造性)。它通常仅用于衡量模型对训练集的学*程度。
基于N-Gram匹配的评估指标
为了在测试集上衡量生成文本的质量,特别是像机器翻译这样的任务,我们使用基于N-Gram匹配的指标,将生成文本与一组参考文本进行比较。
以下是计算匹配N-Gram数量的一个示例过程,通过比较假设序列(模型生成)和参考序列:

The:匹配,一元计数增至1。big:不匹配,一元计数保持为1。cat:匹配,一元计数增至2。sat:不匹配,一元计数保持为2。on:匹配,一元计数增至3。the:匹配,一元计数增至4。on the:这是第一个匹配的二元组,二元计数增至1。rug:不匹配,一元计数保持为4,二元计数保持为1。
基于此,我们可以计算精确率和召回率。
机器翻译中的精确率、召回率与F1分数
在机器翻译中,精确率和召回率用于评估生成译文相对于参考译文的质量。
精确率:衡量生成译文的准确性。它计算的是生成译文中与参考译文匹配的N-Gram所占的比例。
公式:
精确率 = (匹配的N-Gram数量) / (生成译文中的N-Gram总数)
召回率:衡量生成译文的完整性。它计算的是参考译文中的N-Gram有多少在生成译文中被覆盖。
公式:
召回率 = (匹配的N-Gram数量) / (参考译文中的N-Gram总数)
F1分数:是精确率和召回率的调和平均数,用于基于两者综合判断模型性能。
公式:
F1分数 = 2 * (精确率 * 召回率) / (精确率 + 召回率)
自然语言处理评估库
在自然语言处理领域,有多个流行的库提供了各种评估指标的实现。
以下是常用的一些库及其包含的指标:
- NLTK库:包含BLEU和METEOR等指标。
# 示例:初始化BLEU评分计算器 from nltk.translate.bleu_score import sentence_bleu - PyTorch库:提供困惑度和交叉熵损失的计算。
# 示例:使用PyTorch计算交叉熵损失 import torch.nn as nn loss_fn = nn.CrossEntropyLoss() - 还有其他库也包含BLEU和ROUGE等指标。
使用案例:以下是一个使用NLTK库计算生成译文BLEU分数的示例代码。
import nltk.translate.bleu_score as bleu
def calculate_bleu(references, hypothesis):
"""
计算假设句子相对于参考句子的BLEU分数。
references: 列表的列表,每个子列表包含一个参考句子的词元。
hypothesis: 列表,包含假设句子的词元。
"""
# 计算BLEU-4分数,使用均匀权重(0.25, 0.25, 0.25, 0.25)
score = bleu.sentence_bleu(references, hypothesis)
return score
# 创建参考译文列表(每个参考译文是词元列表)
references = [
[‘the’, ‘cat’, ‘is’, ‘on’, ‘the’, ‘mat’],
[‘a’, ‘cat’, ‘sits’, ‘on’, ‘the’, ‘rug’]
]
# 创建假设译文(模型生成)
hypothesis = [‘the’, ‘cat’, ‘sat’, ‘on’, ‘the’, ‘rug’]
# 计算并打印BLEU分数
bleu_score = calculate_bleu(references, hypothesis)
print(f”BLEU score: {bleu_score:.4f}”)
总结
本节课中我们一起学*了评估生成文本质量的核心指标。
- 困惑度:是一个关键的评估指标,用于衡量语言模型和生成式AI模型的效率。它通过计算模型损失的指数得到,值越低表示模型性能越好。
- 交叉熵损失:在困惑度计算中,用于度量模型预测分布与实际分布之间的差异。
- 指标局限性:困惑度提供了模型性能的整体度量,但无法完全反映生成文本的细微质量。
- 机器翻译评估:在机器翻译等任务中,我们使用精确率来衡量生成译文的准确性,使用召回率来衡量其完整性。
- F1分数:是精确率和召回率的调和平均数,用于综合评估模型性能。
- 评估工具:在自然语言处理领域,有诸如NLTK、PyTorch等流行库,它们提供了这些评估指标的现成实现,方便我们使用。

通过掌握这些指标,你可以更科学地评估和比较不同生成式AI模型的文本输出质量。
生成式人工智能工程:116:课程介绍 🎯


在本节课中,我们将要学*一门关于“基于Transformer的语言建模”的课程。这门课程将涵盖用于自然语言处理(NLP)的Transformer模型的基础与高级概念。
课程概述
本课程适合现有的和有抱负的数据科学家、机器学*工程师、深度学*工程师以及人工智能工程师。学*本课程,具备Python和PyTorch的基础知识,并对机器学*和神经网络有所了解将是一个优势,但并非严格要求。
完成本课程后,你将能够:
- 在基于Transformer的架构中应用位置编码和注意力机制来处理序列数据。
- 为语言建模任务使用并实现基于解码器的模型(如GPT)和基于编码器的模型(如BERT)。
- 实现一个Transformer模型,用于将文本从一种语言翻译成另一种语言。
课程模块详解

上一节我们介绍了课程的整体目标和适用人群,本节中我们来看看课程的具体内容安排。
模块一:Transformer基础
在模块一中,你将学*位置编码及其在PyTorch中的实现。你还会了解注意力机制在语言翻译中如何工作,自注意力机制如何助力语言建模,以及基于Transformer的模型如何用于文本分类。此外,你将理解缩放点积注意力机制的功能与实现,并学*如何提升注意力机制的效率。
以下是模块一包含的实践练*:
- 在Jupyter环境中使用PyTorch实现一个基础的自注意力机制和位置编码。
- 应用Transformer,通过数据加载器执行文本分类任务。
模块二:编码器与解码器模型
在模块二中,你将学*像GPT这样的解码器模型和像BERT这样的编码器模型,以及它们的训练过程和PyTorch实现。你还会学*如何使用掩码语言建模(MLM)和下一句预测(NSP)来预训练BERT模型,并为BERT进行数据准备。最后,你将通过理解Transformer架构及其实现,学*Transformer在翻译任务中的应用。
以下是模块二包含的实践练*:
- 构建并训练一个类似GPT的解码器模型和一个类似BERT的编码器模型。
- 使用PyTorch从零开始构建一个用于语言翻译的Transformer模型。
课程学*资源与方法
本课程提供了恰当的内容组合以促进学*。视频简短并聚焦于核心主题。阅读材料主要以文本形式提供详细内容。实验环节提供了技术环境、详细说明和可用于完成动手练*的代码片段。练*和分级测验将帮助你应用所学知识并评估你的掌握程度。
为了从课程中获得最大收益,请观看所有视频,完成实验以练*新技能,并尝试所有测验。
总结

本节课中,我们一起学*了这门“基于Transformer的语言建模”课程的整体介绍。我们了解了课程的目标受众、学*前提、你将掌握的核心技能,以及课程两个主要模块(Transformer基础、编码器与解码器模型)的详细内容和实践安排。现在,让我们开始这段激动人心的学*旅程,祝你好运!
生成式人工智能工程:117:位置编码 📍
在本节课中,我们将要学*位置编码。位置编码是Transformer架构中的关键组成部分,它帮助模型理解序列中元素的顺序信息。
概述
Transformer模型在处理输入序列时,会同时处理所有标记(token)。然而,与人类理解语言类似,单词的顺序对于传达正确的语义至关重要。例如,“国王和王后很伟大”与“王后和国王很伟大”的含义略有不同。如果没有位置信息,模型可能会将这两个句子视为相同。位置编码正是为了解决这个问题而设计的。
上一节我们介绍了Transformer模型并行处理的特点,本节中我们来看看如何为模型注入位置信息。
位置编码的重要性
你是否尝试过打乱字母来组成单词?单词中字母的位置顺序对于传达正确的含义至关重要。同样,在基于Transformer的模型中,捕获每个标记的位置信息对于保持语义至关重要,因为标记是独立且同时被处理的。
考虑以下两个短语:
- “king and queen are awesome.”
- “queen and king are awesome.”
这两个句子略有不同。让我们看看它们的词嵌入向量表示。你会发现,如果不包含位置信息,这两个句子中“king”和“queen”的嵌入向量表示是相同的。为了解决这个问题,我们引入了位置编码。

位置编码将每个嵌入向量在序列中的位置信息整合进去。通常,位置编码被添加到输入嵌入中,使模型能够区分输入序列中不同元素的位置。
如下图所示,在添加位置编码后,第二个句子(绿色)中“king”和“queen”的向量表示与第一个句子(蓝色)中的表示不同了。

位置编码的技术原理
位置编码由一系列正弦波和余弦波组成,涉及两个参数。
以下是这两个参数的解释:
pos参数:代表正弦波随时间变化的位置,类似于标准绘图中的时间变量t或x坐标。它对应序列中每个词的位置索引。i参数:即维度索引,它有效地为每个嵌入维度生成一个独特的正弦波或余弦波,控制每个波的振荡次数。
这些波中的每一个都被添加到词嵌入的不同维度中。让我们通过一个例子进一步探索。
位置编码计算示例
我们以序列“transformers are awesome”为例。其中每个词有一个嵌入向量,每个嵌入向量的维度为4。
pos是每个词嵌入在序列中的特定位置。在这个例子中,序列长度为3。- 每个词嵌入的维度为4,其位置编码将根据其索引
i是奇数还是偶数来区分。
以下是计算过程:
- 为“transformers”这个词的嵌入添加位置编码。对于位置编码中的每个维度
i,引入相应的正弦波或余弦波。因此,对于i=0,添加一个正弦波;对于i=1,添加一个余弦波,依此类推,确保每个维度都由其独特的波表示。 - 同样地,计算单词“are”的位置编码值。
- 最后,计算单词“awesome”的位置编码值。
你可以查看一个表格,其中描绘了对应输入句子的位置编码值。
四张图说明了位置编码,每张图对应词嵌入的一个维度,序列长度为3,因此每个正弦函数只看到三个值。
实际场景中的位置编码
让我们生成一个嵌入维度为8的位置编码来描绘一个更现实的场景。有时为了实用性,你可以将最大序列大小与词汇表大小对齐,通过旋转位置编码的行来指示不同的编码函数,而列代表序列中的时间位置。
位置编码为序列位置提供独特且周期性的值。余弦波永远不会在同一点与基线相交,这确保了模型能够有效地区分和处理可变长度的序列。其值范围在-1和1之间,防止了位置编码掩盖原始嵌入的信息。这些编码还支持可微性和相对位置关系,使模型更容易训练。
位置编码的向量表示
你也可以将每个词嵌入表示为一个列向量X。位置编码可以被概念化为一系列向量,其中每个向量P捕获序列中的一个特定位置。当这个位置向量被添加到其对应的嵌入向量时,组合后的向量保留了位置信息,确保元素序列的顺序在结果向量中得到维持。
可学*的位置编码与段嵌入
在诸如GPT之类的模型中,位置编码不是静态的,而是可学*的参数W。这些由张量表示的可学*参数被添加到嵌入向量中,并在训练期间进行优化。

某些模型(如BERT)中使用的段嵌入与位置编码相关,提供额外的位置信息。你可以将段嵌入与位置编码一起整合到现有的嵌入中。

在PyTorch中实现位置编码
现在让我们看看如何使用PyTorch实现位置编码。
考虑嵌入my_embeddings。你可以构建一个模块来将位置编码集成到嵌入中。
以下是实现步骤:
- 初始化一个
nn.Parameter张量,其形状为(max_seq_len, embed_dim),其中max_seq_len设置为超过训练数据集中最长序列的长度,embed_dim定义其深度(即嵌入维度)。 - 接下来,将标记的位置编码整合到嵌入中。
- 应用Dropout作为正则化技术以减轻过拟合。
- 应用位置编码以获得编码后的标记。
核心代码结构如下:
import torch.nn as nn
class PositionalEncoding(nn.Module):
def __init__(self, max_seq_len, embed_dim, dropout=0.1):
super().__init__()
self.dropout = nn.Dropout(p=dropout)
# 创建可学*的位置编码参数
self.pe = nn.Parameter(torch.zeros(1, max_seq_len, embed_dim))
# 或者,使用固定的正弦/余弦编码进行初始化
# ... 初始化代码 ...
def forward(self, x):
# x 形状: (batch_size, seq_len, embed_dim)
x = x + self.pe[:, :x.size(1), :] # 添加位置编码
return self.dropout(x)
你定义一个具有可学*参数的位置编码模块。实例化一个具有所需形状的nn.Parameter张量。然后将此位置编码与嵌入集成。你也可以应用Dropout作为正则化策略以降低过拟合风险。最后,该模块应用这些位置编码以获得最终的编码标记。
总结
本节课中我们一起学*了位置编码。
- 你了解到位置编码将每个嵌入在序列中的位置信息整合进去。
- 位置编码由一系列正弦波和余弦波组成,涉及两个参数:
pos(位置)和i(维度索引)。 pos参数代表正弦波随时间变化的位置。i参数,即维度索引,有效地为每个嵌入生成一个独特的正弦波或余弦波,控制每个波的振荡次数。- 位置编码也可以是可学*的参数。这些由张量表示的可学*参数被添加到嵌入向量中,并在训练期间进行优化。
- 某些模型(如BERT)中使用的段嵌入与位置编码相关,提供额外的位置信息。


生成式人工智能工程:118:注意力机制 🧠
在本节课中,我们将要学*注意力机制的工作原理。我们将从类比Python字典的翻译任务开始,逐步理解注意力机制如何应用于词向量和序列处理,并最终掌握其核心公式。
概述
注意力机制是生成式AI模型中的关键技术。它使模型能够像人在嘈杂环境中专注于对话一样,聚焦于输入数据中最相关的部分及其关系。本节我们将通过语言翻译的例子,详细拆解注意力机制的工作流程。
从Python字典到注意力机制
上一节我们介绍了注意力机制的核心思想是“聚焦”。本节中,我们来看看如何用一个简单的类比——Python字典——来理解其基础结构。
Python字典通过键值对完成翻译任务。查询时输入键(如法语单词),字典返回对应的值(英语翻译)。


# 类比:一个简单的翻译字典
translation_dict = {
"chat": "cat",
"est": "is",
"sous": "under",
"la": "the",
"le": "the"
}
在注意力机制中,我们使用向量而非字符串。以下是其结构与字典的对应关系:

- 键: 输入序列(如法语单词)的向量表示,构成键矩阵 K。
- 查询: 我们想要翻译的单词的向量表示,构成查询矩阵 Q。初始时,查询向量与键向量相同。
- 值: 输出序列(如英语单词)的向量表示,构成值矩阵 V。
键矩阵 K 和值矩阵 V 的行必须对齐,使得同一个单词的键和值位于相同行。
注意力机制公式 🔢
理解了基本结构后,我们来看看注意力机制的核心计算过程。其基础公式用于翻译单个单词:
H = Q · Kᵀ · V
其中:
- Q 是查询向量。
- Kᵀ 是键矩阵的转置。
- V 是值矩阵。
- H 是输出的向量,代表翻译结果。
计算步骤如下:
- 计算相似度:
Q · Kᵀ进行点积运算。由于我们使用独热编码向量,只有当查询向量与某个键向量完全匹配时,结果向量中对应位置才为1,其余为0。 - 提取值: 将上一步得到的向量(本质是一个选择器)与值矩阵 V 相乘,从而“提取”出对应的值向量(即翻译后的单词向量)。
要从输出向量 H 中得到最终的单词,可以使用以下公式:
ω̂ = argmaxᵢ(H · Vᵀ)
这个公式通过 H 与所有值向量进行点积,找到最匹配的那个值,其索引即为目标单词。
将注意力机制应用于词向量 🧩


前面的例子使用了独热编码,但现实中我们使用词向量。现在,我们来看看注意力机制如何与词向量结合,这使其能够翻译从未见过的单词。
我们将键和值替换为词向量,并保持行对齐。此时,查询向量与键向量的点积不再是非0即1,而是计算它们之间的相似度。
为了从相似度分数中得到一个清晰的选择,我们引入 Softmax 函数来改进公式:
Z = Q · Kᵀ
α = softmax(Z)
H = α · V
Softmax 函数的作用是:
- 将点积结果 Z 归一化为一个概率分布 α。
- 放大最大值的权重(使其接*1),同时抑制较小值的权重(使其接*0)。
- 使得 α *似一个“软”的独热编码向量,从而更平滑地选择值向量。
这个过程实现了一种搜索方法:为查询词向量找到最相似的键词向量,并据此获取对应的值词向量。
将注意力机制应用于序列 📜
在实际应用中,我们需要同时处理整个序列(如一个句子),而非单个单词。幸运的是,矩阵运算使其变得高效。
我们可以将所有查询向量堆叠成查询矩阵 Q,一次性计算整个序列的注意力输出:
H = softmax(Q · Kᵀ) · V
这个公式能够:
- 输入: 一个序列的词向量(粉色部分)。
- 输出: 一组经过上下文信息精炼的词向量(蓝色部分),更适用于翻译等下游任务。
需要注意的是,在Transformer等先进模型中,实际翻译方法会更复杂,例如会加入位置编码来记录单词在序列中的顺序信息。但对于简单的数据问题,位置编码可能并非必需。

总结
本节课中我们一起学*了注意力机制的核心概念。我们了解到:

- 注意力机制使用查询(Q)、键(K)、值(V) 三个矩阵。
- 查询向量应与键、值矩阵中的对应行对齐。
- 注意力机制可以应用于词向量,通过计算相似度来捕获单词间的上下文关系,从而处理未知词汇。
- 通过引入 Softmax 函数,可以改进注意力公式,使模型能够更清晰地从相似度中做出选择。
- 对于序列处理,可以将所有查询向量整合成矩阵 Q,进行高效的批量计算。
生成式人工智能工程:119:自注意力机制 🧠
在本节课中,我们将要学*自注意力机制的核心概念。自注意力机制是Transformer架构的核心,它使模型能够同时关注输入序列中的所有单词,从而生成包含上下文信息的词嵌入。我们将从简单的语言建模任务开始,逐步解析自注意力机制如何工作。
简单语言建模与自注意力
上一节我们介绍了自注意力机制的重要性,本节中我们来看看它在简单语言建模中的应用。简单语言建模在自注意力机制中用于预测句子中的下一个词,这是理解自然语言的一个重要方面。
例如,在下表中,左侧呈现了单词序列,右侧则是简单语言建模预测的单词。


现在,当输入单词“not like”时,简单语言建模将预测单词“hate”。同样,输入单词“do like”将预测单词“like”。这意味着当单词的上下文发生变化时,前面单词的含义也会随之改变。
词嵌入与序列矩阵
单词被转换为嵌入序列的矩阵,其中每个嵌入的单词代表序列矩阵中的一个列向量。
例如,X 代表矩阵,下标 not like 表示单词序列,其中每一列对应一个词嵌入。这意味着矩阵 X_not_like 包含了“not”和“like”的嵌入。因此,每个序列都被转换为一个矩阵 Re,并表示为数据集中的一个样本序列。
考虑 n 个样本序列,记为 Xn。这意味着每个序列可以有不同的长度。需要注意的是,像PyTorch这样的库可能会以不同的方式排列这些向量。
自注意力机制的核心组件
自注意力机制在简单语言建模中涉及生成三个关键组件:查询(Query, Q)、键(Key, K)和值(Value, V)。这个过程始于输入词嵌入与可学*参数的结合。
以下是生成这些矩阵的步骤:
- 你可以通过将输入序列矩阵乘以称为查询投影权重和偏置的可学*参数来推导查询矩阵。这里也会使用一个长度为令牌数量的行向量
1。 - 类似地,你可以通过将输入序列矩阵与另一组可学*参数(键投影权重和偏置)相乘来推导键矩阵。
- 你可以应用相同的过程来获得值矩阵,其中输入再次乘以其对应的一组可学*参数,称为值投影权重和偏置。然而,偏置项并不总是被包含在内,并且序列嵌入通常在这些操作之前添加。为了清晰起见,在这个简化的解释中省略了它们。
自注意力机制利用查询、键和值结构来优化输入词嵌入。在此上下文中,sqrt(D) 代表嵌入的维度。得到的矩阵记为 H‘,其列体现了增强的嵌入,称为上下文嵌入。此外,这些嵌入的数量与输入序列的长度一致,确保了原始表示和增强表示之间的一一对应。
你可以对上下文嵌入应用一个额外的线性层,其中你将得到输出 H,这是使用额外的可学*参数对 H‘ 的细化版本。因此,H 是初始嵌入的更细致入微的表示。
自注意力机制的应用与输出
自注意力机制有助于生成序列输出。例如,将上下文嵌入机制简化为平均操作有助于简化过程。在这个简化框架中,上下文嵌入作为神经网络的输入,其中初始层的输出记为 Z^1。
让我们考虑一个之前用于翻译的自注意力机制示例,其中自注意力机制的输出是你希望翻译的嵌入单词。在这个过程中,通过矩阵乘法计算嵌入与嵌入列表之间的点积。argmax 函数有助于获取翻译单词的令牌索引。
当使用自注意力时,点积被神经网络取代,其中 Z1 代表平均嵌入,作为神经网络的输入参数。该网络生成一系列逻辑值,记为 Z2。此外,神经网络的输入维度与嵌入空间对齐,其输出维度与词汇表大小匹配。
然而,在训练期间,神经网络使用 softmax 函数来计算所有潜在单词的概率分布。这个过程有助于训练网络和注意力机制的可学*参数。在PyTorch中,逻辑值在训练期间直接使用,而 argmax 函数用于确定序列的下一个令牌或单词索引。
预测示例与机制优势
现在让我们使用自注意力机制来预测“not like”的令牌,预期结果是“hate”。自注意力机制通过与可学*参数和偏置的乘法生成查询、键和值矩阵。
在这个例子中,输入 X 乘以投影以获得 Q、K 和 V。这个过程由一系列矩阵乘法组成。这些操作可以并行化并通过图形处理单元(GPU)处理,从而允许更高效地处理更多数据。这是自注意力机制优于循环神经网络(RNN)等其他序列模型的原因之一。
此外,你可以通过将查询矩阵与键矩阵相乘来获得注意力分数,然后对这些分数应用 softmax 函数进行归一化。接下来,将归一化的分数与值矩阵 V 相乘以推导出 H‘,它通过乘以输出投影参数来增强上下文嵌入。进一步,取这些嵌入的平均值,并通过最终映射到词汇表的层。激活对应于每个单词。最后,对这些激活应用 argmax 函数,获得的索引揭示了与“hate”相关的逻辑值。
注意力分数的深入分析
进一步深入注意力机制,你可以观察到每个序列的排列方式是输入令牌位于上方,下方是归一化的注意力和相应的值。这些值充当修改后的词嵌入。
例如,查看左侧的前两个样本,你可以发现第一列始终代表令牌“not”的值,这个模式对每个令牌都成立。同时,归一化的注意力强调了配对之间独特的关系动态,突出了序列内复杂的相互作用。
在这个例子中,从点积(键和查询)导出的注意力分数在自注意力机制中起着关键作用,揭示了模型的关注点。注意力分数针对三个短语进行了说明:“not like”、“not hate”和一个任意序列,阐明了序列内令牌之间的关系。


总结

本节课中我们一起学*了自注意力机制。你了解到,自注意力机制中的简单语言建模用于预测句子中的下一个词,这是理解自然语言的一个重要方面。单词被转换为嵌入序列的矩阵,其中每个嵌入的单词代表序列矩阵中的一个列向量。

你还学*到,自注意力机制在语言建模中利用输入词嵌入和可学*参数生成查询、键和值。自注意力机制使用查询、键、值和上下文嵌入来修改输入行向量。最后,你了解到自注意力机制通过各种方法帮助生成输出。
生成式人工智能工程:120:从注意力机制到Transformer架构 🧠
在本节课中,我们将学*注意力机制的核心概念及其在Transformer架构中的演进。我们将从缩放点积注意力开始,逐步深入到多头注意力机制,并最终理解完整的Transformer编码器层是如何构建和工作的。

从缩放点积注意力到多头注意力
上一节我们介绍了注意力机制的基本思想,本节中我们来看看其具体实现——缩放点积注意力。
缩放点积注意力机制是Transformer模型的基础,其核心是一系列矩阵乘法运算。该机制整合了查询(Q)、键(K)和一个缩放因子。缩放因子用于防止点积结果过大。根据任务类型,可能会使用掩码操作。最后,将结果与值(V)相乘以产生输出。
公式表示如下:
注意力输出 = softmax( (Q * K^T) / sqrt(d_k) ) * V
在自注意力架构中,查询Q、键K和值V都源自同一个输入列向量X。它们分别与各自的可学*参数矩阵相乘。对于语言翻译等任务,会使用交叉注意力机制,其中的值和键来自不同的输入源(例如需要翻译的语言,表示为Z)。
多头注意力机制
理解了基础的注意力计算后,我们来看看如何通过并行处理来增强其能力,即多头注意力。
多头注意力的运作方式是并行执行多个缩放点积注意力过程,每个过程的输出称为一个“头”。这种策略允许每个头关注输入序列的不同部分。


以下是多头注意力的处理步骤:
- 将输入嵌入向量分割成多个部分。
- 每个部分通过独立的注意力机制(一个“头”)进行处理。
- 将所有头的输出拼接起来。
- 将拼接后的结果通过一个最终的线性层。
代码示例:初始化多头注意力
import torch.nn as nn
# 假设嵌入维度为4,使用2个注意力头
embed_dim = 4
num_heads = 2
# 确保嵌入维度能被头数整除
assert embed_dim % num_heads == 0
multihead_attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=False)
你可以通过拼接每个头的输出,然后乘以一个最终的线性层来扩展此过程,以处理任意数量的头。头的数量是一个超参数,唯一的约束是输入维度必须能被头的数量整除。

Transformer架构:编码器
多头注意力本身只是更大架构的一部分。Transformer架构通过引入额外的层来增强注意力机制的效率。
Transformer主要分为编码器和解码器两种类型。在编码器阶段,过程始于处理已与位置编码结合的词嵌入。只有输入X被传递给多头注意力。编码器模型通常不会直接对注意力机制应用掩码。

下一步是“相加与归一化”过程。这一步至关重要,因为它将注意力输出与其原始输入嵌入相加并进行归一化。这个过程增强了模型的深度,同时缓解了潜在的梯度问题。
随后,一个前馈网络对每个位置应用一个全连接层,然后根据特定用例的需求,再进行一次“相加与归一化”。根据需求,可以将更多层集成到架构中。
在PyTorch中构建编码器层
了解了理论架构后,我们来看看如何在代码中实际构建一系列编码器层实例。
首先,指定注意力头的数量和嵌入的维度。然后确定所需的层数。下一步是通过指定头的数量和嵌入的维度来创建一个Transformer编码器层对象。接着,将层数提供给Transformer编码器构造函数,以组装一系列编码器层。在此之前会添加位置编码。
代码示例:堆叠编码器层
import torch
import torch.nn as nn
# 定义参数
num_heads = 2
embed_dim = 4
num_layers = 2
# 创建编码器层
encoder_layer = nn.TransformerEncoderLayer(d_model=embed_dim, nhead=num_heads)
# 堆叠多层以创建编码器
transformer_encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
# 随机初始化输入 (序列长度, 批次大小, 嵌入维度)
x = torch.rand(10, 5, embed_dim)
# 前向传播
output = transformer_encoder(x)
print(output.size()) # 应保持与输入相同的维度

当输入张量X通过Transformer编码器时,它会依次经过所有编码器层。每一层都应用多头自注意力,然后是前馈神经网络,并在每一步都包含残差连接和层归一化。可以看到,输出嵌入保持了与输入相同的大小。
总结
本节课中我们一起学*了注意力机制到Transformer架构的关键演进。
我们学*了缩放点积注意力机制涉及一系列整合了查询、键和缩放因子的矩阵乘法。可能会使用掩码操作,最终与值相乘产生输出。
多头注意力通过并行执行多个缩放点积注意力过程来运作。通过引入额外的层,可以增强注意力机制的效率,正如在Transformer中那样。

最后,在PyTorch中串联堆叠多个Transformer层,允许模型在不同层次上对数据中的复杂关系和表征进行建模。层的深度是一个可以调整以提升性能的重要超参数。
生成式人工智能工程:5:用于分类的Transformer编码器 🧠
在本节课中,我们将学*如何将基于Transformer的模型应用于文本分类任务。你将能够解释其工作原理,并了解创建文本处理流程、构建模型以及训练模型的完整步骤。

概述

传统的神经网络在处理文档分析时,可能导致词语间上下文关系的丢失。通过集成Transformer注意力层,可以同时处理整个词序列。这种方法使得网络在保留上下文信息的同时,能够对文档进行分类。
上一节我们介绍了Transformer的基础架构,本节中我们来看看如何将其具体应用于文本分类任务。
创建文本处理流程
以下是创建文本分类数据管道的步骤。
首先,使用Torch text从AG News数据集的训练分割中创建迭代器。输出包含代表新闻文章的文本及其对应的类别标签。
你可以为非数字的类别标题分配适当的标签:前两个句子为“商业”,最后一个句子为“科学和技术”。
对于AG News数据集,你需要为训练和测试分割都创建迭代器,使用PyTorch的DataLoader,并将95%的训练集用于训练,5%留作验证。
接着,设置一个英语文本的分词器,从数据集中生成词元,并构建词汇表。
然后,设计一个自定义的整理函数来处理序列分类数据集的特殊需求。与之前分类任务的主要区别在于,现在的输出是一个序列索引。此外,为了处理不同长度的序列,你需要应用零填充来标准化结果。
最后,为训练集、验证集和测试集创建数据加载器。检查样本时,你会看到标签是整数,其数量与批次大小匹配。变量sequence是一个张量,保存着索引化的序列。与传统的神经网络不同,在基于Transformer的模型中,张量的第一维代表序列,第二维代表批次。
创建模型
现在,让我们学*在PyTorch中创建模型的步骤。我们将深入探讨用于分类任务的编码器构造函数,并解析相关参数。重点将放在与Transformer架构和输入层顺序处理相关的方面。

嵌入层
嵌入层通过nn.Embedding实例化。该层将词汇表中大小为vocab_size的词元映射到指定维度embedding_dim的密集向量中。
为了观察编码器的运作,你可以调用前向传播函数。让我们探索该函数如何通过变量X处理输入层的每一层。

例如,文本“I like football”被分词并索引为[0, 3, 173, 41]。这些索引被输入到嵌入层,该层将每个词元转换为其对应的高维表示。

具体来说,嵌入层将每个索引映射到嵌入空间中的一个唯一向量,其中第一维对应序列长度(即词元数量),第二维代表嵌入大小。
位置编码
位置编码模块PosEncoder将序列顺序信息嵌入到词向量中。它被应用于嵌入X,以添加上下文的时间信息。

这些张量值与输入嵌入具有相同的维度,因为它们最终会被相加在一起。通过结合这两者,模型能够理解语义及其在序列中的位置。

Transformer编码器层
编码器由多个nn.TransformerEncoderLayer组成,每个层都配置了参数。然后使用nn.TransformerEncoder将这些层堆叠在一起,指定层数num_layers和注意力头数num_heads。
在添加位置编码后,你将Transformer编码器层应用于增强后的嵌入,使模型能够捕获序列的上下文。
编码器生成相同数量的嵌入,且维度相同。这些词元现在包含了上下文信息。然后,你可以沿维度0计算均值并减小维度大小。然而,对于分类任务,你可以使用这个均值。


分类器层
模型包含一个线性层nn.Linear,它充当分类器。其输入大小设置为嵌入维度,输出大小与类别数量匹配。在应用这个线性层之前,你通常会对编码器输出的嵌入进行聚合,例如取平均值。

分类器层预测输入文本所属的标签,确定给定序列的类别分类。

模型前向传播
现在,让我们看看前向传播方法。在模型的前向传播方法中,输入X被用作嵌入层的输入,并乘以模型维度的平方根以稳定梯度,为后续层做准备。其维度与神经网络略有不同,让我们仔细看看。

输入张量X代表一个长度为67的词元序列,按64个样本的批次组织。这被用作嵌入层的输入。嵌入层的输出维度是67 x 64,最后一个维度是嵌入维度100。
输入张量X通过self.pos_encoder(x)操作进行位置编码。位置编码不改变维度。

输入张量X通过self.transformer_encoder(x)操作进行Transformer编码器转换。
Transformer编码器产生的张量输出维度与其接收的输入相同。嵌入的数量等于序列词元的数量。这些被称为上下文嵌入,它们包含了更多关于上下文的信息。

张量x沿维度0取平均值,使用x.mean(dim=0)将序列信息编译成单个嵌入。

对于批次大小为64的情况,你可以将其压缩成一个单一的100维向量,类似于一个拥有64个实例、每个实例有100个特征的数据集。

张量X在前向传播方法中被线性分类器处理,代表模型对给定输入序列的预测。

分类器模型为批次中的每个样本输出64个标签预测。

训练模型
现在让我们看看如何训练模型。模型的学*配置包括:用于随机梯度下降的学*率0.1,以及用于多类分类的交叉熵损失。
选择SGD作为优化器,并配有一个步长调度器,该调度器在每个训练周期后将学*率乘以因子0.1。

为了跟踪进度,初始化了累积损失列表和周期精度列表,分别用于记录累积损失和每个周期的精度。此外,使用accuracy_old来保留上一次迭代的精度。
尽管Transformer通常用于序列到序列任务,但该模型的训练过程与标准分类问题相同。

图表显示了模型在10个训练周期内的损失和精度。你会看到,当训练损失下降时,验证精度会上升。对于更大的数据集,Transformer的表现通常优于神经网络。
总结
本节课中我们一起学*了以下内容:

- 你了解到,通过集成Transformer注意力层,可以在对文本进行分类时保留上下文。
- 创建文本流程:创建迭代器、分配训练集、生成词元、构建词汇表、设计自定义整理函数、应用填充以及创建数据加载器。
- 创建模型:实例化嵌入层、添加位置编码、应用Transformer编码器层、使用分类器层预测输入文本所属的标签。
- 训练模型:使用与标准分类问题相同的过程。
生成式人工智能工程:122:使用解码器和GPT类模型进行语言建模 🧠
在本节课中,我们将要学*解码器(Decoder)在Transformer架构中的核心作用,以及以GPT为代表的解码器模型如何进行自回归式的文本生成。我们将从解码器的基本原理讲起,逐步深入到其具体的工作流程。
概述
Transformer最初为机器翻译任务而设计,包含编码器(Encoder)和解码器(Decoder)两个主要组件。随着发展,解码器在文本生成任务中扮演了关键角色,成为GPT、LLaMA等先进大语言模型的基础。本节将解释解码器是什么,并描述其用于文本生成的工作原理。

解码器与文本生成
上一节我们介绍了Transformer的整体架构,本节中我们来看看其中的解码器组件。解码器是自回归文本生成模型的核心。
- 生成式预训练(GPT):这是一种自监督学*过程。模型训练一个解码器,根据给定的前序词序列来预测序列中的下一个词(Token)。
- 自回归模型:这类模型通过预测每个新词来生成序列,每个新词的预测都严格依赖于前面已生成的所有词。
- 微调:这是一个监督学*过程,用于将预训练好的GPT模型优化到特定任务上,例如问答或分类。
- 基于人类反馈的强化学*(RLHF):这是一种特殊的微调方法,通过人类反馈来提升模型在特定任务上的表现,在开发聊天机器人时尤其有效。
一个关键区别在于,在纯文本生成任务中,解码器独立工作,无需编码器的输入。这与翻译任务不同,翻译时解码器需要通过交叉注意力机制关注编码器的输出。在文本生成中,解码器仅根据前面已生成的词序列来预测下一个词。
解码器的工作原理
理解了解码器的基本定位后,我们来深入其内部工作机制。解码器以自回归方式运行,即根据已出现的词来预测未来的词。
例如,模型从句子起始标记(BOS)开始,预测出第一个词“IBM”。随后,将“IBM”添加到输入序列中,模型基于新的序列“BOS IBM”来预测下一个词“taught”。这个过程持续进行,模拟了人类逐词生成文本的方式。
解码器与编码器在Transformer架构中的主要区别在于解码器使用了掩码自注意力机制。
- 注意力机制核心:其核心是训练过程中的矩阵乘法运算。
- 掩码(Masking):这是一个关键技术,它确保模型在预测某个位置时,只能关注该位置之前的所有词,而不能“偷看”未来的词。这强制实现了自回归属性。需要注意的是,在推理(预测)阶段同样会使用掩码来维持这一特性。
- 编码器用作解码器:理论上,通过添加掩码,一个编码器也可以用于自回归文本生成。我们将在学*训练解码器时重点探讨掩码技术。
GPT的文本生成流程
现在,让我们过渡到使用解码器模型(如GPT)生成文本的自回归过程。我们以一个问答场景为例,模型对提示“How are you?”生成回应“Good, thanks.”
以下是生成过程的概述:

- 输入处理:过程始于提示“How are you?”(某些模型会添加BOS标记)。提示被分词并转换为词嵌入向量(图中灰色部分)。
- 添加位置信息:将位置编码应用到这些词嵌入上(图中粉色部分)。
- 生成上下文嵌入:解码器处理输入,产出一系列转换后的嵌入或标记,称为上下文嵌入(图中蓝色部分)。与静态词嵌入不同,上下文嵌入会根据单词在序列中的具体上下文而变化。
- 预测逻辑值:这些上下文嵌入类似于隐藏层的逻辑值,随后会通过一个最终的线性层,预测出一系列逻辑值(图中红色部分)。为便于理解,这里按顺序展示,但实际上这些计算是同时进行的。
- 选择下一个词:对最后一个位置的逻辑值应用argmax函数,选择得分最高的索引对应的词。在本例中,“good”被选为下一个词。
- 循环生成:被选中的词“good”被追加到先前的输入序列后,形成新的输入序列“How are you? Good”。计算“good”的词嵌入,将更新后的整个序列再次输入模型,重复步骤3-5,生成下一个词(本例中为“thanks”)。
- 终止条件:模型基于不断更新的输入序列持续生成词,直到满足特定条件,例如生成了序列结束标记(EOS),或达到了预设的最大输出长度。
总结
本节课中我们一起学*了:

- 解码器在文本生成中扮演着关键角色,是GPT、LLaMA等复杂模型的基础。
- 解码器以自回归方式工作,根据已出现的词预测未来的词。
- 解码器与编码器的主要区别在于解码器使用了掩码自注意力机制。掩码确保模型在预测时只关注前面的词,从而实现了自回归生成。
- 使用解码器模型生成文本的流程可概括为:
- 对输入词应用词嵌入和位置编码。
- 解码器生成上下文嵌入。
- 通过线性层预测逻辑值序列。
- 对最后一个词的逻辑值应用argmax函数,选择得分最高的词作为输出。
- 将输出词追加到输入序列,并重复上述过程,直至生成完成。
生成式人工智能工程:7:训练解码器模型 🧠

在本节课中,我们将要学*解码器模型的训练过程。我们将解释训练与推理阶段的区别,并深入探讨因果注意力掩码在训练中的应用。
概述
上一节我们介绍了解码器模型的基本概念,本节中我们来看看如何训练一个解码器模型。我们将从模型的输入输出表示开始,逐步理解其训练机制。
序列模型与符号表示
首先,让我们回顾并明确一些用于语言建模序列模型的新符号和概念。
- 你将使用 m̂ₜ 来表示在时间步 t 的标记(token)或单词索引。在适当的时候,它也可用于表示标记的值。
- 这个表示源于神经网络最后一层的最大输出值,该网络与解码器相关联。
- 在预测时,你使用这种方法生成一个词嵌入向量,记为 x̂ₜ,它代表你的预测。
- 你使用 ^ 符号来表示这是一个估计值。虽然词嵌入本身不是一个真正的估计值,而是基于解码器输出的一个假设向量。
自回归解码器的预测任务
让我们深入探讨与自回归Transformer解码器模型相关的预测任务。
从初始时间步 t=0 的第一个词嵌入 x₀ 开始,解码器会生成一个上下文嵌入(通常用灰色表示)。这个上下文嵌入随后被送入神经网络,该网络有助于塑造下一个编码的嵌入。
预测出的词嵌入 x̂₁ 与原始词嵌入结合,并重新输入到模型中。结果,你得到两个上下文嵌入(均以灰色描绘)。最后一个上下文嵌入被用来预测下一个标记。
这个新预测的标记和之前的嵌入被反馈给解码器。这一系列事件产生了三个上下文嵌入。再次地,最后一个嵌入被用于预测后续标记。

这个循环过程在模型的整个操作中不断重复。需要注意的是,在每个步骤中都会添加位置编码。

解码器模型的训练过程
现在,让我们看看解码器模型的训练过程。
这个训练集示例在第一列展示了输入标记,在第二列展示了它们的目标(向前移动一个时间步)。根据模型的不同,会附加特殊的标记。起始标记可能使用也可能不使用,结束标记通常会被包含,并且会应用零填充以确保序列长度一致。
你使用变量 ω 来表示标记,上标表示样本编号。每个序列都是一个独立的样本。这些输入标记被转换为词嵌入,记为 X,并输入到模型中。
本视频重点分析单个序列,类似于在神经网络中检查一个样本。
训练与推理的区别
在训练阶段,解码器的操作与推理阶段有所不同。
- 在整个训练过程中,解码器使用实际的词嵌入,而不是它们的*似值。
- 它处理从索引 0 到 3 的整个输入序列,并采用因果掩码来确保输出预测仅依赖于先前的序列元素。
- 输出记为 H 和 Z,是基于索引 0 到 3 的输入,对索引 1 到 4 的序列的估计值。
- 与推理任务中只使用最后一个标记不同,在训练中,模型使用每个序列位置的所有预测标记。
- 为了实现这一点,所有输出的上下文嵌入(以灰色标识)和输出的逻辑值(以红色标记)都被用来计算损失。
- 预测由 ω̂‘ 表示,即最可能的标记。然后使用真实标记 ω 和逻辑值计算损失。这里的损失是实际标记与预测标记之间的比较,是损失函数的简写。
计算损失后,你可以一次在一个序列或一批序列上训练模型。为简化起见,此后我们将去掉上标 ‘ 以简化符号。
自回归与教师强制
在训练过程中,模型可以使用自己在前一步的预测作为下一步的输入。
例如,初始输入 x₀ 产生输出 ω̂₁,这反过来又生成标记 x̂₁。这个预测出的标记然后被用来生成下一个标记 ω̂₂,过程以此类推。模型的输出被用来确定损失。
教师强制 是一种在训练序列模型(如Transformer)时采用的技术。
在这种方法中,不是将模型自身的预测反馈回来作为后续时间步的输入,而是使用序列中实际的前一个标记。


在这个例子中,x₀ 作为初始输入来产生模型的输出 ω̂₁。对于下一个时间步,使用实际的标记 x₁ 来生成后续输出,而不管模型之前的预测是什么。这个过程在整个序列中持续进行。
这种方法确保即使模型在任何步骤做出了错误的预测,它仍然会收到正确的标记作为下一个输入,使其能够与实际序列保持一致。因此,这有助于模型学*从输入到输出的准确映射以确定损失。
因果注意力掩码
因果注意力掩码是你在解码器训练中可以使用的一种技术,用于确定在生成过程中序列中的每个标记可以关注哪些其他标记。
在这个自注意力过程中,一个上三角为负无穷大值的因果注意力掩码被应用于注意力矩阵。这个掩码确保每个标记只能关注前面的标记或它自身,禁止未来的标记影响注意力分数。
例如,在预测下一个标记时,后续的标记不应影响标记“me”。掩码之后,分数经过 softmax 操作,这消除了未来标记的影响。因此,后续嵌入的计算完全集中在先前和当前的标记上,忽略了未来的标记。
动画展示了在解码器训练期间的掩码注意力机制。它使用完整的输入序列(记为 x₀ 到 x₃)计算注意力分数。x₀ 的分数专门用于预测 ω₁。为了预测第二个标记,注意力仅限于 x₀ 和 x₁,屏蔽掉后续的标记。这种选择性注意力扩展到所有未来的预测。
虽然这个过程看起来是顺序的,但它发生在解码器的注意力机制内部,是并行的。值得注意的是,这种掩码技术对于在推理过程中准确预测中间标记至关重要。
这个过程对于计算概率至关重要。与条件固定的神经网络语言模型不同,注意力模块将后续的概率纳入其估计中。这通过“G”的概率依赖于“me”及其前面的标记,以及“me”依赖于它自己的前驱来说明。一个关键限制是上下文大小,即模型可以视为输入的标记数量。
在训练期间确定似然度后,计算对数并与其他序列结合以训练模型。
总结

本节课中我们一起学*了以下核心内容:

- 在训练阶段,解码器的操作与推理阶段有所不同。
- 在整个训练过程中,解码器使用实际的词嵌入,而不是它们的*似值。
- 在训练中,模型在每个序列位置预测下一个标记。
- 在训练中,模型可以使用自己在前一步的预测作为下一步的输入。
- 在教师强制技术中,不是将模型自身的预测反馈回来作为后续时间步的输入,而是使用序列中实际的前一个标记。
- 因果注意力掩码确保每个标记只能关注前面的标记或它自身,禁止未来的标记影响注意力分数。
因果语言模型:124:使用PyTorch实现因果语言模型解码器 🧠
在本节课中,我们将学*如何使用PyTorch创建一个用于下一个词元预测的解码器模型,即因果语言模型。我们将涵盖数据准备、因果掩码的创建以及自定义GPT模型架构的实现。
数据准备与采样
首先,我们需要准备用于训练的数据。IMDb数据集常用于训练和评估解码器模型。每个数据记录包含一个情感标签和一个文本元素。在训练中,我们通常忽略情感标签,而专注于文本内容。

为了处理文本数据,我们需要创建一个自定义词汇表,用于将文本词元化并映射为索引。在自然语言处理中,通常会使用一些特殊词元:
<unk>:表示词汇表中未找到的词。<pad>:用于将批次中不同长度的序列填充至相同长度。<eos>:表示句子或序列的结束。
语言模型通过分析前文来预测下一个词,其中上下文长度是一个重要的超参数。以下是创建训练样本的步骤:
- 指定上下文大小(即块大小)和输入文本。
- 从文本中随机选择一个起始点,截取长度为块大小的序列作为源序列。
- 将源序列向后移动一个词元,得到目标序列。
以下是一个示例函数,用于生成源序列和目标序列:
def get_sample(text, block_size):
# 随机选择起始点
start_idx = random.randint(0, len(text) - block_size - 1)
# 获取源序列
source = text[start_idx:start_idx + block_size]
# 获取目标序列(源序列向后移动一位)
target = text[start_idx + 1:start_idx + block_size + 1]
return source, target
调用此函数,设置块大小为10,可以观察到源序列和目标序列长度均为10,且目标序列是源序列向后移动一个词元的结果。
整理函数与因果掩码
上一节我们介绍了如何生成训练样本,本节中我们来看看如何将这些样本整理成批次,并创建因果掩码以防止模型看到未来信息。
整理函数 collate_fn 用于将多个 get_sample 函数生成的样本批次化。它确保批次内的所有序列通过填充达到相同长度,并生成对应的源序列和目标序列张量。比较源序列和目标序列的索引,可以确认目标序列是源序列的逐词元移位。
因果掩码的核心作用是:在预测某个位置的词元时,模型只能关注该位置之前的信息,而不能看到之后的信息。在PyTorch中,可以使用 torch.nn.Transformer.generate_square_subsequent_mask 函数来创建这种掩码。
import torch.nn as nn
def create_causal_mask(seq_len):
mask = nn.Transformer.generate_square_subsequent_mask(seq_len)
return mask
此函数生成一个上三角矩阵,对角线及以下元素为0(允许关注),以上元素为负无穷(屏蔽未来信息)。这个掩码的维度与序列长度相同。此外,我们还需要一个填充掩码来忽略填充词元<pad>的影响,通常将填充位置标记为True。
自定义GPT模型架构
理解了数据流和掩码机制后,现在我们来构建模型本身。我们将实现一个简化的自定义GPT模型架构,用于下一个词元的预测。
该模型主要由以下部分组成:
- 词嵌入层:将输入词元索引映射为密集向量。
- 位置编码层:为嵌入向量添加序列位置信息。
- Transformer解码器层:由多层多头自注意力机制和前馈网络组成。这里的关键是使用因果掩码,使自注意力层只能关注当前位置之前的词元,从而让整个编码器堆叠表现出解码器的行为。
- 线性输出层(LM Head):将解码器的输出转换为整个词汇表上的逻辑值(logits)。
以下是模型前向传播的简要流程:
- 输入:词元序列
x。 - 步骤:
x经过词嵌入和位置编码后,输入到Transformer解码器中,同时传入因果掩码和填充掩码。 - 输出:解码器输出上下文感知的嵌入表示,最后通过线性层得到每个位置的下一个词元预测逻辑值。


import torch.nn as nn
import torch
class CustomGPT(nn.Module):
def __init__(self, vocab_size, embed_size, num_layers, num_heads):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_size)
self.pos_encoder = ... # 位置编码实现
decoder_layer = nn.TransformerDecoderLayer(d_model=embed_size, nhead=num_heads)
self.transformer_decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers)
self.lm_head = nn.Linear(embed_size, vocab_size)
def forward(self, x, src_mask=None, src_key_padding_mask=None):
x = self.embedding(x)
x = self.pos_encoder(x)
output = self.transformer_decoder(x, x, tgt_mask=src_mask, tgt_key_padding_mask=src_key_padding_mask)
logits = self.lm_head(output)
return logits

总结
本节课中我们一起学*了使用PyTorch实现因果语言模型解码器的完整流程。
- 数据准备:我们使用IMDb数据集,通过自定义词汇表进行词元化,并利用采样函数生成用于“下一个词元预测”的源序列和目标序列对。
- 掩码机制:我们学*了因果掩码的作用和创建方法,它确保了模型在预测时无法窥见未来的信息,这是自回归生成模型的核心。同时,填充掩码用于忽略填充词元。
- 模型架构:我们构建了一个自定义的GPT式模型,它包含词嵌入、位置编码、多层Transformer解码器(使用因果掩码)以及一个线性输出层。该模型能够接收一个词元序列,并输出序列中每个位置的下一个词元的预测分布。

通过掌握这些核心概念和步骤,你已经具备了实现一个基本因果语言模型解码器的基础知识。
解码器模型:125:使用PyTorch进行训练与推理 🧠

在本节课中,我们将学*如何使用PyTorch实现解码器模型的训练与推理。我们将从创建模型实例开始,逐步讲解损失计算、训练过程,并最终实现自回归文本生成。通过本教程,你将能够理解并实践解码器模型的核心概念。

创建模型实例
上一节我们介绍了解码器模型的基本概念,本节中我们来看看如何创建一个模型实例。

解码器模型是一种神经网络架构,它利用编码后的信息生成输出序列。这类模型在序列到序列的文本生成任务中非常有用,例如机器翻译和图像描述生成。值得注意的是,解码器模型与生成式预训练变换器(GPT)类似。
以下是创建模型实例的步骤:

- 首先指定模型超参数,包括编码器层数为2,注意力头数为2。
- 然后定义自定义GPT模型的实例。

# 示例代码:定义模型超参数和实例
num_layers = 2
num_heads = 2
model = CustomGPTModel(num_layers, num_heads)


计算损失
在创建模型实例后,我们需要了解如何计算损失。损失计算是模型训练的关键步骤。


在损失计算过程中,编码器模型会生成源序列和目标序列。然而,检查目标批次时,你会发现其维度是序列长度 × 批次大小。
在预测阶段,解码器模型会生成逻辑值(logits),例如类别1和类别2。逻辑值的维度是序列长度 × 批次大小 × 类别数量。此外,每个表格代表批次中的一个样本。
为准备损失计算,你可以看到逻辑值被重塑,其中每一行对应一个令牌的预测,跨越序列和批次维度。接下来,为了计算损失,你需要重塑目标张量,使其元素与逻辑值正确对应。这个过程确保逻辑值的每一行都与适当的目标结果对齐,以实现准确的损失估计。
模型训练过程
理解了损失计算后,我们来看看模型的训练过程。
训练过程与其他模型类似,例如卷积神经网络(CNN)、循环神经网络(RNN)、变换器(Transformers)和生成模型。它使用修改后的损失形状以及其他有助于优化的函数,如验证和检查点保存。
评估函数通过计算模型在验证数据集上的平均损失来衡量其性能。训练好的模型可用于生成推理结果。

自回归文本生成(推理)
上一节我们介绍了模型的训练,本节中我们来看看如何进行自回归文本生成,也称为推理。


自回归文本生成是一种文本生成方法,模型根据先前的令牌预测序列中的下一个令牌。这是一个迭代过程,解码器模型一次生成一个令牌,并使用生成的令牌作为下一次预测的输入。

准备一个编码提示有助于创建文本生成过程。这个过程作为模型生成后续令牌的起点。一旦提示被令牌化,解码器模型就可以根据输入进行处理并生成下一个令牌。在这里,填充令牌用于处理多个批次,但对于单个预测来说并非必需。
以下是生成自回归文本的步骤:
- 首先,从输入句子创建编码提示张量,完成句子。
- 然后将提示输入模型。你会看到模型中生成了掩码,并且模型将在过程中输出逻辑值。
- 接下来,选择最终的逻辑值向量。
- 现在,识别并生成具有最高逻辑值得分的令牌。
- 最后,将该令牌附加到提示中,用于后续的自回归文本生成。

你可以看到解码器模型接收更新后的提示,并生成具有最高逻辑值的令牌。然后,将该令牌附加到提示中以生成下一轮自回归文本。这个循环持续进行,直到令牌达到句子末尾,或者达到传递给解码器模型的最大新令牌数。
总结
本节课中我们一起学*了使用PyTorch进行解码器模型的训练与推理。
你学会了通过指定模型超参数来创建模型的小型实例。在计算损失时,编码器模型会生成源序列和目标序列。训练过程与其他模型类似,并使用修改后的损失形状和其他有助于优化的函数。

自回归文本生成是一种文本生成方法,模型根据先前的令牌预测序列中的下一个令牌。你学会了使用generate函数在解码器模型中创建自回归文本。最后,你学会了生成自回归文本:创建编码提示并将其输入模型,然后生成掩码并查看逻辑值输出并选择它。接下来,识别并生成具有最高逻辑值得分的令牌,并将其附加到提示中以生成后续的自回归文本。
生成式人工智能工程:126:使用MLM进行BERT预训练的编码器模型 🧠
在本节课中,我们将学*BERT模型及其仅编码器架构,并了解如何使用掩码语言建模(MLM)来训练BERT模型。
模型概述
由Google开发的双向编码器表示法(BERT),通过其对词语上下文和语义的深度理解,彻底改变了自然语言处理领域。它通过自监督学*在海量文本语料库上进行预训练,主要使用掩码语言建模(MLM)和下一句预测(NSP)任务。BERT的架构允许针对特定任务(如文本摘要、问答和情感分析)进行微调,从而将其广泛的知识适应到专业应用中。


BERT的仅编码器架构
BERT采用了仅编码器架构,即只使用Transformer模型中的编码器部分。这种设计允许BERT同时处理整个文本序列,理论上增强了对文本内部上下文和细微差别的理解。与自回归模型不同,BERT通常不用于文本生成任务,而是在广泛的语言理解任务中表现出色。
考虑预测输入中由[MASK]标记表示的已知单词的任务。例如,输入序列为“CS IBM [MASK] me”。一个自回归模型只能访问“CS”和“IBM”这两个标记来预测被掩码的单词。然而,BERT可以利用[MASK]标记两侧的完整上下文,从而做出更明智的预测。另一个区别是,BERT模型包含了训练中使用的段嵌入。
掩码语言建模(MLM)原理
上一节我们介绍了BERT的架构,本节中我们来看看其核心预训练任务——掩码语言建模。
MLM涉及随机掩码部分输入标记,并训练BERT预测原始的掩码标记。这个任务帮助BERT学*上下文表示并理解词语之间的关系。
以下是MLM的预测流程:
- 编码器输出一组上下文嵌入向量(图中灰色部分)。
- 这些嵌入向量通过另一个层,转换为一组逻辑值(图中红色部分)。
- 通过选择逻辑值最高的索引所对应的单词,来识别被掩码的单词。
# 概念性伪代码,展示MLM预测核心逻辑
contextual_embeddings = encoder(input_sequence_with_masks)
logits = linear_layer(contextual_embeddings)
predicted_token_id = argmax(logits[mask_position])
编码器模型与解码器模型的主要区别在于,编码器模型可以访问整个输入序列。
BERT的双向性优势
像BERT这样的编码器模型的一个标志是其双向训练方法,这使得模型能够理解句子中任何给定单词两侧的上下文。
例如,预测句子“农民耕种 [MASK] 来种植庄稼”中缺失的单词。与GPT等模型相比,由于其因果注意力机制,GPT只考虑前面的文本“农民耕种”。而BERT的架构允许它从两个方向分析文本。
在解码器模型(如GPT)中,因果注意力在视觉上表示为对未来的X(掩码)和对过去的O(激活)。然而,BERT采用双向机制,没有这种掩码,充分利用每个单词周围的上下文(仅由O表示)。这种双向方法使BERT对语言的细微差别和复杂的词语相互依赖关系有更复杂的把握。
MLM的具体实施策略
根据原始的BERT论文,在实施MLM时需遵循特定策略。
以下是具体的掩码替换规则:
- 随机选择15%的单词位置进行掩码。
- 如果某个单词被选中,则在80%的情况下用
[MASK]标记替换它。 - 在10%的情况下用一个随机标记(如“the”)替换它。
- 在10%的情况下保持原词不变,但仍需预测它。
这样做的目的是减少预训练(存在[MASK])和微调(不存在[MASK])之间的不匹配。数据随后用于通过交叉熵损失来预测原始单词。
让我们通过一个例子来说明:85%的单词保持不变。在剩余的15%中,80%被替换为[MASK],10%被替换为随机标记(如“the”),10%保持不变用于预测(如“cultivate”)。


课程总结
本节课中我们一起学*了BERT模型的核心知识。
我们了解到,双向编码器表示法(BERT)的架构允许对文本摘要、问答和情感分析等特定任务进行微调。BERT采用仅编码器架构,使其能够同时处理整个文本序列,从而增强对上下文和文本细微差别的理解。
掩码语言建模(MLM)涉及随机掩码部分输入标记并训练BERT预测原始掩码标记。其预测方法是:编码器输出上下文嵌入向量,这些向量通过另一层转换为逻辑值,然后通过选择逻辑值最高的索引对应的单词来识别被掩码的单词。编码器模型与解码器模型的主要区别在于编码器可以访问整个序列。
像BERT这样的编码器模型的标志是其双向性,这使得模型能够理解句子中任何给定单词两侧的上下文。


生成式人工智能工程:127:使用NSP进行BERT预训练的编码器模型 🧠
在本节课中,我们将学*如何使用下一句预测任务对BERT模型进行预训练,并了解如何将预训练好的BERT模型微调用于下游任务。
概述
BERT模型通过自监督学*在海量文本语料上进行预训练,主要使用两种任务:掩码语言建模和下一句预测。本节重点介绍下一句预测任务,该任务旨在训练模型判断一个句子是否是另一个句子的合理后续。
NSP任务原理

上一节我们介绍了BERT的掩码语言建模任务,本节中我们来看看下一句预测任务。NSP任务的目标是训练模型判断一个给定的句子序列是否逻辑上紧随前一个句子。
例如,给定句子“我的狗很可爱”,我们期望的合理后续句子可能是“它喜欢玩耍”。在BERT的训练过程中,模型也可能遇到一个随机句子,如“他喜欢学医”,作为潜在的后续句。模型的任务就是预测哪个句子是合适的延续。
输入表示与嵌入
为了执行NSP任务,必须对输入文本的嵌入表示进行特殊处理。在BERT中,可以使用标准分词器进行分词以简化流程。请注意,实际的BERT使用的是WordPiece分词。
以下是构建输入的关键步骤:
- 添加特殊标记:在序列开头添加
[CLS]标记。这个特殊标记用于后续的分类任务。在句子结尾插入[SEP]标记,用以表示句子的结束。 - 生成词嵌入:每个分词后的令牌都会关联其对应的词嵌入。
- 引入段嵌入:段嵌入用于区分一个令牌属于第一个句子还是第二个句子。在句子对任务中,为第一个句子(直到第一个
[SEP]标记)的所有令牌分配段嵌入值1,为第二个句子的所有令牌分配段嵌入值2。 - 加入位置编码:最后,加入位置编码,为模型提供令牌在序列中顺序的信息。
数据与标签构建
让我们通过一个例子来看如何构建训练数据。对于句子对“我的狗很可爱”和“它喜欢玩耍”,我们按上述步骤处理嵌入,并设置一个二元变量 Y 来指示句子间的连续性。
- 当
Y = 1时,表示第二个句子是第一个句子的逻辑后续。 - 当
Y = 0时,表示第二个句子不是第一个句子的逻辑后续。例如,句子对“我的狗很可爱”和“他喜欢学医”就属于这种情况。
考虑一个数据集示例:
- “我的名字是戴夫”后接“我住在多伦多”,标签
Y设为1,表示句子是连续的。 - “我的名字是乔伊斯”后接“多伦多是安大略省的首府”,标签
Y设为0,表示句子不相关。
此外,除了 [CLS] 和 [SEP] 标记,还需要引入 [PAD] 填充标记,以确保所有句子长度统一,便于批量处理。数据中也会包含用于掩码语言建模任务的标签。
模型架构与训练
在NSP任务中,输入由词嵌入组成,经过编码器处理后生成上下文嵌入。这些嵌入被用来判断给定的句子对中,第二句是否逻辑上紧随第一句,这就像一个标准的分类任务。
其中,高亮显示的 [CLS] 标记对应NSP分类令牌,它聚合了整个序列的信息。该令牌的输出会被送入一个专门为NSP任务设计的神经网络,以评估句子对之间的关系。
我们可以将其视为一个二分类问题,使用逻辑回归的输出值来确定类别。
为了训练BERT模型的编码器部分,可以利用经过NSP和MLM任务训练的神经网络的输出。训练过程涉及最小化组合损失,即两个任务损失的总和:
总损失 = MLM损失 + NSP损失
在PyTorch中,这些损失被用作损失函数的输入以驱动优化过程。
下游任务微调

预训练完成后,可以对BERT进行微调,以用于特定的下游任务。
微调涉及在特定任务的数据集上训练BERT。例如,在情感分析任务中,模型需要预测输入序列的情感是积极、中性还是消极的。在微调过程中,BERT学*任务特定的模式,并调整其表示以在目标任务上表现良好。
[CLS] 令牌的表示将通过一个神经网络进行转换,以生成模型预测。这些上下文嵌入也可用于许多其他任务,例如构建向量数据库。
总结

本节课中我们一起学*了以下内容:
- 为了执行下一句预测任务,模型被训练来判断一个给定的序列是否逻辑上紧随前一个序列。
- BERT模型的任务是预测哪个句子是合适的延续。
- 在序列开头添加
[CLS]标记,并添加[SEP]标记来表示句子的结束。 - 段嵌入用于区分令牌属于第一个句子还是第二个句子。
- 位置编码为模型提供了令牌在序列中顺序的概念。
- 在NSP任务中,输入由词嵌入组成,经编码器处理生成上下文嵌入,这些嵌入用于判断句子对中第二句是否逻辑上紧随第一句。
- 训练BERT模型的编码器组件涉及最小化组合损失,即掩码词预测损失和判断预测词是否适合上下文的NSP损失之和。
- 微调涉及在特定任务的数据集上训练BERT,例如情感分析,模型在该任务中预测输入序列的情感是积极、消极还是中性。
生成式人工智能工程:12:为BERT准备数据 📚


在本节课中,我们将学*如何使用PyTorch为BERT模型准备数据。我们将重点介绍数据预处理的关键步骤,包括分词、掩码,以及为BERT的特定训练任务(如掩码语言建模和下一句预测)创建训练就绪的输入。

概述
为BERT准备数据主要涉及几个核心步骤:首先进行分词并构建词汇表,然后应用特定的掩码策略,最后将数据格式化为BERT模型可以接受的张量形式。我们将逐一解析这些步骤。
分词与特殊符号定义 🧩

数据准备的第一步是分词和构建词汇表。你可以遵循标准的PyTorch流程,但需要注意两点。
首先,使用 get_tokenizer 函数初始化分词器。在本例中,分词器被设置为使用基础英语模型进行分词;请注意,实际的BERT使用的是WordPiece分词法。

from torchtext.data import get_tokenizer
tokenizer = get_tokenizer('basic_english')
随后,你需要定义特殊符号及其对应的索引:PAD_IDX, CLS_IDX, SEP_IDX, MASK_IDX, UNK_IDX。

PAD_IDX用于表示填充标记。CLS_IDX代表序列的开始。SEP_IDX用作序列之间的分隔符。MASK_IDX用于掩码语言建模。UNK_IDX代表未知标记。
掩码函数 🔍

掩码函数用于对标记执行掩码操作。它接收一个标记作为输入,并决定是否对其进行掩码。如果掩码标志为假,函数立即返回原始标记和一个填充标签。如果掩码标志为真,函数会随机选择一种操作,每种操作有50%的概率。
以下是掩码函数的逻辑:


- 标记被替换为
[MASK],标签被设置为从词汇表中随机选择的一个标记。 - 标记保持不变,标签被设置为同一个标记。
- 标记被替换为
[MASK],标签被设置为原始标记。
函数随后返回修改后的标记及其对应的标签。
为MLM准备数据 🎯

prepare_for_mlm 函数用于为BERT的掩码语言建模训练准备分词后的文本。
该函数接收一个标记列表作为输入,并初始化列表来存储处理后的句子、每个标记的标签以及原始标记(如果需要)。它对每个标记应用BERT的MLM掩码策略,将其替换为掩码标记,并分配一个标签来指示该标记是被掩码了还是保持不变。
函数会检查标记是否为句子分隔符。如果找到一个包含多于两个标记的有效句子,它会将当前句子及其标签添加到相应的列表中。如果包含原始标记,原始标记也会被存储。函数持续这个过程,直到所有标记都被处理完毕。

任何剩余的标记(如果存在)会被添加为一个句子。最后,函数返回为BERT的MLM训练准备好的列表。
MLM输出示例


让我们查看一个MLM输出示例。句子中的每个标记都会根据应用于该标记的掩码操作被标记。

在这个例子中:
- 第一个“the”被掩码。因此,BERT输入是
[MASK],其BERT标签是“the”。 - 标记“sun”、“sets”、“behind”、“and”、“the”、“last”、“the”没有改变。所以它们对应的标签是
[PAD]。 - “Dis”被掩码并替换为一个随机标记。因此,BERT输入是
[MASK],其BERT标签是“human scaled”。 - 最后,“mountains”和句号“.”没有改变,所以它们对应的标签是
[PAD]。

为NSP准备数据 🔗
process_for_nsp 函数用于为BERT训练中的下一句预测任务准备数据。

该函数接收一个分词后的句子列表和一个对应的掩码标签列表作为输入。它验证两个列表长度相同且有足够的句子。函数初始化列表来存储BERT输入的句子对、句子对的掩码标签以及指示句子是否连续的二元标签。
使用一个while循环,函数生成句子对,直到至少有两个可用的索引剩余。它随机选择是创建一个“下一句”场景还是一个“非下一句”场景。
- 对于“下一句”场景,它选择两个连续的句子,添加
[CLS]和[SEP]标记,分配相应的标签,并将is_next标签设为1。 - 对于“非下一句”场景,它选择两个随机且不同的句子,添加
[CLS]和[SEP]标记,分配相应的标签,并将is_next标签设为0。

处理完每一对后,已使用的索引会从可用索引列表中移除。最后,函数返回为BERT的NSP任务准备好的列表:句子对、掩码标签和 is_next 标签。
NSP输出示例
让我们查看一个输出示例。特殊符号 [CLS] 和 [SEP] 首先被添加到输入句子中。BERT标签是使用 prepare_for_mlm 函数创建的。


- 在第一个例子中,第二句紧接第一句。因此
is_next是1。 - 在第二个例子中,第二句不紧接第一句。因此
is_next是0。
准备BERT最终输入 📦
prepare_bert_final_inputs 函数用于准备BERT训练的最终输入列表。它接收BERT输入、BERT标签和 is_next 列表作为输入,并进行填充、重新格式化、重塑并转换为张量。
函数内部有一个名为 zero_pad_list_pair 的嵌套函数,用于用 [PAD] 标记对输入对进行零填充,以确保它们具有相同的长度。这对于在训练期间保持一致的输入形状非常重要。

该函数还包括用于展平嵌套列表的 flatten 函数和用于将标记转换为其对应词汇索引的 tokens_to_index 函数。




函数遍历每个输入对,为每对句子创建段标签,并使用 zero_pad_list_pair 函数对输入对和段标签应用零填充。在BERT中,段嵌入用于区分两个输入句子。这是必要的,因为BERT的输入由一对句子组成(用于NSP或问答类任务),模型需要理解哪些标记属于哪个句子。

如果 to_tensor 标志设置为真,函数会展平填充后的输入和标签,将标记转换为索引,并将它们转换为PyTorch张量。否则,如果 to_tensor 为假,则直接将展平后的填充输入、标签、段标签和 is_next 值附加到最终的输出列表中。
最终输入示例

让我们查看一个输出示例。句子被零填充,每个标记被映射到其词汇索引(例如,[CLS] 到1,“he”到33,[SEP] 到2,[PAD] 到0)。掩码标签也被填充并映射到词汇索引。在本例中,除了标记“he”被掩码外,所有标记都未改变。接下来创建段标签,其中第一句的标记标记为1,第二句的标记标记为2,零填充标记标记为0。最后,返回 is_next 以指示第二句不紧接第一句。
数据写入与完整流程示例 💾
此代码将处理后的互联网电影数据库数据写入CSV文件。输出将如下所示。
让我们查看一个数据准备输出的完整示例。给定一个语料库,会创建句子对作为输入。输入被分词和数字化,添加特殊标记 [CLS] 和 [SEP],然后进行零填充。接着,应用掩码策略。根据掩码策略,创建BERT标签,其中除掩码标记外,每个标记的标签都是0。同时创建段标签,显示每个标记属于哪个句子。最后,is_next 标签显示第二句是否紧接第一句。
总结
本节课中,我们一起学*了为BERT准备数据的完整流程。

你了解到,对于数据准备,首先必须使用 get_tokenizer 函数初始化分词器,然后定义特殊符号及其对应的索引。掩码函数用于对标记执行掩码操作,它接收一个标记作为输入并决定是否对其进行掩码。prepare_for_mlm 函数接收一个标记列表作为输入,并初始化列表来存储处理后的句子、每个标记的标签以及原始标记(如果需要)。它对每个标记应用BERT的MLM掩码策略,将其替换为掩码标记,并分配一个标签来指示该标记是被掩码了还是保持不变。process_for_nsp 函数接收一个分词后的句子列表和一个对应的掩码标签列表作为输入。它验证两个列表长度相同且有足够的句子,并初始化列表来存储BERT输入的句子对、句子对的掩码标签以及指示句子是否连续的二元标签。prepare_bert_final_inputs 函数用于准备BERT训练的最终输入列表。它接收BERT输入、BERT标签和 is_next 列表作为输入。
生成式人工智能工程:13:使用PyTorch预训练BERT模型 🧠

在本节课中,我们将学*如何使用PyTorch框架来预训练一个BERT模型。我们将涵盖从数据加载、模型构建到训练和评估的完整流程。

概述
我们将分步学*以下核心内容:如何为BERT的特定训练任务创建PyTorch数据加载器;如何构建一个BERT模型;以及如何使用PyTorch训练该模型。


创建自定义数据集与数据加载器

首先,我们需要创建一个自定义的数据集类,用于从CSV文件中加载数据到PyTorch的Dataset中。
这个类名为 BRT_CSV_Dataset。它的 __getitem__ 方法根据索引 idx 从数据集中检索单个数据项。该方法会从数据帧中访问对应的行,并将JSON格式的字符串转换回张量。这些张量包括:BERT输入、BERT标签、片段标签和“下一个”标签。
为了在PyTorch DataLoader中进行批处理和序列填充,我们需要定义一个整理函数 collate_batch。
以下是创建训练和测试数据加载器的步骤:
- 定义批处理大小
batch_size。 - 指定训练和测试数据集的CSV文件路径。
- 使用指定的文件路径,为训练集和测试集创建
BRT_CSV_Dataset类的实例。 - 最后,创建对应的DataLoader。


# 示例代码结构
from torch.utils.data import Dataset, DataLoader
import pandas as pd
import torch
class BRT_CSV_Dataset(Dataset):
def __init__(self, file_path):
self.data = pd.read_csv(file_path)
def __getitem__(self, idx):
row = self.data.iloc[idx]
# 将JSON字符串转换为张量
bt_input = torch.tensor(eval(row[‘bt_input’]))
bt_label = torch.tensor(eval(row[‘bt_label’]))
segment_label = torch.tensor(eval(row[‘segment_label’]))
is_next = torch.tensor(eval(row[‘is_next’]))
return bt_input, bt_label, segment_label, is_next
def __len__(self):
return len(self.data)
def collate_batch(batch):
# 实现批处理和填充逻辑
bt_inputs = [item[0] for item in batch]
# ... 填充操作 ...
return padded_bt_inputs, ...
# 创建数据加载器
batch_size = 32
train_dataset = BRT_CSV_Dataset(‘train.csv’)
test_dataset = BRT_CSV_Dataset(‘test.csv’)
train_loader = DataLoader(train_dataset, batch_size=batch_size, collate_fn=collate_batch)
test_loader = DataLoader(test_dataset, batch_size=batch_size, collate_fn=collate_batch)
定义BERT模型架构
上一节我们准备好了数据,本节中我们来看看如何构建BERT模型。BERT模型的核心组成部分包括嵌入层、Transformer编码器层以及用于下游任务的线性层。
BertEmbedding 类


BertEmbedding 类是一个用于BERT模型中嵌入操作和位置编码的模块。与其他模型的主要区别在于,它额外添加了片段嵌入。
完整的BERT模型类
现在,让我们定义完整的BERT模型。定义一个名为 BertModel 的类,它是 torch.nn.Module 的子类。


__init__方法通过指定词汇表大小vocab_size、模型维度d_model、层数n_layers、注意力头数n_heads和丢弃率dropout来初始化BERT模型。- BERT模型包含一个嵌入层,它结合了词元嵌入和片段嵌入。这通过之前定义的
BertEmbedding类实现。 - 使用Transformer编码器层对输入嵌入进行编码。Transformer层的数量及其配置由参数
n_layers、n_heads、dropout和d_model决定。 - 模型包含一个用于下一句预测的线性层,它预测两个连续句子之间的关系。该层以Transformer编码器的输出(形状为
[序列长度, 批大小, 嵌入维度])作为输入,输入是每个序列的[CLS]嵌入。 - 类似地,模型包含一个用于掩码语言建模的线性层,它预测输入序列中被掩码的词元。该层以Transformer编码器的输出为基础,在整个词汇表上进行预测。期望的输出形状是
[序列长度, 批大小, 词汇表大小]。 forward方法定义了BERT模型的前向传播过程。它以BERT输入(输入词元和片段标签)作为输入,并返回NSP和MLM的预测结果。
import torch.nn as nn
import torch
class BertEmbedding(nn.Module):
def __init__(self, vocab_size, d_model, max_len=512):
super().__init__()
self.token_embedding = nn.Embedding(vocab_size, d_model)
self.segment_embedding = nn.Embedding(2, d_model) # 通常只有两种片段(如句子A和B)
self.position_embedding = nn.Embedding(max_len, d_model)
def forward(self, token_ids, segment_ids):
# 组合三种嵌入
pos_ids = torch.arange(token_ids.size(1), device=token_ids.device).unsqueeze(0)
embeddings = self.token_embedding(token_ids) + self.segment_embedding(segment_ids) + self.position_embedding(pos_ids)
return embeddings

class BertModel(nn.Module):
def __init__(self, vocab_size, d_model, n_layers, n_heads, dropout):
super().__init__()
self.embedding = BertEmbedding(vocab_size, d_model)
encoder_layer = nn.TransformerEncoderLayer(d_model=d_model, nhead=n_heads, dropout=dropout, batch_first=True)
self.transformer_encoder = nn.TransformerEncoder(encoder_layer, num_layers=n_layers)
self.nsp_head = nn.Linear(d_model, 2) # 二分类:IsNext 或 NotNext
self.mlm_head = nn.Linear(d_model, vocab_size)
def forward(self, input_ids, segment_ids):
embeddings = self.embedding(input_ids, segment_ids)
encoded = self.transformer_encoder(embeddings)
# 使用 [CLS] 标记的输出进行NSP预测
cls_output = encoded[:, 0, :]
nsp_logits = self.nsp_head(cls_output)
# 使用所有位置的输出进行MLM预测
mlm_logits = self.mlm_head(encoded)
return nsp_logits, mlm_logits
实例化模型与设置训练参数
现在,让我们看看如何设置参数并创建BERT模型的实例。
- 嵌入维度
d_model设为 10。 - 词汇表大小
vocab_size根据词汇表vocab的长度确定。 - 这个迷你BERT模型有两个Transformer层,初始注意力头数设为 2。
- 丢弃率
dropout设为 0.1。
利用这些参数,我们可以创建BERT模型实例以供后续使用。
vocab = [‘[PAD]‘, ‘[UNK]‘, ‘[CLS]‘, ‘[SEP]‘, ‘[MASK]‘, ‘hello’, ‘world’, …] # 示例词汇表
vocab_size = len(vocab)
d_model = 10
n_layers = 2
n_heads = 2
dropout = 0.1
model = BertModel(vocab_size, d_model, n_layers, n_heads, dropout)
定义评估函数与训练循环


模型创建完成后,下一步是在数据上训练它并评估其性能。为了便于这个过程,我们可以定义一个评估函数。该函数用于评估BERT模型的性能,并在训练阶段评估模型的进展。
首先,定义损失函数。损失函数是交叉熵损失,可用于计算预测值与实际值之间的损失。
evaluate 函数接受几个参数:data_loader、model、loss_fn 和 device。在函数内部,使用 model.eval() 将BERT模型置于评估模式。这会禁用dropout和其他训练特定的行为。
初始化变量以跟踪总损失、总下一句损失、总掩码损失和总批次数。评估在关闭梯度的情况下进行,使用 torch.no_grad()。这节省了内存和计算,因为验证不需要梯度。
该函数遍历提供的DataLoader中的批次。使用BERT模型执行前向传播,以获得下一句和掩码语言任务的预测。然后计算两个损失,并将它们相加以获得该批次的总损失。
通过将总损失除以总批次数,可以计算平均损失、平均下一句损失和平均掩码损失。打印平均损失并从函数返回。



在训练开始之前,定义用于训练BERT模型的优化器。优化器是Adam。

让我们看一下训练循环。在每个epoch内,可以按批次遍历训练数据。对于每个批次,执行前向传播,计算损失,并通过反向传播和梯度裁剪更新模型的参数。
每个epoch之后,可以打印平均训练损失,并在测试集上评估模型的性能。也可以在每个epoch后保存模型。

import torch.optim as optim
from tqdm import tqdm
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-4)
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
model.to(device)
def evaluate(data_loader, model, loss_fn, device):
model.eval()
total_loss, total_nsp_loss, total_mlm_loss, num_batches = 0, 0, 0, 0
with torch.no_grad():
for batch in data_loader:
input_ids, segment_ids, nsp_labels, mlm_labels = [b.to(device) for b in batch]
nsp_logits, mlm_logits = model(input_ids, segment_ids)
nsp_loss = loss_fn(nsp_logits, nsp_labels)
mlm_loss = loss_fn(mlm_logits.view(-1, vocab_size), mlm_labels.view(-1))
batch_loss = nsp_loss + mlm_loss
total_loss += batch_loss.item()
total_nsp_loss += nsp_loss.item()
total_mlm_loss += mlm_loss.item()
num_batches += 1
avg_loss = total_loss / num_batches
print(f“评估结果 - 平均损失: {avg_loss:.4f}“)
model.train()
return avg_loss
num_epochs = 10
for epoch in range(num_epochs):
model.train()
total_train_loss = 0
for batch in tqdm(train_loader, desc=f“Epoch {epoch+1}“):
input_ids, segment_ids, nsp_labels, mlm_labels = [b.to(device) for b in batch]
optimizer.zero_grad()
nsp_logits, mlm_logits = model(input_ids, segment_ids)
nsp_loss = loss_fn(nsp_logits, nsp_labels)
mlm_loss = loss_fn(mlm_logits.view(-1, vocab_size), mlm_labels.view(-1))
loss = nsp_loss + mlm_loss
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) # 梯度裁剪
optimizer.step()
total_train_loss += loss.item()
avg_train_loss = total_train_loss / len(train_loader)
print(f“Epoch {epoch+1} - 平均训练损失: {avg_train_loss:.4f}“)
# 在测试集上评估
test_loss = evaluate(test_loader, model, loss_fn, device)
# 保存模型检查点
torch.save(model.state_dict(), f“bert_epoch_{epoch+1}.pth”)
使用训练好的模型进行预测

现在,让我们检查模型的性能。为此,定义一个名为 predict_nsp 的函数,它使用预训练的BERT模型预测第二个句子是否跟随第一个句子。

该函数接受两个句子、一个BERT模型和一个分词器作为输入。函数使用 tokenizer.encode_plus 方法对输入句子进行分词,该方法返回一个分词后输入的字典。然后将分词后的输入转换为张量,并移动到适当的设备进行处理。
通过传递词元张量和片段张量作为输入,使用BERT模型进行预测。选择logits张量的第一个元素并 unsqueeze 以添加一个额外的维度,使其形状变为 [1, 2]。logits通过softmax函数以获得概率,并通过取 argmax 获得预测结果。最后,将预测结果解释并返回为一个字符串,指示第二个句子是否跟随第一个。
例如,将两个句子与BERT模型和分词器一起传递给 predict_nsp 函数。打印结果,根据模型的预测指示第二个句子是否跟随第一个。
接下来,可以定义一个函数来使用预训练的BERT模型执行MLM。该函数接受一个句子、一个BERT模型和一个分词器作为输入。函数使用分词器对输入句子进行分词,并将其转换为词元ID,包括特殊词元。结果存储在 tokens_tensor 变量中。创建填充零的虚拟片段标签作为片段标签。
通过传递词元张量和片段标签作为输入,使用BERT模型进行预测。提取MLM logits作为预测。使用 nonzero 方法识别掩码词元的位置,并存储在 masked_token_index 变量中。请记住,除掩码词元外的所有词元都用0填充。通过取MLM logits在对应位置的 argmax 获得掩码词元的预测索引,其形状为 [批大小, 序列长度, 词汇表大小]。使用分词器的 convert_ids_to_tokens 方法将预测索引转换回词元。然后用预测的词元替换原始句子中掩码词元的位置,得到预测的句子。
调用 predict_mlm 函数,传入一个示例句子、BERT模型和分词器。打印预测的句子,其中掩码词元被预测的词元替换。
# 假设我们有一个简单的分词器(此处为示意,实际使用Hugging Face的BertTokenizer)
class SimpleTokenizer:
def encode_plus(self, text1, text2=None, return_tensors=‘pt’):
# 简化的分词逻辑
tokens = [‘[CLS]‘] + text1.split() + [‘[SEP]‘]
segment_ids = [0] * len(tokens)
if text2:
tokens += text2.split() + [‘[SEP]‘]
segment_ids += [1] * (len(text2.split()) + 1)
# 将词转换为ID(此处简化)
token_ids = [hash(t) % 100 for t in tokens] # 示例映射
return {‘input_ids’: torch.tensor([token_ids]), ‘token_type_ids’: torch.tensor([segment_ids])}
def convert_ids_to_tokens(self, ids):
# 简化的反向转换
return [f“token_{i}” for i in ids]
tokenizer = SimpleTokenizer()
def predict_nsp(sent1, sent2, model, tokenizer):
inputs = tokenizer.encode_plus(sent1, sent2, return_tensors=‘pt’)
input_ids = inputs[‘input_ids’].to(device)
segment_ids = inputs[‘token_type_ids’].to(device)
model.eval()
with torch.no_grad():
nsp_logits, _ = model(input_ids, segment_ids)
probs = torch.softmax(nsp_logits, dim=-1)
prediction = torch.argmax(probs, dim=-1).item()
return “是下一句” if prediction == 1 else “不是下一句”
# 示例使用
result = predict_nsp(“今天天气很好”, “我们出去散步吧”, model, tokenizer)
print(f“NSP预测: {result}“)


def predict_mlm(sentence, model, tokenizer):
# 假设句子中有一个 `[MASK]` 词元
inputs = tokenizer.encode_plus(sentence, return_tensors=‘pt’)
input_ids = inputs[‘input_ids’].to(device)
segment_ids = inputs[‘token_type_ids’].to(device)
model.eval()
with torch.no_grad():
_, mlm_logits = model(input_ids, segment_ids)
# 找到 [MASK] 的位置 (假设ID为4)
masked_positions = (input_ids == 4).nonzero(as_tuple=True)[1]
for pos in masked_positions:
predicted_id = torch.argmax(mlm_logits[0, pos]).item()
predicted_token = tokenizer.convert_ids_to_tokens([predicted_id])[0]
# 在实际应用中,需要将input_ids中的[MASK] ID替换为predicted_id,然后解码回文本
print(f“位置 {pos} 的预测词元: {predicted_token}“)
# 此处应返回完整的预测句子,示例略
return “预测的句子”
# 示例使用
mlm_result = predict_mlm(“今天天气很[MASK]”, model, tokenizer)
总结
本节课中,我们一起学*了使用PyTorch预训练BERT模型的完整流程:
- 创建数据加载器:你需要创建一个自定义数据集类,将数据从CSV文件加载到PyTorch Dataset中。定义一个整理函数,并为训练集和测试集创建相应的数据加载器。
- 构建BERT模型:你需要定义一个用于嵌入操作和位置编码的类,以及一个BERT模型类,两者都是
torch.nn.Module的子类。在BERT模型类中,你需要定义嵌入层、Transformer编码器层、用于NSP和MLM的线性层以及BERT模型的前向传播过程。 - 训练与评估:作为BERT模型训练的一部分,你需要定义一个函数来评估BERT模型的性能,定义用于训练BERT模型的优化器,定义训练循环并监控损失。


通过掌握这些步骤,你已经具备了使用PyTorch从头开始构建和训练一个基础BERT模型的能力。
生成式人工智能工程:14:用于语言翻译的Transformer架构 🚀
在本节课中,我们将要学*Transformer架构如何应用于语言翻译任务。我们将详细描述其编码器-解码器结构,并解释模型如何生成翻译结果。
概述
传统的语言翻译模型,如RNN和LSTM,需要按顺序逐词处理文本。这种方法对于长文本序列来说速度慢且效率低,因为在大跨度文本中保持上下文连贯性具有挑战性。Transformer通过同时处理整个文本序列,彻底改变了语言翻译领域。这种方法极大地加速了翻译任务,并增强了对长文本序列的处理能力,实现了对上下文更深入、更连贯的理解。Transformer还具备其他多项优势,这些优势对于提升语言翻译效果尤为关键。
上一节我们介绍了Transformer的背景与优势,本节中我们来看看其具体的架构。

序列到序列Transformer模型架构
让我们探索一下在机器翻译中广泛使用的序列到序列Transformer模型架构。
首先,源文本被分词。每个词元被映射到词汇表中的对应索引,并转换为词嵌入向量。为了保持词序信息,会向这些嵌入向量中添加位置编码。
编码器处理这些嵌入向量,输出包含上下文信息的嵌入,称为记忆。它封装了在解码阶段翻译短语所需的全部信息。
翻译过程一次生成一个词。从句子开始标记开始。每个新生成的词元随后被嵌入并添加位置编码,同时利用来自编码器的记忆。解码器预测下一个词元。预测出的词元随后成为后续解码步骤的输入。

这个过程递归重复,直到完整序列被翻译完毕,确保翻译结果既连贯又符合上下文。
以下是翻译过程的步骤分解:
- 分词与嵌入:源文本被分词并转换为词嵌入向量。
- 添加位置编码:为嵌入向量添加位置信息。
- 编码器处理:编码器生成包含上下文信息的“记忆”。
- 解码启动:解码从
<BOS>(句子开始)标记开始。 - 自回归生成:解码器结合“记忆”,预测下一个词元,并将其反馈作为下一步的输入,直至生成
<EOS>(句子结束)标记。
编码器内部工作原理
现在,让我们深入编码器的内部,追踪其内部的数据流。
一个源句子进入嵌入层,其中每个词元被转换为一个d维的嵌入向量。随后应用位置编码。接着,向量通过多头注意力层,该层允许模型同时关注句子的不同部分。然后通过一个归一化层。之后,一个前馈网络处理这些向量,产生相同维度d的输出。最后一个归一化层完成编码器的操作。
产生的记忆是一个张量,其维度等于序列长度乘以嵌入大小d。
解码器架构与关键机制
解码器架构与仅解码器模型相似,但有几个关键区别。
一个显著的变化是增加了交叉注意力层,该层专注于编码器输出的“记忆”。这个关键层使得解码器能够参考输入序列的完整上下文。
掩码是解码器机制的另一个重要组成部分。它确保模型按顺序预测每个词,只考虑目标序列中前面的词元,这对于自回归生成过程至关重要。
线性层将上下文嵌入转换为逻辑值,将向量映射到词汇表的维度。这些逻辑值为所有可能的词打分,指导模型预测下一个词。
例如,过程从<BOS>标记开始。解码器随后利用“记忆”生成词元“I”。这个词元被反馈回解码器,与“记忆”结合以生成下一个词“go”。这个过程重复进行,直到<EOS>标记指示序列结束或达到预定长度。

解码器使用交叉注意力来关注编码器的隐藏表示。它计算每个目标位置与编码器中所有源位置之间的注意力分数。这些注意力分数反映了每个源位置对当前目标位置的相关性或重要性。通过引入交叉注意力,解码器可以根据与当前解码步骤的相关性,关注输入序列的不同部分。这种机制帮助模型捕捉长距离依赖关系,并有效地对齐输入和输出序列,这对于准确翻译至关重要。
以下是解码过程的核心步骤:
- 输入与掩码:解码器接收已生成序列(添加掩码确保只能看到前面词元)和编码器“记忆”。
- 自注意力与交叉注意力:通过自注意力处理目标序列,通过交叉注意力关联“记忆”。
- 前馈网络与预测:经前馈网络处理后,通过线性层输出词汇表所有词的概率分布。
- 词元选择与迭代:选择概率最高的词元作为输出,并将其添加回输入序列,重复此过程。
总结
本节课中我们一起学*了Transformer在语言翻译中的应用。你了解到,与RNN不同,Transformer能同时处理整个文本序列进行语言翻译。
在编码器中,源句子进入嵌入层,然后应用位置编码。随后,向量通过多头注意力层和归一化层。接着,前馈网络处理向量,最后的归一化层完成编码器的操作。

在解码器中,交叉注意力层使解码器能够参考输入序列的完整上下文。掩码确保模型按顺序预测每个词,只考虑目标序列中前面的词元。线性层将上下文嵌入转换为逻辑值,将向量映射到词汇表维度。交叉注意力计算每个目标位置与解码器以及编码器中所有源位置之间的注意力分数。
生成式人工智能工程:15:用于翻译的Transformer架构的PyTorch实现 🧠➡️🌐
在本节课中,我们将学*如何使用PyTorch实现一个用于翻译任务的编码器-解码器Transformer模型。我们将从加载数据集开始,逐步构建模型,并最终训练它来完成从德语到英语的翻译。
概述
我们将创建一个完整的翻译模型,其核心流程包括:数据准备、掩码生成、嵌入与位置编码、Transformer模型构建、训练循环以及推理过程。以下是实现的关键步骤。
数据加载与准备

首先,我们需要加载数据集。这里使用批量大小为100,这是一个可以在训练过程中调整的超参数。
batch_size = 100
# 假设 load_dataset 函数返回 (src_sentences, tgt_sentences)
SRC, TGT = load_dataset(batch_size=batch_size)
加载过程会输出成对的德语句子(源语言,SRC)和英语句子(目标语言,TGT)。与训练解码器模型类似,需要确保模型在预测时不会“看到”未来的目标词,因此需要生成一个因果掩码。
构建掩码
create_mask 函数用于为Transformer模型中的源序列和目标序列构建掩码。
以下是该函数的核心逻辑:
- 为目标序列调用之前定义的
square_mask函数生成因果掩码。 - 对于源序列,掩码通常是一个初始化为
False的布尔值数组,因为在编码过程中,整个源序列应该一次性全部可见。 - 该函数还会为目标和源序列生成填充掩码。这些掩码会将输入张量中通常用零标记的填充令牌位置标记为
True。
模型组件


接下来,我们介绍构成翻译模型的核心组件。
位置编码模块
位置编码模块用于向序列中的令牌注入关于其相对或绝对位置的信息。
令牌嵌入模块
令牌嵌入模块用于生成文本的向量形式嵌入。
组装编码器-解码器模型

现在,我们将所有组件组合在一起,创建用于翻译的编码器-解码器模型。

以下是构建步骤:
- 为源语言和目标语言创建嵌入层。
- 创建位置编码层。
- 创建Transformer层。
- 创建一个类似于神经网络输出层的生成器层。

在模型的 forward 方法中:
- 首先为源序列和目标序列生成令牌嵌入和位置编码。
- 然后将这些嵌入作为输入提供给包含编码器和解码器组件的Transformer模型。
- 同时,应用适当的掩码,指示Transformer在处理过程中有选择地忽略输入序列的某些部分。
- 最后,Transformer的输出通过一个线性层(图中未显示),该层负责生成作为模型预测结果的输出向量。
编码器与解码器方法
上一节我们介绍了模型的前向传播流程,本节中我们来看看专门为学*目的定义的编码器和解码器方法。请注意,在实践中,Transformer层本身会处理编码和解码过程。
编码器方法
在编码器方法中:
- 源序列经过令牌嵌入和位置编码。
- 随后,序列由实际的编码器处理,产生一个编码后的向量,通常称为 memory(记忆)。
def encode(self, src, src_mask):
# 嵌入与位置编码
src_embeddings = self.src_embed(src)
src_embeddings = self.positional_encoding(src_embeddings)
# 通过编码器得到 memory
memory = self.encoder(src_embeddings, src_mask)
return memory
解码器方法
解码器方法以目标序列和来自编码器的 memory 作为输入。
- 目标序列像源序列一样,接受令牌嵌入和位置编码。
- 这个准备好的序列然后与
memory一起传递给解码器,最终生成结果向量。
def decode(self, tgt, memory, tgt_mask):
# 嵌入与位置编码
tgt_embeddings = self.tgt_embed(tgt)
tgt_embeddings = self.positional_encoding(tgt_embeddings)
# 通过解码器得到输出
output = self.decoder(tgt_embeddings, memory, tgt_mask)
return output
训练过程
Transformer的训练过程与其他方法有相似之处,但让我们关注关键区别。
以下是训练循环中的关键步骤:
- 遍历每个批次中提供的源序列(SRC)和目标序列(TGT)。
- 目标输入本质上是移除了最后一个令牌的序列。
- 生成必要的掩码。
- 通过模型进行前向传播以生成预测。在此阶段,你还需要输入目标输入。这个过程与推理或预测期间发生的情况类似,Transformer依赖于自己先前的输出来进行未来的预测。
- 目标输出本质上是时间上向前移动的目标输入。在内部,Transformer使用掩码递归地进行预测,并基于其先前的输出。
- 使用目标输出和模型的输出逻辑(logits)计算损失。
类似地,你可以使用验证数据评估模型在训练数据上的损失。
推理(解码)过程
推理或解码过程从准备输入开始。
以下是推理步骤:
- 首先构建 memory 张量,这是通过将源输入及其对应的掩码传递给模型的
encode函数得到的。在此,德语的源文本被编码。 - 初始化一个名为
ys的张量,将其维度设置为[1, 1],并用起始符号填充。该符号作为解码过程的初始令牌。 - 在解码器中,你将输入掩码(确保只关注已预测的令牌)以及包含源输入编码上下文的
memory张量。 - 解码器的输出类似于神经网络的输出。在模型生成的概率分布中,识别与预测单词索引对应的最大值。然后使用该索引从词汇表中检索预测的单词。
- 添加输出令牌索引,并将其作为下一次迭代的解码器输入。
- 重复获取输出令牌索引的过程,直到看到结束符号(EOS)标记或达到最大迭代次数为止。

模型训练与翻译

现在,让我们开始训练模型。超参数和其他基本设置已精心配置。
以下是训练步骤:
- 初始化数据加载器:训练数据加载器和验证数据加载器。
- 使用参数实例化Transformer模型。
- 为了优化,可以选择Adam优化器,同时定义学*率和动量。
- 使用
train_and_evaluate函数训练模型。
这里有一个名为 translate 的函数,它接收你的模型和一个源句子,并返回翻译后的句子。
在循环中,你可以从数据集中抽取句子,并打印原始的德语句子、其真实的英语翻译以及模型的翻译结果。
由于使用了基础的模型,你可以看到模型的翻译与实际英语翻译非常相似。
总结

本节课中我们一起学*了:
create_mask函数用于为Transformer模型内的源序列和目标序列构建掩码。- 线性层负责生成作为模型返回的预测结果的输出向量。
- 在实践中,Transformer层在编码器方法中处理编码和解码过程。源序列经过令牌嵌入和位置编码,然后由实际编码器处理,产生一个通常称为 memory 的编码向量。
- 解码器方法以目标序列和 memory 作为输入。目标序列像源序列一样接受令牌嵌入和位置编码,然后这个准备好的序列与 memory 一起传递给解码器,最终生成结果向量。
translate函数接收你的模型和一个源句子,并返回翻译后的句子。
生成式人工智能工程:0:课程介绍 🚀


在本节课中,我们将要学*一门关于使用Transformer模型进行微调的课程。我们将了解课程背景、目标受众、所需预备知识以及你将学到的核心技能和课程结构。
概述
欢迎来到这门关于使用Transformer进行微调的课程。本课程由人工智能和自然语言处理领域的几个关键趋势所驱动。随着大语言模型在各个领域的持续演进和普及,企业正寻求在特定专业场景中有效利用AI,这使得基于Transformer的微调技术正成为跨行业AI战略的基石。

课程目标与受众
在这里,你将通过使用当前热门的大语言模型来学*AI工程概念,并构建面向职场的技能,以推动你的AI职业发展。本课程为简化起见,将主要关注编码器模型,但你所学的这些方法同样适用于解码器模型。

本课程适合现有的和有抱负的数据科学家、机器学*工程师、深度学*工程师、AI工程师以及希望精通大语言模型应用的开发者。学*本课程,具备Python和PyTorch的基础知识,并对Transformer、微调以及如何加载模型有所了解,将是一个优势。
你将学到什么
完成本课程后,你将能够应用所获得的技能,在生成式AI工程中处理基于Transformer的大语言模型。
- 你将学会使用预训练的Transformer模型处理语言任务,并针对特定任务对它们进行微调。
- 你还将深入了解使用低秩适应(LoRA)和量化低秩适应(Q-LoRA)进行参数高效微调(PEFT)的技术。
课程内容结构
课程将从Transformer和主流语言模型开始,你将学*生成式模型和微调技术。
- 你将回顾高级训练方法,并识别两个强大框架——Hugging Face和PyTorch——之间的差异。
- 你将学*如何使用Hugging Face加载模型并进行推理,以及如何使用Hugging Face训练或预训练模型。
- 你还将理解使用Hugging Face和PyTorch微调模型的重要性。
不仅如此,你还会进一步探索参数高效微调(PEFT)和适配器,例如LoRA和Q-LoRA。
- 你将深入了解软提示和秩的概念。
- 此外,你将定义在自然语言处理中使用的模型量化及其独特方法。
然而,本课程不会在因果大语言模型上进行训练,但这些方法可以轻松迁移。
实践环节与学*方式
本课程包含一系列实验,用以巩固教学视频中的知识。动手练*基于Jupyter Lab环境,供你实践、学*概念和技术。这些实验反映了使用Hugging Face和PyTorch预训练和微调大语言模型的学*过程。
课程内容经过精心搭配以促进学*:
- 视频:简短精炼,聚焦于核心主题。
- 阅读材料:主要以文本形式提供详细内容。
- 实验:提供技术环境、详细说明和代码片段,供你完成动手练*。
- 测验:练*和分级测验将帮助你应用所学知识并评估掌握程度。
为了从课程中获得最大收益,请观看所有视频,完成实验以练*新技能,并尝试所有测验。你还可以通过课程讨论论坛与同学互动,并从课程工作人员那里获得帮助。
总结

本节课中,我们一起了解了这门“生成式人工智能工程”微调课程的概貌。我们明确了课程的学*目标、适合的学*者以及需要掌握的核心技术栈。课程将通过理论讲解、动手实验和测验相结合的方式,帮助你掌握使用Hugging Face和PyTorch对Transformer模型进行高效微调的实用技能。


让我们开始这段激动人心的旅程吧,祝你好运!
生成式人工智能工程:133:Hugging Face 与 PyTorch 对比 🧠

在本节课中,我们将学* Hugging Face 与 PyTorch 这两个在人工智能开发中至关重要的框架。我们将比较它们的角色,并了解它们在实际 AI 任务中的应用场景。

假设你正在研究自然语言处理模型的实现项目。
NLP 模型,如 Hugging Face 和 PyTorch,被广泛应用于医疗保健、金融和客户服务等行业。Hugging Face 是一个以其预训练模型和易用界面而闻名的流行库。另一方面,PyTorch 是一个强大的深度学*框架,常用于从头开始构建自定义的 NLP 模型。
接下来,让我们逐一了解这些框架。
Hugging Face:机器学*的协作平台
Hugging Face 最初是一家聊天机器人公司,但很快发展成为一个专注于机器学*和数据科学的平台与社区。它旨在帮助用户开发、部署和训练机器学*模型。它提供了演示、操作以及将 AI 集成到实际应用中所必需的基础设施。此外,作为用户,你可以探索他人共享的模型和数据集。
由于其促进开发者之间开放共享和测试的作用,Hugging Face 常被称为“机器学*的 GitHub”。这个比喻突显了它作为 AI 开发者协作中心的重要性。
以下是其一些关键特性:
- Transformers 库:Hugging Face 最受欢迎的特性是其 Transformers 库,它提供了多种预训练模型,如 BERT、GPT 和 T5,可随时用于各种 NLP 任务。
- NLP 工具支持:它非常重视为 NLP 应用创建和支持工具。
- 活跃社区:Hugging Face 拥有一个庞大且不断增长的社区,致力于开发和维护模型库及工具。
PyTorch:灵活的深度学*框架
上一节我们介绍了 Hugging Face,本节中我们来看看 PyTorch。PyTorch 是一个基于软件的开源深度学*框架,最初由 Facebook AI Research(现 Meta)开发,用于构建神经网络。它结合了 Torch 机器学*库和基于 Python 的高级 API。其灵活性、易用性等优点,使其成为学术界和研究社区领先的机器学*框架。
PyTorch 支持多种神经网络架构,并建立在广泛理解的 Python 编程语言之上。它提供了丰富的库、预配置甚至预训练的模型。PyTorch 允许数据科学家实时运行和测试部分代码,而无需等待整个代码实现完成,这对于大型深度学*模型来说可能耗时很长。这使得 PyTorch 成为快速原型设计的绝佳平台,并极大地加快了调试过程。
以下是其核心特性:
- 动态计算图:PyTorch 最受欢迎的特性是动态计算图,它允许在运行时动态更改网络架构。
- Pythonic 语法:由于其直观且直接的 Python 基础语法,PyTorch 易于使用。
- 强大的 GPU 加速:PyTorch 提供了强大的图形处理器加速,可以高效处理大规模计算,这对深度学*尤其有益。
框架对比与应用集成
现在,让我们比较这两个框架。PyTorch 是一个多功能的机器学*库,在深度学*方面表现出色。它提供了创建各种机器学*模型的基本工具,并因其适应性和高效性而受到赞赏,尤其是在研发环境中。PyTorch 因其动态管理计算图的能力而广受欢迎,并广泛应用于学术研究和实际应用。
另一方面,Hugging Face 以其 Transformers 库而闻名,专门提供即用型 NLP 模型和工具。它通过为前沿 NLP 模型提供简洁的接口,增强了 PyTorch 和 TensorFlow 等框架。Hugging Face 在文本分类、情感分析和文本生成等任务中特别有用。
作为开发者,你可以将 PyTorch 与 Hugging Face 的 Transformers 集成。这使你能够更直观、更高效地处理复杂的 NLP 任务。
以下是一些应用示例,展示了 Hugging Face 和 PyTorch 如何无缝集成以提供强大的 NLP 解决方案:
- 情感分析:用于对用户评论或社交媒体帖子进行情感分类。
- 语言翻译:可以使用 T5 和 MarianMT 等模型将文本从一种语言翻译到另一种语言。
- 问答系统:可以使用这些框架构建能够根据输入问题和上下文提供答案的系统。
- 文本摘要:可以帮助你从大量文本中自动生成简洁的摘要。
本节课中我们一起学*了以下内容:

- Hugging Face 是一个专注于机器学*、ML 和数据科学的平台与社区,它帮助用户开发、部署和训练机器学*模型。Hugging Face 常被称为“机器学*的 GitHub”。
- Hugging Face 最受欢迎的特性是其 Transformers 库,它提供了多种预训练模型,如 BERT、GPT 和 T5,可随时用于各种 NLP 任务。
- PyTorch 是一个基于软件的开源深度学*框架,用于构建神经网络。PyTorch 支持多种神经网络架构,并建立在广泛理解的 Python 编程语言之上。
- PyTorch 最受欢迎的特性是动态计算图,它允许在运行时动态更改网络架构。
- 一些应用展示了 Hugging Face 和 PyTorch 的无缝集成,以提供强大的 NLP 解决方案,包括情感分析、语言翻译、问答和文本摘要。
生成式人工智能工程:134:使用预训练的Transformer与微调 🧠
在本节课中,我们将学*如何使用预训练的Transformer模型,并了解微调的概念、必要性及其不同方法。
概述

Transformer模型(如BERT、LLaMA、GPT)凭借其基于注意力的架构和在大规模无标签文本数据集上进行预训练的能力,彻底改变了自然语言处理领域。预训练过程使这些模型能够学*丰富的语言表示,这些表示随后可被用于广泛的NLP下游任务。
训练大型语言模型的挑战
训练拥有数十亿参数的大型语言模型在计算上非常昂贵。
- 计算资源:需要强大的硬件,如图形处理器。
- 数据需求:需要大量的训练数据。
- 时间成本:过程耗时,可能需要数周或数月,并涉及多个训练轮次的复杂优化。
- 基础设施:设置和维护必要的基础设施会增加额外成本。
总而言之,训练LLM需要大量的计算资源、时间和资金投入。

什么是微调及其优势

上一节我们介绍了训练全新LLM的挑战,本节中我们来看看一种更高效的策略:微调。
微调使用特定领域的数据,使预训练模型适应特定任务或领域。此过程会调整模型的参数以提高任务性能,同时利用其已有的语言理解能力。与从头开始训练模型相比,微调提高了效率,节省了时间和计算资源。
以下是微调的一些主要优势:
- 迁移学*:在标记数据有限的情况下尤其有价值。
- 时间与资源效率:绕过初始训练阶段,实现更快的收敛。
- 定制化响应:可以调整模型的输出,使其符合特定要求,确保生成准确且上下文相关的输出。
- 任务特定适应:对于情感分析或不同领域的文本生成等应用至关重要。

微调中需避免的陷阱
为了优化微调过程以获得更好的性能和泛化能力,需要注意避免以下常见问题:
- 过拟合:为避免模型仅在训练数据上表现良好,应避免使用过小的数据集或过度延长训练轮次。
- 欠拟合:必须确保充分的训练和适当的学*率,以使模型能够充分学*。
- 灾难性遗忘:防止模型丢失其初始的广泛知识,否则可能影响其在各种NLP任务上的表现。
- 数据泄露:保持训练集和验证集分离,以避免产生误导性的评估指标。
微调的实践挑战与评估
微调因果解码器模型(如用于问答)看似直接,只需创建特定任务的数据集即可。然而,在实践中,这些方法可能需要新颖的损失函数,如强化学*或直接偏好优化。
评估大型语言模型的响应具有挑战性。人类擅长比较两个回答的优劣,但通常难以给出绝对分数。
例如,对于问题“哪个国家拥有南极洲?”,一个回答可能是“南极洲由《南极条约》体系管理”,这准确且信息丰富;另一个回答可能是“我们的企鹅领主在那边掌管一切”,这很幽默但不正确。人类很容易判断第一个回答好,第二个回答不好,但将这些判断量化则更为复杂。
这可以通过微调一个理解语言的LLM(如BERT)来解决,让其产生一个单一的输出分数,这更类似于回归而非分类,即使用奖励建模。如下图所示,第一个回答的得分高于第二个。
微调的主要方法
目前主要有三种微调语言模型的方法:
- 自监督微调:模型学*在大型无标签数据集中预测缺失的单词,例如预测下一个词或被掩盖的词。
- 监督微调:模型使用来自目标任务的标记数据进行微调,以提升其在特定任务(如情感分类)上的性能。
- 基于人类反馈的强化学*:在这种技术中,模型根据人类标注员提供的明确反馈进行调整,使其输出与人类偏好保持一致。

这些方法使语言模型能够通过自监督学*、监督学*或人类反馈来适应特定任务或领域。混合微调结合多种技术可以进一步提升模型性能。OpenAI开发的ChatGPT就是利用此类混合方法的一个例子。
直接偏好优化
直接偏好优化是一种新兴的流行方法,它专注于直接基于人类偏好来优化语言模型。
让我们看看它的一些特点:
- 简洁性:由于以人为中心的优化方式,DPO的实现可能比RLHF更直接。
- 无需奖励模型训练:不需要训练额外的奖励模型。
- 更快收敛:由于依赖直接反馈,可能实现更快的收敛。
监督微调的实施方式
监督微调可以通过两种不同的方式进行:
- 全参数微调:针对特定任务调整模型的所有参数。
- 参数高效微调:这是一种更高效的微调基础模型的方法。在这种方法中,可以在不修改大部分原始参数的情况下对大型预训练模型进行微调。
总结
本节课中我们一起学*了以下核心内容:

- 训练拥有数十亿参数的大型语言模型在计算上非常昂贵,需要强大的硬件和大量的训练数据。
- 微调使用特定领域的数据,使预训练模型适应特定任务或领域。
- 与从头开始训练模型相比,微调提高了效率,节省了时间和计算资源。
- 微调的优势包括迁移学*、时间与资源效率、定制化响应和任务特定适应。
- 解决过拟合、欠拟合、灾难性遗忘和数据泄露等问题可以优化微调,使其在特定领域获得更好的性能和泛化能力。
- 微调语言模型的三种主要方法包括自监督微调、监督微调和基于人类反馈的强化学*。
- 直接偏好优化是一种新兴的流行方法,专注于直接基于人类偏好优化语言模型。
- 监督微调可以通过两种方式进行:全参数微调和参数高效微调。
生成式人工智能工程:135:使用PyTorch进行微调 🧠
在本节课中,我们将学*如何使用PyTorch对预训练模型进行微调。我们将涵盖数据准备、模型定义,并详细讲解如何微调整个模型以及仅微调其最后一层。
概述
微调是机器学*中一个关键过程,旨在使预训练模型适应特定的任务或使用场景。它已成为深度学*的一项基础技术,特别是在用于生成式人工智能的基础模型训练过程中。
上一节我们介绍了微调的基本概念,本节中我们来看看具体的实现步骤。
数据准备
首先,我们需要准备将要使用的数据集。
以下是两个我们将要处理的数据集:
- IMDB数据集:包含50,000个电影评论样本。该数据集较小,包含两个类别:正面和负面。
- AG News数据集:包含120,000个训练样本和7,600个测试样本,涵盖四个类别:世界、体育、商业、科技。
我们的目标是先在AG News数据集上预训练模型,使其从多样化的主题中建立对语言和上下文的理解。然后,在IMDB数据集上进行微调,使模型专门用于情感分析和电影评论任务。


加载与处理数据
首先,定义用于加载IMDB数据的类。
现在,将IMDB数据集加载到训练和测试迭代器中。以下是数据集的一些示例,其中0表示差评,1表示好评。
接下来,定义分词器并加载GloVe词嵌入。
然后,从预训练的GloVe词嵌入模型构建词汇表对象,分配索引值,并将默认索引设置为未知词标记。
您可以使用以下代码将数据转换为映射式数据集:
# 示例代码:将数据转换为映射式数据集
随后,执行随机分割以创建独立的训练集和验证集,并检查设备。
现在,创建整理函数。该函数对数据集进行分词,将词元转换为词元索引序列,并将这些序列及类别标签转换为张量。
接着,将训练集、验证集和测试集转换为数据加载器对象,这些对象由整理函数处理。以下是创建的验证数据加载器对象中的一个示例。
定义模型
现在,您将定义基于Transformer的模型类,即在PyTorch中用于分类的编码器模型类。
该构造函数使用以下配置初始化文本分类器:类别数量、词汇表大小以及Transformer设置(如头数和层数)。它还会设置基本组件:嵌入层、位置编码、Transformer编码器以及用于输出的线性分类器。预训练的词嵌入模型GloVe被用于嵌入层。
前向传播方法将嵌入应用于输入,添加位置编码,使数据通过Transformer编码器,沿第一个维度对数据进行平均,最后使用分类器对数据进行分类。
训练与评估
train_model函数使用提供的优化器和损失准则训练Transformer模型,迭代指定的轮数。它会在验证数据集上评估模型性能,打印每轮的损失,并在验证准确率提高时选择性地保存模型和性能指标。
predict函数接收文本和一个文本处理管道(用于机器学*前的文本预处理),它使用预训练模型来预测文本的标签,以进行数据集上的文本分类。
现在,创建一个函数来评估模型在数据上的准确率。
微调整个模型
现在让我们微调整个模型。
首先,在PyTorch中创建一个输出层有4个神经元的模型对象。
然后,使用load_state_dict方法,加载在AG News数据集上预训练模型的参数。此方法将参数加载到模型对象中,允许您使用选择的任何数据集对其进行微调。
如您所知,AG News数据集有4个类别。由于您要在IMDB数据集上微调预训练模型,而IMDB数据集只有2个类别,因此需要将最后一层的神经元数量从4改为2。重要的是,您应始终根据微调数据集的类别数量调整最后一层的神经元。
现在,定义用于在IMDB数据集上微调模型的损失函数、优化器和调度器。
然后调用train_model函数来完成模型的微调。这一步本质上与从头开始训练模型相同。


这里您可以看到模型的损失与准确率曲线,该模型在验证数据上达到了约90%的准确率。


仅微调最后一层
现在,让我们看看如果仅微调模型的最后一层会发生什么。
为此,您将使用与之前相同的在AG News数据集上预训练的模型。冻结模型的所有层,因为您只需要微调最后一层。
这是通过选择层参数并将requires_grad属性设置为False来实现的。冻结除最后一层外的所有层,通过减少计算量并将优化集中在更少的参数上,可以加速训练。输出层的神经元数量也已从4改为2,因为IMDB数据集的预测类别有两个。
定义重新训练模型的参数,并仅对最后一层进行微调。在这种情况下,训练模型的速度要快得多。
以下是仅微调最后一层的模型的损失与准确率曲线图。


虽然训练速度快得多,但性能却显著下降。因此,您必须在微调整个模型和仅微调关键参数之间进行权衡。


总结
本节课中我们一起学*了以下内容:
您了解到,机器学*中的微调是使预训练模型适应特定任务或使用场景的过程。
整理函数对数据集进行分词,将词元转换为词元索引序列,并将这些序列及类别标签转换为张量。
在定义PyTorch中用于分类的基于Transformer的模型类时,构造函数使用类别数量、词汇表大小以及Transformer设置(如头数和层数)等配置来初始化文本分类器。
前向传播方法将嵌入应用于输入,添加位置编码,使数据通过Transformer编码器,沿第一个维度对数据进行平均,最后使用分类器对数据进行分类。
train_model函数使用提供的优化器和损失准则训练Transformer模型,迭代指定的轮数。
如果您微调整个模型,模型在验证数据上能达到约90%的准确率。
如果您仅微调模型的最后一层,训练速度会快得多,但性能会显著下降。


生成式人工智能工程:4:使用Hugging Face进行微调 🎯
在本节课中,我们将学*如何使用Hugging Face平台和PyTorch来微调预训练模型。我们将了解如何加载数据集、使用分词器、构建数据加载器,并最终使用监督式微调训练器(SFT Trainer)来高效地完成模型微调任务。
概述
Hugging Face是一个开源的机器学*平台,内置了用于自然语言处理应用的Transformers库。该平台允许用户共享机器学*模型和数据集,并展示他们的工作成果。

加载数据集与分词处理
上一节我们介绍了Hugging Face平台。本节中,我们来看看如何加载内置数据集并进行数据预处理。
Hugging Face的内置数据集可以使用load_dataset函数加载。让我们加载Yelp评论数据集,这是一个类似列表的对象,包含来自Yelp平台的用户评论和相应的元数据。
以下是加载和查看数据集的步骤:
- 每条评论都是一个字典,通常包含评论文本本身(键为
text)和用户给出的星级评分(键为label,范围从1到5)。
你可以加载一个BERT分词器对象来对评论进行分词、填充和截断,这有助于高效处理可变长度的序列。
以下是使用分词器处理数据集的步骤:
- 分词器函数从数据集样本中提取文本并应用分词器。
- 然后,你可以将此方法映射到整个数据集。
结果是数据集中的每个样本文本都被标记化,将文本转换为标记索引(字典键为input_ids)。此外,与BERT模型相关的其他参数,如注意力掩码(attention_mask),也已为每个样本生成。
由于模型不需要文本信息,你可以删除text字段,并将label键重命名。数据随后被转换为PyTorch张量。结果是一组包含labels、input_ids、token_type_ids和attention_mask键的张量。
构建数据加载器与加载模型
在准备好数据张量之后,接下来我们需要为模型训练准备数据迭代器并加载预训练模型。
就像在PyTorch中一样,在Hugging Face中,你可以为训练和测试数据集创建数据加载器对象。这允许你迭代批次数据。
现在,你将从Transformers库加载一个预训练的BERT分类模型,该模型有五个类别。该模块专为序列分类设计。
num_labels参数指定类别数量,并决定最终层的神经元数量。

配置优化器与训练循环
加载模型后,我们需要配置训练过程。本节将介绍如何设置优化器、学*率调度器并编写训练函数。
让我们创建一个优化器和学*率调度器来微调模型。你可以使用PyTorch的AdamW优化器,并相应设置设备。

现在,让我们创建一个训练函数来微调模型。这里,还定义了一个评估函数,用于在微调后评估模型的性能。你现在可以训练模型,并观察每个训练周期(epoch)后损失的减少。
使用SFT Trainer简化微调
上一节我们介绍了手动设置训练循环的方法。本节中,我们来看看一个更高效的替代方案——监督式微调训练器。


SFT Trainer(监督式微调训练器)简化并自动化了许多训练任务,与直接使用PyTorch训练相比,该过程更高效且更不易出错。
对于掩码语言模型任务,你的目标应该是使用Transformer模型预测一个被掩码的单词。让我们加载一个掩码语言模型,并使用SFT Trainer对其进行微调,如下所示。
以下是使用SFT Trainer进行微调的步骤:
- 首先,你将加载IMDB数据集,该数据集将用于微调模型。
- 接下来,你将定义训练参数对象。训练参数对象包括学*率和训练周期数等关键参数。
- 最后,你将定义SFT Trainer对象。SFT Trainer参数包括模型、训练参数、数据集以及你想要训练的特定字段键。
让我们训练模型,并查看每个训练周期的损失。

使用Pipeline进行预测
模型训练完成后,我们可以使用它来对新数据进行预测。Hugging Face的Pipeline功能让这个过程变得非常简单。
你可以创建一个Pipeline对象mask_filler来进行预测。参数task指定问题类型。输入包括模型和分词器,这允许你一步完成数据分词和预测。
要进行预测,只需输入文本,例如:"This is a [MASK] movie."。输出结果将是一个可迭代对象。键token_str将包含对[MASK]的预测标记值,键score将指示可能性。样本将根据可能性大小排序。
观察输出,你可以看到"great"是最有可能的标记。
总结

本节课中,我们一起学*了以下核心内容:
- Hugging Face是一个开源的机器学*平台,内置用于自然语言处理应用的Transformers库。
- Hugging Face的内置数据集可以使用
load_dataset函数加载。 - 分词器函数从数据集样本中提取文本并应用分词器。
num_labels参数指定类别数量,并决定最终层的神经元数量。- 评估函数用于在微调后评估模型的性能。
- SFT Trainer(监督式微调训练器)简化并自动化了许多训练任务,与直接使用PyTorch训练相比,该过程更高效且更不易出错。
- SFT Trainer参数包括模型、训练参数、数据集以及你想要训练的特定字段键。
参数高效微调 (PEFT) 教程:137:PEFT 简介 🚀
在本节课中,我们将要学*参数高效微调的概念、重要性、主要方法及其核心原理。通过本教程,你将能够解释PEFT的概念,描述其不同类型和用途,并理解软提示和秩的概念。
监督微调 (SFT) 与全参数微调
上一节我们介绍了课程概述,本节中我们来看看监督微调。监督微调是机器学*中,尤其是在使用预训练模型和迁移学*时常用的一种方法。该方法旨在利用模型从先前训练中获得的知识和理解,并将其适配到新的任务上。
全参数微调是监督微调中常用的一种方法,它涉及更新大型语言模型的学*参数、层和神经元。这种方法需要大量的计算资源、内存以及特定任务的有标签数据。它还存在较高的过拟合风险,耗时且实现复杂。
此外,全参数微调的一个固有问题是灾难性遗忘,即模型在接受新数据训练时会忘记先前学到的信息,导致宝贵的预训练知识丢失。
参数高效微调 (PEFT) 简介

鉴于全参数微调的上述问题,参数高效微调方法应运而生。PEFT方法减少了为有效将大型预训练模型适配到特定下游应用而需要更新的可训练参数数量。这样做,PEFT显著降低了获得一个有效微调模型所需的计算资源和内存存储。
以下是PEFT的主要方法类别:
- 选择性微调
- 附加式微调
- 重参数化微调
这些方法通常被证明比全参数微调方法更稳定,尤其是在NLP应用场景中。
选择性微调
首先我们来了解选择性微调。全参数微调涉及更新神经网络的参数、层和神经元,这需要大量的计算资源和内存。相反,选择性微调只更新层或参数的一个子集。
然而,选择性微调对于其他网络架构有效,但对于Transformer架构效果较差,因为Transformer参数数量庞大,需要更广泛的更新。这一局限性促使了替代方法的发展。
附加式微调
附加式微调是替代方法之一。这种方法不是修改现有的预训练参数,而是在预训练模型中添加新的、任务特定的层或组件。然后,你可以在任务特定数据上训练这些附加层,同时保持预训练参数固定不变。

附加式微调允许进行任务特定的定制,同时保留预训练知识。你可以在模型的任何位置注入附加层。
在Transformer中,适配器用于附加式微调。它们涉及在预训练的Transformer模型中添加层,具体位置在注意力块之间,同时冻结模型的大部分权重。
如果你观察适配器层,它们以一个下投影层开始,用于降低输入维度。随后是一个非线性变换,然后是一个上投影层,将维度恢复到Transformer的原始维度。

这种设计允许你使用现成的Transformer,只需存储适配器即可。Transformer保持对语言的通用理解,而适配器则存储针对特定问题的信息。
软提示
训练大型预训练语言模型需要大量的时间和计算资源。随着模型规模增大,你可以利用软提示来改进训练过程。
软提示是可学*的张量,与输入嵌入向量拼接,可以针对数据集进行优化。软提示方法包括提示调优、前缀调优、P调优和多任务提示调优。
让我们更仔细地看看前缀调优。假设你正在将一个通用聊天机器人解码器模型训练成医疗聊天机器人。与其在整个数据集上训练整个模型,你可以简单地将参数化嵌入向量附加到现有嵌入向量上。然后,通过冻结除这些新嵌入向量外的所有参数来训练新模型。
秩的概念

在了解最流行的PEFT方法——基于重参数化的方法之前,我们先回顾一下秩的概念。秩告诉你张成一个空间所需的最小向量数量。它本质上就是你通常认为的维度。
考虑二维空间中的两个向量。这两个向量可以到达该空间中的任何点,因此秩为2。现在,假设你将这两个向量扩展到三维空间。尽管这两个向量现在存在于三维空间中,但它们仍然只能张成该空间内的一个平面。因此,它们的秩仍然是二,因为它们只能到达二维平面上的点,而非整个三维空间。

在神经网络中,输入和输出层具有固定的维度;然而,你可以使用低秩操作来减少参数数量。即使在更高维度的上下文中,你也只需要两个维度来张成空间,这种维度的降低有助于提高模型效率。
基于重参数化的方法

基于重参数化的方法,例如低秩自适应,利用了使用低秩变换对网络权重进行重参数化的概念。这减少了可训练参数的数量,同时仍然处理高维矩阵,例如网络的预训练参数。
在一个典型的网络中,前向传播方法使用完整的网络。然而,在LoRA中,额外的低秩层被添加到原始层中,从而减少了表示权重矩阵所需的参数数量。这种重参数化有效地捕获了数据中最重要的方向,在保持模型性能的同时显著降低了计算成本。
其他类似LoRA的流行方法包括量化低秩自适应和权重分解低秩自适应。
QLoRA将低秩自适应与量化相结合,减少了模型的内存占用和计算需求。而DoRA则根据分量的大小调整低秩空间的秩,从而优化模型的性能和效率。
总结
本节课中我们一起学*了参数高效微调。我们了解到,PEFT方法减少了为有效将大型预训练模型适配到特定下游应用而需要更新的可训练参数数量。
PEFT方法包括选择性微调、附加式微调和重参数化微调。选择性微调只更新层或参数的一个子集,对于Transformer架构效果较差。附加式微调涉及向预训练模型添加任务特定的层或组件,而不是修改现有的预训练参数。适配器用于附加式微调,涉及在预训练Transformer模型的注意力块之间添加层,同时冻结模型的大部分权重。
软提示是可学*的张量,与输入嵌入向量拼接,可以针对数据集进行优化。秩告诉你张成一个空间所需的最小向量数量。

基于重参数化的方法利用了使用低秩变换对网络权重进行重参数化的概念。这减少了可训练参数的数量,同时仍然处理高维矩阵,例如网络的预训练参数。
生成式人工智能工程:6:低秩适应(LoRA) 🧠
在本节课中,我们将要学*低秩适应(LoRA)技术。这是一种用于高效微调大型预训练模型的方法,它能显著减少训练所需的参数、时间和计算资源。
概述
LoRA 是一种通过向原始模型添加轻量级“插件”来简化大型复杂机器学*模型,使其适应特定任务的技术。它的核心思想是利用预训练模型的高维矩阵,通过低秩分解来减少可训练参数的数量。

LoRA 的工作原理
上一节我们介绍了LoRA的基本概念,本节中我们来看看它的具体工作方式。
LoRA 通过在原始模型的权重矩阵上添加一个低秩更新矩阵来工作。假设原始神经网络层的权重矩阵为 W0,其维度为 D × K(D是输入维度,K是输出维度)。该层的输出计算为 h(x) = W0 * x,其中 x 是上一层的输入。
在LoRA中,我们在前向传播时添加一个额外的矩阵 ΔW。因此,新的权重矩阵变为:
W = W0 + ΔW
而层的输出变为:
h(x) = (W0 + ΔW) * x
关键在于,LoRA 将更新矩阵 ΔW 分解为两个更小的低秩矩阵 B 和 A 的乘积:
ΔW = B * A
其中,矩阵 B 的维度为 D × R,矩阵 A 的维度为 R × K。这里的 R(秩)是一个远小于 D 和 K 的超参数。
参数减少示例
为了更直观地理解参数是如何减少的,让我们看一个例子。
考虑一个网络层,其输入维度为10,输出维度为8。传统的全连接层参数数量为:
10 * 8 = 80 个参数。
现在,我们引入一个秩 R=3 的LoRA适配。以下是参数计算过程:
- 首先,一个将10维输入映射到3维的矩阵(类似 B 矩阵的一部分),参数为
10 * 3 = 30。 - 然后,一个将3维中间结果映射到8维输出的矩阵(类似 A 矩阵的一部分),参数为
3 * 8 = 24。 - 总的可训练参数为
30 + 24 = 54个。
通过这种方式,我们使用 54 个可训练参数*似实现了原本需要 80 个参数才能完成的计算,同时保持了模型的表达能力。图中展示了这一降维过程。

训练与优化过程
了解了LoRA的结构后,我们来看看在训练中如何应用和优化它。
在微调过程中,原始模型的权重矩阵 W0 被冻结(即不更新),只有低秩矩阵 B 和 A 会被训练。这带来了巨大的优势:
- 传统网络的可训练参数数量为:D × K
- 使用LoRA的可训练参数数量为:D × R + R × K
由于 R 远小于 D 和 K,可训练参数总量大幅减少,从而节省了计算资源和内存,尤其是在反向传播过程中。
在前向传播时,通常会对低秩更新应用一个缩放因子,公式如下:
h(x) = W0 * x + (α / R) * (B * A) * x
其中 α 是一个与 R 相关的超参数,用于控制适配更新的强度。
LoRA 的损失函数与应用
LoRA 是一个通用的参数优化框架,特别适用于拥有海量参数的Transformer模型。
在训练中,我们定义一个损失函数 L,它依赖于输入序列 x 和目标 y。由于只有 ΔW(即 B 和 A)是可训练的,损失函数实际上只是矩阵 A 和 B 的函数。优化器(如Adam)通过梯度下降算法只更新这些低秩参数。
根据原始LoRA论文的表述,对于自回归模型(如GPT),损失函数可以表示为对数似然:
L(θ) = Σ_t Σ_i log P(ω_i | x_i; θ)
其中,θ 代表LoRA参数(A 和 B),ω 代表目标词元,x 代表对应的嵌入向量。第一个求和遍历序列中的每个预测位置,第二个求和遍历训练样本。
LoRA 主要应用于Transformer的注意力层,优化其查询(Query)、键(Key)和值(Value) 投影矩阵的参数。它既可以用于编码器,也可以用于解码器。

总结
本节课中我们一起学*了低秩适应(LoRA)技术。
- 核心目标:LoRA 通过向原始模型添加轻量级的低秩适配器,使复杂的机器学*模型能够高效地适应特定任务。
- 核心机制:它利用矩阵代数,将权重更新矩阵 ΔW 分解为两个小矩阵 B 和 A 的乘积(ΔW = B * A),从而大幅减少可训练参数。
- 训练特点:在微调时,原始权重 W0 保持冻结,仅更新低秩矩阵 B 和 A。
- 主要优势:显著减少了训练时间、资源消耗和内存占用。
- 主要应用:特别适用于微调大型Transformer模型,优化其注意力机制中的参数,并可同时应用于编码器和解码器结构。
通过LoRA,我们能够在保持模型性能的同时,以更高效、更经济的方式利用强大的预训练模型。图中概括了LoRA的整体工作流程。



生成式人工智能工程:7:使用Hugging Face和PyTorch进行LoRA 🚀
在本节课中,我们将学*如何使用PyTorch和Hugging Face库来实现LoRA技术。LoRA是一种轻量级的训练技术,能显著减少大型语言模型中的可训练参数量,从而实现更快速、更节省内存的微调过程。
概述
LoRA通过向预训练模型中插入新的、低秩的小权重矩阵进行训练,而非更新整个庞大的原始模型。这种方法不仅训练速度快、内存效率高,还能生成更小的模型权重文件,便于存储和共享。
上一节我们介绍了微调的基本概念,本节中我们来看看如何具体实现LoRA。

使用PyTorch实现LoRA
首先,我们需要准备数据集。这里使用互联网电影数据库数据集进行情感分类任务。
以下是创建数据迭代器的步骤:
- 定义一个类来遍历IMDB数据。
- 使用该类创建训练和测试数据集迭代器。
- 将数据转换为Map风格,并进行随机分割以用于训练和验证。
此时,可以指定训练设备。collate_batch函数用于从单个样本中自定义和创建批次。
通过应用collate函数并设置批次大小为64,将数据集对象转换为数据加载器。
接下来,定义一个简单的文本分类器类,例如TextClassifier。它包含一个嵌入层、一个使用ReLU激活函数的隐藏线性层和一个输出线性层。
现在,我们需要修改高亮的隐藏层,将其转换为LoRA层。
让我们看看LoRALayer类。该类实现了一个LoRA模块,它从两个低秩矩阵A和B开始,并将其乘积按因子alpha进行缩放。
# LoRA层的前向传播方法示例
def forward(self, x):
lora_output = self.A @ self.B @ x
return self.original_output + self.alpha * lora_output
在训练模型时,只更新矩阵A和B。LinearWithLoRA类复制原始线性模型并创建一个LoRA层对象。在其前向传播方法中,它对输入X同时应用原始线性模型和LoRA模型,并将输出相加。
现在,让我们加载一个在AG News数据集上预训练的模型。该模型有4个输出类别,由于其训练样本量大,具有较好的语言理解能力。
要初始化此模型,需将num_classes设置为4。该预训练模型的嵌入层未被冻结,因此初始化时设置freeze=False。IMDB数据集包含两个类别,需要将最终输出层替换为一个输出数为2的新线性层。

接着,将隐藏层替换为LoRA层,确保在训练过程中更新矩阵A和B。
现在,让我们定义train_model函数来微调模型。

首先,为模型设置训练组件并定义学*率。使用交叉熵损失准则,采用随机梯度下降优化器,并设置学*率调度器,在每个训练周期将学*率衰减0.1倍。
然后,通过绘制每个周期的损失和准确率图表来评估模型性能,并监控模型的改进情况。
评估模型性能后,定义评估函数并在测试数据上获得了69%的模型准确率。从模型的LoRA层中,可以获取可学*参数A、B和alpha,并将它们保存下来以便未来加载模型。
矩阵A和B大约有450个参数,而存储整个线性层将需要约12,800个参数,大约是原始参数的28倍。LoRA的主要优势在于,它可以使用参数A、B和alpha来微调模型,同时使用输出层对样本进行分类。
接下来,让我们使用保存的参数加载模型。可以创建模型对象并加载预训练的参数。
此外,定义了一个使用模型进行预测的函数,以展示模型的性能。
使用Hugging Face实现LoRA
LoRA与Hugging Face结合使用,使得模型训练变得更加简单。
首先,加载IMDB数据集来训练模型。接着,加载DistilBERT分词器,并应用该分词器为给定文本创建输入ID和注意力掩码。
屏幕会显示分词后的文本。然后,从Hugging Face Transformers库加载一个类似BERT的双向Transformer模型,在本微调示例中设置输出类别数为2。
接下来,使用TaskType模块中的序列分类类型来定义任务类型,以对评论进行分类。
进一步定义LoRA的配置,并加载预训练的分类模型来应用此配置。设置秩rank和LoRA缩放因子alpha,并为更大的模型包含dropout参数。target_modules参数指定了希望更新的模型部分。
Hugging Face的LoRA通过target_modules参数来增强Transformer模型的特定部分。然后,使用指定的配置将LoRA应用到给定模型,并通过参数高效微调模型进行初始化。
现在,让我们了解如何在模型中设置训练参数。为此,使用TrainingArguments类,它封装了训练模型所需的所有超参数和配置。这使得训练过程变得简单,并有助于管理训练过程的各个方面。

接下来,使用Trainer来训练模型。Trainer类有助于简化Transformer模型的训练和评估,它处理训练循环并保存模型。这意味着你可以使用Trainer轻松地训练模型。

最后,绘制损失与准确率的图表,以验证模型性能的改进。
总结
本节课中,我们一起学*了LoRA如何与PyTorch和Hugging Face协同工作。
在PyTorch部分,模型使用IMDB数据集和自定义类来创建训练和测试数据迭代器。collate_fn函数从各种样本中整理和自定义批次。文本分类器使用嵌入层对文本进行分类。LoRALayer类在预训练模型中实现了LoRA模块。使用train_model函数对模型进行微调,评估模型性能并给出准确率。

在Hugging Face部分,LoRA简化了模型训练过程。模型使用分词器创建输入ID,并从Hugging Face Transformers库加载BERT-like模型。使用序列分类类型定义任务类型,并定义LoRA的配置。TrainingArguments帮助设置模型训练参数,而Trainer类则帮助训练模型。
生成式人工智能工程:8:从量化到QLoRA 🧠
在本节课中,我们将要学*量化(Quantization)与QLoRA(Quantized Low-Rank Adaptation)的核心概念。你将能够定义量化与QLoRA,并解释QLoRA技术的重要性。
什么是QLoRA? 🤔

上一节我们介绍了课程目标,本节中我们来看看QLoRA是什么。
QLoRA代表量化低秩适应。它是一种机器学*中的微调技术,旨在优化大型语言模型的性能和效率。通过结合量化与LoRA技术,可以在不显著牺牲模型准确性的前提下,高效地利用计算资源。
理解量化 📉
在深入QLoRA之前,我们需要先理解其基础组件之一:量化。
量化将数值的精度降低到一个有限的离散级别集合中。这减少了内存使用量,并使得在精度有限的硬件上能够进行高效计算。
例如,将一张图像的色彩转换为256级灰度时,量化可以将灰度级别从256减少到16、8、4甚至2级。这些级别的减少会降低图像的细节表现。这意味着随着量化程度的增加,图像看起来会不那么生动,但占用更少内存的同时仍保持可识别性。

以下是量化范围和级别的定义方式:
在QLoRA中,量化范围通常在-1到1之间。用于表示数值的比特数决定了量化级别。
- 例如,3比特量化表示8个离散级别:-1, -0.75, -0.5, -0.25, 0.25, 0.5, 0.75, 1。因此,数字的量化值可能是:-0.75量化为-0.75,-0.7量化为-0.75,-0.2量化为-0.25,0.05量化为0.25,0.45量化为0.5。
- 类似地,2比特量化表示4个离散级别:-1, -0.5, 0.5, 1。因此,示例数字的量化值可能是:-0.75量化为-1,-0.7量化为-1,-0.2量化为-0.5,0.05量化为0.5,0.45量化为0.5。

QLoRA的独特方法 ⚙️
了解了基础量化后,我们来看看QLoRA采用的独特方法。
QLoRA使用一种独特的量化方法:4比特标准浮点(4-bit NormalFloat, NF4)和双重量化(Double Quantization)。它还采用了分页优化器(Paged Optimizers)。分页优化器并非量化技术,而是一种内存管理技巧。它允许通过根据需求动态加载和卸载模型参数,使大模型能够适应有限的内存。
量化如何减少内存占用 💾
现在,让我们通过一个具体例子,理解量化如何减少模型的内存占用。
我们以一个70亿参数的模型为例,考虑模型参数、梯度、优化器状态和激活值。
以下是各项内存占用的计算:
-
模型参数:
- FP16模型使用16比特表示每个参数。总内存占用为:
70亿参数 * 2字节 = 14 GB。 - 4比特量化模型使用4比特表示每个参数。总内存占用为:
70亿参数 * 0.5字节 = 3.5 GB。
- FP16模型使用16比特表示每个参数。总内存占用为:
-
梯度:
- FP16梯度使用16比特表示每个梯度。总内存占用为:
70亿梯度 * 2字节 = 14 GB。 - 4比特量化梯度使用4比特表示每个梯度。总内存占用为:
70亿梯度 * 0.5字节 = 3.5 GB。
- FP16梯度使用16比特表示每个梯度。总内存占用为:
-
优化器状态:
- FP32优化器状态使用32比特表示每个状态。通常有两个状态,总内存占用为:
2 * 70亿状态 * 4字节 = 56 GB。 - 8比特量化优化器状态使用8比特表示每个状态。对于两个状态,总内存占用为:
2 * 70亿状态 * 1字节 = 14 GB。
- FP32优化器状态使用32比特表示每个状态。通常有两个状态,总内存占用为:
-
激活值:
- 假设FP16激活值与模型参数大小相同,内存占用为:
70亿激活值 * 2字节 = 14 GB。 - 假设4比特量化激活值与模型参数大小相同,内存占用为:
70亿激活值 * 0.5字节 = 3.5 GB。
- 假设FP16激活值与模型参数大小相同,内存占用为:

内存占用对比:
- FP16模型总内存占用:14 GB + 14 GB + 56 GB + 14 GB = 98 GB
- 4比特量化模型总内存占用:3.5 GB + 3.5 GB + 14 GB + 3.5 GB = 24.5 GB
因此,通过应用4比特量化,内存占用从98 GB(FP16)减少到24.5 GB,减少了约75%。这种内存占用的大幅降低,使得我们能够在性能较低的硬件上运行大型模型。
总结 📚
本节课中我们一起学*了量化与QLoRA。

- 理解量化和QLoRA技术对于开发高效、可扩展的机器学*模型至关重要。
- QLoRA是一种用于优化大型语言模型性能的机器学*微调技术。
- QLoRA采用独特的量化方法(NF4和双重量化)来减少内存占用。
- QLoRA可以将内存占用减少高达75%。
- 量化通过定义量化范围和级别,将数值精度降低到有限的离散级别集合中。
生成式人工智能工程:141:高级微调课程介绍 🎯

在本课程中,我们将学*针对大语言模型(LLMs)的高级微调技术。通过掌握这些方法,你将能够使预训练模型更好地适应特定任务和领域需求。
课程概述
大语言模型的高级微调是一项强大的技术。它有助于使预训练模型适应特定的任务和领域。理解高级微调技术并实施其最佳实践,可以创建出符合独特业务需求的高效模型。

本课程将聚焦于不同的微调方法及其最佳实践,涵盖因果大语言模型、基于人类反馈的微调以及直接偏好优化等核心概念。
目标受众

本课程适合现有的和有抱负的数据科学家、机器学*工程师、深度学*工程师、人工智能工程师以及希望精通大语言模型工作的软件开发人员。
预备知识
学*本课程需要具备Python、PyTorch和Hugging Face的基础知识。了解Transformer、指令微调、强化学*、直接偏好优化、最优策略以及因果大语言模型将更有优势。
学*目标
完成本课程后,你将能够:
- 应用指令微调的基础知识及其在Hugging Face上的最佳实践。
- 学*使用人类反馈,通过直接偏好优化(DPO)或*端策略优化(PPO)来训练模型。
课程内容详解
以下是本课程将涵盖的核心模块:
上一节我们介绍了课程的整体目标,本节中我们来看看具体的学*路径。
- 指令微调基础:你将首先探索使用Hugging Face进行指令微调的基础。你将深入了解奖励建模、用于数据集预处理的响应评估,并应用低秩自适应(LoRA)技术。
- 奖励模型训练:你还将学*如何通过遵循特定指令来生成相关响应,从而为语言模型进行奖励模型训练。
- 性能评估:你将探索使用Hugging Face进行奖励建模,以评估模型的性能。
这还不是全部。本课程将进一步深入探讨以下高级概念:
上一部分我们学*了指令微调,接下来我们将进入更深入的策略层面。
- 大语言模型即策略:深入理解大语言模型作为策略的概念,及其作为生成可能响应的分布函数的关系。
- 人类反馈的强化学*:学*基于人类反馈的强化学*(RLHF),以使用技术方法解决PPO问题。
- *端策略优化:进一步学*如何使用PPO,以及如何配置PPO来为输入查询获取可能的响应。
- 直接偏好优化:探索直接偏好优化(DPO)及其算法,使用数学函数寻找DPO问题的最优解。
实践与工具
在本课程中,你将主要聚焦于Hugging Face库,因为它比直接使用PyTorch更加 streamlined(高效便捷)。
本课程包含用于巩固教学视频知识的实验。动手练*基于Jupyter Lab,用于实践所学概念和技术。这些实验反映了使用Hugging Face和PyTorch进行大语言模型预训练和微调的学*过程。
学*建议
为了从课程中获得最大收益:
- 观看所有视频。
- 完成实验以练*新技能。
- 尝试所有测验。
- 你还可以通过课程讨论论坛与同伴互动并向课程工作人员寻求帮助。
让我们开始这段激动人心的旅程。祝你好运!
总结

本节课中我们一起学*了《高级大语言模型微调》课程的介绍。我们明确了课程目标、适合的学*者、所需的预备知识以及将涵盖的核心技术,包括指令微调、奖励建模、RLHF、PPO和DPO。同时,我们也了解了课程以Hugging Face为主要实践工具,并通过实验加强学*效果。现在,你已经为接下来的深入学*做好了准备。
生成式人工智能工程:142:指令调优基础 🧠
在本节课中,我们将要学*指令调优的基础知识。指令调优是提升大型语言模型遵循指令并准确执行特定任务能力的关键技术。通过本教程,你将能够定义指令调优,描述其基本流程,并理解其中的核心概念,如特殊符号、提示格式以及指令掩码。
什么是指令调优?
上一节我们介绍了课程概述,本节中我们来看看指令调优的定义。指令调优,也称为监督式微调,其核心是使用专家标注的数据集对预训练模型进行进一步训练。指令调优 = 使用(指令, 输入, 输出)格式的数据对模型进行有监督微调。这个过程通过为模型提供具体的指令和上下文,来增强模型在特定任务上的表现,并确保任务被准确处理。指令调优通常在执行直接偏好优化或基于人类反馈的强化学*之前使用,旨在为模型建立坚实的理解基础,从而产生精确可靠的结果。

如何训练一个类GPT模型?
理解了指令调优的目的后,我们来看看它在整个模型训练流程中的位置。训练一个类GPT模型通常包含三个主要步骤:
以下是训练流程的三个阶段:
- 预训练:通过预测序列中的下一个词来训练模型,例如在GPT中。
- 指令调优:使用专家标注的示例对模型进行微调。
- RLHF或DPO:应用基于人类反馈的强化学*或直接偏好优化来进一步对齐模型行为。

指令调优示例
为了更具体地理解,让我们深入一个指令调优的例子。考虑一个用于教育目的的大型语言模型,它通过指令调优训练成为一个因果语言模型。这个模型被设计用来回答诸如“哪个是最大的海洋?”这样的问题。类GPT模型与普通因果语言模型的关键区别在于其经过专门训练和包含教育内容。
指令调优的关键组成部分
指令调优有几个关键的考量因素。模型需要指令和答案。一个反直觉的方面是,模型会生成指令以及响应,因为它计算的是整个序列(包括响应)的似然。例如,当输入是“最大的海洋是”时,模型会生成完整的指令和响应,如屏幕上所示。使用指令调优模型,这个过程相对容易管理,模型能通过更有效地解释和执行指令来执行各种任务。
它使用带有模板的数据集,包含三个核心组件:
以下是数据集的三个核心组件:
- 指令:为用户提供方向或命令,指定用户希望模型做什么。它也定义了模型应执行的任务或操作。你可以为模型提供指令来生成文本、翻译语言、总结内容、回答问题或执行特定计算。
- 输入:模型为完成指令而需要处理的数据或上下文。输入数据可以是一段文本、一个列表、一个问题或任何其他模型执行任务所需的数据。需要注意的是,并非所有数据集都包含输入,有些只包含指令和输出。
- 输出:定义期望的结果或响应。
特殊符号的作用
现在,让我们学*特殊符号在指令调优中的角色。考虑这个例子:指令是“创建一个应用线性函数的Python函数”,输出是“def function(x): return a linear function”。这对人类来说难以阅读。你可以看到输出被调整为“def function(x): return x**2 + 3*x”,这说明了特殊符号(如换行符\n)和格式的重要性。这种方法确保模型正确解释和处理结构化数据。请注意,此示例不包含输入。
此外,调整提示格式以保持与不同模型分词器的兼容性至关重要,即使用特殊标记来格式化提示。这种方法有助于正确解释输入的特定部分。例如,使用### Instruction:和### Response:可以确保模型的分词器为这些部分分配了适当的标记索引或ID,从而帮助模型正确处理。这对于像Meta的Open Pretrained Transformer这样的模型的分词器很重要。然而,其他模型或数据可能对指令和响应使用不同的标记,例如### Human:和### Assistant:。
探索指令掩码
在指令掩码中,目标是将损失计算集中在特定的关键标记上,而不是所有输出标记上。与生成式预训练类似,目标是预测向前移动一个时间步的输入,但损失函数略有修改。给定一个序列,特殊标记“指令”位于提示“最大的海洋是”之前,特殊标记“输出”位于响应“太平洋”之前。模型随后学*预测移位后的序列:“指令 最大的海洋是 输出 太平洋 EOS”。
为了理解损失是如何修改的,让我们分解通过最终线性层后的输出逻辑。考虑时间戳T的输入标记(记为ω_t)和相应时间戳的输出逻辑(在PyTorch中通常显示为红色)。交叉熵损失通常在模型的输出逻辑和序列中的下一个标记(记为y_{t+1})之间计算。为清晰起见,将损失写为实际预测标记ω_hat。在损失计算中,在生成式预训练中,你可以对批次中的标记求和。然而,指令掩码将损失计算限制在关键标记上。
例如,输入指令是“最大的海洋是”。预测输出将是移位序列(在图中以黄色框显示)。在常规微调中,模型会学*预测“指令”、“最大的海洋是”、“输出”、“太平洋”和“EOS”。而在指令调优中,通过掩码,模型只学*预测“太平洋”和“EOS”。这种有针对性的方法确保模型专注于序列的重要部分。


指令掩码的注意事项
最后,让我们看看指令掩码的一些考量。研究表明,对于较小的数据集,未掩码的指令有时表现更好。相反,一些库默认对指令进行掩码。在Hugging Face中,你应该使用一个名为DataCollatorForCompletionOnlyLM的函数来指定指令是否被掩码。然而,特殊标记通常会被掩码。
总结
本节课中我们一起学*了指令调优的基础知识。指令调优涉及使用专家策划的数据集训练模型。对于指令调优,模型需要指令和答案。指令调优通过更有效地解释和执行指令来帮助执行各种任务以生成响应。指令调优使用指令、输入和输出。你可以调整提示格式以保持与不同模型分词器的兼容性。最后,指令掩码专注于特定标记的损失计算。


生成式人工智能工程:143:使用Hugging Face进行指令调优 🚀
在本节课中,我们将学*如何使用Hugging Face库对预训练语言模型进行指令调优。指令调优,也称为指令GPT,通过让模型遵循特定指令来生成恰当的回答,从而训练语言模型。我们将涵盖从加载和格式化数据集,到创建模型、定义训练参数,再到构建文本生成管道的完整流程。

概述
指令调优的核心是让模型学会根据给定的“指令”和可选的“上下文”来生成“回答”。数据集中提供了结构化的指令、上下文和回答示例,这对于训练至关重要。模型会将上下文和指令连接成一个单一的输入序列,并据此生成所需的回答。
加载与格式化数据集
首先,我们需要加载一个合适的数据集。这里我们使用Code Alpaca 20K数据集,这是一个编程代码数据集。
以下是该数据集的组成部分:
- instruction: 给模型的唯一任务描述。
- input: 任务的可选上下文或输入(约40%的示例中包含)。
- output: 针对指令的答案。
接下来,将数据集按80%训练集和20%验证集的比例进行划分。为了进行微调,我们将丢弃那些包含输入值的样本。
对数据进行预处理至关重要,我们需要创建函数来生成提示模板。
以下是用于格式化数据的两个关键函数:
formatting_prompt_func: 此函数以数据集为输入,针对数据集中的每个元素,将指令和输出格式化为一个模板。格式通常为:### 指令: {instruction} ### 回答: {output}。在文本末尾添加句子结束(EOS)标记,以指示模型应在此处停止生成文本。formatting_prompt_func_no_response: 此函数与formatting_prompt_func类似,但不包含回答部分。该函数用于在验证模型时,为生成回答准备样本。
为了创建格式化数据集,我们使用以下代码块:
- 第一个代码块对输入进行清理和分词,并对回答进行分词以供评估。
- 第二个代码块定义一个从数据集获取的列表数据集类,并从中创建一个PyTorch数据集对象。这个类有助于从指令生成数据集对象。
创建与配置模型
我们将通过加载Hugging Face上的基础模型来微调Facebook的OPT-350M模型。
为了节省时间和计算资源,可以使用参数高效微调方法,例如低秩适应。首先,将基础模型转换为适合LoRA的PEFT模型。
接下来,通过定义LoRA配置对象来微调LoRA。配置参数包括LoRA秩、目标模块以及用于因果语言模型的任务类型。使用get_peft_model将此配置应用到模型上,将其转换为用于训练的LoRA模型。
为了训练模型,我们定义监督微调配置:
output_dir: 定义模型和训练文件的保存位置。- 可以自定义训练周期数以及训练和评估的批次大小。
evaluation_strategy: 定义评估时机,例如设置为每个训练周期结束后进行评估。max_seq_length: 控制输出回答的最大长度。fp16: 设置为True以启用16位浮点精度,实现高效训练。
定义数据整理器与训练器
现在,我们使用来自Transformers强化学*库的DataCollatorForCompletionOnlyLM来定义数据整理器。该整理器为文本补全任务准备训练语言模型的数据批次,并在计算损失时屏蔽输入中的指令部分。

此外,数据整理器还执行以下操作:
- 为较短的序列添加填充标记,确保一个批次内的序列长度统一,并截断较长的序列。
- 创建注意力掩码,以区分实际指令和填充标记,防止填充标记影响模型学*。
- 将多个序列组合成单个批次,以实现高效处理。
- 管理从开始到结束的批次特殊标记,包括填充标记。
接下来定义训练器。通过将格式化函数传递给训练器来处理数据预处理,从而创建SFT训练器对象。
然后将packing参数设置为False,表示不打包多个短示例。max_prompt_length参数控制提示长度。最后,将数据整理器传递给SFT训练器。

让我们回顾一下数据管道:
- 加载训练数据集。
formatting_prompt_func函数从数据集中的元素生成提示。- 数据整理器处理分词和掩码操作。
训练期间保存的训练损失可以在trainer.state中绘制图表进行查看。
构建文本生成管道与评估

为了简化流程,Hugging Face的管道可以自动处理模型加载、分词、填充和文本生成参数。

要评估模型的文本生成能力,可以使用Transformers库中的文本生成管道。
首先,使用pipeline类定义管道,并为其指定模型和分词器。将任务设置为text-generation。max_length定义生成文本的最大长度。最后,将return_full_text设置为False,使其仅返回回答而不包含指令。
接下来,通过向管道传递输入来生成文本以获取回答。可以使用如num_beams等函数来生成更高质量的文本。early_stopping参数控制基于束搜索方法的停止条件。创建管道后,传入输入即可生成回答。
最后,我们使用双语评估替换分数来评估模型。对于语言模型,使用定量指标进行评估具有挑战性。BLUE是编码任务中的一个有用指标,而SacreBLEU是BLUE的标准化变体。
结果显示,微调后的模型获得了14.7/100的SacreBLEU分数,显著优于基础模型的0.4/100。因此,经过指令微调的模型生成的回答与数据集中预期的回答更加吻合。
总结

本节课中,我们一起学*了使用Hugging Face微调基础模型的完整流程。
首先,我们加载了Code Alpaca 20K编程代码数据集。接着,使用formatting_prompt_func函数格式化数据集,该函数以数据集为输入来创建格式化数据集。我们使用了两个代码块来生成带回答和不带回答的指令。
然后,通过从Hugging Face加载基础模型来微调Facebook的OPT-350M模型。我们使用DataCollatorForCompletionOnlyLM定义数据整理器,为训练语言模型准备数据批次。并通过创建SFT训练器对象来定义训练器。

最后,我们从Transformers库构建了文本生成管道,并使用BLUE分数评估了模型的文本生成能力。
生成式人工智能工程:144:奖励建模与响应评估 🏆
在本节课中,我们将学*奖励建模与响应评估的核心概念。你将了解如何定义奖励建模,以及如何评估生成式模型的响应质量。
概述
奖励建模是量化模型响应质量、指导模型优化并使其与人类偏好对齐的关键技术。响应评估则是应用奖励模型来具体评判模型输出好坏的过程。
什么是奖励建模?
奖励建模通过为模型的响应分配数值分数,来评估其与人类偏好的对齐程度。
奖励建模的核心作用包括:
- 量化响应质量:为响应分配数值,以便评估和比较性能。
- 指导模型优化:通过优化模型参数以最大化奖励分数,从而提升模型表现。
- 融入用户偏好:在评分函数中纳入用户偏好,以定制模型行为。
- 确保一致性与可靠性:提供对响应一致且可靠的评估。
理解响应评估
上一节我们介绍了奖励建模的概念,本节中我们来看看如何具体评估因果解码器模型(如聊天机器人)的响应。
我们使用一个基于用户偏好的奖励函数(如下图中紫色方框所示)。训练该奖励函数,使其能为符合偏好的响应(例如关于“猫”的正面回应)分配高分。

例如,我们向大型语言模型输入一个查询,以确保LLM偏好“猫”。假设LLM1喜欢猫,而LLM2认为猫只是一般。
奖励函数会评估查询并给出分数。为了确保响应与LLM的偏好一致,分数应该较高。这里,由于LLM1喜欢猫,奖励函数会给反映这一偏好的响应打高分。对于LLM2,因为它认为猫只是一般,所以响应分数为0.5。

应用评分函数
现在,让我们考虑一个评分函数,它为生成精确且上下文准确的答案分配更高的分数。
例如,查询是:“哪个国家拥有南极洲?”我们将此查询输入两个聊天机器人A和B。
- 响应A 指出:“南极洲由《南极条约》体系管理。”这是事实且准确的。
- 响应B 声称:“是企鹅领主在掌管那里。”这是一个假设,并非事实。
因此,根据用户对事实准确性的偏好,我们的目标是选择响应A。
以下是应用评分函数的步骤:
-
对输入进行分词:
- 首先,对用户输入查询进行分词。用符号 ω(下标表示序列索引)表示每个词元。
- 接着,对响应A进行分词,用变量 ω̂_A 表示(表示它是A的估计响应)。
- 然后,对响应B进行分词,用变量 ω̂_B 表示。注意,这些序列的长度不必相同。
-
组合输入并评分:
- 评分函数 R 接收查询,并附加聊天机器人的响应。这意味着将查询和响应的词元收集起来,输入到评分函数 R 中。
- 在屏幕上,查询通常显示为白色,响应显示为蓝色。
- 使用符号表示:查询为 ω,响应为 ω̂。因此,对于响应A,评分过程评估其基于查询的准确性和相关性,得到高分0.89。
-
评估另一个响应:
- 同样,评分函数显示查询和不良响应(通常显示为红色)。
- 使用符号表示:查询为 ω,响应为 ω̂_B。
- 因此,响应B得分较低,为0.03。这个评分反映了基于训练数据的用户偏好。例如,如果训练评分函数优先考虑假设性回答,那么响应B可能会得到更高的分数。


总结
本节课中,我们一起学*了奖励建模以及如何创建或训练奖励模型。
奖励模型以提示作为输入,并输出关于响应的奖励分数。奖励建模有助于量化响应质量、指导模型优化、融入奖励偏好以及确保响应的一致性和可靠性。
你学会了使用评分函数:输入查询和两个候选响应,然后根据事实和上下文准确性选择更优的响应。在响应评估过程中,基于生成的响应,你会得到相应的分数或奖励。评分函数接收一个查询并附加聊天机器人的响应来进行计算。


生成式人工智能工程:4:奖励模型训练 🏆
在本节课中,我们将学*奖励模型训练的核心概念。你将能够解释奖励模型的损失函数,并了解如何创建和训练一个奖励模型。
奖励模型损失衡量的是模型预测的响应及其得分,与实际环境或系统给出的响应和得分之间的差异。奖励模型训练是一种高级技术,它训练一个模型来识别另一个模型生成的理想输出,并根据其相关性和准确性为结果分配分数。这个分数有助于微调生成模型,以产生更准确的输出。
训练评分函数
上一节我们介绍了奖励模型的基本概念,本节中我们来看看如何训练一个语言模型来有效地生成奖励。首先,需要训练评分函数。通常,人类评估者会为响应分配分数。然而,让人提供精确的数值分数是具有挑战性的。

下图展示了查询、响应和分配分数的示例:

第一列和第二列代表查询和响应,第三列代表分配的分数。观察各行,模式是一致的:第一行是查询,第二行是得分较高的响应(响应A),第三行是得分较低的响应(响应B)。
对人类而言,对响应进行排序比分配具体分数要容易得多。下图展示了基于分数排列响应,而不分配具体数值的方法:

这种排序方法简化了评估过程,使人类评估者更容易提供准确的反馈。成对排序方法利用了多个样本的排序。在本视频中,我们重点讨论两个样本。目标是开发一个奖励函数,使其满足不等式:好响应的奖励高于坏响应的奖励。
我们采用OpenAI的表示法,其中 R 是一个关于 x 和 y 的函数。这里,x 是查询,y_a 是聊天机器人的正面输出(好响应),y_b 是负面输出(坏响应)。参数 φ 代表Transformer模型的可学*参数。
生成奖励模型损失
为了理解如何生成奖励模型损失,我们来看一个例子:x 是一个查询,y_a 是一个好响应,y_b 是一个坏响应。
为了生成奖励,将 x 和 y_a 输入模型。编码器模型生成表示为蓝色的上下文嵌入。类似于分类任务,将分类标记的输出通过一个线性层(768个输入,1个输出数值,表示为 z_a,蓝色),这代表了对好响应的估计奖励。
对坏样本重复此过程,其输出嵌入显示为红色。目标是确保 y_a 的奖励 z_a 高于 y_b 的奖励 z_b。
接下来,使用Bradley-Terry模型来理解奖励损失模型,通过生成成本或损失函数。首先,找到确保好响应获得比坏响应更高奖励的参数。
为了更好地理解,我们使用一个类似于支持向量机的几何论证,而非纯概率论证。例如,考虑一个数据集。
- 首先,将比较不等式转换为差值,强调最大化好响应和坏响应奖励之间的差距。
- 然后,应用sigmoid函数,将差值解释为响应A优于响应B的概率。
- 最后,通过乘以负一,将其转化为最小化问题。
这种方法简化了参数优化,意味着找到最优参数值 φ 可以最小化差值。然而,减少负sigmoid函数有助于实现好响应相对于坏响应的最大奖励。
基于参数 φ,δ 代表好响应和坏响应奖励之间的差值。此外,δ 衡量了对于给定的 φ,好响应相对于坏响应的奖励质量。这意味着随着 δ 增加,损失应该减少,因为奖励函数的差值在增加。
这种方法以表格形式展示。下表显示了不同 φ 值对奖励差值 δ 以及相应的损失函数(负sigmoid(δ))的影响。

第一列代表不同的 φ 值。第二列显示 δ 值,即好响应和坏响应之间的奖励差值。表中的正值代表对好响应的偏好。第三列显示作为 δ 函数的损失。随着 δ 增加,损失减少。
让我们观察下图,其中x轴代表 δ,y轴显示作为 δ 函数的损失值。使用表中的值绘制图表,评估不同 φ 值的损失。
图表显示,随着 δ 增加,损失减少,这表明当损失最小化时,δ 具有更高的值,正如预期的那样。
为了估计参数,使用最大似然估计,将每个样本的sigmoid输出独立相乘。如果将此输出乘以负一,则将问题转化为最小化问题。为了简化此优化,直接对似然应用对数函数,并使用负一进行缩放,从而得到负对数似然。对数函数是单调递增的,不影响最小值的位置。图表显示两种方法具有相同的最小值。对数函数将乘积转换为求和,简化了微分过程,梯度下降可以帮助找到最优参数。

总结
本节课中我们一起学*了奖励建模以及如何创建或训练奖励模型。

奖励模型训练是一种高级技术,它训练一个模型来识别另一个模型生成的理想输出,并根据其相关性和准确性为结果分配分数。训练评分函数有助于有效地生成奖励。生成奖励模型损失时,编码器模型会生成上下文嵌入。使用Bradley-Terry奖励损失模型,你可以通过生成成本或损失函数来理解奖励损失模型。
生成式人工智能工程:5:使用Hugging Face进行奖励建模 🏆
在本节课中,我们将学*如何使用Hugging Face生态系统进行奖励建模。我们将了解奖励建模的数据集、数据预处理方法,并应用低秩自适应(LoRA)等技术来训练一个奖励模型。课程将涵盖从数据准备到模型训练与评估的完整流程。

概述
奖励建模是训练模型区分高质量和低质量响应的关键步骤。本节将引导你使用Hugging Face库,基于成对偏好数据训练一个奖励模型。我们将使用一个合成指令数据集,学*如何配置模型、定义损失函数并进行评估。
理解数据集
首先,我们需要理解所使用的数据集。考虑一个来自Hugging Face的数据集,例如 de haos/synthetic-instruction-GTJ-pairwise。该数据集通常用于训练和评估遵循指令的模型。
这个数据集通过让模型学*“好”与“坏”的响应配对,来训练模型理解并遵循复杂的指令。以下是数据集中的一个样本数据点。
每个数据点包含三个部分:
- prompt:一个文本提示,模型需要对此作出响应。
- chosen:针对该提示的偏好响应(好的响应)。
- rejected:针对该提示的非偏好响应(差的响应)。
数据预处理
在开始训练过程之前,我们需要对数据进行预处理。这包括定义评分函数、设置分词器和模型。例如,我们可以训练一个GPT-2模型用于序列分类,作为评分函数。这个模型有助于确定响应的质量。
模型的输出层是一个单一的类别或一个神经元,它产生一个代表得分的标量值输出。
以下是准备数据的关键步骤:
首先,定义一个 get_response 函数来结构化数据,将其整理为查询和响应对,便于读取和测试。接着,将此函数应用到整个数据集上。
定义一个名为 add_combined_columns 的函数。该函数接收单个数据点作为示例,并添加两个新列:prompt_chosen 和 prompt_rejected。
prompt_chosen列将提示与偏好响应(chosen)结合起来,并使用“人类”和“助手”标签标注对话部分。prompt_rejected列以相同的标签格式将提示与非偏好响应(rejected)结合起来。
接下来,使用 map 方法将此函数应用到训练集中的每个示例。
然后,过滤掉短于指定最大长度的样本,以确保数据集满足所需的长度标准。
现在,我们来定义核心的预处理函数。调用此函数处理单个数据点,以观察其新格式化的键。
preprocess 函数为奖励训练器(Reward Trainer)对 prompt_chosen 和 prompt_rejected 键进行分词。
chosen键代表偏好响应。rejected键代表非偏好响应。
对这些键进行分词使模型能够处理和理解高质量与低质量响应之间的差异。通过提供成对的“chosen”和“rejected”输入,奖励训练器可以区分并优先选择更好的响应,这有助于训练模型遵循指令。
处理后的数据点包含以下字段:
input_ids_chosen和input_ids_rejected:分别代表“chosen”和“rejected”响应的分词ID。这些ID是模型使用的文本数值表示。attention_mask_chosen和attention_mask_rejected:分别包含“chosen”和“rejected”响应的注意力掩码。
在此阶段,数据点已包含每个标签的文本分词。在训练数据集中,使用 map 方法将 preprocess 函数应用到每个样本,对 prompt_chosen 和 prompt_rejected 文本进行分词。设置 batched=True 参数可以使函数批量处理多个样本,提高效率。需要注意的是,这只是为了解释评分函数的工作原理,并非训练过程本身的一部分。
最后,将数据分割为训练集和测试集。
配置模型与训练参数
你可以使用LoRA来训练模型。首先,为序列分类任务初始化LoRA配置。该配置使用参数高效微调(PEFT)库中的 LoraConfig 类来指定多个参数。
from peft import LoraConfig
lora_config = LoraConfig(
task_type="SEQ_CLS", # 序列分类任务
r=8, # LoRA秩
lora_alpha=32,
target_modules=["q_proj", "v_proj"], # 目标模块
lora_dropout=0.1,
bias="none"
)
接下来,使用Transformers库中的 TrainingArguments 类来定义训练参数,以配置训练过程的各种设置。
from transformers import TrainingArguments
training_args = TrainingArguments(
per_device_train_batch_size=3, # 设置每个设备(GPU/CPU)的批次大小为3。根据GPU大小,可以尝试更大的批次。
num_train_epochs=3, # 指定训练轮数为3。
gradient_accumulation_steps=8, # 梯度累积步数为8。这意味着在执行反向传播或参数更新之前,会累积8个步骤的梯度,这等效于增大了批次大小。
learning_rate=1.41e-5 # 将优化器的学*率设置为1.41e-5。
)
使用奖励训练器进行训练
RewardTrainer 是一个专门设计用于训练奖励函数的训练器。首先,使用 RewardTrainer 来编排训练过程,它会处理诸如批处理、优化、评估和保存模型检查点等任务。它训练模型从反馈信号中学*,并提高其生成高质量响应的能力。
你可以使用各种参数来实例化它:
model:要训练的模型。args:训练参数,通常是TrainingArguments的实例。tokenizer:用于处理文本输入的分词器。train_dataset:用于训练模型的数据集。eval_dataset:用于评估模型的数据集。peft_config:用于配置LoRA。


接下来,使用 RewardTrainer 来训练、保存和评估模型。调用 trainer.train() 方法来启动训练过程。模型从训练数据集中学*,优化其参数以提高性能。使用 evaluate 方法存储评估指标。
使用以下代码绘制训练损失曲线,屏幕显示损失函数正常收敛。
模型评估
现在,将分词过程、生成分数以及比较两个独立函数的输出进行封装。
第一个函数对文本进行分词并生成模型的输出分数。第二个函数则对这些分数进行成对比较。
最后,通过计算胜率来评估模型的性能。例如,如果模型为更好的响应分配了更高的分数,则标记为1(正确),否则标记为0(错误)。
模型评估过程首先定义 N,即要评估的样本数量。接着,初始化一个计数器 correct_selections 来跟踪正确识别偏好响应的数量。代码随后遍历训练数据集中的前N对“chosen”和“rejected”响应。

屏幕显示输出结果为100%的胜率。需要注意的是,此数据基于合成数据。然而,顶尖模型实现的胜率通常在60%到70%之间。
总结

在本视频中,你学*了使用Hugging Face进行奖励建模。
- 来自Hugging Face的合成指令配对数据集通常用于训练和评估遵循指令的模型。
- 定义预处理函数有助于为奖励训练器格式化键并对数据进行分词。
- Transformers库中的
TrainingArguments类定义了训练参数,以配置训练过程的各种设置。 RewardTrainer编排训练过程,并使用trainer.train()方法保存和评估模型。- 分词过程生成分数,并比较两个函数的输出,以达到期望的胜率。
生成式人工智能工程:6:大型语言模型作为分布 🧠
在本节课中,我们将学*大型语言模型如何被视为一种概率分布,并探索如何通过采样从该分布中生成文本。我们将了解Transformer模型如何计算每个词的概率,以及如何通过调整生成参数来控制输出序列的特性。
上一节我们介绍了LLM的基本概念,本节中我们来看看LLM如何作为一个概率分布来运作。

一个语言模型接收一个查询样本 X,并生成一个响应 Y。这里的 Y 是一个随机变量。考虑一个查询:“哪个海洋最大?”。对于这个查询,模型会根据概率分布生成各种可能的响应,例如“太平洋”,接着可能是“太平洋是地球上最大的海洋”、“太平洋面积为1.55亿平方公里”或“大西洋”。
你可以将此表示为 Y ~ π(Y|X),其中策略 π 就是一个概率分布。然而,我们更关注如何生成 Y 中的每一个词元。
Transformer模型通过对其最后一层应用 softmax函数 来生成不同词的概率。对于一个查询“哪个海洋最大”,条形图可视化了可能的词。X轴表示在时间步 t 可能的词,Y轴表示每个词的概率。
以下是模型生成词元的基本过程:
- 模型接收查询,并计算第一个词的概率分布。
- 根据该分布采样或选择下一个词。
- 将已生成的词作为输入的一部分,反馈给模型,计算下一个词的概率分布。
- 重复此过程,直到生成完整的序列。
理解了LLM作为分布的基本原理后,我们来看看模型在具体时间步上是如何工作的。
在时间步 t,模型会基于当前上下文生成一个概率分布,从中可以采样得到不同的实现结果。例如,实现1可能是“太平洋”,实现2可能是“大西洋”,实现3可能是“印度洋”等等。统计每个词出现的次数,你会发现其频率与softmax函数生成的分布成正比。
当时间步从 t 变为 t+1 时会发生什么?模型生成词元时,时间步 t+1 的分布依赖于之前时间步的值。例如,如果时间步 t 的词是“太平洋”,那么下一个词的概率分布会相应改变;对于“印度洋”也是如此。这表明,时间步 t+1 的概率分布依赖于时间步 t 的词及其概率。
这种关系从 t=0 开始。随着 t 增加,时间步 t+1 的分布受到时间步 t 及更早时间步的值的影响,从而产生各种可能的序列。例如,“太平洋”会导致在时间步 t+2 产生特定的分布,类似于“大西洋”、“印度洋”、“南极洋”所产生的情况。最终,当 t 处理更长的序列时,你可以看到从初始查询生成的各种示例。
现在,让我们总结一下分布过程,并聚焦于因果Transformer的工作原理。
这里的输入模型将“最大的海洋”转换为词元嵌入,并通过Transformer传递。我们不是应用 argmax 函数选择最可能的序列,而是选择一个随机输出。例如,你可能选择“太平洋”,也可能选择其他概率较低的词,如“大西洋”或“印度洋”。将这些词传回模型,并通过从模型生成的概率中随机选择词来重复此过程。接下来你会看到“海洋”、“海”和“湖”等词。
上一节我们了解了模型的生成机制,本节中我们来看看能帮助改变LLM生成序列的生成参数。
温度 τ 是softmax函数中的一个超参数,它影响概率分布。温度参数控制着分布的随机性,较高的 τ 值使分布更均匀,较低的 τ 值使其随机性降低。
让我们从softmax方程开始,看看不同的温度值如何影响概率分布:
P(i) = exp(z_i / τ) / Σ_j exp(z_j / τ)


以下是不同温度值的效果示例:
- 在温度
τ=1时,观察分布。 - 将温度增加到
τ=2,分布变得更均匀。 - 将温度增加到
τ=5,分布广泛扩展,显示出更高的随机性。 - 在温度
τ=10时,分布变得更平坦或更均匀。 - 在温度
τ=100时,分布几乎完全均匀,意味着每个词元几乎同等可能,表示随机性最大。
给定表格中的每一列显示了几种随机序列的实现。第一行温度为1,显示每个词含义相似。另一方面,第二行温度较高,显示随机的词。
接下来是Top-K采样,参数为 K。此方法将下一个词元的选择限制在概率最高的前K个词元内。
例如,设 K=3:
- 首先,用温度1计算softmax值,并显示不同词的初始概率分布。
- 接着,应用Top-K采样,选择概率最高的前三个词元,并过滤掉不太被偏好的选项。
- 识别前K个索引并高亮显示对应的词。
- 最后,对前K个值进行归一化,确保总和为1。

为了生成序列,还可以查看其他参数,如集束搜索、Top-p采样、重复惩罚以及最大/最小词元数。
- 集束搜索:跟踪并在每一步扩展各种顶级序列。
- Top-p采样:将采样池限制在累积概率超过阈值
p的最小词元集合内。 - 重复惩罚:惩罚重复的词元序列,以鼓励输出多样性,避免生成重复文本。
- 最大/最小词元数:设置生成序列中词元数量的上限或下限。

本节课中我们一起学*了如何使用LLM作为响应生成的策略。样本查询问题可能基于概率分布提供各种随机响应。Transformer模型使用softmax函数生成不同词的概率,在各个时间步选择词,并改变这些词的概率。生成参数(如温度、Top-K采样、集束搜索、Top-p采样、重复惩罚以及最大/最小词元数)有助于改变使用LLM生成的序列。
生成式人工智能工程:148:从分布到策略 🧠
在本节课中,我们将学*如何将语言模型视为一种策略分布,并理解其在强化学*中的应用。我们将探讨策略的概念、其重要性,以及如何通过策略分布生成多样化的文本序列。
策略的重要性
上一节我们引入了策略的概念,本节中我们来看看策略在人工智能领域,特别是强化学*和大语言模型中的核心作用。
策略是智能体根据当前环境状态决定其行动的具体策略或映射。在强化学*中,策略决定了如何生成一系列动作的分布。对于大语言模型,策略可以指导文本生成过程,探索不同的生成路径,从而产生更多样化且符合语境的输出。
以下是策略的几个关键作用:
- 在强化学*中:策略用于执行任务,例如玩游戏,通过引导动作来实现目标。
- 在大语言模型中:策略增强了决策能力和基于文本的任务性能,帮助模型学*最优响应,从而提高语言理解和生成的准确性与相关性。
- 与传统AI方法的区别:强化学*策略利用随机性来探索未知的可能性。这种方法通过生成创造性、多样化的响应并适应新语境,帮助大语言模型表现得更好,使其更健壮和通用。
语言模型作为策略分布
理解了策略的重要性后,我们来看看如何将语言模型形式化为一个策略分布。
在强化学*的语境下,语言模型可以被视为一个策略分布。它根据输入的查询,遵循策略分布来生成响应。这可以表示为 y ~ π(·|x),其中 x 是长度为 M 的输入序列,y 是长度为 N 的总输出序列。
例如,考虑查询:“哪个是最大的海洋?”。模型会生成各种可能的响应。每个可能的响应被称为一个“rollout”(轨迹)。

以下是几个可能的rollout:
- 太平洋。
- 太平洋是地球上最大的海洋。
- 大西洋面积1.55亿平方公里。
- 大西洋。
为了阐明策略与作为参数函数 θ 的语言模型之间的关系,可以考虑关系式 y ~ π_θ(·|x)。详细的策略分布表示为 θ 的函数,展示了基于先前标记计算响应概率的方式。
对于查询“哪个是最大的海洋?”,模型首先计算不同响应的概率分布。例如,“大西洋”的概率可以表示为 P(“大西洋” | “哪个是最大的海洋?”),或者更一般地,给定查询和前 k-1 个标记时,第 k 个响应标记的概率。
理解Rollouts
最后,让我们深入了解rollouts的概念,即模型如何为每个查询生成不同的响应。
rollouts指的是模型为每个查询生成不同响应的过程。为此,我们来看一组初始查询。

考虑查询:“哪个是最大的海洋?”。模型生成若干个随机响应,在这个例子中是5个,每一个都被称为一个rollout。这个过程会持续进行,例如对于查询“你能给我一些Python代码吗?”,模型也会生成若干个rollouts。
需要重点注意的是,在诸如Hugging Face这样的库中,rollout的定义与强化学*中的定义有所不同。在强化学*中,rollout的定义包含了奖励信号。
总结

本节课中,我们一起学*了如何在强化学*中使用分布作为策略。
- 策略是智能体决定其行动的具体策略或映射。
- 策略有助于在大语言模型中生成动作,增强学*和决策能力,并通过探索提升性能。
- 遵循策略分布,语言模型基于输入的查询生成响应。你可以通过关系式
y ~ π_θ(·|x)来理解策略与作为参数函数θ的语言模型之间的关系。 - Rollouts是模型为每个查询生成不同响应的方式。Hugging Face等库中的rollout定义与强化学*中的定义不同。
生成式人工智能工程:8:从人类反馈中进行强化学*(RLHF)🎯
在本节课中,我们将学*从人类反馈中进行强化学*(RLHF)的核心概念。你将学会如何使用奖励函数计算奖励,并理解模型如何将人类反馈整合到语言模型的微调过程中。
想象一下,让无数只猴子在无限长的时间里随机敲击打字机。最终,它们能打出任何给定的文本,比如莎士比亚的全部作品。但如果引入奖励机制,比如给它们香蕉,是否就能减少所需猴子的数量或缩短所需的时间呢?让我们通过RLHF和这个猴子例子来理解这个过程。
奖励计算与人类反馈
上一节我们介绍了RLHF的基本思想,本节中我们来看看如何具体计算奖励。

首先,一只猴子开始打字以生成相关的词语。当它打出一个相关词语后,文档会被提交审核。如果文档令人满意,猴子会得到一个香蕉作为奖励。猴子会重复这个动作,但最终可能会打出随机词语。这些随机词语中的一部分也可能获得奖励,这个过程不断重复。最终,猴子就能创作出莎士比亚的作品。
为了理解奖励计算,我们使用一个奖励函数,表示为 R(X, Y)。
接下来,插入查询:“哪个国家拥有南极洲?”。奖励函数为插入的查询提供人类反馈。
- 第一个回应是:“?9DFSA”。这个回应不相关,得分为0。
- 第二个回应是:“没有国家拥有南极洲”。这个回应准确,得分为0.9。
- 最终,理想的回应是:“南极洲由一个国际条约管理”。这个回应最准确,获得满分1分。
类似地,你需要准备各种查询和回应的样本来进行训练。
采样过程与“Rollout”
为了审查采样过程,我们需要关注查询和回应的“Rollout”。
以下是Rollout过程的说明:
考虑一个显示在屏幕上的查询表。第一列索引n代表查询编号。第二列代表查询,例如“最大的海洋是?”、“你能给我一些Python代码吗?”、“这是一本儿童书吗?”等等。公式 X ~ D 表示从这个表中随机抽取一个样本。
例如,选择查询编号1作为随机样本:“最大的海洋是?”。接下来,将语言模型表示为一个表格。第一行代表查询“最大的海洋是?”,其中n=1。下面的每一行显示可能的回应,包括“太平洋”、“大西洋”等等。Y ~ D₁ 表示Y是从该表中随机抽样的,K是每个回应的索引。
再取另一个查询:“你能给我一些Python代码吗?”,其中n=2。你可以看到针对这个查询的不同表格,每一行都有不同的回应,由K索引,Y从D₂中抽样。你可以对每个查询问题重复这个过程,包括“这是一本儿童书吗?”、“哪个国家拥有南极洲?”等等。这个过程被称为 Rollout。
期望奖励
现在,让我们在语言模型使用之前,通过一个表格来理解期望奖励的概念。
首先看经验公式:
(1/N) * Σ_{n=1}^{N} [ (1/K) * Σ_{k=1}^{K} R(x_n, y_{n,k}) ]
它通过平均多个查询及其各自回应的奖励来*似期望奖励。在这个公式中,N 代表查询总数,n 代表单个查询,K 代表每个查询的回应数量,k 代表单个回应。
转换经验公式有助于确定实际的期望值公式:
E_{(X,Y) ~ (D, π)}[R(X, Y)]
这个实际值公式代表了在数据分布和模型针对给定输入的回应分布上的期望。
通过结合给定输入下每个回应的概率,可以得到单个查询的期望奖励。因此,期望奖励总结为 Σ_Y π(Y|X) * R(X, Y),其中 π(Y|X) 是给定输入X时回应Y的概率。
整合人类反馈进行微调
让我们学*如何整合人类反馈。例如,取一个需要微调的预训练大语言模型(LLM)。该模型表示为一个策略 π,展示了在给定输入查询时模型的回应分布。
假设有一个标为“模型”的方框。接下来,引入一个查询“谁制作了这门课程?”,表示为一个蓝色方框。你可以看到一个回应“他看起来像布拉德·皮特”,也表示在蓝色方框中。奖励模型的输入将是“查询+回应”。
为了评估并为“查询+回应”生成奖励,我们使用一个预训练的奖励模型。输入查询是“谁制作了这门课程?”,相关回应是“他看起来像布拉德·皮特”。根据查询和回应,奖励模型生成一个奖励值:-10,000。
现在,让我们看看这在微调策略时如何发挥作用。首先,创建一个代理(或LLM),表示为绿色,具有一组可学*参数 θ,以及表示为橙色的奖励模型。接下来,引入一个查询 X 作为代理的输入,以生成回应 Y。这里的查询和回应被称为 Rollout。奖励函数处理 X 和 Y,并使用该奖励来训练LLM并更新参数 θ。
以下是一个针对给定查询“哪个国家拥有南极洲?”的一组Rollout示例。查询作为输入引入代理。你可以在奖励模型的左侧看到这个查询,表明代理正在处理该查询。代理生成多个回应,随后是每个查询的奖励。这些奖励用于更新模型参数 θ。
以下是生成的回应示例:
- 第一个回应:“?9DFSA”,奖励为0。
- 第二个回应:“南极洲是...”,奖励为0.0021。
- 第三个回应:“企鹅霸主”,奖励为0.09。
- 第四个回应:“南极洲是一个国家”,奖励为0.02。
- 第五个回应:“没有国家拥有南极洲”,奖励为0.9。
- 最终回应:“南极洲由一个国际条约管理”,奖励为1。

这些生成的回应是从策略分布中随机产生的。奖励有助于优化策略分布的可学*参数。
总结📝
本节课中我们一起学*了如何计算奖励,以及如何将人类反馈整合到语言模型的微调中。

- 奖励函数 为插入的查询提供人类反馈,以接收相关回应并提供分数。
- 使用查询和回应的 Rollout 来审查采样过程。
- 期望奖励 有助于理解代理在语言模型中的表现,通过经验公式平均多个查询和回应的奖励来实现。
- RLHF 使用回应分布作为输入查询来微调预训练的LLMs。
- 你可以使用预训练的 奖励模型 来评估并为“查询+回应”生成奖励。
- 你可以根据代理生成的每个回应来更新模型参数 θ。
生成式人工智能工程:150:*端策略优化(PPO)🚀
在本节课中,我们将学**端策略优化(PPO)方法。我们将解释策略梯度和PPO的基本概念,回顾策略梯度的目标函数和KL惩罚系数,并学*如何使用对数导数技巧优化采样,以及如何通过梯度上升最大化目标函数。

构建智能体与奖励模型
首先,我们创建一个具有一组可学*参数 θ 的智能体或大语言模型(LLM),以及一个奖励模型。接着,引入一个查询 X 作为智能体的输入,以生成一个响应 Y。

查询和响应构成一个“轨迹”,奖励函数处理 X 和 Y,生成的奖励用于训练LLM并更新可学*参数 θ。
现在,我们来理解策略梯度和PPO方法。
理解策略梯度与PPO方法

策略梯度方法的目标函数是我们希望最大化的内容,而*端策略优化是实现这一目标的方法之一。
这些方法包含多个方面,如下图所示。首先,策略梯度方法构成了各种强化学*算法的基础。其次,裁剪替代目标等方法通过确保策略更新不会过于剧烈来稳定训练。KL惩罚系数用于调节新旧策略之间的差异,在训练过程中保持稳定性。另一个重要方面是优势函数,它用于估计奖励。在本视频中,我们将只回顾通用的策略梯度目标函数和KL惩罚系数。
策略梯度目标函数
策略梯度方法的目标函数类似于需要最大化的“分数”。为此,我们从奖励函数编码器开始,它估计输入对 x 和 Y 的奖励。接着,引入需要微调的模型。该模型表示为策略 π 或LLM(例如可以进行指令微调的GPT类模型)。
让我们看看优化过程的第一步。对于给定的数据查询 X,从数据集中推导出样本响应 Y 并估计奖励。估计的奖励表示为策略 π 寻找参数 θ 的期望奖励样本。

在第二步中,扩展整个数据集,并估计所有查询的期望奖励。然而,我们的目标是找到最大化期望奖励的最优策略。此外,引入一个参考模型作为正则化项,以确保模型不会偏离原始模型太远。接下来,控制超参数 β。解决这个问题具有挑战性,因为我们试图从采样中找到数据。

对数导数技巧
为了解决这个问题,即使是基本的策略梯度方法也需要对强化学*统计有基础的理解。对数导数技巧类似于蒙特卡洛方法中使用的技术,它为我们提供了优化这个问题的思路。两种方法都涉及基于采样数据估计梯度以优化函数。
需要注意的是,对数导数技巧只解决了PPO问题的一个方面。让我们来看看对数导数技巧。
为了计算导数,需要找到最大化目标函数(即期望奖励)的策略。为了使这个过程更容易,我们暂时忽略正则化项。首先,通过关注单个查询的期望奖励来简化表达式,注意导数不能直接以这种形式计算。接着,将单个查询或表达式转换为解析分布,从而允许直接针对参数 θ 进行优化。为了找到最佳参数,计算梯度并突出对数变换的梯度。然后,将表达式重新排列为可追踪的形式,并将梯度代回表达式。这将采样和表达式转换为允许使用样本进行梯度计算的形式。最后,提取出梯度以完成变换。

模型训练技巧
以下是训练模型的一些技巧。在训练模型时,定期使用人类反馈进行评估。开始训练模型时使用适中的 β 值,并提高温度以探索更多选项。
总结

在本视频中,我们学*了如何使用策略梯度方法和KL惩罚系数训练模型。策略梯度方法旨在最大化目标函数,而PPO有助于实现这种最大化。为了优化策略,我们推导样本响应、估计奖励并扩展数据集。我们可以通过识别最大化目标函数的策略、简化表达式并将其转换为解析分布来计算对数导数。为了训练模型,应定期使用人类反馈评估模型,使用适中的 β 值,并提高温度。
生成式人工智能工程:151:使用Hugging Face进行PPO 🚀
在本节课中,我们将学*如何使用Hugging Face库实现*端策略优化(PPO),重点了解情感分析评分函数、数据集及其分词处理过程。
概述 📋

PPO是强化学*中的一种算法,用于优化策略模型。在本节中,我们将构建一个使用情感分析作为奖励函数的PPO流程。具体来说,我们将:
- 描述用于情感分析的评分函数。
- 解释IMDB数据集及其使用Hugging Face进行的分词处理。
评分函数介绍
上一节我们介绍了PPO的基本概念,本节中我们来看看如何为PPO定义一个评分函数。在生成式AI应用中,对模型生成的回复进行情感分析,可以作为有效的评分函数,用于奖励正面回复而非负面回复。
在强化学*的*端策略优化中,奖励函数为策略采取的行动质量提供反馈。它同样可以评估生成式模型(如聊天机器人)所生成回复的质量。
初始化情感分析管道
首先,我们使用一个在IMDB影评数据集上微调过的预训练模型来初始化一个情感分析管道。
from transformers import pipeline
sentiment_pipe = pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english")
应用评分函数
现在,将情感分析管道应用于两段示例文本,以展示其结果。
texts = ["This movie was fantastic!", "I did not enjoy this film at all."]
用于情感分析管道的参数字典 S Wgs 指定了应返回所有四种结果,应用函数为 none,批次大小为 2。
运行定义好的管道对象处理上述文本:
outputs = sentiment_pipe(texts, **S_Wgs)
可以看到输出为每段文本对应的负面和正面情感概率值。来自情感分析管道的分数用于评估生成回复的质量或相关性,它表示模型对生成正面回复可能性的置信度。
提取奖励分数
接下来,遍历管道输出列表,从每个输出中提取分数,将其转换为张量并存储在奖励列表中。这些分数将作为模型对生成正面回复可能性的置信度,进而用作PPO训练中的奖励。
rewards = []
for output in pipe_outputs_list:
score = output['score'] # 假设输出中包含‘score’键
reward_tensor = torch.tensor([score])
rewards.append(reward_tensor)
数据集与分词处理
了解了评分机制后,我们需要准备训练数据。接下来,我们看看所使用的数据集以及如何对其进行预处理。
IMDB数据集
IMDB数据集包含50,000条电影评论。在本教程中,我们仅使用评论文本进行分析。首先,过滤掉长度小于或等于200个字符的评论,只保留长度大于200的序列。


长度采样器有助于在数据处理中变化文本长度,这能增强模型的鲁棒性并模拟真实的训练条件。同时,它通过管理文本输入长度来确保训练效率并维持模型性能。
长度采样器的范围介于设定的最小文本长度和最大文本长度之间。
分词处理
接下来,加载与因果语言模型关联的预训练分词器,并将填充标记设置为句子结束标记。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
tokenizer.pad_token = tokenizer.eos_token # 将EOS标记设为填充标记
现在,将评论文本分词为输入ID。将分词后的序列截断到所需长度,并将其赋值给 input_ids。
def tokenize_function(examples):
return tokenizer(examples["text"], truncation=True, max_length=512)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
分词后,你可以看到创建了输入ID和查询的样本文本。这可以作为模型的输入。
构建数据集函数

让我们将到目前为止讨论的所有步骤组合成一个单一的函数来构建数据集。
屏幕上显示了数据集在处理前后的两个主要差异:
- 添加了两个新的键。
- 由于移除了短于200个字符的文本,行数减少了。
下图展示了数据在清洗和分词前后的一个示例。
总结 🎯
本节课中,我们一起学*了使用Hugging Face进行PPO的过程,包括生成回复和计算评分。
- 奖励函数:PPO通过奖励函数为策略采取的行动质量提供反馈。
- 情感分析评分:情感分析管道的分数用于评估生成回复的质量。
- 分数提取:生成回复的分数从管道输出列表中提取。
- 长度采样器:长度采样器通过变化文本长度来增强模型鲁棒性,并模拟真实的训练条件。

通过掌握这些核心步骤,你已经为使用情感分析作为奖励机制来训练和优化生成式模型打下了基础。
生成式人工智能工程:152:PPO训练器 🚀
在本节课中,我们将学**端策略优化(PPO)训练器的核心概念与使用方法。我们将探索如何初始化PPO配置、使用PPO和参考模型生成文本,并解释不同模型之间的比较。
探索PPO配置初始化

上一节我们介绍了PPO的基本概念,本节中我们来看看如何初始化PPO配置。PPOConfig类用于指定PPO训练所需的模型和学*率。
以下是配置的关键步骤:
- 指定模型名称:
model_name参数用于设定需要微调的模型。 - 设置学*率:
learning_rate参数控制模型参数更新的步长。
加载模型与参考模型
为了稳定训练过程,我们需要加载一个参考模型。其核心作用是通过KL散度来约束当前策略(即被训练的模型)与参考策略(即参考模型)之间的差异,防止训练过程偏离太远。
AutoModelForCausalLMWithValueHead类扩展了标准的AutoModelForCausalLM类,专门用于强化学*场景,它在语言模型基础上增加了一个价值头(Value Head)来估计状态的价值。
数据整理器(Data Collator)
数据整理器函数对于准备适合PPO训练器格式的数据批次至关重要。它确保来自数据样本的每个特征(如input_ids, attention_mask)被正确地分组和填充,形成规整的张量批次。
以下是一个简化的代码示例,展示了其工作原理:
def data_collator(data):
# 假设data是一个字典列表,每个字典包含‘input_ids’等键
input_ids = [item[‘input_ids’] for item in data]
# 对input_ids进行填充,使其长度一致
padded_ids = pad_sequence(input_ids, batch_first=True, padding_value=tokenizer.pad_token_id)
return {‘input_ids’: padded_ids}
理解PPO训练器
PPOTrainer是训练过程的核心。它处理查询样本,并优化聊天机器人的策略,以完成确保生成高质量回复的复杂任务。
现在,让我们初始化PPO训练器。这需要组合之前准备好的所有组件。
以下是初始化所需的关键组件列表:
- 配置:首先,使用包含学*率和模型名称等设置的
PPOConfig。 - 模型:接下来,输入待微调的主模型。
- 参考模型:然后,输入用于稳定训练的参考模型。
- 分词器:接着,插入用于处理输入文本的分词器。
- 数据集:最后,提供模型训练所需的输入数据集。
- 数据整理器:数据整理器负责对输入数据进行批处理和格式化。
训练循环与奖励塑造
在PPO训练中,stats_all列表存储每个批次的训练统计信息。我们可以通过设置奖励来引导模型生成特定情感的文本。
- 将情感分数变化值
change_score设为1,会为积极情感提供更高的奖励,从而鼓励聊天机器人生成积极回复。 - 反之,将
change_score设为0,则会增加为消极情感提供高奖励的可能性,导致模型生成消极回复。
屏幕上展示了使用情感分析进行PPO算法的训练循环代码。该循环遍历PPO训练器和数据加载器提供的数据批次。
以下是每个训练步骤的核心操作:
- 生成回复:对于批次中的每个查询张量,随机采样一个输出长度,并以此设置生成新令牌的最大数量,然后使用PPO训练器生成回复。
- 处理回复:将生成的回复张量添加到回复张量列表中,并根据生成的长度进行修剪。
- 情感分析:将回复张量解码为文本,与原始查询拼接,然后应用情感分析管道分析拼接后的文本。
- 计算奖励:提取情感分数并将其转换为张量,作为奖励信号。
- 模型更新:执行PPO步骤,传入查询、回复和奖励,调用
trainer.step()方法更新模型。 - 记录统计:记录统计信息(包括批次和奖励),并将其追加到
stats_all列表中。PPO训练器会返回一个包含奖励和各项损失的字典。
结果分析与模型保存
我们可以编写一个函数来展示这些统计值。
- 将
return_objective参数设为True有助于获取优化模型参数所需的统计信息,例如模型损失和价值损失。 - 将其设为
False则有助于获取与强化学*相关的其他指标,例如优势估计和奖励计算。
训练完成后,是时候保存微调好的模型了。接下来,我们可以绘制PPO训练损失和PPO平均奖励随时间变化的图表。


从图表中可以看到,损失随时间推移而下降,同时PPO平均奖励随时间推移而上升。为了进行对比,我们也可以通过将change_score值设为0来训练一个专注于消极情感的模型(Model0)。


使用PPO和参考模型生成文本
现在,我们来学*如何使用训练好的模型生成文本。首先,需要根据CPU和GPU的可用性来分配计算设备。
接着,定义一个解码函数,用于对输入文本进行分词,并使用提供的模型生成回复。请注意,不能使用标准的text-generation流水线,因为它与AutoModelForCausalLMWithValueHead类不兼容。
我们可以使用相同的输入文本来查看三个模型的回复:
- Model1:在积极情感上训练,生成积极回复。
- Model0:在消极情感上训练,生成消极回复。
- 参考模型:即原始模型,提供中性回复。


模型比较

现在让我们比较Model0和Model1。屏幕上展示了积极情感模型和消极情感模型生成的回复示例及其情感分数。
可以看到,积极模型生成积极回复并获得高分。相反,消极模型生成消极回复并获得低分。

总结
本节课中我们一起学*了PPO配置及其使用Hugging Face库进行的训练过程。我们了解到:
PPOConfig类用于指定PPO训练的模型和学*率。- PPO训练通过KL散度来更新模型,约束其与参考模型的差异。
data_collator函数对于准备适合PPO训练器格式的数据批次至关重要。PPOTrainer收集对话样本并优化聊天机器人的策略。- 在PPO中,
stats_all列表存储每个批次的训练统计信息。 - 绘制PPO损失和PPO平均奖励随时间变化的图表显示,PPO损失下降,而PPO平均奖励上升。


生成式人工智能工程:153:DPO分区函数 🧠
在本节课中,我们将学*直接偏好优化(DPO)的基本概念及其模型构成,并重点探讨如何利用分区函数将一个简单的概率分布转换为更复杂的分布,以服务于DPO的目标函数。
概述

直接偏好优化(DPO)是一种强化学*技术,旨在比传统方法更直接、更高效地根据人类偏好对模型进行微调。它通过收集人类对不同模型输出的偏好数据来直接优化模型参数,使其输出更符合人类选择。

DPO的核心概念与模型

上一节我们介绍了DPO的基本目标,本节中我们来看看DPO具体涉及哪些模型。
DPO涉及三个核心模型:奖励函数、目标解码器模型和参考模型。
- 奖励函数:使用一个编码器模型。例如,一个用于评估大语言模型(LLM)文本相关性的奖励模型。如果输入文本是“this is a”,而响应是“cat”,奖励模型可能会给出一个低分(如0.1),因为“cat”与LLM无关。反之,如果响应是“reward function”,奖励模型则会给出高分(如0.99),因为这与LLM高度相关。
- 目标解码器模型:具有参数
θ的模型,这是我们希望优化的策略模型π_θ。 - 参考模型:一个固定的初始模型,用于在优化过程中提供正则化约束。
在GPU上同时运行这三个模型具有挑战性。我们的主要目标是获得一个最优策略 π* 及其参数 θ,以最大化以下目标函数:

J(θ) = E[ r(x, y) ] - β * D_KL( π_θ(y|x) || π_ref(y|x) )

其中,β 是一个衡量与参考模型偏离程度的正则化项。这个优化问题通常需要强化学*中的高级方法(如*端策略优化PPO)来解决。
然而,在DPO中,我们可以将这个复杂的强化学*问题转化为一个更简单、更易于优化的目标函数。
分区函数的作用
上一节我们了解了DPO希望简化的复杂优化问题,本节中我们来看看分区函数如何帮助实现从简单分布到复杂分布的转换。
分区函数是一个广泛的主题,此处我们仅通过一个直观示例来展示其作用:将一个简单的逻辑分布转换为一个更复杂的自定义分布。
首先,让我们探索逻辑函数 σ(x),它将任何实数映射到0和1之间。此函数构成了逻辑概率函数的基础,该函数计算给定 x 时 y 为0或1的概率,我们将其称为 P_ref,因为我们将基于它创建一个新的分布。
以下是构建新分布的关键步骤:
- 绘制基础概率:首先,绘制
y=0给定x的概率,即1 - σ(x)(蓝色曲线)。随着x增加,此概率下降。接着,绘制y=1给定x的概率,即σ(x)(红色曲线)。随着x增加,此概率上升。这两个概率之和,即分区函数Z(x),恒等于1,确保这是一个有效的概率分布。 - 缩放概率(非标准化):我们可以使用一个指数函数来缩放
y=0的概率,该函数始终为正且沿Y轴对称。同样,可以使用一个高斯(钟形)函数来缩放y=1的概率。然而,缩放后的函数本身不再是有效的概率分布,因为它们对于每个x的概率之和不一定为1。 - 应用分区函数进行标准化:分区函数
Z在这里扮演着关键角色,用于对这些自定义的、缩放后的概率函数进行归一化。通过将每个缩放后的概率除以其对应的分区函数值Z(x),我们可以得到新的标准化概率。 - 验证结果:标准化后,对于
y=0和y=1的新概率函数,在任意x值下,两者之和严格等于1。这演示了归一化如何确保自定义函数满足有效概率分布的标准。

这个过程直观地展示了分区函数如何作为“归一化常数”,将一组未经验证、可能无效的分数或权重,转化为一个结构良好、总和为1的概率分布。在DPO的推导中,类似的技巧被用于将基于奖励的复杂策略优化目标,转化为一个类似于分类问题的、更简单的最大似然目标。
总结

本节课中我们一起学*了:
- 直接偏好优化(DPO) 是一种用于根据人类偏好微调模型的强化学*技术,它通过直接优化策略模型来对齐人类选择,比传统奖励建模方法更高效。
- DPO涉及三个模型:奖励函数、目标解码器模型和参考模型。
- DPO的关键优势在于能够将复杂的强化学*优化问题,转化为一个更简单、更易于处理的目标函数。
- 分区函数 在概率建模中起着核心作用,它通过归一化操作,能够将简单的或自定义的非标准化分布,转换为有效的、总和为1的复杂概率分布,这一原理是DPO数学推导中的重要基础。
生成式人工智能工程:154:DPO最优解 🎯
在本节课中,我们将学*直接偏好优化(DPO)的最优解。我们将推导强化学*目标函数的闭式解,理解DPO目标,并最终找到DPO问题的最优解。
目标函数与KL散度
目标函数是机器学*的基础。它们协调算法和数据,以揭示模式、趋势和见解。这些数学工具指导机器学*模型完成学*过程,旨在实现准确的预测。本质上,目标函数衡量机器学*模型的预测结果与实际目标值之间的差异。
这个性能指标至关重要,它为优化提供了一个明确的目标。

让我们学*如何找到强化学*目标函数的闭式解。令 π* 为期望策略,π_ref 为任意参考策略,两者都代表给定输入 x 时输出 y 的概率 P(y|x)。KL散度衡量这两个概率分布之间的差异,如下图所示,它通过采样获得。

当且仅当 π* 和 π_ref 完全相同时,KL散度被最小化为0。为了更好地理解,让我们用一个一维示例来可视化,使用两个高斯分布。在y轴上,你有y的可能值。以0为中心的绿色高斯分布代表 π*,而以-4为中心(初始状态)的红色高斯分布代表 π_ref。当它们重叠时,KL散度达到0。
通过操作方程,你可以将强化学*目标函数问题表述为最小化KL散度,使 π* 与 π_ref 对齐,并确保你学*到的策略与期望行为匹配。
数学变换技巧
接下来,我们将运用一些巧妙而简单的数学技巧来推导目标函数。
第一个技巧是将最大化问题转换为最小化问题。下图绘制了函数 f(w)。红点标记了 f(w) 达到其最大值 ŵ 的点,该点通过取 arg max f(w) 找到。

接下来,你将通过取负来转换这个函数以找到其最小值。红点现在移动到取负后的函数达到最小值的点,从而有效地将 arg max 转换为 arg min。这个简单的变换允许你在寻找函数的最大值和最小值之间高效切换。
以同样的方式,将一个函数乘以一个标量不会改变最小值的位置。在下图中,函数 g(w) 被绘制在图上。最小值的位置沿水平轴。将函数乘以标量 c 不会改变这个位置。函数被 c 缩放,产生一个新函数 c * f(w)。请注意,尽管函数的形状发生了变化,但最小值保持在相同的位置。
现在,让我们使用最后两个例子来重新表述强化学*目标。
首先,从初始方程开始。为了使优化更容易,将整个表达式乘以负一,将最大化问题转化为最小化问题。注意各项现在是如何反转的。然后将表达式乘以 1/β。这些操作都不会影响最优值的位置。最后,将所有内容表示为期望值。这有助于简化优化过程。
推导DPO目标
让我们专注于目标并进一步简化方程。
以下是方程的简化形式,包含你的策略与参考策略的对数比率,以及一个涉及奖励的项。
现在,让我们用一些简单的代数来重新表述DPO目标。从初始目标开始,你有你的策略和参考策略的对数比率减去由 β 缩放的奖励项。
接下来,将奖励项表示为对数形式,这允许我们在后续步骤中合并对数。通过合并对数,将方程简化为显示比率除以奖励项指数的对数。
为了归一化,添加并减去一个归一化项 Z(x),确保分布总和为1。结合这个归一化,调整你的方程以明确包含 Z(x)。分母包含一个分布,即奖励加权的分布,它将显示为奖励策略 π_R。
对于KL散度,必须消除额外的 log Z 项。在下图中,函数 f(w) 被绘制在图上。红点标记了该函数最小值的位置。
接下来,证明减去一个常数 C 不会改变最小值的位置。函数通过减去常数 C 进行调整,产生一个新函数 f(w) - C。请注意,尽管函数的垂直位置发生了变化,但最小值的x值没有改变。
从简化后的表达式开始,你将制定目标函数。现在,最小化期望值,并通过注意到常数项 Z(x) 不是感兴趣的参数来简化。这导致了目标函数更简洁的表述。

接下来,将目标表达为最小化策略与奖励策略在数据上的KL散度,这正是最初的目标。因此,最小化此表达式的策略就是奖励策略,也就是该问题的最优解。

最优解将参考模型按奖励函数进行缩放,其中 β 参数控制常数。考虑输入标记 x 为 “This is a”,设置 β 为1。在下表中,第一列显示了大语言模型的两个输出,第二列显示了这些输出的概率。第一个输出是 “cats”。由于这不罕见,参考模型为其分配了0.8的概率。第二个输出是奖励函数,由于这不太可能,参考模型为其分配了0.1的概率。
如果第三列所示的奖励模型针对与大语言模型相关的问题进行了优化,“cats”将获得比奖励函数更低的分数,而奖励函数的概率会增加。通过取这些概率的乘积并进行归一化,新模型将为奖励函数分配大约1.0的概率,为“cats”分配0.0的概率。改变 β 将改变模型权衡参考模型与奖励函数的方式。
配分函数的计算挑战
计算这个配分函数本质上是不切实际的。让我们用具体的例子来说明这一点。


首先,对于序列长度为1的情况,Z(x) 对词汇表 V 中的所有单词求和,例如 “UBA”、“Aaron” 和 “Zziva”。其次,对于序列长度为2的情况,Z(x) 对词汇表中所有可能的单词对求和,形成一个更大的集合 V²。最后,推广到序列长度为 T 的情况,Z(x) 对词汇表中所有长度为 T 的可能序列求和,即 V^T。随着 T 的增加,项数呈指数级增长,这使得配分函数随着 T 的增加而越来越难以计算。
然而,有了奖励策略,你现在可以应对这种复杂性。
总结

在本节课中,我们一起学*了以下核心概念:
- 目标函数:协调算法和数据,以揭示模式、趋势和见解,从而产生准确的预测。它们衡量机器学*模型的预测结果与实际目标值之间的差异。
- KL散度:衡量两个概率分布(期望策略和任意参考策略)之间的差异。
- 最优解:将参考模型按奖励函数进行缩放,其中
β参数控制权衡常数。


生成式人工智能工程:14:从最优策略到DPO 🧠
在本节课中,我们将学*如何通过直接偏好优化(DPO)来训练生成式因果大语言模型。我们将推导DPO的目标函数,并找到最大化它的表达式。同时,我们将使用Bradley Terry模型来理解损失,并将其转换为成本。
概述
基于人类反馈的强化学*(RLHF)是优化大语言模型的有效技术,但它也带来了计算复杂性、不可微分性和不稳定性等挑战。DPO通过利用奖励函数的闭式最优策略来重新表述问题,从而解决这些问题。
从评分到成对排序
首先,我们使用一个评分函数数据集来通过DPO训练生成式因果大语言模型。人类评估者为回答分配分数,但分配精确的数值分数具有挑战性。
以下是查询、回答和分数的示例表。第一行是查询,第二行是得分较高的回答A,第三行是得分较低的回答B。


对回答进行排序比分配分数更容易。第二张表按受欢迎程度排列回答,无需数值分数,这简化了人类评估者的评估工作。
你的重点将放在两个样本的成对排序上,但该方法可以扩展到多个样本。为保持一致性,我们遵循DPO文献中的相同符号:W(win)代表回答A,L(lose)代表回答B。
使用采样符号,其中D是数据集,波浪线表示采样值。X(查询)、Y_W(获胜回答)和Y_L(失败回答)从数据集中抽取,如上表示例所示。
Bradley Terry模型与损失函数
在原始的Bradley Terry模型中,损失是sigmoid函数对获胜回答与失败回答得分之差的对数。
使用采样符号,可以将求和转换为在数据集D上的期望值。
现在,让我们关注log和sigmoid函数内部的参数,它本质上代表了损失函数,即获胜与失败回答的得分差。
定义DPO问题与挑战
为了解决给定的直接偏好优化(DPO)问题,你需要找到奖励策略,其中策略π是最优解。这里:
X是查询Y是回答Z是配分函数π_ref是参考模型R是奖励函数β是正则化参数
主要问题是你无法求解配分函数Z,因为它涉及对所有可能组合的求和。
然而,通过一些巧妙而简单的数学运算,你可以消除计算配分函数的需要,并为你的因果大语言模型找到一个公式化的成本函数。这使你能够直接基于Bradley Terry模型训练你的模型,而无需经历PPO的困难训练过程。
推导DPO目标函数
现在,我们将从最优解开始推导DPO目标函数。
首先,分离指数项,并在等式两边同时乘以配分函数。
然后,对等式两边取自然对数,以线性化指数项,并求解奖励函数。
现在,你得到了用最优解表示的奖励模型。
将正样本(获胜回答,用蓝色表示)和负样本(失败回答,用红色表示)的方程值代入。
回想一下Bradley Terry模型的损失函数。
通过将这些表达式代入方程来减去两个样本的奖励模型,不仅消除了对配分函数的需要,而且这里显示的新损失函数现在变成了大语言模型及其参考模型的函数。这消除了对单独奖励函数的需求,给出了一个最大化DPO目标的表达式。
简化与理解损失函数
让我们逐步简化表达式以更好地理解它。
首先,从一个样本输出的损失函数的初始方程开始。
接下来,设β为1,通过移除β缩放因子来简化方程。
为了进一步简化,用常数C替换参考模型,这意味着你以相等的概率随机选择词汇表中的任何单词。
利用对数定律,你可以将对数内的项合并。
最后,将对数的参数设为单个变量U,它代表正样本概率与负样本概率的比值。
这种最终形式显示损失是U的对数的函数。
损失函数随U变化的分析
现在,让我们绘制损失随U变化的函数图。
从初始方程开始,考虑当给定查询的获胜回答的策略概率小于失败回答的策略概率时的情况。
如果获胜回答的策略概率增加,但仍小于失败回答的策略概率,则U对应于0到1的范围。因此,随着U增加,模型变得更好。绘制损失随U变化的函数图,你可以看到随着获胜回答的概率增加,损失减少。
当获胜回答的策略概率大于失败回答的策略概率时,U的范围从1到无穷大,代表更受青睐的补全概率更高。随着U增加,表明正样本概率更高,损失继续减少,如图表相应部分所示。
将损失转换为成本
以类似于Bradley Terry模型的方式,你可以将损失转换为成本。
让我们从初始方程开始。这里,损失函数表示为获胜与失败策略概率比值的对数(经β缩放)的sigmoid函数的负值。
接下来,将此表达式插入Bradley Terry模型。这将方程重新表述为最小化对数似然,从而将损失转换为成本。
要在PyTorch中实现这一点,你可以编写一个损失函数并计算损失,或者使用Hugging Face内置的DPOTrainer。
总结
本节课中我们一起学*了以下核心内容:

- DPO利用奖励函数的闭式最优策略来重新表述问题。
- 为了解决给定的DPO问题,你需要找到奖励策略,该策略由以下表达式给出:
R(X, Y) = β * log(π(Y|X) / π_ref(Y|X)) + β * log Z(X)。 - 减去两个样本的奖励模型消除了对配分函数的需要,并且新的损失函数变成了大语言模型及其参考模型的函数。
- 损失函数表示为获胜与失败策略概率比值的对数(经
β缩放)的sigmoid函数的负值:L_DPO(π_θ; π_ref) = -E_(X, Y_W, Y_L)~D [ log σ( β * log(π_θ(Y_W|X)/π_ref(Y_W|X)) - β * log(π_θ(Y_L|X)/π_ref(Y_L|X)) ) ]。

通过这种方式,DPO提供了一种更稳定、更高效的方法来根据人类偏好微调大语言模型。
生成式人工智能工程:15:使用Hugging Face进行DPO 🚀
在本节课中,我们将学*如何使用Hugging Face工具进行直接偏好优化。我们将介绍可用于模型微调的库和资源,并逐步实现DPO微调,包括数据预处理、模型创建、训练、评估和推理。
与*端策略优化相比,通过DPO微调语言模型更为简便。DPO微调主要包含两个步骤:首先是数据收集,即针对每个提示,收集包含正例和负例选择的偏好数据集;其次是优化,即直接最大化DPO损失的似然函数。
数据准备 📊
上一节我们介绍了DPO的基本概念,本节中我们来看看如何准备数据。
我们将使用HuggingFace上由Bra Home提供的数据集。首先,使用以下命令加载数据:
# 加载数据集的代码示例

该数据集分为六个部分,每条记录包含七个特征。我们只需要其中三个特征:chosen、rejected和prompt。本质上,该数据集为每个提示提供了一个偏好的回复和一个被拒绝的回复。
为了更好地理解数据集,可以通过执行以下命令来检查一条样本记录:
# 查看样本记录的代码示例
这将显示数据集中的一条记录,展示其结构和内容。
在使用该数据集进行DPO训练之前,必须对其进行重新格式化。具体来说,需要提取提示、被拒绝的回复和选中的回复,以匹配DPO训练器的输入要求。
以下是处理步骤:
- 定义一个处理函数,用于移除不需要的特征,并格式化被拒绝和选中的回复。
- 使用
map方法,将处理函数应用到整个数据集。 - 最后,创建训练集和评估集。
经过预处理后,一条样本记录如下所示:
# 预处理后样本记录的示例
模型与分词器配置 ⚙️
在准备好数据之后,下一步是创建和配置模型与分词器。
首先,使用Hugging Face Transformers库中的AutoModelForCausalLM类加载解码器GPT-2模型。
# 加载GPT-2模型的代码示例
接下来,加载一个参考模型,这本质上是GPT-2模型的另一个实例。保留一个未修改的模型版本用于参考非常有用。
为了处理文本数据,需要一个分词器。按如下方式加载GPT-2分词器:
# 加载分词器的代码示例
然后,通过将其填充标记设置为序列结束标记来配置分词器。这确保了填充处理的一致性。
为了实现内存高效的微调,可以集成参数高效的LoRA配置。这应用于注意力参数,可以加速训练。你可以尝试不同的权重和参数值以优化性能。
训练与评估 🏋️
配置好模型后,现在我们来定义训练参数并进行训练。
以下是定义训练参数的方法。超参数与其他方法类似,但多了一个beta参数。beta参数是DPO损失的温度参数,通常在0.1到0.5的范围内。
# 定义训练参数的代码示例
接下来,定义DPO训练器。参考模型设置为None,因为你传递了PEFT配置,这意味着它是添加适配器LoRA层之前的原始模型。
通过运行trainer.train(),你可以开始在提供的数据上使用DPO方法训练模型。
现在,让我们绘制模型的训练损失图。训练日志可以从trainer.state中检索,这是一个JSON文件。可以看到,在训练过程中,训练损失在不断下降。
模型推理与结果 📈
训练完成后,我们来看看如何使用模型生成回复。
首先,加载训练好的DPO模型。同时,为了进行比较,也加载原始的GPT-2模型。
你也可以尝试使用pipeline函数和GPT-2分词器。
接下来,为DPO模型定义生成配置。
对于模型推理,定义输入提示:“Is a higher octane gasoline better for your car?”
现在,使用分词器对提示进行编码。然后,分别使用DPO模型和原始GPT-2模型生成文本。
最后,解码生成的文本并打印结果。
结果显示,DPO模型能够生成更高效、更直接的回复。

总结 ✨
本节课中我们一起学*了使用DPO微调语言模型的两个主要步骤:数据收集和优化。

要在Hugging Face上使用DPO微调语言模型:
- 第一步是预处理数据集,你需要重新格式化它,然后定义并应用处理函数,最后创建训练集和评估集。
- 下一步是为你的任务创建和配置模型与分词器。
- 然后,你将定义训练参数和DPO训练器。
- 接着,绘制模型的训练损失图,以确保其在训练过程中不断下降。
- 最后,加载训练好的模型来生成回复,随后进行模型推理。
生成式人工智能工程:157:课程介绍 🚀
在本课程中,我们将学*如何利用Ra和Langchain构建AI智能体。你将通过实践AI工具和技术,掌握推动AI职业生涯发展所需的实用技能。




目标学员
本课程适合现有或志向成为数据科学家、机器学*工程师、深度学*工程师、AI工程师以及希望精通大语言模型(LLMs)的开发者。
预备知识
学*本课程,具备Python和PyTorch的基础知识,并对Transformer、嵌入(Embeddings)和掩码(Masking)有所了解将更有优势。
学*目标
完成本课程后,你将能够:

- 解释检索增强生成(RAG)、编码器和F等关键概念。
- 应用上下文学*的基础知识和提示工程的高级方法来优化提示设计。
- 运用RAG、PyTorch、Hugging Face、LLMs和Langchain等技术解决不同的应用问题,获得职场实用技能。
课程内容概览
本课程主要聚焦于与RAG、上下文学*和Langchain相关的概念。

课程开始时,你将学*用于生成响应的AI框架——RAG及其流程。接着,你将使用上下文编码器、问题编码器及其分词器,并探索由Facebook AI Research开发的F库。你还将练*将RAG与Hugging Face和PyTorch结合,应用于不同场景。
上下文学*的基础知识和提示工程的高级方法将帮助你提升提示设计能力。进一步,你将描述Langchain的核心概念、组件和聊天模型,并探索提示模板、示例选择器和输出解析器。
继续深入,你将利用文档加载器、文本分割器、向量数据库和嵌入等工具来提升LLM生成响应的质量。通过对Langchain框架的深度探索,你还将学*链的序列、Langchain工具与智能体及其记忆机制。
在实践实验中,你将使用Jupyter Lab环境来练*这些概念和技术,为在项目中应用它们打下坚实基础。最后,你将通过一个基于真实场景的指导项目来学*职场实用技能。
课程结构与学*建议
本课程混合了多种内容形式以促进学*:视频简短并聚焦主题;阅读材料以文本形式提供详细内容;实验则提供技术环境、详细说明和可用于完成动手练*的代码片段。练*和分级测验将帮助你应用所学并评估知识掌握程度。
为从本课程中获得最大收益,请观看所有视频,完成实验以练*新技能,并尝试所有测验。你也可以通过课程讨论论坛与同学互动,并从课程工作人员那里获得帮助。
让我们开始这段激动人心的旅程吧。祝你好运!


总结


本节课我们一起了解了《生成式人工智能工程》课程的整体介绍,明确了课程目标、适合人群、所需基础以及将要学*的核心技术与框架(如RAG、Langchain)。课程将通过视频、阅读、实验和项目相结合的方式,帮助你构建使用AI工具解决实际问题的能力,为你的AI职业生涯做好准备。
生成式人工智能工程:158:检索增强生成(RAG)入门
在本节课中,我们将学*检索增强生成(RAG)的基本概念和工作流程。RAG是一种结合了信息检索与文本生成的AI框架,旨在帮助大型语言模型(LLM)生成更准确、更符合特定领域知识的回答。
什么是RAG?🤔
上一节我们介绍了课程目标,本节中我们来看看RAG的定义。
RAG是一个AI框架,用于优化大型语言模型(LLM)的输出。它利用LLM的能力,并结合特定领域知识或组织的内部数据库,而无需重新训练模型本身。
预训练的LLM在处理其训练数据之外的特定领域知识时可能面临挑战。虽然它们在通用任务上表现良好,但对于专业查询可能会给出不准确的回答。因此,引入外部相关知识源有助于确保回答的准确性。
考虑一个公司手机政策的例子。如果你向一个聊天机器人询问公司的手机政策,聊天机器人需要从其知识库中提供答案,因为公司政策通常包含机密信息,不会公开在互联网上。为了生成这种特定领域的回答,RAG过程就非常有帮助。

RAG如何工作?🔧
了解了RAG的目的后,我们接下来探索其核心工作流程。
RAG基于输入的提示或问题,结合检索到的信息,并生成自然语言来创建回答。它使用的知识库内容多样,包括训练过的聊天机器人数据、未公开在网上的公司政策以及大型文档等。
RAG主要由两个核心组件构成:
- 检索器:RAG的核心,负责从知识库中查找相关信息。
- 生成器:功能类似于聊天机器人,负责生成最终回答。
RAG过程包含以下几个关键步骤:
以下是RAG流程的具体步骤:
- 文本嵌入:输入的提示或问题通过一个“问题编码器”被转换为高维向量。同时,知识库中的文档被“内容编码器”单独转换为高维向量并嵌入。
- 检索:系统将输入提示的向量与知识库中的文本块向量进行匹配,以检索出相似的信息。
- 增强查询创建:系统将检索到的向量所关联的文本与原始提示结合起来,创建一个“增强查询”。
- 模型生成:语言模型利用创建好的增强查询,结合知识库内容,生成最终的回答。
编码器将提示和知识库转换为代表信息的嵌入向量。上下文和问题的嵌入可以由同一个编码器生成。这种方法易于理解,因为它主要涉及将文本转换为嵌入向量,尽管其效果可能并非最优。
提示编码与知识库构建 📝
现在,让我们深入了解提示和知识库是如何被编码成向量的。
提示编码
输入的提示使用词元嵌入和向量平均进行编码,并转换为向量表示。
在词元嵌入中,提示中的每个词元(如单词或子词)都使用一个预训练的模型(例如BERT或GPT)转换为高维向量。公式可以简化为:
Token_Embedding = Model(token)
当所有词元都被嵌入后,系统计算所有词元向量的平均值,为整个提示创建一个单一的向量表示。这意味着平均后的向量表示以一种简洁的方式捕获了输入提示的含义。
Prompt_Vector = Average(Token_Embedding_1, Token_Embedding_2, ..., Token_Embedding_n)
知识库向量化
考虑屏幕上显示的公司手机政策。你可以看到公司政策文档很大,直接将其插入聊天机器人是具有挑战性的。
因此,原始政策文档应被分解成更小、更易管理的文本块,以实现有针对性且高效的检索。
接下来,将每个文本块嵌入为向量,并将其索引到知识库中。
现在,通过使用预训练的词元嵌入模型将文本块转换为高维向量,来编码文本块以获得向量表示。同样,在嵌入所有词元后,系统对每个文本块内的词元向量取平均,为该文本块创建单一的向量表示。
文本块和嵌入向量的组合就代表了知识库,它捕获了每个块的信息。将这些嵌入向量插入一个向量数据库,并用块ID作为键来表示知识库。对这些嵌入向量进行的距离操作使用块ID来查找相关信息。
检索与匹配 🔍
RAG过程的下一步是从知识库中搜索与输入提示相关的上下文。
为此,系统将提示向量与知识库中代表文本块的向量进行比较。
让我们问一个关于公司手机政策的问题。知识库和问题都被转换为向量表示。
此外,系统使用距离度量计算提示向量与每个上下文向量之间的距离,以识别它们之间的相似性。
接下来,它选择3到5个与提示向量最接*的上下文向量,使用距离度量来呈现更相关的信息,以增强输入。
让我们将查询嵌入记为 Q,将知识库中的嵌入记为 C1 和 C2。
所选的距离度量会影响检索结果:
- 如果使用点积,它考虑向量的方向和大小,通过优先考虑整体对齐度,你可能会发现知识库嵌入 C2 在数值上更接*上下文向量。
- 现在考虑余弦相似度,它专注于方向以测量角度差异,因此知识库嵌入 C2 是一个更好的选择。
这意味着,对于重视向量大小的场景,点积更可取;对于重视向量方向的场景,余弦距离更可取。
为了选择最相关的前K个上下文(其中K是一个超参数),假设公司手机政策有7个块,我们选择块ID 6、2和0。真实数据集使用块库来加速此过程。这意味着你应该选择与查询相似且与公司手机或一般公司政策相关的文本块ID。
最后,从知识库中选出的文本与查询一起被输入聊天机器人,以生成合适的回答。

这意味着在RAG的帮助下,聊天机器人可以提供高效的响应。
总结 📚
本节课中,我们一起学*了如何利用RAG在模型未经预训练的情况下生成回答。

聊天机器人根据问题生成回答。然而,为特定领域(如公司手机政策)生成回答具有挑战性。为了生成关于公司手机政策的回答,输入的提示使用词元嵌入和向量平均进行编码,其中提示被分解成尽可能小的文本块。这些块被嵌入并转换为高维向量,以便使用距离度量搜索相关上下文。从知识库中选择最接*文本块的向量,以生成合适的回答。
生成式人工智能工程:159:RAG编码器与FAISS 🧠
在本节课中,我们将学*检索增强生成(RAG)中的两个核心组件:上下文编码器与Facebook AI相似性搜索(FAISS)。我们将了解它们如何协同工作,将用户查询与海量文档库进行匹配,从而为语言模型提供实时、准确的信息来生成回答。
RAG流程概述 🔄
上一节我们介绍了RAG的基本概念,本节中我们来看看其具体的工作流程。
RAG流程结合了语言模型与实时信息检索的能力。它首先将用户提供的提示和相关文档编码成向量,存储到向量数据库中。然后,系统根据编码后的问题向量与文档向量之间的距离,检索出最相关的上下文向量。最后,生成器将检索到的上下文与原始提示结合,生成最终的回答。


理解上下文编码器 📄
现在,让我们深入了解流程中的第一个关键部分:上下文编码器。
上下文编码器负责将可能包含答案的文档或段落编码成固定维度的向量(嵌入)。这使得系统能够将问题嵌入与这些文档嵌入进行比较,从而找到最佳匹配。


使用上下文分词器
以下是使用上下文分词器的步骤:

- 从Transformers库中导入DPR上下文编码器的分词器。
- 加载与指定模型关联的预训练分词器。
from transformers import DPRContextEncoderTokenizer
tokenizer = DPRContextEncoderTokenizer.from_pretrained('facebook/dpr-ctx_encoder-single-nq-base')
- 准备一个包含句子对的文本列表作为输入。
- 分词器将对输入文本进行分词、填充和截断(最大长度通常为256个标记),并将其转换为PyTorch张量字典。
texts = ["这是第一个句子。", "这是与之配对的第二个句子。"]
token_info = tokenizer(texts, padding=True, truncation=True, max_length=256, return_tensors="pt")
输出的 token_info 包含RAG所需的基本信息:
input_ids:输入文本对应的标记ID或索引。token_type_ids:段落ID,用于区分句子对。attention_mask:注意力掩码,标识哪些是有效标记。
生成RAG嵌入
接下来,我们需要使用上下文编码器模型来生成嵌入向量。
- 从Transformers库中导入DPR上下文编码器类。
- 初始化编码器,加载预训练的DPR上下文编码器模型。
from transformers import DPRContextEncoder
encoder = DPRContextEncoder.from_pretrained('facebook/dpr-ctx_encoder-single-nq-base')
- 将分词后的输入(
token_info)传递给上下文编码器,以获得嵌入向量。
with torch.no_grad():
context_embeddings = encoder(**token_info).pooler_output
pooler_output 的形状为 [批量大小, 768],其中768是由DPR模型定义的每个文本对的嵌入向量维度。
实际应用示例
假设我们加载了一个包含公司政策的文本文件,并将其预处理为单独的段落。将所有段落编码后,我们得到一个形状为 [76, 768] 的上下文嵌入矩阵,其中76代表段落数量,768代表每个段落的嵌入维度。
探索FAISS(Facebook AI相似性搜索) 🔍

在将文档编码成向量后,我们需要一种高效的方法来搜索它们。这就是FAISS的用武之地。
FAISS是由Facebook AI Research开发的一个库,专门用于高效搜索大规模高维向量集合。


本质上,FAISS是一个用于计算问题嵌入与上下文向量数据库之间距离的工具。
使用FAISS建立索引
以下是使用FAISS的步骤:
- 导入FAISS库。
- 将上下文嵌入转换为NumPy数组。
- 初始化一个使用L2(欧几里得)距离的FAISS索引对象。
- 将上下文嵌入添加到这个索引中,使其可被搜索。

import faiss
import numpy as np
# 将嵌入转换为numpy数组
context_embeddings_np = context_embeddings.cpu().numpy().astype('float32')

# 获取嵌入维度
d = context_embeddings_np.shape[1]
# 创建索引(这里使用简单的L2距离索引)
index = faiss.IndexFlatL2(d)
# 将向量添加到索引中
index.add(context_embeddings_np)
理解问题编码器 ❓

与编码文档的上下文编码器相对应,问题编码器负责将输入的问题编码成固定维度的向量表示,以捕捉其含义和上下文,从而便于寻找答案。


其使用方式与上下文编码器类似:
- 从Transformers库中导入DPR问题编码器及其分词器。
- 加载预训练的问题编码器分词器和模型。
from transformers import DPRQuestionEncoder, DPRQuestionEncoderTokenizer
question_tokenizer = DPRQuestionEncoderTokenizer.from_pretrained('facebook/dpr-question_encoder-single-nq-base')
question_encoder = DPRQuestionEncoder.from_pretrained('facebook/dpr-question_encoder-single-nq-base')
- 对问题进行分词,并通过问题编码器将其转换为密集的向量嵌入。
question = "公司的休假政策是什么?"
question_tokens = question_tokenizer(question, return_tensors="pt")
with torch.no_grad():
question_embedding = question_encoder(**question_tokens).pooler_output
问题编码器生成的嵌入将用于在上下文嵌入的语料库中搜索最相关的文档。

检索与生成答案 🎯
现在,我们已经拥有了所有组件,可以测试整个检索过程并生成答案了。
检索相关上下文
首先,我们使用FAISS索引来查找与问题嵌入最接*的上下文嵌入。
# 将问题嵌入转换为numpy数组以用于FAISS
question_embedding_np = question_embedding.cpu().numpy().astype('float32')
# 搜索最相似的3个上下文向量
k = 3
distances, indices = index.search(question_embedding_np, k)
print(f"距离: {distances}")
print(f"索引: {indices}")
- 距离:显示问题嵌入与最接*的三个上下文嵌入之间的欧几里得距离。数值越小,表示匹配度越高,上下文越相关。
- 索引:提供在FAISS索引中,最接*的上下文嵌入所对应的位置。这些索引可用于检索实际的文档段落。
生成最终回答
最后,我们将检索到的最相关上下文与原始问题一起,输入到一个生成式模型(如BART)中来产生最终答案。
- 导入BART模型和分词器。
- 加载预训练的BART模型。
from transformers import BartForConditionalGeneration, BartTokenizer
bart_tokenizer = BartTokenizer.from_pretrained('facebook/bart-large')
bart_model = BartForConditionalGeneration.from_pretrained('facebook/bart-large')
- 构建包含问题和检索上下文的输入文本。
- 使用BART模型生成回答。
# 假设 top_contexts 是检索到的最相关段落列表
input_text = f"问题: {question} 上下文: {' '.join(top_contexts)}"
# 对输入进行分词
inputs = bart_tokenizer(input_text, max_length=1024, truncation=True, return_tensors="pt")
# 生成回答
summary_ids = bart_model.generate(
inputs["input_ids"],
max_length=150,
min_length=40,
length_penalty=2.0,
num_beams=4,
early_stopping=True
)
# 解码生成的标记为文本
answer = bart_tokenizer.decode(summary_ids[0], skip_special_tokens=True)
print(f"生成的回答: {answer}")
通过这种方式,即使聊天机器人模型没有专门针对公司政策进行训练,它也能利用RAG检索到的实时、相关信息,生成准确且相关的回答。


总结 📝
本节课中我们一起学*了RAG流程的核心组成部分:
- RAG流程:涉及将提示编码为向量、存储并检索相关向量以生成响应。
- DPR上下文编码器:负责将潜在的回答段落或文档编码成向量嵌入。
- FAISS:一个用于高效搜索大规模高维向量库的工具,用于快速找到与问题最相关的上下文。
- 问题编码器:负责将输入问题编码成固定维度的向量表示。
- 答案生成:我们学*了如何结合检索到的上下文,使用如BART这样的生成模型来生成最终答案,而无需对模型进行额外的训练。
通过结合这些技术,我们可以构建出能够动态访问最新信息并生成高质量回答的智能系统。


生成式人工智能工程:160:LangChain简介

在本节课中,我们将学*LangChain的基本概念,包括其定义、重要性、核心组件以及典型应用场景。
什么是LangChain?🤔
LangChain是一个开源框架,旨在帮助开发者利用大型语言模型(LLMs)来构建应用程序。
LangChain为LLMs提供了一个通用接口,并创建了一个环境,用于将应用程序与外部数据集和工作流集成。此外,LangChain提供了工具和思路,用于定制生成的模型、提高其准确性并提供相关信息。例如,开发者可以利用LangChain组件创建新的提示链,或使用现有的提示模板,让LLMs能够基于不同的变量生成响应。
为什么LangChain很重要?💡

LangChain简化了如GPT-4等语言模型的集成过程,使开发者能够更便捷地构建自然语言处理(NLP)应用程序。它为开发者提供了无缝访问先进人工智能和机器学*技术的能力。
LangChain帮助开发者将AI功能无缝地实施到他们的项目中,从而解锁新的可能性并增强软件功能。同时,LangChain为开发者提供了无与伦比的灵活性和自由度,允许他们自由探索代码库、进行定制,并根据业务需求开发商业产品。
LangChain的核心组件 🧩

现在,我们来了解一下LangChain的组成部分。LangChain框架由多个开源库构成。
以下是其主要组件:
- LangChain:这是核心部分,包含链(Chains)、代理(Agents) 和检索策略(Retrieval Strategies)。这些组件共同构成了应用程序的认知架构基础。
- LangChain Core:这是LangChain的表达式语言,也是所有抽象概念的基础。
- LangChain Community:这是第三方集成库。它包含合作伙伴包,例如
langchain-ibm、langchain-openai和langchain-anthropic。它还将多个集成拆分到依赖于LangChain Core的轻量级包中。

LangChain的应用场景 🌐
接下来,我们看看LangChain的用例。LangChain提供了多种应用,鼓励开发者在不同领域创建无缝的AI解决方案。
以下是LangChain的一些主要应用方向:
- 构建聊天机器人和AI代理:增强用户交互体验。
- 简化API集成:与各种服务无缝连接。
- 数据洞察与分析:通过理解表格数据中的代码和查询,提取有价值的见解。
- 文档处理与内容管理:生成基于文档的问答、总结文本,并增强信息检索和内容管理能力。
- 广泛的NLP任务:作为一个综合性工具,扩展平台能力以提取和评估数据。
总结 📝

本节课中,我们一起学*了LangChain的概述。LangChain是一个开源框架,用于帮助开发者利用LLMs构建AI应用程序。它简化了如GPT-4等语言模型的集成,使开发者能够更便捷地构建NLP应用。LangChain的组件包括LangChain本身、LangChain Core和LangChain Community。LangChain可应用于多个领域,如内容管理、文本摘要、构建AI聊天机器人和代理、理解代码和查询,以及与API集成。
生成式人工智能工程:161:上下文学*简介 🧠
在本节课中,我们将要学*上下文学*的基本概念,并了解提示工程的基础知识。上下文学*是一种无需额外训练即可让大语言模型适应新任务的方法。
什么是上下文学*?
上一节我们介绍了课程主题,本节中我们来看看上下文学*的定义。
上下文学*是提示工程的一种特定方法。在这种方法中,任务的演示会以自然语言的形式,作为提示的一部分提供给模型。然而,上下文学*不需要额外的训练。模型在推理时,会从上下文或提示中提供的一小部分示例中学*新任务。
上下文学*的优缺点
了解了定义后,接下来我们分析其优势与局限。
上下文学*不需要针对特定数据集对模型进行微调。这可以极大地减少使大语言模型适应特定任务所需的资源和时间,同时提升其性能。
尽管上下文学*效率很高,但它受限于在上下文中实际能提供的内容。复杂的任务可能需要梯度步骤或更传统的机器学*训练方法,这些方法涉及根据误差梯度调整模型的权重。
提示工程基础

在探讨了上下文学*之后,现在让我们深入了解提示工程。首先从理解什么是提示及其在与AI系统交互中的作用开始。
本质上,提示是给予大语言模型的指令或输入,旨在引导其执行特定任务或生成期望的输出。一个提示包含两个主要组成部分:
以下是提示的两个核心组成部分:
- 指令:清晰、直接的命令,告诉AI要做什么。指令需要具体,以确保大语言模型理解任务。
- 上下文:帮助大语言模型理解指令的必要信息或背景。它可以是数据、参数或任何相关的细节,用以塑造AI的回应。

通过有效结合这些元素,你可以定制像IBM、OpenAI、Google或Meta开发的大语言模型,来执行从回答查询、分析数据到生成内容的各种任务。

提示工程的定义与重要性
现在你已经熟悉了提示的基本构成,让我们深入探讨为什么提示工程对于增强AI能力至关重要。
提示工程是一个专业化的过程,在这个过程中,你设计和优化用于与AI系统(特别是大语言模型)交互的问题、命令或陈述。目标不仅仅是提出问题,而是以最佳方式提出问题。这涉及精心设计清晰、上下文丰富的提示,以从AI获得最相关和最准确的响应。

这个过程在从客户服务自动化到高级研究和计算语言学等多个领域都至关重要。
提示工程通过直接影响大语言模型运行的有效性和准确性来提升其效能。它通过使大语言模型能够生成精确且完全符合上下文的响应来确保相关性。它通过更清晰的提示和减少误解来促进满足用户期望。它消除了持续微调的需要,允许模型在其上下文中进行适应和学*。
提示构成要素解析
为了更具体地理解,让我们分解一个结构良好的提示所包含的要素。
以下是一个提示的四个关键元素:
- 指令:告诉大语言模型需要做什么。例如:
将以下客户评论分类为中立、负面或正面情感。这直接引导大语言模型的行为。 - 上下文:帮助大语言模型理解其运作的场景或背景。例如,指明该评论是针对新推出产品的反馈,这可以帮助大语言模型结合产品的新颖性来衡量情感分析。
- 输入数据:提示中需要大语言模型处理的实际数据。例如客户评论:
“产品到货晚了,但质量超出了我的预期。”大语言模型使用这些数据来执行指令指定的任务。 - 输出指示符:提示中期望大语言模型给出回应的部分。它是一个清晰的标记,告诉AI在哪里交付其分析。在本例中,
情感:表示等待大语言模型附加其分类结果。

课程总结
本节课中我们一起学*了上下文学*和提示工程的核心内容。

- 上下文学*是一种提示工程方法,其中任务演示作为提示的一部分提供给模型。
- 提示是给予大语言模型的输入,用于引导其执行特定任务,由指令和上下文构成。
- 提示工程是设计和优化提示以获得AI相关且准确响应的过程。
- 提示工程有几个优点:提升大语言模型的有效性和准确性;确保响应相关性;促进满足用户期望;消除持续微调的需要。
- 一个提示包含四个关键要素:指令、上下文、输入数据和输出指示符。
生成式人工智能工程:5:提示工程的高级方法 🚀
在本节课中,我们将学*提示工程的高级方法。通过学*零样本提示、少样本提示、思维链提示和自洽性等技术,你将能够设计更有效的提示,并了解如何利用相关工具在实际场景中应用这些方法。
零样本提示
上一节我们介绍了课程概述,本节中我们来看看第一种高级方法:零样本提示。这种提示方法要求大型语言模型(LLM)在没有接受过任何针对该任务的具体训练或示例的情况下执行任务。
示例:
- 提示:
The Eiffel Tower is located in Berlin.(埃菲尔铁塔位于柏林。) - 任务:请判断该陈述是真是假。
- 模型输出:
False(假)
这个任务要求LLM在不依赖任何先前针对特定查询的微调的情况下,理解上下文和信息。
少样本提示
理解了无需示例的零样本提示后,我们来看看提供示例的提示方法。少样本提示分为两种:单样本提示和少样本提示。
单样本提示
单样本提示为LLM提供一个示例,以帮助其执行类似任务。
示例:
- 示例:
How is the weather today? -> Quel temps fait-il aujourd'hui?(今天天气怎么样? -> 今天天气怎么样?) - 新提示:
Where is the nearest supermarket?(最*的超市在哪里?) - 预期输出:
Où se trouve le supermarché le plus proche?(最*的超市在哪里?)
AI利用初始示例来正确执行新的翻译任务。
少样本提示
少样本提示让AI在解决类似任务之前,先学*一小部分示例。这有助于AI从少数实例中归纳,以处理新数据。
以下是示例:
- 示例1:
I just got a promotion at work!(我刚刚升职了!) ->Happiness(快乐) - 示例2:
I lost my wallet yesterday.(我昨天丢了钱包。) ->Sadness(悲伤) - 示例3:
The deadline is tomorrow and I'm not ready.(明天就是截止日期,我还没准备好。) ->Anxiety(焦虑) - 新提示:
That movie was so scary. I had to cover my eyes.(那部电影太吓人了。我不得不捂住眼睛。) - 预期输出:
Fear(恐惧)
这些示例教会LLM根据上下文对情绪进行分类。
思维链提示
除了提供示例,我们还可以引导模型进行逐步推理。思维链提示是一种用于引导LLM进行复杂推理、逐步解决问题的方法。这种方法对于需要多个中间步骤或模仿人类思维过程的推理问题非常有效。
示例:
一家商店最初有22个苹果,卖出了15个,然后新到了一批8个苹果。请问现在有多少个苹果?
通过将计算分解为清晰的连续步骤,模型得出了正确答案,并提供了透明的解释。
自洽性
为了进一步提高输出的可靠性,我们可以采用自洽性技术。这种方法通过生成对同一问题的多个独立答案,然后评估这些答案以确定最一致的结果,从而增强输出的可靠性和准确性。
示例:
问题:When I was 6, my sister was half my age. Now I am 70. What age is my sister?(当我6岁时,我妹妹的年龄是我的一半。现在我70岁。我妹妹多大?)
模型被提示进行三种独立的计算和解释,以确保准确性。通过交叉验证通往同一答案的多种路径,这种方法可以验证LLM响应的可靠性。
提示工程工具与应用
掌握了核心方法后,我们来看看支持这些方法的工具和应用。某些工具可以促进与LLM的交互,例如OpenAI的Playground、LangChain、Hugging Face的模型中心和IBM的AI Classroom。
这些工具的主要功能包括:
- 允许你开发、实验、评估和部署提示。
- 支持实时调整和测试提示,并立即看到对输出的影响。
- 提供适用于不同任务和语言的各种预训练模型。
- 促进团队或社区之间共享和协作编辑提示。
- 提供工具来跟踪更改、分析结果并根据性能指标优化提示。
LangChain 与提示模板
在众多工具中,让我们进一步了解LangChain。LangChain使用提示模板,这是为LLM生成有效提示的预定义“配方”。
这些模板通常包含:
- 给语言模型的指令。
- 帮助模型理解上下文和预期响应的少量示例。
- 向语言模型提出的具体问题。
以下是一个应用LangChain提示模板的代码片段:
# 首先,从langchain_core.prompts导入PromptTemplate
from langchain_core.prompts import PromptTemplate

# 定义一个笑话提示模板
joke_template = PromptTemplate.from_template("Tell me a {adjective} joke about {content}.")
# 使用模板,为占位符填入具体值
prompt = joke_template.format(adjective="funny", content="chickens")
# 这将生成提示:"Tell me a funny joke about chickens."
这种方法简化了提示创建过程,使提示在不同上下文中保持一致且易于适配。

智能体应用
在提示工程的应用中,智能体是一个核心概念。它由LLM驱动,并集成LangChain等工具,能够使用不同的提示跨各种领域执行复杂任务。
变革性的应用包括:
- 带来源的问答智能体:提供答案并引用信息来源。
- 内容创作与总结智能体:用于生成和总结内容。
- 数据分析与商业智能智能体:用于分析数据和提供商业洞察。
- 多语言智能体:用于无缝的上下文感知翻译和沟通。
总结
本节课中我们一起学*了提示工程的高级方法。主要内容包括:
- 高级提示方法:包括零样本提示、少样本提示、思维链提示和自洽性。
- 工具:如LangChain,它使用提示模板来生成有效的提示。
- 应用:智能体可以利用不同的提示跨领域执行复杂任务。

通过掌握这些方法和工具,你将能够更有效地设计与大型语言模型交互的提示,从而解锁生成式AI在各类场景中的应用潜力。
生成式人工智能工程:6:LangChain核心概念 🧠
在本节课中,我们将要学*LangChain的核心概念。LangChain是一个开源框架,旨在简化使用大型语言模型构建应用程序的过程。我们将逐一介绍其关键组件,包括语言模型、聊天模型、聊天消息、提示模板和输出解析器。
什么是LangChain?
LangChain是一个开源接口,它通过提供结构化的方式,简化了使用大型语言模型的应用开发流程。它便于将语言模型集成到各种用例中,包括自然语言处理和数据检索。
LangChain由多个组件构成,主要包括:文档、链、代理、语言模型、聊天模型、聊天消息、提示模板和输出解析器。本节视频将重点回顾语言模型、聊天模型、聊天消息、提示模板和输出解析器这几个核心部分。
语言模型


语言模型是LangChain中LLMs的基础。它接收文本输入并生成文本输出,可用于完成任务和总结文档等。


LangChain支持多种语言模型,例如来自IBM、OpenAI、Google和Meta的模型。例如,要使用语言模型为新的销售方案生成回复,我们可以使用IBM的Watsonx.ai平台。
以下是一个使用基于Mistral 8x7B Instruct模型创建LLM的代码示例:
# 确保已导入必要的依赖项,例如来自IBM Watson Machine Learning包的genai和model_inference
from ibm_watson_machine_learning.foundation_models import Model

# 通过调整tokens和temperature等参数来自定义模型
model = Model(
model_id="mistralai/mistral-8x7b-instruct-v0.1",
params={
"max_new_tokens": 100,
"temperature": 0.7
}
)
# 使用模型为插入的提示生成响应
prompt = "提出一个新的销售方案。"
response = model.generate_text(prompt)
print(response)
创建模型对象后,模型会为插入的提示生成响应文本,你可以查看生成的示例回复。
聊天模型
上一节我们介绍了基础的语言模型,本节中我们来看看专为高效对话设计的聊天模型。聊天模型能够理解问题或提示,并像人类一样进行回应。
首先,我们需要使用Watsonx.ai创建一个语言模型,然后使用WatsonxLLM函数将其转换为聊天模型。这会将模型转换为能够进行对话的会话式LLM。
例如,要查看响应,可以向模型插入一个问题,比如“人类最好的朋友是谁?”。你可以查看针对该问题生成的示例回复。

from ibm_watson_machine_learning.foundation_models import Model
from langchain_ibm import WatsonxLLM

# 创建基础语言模型
base_model = Model(model_id="...", params={...})
# 转换为LangChain可用的聊天模型
chat_model = WatsonxLLM(model=base_model)
# 插入问题
question = "人类最好的朋友是谁?"
response = chat_model.invoke(question)
print(response)
聊天消息
为了使模型在动态聊天环境中有效工作,聊天模型需要处理各种类型的聊天消息。
以下是主要的聊天消息类型:
- HumanMessage:代表用户的输入。
- AIMessage:由模型生成的回复。
- SystemMessage:用于向模型提供指令。
- FunctionMessage:用于传递函数调用的结果,包含名称参数。
- ToolMessage:用于工具交互以实现特定结果。
每条聊天消息都包含两个关键属性:role(发言者)和content(发言内容)。
让我们看一个系统生成消息的例子。在这个例子中,模型被指令“扮演一个AI机器人,用一个短句回答问题:吃什么?”。
为了响应这个问题,聊天模型会创建一个消息列表。首先,使用SystemMessage将模型配置为一个健身活动机器人;然后,使用HumanMessage和AIMessage模拟过去的对话。接下来,模型基于之前的对话生成响应。
你也可以仅使用HumanMessage作为输入来操作聊天模型,并允许模型在没有SystemMessage或AIMessage提示的情况下生成响应。这意味着聊天机器人直接响应用户的输入。
from langchain.schema import HumanMessage, SystemMessage, AIMessage
# 配置系统指令
system_message = SystemMessage(content="你是一个健身活动机器人,请用简短句子回答。")
# 模拟历史对话
history = [
HumanMessage(content="我今天应该做什么运动?"),
AIMessage(content="建议进行30分钟慢跑。")
]
# 当前用户输入
current_input = HumanMessage(content="那明天呢?")
# 组合所有消息并调用模型
messages = [system_message] + history + [current_input]
response = chat_model.invoke(messages)
print(response)
提示模板
接下来,我们探讨LangChain中用于格式化输入的提示模板。提示模板将用户的问题或消息转化为清晰的指令,语言模型利用这些指令生成恰当且连贯的响应。

提示模板主要有以下几种类型:
- 字符串提示模板:适用于简单的字符串格式化。
- 聊天提示模板:适用于消息列表。
- 特定消息模板:如
AIMessagePromptTemplate、SystemMessagePromptTemplate、HumanMessagePromptTemplate,允许灵活的角色分配。 - 消息占位符:提供对消息渲染的完全控制。
- 少样本提示模板:为LLMs提供具体的示例(样本),以指导其输出。
让我们使用聊天提示模板来生成响应。在这个提示模板中,你需要指定消息的角色和内容。在内容中,可以包含参数占位符以便重复使用,从而基于输入参数生成动态灵活的消息。
from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate
from langchain.schema import HumanMessage
# 创建带占位符的提示模板
template = ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的助手。"),
("human", "请用{language}总结以下文本:{text}")
])
# 格式化提示
formatted_prompt = template.format_messages(
language="中文",
text="这里是需要总结的长篇内容..."
)
# 调用模型
response = chat_model.invoke(formatted_prompt)
print(response)
示例选择器
在提示模板中,从示例库中选择最相关的示例放入提示中非常重要。提示模板中的示例选择器使这一过程更加高效。
例如,少样本提示模板为LLM提供具体的示例。这些示例告知模型插入的上下文,并指导LLM生成期望的输出。
使用LangChain的示例选择器,你可以通过以下方式优化少样本提示模板:
- 语义相似度:选择与输入最语义相似的示例。
- 最大边际相关性:在相似性和多样性之间取得平衡。
- NGram重叠:基于文本重叠度选择示例。



以下屏幕显示了使用NGram重叠示例选择器来选择示例以形成少样本提示的过程。

输出解析器
最后,我们来了解LangChain中用于结构化输出的组件——输出解析器。输出解析器将LLM的输出转换为更合适的格式,以便生成结构化数据。

LangChain提供了一个输出解析器库,支持多种数据格式,包括JSON、XML、CSV和Pandas DataFrames。输出解析器允许你定制模型的输出,以满足特定的数据处理需求。
例如,让我们使用逗号分隔列表输出解析器将LLM的响应转换为CSV格式。这种输出解析器能有效地构建输出结构,并简化其在电子表格应用程序中的处理和分析。

from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain.prompts import PromptTemplate
# 创建输出解析器
output_parser = CommaSeparatedListOutputParser()
# 获取格式指令,用于告诉模型如何格式化输出
format_instructions = output_parser.get_format_instructions()

# 创建提示模板,包含格式指令
prompt = PromptTemplate(
template="列出三种{subject}。\n{format_instructions}",
input_variables=["subject"],
partial_variables={"format_instructions": format_instructions}
)
# 格式化提示并调用模型
model_input = prompt.format(subject="水果")
model_output = chat_model.invoke(model_input)
# 解析输出
parsed_list = output_parser.parse(model_output)
print(parsed_list) # 输出例如:['苹果', '香蕉', '橙子']
总结
本节课中我们一起学*了LangChain的核心组件。LangChain是一个简化使用LLMs进行应用开发的开源接口。
其核心组件包括:
- 语言模型:使用文本输入生成文本输出。
- 聊天模型:理解问题或提示并像人类一样回应,能处理各种聊天消息(如HumanMessage, AIMessage, SystemMessage等)。
- 提示模板:将问题或消息转化为清晰的指令,并可通过示例选择器优化上下文提供。
- 输出解析器:将LLM的输出转换为如JSON、CSV等合适的结构化格式。

掌握这些核心概念是使用LangChain构建强大生成式AI应用的基础。
生成式人工智能工程:164:构建RAG应用的LangChain文档 📄


在本节课中,我们将学*如何使用LangChain工具来构建检索增强生成(RAG)应用。我们将重点探讨LangChain文档处理流程中的核心组件,包括文档对象、文档加载器、文本分割器、向量数据库和检索器。
概述

构建基于大型语言模型(LLM)的应用,尤其是RAG应用,需要整合用户特定的外部数据。LangChain通过提供易于使用的接口,简化了集成数据、API和预训练语言模型的过程。本节课将详细介绍LangChain中用于处理文档以支持RAG应用的关键工具和步骤。
LangChain文档处理流程

RAG应用的核心在于检索步骤,它确保能够获取足够的相关数据来增强生成过程。数据获取流程包含多个步骤,让我们逐一了解。
文档对象
在LangChain中,文档对象是承载数据信息的容器。它包含两个关键属性:
page_content:存储文档的文本内容,其类型限定为字符串。metadata:存储与文档关联的任意元数据,例如文档ID、文件名或其他相关信息。
这些属性使得文档对象能够在LangChain的RAG应用中有效地管理和使用文档数据。
文档加载器
将文档加载到系统中是一个至关重要的过程。LangChain支持从超过100种数据源加载文档,包括Airbyte、Unstructured等主要提供商。它能处理多种文档类型,例如HTML、PDF和代码,并可以从各种位置加载,如S3存储桶和公共网站。

例如,基于网络的加载器可以直接从网站或URL导入文本内容。


文本分割器
接下来是文档转换步骤,即将数据分割成块。通过将大型文档分割成可管理的小块,LangChain能够从文档中检索出相关的独立片段。LangChain提供了多种定制的文本分割器。


以下是两种常见的分割器示例:
- 递归字符文本分割器:递归地分割文本。
- Markdown标题文本分割器:利用Markdown标题来划分文本。
我们也可以使用字符文本分割器,并指定用户自定义的分隔符,来查看内容被分割成了多少块。
文档嵌入

嵌入用于捕捉文本的语义含义。例如,我们可以使用来自Watsonx.ai的嵌入模型为文档创建嵌入向量。这些向量将文本转换为数值形式,便于后续的相似性比较。
向量数据库存储

随着嵌入的重要性日益增加,高效存储这些数据的需求也在增长。向量数据库专门用于存储嵌入向量并执行高效的相似性搜索。

例如,我们可以使用Chroma向量数据库来存储嵌入,并执行相似性搜索,为输入的查询检索相关内容。通过计算距离并找到最接*的文本块嵌入,可以查看LLM根据查询检索到的内容。
检索器
数据存储在向量数据库后,高效地检索数据至关重要。LangChain支持多种检索算法,旨在提升搜索效果。
LangChain提供了多种检索器,例如:
- 向量存储检索器:用于执行相似性搜索。
- 父文档检索器:用于在文档块内部进行搜索。
- 自查询检索器:用于解析查询并分离出过滤条件。
让我们使用向量存储检索器为文档识别LangChain的相关信息。这是一个响应示例。可以看到,它显示了与在向量数据库中进行相似性搜索策略相似的结果,因为向量存储检索器正是基于相似性原理工作的。

总结
本节课中,我们一起学*了LangChain为RAG应用提供的全面工具,这些工具专注于检索步骤以确保充分的数据获取。我们了解到:
- LangChain中的文档对象是数据信息的容器,包含
page_content和metadata两个关键属性。 - LangChain文档加载器能够处理来自多种位置的HTML、PDF和代码等多种文档类型。
- LangChain通过将文档分割成可管理的片段,来检索相关的独立部分。
- 嵌入模型用于为文档创建语义向量表示。
- LangChain提供了多种检索器,如向量存储检索器,以高效地从存储的数据中查找信息。

掌握这些组件是构建高效、准确的RAG应用程序的基础。
生成式人工智能工程:165:构建应用的LangChain链和代理 🧠


在本节课中,我们将要学*LangChain框架中的两个核心概念:链和代理。我们将了解链如何通过一系列顺序调用处理信息,以及代理如何利用语言模型和外部工具来动态执行任务。掌握这些概念是构建智能、交互式应用的基础。



链:构建顺序处理流程 🔗
上一节我们介绍了LangChain平台,本节中我们来看看其核心组件之一——链。在LangChain中,链是一系列顺序调用的组合。一个顺序链由多个基本步骤构成,其中每一步接收一个输入并生成一个输出,从而创建一个无缝的信息流。第一步的输出会成为第二步的输入。
让我们通过一个具体例子来理解如何创建一个包含三个独立链的顺序链。这个链的目标是:根据用户输入的地点,找出该地的著名菜肴、提供食谱并估算烹饪时间。
以下是创建这个顺序链的步骤:
-
链1:确定地点对应的著名菜肴
此链接收用户指定的地点(例如“中国”)作为输入,并输出该地的著名菜肴(例如“北京烤鸭”)。# 定义提示词模板 template_1 = “请列出{location}的一道著名菜肴。” prompt_1 = PromptTemplate(input_variables=[“location”], template=template_1) # 创建LLM链 location_chain = LLMChain(llm=llm_model, prompt=prompt_1, output_key=“meal”) -
链2:获取菜肴的食谱
此链接收链1输出的菜肴名称作为输入,并输出该菜肴的简单食谱。# 定义提示词模板 template_2 = “请提供{meal}的简易食谱。” prompt_2 = PromptTemplate(input_variables=[“meal”], template=template_2) # 创建LLM链 meal_chain = LLMChain(llm=llm_model, prompt=prompt_2, output_key=“recipe”) -
链3:估算食谱的烹饪时间
此链接收链2输出的食谱作为输入,并估算出所需的烹饪时间。# 定义提示词模板 template_3 = “根据以下食谱,估算烹饪时间:{recipe}” prompt_3 = PromptTemplate(input_variables=[“recipe”], template=template_3) # 创建LLM链 time_chain = LLMChain(llm=llm_model, prompt=prompt_3, output_key=“time”)

最后,使用SequentialChain将这三个独立的链组合成一个统一的流程。通过调用这个组合链并设置verbose=True,可以清晰地追踪信息从开始到结束的流转过程。

内存:在链中保存对话历史 💾

理解了链的基本流程后,我们来看看如何让应用记住之前的对话。在LangChain应用中,内存存储对于读写历史数据至关重要。每个链可以依赖特定的输入,例如用户输入和内存内容。
链在执行其核心逻辑之前,会从内存中读取信息以增强用户输入;在执行之后,会将当前运行的输入和输出写回内存。这确保了跨交互的连续性和上下文保存。

ChatMessageHistory类就是用来有效管理和存储对话历史的,包括人类消息和AI消息。它允许向历史记录中添加消息。

from langchain.memory import ChatMessageHistory


history = ChatMessageHistory()
# 添加AI消息
history.add_ai_message(“你好!我是AI助手。”)
# 添加用户消息
history.add_user_message(“法国的首都是什么?”)
# 后续的链可以基于存储的内存(history.messages)生成响应
代理:动态决策与执行系统 🤖

除了预定义的顺序链,LangChain还提供了更灵活的代理系统。代理是动态系统,其中语言模型负责决定和排序要执行的动作(例如调用预定义的链或工具)。模型生成文本来指导动作,但不直接执行它们。
代理的关键在于它们能与外部工具(如搜索引擎、数据库和网站)集成,以完成用户请求。例如,如果用户询问“意大利的人口”,代理会使用语言模型来规划步骤:可能先调用搜索工具查找最新数据,然后整理并返回答案。这展示了代理自主利用LLM推理能力与外部工具结合的能力。


让我们看一个创建Pandas DataFrame代理的例子,它允许用户用自然语言查询和可视化数据:
from langchain.agents import create_pandas_dataframe_agent
import pandas as pd

# 假设df是一个Pandas DataFrame
df = pd.read_csv(‘data.csv’)
# 创建代理
agent = create_pandas_dataframe_agent(llm=chat_model, df=df, verbose=True)
# 执行查询
response = agent.invoke(“数据框中有多少行?”)
print(response[‘output’]) # 例如:“数据框中有139行。”
在这个例子中,LLM将自然语言查询转化为在后台执行的Python代码(如len(df)),从而提供精确的答案。
总结 📝
本节课中我们一起学*了LangChain框架中构建应用的两个核心模块:
- 链:作为一系列顺序调用,它将复杂任务分解为多个步骤,每一步的输出作为下一步的输入,形成流畅的信息处理管道。创建链通常涉及定义提示模板、构建LLM链对象,最后将它们组合成顺序链。
- 内存:通过
ChatMessageHistory等类实现,使应用能够保存和利用对话历史,保持交互的连续性。 - 代理:这是一种更高级的动态系统,它利用语言模型来决策动作序列,并通过集成外部工具(如搜索引擎、数据库)来执行任务,从而灵活地响应用户的复杂请求。

掌握链和代理的使用,是开发能够理解上下文、执行多步骤任务并与外部世界交互的智能应用程序的关键。
生成式人工智能工程:166:课程介绍 🚀

在本节课中,我们将要学*一门关于使用RAG和LangChain构建生成式AI应用的课程。本课程旨在通过一个顶点项目,让你应用新学到的知识和技能,从而提升你的就业竞争力,并加速你在AI领域的职业发展。
课程概述 📖

本课程适合对AI工程感兴趣的学*者,内容包括大型语言模型的训练、开发、微调和部署。现有的和有抱负的数据科学家、机器学*工程师都将从本课程中受益匪浅。
学*本课程需要具备Python基础知识。如果对LLM、LangChain和RAG有所了解,将是额外的优势。
你将学到什么 🎯
完成本课程后,你将能够掌握以下核心技能:
以下是本课程的核心学*目标列表:
- 使用LangChain从多种来源(如PDF、CSV、URL和文本)加载文档。
- 应用RAG和LangChain的文本分割技术,以提升模型的响应能力。
- 创建和配置向量数据库来存储文档嵌入。
- 开发一个检索器,根据查询获取相关的文档片段。
- 设置一个简单的Radio接口与模型进行交互。
- 使用LangChain和LLM构建一个问答机器人,来回答从已加载文档中提取的问题。
课程内容与结构 🗺️
上一节我们介绍了课程的整体目标,本节中我们来看看课程的具体内容和结构安排。

第一阶段:文档加载与处理
你将首先学*使用LangChain从不同来源(如PDF、CSV、URL和文本)加载文档。此外,你将了解到将整个文档内容输入提示词时,在信息检索方面的局限性。
你将在动手实验练*中使用Jupyter Lab环境来实践这些概念和技术。

第二阶段:RAG与检索增强
接下来,你将学*与使用LangChain实现RAG相关的概念。
以下是本阶段的核心学*内容:
- 学*用于优化处理的文本分割策略,并应用它们来增强模型的响应能力。
- 学*如何使用Watson X嵌入模型为文档生成嵌入。
- 熟悉如何创建和配置向量数据库来存储这些嵌入。
- 学*如何开发一个检索器,根据查询获取文档片段。
- 探索实现高级检索器的技术。

你将再次在动手实验中练*这些概念和工具。
第三阶段:集成与构建应用
然后,你将学*如何将前端界面与LLM应用集成,并设置一个Symbol Radio接口来与LLM模型交互。
最后,你将构建一个QA机器人来回答从已加载文档中提取的问题。在此阶段,你也能够通过动手实验来练*这些技术。
最终,你将通过参与一个基于真实场景的指导项目,完成获得工作所需技能的“最后一公里”。
学*资源与建议 💡
本课程提供了丰富多样的学*机会来辅助你的学*之旅。教学视频配有信息丰富的阅读材料,以促进扎实的概念学*。以动手实验形式提供的技术实践环境,允许你应用对关键工具和技术的概念性知识。最后,测验和项目将帮助你评估自己的学*成果。

为了从本课程中获得最大收益,请观看每一段视频,阅读每一份材料,完成所有动手实验和活动,并尝试所有测验。
总结与开始 🏁
本节课中我们一起学*了这门关于RAG和LangChain的生成式AI应用课程的目标、内容、结构以及学*建议。本课程涵盖了从文档处理、检索增强到应用构建的完整流程。

加入我们,一起探索这个不断增长且充满活力的、结合了RAG和LangChain的AI应用领域。如果你对课程材料有任何疑问,请随时在讨论区联系我们。让我们开始吧。祝你一切顺利!
生成式人工智能工程:167:从不同来源加载文档 📄
在本节课中,我们将学*如何使用 LangChain 的文档加载器(Document Loaders)从多种来源高效地导入数据。这是构建检索增强生成(RAG)应用的第一步,也是处理和分析信息的基础。
LangChain 通过文档加载器从网站、文件和数据库等多种来源收集信息,并将其转换为 LangChain 可以处理的格式。文档加载器充当连接器,拉取数据并进行格式转换。收集必要信息是创建 RAG 应用的第一步,之后才能开始处理数据,以根据用户查询找到相关答案。这个过程帮助你的应用高效地访问和使用广泛的信息。
加载文本文件 📝
如果你正在处理纯文本文件,可以利用 LangChain 中的 TextLoader 类来高效加载它们。LangChain 的 load 方法专门设计用于从配置好的源加载数据为文档对象。

以下是使用该方法加载文本数据的代码示例。加载后,每个包含元数据和页面内容属性的文档对象会被存储在一个列表中。

from langchain.document_loaders import TextLoader
loader = TextLoader("example.txt")
documents = loader.load()
这是一个打印文本文件内容的示例,展示了文档对象的结构和访问方式。
加载 PDF 文件 📑
对于 PDF 文件,LangChain 中的 PyPDFLoader 类是你的首选工具。这个类可以将 PDF 加载到一个文档对象数组中,每个文档对象捕获一页的内容,以及元数据和页码。
你可以看到这里加载了一篇学术论文的 PDF 文件。这是第一页的内容。
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("example.pdf")
documents = loader.load()
你也可以使用 PyMuPDFLoader,这是 LangChain 中最快的 PDF 解析工具。
这个加载器能快速处理每一页,并提供关于 PDF 及其页面的详细元数据,每页返回一个文档对象。让我们用这个加载器重新加载上一张幻灯片中的论文。


PyMuPDFLoader 与 PyPDFLoader 的关键区别在于,它包含了更全面的元数据。

加载 Markdown 文件
如果你正在处理 Markdown 文件,LangChain 提供了 UnstructuredMarkdownLoader 类来加载它。

请注意,内容可能包含许多换行符。
加载 JSON 文件
处理 JSON 文件时,需要考虑其独特的键值对结构。让我们看一个例子。

我们将使用以下代码来加载和显示一个 JSON 文件。
# 示例代码:加载JSON文件
这是文件内容。它包含各种字段的结构,你特别希望从 messages 中提取 content 字段,以便在 LangChain 中进行进一步处理。你该怎么做?
LangChain 提供了专门设计用于加载 JSON 文件的 JSONLoader 类。这个加载器利用 jq 模式根据特定需求解析 JSON 文件。
例如,如果你想从 JSON 数据的 message 键下的 content 字段中提取值,你应该为 content 定义一个合适的 jq 模式。设置好后,加载器将提取这些值,并将它们加载到一个文档对象数组中,其中每个文档代表一条消息内容。
加载 CSV 文件 📊
处理 CSV 文件中的数据时,LangChain 中的 CSVLoader 是理想的工具。这个加载器通过将数据的每一行转换为一个单独的文档对象来处理 CSV 文件。
以下是一个示例。



from langchain.document_loaders import CSVLoader

loader = CSVLoader("example.csv")
documents = loader.load()
如果你想将所有数据加载到一个文档对象中,可以使用 UnstructuredCSVLoader。

与将每一行视为一个单独文档(由表头定义数据)的 CSVLoader 不同,UnstructuredCSVLoader 将整个 CSV 文件视为一个单一的非结构化表格元素。

这在将数据作为表格而非单个条目进行分析时非常有用。
从网站加载内容 🌐
如果需要加载和解析在线网页,标准方法是使用一个名为 BeautifulSoup 的 Python 包。
例如,这里有一个你想加载的网站。这是网页 URL 和网页部分内容的截图。
你可以使用此处显示的代码,通过 BeautifulSoup 来加载和解析它。
让我们看看它的表现。这是加载响应部分内容的截图。
你可以看到,BeautifulSoup 加载了网页内容,并包含了一些 H 标签和外部链接,这些内容是不必要的,甚至在你只想加载网页文本内容时会成为负担。

BeautifulSoup 有其局限性。有更好的方法吗?是的。
LangChain 中的 WebBaseLoader 旨在高效地从 HTML 网页中提取所有文本,并将其转换为适合下游处理的文档格式。
让我们再次使用 WebBaseLoader 加载相同的 URL。加载后,可以观察到加载器只捕获了网页的所有文本,避免了 HTML 标签和链接,只包含换行符。
当需要加载多个网站时,你该怎么做?你可以创建一个这些网站的列表,并将其传递给 WebBaseLoader。加载器会将每个网站的所有内容加载到一个文档对象中,并将它们格式化为一个数组。
加载 Word 文档


LangChain 还提供了 Docx2txtLoader 来帮助将 Word (.docx) 文件加载为文档内容,类似于文本加载器。
以下是如何加载 Word 文档的示例。
from langchain.document_loaders import Docx2txtLoader
loader = Docx2txtLoader("example.docx")
documents = loader.load()

使用通用文件加载器
对于因文件格式未知或多样而需要灵活性的项目,LangChain 中的 UnstructuredFileLoader 非常完美。
这个加载器设计用于通用目的,支持多种文件类型,包括文本文件、PowerPoint 演示文稿、HTML 页面、PDF、图像等。
例如,如果你有一个 Markdown 文件和一个文本文件需要加载,你可以只使用这一个加载器。
UnstructuredFileLoader 能高效地将这些不同文件的内容转换为单个文档对象。
总结 📚
本节课中,我们一起学*了 LangChain 如何使用文档加载器作为连接器来收集数据并将其转换为兼容格式。

- 你可以使用 LangChain 中的
TextLoader类来加载纯文本文件。 - 对于 PDF 文件,LangChain 中的
PyPDFLoader类或PyMuPDFLoader是理想选择。 - 对于 Markdown 文件,LangChain 提供了
UnstructuredMarkdownLoader。 - LangChain 提供了
JSONLoader类,专门设计用于加载 JSON 文件。 - 处理 CSV 文件中的数据时,LangChain 中的
CSVLoader是理想的工具。 - 要加载和解析在线网页,可以使用 Python 包
BeautifulSoup或 LangChain 中的WebBaseLoader。 - 要加载多个网站,请使用
WebBaseLoader。 - 最后,你了解到对于文件格式未知或多样的项目,LangChain 中的
UnstructuredFileLoader是一个理想的解决方案。
生成式人工智能工程:168:优化处理的文本分割策略 📄
在本节课中,我们将学* Langchain 如何利用文本分割器来分割、合并、筛选和操作文档,以满足特定需求。我们将探讨几种常用的分割器及其使用方法。
文本分割是处理长文档的关键步骤,目的是将其分割成适合大语言模型上下文窗口的小块。Langchain 提供了多种内置的文档分割器来简化这一过程。
文本分割器的工作原理
首先,文本分割器将文本分割成小的、有语义意义的块,通常是句子。接着,它将这些小块组合成更大的块,以达到特定的大小。一旦达到该大小,分割器就将该块指定为一个独立的文本片段。然后,分割器会创建一个新的块,并与前一个块保持一定的重叠,以确保块之间的上下文连贯性。
从根本上讲,文本分割器沿着两个轴进行操作:
- 如何分割文本:指用于将文本分解成更小块的方法或策略。这可能涉及按特定字符、单词、句子甚至自定义标记进行分割。
- 如何测量块大小:指用于确定一个块何时完成的标准。这可能涉及计算字符数、单词数、标记数或自定义指标。
文本分割器的关键参数
文本分割器中的某些关键参数决定了其工作方式。
以下是这些参数:
- 分隔符:指用于将文本分割成可管理块的字符或字符集。例如,常见的分隔符有换行符、段落分隔符或空格。默认分隔符是按段落。
- 块大小:指每个块可以包含的最大字符数。块是指从较大文本体中分离出来的文本段。默认值为 1000。
- 块重叠:指连续块之间重叠的字符数。默认值为 200。
- 长度函数:长度函数决定了如何计算块的长度。
常用文本分割器
Langchain 提供了几种不同类型的文本分割器。让我们探讨一些常用的分割器及其使用方法。
字符文本分割器
这是最简单的方法。分割基于字符进行,这些字符也称为分隔符。您可以通过字符数来测量块长度。
例如,这里有一段文本。字符分割器可以将文本分割成不同的块,每个块由自定义的分隔符分隔。您甚至可以在块之间设置重叠,以确保信息不会丢失。
以下是展示如何使用字符文本分割器的代码。使用单个字符作为分隔符,块大小设置为 200,重叠为 20。
# 示例代码:使用字符文本分割器
from langchain.text_splitter import CharacterTextSplitter
splitter = CharacterTextSplitter(separator=" ", chunk_size=200, chunk_overlap=20)
chunks = splitter.split_text(your_text)

这意味着当字符数达到 200 时,文本将被分割。分割后,您可以看到文本如何被分成两个块,每个块之间有一些用黄色显示的字符重叠。

递归字符文本分割器
递归字符文本分割器采用递归作为核心机制来完成文本分割。它推荐用于通用文本。它接收一大段文本,并尝试将其分割,直到块足够小。它通过使用一组字符来实现这一点。默认提供的字符是按段落、句子、单词或字符。
例如,这里它接收文本,然后尝试按第一个分隔符(即按段落,由 \n 字符表示)进行分割。您最终得到三个块。接下来,评估每个块以确保它们小于指定的大小,比如 100 个字符。前两个块满足此条件,但第三个块不满足。因此,您继续进行下一级的分割,即按句子分割。这将第三个块进一步分割,得到四个块。之后,您会发现合并前两个块仍然在 100 个字符的限制内,因此可以合并它们。这就得到了最终的块集。
以下是展示如何使用递归字符文本分割器的代码。
# 示例代码:使用递归字符文本分割器
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)
chunks = splitter.split_text(your_text)
代码文本分割器
代码文本分割器使您能够分割代码,并支持多种语言。它提供的支持语言列表如下:
- Python
- C
- C++
- Java
- ...

分割策略基于递归字符文本分割器。

以下是分割 Python 代码的示例。虽然它基于递归字符文本分割器,但当应用于代码分割器时,您需要在调用 RecursiveCharacterTextSplitter 类后添加 from_language,并在其中指定您要分割的语言。
# 示例代码:使用代码文本分割器(以Python为例)
from langchain.text_splitter import RecursiveCharacterTextSplitter, Language

splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON,
chunk_size=200,
chunk_overlap=50
)
chunks = splitter.split_text(your_python_code)
Markdown 标题文本分割器

分块的目标是保持具有共同上下文的文本在一起。考虑到这一点,您可能希望尊重文档结构。Markdown 文件按标题组织,在特定标题组内创建块是一个直观的想法。您可以使用 Markdown 标题文本分割器来应对这一挑战,它将按指定的标题集分割 Markdown 文件。

例如,如果您有这样的 Markdown 原始文本,您可以使用 Markdown 标题文本分割器按任何标题(在本例中是 ## Bar 和 ### BS)来分割内容。
以下是实现按标题分割 Markdown 文件的代码。
# 示例代码:使用Markdown标题文本分割器
from langchain.text_splitter import MarkdownHeaderTextSplitter

headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]
splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
chunks = splitter.split_text(your_markdown_text)

总结
本节课中,我们一起学*了 Langchain 如何使用文本分割器将长文档分割成适合大语言模型上下文窗口的小块。

文本分割器沿着两个轴进行操作:第一个是用于将文本分解成更小块的方法,第二个是如何测量块的大小。文本分割器的关键参数包括分隔符、块大小、块重叠和长度函数。一些常用的分割器包括按字符分割、递归按字符分割、代码分割器和 Markdown 标题文本分割器。
向量数据库入门:169:存储嵌入 🧠
在本节课中,我们将学*如何使用向量数据库存储嵌入向量。你将能够描述存储嵌入向量的方法,解释 Langchain 支持的向量数据库 ChromaDB 的用法,并讨论如何在向量数据库中进行相似性搜索以检索与查询最相关的内容。
从数据到向量存储
上一节我们介绍了如何从各种来源加载、分割数据并生成嵌入向量,为下游任务做准备。在获得数据源的嵌入向量后,下一个关键步骤是存储它们。你可以使用专门为存储嵌入向量设计的向量存储来实现这一目标。
向量数据库的功能不仅仅是存储数据,它还能通过相似性搜索,根据查询检索所需信息。
其工作原理如下:
- 查询首先被转换为嵌入向量。
- 该嵌入向量被输入向量数据库。
- 数据库执行相似性计算,搜索并检索与查询最相关的内容。
为什么需要向量数据库?
向量数据库至关重要,因为嵌入向量将数据(通常是文本等非结构化数据)转换为高维空间中的数值向量格式。像 SQL 这样的传统数据库并非为存储和查询大量向量数据而优化,通常难以有效地存储和搜索这些向量表示。
相比之下,向量存储可以使用复杂的相似性算法对向量进行索引并快速搜索相似向量。这种能力使得应用程序能够基于目标向量查询找到相关向量,从而实现高效的信息检索。
认识 ChromaDB 🗄️
接下来,我们来看看 Langchain 支持的一个向量数据库:ChromaDB。ChromaDB 是一个用于存储和检索向量嵌入的开源向量存储。它的主要用途是保存嵌入向量和元数据,供大型语言模型后续使用。此外,它也是一个强大的工具,可用于构建基于文本数据的语义搜索引擎。
以下是 ChromaDB 的主要特点:
- 开源:可免费使用和修改。
- 存储嵌入与元数据:不仅能存向量,还能关联额外的描述信息。
- 支持语义搜索:擅长理解查询的深层含义,而不仅仅是关键词匹配。
使用代码构建 ChromaDB 数据库
让我们看看如何使用代码实现 ChromaDB。在构建向量数据库之前,假设你已经加载并将目标数据分割成块。此外,最好准备好一个嵌入模型对象。在本示例中,已使用 WatsonX 构建了一个嵌入模型。
使用 Langchain 构建 ChromaDB 向量数据库非常简单。
- 从
langchain.vectorstores导入Chroma类。 - 使用这个
Chroma类,传入数据块和嵌入模型。



ChromaDB 将自动处理其余工作,使过程无缝且高效。

# 示例代码结构
from langchain.vectorstores import Chroma
from your_embedding_module import YourEmbeddingModel
# 假设已有数据块 documents 和嵌入模型 embedding_model
vectorstore = Chroma.from_documents(documents=documents, embedding=embedding_model)
理解相似性搜索过程 🔍
现在,我们来看看向量数据库中的相似性搜索。这个过程从一条查询开始,查询可以是任何你感兴趣的文本问题。
- 查询向量化:嵌入模型将此查询转换为数值向量格式,将其转化为向量数据库可以处理的高维向量。
- 向量比对:向量数据库中包含许多与源数据相关的预存向量。当嵌入后的查询向量进入数据库时,系统会执行相似性计算。
- 计算相似度:这些相似性计算可以基于欧几里得距离、余弦相似度、曼哈顿距离等。本质上,它将查询向量与存储中的所有向量进行比较,以找到与查询最相似的向量。
- 返回结果:相似性搜索的结果是检索到与查询最相关的内容。这个输出就是最终的答案。

执行相似性搜索的代码
以下是如何执行相似性搜索的代码示例。查询可以是关于源数据的任何文本。然后,你调用之前构建的向量数据库并基于查询执行搜索。默认情况下,搜索将返回向量数据库中最相似的前四条内容。
例如,这里的查询是关于邮件政策的,搜索返回的内容如下所示。

# 执行相似性搜索
query = "公司的邮件使用政策是什么?"
results = vectorstore.similarity_search(query, k=4) # 返回最相似的4个结果
# 打印结果
for doc in results:
print(doc.page_content)
print("-" * 20)
课程总结
本节课中,我们一起学*了以下核心内容:
- 你可以使用向量存储来存储来自数据源的嵌入向量。
- 向量数据库通过相似性搜索基于查询检索信息,从而获取相关内容。
- ChromaDB 是一个能保存嵌入向量及元数据的向量存储,是进行语义搜索的强大工具。
- 使用 Langchain 构建 ChromaDB 向量数据库时,你需要从
langchain.vectorstores导入Chroma类,并传入数据块和嵌入模型,之后它将自动形成向量数据库。 - 相似性搜索的过程始于一个查询,嵌入模型将其转换为数值格式。向量数据库将查询向量与其存储中的所有向量进行比较,以找到最相似的向量。

掌握这些知识,你便能够有效地利用向量数据库管理和检索嵌入向量,为构建更智能的应用程序打下基础。
生成式人工智能工程:170:探索LangChain中的高级检索器(第1部分)🔍
在本节课中,我们将要学*LangChain中检索器的基本概念,并深入探讨其中一种最基础的检索器类型——基于向量存储的检索器。我们将了解它的工作原理、创建方法以及两种核心的检索策略。
什么是LangChain检索器?🤔
上一节我们介绍了课程目标,本节中我们来看看LangChain检索器的核心定义。
LangChain检索器是一个接口,它能够根据一个非结构化的查询返回相关文档。它比向量存储的概念更通用。它的主要目的不一定是存储文档,而是检索文档或其片段。
一个LangChain检索器接受一个字符串查询作为输入,并返回一个文档或文本块的列表作为输出。

虽然检索数据的过程听起来简单,但其实现可能相当复杂,存在多种可能的实现方式。

基于向量存储的检索器📚
在理解了检索器的基本定义后,本节中我们来学*最简单的一种检索器类型——基于向量存储的检索器。
这种检索器从向量数据库中检索文档。回想一下,这个向量数据库是通过加载源文档、将其分割成块并嵌入向量而创建的。基于向量存储的检索器接入这个已存在的向量存储。
它接受一个查询,并检索最相似的数据。在这个场景下,最相似的数据就是文本块。
以下是基于向量存储的检索器的工作原理:
- 将查询文本进行向量嵌入。
- 使用相似性搜索或最大边际相关性算法,将查询向量与向量库中所有文本块的向量进行比较。
- 检索出最相关的文本块。
基于向量存储的检索器易于理解,因为它查询的是一个现成的向量存储,并且不需要大型语言模型来检索最相似的文本块。
检索策略:相似性搜索与MMR ⚖️
我们已经知道基于向量存储的检索器如何工作,现在具体看看它使用的两种核心检索策略。

除了使用相似性分数,你还可以使用最大边际相关性检索。
MMR是一种用于平衡检索结果相关性和多样性的技术。它选择那些既与查询高度相关,又彼此之间相似度最低的文档。

这种方法有助于避免冗余,并确保更全面地覆盖查询的不同方面。

在这个具体例子中,查询“邮件政策”返回了三个检索到的文档。
总结 📝
本节课中我们一起学*了LangChain检索器的基础知识。
我们了解到,LangChain检索器是一个基于非结构化查询返回文档的接口,它拥有多种类型。基于向量存储的检索器是其中一种,它从向量数据库中检索文档。

它可以直接通过向量存储对象的.as_retriever()方法创建,并使用相似性搜索或MMR策略进行检索。
- 相似性搜索:检索器接受查询并返回最相似的数据。
- MMR:一种用于平衡检索结果相关性和多样性的技术。
生成式人工智能工程:5:探索LangChain中的高级检索器(第二部分)🔍
在本节课中,我们将要学*LangChain中三种高级检索器的工作原理与应用。这些检索器能够处理更复杂的查询场景,提升信息检索的准确性和丰富度。
上一节我们介绍了基础的向量存储检索器,本节中我们来看看三种更高级的检索器:多查询检索器、自查询检索器和父文档检索器。
多查询检索器
多查询检索器与基于向量的检索器类似,但它使用大语言模型来生成原始查询的不同版本。这样做是为了克服因查询措辞的细微变化,或嵌入未能很好捕捉数据语义而可能导致的不同检索结果,从而生成一组更丰富的检索文档。

以下是其工作原理的图示:

在这个特定示例中,创建了一个使用 mixtral-8x7b 基础模型的Watson X LLM实例来生成不同的查询版本。然后使用 MultiQueryRetriever.from_llm 方法创建多查询检索器对象。该方法接受一个 retriever 参数,即用于为每个查询检索结果的基于向量的检索器。在本例中,使用的是简单的相似性搜索检索器,但也可以使用其他检索器,如MMR检索器。此外,MultiQueryRetriever.from_llm 方法还接受一个 llm 参数,该LLM用于为每个传入的查询生成替代版本。
对于每个查询,多查询检索器都会检索一组相关文档,并取所有查询结果的唯一并集,从而获得一组更大的潜在相关文档。


自查询检索器

现在,假设文档不仅包含文本,还包含关于这些文档的元数据。换句话说,假设你的文档如以下代码所示:

这里看到的文档包含描述电影的文本,以及一些与这些电影相关的元数据,例如电影上映年份、导演和IMDB评分。到目前为止,所看过的检索器都无法访问这些元数据,因为只考虑了文档文本。这就是自查询检索器的用武之地。自查询检索器将查询转换为两个部分:一个用于语义查找的字符串,以及一个与之配套的元数据过滤器。


让我们来设置自查询检索器。第一个单元格将刚刚看到的文档转换为可以从中检索文档的向量存储。第二个单元格描述了向量存储中文档的元数据字段。例如,year 属性被描述为一个指示电影上映年份的整数。


这些元数据字段描述有助于LLM创建有意义的元数据过滤器来选择相关文档。



给定此处描述的向量存储和元数据描述,可以使用 SelfQueryRetriever.from_llm 方法基于文本和元数据检索文档。该方法接受LLM、向量数据库、文档内容描述和元数据字段描述作为属性。


使用查询“我想看一部评分高于8.5的电影”检索文档,成功返回了两部评分大于8.5的电影。

父文档检索器
在为检索而分割文档时,常常存在相互冲突的需求。一方面,可能需要较小的文档,以便其嵌入能够准确反映其含义。另一方面,又需要足够长的文档,以便保留每个文本块的上下文。这就是父文档检索器的作用所在。在检索过程中,父文档检索器首先获取较小的文本块,查找它们的父ID,然后返回这些小文本块所在的大型文档。
让我们来设置父文档检索器。父文档检索器有两个文本分割器:一个父分割器,将文本分割成待检索的大块;一个子分割器,将文档分割成小块以生成有意义的嵌入。还需要一个用于嵌入的向量存储和一个用于存储父文档的存储。最后,需要创建父文档检索器对象,并使用 add_documents 方法将文档添加到其中。
父文档检索器可以使用与之前所见所有检索器相同的统一语法来调用。请注意,对于查询“smoking policy”,父文档检索器检索的是由父分割器生成的大块,而不是由子分割器创建的小块。


在这里,检索到的文本块正是查询所要求的“smoking policy”。
总结
本节课中我们一起学*了三种高级检索器。多查询检索器使用LLM创建查询的不同版本,生成一组更丰富的检索文档。自查询检索器将查询转换为两个部分:一个用于语义查找的字符串,以及一个与之配套的元数据过滤器。最后,学*了父文档检索器有两个文本分割器:一个父分割器将文本分割成待检索的大块,一个子分割器将文档分割成小块以生成有意义的嵌入。


生成式人工智能工程:172:开始使用Gradio 🚀
在本节课中,我们将学*如何使用Gradio库快速为机器学*模型或Python函数创建交互式Web界面。我们将从了解Gradio是什么开始,逐步学*如何安装、构建一个简单的界面,并探索处理多种输入类型(如文本、数字和文件)的方法。
Gradio是一个开源的Python库,用于创建可定制的、基于Web的用户界面。它设计简单易用,特别适合用于展示机器学*模型和计算工具。
接下来,我们分步来看它的工作原理。
首先,你需要编写Python代码来定义应用程序的函数和逻辑。接着,你使用Gradio为这些函数创建一个界面。这里,你使用Gradio的Interface类来指定函数的输入和输出组件,然后配置界面以定义用户如何与你的应用程序交互。
下一步是使用launch方法启动Gradio服务器。这会在你的机器上启动一个本地服务器,为你的应用程序创建一个Web界面。
作为最后一步,你可以通过Gradio提供的本地或公共URL访问这个Web界面。用户可以与界面交互,实时提供输入并接收输出。
安装与基础设置
首先,你需要使用pip安装Gradio包。
pip install gradio
安装完成后,你可以在Python代码中导入Gradio。
import gradio as gr
现在,让我们编写一个简单的Gradio界面,它包含一个文本输入字段,并将输入的文本显示为输出。
gr.Interface函数是Gradio库的核心组件。它用于为Python函数创建交互式Web界面,并可以自定义输入和输出组件。
接着,你定义用户输入查询后要执行的函数。你可以根据具体用例在此定义任何功能。在本例中,我们只是返回输入的文本。
gr.Textbox用于创建一个文本框,你还可以为文本框定义自定义标签。同样地,你可以为输出创建一个文本框。
import gradio as gr
def greet(name):
return f"Hello 数据科学与人工智能笔记(全)!"
demo = gr.Interface(
fn=greet,
inputs=gr.Textbox(label="Your name"),
outputs=gr.Textbox(label="Greeting")
)
demo.launch()
使用launch方法,你可以运行这个界面。这是从上述代码中得到的Gradio界面。如图所示,这里有两个文本框,一个用于输入,另一个用于输出。

处理多种输入类型

让我们看看如何在Gradio界面中设置多个输入。
就像gr.Textbox用于创建文本字段一样,gr.Number用于创建数字字段。你可以将gr.Number和gr.Textbox作为输入列表传递。同样地,如果你想添加更多输入,可以在这里的输入列表中添加。
以下是处理文本和数字输入的示例代码:
import gradio as gr
def combine_text_and_number(text, number):
return f"Text: {text}, Number: {number}"

demo = gr.Interface(
fn=combine_text_and_number,
inputs=[gr.Textbox(label="Enter text"), gr.Number(label="Enter a number")],
outputs=gr.Textbox(label="Combined Result")
)
demo.launch()

现在,让我们看看生成的代码输出。你可以观察到,现在有两种类型的输入:一种用于文本,另一种用于数字值。
文件上传功能
你还可以使用Gradio创建一个上传或拖放文件的选项。
以下是一个计算用户上传文件数量的代码示例。

gr.File允许用户在Web界面中上传文件。它支持多文件上传,并为后端函数提供上传文件的路径以供进一步处理。
接着,你定义count_files函数来计算用户上传的文件数量。
import gradio as gr

def count_files(files):
return len(files)
demo = gr.Interface(
fn=count_files,
inputs=gr.File(file_count="multiple"),
outputs=gr.Textbox(label="Number of files")
)
demo.launch()
启动代码后,它会生成一个唯一的Web界面链接,在会话运行期间,可以从任何地方访问该链接。现在,你拥有了通过Web界面上传或拖放文件的选项。
总结

本节课中,我们一起学*了以下内容:
- Gradio是一个用于创建可定制Web用户界面的开源Python库。
- 要设置它,你需要编写Python代码,创建Gradio界面,使用
launch方法启动Gradio服务器,并通过Gradio提供的本地或公共URL访问Web界面。 - 你学会了如何使用
gr.Interface函数编写一个简单的文本输入和输出界面。 - 最后,你学会了如何在Web界面中使用
gr.File组件来实现文件的上传或拖放功能。

浙公网安备 33010602011771号