Published on

transformersでdeepspeedを使ってみる(config編)

Author
株式会社イエローバックの機械学習エンジニアです

はじめに

前回の記事で deepspeed を用いて文章分類をおこないました。

今回は前回使用した deepspeed の設定まわりをみていこうと思います。

deepspeed の config の各種パラメタについては、https://www.deepspeed.ai/docs/config-json/ に記載されています。 transformers で deepspeed を使用する際には、transformers の trainer に--deepspeedオプションで deepspeed 用の config ファイルを渡します。 その際に一部のパラメタは transformers 側のパラメタをもとに自動で設定することも可能です。

今回は、このあたりについて少し掘り下げてみます。

環境

transformers の deepspped まわりに開発は比較的活発なので、バージョンによって変わる可能性があります。 今回調査したのは以下の環境です。

  • python: 3.8
  • CuDA: 11.1
  • pytorch: 1.8.1
  • transformers: 4.6.1
  • deepspeed: 0.3.16

サンプル ds 用 config

まず前回用いたサンプル用の config が以下になります。

~/ldn/ds_config_zero2.json
{
    "fp16": {
        "enabled": "auto",
        "loss_scale": 0,
        "loss_scale_window": 1000,
        "initial_scale_power": 16,
        "hysteresis": 2,
        "min_loss_scale": 1
    },

    "optimizer": {
        "type": "AdamW",
        "params": {
            "lr": "auto",
            "betas": "auto",
            "eps": "auto",
            "weight_decay": "auto"
        }
    },

    "scheduler": {
        "type": "WarmupDecayLR",
        "params": {
            "warmup_min_lr": "auto",
            "warmup_max_lr": "auto",
            "warmup_num_steps": "auto",
            "total_num_steps": "auto"
        }
    },

    "zero_optimization": {
        "stage": 2,
        "allgather_partitions": true,
        "allgather_bucket_size": 6e7,
        "overlap_comm": true,
        "reduce_scatter": true,
        "reduce_bucket_size": 6e7,
        "contiguous_gradients": true,
        "cpu_offload": true
    },

    "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
}

optimizer や scheduler が未設定の場合に transformers 側のものが使用されますが、今回は deepspeed 側の optimizer,scheduler を利用する前提とします。

自動設定可能な項目

transformers では deepspeed 用の config を読み込んだあとに"auto"と設定された一部のパラメタを transformers のオプションをもとに設定して deepspeed に渡します。

これらは、DeepSpeedConfigHF クラスのconfig_process()config_finalize()あたりで設定されていますが、まとめると以下のようになります。

deepspeed configtransformers args
train_micro_batch_size_per_gpuargs.per_device_train_batch_size
gradient_accumulation_stepsargs.gradient_accumulation_steps
train_batch_sizeargs.world_size * args.per_device_train_batch_size * args.gradient_accumulation_steps
gradient_clippingargs.max_grad_norm
optimizer.lrargs.learning_rate
optimizer.betas[args.adam_beta1, args.adam_beta2]
optimizer.epsargs.adam_epsilon
optimizer.weight_decayargs.weight_decay
scheduler.warmup_min_lr0
scheduler.warmup_max_lrargs.learning_rate
scheduler.warmup_num_stepsargs.warmup_steps
scheduler.total_num_steps読み込んだ学習データから計算された値
fp16.enabledfp16_backend == "amp"または"apex"のときに"enabled"。 ただし"apex"のときには ZeRO 関連機能が有効にならないため apex は避けるべき

(以下は ZeRO-3 のときのみ)

deepspeed configtransformers args
zero_optimization.reduce_bucket_sizehidden_size * hidden_size
zero_optimization.stage3_prefetch_bucket_size0.9 * hidden_size * hidden_size
zero_optimization.stage3_param_persistence_threshold10 * hidden_size

optimizer type

deepspeed のoptimizer.typeは transformers 側からは自動で設定されないため、あらかじめ ds 用 config で明示的に指定する必要があります。 transformers のデフォルトは AdamW ですが、deepspeed では、Adam, AdamW, OneBitAdam, Lamb, OneBitLamb をネイティブにサポートしているようです。

scheduler type

deepspeed のscheduler.typeは transformers 側からは自動で設定されないため、あらかじめ ds 用 config で明示的に指定する必要があります。

deepspeed のscheduler.typeと transformers の"--lr_scheduler_type"の対応は transformers のintegrations.py のコメント によると、以下のようになっています。

DS name--lr_scheduler_typeHF funcNotes
LRRangeTestnanaLRRT
OneCyclenana1CLR
WarmupLRconstant_with_warmupget_constant_schedule_with_warmupw/ warmup_min_lr=0
WarmupDecayLRlinearget_linear_schedule_with_warmup

おわりに

今回は transformers+deepspeed を使用する際の deepspeed の設定まわりについて調べてみました。