构建 LLMOPs 管道
原文:towardsdatascience.com/building-an-llmops-pipeline-08d367b36d64Image fromby2023 年是见证生成式人工智能(Generative AI)领域各种大型语言模型(LLMs)崛起的一年。LLMs 拥有惊人的力量和潜力,但将它们投入生产一直是对用户的一个持续挑战。一个特别普遍的问题是应该使用哪个 LLM?更具体地说,如何评估
原文:
towardsdatascience.com/building-an-llmops-pipeline-08d367b36d64
Image from Unsplash by Sigmund
2023 年是见证生成式人工智能(Generative AI)领域各种大型语言模型(LLMs)崛起的一年。LLMs 拥有惊人的力量和潜力,但将它们投入生产一直是对用户的一个持续挑战。一个特别普遍的问题是应该使用哪个 LLM?更具体地说,如何评估一个 LLM 的准确性?当有大量模型可供选择、不同的数据集用于微调/RAG 以及各种提示工程/调整技术需要考虑时,这尤其具有挑战性。
为了解决这个问题,我们需要为 LLMs 建立DevOps最佳实践。拥有一个可以帮助评估不同模型、数据集和提示的工作流程或管道。这个领域开始被称为LLMOPs/FMOPs。在 LLMOPs 中可以考虑的一些参数如下,这是一个(极其)简化的流程:
LLM 评估考虑因素(作者)
在这篇文章中,我们将尝试通过构建一个微调、部署和评估Llama 7B 模型的管道来解决此问题。您也可以通过将其用作模板来比较多个 LLMs、数据集和提示来扩展此示例。对于这个例子,我们将使用以下工具来构建这个管道:
-
SageMaker JumpStart: SageMaker JumpStart 提供了各种 FM/LLMs,用于即时的微调和部署。这两个过程可能相当复杂,因此 JumpStart 抽象出了具体细节,并允许您指定数据集和模型元数据以进行微调和部署。在这种情况下,我们选择了 Llama 7B 并进行了指令微调,该功能是即插即用的。要深入了解 JumpStart 微调,请参阅这篇博客和这个 Llama 代码示例,我们将以此作为参考。
-
SageMaker Clarify/FMEval: SageMaker Clarify 通过 SageMaker Studio UI 和开源的 Python FMEVal 库 提供了一个基础模型评估工具。该功能内置了多种不同的算法,覆盖了不同的自然语言处理领域,如文本生成和摘要。在这个例子中,我们使用这个库来评估 Llama 模型在摘要用例中的应用。要深入了解该库,请参阅我的入门文章这里。
-
SageMaker Pipelines Step Decorator: SageMaker Pipelines 是 SageMaker 中的一个 MLOps 功能,它帮助您实现 ML 工作流的运营化。使用 Pipelines,您可以定义不同的步骤和参数来构建您的 ML 工作流。在 Pipelines 中特别有一个名为步骤装饰器的功能,您可以将 Python 代码提升并转换为可以作为 Pipeline 链接在一起的功能。在这个例子中,我们将使用这个功能来构建使用上述工具进行训练和评估的函数。要了解步骤装饰器的介绍,请参阅我的入门文章这里。
现在我们已经了解了我们的堆栈,让我们动手实践吧!
注意:本文假设读者对 Python、LLMs 和 Amazon SageMaker 有基本了解。
免责声明:我是 AWS 的机器学习架构师,我的观点仅代表个人。
目录
-
设置与数据集准备
-
管道步骤构建
-
管道执行
-
其他资源与结论
1. 设置与数据集准备
对于开发,我们将在新的SageMaker Studio环境中工作(已启用本地内核支持)。我们将使用具有 Python3 内核的 ml.c5.18xlarge 实例。
对于我们的用例,我们将使用 Llama 进行微调和评估摘要用例。对于我们的数据集,我们将使用公共Dolly 数据集(许可证:cc-by-sa-3.0)。我们可以使用内置的 HuggingFace 数据集库来获取此数据集,并过滤摘要数据点。我们还创建了用于微调和评估的训练和测试数据集。
import datasets
# dolly dataset
dolly_dataset = load_dataset("databricks/databricks-dolly-15k", split="train")
# summarization use-case
summarization_dataset = dolly_dataset.filter(lambda example: example["category"] == "summarization")
summarization_dataset = summarization_dataset.remove_columns("category")
# train test split
train_and_test_dataset = summarization_dataset.train_test_split(test_size=0.1)
# local train dataset
train_and_test_dataset["train"].to_json("train.jsonl")
# test dataset
train_and_test_dataset["test"].to_json("test.jsonl")
我们还指定了模型元数据以供 JumpStart 检索适当的 Llama 7B 模型。
import sagemaker
model_id, model_version = "meta-textgeneration-llama-2-7b", "2.*"
对于训练,我们将实现指令微调,因此我们准备了一个提示和响应模板,或者在这种情况下,文本和摘要。我们将使用以下示例作为训练部分的参考。
import json
template = {
"prompt": "Below is an instruction that describes a task, paired with an input that provides further context. "
"Write a response that appropriately completes the request.nn"
"### Instruction:n{instruction}nn### Input:n{context}nn",
"completion": " {response}",
}
with open("template.json", "w") as f:
json.dump(template, f)
然后,我们将这些文件上传到公共 S3 路径以进行训练,以及后续的推理/评估。
from sagemaker.s3 import S3Uploader
import sagemaker
import random
output_bucket = sagemaker.Session().default_bucket()
local_data_file = "train.jsonl"
test_data_file = "test.jsonl"
train_data_location = f"s3://{output_bucket}/dolly_dataset"
test_data_location = f"s3://{output_bucket}/test_dataset"
S3Uploader.upload(local_data_file, train_data_location)
S3Uploader.upload("template.json", train_data_location)
S3Uploader.upload(test_data_file, test_data_location)
print(f"Training data: {train_data_location}")
print(f"Test data: {test_data_location}")
print(f"Output bucket: {output_bucket}")
2. 流水线步骤构建
流水线设置
为了设置我们的流水线,我们需要几个单独的文件来定义我们的执行环境。其中一个是一个 config.yaml 文件,它将定义流水线执行的硬件以及您定义的任何其他配置。该配置文件还指向您的 requirements.txt 文件,该文件将在流水线环境中安装。
SchemaVersion: '1.0'
SageMaker:
PythonSDK:
Modules:
RemoteFunction:
InstanceType: ml.m5.xlarge
Dependencies: ./requirements.txt
IncludeLocalWorkDir: true
CustomFileFilter:
IgnoreNamePatterns: # files or directories to ignore
- "*.ipynb" # all notebook files
jsonlines
sagemaker
fmeval
然后,我们可以将此配置文件作为环境变量指向:
import os
# Set path to config file
os.environ["SAGEMAKER_USER_CONFIG_OVERRIDE"] = os.getcwd()
使用流水线,您还可以定义将被注入到您的流水线中的参数。在这种情况下,我们定义了执行每个步骤的工作硬件实例类型:
import sagemaker
from sagemaker.workflow.function_step import step
from sagemaker.workflow.parameters import ParameterString
sagemaker_session = sagemaker.session.Session()
role = sagemaker.get_execution_role()
bucket = sagemaker_session.default_bucket()
region = sagemaker_session.boto_region_name
instance_type = ParameterString(name="TrainInstanceType",
default_value="ml.c5.18xlarge")
训练与部署步骤
对于训练步骤,我们将使用 SageMaker JumpStart 来微调我们的 Llama 7B 模型。我们将向函数传递几个不同的参数:
-
训练数据路径:这是指向 train.jsonl 和 template.json 文件的 S3 位置。
-
模型元数据:JumpStart 根据您传递的模型 ID 和版本识别要下拉的模型。在这种情况下,我们指定了 Llama 7B 模型。
一旦我们定义了这些,我们就使用这些参数设置 JumpStart 估算器。可选地,您还可以根据您想要测试的性能旋钮定义模型特定的微调参数。注意带有函数的步骤装饰器,表示我们正在处理 SageMaker 流水线步骤而不是普通的 Python 函数。
# step one
@step(
name = "train-deploy",
instance_type = instance_type,
keep_alive_period_in_seconds=300
)
def train_deploy(train_data_path: str,
model_id: str = "meta-textgeneration-llama-2-7b",
model_version: str = "2.*") -> str:
import sagemaker
from sagemaker.jumpstart.estimator import JumpStartEstimator
# configure JumpStart Estimator
estimator = JumpStartEstimator(
model_id=model_id,
model_version=model_version,
environment={"accept_eula": "true"},
disable_output_compression=True,
)
estimator.set_hyperparameters(instruction_tuned="True", epoch="1", max_input_length="1024")
estimator.fit({"training": train_data_path})
## deploy fine-tuned model
finetuned_predictor = estimator.deploy()
endpoint_name = finetuned_predictor.endpoint_name
return endpoint_name
训练完成后,我们将微调后的 Llama 模型部署到SageMaker 实时端点,我们可以调用该端点进行推理。默认情况下,此实例类型用于训练和推理,具体取决于您选择的 LLM,但您可以进行调整,如果您想自己选择硬件的话。
我们将端点名称作为下一步的输入,以便我们可以使用该端点进行推理和评估。当您最终执行 Pipeline 时,在 Studio UI 中,您将看到成功的 JumpStart 训练作业和端点创建。
JumpStart 训练作业(作者截图)
端点创建截图(作者截图)
现在我们有了端点,我们可以继续运行样本推理并对这些结果进行评估。
推理与评估步骤
在我们的第二步中,我们再次为我们的函数定义某些参数:
-
端点名称:我们在评估之前对此端点进行推理。
-
S3_Test_Path:我们之前也将一个与训练数据集分开的测试数据集推送到 S3。我们将从这个路径下载数据集,并在评估之前进行推理。
我们首先使用Boto3 Python SDK下载 S3 的"test.jsonl"文件:
def evaluate(endpoint_name: str, output_bucket: str = output_bucket, test_data_file: str = "test.jsonl",
key_path: str = "test_dataset/test.jsonl") -> str:
# download S3 file
s3 = boto3.client("s3")
s3.download_file(output_bucket, key_path, test_data_file)
然后,我们使用此文件进行推理,并创建一个新的 JSONLines 文件,我们可以在此文件上运行我们的评估算法。出于时间考虑,我们限制此样本仅为 20 个数据点,但您可以根据需要增加测试数据集。
with jsonlines.open(input_file) as input_fh, jsonlines.open(output_file, "w") as output_fh:
for i, datapoint in enumerate(input_fh, start=1):
instruction = datapoint["instruction"]
context = datapoint["context"]
summary = datapoint["response"]
payload = prepare_payload(datapoint)
response = runtime.invoke_endpoint(EndpointName=endpoint_name, Body=json.dumps(payload),
ContentType=content_type, CustomAttributes='accept_eula=true')
result = json.loads(response['Body'].read().decode())[0]['generation']
line = {"instruction": instruction, "context": context, "summary": summary, "model_output": result}
output_fh.write(line)
# evaluate just 20 datapoints for example
if i == 20:
break
函数的这一部分运行完成后,应该生成一个"results.jsonl"文件,该文件将包含几个不同的参数:
-
文档/输入:这是需要总结的原始文本。
-
真实值/实际输出:这是测试数据集中存在的摘要,这是我们评估的真实值。
-
模型输出:这是我们使用微调后的 Llama 7B 模型进行的推理。我们将运行我们的评估算法,比较真实值与模型推理结果。
现在我们有了用于评估的数据集,我们可以导入 FMEval 库:
import fmeval
from fmeval.data_loaders.data_config import DataConfig
from fmeval.constants import MIME_TYPE_JSONLINES
from fmeval.eval_algorithms.summarization_accuracy import SummarizationAccuracy
注意,我们拉取了 SummarizationAccuracy 算法,该算法将返回如 Meteor、Rouge 和 Bert 等指标。要查看这些算法的完整实现,您可以参考此链接。一般来说,每个指标都有其优缺点,您可以选择您想要用于评估的指标。
-
Rouge: Rouge-N 分数,本质上是在真实值和模型推理摘要之间搜索 N-gram 词重叠。
-
Meteor: 基于 Rouge,结合词干提取和同义词,这将捕获更多文本中的相似性,即使单词不完全匹配。
-
BertScore: 使用余弦相似度匹配单词,同时利用 BERT 的预训练嵌入,你会在安装包时看到这一点。
对于这些指标的深入探讨,我会参考以下文章。
对于我们使用 FMEval 包的实现,我们首先在 FMEval 特定的DataConfig 对象中配置我们的数据集,并指定真实值和模型推理列。
config = DataConfig(
dataset_name="dolly_summary_model_outputs",
dataset_uri="results.jsonl",
dataset_mime_type=MIME_TYPE_JSONLINES,
model_input_location="instruction",
target_output_location="summary",
model_output_location="model_output"
)
然后,我们实例化 SummarizationAccuracy 算法,并使用指定的 DataConfig 对象运行评估。
eval_algo = SummarizationAccuracy()
eval_output = eval_algo.evaluate(dataset_config=config, save=True)
res = json.dumps(eval_output, default=vars, indent=4)
serialized_data = json.loads(res)
# print metrics to CW logs, realistically push to somewhere to visualize
for item in serialized_data:
for key, value in item.items():
print(f"Key: {key}, Value: {value}")
在这个例子中,我们将指标直接写入 CloudWatch 日志,在现实场景中,你可以将其导出到 S3 或 QuickSight 等可视化工具,以获得更美观的评估视图。如果你检查了管道执行后的第二步的 CloudWatch 日志,你会注意到指标已经被输出。
评估指标(作者截图)
3. 管道执行
一旦定义了两个管道步骤,你就可以像使用纯 Python 函数一样简单地将它们链接在一起。
# stitch together pipeline
from sagemaker.workflow.pipeline import Pipeline
endpoint_name = train_deploy(train_data_location)
eval_metrics = evaluate(endpoint_name)
然后,我们可以定义一个管道对象并启动执行:
pipeline = Pipeline(
name="llm-train-eval-pipeline",
parameters=[
instance_type
],
steps=[
eval_metrics,
],
)
# execute Pipeline
pipeline.upsert(role_arn=role)
execution = pipeline.start()
execution.describe()
execution.wait()
你可以在 Studio UI 中查看和监控管道执行,这个管道大约需要 45 分钟才能成功完成。
执行状态(作者截图)
DAG(作者截图)
4. 其他资源与结论
GenAI-Samples/LLMOps-Pipeline at master · RamVegiraju/GenAI-Samples
我希望这篇文章是 LLMOPs 和利用不同 SageMaker 功能构建管道的有用介绍。随着 LLM 用例的扩展,对正确实验的需求也在增加,以确定你 LLM 的理想配置。这个例子可以扩展以包含多个 LLM、数据集、提示模板等。请关注 GenAI/LLM 空间中的更多内容。
如常,感谢您阅读,欢迎随时留下任何反馈。
更多推荐

所有评论(0)