知識蒸留とは?大規模AIモデルを軽量化するKnowledge Distillation

高性能な大規模モデルの知識を小さなモデルに引き継ぐ「知識蒸留」は、AIの軽量化・低コスト化に欠かせない技術です。教師モデルと生徒モデルの関係から実用例までを整理します。

知識蒸留:大規模AIモデルの知識を小型モデルに凝縮する技術

AI初心者

AIを知りたい

先生、スマートフォンでAIモデルを動かしたいんですが、GPT-4のような巨大モデルは無理ですよね。小さくても賢いモデルを作る方法はありますか?

AIエンジニア

AIエンジニア

あるよ。その代表的な手法が「知識蒸留(Knowledge Distillation)」だ。大きくて高精度な「教師モデル(Teacher)」の知識を、小さくて軽量な「生徒モデル(Student)」に移し替える技術なんだ。教師モデルの出力する確率分布(ソフトラベル)を生徒モデルが模倣するように学習することで、単にラベルから学ぶよりも豊かな情報を吸収できるんだよ。

AI初心者

AIを知りたい

「ソフトラベル」と普通のラベルは何が違うんですか?

AIエンジニア

AIエンジニア

例えば猫の画像を分類する場合、通常のラベル(ハードラベル)は「猫=1、犬=0、鳥=0」のように正解が1つだけだよね。でも教師モデルのソフトラベルは「猫=0.85、犬=0.12、鳥=0.03」のように、各クラスの確率分布を出力する。この確率分布には「猫と犬は似ている」という教師モデルが学んだ暗黙の知識が含まれているんだ。生徒モデルはこのソフトラベルから、正解ラベルだけでは得られないクラス間の関係性を学ぶことができるよ。

知識蒸留とは。

知識蒸留(Knowledge Distillation)は、大規模で高精度な教師モデル(Teacher Model)の出力確率分布を「ソフトターゲット」として活用し、小型の生徒モデル(Student Model)を効率的に訓練するモデル圧縮手法です。2015年にHintonらが論文「Distilling the Knowledge in a Neural Network」で体系化しました。教師モデルのソフトマックス出力を温度パラメータ(Temperature)で軟化させ、クラス間の類似性情報を含む確率分布を生徒モデルに学習させることで、ハードラベルのみの学習より高い精度を実現します。2026年現在、LLM分野ではGPT-4やClaude Opusなどの巨大モデルを教師として、Phi-3やGemma 2などの小型モデルを蒸留する手法が注目されています。DistilBERTはBERTの知識蒸留により、パラメータ数を40%削減しつつ97%の精度を維持した代表的な成功例です。エッジデバイスへのAI展開、推論コスト削減、レスポンス高速化など、実用的な観点から非常に重要な技術です。

知識蒸留とその他のモデル圧縮手法の比較

AIモデルを軽量化する手法は知識蒸留だけではなく、用途に応じて使い分けや組み合わせが重要です。

手法 概要 圧縮率 精度維持 実装の難易度 代表例
知識蒸留 教師モデルの知識を生徒モデルに転移 2〜10倍 高い(95〜99%) 中程度 DistilBERT, TinyBERT
量子化(Quantization) 重みの精度をfp32→int8/int4に低減 2〜8倍 高い(96〜99%) 低い GPTQ, AWQ, bitsandbytes
枝刈り(Pruning) 重要度の低い重みやニューロンを削除 2〜10倍 中〜高(90〜98%) 中程度 SparseGPT, Wanda
低ランク分解 重み行列を低ランク行列の積に分解 1.5〜5倍 中〜高 高い SVD分解, TensorTrain
アーキテクチャ設計 軽量なモデル構造を設計 任意 設計次第 高い MobileNet, EfficientNet

AI初心者

AIを知りたい

LLMの世界でも知識蒸留は使われているんですか?

AIエンジニア

AIエンジニア

もちろんだよ。2026年のLLM分野では知識蒸留が非常に重要な役割を果たしている。MicrosoftのPhi-3やGoogleのGemma 2は、大規模モデルの出力データを用いて小型モデルを訓練する「蒸留ベースの学習」を活用しているとされているよ。OpenAIのGPT-4oミニも大型モデルの知見を小型化した例だ。ただし、他社のモデル出力を蒸留に使うのは利用規約で禁止されている場合が多いから注意が必要だよ。

知識蒸留の温度パラメータと実装のポイント

知識蒸留を実装する際に理解すべき重要なパラメータと設定を解説します。

パラメータ 説明 推奨値 影響
Temperature(T) ソフトマックスの温度。高いほど出力が軟化 2〜20(一般的に4〜8) 高Tでクラス間関係の情報が増えるが、ノイズも増加
alpha(α) 蒸留損失とハードラベル損失の重み比率 0.5〜0.9 高αで蒸留損失を重視、低αでハードラベルを重視
生徒モデルサイズ 教師モデルに対する生徒のパラメータ比 教師の10〜50% 小さすぎると教師の知識を吸収しきれない
学習率 生徒モデルの学習率 1e-4〜5e-4 通常の学習よりやや低めが安定

AI初心者

AIを知りたい

知識蒸留を実際に試してみたいのですが、何から始めればいいですか?

AIエンジニア

AIエンジニア

まずは画像分類で試すのが最も分かりやすいよ。PyTorchで教師モデル(ResNet-50)と生徒モデル(ResNet-18)を使い、CIFAR-10データセットで知識蒸留を実装してみよう。Hugging FaceのTransformersライブラリにもDistilBERTの学習コードが公開されているから、NLP分野の蒸留も試せるよ。知識蒸留はエッジAI開発やコスト削減に直結する実践的なスキルだから、ぜひマスターしておこう。

AI初心者

AIを知りたい

大きなモデルの知識を小さなモデルに「蒸留」するというイメージがよく分かりました。まずはResNetで実験してみます!

Kerasで知識蒸留を実装する:Teacher-StudentモデルのDistillation Loss

知識蒸留(Knowledge Distillation)は、大きなTeacherモデルの知識を小さなStudentモデルに転移する技術です。以下ではKerasのカスタム訓練ステップを使い、KLダイバージェンスとクロスエントロピーを組み合わせたDistillation Lossを実装します。


import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np

# --- Teacherモデル(大きめのモデル) ---
def build_teacher(input_shape, num_classes):
    model = keras.Sequential([
        layers.Input(shape=input_shape),
        layers.Conv2D(64, 3, padding="same", activation="relu"),
        layers.Conv2D(64, 3, padding="same", activation="relu"),
        layers.MaxPooling2D(2),
        layers.Conv2D(128, 3, padding="same", activation="relu"),
        layers.Conv2D(128, 3, padding="same", activation="relu"),
        layers.MaxPooling2D(2),
        layers.GlobalAveragePooling2D(),
        layers.Dense(256, activation="relu"),
        layers.Dropout(0.3),
        layers.Dense(num_classes)  # logitsを出力(softmaxなし)
    ], name="teacher")
    return model

# --- Studentモデル(小さいモデル) ---
def build_student(input_shape, num_classes):
    model = keras.Sequential([
        layers.Input(shape=input_shape),
        layers.Conv2D(16, 3, padding="same", activation="relu"),
        layers.MaxPooling2D(2),
        layers.Conv2D(32, 3, padding="same", activation="relu"),
        layers.MaxPooling2D(2),
        layers.GlobalAveragePooling2D(),
        layers.Dense(64, activation="relu"),
        layers.Dense(num_classes)  # logitsを出力
    ], name="student")
    return model

# --- 蒸留トレーナー ---
class Distiller(keras.Model):
    def __init__(self, student, teacher):
        super().__init__()
        self.student = student
        self.teacher = teacher
        self.teacher.trainable = False  # Teacherは固定

    def compile(self, optimizer, temperature=3.0, alpha=0.7):
        super().compile(optimizer=optimizer)
        self.temperature = temperature
        self.alpha = alpha
        self.student_loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)
        self.distillation_loss_fn = keras.losses.KLDivergence()

    def train_step(self, data):
        x, y = data

        # Teacherの予測(勾配計算不要)
        teacher_logits = self.teacher(x, training=False)

        with tf.GradientTape() as tape:
            # Studentの予測
            student_logits = self.student(x, training=True)

            # 通常のクロスエントロピー損失(hard label)
            student_loss = self.student_loss_fn(y, student_logits)

            # 蒸留損失(soft label)
            # 温度付きsoftmaxで確率分布を軟化
            teacher_soft = tf.nn.softmax(teacher_logits / self.temperature, axis=-1)
            student_soft = tf.nn.softmax(student_logits / self.temperature, axis=-1)
            distillation_loss = self.distillation_loss_fn(teacher_soft, student_soft)
            distillation_loss *= self.temperature ** 2  # 温度のスケーリング補正

            # 合計損失: alpha * 蒸留損失 + (1-alpha) * 通常損失
            total_loss = (self.alpha * distillation_loss
                          + (1 - self.alpha) * student_loss)

        # Studentのみ更新
        grads = tape.gradient(total_loss, self.student.trainable_variables)
        self.optimizer.apply_gradients(zip(grads, self.student.trainable_variables))

        return {
            "total_loss": total_loss,
            "student_loss": student_loss,
            "distillation_loss": distillation_loss
        }

# --- 実行例(MNISTで蒸留) ---
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(-1, 28, 28, 1).astype("float32") / 255.0
x_test = x_test.reshape(-1, 28, 28, 1).astype("float32") / 255.0

teacher = build_teacher((28, 28, 1), 10)
student = build_student((28, 28, 1), 10)

# Teacherを先に学習
teacher.compile(optimizer="adam",
                loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                metrics=["accuracy"])
teacher.fit(x_train, y_train, epochs=5, batch_size=128, verbose=1)
teacher_acc = teacher.evaluate(x_test, y_test, verbose=0)[1]
print(f"Teacher accuracy: {teacher_acc:.4f}")
print(f"Teacher params: {teacher.count_params():,}")

# 蒸留でStudentを学習
distiller = Distiller(student=student, teacher=teacher)
distiller.compile(optimizer="adam", temperature=3.0, alpha=0.7)
distiller.fit(x_train, y_train, epochs=5, batch_size=128, verbose=1)

# Student単体の精度を確認
student.compile(optimizer="adam",
                loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                metrics=["accuracy"])
student_acc = student.evaluate(x_test, y_test, verbose=0)[1]
print(f"Student accuracy (distilled): {student_acc:.4f}")
print(f"Student params: {student.count_params():,}")
# Teacher: ~300K params → Student: ~30K params で精度低下を最小限に

蒸留の核心はtemperatureとalphaの2つのハイパーパラメータにあります。温度を高くするとTeacherのsoftmax出力が平坦になり、不正解クラス間の相対的な関係性(dark knowledge)がStudentに伝わりやすくなります。alphaは蒸留損失と通常損失のバランスを制御し、0.7前後で蒸留損失を重視するのが一般的です。この手法によりパラメータ数を1/10に圧縮しても精度低下を1〜2%に抑えられるケースが多く、エッジデバイスへのデプロイで威力を発揮します。