LLM工程师手册——工具与安装

598 阅读33分钟

本章介绍将在全书中使用的所有必要工具,特别是在实现和部署LLM双生子项目中。在本书的这一部分,我们不打算深入讲解LLM、RAG、MLOps或LLMOps概念,而是快速带你浏览我们的技术栈和前置条件,以避免在书中重复讲解工具的设置方式及选择理由。从第3章开始,我们将通过实现一个ETL数据收集管道来爬取互联网数据,从而正式展开LLM双生子的使用案例探索。

在本章的第一部分,我们将介绍Python生态系统中的工具,用于管理多个Python版本、创建虚拟环境并安装项目运行所需的固定依赖项。我们还将展示如何在本地机器上安装 LLM-Engineers-Handbook 仓库(如果你想自己尝试代码):github.com/PacktPublis…

接下来,我们将探讨所有使用的MLOps和LLMOps工具,从通用工具(如模型注册表)开始,逐步转向更偏向LLM的工具,如LLM评估和提示监控工具。我们还将了解如何使用ZenML管理拥有多个ML管道的项目,这个编排器弥合了ML和MLOps之间的差距。此外,我们将快速浏览用于NoSQL和向量存储的数据库,并展示如何使用Docker在本地机器上运行所有这些组件。最后,我们会简要回顾AWS,展示如何创建AWS用户和访问密钥,并安装和配置AWS CLI,以编程方式管理云资源。我们还将介绍SageMaker,以及为什么使用它来训练和部署我们的开源LLM。

如果你熟悉这些工具,可以跳过本章。我们也在仓库的README中解释了项目的安装方法和所有必要组件的设置,因此如果你打算在阅读本书时运行代码,也可以将README作为更简洁的参考文档。

简而言之,本章将涵盖以下主题:

  • Python生态系统和项目安装
  • MLOps和LLMOps工具
  • 用于存储非结构化数据和向量数据的数据库
  • AWS的准备工作

在本章结束时,你将了解本书中将使用的所有工具,并且学会如何安装LLM-Engineers-Handbook 仓库、设置其余工具,以及如何在阅读书籍时运行代码。

Python生态系统和项目安装

任何Python项目都需要三个基本工具:Python解释器、依赖管理工具和任务执行工具。Python解释器用于执行Python项目中的代码。本书中的所有代码都在Python 3.11.8版本下进行了测试。你可以从此处下载Python解释器:www.python.org/downloads/。我们建议使用pyenv安装指定的Python版本(Python 3.11.8),使安装过程更加简便。

为了避免安装多个全局Python版本,我们建议使用pyenv来管理它们。pyenv是一款Python版本管理工具,可让你在不同项目之间管理多个Python版本。你可以通过以下链接安装它:github.com/pyenv/pyenv…

安装pyenv后,可以使用以下命令通过pyenv安装最新的Python 3.11版本:

pyenv install 3.11.8

接下来,列出所有已安装的Python版本,确认安装是否成功:

pyenv versions

你应该看到类似以下的输出:

* system
  3.11.8

若要在整个系统中默认使用Python 3.11.8(每次打开新终端时),请使用以下命令:

pyenv global 3.11.8

然而,我们只希望在本地的项目库中使用Python 3.11.8版本。为此,首先需要克隆项目库并进入该目录:

git clone https://github.com/PacktPublishing/LLM-Engineers-Handbook.git 
cd LLM-Engineers-Handbook

因为我们在库中定义了一个.python-version文件,pyenv会从该文件中读取版本信息,因此只要在此文件夹中工作,pyenv就会自动使用该版本。为了确认,进入该项目库后运行以下命令:

python --version

应该输出:

Python 3.11.8

如果你想创建.python-version文件,可以在目录中运行以下命令:

pyenv local 3.11.8

然后,pyenv在这个特定目录中工作时会一直使用该Python版本。

现在,我们已使用pyenv安装了正确的Python版本,接下来可以进入Poetry的安装,它将作为我们的依赖和虚拟环境管理工具。

Poetry:依赖和虚拟环境管理

Poetry是Python生态系统中最流行的依赖和虚拟环境管理工具之一。让我们先澄清什么是依赖管理工具。在Python中,依赖管理工具允许你指定、安装、更新和管理项目所依赖的外部库或包。例如,以下是一个简单的Poetry依赖文件,使用Python 3.11并包含requestsnumpy包。

[tool.poetry.dependencies]
python = "^3.11"
requests = "^2.25.1"
numpy = "^1.19.5"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

使用Poetry固定依赖版本,确保始终安装项目所需的正确依赖版本。Poetry默认将所有依赖项保存在pyproject.toml文件中,存储在项目根目录,例如克隆的LLM-Engineers-Handbook仓库。

使用Poetry的另一个重要优势是它会创建一个新的Python虚拟环境,并在其中安装指定的Python版本和依赖项。虚拟环境允许将项目的依赖与全局Python依赖和其他项目隔离,防止项目间的版本冲突。例如,假设项目A需要numpy==1.19.5,项目B需要numpy==1.26.0。如果将两个项目放在全局Python环境中运行会导致冲突,而Poetry能确保每个项目在其独立环境中运行,避免依赖冲突。

你可以从python-poetry.org/docs/下载Poetry。我们在本书中使用Poetry 1.8.3版本。安装Poetry后,进入克隆的LLM-Engineers-Handbook仓库,并运行以下命令安装所需的Python依赖项:

poetry install --without aws

该命令会从仓库中的pyproject.tomlpoetry.lock文件中提取所有依赖项。安装完成后,可以通过运行poetry shell激活Poetry环境,或在命令前加上poetry run前缀,如下所示:

poetry run <your command>

Poetry的一个重要特点是锁定依赖树的确切版本信息,记录在poetry.lock文件中。虽然pyproject.toml文件中可能指定了版本范围(例如requests = "^2.25.1"),但poetry.lock文件会记录确切的版本(如requests = "2.25.1")以及子依赖项的版本,从而确保所有安装的项目使用相同的依赖版本。这种一致性可以减少“只在我机器上有效”的问题。

其他类似工具有Venv和Conda,但它们缺乏依赖管理功能,需要使用Python的requirements.txt文件,相比Poetry的锁文件功能较弱。Pipenv在功能上接近Poetry,但速度较慢,uv是一个使用Rust构建的替代Poetry的工具,运行速度极快,值得一试:github.com/astral-sh/u…

Poe the Poet:任务执行工具

Poe the Poet是一个Poetry插件,用于管理和执行与项目交互所需的CLI命令。它可以帮助你在Python项目中定义和运行任务,简化自动化和脚本执行。其他常见选择有Makefile、Invoke或shell脚本,但Poe the Poet使得不再需要为项目任务编写单独的shell脚本或Makefile,可以通过Poetry的配置文件直接管理任务。

使用Poe the Poet时,你可以将所有命令添加到pyproject.toml文件中,并使用命令别名执行。例如,我们可以在pyproject.toml中定义以下任务:

[tool.poe.tasks]
test = "pytest"
format = "black ."
start = "python main.py"

然后可以使用poe命令运行这些任务:

poetry poe test
poetry poe format
poetry poe start

安装Poe the Poet作为Poetry插件的命令如下:

poetry self add 'poethepoet[poetry_plugin]'

总结来说,使用一个工具来封装所有CLI命令有助于简化应用程序复杂性,增强协作性,相当于开箱即用的文档。

假设你已安装pyenv和Poetry,以下是克隆仓库并安装依赖项及Poe the Poet的所有命令:

git clone https://github.com/PacktPublishing/LLM-Engineers-Handbook.git
cd LLM-Engineers-Handbook
poetry install --without aws
poetry self add 'poethepoet[poetry_plugin]'

为了使项目完全运行,还需完成一些步骤,例如在.env文件中填写凭证并获取OpenAI和Hugging Face的令牌。不过,本书并非安装指南,这些详细信息已移至仓库的README中,供有意运行代码的读者参考:github.com/PacktPublis…

现在我们已经完成了Python项目的安装,接下来介绍本书中将使用的MLOps工具。如果你对这些工具已经熟悉,可以跳过以下工具部分,直接进入“存储非结构化数据和向量数据的数据库”部分。

MLOps和LLMOps工具

本节将快速介绍全书中使用的所有MLOps和LLMOps工具及其在使用MLOps最佳实践构建ML系统中的作用。在本书的这一部分,我们不打算详细介绍用于实现LLM双生子项目的所有MLOps组件(如模型注册表和编排器),而是简要介绍它们的功能和用途。随着本书对LLM双生子项目的开发,你将看到我们如何实际应用这些工具。在第11章中,我们将深入探讨MLOps和LLMOps的理论,并将所有内容串联起来。由于MLOps和LLMOps领域高度实用,因此我们将理论留到最后,这样在经历LLM双生子项目的实现后会更容易理解。

此外,本节不专门演示如何设置每个工具,主要关注每个工具的用途,并强调在本书中使用的核心功能。

使用Docker,你可以快速在本地运行整个基础设施。若你希望自行执行书中的步骤,可以通过以下三步在本地托管应用程序:

  1. 安装Docker 27.1.1或更高版本。
  2. 按仓库README中的说明,在.env文件中填写所有必要的凭证。
  3. 运行poetry poe local-infrastructure-up以在本地启动ZenML(http://127.0.0.1:8237/)和MongoDB及Qdrant数据库。

关于如何在本地运行所有内容的更多细节,可参考LLM-Engineers-Handbook仓库的README:github.com/PacktPublis…。在书中,我们还将展示如何将每个组件部署到云端。

Hugging Face:模型注册表

模型注册表是一个集中式的存储库,用于管理ML模型的整个生命周期。它存储模型及其元数据、版本历史和性能指标,作为统一的“事实来源”。在MLOps中,模型注册表对于跟踪、共享和记录模型版本至关重要,能够促进团队协作。同时,它是部署过程中的关键元素,可与持续集成和持续部署(CI/CD)管道集成。

我们使用Hugging Face作为我们的模型注册表,因为它的生态系统可以让我们轻松地将微调的LLM双生子模型分享给阅读本书的用户。此外,通过遵循Hugging Face的模型注册表接口,我们可以轻松地将模型集成到LLM生态系统中的各种框架中,例如用于微调的Unsloth和用于推理的SageMaker。

我们的微调LLM可以在Hugging Face上找到:

image.png

为了方便演示,我们在Hugging Face Spaces上提供了这些模型:

许多ML工具提供模型注册表功能,例如ZenML、Comet和SageMaker(我们将在后续章节中介绍)也各自拥有模型注册表。尽管它们是不错的选择,我们选择Hugging Face主要是因为其生态系统,便于在开源环境中轻松共享和集成。因此,你通常会选择与项目工具和需求集成最佳的模型注册表。

ZenML:编排器、工件和元数据

ZenML充当ML与MLOps之间的桥梁,提供多种MLOps功能,以便更轻松地实现ML管道的可追溯性、可复现性、部署和维护。ZenML的核心设计目的是创建可复现的机器学习工作流,解决从Jupyter笔记本中的探索性研究过渡到生产环境的挑战。它应对生产中的复现性问题,如版本管理困难、实验复现、复杂的ML工作流组织、训练与部署间的桥接以及元数据的跟踪。因此,ZenML的主要功能包括编排ML管道、存储和版本化ML管道输出,以及将元数据附加到工件上以实现更好的可观察性。

与传统ML平台不同,ZenML引入了“堆栈”(stack)的概念,允许你在多个基础设施选项上运行ZenML。堆栈可将ZenML连接到不同的云服务,例如:

  • 编排器和计算引擎(例如AWS SageMaker或Vertex AI)
  • 远程存储(例如AWS S3或Google Cloud Storage存储桶)
  • 容器注册表(例如Docker Registry或AWS ECR)

因此,ZenML通过其堆栈功能将所有基础设施和工具整合在一个地方,使开发过程迭代更快,并易于监控整个ML系统。其优势在于,ZenML不会将你绑定到特定云平台,完全将Python代码的实现与运行基础设施抽象分离。例如,在我们的LLM双生子项目中,我们使用了AWS堆栈:

  • SageMaker作为编排器和计算资源
  • S3作为远程存储,用于存储和追踪工件
  • ECR作为容器注册表

但是,Python代码中没有S3或ECR的特定实现细节,因为ZenML处理了这些部分。因此,我们可以轻松切换到其他提供商,如Google Cloud Storage或Azure。关于ZenML堆栈的更多详细信息,可以在此处开始:docs.zenml.io/user-guide/…

我们将重点介绍本书中使用的ZenML功能,如编排、工件和元数据。有关ZenML的更多信息,请查看他们的入门指南:docs.zenml.io/user-guide/…

ZenML的本地服务器版本作为Python包安装。因此,运行poetry install时会安装一个ZenML调试服务器,可供本地使用。在第11章中,我们将展示如何使用其无服务器云选项将ML管道部署到AWS。

编排器

编排器是一种自动化、调度和协调所有ML管道的系统。它确保每个管道(如数据引入、预处理、模型训练和部署)按正确的顺序执行,并高效处理依赖关系。通过管理这些流程,编排器能够优化资源利用率、优雅地处理故障并增强可扩展性,使得复杂的ML管道更可靠且易于管理。

ZenML如何作为编排器工作?它使用管道和步骤的概念。管道是包含多个步骤的高级对象。使用@pipeline装饰器可以将一个函数定义为ZenML管道,而使用@step装饰器可以将函数定义为步骤。这是使用编排器时的标准模式:定义一个高级函数(通常称为管道),并在其中调用多个单元/步骤/任务。

让我们看看如何使用ZenML实现一个管道,以下代码展示了为LLM双生子项目实现的一个ML管道。代码片段中,我们定义了一个ZenML管道,该管道根据用户名在数据库中查询用户信息,并爬取该用户提供的所有链接:

from zenml import pipeline
from steps.etl import crawl_links, get_or_create_user

@pipeline
def digital_data_etl(user_full_name: str, links: list[str]) -> None:
    user = get_or_create_user(user_full_name)
    crawl_links(user=user, links=links)

你可以通过以下CLI命令运行该管道:

poetry poe run-digital-data-etl

要可视化管道运行情况,可以访问ZenML仪表板(http://127.0.0.1:8237/),在左侧面板点击“Pipelines”标签,再点击“digital_data_etl”管道,如图2.2所示。

image.png

点击“digital_data_etl”管道后,你可以看到所有先前和当前的管道运行情况,如图2.3所示。你可以查看哪些运行成功、失败或仍在进行中。此外,你还可以看到用于运行该管道的堆栈,默认堆栈用于在本地运行你的ML管道。

image.png

现在,点击最新的“digital_data_etl”管道运行(或任何成功或仍在运行的任务),你可以查看管道的步骤、输出和见解,如图2.4所示。这种结构通常称为有向无环图(DAG)。有关DAG的更多内容将在第11章中介绍。

image.png

点击特定步骤后,你可以深入查看其代码和配置,还可以查看该步骤的日志输出,从而避免在不同工具之间切换,如图2.5所示。

image.png

现在我们了解了如何定义一个ZenML管道并在仪表板中查看它,接下来快速了解如何定义一个ZenML步骤。在下面的代码片段中,我们定义了get_or_create_user()步骤,它与普通的Python函数一样,只是加上了@step装饰器。我们不会详细讲解逻辑细节,因为将在第3章中讨论ETL逻辑。目前,我们仅关注ZenML的功能。

from loguru import logger
from typing_extensions import Annotated
from zenml import get_step_context, step
from llm_engineering.application import utils
from llm_engineering.domain.documents import UserDocument

@step
def get_or_create_user(user_full_name: str) -> Annotated[UserDocument, "user"]:
    logger.info(f"Getting or creating user: {user_full_name}")
    first_name, last_name = utils.split_user_full_name(user_full_name)
    user = UserDocument.get_or_create(first_name=first_name, last_name=last_name)
    return user

在ZenML步骤中,你可以定义任何Python逻辑。这个简单示例中,我们只是创建或检索用户信息,但可以替换成任何其他操作,例如数据收集、特征工程和训练。需要注意的是,要将ZenML集成到代码中,需要编写模块化代码,使每个函数只完成一件事。模块化代码便于使用@step装饰器修饰各函数,然后在使用@pipeline装饰器的主函数中将多个步骤组合在一起。决定每个步骤的粒度是一个重要的设计选择,因为在云端部署时,每个步骤将作为不同的单元在不同机器上运行。

为解耦代码与ZenML,我们将所有应用逻辑和领域逻辑封装到llm_engineering Python模块中,同时定义了pipelinessteps文件夹,用于存放ZenML逻辑。在steps模块中,我们只引用了llm_engineering Python模块中所需的内容(类似于使用Python包)。在pipelines模块中,我们仅将ZenML步骤聚合起来构成最终管道。采用此设计,我们可以轻松地将ZenML替换为其他编排器,或将应用逻辑用于其他用例(例如REST API)。只需替换ZenML代码,而无需修改包含全部逻辑的llm_engineering模块。

这种文件夹结构在LLM-Engineers-Handbook仓库的根目录中反映,如图2.6所示。

image.png

在编写ZenML步骤时需要考虑的最后一点是,如果返回一个值,它必须是可序列化的。ZenML可以序列化大多数能简化为基本数据类型的对象,但有一些例外。例如,我们在代码中使用了UUID类型作为ID,而UUID并非ZenML原生支持的类型。因此,我们需要扩展ZenML的materializer来支持UUID。我们已经将该问题反馈给ZenML团队,未来版本将支持UUID,但这也是一个关于将函数输出转换为工件的序列化方面的良好示例。

工件和元数据

正如前一节提到的,ZenML会将任何步骤的输出转换为工件。首先,快速理解一下什么是工件。在MLOps中,工件是机器学习生命周期中生成的任何文件,例如数据集、训练模型、检查点或日志。工件对于复现实验和部署模型至关重要。我们可以将任何东西转换为工件。例如,模型注册表是工件的一个特殊用例。因此,工件具有以下独特属性:它们是版本化的、可共享的,并附带元数据,以便快速理解内容。例如,当将数据集包装为工件时,可以在其元数据中添加数据集大小、训练-测试分割比、标签类型等信息,使人无需下载即可了解数据集内容。

让我们回到digital_data_etl管道示例,其中的步骤输出为爬取的链接,这些链接作为工件输出,如图2.7所示。

image.png

点击crawled_links工件并导航到“Metadata”标签,我们可以快速查看为特定作者爬取的所有域名、每个域名下爬取的链接数量以及成功爬取的数量,如图2.8所示。

image.png

一个更有趣的工件及其元数据示例是生成的数据集工件。在图2.9中,我们可以查看instruct_datasets工件的元数据,它是自动生成的,将用于微调LLM双生子模型。关于指令数据集的更多细节将在第5章中讨论。目前,我们想强调的是,在数据集的元数据中预先计算了许多有用的信息,例如包含的数据类别数量、存储大小,以及训练和测试分割中的样本数量。

image.png

元数据是手动添加到工件中的,如下面的代码片段所示。因此,你可以预先计算并将任何对业务和项目中的数据集发现有帮助的信息附加到工件的元数据中:

# 更多的导入
from zenml import ArtifactConfig, get_step_context, step

@step
def generate_instruction_dataset(
    prompts: Annotated[dict[DataCategory, list[GenerateDatasetSamplesPrompt]], "prompts"]) -> Annotated[
    InstructTrainTestSplit,
    ArtifactConfig(
        name="instruct_datasets",
        tags=["dataset", "instruct", "cleaned"],
    ),
]:
    datasets = … # 生成数据集
    step_context = get_step_context()
    step_context.add_output_metadata(output_name="instruct_datasets", metadata=_get_metadata_instruct_dataset(datasets))
    return datasets

def _get_metadata_instruct_dataset(datasets: InstructTrainTestSplit) -> dict[str, Any]:
    instruct_dataset_categories = list(datasets.train.keys())
    train_num_samples = {
        category: instruct_dataset.num_samples for category, instruct_dataset in datasets.train.items()
    }
    test_num_samples = {category: instruct_dataset.num_samples for category, instruct_dataset in datasets.test.items()}
    return {
        "data_categories": instruct_dataset_categories,
        "test_split_size": datasets.test_split_size,
        "train_num_samples_per_category": train_num_samples,
        "test_num_samples_per_category": test_num_samples,
    }

此外,你可以使用其通用唯一标识符(UUID)轻松下载和访问数据集的特定版本,UUID可以在ZenML仪表板或CLI中找到:

from zenml.client import Client
artifact = Client().get_artifact_version('8bba35c4-8ff9-4d8f-a039-08046efc9fdc')
loaded_artifact = artifact.load()

探索ZenML的最后一步是了解如何运行和配置ZenML管道。

如何运行和配置ZenML管道

所有的ZenML管道都可以通过run.py文件调用,该文件位于我们的GitHub仓库的tools/run.py中。在run.py文件中,我们实现了一个简单的CLI,允许指定要运行的管道。例如,要调用digital_data_etl管道来爬取Maxime的内容,可以运行:

python -m tools.run --run-etl --no-cache --etl-config-filename digital_data_etl_maxime_labonne.yaml

或要爬取Paul的内容,可以运行:

python -m tools.run --run-etl --no-cache --etl-config-filename digital_data_etl_paul_iusztin.yaml

正如介绍Poe the Poet时所述,我们所有与项目交互的CLI命令都将通过Poe来执行,以简化和标准化项目。因此,我们将这些Python调用封装在以下Poe CLI命令下:

poetry poe run-digital-data-etl-maxime
poetry poe run-digital-data-etl-paul

我们仅需在抓取不同人员内容时更改ETL配置文件的名称。ZenML允许我们在运行时注入特定的配置文件,方法如下:

config_path = root_dir / "configs" / etl_config_filename
assert config_path.exists(), f"Config file not found: { config_path }"
run_args_etl = {
    "config_path": config_path,
    "run_name": f"digital_data_etl_run_{dt.now().strftime('%Y_%m_%d_%H_%M_%S')}"
}
digital_data_etl.with_options()(**run_args_etl)

在配置文件中,我们指定了作为参数输入管道的所有参数。例如,configs/digital_data_etl_maxime_labonne.yaml配置文件如下所示:

parameters:
  user_full_name: Maxime Labonne # [名] [姓]
  links:
    # 个人博客
    - https://mlabonne.github.io/blog/posts/2024-07-29_Finetune_Llama31.html
    - https://mlabonne.github.io/blog/posts/2024-07-15_The_Rise_of_Agentic_Data_Generation.html
    # Substack
    - https://maximelabonne.substack.com/p/uncensor-any-llm-with-abliteration-d30148b7d43e
    … # 更多链接

digital_data_etl函数签名如下:

@pipeline
def digital_data_etl(user_full_name: str, links: list[str]) -> str:

这种方法允许我们在运行时配置每个管道而无需修改代码,同时可以清晰地追踪所有管道的输入,确保可复现性。如图2.10所示,每个管道都有一个或多个配置文件。

image.png

其他与ZenML相似、功能强大的流行编排器包括Airflow、Prefect、Metaflow和Dagster。此外,如果你是Kubernetes的重度用户,可以选择Argo Workflows或Kubeflow(后者仅在Kubernetes上运行)。我们仍然认为ZenML在易用性、功能和成本之间达到了最佳平衡。此外,这些工具中没有一个提供ZenML的“堆栈”功能,使其能够避免绑定到任何特定云生态系统。

在第11章中,我们将深入探讨如何利用编排器来实现MLOps最佳实践。但既然我们已经了解了ZenML的用途和使用方法,接下来让我们继续学习实验追踪器。

Comet ML:实验追踪器

训练ML模型是一个完全迭代和实验性的过程。与传统软件开发不同,它需要运行多个并行实验,根据预定义的指标进行比较,并决定哪一个模型应进入生产。实验追踪工具允许记录所有必要的信息,如指标和模型预测的可视化,以便比较所有实验并快速选择最佳模型。我们的LLM项目也不例外。

如图2.11所示,我们使用Comet来追踪所有实验中的训练和评估损失等指标,以及梯度范数的值。

image.png

使用实验追踪器,你不仅可以记录训练和评估指标,还可以记录训练超参数,以便在实验之间跟踪不同的配置。

此外,实验追踪器还能自动记录系统指标,如GPU、CPU和内存的使用情况,让你清晰了解训练所需的资源以及可能导致训练速度变慢的瓶颈,如图2.12所示。

image.png

你无需在本地设置Comet。我们将在本书中免费使用其在线版本,无任何限制。此外,如果你想更深入地了解Comet ML实验追踪器,我们在微调LLM双生子模型时公开了使用Comet ML记录的训练实验。可以在此访问:www.comet.com/mlabonne/ll…

其他流行的实验追踪器包括W&B、MLflow和Neptune。我们都使用过这些工具,它们功能大致相同,但Comet ML因其易用性和直观的界面而脱颖而出。接下来,我们将探讨MLOps的最后一部分:Opik提示监控。

Opik:提示监控

在记录和监控提示时,无法使用标准工具和技术。原因较为复杂,我们将在第11章深入探讨。不过,简单解释一下,不能使用标准日志工具的原因在于提示是复杂且非结构化的链条。

在与LLM应用交互时,你会将多个输入提示和生成的输出链接为一个轨迹,其中一个提示依赖于先前的提示。因此,你需要一种直观的方法将这些轨迹分组到专用仪表板中,以便更轻松地调试和监控提示轨迹,而非简单的文本日志。

我们使用了Opik,这是由Comet开发的开源提示监控工具,因其简洁和易用的理念,正适合当前LLM环境。其他具有类似功能的选择包括Langfuse(开源,langfuse.com)、Galileo(不开源,rungalileo.io)和LangSmith(不开源,www.langchain.com/langsmith),但我们发现它们的使用和实现更为复杂。Opik提供了免费的开源版本,并包含无服务器选项,你可以完全控制。关于Opik的更多信息可在此阅读:github.com/comet-ml/op…

用于存储非结构化数据和向量数据的数据库

我们还将介绍在示例中使用的NoSQL和向量数据库。在本地工作时,这些数据库已通过Docker集成。因此,按照上几节中的指示运行poetry poe local-infrastructure-up时,本地的Docker将自动拉取并运行这两个数据库的镜像。此外,当部署项目时,我们将展示如何使用它们的无服务器选项,并与LLM双生子项目的其余部分集成。

MongoDB:NoSQL数据库

MongoDB是当前最受欢迎、强大、快速且功能丰富的NoSQL数据库之一。它与大多数云生态系统(如AWS、Google Cloud、Azure和Databricks)集成良好。因此,选择MongoDB作为我们的NoSQL数据库是显而易见的。

在撰写本书时,MongoDB已被Novo Nordisk、Delivery Hero、Okta和沃尔沃等大型公司采用。MongoDB的广泛应用表明,它将在很长一段时间内保持领先的NoSQL数据库地位。

我们使用MongoDB作为NoSQL数据库来存储从互联网收集的原始数据,然后在处理后将其推送到向量数据库中。由于我们处理的是非结构化文本数据,NoSQL数据库的灵活性非常适合这一用途。

Qdrant:向量数据库

Qdrant(qdrant.tech/)是当前最受欢迎、强大且功能丰富的向量数据库之一。虽然我们的小型MVP项目几乎可以使用任何向量数据库,但我们希望选择一个轻量级且未来几年可能在行业中持续使用的数据库。

我们将使用Qdrant来存储从MongoDB处理和转换后的数据,以便用于生成式AI(GenAI)的应用。

Qdrant被X(原Twitter)、迪士尼、微软、Discord以及强生等大公司采用,说明Qdrant很可能会在向量数据库领域占据一席之地。

在本书撰写时,其他流行的选择包括Milvus、Redis、Weaviate、Pinecone、Chroma和pgvector(用于向量索引的PostgreSQL插件)。我们发现Qdrant在每秒请求数(RPS)、延迟和索引时间方面提供了最佳的平衡,使其成为许多生成式AI应用的可靠选择。

详细比较所有向量数据库本身就可以写成一章内容,我们在此不作深入讨论。不过,如果你感兴趣,可以查看Superlinked的向量数据库比较资源:superlinked.com/vector-db-c…,该资源从许可证、发布年份到数据库功能、嵌入模型和支持的框架等各方面对顶级向量数据库进行了比较。

准备AWS

本章的最后部分将重点介绍如何设置AWS账户(如果还没有的话)、AWS访问密钥以及CLI。此外,我们还将了解SageMaker的用途以及为什么选择它。

我们选择AWS作为云提供商,因为它是当前最流行的云平台,并且是我们(本书作者)最有经验的云平台。实际上,其他大型云提供商(如GCP或Azure)也提供类似的服务。因此,具体的应用选择通常取决于开发时间(取决于经验丰富的云平台)、功能和成本之间的权衡。但对于我们的MVP来说,AWS是一个完美的选择,因为它为我们所需的所有服务提供了强大的功能,例如S3(对象存储)、ECR(容器注册表)和SageMaker(用于训练和推理的计算资源)。

设置AWS账户、访问密钥和CLI

由于AWS界面可能会发生变化,最好的指导方式是将你引导至AWS的官方教程:docs.aws.amazon.com/accounts/la…

成功创建AWS账户后,可以访问AWS控制台:console.aws.amazon.com。选择“Sign in using root user email”(在登录按钮下方找到),然后输入账户的电子邮件地址和密码。

接下来,我们需要生成访问密钥以便通过编程方式访问AWS。最好的方法是首先创建具有管理员访问权限的IAM用户,详细步骤请参见AWS官方教程:docs.aws.amazon.com/streams/lat…

对于生产账户,最佳实践是通过最小权限策略授予权限,即仅给予用户执行其角色所需的权限。然而,为简化测试账户的设置,我们将使用AdministratorAccess托管策略,授予用户完整访问权限,如上述教程所述并如图2.13所示。

image.png

接下来,你需要为刚创建的IAM用户创建访问密钥,操作步骤请参见此教程:docs.aws.amazon.com/IAM/latest/…

访问密钥如下所示:

aws_access_key_id = <your_access_key_id>
aws_secret_access_key = <your_secret_access_key>

请注意妥善保管它们,因为创建后你将无法再次查看。同时要谨慎分享它们,因为它们可能被用来访问你的AWS账户并操作各种AWS资源。

最后一步是安装AWS CLI并使用新创建的访问密钥进行配置。可以通过以下链接安装AWS CLI:docs.aws.amazon.com/cli/latest/…

安装AWS CLI后,可以运行aws configure来配置。以下是一个AWS配置示例:

[default]
aws_access_key_id = *************
aws_secret_access_key = ************
region = eu-central-1
output = json

更多关于配置AWS CLI的详细信息,请参考此教程:docs.aws.amazon.com/cli/v1/user…

此外,为了将项目与AWS凭证配置同步,你需要在.env文件中填写以下变量:

AWS_REGION="eu-central-1" # 替换为你的AWS区域。默认使用"eu-central-1"。
AWS_ACCESS_KEY="<your_aws_access_key>"
AWS_SECRET_KEY="<your_aws_secret_key>"

关于本书中实际操作相关费用的重要提示

本书中的所有云服务都遵循其免费选项,除了AWS。因此,如果你使用个人AWS账户,在跟随本书操作时,你需要自行承担AWS费用。尽管部分服务可能在AWS的免费层使用范围内,但其他服务不在其中,因此建议你定期检查账单控制台。

大部分费用将来自测试SageMaker进行训练和推理。根据我们的测试,使用本书和仓库提供的规范,AWS费用大约在50到100美元之间。

请参阅AWS文档以设置账单报警来监控你的费用:docs.aws.amazon.com/AmazonCloud…

SageMaker:训练和推理计算资源

本章的最后主题是了解SageMaker及其选择理由。SageMaker是一个用于训练和部署ML模型的ML平台。官方定义如下:AWS SageMaker是AWS的一个全托管机器学习服务,使开发人员和数据科学家能够大规模构建、训练和部署机器学习模型。它通过处理底层基础设施简化了过程,使用户可以高效地专注于开发高质量模型。

我们将使用SageMaker来微调并操作我们的训练管道,利用GPU集群,并将自定义的LLM双生子模型部署为可以在全球实时访问的REST API。

为什么选择AWS SageMaker?

我们还需要讨论为什么选择AWS SageMaker而不是更简单、更具成本效益的选项,如AWS Bedrock。首先,解释一下Bedrock及其优势。

Amazon Bedrock是一种用于部署LLM的无服务器解决方案。无服务器意味着无需管理服务器或基础设施。它提供预训练模型,可以通过API直接访问。在我们撰写本书时,Bedrock仅支持Mistral、Flan、Llama 2和Llama 3(支持选项有限)。你可以发送输入数据并从模型中接收预测,而无需管理底层基础设施或软件。这种方法显著降低了将AI功能集成到应用程序中的复杂性和时间,使缺乏机器学习经验的开发人员也能轻松上手。然而,这种集成便利性伴随着有限的自定义选项,因为只能使用Amazon Bedrock提供的预训练模型和API。Bedrock的定价基于API调用次数,结构简单,便于成本估算和控制。

另一方面,SageMaker是一个完整的平台,用于构建、训练和部署机器学习模型。它允许你完全自定义ML流程,甚至可用于研究。正因如此,SageMaker主要被数据科学家和机器学习专家使用,他们了解编程、机器学习概念,且熟悉使用AWS等云平台。SageMaker采用AWS服务常见的按需付费模式,即根据计算资源、存储和所需服务的使用量付费。

与Bedrock相比,即使SageMaker端点未被使用,你仍需支付AWS上部署资源的费用,如在线的EC2实例。因此,你必须设计自动缩放系统来删除未使用的资源。总结来说,Bedrock提供现成的解决方案,允许快速部署由基础模型驱动的API端点;而SageMaker是一个多功能平台,能够完全自定义ML逻辑。

那么,为什么选择SageMaker而非Bedrock?Bedrock适合快速原型开发,但这是一本关于LLM工程的书,我们的目标是深入探索Bedrock试图掩盖的所有工程细节。因此,SageMaker的高度自定义能力使我们能够展示部署模型所需的全部工程过程。

实际上,即使SageMaker也并非完全可定制。如果你想要完全控制部署过程,可以使用AWS的Kubernetes自托管服务EKS,这样可以直接访问虚拟机,完全自定义ML管道的构建、交互和资源管理方式。类似地,你可以选择AWS的Kubernetes版本ECS。使用EKS或ECS还可以降低成本,因为这些服务的费用相对较低。

总结而言,SageMaker在完全控制和自定义与全托管服务之间取得了平衡。这个平衡确保了你在享受托管服务便利的同时,也拥有所需的控制力。

总结

本章回顾了全书中使用的核心工具。首先,我们了解了如何安装支持本仓库的正确版本的Python;接着,我们讨论了如何使用Poetry创建虚拟环境并安装所有依赖项;最后,我们学习了如何使用任务执行工具Poe the Poet来汇总运行应用所需的所有命令。

接下来,我们审查了确保MLOps最佳实践的所有工具,包括用于共享模型的模型注册表、用于管理训练实验的实验追踪器、用于管理ML管道和工件的编排器,以及用于管理所有文件和数据集的元数据。我们还了解了实现LLM双生子项目所需的数据库类型。最后,我们探讨了设置AWS账户、生成访问密钥以及配置AWS CLI以编程访问AWS云的流程,并深入理解了选择AWS SageMaker来构建LLM双生子应用的原因。

下一章中,我们将从数据收集ETL开始探索LLM双生子项目的实现,收集并存储网络上的帖子、文章和代码库到数据仓库中。

参考文献