🖥

倧芏暡モデルを支える分散䞊列孊習のしくみ Part1

に公開
2023/06/22

はじめに

Turing 株匏䌚瀟のリサヌチチヌムでむンタヌンをしおいる東京工業倧孊 B4 暪田研究宀の藀井(@okoge_kaz)です。

自然蚀語凊理分野における倧芏暡深局孊習の重芁性は日に日に高たっおいたすが、GPT-3, GPT-4 などのモデルの孊習には膚倧な蚈算コストがかかり、容易に孊習できなくなっおいたす。実際、モデルサむズが近幎急速に倧きくなっおいるこずにより、孊習に必芁な蚈算量(FLOPs)は以䞋のように幎々膚倧になっおいたす。近幎の倧芏暡モデルでは、NVIDIA H100 80GB であっおも 1 ぀の GPU では、モデルをのせるこずすらできたせん。

flops-graph
Compute Trends Across Three Eras of Machine Learning より

たたScaling Laws によるず、倧芏暡なモデルは小さいモデルず比范しおより優れた性胜を発揮するため、自動運転などの難易床が高いタスクを解くためには倧芏暡なモデルが䞍可欠です。しかし倧芏暡モデルの孊習には、䞊述のように膚倧な蚈算資源が必芁であり、倧芏暡なGPUリ゜ヌスず時間がかかりたす。

Turingでは基盀モデルを甚いた完党自動運転の実珟を目指しおおり、基盀モデルの孊習を支える技術に関しお研究開発を行っおいたす。

https://zenn.dev/turing_motors/articles/bd575a1097a266

たさにこの倧芏暡モデルの孊習の裏で掻躍しおいるのが分散䞊列孊習です。本蚘事では、この分散䞊列孊習の技術に぀いお説明を行いたす。

分散䞊列孊習のメリット

モデルが倧きすぎお 1 ぀の GPU に収たらないこずや、デヌタセットが倧きすぎお 1 ぀の GPU では 100 日間かかるような堎合、埌述する Model Parallel や、 Data Parallel を甚いるこずで蚓緎を可胜にしたり、蚓緎プロセスを高速化し劥圓な時間で結果を埗るこずができたす。

16 日かかっおいた孊習を DDP(Distributed Data Parallel)を甚いるこずで、2 日間に短瞮したり、Model Parallel を䜿うこずでバッチサむズを 2 から 16 に拡倧したりするこずができたす。

分散䞊列孊習のコンセプト

次に分散䞊列孊習のコンセプトに぀いお説明したす。
たず分散䞊列孊習(Distributed Parallel Training)には耇数 GPU (もしくは耇数 node) が必芁です。

分散䞊列孊習では孊習の過皋で、デバむスは通信により情報を共有したす。
(ここでの情報ずはモデルの募配など孊習に必芁な情報をむメヌゞしおください)

なぜ「情報の共有」が必芁なのかずいうず、分散䞊列孊習では孊習察象のタスクを分割し耇数のデバむスでそれを凊理したす。しかし䜕も通信しなければ、各デバむスは自分が凊理しおいる内容は分かっおも、他のデバむスが凊理しおいる内容に぀いおは知り埗たせん。元々凊理したかった分割する前のタスクず同じ結果を埗るためには、各デバむスの凊理内容を適切なタむミングで同期する必芁がありたす。そのため、分散䞊列孊習ではデバむス間(node間)で情報を共有するこずが必芁ずなるのです。

分散䞊列孊習をより深く理解するには以䞋の4぀の抂念を抌さえる必芁がありたす。

  • Host
    通信ネットワヌクのメむンデバむスのこずをホスト(Host)ず呌びたす。䞀般的に、分散環境を初期化する際、匕数でどのデバむスをホストにするのか指定したす。
  • Port
    分散䞊列孊習の際、通信に甚いるホストのマスタヌポヌトのこずを指したす。
  • Rank
    分散䞊列孊習の際、ネットワヌク䞊の機噚に䞎えられる固有の ID です。
  • World Size
    分散䞊列孊習に甚いるデバむスの数のこずです。2 nodes 16 GPUs による分散孊習の際は、world_size = 16 ずなりたす。

data-parallel
(Colossal-AI Concepts Distributed Trainingsより)

具䜓䟋を瀺したす。
䞊蚘のような構成の堎合は、䞀般的に Host は Node 0 の GPU 0 が担いたす。

たたポヌトは、他のナヌザヌず port 番号が被らないように(JOB_ID + 10000) % CONSTANT_VALUE などを蚭定するこずが倚いです。

Rank は以䞋のような圢で割り振られたす。(必ず䞀意になるように割り振られたす。)

Node GPU index Rank
0 0 0
0 1 1
0 2 2
0 3 3
1 0 4
1 1 5
1 2 6
1 3 7

World Size は 8 枚 GPU があるので 8 です。

分散䞊列化手法

分散䞊列孊習に぀いお「なんずなく」むメヌゞが぀いおきたのではないかず思いたす。
以䞋では、それぞれの手法に぀いお詳现な説明を行うこずで目的に沿った䞊列化手法を遞べるようになるための説明を行いたす。

Data Parallel

Data Parallel(デヌタ䞊列)は、グロヌバルバッチサむズを倧きくするこずで孊習時間を短瞮するこずを目的ずした䞊列化手法です。

デヌタセットを GPU の個数に分割し、分割したデヌタセットを各 GPU に割り圓おたす。各 GPU はモデルの耇補をそれぞれ有しおいるため、割り圓おられたデヌタセットをそれぞれ別々に孊習したす。そしお誀差逆䌝播(back-propagation)の埌に、モデルの募配を GPU 間で共有するこずで同期をずりたす。

デヌタ䞊列の様子を衚したむラストが䞋蚘になりたす。

data-parallel-concept

(Colossal-AI Concepts Paradigms of Parallelismより)

各 GPU はデヌタセット党䜓の 1/4 のデヌタを孊習しおいるだけに芋えたすが、モデルの募配をデバむス間(GPU 間)で同期するこずでデヌタセットをすべお孊習したのず同じ結果を埗るこずができたす。

では Data Parallel で䜕が倉わったのでしょうか倧きな倉化は、グロヌバルバッチサむズの倉化ず、孊習時間の短瞮です。

たず、グロヌバルバッチサむズの倉化ですが、1 ぀の GPU だけで孊習をしおいた際のバッチサむズをBずするず、4 ぀の GPU を甚いお DP(Data Parallel)を行うずグロヌバルバッチサむズは 4 \times B = 4B ずなりたす。そのため、Loss の掚移や正解率の掚移は 1 ぀の GPU で4Bのバッチサむズで孊習した結果ず類䌌するようになりたす。そのため、孊習を高速化するために GPU 数を倧きくするず、それに応じおグロヌバルバッチサむズも増倧したす。これはLarge Batch Problem を匕き起こしかねず、泚意が必芁です。

次に孊習の短瞮ですが、これは盎感的にも分かるように、1 ぀の GPU が担圓するべきデヌタセットサむズが䞊列数Pに応じお1/Pになるからです。しかし、モデルの募配を同期するためにデバむス間で通信が発生するため実際の孊習時間は短瞮されど、1/Pにピッタリ䞀臎するわけではないこずに泚意が必芁です。

ZeRO

デヌタ䞊列(Data Parallel)では、各 GPU がそれぞれモデル党䜓の重みを有しおいたした。しかし、これは冗長性の芳点で芋るず無駄があるずも蚀えたす。それぞれの GPU がモデルの状態(Optimizer の状態、募配、モデルパラメヌタヌ)を耇補するのではなく、GPU 間で分割するこずで Data Parallel のメモリの冗長性を排陀するこずができれば、さらに良さそうです。

実はこれを実珟しおいるのが Zero Redundancy Optimizer 通称 ZeRO です。この蚘事では ZeRO に関する説明は省略したすが、続線の「倧芏暡モデルを支える分散䞊列孊習のしくみ Part2」にお詳しく解説する予定です。

Model Parallel

Data Parallel はモデルの耇補をそれぞれの GPU がも぀こずで、孊習を高速化しようずするアプロヌチでした。これから玹介する Model Parallel は、モデルを分割しお耇数 GPU に分散させるこずで 1 ぀の GPU には収たりきらないようなモデルを孊習可胜にしようずするアプロヌチです。

モデル䞊列には Tensor Parallel(テン゜ル䞊列)ず Pipeline Parallel(パむプラむン䞊列)の 2 皮がありたす。

以䞋では、それぞれの䞊列化手法に぀いお説明を行いたす。

Tensor Parallel

Tensor Parallel の基本

Tensor Parallel(テン゜ル䞊列) ずは、テン゜ルを特定の次元に沿っおN個のチャンクに分割し、各 GPU は分割された1/Nのテン゜ルだけを凊理したす。蚈算結果の党䜓が必芁なずきは、通信するこずで蚈算結果を集玄したす。

具䜓䟋を芋たほうが分かりやすいず思うので以䞋の図を䜿っお説明したす。

tensor-parallel-concept

(huggingface より)

今我々が蚈算したいのは

Y = XA

ずしたす。ここでAを列方向に分割するこずを考えたす。(䞊図の column parallelism に盞圓)

するず蚈算匏は以䞋のように衚せたす。

Y = X A = X [A_1, A_2, ..., A_N]

よっお各 GPU ではY_k = X A_kを蚈算すれば良くなりたす。そしお埗られたY_kをその埌集玄したす。

これによりYを埗るこずができたした。(なお図の右䞊にY_1, Y_1ずありたすが、正しくはY_1, Y_2です)

同様に、行方向の分割に぀いおも可胜です。以䞋では列方向の分割ず、行方向の分割を組み合わせたものを瀺したす。

A = [A_1, A_2]^Tず衚せるため

Y = X A = [X_1, X_2] [A_1, A_2]^T = X_1 A_1 + X_2 A_2 = Y_1 + Y_2

ず衚せたす。これにより行列積 Y = XA をより小さいなサむズの行列積 Y_1 = X_1 A_1, Y_2 = X_2 A_2に分割するこずができたす。

掻性化関数ず Tensor Parallel

䞊述の列方向分割により、Y = X A を Y=[Y_1, Y_2, ..., Y_n] = X [ A_1, A_2, ..., A_n]ず分割できた堎合、蚈算が終わった埌に結果を集玄しおしたうこずは䞀般に非効率です。

䟋えば蚈算埌に掻性化関数を通る堎合

\begin{align} \text{GeLU}(Y) &= [\text{GeLU}(Y_1),\text{GeLU}(Y_2),...,\text{GeLU}(Y_n) ] \\ &= [\text{GeLU}(X A_1), \text{GeLU}(X A_2), ...., \text{GeLU}(X A_n)] \end{align}

ず衚せるので、

行列を分割 → 行列積蚈算 → 集箄 → 埗られた行列を分割 → 掻性化関数 ずするよりも

行列を分割 → 行列積蚈算 → 掻性化関数

ずしたほうが、集玄するための通信コストや、分割する際のオヌバヌヘッドをなくすこずができたす。

以䞋の図のように出力ベクトルを集玄する必芁があるたで分割したたた凊理するこずで、任意の深さの MLP においお GPU 間の同期を必芁ずせず孊習を進めるこずができたす。

tensor-parallel-mlp

(Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism)

䞊蚘のような GPU 間の同期が少なくなくおすむ構造が倚い堎合は、Y = X A = [X_1, X_2] [A_1, A_2]^T = X_1 A_1 + X_2 A_2 = Y_1 + Y_2 よりも、Y = X A = X [A_1, A_2, ..., A_N] のように分割したほうが効率的です。なぜならば、X A = [X_1, X_2] [A_1, A_2]^Tでは、\text{GeLU}(Y) = \text{GeLU}(XA) = \text{GeLU}(X_1 A_1 + X_2 A_2)のように GeLU 関数の前に蚈算結果を同期するこずが必芁になるからです。

Multi-Head Attention 局においおは以䞋のように、䞊列化可胜な構造を有しおいたす。(= それぞれの Head が独立になっおいる) そのため、Y = X A = X [A_1, A_2, ..., A_N] のように分割するこずで䞊列化を行うこずが効果的です。

tensor-parallel-self-attention

(Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism)

Pipeline Parallel

About

パむプラむン䞊列のコンセプトは、モデルを分割し、分割した Layers を別々の GPU が持぀ずいうものです。これにより、1 ぀の GPU に乗りきらないサむズのモデルを孊習するこずができたす。

pipeline-parallel-overview

(Colossal-AI: Concepts/Paradigms of Parallelism Pipeline Parallel)

Naive Model Parallel

パむプラむン䞊列の考え方を愚盎に行うのが Naive Model Parallell(Vertical)です。この方法では GPU の倧郚分を掻甚するこずができないため実際に甚いられるこずはありたせん。しかし Pipeline Parallel を理解する助けになるため玹介したす。

Naive Model Parallel では以䞋のようにモデルを耇数の GPU に分割したす。

=================  =================
| 0 | 1 | 2 | 3 |  | 4 | 5 | 6 | 7 |
=================  =================
     GPU:0              GPU:1

同䞀 GPU 内で layer:0 → layer:1 → layer:2 → layer: 3 ず䌝搬させおいく際は通垞の堎合ず倉わりありたせん。しかし、layer: 3 → layer: 4 ぞ䌝搬させる際は GPU が異なるため GPU 間で通信が必芁ずなりたす。具䜓的には、䞭間掻性(intermediate activation)を GPU:0 → GPU:1 ぞ送るこずで実珟したす。

(逆䌝搬の際は、GPU:1 → GPU:0 ぞ入力テン゜ル(input tensor)を䌝達したす。)

特に問題がなさそうに芋える Naive Model Parallel ですが、GPU:0 で䌝搬が行われおいる際は GPU:1 は idle 状態になっおしたっおいたす。これは効率性の芳点からは臎呜的です。䟋えば、この方法ではGPU 数が 8 枚ある際は 7 枚が垞に idle 状態になっおしたいたす。GPU 間でデヌタを送るこずによるオヌバヌヘッドも存圚するため、このたたでは非垞に効率が悪い䞊列凊理になっおしたいたす。

この問題を解決するための手法が、ミニバッチをマむクロバッチに分けおパむプラむン化する方法です。

この方法による分割䞊列をパむプラむン䞊列ず呌びたす。

Pipeline Parallel

pipeline-parallel

(Google Research: Introducing Gpipe, an Open Source LIbrary for Efficiently Training Large-scale Neural Network Models)

䞊図は 1 ぀の GPU 以倖が idle になっおいる非効率な方法から、バッチをさらに分割しマむクロバッチずするこずでパむプラむンを䜜り GPU の皌働率を䞊げおいる状態を瀺しおいたす。

ある時刻t = t_kに぀いお芋おみるず以䞋のようになりたす。

pipeline-parallel-with-time

Naive Model Parallel では 1 ぀の GPU(Device2)しか掻甚できおいないのが、Pipleline Parallel では 3 ぀の GPU(Device3, Deveice2, Device 1)を利甚できおいるこずがわかりたす。

(Pipeline Parallel の図に”Bubble”ず曞かれた郚分がありたすが、ここは GPU デバむスが idle 状態にあり有効掻甚できおいないこずを衚しおいたす。)

Pipeline Parallel においお効率を䞊げるには Bubble の郚分を小さくするこずが重芁です。Bubble を小さくするにはデヌタのチャンク数(F_{i, j}のjはチャンクの index を衚しおいたす。䞊図では chunk 数は 4です。)を倧きくすればよいですが、分割しすぎるこずによる匊害も存圚したす。chunk 数は孊習における Hyperparameter の 1 ぀ずなりえたす。

぀づく

この蚘事では分散䞊列孊習に関する基本的なコンセプト、各皮分散䞊列孊習に぀いお抂説したした。

続線の 「倧芏暡モデルを支える分散䞊列孊習のしくみ Part 2」 ではさらに ZeRO を始めずする最新の分散孊習の技術に぀いお解説したす。たた、「倧芏暡モデルを支える分散䞊列孊習のしくみ 実践線 Part 1」 では今回の蚘事にお説明した抂念を甚いお実際に孊習を行うための実装䟋を瀺したす。(PyTorch, DeepSpeedでの実装䟋を瀺す予定です。)

Turing では自動運転モデルの孊習や、自動運転を支えるための倧芏暡蚀語モデルの䜜成のために分散䞊列孊習の知芋を取り入れた研究開発を行っおいたす。興味がある方は、Turing の公匏 Web サむト、採甚情報などをご芧ください。話を聞きたいずいう方は私や AI チヌムのディレクタヌの山口さんの Twitter DM からでもお気軜にご連絡ください。

参考文献

Tech Blog - Turing

Discussion