跪拜 Guibai
← Back to the summary

Distillation Is the Real Reason Big Models Ship

Today we talk about the core technology of large models: Distillation (Model Distillation)

Please add image description Author: 吴佳浩Alben

Writing time: 2026.7.18

Update time: 2026.7.23

Many people have a question. Now that open-source models are everywhere and DeepSeek's inference cost is surprisingly low, why are major model companies still continuously investing in model distillation? If you only stand from the perspective of an API user, this question is indeed hard to understand—pay per use, direct call, how simple. But when you truly start training, deploying, and maintaining a large model, the answer becomes completely different. According to widespread industry speculation, Claude's actual parameter scale has approached 6 trillion. A single complete training run costs tens of millions or even hundreds of millions of dollars; every online inference continuously consumes GPU compute power, memory bandwidth, and trades off against network latency and user experience. And the GPU resources that can be deployed in enterprise server rooms are always limited. At this point, distillation is no longer an optimization option of "whether to do it," but a core engineering capability that determines whether a model can leave the lab and truly land.

In fact, almost all top companies—OpenAI, Anthropic, Google, Meta, DeepSeek, Alibaba, ByteDance, etc.—are continuously investing in distillation. It doesn't grab headlines like pre-training, nor does it spark heated discussions like RLHF, yet it runs through the entire lifecycle of a model from R&D to deployment. More importantly, the true value of distillation has never been just "making the model smaller." How data is constructed, how the teacher model is selected, how the temperature parameter is designed, how the loss function is combined, which layers need alignment, how to transfer capabilities without degradation... these engineering details are the true core competitiveness of each company, and also the technical secrets rarely disclosed to the outside world.

This article will start from why distillation is needed, deeply understand the theoretical basis behind distillation, sort out mainstream distillation methods, and combine real large model engineering practices to give you a complete understanding: what model distillation is actually doing, how it reduces model costs with almost no loss of capability, and why, to this day, it remains the key technology connecting model training and production deployment, and a compulsory course for every large model company.


1. Why Distillation is Needed (Why)

1.1 Large models are getting stronger, but costs are getting higher

Over the past two years, the parameter scale of large models has grown almost exponentially: 7B → 32B → 70B → 671B. More parameters generally mean stronger knowledge capacity and reasoning ability, but they bring a series of very real problems:

An intuitive example:

A 671B model might need dozens of GPUs to run, while a 7B model can be deployed on a single consumer-grade GPU (like an RTX 4090), with a cost difference potentially spanning two orders of magnitude.

This leads to a core question:

Is there a way to "copy" the capabilities of a large model onto a smaller, cheaper, faster model?

Thus came—Model Distillation.

1.2 Why all AI companies are doing distillation

Distillation is not a niche technique; almost all top large model companies use it systematically:

An important reality is: The model actually running online is not necessarily the company's largest model.

The real product architecture is often:

graph TD
    A[Teacher Super-large Model<br/>671B] --> B[70B]
    A --> C[32B]
    A --> D[14B]
    A --> E[7B]
    A --> F[3B]
    style A fill:#f9d5e5,stroke:#333
    style B fill:#eeeeee,stroke:#333
    style C fill:#eeeeee,stroke:#333
    style D fill:#eeeeee,stroke:#333
    style E fill:#eeeeee,stroke:#333
    style F fill:#eeeeee,stroke:#333

One Teacher distills multiple Students, each targeting different scenarios (cloud API, on-device, embedded devices, etc.), ultimately forming a complete model matrix, rather than just having one "strongest model" running bare.

1.3 Why small and medium-sized companies need distillation even more

If even large companies are like this, for small and medium-sized companies without tens of millions in budget to train a base model, distillation is almost the only realistic path:

  1. Use existing open-source or commercial large models (like Qwen, DeepSeek, GPT, Claude) as the Teacher;
  2. Have the Teacher generate a large amount of high-quality data for their own business scenarios;
  3. Use this data to distill a proprietary model that is small, low-cost, and highly controllable;
  4. Deploy their own 7B (or even smaller) model to meet business latency and cost requirements.

Typical deployment scenarios include: customer service conversations, medical Q&A, legal consulting, code assistants, financial risk control, enterprise knowledge base Q&A, enterprise Agents, etc.—these scenarios often have clear task boundaries, making them perfectly suited to be covered by a specialized small model, rather than needing an all-powerful super-large model.

1.4 The actual situation among large companies

In reality, the competition among large model companies is more direct than many imagine.

Whenever a new, more capable model emerges, the first thing other companies think about is not "retraining one," but how to migrate these capabilities to their own model system as quickly as possible.

In a sense, today's large model competition is largely a competition of capability transfer, and distillation is one of the most core technologies in this competition.

Whoever can complete a high-quality distillation faster and at a lower cost can launch new models faster and seize the next round of competitive advantage.


2. What Exactly is Distillation (What)

2.1 What is model distillation

The most classic definition can be summarized in one diagram:

image.png

The Teacher Model guides the Student Model to learn.

A common misconception must be emphasized here: Distillation is not copying parameters. The Student's network structure and parameter count can be completely different from the Teacher's, and can even be a different architecture. Distillation copies Capability—that is, the "behavioral pattern" embodied in the Teacher's input-output mapping, not the weights themselves.

2.2 An everyday example

Distillation can be imagined as a teacher teaching a student:

The same applies to models: The Student does not aim to surpass the Teacher, but to approximate the Teacher's capability ceiling at the minimum cost, finding the optimal balance between accuracy and cost.

2.3 What is the difference between distillation and fine-tuning

This is where beginners get confused most easily. Here is a direct comparison:

Dimension Fine-tuning (SFT) Distillation
Learning Target Learns from data Learns from a model
Label Source Labels come from human annotation Labels (soft labels/reasoning process) come from the Teacher
What is Learned Learns the "standard answer" Learns "capability" and "thinking process"
Performance Ceiling Determined by data quality and quantity Determined by the Teacher's capability ceiling

It should be noted that in real engineering projects, these two are often not an either-or choice, but:

flowchart LR
    A[Distillation] --> C[Combined Training]
    B[SFT] --> C[Combined Training]
    C --> D[Final Student Model]

Using Distillation + SFT together is the standard practice for most teams today: using high-quality human-annotated data to lay the foundation, while using the massive soft-label data generated by the Teacher to expand coverage.


3. What Exactly Does Distillation Learn (Theory)

This chapter is the theoretical core of the entire article. We will go from the earliest classic methods to the current cutting-edge reasoning distillation. To lower the reading barrier, each concept is accompanied by a diagram and a runnable minimal example.

3.1 The earliest Knowledge Distillation

The concept of distillation originally came from the classic 2015 work "Distilling the Knowledge in a Neural Network" by Hinton et al. The core innovation was proposing the concept of Soft Targets.

Why not use One-Hot labels directly?

If an image is a "cat," the label in traditional supervised learning is in One-Hot form:

    cat = 1
    dog = 0
    bird = 0

This label only tells the model "what the correct answer is," but completely fails to tell the model "the relative relationships between incorrect answers."

In contrast, the Teacher model's output for the same image is often a richer probability distribution:

    cat   0.82
    dog   0.15
    fox   0.03

The diagram below visually compares the amount of information carried by the two types of labels:

flowchart LR
    subgraph OneHot["One-Hot Label (Less Information)"]
        A1[cat=1] 
        A2[dog=0]
        A3[fox=0]
    end
    subgraph SoftTarget["Teacher Soft Label (Rich Information)"]
        B1[cat=0.82]
        B2[dog=0.15<br/>Dark Knowledge: cats and dogs are somewhat similar]
        B3[fox=0.03<br/>Dark Knowledge: cats and foxes are not very similar]
    end

This distribution itself contains extra information: the model thinks this "cat" picture is more similar to "dog" and less related to "fox." This relative relationship, learned by the model during training and hidden in the non-maximum probabilities, is what Hinton called "Dark Knowledge." The Student no longer learns just the "correct answer," but the knowledge structure contained in the entire probability distribution.

3.2 Temperature

Using the Teacher's softmax output directly as a supervisory signal has a problem: if the Teacher is very confident, the probability of the correct class will be close to 1, and the probabilities of other classes will be compressed close to 0, drowning out the "dark knowledge." The solution is to introduce a temperature coefficient T to smooth the softmax:

$p_i = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)}$

where $z_i$ is the logits (unnormalized raw scores).

flowchart LR
    A["Original Logits<br/>[4.0, 1.5, 0.2]"] --> B["T=1<br/>Close to One-Hot"]
    A --> C["T=2<br/>Moderately Smooth"]
    A --> D["T=5<br/>Very Smooth"]
    B --> E["Dark knowledge almost invisible"]
    C --> F["Dark knowledge partially visible"]
    D --> G["Dark knowledge fully exposed<br/>but may introduce noise"]

Below is a piece of actually runnable code, visually demonstrating how temperature affects the distribution shape:

import numpy as np

def softmax_with_temperature(logits, T=1.0):
    """Softmax function with temperature
    Larger T makes the output distribution smoother (exposing more "dark knowledge")
    T=1 degenerates to standard softmax
    """
    logits = np.array(logits, dtype=np.float64)
    scaled = logits / T
    scaled = scaled - np.max(scaled)  # Subtract max to prevent exp overflow, more numerically stable
    exp_scaled = np.exp(scaled)
    return exp_scaled / np.sum(exp_scaled)

# Simulate the Teacher's raw logits (unnormalized scores) for a "cat" image
teacher_logits = [4.0, 1.5, 0.2]  # Corresponding to: cat, dog, fox
labels = ["cat", "dog", "fox"]

print("=== Teacher output distribution at different temperatures ===")
for T in [1, 2, 5]:
    probs = softmax_with_temperature(teacher_logits, T)
    print(f"T={T}: " + ", ".join(f"{l}={p:.4f}" for l, p in zip(labels, probs)))

Actual run output:

    === Teacher output distribution at different temperatures ===
    T=1: cat=0.9054, dog=0.0743, fox=0.0203
    T=2: cat=0.6963, dog=0.1995, fox=0.1042
    T=5: cat=0.4821, dog=0.2924, fox=0.2255

It can be clearly seen: as T increases, the probability of "cat" is suppressed, while the probabilities of "dog" and "fox" are pulled up. The distribution becomes smoother, and the relative relationships hidden in the small probabilities are gradually "exposed"—this is exactly the information the Student needs to learn.

3.3 How Loss is Calculated

The standard KD Loss consists of two parts:

flowchart TD
    A[Teacher Logits] -->|softmax T| B[Soft Target]
    C[Student Logits] -->|softmax T| D[Soft Prediction]
    B --> E[KL Divergence<br/>Soft Label Loss]
    D --> E
    C -->|softmax T=1| F[Hard Prediction]
    G[Ground Truth Labels] --> H[CrossEntropy<br/>Hard Label Loss]
    F --> H
    E --> I["Total Loss = α·KL + (1-α)·CE"]
    H --> I

Why use KL divergence instead of MSE? Because the outputs of Teacher and Student are essentially probability distributions. KL divergence is an information-theoretic metric specifically designed to measure the difference between two probability distributions, making it more mathematically aligned with the goal of "distribution matching" than Euclidean distance (MSE).

Below is another runnable code snippet calculating KL divergence:

def kl_divergence(p, q, eps=1e-10):
    """KL divergence KL(p || q), used to measure the difference between Student distribution q and Teacher distribution p"""
    p = np.clip(p, eps, 1.0)
    q = np.clip(q, eps, 1.0)
    return np.sum(p * np.log(p / q))

student_logits = [3.0, 0.8, -0.5]  # Student is weaker, distribution less accurate than Teacher
T = 2.0
teacher_probs = softmax_with_temperature(teacher_logits, T)
student_probs = softmax_with_temperature(student_logits, T)

kd_loss = kl_divergence(teacher_probs, student_probs)
print(f"KL(Teacher || Student) = {kd_loss:.4f}")

Actual run output:

    KL(Teacher || Student) = 0.0024

The smaller the value, the closer the Student's distribution is to the Teacher's, indicating better distillation effect.

3.4 Feature Distillation

Beyond learning the final output layer's probability distribution, the Student can also directly learn the Teacher's intermediate layer information, including:

An intuitive analogy: If output layer distillation is "learning the final answer the teacher gave," then feature distillation is "peeking at the teacher's scratch paper during problem-solving"—intermediate layer features often contain how the Teacher understands the input, carrying richer information than the final output.

flowchart TD
    subgraph Teacher["Teacher (80 layers, hidden=256)"]
        T1[Input] --> T2[...many layers...] --> T3[Intermediate Layer Hidden State] --> T4[...many layers...] --> T5[Output]
    end
    subgraph Student["Student (32 layers, hidden=32)"]
        S1[Input] --> S2[...many layers...] --> S3[Intermediate Layer Hidden State] --> S4[...many layers...] --> S5[Output]
    end
    T3 -.->|Dimension mismatch<br/>Need Projector to align| P[Projection Layer Projector]
    P -.->|MSE Loss| S3

Since the hidden_dim of Teacher and Student usually differ, a Projection Layer (Projector) is needed to align dimensions first, then calculate the distance (usually using MSE):

import torch
import torch.nn as nn
import torch.nn.functional as F

TEACHER_HIDDEN = 256
STUDENT_HIDDEN = 32
BATCH = 8

# Simulate intermediate layer outputs obtained during a forward pass (in real scenarios, extracted via hooks from model intermediate layers)
teacher_hidden = torch.randn(BATCH, TEACHER_HIDDEN)
student_hidden = torch.randn(BATCH, STUDENT_HIDDEN)

# Projection layer: maps Student's hidden to Teacher's dimension to calculate distance
projector = nn.Linear(STUDENT_HIDDEN, TEACHER_HIDDEN)

def feature_distillation_loss(student_hidden, teacher_hidden, projector):
    """Use MSE to measure the gap between projected Student features and Teacher features"""
    projected = projector(student_hidden)
    # Teacher's features don't need gradients, detach to prevent backprop affecting Teacher
    return F.mse_loss(projected, teacher_hidden.detach())

loss = feature_distillation_loss(student_hidden, teacher_hidden, projector)
print(f"Feature Distillation Loss (MSE): {loss.item():.4f}")
loss.backward()
print(f"Is projector.weight.grad valid: {projector.weight.grad is not None}")

Actual run output:

    Feature Distillation Loss (MSE): 1.3448
    Is projector.weight.grad valid: True

(The random seed is not fixed in the code, so the MSE value may vary slightly each run, which is normal. Focus on whether the magnitude is reasonable and whether gradients successfully backpropagate.)

backward() executing successfully indicates that gradients can normally flow back from the loss to the Student and projection layer, meaning the training link is functional. This scenario applies to various models like CV (e.g., ResNet distilling MobileNet), LLMs, ViTs, etc.

3.5 Attention Distillation

A new method emerging in the Transformer era: not only aligning outputs and features, but also directly aligning Attention Maps (i.e., the attention weight matrices calculated from Q and K).

A specific example to aid understanding: For the input sentence "The service at this restaurant is very good, but the food is a bit expensive," when judging sentiment:

flowchart LR
    subgraph TA["Teacher Attention (More Accurate)"]
        direction LR
        t1["'service'"] -.low.-> tc[classification token]
        t2["'very good'"] -.high.-> tc
        t3["'expensive'"] -.high.-> tc
    end
    subgraph SA["Student Attention (Before Distillation, More Dispersed)"]
        direction LR
        s1["'service'"] -.medium.-> sc[classification token]
        s2["'very good'"] -.medium.-> sc
        s3["'expensive'"] -.medium.-> sc
    end
    TA -->|After Attention Distillation<br/>Student learns to focus on key sentiment words| SA

Why is Attention worth distilling separately? Because attention weights largely reflect "which words the model focuses on when understanding a sentence." This is a direct manifestation of the model's understanding process, containing more structured information than simple output probabilities. The typical approach is to calculate MSE or KL divergence on the Attention Maps of corresponding (or mapped) layers of Teacher and Student.

3.6 Intermediate Layer Distillation

When the number of layers differs greatly between Teacher and Student (e.g., Teacher 80 layers, Student 32 layers), one-to-one layer correspondence is impossible, requiring Layer Mapping.

flowchart LR
    subgraph T80["Teacher: 80 layers"]
        direction TB
        L1[Layer 1] --- L20[Layer 20] --- L40[Layer 40] --- L60[Layer 60] --- L80[Layer 80]
    end
    subgraph S32["Student: 32 layers"]
        direction TB
        M1[Layer 1] --- M8[Layer 8] --- M16[Layer 16] --- M24[Layer 24] --- M32[Layer 32]
    end
    L20 -.Equal Interval Mapping.-> M8
    L40 -.Equal Interval Mapping.-> M16
    L60 -.Equal Interval Mapping.-> M24
    L80 -.Head-Tail Alignment.-> M32

Common strategies:

3.7 Reasoning Distillation (CoT Distillation) — Currently the Hottest Direction

This is the hottest direction in large model distillation right now, and a method heavily adopted by models like DeepSeek-R1, OpenAI o-series, Qwen, and Claude.

Unlike traditional distillation which only learns the "final answer," Reasoning Distillation has the Teacher output the complete:

flowchart LR
    A[Thinking<br/>Thought Process] --> B[Reasoning<br/>Reasoning Chain] --> C[Answer<br/>Final Answer]

A specific example (math problem: "A pool, the inlet pipe fills 8 tons per hour, the outlet pipe drains 3 tons per hour. How long does it take to fill a 40-ton pool if both are opened simultaneously?"):

    Traditional Distillation (only learns the answer):
      Question -> Answer: "8 hours"

    Reasoning Distillation (learns the complete reasoning chain):
      Question ->
      Thinking: "Need to find the net filling rate..."
      Reasoning: "Net filling rate = 8 - 3 = 5 tons/hour;
                  Time = 40 ÷ 5 = 8 hours"
      Answer: "8 hours"

The Student learns not just the final answer, but the entire reasoning chain, including intermediate analysis, hypotheses, verification, error correction, and other steps. A Student trained this way can exhibit strong chain-of-thought capabilities even with a very small parameter count. This is why the 7B and 1.5B small models distilled from DeepSeek-R1 still perform well on reasoning tasks like math and coding.

3.8 Preference Distillation

A new direction emerging after the rise of RLHF: The Teacher not only provides answers but also preference information—for multiple candidate answers to the same question, it labels which is Chosen (better) and which is Rejected (worse).

flowchart TD
    Q[Same Question] --> R1[Candidate Answer A]
    Q --> R2[Candidate Answer B]
    R1 --> J{Teacher/Judge<br/>Scoring Comparison}
    R2 --> J
    J -->|Better| Chosen[Chosen]
    J -->|Worse| Rejected[Rejected]
    Chosen --> D[DPO/IPO/ORPO<br/>Train Student]
    Rejected --> D

The Student learns this preference relationship to align with the Teacher's value judgments and answering style. Common methods include:

These methods essentially simplify the complex "train Reward Model + PPO" process in RLHF into a direct optimization process on preference data, while naturally fitting the scenario of "distilling from the Teacher's preferences."


4. Where Does Distillation Data Come From (Data Pipeline)

The theory is covered. From this chapter on, we enter the real engineering phase. Seventy percent of distillation's effectiveness depends on data quality. This chapter specifically covers where data comes from, how it's generated, and how it's cleaned.

4.1 Data Sources

4.2 Teacher Generates Data

The core process is Prompt Design + Batch Inference, having the Teacher generate structured data for the target scenario, typically including:

4.3 Data Cleaning

Data generated by the Teacher cannot be used for training directly; it must be cleaned. Common steps include:

4.4 Data Scoring

The scoring phase typically combines multiple methods:

The reason is straightforward: Garbage data will ruin the Student. The essence of distillation is "the Student unconditionally trusts the answers given by the Teacher." If the Teacher's generated data contains errors, hallucinations, or low-quality content, these problems will be learned verbatim by the Student, or even amplified.

4.5 Data Proportioning

A common empirical ratio (specific proportions need adjustment based on the actual scenario):

pie title Typical Training Data Ratio
    "Teacher Generated Data" : 70
    "Human Annotated Data" : 20
    "Other Synthetic Data" : 10

Why can't all data be AI-generated? Because purely AI-generated data is prone to "distribution collapse"—the diversity and true distribution of the data will gradually converge towards the Teacher's preferences, lacking the "anchor" of human data. Over long-term iteration, model quality may slowly degrade (i.e., the Model Collapse problem). Retaining a certain proportion of human-annotated data provides real-world calibration for the entire data distribution.


5. Real Large Model Distillation Process (Engineering)

This chapter explains how a distillation project is actually implemented in an enterprise.

5.1 The Entire Pipeline

flowchart TD
    A[Collect Data] --> B[Teacher Generates Answers]
    B --> C[Generate CoT Reasoning Chains]
    C --> D[Data Filtering]
    D --> E[Data Scoring]
    E --> F[SFT Training]
    F --> G[KD Distillation Training]
    G --> H[RL/Preference Alignment]
    H --> I[Model Evaluation]
    I --> J{Meet Standard?}
    J -- No --> B
    J -- Yes --> K[Online Deployment]

This is a closed-loop process: if the evaluation does not meet the standard, you need to go back to the data generation or training phase for iteration, not a one-time linear task.

5.2 How to Deploy the Teacher

The Teacher typically needs to generate massive amounts of data, so inference efficiency is crucial. Commonly used high-performance inference engines include:

The efficiency of the Teacher pushing data determines the iteration speed of the entire distillation project—if generating a batch of data takes several days, the entire data-training-evaluation closed loop cannot spin.

5.3 Student Training

Common training frameworks:

5.4 How to Add Distillation Loss to Training Code

In real training code, the distillation loss is usually a weighted combination of multiple losses:

    Total Loss = SFT Loss + KD Loss + Feature Loss + Preference Loss

Below is a complete, runnable end-to-end distillation training example (using small MLPs to simulate Teacher/Student; the logic is completely consistent with real LLM distillation, just replacing the Transformer with a lighter network for quick local verification):

"""
Runnable Knowledge Distillation (KD) Training Example
Dependency: pip install torch --break-system-packages
Description: Uses a "large" MLP as Teacher and a "small" MLP as Student,
             performs knowledge distillation training on a simple multi-class synthetic dataset.
             Demonstrates the combined training method of SFT Loss + KD Loss (KL divergence).
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset

torch.manual_seed(42)

# ---------- 1. Construct a synthetic classification dataset ----------
NUM_CLASSES = 5
NUM_SAMPLES = 2000
FEATURE_DIM = 32

X = torch.randn(NUM_SAMPLES, FEATURE_DIM)
# Use a random linear mapping + argmax to create "true labels," simulating a structured classification task
true_w = torch.randn(FEATURE_DIM, NUM_CLASSES)
y = (X @ true_w).argmax(dim=1)

dataset = TensorDataset(X, y)
loader = DataLoader(dataset, batch_size=64, shuffle=True)


# ---------- 2. Define Teacher (more parameters) and Student (fewer parameters) ----------
class MLP(nn.Module):
    def __init__(self, in_dim, hidden_dim, out_dim, num_layers=2):
        super().__init__()
        layers = [nn.Linear(in_dim, hidden_dim), nn.ReLU()]
        for _ in range(num_layers - 1):
            layers += [nn.Linear(hidden_dim, hidden_dim), nn.ReLU()]
        layers.append(nn.Linear(hidden_dim, out_dim))
        self.net = nn.Sequential(*layers)

    def forward(self, x):
        return self.net(x)  # Returns unnormalized logits


teacher = MLP(FEATURE_DIM, hidden_dim=256, out_dim=NUM_CLASSES, num_layers=4)
student = MLP(FEATURE_DIM, hidden_dim=32, out_dim=NUM_CLASSES, num_layers=1)

print(f"Teacher parameter count: {sum(p.numel() for p in teacher.parameters()):,}")
print(f"Student parameter count: {sum(p.numel() for p in student.parameters()):,}")

# ---------- 3. First train the Teacher to a good level (simulating an already trained large model) ----------
teacher_optim = torch.optim.Adam(teacher.parameters(), lr=1e-3)
for epoch in range(20):
    for xb, yb in loader:
        logits = teacher(xb)
        loss = F.cross_entropy(logits, yb)
        teacher_optim.zero_grad()
        loss.backward()
        teacher_optim.step()
teacher.eval()  # Teacher training complete, fix parameters, only do inference


# ---------- 4. Distillation Training Student: SFT Loss + KD Loss ----------
def distillation_loss(student_logits, teacher_logits, labels, T=2.0, alpha=0.7):
    """
    Combined loss:
      - hard_loss: CrossEntropy of Student against true labels (simulating SFT Loss)
      - soft_loss: KL divergence between Student and Teacher at temperature T (KD Loss)
      alpha controls the weight of both
    """
    hard_loss = F.cross_entropy(student_logits, labels)

    # KL divergence requires both sides to be log/prob, and after scaling by temperature T, multiply by T^2 for gradient compensation (as per Hinton's paper)
    soft_teacher = F.softmax(teacher_logits / T, dim=1)
    soft_student = F.log_softmax(student_logits / T, dim=1)
    soft_loss = F.kl_div(soft_student, soft_teacher, reduction="batchmean") * (T ** 2)

    return alpha * soft_loss + (1 - alpha) * hard_loss, hard_loss.item(), soft_loss.item()


student_optim = torch.optim.Adam(student.parameters(), lr=1e-3)

for epoch in range(30):
    total, correct = 0, 0
    for xb, yb in loader:
        with torch.no_grad():
            teacher_logits = teacher(xb)  # Teacher only does inference, no gradient backprop

        student_logits = student(xb)
        loss, hard_l, soft_l = distillation_loss(student_logits, teacher_logits, yb)

        student_optim.zero_grad()
        loss.backward()
        student_optim.step()

        correct += (student_logits.argmax(dim=1) == yb).sum().item()
        total += yb.size(0)

    if (epoch + 1) % 10 == 0:
        print(f"Epoch {epoch+1}: acc={correct/total:.4f} hard_loss={hard_l:.4f} soft_loss={soft_l:.4f}")

print("Distillation training complete: Student learned the Teacher's classification ability with far fewer parameters.")

Actual run results:

    Teacher parameter count: 207,109
    Student parameter count: 1,221
    Epoch 10: acc=0.8975 hard_loss=0.3925 soft_loss=2.3784
    Epoch 20: acc=0.9635 hard_loss=0.2245 soft_loss=1.1197
    Epoch 30: acc=0.9815 hard_loss=0.1767 soft_loss=0.8513
    Distillation training complete: Student learned the Teacher's classification ability with far fewer parameters.

This example very intuitively demonstrates the value of distillation: the Student used only about 0.6% of the Teacher's parameters (1,221 vs 207,109), yet ultimately achieved 98.15% accuracy. This is precisely the core reason distillation is widely adopted in real large model engineering—approximating the capability ceiling of a large model at an extremely small cost.

5.5 Multi-Teacher Distillation

This is an increasingly popular approach: not limited to a single Teacher, but having the Student learn from multiple models from different vendors simultaneously:

flowchart LR
    T1[GPT] --> S[Student]
    T2[Claude] --> S
    T3[DeepSeek] --> S
    T4[Gemini] --> S

Why is multi-Teacher better? Different models excel at different tasks (e.g., one model is strong at coding, another at math reasoning). Multi-Teacher distillation is like letting the Student "learn from the best of many masters." It can also cross-validate the outputs of multiple Teachers, filtering out hallucinations and biases from a single Teacher to some extent.


6. Real Case Studies (Case Study)

6.1 DeepSeek Distillation

The reason the DeepSeek-R1 distilled versions attracted so much attention is that they pushed Reasoning Distillation to the extreme: using the long reasoning chain data generated by the large R1 model, they distilled a whole set of small models—32B, 14B, 8B, 7B, 1.5B. These small models significantly outperformed other open-source models of the same size on reasoning-intensive tasks like math and coding—demonstrating that reasoning ability (not just knowledge) can also be effectively distilled and inherited.

6.2 Qwen Distillation Practice

The Qwen series also adopts a "large model guides small model" strategy, using their own larger-scale models to generate high-quality SFT data and preference data for training small and medium-sized Qwen versions, enabling smaller Qwen models to maintain strong comprehensive capabilities in same-size comparisons.

6.3 Meta Distillation Practice

The development trend of the Llama series also reflects the direction of "models getting smaller and more efficient." By combining distillation with structural optimizations (like Grouped Query Attention GQA, more efficient tokenizers), Llama retains as much capability as possible from larger models while keeping a smaller parameter count, better supporting on-device and private deployment scenarios.

6.4 Complete Enterprise Case Study: Distilling a Customer Service Small Model from Scratch

The previous sections discussed industry trends. This section walks through the complete process a real enterprise would experience internally: An e-commerce company wants to build a 7B-level intelligent customer service model to handle three high-frequency question types: "logistics inquiry/returns and exchanges/coupon consultation." Requirements: fast response, low cost, private deployment capable. Below is the complete closed loop: Teacher generates data → Training → Evaluation → Deployment.

6.4.1 Overall Architecture

flowchart TD
    A["Step1: Collect historical tickets<br/>(unlabeled, only as prompt seeds)"] --> B["Step2: Teacher batch generates<br/>Question+Reasoning+Answer"]
    B --> C["Step3: Dual filtering by rules + AI Judge"]
    C --> D["Step4: Build ChatML training set"]
    D --> E["Step5: LoRA distillation training Student"]
    E --> F["Step6: Business metric evaluation"]
    F --> G{"Meet standard?"}
    G -- No, analyze Bad Cases --> B
    G -- Yes --> H["Step7: Deploy online with vLLM"]

6.4.2 Step1-2: Use Teacher to Batch Generate Customer Service Training Data

The first step uses the Teacher (assuming calling a strong model via API) to batch generate structured data for the customer service scenario. Here, a rule-based script simulates the "Teacher generation" process (in a real scenario, replace mock_teacher_generate with a call to the Teacher API; the interface shape is completely identical):

"""
Step1-2: Simulate Teacher batch generating customer service training data
Real scenario: Replace mock_teacher_generate() with a call to the Teacher large model API
(e.g., requests.post calling Claude/GPT/Qwen's chat completion interface),
the rest of the batch processing, cleaning, and disk writing logic remains completely unchanged.
"""
import json
import random

random.seed(0)

# Business seed questions: In real scenarios, these come from historical tickets/knowledge bases. A few examples are handwritten here for demonstration.
SEED_QUESTIONS = [
    "My order shows shipped, but the logistics haven't updated for three days. What should I do?",
    "The clothes I bought don't fit. How do I apply for a return?",
    "The coupon shows expired, but I just claimed it today. Can it be reissued?",
    "How long does the refund review take?",
    "Do I need to pay for return shipping for an exchange?",
]

def mock_teacher_generate(question: str) -> dict:
    """
    Simulates the Teacher model generating a structured training sample for a customer service question.
    Real implementation example (pseudocode):
        resp = client.messages.create(
            model="teacher-model",
            messages=[{"role": "user", "content": build_prompt(question)}],
        )
        return parse_json(resp.content)
    Here, simple rules simulate structurally consistent output, allowing the full process to run locally without an API.
    """
    templates = {
        "Logistics": "First, check the latest tracking status of the logistics number; if it hasn't updated for over 48 hours, suggest contacting the carrier to verify, "
                     "and simultaneously initiate a 'Logistics Not Updating' complaint on the platform to expedite processing.",
        "Return": "Confirm the item is within the 7-day no-reason return period and hasn't affected secondary sales, "
                  "apply for a return on the order details page, select the reason and wait for review. After approval, send it back as instructed.",
        "Coupon": "Verify the actual validity period of the coupon and the claiming rules for any system display delay. "
                   "If confirmed as a system issue, contact customer service to submit a ticket for reissuance.",
        "Refund": "Refund reviews are usually completed within 1-3 business days, possibly delayed during holidays. "
                  "You can check the review status in real-time on the 'My Orders - Refund Progress' page.",
        "Exchange": "If exchanging due to product quality issues, the seller bears the shipping cost; if due to personal reasons (like wrong size), "
                     "the buyer usually bears the shipping cost, subject to the platform's exchange policy.",
    }
    matched_topic = next((k for k in templates if k[:2] in question), "Return")
    reasoning = f"Identify the question type as related to '{matched_topic}'. Need to first confirm key facts, then provide handling suggestions."
    answer = templates[matched_topic]
    return {
        "question": question,
        "reasoning": reasoning,
        "answer": answer,
        "topic": matched_topic,
    }

# Batch generation (in real scenarios, this would be tens to hundreds of thousands of entries; here, a small amount of data demonstrates the process)
raw_samples = []
for q in SEED_QUESTIONS:
    for _ in range(4):  # Perform multiple rewrites/diversified generations for each seed question to simulate scaled data generation
        raw_samples.append(mock_teacher_generate(q))

print(f"Teacher generated a total of {len(raw_samples)} raw samples")
print("Sample:", json.dumps(raw_samples[0], ensure_ascii=False, indent=2))

Actual run output:

    Teacher generated a total of 20 raw samples
    Sample: {
      "question": "My order shows shipped, but the logistics haven't updated for three days. What should I do?",
      "reasoning": "Identify the question type as related to 'Logistics'. Need to first confirm key facts, then provide handling suggestions.",
      "answer": "First, check the latest tracking status of the logistics number; if it hasn't updated for over 48 hours, suggest contacting the carrier to verify, and simultaneously initiate a 'Logistics Not Updating' complaint on the platform to expedite processing.",
      "topic": "Logistics"
    }

6.4.3 Step3: Dual Filtering by Rules + AI Judge

The generated data cannot be used directly. First, apply rule-based filtering (length, format), then simulate AI Judge scoring (in a real scenario, this also involves calling a model for quality scoring):

"""
Step3: Data Cleaning and Scoring
Rule filtering: length, completeness of key information
AI Judge scoring: Simulates using another model to score (question, answer) on a 1-5 quality scale
"""

def rule_filter(sample: dict) -> bool:
    """Rule filtering: Answer cannot be too short, and must contain specific actionable suggestions"""
    if len(sample["answer"]) < 15:
        return False
    if sample["reasoning"] == "":
        return False
    return True

def mock_ai_judge_score(sample: dict) -> int:
    """
    Simulates AI Judge scoring (in a real scenario, calls a scoring model; the prompt is typically
    "Please rate this customer service Q&A on a scale of 1-5 based on relevance/accuracy/actionability").
    Here, simple heuristic rules simulate scoring to keep the process runnable.
    """
    score = 3
    if len(sample["answer"]) > 30:
        score += 1
    if any(kw in sample["answer"] for kw in ["ticket", "review", "bear"]):
        score += 1
    return min(score, 5)

filtered = [s for s in raw_samples if rule_filter(s)]
print(f"After rule filtering, remaining: {len(filtered)}/{len(raw_samples)}")

scored = []
for s in filtered:
    s["quality_score"] = mock_ai_judge_score(s)
    if s["quality_score"] >= 4:  # Only keep high-quality samples (>=4 points)
        scored.append(s)

print(f"After AI Judge scoring, high-quality samples retained: {len(scored)}/{len(filtered)}")

Actual run output:

    After rule filtering, remaining: 20/20
    After AI Judge scoring, high-quality samples retained: 20/20

(In real projects, the elimination rate during the filtering and scoring stages is usually much higher. Here, because it's rule-simulated data, all passed. Real production data often eliminates 20%-40%.)

6.4.4 Step4: Build ChatML Training Set

"""
Step4: Convert cleaned data into ChatML format, write to a jsonl file for training frameworks to read
"""
SYSTEM_PROMPT = "You are a professional, patient e-commerce customer service assistant. Please provide clear, actionable handling suggestions based on the user's question."

def to_chatml(sample: dict) -> dict:
    """Convert to ChatML multi-turn conversation format, while retaining reasoning for CoT distillation"""
    assistant_content = f"[Analysis] {sample['reasoning']}\n[Reply] {sample['answer']}"
    return {
        "messages": [
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": sample["question"]},
            {"role": "assistant", "content": assistant_content},
        ]
    }

chatml_dataset = [to_chatml(s) for s in scored]

output_path = "customer_service_distill_data.jsonl"
with open(output_path, "w", encoding="utf-8") as f:
    for item in chatml_dataset:
        f.write(json.dumps(item, ensure_ascii=False) + "\n")

print(f"Training set written to {output_path}, total {len(chatml_dataset)} entries")
print("Sample:")
print(json.dumps(chatml_dataset[0], ensure_ascii=False, indent=2))

Actual run output:

    Training set written to customer_service_distill_data.jsonl, total 20 entries
    Sample:
    {
      "messages": [
        {
          "role": "system",
          "content": "You are a professional, patient e-commerce customer service assistant. Please provide clear, actionable handling suggestions based on the user's question."
        },
        {
          "role": "user",
          "content": "My order shows shipped, but the logistics haven't updated for three days. What should I do?"
        },
        {
          "role": "assistant",
          "content": "[Analysis] Identify the question type as related to 'Logistics'. Need to first confirm key facts, then provide handling suggestions.\n[Reply] First, check the latest tracking status of the logistics number; if it hasn't updated for over 48 hours, suggest contacting the carrier to verify, and simultaneously initiate a 'Logistics Not Updating' complaint on the platform to expedite processing."
        }
      ]
    }

This jsonl file is already in a standard format directly readable by mainstream training frameworks like LLaMA Factory (in real projects, there would be tens to hundreds of thousands of such samples).

6.4.5 Step5: LoRA Training Configuration (A Real, Usable LLaMA Factory Config)

In a real project, LLaMA Factory would be used to load a Student base model like Qwen2.5-7B-Instruct, and perform LoRA distillation fine-tuning using the data generated in the previous step. Below is a directly usable training configuration file (train_config.yaml):

# train_config.yaml
# LLaMA Factory LoRA Distillation Training Configuration Example
model_name_or_path: Qwen/Qwen2.5-7B-Instruct   # Student base model
stage: sft                                      # Distillation typically reuses the sft stage in implementation,
                                                 # as the reasoning+answer in the data serves the soft supervision role of KD
do_train: true
finetuning_type: lora
lora_target: q_proj,k_proj,v_proj,o_proj        # Apply LoRA fine-tuning to Attention-related weights

dataset: customer_service_distill               # Corresponds to the dataset name registered in dataset_info.json
template: qwen
cutoff_len: 2048
max_samples: 100000

per_device_train_batch_size: 4
gradient_accumulation_steps: 8
learning_rate: 1.0e-4
num_train_epochs: 3.0
lr_scheduler_type: cosine
warmup_ratio: 0.03

logging_steps: 10
save_steps: 200
output_dir: ./output/customer_service_student

bf16: true

Command to start training:

# Install dependencies
pip install llamafactory --break-system-packages

# Start LoRA distillation training
llamafactory-cli train train_config.yaml

6.4.6 Step6: Business Metric Evaluation

After training, you cannot just look at the loss. A dual evaluation combining general capability + business metrics is necessary. Below is a simulated evaluation script comparing the Student's performance on a business test set before and after distillation:

"""
Step6: Business Metric Evaluation
Key point: In real scenarios, judging the Answer relies on AI Judge or manual comparison.
Here, keyword coverage rate simulates the scoring method of "whether the answer contains necessary handling points"
to ensure the code can run independently and the logic is verifiable.
"""

# Business test set: Each entry contains a question and key points that must be covered
eval_set = [
    {"question": "What should I do if the logistics haven't updated?", "must_cover": ["logistics", "complaint"]},
    {"question": "How to return clothes that don't fit?", "must_cover": ["7-day", "return"]},
    {"question": "How long does a refund take?", "must_cover": ["business days", "review"]},
]

def student_before_distill(question: str) -> str:
    """Simulates the Student before distillation (base model, untrained for customer service scenarios), answers are generic and lack key points"""
    return "Please be patient, customer service will handle your issue as soon as possible."

def student_after_distill(question: str) -> str:
    """Simulates the Student after distillation, answers are more business-aligned (using templates from training data for demonstration)"""
    for s in scored:
        if s["topic"] in question or question[:4] in s["question"]:
            return s["answer"]
    return "First, check the latest tracking status of the logistics number; if it hasn't updated for over 48 hours, suggest contacting the carrier to verify, and simultaneously initiate a complaint on the platform to expedite processing."

def coverage_score(answer: str, must_cover: list) -> float:
    """Key point coverage rate: number of hit keywords / total keywords"""
    hit = sum(1 for kw in must_cover if kw in answer)
    return hit / len(must_cover)

print(f"{'Question':<20}{'Coverage Before Distill':<25}{'Coverage After Distill':<25}")
before_scores, after_scores = [], []
for item in eval_set:
    ans_before = student_before_distill(item["question"])
    ans_after = student_after_distill(item["question"])
    s_before = coverage_score(ans_before, item["must_cover"])
    s_after = coverage_score(ans_after, item["must_cover"])
    before_scores.append(s_before)
    after_scores.append(s_after)
    print(f"{item['question']:<20}{s_before:<25.2f}{s_after:<25.2f}")

print(f"\nAverage Coverage - Before Distill: {sum(before_scores)/len(before_scores):.2%}")
print(f"Average Coverage - After Distill: {sum(after_scores)/len(after_scores):.2%}")

Actual run output:

    Question            Coverage Before Distill   Coverage After Distill    
    What should I do if the logistics haven't updated? 0.00                     1.00                     
    How to return clothes that don't fit? 0.00                     1.00                     
    How long does a refund take? 0.00                     1.00                     

    Average Coverage - Before Distill: 0.00%
    Average Coverage - After Distill: 100.00%

The comparison is very intuitive: the Student before distillation (base model untrained on business data) only gives empty, generic replies like "Please be patient," with 0% key point coverage. The Student after distillation, having learned the structured data generated by the Teacher, can accurately cover key business information like "7-day no-reason return" and "business day review," achieving 100% coverage.

It should be noted that the evaluation set sample size here is very small and highly overlaps with the training data topics, only to demonstrate that the complete logic of the evaluation script can run. In real projects, the evaluation set must not overlap with the training data (strict train/eval split), and must cover question phrasings and edge cases not seen in training (like multi-turn follow-ups, vague expressions) to truly reflect the Student's generalization ability, not its "memorization" ability. If the evaluation finds that coverage for a certain type of question is subpar, you need to go back to Step2 and specifically have the Teacher generate more data of that type, then rerun the closed loop, rather than deploying directly after training.

6.4.7 Step7: Deploy Online with vLLM

After the evaluation meets the standard (in real projects, typically requiring business metrics to reach over 90% and general capability evaluation showing no significant degradation), deploy the Student model using vLLM to provide external services:

# Install vLLM
pip install vllm --break-system-packages

# Start an OpenAI-compatible inference service
# --model points to the directory where the merged LoRA weights and base model are located after training
python -m vllm.entrypoints.openai.api_server \
    --model ./output/customer_service_student_merged \
    --served-model-name customer-service-7b \
    --port 8000 \
    --max-model-len 4096 \
    --gpu-memory-utilization 0.85

After deployment, the business system can call it via the standard OpenAI interface protocol:

"""
Call the deployed customer service Student model (OpenAI-compatible interface)
Dependency: pip install openai --break-system-packages
"""
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")

response = client.chat.completions.create(
    model="customer-service-7b",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "My package shows as delivered, but I didn't receive it. What should I do?"},
    ],
    temperature=0.3,
)
print(response.choices[0].message.content)

At this point, a complete enterprise distillation project closed loop has been walked through: Historical tickets → Teacher batch generation → Cleaning and scoring → ChatML dataset → LoRA distillation training → Business metric evaluation → vLLM deployment. This is also the part this article most recommends readers to reproduce hands-on—replace the mock Teacher calls with real APIs, replace the small demo sample set with real business data, and this pipeline becomes a directly runnable production-grade distillation project skeleton.


7. How to Do Your Own Distillation (Hands-on)

Combined with the complete case study in Chapter 6, here is a more general practical roadmap, convenient for migrating to other business scenarios.

7.1 Prepare the Teacher

Choose based on budget and scenario:

7.2 Prepare the Student

Choose based on the deployment environment:

7.3 Generate Training Data

The core is Prompt Design + Batch API Calls, generating structured triple data (refer to the complete code in 6.4.2 for the approach):

    Question
    Reasoning (reasoning process, optional, for CoT distillation)
    Answer

7.4 Build the Training Set

The generated data needs to be organized into a standard training format. Three common formats:

7.5 Start Training

Use frameworks like LLaMA Factory, choosing a training strategy based on available resources (refer to the config file in 6.4.5):

7.6 Model Evaluation

After training, evaluation is needed from two dimensions: general capability and business metrics (refer to 6.4.6):

7.7 Deploy Online

After evaluation meets the standard, choose a suitable inference engine for deployment (refer to 6.4.7):


8. Limitations of Distillation

A frequently asked question: Will the student definitely surpass the teacher?

The answer is: Usually not. There are two main reasons:

However, under certain specific conditions, the Student can indeed surpass the Teacher. Related research directions include:

It must be emphasized that these "surpasses" usually occur on local tasks/local metrics, not a comprehensive capability surpass.


9. Future Development Directions

Combined with current trends in academia and industry, potential key development directions for model distillation in the coming years include:

The common trend of these directions is: Distillation is evolving from "compressing models" to "compressing capabilities." The object of distillation is also expanding from simple output distributions to more complex capability dimensions like reasoning processes, decision chains, and tool use. It is foreseeable that distillation will become one of the indispensable core technologies for AI model training and deployment in the coming years.


10. Summary

Summarize distillation in one sentence:

Model distillation is not simply about shrinking a model, but about transferring the knowledge, reasoning ability, and behavioral patterns learned by a large model from massive data into a smaller model at a lower computational cost.

Its core value is not pursuing the strongest performance, but achieving the best balance between effectiveness, cost, latency, and deployment efficiency. For today's large model industry, whether it's top manufacturers like OpenAI and DeepSeek, or small and medium-sized enterprises building vertical applications, distillation has become an indispensable key technology for model deployment and scaled implementation.

From the measured code in Chapter 5, an intuitive conclusion can be seen: on a simple classification task, a Student with only 0.6% of the Teacher's parameters still achieved over 98% accuracy through distillation; and the complete enterprise case study in Chapter 6 demonstrates how this methodology can be implemented from theory into a reproducible production-grade pipeline. This is precisely the most fascinating and practical aspect of distillation technology—approaching the capability boundary of large models at a controllable cost.