拡散モデルによるコード生成モデル「Dream-Coder 7B」をmacOSで動かして他モデルと比較してみた

ジェネラティブエージェンツの西見です。

Googleが発表した拡散モデルを利用した言語モデル「Gemini Diffusion」があまりにも爆速で動作していたのは記憶に新しいです。

deepmind.google

そんな中、2025年7月15日に拡散モデルベースのオープンウェイトのLLMである「Dream-Coder」が公開されたのを見て、ローカルでどのぐらいの速度が出してくれるのかが気になり、検証してみました。

github.com

Dream-Coderとは

Dream-Coderは、香港大学NLPグループが開発した拡散モデルベースのコード生成LLMです。従来の自己回帰モデル(左から右への逐次生成)とは異なり、discrete diffusion modelingという手法を採用しています。

discrete diffusion modelingとは、画像生成で使われる拡散モデルをテキスト生成に応用した技術です。通常の拡散モデルは連続値(画像のピクセル値など)を扱いますが、コードは離散的なトークン(単語や記号)で構成されるため、「discrete(離散的)」な拡散モデルが必要になります。ノイズだらけのコードから始めて、段階的にノイズを取り除いていくことで、最終的なコードを生成します。

検証内容

今回は、Dream-Coder 7Bと同パラメータ数の自己回帰モデル2つを比較しました。

  • Dream-Coder-v0-Instruct-7B
  • Qwen2.5-Coder-7B-Instruct
  • CodeLlama-7b-Instruct-hf

評価方法

3つのテストケースで各5回実行(計15回/モデル)し、生成速度を計測しました。例によってMac Studioでの検証のため、GPUマシンとは異なる結果が出る点に注意してください。実行環境は次の通りです。

Mac Studio (2025) 
Apple M3 Ultra
Mem: 512GB
Storage: 4TB SSD
macOS 15.5

各テストケースは次の問題設定にしました。

フィボナッチ数列
Write a function to calculate the nth Fibonacci number efficiently.

クイックソート
Implement quicksort algorithm for a list of integers.

回文判定
Check if a string is a palindrome, ignoring case and spaces.

実行結果

モデル 平均生成時間 標準偏差 変動係数 中央値
Dream-Coder-v0-Instruct-7B 13.26秒 0.88秒 6.6% 13.04秒
Qwen2.5-Coder-7B-Instruct 12.09秒 3.13秒 25.9% 11.88秒
CodeLlama-7b-Instruct-hf 12.75秒 3.04秒 23.8% 13.79秒

爆速という訳ではなく、他モデルとの差がつかない結果になりましたが、拡散モデルは自己回帰モデルに比べて、生成にかかる時間は安定していました。これが拡散モデルっぽい挙動なのかなー、と思っています。

当初MPSを有効にせずに実行して、信じられない時間がかかっていたのですが、MPSを有効にしたところ、それなりのスピードで動いてくれました。

環境 実行時間 メモリ使用量
CPU 20分以上 28.75GB
MPS 13.26秒 6.03GB

macOSでDream-Coderを動かす手順

というわけで、手元で動かしてみたい人は次の手順でセットアップし、コードを実行してみてください。uvがセットアップされていることが前提です。

パッケージのセットアップ

# 基本パッケージ
uv add torch transformers accelerate
uv add datasets psutil

# HuggingFaceトークンの設定
# .envファイルを作成してHF_TOKENを設定
echo "HF_TOKEN=your_huggingface_token_here" > .env

実行コード

# dream_coder_mps.py
import os
import time
import torch
from transformers import AutoModel, AutoTokenizer
from pathlib import Path
from dotenv import load_dotenv

# 環境変数の読み込み
load_dotenv()

def load_dream_coder():
    """Dream-Coderモデルの読み込み"""
    model_id = "Dream-org/Dream-Coder-v0-Instruct-7B"
    
    print("モデルをダウンロード中...")
    tokenizer = AutoTokenizer.from_pretrained(
        model_id, 
        trust_remote_code=True
    )
    
    # パディングトークンの設定
    if tokenizer.pad_token is None:
        tokenizer.pad_token = tokenizer.eos_token
    
    # MPSデバイスでの読み込み
    model = AutoModel.from_pretrained(
        model_id,
        torch_dtype=torch.float16,  # MPSではfloat16を使用
        trust_remote_code=True
    ).to("mps").eval()
    
    print("モデル読み込み完了")
    return model, tokenizer

def generate_code(model, tokenizer, prompt):
    """コード生成の実行"""
    # チャット形式でのプロンプト準備
    messages = [{"role": "user", "content": prompt}]
    inputs = tokenizer.apply_chat_template(
        messages,
        return_tensors="pt",
        return_dict=True,
        add_generation_prompt=True
    )
    
    # MPSデバイスに移動
    inputs = {k: v.to("mps") for k, v in inputs.items()}
    
    # コード生成の実行
    start_time = time.time()
    
    with torch.no_grad():
        outputs = model.diffusion_generate(
            inputs=inputs['input_ids'],
            attention_mask=inputs.get('attention_mask', None),
            max_new_tokens=256,
            steps=50,  # 拡散ステップ数
            temperature=0.1,
            top_p=0.95,
            alg="entropy",
            alg_temp=0.0
        )
    
    generation_time = time.time() - start_time
    
    # 結果のデコード
    if isinstance(outputs, torch.Tensor):
        input_length = inputs['input_ids'].shape[1]
        if outputs.dim() > 1:
            generated_ids = outputs[0][input_length:]
        else:
            generated_ids = outputs[input_length:]
        generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
    else:
        generated_text = str(outputs)
    
    return generated_text, generation_time

def main():
    # モデルの読み込み
    model, tokenizer = load_dream_coder()
    
    # テスト用プロンプト
    test_prompts = [
        "Write a function to calculate the nth Fibonacci number efficiently.",
        "Implement quicksort algorithm for a list of integers.",
        "Check if a string is a palindrome, ignoring case and spaces."
    ]
    
    for i, prompt in enumerate(test_prompts, 1):
        print(f"\n=== テスト {i}: {prompt[:50]}... ===")
        
        generated_code, exec_time = generate_code(model, tokenizer, prompt)
        
        print(f"実行時間: {exec_time:.2f}秒")
        print(f"生成コード:\n{generated_code}")
        print("-" * 50)

if __name__ == "__main__":
    main()

※コードはClaude Codeに書いてもらっています。

スクリプトの実行

スクリプトを main.py という名前で保存し、以下のコマンドを実行してください。

# スクリプトの実行
uv run python main.py

まとめ

GPUで動かしてみたいなぁ。現場からは以上です。