「K2-Horizon-32B-NVFP4」長文・推論対応モデル:当サイトでの回答例・必要VRAM 32GB〜

2026年11月8日

「K2-Horizon-32B-NVFP4」長文・推論対応モデル:当サイトでの回答例・必要VRAM 32GB〜

基本情報

項目 内容
リポジトリ IFM/K2-Horizon-32B-NVFP4
まとめページ K2-Horizon のまとめ(記事3本)
公開元のまとめ IFM(MBZUAI) のモデルとライセンスのまとめ
公開日 2026-09-22
ライセンス apache-2.0
配布形式 safetensors
出典の種類 公開元の一次情報(公式HFリポジトリとGitHubリンクが存在し一次情報)

情報を集めた時点で、当サイトのコードが確定させた値です。日付はJSTで表示しています。

当サイトで確かめたこと

  • 配布元のコード(transformers 5.18.0・NVFP4・compressed-tensors・版 71b0789349)を Modal のクラウドの GPU(NVIDIA A100-SXM4-80GB・ピーク時の VRAM 71.3GB)で動かし、回答を原文のまま載せています。
  • Qwen3 のトークナイザより約18%少ないトークンで同じ日本語を表せます。同じ文脈長に入る日本語が約1.22倍になり、生成も1字あたりで速くなります。

詳しい値と条件は、下の「当サイトでの実測」の節にあります。

概要

IFMは、オープンウェイトの大規模言語モデル「K2-Horizon-32B」のStage1チェックポイントをNVFP4形式に量子化したモデル「IFM/K2-Horizon-32B-NVFP4」を公開しました。

本モデルは、32BのDense(高密度)構成で512K(524,288トークン)の長大なコンテキスト長を備えたdecoder-onlyモデルです。lm_headを除くすべての線形層で重みと活性化(Activations)の両方がNVFP4に量子化されており、ネイティブなNVFP4サポートを持つNVIDIA Blackwell世代(Bシリーズ)以降のGPU向けに最適化されています。

スペック

  • パラメータ数: 32B(総パラメータ 32B / 活性パラメータ 32B)
  • アーキテクチャ: Dense(decoder-only)
  • コンテキスト長: 512K(524,288トークン)

性能

公開元が公開したモデルカードには、オープンウェイトのDenseモデル群との比較表、およびBF16版とNVFP4版の量子化比較表が掲載されています。

まず、同規模帯のオープンウェイトDenseモデルとの比較結果は以下の通りです。

→ 横にスクロールできます

K2-Horizon-32B-Stage1 Open-weight dense models / Qwen3.8-27B Open-weight dense models / Muse Glimmer-30B Open-weight dense models / IBM Granite 4.2 30B
# Params 32B 27B 30B 30B
# Activated params 32B 27B 30B 30B
Architecture Dense Dense Dense Dense
Agents
tau3-Banking Agentic tool use 22.5 48.0 23.5 14.4
Coding
Terminal-Bench 2.1 Agentic terminal use 36.6 79.8 51.7 26.6
SciCode Scientific coding 30.2 44.7 43.6 36.6
Scientific Reasoning
Humanity’s Last Exam (without tools) Expert-level reasoning 22.8 33.9 22.0 11.2
GPQA Diamond Graduate-level science QA 82.3 90.5 83.5 64.4
CritPt Frontier physics reasoning 1.4 5.4 2.6 0.3
General
AA-LCR Long-context reasoning 65.3 77.3 80.0 46.7
AA-Omniscience Accuracy Factual accuracy 16.8 15.6 27.0 10.1
AA-Omniscience Non-Hallucination Non-hallucination rate 58.3 69.7 18.1 74.4

この表から、K2-Horizon-32B-Stage1は専門家レベルの超難問を集めたHumanity’s Last Exam(22.8%)や博士課程レベルの科学問題を問うGPQA Diamond(82.3%)においてMuse Glimmer-30Bと同等の高水準な推論能力を示し、IBM Granite 4.2 30Bを上回っていることが分かります。一方で、ターミナル操作の完遂度を測るTerminal-Bench 2.1(36.6%)やtau3-Banking(22.5%)、SciCode(30.2%)といったエージェント機能やコーディング分野の指標ではQwen3.8-27B(79.8%、48.0%、44.7%)に及ばず、明確な差をつけられています。

続いて、元モデルのBF16版と本NVFP4版の直接比較データです(コンテキスト長65,536トークン、0-shot評価)。

→ 横にスクロールできます

K2-Horizon-32B-Stage1 IFEval (Prompt) GSM8K MBPP MMLU-Pro GPQA-Diamond BBH (3-shot) AIME 26 (avg @ 32) Average
BF16 86.69 96.21 94.40 81.52 81.76 93.20 92.60 89.5
NVFP4 85.40 96.59 93.20 80.47 78.82 93.30 91.15 88.4

公開元の測定によると、平均スコアはBF16版の89.5%からNVFP4版の88.4%へとわずか1.1ポイントの低下にとどまっています。算数の文章題を解くGSM8K(96.59%)や推論タスクのBBH(93.30%)ではBF16と同等以上の数値を維持している一方、GPQA-Diamond(81.76% → 78.82%)、数学オリンピック予選レベルを問うAIME 26(92.60% → 91.15%)、Python課題を解くMBPP(94.40% → 93.20%)、指示追従性を測るIFEval(86.69% → 85.40%)などの難関分野では僅かなスコア低下が見られます。なお、現時点でNVFP4版の評価は非エージェントタスクに限定されており、エージェントタスクの結果は後日公開予定とされています。

得意なこと・想定用途

「IFM/K2-Horizon-32B-NVFP4」は、32Bという扱いやすい規模でありながら、極めて長大なコンテキスト処理能力と高度な推論能力、そして柔軟なツール利用(Tool Use)機能を兼ね備えており、以下のような用途で特に強みを発揮します。

長大なコンテキスト処理(512Kトークン対応)

本モデルは、中間トレーニング(midtraining)ステージ以降で524,288トークン(512K)のネイティブなコンテキストウィンドウに対応しています。これにより、学術論文の束や大規模なソースコードベース、長大な契約書といった膨大なドキュメントを丸ごと入力し、その内容に基づいた高度な分析、要約、Q&Aを行うことが可能です。

思考プロセスを伴う高度な推論

本モデルは、思考プロセス(Reasoning)を出力する機能を備えています。API経由で利用する際、推奨設定である reasoning_effort="high" を指定することで、モデルは回答に至るまでの思考プロセスを reasoning_content に、最終的な回答を content に分けて出力します。これにより、数学や科学、複雑な論理パズルなどの難解なタスクにおいて、ステップバイステップで正確な思考を重ねた高品質な回答を得ることができます。

柔軟なツール利用(Tool Use)とエージェント連携

本モデルは、外部ツールとの連携において複数のフォーマットをサポートしています。具体的には、json、xml、xml_typed の3種類のツール呼び出しフォーマットに対応しており、デフォルトでは xml が使用されます。これらは chat_template_kwargs を通じてリクエストごとに切り替えることができ、システムの要件に合わせた柔軟なエージェント構築が可能です。

Blackwell世代GPUでの高速・省メモリ推論

本モデルの最大の特徴は、lm_head を除くすべての線形層の重み(Weights)と活性化(Activations)がNVFP4形式に量子化されている点です。これにより、NVIDIA Blackwell世代(Bシリーズ)以降のネイティブなNVFP4サポートを備えたGPUにおいて、メモリ使用量を大幅に削減しつつ、非常に高速な推論を実行することができます。BF16版と比較して性能低下はごくわずか(平均スコアで1.1ポイントの低下)に抑えられているため、Blackwell世代のハードウェアを所有する技術者にとって、極めて実用的な選択肢となります。

動作環境

動作環境の目安(Local Model Watch 算出)

手持ちのVRAM 選べる量子化 ファイルサイズ 必要メモリの目安
RTX 5090 など 32GB NVFP4 21.7GB 26.0GB

推論エンジンの対応状況(各プロジェクトのソースにあるモデル登録表とアーキテクチャ名を当サイトが機械的に照合。2026-11-08 時点): llama.cpp: 登録あり、vLLM: 登録あり、MLX (mlx-lm): 未登録。「未登録」はその時点の登録表に名前が無いという意味で、動かないことの断定ではありません。

必要メモリは配布ファイルの実サイズに実行時のオーバーヘッド(KVキャッシュ等)を20%見込んだ概算で、文脈長・バッチサイズ・推論エンジンによって増減します。公開元の公称値ではなく、当サイトがファイルサイズから機械的に算出した値です。 他のモデルとの比較はVRAM別の早見表を参照してください。 量子化の名前の読み方は用語集にあります。

手元で動かせるか

現時点では Ollama・LM Studio・llama.cpp で動かせません(GGUF版を確認できていない)。

公開元の配布は safetensors のみです。ただし llama.cpp の登録表にはこのモデルのアーキテクチャ名があるため、GGUFへの変換自体は可能な状態で、変換版が公開されれば動くようになります。現時点で動かすなら transformers(PyTorch)・vLLM で読み込む形になり、必要メモリは上の表のとおりです。

ライセンス apache-2.0(商用利用可): 商用利用を含めて自由に利用・改変・再配布できます。再配布時にライセンス文と変更点の告知が必要で、特許の許諾条項があります。

配布形式・当サイトが見つけた変換版・各推論エンジンの登録表を突き合わせた判定です。「確認できていない」は当サイトが見つけていないという意味で、存在しないことの断定ではありません。ライセンスの要約は法的助言ではないため、実際の利用にあたっては公開元の原文を確認してください。

当サイトでの実測

モデルカードの記載ではなく、当サイトが測った結果です。ファイルの中身は当サイトのサーバー(GPUなし)で読み込んで確かめ、このモデルはそのサーバーでは動かせないため、クラウドの GPU を借りて動かしました(条件は下記)。測り方と全モデルの一覧は観測データにまとめています。

日本語のトークン効率

トークナイザ 日本語1,000字あたりのトークン数 同じ内容の英文との比
このモデル 565 1.04倍
Qwen3 688 1.26倍
Llama 3.2 744 1.36倍
Gemma 3 564 1.03倍
gpt-oss 795 1.45倍
LLM-jp-3 497 0.85倍

Qwen3 のトークナイザより約18%少ないトークンで同じ日本語を表せます。同じ文脈長に入る日本語が約1.22倍になり、生成も1字あたりで速くなります。

IFM/K2-Horizon-32B-NVFP4 の tokenizer.json で、当サイトが書き下ろした固定の文章(876字。ニュース・会話・技術文書・敬語のメール・紀行文・料理の6種)と、同じ内容の英文を数えました。トークン数が少ないほど、同じ文脈長に長い日本語が入ります。

配布元のコードで動かした結果(クラウドのGPU)

このモデルのアーキテクチャ(K2HorizonForCausalLM)は llama.cpp の変換スクリプトの登録表に無く(2026-10-01時点)、GGUF 版も確認できていないため、配布元が Hugging Face で配っている Python のコード(transformers の trust_remote_code)で、配布されている重み(NVFP4・compressed-tensors)を読み込んで動かしました。配布元のコードは、ネットワークを遮断したクラウドのコンテナの中だけで実行しています。

読み込んだ直後、量子化された層は NVFP4 のまま(詰めた形)で保持されていました(読み込んだ後の重みを型ごとに数えた値: UINT8 約161億・BFLOAT16 約26億・FLOAT8_E4M3FN 約20億)。回答している間の VRAM は、これより大きくなりました(表)。

項目 結果
重みを読み込んだ直後のVRAM(上記の持ち方) 21.7GB
5問に答えている間の最大のVRAM 71.3GB
生成(5問の回答の中央値・transformers) 2.2 トークン/秒
生成を日本語の字数に換算 約3.8字/秒

条件: Modal の NVIDIA A100-SXM4-80GB(VRAM 79GB)・transformers 5.18.0・PyTorch 2.14.1+cu130・IFM/K2-Horizon-32B-NVFP4 の版 71b0789349・重みは NVFP4・compressed-tensors・計算は BF16・2026-10-02に測定しました。

NVIDIA A100-SXM4-80GB には NVFP4 の演算器が無いため、この速さと VRAM は、NVFP4 の計算に対応した GPU と推論エンジンで動かした場合の目安になりません。transformers での生成は専用の推論エンジンより大幅に遅いため、速さは参考値です。

日本語の質問への回答(原文のまま)

上の条件で、全モデルに同じ5問を日本語で尋ねた回答を、手を加えずに載せています(温度0・チャットテンプレートが対応していれば思考なし・最大1024トークン)。正誤は判定していません。答えが1つに決まる問いには、正解を併記します。

要約

次の記事を3文で要約してください。

みどり市は今年の春、市内に三つある公立図書館のうち最も古い中央図書館の改修を終え、十月一日に再開館した。改修は約一年半にわたり、総事業費は約十二億円。このうち約四億円は国の補助金でまかなわれた。

最大の変化は、閲覧席の配置である。以前は館内の座席の大半が学習用の机で、試験期間になると中高生で埋まり、高齢の利用者や小さな子ども連れが座れないという苦情が毎年寄せられていた。改修後は座席数を百二十席から二百席に増やしたうえで、学習用の机を三階にまとめ、一階は会話ができる「にぎわいフロア」、二階は静かに本を読む「閲覧フロア」に分けた。一階には予約制のグループ学習室が四室と、飲み物を持ち込めるカフェスペースも設けられた。

蔵書の面でも見直しが進んだ。貸出の少ない古い資料約三万冊を市内の他の館や保存書庫に移し、代わりに子ども向けの絵本と、介護・子育て・仕事探しといった暮らしに関わる実用書を約一万五千冊増やした。電子書籍の貸出サービスも始まり、利用登録をすれば自宅から一度に三冊まで、二週間借りられる。

建物そのものにも手が入った。築四十年を超えていた空調設備を入れ替え、屋上には太陽光パネルを設置して、館内で使う電力の約二割をまかなえるようにした。車いすの利用者から要望の多かったエレベーターは一基から二基に増え、書架の間の通路も以前より三十センチ広げられた。入口には段差のないスロープが新たに設けられ、授乳室と多目的トイレも各階に一つずつ置かれた。

一方で課題も残る。駐車場は改修前と同じ四十台のままで、週末には近くの道路に順番待ちの車が並んだ。市は隣接する市有地を臨時駐車場として開放したが、恒久的な対策は決まっていない。また、開館時間を平日の夜九時まで延ばしたことで職員の負担が増えており、職員団体は人員の追加を求めている。

利用者の受け止めはおおむね好意的だ。週に二度、孫を連れて来るという七十代の女性は「前は子どもが声を出すと周りの目が気になったが、一階なら気兼ねなく絵本を読んであげられる」と話す。一方、受験を控えた高校生の中には「学習席が三階だけになり、夕方はすぐに満席になる」と不満を漏らす人もいる。図書館は、学習席の空き状況をホームページで確認できる仕組みを年内に導入する予定だ。

再開館に合わせて、催しも増えた。毎週土曜の午前には読み聞かせ会、月に一度は地元の農家や商店主を講師に招く「まちの先生」講座が開かれる。十一月には、市内の中学生が選んだ本を紹介する展示も予定されている。

館長の山本和子さんは「本を借りる場所から、人が集まって過ごす場所へ変えたかった。再開館からの一か月で来館者は以前の一・八倍になった」と話す。市は来年度、残る二つの図書館についても利用者への聞き取りを行い、改修の必要性を検討する方針だ。

みどり市の中央図書館は約1年半の改修を経て再開し、座席を120席から200席に増やし、階層ごとの利用目的を分離するなど、利用者のニーズに応じた改善が図られた。蔵書は貸出の少ない資料を削減し、実用書や絵本を増やし、電子書籍貸出や太陽光発電など環境面でも手が入ったが、駐車場不足や職員負担といった課題は残っている。再開後の一か月で来館者は前年比1.8倍に増加し、図書館は「人が集まって過ごす場所」へと変貌しつつ、残る二つの図書館の改修も検討される。

敬語のメール

取引先の担当者に、来週火曜日の打ち合わせを水曜日に変更してほしいとお願いするメールを、丁寧な敬語で書いてください。

**件名:** 次回打ち合わせ日程の変更のお願い(来週火曜日の打ち合わせを水曜日に変更)

---

拝啓 平素より格別のご高配を賜り、誠にありがとうございます。

このたびは突然の変更で恐縮ではございますが、来週の打ち合わせが予定されております**火曜日(○月○日)**の件につきまして、**水曜日(○月○日)**に変更させていただきたく存じます。

つきましては、下記のとおりご都合のよいお時間にてご検討いただけますと幸いに存じます。

| 項目 | 内容 |
|------|------|
| 変更前 | 火曜日(○月○日) |
| 変更後 | 水曜日(○月○日) |
| 目的 | 打ち合わせ(議題は不变) |
| 場所・方法 | 従来通り(オンライン/会議室) |

ご多忙の折から恐れ入りますが、**水曜日の日程で問題がなければご確認のほど、よろしくお願い申し上げます**。  
もし水曜日でもご都合がよろしくない場合は、代替案をご提示いただけますと幸いです。

何卒ご理解のほど、よろしくお願い申し上げます。

敬具

---

**株式会社○○ ○○ 部 ○○ 様**  
担当:○○(○○)  
電話番号:○○-○○-○○  
メールアドレス:○○@○○.co.jp

文章題

りんごが1個120円、みかんが1個45円です。りんごを3個とみかんを8個買って、1,000円札で払いました。おつりはいくらですか。考え方も示してください。

(思考だけで上限に達し、回答は出ませんでした)

参考: 正解は280円です(120×3=360円、45×8=360円、1,000−720=280円)。

知識

日本の都道府県のうち、海に面していない県をすべて挙げてください。

(思考だけで上限に達し、回答は出ませんでした)

参考: 正解は8県です(栃木県・群馬県・埼玉県・山梨県・長野県・岐阜県・滋賀県・奈良県)。

指示どおりの創作

「春」をテーマにした俳句を3つ、五・七・五の音数を守って作ってください。それぞれに一言の説明を付けてください。

(思考だけで上限に達し、回答は出ませんでした)

環境構築・ダウンロード・実行の手順(当サイトで実行したもの)

当サイトでは、ダウンロードと実行を Modal の2つのコンテナに分けました。どちらも Debian(slim)・Python 3.12 のイメージです。ダウンロードのコンテナでは配布元のコードを実行せず、配布元のコードを実行するコンテナはネットワークを遮断しています。入れたパッケージは次のとおりです(版は指定していません。実際に入った版は下の一覧)。

# 1. ダウンロード用の実行環境を作る(GPU なし)
python -m pip install huggingface_hub

# 2. 実行用の実行環境を作る(GPU あり・ネットワークなし)
python -m pip install torch transformers accelerate safetensors sentencepiece tiktoken protobuf einops compressed-tensors

そのうえで、次のスクリプト(remote_code_remote.py、当サイトが書いたもの。全文)の download(job) をダウンロードのコンテナで、run(job) を実行のコンテナで呼びました。ファイルの置き場所は /models/<dir> です。

"""
Modal のコンテナの中で動く、配布元のコード(trust_remote_code)による実測(2026-09-30)。
lmw/lab/gpu_modal.py の download_weights / run_remote_code から呼ばれる。**標準ライブラリだけを先頭で読み**、
huggingface_hub・torch・transformers は各関数の中で読む(コンテナごとに入っているものが違う)。

【2つのコンテナに分ける】
- download: GPU の無いコンテナ・ネットワークあり。ファイルを落とすだけで、**配布元のコードは実行しない**
  (import もしない)。重みは safetensors だけ(呼び出し側が渡すファイル名の一覧に pickle は入らない)。
- run: GPU のコンテナ・**ネットワーク遮断**(block_network)・Modal の機能も使えない(restrict_modal_access)・
  Volume は読み取り専用。ここでだけ配布元のコードを実行する。外へ送ることも、ほかのコンテナを起動することもできない。

記事執筆用の LLM は呼ばない。回答の良し悪しも判定しない(原文を返すだけ)。
"""
import json
import os
import re
import platform
import time
from datetime import datetime, timezone

MOUNT = "/models"
# コンテナのイメージ(lmw/lab/gpu_modal.py が使い、記事の手順にも同じ値を載せる)
PYTHON_VERSION = "3.12"
DOWNLOAD_PACKAGES = ("huggingface_hub",)
# compressed-tensors は量子化済みの配布(NVFP4 等。2026-10-01、K2-Horizon-32B-NVFP4)を transformers が読むために入れる
RUN_PACKAGES = ("torch", "transformers", "accelerate", "safetensors", "sentencepiece", "tiktoken", "protobuf", "einops",
                "compressed-tensors")

def installed_packages() -> list[str]:
    """このコンテナに入っているパッケージの版(pip freeze と同じ形)。記事の手順に載せる。"""
    from importlib import metadata
    seen = {}
    for dist in metadata.distributions():
        name = dist.metadata["Name"]
        if name and name.lower() not in seen:
            seen[name.lower()] = f"{name}=={dist.version}"
    return sorted(seen.values(), key=str.lower)

def _log(t0: float, message: str) -> None:
    print(f"[rc {time.time() - t0:6.0f}s] {message}", flush=True)

def download(job: dict) -> dict:
    """job: {"repo", "revision", "files": [...], "dir"}。戻り値: {"bytes", "seconds", "packages"}。"""
    from huggingface_hub import hf_hub_download
    t0 = time.time()
    target = os.path.join(MOUNT, job["dir"])
    os.makedirs(target, exist_ok=True)
    total = 0
    for name in job["files"]:
        path = hf_hub_download(job["repo"], name, revision=job["revision"], local_dir=target)
        total += os.path.getsize(path)
    seconds = round(time.time() - t0)
    _log(t0, f"ダウンロード {total / 1024 ** 3:.1f}GB・{seconds}秒({total / 1024 ** 2 / max(seconds, 1):.0f}MB/秒)")
    return {"bytes": int(total), "seconds": int(seconds), "packages": installed_packages()}

def _chat_ids(tok, text: str):
    messages = [{"role": "user", "content": text}]
    try:
        out = tok.apply_chat_template(messages, add_generation_prompt=True, enable_thinking=False,
                                      return_tensors="pt")
    except TypeError:
        out = tok.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")
    # 版によっては dict(BatchEncoding)が返る
    return out["input_ids"] if hasattr(out, "keys") else out

# 思考の目印。<think> に限らない(K2-Horizon は <ifm|think>・<ifm|think_fast> と </ifm|think>。2026-09-30)
_THINK_OPEN_RE = re.compile(r"<(?!/)([^<>\s]*think[^<>\s]*)>")

def split_thinking(generated: str, prompt_text: str = "") -> tuple[str, str]:
    """
    生成した文字列(特別な記号を残したもの)を (回答, 思考) に分ける。llama-server の reasoning_content と同じ扱い。
    チャットテンプレートがプロンプトの末尾で思考の目印を開けている場合は、生成は思考の途中から始まる。
    閉じる目印は、開けた目印の名前から決める(<X> → </X>。ただし think_fast 等の変種は </…think> でも閉じる)。
    2026-09-30、特別な記号ごと消して文字にし、次に <think> だけを前提にしたため、K2-Horizon の英語の思考が
    2度「回答」として記録された。
    """
    text = generated
    opened = None
    tail = prompt_text.rstrip()
    for m in _THINK_OPEN_RE.finditer(tail):
        if m.end() == len(tail):
            opened = m
    if opened:
        text = opened.group(0) + text
    m = _THINK_OPEN_RE.search(text)
    if not m:
        return text, ""
    before, rest = text[:m.start()], text[m.end():]
    name = m.group(1)
    closers = [f"</{name}>"]
    if "|" in name or "_" in name:
        # 変種(think_fast)の閉じ方が本体(</ifm|think>)と同じテンプレートがある
        closers.append("</" + re.sub(r"(think)[^|<>]*$", r"\1", name) + ">")
    found = [(rest.find(c), c) for c in closers if c in rest]
    if found:
        at, closer = min(found)
        return before + rest[at + len(closer):], rest[:at]
    return before, rest

def run(job: dict) -> dict:
    """
    job: {"dir", "prompts": [{"id", "ja"}], "max_tokens"}。
    戻り値は記録の一部。チャットテンプレートが無ければ {"no_chat_template": True}。
    """
    import torch
    import transformers
    from transformers import AutoModelForCausalLM, AutoTokenizer
    os.environ["HF_HUB_OFFLINE"] = "1"
    os.environ["TRANSFORMERS_OFFLINE"] = "1"
    t0 = time.time()
    path = os.path.join(MOUNT, job["dir"])
    gpu = torch.cuda.get_device_name(0)
    vram_total = torch.cuda.get_device_properties(0).total_memory
    _log(t0, f"GPU: {gpu}・transformers {transformers.__version__}・torch {torch.__version__}")
    # 版は str にする(torch.__version__ は torch の型で、torch の無いホストで結果を読めない。2026-09-30、1本ぶん失った)
    record = {"gpu": gpu, "vram_total": int(vram_total), "transformers": str(transformers.__version__),
              "torch": str(torch.__version__), "dtype": "bfloat16", "packages": installed_packages(),
              "python": platform.python_version(),
              "measured_at": datetime.now(timezone.utc).isoformat()}
    # 量子化済みの配布なら、その形式を記録する(記事に「BF16 のまま」と書かない)
    with open(os.path.join(path, "config.json"), encoding="utf-8") as f:
        quant = json.load(f).get("quantization_config") or {}
    if quant:
        groups = list((quant.get("config_groups") or {}).values())
        record["quantization"] = {"quant_method": quant.get("quant_method"),
                                  "format": quant.get("format") or (groups[0].get("format") if groups else None),
                                  "weights_bits": ((groups[0].get("weights") or {}).get("num_bits") if groups else None)}
        _log(t0, f"量子化済みの配布: {record['quantization']}")
    tok = AutoTokenizer.from_pretrained(path, trust_remote_code=True, local_files_only=True)
    if not getattr(tok, "chat_template", None):
        _log(t0, "チャットテンプレートがありません")
        return plain({**record, "no_chat_template": True})
    model = AutoModelForCausalLM.from_pretrained(path, trust_remote_code=True, local_files_only=True,
                                                 use_safetensors=True, dtype=torch.bfloat16,
                                                 device_map="cuda")
    model.eval()
    torch.cuda.synchronize()
    # 読み込んだ後の重みの持ち方(型ごとの要素数と、重みを持つ末端の層のクラス)。量子化済みの配布が 4bit のまま
    # 読まれたのか、BF16 へ展開されたのかを、推測ではなく数えて記録する
    dtypes, classes = {}, {}
    for tensor in list(model.parameters()) + list(model.buffers()):
        name = str(tensor.dtype).replace("torch.", "")
        dtypes[name] = dtypes.get(name, 0) + int(tensor.numel())
    for module in model.modules():
        if not list(module.children()) and list(module.parameters(recurse=False)):
            cls = type(module).__name__
            classes[cls] = classes.get(cls, 0) + 1
    record["loaded_weights"] = {"dtypes": dtypes, "leaf_classes": classes}
    record["vram_loaded"] = int(torch.cuda.memory_allocated())
    record["load_sec"] = round(time.time() - t0)
    _log(t0, f"読み込み: VRAM {record['vram_loaded'] / 1024 ** 3:.1f}GB・{record['load_sec']}秒")
    torch.cuda.reset_peak_memory_stats()
    outputs = []
    # 打ち切り(Modal の timeout)の前に、答えた分だけで返す。打ち切られると答えた分も全て失う
    # 次の1問が最長(max_tokens)まで書いたときの時間を、ここまでで最も遅かった速さで見積もって判定する
    stop_at = t0 + job.get("answer_budget_sec", 10 ** 9)
    for prompt in job["prompts"]:
        slowest = min((o["gen_tps"] for o in outputs if o.get("gen_tps")), default=None)
        worst = job["max_tokens"] / slowest if slowest else 0
        if time.time() + worst > stop_at:
            _log(t0, f"時間の上限が近いため、残りの問い({prompt['id']} から)は尋ねません")
            record["unanswered"] = [p["id"] for p in job["prompts"][len(outputs):]]
            break
        ids = _chat_ids(tok, prompt["ja"]).to("cuda")
        started = time.time()
        with torch.no_grad():
            generated = model.generate(ids, max_new_tokens=job["max_tokens"], do_sample=False)
        torch.cuda.synchronize()
        seconds = time.time() - started
        new = generated[0][ids.shape[1]:]
        count = int(new.shape[0])
        # 特別な記号(<think> 等)を残して文字にし、思考と回答を分けてから、残りの特別な記号を消す
        prompt_text = tok.decode(ids[0][-8:], skip_special_tokens=False)
        answer, thinking = split_thinking(tok.decode(new, skip_special_tokens=False), prompt_text)
        for special in tok.all_special_tokens:
            answer = answer.replace(special, "")
        outputs.append({
            "id": prompt["id"],
            "content": answer.strip(),
            "reasoning_chars": len(thinking.strip()),
            "finish_reason": "length" if count >= job["max_tokens"] else "stop",
            "completion_tokens": count,
            "gen_tps": round(count / seconds, 2) if seconds > 0 else None,
            "prompt_tokens": int(ids.shape[1]),
        })
        _log(t0, f"回答 {prompt['id']}: {count}トークン・{outputs[-1]['gen_tps']} トークン/秒")
    record["vram_peak"] = int(torch.cuda.max_memory_allocated())
    record["outputs"] = outputs
    gen = sorted(o["gen_tps"] for o in outputs if o.get("gen_tps"))
    if gen:
        record["tg_tps"] = gen[len(gen) // 2]
    return plain(record)

def plain(value):
    """JSON に通して、標準の型だけにする(ホストには torch も transformers も無い)。"""
    return json.loads(json.dumps(value, default=str))

一部の回答の末尾に残っていたチャットテンプレートの終端の記号(<|ifm|im_end|>)は、記録の後に取り除いて載せています。上のスクリプトはこの記号を外しません。

download に渡した job(実際に使った値):

{
  "repo": "IFM/K2-Horizon-32B-NVFP4",
  "revision": "71b07893498c52a6508bfce3e3ccb5456ba75328",
  "files": [
    "chat_template.jinja",
    "config.json",
    "configuration_k2_horizon.py",
    "generation_config.json",
    "model-00001-of-00002.safetensors",
    "model-00002-of-00002.safetensors",
    "model.safetensors.index.json",
    "modeling_k2_horizon.py",
    "tokenizer.json",
    "tokenizer_config.json"
  ],
  "dir": "run-1790872724"
}

run に渡した job(実際に使った値):

{
  "dir": "run-1790872724",
  "max_tokens": 1024,
  "answer_budget_sec": 3420,
  "prompts": [
    {
      "id": "summary",
      "ja": "次の記事を3文で要約してください。\n\nみどり市は今年の春、市内に三つある公立図書館のうち最も古い中央図書館の改修を終え、十月一日に再開館した。改修は約一年半にわたり、総事業費は約十二億円。このうち約四億円は国の補助金でまかなわれた。\n\n最大の変化は、閲覧席の配置である。以前は館内の座席の大半が学習用の机で、試験期間になると中高生で埋まり、高齢の利用者や小さな子ども連れが座れないという苦情が毎年寄せられていた。改修後は座席数を百二十席から二百席に増やしたうえで、学習用の机を三階にまとめ、一階は会話ができる「にぎわいフロア」、二階は静かに本を読む「閲覧フロア」に分けた。一階には予約制のグループ学習室が四室と、飲み物を持ち込めるカフェスペースも設けられた。\n\n蔵書の面でも見直しが進んだ。貸出の少ない古い資料約三万冊を市内の他の館や保存書庫に移し、代わりに子ども向けの絵本と、介護・子育て・仕事探しといった暮らしに関わる実用書を約一万五千冊増やした。電子書籍の貸出サービスも始まり、利用登録をすれば自宅から一度に三冊まで、二週間借りられる。\n\n建物そのものにも手が入った。築四十年を超えていた空調設備を入れ替え、屋上には太陽光パネルを設置して、館内で使う電力の約二割をまかなえるようにした。車いすの利用者から要望の多かったエレベーターは一基から二基に増え、書架の間の通路も以前より三十センチ広げられた。入口には段差のないスロープが新たに設けられ、授乳室と多目的トイレも各階に一つずつ置かれた。\n\n一方で課題も残る。駐車場は改修前と同じ四十台のままで、週末には近くの道路に順番待ちの車が並んだ。市は隣接する市有地を臨時駐車場として開放したが、恒久的な対策は決まっていない。また、開館時間を平日の夜九時まで延ばしたことで職員の負担が増えており、職員団体は人員の追加を求めている。\n\n利用者の受け止めはおおむね好意的だ。週に二度、孫を連れて来るという七十代の女性は「前は子どもが声を出すと周りの目が気になったが、一階なら気兼ねなく絵本を読んであげられる」と話す。一方、受験を控えた高校生の中には「学習席が三階だけになり、夕方はすぐに満席になる」と不満を漏らす人もいる。図書館は、学習席の空き状況をホームページで確認できる仕組みを年内に導入する予定だ。\n\n再開館に合わせて、催しも増えた。毎週土曜の午前には読み聞かせ会、月に一度は地元の農家や商店主を講師に招く「まちの先生」講座が開かれる。十一月には、市内の中学生が選んだ本を紹介する展示も予定されている。\n\n館長の山本和子さんは「本を借りる場所から、人が集まって過ごす場所へ変えたかった。再開館からの一か月で来館者は以前の一・八倍になった」と話す。市は来年度、残る二つの図書館についても利用者への聞き取りを行い、改修の必要性を検討する方針だ。"
    },
    {
      "id": "keigo",
      "ja": "取引先の担当者に、来週火曜日の打ち合わせを水曜日に変更してほしいとお願いするメールを、丁寧な敬語で書いてください。"
    },
    {
      "id": "arithmetic",
      "ja": "りんごが1個120円、みかんが1個45円です。りんごを3個とみかんを8個買って、1,000円札で払いました。おつりはいくらですか。考え方も示してください。"
    },
    {
      "id": "prefectures",
      "ja": "日本の都道府県のうち、海に面していない県をすべて挙げてください。"
    },
    {
      "id": "haiku",
      "ja": "「春」をテーマにした俳句を3つ、五・七・五の音数を守って作ってください。それぞれに一言の説明を付けてください。"
    }
  ]
}
実行のコンテナに入っていたパッケージの版(全文)
accelerate==1.15.0 aiohappyeyeballs==2.6.1 aiohttp==3.12.7 aiosignal==1.3.2 annotated-doc==0.0.5 annotated-types==0.8.0 anyio==4.15.1 attrs==25.3.0 cbor2==5.7.0 certifi==2026.7.22 charset-normalizer==3.5.2 click==8.5.0 compressed-tensors==0.19.0 cuda-bindings==13.4.3 cuda-pathfinder==1.8.2 cuda-toolkit==13.0.3.0 einops==0.8.2 filelock==4.0.8 frozenlist==1.6.0 fsspec==2026.9.0 grpclib==0.4.8 h11==0.16.0 h2==4.2.0 hf-xet==1.6.0 hpack==4.1.0 httpcore==1.0.9 httpx==0.28.1 huggingface_hub==1.33.0 hyperframe==6.1.0 idna==3.20 Jinja2==3.1.6 loguru==0.7.3 markdown-it-py==4.2.0 MarkupSafe==3.0.3 mdurl==0.1.2 mpmath==1.3.0 multidict==6.4.4 networkx==3.7 numpy==2.5.3 nvidia-cublas==13.1.1.3 nvidia-cuda-cupti==13.0.85 nvidia-cuda-nvrtc==13.0.88 nvidia-cuda-runtime==13.0.96 nvidia-cudnn-cu13==9.24.0.43 nvidia-cufft==12.0.0.61 nvidia-cufile==1.15.1.6 nvidia-curand==10.4.0.35 nvidia-cusolver==12.0.4.66 nvidia-cusparse==12.6.3.3 nvidia-cusparselt-cu13==0.8.1 nvidia-nccl-cu13==2.30.7 nvidia-nvjitlink==13.4.92 nvidia-nvshmem-cu13==3.4.5 nvidia-nvtx==13.0.85 packaging==26.3 pip==25.1.1 propcache==0.3.1 protobuf==7.36.2 psutil==7.2.2 pydantic==2.13.5 pydantic_core==2.46.5 Pygments==2.21.0 PyYAML==6.0.3 regex==2026.9.29 requests==2.34.2 rich==15.0.0 safetensors==0.8.0 sentencepiece==0.2.2 setuptools==84.0.0 shellingham==1.5.4 sympy==1.14.0 tiktoken==0.14.0 tokenizers==0.23.2 torch==2.14.1 tqdm==4.70.1 transformers==5.18.0 triton==3.8.0 typer==0.27.2 typing-inspection==0.4.4 typing_extensions==4.16.0 urllib3==2.8.0 uv==0.7.19 wheel==0.45.1 yarl==1.20.0
ダウンロードのコンテナに入っていたパッケージの版(全文)
aiohappyeyeballs==2.6.1 aiohttp==3.12.7 aiosignal==1.3.2 anyio==4.15.1 attrs==25.3.0 cbor2==5.7.0 certifi==2025.4.26 click==8.5.0 filelock==4.0.7 frozenlist==1.6.0 fsspec==2026.9.0 grpclib==0.4.8 h11==0.16.0 h2==4.2.0 hf-xet==1.6.0 hpack==4.1.0 httpcore2==2.13.1 httpx2==2.13.1 huggingface_hub==2.0.0 hyperframe==6.1.0 idna==3.20 multidict==6.4.4 packaging==26.3 pip==25.1.1 propcache==0.3.1 protobuf==6.31.1 PyYAML==6.0.3 tqdm==4.70.1 truststore==0.10.4 typing_extensions==4.16.0 uv==0.7.19 wheel==0.45.1 yarl==1.20.0

入手方法

本モデルは、Hugging Faceのリポジトリから自由にダウンロードして利用することができます。ライセンスは「Apache-2.0」が適用されており、利用にあたって事前の同意が必要なゲート付き(Gated)モデルではないため、誰でも即座に入手可能です。

配布形式は safetensors となっており、サービングエンジンとして vLLM や SGLang、および Hugging Faceの Transformers ライブラリに対応しています。

以下に、公開元が提示している各エンジンでの利用方法およびコード例を示します。なお、以下のコード例はBF16版(IFM/K2-Horizon-32B)を基準に書かれていますが、NVFP4版を動かす際も同様の設定やパーサーの指定が推奨されます。

vLLMでのサービング

vLLMを使用してサーバーを起動する場合のコマンド例です。チャット用の k2_horizon 推論パーサーおよびエージェント用のツールコールパーサーを有効にすることが推奨されています。

vllm serve IFM/K2-Horizon-32B \
  --revision main \
  --model-impl vllm \
  --tensor-parallel-size 2 \
  --trust-remote-code \
  --dtype bfloat16 \
  --max-model-len 131072 \
  --reasoning-parser k2_horizon \
  --enable-auto-tool-choice \
  --tool-call-parser k2_horizon

SGLangでのサービング

SGLangを使用する場合のコマンド例です。こちらは 2× H200 環境で検証されたレシピに基づいています。

python3 -m sglang.launch_server \
  --model-path IFM/K2-Horizon-32B \
  --revision main \
  --tp 2 \
  --dtype bfloat16 \
  --attention-backend fa3 \
  --reasoning-parser k2_horizon \
  --tool-call-parser k2_horizon \
  --host 0.0.0.0 --port 30000

APIの利用例(Python)

サービング起動後、OpenAI互換APIを介して思考プロセス付きの回答を取得するPythonコード例です。推奨設定として temperature=1.0、top_p=0.95、reasoning_effort="high" を指定しています。

from openai import OpenAI

client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")
response = client.chat.completions.create(
    model="IFM/K2-Horizon-32B",
    messages=[{"role": "user", "content": "Explain the result step by step."}],
    temperature=1.0,
    top_p=0.95,
    max_tokens=32768,
    extra_body={"chat_template_kwargs": {"reasoning_effort": "high", "tool_call_format": "xml"}},
)
message = response.choices[0].message
print("Reasoning:", getattr(message, "reasoning_content", None))
print("Answer:", message.content)

Transformersでの利用例

Transformers(バージョン 5.15.0、PyTorch 2.13.0、Safetensors 0.8.0 で検証済み)を使用して直接モデルをロードし、テキストを生成するコード例です。

from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "IFM/K2-Horizon-32B"
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    model_id, device_map="auto", dtype="bfloat16", low_cpu_mem_usage=True, trust_remote_code=True
)

inputs = tokenizer("Explain why long-context evaluation is difficult.", return_tensors="pt").to(model.device)
inputs.pop("token_type_ids", None)
outputs = model.generate(**inputs, max_new_tokens=32768, temperature=1.0, top_p=0.95, do_sample=True)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

関連記事

次に読むなら

出典

更新履歴

  • 2026-09-24: 記事の組み立てを更新しました: 「手元で動かせるか」の判定(配布形式・変換版・推論エンジンの登録表・ライセンス・実測の圧縮率)を追加
  • 2026-09-25: タイトルを、記事で分かる内容(必要VRAM・配布ファイル等)を示す形に変更しました。
  • 2026-09-28: 当サイトでの実測(日本語のトークン効率)を追加しました。
  • 2026-10-02: 当サイトでの実測(配布元のコードで動かしたVRAMと日本語の質問への回答)を追加しました。
  • 2026-10-02: タイトルを、記事で分かる内容(必要VRAM・配布ファイル等)を示す形に変更しました。