← Makalelere geri dön
August 14, 2026
5 dakikalık okuma

Model Pruning for Low-Latency Trading Inference

Model Pruning for Low-Latency Trading Inference
#model-compression
#pruning
#lottery-ticket
#latency
#deployment

DeepLOB makalemiz dağıtım bölümünü üç maddeyle bitiriyor - ONNX artı TensorRT, INT8 nicemleme, FPGA - ve bunların hiçbirini ele almıyoruz. Bu makale, her üçünün de altında yatan ilk sorunun eksik çözümüdür: Model olması gerekenden daha büyük. Sinir ağı budaması gereksiz parametreleri ortadan kaldırır ve literatürdeki ilginç iddia, bunun hafıza tasarrufu sağladığı değil, ağırlıkların %10-20'sini tutan bir alt ağın yoğun modelin doğruluğunu eşleştirebileceğidir.

Gecikmenin önemli olduğu, blogun zaten yaptığı bir durumdur - mesajlaşma yolunda ZigBolt ve başa baş aritmetiğiyle IPC vergisi - ve spread modelleme zaten hızlı ama biraz daha kötü ile yavaş ama daha iyi arasında bir dengeye sahip ve çıkarım gecikmesi satırına sahip bir gradyan artırmaya karşı derin öğrenme tablosuyla tamamlanıyor. Hiçbirinin kapsamadığı şey, belirli bir modeli nasıl küçülteceğinizdir. Bir alıntılama döngüsündeki aşamalardan model çıkarımı tamamen bizim kontrolümüz altındadır; taşıma ayakları, algotrading için veri iletişimi bölümünde tekrarlanabilir p50/p95/p99 numaralarıyla kaplıdır.

Bu makalenin içeriği: büyüklük budama, yapılandırılmış budama, Yinelemeli Büyüklük Budama, hareket budama, bilgi damıtma ve bir ticari MLP'ye uygulanan NVIDIA 2:4 yarı yapılandırılmış seyreklik için matematik ve çalışma kodu.

Ne değildir: ölçülen bir sonuç. Bu blogdaki her ampirik makale bir kaynak çizgisi veya tamamlayıcı bir repo taşıyor ve bu makale henüz ikisini de taşımıyor. Azlık-doğruluk-gecikme eğrisi aşağıda alıntı yapılacak bir tablo olarak değil, çalıştırılacak bir deney olarak belirtilmiştir. Buradaki her şeyi yöntem olarak ve sayıları da beklemede olarak değerlendirin.

Budama Size Ne Kazandırır?

Hızlı bir önbellek katmanına sığan kompakt sinir ağı

Kısıtlama bir boyuttadır. Orta frekanslı bir modeli düşünün - sipariş defteri özellikleri üzerinde 2048 gizli birime sahip 4 katmanlı bir MLP:

Parameters=dinh+h2(L1)+hdout\text{Parameters} = d_{\text{in}} \cdot h + h^2 \cdot (L - 1) + h \cdot d_{\text{out}}

İçin din=100d_{\text{in}} = 100, h=2048h = 2048, L=4L = 4, dout=3d_{\text{out}} = 3, bu kabaca 12,6 milyon parametre anlamına gelir - float32'de yaklaşık 48 MB. L2 genellikle 1-4 MB boyutunda olduğundan ağırlıklar uymuyor; her ileri geçişte daha uzaktan yayınlanırlar. Bunların %95'ini budadığınızda kabaca 630.000 etkin parametreye ve 2.4 MB'a ulaşırsınız ki bu da uygundur.

Bunun duvar saati zamanına dönüşüp dönüşmeyeceği, çekirdeğin hafızaya bağlı olup olmamasına bağlıdır ve bu, boyut sorusundan ziyade aritmetik yoğunluk sorusudur. Backtest motor hız merdiveni, bir ceza faktörü ileri sürmek yerine tavan hattı modelini (Williams, Waterman & Patterson) ölçülü bir örnek üzerinden çalıştırır; Aynı çerçeve burada da geçerlidir ve aynı disiplin şunları yapmalıdır: Hızlanma talebinde bulunmadan önce taşınan baytları ölçmelidir.

Budamanın Temelleri

Sinir kafesi boyunca yapılandırılmış ve yapılandırılmamış budama

Yapılandırılmamış Budama

En basit yaklaşım: bireysel ağırlıkları büyüklüklerine göre sıfıra ayarlamak. Bir ağırlık matrisi verildiğinde WRm×n\mathbf{W} \in \mathbb{R}^{m \times n}, ikili maske oluşturun M{0,1}m×n\mathbf{M} \in \{0, 1\}^{m \times n} öyle ki:

Mij={1if Wijτ0if Wij<τM_{ij} = \begin{cases} 1 & \text{if } |W_{ij}| \geq \tau \\ 0 & \text{if } |W_{ij}| < \tau \end{cases}

Neresi τ\tau İstenilen seyreklik seviyesine ulaşmak için seçilen bir eşiktir ss:

s=1M0mns = 1 - \frac{\|\mathbf{M}\|_0}{m \cdot n}

Budanmış matris W=WM\mathbf{W}' = \mathbf{W} \odot \mathbf{M}, ile \odot Hadamard ürünü. Sezgi, sıfıra yakın ağırlıkların katmanın çıktısına çok az katkıda bulunduğu yönündedir.

Sorun, seyreklik numarasını yanlış okumak kolay olduğu için açıkça ifade edilmiştir: yapılandırılmamış seyreklik, standart donanımda bir hızlanma anlamına gelmez. Seyrek çekirdeklere veya seyreklik desteğine sahip donanıma geçmediğiniz sürece, %90'ı sıfır olan bir matris, aynı sayıda çoklu birikimler yayınlamaya devam eder. Aşağıdaki kod yazdırıldığında Sparsity: 90.0%, bu bir sıfır sayısıdır - 10x herhangi bir şey değildir ve yoğun bir CPU GEMM'de de 1,01x değildir. Zaman kazandıran yollar, yapılandırılmış budama (daha küçük matrisler) ve 2:4 yarı yapılandırılmış seyrekliktir (donanım desteği), her ikisi de aşağıdadır.

Yapılandırılmış Budama

Yapılandırılmış budama tüm nöronları, kanalları veya dikkat kafalarını ortadan kaldırır. Doğrusal bir katman için y=Wx+b\mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b} ile WRm×n\mathbf{W} \in \mathbb{R}^{m \times n}, nöronun çıkarılması jj sıfırlar jj-inci sıra W\mathbf{W} ve jj-th elemanı b\mathbf{b}:

importance(j)=Wj,:2=k=1nWjk2\text{importance}(j) = \|\mathbf{W}_{j,:}\|_2 = \sqrt{\sum_{k=1}^{n} W_{jk}^2}

En küçük nöronlar 2\ell_2-normal önce git. Bu, gerçekten daha küçük matrisler üreten varyanttır; ancak yalnızca katmanları gerçekten yeniden oluşturursanız. Satırları sıfırlamak ve tensörü orijinal şeklinde bırakmak FLOP sayısında hiçbir şeyi değiştirmez; uygulama bölümündeki yeniden oluşturma adımı, maskeyi bir maskeye dönüştüren adımdır. 512×n512 \times n matris.

Evrişimli katmanlar için analog, filtre budamasıdır. Verilen WRCout×Cin×k×k\mathbf{W} \in \mathbb{R}^{C_{\text{out}} \times C_{\text{in}} \times k \times k}çıkış filtresinin önemi ii şu:

importance(i)=Wi,:,:,:F=c,h,wWi,c,h,w2\text{importance}(i) = \|\mathbf{W}_{i,:,:,:}\|_F = \sqrt{\sum_{c,h,w} W_{i,c,h,w}^2}

Filtrenin çıkarılması ii FLOP'ları orantılı olarak azaltarak tüm çıkış kanalını ortadan kaldırır.

Piyango Bileti Hipotezi

Geniş bir sinirsel kafes içinde izole edilmiş küçük kazanan alt ağ

2019'da Frankle ve Carbin, Piyango Bileti Hipotezini (LTH) tanıttı: Rastgele başlatılan yoğun bir ağ içinde, orijinal başlatmadan eğitilen, karşılaştırılabilir sayıda yinelemede tam ağın doğruluğuyla eşleşen seyrek bir alt ağ - bir "kazanan bilet" vardır.

Resmi olarak düşünün f(x;θ0)f(\mathbf{x}; \boldsymbol{\theta}_0) ile başlatıldı θ0Dθ\boldsymbol{\theta}_0 \sim \mathcal{D}_\theta. Yakınsama eğitimi aldıktan sonra elde ederiz θ\boldsymbol{\theta}^* ve bir budama maskesi türetin m\mathbf{m}. LTH var olduğunu belirtiyor m\mathbf{m} öyle ki:

f(x;mθ0)trainθmwhereacc(θm)acc(θ)f(\mathbf{x}; \mathbf{m} \odot \boldsymbol{\theta}_0) \xrightarrow{\text{train}} \boldsymbol{\theta}_m^* \quad \text{where} \quad \text{acc}(\boldsymbol{\theta}_m^*) \geq \text{acc}(\boldsymbol{\theta}^*)

ile m0θ0\|\mathbf{m}\|_0 \ll |\boldsymbol{\theta}_0|. Orijinal deneyler, kazanan biletlerin parametrelerin %10-20'sini koruduğu MNIST ve CIFAR-10 üzerinde yapıldı. Sipariş defteri verilerinin varsayım yoluyla aktarılmasıyla ilgili hiçbir şey yok - LOB özellikleri durağan değildir ve etiket neredeyse gürültüdür, bu da görüntü sınıflandırmasından tam olarak önemli olabilecek şekillerde farklı bir rejimdir.

Yinelemeli Büyüklük Budama (IMP)

Bilet IMP tarafından bulunur:

  1. Ağı şununla başlatın: θ0\boldsymbol{\theta}_0.
  2. Yakınsama eğitimi, elde etme θ\boldsymbol{\theta}^*.
  3. Budama p%p\% en küçük büyüklükteki ağırlıkların maske oluşturması m\mathbf{m}.
  4. Hayatta kalan ağırlıkları değerlerine sıfırlayın θ0\boldsymbol{\theta}_0 (geri sarma).
  5. Maskeli ağ ile 2. adımdan itibaren tekrarlayın.

Her turda kuru erik fraksiyonu pp (genellikle %20), yani sonra nn turlar (1p)n(1-p)^n parametrelerden biri hayatta kalır. 10 turdan sonra p=0.2p = 0.2kabaca %10,7'si kalıyor.

Ticaret Modelleriyle İlgili Üç Hipotez, Hiçbiri Test Edilmedi

LTH'nin özellikle piyasa verileri üzerinde iyi çalışması gerektiğini iddia etmek cazip geliyor. Bu tür üç argüman ortaya çıkıyor; üçü de hipotezdir ve bunları gerçek olarak belirtmek, bu blogun kaçınmak için var olduğu başarısızlık modudur.

  1. Finansal sinyaller seyrektir. Emir defteri anlık görüntülerinin çoğu gürültüden oluşur, dolayısıyla seyrek bir alt ağ, doğal olarak seyrek bir sinyalle hizalanabilir. Test edilebilir: IMP'yi aynı seyrekliğe sahip rastgele bir maskeyle karşılaştırın; eğer işi seyrekliğin kendisi yapıyorsa, rastgele maske çok geride olmamalıdır.
  2. Kazanan biletler rejimler arasında genelleşir. Bu, piyasalarla ilgili, arkasında herhangi bir alıntı bulunmayan ampirik bir iddiadır ve üçü arasında en ilginç olanıdır. HMM'lerle rejim tespiti adresinden rejim etiketlerine karşı doğrudan test edilebilir: A rejimindeki bileti bulun, B rejiminde yeniden eğitin ve B'de yerel olarak bulunan bir biletle karşılaştırın.
  3. Yetersizlik düzenli hale gelir. Daha düşük etkili kapasite, mikroyapı gürültüsüne uyumu azaltabilir; bu, budanmış modelin örnek dışı boşluğunun, yalnızca karşılaştırılabilir olmakla kalmayıp, yoğun modelinkinden daha küçük olmasıyla ortaya çıkar.

Uygulama: Ticaret MLP'sini Budama

Nöral katmanlar hassas bir seyrek maskeyle dönüştürüldü

Temel Model

import torch
import torch.nn as nn
import torch.nn.utils.prune as prune
from copy import deepcopy

class TradingMLP(nn.Module):
    """MLP for mid-price direction prediction from order book features."""
    def __init__(self, input_dim=100, hidden_dim=2048,
                 num_layers=4, output_dim=3):
        super().__init__()
        layers = []
        dims = [input_dim] + [hidden_dim] * (num_layers - 1) + [output_dim]
        for i in range(len(dims) - 1):
            layers.append(nn.Linear(dims[i], dims[i + 1]))
            if i < len(dims) - 2:
                layers.append(nn.BatchNorm1d(dims[i + 1]))
                layers.append(nn.ReLU())
                layers.append(nn.Dropout(0.1))
        self.network = nn.Sequential(*layers)

    def forward(self, x):
        return self.network(x)

    def count_parameters(self):
        return sum(p.numel() for p in self.parameters())

model = TradingMLP(input_dim=100, hidden_dim=2048,
                   num_layers=4, output_dim=3)
print(f"Total parameters: {model.count_parameters():,}")

Yapılandırılmamış Büyüklükte Budama

def apply_unstructured_pruning(model, sparsity=0.9):
    """Apply global unstructured L1 pruning to all Linear layers."""
    parameters_to_prune = []
    for name, module in model.named_modules():
        if isinstance(module, nn.Linear):
            parameters_to_prune.append((module, 'weight'))

    prune.global_unstructured(
        parameters_to_prune,
        pruning_method=prune.L1Unstructured,
        amount=sparsity,
    )
    return model

def compute_sparsity(model):
    """Fraction of zero weights. Note: a *count*, not a speedup."""
    total, zeros = 0, 0
    for name, param in model.named_parameters():
        if 'weight' in name:
            total += param.numel()
            zeros += (param == 0).sum().item()
    return zeros / total if total > 0 else 0

pruned_model = apply_unstructured_pruning(deepcopy(model), sparsity=0.9)
print(f"Sparsity: {compute_sparsity(pruned_model):.1%}")

Yapılandırılmış Budama, Onu Gerçek Hale Getiren Yeniden Yapılanmayla

Satırları maskelemek işin yarısıdır. Hızlanmayı sağlayan kısım, her katmanı küçültülmüş şekliyle yeniden oluşturmaktır; bu, kaldırma işleminin ileriye doğru yayılması anlamına gelir: satırın düşürülmesi jj katman ii sütunu da düşürür jj katman i+1i+1 ve kanal jj herhangi birinin BatchNorm1d aralarında.

def apply_structured_pruning(model, fraction=0.75):
    """Mask entire neurons by L2-norm of their weight rows."""
    for name, module in model.named_modules():
        if isinstance(module, nn.Linear) and module.out_features > 10:
            prune.ln_structured(
                module, name='weight', amount=fraction, n=2, dim=0
            )
    return model

def rebuild_pruned_mlp(model):
    """
    Physically shrink a structurally pruned TradingMLP.

    Walks the Sequential once. For each Linear: drop the input columns
    the previous layer no longer emits, then drop its own dead output
    rows. BatchNorm1d channels follow the preceding Linear's survivors.
    """
    new_layers = []
    keep_in = None  # surviving output indices of the previous Linear

    for layer in model.network:
        if isinstance(layer, nn.Linear):
            if prune.is_pruned(layer):
                prune.remove(layer, 'weight')

            W, b = layer.weight.data, layer.bias.data
            keep_out = (W.norm(dim=1) > 0).nonzero(as_tuple=True)[0]
            W = W[keep_out]
            if keep_in is not None:
                W = W[:, keep_in]

            new = nn.Linear(W.shape[1], W.shape[0])
            new.weight.data = W.clone()
            new.bias.data = b[keep_out].clone()
            new_layers.append(new)
            keep_in = keep_out

        elif isinstance(layer, nn.BatchNorm1d):
            new = nn.BatchNorm1d(len(keep_in))
            new.weight.data = layer.weight.data[keep_in].clone()
            new.bias.data = layer.bias.data[keep_in].clone()
            new.running_mean = layer.running_mean[keep_in].clone()
            new.running_var = layer.running_var[keep_in].clone()
            new.num_batches_tracked = layer.num_batches_tracked.clone()
            new_layers.append(new)

        else:  # ReLU, Dropout -- shape-agnostic, reuse as is
            new_layers.append(layer)

    rebuilt = deepcopy(model)
    rebuilt.network = nn.Sequential(*new_layers)
    return rebuilt

Buna güvenmeden önce, blogun geri kalanındaki denklik kapılarıyla aynı ruhla kontrol edilmesi gereken iki şey:

  • Şekiller. rebuilt gizli boyutları şu adreste göstermelidir: (1fraction)h(1 - \text{fraction}) \cdot h — 512 için fraction=0.75, h=2048h = 2048 - ve iç matrislerin her iki boyutu da küçüldüğünden ikinci dereceden düşen bir parametre sayısı.
  • Çıkışlar. Giriş eval() mod, rebuilt(x) maskeli modelinkiyle eşleşmelidir rebuilt-aynı partide kayan nokta toleransına ücretsiz çıktı. Aksi takdirde sütun yayılımı yanlıştır ve her alt sayı düşündüğünüzden farklı bir modeli ölçüyor demektir.

Satır hayatta kalma testi, maskelenmiş bir satırın tam olarak sıfır olduğunu ve canlı bir satırın olmadığını varsayar. Bu şu anlama gelir: ln_structured çıktı; Başka bir prosedürün gerçekten tamamen sıfır canlı bir nöron üretmesi durumunda bu geçerli olmayacaktır; bu nedenle, norm testine körü körüne güvenmek yerine, hayatta kalanın talep edilen fraksiyona göre sayıldığını iddia edin.

Yinelemeli Büyüklük Budama (Piyango Bileti Arama)

def lottery_ticket_search(model_cls, model_kwargs, train_fn, eval_fn,
                          rounds=10, prune_rate=0.2, device='cpu'):
    """
    Iterative Magnitude Pruning to find a winning ticket.

    Parameters
    ----------
    model_cls : class -- model constructor
    model_kwargs : dict -- constructor arguments
    train_fn : callable -- train_fn(model) trains the model in-place
    eval_fn : callable -- eval_fn(model) returns out-of-sample accuracy
    rounds : int -- number of pruning rounds
    prune_rate : float -- fraction of surviving weights pruned per round
    """
    model_init = model_cls(**model_kwargs).to(device)
    theta_0 = deepcopy(model_init.state_dict())

    mask = {}
    for name, param in model_init.named_parameters():
        if 'weight' in name:
            mask[name] = torch.ones_like(param, dtype=torch.bool)

    results = []

    for round_idx in range(rounds):
        model = model_cls(**model_kwargs).to(device)
        state = deepcopy(theta_0)
        for name in mask:
            state[name] = state[name] * mask[name].float()
        model.load_state_dict(state)

        train_fn(model)

        acc = eval_fn(model)
        surviving = sum(m.sum().item() for m in mask.values())
        total = sum(m.numel() for m in mask.values())
        sparsity = 1.0 - surviving / total

        results.append({
            'round': round_idx,
            'accuracy': acc,
            'sparsity': sparsity,
            'surviving_params': int(surviving)
        })
        print(f"Round {round_idx}: acc={acc:.4f}, "
              f"sparsity={sparsity:.1%}")

        all_weights = []
        for name, param in model.named_parameters():
            if name in mask:
                alive = param.data.abs()[mask[name]]
                all_weights.append(alive.flatten())

        all_weights = torch.cat(all_weights)
        k = int(len(all_weights) * prune_rate)
        if k == 0:
            break
        threshold = all_weights.kthvalue(k).values.item()

        for name, param in model.named_parameters():
            if name in mask:
                mask[name] = mask[name] & (
                    param.data.abs() >= threshold
                )

    return results, mask

results bu makalenin size borçlu olduğu seyreklik-doğruluk eğrisinin hammaddesidir. eval_fn temizlenmiş bölünmelerde gerçekten örnek dışı olmalıdır - örnek içinde puanlanan bir IMP çalışması hiçbir şey ifade etmeyen güzel bir eğri rapor edecektir.

Ölçmek

Soyut verimlilik sınırı, seyreklik doğruluğunu ve gecikmeyi dengeliyor

Gecikme, blogun geri kalanıyla aynı koşum kuralıyla ölçülür - ısınma hariç, en iyisi N, ortalama yerine p50/p95/p99 rapor edilir - ve kodla birlikte bu protokol Polars vs pandas içindedir. Budamaya özgü üç nokta:

  • Maskelenmiş olanı değil, yeniden oluşturulmuş modeli kıyaslayın. Parti boyutu 1'deki maskeli model, yoğun şekli ölçer.
  • Toplu iş boyutunu bildirin. Grup 1 (alıntılama döngüsü) ve grup 256 (araştırma taraması), belleğe bağlı/hesaplamaya bağlı satırın farklı taraflarında bulunur ve budama onlara farklı şekilde yardımcı olur.
  • Belirtilen etiket tanımıyla aynı ufukta, aynı bölünmede doğruluğu raporlayın. Eşleştirme doğruluğu sütununun bulunmadığı bir gecikme tablosu, modelin tamamen silinmesine yönelik bir argümandır.

İleri Teknikler

Gelişmiş ağ sıkıştırma yolları tek bir verimli modelde birleşiyor

Bilgi Damıtmayla Budama

Tek başına budama ve ince ayar yapmak yerine, orijinal yoğun modeli bir öğretmen olarak kullanın. Budanan öğrenci, görev kaybı ve öğretmenin çıktı dağılımından KL farklılığının birleşimini en aza indirir:

L=(1α)Ltask(y,y^s)+αT2DKL(σ(ztT)σ(zsT))\mathcal{L} = (1 - \alpha) \cdot \mathcal{L}_{\text{task}}(\mathbf{y}, \hat{\mathbf{y}}_s) + \alpha \cdot T^2 \cdot D_{\text{KL}}\left(\sigma\left(\frac{\mathbf{z}_t}{T}\right) \| \sigma\left(\frac{\mathbf{z}_s}{T}\right)\right)

Neresi zt\mathbf{z}_t Ve zs\mathbf{z}_s öğretmen ve öğrenci logitleri, TT sıcaklık ve α\alpha hedefleri dengeler. T2T^2 faktör, aksi takdirde küçülen damıtma gradyanlarını yeniden ölçeklendirir. 1/T21/T^2.

def distillation_loss(student_logits, teacher_logits, labels,
                      temperature=3.0, alpha=0.5):
    """Combined task + distillation loss."""
    task_loss = nn.CrossEntropyLoss()(student_logits, labels)

    soft_student = nn.functional.log_softmax(
        student_logits / temperature, dim=-1
    )
    soft_teacher = nn.functional.softmax(
        teacher_logits / temperature, dim=-1
    )
    kd_loss = nn.functional.kl_div(
        soft_student, soft_teacher, reduction='batchmean'
    )

    return (1 - alpha) * task_loss + alpha * (temperature ** 2) * kd_loss

Hareket Budama

Hareket budaması (Sanh ve diğerleri, 2020), mutlak büyüklüğe göre budamak yerine, eğitim sırasında sıfıra doğru hareket eden ağırlıkları budamaktadır. Önem puanı, gradyan ağırlık çarpımını toplar:

Sij(t)=Sij(t1)+Wij(t)Wij(t1)sign(Wij(t)LWij(t))S_{ij}^{(t)} = S_{ij}^{(t-1)} + \left| W_{ij}^{(t)} - W_{ij}^{(t-1)} \right| \cdot \text{sign}\left(W_{ij}^{(t)} \cdot \frac{\partial \mathcal{L}}{\partial W_{ij}^{(t)}}\right)

Negatif puanlı ağırlıklar budanır. Büyüklük budama konusundaki argüman özellikle ince ayar ile ilgilidir: Önceden eğitilmiş bir modeli uyarladığınızda, büyüklük dağılımı ön eğitim görevi tarafından şekillendirilir, dolayısıyla büyüklük eski bir önem sinyalidir ve seyahat yönü daha yeni bir sinyaldir. Dönen pencerelerde yeniden eğitilen bir ticaret modeli için bu, sıfırdan eğitimden daha yaygın bir durumdur.

NVIDIA 2:4 Yapılandırılmış Seyreklik

Amper ve üzeri NVIDIA GPU'lar donanımda 2:4 yapısal seyrekliği destekler: her 4 bitişik ağırlıktan tam olarak 2'si sıfır olmalıdır.

i,W[4i:4i+4]0=2\forall i, \quad \|\mathbf{W}_{[4i:4i+4]}\|_0 = 2

Bu, donanımın gerçekten ödüllendirdiği ince taneli seyrekliğin bir biçimidir, bu nedenle yapılandırılmamış budamadaki %90 sıfır sayısından daha önemlidir. Kısıtlama küresel olmaktan ziyade yereldir - her dört kişiden hangisinin hayatta kalacağı umrunda değildir - dolayısıyla küresel bir maskeyi sabitlemekten çok daha zayıf bir kısıtlamadır, ancak sunulan tek seyreklik seviyesi %50'dir.

from torch.ao.pruning import WeightNormSparsifier

sparsifier = WeightNormSparsifier(
    sparsity_level=0.5,
    sparse_block_shape=(1, 4),
    zeros_per_block=2,
)

sparsifier.prepare(
    model, config=[{"tensor_fqn": "network.0.weight"}]
)
sparsifier.step()
sparsifier.squash_mask()

Hızlanmanın gerçekleştirilmesi, seyrek tensör çekirdeklerini kullanmak için çıkarım yolunu gerektirir (bir ONNX dışa aktarma artı TensorRT yapısı) veya torch.sparse.to_sparse_semi_structured. Yoğun bir çalışma süresi aracılığıyla 2:4 maskeli bir modeli dışa aktarmak size doğruluk maliyeti getirir ve hiçbir fayda sağlamaz.

Üretim Dağıtımı

Düşük gecikmeli bir donanım kanalına giren yalın çıkarım modeli

Doğrulama

Budanmış bir model, sıkıştırılmış eski bir model değil, yeni bir modeldir ve diğer adaylarla aynı kabul kapısından geçer: [ileriye doğru ilerleme optimizasyonu](/tr/blog/post/ileriye doğru yürüme optimizasyonu) başına yuvarlanan yeniden eğitim ve örnek dışı yeniden doğrulama, sönük Sharpe oranından seçim etkisi düzeltmesi ile. Bu düzeltme burada isteğe bağlı değildir - IMP, aday modellerin bir dizisini oluşturur, bu nedenle arama altında on turda en iyi görünen seyreklik düzeyi seçildi ve Sharpe'ın etkin deneme sayısı kadar söndürülmesi gerekiyor. "Sharpe %5'ten fazla düşerse reddet" gibi düz bir kural bu aritmetikte geçerli değildir, bu yüzden bu makalede böyle bir kural bulamazsınız.

Niceleme Yığınlama

Budama kuantizasyonla oluşur. %90 seyrek ve INT8'e nicemlenmiş bir modelin sıkıştırma oranı şu şekildedir:

CR=1(1s)boriginalbquantized=10.1328=40×\text{CR} = \frac{1}{(1 - s)} \cdot \frac{b_{\text{original}}}{b_{\text{quantized}}} = \frac{1}{0.1} \cdot \frac{32}{8} = 40\times

48 MB'lık bir model 1,2 MB olur. Bu bir depolama iddiasıdır ve başka bir şey değildir. 1,2 MB modelinin aynı kararları üretip üretmediği, kendi cevabı olan ayrı bir sorudur ve bunu sormanın nedeni GPU hassas tuzağı'tir: bu blogda, tamamen makul görünen bir geriye dönük test hesaplamasında tek başına fp32'nin 211'lik göreceli bir hata ürettiği gösterilmiştir. INT8 bundan çok daha agresif bir indirgemedir. Ölçülen ve budanan bir modeli, fp32 yoğun modele karşı yalnızca niceliksel bir eşlik kapısının arkasına gönderin - karar anlaşması oranı ve ertelenmiş bir dönemde PnL deltası, bir garanti değil.

İzleme

Budanmış modeller dağıtım değişimine daha duyarlı olabilir. İzlemeye değer:

  • Aktivasyon seyrekliği: Hayatta kalan nöronlar çoğunlukla sıfırlar yayarsa, etkili model amaçlanandan daha küçüktür ve muhtemelen bozulur.
  • Yeniden eğitim sırasındaki eğim normları: Patlayan eğimler, hayatta kalan alt ağdan, kaldırılanları çok agresif bir şekilde telafi etmesinin istendiğini gösteriyor.
  • Tahmin entropisi: Gürültülü mikro yapı verilerine aşırı güvenen, budanmış bir model muhtemelen eğitim rejimine uygundur.

Sonuç

Yoğun belirsizlikten çözüm sağlayan kompakt, verimli sinir ağı

Yöntemler iyice yerleşmiştir ve tarama tamamlanana kadar bu makalenin iddia ettiği tek şey budur. Yapılandırılmamış budama size bir seyreklik numarası verir ve hız vermez. Yapılandırılmış budama, ancak ve ancak katmanları maskelemek yerine yeniden oluşturursanız size hız kazandırır. Piyango Bileti Hipotezi, kompakt modelin aşırı parametreli modelin içinde zaten mevcut olduğunu öne sürüyor; ancak bu, sipariş defteri verilerinde değil, görüntü kıyaslamalarında gösterilmiştir ve yukarıda verilen piyasa verileri üzerinde "çalışması" gereken üç neden, bulgular değil, deneyler içeren hipotezlerdir.

Literatürden elde edilen pratik buluşsal yöntem, başlangıçtan itibaren küçük tasarlamak yerine, büyük olanı eğitmek ve budamaktır: büyük model, kayıp manzarasını daha etkili bir şekilde araştırır ve budama, önemli olan yolları korur. Bunun bir ticaret modeli için geçerli olup olmadığı, ne kadar seyrek ve ne kadar doğruluk maliyetiyle bir IMP süpürmesidir - ve bu makale, bu taramadan sonra, içindeki rakamlarla birlikte tekrar okunmalıdır.

Sorumluluk Reddi: Bu makalede sağlanan bilgiler yalnızca eğitim ve bilgilendirme amaçlıdır ve finansal, yatırım veya ticaret tavsiyesi niteliği taşımaz. Kripto para ticareti önemli bir kayıp riski içerir.

Yazarlar

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

Piyasanın Önünde Olun

Özel yapay zeka ticaret içgörüleri, piyasa analizi ve platform güncellemeleri için bültenimize abone olun.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.