メインコンテンツへ移動
AIチュートリアル

TensorFoldで2台のDGX SparkにDeepSeek-V4.1-Flash-1017をサーブする

TensorFold、ConnectX-7ネットワーキング、Engramストレージ、投機的デコーディング、OpenAI互換APIを使用して、2.9ビットのEXL3 DeepSeek-V4.1-Flash-1017チェックポイントを2台のNVIDIA DGX Sparkにまたがってデプロイする方法を解説します。

TensorFoldで2台のDGX SparkにDeepSeek-V4.1-Flash-1017をサーブする

このプロジェクトの内容

このデプロイ手順では、128 GBのユニファイドメモリを搭載した2台のNVIDIA DGX SparkでDeepSeek-V4.1-Flash-1017を提供します。TensorFoldのネイティブZigエンジンを使用し、各Sparkにつき1ランクを割り当てたテンソル並列化を行い、ポート8888でOpenAI互換APIを公開します。

この構成では、実験的な2.9ビットEXL3チェックポイントMia-AiLab/DeepSeek-V4.1-Flash-1017-EXL3-EAQ-v1-2.9-bpwを使用します。モデルの大きなコンテキストウィンドウ、画像入力、ツール呼び出し、思考モードの任意指定、およびチェックポイントのドラフトブロックを使用したDSpark投機的デコーディングに対応しています。

このリポジトリは、準備、Dockerイメージの構築、チェックポイントのダウンロード、ランクキャッシュの作成、ワーカーのセットアップ、サーバーの起動、スモークテスト、シャットダウンを自動化します。1台目のSparkがヘッドとして機能し、APIの実行、セットアップの調整、ランク0の起動を担当します。2台目のSparkはランク1を実行します。

アーキテクチャと重要な制限

TensorFoldはBertholomusAIのdsv41ファミリーを、テンソル並列度2で実行します。2つのランクは専用のConnectX-7リンク経由で通信します。ヘッドがAPIトラフィックを受け付け、両方のSparkがそれぞれ約98 GiBのモデル重みをメモリに保持します。

チェックポイントは約196 GiBで、39個のシャードに分割されています。デプロイには、合計約203 GBを占有するEngramの非量子化テーブル2つも必要です。Engramテーブルは両方のSparkのローカルディスクにコピーしてください。デコードでは4 KiB単位のランダム読み取りが多数発生するため、NFS経由で読み取るとプリフィルとデコードの性能が大幅に低下する可能性があります。

モデルは技術的には1,048,576トークンのウィンドウを使用できますが、実用上の制限となるのはヘッドで利用可能なメモリです。他のデスクトップサービスや開発サービスを実行しているヘッドでは、メモリを追加で解放しない限り、READMEではCONTEXTを524288以下に保つことを推奨しています。

ハードウェアとソフトウェアの要件

  • それぞれ128 GBのユニファイドメモリを搭載した、2台のNVIDIA DGX Sparkまたは同等のGB10システム。
  • ConnectX-7ポート間の直接QSFP接続。
  • 直接リンク上のIPv4アドレス、正常な接続、および動作するRoCE v2 GID。
  • 両システムで同じGPUクロック上限を設定すること。READMEではnvidia-smi -q -d CLOCKでクロックを確認し、適切な場合はnvidia-smi -lgc 0,2200を使用することを推奨しています。
  • ヘッドからワーカーへの鍵ベースSSH。
  • 両システムにNVIDIAコンテナランタイム付きのDockerがあり、ユーザーがdockerグループに所属していること。
  • ヘッドにgit、python3、Hugging Face CLIがあること。
  • チェックポイント、Engramテーブル、ランク用重みファイル、Dockerイメージ、キャッシュに十分なストレージがあること。標準構成では、ヘッドに約505 GB、ワーカーに約310 GBが必要です。

直接リンクは、各Spark上で2つのネットワークデバイスと2つのRoCEデバイスとして表示される場合があります。両方のレールにリンク上のアドレスを設定し、両システムで同じMTUを使用してください。

ネットワークとSSH接続を準備する

ConnectX-7インターフェースにプライベートサブネットのアドレスを割り当てます。たとえばREADMEでは、192.0.2.1/24や192.0.2.2/24などのアドレスを使用しています。2台のシステムが目的のリンク経由で相互にpingできることを確認してください。

ヘッドでワーカー用のSSHキーをインストールし、非対話アクセスを確認します。

ssh-copy-id user@<worker>
ssh -o BatchMode=yes user@<worker> true

ローカル設定ファイルにワーカーのSSHターゲットとファブリックアドレスを設定します。ワーカー側にこのリポジトリのコピーは必要ありません。

NFSまたはローカルストレージを設定する

デフォルトでは、ワーカーはNFS経由でヘッドのHugging Faceキャッシュを読み取り専用でマウントします。ヘッドにNFSサーバーをインストールし、キャッシュをエクスポートします。

sudo apt install nfs-kernel-server
echo "$HOME/.cache/huggingface <worker-CX7-address>(ro,no_subtree_check)" | sudo tee -a /etc/exports
sudo exportfs -ra

ワーカーには、Ubuntuのnfs-commonなど、ホストのNFSクライアントヘルパーが必要になる場合があります。準備スクリプトは読み取り専用のDocker NFSボリュームを作成し、ワーカーが必要なファイルを確認できることを検証します。

分離性を高めたい場合やNFSが適さない場合は、必要なキャッシュのローカルコピーを各Sparkに配置します。HF_CACHEをローカルツリーに設定し、NFS_VOLUMEを対応するホストパスに設定し、HEAD_NFS_SERVERは空のままにして、PREPARE=0を設定します。ローカルキャッシュには、チェックポイントの小さなファイル、ビジョンシャードmodel-00039-of-00039、提供用のmodel/フォルダー、キット、トークンマップ、適切なランク用重みファイルを含める必要があります。

リポジトリをクローンして設定する

ヘッドでリポジトリをクローンし、ローカル設定ファイルを作成します。

git clone https://github.com/MiaAI-Lab/DeepSeek-v4.1-Flash-1017-2x-DGX-Sparks.git
cd DeepSeek-v4.1-Flash-1017-2x-DGX-Sparks
cp scripts/local.sh.example scripts/local.sh

scripts/local.shを編集し、少なくともWORKERにワーカーのSSHターゲットを設定します。ワーカーへのSSH経路が直接ファブリック経路と異なる場合は、FABRIC_PEERにワーカーのConnectX-7アドレスを設定します。

ほとんどの設定はscripts/config.shで定義されています。主な設定値は次のとおりです。

  • 固定されたEXL3チェックポイント用のMODEL_IDとMODEL_REVISION。
  • デフォルトが1048576のCONTEXT。
  • デフォルトで同時に4件のリクエストを処理するPARALLEL。
  • Hugging Face認証が必要な場合のHF_TOKEN。
  • 標準モードまたはabliteratedモードを選択するABLIT。

起動前にドライランを実行する

ドライランモードを使用して、変更を加えずに検出されたリンクとDockerコマンドを確認します。

DRY_RUN=1 ./start.sh

リポジトリでは、シェル構文のチェックに加えて、準備中に想定されるネットワーク、Docker、GPU、SSH、メモリの条件も検証されます。設定された最小利用可能メモリであるMIN_AVAIL_GIBが両方のSparkで利用できない限り、起動は続行されません。

2ランクサーバーを起動する

ヘッドから完全なデプロイを開始します。

./start.sh

初回実行時、スクリプトはTensorFoldイメージをビルドし、チェックポイントとEngramテーブルをダウンロードし、RoPEテーブルと画像ルーティングデータを準備し、トークンファイルとランクファイルを作成し、ワーカーのストレージを設定して、両方のランクを起動します。ZigイメージのビルドにはSpark上で約6分かかり、約24.9 GBのイメージが生成されます。

起動後、スクリプトはAPIを待機してスモークテストを実行します。起動に成功すると、DeepSeek-V4.1-Flash-1017がポート8888で稼働中であることが報告されます。

使用できるライフサイクルコマンドは次のとおりです。

./start.sh restart
./stop.sh

サービング設定を変更した後はrestartを使用します。停止スクリプトは、コンテナを削除する前にログを圧縮して保存し、LOG_KEEPに従って最新のログを保持します。

OpenAI互換APIを呼び出す

クライアントは、ヘッドのアドレスに/v1パスを付け、正確なモデル識別子DeepSeek-V4.1-Flash-1017を使用してください。たとえば、OpenAI互換のPythonクライアントは次のように設定できます。

from openai import OpenAI

client = OpenAI(
    base_url="http://<head-address>:8888/v1",
    api_key="unused"
)

response = client.chat.completions.create(
    model="DeepSeek-V4.1-Flash-1017",
    messages=[
        {"role": "user", "content": "Explain tensor parallelism briefly."}
    ],
    max_tokens=512
)

print(response.choices[0].message.content)

リクエストで思考を無効にしない限り、モデルは回答前に思考します。推論と最終回答に十分なmax_tokensを割り当てるか、環境変数で関連する思考および推論設定を構成してください。

テキスト、ツール、画像、長いプロンプトを検証する

リポジトリには、稼働中のエンドポイントを確認する検証ユーティリティが含まれています。

python3 tools/validate.py http://<head-address>:8888 --model DeepSeek-V4.1-Flash-1017
API_URL=http://<head-address>:8888 python3 tools/toolcheck.py

検証スイートは、テキスト、思考、ツール、画像入力、長いプロンプトを対象とします。READMEには、画像入力と検証ツールは報告されたハードウェア構成で完全には確認されていないと記載されています。そのため、これらのテストは保証ではなく、デプロイ時の確認として扱ってください。

追加の診断を行うには、ランクのログを確認します。

docker logs -f dsv41-1017-exl3-tf
ssh <worker> docker logs -f dsv41-1017-exl3-tf

abliteratedモードを使用する

デフォルト設定では、標準のMia EAQ 2.9-bitチェックポイントを使用します。ABLIT=1を設定すると、そのチェックポイントを維持したまま、drowzeys/keys-DeepSeek-V4.1-Flash-1017-Abliteratedの対応するKeysオーバーレイを適用します。

これはアクセス制限付きのHugging Faceリポジトリです。準備する前にリポジトリの規約に同意し、アクセス権のあるアカウントで認証してください。

hf auth login
ABLIT=1 ./scripts/prepare.sh
ABLIT=1 ./start.sh restart

または、トークンをシェル履歴に残さずに指定できます。

read -rsp 'Hugging Face read token: ' HF_TOKEN; echo
export HF_TOKEN
ABLIT=1 ./start.sh restart
unset HF_TOKEN

オーバーレイはABLIT_REVISIONで固定されています。準備処理では、標準データとabliteratedデータが誤って混在しないよう、別個のキャッシュパスとランクキャッシュが作成されます。書き換えられたシャードには、オーバーレイとランクファイルに加えて、別のチェックポイントと同程度の空き容量が必要になる場合があります。次のコマンドで標準モードに戻せます。

ABLIT=0 ./start.sh restart

READMEには、標準モードの性能および正確性の結果は、このエンジンでのabliteratedモードを検証するものではないと明記されています。また、準備と起動では同じABLIT設定を使用してください。

性能とチューニングの指針

実際の文章およびコードのプロンプトによる測定では、1つの文章ストリームで約74.9トークン/秒、1つのコードストリームで109.9トークン/秒に達しました。4ストリームでは、総スループットは文章で約130.0トークン/秒、コードで155.5トークン/秒に達しました。プリフィルは、8Kから256Kトークンまでのプロンプト全体で、およそ2,000から2,180トークン/秒でした。

結果は、プロンプトの内容、GPUクロック、同時実行ワークロード、ストレージ、メモリ負荷によって異なります。ランダムな単語によるベンチマークは通常の文章やコードよりドラフト性能が低いため、低いデコード値をエンジン全般の上限として扱わないでください。

  • 両方のSparkのローカルディスクにEngramテーブルを保持する。
  • テンソル並列ランクは繰り返し同期するため、GPUクロック上限を一致させる。
  • 特に他のソフトウェアも実行する場合は、CONTEXTをヘッドのメモリ容量内に収める。
  • 長いプロンプトや複数の同時ユーザーに対応する場合は、PARALLELを控えめに設定する。
  • ネットワークまたはデプロイ設定を変更する前に、DRY_RUN=1を使用する。

オプションのエンジンスイッチとパッチ

リポジトリにはzig/patches/以下にエンジンパッチが含まれています。これらはデフォルトでは無効で、start.shを通じて両方のランクに渡されます。READMEによると、デフォルトイメージは、さまざまなプロンプトおよび同時実行シナリオでトークンハッシュが一致することを確認済みです。

TF_DS_ENGRAM_LANES=2を有効にすると、Engramの読み取り用に2つ目のI/Oリングが追加されますが、測定ではテスト対象のストレージ上で速度が低下しました。TF_DS_IDX_SPLIT=1は、prefillインデクサーの行をランク間で分割しますが、テストしたペアでは測定可能な向上はありませんでした。この設定がランク間で一致しない場合、ペアがハングする可能性があるため、デフォルトでは無効のままです。

パッチ0005は、別の長いプロンプトをprefillしている間に、既存のストリームがデコードラウンドを受け取れるよう、プロンプト処理のインターリーブを改善します。動作はPREFILL_INTERLEAVE_MSで調整できます。パッチ0006は、TF_DS_IMAGE_CACHE=0を設定してこの動作を無効にしていない場合、画像入力のプロンプト状態を保持します。

メモリを監視し、安全でないコンテキストサイズを避ける

メモリは運用上の主な制約です。READMEによると、他のソフトウェアが約6 GiBを使用しているヘッドでは512Kコンテキストが実用的でしたが、655K構成では会話が長くなるにつれてメモリガードを下回る可能性があります。高負荷で長いプロンプトを同時に処理している際にメモリ下限へ達すると、Sparkがフリーズする可能性があるため、ウォッチドッグが組み込まれています。

まずはデフォルト値または控えめなCONTEXT値から開始し、実際のワークロードでテストして、長い履歴の処理中に空きメモリを監視してください。モデルの読み込みに成功していても、キャッシュされた会話が大きくなるにつれて空きメモリが減少することがあります。

まとめ

このリポジトリは、直接接続された2台のDGX SparkにDeepSeek-V4.1-Flash-1017を分散配置する自動化手段を提供します。ハードウェア、RoCEネットワーク、SSH、Docker、ストレージ、Hugging Faceへのアクセスを準備すれば、start.shが複数段階のセットアップを処理し、使い慣れたOpenAI互換エンドポイントを公開します。

信頼性の高いデプロイでは、ローカルのEngramストレージ、揃えたGPUクロック、控えめなコンテキストサイズ、画像・ツール・長文脈ワークロードの入念な検証を優先してください。abliteratedオーバーレイとエンジン切り替えは実験用に利用できますが、文書化された性能および正確性の測定は主に標準チェックポイントとデフォルトのエンジン構成に適用されます。