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?

Modalの使い方覚書

Posted at

Modalとは?

Modalは、Pythonスクリプトをそのままクラウド上のGPUで実行できるサーバーレスプラットフォームです。「GPUクラウド界のHeroku」と表現されるように、インフラ管理の複雑さを抽象化し、開発者はコードに集中できます。

Modalの最大の特徴

コマンド実行からわずか15秒でGPU実行開始

従来のGPUクラウドサービス(RunPod、Lambda Labsなど)では、インスタンス起動だけで5分程度かかりますが、Modalは圧倒的に高速です。

Modalのメリット・デメリット

メリット

1. 開発速度の圧倒的な向上

  • インフラコードが不要: Dockerfileもレジストリも不要
  • 一つのファイルで完結: Pythonデコレータだけで環境指定
  • ローカルとリモートのシームレス連携: ファイル転送も簡単

実際の開発効率の比較:

  • 従来のサービス: 1時間に10回程度の試行錯誤(5分起動 + 30秒実行)
  • Modal: 1時間に20〜30回の試行錯誤(15秒起動 + 30秒実行)

2. RunPodとの比較で優れている点

RunPodの場合:

# 1. Dockerイメージをビルド
docker build -t myimage:v1 .

# 2. レジストリにpush
docker push registry.example.com/myimage:v1

# 3. RunPodで設定
# - イメージタグを手動で指定
# - 毎回更新が必要

Modalの場合:

# たったこれだけ
import modal

app = modal.App()
image = modal.Image.debian_slim().pip_install("torch")

@app.function(gpu="A100")
def train():
    # ここに処理を書くだけ
    pass

3. その他の便利機能

  • ローカルパッケージの転送が簡単: .mount()で一発
  • Pythonオブジェクトをそのまま引数に渡せる: シリアライズ不要
  • オートスケール: 使わない時は自動停止(課金ゼロ)
  • 毎月$30の無料クレジット: 小規模実験なら実質無料

デメリット

1. 料金が高め

  • T4: 約$0.59/時間
  • A100: 約$2.5/時間
  • RunPod比較で1.5〜2倍程度高い

2. サブスクリプションプランなし

  • 完全従量課金のみ
  • 長時間のトレーニングには不向き

3. 完全なコンテナ環境ではない

  • 関数実行プラットフォーム
  • 対話的な開発には向かない場合も

4. 学習コスト

  • ローカル/リモート両方で同じスクリプトが実行される仕組み
  • Pythonバージョンの違いなどに注意が必要

どんな時にModalを使うべきか?

Modalが最適:

  • 頻繁に環境を変える実験
  • 短時間の推論タスク
  • パラメータ探索などの並列実行
  • プロトタイプ開発

RunPodなどが最適:

  • 長時間のトレーニング(数時間〜数日)
  • コスト重視
  • 対話的な開発環境が欲しい
  • 完全なコンテナ制御が必要

セットアップ手順

1. インストール

# uvを使う場合
uv init --app
uv add modal

# pipを使う場合
pip install modal

2. アカウント作成とログイン

  1. Modal公式サイトでアカウント作成
  2. ターミナルでログイン:
modal setup

基本的な使い方

Hello World

import modal

app = modal.App()
image = modal.Image.debian_slim().pip_install("torch", "numpy")

@app.function(gpu="T4", image=image)
def train_simple_model():
    import torch
    
    print(f"使用デバイス: {torch.cuda.get_device_name(0)}")
    
    # 簡単な計算
    X = torch.randn(1000, 20).cuda()
    y = torch.randn(1000, 1).cuda()
    
    model = torch.nn.Linear(20, 1).cuda()
    optimizer = torch.optim.Adam(model.parameters())
    
    for epoch in range(100):
        optimizer.zero_grad()
        pred = model(X)
        loss = torch.nn.functional.mse_loss(pred, y)
        loss.backward()
        optimizer.step()
        
        if epoch % 20 == 0:
            print(f"Epoch {epoch}, Loss: {loss.item():.4f}")
    
    return f"訓練完了!"

@app.local_entrypoint()
def main():
    result = train_simple_model.remote()
    print(result)

実行

modal run gpu_test.py

実践: NVIDIA Cosmosを動かす

ここからが本題です。既存のDockerイメージ(NVIDIA Cosmosなど)をModalで使う方法を解説します。

方法1: レジストリからイメージを取得

import modal

# NVIDIA Cosmosの公式イメージを使用
image = modal.Image.from_registry(
    "nvcr.io/nvidia/cosmos/cosmos-predict2-container:1.1"
)

app = modal.App("cosmos-inference")

@app.function(
    image=image,
    gpu="A100",
    timeout=3600,
    secrets=[modal.Secret.from_name("nvidia-ngc")]  # NGC認証が必要な場合
)
def run_cosmos_inference(input_video_path: str):
    """Cosmosで推論を実行"""
    import subprocess
    import os
    
    # Cosmosの推論スクリプトを実行
    result = subprocess.run(
        [
            "python", "/workspace/inference.py",
            "--input", input_video_path,
            "--output", "/tmp/output.mp4"
        ],
        capture_output=True,
        text=True
    )
    
    if result.returncode == 0:
        # 結果を読み込んで返す
        with open("/tmp/output.mp4", "rb") as f:
            return f.read()
    else:
        raise Exception(f"Error: {result.stderr}")

@app.local_entrypoint()
def main(video_path: str):
    print("Cosmosでの推論を開始...")
    result = run_cosmos_inference.remote(video_path)
    
    # 結果を保存
    with open("output.mp4", "wb") as f:
        f.write(result)
    print("完了!output.mp4に保存しました")

方法2: イメージに追加設定を加える

import modal

# Cosmosイメージにカスタム設定を追加
image = (
    modal.Image.from_registry(
        "nvcr.io/nvidia/cosmos/cosmos-predict2-container:1.1"
    )
    .pip_install("opencv-python", "pillow")  # 追加パッケージ
    .run_commands(
        "apt-get update && apt-get install -y ffmpeg"  # 追加ツール
    )
)

app = modal.App("cosmos-custom")

@app.function(image=image, gpu="A100")
def process_with_cosmos():
    import os
    
    # Cosmosコンテナ内でコマンド実行
    os.system("cd /workspace && python your_custom_script.py")
    
    return "処理完了"

方法3: ローカルファイルをマウント

import modal

image = modal.Image.from_registry(
    "nvcr.io/nvidia/cosmos/cosmos-predict2-container:1.1"
)

app = modal.App("cosmos-with-data")

@app.function(
    image=image,
    gpu="A100",
    mounts=[
        modal.Mount.from_local_dir(
            "./input_videos",
            remote_path="/data/inputs"
        )
    ]
)
def batch_process():
    """複数の動画を一括処理"""
    import os
    import subprocess
    
    input_dir = "/data/inputs"
    output_dir = "/tmp/outputs"
    os.makedirs(output_dir, exist_ok=True)
    
    # すべての動画を処理
    for video_file in os.listdir(input_dir):
        if video_file.endswith('.mp4'):
            input_path = os.path.join(input_dir, video_file)
            output_path = os.path.join(output_dir, f"out_{video_file}")
            
            subprocess.run([
                "python", "/workspace/inference.py",
                "--input", input_path,
                "--output", output_path
            ])
    
    return "バッチ処理完了"

NGC認証が必要な場合

NVIDIA NGC Catalogのプライベートイメージを使う場合、認証情報をModal Secretsに保存します。

# Modalのダッシュボードで以下を設定:
# Secret名: nvidia-ngc
# NGC_API_KEY: your_ngc_api_key
@app.function(
    image=image,
    gpu="A100",
    secrets=[modal.Secret.from_name("nvidia-ngc")]
)
def authenticated_run():
    # NGC認証が自動的に行われる
    pass

実行例

# 基本的な実行
modal run cosmos_inference.py

# コマンドライン引数を渡す
modal run cosmos_inference.py --video-path ./input.mp4

# デプロイして永続化
modal deploy cosmos_inference.py

便利なTips

1. 複数GPU使用

@app.function(gpu="A100:2")  # A100を2つ
def multi_gpu_training():
    import torch
    print(f"{torch.cuda.device_count()}個のGPU使用中")

2. Volumeで結果を永続化

volume = modal.Volume.from_name("cosmos-outputs", create_if_missing=True)

@app.function(
    image=image,
    gpu="A100",
    volumes={"/outputs": volume}
)
def save_results():
    # /outputsに保存したファイルは永続化される
    import torch
    torch.save(model, "/outputs/model.pth")

ローカルにダウンロード:

modal volume get cosmos-outputs model.pth ./local_model.pth

3. クラスベースのリモートオブジェクト

@app.cls(gpu="A100", image=image)
class CosmosProcessor:
    def __init__(self):
        # モデルのロードなど(1回だけ実行)
        self.model = load_cosmos_model()
    
    @modal.method()
    def process(self, video_data):
        # 何度も呼び出せる
        return self.model.inference(video_data)

@app.local_entrypoint()
def main():
    processor = CosmosProcessor()
    
    # 複数の動画を処理(モデルロードは1回だけ)
    for video in videos:
        result = processor.process.remote(video)

トラブルシューティング

インスタンスが勝手に増える問題

Modalはデフォルトでオートスケーリングします。これを制御するには:

@app.cls(
    gpu="A100",
    concurrency_limit=1,  # 同時実行は1つまで
    allow_concurrent_inputs=100  # キューには100個まで
)
class Processor:
    @modal.method()
    def process(self, data):
        return result

Pythonバージョンの不一致

ローカルとリモートのPythonバージョンが異なる場合、エラーが発生することがあります。

# イメージでPythonバージョンを明示
image = modal.Image.from_registry(
    "nvcr.io/nvidia/cosmos/cosmos-predict2-container:1.1",
    add_python="3.10"  # Python 3.10を使用
)

料金の目安

  • T4: $0.59/時間(約90円/時間)
  • A100: $2.5/時間(約375円/時間)
  • 毎月$30の無料クレジット付き

実例:

  • 2分の実験(環境構築含む): 約$0.02
  • 2回目以降(イメージキャッシュ済み): さらに安い

短時間の処理なら数セント程度で最新GPUが利用できます。

まとめ

Modalを選ぶべき場合

  • ✅ 頻繁な試行錯誤が必要
  • ✅ 短時間の推論・実験
  • ✅ インフラ管理の手間を削減したい
  • ✅ プロトタイプ開発

RunPodなど他サービスを選ぶべき場合

  • ✅ 長時間のトレーニング
  • ✅ コスト最優先
  • ✅ 完全なコンテナ制御が必要
  • ✅ 対話的な開発環境

Modalは「15秒でGPUが動く」という圧倒的な開発速度が魅力です。特にNVIDIA Cosmosのような複雑な環境でも、Dockerイメージを指定するだけで簡単に動かせるのは大きなメリットです。

毎月$30の無料枠があるので、まずは試してみることをおすすめします!

参考リンク

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?