HUOXIU

同じ LLM が異なる GPU で異なる結果を出力するのはなぜですか?

編集者注:大規模言語モデル(LLM)の導入とそれに伴う計算能力のスケールアップにおいて、GPUの交換はモデルの出力に大きな影響を与える可能性がありますか?この質問への答えは、異なるハードウェア環境におけるLLMの一貫性と信頼性を確保するために非常に重要です。

本日ご紹介する記事の核となる主張は、開発環境、システム構成、ランダム シードが同じであっても、GPU が異なると LLM が異なるモデル出力を生成する可能性があるということです。

著者らは、Mistral-7b-v0.1モデルが2つの異なるGPU(Nvidia Tesla T4とNvidia A10G)を使用した場合、同じ入力に対して異なる出力を生成することを実験的に実証しました。この差は主に、並列計算、ハードウェアアーキテクチャ、そしてモデル量子化の影響の差に起因しています。この不正確さは、キューワードの長さが長くなるにつれて増幅されます。キューワードが長くなると計算量が増えるため、不正確さの伝播が悪化するからです。スケーリングのために複数のGPUを使用する場合、モデルシャーディング戦略は、計算配分の違いにより理論上は結果にばらつきが生じる可能性があります。しかし、実際には、PyTorchの設計は一貫した結果を保証するようです。

著者 | アニス・ザカリ

編纂者:岳陽

依存ライブラリやコンポーネントのバージョンの違いがシステムの動作に変化をもたらす可能性があることは、多くの技術エンジニアが理解しています。しかし、大規模言語モデル(LLM)の分野では、膨大な計算負荷のため、学習と推論の両方のタスクでGPUに大きく依存しています。しかし、GPUの変更がLLMの出力にも影響を与える可能性があることを真に理解している人はほとんどいません。

完全に同一の開発環境を 2 つ作成する場合:

  • 依存するライブラリまたはコンポーネントのバージョンを指定できます。
  • Docker化が使えます。
  • LLM の温度は 0 に設定できます。
  • 任意のランダムシードを選択できます。ただし、全く同じGPUモデルを使用していない場合は、上記のすべての努力が無駄になります。

本稿では、この現象を強調するために実験を行い、どこで違いが生じるのか、そしてその理由を説明します。

注: 実験プロセスや特定のコードを再現することに興味がない場合は、この記事に示されているコード スニペットをスキップして、「7. 同じ入力と同じ LLM が 2 つの異なる GPU でこのように異なるモデル応答を生成するのはなぜですか?」に直接進んでください。前のコード スニペットを見なくても、「結論」セクションは基礎となる原理を理解するのに役立ちます。

01 なぜこの記事を書くのか?

ある日、OpenAIとAnthropicのモデルがなぜ決定論的システムとして設計されていないのか、何人かの人たちと議論していました。私は、それらのモデルがMixture of Experts(MoE)アプローチ[1]を採用している可能性があると説明しました。このアプローチでは、最適なエキスパートモデルが他のトークンの処理に忙しい場合、トークンが最適なエキスパートモデルにルーティングされないことがあり、モデルの応答に一貫性がなくなる可能性があります。

もう一つの要因は、OpenAIが効率向上のためにクエリをバッチ処理していることです。バッチサイズは受信クエリの数に応じて変化するため、GPUの計算戦略が変化し、モデルの応答が異なる可能性があります。

「GPU が異なれば、モデルの応答も異なる可能性がありますよね?」と誰かが指摘したとき、会話は面白くなり始めました。

よく考えてみてください…OpenAI APIを使うとき、実際にはリモートサーバーが計算を実行し、モデルのレスポンスを返してくれます。このマシンが常に同じコンピューティングインフラストラクチャ上で動作していないと、結果として得られるモデルのレスポンスは同じにはなりません。

これについて考えると、他の問題が浮かび上がるかもしれません。

  • 実稼働開発環境で LLM アプリを実行していて、それを異なる GPU を持つ他のインスタンスに拡張する必要がある場合、深刻な問題が発生するでしょうか?
  • 開発環境の GPU が本番環境の GPU と大きく異なる場合はどうなりますか?

これらの疑問から、私はこの現象を浮き彫りにし、その潜在的な影響の範囲を探るための実験を計画しました。

02 実験環境を構成する

この現象を明らかにするために、使用するGPUが異なる2つの同一の開発環境を構築します。1つ目の環境ではNvidia Tesla T4を使用し、2つ目の環境ではNvidia A10Gを使用します。そして、Mistral-7b-v0.1を使用してテストを行い、何が起こるかを確認します。

ノートブックで実験を実行するには、次の手順に従います。

2.1 開発環境を構成する

1. CUDAバージョンを設定する

2. トランスフォーマーとその他の依存関係を構成する

3. ランダムシードを設定する

注1:

transformers.set_seed のみを設定すれば十分なはずですが、それでも確実にしておきたいのです。

注2:

この例では Python 3.10 を使用します。

2.2 ミストラルモデルの読み込み

Hugging Face から Mistral-7B-v0.1 モデルをロードするには、環境変数 HF_TOKEN に Hugging Face トークンを設定する必要があります。

この記事では、モデルの量子化バージョンを使用して計算精度を下げ、GPU メモリの使用量を削減します。

2.3 トランスフォーマーライブラリのパイプラインの使用

トランスフォーマー ライブラリのパイプラインを使用して、大規模言語モデル (LLM) からモデル応答を生成するプロセスを簡素化します。

モデル出力が予測可能かつ一貫性があることを保証するために、大規模言語モデルの語彙から最も可能性の高いトークンを継続的に予測する必要があります。そのため、top_k を 1 に設定するか、temperature を 0 に近い値に設定します。

さらに、簡単にするために、 max_new_tokensパラメータを 1 に設定し、LLM が 1 つのトークンのみを使用してプロンプトの単語を完成できるようにします。

「I enjoy walking in the」というプロンプトシーケンスが与えられた場合、大規模言語モデル(LLM)は「woods」という単語のみを生成します。大規模言語モデル(LLM)がこの単語を正しく生成して出力した場合、実験を続行できます。

03 実験結果: T4 vs A10G

これら 2 つの GPU を活用するために、AWS SageMaker 経由で ml.g4dn.xlarge (T4) インスタンスと ml.g5.xlarge (A10G) インスタンスを起動しました。

簡単なクエリを実行してみましょう。

T4 モデルと A10G モデルは同じ応答を返します。

ここまでは順調です。しかし、これはまだ短いクエリです。RAG(Retrieval Augmentation)アプリケーションでは、通常、数万個のトークンを処理します。では、Hugging Faceでホストされているllama-2-arxiv-papers-chunkedデータセットを使って、より大規模なクエリテストを実施してみましょう。

以下のコード例では、データセットインデックス0、4518、4519、799から取得したテキスト断片を用いて、RAGの動作を模倣します。チャンク4518と4519は「Llama 2」について議論していますが、他の断片は議論していません。LLMはこのコンテキスト情報に基づいて、「Llama 2の何が特別なのか?」と答えることを期待しています。このプロンプトは約1,400トークンの長さです。

T4 モデルの出力は次のとおりです。

A10G モデルの出力は次のとおりです。

それは本当に興味深いですね。一見すると、2つのモデルは応答の始まりが同じなので似ているように見えます。しかし、「など」の後になると、違いが明らかになります。

T4 モデルの出力は次のようになります。「など…これは、内部のすべてが異なる実行間で一貫しているため、出力をより信頼できることも意味します…」

A10G モデルの出力は次のとおりです。「など…これは、これらのテキストで取り上げられているトピックに具体的に関連する質問をするときに、より自信を持って質問できることも意味します…」

04 T4 ColabとT4 SageMaker

同じ GPU を使用した 2 つの開発環境で同じモデル出力が生成されるかどうか疑問に思っていました。一連のテストを実施したところ、結果はまったく同じでした。

05 同じユーザー入力と同じ LLM が 2 つの異なる GPU でこのように異なる結果を生成するのはなぜですか?

最終的に、LLMの自己回帰的な性質により、これらのモデルの応答は劇的に異なります。次のトークンは前のトークンに基づいて選択されるため、わずかな変化でもバタフライ効果のように連鎖反応を引き起こす可能性があります。

これらの模範解答は、プロンプトで要求されている状況に基づいていないことにご注意ください。LLMは指示に完全に従うわけではありませんが、これは重要ではありません。

LLMは常に先行するトークンの中から最も確率の高いトークンを選択すると仮定しているので、この違いはGPU上での確率の計算方法にあると確信できます。この確率がどのように計算されるかを見てみましょう。

06 トークンを選択する確率を計算します。

選択された各トークンの確率を出力するために、従来のパイプラインをバイパスし、`tokenizer` メソッドとmodel.generateメソッドを直接使用します。これによりreturn_dict_in_generate=Trueoutput_scores=Trueを設定できます。次に、計算を実行し、トークンを正規化し、遷移スコア(自然言語処理、特に自己回帰モデルを使用してテキストを生成する場合、モデルは各次のトークンに確率スコアを割り当て、そのトークンがトークンシーケンス内の次のトークンとなる可能性を反映します)を確率に変換します。

上記のコードは、各トークンのID、デコードされたトークン、および対応する確率を表示します。完全な出力はかなり長いため、ここでは関連するモデル出力のみを記載しています。

T4出力:

A10G出力:

さて、いよいよ面白くなってきました。T4とA10Gの確率は全く同じではありません。通常、これはトークンの順序に影響を与えません(生成されたトークンのシーケンスに違いは見られません)が、場合によっては影響を与えることがあります。

例えば、T4モデルでは「trust」の出現確率は18.74%であるのに対し、A10Gモデルでは「be」の出現確率はさらに高く、18.62%に達します。この観点から見ると、大規模言語モデルの自己回帰的な性質により、生成されるコンテンツは発散すると考えられます。

注意: 大規模な言語モデルを量子化すると計算精度が低下し、このような種類の矛盾がより一般的になります。

ここで、「なぜ GPU によって計算結果が異なるのか」という当然の疑問が生じます。

07 GPU によってモデルの計算結果が異なるのはなぜですか?

私はCUDAの専門家(高性能並列コンピューティングアプリケーションを開発するためのCUDA C/C++プログラミングに精通し、GPU上で計算タスクを最適化して最高のパフォーマンスを引き出す方法を理解している人)ではありませんが、いくつか調査を行いました。異なるGPU間の計算性能の違いは、いくつかの要因に起因していると考えられます。

並列計算処理:

GPUは、多数の計算タスクを効率的に並列処理できるという特徴があります。しかし、GPUによって並列タスクの処理方法が異なる場合があり、演算の順序やメモリへのアクセス方法に影響を及ぼします。

これは非常に重要です。プログラミングにおいては、数値が大きく異なる単純な加算であっても、非結合性となり、精密な計算の精度に影響を与える可能性があるためです。「非結合性」とは、(a + b) + c ≠ a + (b + c) であることを意味します。

非結合性

したがって、計算タスクは分割され、個別に処理された後、非連想的な方法で結合されます。したがって、これらの部分がどのように再結合されるかが最終的な結果に影響します。

以下は非結合計算の簡単な例です。

大規模言語モデル (LLM) の場合、数百万回の計算により小さなエラーが繰り返されて多様化が生じる可能性があり、それがシーケンス生成時の単語選択に影響します。

ハードウェア アーキテクチャ:

Nvidia Tesla T4やNvidia A10GなどのGPUはそれぞれ異なるハードウェアアーキテクチャを備えています。これらのハードウェアアーキテクチャは、並列処理能力、メモリ帯域幅、計算ユニットなど、モデルの様々な側面のパフォーマンスを最適化できます。

例えば、T4モデルはTuring [2]アーキテクチャを採用していますが、A10GモデルはAmpere [3]アーキテクチャに基づいています。

モデルアーキテクチャが異なると、浮動小数点演算、メモリアクセスパターン、その他の低レベル演算の実装も異なります。これらの実装におけるわずかな違いでも、計算結果に矛盾が生じる可能性があります。

たとえば、計算速度に最適化されたモデル アーキテクチャは、両方が同じ浮動小数点演算を実行している場合でも、より高い計算精度に最適化されたモデル アーキテクチャとは異なるモデル応答を生成する可能性があります。

モデル量子化の効果:

モデル量子化によって計算精度を下げると、メモリと計算リソースを節約できますが、新たな誤差要因も発生します。これらの誤差の影響は、GPUが低精度演算をどのように処理するかによって異なります。

モデルの量子化には数値の近似が含まれるため、異なる GPU ではこれらの近似を異なる方法で処理する可能性があり、最終的にはトークンの予測確率が変化する可能性があります。

08 複数の GPU を使用して LLM を水平方向にスケーリングする場合に考慮すべきことは何ですか?

素晴らしい質問ですね、ありがとうございます! :)

同じモデルの GPU の数を増やすだけの場合 (たとえば、単一の A10G GPU から 4 つの A10G GPU を持つインスタンスにスケーリングする場合)、心配する必要はありますか?

推論に複数の GPU を使用する場合、選択できる戦略がいくつかあります。

  • 最初の戦略は、モデルをGPUにロードできる場合は、各GPUにモデルのコピーをロードすることです。例えば、パイプラインに4つのクエリが送られた場合、各クエリを異なるGPUで処理できます。これにより、1つのGPUのみを使用した場合と同じ出力が得られますが、スループットは向上します。
  • 2つ目の戦略は、モデルが大きすぎて単一のGPUに収まらない場合によく使用されます。これは、モデルの重みを複数のGPUに分散させるモデルシャーディングを伴います。理論的には、このアプローチは計算の分散と実行の違いによりモデルの応答にばらつきが生じる可能性がありますが、実際のテストでは、モデルシャーディングを使用して得られたシーケンスと確率は、単一のGPUで得られたものと一致しています。これは、PyTorchが決定論的な操作を念頭に置いて設計されているためだと考えられます。

09 結論

開発環境、システム設定、乱数シードが同じであっても、GPUによってLLMの結果が異なる可能性があることを実証しました。この不正確さはキューワードの長さに応じて増大します。キューワードが長くなるとより多くの計算能力が必要になるため、不正確さの伝播が悪化し、2つのGPU間の差異に寄与します。さらに、この影響はモデル量子化を実行するとさらに顕著になります。

この状況が必ずしも壊滅的だと言っているわけではありませんが、LLM を導入する際には注意する必要がある要素です。

開発中に使用するGPUが本番環境で使用するものと異なる場合は、パフォーマンスが許容範囲内に収まっていることを確認するためのテスト実験を実施する必要があります。これは、LLMを異なるGPUを搭載した新しいインスタンスに拡張する場合にも重要です。

ここまで読んでくださった方、本当にありがとうございます!楽しんでいただけたら嬉しいです。もし気に入っていただけたら、ぜひ「いいね!」を押して、これからも書き続けていきたいと思います。そして、ぜひコメント欄で感想を共有してくださいね。


アニス・ザカリ

私はパリを拠点とする情熱的なML/AIエンジニアです。特にNLP、LLM、ソフトウェアエンジニアリング、クラウドエンジニアリングに興味があります。

記事内のリンク

[1]https://standardscaler.com/2024/03/06/the-non-determinism-of-openai-and-anthropic-models/

[2]https://www.nvidia.com/fr-fr/geforce/turing/

[3]https://www.nvidia.com/fr-fr/data-center/ampere-architecture/

オリジナルリンク:

https://medium.com/@anis.zakari/gpu の動作が変化すると、llm の動作も変化します。