gpt-oss-20b 紹介・検証:OpenAIが公開した新しいOSS LLMの実力と導入方法
2025年8月、OpenAIは長らくクローズドだった戦略を大きく転換し、約210億パラメータ規模の大規模言語モデル 「gpt-oss-20b」 をオープンモデルとして公開しました。
Mixture-of-Experts構造やMXFP4量子化といった最新技術を採用し、高性能と軽量性を両立した本モデルは、研究用途から商用サービス、さらにはエッジデバイスへの組み込みまで幅広い応用が可能です。
本記事では、この新モデルの技術仕様や導入方法、実際のベンチマーク結果を交え、そのポテンシャルと課題を徹底的に検証します。
皆様のLLM活用・OSS LLM理解の一助になれば幸いです。
目次
はじめに
こんにちは。クラウドソリューショングループのwatanabe.tです。
2025年8月、OpenAIが新たにオープンモデルとして公開した大規模言語モデル「gpt-oss-20b」は、これまでクローズドだった同社のLLM戦略に大きな変化をもたらしました。
本記事では、モデルの概要や技術仕様、実際の導入方法、そして性能評価を通して、その実力を検証していきます。
モデル概要・技術仕様
gpt-oss-20bは、OpenAIが2025年8月にApache 2.0ライセンスのもとで公開したオープンソースの大規模言語モデル(LLM)です。OpenAIがオープンウェイトのモデルを提供するのはGPT-2以来であり、その戦略的な転換は業界に大きな影響を与えました。
本章では、以下のOpenAI公式ページを参考に、詳細を解説していこうと思います。
OpenAI が提供するオープンモデル | OpenAI
このモデルは「Mixture-of-Experts(MoE)」構造を採用しており、全体のパラメータ数は約210億(21B)ですが、推論時にアクティブになるのは約36億(3.6B)のパラメータに抑えられています。
これにより、モデルサイズは大規模でありながら実行時の計算負荷を抑え、高速な応答と低メモリ消費を両立していることが大きな特徴です。
さらに、MXFP4量子化により16GBクラスのGPUでも動作可能で、最大128Kトークンの長文処理にも対応しています。
NVIDIAのBlackwellなどの新世代GPUがMXFP4処理に最適化されたことで、FP8と比べて推論速度が最大5倍向上するようになり、
量子化の利点である高速な応答と低メモリ消費の恩恵を受けつつ、弱点であった精度の劣化を低減することが可能になりました。
今回、OpenAIはオープンモデルとして gpt-oss-120b と gpt-oss-20b を公開しましたが、
本記事で取り上げている gpt-oss-20b は標準でMXFP4精度でトレーニングされているため、
高いメモリ効率を維持しつつ、これまで提供されていたOpenAI o3やGPT-4o-miniなどにも劣らない性能を誇るとうたっています。
さらに、オープンモデル化されたことで、商用・非商用問わず自由にモデルを利用・改変できる点も大きな利点です。
研究用途はもちろん、プロダクト組み込みやエッジデバイスでの活用など、これまでクローズドモデルでは制限があった領域でも展開が可能になりました。
総じて、 gpt-oss-20b は「高性能かつ軽量」という二律背反を両立させたモデルであり、長文処理・低リソース環境・OSS活用という三つの観点からも非常に注目されています。
導入・実行方法
続けて、次の章で実施するベンチマークのための導入・実行環境構築について紹介していきます。
ライトに試す分には、公式ページで紹介されているようにOllama、LM Studioなどを利用することが可能ですが、
ベンチマークではJMMLUでスコア計測するため、HuggingFaceからダウンロードしたうえでPythonでローカル実行できるようにしていきます。

Ollamaで実行してみた結果
なお、MXFP4は前述の通りBlackwell以降でサポートされた形式のため、RTX4000シリーズやA100などの古いGPUではMXFP4を利用できず、
16bit量子化の状態でロードされてしまうためより多くのVRAMが必要となります。
そのため、今回の検証はAWS EC2の g6.xlarge インスタンスを利用して行いました。
(NVIDIA L4 Tensor Core GPUを搭載しているので安心ですね)
さて、さっそく実際に gpt-oss-20b を動かしていきましょう。
MXFP4形式に対応させるためには、最新版の transformers, triton_kernels をインストールしなければならないのですが、本検証時点ではPyPIにパッケージが公開されていないため、GitHubから直接インストールします。
(パッケージマネージャにuvを利用していますが、pipでも必要なものは同じです)
uv add git+https://github.com/huggingface/transformers "git+https://github.com/triton-lang/triton@v3.4.0#subdirectory=python/triton_kernels" "triton>=3.4.0" kernels
JMMLUは以前の拙記事(オンプレLLM導入の現実解:GPUゼロでも動く日本語LLMをJMMLUで評価)でも利用しましたが、
gpt-oss-20b の出力が想定通りにならない部分(後述します)があり、一部改修して以下のようにしています。
#!/usr/bin/env python3
"""
main.py
LLMU 形式 (question, A, B, C, D, answer) の CSV を読み込み、
Hugging Face Transformers の pipeline でゼロショット回答を行い精度を計測する。
CSV 必須列: question, A, B, C, D, answer
"""
from __future__ import annotations
import argparse
import re
import time
import logging
from pathlib import Path
from typing import List
import pandas as pd
from tqdm import tqdm
from transformers.pipelines import pipeline
from transformers.pipelines.base import Pipeline
from transformers.models.auto.tokenization_auto import AutoTokenizer
from transformers.models.auto.modeling_auto import AutoModelForCausalLM
# ---------- Prompt ----------
PROMPT_TEMPLATE = """You are an expert exam-taker.
Read the multiple-choice question and reply **ONLY** with the SINGLE letter
(A, B, C, or D) that correctly completes it.
Question:
{question}
A. {A}
B. {B}
C. {C}
D. {D}
Answer:"""
SYSTEM_PROMPT = "Reasoning: high"
# -----------------------------
LETTER_RE = re.compile(r"assistantfinal([A-D])")
def extract_letter(text: str) -> str:
"""gpt-oss は出力の最後に assistantfinal と出力され、その後に回答の文字が続くので、
その文字を抽出する。大文字の A, B, C, D"""
m = LETTER_RE.search(text)
return m.group(1) if m else ""
def build_pipeline(
model_name: str,
device: str | int | None,
max_new_tokens: int,
**model_kwargs,
) -> Pipeline:
"""
HuggingFace からモデルとトークナイザをロードして text-generation pipeline を生成
device: "cpu", "cuda", "mps", or int (GPU ID). None なら auto
model_kwargs: torch_dtype, trust_remote_code など自由に渡せる
"""
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=True)
return pipeline(
"text-generation",
model=model_name,
torch_dtype="auto",
device_map=device,
eos_token_id=tokenizer.eos_token_id,
return_full_text=False
)
def batch_iter(lst: List[str], n: int):
"""n 個ずつのバッチイテレータ"""
for i in range(0, len(lst), n):
yield lst[i : i + n]
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("csv_path", help="問題 CSV パス")
parser.add_argument(
"--model",
required=True,
help="Hugging Face Model ID or local path (例: meta-llama/Meta-Llama-3-8B-Instruct)",
)
parser.add_argument(
"--device",
default=None,
help='"cpu", "cuda", "mps" もしくは GPU ID。未指定なら自動',
)
parser.add_argument("--batch-size", type=int, default=4, help="推論バッチサイズ")
parser.add_argument("--max-new-tokens", type=int, default=1, help="生成トークン数上限")
parser.add_argument(
"--trust-remote-code",
action="store_true",
help="モデルのカスタムコードを信頼する (必要な場合のみ)",
)
args = parser.parse_args()
logging.basicConfig(
filename='app.log', # 出力先ファイル
level=logging.INFO, # 出力レベル
format='%(asctime)s [%(levelname)s] %(message)s' # フォーマット
)
# CSV 読み込み & 検証
df = pd.read_csv(args.csv_path)
required_cols = {"question", "A", "B", "C", "D", "answer"}
if not required_cols.issubset(df.columns):
raise ValueError(f"CSV must contain columns: {required_cols}")
# モデルロード
print("Loading model …")
pipe = build_pipeline(
model_name=args.model,
device=args.device,
max_new_tokens=args.max_new_tokens,
trust_remote_code=args.trust_remote_code
)
prompts = [
[
{
"role": "system",
"content": SYSTEM_PROMPT
},
{
"role": "user",
"content": PROMPT_TEMPLATE.format(
question=row["question"],
A=row["A"],
B=row["B"],
C=row["C"],
D=row["D"],
)
}
]
for _, row in df.iterrows()
]
predictions, durations = [], []
print("Running inference …")
for batch in tqdm(batch_iter(prompts, args.batch_size), total=(len(prompts) + args.batch_size - 1) // args.batch_size):
t0 = time.perf_counter()
outputs = pipe(batch, max_new_tokens=args.max_new_tokens)
durations.extend([time.perf_counter() - t0] * len(batch))
if outputs:
for output in outputs:
# pipeline の戻り値は list[dict] or str depending on version
logging.info(f"Model output: {output}")
content = str(output[0]["generated_text"] if isinstance(output, list) else output)
predictions.append(extract_letter(content))
# 結果集計
df["prediction"] = predictions
df["correct"] = df["prediction"].str.upper() == df["answer"].str.upper()
df["seconds"] = durations
accuracy = df["correct"].mean()
print(f"\nAccuracy: {accuracy:.2%} ({df['correct'].sum()}/{len(df)})")
print(f"Avg latency: {df['seconds'].mean():.2f} s / question (batched)")
model_name = args.model.split("/")[-1] # 最後の部分をモデル名とする
out_path = Path("results") / f"{Path(args.csv_path).stem}_results-{model_name}"
df.to_csv(out_path.with_suffix(".csv"), index=False)
print(f"Detailed results saved to {out_path}.csv")
if __name__ == "__main__":
main()
ベンチマーク実施結果
公式ベンチマークでは、数学・推論・医療・コードなど複数の領域で、o3-miniに近い性能を発揮したとありますが、結果は以下の通りでした。
スコア | 平均回答時間(s) | |
コンピュータセキュリティ | 61.68% | 16.66 |
日本の熟語 | 61.33% | 12.31 |
日本の地理 | 72.66% | 15.01 |
日本の公民 | 74.67% | 9.86 |
サマリーだけ見ると、思ったよりスコアは伸びていません。大きくこの原因は2つあると考えています。
1つ目は、日本語の入出力に対応したInstruction Tuningがされる前のモデルであり、日本語での入力に対して英語で考え、英語で回答していること。
2つ目は、回答を考える過程も出力されており、それでトークンを消費してトークン上限まで達していることです。
これら2つが原因で、日本語についてより長く考えることが必要になり、問題の回答にたどり着く前に出力が終了している設問が多くみられました。
そのため、出力が中途半端に終了している問題を除いた場合は、かなり高いスコアとなっていました。
(検討過程を出力させないオプションなどあるのかもしれませんが、今回の検証では把握することができませんでした)
ちなみに、今回は回答の正確性を上げるため推論レベルを「High」にしています。
(コード内ではシステムプロンプトで与えている部分)
ここが原因の可能性もあるため、引き続き検証が必要ですね。
おわりに
gpt-oss-20b は、OpenAIがOSSとして提供する新しい選択肢であり、軽量かつ高性能な推論が可能です。
導入のしやすさやライセンスの自由度は魅力的ですが、精度や安全性の面での検証は引き続き必要です。
本記事を参考に、ぜひ皆さんも実際に試してみてください。
また、株式会社アイソルートでは完全閉域網でLLMを動作させるサービスを提供しています。
社外秘情報を扱う高性能・高セキュリティLLMなど、ご興味がありましたら是非一度お問い合わせください。