合作伙伴关系:Amazon SageMaker 和 Hugging Face

发布于2021年3月23日
在 GitHub 上更新
hugging-face-and-aws-logo

看看这些笑容!

今天,我们宣布 Hugging Face 与 Amazon 建立战略合作伙伴关系,旨在帮助企业更轻松地利用最先进的机器学习模型,并更快地推出尖端 NLP 功能。

通过此次合作,Hugging Face 将亚马逊网络服务作为其首选云提供商,为客户提供服务。

作为双方共同客户的第一个赋能步骤,Hugging Face 和 Amazon 正在推出新的 Hugging Face 深度学习容器(DLC),让在 Amazon SageMaker 中训练 Hugging Face Transformer 模型变得前所未有的简单。

要了解如何使用 Amazon SageMaker Python SDK 访问和使用新的 Hugging Face DLC,请查看下面的指南和资源。

2021 年 7 月 8 日,我们扩展了 Amazon SageMaker 集成,增加了 Transformers 模型的轻松部署和推理。如果您想了解如何使用 Amazon SageMaker 轻松部署 Hugging Face 模型,请查看新博客文章文档


特性与优势 🔥

仅需一条命令

通过 Amazon SageMaker 中提供的新的 Hugging Face 深度学习容器,训练尖端的基于 Transformer 的 NLP 模型从未如此简单。有针对 TensorFlow 和 PyTorch 专门优化的变体,适用于单 GPU、单节点多 GPU 和多节点集群。

加速机器学习从科学到生产的转化

除了 Hugging Face DLC,我们还为 SageMaker Python SDK 创建了一流的 Hugging Face 扩展,以加速数据科学团队,将设置和运行实验所需的时间从数天缩短到数分钟。

您可以将 Hugging Face DLC 与 Amazon SageMaker 的自动模型调优功能结合使用,以便自动优化训练超参数并快速提高模型的准确性。

借助基于 SageMaker Studio 的集成开发环境(IDE),您可以轻松跟踪和比较您的实验和训练工件。

内置性能

通过 Hugging Face DLC,SageMaker 客户将受益于针对 PyTorch 或 TensorFlow 的内置性能优化,从而更快地训练 NLP 模型,并灵活选择训练基础设施,为您的工作负载提供最佳性价比。

Hugging Face DLC 完全集成了 SageMaker 分布式训练库,可以使用 Amazon EC2 上可用的最新一代实例,比以往任何时候都更快地训练模型。


资源、文档与示例 📄

您可以在下方找到所有重要资源,包括已发布的博客文章、视频、文档和示例 Notebook/脚本。

博客/视频

文档

示例 Notebook


入门:端到端文本分类 🧭

在本入门指南中,我们将使用新的 Hugging Face DLC 和 Amazon SageMaker 扩展,通过 Transformers 和 Datasets 库训练用于二元文本分类的 Transformer 模型。

我们将使用 Amazon SageMaker Notebook 实例作为示例。您可以在此处了解如何设置 Notebook 实例

我们将要做什么

  • 设置开发环境并安装 SageMaker
  • 创建训练脚本 train.py
  • 预处理我们的数据并将其上传到 Amazon S3
  • 创建 HuggingFace Estimator 并训练我们的模型

设置开发环境并安装 sagemaker

如上所述,我们将使用 SageMaker Notebook 实例进行此操作。要开始,您需要进入您的 Jupyter Notebook 或 JupyterLab 并使用 conda_pytorch_p36 内核创建一个新的 Notebook。

注意:使用 Jupyter 是可选的:我们也可以从任何安装了 SDK、与云有连接和适当权限的地方启动 SageMaker 训练作业,例如笔记本电脑、其他 IDE 或 Airflow 或 AWS Step Functions 等任务调度器。

之后我们可以安装所需的依赖项

pip install "sagemaker>=2.31.0" "transformers==4.6.1" "datasets[s3]==1.6.2" --upgrade

要在 SageMaker 上运行训练,我们需要创建一个 SageMaker 会话并提供具有正确权限的 IAM 角色。此 IAM 角色稍后将附加到训练作业,使其能够下载数据,例如从 Amazon S3。

import sagemaker

sess = sagemaker.Session()
# sagemaker session bucket -> used for uploading data, models and logs
# sagemaker will automatically create this bucket if it not exists
sagemaker_session_bucket=None
if sagemaker_session_bucket is None and sess is not None:
    # set to default bucket if a bucket name is not given
    sagemaker_session_bucket = sess.default_bucket()

role = sagemaker.get_execution_role()
sess = sagemaker.Session(default_bucket=sagemaker_session_bucket)

print(f"sagemaker role arn: {role}")
print(f"sagemaker bucket: {sess.default_bucket()}")
print(f"sagemaker session region: {sess.boto_region_name}")

创建训练脚本 train.py

在 SageMaker TrainingJob 中,我们执行一个带有命名参数的 Python 脚本。在此示例中,我们使用 PyTorch 和 transformers。该脚本将:

  • 传递传入的参数(来自 HuggingFace Estimator 的超参数)
  • 加载我们的数据集
  • 定义我们的计算指标函数
  • 设置我们的 Trainer
  • 使用 trainer.train() 运行训练
  • 评估训练并在最后将模型保存到 S3。
from transformers import AutoModelForSequenceClassification, Trainer, TrainingArguments
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
from datasets import load_from_disk
import random
import logging
import sys
import argparse
import os
import torch

if __name__ == "__main__":

    parser = argparse.ArgumentParser()

    # hyperparameters sent by the client are passed as command-line arguments to the script.
    parser.add_argument("--epochs", type=int, default=3)
    parser.add_argument("--train-batch-size", type=int, default=32)
    parser.add_argument("--eval-batch-size", type=int, default=64)
    parser.add_argument("--warmup_steps", type=int, default=500)
    parser.add_argument("--model_name", type=str)
    parser.add_argument("--learning_rate", type=str, default=5e-5)

    # Data, model, and output directories
    parser.add_argument("--output-data-dir", type=str, default=os.environ["SM_OUTPUT_DATA_DIR"])
    parser.add_argument("--model-dir", type=str, default=os.environ["SM_MODEL_DIR"])
    parser.add_argument("--n_gpus", type=str, default=os.environ["SM_NUM_GPUS"])
    parser.add_argument("--training_dir", type=str, default=os.environ["SM_CHANNEL_TRAIN"])
    parser.add_argument("--test_dir", type=str, default=os.environ["SM_CHANNEL_TEST"])

    args, _ = parser.parse_known_args()

    # Set up logging
    logger = logging.getLogger(__name__)

    logging.basicConfig(
        level=logging.getLevelName("INFO"),
        handlers=[logging.StreamHandler(sys.stdout)],
        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    )

    # load datasets
    train_dataset = load_from_disk(args.training_dir)
    test_dataset = load_from_disk(args.test_dir)

    logger.info(f" loaded train_dataset length is: {len(train_dataset)}")
    logger.info(f" loaded test_dataset length is: {len(test_dataset)}")

    # compute metrics function for binary classification
    def compute_metrics(pred):
        labels = pred.label_ids
        preds = pred.predictions.argmax(-1)
        precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average="binary")
        acc = accuracy_score(labels, preds)
        return {"accuracy": acc, "f1": f1, "precision": precision, "recall": recall}

    # download model from model hub
    model = AutoModelForSequenceClassification.from_pretrained(args.model_name)

    # define training args
    training_args = TrainingArguments(
        output_dir=args.model_dir,
        num_train_epochs=args.epochs,
        per_device_train_batch_size=args.train_batch_size,
        per_device_eval_batch_size=args.eval_batch_size,
        warmup_steps=args.warmup_steps,
        evaluation_strategy="epoch",
        logging_dir=f"{args.output_data_dir}/logs",
        learning_rate=float(args.learning_rate),
    )

    # create Trainer instance
    trainer = Trainer(
        model=model,
        args=training_args,
        compute_metrics=compute_metrics,
        train_dataset=train_dataset,
        eval_dataset=test_dataset,
    )

    # train model
    trainer.train()

    # evaluate model
    eval_result = trainer.evaluate(eval_dataset=test_dataset)

    # writes eval result to file which can be accessed later in s3 output
    with open(os.path.join(args.output_data_dir, "eval_results.txt"), "w") as writer:
        print(f"***** Eval results *****")
        for key, value in sorted(eval_result.items()):
            writer.write(f"{key} = {value}\\n")

    # Saves the model to s3; default is /opt/ml/model which SageMaker sends to S3
    trainer.save_model(args.model_dir)

预处理我们的数据并将其上传到 S3

我们使用 datasets 库下载并预处理 imdb 数据集。预处理后,数据集将上传到当前会话的默认 S3 存储桶 sess.default_bucket(),并在我们的训练作业中使用。imdb 数据集包含 25000 条训练和 25000 条测试的高度两极分化的电影评论。

import botocore
from datasets import load_dataset
from transformers import AutoTokenizer
from datasets.filesystems import S3FileSystem

# tokenizer used in preprocessing
tokenizer_name = 'distilbert-base-uncased'

# filesystem client for s3
s3 = S3FileSystem()

# dataset used
dataset_name = 'imdb'

# s3 key prefix for the data
s3_prefix = 'datasets/imdb'

# load dataset
dataset = load_dataset(dataset_name)

# download tokenizer
tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)

# tokenizer helper function
def tokenize(batch):
    return tokenizer(batch['text'], padding='max_length', truncation=True)

# load dataset
train_dataset, test_dataset = load_dataset('imdb', split=['train', 'test'])
test_dataset = test_dataset.shuffle().select(range(10000)) # smaller the size for test dataset to 10k

# tokenize dataset
train_dataset = train_dataset.map(tokenize, batched=True, batch_size=len(train_dataset))
test_dataset = test_dataset.map(tokenize, batched=True, batch_size=len(test_dataset))

# set format for pytorch
train_dataset = train_dataset.rename_column("label", "labels")
train_dataset.set_format('torch', columns=['input_ids', 'attention_mask', 'labels'])
test_dataset = test_dataset.rename_column("label", "labels")
test_dataset.set_format('torch', columns=['input_ids', 'attention_mask', 'labels'])

# save train_dataset to s3
training_input_path = f's3://{sess.default_bucket()}/{s3_prefix}/train'
train_dataset.save_to_disk(training_input_path,fs=s3)

# save test_dataset to s3
test_input_path = f's3://{sess.default_bucket()}/{s3_prefix}/test'
test_dataset.save_to_disk(test_input_path,fs=s3)

创建 HuggingFace Estimator 并训练我们的模型

为了创建 SageMaker Trainingjob,我们可以使用 HuggingFace Estimator。Estimator 处理端到端的 Amazon SageMaker 训练。在 Estimator 中,我们定义了应作为 entry_point 使用的微调脚本、应使用的 instance_type 以及传入的超参数。此外,还提供了许多高级控制,例如自定义输出和检查点位置、指定本地存储大小或网络配置。

SageMaker 负责为我们启动和管理所有必需的带有 Hugging Face DLC 的 Amazon EC2 实例,它上传提供的微调脚本,例如我们的 train.py,然后将数据从 S3 存储桶 sess.default_bucket() 下载到容器中。一旦数据准备就绪,训练作业将通过运行自动启动。

/opt/conda/bin/python train.py --epochs 1 --model_name distilbert-base-uncased --train_batch_size 32

您在 HuggingFace Estimator 中定义的超参数将作为命名参数传入。

from sagemaker.huggingface import HuggingFace

# hyperparameters, which are passed into the training job
hyperparameters={'epochs': 1,
                 'train_batch_size': 32,
                 'model_name':'distilbert-base-uncased'
                 }

# create the Estimator
huggingface_estimator = HuggingFace(
      entry_point='train.py',
      source_dir='./scripts',
      instance_type='ml.p3.2xlarge',
      instance_count=1,
      role=role,
      transformers_version='4.6',
      pytorch_version='1.7',
      py_version='py36',
      hyperparameters = hyperparameters
)

要启动训练,我们调用 .fit() 方法并将 S3 URI 作为输入。

# starting the train job with our uploaded datasets as input
huggingface_estimator.fit({'train': training_input_path, 'test': test_input_path})

附加功能 🚀

除了深度学习容器和 SageMaker SDK,我们还实现了其他附加功能。

分布式训练:数据并行

您可以直接使用 SageMaker 数据并行库进行分布式训练。我们已将数据并行功能直接添加到 Trainer 中。如果您的 train.py 使用 Trainer API,您只需在 HuggingFace Estimator 中定义 distribution 参数即可。

# configuration for running training on smdistributed Data Parallel
distribution = {'smdistributed':{'dataparallel':{ 'enabled': True }}}

# create the Estimator
huggingface_estimator = HuggingFace(
        entry_point='train.py',
        source_dir='./scripts',
        instance_type='ml.p3dn.24xlarge',
        instance_count=2,
        role=role,
        transformers_version='4.4.2',
        pytorch_version='1.6.0',
        py_version='py36',
        hyperparameters = hyperparameters
        distribution = distribution
)

“入门:端到端文本分类 🧭”示例可直接用于分布式训练。

分布式训练:模型并行

您可以直接使用 SageMaker 模型并行库进行分布式训练。我们已将模型并行功能直接添加到 Trainer 中。如果您的 train.py 使用 Trainer API,您只需在 HuggingFace Estimator 中定义 distribution 参数即可。
有关调整的详细信息,请查看此处

# configuration for running training on smdistributed Model Parallel
mpi_options = {
    "enabled" : True,
    "processes_per_host" : 8
}

smp_options = {
    "enabled":True,
    "parameters": {
        "microbatches": 4,
        "placement_strategy": "spread",
        "pipeline": "interleaved",
        "optimize": "speed",
        "partitions": 4,
        "ddp": True,
    }
}

distribution={
    "smdistributed": {"modelparallel": smp_options},
    "mpi": mpi_options
}

 # create the Estimator
huggingface_estimator = HuggingFace(
        entry_point='train.py',
        source_dir='./scripts',
        instance_type='ml.p3dn.24xlarge',
        instance_count=2,
        role=role,
        transformers_version='4.4.2',
        pytorch_version='1.6.0',
        py_version='py36',
        hyperparameters = hyperparameters,
        distribution = distribution
)

竞价型实例

通过为 SageMaker Python SDK 创建 HuggingFace 框架扩展,我们还可以利用完全托管的 EC2 竞价型实例的优势,节省高达 90% 的训练成本。

注意:除非您的训练作业能很快完成,否则我们建议您在托管式竞价型训练中使用检查点,因此您需要定义checkpoint_s3_uri

要将竞价型实例与 HuggingFace Estimator 配合使用,我们必须将 use_spot_instances 参数设置为 True,并定义您的 max_waitmax_run 时间。您可以在此处阅读有关托管式竞价型训练生命周期的更多信息。

# hyperparameters, which are passed into the training job
hyperparameters={'epochs': 1,
                 'train_batch_size': 32,
                 'model_name':'distilbert-base-uncased',
                 'output_dir':'/opt/ml/checkpoints'
                 }
# create the Estimator

huggingface_estimator = HuggingFace(
        entry_point='train.py',
        source_dir='./scripts',
        instance_type='ml.p3.2xlarge',
        instance_count=1,
          checkpoint_s3_uri=f's3://{sess.default_bucket()}/checkpoints'
        use_spot_instances=True,
        max_wait=3600, # This should be equal to or greater than max_run in seconds'
        max_run=1000,
        role=role,
        transformers_version='4.4',
        pytorch_version='1.6',
        py_version='py36',
        hyperparameters = hyperparameters
)

# Training seconds: 874
# Billable seconds: 105
# Managed Spot Training savings: 88.0%

Git 仓库

当您创建 HuggingFace Estimator 时,您可以指定一个存储在 GitHub 仓库中的训练脚本作为 Estimator 的入口点,这样您就不必在本地下载脚本。如果启用了 Git 支持,那么 entry_pointsource_dir(如果提供)应该是 Git 仓库中的相对路径。

作为示例,使用 git_config来自 transformers 仓库的示例脚本

请注意,您需要将 output_dir 定义为脚本的超参数,以便在训练后将模型保存到 S3。建议:将 output_dir 定义为 /opt/ml/model,因为它是默认的 SM_MODEL_DIR,并将上传到 S3。

# configure git settings
git_config = {'repo': 'https://github.com/huggingface/transformers.git','branch': 'master'}

 # create the Estimator
huggingface_estimator = HuggingFace(
        entry_point='run_glue.py',
        source_dir='./examples/text-classification',
        git_config=git_config,
        instance_type='ml.p3.2xlarge',
        instance_count=1,
        role=role,
        transformers_version='4.4',
        pytorch_version='1.6',
        py_version='py36',
        hyperparameters=hyperparameters
)

SageMaker 指标

SageMaker 指标可以自动解析日志中的指标并将这些指标发送到 CloudWatch。如果您希望 SageMaker 解析日志,您必须在配置训练作业时指定您希望 SageMaker 发送到 CloudWatch 的指标。您指定要发送的指标名称和 SageMaker 用于解析算法发出的日志以查找这些指标的正则表达式。

# define metrics definitions

metric_definitions = [
{"Name": "train_runtime", "Regex": "train_runtime.*=\D*(.*?)$"},
{"Name": "eval_accuracy", "Regex": "eval_accuracy.*=\D*(.*?)$"},
{"Name": "eval_loss", "Regex": "eval_loss.*=\D*(.*?)$"},
]

# create the Estimator

huggingface_estimator = HuggingFace(
        entry_point='train.py',
        source_dir='./scripts',
        instance_type='ml.p3.2xlarge',
        instance_count=1,
        role=role,
        transformers_version='4.4',
        pytorch_version='1.6',
        py_version='py36',
        metric_definitions=metric_definitions,
        hyperparameters = hyperparameters
)

常见问题解答 🎯

您可以在文档中找到完整的常见问题解答

问:什么是深度学习容器?

答:深度学习容器(DLC)是预装了深度学习框架和库(如 transformers、datasets、tokenizers)的 Docker 镜像,通过让您跳过从头构建和优化环境的复杂过程,从而轻松训练模型。

问:我必须使用 SageMaker Python SDK 才能使用 Hugging Face 深度学习容器吗?

答:您可以在不使用 SageMaker Python SDK 的情况下使用 HF DLC,并使用其他 SDK 启动 SageMaker 训练作业,例如 AWS CLIboto3。DLC 也可通过 Amazon ECR 获得,可以拉取并在任何选择的环境中使用。

问:为什么我应该使用 Hugging Face 深度学习容器?

答:DLC 是经过全面测试、维护和优化的深度学习环境,无需安装、配置或维护。

问:为什么我应该使用 SageMaker Training 来训练 Hugging Face 模型?

答:SageMaker Training 提供许多优势,将提高您使用 Hugging Face 的生产力:(1) 首先,它具有成本效益:训练实例仅在作业持续期间存在,并按秒付费。不再有整晚让 GPU 实例运行的风险:训练集群在作业结束时立即停止!它还支持 EC2 竞价容量,可将成本降低多达 90%。(2) SageMaker 还附带许多内置自动化功能,可促进团队协作和 MLOps:训练元数据和日志会自动持久化到无服务器托管的元数据存储,并且与 S3 的 I/O(用于数据集、检查点和模型工件)完全托管。最后,SageMaker 还允许大幅扩展和扩展:您可以并行启动多个训练作业,还可以启动大规模分布式训练作业。

问:我用 Amazon SageMaker 训练了模型,能用 🤗/Transformers 吗?

答:是的,您可以从 S3 下载训练好的模型,并直接与 transformers 一起使用,或将其上传到 Hugging Face 模型中心

问:Amazon SageMaker 如何保护我的数据和代码?

答:Amazon SageMaker 提供多种安全机制,包括静态加密传输中加密虚拟私有云 (VPC) 连接身份和访问管理 (IAM)。要了解更多关于 AWS 云和 Amazon SageMaker 中的安全,您可以访问Amazon SageMaker 中的安全AWS 云安全

问:这在我的地区可用吗?

答:有关受支持的区域列表,请访问AWS 区域表,了解所有 AWS 全球基础设施。

问:我需要向 Hugging Face 支付许可费才能使用 DLC 吗?

答:不需要——Hugging Face DLC 是开源的,并根据 Apache 2.0 许可。

问:如何在训练好的模型上运行推理?

答:您有多种选项可以在训练好的模型上运行推理。一种选择是使用 Hugging Face 加速推理 API 托管服务:首先将训练好的模型上传到您的 Hugging Face 帐户以公开或私有部署它们。另一个不错的选择是使用 SageMaker Inference 在 Amazon SageMaker 中运行您自己的推理代码。我们正在努力将来提供 Amazon SageMaker 与 Hugging Face Inference DLC 的集成解决方案——敬请期待!

问:你们为该解决方案提供高级支持或支持 SLA 吗?

答:AWS 提供 AWS 技术支持等级,涵盖 AWS 产品和服务的开发和生产问题——请参阅 AWS 支持了解具体细节和范围。

如果您有 Hugging Face 社区可以帮助回答和/或从中受益的问题,请在 Hugging Face 论坛中发布

如果您需要 Hugging Face 团队提供高级支持以加速您的 NLP 路线图,我们的专家加速计划提供来自我们开源、科学和机器学习工程团队的直接指导——联系我们了解更多信息

问:通过此次合作,你们接下来计划做什么?

答:我们的共同目标是使最先进的机器学习普及化。我们将继续创新,使研究人员、数据科学家和机器学习从业人员能够更轻松地管理、训练和运行最先进的模型。如果您对 AWS 与 Hugging Face 的集成有功能请求,请在Hugging Face 社区论坛中告诉我们

问:我使用 Hugging Face 与 Azure Machine Learning 或 Google Cloud Platform,这项合作对我意味着什么?

答:Hugging Face 的一个基本目标是让尽可能多的人能够使用最新的人工智能,无论他们使用何种框架或开发环境。虽然我们正在将集成工作重点放在 Amazon Web Services 作为我们的首选云提供商上,但我们将继续努力为所有 Hugging Face 用户和客户提供服务,无论他们运行在何种计算环境中。

社区

注册登录以评论