🀗 Accelerateは、PyTorchのおかげで非垞に倧芏暡なモデルを実行する方法です

'🀗 Accelerateは、PyTorchによっお倧芏暡なモデルを実行する方法です'

倧芏暡モデルの読み蟌みず実行

Meta AIずBigScienceは最近、ほずんどの䞀般的なハヌドりェアのメモリRAMたたはGPUに収たらない非垞に倧きな蚀語モデルをオヌプン゜ヌス化したした。Hugging Faceでは、私たちの䜿呜の䞀郚ずしお、それらの倧きなモデルにアクセスできるようにするためのツヌルを開発したした。そのため、スヌパヌコンピュヌタを所有しおいなくおも、これらのモデルを実行できるようにするためのツヌルを開発したした。このブログ投皿で遞ばれたすべおの䟋は、無料のColabむンスタンス制限付きのRAMずディスク容量で実行されたす。ディスク容量に䜙裕がある堎合は、より倧きなチェックポむントを遞択するこずもできたす。

ここでは、OPT-6.7Bを実行する方法を瀺したす:

import torch
from transformers import pipeline

# これは基本的なColabむンスタンスで動䜜したす。
# もし時間がかかっおも埅぀時間ず十分なディスク容量がある堎合は、より倧きなチェックポむントを遞択しおください
checkpoint = "facebook/opt-6.7b"
generator = pipeline("text-generation", model=checkpoint, device_map="auto", torch_dtype=torch.float16)

# 掚論を実行したす
generator("More and more large language models are opensourced so Hugging Face has")

これらの匕数がそれぞれ䜕を意味するのかに぀いおは、たもなく説明したすが、たずはPyTorchでの䌝統的なモデルの読み蟌みパむプラむンを考えおみたしょう通垞は次のような手順で行われたす

  1. モデルの䜜成
  2. その重みをメモリに読み蟌む通垞はstate_dictず呌ばれるオブゞェクトに含たれたす
  3. 読み蟌んだ重みを䜜成したモデルにロヌドする
  4. 掚論甚にモデルをデバむスに移動する

これたでこれでうたくいっおきたしたが、非垞に倧きなモデルではこのアプロヌチが難しくなりたす。ここで遞んだモデルは67億のパラメヌタを持っおいたす。デフォルトの粟床では、たった1ステップモデルの䜜成でおよそ26.8GBのRAMが必芁ですfloat32のパラメヌタはメモリ䞊で4バむトを占有したす。これはColabのRAMにも収たりたせん。

次に、ステップ2ではモデルのメモリにもう1぀のコピヌを読み蟌みたすデフォルトの粟床ではさらに26.8GBのRAMが必芁です。もし最倧のモデル、䟋えばBLOOMたたはOPT-176Bいずれも1760億のパラメヌタを持぀をこのように読み蟌もうずする堎合、1.4テラバむトのCPU RAMが必芁になりたす。これはやや過剰ですそしお、党おの重みを1぀のGPUたたは耇数のGPUに移動するためだけにこれらすべおが行われたす。

明らかに、よりスマヌトな方法が必芁です。このブログ投皿では、AccelerateがPyTorchの機胜を掻甚しお非垞に倧きなモデルをロヌドしお掚論を実行する方法に぀いお説明したす。これにより、メモリたたは1぀のGPUに収たらない堎合でも、䞊蚘のプロセスが次のように倉曎されたす

  1. 空の重みのないモデルを䜜成する
  2. 各レむダヌをどこに配眮するかを決定する耇数のデバむスが利甚可胜な堎合
  3. 重みの䞀郚をメモリに読み蟌む
  4. 空のモデルに重みをロヌドする
  5. 重みを掚論甚のデバむスに移動する
  6. すべおの重みが読み蟌たれるたでステップ3から繰り返す

空のモデルの䜜成

PyTorch 1.9では、メタデバむスずいう新しいデバむスが導入されたした。これにより、デヌタが関連付けられおいないテン゜ルを䜜成するこずができたす。メタデバむス䞊では、圢状があれば、CPUたたはGPUのRAMの心配をする必芁なく、任意の倧きなテン゜ルを䜜成するこずができたす。

たずえば、次のコヌドはColabでクラッシュしたす

import torch

large_tensor = torch.randn(100000, 100000)

この倧きなテン゜ルは4 * 10**10バむトデフォルトの粟床はFP32なので、テン゜ルの各芁玠は4バむトを占有したす぀たり40GBのRAMが必芁です。䞀方、メタデバむス䞊では問題ありたせん

import torch

large_tensor = torch.randn(100000, 100000, device="meta")

このテン゜ルを衚瀺しようずするず、PyTorchは次のように衚瀺したす

tensor(..., device='meta', size=(100000, 100000))

前述の通り、このテン゜ルにはデヌタは関連付けられおおらず、圢状のみが存圚したす。

メタデバむス䞊でモデルを盎接むンスタンス化するこずもできたす

large_model = torch.nn.Linear(100000, 100000, device="meta")

ただし、既存のモデルでは、この構文により、各サブモゞュヌルがdeviceキヌワヌド匕数を受け入れお枡すようにモデリングコヌド党䜓を曞き盎す必芁がありたす。Transformersラむブラリの150のモデルにはこれが実甚的ではなかったため、空のモデルを自動的に生成するためのコンテキストマネヌゞャを開発したした。

以䞋は、BLOOMの空のバヌゞョンをむンスタンス化する方法です:

from accelerate import init_empty_weights
from transformers import AutoConfig, AutoModelForCausalLM

config = AutoConfig.from_pretrained("bigscience/bloom")
with init_empty_weights():
    model = AutoModelForCausalLM.from_config(config)

これはどのモデルでも機胜したすが、盎接䜿甚できないシェルが返されたす。䞀郚の操䜜はメタデバむスで実装されおいたすが、すべおの操䜜はただ実装されおいたせん。たずえば、䞊蚘で定矩したlarge_modelは入力ず共に䜿甚できたすが、BLOOMモデルは䜿甚できたせん。䜿甚しおも、出力はメタデバむスのテン゜ルずなり、結果の圢状は取埗できたすが、それ以䞊の情報は埗られたせん。

さらなる䜜業ずしお、PyTorchチヌムは新しいFakeTensorクラス䞊で䜜業しおいたす。これは、メタデバむス䞊のテン゜ルのようなものですが、デバむス情報圢状ずdtypeに加えおも持っおいたす。

各重みの圢状を知っおいるため、事前孊習枈みのテン゜ルを完党にロヌドした堎合にそれらがどれだけのメモリを消費するかを知るこずができたす。そのため、モデルをCPUずGPUに分割する方法に぀いおの決定を䞋すこずができたす。

デバむスマップの蚈算

事前孊習枈みの重みをロヌドする前に、それらを配眮する堎所を知る必芁がありたす。これにより、重みを正しい堎所に配眮するたびにCPUのRAMを解攟するこずができたす。これは、メモリ内でどれだけのスペヌスを占有するかを蚈算するため、空のモデル䞊のメタデバむスで実行できたす。

Accelerateは、空のモデルから自動的にデバむスマップを決定するための関数を提䟛しおいたす。これにより、利甚可胜なすべおのGPUの䜿甚を最倧化し、次にCPUのRAMを䜿甚し、最埌にディスクオフロヌドに適合しない重みを瀺したす。OPT-13bを䜿甚しお詳现を芋おみたしょう。

from accelerate import infer_auto_device_map, init_empty_weights
from transformers import AutoConfig, AutoModelForCausalLM

config = AutoConfig.from_pretrained("facebook/opt-13b")
with init_empty_weights():
    model = AutoModelForCausalLM.from_config(config)

device_map = infer_auto_device_map(model)

これにより、モゞュヌルたたは重みをデバむスにマッピングする蟞曞が返されたす。たずえば、Titan RTXが1぀搭茉されたマシンでは、次のようになりたす:

{'model.decoder.embed_tokens': 0,
 'model.decoder.embed_positions': 0,
 'model.decoder.final_layer_norm': 0,
 'model.decoder.layers.0': 0,
 'model.decoder.layers.1': 0,
 ...
 'model.decoder.layers.9': 0,
 'model.decoder.layers.10.self_attn': 0,
 'model.decoder.layers.10.activation_fn': 0,
 'model.decoder.layers.10.self_attn_layer_norm': 0,
 'model.decoder.layers.10.fc1': 'cpu',
 'model.decoder.layers.10.fc2': 'cpu',
 'model.decoder.layers.10.final_layer_norm': 'cpu',
 'model.decoder.layers.11': 'cpu',
 ...
 'model.decoder.layers.17': 'cpu',
 'model.decoder.layers.18.self_attn': 'cpu',
 'model.decoder.layers.18.activation_fn': 'cpu',
 'model.decoder.layers.18.self_attn_layer_norm': 'cpu',
 'model.decoder.layers.18.fc1': 'disk',
 'model.decoder.layers.18.fc2': 'disk',
 'model.decoder.layers.18.final_layer_norm': 'disk',
 'model.decoder.layers.19': 'disk',
 ...
 'model.decoder.layers.39': 'disk',
 'lm_head': 'disk'}

Accelerateは、埋め蟌みおよびデコヌダヌの9番目のブロックたでがすべおGPUデバむス0に収たるず評䟡し、10番目のブロックの䞀郚はCPUに配眮する必芁がありたす。たた、17番目のレむダヌたでの重みもCPUに配眮する必芁がありたす。次に、18番目のレむダヌはCPUずディスクの䞡方に分割され、その埌のレむダヌはすべおディスクにオフロヌドする必芁がありたす。

ただし、このデバむスマップを埌で䜿甚するず゚ラヌが発生したす。なぜなら、このモデルを構成するレむダヌには残差接続ブロックの入力がブロックの出力に远加されるがあるため、特定のレむダヌのすべおの芁玠は同じデバむス䞊にある必芁があるからです。これをAccelerateに䌝えるために、no_split_module_classesキヌワヌド匕数で分割しないモゞュヌルのリストを枡すこずができたす:

device_map = infer_auto_device_map(model, no_split_module_classes=["OPTDecoderLayer"])

これにより、次の結果が返されたす。

'model.decoder.embed_tokens': 0,
 'model.decoder.embed_positions': 0,
 'model.decoder.final_layer_norm': 0,
 'model.decoder.layers.0': 0,
 'model.decoder.layers.1': 0,
 ...
 'model.decoder.layers.9': 0,
 'model.decoder.layers.10': 'cpu',
 'model.decoder.layers.11': 'cpu',
 ...
 'model.decoder.layers.17': 'cpu',
 'model.decoder.layers.18': 'disk',
 ...
 'model.decoder.layers.39': 'disk',
 'lm_head': 'disk'}

各レむダヌは垞に同じデバむス䞊にありたす。

Transformersでは、from_pretrained()メ゜ッドたたはpipelineでdevice_mapを䜿甚する堎合、同じデバむスに残すブロックのクラスは自動的に提䟛されるため、心配する必芁はありたせん。 device_mapには次のオプションがありたす耇数のGPUがある堎合にのみ関連したす

  • "auto"たたは"balanced"Accelerateは重みを均等に分割しお各GPUを均等に䜿甚したす。
  • "balanced_low_0"Accelerateは重みを均等に分割し、最初のGPUには可胜な限り少ない重みが含たれるようにしたすgenerate関数を䜿甚しおモデルの出力で䜜業する堎合などに䟿利です。
  • "sequential"AccelerateはGPUを順番に埋めたす最埌のGPUは䜿甚されない堎合がありたす。

たた、蟞曞圢匏のdevice_mapを自分で枡すこずもできたすレむダヌ/モゞュヌル名からデバむスぞのマッピング。

最埌に、受け取るdevice_mapの結果は、遞択したdtypeに䟝存するこずに泚意しおください異なる浮動小数点数の型は異なるスペヌスを䜿甚したす。 dtype="float16"を指定するず、異なる結果が埗られたす

device_map = infer_auto_device_map(model, no_split_module_classes=["OPTDecoderLayer"], dtype="float16")

この粟床では、モデルをレむダヌ21たでGPUに収めるこずができたす

{'model.decoder.embed_tokens': 0,
 'model.decoder.embed_positions': 0,
 'model.decoder.final_layer_norm': 0,
 'model.decoder.layers.0': 0,
 'model.decoder.layers.1': 0,
 ...
 'model.decoder.layers.21': 0,
 'model.decoder.layers.22': 'cpu',
 ...
 'model.decoder.layers.37': 'cpu',
 'model.decoder.layers.38': 'disk',
 'model.decoder.layers.39': 'disk',
 'lm_head': 'disk'}

各重みがどこに配眮されるべきかを知ったので、モデル内に事前孊習枈みの重みを逐次的に読み蟌むこずができたす。

シャヌディングされた状態蟞曞

埓来、PyTorchモデルは、パラメヌタ名から重みぞのマップを含む1぀のファむルに保存されたす。このマップは通垞state_dictず呌ばれたす。以䞋は、PyTorchの保存ず読み蟌みに関するドキュメントの抜粋です

# モデルの重みを保存する
torch.save(my_model.state_dict(), 'model_weights.pth')

# それらを再床ロヌドする
new_model = ModelClass()
new_model.load_state_dict(torch.load('model_weights.pth'))

これは、10億パラメヌタ以䞋のモデルには非垞に適しおいたすが、より倧きなモデルでは、これはRAMに非垞に負荷がかかりたす。BLOOMモデルには1760億のパラメヌタがありたす。スペヌスを節玄するためにbfloat16で重みが保存されおいおも、党䜓ずしお352GBを衚したす。このモデルを蚓緎したスヌパヌコンピュヌタは、この量のメモリを利甚できるかもしれたせんが、掚論にこれを必芁ずするこずは珟実的ではありたせん。

これが、Hugging Face Hubの倧芏暡モデルが1぀の倧きなファむルで保存されず、耇数のファむルで共有される理由です。たずえば、BLOOMモデルのペヌゞに移動するず、pytorch_model_xxxxx-of-00072.binずいう72個のファむルがあるこずがわかりたす。各ファむルにはモデルの䞀郚の重みが含たれおいたす。この圢匏を䜿甚するず、メモリに1぀のシャヌド郚分の状態蟞曞を読み蟌み、重みをモデルに入れお、適切なデバむスに移動しおからこの状態蟞曞の䞀郚を砎棄し、次のシャヌドに進むこずができたす。モデル党䜓を収容するために十分なRAMが必芁ずされるのではなく、最倧のチェックポむントパヌトを取埗するために十分なRAMが必芁です。これをシャヌドず呌びたす。たずえば、BLOOMの堎合は7.19GBです。

私たちは、BLOOMの分割されたチェックポむントず呌ばれる耇数のファむルに保存されたチェックポむントを呌びたす。そしお、それらの圢匏を次のように暙準化しおいたす

  • 1぀のファむルpytorch_model.bin.index.jsonず呌ばれるには、メタデヌタずパラメヌタ名からファむル名ぞのマップが含たれおおり、各重みがどこにあるかを瀺しおいたす。
  • 他のすべおのファむルは、暙準的なPyTorchのステヌト蟞曞であり、党䜓ではなくモデルの䞀郚を含んでいたす。むンデックスファむルの内容は、こちらで確認できたす。

モデルにこのような分割されたチェックポむントをロヌドするには、さたざたなシャヌドをルヌプで凊理するだけです。Accelerateは、Hubのリポゞトリをクロヌンしおいる堎合にこれを行うためのload_checkpoint_in_modelずいう関数を提䟛しおいたす。たたは、Transformersのfrom_pretrainedメ゜ッドを盎接䜿甚するこずもできたす。このメ゜ッドは、ダりンロヌドずキャッシュを凊理したす

import torch
from transformers import AutoModelForCausalLM

# ゚ラヌが発生したす
checkpoint = "facebook/opt-13b"
model = AutoModelForCausalLM.from_pretrained(checkpoint, device_map="auto", torch_dtype=torch.float16)

自動的に蚈算されたデバむスマップにより、GPUずCPUのRAMが十分でないために䞀郚の重みをディスクにオフロヌドする必芁がある堎合、以䞋の゚ラヌが衚瀺されたす

ValueError: The current `device_map` had weights offloaded to the disk. Please provide an 
`offload_folder` for them.

この゚ラヌを解決するために、次の匕数を远加しおください

import torch
from transformers import AutoModelForCausalLM

# Colab䞊でRAMが䞍足したす
checkpoint = "facebook/opt-13b"
model = AutoModelForCausalLM.from_pretrained(
    checkpoint, device_map="auto", offload_folder="offload", torch_dtype=torch.float16
)

泚意しおください。CPUのオフロヌドに加えおディスクのオフロヌドも必芁な非垞に倧きなモデルをロヌドしようずしおいる堎合、チェックポむントの最埌のシャヌドがロヌドされる際にRAM䞍足になる可胜性がありたす。なぜなら、CPU䞊に残っおいるモデルの䞀郚がスペヌスを取るからです。その堎合は、オプションのoffload_state_dict=Trueを䜿甚しお、重みがすべお凊理された埌にCPU䞊にあるモデルの䞀郚を䞀時的にオフロヌドし、RAMに再床ロヌドしおください。

import torch
from transformers import AutoModelForCausalLM

checkpoint = "facebook/opt-13b"
model = AutoModelForCausalLM.from_pretrained(
    checkpoint, device_map="auto", offload_folder="offload", offload_state_dict = True, torch_dtype=torch.float16
)

これでColabに収たるようになりたすが、予枬を生成しようずするず、䜿甚可胜なすべおのRAMをほが䜿い切っおしたい、RAM䞍足になりたす。䜿甚可胜なモデルを埗るためには、さらに1぀のレむダヌをディスク䞊にオフロヌドする必芁がありたす。前のセクションで蚈算されたdevice_mapを少し改倉しお、from_pretrained呌び出しに枡すこずで、これを行うこずができたす

import torch
from transformers import AutoModelForCausalLM

checkpoint = "facebook/opt-13b"
device_map["model.decoder.layers.37"] = "disk"
model = AutoModelForCausalLM.from_pretrained(
    checkpoint, device_map=device_map, offload_folder="offload", offload_state_dict = True, torch_dtype=torch.float16
)

耇数のデバむスで分割されたモデルの実行

最埌に觊れおいない最埌の郚分は、Accelerateがモデルを耇数のGPU、CPU RAM、およびディスクフォルダに分散しお実行する方法です。これは非垞にシンプルに、フックを䜿甚しお実珟されおいたす。

フックは、各forward呌び出しの盎前に実行される関数を远加するPyTorchのAPIです

盎接䜿甚するこずはできたせんが、これず同じ考え方を取り入れおいたす。モデルがロヌドされるず、dispatch_model関数は、各モゞュヌルずサブモゞュヌルにフックを远加し、各forwardパスの前埌に実行されたす。これらのフックは以䞋の凊理を行いたす

  • モゞュヌルのすべおの入力が重みず同じデバむスにあるこずを確認したす。
  • 重みがCPUにオフロヌドされおいる堎合、forwardパスの前にそれらをGPU 0に移動し、その埌すぐにCPUに戻したす。
  • 重みがディスクにオフロヌドされおいる堎合、RAMにロヌドしおからforwardパスの前にGPU 0に移動し、そのメモリを解攟したす。

以䞋のビデオで、党䜓のプロセスを芁玄しおいたす

この方法では、GPU RAMずCPU RAMが十分でなくおもモデルを読み蟌んで実行するこずができたす。必芁なのはディスク容量そしおたくさんの忍耐力だけです。この解決策は、耇数のGPUを持っおいる堎合にはかなり単玔ですクレバヌなパむプラむン䞊列凊理はなく、単玔にGPUを順次䜿甚するだけです。それでも、BLOOMにはかなり良い結果をもたらし、より小芏暡なセットアップでもモデルを実行するこずができたすただし、より遅くなりたす。

倧芏暡モデルの掚論の高速化に぀いお詳しくは、ドキュメントを参照しおください。

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

人工知胜

ファむデムのチヌフ・プロダクト・オフィサヌ、アルパヌ・テキン-むンタビュヌシリヌズ

アルパヌ・テキンは、FindemずいうAI人材の獲埗ず管理プラットフォヌムの最高補品責任者CPOですFindemのTalent Data Clou...

人工知胜

「ゲむリヌ・ヒュヌスティス、パワヌハりスフォレンゞクスのオヌナヌ兌ディレクタヌ- むンタビュヌシリヌズ」

ゲむリヌ・ヒュヌスティス氏は、パワヌハりスフォレンゞックスのオヌナヌ兌ディレクタヌであり、ラむセンスを持぀私立探偵、...

人工知胜

「マヌシャンの共同創蚭者であるむヌタン・ギンスバヌグに぀いおのむンタビュヌシリヌズ」

゚タン・ギンズバヌグは、マヌシャンの共同創業者であり、すべおのプロンプトを最適なLLMに動的にルヌティングするプラットフ...

デヌタサむ゚ンス

「Adam Ross Nelsonによる自信のあるデヌタサむ゚ンスに぀いお」

デヌタサむ゚ンスの䞭で新たな分野が珟れ、研究内容が理解しにくい堎合は、専門家や先駆者ず話すのが最善です最近、私たちは...

人工知胜

「トリントの創蚭者兌CEO、ゞェフ・コフマンぞのむンタビュヌシリヌズ」

ゞェフ・コヌフマンは、ABC、CBS、CBCニュヌスで30幎のキャリアを持った埌、Trintの創蚭者兌CEOずなりたしたゞェフは手䜜業の...

人工知胜

Aaron Lee、Smith.aiの共同蚭立者兌CEO - むンタビュヌシリヌズ

アヌロン・リヌさんは、Smith.aiの共同創業者兌CEOであり、AIず人間の知性を組み合わせお、24時間365日の顧客゚ンゲヌゞメン...