21
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

DeepSpeedのインストール&ZeRO-Offload利用法

Last updated at Posted at 2021-01-23

巨大なニューラルネットを学習するZeRO-Offloadと、それを含むDeepSpeedライブラリが話題だったので使ってみました。(2021/1/24時点)
本家ドキュメント https://www.deepspeed.ai/getting-started/

インストール

apt install openmpi-bin
pip3 install mpi4py
pip3 install torch
pip3 install deepspeed

注意点

  • GPUが無いと動かない
  • インストールされているCUDAのバージョンとPyTorchのCUDAのバージョンが違うと動かない

参考

  • CUDA10.1で動作確認(PyTorchはCUDA10.1のものを指定してインストール https://pytorch.org/get-started/locally/)
  • CUDA11.0では今の所、deepspeedインストール時にtritonのインストール失敗で動かせていない ※サポート外? 情報求む

サンプル動作

ニューラルネット定義

  • 約1億パラメータのモデル
  • 記法は通常のPyTorchから変更なし

import torch
import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(200, 10000)
        self.fc2 = nn.Linear(10000, 10000)
        self.fc3 = nn.Linear(10000, 1)

    def forward(self, x):
        h = self.fc1(x)
        h = F.relu_(h)
        h = self.fc2(h)
        h = F.relu_(h)
        h = self.fc3(h)
        return h

学習(DeepSpeedなし)

※DeepSpeed利用法に焦点を当てて無意味なデータを学習

import numpy as np
import torch.optim as optim

model = Model()
optimizer = optim.Adam(model.parameters(), lr=1e-4)
batch_size = 1000
model.cuda()

for e in range(1000):
    x = torch.FloatTensor(np.random.rand(batch_size, 200)).cuda()
    y_ = torch.FloatTensor(np.random.randn(batch_size, 1)).cuda()

    y = model(x)
    loss = (y - y_).pow(2).mean()
    loss.backward()
    optimizer.step()
    print(e, loss.item())

学習(DeepSpeed)

sample.py (上のネットワーク定義も入れる)

import argparse
import numpy as np

parser = argparse.ArgumentParser()
parser.add_argument('--deepspeed', action='store_true')
parser.add_argument('--deepspeed_config', type=str)
parser.add_argument('--local_rank', type=int)

cmd_args = parser.parse_args()

model = Model()
model_engine, optimizer, _, _ = deepspeed.initialize(args=cmd_args, model=model, model_parameters=model.parameters())

batch_size = 1000  # ここは設定ファイル(下)と合わせた

for e in range(1000):
    x = torch.HalfTensor(np.random.rand(batch_size, 200)).cuda()  # 半精度
    y_ = torch.HalfTensor(np.random.randn(batch_size, 1)).cuda()  # 半精度

    y = model_engine(x)  # 変更
    loss = (y - y_).pow(2).mean()
    model_engine.backward(loss)  # 変更
    model_engine.step()  # 変更
    print(e, loss.item())

ds_config.json

{
    "train_batch_size": 1000,
    "gradient_accumulation_steps": 1,
    "optimizer": {
        "type": "Adam",
        "params": {
            "lr": 1e-4
        }
    },
    "fp16": {
        "enabled": true
    },
    "zero_optimization": true
}

コマンド

deepspeed sample.py --deepspeed --deepspeed_config=ds_config.json

動作結果

データ型はFloat, Halfのどちらでも実験

DeepSpeed 浮動小数点数 学習結果 時間
なし Float OK 3分49秒
なし Half nan 45秒
あり Float ZeRO非対応 ---
あり Half OK 1分15秒

このサンプルでは、

  • DeepSpeedにより同じ計算における速度向上はなかった
  • DeepSpeedは半精度でもオーバーフローに対応して学習できていた(そういったメッセージが出ていたことを確認)
  • 結果として、(もし学習精度が同程度なら)半精度学習として速く軽くなる利用価値はある

巨大なモデルの学習

15GBのGPUメモリに乗り切らないモデルを作ってみた

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(200, 20000)
        self.fc2 = nn.Linear(20000, 20000)
        self.fc3 = nn.Linear(20000, 20000)
        self.fc4 = nn.Linear(20000, 1)

    def forward(self, x):
        h = self.fc1(x)
        h = F.relu_(h)
        h = self.fc2(h)
        h = F.relu_(h)
        h = self.fc3(h)
        h = F.relu_(h)
        h = self.fc4(h)
        return h

結果

RuntimeError: CUDA out of memory.

GPUに乗らないモデルはこのままでは扱えなかった。
対応するドキュメント https://www.deepspeed.ai/tutorials/zero-offload/ に従ってds_config.jsonのzero_optimization部分を

{
    "zero_optimization": {
        "stage": 2,
        "cpu_offload": true,
        "contiguous_gradients": true,
        "overlap_comm": true
    }
}

のように書き換えてZeRO-Offloadを有効したがやはりダメ、どんな容量オーバーモデルに対してもすぐ使える方法という訳ではなさそう。
引き続き要調査。

※追記:あくまでTransfomerのような大量のTensorを使うモデルの最適化が目的で、巨大なTensorを分解してまでGPUに載せようとするわけではない、とのこと。

現時点での感想

長所

  • 呼び出しを変えるだけで半精度モデル学習をオーバーフロー対処付きで使える

今後に注目

  • コード側で引数を準備して呼び出し方を変えるので、コードの一部だけ変えるだけ、とまでは言えない(並列システムだから仕方ない気も)
  • 巨大なモデルの学習はできるとは限らない

情報はぜひコメントお願いします。

21
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?