Knowledge Distillation: Compressing Trading Models for Low-Latency Deployment
Ketegangan antara akurasi dan latensi dalam trading berbasis ML sudah memiliki jawaban yang dipublikasikan di blog ini. Pemodelan spread dengan machine learning merekomendasikan pembagian dua tahap: model gradient-boosting yang cepat menangani quoting real-time yang kritis terhadap latensi, sementara model deep berjalan secara asynchronous dan memberinya sinyal sekunder atau menyesuaikan parameternya. Dua model, dua jam, satu sistem.
Knowledge distillation adalah jawaban yang berbeda untuk ketegangan yang sama. Alih-alih menjalankan model lambat di samping model cepat, gunakan model lambat itu sekali secara offline untuk melatih model cepat — student mempelajari seluruh distribusi probabilitas teacher atas hasil, bukan hanya hard labels, lalu teacher sepenuhnya meninggalkan hot path. Satu model saat inference, tanpa coupling asynchronous, tanpa staleness window.
Jawaban mana yang menang adalah pertanyaan empiris, dan artikel ini belum menjawabnya. Yang berikut ini adalah perangkatnya, ditambah pernyataan eksplisit tentang pengukuran yang akan menentukannya. Tidak ada satu pun di sini yang merupakan hasil benchmark; di tempat yang biasanya memerlukan angka, ada penanda yang menyatakan apa yang harus dijalankan.
Satu koreksi framing sejak awal, dari DeepLOB dan deep learning pada order book: akurasi klasifikasi yang tinggi tidak otomatis berubah menjadi profit — pergerakan yang diprediksi harus melewati bid-ask spread. Karena itu, "mempertahankan akurasi arah teacher" adalah sasaran yang keliru untuk dioptimalkan dalam setup distillation.
Kerangka Teacher-Student

Formulasi asli oleh Hinton, Vinyals, dan Dean (2015) sederhana. Anda memiliki model teacher (besar, lambat, akurat) dan model student (kecil, cepat, dan akan dilatih). Student belajar dari dua sinyal secara bersamaan:
- Target keras: label ground-truth (misalnya harga naik atau turun)
- Target lunak: distribusi probabilitas output teacher atas semua kelas
Fungsi loss student menggabungkan keduanya:
dengan dan adalah logits teacher dan student, adalah fungsi softmax, adalah parameter temperature, dan mengatur keseimbangan antara dua komponen loss.
Mengapa Soft Targets Penting untuk Trading
Formulasi tiga kelas up/stationary/down untuk mid-price, thresholding , serta alasan imbalance yang dihasilkan membuat Anda melaporkan weighted F1, bukan akurasi, semuanya sudah disiapkan di DeepLOB — gunakan skema label itu di sini. Hal yang khusus untuk distillation adalah apa yang dikeluarkan teacher sebelum argmax: "up" yang keras membawa satu bit, sedangkan 0.72/0.21/0.07 juga mengatakan bahwa pergerakan mungkin terhenti dan hampir pasti tidak akan berbalik. Struktur lintas kelas itu adalah sinyal pelatihan tambahan, dan itulah sebabnya student dengan soft targets dapat melakukan generalisasi lebih baik daripada student yang sama jika dilatih hanya dengan label.
Peringatan tentang apa yang bukan merupakan confidence tersebut. Output softmax bukan uncertainty yang terkalibrasi, dan memperlakukan 0.55 versus 0.85 sebagai input position-sizing adalah jalan pintas yang conformal prediction untuk trading sengaja ditolak — metode itu menurunkan sizing dari lebar interval, edge ratio, dan no-trade filter ketika interval melintasi nol, dan tidak satu pun dari hal itu diberikan oleh softmax mentah. Agar klaim sizing di sini layak, kalibrasi student harus diukur terhadap kalibrasi teacher (reliability diagram, ECE) dan ditunjukkan bahwa distillation mempertahankannya. Hasil itu belum ada dalam artikel ini.
Temperature dan Soft Targets

Parameter temperature mengontrol "kelembutan" distribusi probabilitas. Dengan logits , softmax dengan temperature adalah:
Ketika (softmax standar), distribusinya tajam — kelas dominan mendapat sebagian besar massa probabilitas. Ketika meningkat, distribusi menjadi lebih datar dan magnitudo relatif logits terlihat lebih jelas.
| Temperature | Efek | Kasus penggunaan |
|---|---|---|
| Softmax standar, tajam | Inference normal | |
| Pelunakan sedang | Distillation umum | |
| Pelunakan berat | Ketika teacher sangat yakin | |
| Hampir seragam | Jarang berguna, menghapus sinyal |
Ada argumen yang masuk akal bahwa model trading menginginkan temperature sedang: prediksi finansial jauh kurang yakin daripada klasifikasi gambar, sehingga teacher mungkin mengeluarkan 0.55/0.30/0.15, bukan 0.99/0.005/0.005, sehingga hanya tersisa sedikit ketajaman untuk dilunakkan sebelum sinyal menghilang. Itu adalah argumen, bukan temuan — rentangnya harus berasal dari sweep pada data nyata, dinilai dengan weighted F1, dan mungkin berbeda menurut regime.
Faktor dalam suku KL divergence mengompensasi magnitudo gradient yang berkurang pada temperature tinggi. Tanpanya, loss distillation akan menjadi sangat kecil ketika meningkat.
Memilih Temperature melalui Grid Search
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from sklearn.metrics import f1_score
def distillation_loss(
student_logits: torch.Tensor,
teacher_logits: torch.Tensor,
labels: torch.Tensor,
temperature: float,
alpha: float,
) -> torch.Tensor:
"""Combined hard-target + soft-target distillation loss."""
hard_loss = F.cross_entropy(student_logits, labels)
soft_teacher = F.log_softmax(teacher_logits / temperature, dim=-1)
soft_student = F.log_softmax(student_logits / temperature, dim=-1)
soft_loss = F.kl_div(
soft_student,
soft_teacher,
log_target=True,
reduction="batchmean",
)
return alpha * hard_loss + (1.0 - alpha) * (temperature ** 2) * soft_loss
def search_temperature(
teacher: nn.Module,
student_factory, # callable returning a fresh student
train_loader: DataLoader,
val_loader: DataLoader,
temperatures: list[float] = [1, 2, 3, 5, 8, 12],
alpha: float = 0.3,
epochs: int = 30,
lr: float = 1e-3,
device: str = "cuda",
):
"""Grid search over temperature, scored by weighted F1 (not accuracy:
the up/flat/down label scheme is heavily imbalanced toward flat)."""
best_f1, best_T, best_student = 0.0, 1.0, None
for T in temperatures:
student = student_factory().to(device)
optimizer = torch.optim.AdamW(student.parameters(), lr=lr)
for epoch in range(epochs):
student.train()
for X, y in train_loader:
X, y = X.to(device), y.to(device)
with torch.no_grad():
teacher_logits = teacher(X)
student_logits = student(X)
loss = distillation_loss(
student_logits, teacher_logits, y, T, alpha
)
optimizer.zero_grad()
loss.backward()
optimizer.step()
student.eval()
preds, targets = [], []
with torch.no_grad():
for X, y in val_loader:
preds.append(student(X.to(device)).argmax(dim=-1).cpu())
targets.append(y)
f1 = f1_score(
torch.cat(targets), torch.cat(preds), average="weighted"
)
print(f"T={T:>4.1f} val_weighted_f1={f1:.4f}")
if f1 > best_f1:
best_f1, best_T, best_student = f1, T, student
print(f"\nBest temperature: T={best_T}, val_weighted_f1={best_f1:.4f}")
return best_T, best_student
Mendistilasi Ensemble menjadi Satu Model

Ensemble quant mencampurkan inductive bias: pohon gradient-boosted pada fitur order-book, 1D-CNN pada tick terbaru, transformer pada window multi-timeframe, dan model linear pada faktor makro. Rata-rata lebih stabil daripada setiap anggota secara individual, dan menjalankan keempatnya melipatgandakan latensi serta biaya — situasi yang ditangani oleh pembagian dua tahap dari pemodelan spread dengan machine learning dengan menurunkan anggota lambat ke side channel asynchronous. Distillation justru memadatkan keempatnya menjadi satu student di hot path.
Output teacher ensemble adalah rata-rata output softmax para anggotanya:
dengan adalah jumlah anggota ensemble. Student dilatih terhadap distribusi rata-rata ini.
class EnsembleTeacher(nn.Module):
"""Wraps K models, returns averaged logits for distillation."""
def __init__(self, models: list[nn.Module]):
super().__init__()
self.models = nn.ModuleList(models)
@torch.no_grad()
def forward(self, x: torch.Tensor) -> torch.Tensor:
logits = torch.stack([m(x) for m in self.models], dim=0)
return logits.mean(dim=0) # average logits, not softmax
class TradingStudent(nn.Module):
"""Lightweight MLP for sub-millisecond inference."""
def __init__(self, input_dim: int, hidden: int = 64, n_classes: int = 3):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, hidden),
nn.ReLU(),
nn.BatchNorm1d(hidden),
nn.Linear(hidden, hidden),
nn.ReLU(),
nn.BatchNorm1d(hidden),
nn.Linear(hidden, n_classes),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.net(x)
Asimetri jumlah parameter adalah inti persoalannya: MLP dua lapis dengan 64 unit tersembunyi memiliki sekitar 8.000 parameter untuk tugas 60 fitur dan 3 kelas, sedangkan jumlah gabungan ensemble mencapai jutaan.
Apa yang Dipertahankan dan Hilang oleh Student
Ini adalah pertanyaan empiris yang paling menentukan, dan artikel ini belum menjawabnya. Intuisinya adalah student mengikuti ensemble di dalam distribusi dan tertinggal pada regime yang penuh tekanan, ketika keragaman ensemble melakukan pekerjaan penting — tetapi angka retensi hanya bermakna jika diukur pada data order-book nyata, dipisahkan menurut regime, dan dilaporkan sebagai weighted F1. Student yang bertahan pada hari tenang tetapi runtuh selama liquidation cascade adalah produk yang berbeda dari student yang menurun secara bertahap, dan angka agregat tidak dapat membedakan keduanya.
Tiga mitigasi layak diuji terhadap pengukuran tersebut, bukan dinyatakan sejak awal:
- Sertakan periode penuh tekanan dalam dataset distillation, sehingga student melihat regime saat kesenjangan diperkirakan terbuka.
- Distillation berbasis fitur — cocokkan representasi intermediate, bukan hanya output akhir.
- Regime head tambahan pada student, yang memaksa fitur sadar-regime masuk ke trunk bersama.
Self-Distillation: Ketika Student Menjadi Teacher

Self-distillation adalah teknik ketika sebuah model menyuling pengetahuan dari dirinya sendiri.
Born-Again Networks (BANs)
Latih student dengan arsitektur yang identik dengan teacher. Student "born-again" sering mengungguli model asli, dan prosesnya beriterasi:
Setiap generasi dilatih pada soft targets dari generasi sebelumnya, dengan peningkatan yang biasanya jenuh setelah beberapa generasi. Untuk model trading, ini tidak membutuhkan biaya arsitektur — tidak ada fitur baru, data baru, hanya prosedur training yang berbeda — sehingga murah untuk diuji dan tidak ada alasan untuk melaporkannya tanpa pengujian.
Self-Distillation Berdasarkan Kedalaman
Pasang classifier tambahan pada layer intermediate. Exit terdalam berfungsi sebagai teacher bagi exit yang lebih dangkal. Saat inference, Anda memilih exit: dangkal untuk latensi lebih rendah, dalam untuk akurasi maksimum.
Inilah gagasan yang paling cocok dengan sistem trading, karena kedalaman exit menjadi knob latensi saat runtime: satu network yang dilatih mencakup rentang budget, alih-alih berkomitmen pada satu arsitektur saat training. Ketika order book bergerak cepat, pilih exit dangkal dan terima posterior yang lebih buruk; ketika tenang, bayar biaya untuk kedalaman penuh. Kurva akurasi-per-exit dan latensi-per-exit dapat diukur, dan titik persilangannya menentukan apakah knob itu layak dipertahankan.
class SelfDistillingNet(nn.Module):
"""Network with early-exit classifiers for variable-latency inference."""
def __init__(self, input_dim: int, n_classes: int = 3):
super().__init__()
self.block1 = nn.Sequential(
nn.Linear(input_dim, 128), nn.ReLU(), nn.BatchNorm1d(128)
)
self.block2 = nn.Sequential(
nn.Linear(128, 64), nn.ReLU(), nn.BatchNorm1d(64)
)
self.block3 = nn.Sequential(
nn.Linear(64, 32), nn.ReLU(), nn.BatchNorm1d(32)
)
self.exit1 = nn.Linear(128, n_classes)
self.exit2 = nn.Linear(64, n_classes)
self.exit3 = nn.Linear(32, n_classes) # final exit
def forward(
self, x: torch.Tensor, exit_layer: int = 3
) -> torch.Tensor:
h1 = self.block1(x)
if exit_layer == 1:
return self.exit1(h1)
h2 = self.block2(h1)
if exit_layer == 2:
return self.exit2(h2)
h3 = self.block3(h2)
return self.exit3(h3)
def forward_all_exits(self, x: torch.Tensor):
"""Return logits from all exits (for self-distillation training)."""
h1 = self.block1(x)
h2 = self.block2(h1)
h3 = self.block3(h2)
return self.exit1(h1), self.exit2(h2), self.exit3(h3)
def self_distillation_step(
model: SelfDistillingNet,
x: torch.Tensor,
y: torch.Tensor,
temperature: float = 4.0,
alpha: float = 0.5,
) -> torch.Tensor:
"""One training step with self-distillation from deepest exit."""
logits_1, logits_2, logits_3 = model.forward_all_exits(x)
loss_hard = F.cross_entropy(logits_3, y)
loss_distill_1 = distillation_loss(
logits_1, logits_3.detach(), y, temperature, alpha
)
loss_distill_2 = distillation_loss(
logits_2, logits_3.detach(), y, temperature, alpha
)
return loss_hard + 0.5 * loss_distill_1 + 0.5 * loss_distill_2
Dari Mana Budget Inference Berasal

Distillation hanya penting jika inference harus berada dalam budget yang ketat, dan seluruh tangga tick-to-trade — NIC ke userspace, kernel bypass, total di bawah 100 µs, serta tier di bawah 10 µs yang memaksa penggunaan FPGA dan shared memory — sudah diuraikan dalam data dan komunikasi dalam algorithmic trading. Baris yang masih dibiarkan terbuka oleh tangga itu adalah inference model, dan itulah baris yang hendak diisi oleh distillation.
Tahan diri untuk mengisi baris lain dengan tabel latensi berbagai kelas model. Pemodelan spread dengan machine learning sudah menerbitkan perbandingan GBM versus deep learning, beserta catatan yang lebih penting daripada angkanya: latensi bergantung pada implementasi, dan model LightGBM yang sama membutuhkan puluhan mikrodetik per baris dari Python tetapi hanya beberapa mikrodetik dari predictor terkompilasi. Setiap klaim latensi di sini harus menyebut framework, core, dan batch size, atau klaim itu hanyalah noise.
Khusus pada GPU: overhead tetap per launch harus diamortisasi sebelum perangkat memberi manfaat apa pun, dan inference satu baris berada jauh di sebelah kiri ridge roofline, tempat manfaat itu tidak pernah muncul. Kapan GPU menguntungkan mengukur kurva amortisasi itu dengan benar melalui batch sweep, termasuk bagaimana kartu PCIe diskret mendorong ridge lebih ke kanan — baca artikel itu, bukan mempercayai konstanta yang dikutip dari ingatan.
Quantization setelah Distillation
Student hasil distillation dapat dikompresi lebih jauh: bobot INT8 (kira-kira 2x pada CPU dengan AVX-512 VNNI), bobot biner/ternary yang mengubah perkalian menjadi penjumlahan, serta pruning untuk melewati komputasi yang mendekati nol.
Klaim yang menggoda adalah bahwa distillation lalu quantization mempertahankan lebih banyak akurasi daripada quantization saja, karena student sudah mempelajari representasi yang kompak. Jangan melakukan deployment berdasarkan klaim itu. Jebakan presisi GPU adalah posisi tetap blog ini terhadap presisi numerik yang dikurangi: proses itu diam-diam mengembalikan hasil sampah yang terlihat masuk akal, dan yang membuat jalur cepat layak dikirim adalah equivalence gate yang diukur dengan angka — fill yang bergeser, delta PnL dalam bps — bukan pernyataan. Student INT8 adalah model yang berbeda sampai gate tersebut diukur terhadap student FP32.
import torch.quantization as quant
def quantize_student(student: nn.Module, calibration_loader: DataLoader):
"""Post-training static quantization for CPU deployment."""
student.cpu()
student.eval()
student.qconfig = quant.get_default_qconfig("x86")
student_prepared = quant.prepare(student)
with torch.no_grad():
for X, _ in calibration_loader:
student_prepared(X)
student_quantized = quant.convert(student_prepared)
return student_quantized
Deployment FPGA: Pipeline Distill-to-Bitstream

FPGA adalah tier di bawah 10 µs dalam tangga latensi, dan ulasan Tbricks/Broadridge membahasnya dalam produksi bersama NIC kernel-bypass — latensi deterministik, tanpa jitter OS, ditempatkan bersama network stack. Yang belum dibahas di mana pun di blog ini adalah bagaimana model hasil distillation masuk ke salah satunya.
Catatan produksi DeepLOB mencantumkan ONNX/TensorRT, quantization INT8, dan deployment FPGA sebagai tiga opsi lalu berhenti di sana. Opsi ketiga itu dapat diperluas sebagai berikut:
1. Train ensemble teacher (offline, GPU cluster, hours/days)
|
2. Distill to small MLP student (offline, single GPU, minutes)
|
3. Quantize student to INT8 / fixed-point (offline, CPU)
|
4. Convert to HLS (High-Level Synthesis) or RTL
|
5. Synthesize FPGA bitstream (offline, hours)
|
6. Deploy to FPGA card in production server
|
7. Inference: market data -> FPGA -> trading signal
Batasan yang mengikat adalah model harus muat dalam elemen logika perangkat — LUT, irisan DSP, block RAM. Sebagai budget perkiraan orde besaran, bukan pengukuran: MLP dua lapis dengan 64 unit tersembunyi dan bobot INT8 memiliki sekitar 8.000 multiply-accumulates per inference dan sekitar 16 KB bobot, sebagian kecil dari perangkat kelas menengah. Di sinilah distillation benar-benar berguna — ensemble teacher tidak muat pada budget apa pun; student masih jauh dari batas tersebut.
Tools yang mengotomatiskan PyTorch/ONNX menjadi hardware yang dapat disintesis mencakup AMD/Xilinx Vitis AI, hls4ml (dari CERN), dan FINN (dari Xilinx Research).
Contoh: Konversi hls4ml
import hls4ml
import onnx
dummy_input = torch.randn(1, 60) # 60 input features
torch.onnx.export(student, dummy_input, "student.onnx", opset_version=13)
hls_config = hls4ml.utils.config_from_onnx_model(
onnx.load("student.onnx"),
granularity="name",
default_precision="ap_fixed<16,8>",
default_reuse_factor=1, # full parallelism
)
hls_model = hls4ml.converters.convert_from_onnx_model(
"student.onnx",
hls_config=hls_config,
output_dir="hls_student",
backend="VivadoAccelerator",
board="alveo-u250",
)
hls_model.compile()
hls_model.build(csim=True, synth=True)
hls_model.report()
hls_model.report() adalah satu-satunya sumber kredibel untuk angka resource dan latensi pada model, board, precision, dan reuse factor tertentu — angkanya berubah secara signifikan hanya karena default_reuse_factor. Mengutip tabel sintesis "tipikal" tanpa menjalankannya adalah tebakan.
Pertimbangan Praktis

Menghitung Logits Teacher terlebih Dahulu
Distillation membutuhkan prediksi teacher untuk seluruh training set — biaya offline satu kali yang layak dibayar dengan sengaja: jalankan ensemble sekali, simpan logits, lalu latih student terhadap cache. Temperature sweep dan pencarian arsitektur kemudian tidak menambah biaya forward pass teacher, dan inilah yang membuat sweep di atas praktis.
Satu Monitor Khusus Distillation
Kebersihan feature pipeline, normalisasi rolling karena parameter z-score bergeser, pemantauan distribution shift input, dan retraining yang dipicu regime semuanya dibahas dalam bagian produksi DeepLOB dan berlaku tanpa perubahan di sini.
Monitor khusus distillation adalah KL divergence teacher-student pada data live. Teacher masih ada secara offline; jalankan teacher pada sampel input live dan bandingkan distribusinya. KL yang meningkat berarti aproksimasi student memburuk pada regime yang tidak digunakan saat distillation — dan sinyal ini muncul sebelum akurasi, karena tidak menunggu label. Threshold retraining harus dikalibrasi terhadap KL yang diamati pada periode yang diketahui baik dan terdegradasi; jika dipilih a priori, threshold itu arbitrer.
Kapan Tidak Perlu Melakukan Distillation
- Teacher sudah kecil (model linear, GBM dangkal): distillation menambah satu tahap pipeline tanpa kompresi.
- Latensi bukan kendala (rebalancing harian, sinyal akhir hari): deploy teacher.
- Interpretability lebih penting daripada kecepatan: network hasil distillation lebih sulit dijelaskan daripada tree ensemble yang digantikannya.
- Pembagian dua tahap sudah berfungsi: jika model lambat asynchronous dalam arsitektur pemodelan spread sudah memberikan hasil, distillation harus mengunggulinya dalam perbandingan terukur sebelum dapat membenarkan penggantian sistem yang sudah bekerja.
Ringkasan

Distillation adalah alternatif yang koheren terhadap pembagian cepat/lambat dua tahap: latih teacher terbaik yang mampu Anda biayai secara offline, transfer struktur soft targets-nya ke student yang cukup kecil untuk hot path, lakukan quantization, lalu deploy pada CPU atau FPGA. Varian berdasarkan kedalaman melangkah lebih jauh dan menjadikan latensi sebagai pilihan runtime, bukan pilihan saat training.
Hal yang sengaja tidak diklaim artikel ini adalah bahwa semua itu mengungguli apa yang sudah diterbitkan blog ini. Putusan tersebut memerlukan tiga pengukuran pada data order-book nyata: kurva retensi weighted F1 student-versus-ensemble yang dipisahkan menurut regime, temperature sweep, dan parity gate INT8 dengan gaya jebakan presisi GPU. Sampai semua itu tersedia, ini adalah uraian teknik, bukan rekomendasi untuk melakukan deployment.
Referensi
-
Hinton, G., Vinyals, O., & Dean, J. (2015). Distilling the Knowledge in a Neural Network. arXiv:1503.02531
-
Furlanello, T., Lipton, Z. C., Tschannen, M., Itti, L., & Anandkumar, A. (2018). Born-Again Neural Networks. ICML. arXiv:1805.04770
-
Zhang, L., Song, J., Gao, A., Chen, J., Bao, C., & Ma, K. (2019). Be Your Own Teacher: Improve the Performance of Convolutional Neural Networks via Self Distillation. ICCV. arXiv:1905.08094
-
Romero, A., Ballas, N., Kahou, S. E., Chassang, A., Gatta, C., & Bengio, Y. (2015). FitNets: Hints for Thin Deep Nets. ICLR. arXiv:1412.6550
-
Gou, J., Yu, B., Maybank, S. J., & Tao, D. (2021). Knowledge Distillation: A Survey. International Journal of Computer Vision, 129, 1789-1819. arXiv:2006.05525
-
Duarte, J., et al. (2018). Fast Inference of Deep Neural Networks in FPGAs for Particle Physics (hls4ml). Journal of Instrumentation, 13, P07027. arXiv:1804.06913
-
Umuroglu, Y., et al. (2017). FINN: A Framework for Fast, Scalable Binarized Neural Network Inference. FPGA '17. arXiv:1612.07119
-
Zhang, Z., Zohren, S., & Roberts, S. (2019). DeepLOB: Deep Convolutional Neural Networks for Limit Order Books. IEEE Transactions on Signal Processing, 67(11), 3001-3012. arXiv:1808.03668
Penulis
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.