0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NVIDIA Cosmos-Reason2をローカルで動かして動画推論する(transformers + vLLM)

0
Last updated at Posted at 2026-05-02

cosmos_reason2_thumbnail_ja.png

2026年4月29日、NVIDIAはHugging Faceで公開中のCosmos-Reason2モデルファミリーに32Bバリアントを追加し、既存の2B・8Bと合わせて3サイズが揃いました。Cosmos-Reason2は物理的な常識判断と身体的な連鎖思考推論に特化したオープンなVision-Language Modelで、Qwen3-VLアーキテクチャをベースに構築されています。ソースコードはApache 2.0ライセンス、モデルウェイトはNVIDIA Open Model License(商用利用可)で公開されています。

この記事では2Bモデルをtransformersでロードして動画推論を1回実行し、同じウェイトをvLLMでサーブしてOpenAI互換エンドポイントを叩くところまでやります。モデルコードは全体で50行未満です。

※本記事は公開情報をもとにした個人的なまとめであり、各企業の公式見解ではありません。

なぜ今見る価値があるか

Cosmos-Reason2は汎用的なキャプション生成VLMではありません。空間・時間・物理法則に関する推論に特化しており、最新リリースではロボット用途向けに軌道座標がプランニング出力に追加されています。アーキテクチャがQwen3-VLなので、vllm serveqwen-vl-utilsエコシステムとの既存連携がそのまま使えます。

個人的には、固定カメラでワークベンチを撮影して「今何が起きたか」を構造化テキストで返すデモを追いかけていて、Cosmos-Reason2-2Bはローカルで動かして「これならプロダクトに出せる」と思えた初めてのオープンモデルでした。

ハードウェア要件

GPUに合わせてバリアントを選んでください。数値はNVIDIA Cosmos-Reason2のGitHub READMEからの引用です。

  • Cosmos-Reason2-2B: GPUメモリ最低24GB(RTX 4090、RTX 5090、A10G、L4はfp16でOK)
  • Cosmos-Reason2-8B: GPUメモリ最低32GB(A100 40GB、L40S、H100)
  • Cosmos-Reason2-32B: fp16で約80GB(H100 80GB単体か、A100 40GB x2のtensor parallel 2)
  • CPU: 動画フレームをストリーミングする場合は8コア以上推奨
  • ディスク: 2Bウェイトで10GB、32Bウェイトで約70GB
  • ffmpegがシステムレベルでインストール済みであること(動画デコーダが呼び出します)

2Bなら手元のRTX 4090で十分動きます。8B以上はクラウドGPUを検討してください。

1. 環境構築

uvが一番すっきりします。NVIDIAのサンプルスクリプトでも使われています。必要なtransformersのバージョンは4.57.0以上で、Qwen3VLForConditionalGenerationQwen3VLProcessorが入ったのがこのバージョンからです。

sudo apt-get install -y curl ffmpeg git git-lfs
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env
uv venv --python 3.12
source .venv/bin/activate
uv pip install \
    "transformers==4.57.3" \
    "accelerate==1.12.0" \
    "torch==2.9.0" \
    "torchvision" \
    "torchcodec==0.9.1" \
    "av==16.1.0" \
    "pillow==12.0.0"

transformersのバージョンを指定せずに4.56.x系が入ると、AutoModelからKeyError: 'qwen3_vl'が出ます。最初のハマりポイントです。

2. 画像で最小限の推論を試す

まずは画像1枚で動く部分を確認します。processorはモデルと紐づいているので、同じHubリポジトリから取得します。

# image_reason.py
import torch
import transformers

transformers.set_seed(0)

model_id = "nvidia/Cosmos-Reason2-2B"

model = transformers.Qwen3VLForConditionalGeneration.from_pretrained(
    model_id,
    dtype=torch.float16,
    device_map="auto",
    attn_implementation="sdpa",
)

processor = transformers.Qwen3VLProcessor.from_pretrained(model_id)

conversation = [
    {"role": "system", "content": [{"type": "text", "text": "You are a careful physical reasoning assistant."}]},
    {"role": "user", "content": [
        {"type": "image", "image": "https://images.cocodataset.org/val2017/000000039769.jpg"},
        {"type": "text", "text": "List the objects on the surface and any safety risks."},
    ]},
]

inputs = processor.apply_chat_template(
    conversation,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt",
).to(model.device)

generated = model.generate(**inputs, max_new_tokens=512)
trimmed = [out[len(inp):] for inp, out in zip(inputs.input_ids, generated)]
print(processor.batch_decode(trimmed, skip_special_tokens=True)[0])

ポイントが2つあります。メディア(画像・動画)はテキストより前に置くこと。学習時のデータ順序と合わせないと品質が目に見えて落ちます。もう1つ、device_map="auto"は単一GPU環境ならこれだけで十分です。マルチGPUはaccelerate設定に移行してください。

画像1枚で応答が返ってくれば、環境構築は完了です。

3. 動画推論(本命のユースケース)

動画パスではapply_chat_templatefpsを渡します。NVIDIAのリファレンススクリプトは4FPSでサンプリングしており、出発点としては妥当です。

# video_reason.py
import torch
import transformers

transformers.set_seed(0)

model = transformers.Qwen3VLForConditionalGeneration.from_pretrained(
    "nvidia/Cosmos-Reason2-2B",
    dtype=torch.float16,
    device_map="auto",
    attn_implementation="sdpa",
)

processor = transformers.Qwen3VLProcessor.from_pretrained("nvidia/Cosmos-Reason2-2B")

# 視覚トークン数を制限してメモリ使用量を安定させる
PIXELS_PER_TOKEN = 32 * 32
processor.video_processor.size = {
    "shortest_edge": 256 * PIXELS_PER_TOKEN,
    "longest_edge": 8192 * PIXELS_PER_TOKEN,
}

conversation = [
    {"role": "system", "content": [{"type": "text", "text": "You are a careful physical reasoning assistant."}]},
    {"role": "user", "content": [
        {"type": "video", "video": "./assets/sample.mp4"},
        {"type": "text", "text": "Describe each phase of the action and predict the next likely step."},
    ]},
]

inputs = processor.apply_chat_template(
    conversation,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt",
    fps=4,
).to(model.device)

out = model.generate(**inputs, max_new_tokens=4096)
trimmed = [o[len(i):] for i, o in zip(inputs.input_ids, out)]
print(processor.batch_decode(trimmed, skip_special_tokens=True)[0])

視覚トークンのキャップは重要です。長い動画を高解像度で流すとmax_position_embeddingsを超えてCUDA OOMが出ますが、エラーメッセージが動画長を示さないので原因特定に手間取ります。他のデバッグより先にこのキャップを設定してください。

4. vLLMで同じウェイトをサーブする

開発中の単発呼び出し以上のことをするなら、vLLMに切り替えます。Cosmos-Reason2のREADMEにserveコマンドがそのまま載っています。vLLM 0.11.0以上が必要です。

uv pip install "vllm>=0.11.0"

vllm serve nvidia/Cosmos-Reason2-2B \
    --allowed-local-media-path "$(pwd)" \
    --max-model-len 16384 \
    --media-io-kwargs '{"video": {"num_frames": -1}}' \
    --reasoning-parser qwen3 \
    --port 8000

3つのフラグは省略不可で、ここが最もハマりやすいポイントです。

  • --allowed-local-media-pathを指定しないと、file://URLのリクエストがすべて400で返ります。
  • --media-io-kwargs '{"video": {"num_frames": -1}}'がないと、vLLMがデフォルトのフレーム予算で動画を無言で切り詰めます。
  • **--reasoning-parser qwen3がないと、サーバーが<think>...</think>ブロックを生のまま返します。**ダウンストリームのパーサーが黙って誤処理するので、ここが一番危険です。

初回起動時はCUDAグラフのコンパイルに数分かかります。4分経っても動かないからといってプロセスを落とさないでください。

5. コードからサーバーを叩く

vLLMはOpenAI互換のChat Completions APIを公開するので、公式のopenai SDKがそのまま使えます。

# client.py
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")

resp = client.chat.completions.create(
    model="nvidia/Cosmos-Reason2-2B",
    messages=[
        {"role": "system", "content": "You are a careful physical reasoning assistant."},
        {"role": "user", "content": [
            {"type": "video_url", "video_url": {"url": "file:///workspace/assets/sample.mp4"}},
            {"type": "text", "text": "Summarize what is happening and flag any safety risks."},
        ]},
    ],
    max_tokens=2048,
)

msg = resp.choices[0].message
print("REASONING:", getattr(msg, "reasoning_content", None))
print("ANSWER:", msg.content)

--reasoning-parser qwen3をオンにすると、vLLMがchain-of-thoughtをreasoning_contentに、最終回答をcontentに分割してくれます。contentの中に生の<think>タグが見えたら、serveフラグを再確認してください。

知っておきたいハマりポイント

  • **transformersは4.57.0以上が必須。**古いリリースにはQwen3VLForConditionalGenerationが含まれていません。
  • **メディアはテキストより前に。**学習データの順序と合わせないと精度が静かに下がります。
  • **--reasoning-parser qwen3はvLLMサーブ時に必須。**なしだとanswerフィールドに<think>...</think>ブロックがそのまま入ります。
  • **長い動画ではprocessor.video_processor.sizeをキャップする。**OOMが出る前に動画長を示すエラーメッセージは出ません。
  • **vLLMの初回起動は遅い。**CUDAグラフのコンパイルに数分かかります。4分時点でプロセスを停止しないでください。
  • **32Bバリアントではfp16 vs bf16に注意。**32BカードはHopperとBlackwellでbf16を推奨しています。Ampereのfp16でも動きますが、長いコンテキストでやや不安定になることがあります。

まとめ

  • Cosmos-Reason2はQwen3-VLベースの推論VLMで、3サイズのオープンウェイトが公開されています。32Bバリアントは2026年4月29日に追加されました。
  • 推論フローは標準のtransformers(4.57.0以上)+ Qwen3VLForConditionalGeneration + Qwen3VLProcessorで完結します。カスタムコードは不要です。
  • プロダクション向けには、vLLM 0.11+と--reasoning-parser qwen3が公式サポートのパスで、OpenAIクライアントがそのまま動きます。
  • ロボティクス、自動運転、スマートスペースがターゲットなので、「今ここで何が起きていて、次に何が起きるべきか」が得意な一方、汎用的な画像キャプションには向いていません。
  • 32Bウェイトは公開から間もないです。本番に載せる前に自前の評価セットで検証することを推奨します。

参考

  • nvidia-cosmos/cosmos-reason2 on GitHub
  • Cosmos-Reason2 documentation (NVIDIA)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?