关于人工智能:使用-DeepSpeed-和-Hugging-Face-🤗-Transformer-微调-FLANT5-XLXXL

Scaling Instruction-Finetuned Language Models 论文公布了 FLAN-T5 模型,它是 T5 模型的增强版。FLAN-T5 由很多各种各样的工作微调而得,因而,简略来讲,它就是个方方面面都更优的 T5 模型。雷同参数量的条件下,FLAN-T5 的性能相比 T5 而言有两位数的进步。Google 在 Hugging Face 上开源了 5 个 FLAN-T5 的 checkpoints,参数量范畴从 8000 万 到 110 亿。

在之前的一篇博文中,咱们曾经学习了如何 针对聊天对话数据摘要生成工作微调 FLAN-T5,那时咱们应用的是 Base (250M 参数) 模型。本文,咱们将钻研如何将训练从 Base 扩大到 XL (30 亿参数) 或 XXL (110 亿参数)。

这意味着咱们将学习如何利用模型并行、多 GPU 以及 DeepSpeed ZeRO 来微调 FLAN-T5 XL 和 XXL。

除了作为教程的局部之外,咱们还跑了一系列试验,这些试验数据能够帮忙你抉择正确的硬件设置。你能够在 后果和试验 局部找到详细信息。

# install git lfs for pushing artifacts
!sudo apt install git-lfs
# install torch with the correct cuda version, check nvcc --version
!pip install torch --extra-index-url https://download.pytorch.org/whl/cu116 --upgrade
# install Hugging Face Libraries
!pip install "transformers==4.26.0" "datasets==2.9.0" "accelerate==0.16.0" "evaluate==0.4.0" --upgrade
# install deepspeed and ninja for jit compilations of kernels
!pip install "deepspeed==0.8.0" ninja --upgrade
# install additional dependencies needed for training
!pip install rouge-score nltk py7zr tensorboard

解决数据集

与 针对聊天对话的摘要生成工作微调 FLAN-T5 一文中相似,咱们须要先筹备一个用于微调的数据集。本文,咱们将在 CNN Dailymail 数据集 上微调 FLAN-T5-XXL。咱们不会赘述如何生成数据集,如果你想理解数据集生成的具体步骤,请参阅前文提到的 Fine Tune FLAN-T5。

咱们定义了一些参数,本文的示例都会基于这些参数,但你能够依据理论须要进行调整。

# 试验配置
model_id = "google/flan-t5-xxl" # Hugging Face 模型 Id
dataset_id = "cnn_dailymail" # Hugging Face 数据集 Id
dataset_config = "3.0.0" # 数据集版本
save_dataset_path = "data" # 寄存解决后数据的本地门路
text_column = "article" # 输出文本所属列
summary_column = "highlights" # 输入文本所属列
# 定制指令提醒格局
prompt_template = f"Summarize the following news article:\n{{input}}\nSummary:\n"

与 Fine Tune FLAN-T5 不同,这次咱们把预处理和训练离开。这样咱们就能够在非 GPU 实例上运行预处理。咱们先对数据集进行预处理 (即分词) 并将其保留到磁盘,而后训练脚本再从磁盘中加载预处理后的数据集。

from datasets import load_dataset
from transformers import AutoTokenizer
import numpy as np

# Load dataset from the hub
dataset = load_dataset(dataset_id,name=dataset_config)
# Load tokenizer of FLAN-t5-base
tokenizer = AutoTokenizer.from_pretrained(model_id)

print(f"Train dataset size: {len(dataset['train'])}")
print(f"Test dataset size: {len(dataset['test'])}")

# Train dataset size: 287113
# Test dataset size: 11490

咱们在配置文件中定义了一个 prompt_template,其可用于来构建指令提醒,以进步咱们模型的性能。 prompt_template 有“固定”的开始词和完结词,文档放在两头。这意味着咱们须要确保 “固定”模板词 + 文档 总长不超过模型反对的最大序列长度。因而咱们须要计算模型反对的最大文档长度,稍后咱们会依据它来填充或截断模板中的文档。

prompt_length = len(tokenizer(prompt_template.format(input=""))["input_ids"])
max_sample_length = tokenizer.model_max_length - prompt_length
print(f"Prompt length: {prompt_length}")
print(f"Max input length: {max_sample_length}")

# Prompt length: 12
# Max input length: 500
Prompt length: 12
Max input length: 500

当初咱们晓得,模型反对的最大输出文档长度为 500。除了输出之外,咱们还须要晓得最大“指标”序列长度,咱们能够通过遍历数据集中的摘要长度来失去。(代码须要运行几分钟)

from datasets import concatenate_datasets
import numpy as np

# The maximum total input sequence length after tokenization.
# Sequences longer than this will be truncated, sequences shorter will be padded.
tokenized_inputs = concatenate_datasets([dataset["train"], dataset["test"]]).map(lambda x: tokenizer(x[text_column], truncation=True), batched=True, remove_columns=[text_column, summary_column])
max_source_length = max([len(x) for x in tokenized_inputs["input_ids"]])
max_source_length = min(max_source_length, max_sample_length)
print(f"Max source length: {max_source_length}")

# The maximum total sequence length for target text after tokenization.
# Sequences longer than this will be truncated, sequences shorter will be padded."
tokenized_targets = concatenate_datasets([dataset["train"], dataset["test"]]).map(lambda x: tokenizer(x[summary_column], truncation=True), batched=True, remove_columns=[text_column, summary_column])
target_lenghts = [len(x) for x in tokenized_targets["input_ids"]]
# use 95th percentile as max target length
max_target_length = int(np.percentile(target_lenghts, 95))
print(f"Max target length: {max_target_length}")
  0%|          | 0/299 [00:00<?, ?ba/s]
Max source length: 500
    
  0%|          | 0/299 [00:00<?, ?ba/s]
Max target length: 129

当初所有准备就绪,能够解决数据集了。

import os

def preprocess_function(sample, padding="max_length"):
    # created prompted input
    inputs = [prompt_template.format(input=item) for item in sample[text_column]]

    # tokenize inputs
    model_inputs = tokenizer(inputs, max_length=tokenizer.model_max_length, padding=padding, truncation=True)

    # Tokenize targets with the `text_target` keyword argument
    labels = tokenizer(text_target=sample[summary_column], max_length=max_target_length, padding=padding, truncation=True)

    # If we are padding here, replace all tokenizer.pad_token_id in the labels by -100 when we want to ignore
    # padding in the loss.
    if padding == "max_length":
        labels["input_ids"] = [
            [(l if l != tokenizer.pad_token_id else -100) for l in label] for label in labels["input_ids"]
        ]

    model_inputs["labels"] = labels["input_ids"]
    return model_inputs

# process dataset
tokenized_dataset = dataset.map(preprocess_function, batched=True, remove_columns=list(dataset["train"].features))

# save dataset to disk
tokenized_dataset["train"].save_to_disk(os.path.join(save_dataset_path,"train"))
tokenized_dataset["test"].save_to_disk(os.path.join(save_dataset_path,"eval"))

应用 deepspeed 微调模型

筹备结束!咱们当初能够开始训练模型了!如前所述,咱们将应用集成了 DeepSpeed 的 Hugging Face Trainer。因而咱们须要创立一个 deespeed_config.json。DeepSpeed 配置 定义了要应用的 ZeRO 策略以及是否要应用混合精度训练等配置项。 Hugging Face Trainer 容许咱们从 deepspeed_config.json 中的 TrainingArguments 继承相干配置以防止反复设置,查看 文档理解更多信息。

咱们创立了 4 组 deepspeed 配置文件用于试验,包含 CPU 卸载混合精度:

  • ds_flan_t5_z3_config.json
  • ds_flan_t5_z3_config_bf16.json
  • ds_flan_t5_z3_offload.json
  • ds_flan_t5_z3_offload_bf16.json

你能够依据你的运行环境抉择,例如如果在 NVIDIA V100s 上运行,你就不能应用带 bf16 的配置,因为 V100 不反对 bfloat16 数据类型。

在微调 T5 模型时,不能应用 fp16,因为它会导致精度溢出问题,参见问题 #4586,#10830,和拉取申请 #10956

如结尾所述,咱们应用的是 p4dn.24xlarge AWS EC2 实例,该实例蕴含 8 张显存为 40GB 的 NVIDIA A100。这意味着咱们能够应用 bf16,它将缩小近一半的模型显存占用,使咱们可能在不卸载的状况下高效训练。

咱们将应用 ds_flan_t5_z3_config_bf16.json。如果你不想用 auto 值,能够查看 文档。

{
  "bf16": {
    "enabled": "auto"
  },
  "optimizer": {
    "type": "AdamW",
    "params": {
      "lr": "auto",
      "betas": "auto",
      "eps": "auto",
      "weight_decay": "auto"
    }
  },
  "scheduler": {
    "type": "WarmupLR",
    "params": {
      "warmup_min_lr": "auto",
      "warmup_max_lr": "auto",
      "warmup_num_steps": "auto"
    }
  },
  "zero_optimization": {
    "stage": 3,
    "overlap_comm": true,
    "contiguous_gradients": true,
    "sub_group_size": 1e9,
    "reduce_bucket_size": "auto",
    "stage3_prefetch_bucket_size": "auto",
    "stage3_param_persistence_threshold": "auto",
    "stage3_max_live_parameters": 1e9,
    "stage3_max_reuse_distance": 1e9,
    "stage3_gather_16bit_weights_on_model_save": false
  },
  "gradient_accumulation_steps": "auto",
  "gradient_clipping": "auto",
  "steps_per_print": 2000,
  "train_batch_size": "auto",
  "train_micro_batch_size_per_gpu": "auto",
  "wall_clock_breakdown": false
}

当初,该训练脚本上场了。咱们依据 Fine Tune FLAN-T5 筹备了一个 run_seq2seq_deepspeed.py 训练脚本,它反对咱们配置 deepspeed 和其余超参数,包含 google/flan-t5-xxl 的模型 ID。

咱们应用 deepspeed 启动器触发训练,输出给启动器的参数包含 GPU 数量、deepspeed 配置及其它超参数 (如 google/flan-t5-xxl 的模型 ID)。

!deepspeed --num_gpus=8 scripts/run_seq2seq_deepspeed.py \
    --model_id $model_id \
    --dataset_path $save_dataset_path \
    --epochs 3 \
    --per_device_train_batch_size 8 \
    --per_device_eval_batch_size 8 \
    --generation_max_length $max_target_length \
    --lr 1e-4 \
    --deepspeed configs/ds_flan_t5_z3_config_bf16.json
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using `tokenizers` before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
deepspeed --num_gpus=8 scripts/run_seq2seq_deepspeed.py --model_id google/flan-t5-xxl --dataset_path data --epochs 3 --per_device_train_batch_size 8 --per_device_eval_batch_size 8 --generation_max_length 129 --lr 1e-4 --deepspeed configs/ds_flan_t5_z3_config_bf16.json

DeepSpeed 先将模型加载到 CPU 上,而后将其拆分到 8 张 A100 上而后开始训练。应用 CNN Dailymail 数据集 进行训练大概须要 10 个小时,费用约为 322 美元

后果与试验

为了更好地理解硬件要求,咱们对 FLAN-T5 XL 和 XXL 进行了一系列试验,以帮忙咱们评估和理解硬件需要以及训练这些模型的老本。

下表列出了试验和相干设置的详细信息。

数据集: "cnn_dailymail"

  • 训练样本数: 287113
  • 验证样本数: 13368

超参:

  • epochs: 3
  • 学习率: 1e-4

运行环境设置:

  • 4x V100 16GB: p3.8xlarge
  • 4x A10G 24GB: g5.24xlarge
  • 8x V100 16GB: p3.16xlarge
  • 8x A100 40GB: p4dn.24xlarge
模型 DeepSpeed 卸载 硬件 GPU每卡batch size 精度 时长 费用
FLAN-T5-XL (3B) No 4x V100 16GB OOM fp32
FLAN-T5-XL (3B) No 8x V100 16GB 1 fp32 105h ~$2570
FLAN-T5-XL (3B) No 8x A100 40GB 72 bf16 2.5h ~$81
FLAN-T5-XL (3B) Yes 4x V100 16GB 8 fp32 69h ~$828
FLAN-T5-XL (3B) Yes 8x V100 16GB 8 fp32 32h ~$768
FLAN-T5-XXL (11B) No 8x A100 40GB 8 bf16 10h ~$322
FLAN-T5-XXL (11B) Yes 4x V100 16GB OOM fp32
FLAN-T5-XXL (11B) Yes 8x V100 16GB OOM fp32
FLAN-T5-XXL (11B) Yes 4x A10G 24GB 24 bf16 90h ~$732
FLAN-T5-XXL (11B) Yes 8x A100 40GB 48 bf16 19h ~$613

咱们能够看到 bf16fp32 相比具备显著劣势。FLAN-T5-XXL 能放进 4 张 A10G (24GB),但放不进 8 张 V100 16GB。

咱们的试验还表明,如果模型能够无需卸载同时以 batch size 大于 4 的配置跑在 GPU 上,其速度将比卸载模型和减小 batch size 的配置快约 2 倍且更具老本效益。


英文原文: https://www.philschmid.de/fine-tune-flan-t5-deepspeed

原文作者: Philipp Schmid

译者: Matrix Yao (姚伟峰),英特尔深度学习工程师,工作方向为 transformer-family 模型在各模态数据上的利用及大规模模型的训练推理。

审校、排版: zhongdongy (阿东)

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理