← Terug naar artikelen
August 14, 2026
5 min leestijd

Model Pruning for Low-Latency Trading Inference

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

Ons [DeepLOB-artikel] (/en/blog/post/deeplob-deep-learning-order-book) eindigt het implementatiegedeelte met drie opsommingen – ONNX plus TensorRT, INT8-kwantisering, FPGA – en geen enkele behandeling ervan. Dit artikel is de ontbrekende oplossing voor het eerste probleem onder alle drie: het model is groter dan nodig is. Door het snoeien van neurale netwerken worden overtollige parameters verwijderd, en de interessante bewering in de literatuur is niet dat dit geheugen bespaart, maar dat een subnetwerk dat 10-20% van de gewichten in handen heeft, de nauwkeurigheid van het compacte model kan evenaren.

Dat de latentie er überhaupt toe doet, is een bewering die de blog al heeft gedaan — ZigBolt op het berichtenpad en de IPC tax met de break-even rekenkunde — en spread modeling is al eigenaar van de snelle maar iets slechtere versus trage maar betere afweging, compleet met een gradiëntversterkende versus diepgaande leertabel met een rij inferentie-latentie. Wat geen van hen behandelt, is hoe je een bepaald model kleiner maakt. Van de stadia in een citaatlus hebben we de modelinferentie volledig onder controle; de transporttrajecten zijn bedekt, met reproduceerbare p50/p95/p99-nummers, in datacommunicatie voor algotrading.

Wat dit artikel is: de wiskundige en werkcode voor magnitude-pruning, gestructureerde pruning, Iterative Magnitude Pruning, bewegings-pruning, kennisdestillatie en NVIDIA 2:4 semi-gestructureerde sparsity, toegepast op een handels-MLP.

Wat het niet is: een gemeten resultaat. Elk empirisch artikel op deze blog bevat een herkomstregel of een bijbehorende repository, en dit bevat nog geen van beide. De curve van spaarzaamheid versus nauwkeurigheid versus latentie wordt hieronder vermeld als een experiment om uit te voeren, niet als een tabel om te citeren. Beschouw alles hier als de methode en de cijfers als in behandeling.

Wat snoeien je oplevert

Compact neuraal netwerk passend in een snelle cachelaag

De beperking is een maat één. Overweeg een middenfrequentiemodel – een 4-laags MLP met 2048 verborgen eenheden boven orderboekfuncties:

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

Voor din=100d_{\text{in}} = 100, h=2048h = 2048, L=4L = 4, dout=3d_{\text{out}} = 3, dat zijn grofweg 12,6 miljoen parameters - ongeveer 48 MB in float32. L2 is doorgaans 1-4 MB, dus de gewichten passen niet; ze worden bij elke voorwaartse pass van verder weg gestreamd. Snoei 95% ervan en je hebt ongeveer 630.000 effectieve parameters en 2,4 MB, wat wel past.

Of dat zich vertaalt in wandkloktijd hangt af van de vraag of de kernel geheugengebonden is, en dat is eerder een kwestie van rekenkundige intensiteit dan van een kwestie van grootte. De backtest engine speed ladder werkt het daklijnmodel (Williams, Waterman & Patterson) uit aan de hand van een gemeten voorbeeld in plaats van een straffactor te hanteren; dezelfde framing is hier van toepassing, en dezelfde discipline zou: de verplaatste bytes moeten meten voordat de versnelling wordt geclaimd.

Basisprincipes van snoeien

Gestructureerd en ongestructureerd snoeien over een neuraal rooster

Ongestructureerd snoeien

De eenvoudigste aanpak: stel individuele gewichten in op nul op basis van hun omvang. Gegeven een gewichtsmatrix WRm×n\mathbf{W} \in \mathbb{R}^{m \times n}, maak een binair masker M{0,1}m×n\mathbf{M} \in \{0, 1\}^{m \times n} zodanig dat:

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

waar τ\tau is een drempel die is gekozen om het gewenste spaarzaamheidsniveau te bereiken ss:

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

De gesnoeide matrix is W=WM\mathbf{W}' = \mathbf{W} \odot \mathbf{M}, met \odot het Hadamard-product. De intuïtie is dat gewichten van bijna nul weinig bijdragen aan de output van de laag.

Het probleem, duidelijk geformuleerd omdat het gemakkelijk is om het sparsity-nummer verkeerd te lezen: ongestructureerde sparsity vertaalt zich niet in een versnelling van standaardhardware. Een matrix met 90% nullen geeft nog steeds hetzelfde aantal multiply-accumulates af, tenzij je overschakelt naar spaarzame kernels of hardware met spaarzaamheidsondersteuning. Wanneer de onderstaande code wordt afgedrukt Sparsity: 90.0%, dat is een telling van nullen - het is niets 10x, en op een GEMM met een dichte CPU is het ook geen 1.01x. De paden die wel tijd kopen zijn gestructureerd snoeien (kleinere matrices) en 2:4 semi-gestructureerde sparsity (hardwareondersteuning), beide hieronder.

Gestructureerd snoeien

Gestructureerd snoeien verwijdert hele neuronen, kanalen of aandachtshoofden. Voor een lineaire laag y=Wx+b\mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b} met WRm×n\mathbf{W} \in \mathbb{R}^{m \times n}, neuron verwijderen jj nult de jj-de rij van W\mathbf{W} en de jj-de element van b\mathbf{b}:

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

Neuronen met de kleinste 2\ell_2-normeer eerst. Dit is de variant die echt kleinere matrices oplevert, maar alleen als je de lagen daadwerkelijk opnieuw opbouwt. Het op nul zetten van rijen en het in zijn oorspronkelijke vorm laten van de tensor verandert niets aan de FLOP-telling; de herbouwstap in de implementatiesectie is wat het masker omzet in een 512×n512 \times n matrix.

Voor convolutionele lagen is het analogon filtersnoei. Gegeven WRCout×Cin×k×k\mathbf{W} \in \mathbb{R}^{C_{\text{out}} \times C_{\text{in}} \times k \times k}, het belang van een uitvoerfilter ii is:

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}

Filter verwijderen ii elimineert een volledig uitgangskanaal, waardoor de FLOP's proportioneel worden verminderd.

De loterijhypothese

Klein winnend subnetwerk geïsoleerd binnen een enorm neuraal rooster

In 2019 introduceerden Frankle en Carbin de Lottery Ticket Hypothesis (LTH): binnen een willekeurig geïnitialiseerd dicht netwerk bestaat er een schaars subnetwerk - een 'winnend ticket' - dat, getraind vanaf de oorspronkelijke initialisatie, overeenkomt met de nauwkeurigheid van het volledige netwerk in een vergelijkbaar aantal iteraties.

Formeel, overweeg f(x;θ0)f(\mathbf{x}; \boldsymbol{\theta}_0) geïnitialiseerd met θ0Dθ\boldsymbol{\theta}_0 \sim \mathcal{D}_\theta. Na training tot convergentie verkrijgen we θ\boldsymbol{\theta}^* en leid een snoeimasker af m\mathbf{m}. De LTH stelt dat dit bestaat m\mathbf{m} zodanig dat:

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}^*)

met m0θ0\|\mathbf{m}\|_0 \ll |\boldsymbol{\theta}_0|. De oorspronkelijke experimenten waren op MNIST en CIFAR-10, waarbij winnende tickets 10-20% van de parameters behielden. Niets daarover wordt op basis van aanname overgebracht naar het orderboek - LOB-kenmerken zijn niet-stationair en het label is bijna ruis, wat een ander regime is dan beeldclassificatie op precies de manieren die er toe doen.

Iteratief snoeien op grootte (IMP)

Het ticket wordt gevonden door IMP:

  1. Initialiseer het netwerk met θ0\boldsymbol{\theta}_0.
  2. Train naar convergentie, verkrijgen θ\boldsymbol{\theta}^*.
  3. Snoei de p%p\% van gewichten met de kleinste omvang, waardoor een masker ontstaat m\mathbf{m}.
  4. Reset de overgebleven gewichten naar hun waarden in θ0\boldsymbol{\theta}_0 (terugspoelen).
  5. Herhaal vanaf stap 2 met het gemaskeerde netwerk.

Elke ronde snoeit een fractie pp (meestal 20%), dus daarna nn rondes (1p)n(1-p)^n van de parameters blijft bestaan. Na 10 ronden om p=0.2p = 0.2blijft er ongeveer 10,7% over.

Drie hypothesen over handelsmodellen, geen enkele getest

Het is verleidelijk om te beweren dat LTH vooral goed zou moeten werken op basis van marktgegevens. Er komen drie van dergelijke argumenten naar voren; het zijn alle drie hypothesen, en het als feiten verkondigen ervan is de manier waarop deze blog probeert te falen.

  1. Financiële signalen zijn schaars. Het grootste deel van een momentopname van een orderboek bestaat uit ruis, dus een schaars subnetwerk kan op natuurlijke wijze in lijn zijn met een schaars signaal. Testbaar: vergelijk IMP met een willekeurig masker met dezelfde schaarsheid; als de spaarzaamheid zelf het werk doet, zou het willekeurige masker niet ver achter moeten blijven.
  2. Winnende kaartjes worden algemeen verspreid over regimes. Dit is een empirische bewering over markten zonder enige bronvermelding, en het is de meest interessante van de drie. Het is direct toetsbaar aan de regimelabels van regime detectie met HMMs: zoek het ticket in regime A, train het opnieuw in regime B en vergelijk het met een ticket dat oorspronkelijk in B is gevonden.
  3. De schaarsheid wordt geregulariseerd. Een lagere effectieve capaciteit kan de aanpassing aan microstructuurruis verminderen - wat zou blijken uit het feit dat de buiten-steekproefafstand van het gesnoeide model kleiner is dan die van het dichte model, en niet alleen maar vergelijkbaar.

Implementatie: een handels-MLP snoeien

Neurale lagen getransformeerd door een nauwkeurig dun masker

Het basismodel

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():,}")

Ongestructureerd snoeien op grootte

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%}")

Gestructureerd snoeien, met de wederopbouw die het echt maakt

Rijen maskeren is het halve werk. De helft die de versnelling produceert, herbouwt elke laag in zijn gereduceerde vorm – wat betekent dat de verwijdering naar voren wordt voortgeplant: drop-row jj van laag ii laat ook een kolom vallen jj van laag i+1i+1 en kanaal jj van welke dan ook BatchNorm1d tussen hen.

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

Twee dingen die u moet controleren voordat u hierop vertrouwt, in dezelfde geest als de gelijkwaardigheid die de rest van de blog kenmerkt:

  • Vormen. rebuilt zou verborgen afmetingen moeten tonen op (1fraction)h(1 - \text{fraction}) \cdot h — 512 voor fraction=0.75, h=2048h = 2048 – en een aantal parameters dat kwadratisch is gedaald, omdat beide dimensies van de interne matrices kleiner worden.
  • Uitgangen. In eval() modus, rebuilt(x) moet overeenkomen met die van het gemaskerde model rebuilt-vrije uitvoer naar tolerantie met drijvende komma voor dezelfde batch. Als dit niet het geval is, is de kolomvoortplanting verkeerd en meet elk stroomafwaarts getal een ander model dan u denkt.

Bij de rij-overlevingstest wordt ervan uitgegaan dat een gemaskeerde rij precies nul is, en een live-rij niet. Dat geldt voor ln_structured uitvoer; het zou niet standhouden als een andere procedure een echt levend neuron van nul zou opleveren, dus laat de overlevende meetellen voor de gevraagde fractie in plaats van blindelings op de normtest te vertrouwen.

Iteratief snoeien op grootte (zoeken naar loten)

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 is de grondstof voor de schaarsheid-versus-nauwkeurigheidscurve die dit artikel u verschuldigd is. eval_fn moet echt buiten de steekproef liggen, bij opgeschoonde splitsingen - een IMP-run die in de steekproef wordt gescoord, zal een mooie curve rapporteren die niets betekent.

Meten

Abstracte efficiëntiegrens die schaarse nauwkeurigheid en latentie in evenwicht brengt

De latentie wordt gemeten met dezelfde harnasconventie als de rest van de blog – opwarming uitgesloten, best-of-N, p50/p95/p99 gerapporteerd in plaats van een gemiddelde – en dat protocol, met de code, staat in Polars vs pandas. Drie punten specifiek voor snoeien:

  • Benchmark het herbouwde model, niet het gemaskeerde model. Een gemaskerd model met batchgrootte 1 meet de dichte vorm.
  • Rapporteer de batchgrootte. Batch 1 (citerende lus) en batch 256 (onderzoeksopruiming) bevinden zich aan verschillende kanten van de geheugen-/rekengebonden lijn, en snoeien helpt ze op verschillende manieren.
  • Rapporteer nauwkeurigheid op dezelfde splitsing, op dezelfde horizon, met de labeldefinitie vermeld. Een latentietabel zonder de overeenkomende nauwkeurigheidskolom is een argument om het model volledig te verwijderen.

Geavanceerde technieken

Geavanceerde netwerkcompressiepaden die samenkomen in één efficiënt model

Snoeien met kennisdestillatie

In plaats van op zichzelf te snoeien en te verfijnen, kun je als leraar het oorspronkelijke compacte model gebruiken. De gesnoeide leerling minimaliseert een combinatie van taakverlies en de KL-afwijking van de outputverdeling van de leraar:

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)

waar zt\mathbf{z}_t En zs\mathbf{z}_s zijn docenten- en leerlinglogits, TT is de temperatuur, en α\alpha brengt de doelstellingen in evenwicht. De T2T^2 factor herschaalt de destillatiegradiënten, die anders kleiner worden 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

Beweging Snoeien

In plaats van te snoeien op absolute grootte, snoeit bewegingssnoei (Sanh et al., 2020) gewichten die tijdens de training naar nul toe bewegen. De belangrijkheidsscore telt het gradiënt-gewichtsproduct op:

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)

Gewichten met negatieve scores worden gesnoeid. Het argument daarvoor over het snoeien van de omvang gaat specifiek over het verfijnen: wanneer je een vooraf getraind model aanpast, werd de omvangsverdeling gevormd door de taak vóór de training, dus de omvang is een oud belangsignaal en de reisrichting is een recenter signaal. Voor een handelsmodel dat is omgeschoold op rollende vensters, is dat de meest voorkomende situatie dan helemaal opnieuw trainen.

NVIDIA 2:4 gestructureerde spaarzaamheid

NVIDIA GPU's van Ampere en later ondersteunen 2:4 gestructureerde spaarzaamheid in hardware: van elke vier opeenvolgende gewichten moeten er precies twee nul zijn.

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

Dit is de enige vorm van fijnmazige spaarzaamheid die de hardware daadwerkelijk beloont, en daarom is het belangrijker dan het 90%-nulgetal van ongestructureerd snoeien. De beperking is eerder lokaal dan mondiaal – het maakt niet uit welke twee van de vier overleven – dus het is een veel zwakkere beperking dan het opzetten van een mondiaal masker, ook al is 50% het enige spaarniveau dat wordt aangeboden.

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()

Om de versnelling te realiseren is het inferentiepad nodig om de schaarse tensorkernen te gebruiken - een ONNX-export plus TensorRT-build, of torch.sparse.to_sparse_semi_structured. Het exporteren van een 2:4-gemaskeerd model via een compacte runtime levert u de nauwkeurigheidskosten op, maar geen enkel voordeel.

Productie-implementatie

Lean inferentiemodel dat een hardwarekanaal met lage latentie binnengaat

Validatie

Een gesnoeid model is een nieuw model, niet een gecomprimeerd oud model, en het gaat door dezelfde acceptatiepoort als elke andere kandidaat: doorlopende herscholing en hervalidatie buiten de steekproef per [walk-forward optimalisatie] (/en/blog/post/walk-forward-optimization), met de selectie-effectcorrectie van [de gedeflateerde Sharpe-ratio] (/en/blog/post/deflated-sharpe-multiple-testing). Die correctie is hier niet optioneel: IMP genereert een reeks kandidaat-modellen, dus tijdens de zoekopdracht werd het sparsity-niveau gekozen dat er het beste uitziet over tien ronden, en de Sharpe moet worden gedefleerd met het effectieve aantal pogingen. Een platte regel als "afwijzen als Sharpe meer dan 5% daalt" overleeft deze rekenkunde niet, en daarom zul je er in dit artikel geen vinden.

Kwantiseringsstapeling

Snoeien componeert met kwantisering. Een model dat voor 90% schaars is en gekwantiseerd op INT8, heeft een compressieverhouding van:

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

Een model van 48 MB wordt 1,2 MB. Dat is een opslagclaim en niets meer. Of het 1,2 MB-model dezelfde beslissingen levert is een aparte vraag met een eigen antwoord, en de GPU-precisieval is de reden om deze vraag te stellen: op deze blog is aangetoond dat fp32 alleen al een relatieve fout van 211 oplevert in een backtest-berekening die er volkomen redelijk uitzag. INT8 is een veel agressievere reductie dan dat. Stuur een gekwantiseerd en gesnoeid model alleen achter een gekwantificeerde pariteitspoort ten opzichte van het FP32-dichte model - beslissingsovereenkomstpercentage en PnL-delta over een uitgestelde periode, geen garantie.

Toezicht

Gesnoeide modellen kunnen gevoeliger zijn voor distributieverschuivingen. Het bekijken waard:

  • Activeringssparsiteit: als overlevende neuronen voornamelijk nullen uitzenden, is het effectieve model kleiner dan bedoeld en waarschijnlijk vernederend.
  • Gradientnormen tijdens hertraining: exploderende gradiënten suggereren dat aan het overgebleven subnetwerk wordt gevraagd om te agressief te compenseren voor wat is verwijderd.
  • Voorspellingsentropie: een gesnoeid model dat overmoedig wordt op basis van luidruchtige microstructuurgegevens, past waarschijnlijk in het trainingsregime.

Conclusie

Compact, efficiënt neuraal netwerk dat een oplossing biedt voor grote onzekerheid

De methoden zijn goed ingeburgerd en, totdat de sweep loopt, is dat alles wat dit artikel beweert. Ongestructureerd snoeien geeft u een schaars getal en geen snelheid. Gestructureerd snoeien geeft u snelheid als – en alleen als – u de lagen opnieuw opbouwt in plaats van ze te maskeren. De Lottery Ticket Hypothese suggereert dat het compacte model al bestaat binnen het overgeparameteriseerde model, hoewel dat is aangetoond op basis van beeldbenchmarks en niet op basis van orderboekgegevens. De drie redenen waarom het zou moeten werken op basis van marktgegevens die hierboven zijn gegeven, zijn hypothesen met daaraan gekoppelde experimenten, en geen bevindingen.

De praktische heuristiek uit de literatuur is om groot te trainen en te snoeien in plaats van vanaf het begin klein te ontwerpen: het grote model onderzoekt het verlieslandschap effectiever, en snoeien behoudt de paden die er toe deden. Of dat geldt voor een handelsmodel, tegen welke schaarsheid en tegen welke nauwkeurigheidskosten, is één IMP-sweep verwijderd – en dit artikel zou na die sweep opnieuw moeten worden gelezen, met cijfers erin.

Disclaimer: De informatie in dit artikel is uitsluitend bedoeld voor educatieve en informatieve doeleinden en vormt geen financieel, beleggings- of handelsadvies. Het handelen in cryptovaluta brengt een aanzienlijk risico op verlies met zich mee.

Auteurs

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

Blijf de markt voor

Abonneer je op onze nieuwsbrief voor exclusieve AI-handelsinzichten, marktanalyses en platformupdates.

We respecteren je privacy. Je kunt je op elk moment afmelden.