vLLMと大規模モデル
Hamel Husainは、大規模言語モデルLlama-2-70bを単一GPUで実行できないことを訂正し、vLLMとModal Labsを用いた複数GPUでの分散推論の実装方法を具体的なコード例とともに解説している。
キーポイント
大規模モデルの実行に関する訂正と現実
著者は、Llama-2-70bのような大規模モデルが単一GPU(A100)に収まらないことを認め、以前の誤った情報を訂正している。
分散推論の実装手法
モデルが単一GPUに収まらない場合、vLLMのtensor_parallel_sizeパラメータなどを用いて複数GPUにモデルを分割する手法を紹介している。
Modal Labsを利用した再現可能な環境構築
サーバーレスコンピュートプラットフォームのModal Labsを利用することで、経済的かつ再現性の高い実験環境を構築する方法を説明している。
キャッシュ問題と強制リビルドの必要性
モデルを変更した際、キャッシュが原因で古いモデルが使用される問題が発生し、force_build=Trueでイメージを強制リビルドする必要があったと報告している。
影響分析・編集コメントを表示
影響分析
この記事は、大規模言語モデルの実運用における技術的課題とその解決策を具体的に示しており、研究者や実務者にとって実用的な知見を提供している。特に、クラウド環境での再現可能な実験設定の重要性を強調しており、AI開発の民主化と実用化を促進する内容と言える。
編集コメント
技術ブログとして実践的な知見に富んでおり、特にキャッシュ問題への対応など現場で遭遇する課題への洞察が価値がある。ただし、業界を変えるような画期的な新技術の発表ではなく、既存技術の応用と実装ノウハウの共有に留まっている。
このノートの以前のバージョンでは、単一のA100でLlama 70bを実行できると示唆していました。これは誤りでした。Modalコンテナが、はるかに小さい7bモデルのダウンロードをキャッシュしていたためです。この誤りを反映するように投稿を更新しました。誤りを発見してくれたCade Daniel氏に感謝します。
Llama-2-70bのような大規模モデルは、単一のGPUに収まらない場合があります。私は以前、より小さい7bモデルを様々な推論ツールでプロファイリングしました。モデルが単一のGPUに収まりきらない場合、モデルを複数のGPUに分割する様々な技術を使用できます。
計算と再現性
サーバーレスコンピューティングにModal Labsを使用しました。Modalは非常に経済的で、機械学習ユースケース向けに構築されています。他のクラウドとは異なり、豊富なA100が利用可能です。30ドルの無料クレジットも提供されており、このノートの実験を実行するには十分以上です。Modalのおかげで、このノートで参照するスクリプトは再現可能です。
このノートでは、modalクライアントを使用しています。
vLLMによる分散推論
tensor_parallel_size
Llama v2 13b用のModalコード例を以下のように修正しました。
def download_model_to_folder():
from huggingface_hub import snapshot_download
snapshot_download(
- "meta-llama/Llama-2-13b-chat-hf",
+ "meta-llama/Llama-2-70b-chat-hf",
local_dir="/model",
token=os.environ["HUGGINGFACE_TOKEN"],
)
image = (
Image.from_dockerhub("nvcr.io/nvidia/pytorch:22.12-py3")
.pip_install("torch==2.0.1", index_url="https://download.pytorch.org/whl/cu118")
+ # vLLMを2023年8月2日時点のバージョンに固定
+ .pip_install("vllm @ git+https://github.com/vllm-project/vllm.git@79af7e96a0e2fc9f340d1939192122c3ae38ff17")
- # vLLMを2023年7月19日時点のバージョンに固定
- .pip_install("vllm @ git+https://github.com/vllm-project/vllm.git@bda41c70ddb124134935a90a0d51304d2ac035e8")
# 最大ダウンロード速度のため、最小限のhf-transferパッケージを使用。プログレスバーはありませんが、700MB/sを期待できます。
- .pip_install("hf-transfer~=0.1")
+ # キャッシュを無効化するために強制的にリビルド(初回以降は`force_build=True`を削除可能)
+ .pip_install("hf-transfer~=0.1", force_build=True)
.run_function(
download_model_to_folder,
secret=Secret.from_name("huggingface"),
timeout=60 * 20)
)
...
-@stub.cls(gpu="A100", secret=Secret.from_name("huggingface"))
+# 40GBバージョンのA100が最低4台必要
+@stub.cls(gpu=gpu.A100(count=4, memory=40), secret=Secret.from_name("huggingface"))
class Model:
def __enter__(self):
from vllm import LLM
# モデルをロード。ヒント:MPTモデルは`trust_remote_code=true`が必要な場合があります。
- self.llm = LLM(MODEL_DIR)
+ self.llm = LLM(MODEL_DIR, tensor_parallel_size=4)使用した実際のスクリプトは、big-inference-vllm.pyを参照してください。
上記のコードを実行してモデル名を変更した場合、キャッシュを無効化するためにイメージの強制リビルドが必要であることがわかりました。そうしないと、古いバージョンのモデルが使用されてしまいます。force_build=Trueを追加することで強制リビルドできます。
このノートを最初に書いたとき、私はmeta-llama/Llama-2-70b-chat-hfをロードできると誤って信じ込んでいました。
HuggingFaceとWeights & Biasesの適切なシークレットを設定した後、以下のコマンドでModal上でこのコードを実行できます:
modal run big-inference-vllm.pyLlama v2 70bを実行するには、少なくとも4台のA100 GPUが必要です。
より小さいモデルではどうなるか?
分散推論は単一のGPUに収まらない大規模モデルにとって興味深いものですが、より小さいモデルをこの方法で実行する場合も興味深い結果が得られます。以下では、Llama v2 7bのスループットをテストします。
llm.generateを呼び出す
以下は結果で、各行について5回の実行を平均したものです:
すべての個別の実行はここで確認できます。私の実験では、70bモデルを実行するには最低4台のA100が必要だったため、そのモデルについては1行しかありません(Modalには1台、2台、または4台のGPUを持つインスタンスしかないため)。
ここで示すtok/secの数値は、このノートで示したレイテンシベンチマークとは大きく異なります。この特定のベンチマークは、複数のリクエストを並行して実行することでスループットを最大化しています。以前のレイテンシベンチマークは、単一のリクエストを処理するのにかかる時間を測定したものです。
A100はA10よりもはるかに高速ですが、A10は大幅に安価です¹。
A10では、GPU数を増やすと最初はスループットが向上しますが、その後は頭打ちになるようです。スループットを最大化する適切なGPU数には、ちょうど良い領域があるように見えます。Modalには特定のGPU数を持つインスタンスしかないため、これについては詳細に調査しませんでした²。
はるかに大きなLlama v2 70b
余談:パイプライン並列処理
理論的には、パイプライン並列処理("PP")はテンソル並列処理よりも遅いですが、PPに対応したツールはHuggingFace Hubのより広範なモデルと互換性があります。デフォルトでは、HuggingFaceのaccelerateは、device_map="auto"を指定するとモデルを複数のGPUに自動的に分割します。
このブログ投稿とこれらのドキュメントは、優れた出発点となります。今後、この種の並列処理や他の種類の並列処理について探求していく予定です。
¹ 2023年8月6日現在、2台のA10のコストは.000612ドル/秒です。
² A10とA100では、最大4台のGPUしか利用できません。さらに、vLLMとLlama 70bでは、奇数台のGPUをうまく扱えない問題に遭遇しました。↩︎
原文を表示
A previous version of this note suggested that you could run Llama 70b on a single A100. This was incorrect. The Modal container was caching the download of the much smaller 7b model. I have updated the post to reflect this. h/t to Cade Daniel for finding the mistake.
Large models like Llama-2-70b may not fit in a single GPU. I previously profiled the smaller 7b model against various inference tools. When a model is too big to fit on a single GPU, we can use various techniques to split the model across multiple GPUs.
Compute & Reproducibility
I used Modal Labs for serverless compute. Modal is very economical and built for machine learning use cases. Unlike other clouds, there are plenty of A100s available. They even give you $30 of free credits, which is more than enough to run the experiments in this note. Thanks to Modal, the scripts I reference in this note are reproducible.
In this note, I’m using modal client
Distributed Inference w/ vLLM
tensor_parallel_size
I modified this example Modal code for Llama v2 13b
def download_model_to_folder(): from huggingface_hub import snapshot_download snapshot_download( - "meta-llama/Llama-2-13b-chat-hf", + "meta-llama/Llama-2-70b-chat-hf", local_dir="/model", token=os.environ["HUGGINGFACE_TOKEN"], ) image = ( Image.from_dockerhub("nvcr.io/nvidia/pytorch:22.12-py3") .pip_install("torch==2.0.1", index_url="https://download.pytorch.org/whl/cu118") + # Pin vLLM to 8/2/2023 + .pip_install("vllm @ git+https://github.com/vllm-project/vllm.git@79af7e96a0e2fc9f340d1939192122c3ae38ff17") - # Pin vLLM to 07/19/2023 - .pip_install("vllm @ git+https://github.com/vllm-project/vllm.git@bda41c70ddb124134935a90a0d51304d2ac035e8") # Use the barebones hf-transfer package for maximum download speeds. No progress bar, but expect 700MB/s. - .pip_install("hf-transfer~=0.1") + #Force a rebuild to invalidate the cache (you can remove force_build=True after the first time) + .pip_install("hf-transfer~=0.1", force_build=True) .run_function( download_model_to_folder, secret=Secret.from_name("huggingface"), timeout=60 * 20) ) ... -@stub.cls(gpu="A100", secret=Secret.from_name("huggingface")) +# You need a minimum of 4 A100s that are the 40GB version +@stub.cls(gpu=gpu.A100(count=4, memory=40), secret=Secret.from_name("huggingface")) class Model: def __enter__(self): from vllm import LLM # Load the model. Tip: MPT models may require trust_remote_code=true. - self.llm = LLM(MODEL_DIR) + self.llm = LLM(MODEL_DIR, tensor_parallel_size=4) ...
See big-inference-vllm.py for the actual script I used.
I found that when I ran the above code and changed the model name, I had to force a rebuild of the image to invalidate the cache. Otherwise, the old version of the model would be used. You can force a rebuild by adding force_build=True
When I initially wrote this note, I was fooled into believing I could load meta-llama/Llama-2-70b-chat-hf
After setting the appropriate secrets for HuggingFace and Weights & Biases, You can run this code on Modal with the following command:
modal run big-inference-vllm.py
You need at least 4 A100 GPUs to serve Llama v2 70b.
What Happens With Smaller Models?
Even though distributed inference is interesting for big models that do not fit on a single GPU, interesting things happen when you serve smaller models this way. Below, I test throughput for Llama v2 7b
Call llm.generate
Here are the results, averaged over 5 runs for each row:
You can see all the individual runs here. In my experiments, the 70b model needed a minimum of 4 A100s to run, so that’s why there is only one row for that model (Modal only has instances with 1, 2, or 4 GPUs).
The tok/sec number you see here is VERY different than the latency benchmark shown on this note. This particular benchmark maximizes throughput by running multiple requests in parallel. The previous latency benchmark measures the time it takes to process a single request.
A100s are much faster than A10s, but A10s are significantly cheaper.1
On A10s, scaling up to more GPUs increases throughput at first, but then seems to diminish. It appears like there is a Goldilocks zone in terms of the right number of GPUs to maximize throughput. I did not explore this in detail, as Modal only has instances with specific numbers of GPUs.2
The much larger Llama v2 70b
Aside: Pipeline Parallelism
In theory, Pipeline Parallelism (“PP”) is slower than Tensor Parallelism, but tools for PP are compatible with a wider range of models from the HuggingFace Hub. By default, HuggingFace accelerate will automatically split the model across multiple GPUs when you pass device_map="auto"
This blog post and these docs are an excellent place to start. I will explore this and other kinds of parallelism in future notes.
As of 8/6/2023 2 A10s costs .000612 / sec
For A10 and A100s you can only get up to 4 GPUs. Furthermore, I ran into an issue with vLLM and llama 70b, where it doesn’t like an odd number of GPUs.↩︎
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み