《Hands-On Large Language Models》第1章 大型语言模型简介

第一部分 理解语言模型

Understanding Language Models

\(\text{1}\) 大型语言模型简介

An Introduction to Large Language Models

人类正处于一个拐点。从 \(\text{2012}\) 年开始,构建 \(\text{AI}\) 系统(使用深度神经网络)的发展加速,到了十年末,它们产生了第一个能够写出与人类所写文章无法区分的软件系统。这个系统是一个名为 \(\text{Generative Pre-trained Transformer 2}\),简称 \(\text{GPT}-2\)\(\text{AI}\) 模型。\(\text{2022}\) 年标志着 \(\text{ChatGPT}\) 的发布,它展示了这项技术将如何深刻地彻底改变我们与技术和信息的交互方式。新的 \(\text{AI}\) 模型最初是类似人类的聊天机器人,在五天内达到了一百万活跃用户,随后在两个月内达到了一亿活跃用户,并迅速演变为我们在处理翻译、文本生成、摘要等常见任务方法上的重大转变。它成为了程序员、教育工作者和研究人员不可或缺的工具

\(\text{ChatGPT}\) 的成功是空前的,并普及了对其背后技术——即大型语言模型 (\(\text{LLM}\))——的更多研究。专有模型和公共模型都在稳步发布,它们的性能不断接近,并最终赶上了 \(\text{ChatGPT}\) 的表现。毫不夸张地说,几乎所有的注意力都集中在 \(\text{LLM}\) 上。

因此,至少对我们而言,\(\text{2023}\) 年将永远被称为彻底改变我们领域的一年,这个领域就是语言人工智能 (\(\text{Language AI}\)),其特点是开发出能够理解和生成人类语言的系统。

然而,\(\text{LLM}\) 已经存在一段时间了,而较小的模型至今仍然具有相关性。\(\text{LLM}\) 远不止是一个单一的模型,语言 \(\text{AI}\) 领域还有许多其他值得探索的技术和模型。

在本书中,我们的目标是让读者对 \(\text{LLM}\) 和整个语言 \(\text{AI}\) 领域的基本原理有扎实的理解。本章是本书其余章节的支架,并将介绍我们将在所有章节中使用的概念和术语。

但最重要的是,我们打算在本章中回答以下问题:

  • 什么是语言 \(\text{AI}\)
  • 什么是大型语言模型?
  • 大型语言模型的常见用例和应用是什么?
  • 我们如何自己使用大型语言模型?

什么是语言 \(\text{AI}\)

What Is Language AI?

人工智能 (\(\text{AI}\)) 一词通常用于描述致力于执行接近人类智能的任务(例如语音识别、语言翻译和视觉感知)的计算机系统。它是软件的智能,而不是人类的智能。

以下是人工智能学科创始人之一提供的更正式的定义:

[人工智能是]制造智能机器,特别是智能计算机程序的科学和工程。它与使用计算机理解人类智能的类似任务相关,但 \(\text{AI}\) 不必局限于生物学上可观察到的方法。 —\(\text{John McCarthy}\)\(\text{2007}^1\)

由于 \(\text{AI}\) 的不断发展,该术语已被用来描述各种各样的系统,其中一些可能并未真正体现智能行为。例如,计算机游戏中的角色(\(\text{NPC}\) [非玩家角色])经常被称为 \(\text{AI}\),尽管许多不过是 \(\text{if}-\text{else}\) 语句。

语言 \(\text{AI}\) 指的是 \(\text{AI}\) 的一个子领域,专注于开发能够理解、处理和生成人类语言的技术。随着机器学习方法在解决语言处理问题上不断取得成功,语言 \(\text{AI}\) 一词通常可以与自然语言处理 (\(\text{NLP}\)) 互换使用。

我们使用语言 \(\text{AI}\) 这个术语来涵盖那些技术上可能不是 \(\text{LLM}\),但仍对该领域产生重大影响的技术,比如检索系统如何能赋予 \(\text{LLM}\) 超能力(参见\(\text{8}\))。

在本书中,我们希望专注于在塑造语言 \(\text{AI}\) 领域方面发挥重要作用的模型。这意味着探索的不仅仅是孤立的 \(\text{LLM}\)。然而,这引出了一个问题:什么是大型语言模型?

为了在本章中开始回答这个问题,我们首先来探索语言 \(\text{AI}\) 的历史。

语言 \(\text{AI}\) 的近代史

A Recent History of Language AI

如图 \(\text{1-1}\) 所示,语言 \(\text{AI}\) 的历史涵盖了旨在表征和生成语言的许多发展和模型。

F1.1

然而,语言对于计算机来说是一个棘手的概念。文本本质上是非结构化的,当用零和一(单个字符)来表示时,它会失去其含义。因此,纵观语言 \(\text{AI}\) 的历史,有一个重要的焦点是以结构化的方式表示语言,以便计算机可以更容易地使用它。图 \(\text{1-2}\) 中提供了这些语言 \(\text{AI}\) 任务的示例。

F1.2

将语言表征为词袋模型

Representing Language as a Bag-of-Words

我们的语言 \(\text{AI}\) 历史始于一种称为词袋模型\(\text{bag-of-words}\))的技术,这是一种表征非结构化文本的方法。它最早在大约 \(\text{1950}\) 年代被提及,但在 \(\text{2000}\) 年代左右流行起来。

词袋模型的工作原理如下:假设我们有两句话,我们想为其创建数字表示。词袋模型的第一步是分词\(\text{tokenization}\)),即将句子分割成单独的词或子词(\(\text{tokens}\))的过程,如图 \(\text{1-3}\) 所示。

F1.1

最常见的分词方法是通过空格分割来创建单独的词。然而,这有其缺点,因为某些语言(例如普通话)在单个词周围没有空格。在下一章中,我们将深入探讨分词以及该技术如何影响语言模型。

如图 \(\text{1-4}\) 所示,在分词之后,我们结合每句话中所有独特的词来创建一个词汇表\(\text{vocabulary}\)),我们可以用它来表征这些句子。

F1.1

使用我们的词汇表,我们简单地计算每个词在每句话中出现的次数,名副其实地创建了一个词袋。因此,词袋模型旨在以数字形式创建文本的表征,这些数字也称为向量向量表征,如图 \(\text{1-5}\) 所示。在本书中,我们将这类模型称为表征模型\(\text{representation models}\))。

F1.1

尽管词袋模型是一种经典方法,但它绝非完全过时。在\(\text{5}\)中,我们将探讨它如何仍然可以用于补充更近代的语言模型。

使用密集向量嵌入实现更好的表征

Better Representations with Dense Vector Embeddings

词袋模型(\(\text{Bag-of-words}\))虽然是一种优雅的方法,但它有一个缺陷:它认为语言不过是一个几乎是字面意义上的词袋,忽略了文本的语义性质或含义

\(\text{2013}\) 年发布的 \(\text{word}2\text{vec}\) 是最早成功地在嵌入\(\text{embeddings}\))中捕捉文本含义的尝试之一。嵌入是数据的向量表征,它试图捕捉数据的含义。为此,\(\text{word}2\text{vec}\) 通过在大量的文本数据(例如整个 \(\text{Wikipedia}\))上进行训练,学习词语的语义表征。

为了生成这些语义表征,\(\text{word}2\text{vec}\) 利用了神经网络。这些网络由相互连接的节点层组成,用于处理信息。如图 \(\text{1-6}\) 所示,神经网络可以有许多层,其中每条连接都有一个权重,该权重取决于输入。这些权重通常被称为模型的参数

F1.1

\(\text{word}2\text{vec}\) 使用这些神经网络,通过查看词语在给定句子中倾向于出现在哪些其他词语的旁边,来生成词嵌入。我们首先为词汇表中的每个词分配一个向量嵌入,例如每个词都有 \(\text{50}\) 个值,并用随机值初始化。然后,如图 \(\text{1-7}\) 所示,在每个训练步骤中,我们从训练数据中取出词对,模型会尝试预测它们是否可能在句子中是相邻的词。

F1.1

在这个训练过程中,\(\text{word}2\text{vec}\) 学习了词语之间的关系,并将这些信息提取到嵌入中。如果两个词倾向于拥有相同的邻居,它们的嵌入就会彼此更靠近,反之亦然。在\(\text{2}\)中,我们将更仔细地研究 \(\text{word}2\text{vec}\) 的训练过程。

由此产生的嵌入捕捉了词语的含义,但这到底意味着什么?为了说明这种现象,让我们稍微过度简化一下,想象我们有几个词的嵌入,即“\(\text{apple}\)”(苹果)和“\(\text{baby}\)”(婴儿)。嵌入试图通过表征词语的属性来捕捉含义。例如,“\(\text{baby}\)”这个词在“新生儿”和“人类”这两个属性上可能得分很高,而“\(\text{apple}\)”在这个属性上得分很低。

如图 \(\text{1-8}\) 所示,嵌入可以有许多属性来表征词语的含义。由于嵌入的大小是固定的,因此选择其属性是为了创建该词语的心智表征

F1.1

在实践中,这些属性通常非常晦涩,很少与单个实体或人类可识别的概念相关。然而,这些属性结合在一起对计算机是有意义的,并且是将人类语言转化为计算机语言的良好方式。

嵌入非常有帮助,因为它们允许我们衡量两个词语之间的语义相似性。使用各种距离度量,我们可以判断一个词与另一个词的接近程度。如图 \(\text{1-9}\) 所示,如果我们将这些嵌入压缩成二维表征,您会注意到含义相似的词语倾向于更靠近。在\(\text{5}\)中,我们将探讨如何将这些嵌入压缩到 \(\text{n}\) 维空间。

F1.1

嵌入的类型

Types of Embeddings

嵌入有许多类型,例如词嵌入句子嵌入,它们用于指示不同级别的抽象(词语与句子),如图 \(\text{1-10}\) 所示。

F1.1

例如,词袋模型文档级别创建嵌入,因为它代表了整个文档。相比之下,\(\text{word}2\text{vec}\) 仅为词语生成嵌入。

在整本书中,嵌入将扮演核心角色,因为它们被用于许多用例,例如分类(参见\(\text{4}\))、聚类(参见\(\text{5}\))以及语义搜索和检索增强生成(参见\(\text{8}\))。在\(\text{2}\)中,我们将首次深入探讨分词嵌入\(\text{token embeddings}\))。

使用注意力机制编码和解码上下文

Encoding and Decoding Context with Attention

\(\text{word}2\text{vec}\) 的训练过程创建了静态的、可下载的词语表征。例如,无论“\(\text{bank}\)”(银行/河岸)在何种上下文中使用,它将始终具有相同的嵌入。然而,“\(\text{bank}\)”可以指金融机构,也可以指河流的岸边。它的含义,因此它的嵌入,应该根据上下文而变化。

编码这种文本的一个进步是通过循环神经网络 (\(\text{RNNs}\)) 实现的。这些是神经网络的变体,可以将序列建模为额外的输入

为此,这些 \(\text{RNNs}\) 用于两个任务:编码或表征输入句子,以及解码或生成输出句子。图 \(\text{1-11}\) 通过展示“\(\text{I love llamas}\)”这样的句子如何被翻译成荷兰语“\(\text{Ik hou van lama’s}\)”来阐释这个概念。

F1.1

此架构中的每一步都是自回归\(\text{autoregressive}\))的。如图 \(\text{1-12}\) 所示,在生成下一个词时,此架构需要消耗所有先前生成的词语

F1.1

编码步骤旨在尽可能好地表征输入,以嵌入的形式生成上下文,该上下文作为解码器的输入。为了生成此表征,它将词嵌入作为输入,这意味着我们可以使用 \(\text{word}2\text{vec}\) 进行初始表征。在图 \(\text{1-13}\) 中,我们可以观察到这个过程。请注意输入是如何按顺序、一次一个地处理的,输出也是如此。

F1.1

然而,这个上下文嵌入使得处理较长的句子变得困难,因为它仅仅是一个单一的嵌入来代表整个输入。\(\text{2014}\) 年,引入了一种称为注意力机制\(\text{attention}\))的解决方案,它极大地改进了原有架构。注意力机制允许模型专注于输入序列中相互关联(“关注”彼此)并放大其信号的部分,如图 \(\text{1-14}\) 所示。注意力机制选择性地确定给定句子中哪些词语最重要。

F1.1

例如,输出词“\(\text{lama’s}\)”是荷兰语中“\(\text{llamas}\)”(美洲驼)的意思,这就是为什么两者之间的注意力分数很高。类似地,词语“\(\text{lama’s}\)”和“\(\text{I}\)”之间的注意力较低,因为它们的关联性不高。在\(\text{3}\)中,我们将更深入地探讨注意力机制。

通过在解码步骤中添加这些注意力机制\(\text{RNN}\) 可以为序列中的每个输入词生成与潜在输出相关的信号所有输入词的隐藏状态都被传递给解码器,而不仅仅是传递一个上下文嵌入。图 \(\text{1-15}\) 展示了这一过程。

F1.1

因此,在生成“\(\text{Ik hou van lama’s}\)”的过程中,\(\text{RNN}\) 会跟踪它主要关注的词语来执行翻译。与 \(\text{word}2\text{vec}\) 相比,这种架构允许通过“关注”整个句子来表征文本的顺序性质及其出现的上下文。然而,这种顺序性质阻碍了模型在训练过程中的并行化

注意力机制就是你所需要的一切

Attention Is All You Need

注意力机制的真正力量,以及驱动大型语言模型惊人能力的关键,最早出现在 \(\text{2017}\) 年发布的著名论文 《注意力机制就是你所需要的一切》(\(\text{Attention is all you need}\) 中。作者提出了一种名为 \(\text{Transformer}\) 的网络架构,它完全基于注意力机制,并移除了我们之前看到的循环网络\(\text{recurrence network}\))。与循环网络相比,\(\text{Transformer}\) 可以并行训练,这极大地加快了训练速度。

\(\text{Transformer}\) 中,编码器解码器组件堆叠在一起,如图 \(\text{1-16}\) 所示。该架构仍然是自回归的,在生成一个新词之前,需要消耗所有先前生成的词。

F1.1

现在,编码器和解码器块都将围绕注意力机制展开,而不是利用具有注意力功能的 \(\text{RNN}\)。如图 \(\text{1-17}\) 所示,\(\text{Transformer}\) 中的编码器块由两部分组成:自注意力\(\text{self-attention}\))和一个前馈神经网络\(\text{feedforward neural network}\))。

F1.1

与以前的注意力方法相比,自注意力可以关注单个序列内的不同位置,从而更轻松、更准确地表征输入序列,如图 \(\text{1-18}\) 所示。它不再是一次处理一个分词\(\text{token}\)),而可以一次性查看整个序列

F1.1

与编码器相比,解码器有一个额外的层,该层会关注编码器的输出(以找到输入中的相关部分)。如图 \(\text{1-19}\) 所示,此过程类似于我们前面讨论过的 \(\text{RNN}\) 注意力解码器

F1.1

如图 \(\text{1-20}\) 所示,解码器中的自注意力层掩盖未来的位置,使其仅关注较早的位置,以防止在生成输出时信息泄漏。

F1.1

这些构建块共同创建了 \(\text{Transformer}\) 架构,并且是语言 \(\text{AI}\) 中许多有影响力的模型(例如 \(\text{BERT}\)\(\text{GPT}-1\),我们将在本章后面介绍)的基础。在本书中,我们将使用的大多数模型都是基于 \(\text{Transformer}\) 的模型。

\(\text{Transformer}\) 架构的内涵远不止我们迄今为止探讨的这些。在\(\text{2}\)\(\text{3}\)中,我们将探讨 \(\text{Transformer}\) 模型工作得如此出色的众多原因,包括多头注意力\(\text{multi-head attention}\))、位置嵌入\(\text{positional embeddings}\))和层归一化\(\text{layer normalization}\))。

表征模型:仅编码器 (\(\text{Encoder}\)-Only) 模型

Representation Models: Encoder-Only Models

最初的 \(\text{Transformer}\) 模型是编码器-解码器架构,它非常适用于翻译任务,但不能轻易地用于其他任务,例如文本分类。

\(\text{2018}\) 年,引入了一种名为 \(\text{BERT}\)(来自 \(\text{Transformer}\) 的双向编码器表征,\(\text{Bidirectional Encoder Representations from Transformers}\))的新架构,该架构可用于各种任务,并将成为未来几年语言 \(\text{AI}\) 的基础。\(\text{BERT}\) 是一种仅编码器 (\(\text{Encoder}\)-Only) 架构,它专注于表征语言,如图 \(\text{1-21}\) 所示。这意味着它只使用编码器,并完全移除了解码器。

F1.1

这些编码器块与我们之前看到的相同:自注意力之后是前馈神经网络。输入包含一个额外的分词\(\text{token}\)),即 \(\text{[CLS]}\) 或分类分词,它被用作整个输入的表征。通常,我们使用这个 \(\text{[CLS]}\) 分词作为输入嵌入,用于在特定任务(如分类)上微调模型。

训练这些编码器堆栈可能是一项困难的任务,\(\text{BERT}\) 通过采用一种称为掩码语言建模\(\text{masked language modeling}\))的技术来解决(参见\(\text{2}\)\(\text{11}\))。如图 \(\text{1-22}\) 所示,此方法会掩盖输入的一部分,让模型进行预测。这种预测任务很困难,但它允许 \(\text{BERT}\) 创建更准确的(中间)输入表征。

F1.1

这种架构和训练过程使得 \(\text{BERT}\) 及相关架构在表征上下文语言方面表现出色。类似 \(\text{BERT}\) 的模型通常用于迁移学习\(\text{transfer learning}\)),它涉及首先对其进行语言建模的预训练,然后针对特定任务进行微调。例如,通过在整个 \(\text{Wikipedia}\) 上训练 \(\text{BERT}\),它学会了理解文本的语义和上下文性质。然后,如图 \(\text{1-23}\) 所示,我们可以使用该预训练模型来微调它以进行特定任务,例如文本分类

F1.1

预训练模型的一个巨大好处是,大部分训练工作已经为我们完成了。针对特定任务的微调通常计算密集度较低所需数据较少。此外,类似 \(\text{BERT}\) 的模型在其架构中的几乎每个步骤都会生成嵌入。这也使得 \(\text{BERT}\) 模型可以作为特征提取机器,而无需针对特定任务进行微调。

类似 \(\text{BERT}\)仅编码器模型将在本书的许多部分中使用。多年来,它们一直并仍然被用于常见的任务,包括分类任务(参见\(\text{4}\))、聚类任务(参见\(\text{5}\))和语义搜索(参见\(\text{8}\))。

在整本书中,我们将仅编码器模型称为表征模型\(\text{representation models}\)),以区别于我们称为生成模型\(\text{generative models}\))的仅解码器模型。请注意,主要区别并不在于底层的架构以及这些模型的工作方式。表征模型主要关注于表征语言(例如,通过创建嵌入),并且通常不生成文本。相比之下,生成模型主要关注于生成文本,并且通常不训练用于生成嵌入。

表征模型和生成模型及其组件的区别也将在大多数图片中显示。表征模型蓝绿色的,带有一个小的向量图标(表示其专注于向量和嵌入),而生成模型粉红色的,带有一个小的聊天图标(表示其生成能力)。

生成模型:仅解码器 (\(\text{Decoder}\)-Only) 模型

Generative Models: Decoder-Only Models

\(\text{BERT}\)仅编码器 (\(\text{encoder}\)-only) 架构类似,\(\text{2018}\) 年提出了仅解码器 (\(\text{decoder}\)-only) 架构,以针对生成任务。该架构因其生成能力而被称为 \(\text{Generative Pre-trained Transformer}\) (\(\text{GPT}\))(现在被称为 \(\text{GPT}\)-1,以区别于后续版本)。如图 \(\text{1-24}\) 所示,它堆叠了解码器块,类似于 \(\text{BERT}\) 堆叠编码器的架构。

F1.1

\(\text{GPT}-1\) 是在由 \(\text{7,000}\) 本书和 \(\text{Common Crawl}\)(一个大型网页数据集)组成的语料库上训练的。由此产生的模型包含 \(\text{1.17}\) 亿个参数。每个参数都是一个数值,代表模型对语言的理解。

如果其他条件保持不变,我们期望更多的参数会极大地影响语言模型的能力和性能。考虑到这一点,我们看到越来越大的模型在稳步发布。如图 \(\text{1-25}\) 所示,\(\text{GPT}-2\) 拥有 \(\text{15}\) 亿个参数,紧随其后的是 \(\text{GPT}-3\),使用了 \(\text{1,750}\) 亿个参数。

F1.1

这些生成式仅解码器模型,尤其是那些“更大”的模型,通常被称为大型语言模型 (\(\text{LLM}\))。正如我们将在本章后面讨论的那样,\(\text{LLM}\) 一词并仅限于生成模型(仅解码器),也包括表征模型(仅编码器)。

作为序列到序列的机器,生成式 \(\text{LLM}\) 接收一些文本并尝试对其进行自动补全。虽然这是一个方便的功能,但它们真正的力量来自于被训练成聊天机器人。如果它们可以被训练来回答问题,而不是补全文本呢?通过微调这些模型,我们可以创建可以遵循指令的指令模型或聊天模型。

如图 \(\text{1-26}\) 所示,由此产生的模型可以接收用户查询(提示\(\text{prompt}\)),并输出一个最有可能遵循该提示的响应。因此,您经常会听到生成模型被称为补全模型

F1.1

这些补全模型的一个重要组成部分被称为上下文长度\(\text{context length}\))或上下文窗口\(\text{context window}\))。如图 \(\text{1-27}\) 所示,上下文长度代表模型可以处理的最大分词数\(\text{tokens}\))。大的上下文窗口允许将整个文档传递给 \(\text{LLM}\)。请注意,由于这些模型的自回归性质,随着新分词的生成,当前的上下文长度将会增加

F1.1

生成式 \(\text{AI}\) 之年

The Year of Generative AI

\(\text{LLM}\) 对该领域产生了巨大的影响,并导致一些人将 \(\text{2023}\)称为生成式 \(\text{AI}\) 之年,这得益于 \(\text{ChatGPT}\) (\(\text{GPT}-3.5\)) 的发布、采用和媒体报道。当我们提到 \(\text{ChatGPT}\) 时,我们实际上指的是产品,而不是底层的模型。当它首次发布时,它由 \(\text{GPT}-3.5\) \(\text{LLM}\) 提供支持,此后已发展到包括更多性能更高的变体,例如 \(\text{GPT}-4\)

\(\text{GPT}-3.5\) 并不是唯一在生成式 \(\text{AI}\) 之年产生影响的模型。如图 \(\text{1-28}\) 所示,开源专有\(\text{LLM}\) 都以惊人的速度进入了大众视野。这些开源的基础模型通常被称为 \(\text{foundation models}\),并且可以针对特定任务(例如遵循指令)进行微调。

F1.1

除了广受欢迎的 \(\text{Transformer}\) 架构之外,还出现了新的有前景的架构,例如 \(\text{Mamba}\)\(\text{RWKV}\)。这些新颖的架构试图在具备更大的上下文窗口更快的推理速度等额外优势的同时,达到 \(\text{Transformer}\) 级别的性能。

这些发展体现了该领域的演变,并展示了 \(\text{2023}\) 年确实是 \(\text{AI}\) 领域忙碌的一年。我们竭尽全力才能跟上语言 \(\text{AI}\) 内外发生的诸多进展。

因此,本书探索的不仅仅是最新的 \(\text{LLM}\)。我们将探讨如何使用其他模型,例如嵌入模型仅编码器模型,甚至词袋模型,来增强 \(\text{LLM}\) 的能力。

“大型语言模型”不断变化的定义

The Moving Definition of a “Large Language Model”

在我们回顾语言 \(\text{AI}\) 的近代史时,我们观察到通常只有生成式仅解码器 (\(\text{Transformer}\)) 模型被广泛地称为大型语言模型。尤其当它们被认为是“大”模型时。在实践中,这似乎是一个相当受限的描述!

如果我们创建一个与 \(\text{GPT}-3\) 具有相同能力,但体积小 \(\text{10}\)的模型呢?这样的模型会超出“大型”语言模型的分类吗?

同样,如果一个模型像 \(\text{GPT}-4\) 一样大,可以进行准确的文本分类,但不具备任何生成能力呢?即使它的主要功能不是生成语言,但仍然表征文本,它仍然够得上大型“语言模型”的资格吗?

这类定义的问题在于,我们会将有能力的模型排除在外。我们给一个模型或另一个模型起什么名字,并不会改变它的行为方式。

鉴于“大型语言模型”这一术语的定义倾向于随着新模型的发布而演变,我们希望明确它在本书中的含义。“大”是武断的,今天被认为是大型的模型明天可能就变小了。目前同一种事物有许多不同的名称,对我们来说,“大型语言模型”也包括不生成文本并且可以在消费级硬件上运行的模型。

因此,除了涵盖生成模型外,本书还将涵盖参数少于 \(\text{10}\) 亿不生成文本的模型。我们将探索如何使用嵌入模型表征模型,甚至词袋模型来增强 \(\text{LLM}\) 的能力。

大型语言模型的训练范式

The Training Paradigm of Large Language Models

传统的机器学习通常涉及针对特定任务(例如分类)训练模型。如图 \(\text{1-29}\) 所示,我们认为这是一个一步到位的过程。

F1.1

相比之下,创建 \(\text{LLM}\) 通常至少包含两个步骤:

语言建模(\(\text{Language modeling}\) 第一步,称为预训练\(\text{pretraining}\)),占据了大部分的计算和训练时间。\(\text{LLM}\)大量的互联网文本语料库上进行训练,使模型能够学习语法、上下文和语言模式。这个广泛的训练阶段尚未针对除预测下一个词之外的特定任务或应用。由此产生的模型通常被称为基础模型\(\text{foundation model}\))或基座模型\(\text{base model}\))。这些模型通常不遵循指令

微调(\(\text{Fine-tuning}\) 第二步,微调\(\text{fine-tuning}\)),有时也称为后训练\(\text{post-training}\)),涉及使用先前训练好的模型,并针对一个更窄的任务对其进行进一步训练。这使得 \(\text{LLM}\) 能够适应特定的任务或表现出期望的行为。例如,我们可以微调一个基座模型,使其在分类任务上表现良好,或使其能够遵循指令。这节省了大量的资源,因为预训练阶段成本非常高昂,并且通常需要大多数个人和组织望尘莫及的数据和计算资源。例如,\(\text{Llama 2}\) 是在一个包含 \(\text{2}\) 万亿个分词的数据集上训练的。可以想象创建该模型所需的计算量!在\(\text{12}\)中,我们将介绍几种在您自己的数据集上微调基础模型的方法。

任何经过第一步(预训练)的模型,我们都将其视为预训练模型\(\text{pretrained model}\)),这也包括经过微调的模型。这种两步训练方法如图 \(\text{1-30}\) 所示。

F1.1

可以添加额外的微调步骤,以使模型更符合用户的偏好,我们将在\(\text{12}\)中探讨这一点。

大型语言模型的应用:是什么让它们如此有用?

Large Language Model Applications: What Makes Them So Useful?

\(\text{LLM}\) 的特性使其适用于广泛的任务。借助文本生成和提示(\(\text{prompting}\)),其限制似乎几乎只在于您的想象力。下面,我们探讨一些常见的任务和技术:

检测客户留下的评论是积极还是消极 这是一个(有监督)分类任务,可以使用仅编码器和仅解码器模型来处理,无论是使用预训练模型(参见\(\text{4}\))还是通过微调模型(参见\(\text{11}\))。

开发一个系统来查找工单问题中的常见主题 这是一个(无监督)分类任务,我们没有预定义的标签。我们可以利用仅编码器模型执行分类本身,并利用仅解码器模型来标记这些主题(参见\(\text{5}\))。

构建用于检索和检查相关文档的系统 语言模型系统的一个主要组成部分是其添加外部信息资源的能力。通过语义搜索,我们可以构建系统,使 \(\text{LLM}\) 能够轻松访问和查找所需信息(参见\(\text{8}\))。您还可以通过创建或微调自定义嵌入模型来改进您的系统(参见\(\text{12}\))。

构建一个 \(\text{LLM}\) 聊天机器人,可以利用外部资源,例如工具和文档 这是多种技术的组合,它展示了 \(\text{LLM}\) 的真正力量可以通过附加组件来体现。提示工程(参见\(\text{6}\))、检索增强生成 (\(\text{RAG}\))(参见\(\text{8}\))和微调 \(\text{LLM}\)(参见\(\text{12}\))等方法都是 \(\text{LLM}\) 拼图的组成部分。

构建一个 \(\text{LLM}\),能够根据一张显示您冰箱里产品的图片来撰写食谱 这是一个多模态任务,其中 \(\text{LLM}\) 接收图像并对其所看到的内容进行推理(参见\(\text{9}\))。\(\text{LLM}\) 正在被适配到其他模态,例如视觉,这开启了各种有趣的用例。

\(\text{LLM}\) 应用的创建过程令人非常满意,因为它们在一定程度上受限于您所能想象到的事物。随着这些模型变得越来越准确,将它们实际用于角色扮演撰写儿童书籍等创意用例只会变得越来越有趣。

负责任的 \(\text{LLM}\) 开发与使用

Responsible LLM Development and Usage

由于 \(\text{LLM}\) 的广泛采用,其影响已经且可能将继续是重大的。在我们探索 \(\text{LLM}\) 令人难以置信的能力时,重要的是要考虑到它们的社会和伦理影响。需要考虑的几个关键点:

偏见与公平性 \(\text{LLM}\) 是在可能包含偏见的大量数据上训练的。\(\text{LLM}\) 可能会从这些偏见中学习、开始重现它们,并有可能放大它们。由于训练 \(\text{LLM}\) 的数据很少共享,因此除非您亲自尝试,否则它们可能包含哪些潜在偏见仍然不清楚。

透明度与问责制 由于 \(\text{LLM}\) 令人难以置信的能力,您何时在与人类交谈或何时在与 \(\text{LLM}\) 交谈并不总是明确的。因此,当缺乏人工干预时,\(\text{LLM}\) 在与人类互动中的使用可能会产生意外后果。例如,用于医疗领域的基于 \(\text{LLM}\) 的应用可能会受到作为医疗设备的监管,因为它们可能会影响患者的健康。

生成有害内容 \(\text{LLM}\) 不一定会生成事实内容,并可能会自信地输出不正确的文本。此外,它们可用于生成虚假新闻、文章和其他误导性的信息来源

知识产权 \(\text{LLM}\) 的输出是您的知识产权还是 \(\text{LLM}\) 创建者的知识产权?当输出与训练数据中的某个短语相似时,知识产权是否属于该短语的作者?在无法访问训练数据的情况下,\(\text{LLM}\) 何时使用了受版权保护的材料仍然不清楚。

监管 由于 \(\text{LLM}\) 的巨大影响,各国政府已开始对商业应用进行监管。一个例子是《欧洲 \(\text{AI}\) 法案》\(\text{European AI Act}\)),该法案监管包括 \(\text{LLM}\) 在内的基础模型的开发和部署。

在您开发和使用 \(\text{LLM}\) 时,我们想强调伦理考量的重要性,并敦促您了解更多关于安全和负责任地使用 \(\text{LLM}\) 和一般 \(\text{AI}\) 系统的知识。

所需的有限资源就是您所需要的一切

Limited Resources Are All You Need

我们迄今为止多次提到的计算资源通常与您系统上可用的 \(\text{GPU}\)(图形处理单元)有关。一个强大的 \(\text{GPU}\)(显卡)将使训练和使用 \(\text{LLM}\) 都更加高效和快速。

在选择 \(\text{GPU}\) 时,一个重要的组成部分是您可用的 \(\text{VRAM}\)(视频随机存取存储器)量。这指的是您的 \(\text{GPU}\) 上可用的内存量。实际上,您拥有的 \(\text{VRAM}\) 越多越好。这样做的原因是,如果您没有足够的 \(\text{VRAM}\),有些模型根本无法使用。

由于训练和微调 \(\text{LLM}\)\(\text{GPU}\) 方面可能是一个昂贵的过程,那些没有强大 \(\text{GPU}\) 的人通常被称为 \(\text{GPU}\) 贫困者”\(\text{GPU-poor}\))。这说明了争夺计算资源来训练这些庞大模型的竞争。例如,为了创建 \(\text{Llama 2}\) 系列模型,\(\text{Meta}\) 使用了 \(\text{A100}-\text{80 GB}\)\(\text{GPU}\)。假设租用这样一个 \(\text{GPU}\) 的成本为 \(\text{\$1.50}\)/小时,创建这些模型的总成本将超过 \(\text{\$5,000,000}\)

不幸的是,没有一个单一的规则可以准确确定您需要为特定模型准备多少 \(\text{VRAM}\)。这取决于模型的架构和大小压缩技术上下文大小、运行模型的后端等等。

本书是为“\(\text{GPU}\) 贫困者”准备的! 我们将使用用户无需最昂贵的 \(\text{GPU}\) 或巨额预算即可运行的模型。为此,我们将所有代码放在 \(\text{Google Colab}\) 实例中。在撰写本文时,免费的 \(\text{Google Colab}\) 实例将为您提供一个带有 \(\text{16 GB}\) \(\text{VRAM}\)\(\text{T}4\) \(\text{GPU}\),这是我们建议的最低 \(\text{VRAM}\) 量。

与大型语言模型交互

Interfacing with Large Language Models

\(\text{LLM}\) 交互不仅是使用它们,也是培养对其内部工作原理理解的关键组成部分。由于该领域的诸多发展,目前存在大量的技术、方法和软件包用于与 \(\text{LLM}\) 通信。在整本书中,我们打算探索执行此操作最常见的技术,包括使用专有(闭源)模型公开可用模型

专有(闭源)模型

Proprietary, Private Models

闭源 \(\text{LLM}\) 是指不对公众共享其权重和架构的模型。它们由特定的组织开发,其底层代码被保密。此类模型的示例包括 \(\text{OpenAI}\)\(\text{GPT}-4\)\(\text{Anthropic}\)\(\text{Claude}\)。这些专有模型通常有强大的商业支持,并已在其服务中开发和集成。

您可以通过与 \(\text{LLM}\) 通信的接口(称为 \(\text{API}\),应用程序编程接口)访问这些模型,如图 \(\text{1-31}\) 所示。例如,要在 \(\text{Python}\) 中使用 \(\text{ChatGPT}\),您可以使用 \(\text{OpenAI}\) 的软件包来与该服务进行交互,而无需直接访问它。

F1.1

专有模型的一个巨大好处是用户不需要拥有强大的 \(\text{GPU}\) 即可使用 \(\text{LLM}\)。提供商负责托管和运行模型,并且通常拥有更多的计算资源。托管和使用模型方面不需要专业知识,这显著降低了入门门槛。此外,由于这些组织的巨大投入,这些模型的性能往往优于其开源同行。

缺点是它可能是一项昂贵的服务。提供商管理托管 \(\text{LLM}\) 的风险和成本,这通常转化为付费服务。此外,由于无法直接访问模型,因此没有方法可以自己微调它。最后,您的数据会与提供商共享,这在许多常见用例中是不理想的,例如共享患者数据。

开放模型

Open Models

开放 \(\text{LLM}\) 是指向公众共享其权重和架构供使用的模型。它们仍由特定的组织开发,但通常会共享用于创建或在本地运行模型的代码——这些代码带有不同级别的许可,可能允许或可能不允许模型的商业用途。\(\text{Cohere}\)\(\text{Command R}\)\(\text{Mistral}\) 模型、\(\text{Microsoft}\)\(\text{Phi}\)\(\text{Meta}\)\(\text{Llama}\) 模型都是开放模型的示例。

关于什么才真正代表开源模型,目前仍在持续讨论中。例如,一些公开共享的模型带有限制性商业许可,这意味着该模型不能用于商业目的。对于许多人来说,这不符合开源的真正定义,开源模型的使用不应有任何限制。类似地,训练模型所用的数据及其源代码也很少共享。

如图 \(\text{1-32}\) 所示,只要您拥有可以处理这类模型的强大 \(\text{GPU}\),您就可以下载这些模型并在您的设备上使用它们。

F1.1

这些本地模型的一个主要优势是您(用户)对模型拥有完全的控制权。您可以在不依赖 \(\text{API}\) 连接的情况下使用模型、微调它,并通过它运行敏感数据。您不依赖任何服务,并且对导致模型输出的过程拥有完全的透明度。这种优势得益于使这些过程成为可能的庞大社区,例如 \(\text{Hugging Face}\),展示了协作努力的可能性。

缺点是您需要强大的硬件来运行这些模型,并且在训练或微调它们时需要更多。此外,设置和使用这些模型需要特定的知识(我们将在本书中介绍)。

我们通常更倾向于使用开源模型。与使用专有 \(\text{LLM}\) 相比,它赋予的自由度——可以尝试各种选项、探索内部工作原理以及在本地使用模型——可以说提供了更多的益处。

开源框架

Open Source Frameworks

与闭源 \(\text{LLM}\) 相比,开源 \(\text{LLM}\) 要求您使用特定的软件包来运行它们。在 \(\text{2023}\) 年,发布了许多不同的软件包和框架,它们都以自己的方式与 \(\text{LLM}\) 交互并利用 \(\text{LLM}\)。在数百个可能有价值的框架中摸索并不是最愉快的体验。

因此,您甚至可能会在本书中错过您最喜欢的框架!

我们没有试图涵盖现有的每一个 \(\text{LLM}\) 框架(框架太多,而且数量还在持续增长),而是旨在为您提供利用 \(\text{LLM}\) 的坚实基础。我们的想法是,阅读本书后,您可以轻松掌握大多数其他框架,因为它们的工作方式都非常相似。

我们试图实现的直觉是其中的重要组成部分。如果您不仅对 \(\text{LLM}\) 有直观的理解,而且对在实践中使用常用框架也有直观的理解,那么转向其他框架应该是一项简单明了的任务。

更具体地说,我们专注于后端软件包。这些是没有 \(\text{GUI}\)(图形用户界面)的软件包,旨在高效地加载和运行您设备上的任何 \(\text{LLM}\),例如 \(\text{llama.cpp}\)\(\text{LangChain}\) 以及许多框架的核心——\(\text{Hugging Face Transformers}\)

我们将主要介绍通过代码与大型语言模型交互的框架。尽管这有助于您学习这些框架的基础知识,但有时您可能只想要一个带有本地 \(\text{LLM}\) 的类似 \(\text{ChatGPT}\) 的界面。幸运的是,有许多出色的框架可以实现这一点。一些示例包括 \(\text{text-generation-webui}\)\(\text{KoboldCpp}\)\(\text{LM Studio}\)

生成您的第一个文本

Generating Your First Text

使用语言模型的一个重要组成部分是选择它们。查找和下载 \(\text{LLM}\) 的主要来源是 \(\text{Hugging Face Hub}\)\(\text{Hugging Face}\) 是著名 \(\text{Transformers}\) 软件包背后的组织,多年来一直推动着语言模型的普遍发展。顾名思义,该软件包是基于我们在“语言 \(\text{AI}\) 的近代史”中讨论过的 \(\text{Transformer}\) 框架构建的。

在撰写本文时,您将在 \(\text{Hugging Face}\) 的平台上找到超过 \(\text{800,000}\) 个模型,它们用于许多不同的目的,从 \(\text{LLM}\) 和计算机视觉模型到处理音频和表格数据的模型。在这里,您可以找到几乎任何开源 \(\text{LLM}\)

虽然我们将在本书中探索各种模型,但让我们从生成模型开始编写我们的第一行代码。我们在本书中使用的主要生成模型是 \(\text{Phi}-3-\text{mini}\),这是一个相对较小(\(\text{38}\) 亿参数)但性能相当不错的模型。由于其体积小,该模型可以在 \(\text{VRAM}\) 少于 \(\text{8 GB}\) 的设备上运行。如果您执行量化\(\text{quantization}\)),这是一种我们将在\(\text{7}\)\(\text{12}\)中进一步讨论的压缩技术,您甚至可以使用少于 \(\text{6 GB}\)\(\text{VRAM}\)。此外,该模型根据 \(\text{MIT}\) 许可证授权,允许该模型不受限制地用于商业目的

请记住,新的、改进的 \(\text{LLM}\) 会频繁发布。为了确保本书保持最新,大多数示例都设计为可与任何 \(\text{LLM}\) 配合使用。我们还将在与本书相关的代码库中重点介绍不同的模型供您试用。

让我们开始吧!当您使用一个 \(\text{LLM}\) 时,会加载两个模型

  • 生成模型本身
  • 其底层的分词器\(\text{tokenizer}\)

分词器负责将输入文本分割成词元\(\text{tokens}\)),然后再将其馈送给生成模型。您可以在 \(\text{Hugging Face}\) 网站上找到分词器和模型,并且只需要传递相应的 \(\text{ID}\)。在这种情况下,我们使用 “\(\text{microsoft/Phi}-3-\text{mini}-4\text{k}-\text{instruct}\)” 作为模型的主路径。

我们可以使用 \(\text{transformers}\) 来加载分词器和模型。请注意,我们假设您有一个 \(\text{NVIDIA}\) \(\text{GPU}\)\(\text{device\_map="cuda"}\)),但您可以选择不同的设备。如果您无法访问 \(\text{GPU}\),可以使用我们在本书代码库中提供的免费 \(\text{Google Colab}\) 笔记本

1
2
3
4
5
6
7
8
9
from transformers import AutoModelForCausalLM, AutoTokenizer
# Load model and tokenizer
model = AutoModelForCausalLM.from_pretrained(
"microsoft/Phi-3-mini-4k-instruct",
device_map="cuda",
torch_dtype="auto",
trust_remote_code=True,
)
tokenizer = AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct")

运行代码将开始下载模型,根据您的互联网连接速度,可能需要几分钟。

虽然我们现在有足够的条件开始生成文本,但 \(\text{transformers}\) 中有一个可以简化该过程的巧妙技巧,即 \(\text{transformers.pipeline}\)。它将模型、分词器和文本生成过程封装到一个函数中:

1
2
3
4
5
6
7
8
9
10
from transformers import pipeline
# Create a pipeline
generator = pipeline(
"text-generation",
model=model,
tokenizer=tokenizer,
return_full_text=False,
max_new_tokens=500,
do_sample=False
)

以下参数值得一提:

\(\text{return\_full\_text}\) 通过将其设置为 \(\text{False}\)不会返回提示本身,而仅仅返回模型的输出。

\(\text{max\_new\_tokens}\) 模型将生成的最大词元数。通过设置限制,我们可以防止冗长且难以驾驭的输出,因为某些模型可能会持续生成输出直到达到它们的上下文窗口。

\(\text{do\_sample}\) 模型是否使用采样策略来选择下一个词元。通过将其设置为 \(\text{False}\),模型将始终选择下一个最有可能的词元。在\(\text{6}\)中,我们将探讨几个可以激发模型输出中创造性的采样参数。

为了生成我们的第一个文本,让我们指示模型讲一个关于鸡的笑话。为此,我们将提示格式化为一个字典列表,其中每个字典都与对话中的一个实体相关。我们的角色是“\(\text{user}\)”(用户),我们使用“\(\text{content}\)”(内容)键来定义我们的提示:

1
2
3
4
5
6
7
# The prompt (user input / query)
messages = [
{"role": "user", "content": "Create a funny joke about chickens."}
]
# Generate output
output = generator(messages)
print(output[0]["generated_text"])
1
Why don't chickens like to go to the gym? Because they can't crack the egg-sistence of it!

就是这样!本书中生成的第一个文本是一个关于鸡的不错的笑话。

总结

在本书的第一章中,我们深入探讨了 \(\text{LLM}\) 对语言 \(\text{AI}\) 领域带来的革命性影响。它显著改变了我们处理翻译、分类、摘要等任务的方法。通过回顾语言 \(\text{AI}\) 的近代史,我们探索了几种 \(\text{LLM}\) 的基本原理,从简单的词袋模型表征到使用神经网络的更复杂的表征。

我们讨论了注意力机制作为在模型中编码上下文的一个步骤,它是使 \(\text{LLM}\) 如此强大的关键组成部分。我们涉及了使用这种惊人机制的两个主要模型类别:像 \(\text{BERT}\) 一样的表征模型(仅编码器),以及像 \(\text{GPT}\) 系列模型一样的生成模型(仅解码器)。在本书中,这两个类别都被视为大型语言模型

总的来说,本章概述了语言 \(\text{AI}\) 的全景,包括其应用、社会和伦理影响,以及运行此类模型所需的资源。最后,我们使用 \(\text{Phi}-3\)(一个将在本书中使用的模型)生成了我们的第一个文本。

在接下来的两章中,您将了解一些底层过程。我们首先在\(\text{2}\)中探索分词嵌入,这两个经常被低估但对语言 \(\text{AI}\) 领域至关重要的组成部分。紧随其后的是\(\text{3}\)对语言模型的深入探究,您将在其中发现用于生成文本的精确方法。

书籍各章的机翻md文件:
《Hands-On Large Language Models》目录及前言
《Hands-On Large Language Models》第1章 大型语言模型简介
《Hands-On Large Language Models》第2章 词元与嵌入
《Hands-On Large Language Models》第3章 深入了解大型语言模型
《Hands-On Large Language Models》第4章 文本分类
《Hands-On Large Language Models》第5章 文本聚类和主题建模
《Hands-On Large Language Models》第6章 提示工程
《Hands-On Large Language Models》第7章 高级文本生成技术与工具
《Hands-On Large Language Models》第8章 语义搜索与检索增强生成
《Hands-On Large Language Models》第9章 多模态大型语言模型
《Hands-On Large Language Models》第10章 创建文本嵌入模型
《Hands-On Large Language Models》第11章 微调用于分类的表征模型
《Hands-On Large Language Models》第12章 生成模型的微调