Press "Enter" to skip to content

在Amazon SageMaker上微调MPT-7B

Jeffery Ho 在 Unsplash 上的照片。

学习如何准备数据集并创建训练作业,在 Amazon SageMaker 上对 MPT-7B 进行微调

每周都会宣布发布新的大型语言模型 (LLMs),每个模型都试图超越前辈并占据评估排行榜的领先位置。其中最新的模型之一是 MPT-7B,由 MosaicML 发布。与其它同类模型不同,这个 70 亿参数的模型是开源的,并且可用于商业用途 (Apache 2.0 许可证) 🚀。

像 MPT-7B 这样的基础模型是在从网络中爬取的数万亿个标记 (100 个标记 ~ 75 个单词) 的数据集上进行预训练的,当很好地提示时,它们可以产生令人印象深刻的输出。但是,为了真正释放大型语言模型在实际应用中的价值,聪明的提示工程可能不足以使它们适用于您的用例,因此需要在特定于领域的数据集上对基础模型进行微调。

LLMs 有数十亿的参数,因此对这样大型的模型进行微调是具有挑战性的。好消息是,与预训练基础模型相比,微调要便宜得多、更快,因为 1) 特定于领域的数据集是“小”的,2) 微调仅需要对训练数据进行少量的遍历。

本文将介绍以下内容:

  • 如何创建和结构化用于微调大型语言模型的数据集。
  • 什么是分布式训练作业以及如何配置完全分片的数据并行。
  • 如何定义一个 😊 HuggingFace 评估器。
  • 如何在 Amazon SageMaker 上启动微调 MPT-7B 的训练作业。

1. 安装依赖项和设置 S3 路径

让我们从安装 SageMaker Python SDK 和其他一些软件包开始。这个 SDK 使得用几行 Python 代码就可以在 AWS 上训练和部署机器学习模型成为可能。下面的代码可以在 Github 上的 sagemaker_finetuning.ipynb 笔记本中找到。在 SageMaker Studio、SageMaker 笔记本实例或在您的笔记本电脑上身份验证后运行。

!pip install "sagemaker==2.162.0" s3path boto3 --quietfrom sagemaker.huggingface import HuggingFacefrom sagemaker.inputs import TrainingInputfrom sagemaker import s3_utilsimport sagemakerimport boto3import json

下一步是定义数据将在 S3 中保存的路径,并创建 SageMaker 会话。

# 定义 S3 路径bucket             = "<YOUR-S3-BUCKET>"training_data_path = f"s3://{bucket}/toy_data/train/data.jsonl"test_data_path     = f"s3://{bucket}/toy_data/test/data.jsonl"output_path        = f"s3://{bucket}/outputs"code_location      = f"s3://{bucket}/code"# 创建 SageMaker 会话sagemaker_session  = sagemaker.Session()region             = sagemaker_session.boto_region_namerole               = sagemaker.get_execution_role()

2. 构建微调数据集

我们将创建一个虚拟数据集来演示如何微调 MPT-7B。由于在完整数据集上训练这样大小的模型需要很长时间并且成本很高,因此首先在小数据集上测试和调试训练作业,其次将训练扩展到完整数据集。

  • 将数据集格式化为字典列表 —— 数据集应该格式化为字典列表,其中每个示例都有一个键值对结构,例如:
{    "prompt": "什么是 Pastel de Nata?",    "response": "Pastel de Nata 是一种葡萄牙式蛋挞蛋糕,可以选择撒上肉桂粉。"}

prompt 是输入给模型的内容(例如,一个问题)。response 是模型训练的输出(例如,回答 prompt 中的问题的答案)。通常会对原始的 prompt 进行预处理,以适应提示模板,从而帮助模型生成更好的输出。请注意,模型是针对因果语言建模进行训练的,因此可以将其视为“文档补全器”。最好设计提示模板,使模型认为它正在完成一份文档。Andrej Karpathy在他的演讲 State of GPT 中很好地解释了这个机制。

prompt_template = """编写一个响应,以适当地回答下面的问题。### 问题:{question}### 响应:"""dataset = [    {"prompt": "什么是Pastel de Nata?",     "response": "Pastel de Nata是一种葡萄牙蛋挞糕点,可以选择撒上肉桂粉。"},    {"prompt": "阿姆斯特丹有哪些著名博物馆?",     "response": "阿姆斯特丹是各种世界著名博物馆的所在地,没有去参观Rijksmuseum、Van Gogh Museum或Stedelijk Museum的旅行是不完整的。"},    {"prompt": "欧洲议会在哪里?",     "response": "斯特拉斯堡是欧洲议会的正式所在地。"},    {"prompt": "荷兰的天气怎么样?",     "response": "荷兰是一个拥有温和夏季和寒冷冬季的典型海洋气候的国家。"},    {"prompt": "Poffertjes是什么?",     "response": "Poffertjes是一种传统的荷兰面糊小吃。看起来像小巧、松软的煎饼,是用酵母和荞麦面制成的。"},]# 基于模板格式化 promptfor example in dataset:    example["prompt"] = prompt_template.format(question=example["prompt"])training_data, test_data = dataset[0:4], dataset[4:]print(f"训练数据大小:{len(training_data)}\n测试数据大小:{len(test_data)}")
  • 将训练和测试数据上传到 S3 — 将训练和测试集准备好并格式化为字典的列表后,我们使用下面的实用程序函数将它们作为 JSON 行上传到 S3:
def write_jsonlines_to_s3(data, s3_path):    """将字典列表作为 JSON 行文件写入 S3"""    json_string = ""    for d in data:        json_string += json.dumps(d) + "\n"    s3_client   = boto3.client("s3")        bucket, key = s3_utils.parse_s3_url(s3_path)    s3_client.put_object(         Body   = json_string,         Bucket = bucket,         Key    = key,    )write_jsonlines_to_s3(training_data, training_data_path)write_jsonlines_to_s3(test_data, test_data_path)

3. SageMaker 训练作业

有了 S3 中的数据集,我们现在将在 Amazon SageMaker 中创建一个训练作业。为此,我们必须创建一个入口脚本,修改配置文件,指定训练设置,并定义一个 HuggingFace 估算器。我们将从 LLM Foundry 和 Composer 库的 CLI 启动器的训练脚本中(重新)使用,该启动器设置了分布式训练环境。这两个软件包都由 MosaicML 维护,后者是 MPT-7B 背后的公司。工作文件夹应该结构化如下:

└── fine-tune-mpt-7b-sagemaker/    ├── training_script_launcher.sh    ├── fine_tuning_config.yaml    ├── sagemaker_finetuning.ipynb

现在我们将深入研究这些文件中的每一个。

  • 创建配置文件 finetuning_config.yaml — LLM Foundry 存储库中提供的模板是一个很好的起点,特别是 mpt-7b-dolly-sft.yaml 文件。但是,根据您的数据集大小和训练实例,您可能需要调整其中的一些配置,例如批量大小。我已经修改了文件以在 SageMaker 中微调模型(请查看 finetuning_config.yaml)。您应该注意的参数如下:
max_seq_len: 512global_seed: 17...# 数据加载器strain_loader:  name: finetuning  dataset:    hf_name: json    hf_kwargs:        data_dir: /opt/ml/input/data/train/...eval_loader:  name: finetuning  dataset:    hf_name: json    hf_kwargs:        data_dir: /opt/ml/input/data/test/ ...max_duration: 3epeval_interval: 1ep...global_train_batch_size: 128...# FSDPfsdp_config:  sharding_strategy: FULL_SHARD  mixed_precision: PURE  activation_checkpointing: true  activation_checkpointing_reentrant: false  activation_cpu_offload: false  limit_all_gathers: true  verbose: false# 检查点到本地文件系统或远程对象存储save_folder: /tmp/checkpointsdist_timeout: 2000

max_seq_length 指定输入的最大标记数(记住,100个标记 ~ 75个单词)。 训练和测试数据将使用 😊 Datasets 库从与训练作业关联的容器内的 /opt/ml/input/data/{train, test} 目录中加载。请查看 SageMaker Training Storage Folders 的文档以了解容器目录的结构。 max_duration 指定微调的时期数量。 通常选择两到三个时期。 eval_interval 指示模型在测试集上评估的频率。

分布式训练策略是 Fully Sharded Data Parallel(FSDP),它可以有效地训练像 MPT-7B 这样的大型模型。与传统的数据并行策略不同,后者在每个 GPU 中保存模型的副本,而 FSDP 在数据并行工作程序之间分段模型参数、优化器状态和梯度。如果您想了解有关 FSDP 的更多信息,请查看这篇富有见地的 PyTorch 入门文章。FSDP 集成在 Composer 中,这是 LLM Foundry 使用的分布式训练库。

save_folder 确定模型检查点(.pt 文件)保存在哪里。我们将其设置为临时文件夹 /tmp/checkpoints

  • 创建入口脚本 launcher.sh —— 使用 bash 脚本作为入口点。 bash 脚本克隆 LLM Foundry 存储库,安装要求,并更重要的是使用 Composer 库的分布式启动器运行训练脚本。请注意,通常,SageMaker 中的训练作业使用像 python train.py 这样的命令运行训练脚本。但是,可以将 bash 脚本作为入口点传递,这在我们的情况下提供了更大的灵活性。最后,我们将保存在 /tmp/checkpoints 中的模型检查点转换为 HuggingFace 模型格式,并将最终工件保存到 /opt/ml/model/ 中。SageMaker 将压缩此目录中的所有文件,创建 tarball model.tar.gz 并将其上传到 S3。tarball 对推理很有用。
# 从 MosaicML 克隆 llm-foundry 包# 这是训练脚本托管的地方git clone https://github.com/mosaicml/llm-foundry.gitcd llm-foundry# 安装所需的软件包pip install -e ".[gpu]"pip install git+https://github.com/mosaicml/composer.git@dev# 使用微调配置运行训练脚本composer scripts/train/train.py /opt/ml/code/finetuning_config.yaml# 将 Composer 检查点转换为 HuggingFace 模型格式python scripts/inference/convert_composer_to_hf.py \    --composer_path /tmp/checkpoints/latest-rank0.pt \    --hf_output_path /opt/ml/model/hf_fine_tuned_model \    --output_precision bf16# 打印模型工件目录的内容ls /opt/ml/model/
  • 定义 😊 HuggingFace 估计器 —— Estimator 设置用于运行训练作业的 Docker 容器。我们将使用带有 PyTorch 2.0.0 和 Python 3.10 的图像。 bash 脚本和配置文件会自动上传到 S3,并在容器中提供(由 SageMaker Python SDK 处理)。我们将训练实例设置为具有 8 个 NVIDIA A10G GPU 的 g5.48xlargep4d.24xlarge 也是不错的选择。尽管它更昂贵,但它配备了 8 个 NVIDIA A100 GPU。我们还指示要跟踪训练和测试集上的指标(交叉熵和困惑度)。通过正则表达式捕获这些指标的值并将其发送到 Amazon CloudWatch。
# 为训练作业定义容器映像training_image_uri = f"763104351884.dkr.ecr.{region}.amazonaws.com/huggingface-pytorch-training:2.0.0-transformers4.28.1-gpu-py310-cu118-ubuntu20.04-v1.1"# 定义要发送到 CloudWatch 的指标metrics = [    # 在训练集上    {"Name": "train:LanguageCrossEntropy",     "Regex": "Train metrics\/train\/LanguageCrossEntropy: ([+-]?((\d+\.?\d*)|(\.\d+)))"},    {"Name": "train:LanguagePerplexity",     "Regex": "Train metrics\/train\/LanguagePerplexity: ([+-]?((\d+\.?\d*)|(\.\d+)))"},    # 在测试集上    {"Name": "test:LanguageCrossEntropy",     "Regex": "Eval metrics\/eval\/LanguageCrossEntropy: ([+-]?((\d+\.?\d*)|(\.\d+)))"},    {"Name": "test:LanguagePerplexity",     "Regex": "Eval metrics\/eval\/LanguagePerplexity: ([+-]?((\d+\.?\d*)|(\.\d+)))"},]estimator_args = {    "image_uri": training_image_uri,     # Training container image    "entry_point": "launcher.sh",        # Launcher bash script    "source_dir": ".",                   # Directory with launcher script and configuration file    "instance_type": "ml.g5.48xlarge",   # Instance type    "instance_count": 1,                 # Number of training instances    "base_job_name": "fine-tune-mpt-7b", # Prefix of the training job name    "role": role,                        # IAM role    "volume_size": 300,                  # Size of the EBS volume attached to the instance (GB)    "py_version": "py310",               # Python version    "metric_definitions": metrics,       # Metrics to track    "output_path": output_path,          # S3 location where the model artifact will be uploaded    "code_location": code_location,      # S3 location where the source code will be saved    "disable_profiler": True,            # Do not create profiler instance    "keep_alive_period_in_seconds": 240, # Enable Warm Pools while experimenting}huggingface_estimator = HuggingFace(**estimator_args)

⚠️ 请确保请求SageMaker培训的相应配额,以及在使用此功能时请求Warm Pools的配额。如果您计划在SageMaker中运行许多作业,请查看SageMaker Saving Plans。

  • 启动培训作业🚀 — 我们已准备好在Amazon SageMaker上开始培训作业:
huggingface_estimator.fit({    "train": TrainingInput(        s3_data=training_data_path,        content_type="application/jsonlines"),    "test": TrainingInput(        s3_data=test_data_path,        content_type="application/jsonlines"),}, wait=True)

培训时间取决于数据集的大小。对于我们的虚拟数据集,训练大约需要20分钟才能完成。一旦完成模型训练并转换为😊 HuggingFace格式,SageMaker将上传模型tarball(model.tar.gz)到S3 output_path。我发现在实践中,上传步骤需要相当长的时间(超过1小时),这可能是由于压缩模型工件的大小(约25GB)。

4. 总结

在本文中,我展示了如何准备数据集并在SageMaker中创建培训作业,以针对您的用例对MPT-7B进行微调。该实现利用了LLM Foundry的培训脚本,并使用Composer库的分布式培训启动器。一旦您微调了模型并希望部署它,我建议您查看Philipp Schmid的博客文章;有许多关于如何在SageMaker中部署LLMs的示例。祝您玩得愉快,微调您的MPT-7B模型!🎉

本文中使用的所有代码都可在Github上获得:

GitHub – jpcpereira/sagemaker-fine-tune-mpt-7b

github.com

— João Pereira

感谢您的阅读。希望本文能帮助您在Amazon SageMaker中开始微调大型语言模型,如MPT-7B。如果您想阅读我的未来文章,请关注我。非常感谢您的反馈!如果您有任何问题,请在下面留下评论或直接通过电子邮件LinkedIn与我联系。

Leave a Reply

Your email address will not be published. Required fields are marked *