はじめに
AI開発において 合成データ(Synthetic Data) の重要性が高まっています。
AD/ADAS(自動運転/先進運転支援システム)やロボティクスといった「フィジカルAI」の領域では、危険なエッジケースの実データ収集が課題の1つです。例えば自動運転開発においては、ティアフォー社がNVIDIA Cosmos Predictを活用してエッジケースを生成する取り組みが進んでいます。
従来のNVIDIA Cosmosは理解(Reason)・変換(Transfer)・生成(Predict)でモデルが分かれていましたが、最新の「Cosmos 3」ではこれらが単一のomnimodelに統合されました。以前の記事(NVIDIA Cosmosファミリー実践入門 — Embed1・Reason2・Transfer2.5をColabで動かす)では、Embed1・Reason2・Transfer2.5を試しましたが、生成(旧Predict相当)は未着手でした。
そこで今回はCosmos 3を使い、Text-to-Video / Image-to-Video / Video-to-Videoによる合成データ生成をGoogle Colab上で試した手順と出力結果をまとめます。題材はドライバーモニタリングシステム(DMS)です。DMSの車内データはプライバシー規制やアノテーションコストの壁が厚く、収集が開発のボトルネックになりがちなので、Cosmos 3がエッジケースをリアルに生成できれば開発効率向上が期待できます。
想定読者
- 最新のOmnimodal World Model(Cosmos3-Nano, 16B)をGoogle Colab(A100)で動かしてみたい方
- どの程度の出力クオリティが得られるのか、ざっくり知りたい方
NVIDIA Cosmosとは
NVIDIA Cosmos 3は、物理世界のシミュレーションを目的として構築された、フィジカルAI向けの基盤モデル群です。Mixture-of-Transformers(MoT)アーキテクチャを採用し、テキスト・画像・動画を単一のパイプラインで処理できるオムニモーダル性が特徴です。
本検証では、Hugging Faceフォーマットで公開されている事前学習済みモデルを使用し、テキスト入力(T2V)、画像入力(I2V)および動画入力(V2V)による合成データ生成を試します。
本記事ではアーキテクチャ詳細等は割愛しますので、以下でご確認いただけますと幸いです。
参考リンク:
- NVIDIA Research: Cosmos 3
- NVIDIA AI: Cosmos
- NVIDIA Launch Blog (JP)
- GitHub: Cosmos 公式リポジトリ
- GitHub: Cosmos Model Family
- GitHub: Cosmos 3 Cookbooks
Colabで合成データ生成
今回は「(1) 入力とするベースの運転動画を生成」→「(2) 画像入力・動画入力で合成データ生成」という2ステップで進めます。本来のユースケースではリアルな運転データを入力として拡張するイメージなので、あえて「通常のベース動画を生成 → それを起点にエッジケースを生成」という順番にしています。
セットアップ
実行環境はGoogle ColabのA100を使用しますので、RuntimeでA100を選んでください。
次に事前にHugging Faceでモデルの利用規約(Cosmos-1.0-Guardrail)に同意し、ColabのSecretsに HF_TOKEN を登録しておきます。
環境構築とライブラリをインストールします。
# ==========================================
# 環境構築とライブラリのインストール
# ==========================================
print("ライブラリをインストールしています...")
!pip install -q --upgrade diffusers transformers accelerate huggingface_hub hf_transfer cosmos_guardrail
import os
os.environ["HF_XET_HIGH_PERFORMANCE"] = "1"
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
import torch
import cv2
from PIL import Image
from huggingface_hub import login
from google.colab import userdata
from diffusers import Cosmos3OmniPipeline
from diffusers.utils import export_to_video
モデルのロード
Cosmos3 Nano をロードします。VRAM節約のため bfloat16 でロードし、メモリ最適化を適用します。また enable_safety_checker=False を指定しています。これがないと出力にモザイクがかかります(詳細は後述)。
# ==========================================
# Hugging Face認証とモデルのロード
# ==========================================
login(token=userdata.get("HF_TOKEN"))
print("Cosmos3 Nanoモデルをロード中... (約32GBのダウンロードがあるため数分かかります)")
pipe = Cosmos3OmniPipeline.from_pretrained(
"nvidia/Cosmos3-Nano",
torch_dtype=torch.bfloat16,
enable_safety_checker=False
).to("cuda")
print("メモリ最適化(オフロード&スライシング)を適用中...")
pipe.enable_model_cpu_offload()
pipe.vae.enable_slicing()
pipe.vae.enable_tiling()
入力の生成 (Text-to-Video)
今回は合成データ生成の入力そのものも生成します。ネット上によさげな運転中の動画・画像がなく、あってもプライバシー的にちょっと使いづらいので、ベース映像もAIで作ります。本当は実際の画像を使いたいところですが、プライバシー等の事情もあるので今回はAI生成にしています。
# ==========================================
# ベース動画(正常運転)の生成
# ==========================================
print("\n--- 1. 正常運転のベース動画を生成中 ---")
prompt = "A highly detailed, cinematic close-up portrait of a 30-year-old Japanese male sitting in the driver's seat of a modern car. He is holding the steering wheel with both hands, looking straight ahead. Shot on a professional 8k cinema camera, near-infrared (IR) lighting setup, sharp focus on the face and eyes, perfectly clear and completely visible facial features, facial motion capture reference, photorealistic, automotive interior. --ar 16:9"
negative_prompt = "mosaic, censored, anonymized, pixelated face, blurred face, blurry, dashcam, security camera, CCTV, privacy filter, obscured face, black box over face, watermark, text, out of focus, deformed, low quality, artifact"
# 約2秒間(49フレーム)の正常運転動画を生成
output_base = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
num_frames=49,
guidance_scale=7.0,
num_inference_steps=40,
)
base_frames = output_base.video
export_to_video(base_frames, "01_base_driving.mp4", fps=24)
print("✅ ベース動画を保存しました: 01_base_driving.mp4")
# ==========================================
# 比較用シード画像の抽出
# ==========================================
print("\n--- 2. 最終フレームを抽出中 ---")
last_frame_image = base_frames[-1]
last_frame_image.save("02_last_frame_seed.png")
print("✅ シード画像を保存しました: 02_last_frame_seed.png")
だいたい2分ほどで2秒間の動画が生成されます。IRのインストラクションは無視されました…正直なところ、質感もややいまいちな印象です。Nanoなのでしょうがないのかもしれません。また上記コードではシードを固定していないため、実行のたびに結果は変わりますのでご注意ください。
01_base_driving.mp4
画像入力 (Image-to-Video) / 動画入力 (Video-to-Video)
上記で生成した入力を使って、居眠り動画を生成してみます。前ステップで直接居眠り動画を生成してもよかったのですが、想定ユースケース的には「リアルな運転データを入力としてデータ拡張する」イメージなので、あえてこの順番にしています。
# ==========================================
# 合成データ生成(I2V vs V2V)
# ==========================================
# 共通のエッジケースプロンプト
prompt_edge = (
"The driver fights against intense drowsiness, slowly nodding off, closing his eyes, "
"and his head dropping down slightly. "
"Perfectly static, fixed dashboard camera angle. The camera is rigidly mounted and completely stationary. "
"Locked-off shot. The face is perfectly clear and completely visible."
)
negative_prompt_edge = (
"camera movement, tracking, panning, zooming, shaking, handheld, dynamic camera, moving camera, "
"mosaic, censored, anonymized, pixelated face, blurred face, dashcam, security camera, CCTV"
)
print("\n--- 3. 画像入力(I2V)でエッジケースを生成中 ---")
output_i2v = pipe(
prompt=prompt_edge,
negative_prompt=negative_prompt_edge,
image=last_frame_image, # 抽出した静止画をシードにする
num_frames=49,
guidance_scale=7.0,
num_inference_steps=40,
)
i2v_frames = output_i2v.video
export_to_video(i2v_frames, "03_edge_I2V.mp4", fps=24)
print("✅ I2V動画を保存しました: 03_edge_I2V.mp4")
print("\n--- 4. 動画入力(V2V)でエッジケースを生成中 ---")
output_v2v = pipe(
prompt=prompt_edge,
negative_prompt=negative_prompt_edge,
video=base_frames, # 正常運転の動画そのものをシードにする
num_frames=49,
guidance_scale=7.0,
num_inference_steps=40,
)
v2v_frames = output_v2v.video
export_to_video(v2v_frames, "04_edge_V2V.mp4", fps=24)
print("✅ V2V動画を保存しました: 04_edge_V2V.mp4")
print("\n🎉 全ての生成処理が完了しました!")
こちらもそれぞれ大体2分ほどで生成されます。破綻なく居眠りしてそうな感じです。
03_edge_I2V.mp4
04_edge_V2V.mp4
【補足1】Geminiで入力を作らせた場合
Cosmos3-Nanoによるベース入力動画の生成がいまいちだったので、Gemini Proにも作らせてみました。入力はCosmos3と全く同じ以下です。
prompt = "A highly detailed, cinematic close-up portrait of a 30-year-old Japanese male sitting in the driver's seat of a modern car. He is holding the steering wheel with both hands, looking straight ahead. Shot on a professional 8k cinema camera, near-infrared (IR) lighting setup, sharp focus on the face and eyes, perfectly clear and completely visible facial features, facial motion capture reference, photorealistic, automotive interior. --ar 16:9"
negative_prompt = "mosaic, censored, anonymized, pixelated face, blurred face, blurry, dashcam, security camera, CCTV, privacy filter, obscured face, black box over face, watermark, text, out of focus, deformed, low quality, artifact"
gemini_generated_input.mp4
まだこちらのほうがマシな印象です。実写の映像・画像を入力にするのが理想ですが、プライバシーの観点から今回はAIで生成する方法を採用しています。
さてこちらをベースに、居眠り動画を生成してみます。
import cv2
from PIL import Image
from diffusers.utils import export_to_video
# 動画を読み込んでPIL画像のリストに変換する処理
def load_video_to_pil_frames(video_path):
cap = cv2.VideoCapture(video_path)
frames = []
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# OpenCVは標準がBGRなので、RGBに変換してからPIL画像にする
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames.append(Image.fromarray(frame_rgb))
cap.release()
return frames
# ==========================================
# ファイルの読み込みと保存の実行
# ==========================================
base_frames = load_video_to_pil_frames("/content/gemini_generated_input.mp4")
export_to_video(base_frames, "01_base_driving.mp4", fps=24)
print("✅ ベース動画を保存しました: 01_base_driving.mp4")
last_frame_image = base_frames[-1]
last_frame_image.save("02_last_frame_seed.png")
print("✅ シード画像を保存しました: 02_last_frame_seed.png")
03_edge_I2V.mp4
04_edge_V2V.mp4
【補足2】enable_safety_checker=Trueの場合
pipe = Cosmos3OmniPipeline.from_pretrained(
"nvidia/Cosmos3-Nano",
torch_dtype=torch.bfloat16,
# enable_safety_checker=False # デフォルトはTrue
).to("cuda")
pipe.enable_model_cpu_offload()
pipe.vae.enable_slicing()
pipe.vae.enable_tiling()
output_with_safety_checker = pipe(
prompt=prompt_edge,
image=last_frame_image, # 抽出した静止画をシードにする
num_frames=49,
guidance_scale=7.0,
num_inference_steps=40,
)
frames = output_with_safety_checker.video
export_to_video(frames, "generated_with_safety_checker.mp4", fps=24)
generated_with_safety_checker.mp4
このように enable_safety_checker を無効化せずデフォルト設定のまま実行すると、AIが「プライバシー保護が必要」と判定するのか、高確率で顔にモザイクがかかります。検証目的で顔や瞼の動きを生成したい場合は、このパラメータの制御が事実上必須になりそうです。
ただしCookbookにも記載の通り、ガードレール(=セーフティチェック)を無効化する場合、そのライセンス遵守の責任は利用者側が負う旨が明記されています。検証目的とはいえ、この点は理解した上で使う必要があります。
To disable guardrails server-wide (you are responsible for license compliance), add --no-guardrails to any vllm serve command below:
【補足3】生成物のライセンスと著作権
AI生成データを利用する際、ライセンスや法的なリスクは気になるところです。ざっと調べた限り、結論としては商用利用も含めて利用は可能そうです。
詳細
-
NVIDIA Cosmos 3(商用利用可)
ライセンス上、生成物の商用利用が明示的に認められています。
出典:NVIDIA Open Model License Agreement -
Google Gemini(商用利用可)
Googleが生成物の所有権を主張することはありません。
出典:Google 生成 AI 追加利用規約 -
日本の著作権法(原則、著作権なし)
AIの自動生成物には「人間の創作意図」がないとされ著作権は発生しません。自由に使える反面、無断転載されても法的に止めるのが難しい「フリー素材」に近い扱いになります。
出典:文化庁 AIと著作権に関する考え方について -
YouTubeへの投稿(要ラベル)
リアルなAI生成動画を投稿する際は、「改変されたコンテンツ、または合成コンテンツ」の申告ラベルを付ける義務があります。
出典:YouTube ヘルプ
※ ライセンスまわりは変更される可能性もあるので、実際に商用利用する際は最新の規約を確認してください。
おわりに
Cosmos3 NanoによるText-to-Videoでのゼロからの生成、さらにそれをベースとしたImage-to-Video/Video-to-Videoによる合成データ生成を試しました。
Text-to-Videoの出力クオリティ自体は、Nanoだったこともありますが、正直まだ発展途上という印象です(IRの指示が無視されたり等)。一方でImage-to-Video/Video-to-Videoは、比較的破綻なく生成できる印象です。ただしあくまで定性評価にとどまっているので、使用カメラの撮像特性が再現できるか?なども含め定量評価は今後の課題です。Cosmos3 SuperやCosmos3 Edgeではどう変わるのかも気になるところです。
