3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kimi K2.5ってなんだ?〜1兆パラメータMoEモデルをシングルGPUで動かす完全実践ガイド〜

3
Posted at

この記事の対象読者

  • LLM(大規模言語モデル)に興味があり、ローカルで動かしてみたい方
  • llama.cppを使ったことがある、またはこれから使ってみたい方
  • GPU(CUDA)を使った推論環境の構築に関心がある方
  • 「1兆パラメータって本当にシングルGPUで動くの?」という疑問を持っている方

この記事で得られること

  • Kimi K2.5の全体像: MoEアーキテクチャの仕組みと、なぜ1兆パラメータでも動かせるのかの理解
  • 完全な環境構築手順: Windows + llama.cpp + CUDAでKimi K2.5を動かすまでのステップバイステップガイド
  • 3つの壁と突破法: 筆者が実際にハマったトラブルとその解決策(GPU認識不可、メモリ不足、GPU使用率0%問題)
  • 環境別の設定テンプレート: 開発・本番・ベンチマーク用の設定ファイル一式

この記事で扱わないこと

  • マルチGPU(NVLink)構成での実行
  • Linux / macOS 環境でのセットアップ
  • モデルのファインチューニングや学習
  • vLLM / SGLang 等の他の推論エンジンでの実行

1. Kimi K2.5との出会い

「96GBのVRAMがあれば、何でも動かせるだろう。」

RTX PRO 6000 Blackwell Workstation Editionを手に入れたとき、私はそう思っていました。VRAM 96GB。一般的なGPU(RTX 4090で24GB)の4倍です。これだけあれば、どんなモデルだって余裕で動くに決まっている。

そんな自信満々の私の前に現れたのが、Kimi K2.5でした。

「1兆パラメータ」という数字を見たとき、正直なところ実感が湧きませんでした。しかし、最も小さい量子化版でも223GBと知ったとき、96GBのVRAMが急に小さく見えました。引っ越しに例えるなら、「大きなトラックを借りたのに、荷物が3台分あった」ような状態です。

結論から言うと、動きはしましたが、期待通りにはいきませんでした。GPU使用率はほぼ0%。96GBのVRAMは「計算装置」ではなく「高速メモリ」として使われていたのです。

この記事では、その過程で遭遇した3つの壁と、そこから学んだことを共有します。失敗談も含めて赤裸々に書きますので、同じ沼にハマる方が一人でも減れば幸いです。

ここまでで、この記事がどんな内容か、なんとなくイメージできたでしょうか。次は、この記事で登場する用語を整理しておきましょう。


2. 前提知識の確認

本題に入る前に、この記事で頻繁に登場する用語を確認します。すでにご存知の方は読み飛ばしてください。

2.1 MoE(Mixture of Experts)とは

MoEは「Mixture of Experts(混合エキスパート)」の略です。

通常のLLMは、入力されたテキストに対して全てのパラメータを使って計算します。一方、MoEモデルは複数の「エキスパート(専門家)」を持ち、入力に応じて一部のエキスパートだけを選んで計算します。

料理で例えるなら、普通のLLMは「全員が全品担当するキッチン」です。MoEは「和食担当・洋食担当・デザート担当が分かれた分業制キッチン」。注文に応じて適切な担当者だけが動きます。結果として、パラメータの総数は巨大でも、実際に動くパラメータは少なく済むのです。

Kimi K2.5の場合、1兆パラメータのうち実際に動くのは320億(32B)パラメータだけです。

2.2 GGUF(GPT-Generated Unified Format)とは

GGUFは、llama.cppが使用するモデルファイルフォーマットです。

元のモデルファイル(PyTorchの.safetensorsなど)は非常に大きく、特定のフレームワークに依存します。GGUFはそれを単一のファイルにまとめ、CPUでもGPUでも効率よく読み込める形式に変換したものです。

2.3 llama.cppとは

llama.cppは、C/C++で書かれた軽量なLLM推論エンジンです。

Metaが公開したLLaMAモデルを動かすために開発されましたが、現在ではLLaMA以外のモデル(Kimi K2.5を含む)も幅広くサポートしています。最大の特徴は、PythonやPyTorchなしで動作すること。実行ファイル1つとモデルファイルがあれば推論できます。

2.4 量子化(Quantization)とは

量子化は、モデルのパラメータをより少ないビット数で表現する技術です。

通常、パラメータは16ビット(FP16)や32ビット(FP32)の浮動小数点数で保存されます。これを4ビットや2ビット、さらには1ビットまで圧縮するのが量子化です。ファイルサイズは劇的に縮小しますが、精度は多少犠牲になります。

量子化レベル 1パラメータあたり 特徴
FP16(非量子化) 16ビット 最高精度だがサイズ大
Q4_K_M 約4.5ビット バランス型、最も一般的
IQ2_XXS 約2ビット かなり小さいが精度低下あり
IQ1_S / TQ1_0 約1ビット 極限の圧縮、大幅な精度低下

Kimi K2.5は1兆パラメータあるため、最も強い量子化(TQ1_0)でも223GBになります。

これらの用語が押さえられたら、Kimi K2.5の背景を見ていきましょう。


3. Kimi K2.5が生まれた背景

3.1 Moonshot AI(月之暗面)について

Kimi K2.5を開発したのは、中国・北京に拠点を置く**Moonshot AI(月之暗面)**です。

2023年3月、清華大学の研究者であるYang Zhilin氏らによって設立されました。社名はPink Floydのアルバム「The Dark Side of the Moon」に由来しています。設立からわずか3年で、評価額は**43億ドル(約6,500億円)**に到達。中国AI業界の「AIタイガー」と呼ばれる企業の一角を占めています。

3.2 K2からK2.5への進化

Kimi K2.5の系譜を簡単に整理します。

モデル リリース 特徴
Kimi K1.5 2025年1月 OpenAI o1に匹敵する推論性能
Kimi K2 2025年7月 1兆パラメータMoE、テキスト特化
Kimi K2 Thinking 2025年11月 推論特化版、200-300回の連続ツール呼び出し
Kimi K2.5 2026年1月 ネイティブマルチモーダル(視覚+言語)対応

K2.5の最大の進化点は、MoonViTと呼ばれる4億パラメータの視覚エンコーダが追加されたことです。画像や動画を「後付け」ではなく「生まれつき」理解できるモデルになりました。約15兆トークンの視覚+テキスト混合データで継続事前学習されています。

3.3 なぜ1兆パラメータなのか

「1兆パラメータ」と聞くと圧倒されますが、MoEアーキテクチャのおかげで実態はもう少しスリムです。

Kimi K2.5のMoE構造を表にまとめます。

項目
総パラメータ数 1兆(1T)
推論時のアクティブパラメータ 320億(32B)
レイヤー数 61(うちDenseレイヤー1)
エキスパート数 384
1トークンあたりの選択エキスパート数 8
Attention Hidden Dimension 7,168
コンテキスト長 256K トークン

384人のエキスパートから8人だけ選ぶ。つまり、推論時は**全体の約2%**しか使わない計算です。これがMoEの効率の良さです。

ベンチマークでは、GPT-5.2やClaude 4.5 Opusと同等以上のスコアを複数の評価項目で記録しています。オープンソース(Modified MITライセンス)でこの性能は注目に値します。

背景がわかったところで、基本的な仕組みを見ていきましょう。


4. 基本概念と仕組み

4.1 MoEアーキテクチャの動作原理

Kimi K2.5が推論リクエストを受け取ったときの処理の流れを整理します。

入力テキスト
    ↓
トークン化(テキストを数値に変換)
    ↓
各レイヤーで処理(61レイヤー)
    ↓ 各レイヤー内で...
    ├── Attention(全体の文脈を把握)
    └── MoEブロック
         ├── ルーター: 384エキスパートから8つ選択
         └── 選ばれた8エキスパートだけ計算
    ↓
出力トークンを生成

ポイントは「ルーター」の存在です。各トークンに対して、ルーターが「このトークンにはどのエキスパートが最適か」を判断し、8つを選びます。残りの376エキスパートは何もしません。これが計算コストを抑える仕組みです。

4.2 量子化によるサイズ削減

Kimi K2.5の各量子化版のサイズを比較します。

量子化 サイズ FP16比 精度への影響
FP16(非量子化) 約2TB 100% なし
IQ2_XXS 327GB 約16% 中程度
IQ1_S 276GB 約14% 大きい
TQ1_0 223GB 約11% かなり大きい

TQ1_0は元の約1/9のサイズですが、それでも223GBあります。96GBのVRAMに収まらないため、GPU/CPUハイブリッド実行が必要になります。

4.3 GPU/CPUハイブリッド実行の仕組み

llama.cppには -ngl(Number of GPU Layers)というパラメータがあります。これは「モデルの何レイヤーをGPUに配置するか」を指定します。

-ngl の仕組み(Kimi K2.5、61レイヤーの場合)

-ngl 61: 全レイヤーをGPUに配置(VRAM不足でエラー)
-ngl 26: 26レイヤーをGPU、35レイヤーをCPUに配置
-ngl  0: 全レイヤーをCPUに配置(GPUを使わない)

高速道路(GPU)と一般道(CPU)を走り分けるようなものです。高速道路に乗れる区間が多いほど速くなります。しかし、高速道路の長さ(VRAMサイズ)には限りがあります。

推奨nglの計算式:

推奨ngl = (VRAM容量 ÷ モデルサイズ) × レイヤー数

例: 96GB VRAM、223GBモデル、61レイヤー
ngl = (96 / 223) × 61 ≈ 26

ただし、推論時にはKVキャッシュ等の追加メモリも必要なため、実際には計算値より少し余裕を持たせます。

基本概念が理解できたところで、実際にコードを書いて動かしてみましょう。


5. 実践:実際に動かしてみよう

5.1 環境構築

今回の検証環境は以下の通りです。

項目 スペック
OS Windows 11 Pro
GPU NVIDIA RTX PRO 6000 Blackwell Workstation Edition
VRAM 96GB
RAM 128GB DDR5
CPU Intel Core(Alder Lake)
ストレージ NVMe SSD(空き500GB以上推奨)
CUDA 13.0(ドライバー対応)
llama.cpp b7898

Step 1: ディレクトリ作成

New-Item -ItemType Directory -Path "C:\Kimi" -Force
New-Item -ItemType Directory -Path "C:\Kimi\llama-cpp" -Force
New-Item -ItemType Directory -Path "C:\Kimi\models" -Force
New-Item -ItemType Directory -Path "C:\Kimi\configs" -Force

Step 2: llama.cppのダウンロード

ここが最初の落とし穴でした。llama.cppの本体とCUDA DLLsは別パッケージです。

# 本体のダウンロード
$llamaUrl = "https://github.com/ggml-org/llama.cpp/releases/download/b7898/llama-b7898-bin-win-cuda-13.1-x64.zip"
Invoke-WebRequest -Uri $llamaUrl -OutFile "C:\Kimi\llama-cpp.zip"
Expand-Archive -Path "C:\Kimi\llama-cpp.zip" -DestinationPath "C:\Kimi\llama-cpp" -Force

# CUDA DLLs(これを忘れるとGPUが認識されない!)
$cudaUrl = "https://github.com/ggml-org/llama.cpp/releases/download/b7898/cudart-llama-bin-win-cuda-13.1-x64.zip"
Invoke-WebRequest -Uri $cudaUrl -OutFile "C:\Kimi\cudart.zip"
Expand-Archive -Path "C:\Kimi\cudart.zip" -DestinationPath "C:\Kimi\cudart-temp" -Force
Copy-Item "C:\Kimi\cudart-temp\*.dll" -Destination "C:\Kimi\llama-cpp\" -Force

私はここでハマりました。 本体だけダウンロードして「GPUが認識されない!」と30分悩みました。公式リリースページをよく見ると、CUDA DLLsは別のzipファイルとして提供されています。ドキュメントにもっと目立つように書いてほしい…。

Step 3: GPU認識の確認

& "C:\Kimi\llama-cpp\llama-cli.exe" --list-devices

正常な場合の出力:

ggml_cuda_init: found 1 CUDA devices:
  Device 0: NVIDIA RTX PRO 6000 Blackwell Workstation Edition, compute capability 12.0, VMM: yes
load_backend: loaded CUDA backend from C:\Kimi\llama-cpp\ggml-cuda.dll
Available devices:
  CUDA0: NVIDIA RTX PRO 6000 Blackwell Workstation Edition (97886 MiB, 95288 MiB free)

CUDAデバイスが表示されない場合は、5.5節のトラブルシューティングを参照してください。

Step 4: モデルのダウンロード

Hugging Face CLIを使ってGGUF版をダウンロードします。TQ1_0(223GB)が最小の量子化版です。

"""
Kimi K2.5 モデルダウンローダー
実行方法: python download_model.py
所要時間: 回線速度により2-8時間程度
"""
from huggingface_hub import hf_hub_download

REPO_ID = "unsloth/Kimi-K2.5-GGUF"
LOCAL_DIR = r"C:\Kimi\models\UD-TQ1_0"

FILES = [
    f"UD-TQ1_0/Kimi-K2.5-UD-TQ1_0-0000{i}-of-00005.gguf"
    for i in range(1, 6)
]

def main():
    for f in FILES:
        print(f"Downloading {f}...")
        hf_hub_download(
            repo_id=REPO_ID,
            filename=f,
            local_dir=LOCAL_DIR,
        )
    print("Download complete!")

if __name__ == "__main__":
    main()

事前に pip install huggingface_hub が必要です。

5.2 環境別の設定ファイル

用途に応じて3種類の設定ファイルを用意しました。そのままコピーして使えます。

開発環境用(config_dev.yaml)

デバッグや動作確認に最適化した設定です。GPUレイヤーを少なくして起動を速くしています。

# config_dev.yaml - 開発・動作確認用
# 最小限のリソースで素早く起動し、動作確認するための設定
server:
  host: "127.0.0.1"  # ローカルのみ
  port: 8080
model:
  path: "C:\\Kimi\\models\\UD-TQ1_0\\UD-TQ1_0\\Kimi-K2.5-UD-TQ1_0-00001-of-00005.gguf"
  ngl: 10              # GPUレイヤー少なめ(起動が速い)
  context_size: 2048    # コンテキスト小さめ(メモリ節約)
  threads: 8            # CPUスレッド控えめ
  batch_size: 512
logging:
  level: "DEBUG"

本番環境用(config_prod.yaml)

VRAMとCPUリソースを最大限に活用する設定です。

# config_prod.yaml - 本番・最大性能用
# GPU/CPUリソースを最大活用し、最高のスループットを目指す設定
server:
  host: "0.0.0.0"      # 外部アクセス許可
  port: 8080
model:
  path: "C:\\Kimi\\models\\UD-TQ1_0\\UD-TQ1_0\\Kimi-K2.5-UD-TQ1_0-00001-of-00005.gguf"
  ngl: 26              # 96GB VRAMの最大活用(計算式: 96/223*61≈26)
  context_size: 8192    # 大きめのコンテキスト
  threads: 16           # CPUスレッドも全開
  batch_size: 2048
logging:
  level: "INFO"

ベンチマーク環境用(config_bench.yaml)

性能測定に特化した設定です。変数を統制するため、最小限の構成にしています。

# config_bench.yaml - ベンチマーク・性能測定用
# 再現性のある測定のために、パラメータを固定した設定
server:
  host: "127.0.0.1"
  port: 8081            # 開発環境と衝突しないポート
model:
  path: "C:\\Kimi\\models\\UD-TQ1_0\\UD-TQ1_0\\Kimi-K2.5-UD-TQ1_0-00001-of-00005.gguf"
  ngl: 26              # 本番と同じGPUレイヤー
  context_size: 4096    # 測定用の固定値
  threads: 16
  batch_size: 512
benchmark:
  warmup_requests: 3    # ウォームアップ回数
  test_requests: 10     # 測定回数
  prompt: "Explain the concept of mixture of experts in neural networks."
logging:
  level: "WARNING"      # ログを最小限にして計測への影響を抑える

設定ファイルを読み込むランチャースクリプト

"""
Kimi K2.5 サーバーランチャー
実行方法: python launcher.py [dev|prod|bench]
"""
import yaml
import subprocess
import sys
import os

LLAMA_SERVER = r"C:\Kimi\llama-cpp\llama-server.exe"
CONFIG_DIR = r"C:\Kimi\configs"

def load_config(env: str) -> dict:
    """指定された環境の設定ファイルを読み込む"""
    config_path = os.path.join(CONFIG_DIR, f"config_{env}.yaml")
    if not os.path.exists(config_path):
        print(f"Error: {config_path} が見つかりません")
        sys.exit(1)
    with open(config_path, encoding="utf-8") as f:
        return yaml.safe_load(f)

def build_command(config: dict) -> list[str]:
    """設定からllama-serverの起動コマンドを組み立てる"""
    model = config["model"]
    server = config["server"]
    return [
        LLAMA_SERVER,
        "-m", model["path"],
        "-ngl", str(model["ngl"]),
        "-c", str(model["context_size"]),
        "-t", str(model["threads"]),
        "--host", server["host"],
        "--port", str(server["port"]),
    ]

def main():
    env = sys.argv[1] if len(sys.argv) > 1 else "dev"
    if env not in ("dev", "prod", "bench"):
        print("Usage: python launcher.py [dev|prod|bench]")
        sys.exit(1)

    config = load_config(env)
    cmd = build_command(config)

    print(f"Starting Kimi K2.5 server ({env} mode)...")
    print(f"  GPU Layers: {config['model']['ngl']}")
    print(f"  Context: {config['model']['context_size']}")
    print(f"  Address: {config['server']['host']}:{config['server']['port']}")
    print(f"  Command: {' '.join(cmd)}")
    print("-" * 60)

    subprocess.run(cmd)

if __name__ == "__main__":
    main()

5.3 基本的な使い方

サーバーの起動(設定ファイルなしの場合)

$model = "C:\Kimi\models\UD-TQ1_0\UD-TQ1_0\Kimi-K2.5-UD-TQ1_0-00001-of-00005.gguf"

& "C:\Kimi\llama-cpp\llama-server.exe" `
    -m $model `
    -ngl 26 `
    -c 4096 `
    -t 16 `
    --host 0.0.0.0 `
    --port 8080

ランチャー経由の起動

python launcher.py prod

APIの呼び出し(PowerShell)

$body = @{
    prompt = "Hello! Please introduce yourself."
    n_predict = 100
    temperature = 0.7
} | ConvertTo-Json

$response = Invoke-RestMethod `
    -Uri "http://localhost:8080/completion" `
    -Method Post `
    -Body $body `
    -ContentType "application/json"

$response.content

APIの呼び出し(Python)

"""
Kimi K2.5 APIクライアント
実行方法: python client.py
前提条件: llama-serverが起動済みであること
"""
import requests

BASE_URL = "http://localhost:8080"

def chat(prompt: str, max_tokens: int = 200) -> str:
    """llama-serverにリクエストを送信し、レスポンスを返す"""
    response = requests.post(
        f"{BASE_URL}/completion",
        json={
            "prompt": prompt,
            "n_predict": max_tokens,
            "temperature": 0.7,
        },
    )
    response.raise_for_status()
    return response.json()["content"]

def main():
    answer = chat("What is the Mixture of Experts architecture?")
    print(answer)

if __name__ == "__main__":
    main()

5.4 実行結果

サーバー起動時のログ(正常時):

load_tensors: offloading output layer to GPU
load_tensors: offloading 25 repeating layers to GPU
load_tensors: offloaded 26/62 layers to GPU
load_tensors:   CPU_Mapped model buffer size = 170950.23 MiB
load_tensors:        CUDA0 model buffer size = 92176.02 MiB

26レイヤーがGPUに、残りがCPUに配置されています。

推論のパフォーマンス:

項目 結果
動作 成功
推論速度 約1-2 tokens/sec
GPU VRAM使用 94-96GB(ほぼフル)
GPU計算使用率 0-6%(主にCPU処理)

正直な感想: 動いたことは動きましたが、1-2 tokens/secは「待つ」レベルです。100トークンの生成に1分近くかかります。リアルタイムチャットには厳しい速度でした。

5.5 よくあるエラーと対処法

私が遭遇した3つの壁を含め、起こりうるエラーをまとめました。

# エラーメッセージ 原因 対処法
1 no devices with dedicated memory found CUDA DLLs未配置 cudart-llama-bin-win-cuda-*.zip を別途ダウンロードし、llama-cppフォルダに展開する
2 cudaMalloc failed: out of memory -ngl が大きすぎてVRAM不足 -ngl の値を下げる。計算式: (VRAM GB / モデルサイズ GB) × レイヤー数
3 GPU-Util 0% だが VRAM は使用中 モデルがVRAMに収まらず、計算の大部分がCPUで実行 より小さい量子化版を使うか、-ngl を上げてGPU比率を増やす
4 ModuleNotFoundError: No module named 'huggingface_hub' パッケージ未インストール pip install huggingface_hub を実行
5 サーバーが 0.0.0.0:8080 で応答しない ファイアウォールでブロック Windows Defenderファイアウォールで8080番ポートを許可する
6 failed to load model + ファイルパスエラー 分割GGUFの1つ目以外を指定 -m には必ず 00001-of-00005.gguf を指定する(残りは自動読み込み)

第1の壁(GPU認識不可)の詳細

これが最もハマりやすいポイントです。llama.cppの公式リリースページには2つのzipがあります。

ファイル名 内容
llama-b7898-bin-win-cuda-13.1-x64.zip llama.cpp本体 + ggml-cuda.dll
cudart-llama-bin-win-cuda-13.1-x64.zip CUDA Runtime DLLs(cudart64_13.dll, cublas64_13.dll, cublasLt64_13.dll

両方をダウンロードし、同じフォルダに配置する必要があります。以下のDLLが揃っているか確認してください。

# 必要なDLLの存在確認
Get-ChildItem "C:\Kimi\llama-cpp" | Where-Object {
    $_.Name -match "cuda|cublas"
} | Select-Object Name

期待される出力:

ggml-cuda.dll
cudart64_13.dll
cublas64_13.dll
cublasLt64_13.dll

第3の壁(GPU使用率0%問題)の詳細

nvidia-smiで確認すると、VRAMは96GB使用済みなのに、GPU-Utilはほぼ0%という不思議な状態でした。

+-----------------------------------------------------------------------------------------+
| GPU  Name                  | Memory-Usage | GPU-Util |
|=========================================+============+==========|
|   0  NVIDIA RTX PRO 6000 Blac...        |  96418MiB / 97887MiB |      1% |
+-----------------------------------------------------------------------------------------+

サーバーログの graph splits = 797 が原因を示していました。計算グラフが797回に分割され、GPU/CPU間でデータが行ったり来たりしている状態です。

根本原因: モデル(223GB)がVRAM(96GB)より大きいため、GPUは主に「高速メモリ」として使われ、計算の大部分はCPUが担当していました。これはハードウェアの問題ではなく、純粋なメモリ制約です。

5.6 環境診断スクリプト

問題が発生した場合は、以下のスクリプトで環境を一括診断できます。

#!/usr/bin/env python3
"""
Kimi K2.5 環境診断スクリプト
実行方法: python check_env.py
"""
import os
import sys
import shutil
import subprocess


def check_python():
    """Pythonバージョンを確認"""
    ver = sys.version_info
    ok = ver >= (3, 9)
    status = "OK" if ok else "NG"
    print(f"  [{status}] Python: {sys.version}")
    if not ok:
        print("       -> Python 3.9以上が必要です")
    return ok


def check_packages():
    """必須パッケージの確認"""
    required = ["huggingface_hub", "requests", "yaml"]
    pkg_map = {"yaml": "pyyaml"}  # importとpip名が異なるもの
    all_ok = True
    for pkg in required:
        try:
            __import__(pkg)
            print(f"  [OK] {pkg}: インストール済み")
        except ImportError:
            pip_name = pkg_map.get(pkg, pkg)
            print(f"  [NG] {pkg}: 未インストール -> pip install {pip_name}")
            all_ok = False
    return all_ok


def check_nvidia_smi():
    """nvidia-smiの実行確認"""
    try:
        result = subprocess.run(
            ["nvidia-smi", "--query-gpu=name,memory.total,memory.free",
             "--format=csv,noheader"],
            capture_output=True, text=True, timeout=10,
        )
        if result.returncode == 0:
            for line in result.stdout.strip().split("\n"):
                print(f"  [OK] GPU: {line.strip()}")
            return True
        else:
            print(f"  [NG] nvidia-smi エラー: {result.stderr.strip()}")
            return False
    except FileNotFoundError:
        print("  [NG] nvidia-smi が見つかりません(NVIDIAドライバ未インストール?)")
        return False


def check_llama_cpp():
    """llama.cppバイナリの確認"""
    llama_dir = r"C:\Kimi\llama-cpp"
    server_path = os.path.join(llama_dir, "llama-server.exe")

    if not os.path.exists(server_path):
        print(f"  [NG] llama-server.exe が見つかりません: {server_path}")
        return False
    print(f"  [OK] llama-server.exe: 存在確認済み")

    # CUDA DLLsの確認
    cuda_dlls = ["ggml-cuda.dll", "cudart64_13.dll",
                 "cublas64_13.dll", "cublasLt64_13.dll"]
    all_found = True
    for dll in cuda_dlls:
        path = os.path.join(llama_dir, dll)
        if os.path.exists(path):
            print(f"  [OK] {dll}: 存在確認済み")
        else:
            print(f"  [NG] {dll}: 見つかりません!")
            all_found = False

    if not all_found:
        print("       -> cudart-llama-bin-win-cuda-*.zip を別途ダウンロードしてください")
    return all_found


def check_disk_space():
    """ディスク空き容量の確認"""
    model_dir = r"C:\Kimi\models"
    if os.path.exists(model_dir):
        total, used, free = shutil.disk_usage(model_dir)
        free_gb = free / (1024 ** 3)
        ok = free_gb >= 250
        status = "OK" if ok else "NG"
        print(f"  [{status}] ディスク空き: {free_gb:.1f} GB")
        if not ok:
            print("       -> TQ1_0モデル(223GB)のダウンロードには250GB以上推奨")
        return ok
    else:
        print(f"  [--] {model_dir} が存在しません")
        return False


def check_model_files():
    """モデルファイルの存在確認"""
    model_dir = r"C:\Kimi\models\UD-TQ1_0\UD-TQ1_0"
    if not os.path.exists(model_dir):
        print(f"  [--] モデルディレクトリ未作成: {model_dir}")
        return False

    expected = [
        f"Kimi-K2.5-UD-TQ1_0-0000{i}-of-00005.gguf"
        for i in range(1, 6)
    ]
    all_found = True
    total_size = 0
    for fname in expected:
        path = os.path.join(model_dir, fname)
        if os.path.exists(path):
            size_gb = os.path.getsize(path) / (1024 ** 3)
            total_size += size_gb
            print(f"  [OK] {fname}: {size_gb:.1f} GB")
        else:
            print(f"  [NG] {fname}: 見つかりません")
            all_found = False

    if all_found:
        print(f"       合計: {total_size:.1f} GB")
    return all_found


def main():
    print("=" * 60)
    print("Kimi K2.5 環境診断")
    print("=" * 60)

    sections = [
        ("1. Python環境", check_python),
        ("2. 必須パッケージ", check_packages),
        ("3. NVIDIA GPU", check_nvidia_smi),
        ("4. llama.cpp + CUDA DLLs", check_llama_cpp),
        ("5. ディスク空き容量", check_disk_space),
        ("6. モデルファイル", check_model_files),
    ]

    results = []
    for title, check_fn in sections:
        print(f"\n{title}")
        print("-" * 40)
        results.append(check_fn())

    print("\n" + "=" * 60)
    if all(results):
        print("診断結果: すべてOKです!サーバーを起動できます。")
    else:
        ng_count = sum(1 for r in results if not r)
        print(f"診断結果: {ng_count}件の問題が見つかりました。上記の指示に従って修正してください。")
    print("=" * 60)


if __name__ == "__main__":
    main()

実装方法がわかったので、次は具体的なユースケースを見ていきます。


6. ユースケース別ガイド

6.1 ユースケース1: 対話型チャット(Web UI経由)

想定読者: ブラウザからKimi K2.5と対話してみたい方

推奨構成: llama-serverの組み込みWeb UIを使用

llama-serverを起動すると、http://localhost:8080 にWeb UIが自動で提供されます。追加のインストールは不要です。

起動コマンド:

$model = "C:\Kimi\models\UD-TQ1_0\UD-TQ1_0\Kimi-K2.5-UD-TQ1_0-00001-of-00005.gguf"

& "C:\Kimi\llama-cpp\llama-server.exe" `
    -m $model `
    -ngl 26 `
    -c 4096 `
    -t 16 `
    --host 127.0.0.1 `
    --port 8080

ブラウザで http://localhost:8080 を開くと、チャットインターフェースが表示されます。推論速度が1-2 tokens/secのため、長文の回答には時間がかかる点に注意してください。

6.2 ユースケース2: PythonからのAPI呼び出し

想定読者: 自分のアプリケーションにKimi K2.5を組み込みたい方

推奨構成: llama-server + requests ライブラリ

サンプルコード:

"""
Kimi K2.5 バッチ推論クライアント
実行方法: python batch_inference.py
前提条件: llama-serverが起動済みであること
"""
import requests
import json
import time

BASE_URL = "http://localhost:8080"


def complete(prompt: str, max_tokens: int = 200,
             temperature: float = 0.7) -> dict:
    """単一のプロンプトを推論する"""
    response = requests.post(
        f"{BASE_URL}/completion",
        json={
            "prompt": prompt,
            "n_predict": max_tokens,
            "temperature": temperature,
        },
        timeout=300,  # 推論が遅いため長めに設定
    )
    response.raise_for_status()
    return response.json()


def batch_inference(prompts: list[str]) -> list[str]:
    """複数のプロンプトを順次推論する"""
    results = []
    for i, prompt in enumerate(prompts, 1):
        print(f"Processing {i}/{len(prompts)}: {prompt[:50]}...")
        start = time.time()
        result = complete(prompt)
        elapsed = time.time() - start
        content = result.get("content", "")
        tokens = result.get("tokens_predicted", 0)
        speed = tokens / elapsed if elapsed > 0 else 0
        print(f"  -> {tokens} tokens in {elapsed:.1f}s ({speed:.1f} t/s)")
        results.append(content)
    return results


def main():
    prompts = [
        "Explain quantum computing in simple terms.",
        "What is the difference between TCP and UDP?",
        "Write a Python function to calculate fibonacci numbers.",
    ]
    results = batch_inference(prompts)

    for prompt, result in zip(prompts, results):
        print(f"\n{'='*60}")
        print(f"Q: {prompt}")
        print(f"A: {result[:200]}...")


if __name__ == "__main__":
    main()

6.3 ユースケース3: OpenAI互換APIとして利用

想定読者: 既存のOpenAI APIベースのコードを流用したい方

推奨構成: llama-server + openai ライブラリ

llama-serverはOpenAI互換のAPIエンドポイントを提供しています。既存のコードの base_url を変更するだけで利用できます。

サンプルコード:

"""
Kimi K2.5 OpenAI互換クライアント
実行方法: python openai_compat.py
前提条件: pip install openai, llama-serverが起動済みであること
"""
from openai import OpenAI

# llama-serverをOpenAI互換エンドポイントとして使用
client = OpenAI(
    base_url="http://localhost:8080/v1",
    api_key="not-needed",  # ローカルサーバーでは認証不要
)


def chat(user_message: str) -> str:
    """OpenAI互換APIでチャットする"""
    response = client.chat.completions.create(
        model="kimi-k2.5",  # ローカルでは任意の文字列でOK
        messages=[
            {
                "role": "system",
                "content": "You are Kimi, an AI assistant. Answer concisely.",
            },
            {"role": "user", "content": user_message},
        ],
        max_tokens=200,
        temperature=0.7,
    )
    return response.choices[0].message.content


def main():
    questions = [
        "What is MoE in the context of LLMs?",
        "How does quantization affect model quality?",
    ]
    for q in questions:
        print(f"Q: {q}")
        print(f"A: {chat(q)}")
        print()


if __name__ == "__main__":
    main()

これにより、LangChainやLlamaIndex等のフレームワークからもそのまま利用できます。

ユースケースを把握できたところで、この先の学習パスを確認しましょう。


7. 学習ロードマップ

この記事を読んだ後、次のステップとして以下をおすすめします。

初級者向け(まずはここから)

  1. 小さいモデルで練習する: いきなり1兆パラメータは荷が重いです。まずは7Bや13Bのモデル(例: Llama 3.1 8B GGUF)でllama.cppの使い方に慣れましょう
  2. llama.cppの公式ドキュメントを読む: GitHub README にパラメータの詳細が記載されています
  3. 量子化の違いを体感する: 同じモデルのQ4_K_MとQ2_Kを比較して、品質と速度のトレードオフを実感してみましょう

中級者向け(実践に進む)

  1. nglパラメータの最適化: 自分のGPUに合わせてnglを1ずつ変えながら、速度とメモリ使用量の最適点を探る
  2. llama.cppのサーバーオプションを深掘り: --ctx-size, --batch-size, --parallel 等のチューニング
  3. OpenAI互換APIの活用: LangChainやLlamaIndex経由でRAGパイプラインを構築する

上級者向け(さらに深く)

  1. マルチGPU構成に挑戦: 2枚以上のGPUでKimi K2.5のGPU比率を上げる
  2. vLLM / SGLangでの実行: 公式デプロイメントガイド にvLLMとSGLangの設定例があります
  3. Kimi K2.5のAgent Swarm機能を試す: Kimi Code CLI で最大100のサブエージェントによる並列実行を体験する

8. まとめ

この記事では、Kimi K2.5について以下を解説しました。

  1. Kimi K2.5とは何か: Moonshot AI(月之暗面)が開発した1兆パラメータのMoEモデル。384エキスパートから8つを選択して推論する効率的なアーキテクチャ
  2. シングルGPUでの動かし方: Windows + llama.cpp + CUDA DLLsの環境構築から、環境別設定ファイル、API呼び出しまでの完全手順
  3. 3つの壁と教訓: GPU認識不可→CUDA DLLs別途配置、メモリ不足→ngl調整、GPU使用率0%→メモリ制約の理解

私の所感

96GBのVRAMは「巨大」ですが、1兆パラメータモデルの前では足りませんでした。

今回の検証で最も印象的だったのは、GPUが「計算装置」ではなく「高速メモリ」として使われていたことです。96GBのVRAMはほぼ満杯なのに、GPU使用率は0%。高性能なスポーツカーのエンジンではなく、トランクの広さだけが活用されている状態です。

しかし、ネガティブなことばかりではありません。この検証で以下が確認できました。

  • Blackwell (sm_120) はllama.cpp b7898 + CUDA 13.1で動作する: 新アーキテクチャの互換性が確認できた
  • GPU/CPUハイブリッドで超巨大モデルも「動く」: 速くはないが、動くことには価値がある
  • 1兆パラメータの民主化が始まっている: API経由ではなくローカルで動かせる時代が近づいている

私の見解では、シングルGPUでの実用的な速度にはGPUメモリの進化(次世代の192GB+VRAMなど)が必要です。あるいは、より効率的な量子化技術で50-80GB級まで圧縮できれば、世界が変わるでしょう。

1兆パラメータの壁は高いですが、登れない壁ではありません。次回は複数GPU構成か、より小さいモデルでの検証に挑みたいと思います。


参考文献

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?