← Мақалаларға оралу
August 14, 2026
5 мин оқу

Model Pruning for Low-Latency Trading Inference

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

DeepLOB мақаламыз deployment section-ін үш тармақпен аяқтайды — ONNX және TensorRT, INT8 quantization, FPGA — бірақ олардың ешқайсысын ашпайды. Бұл мақала сол үшеуінің астарындағы бірінші мәселені толтырады: model қажет болғаннан үлкен. Neural network pruning redundant parameter-лерді алып тастайды, ал literature-дегі қызықты claim memory үнемдеу емес, weight-тердің 10-20%-ын сақтайтын subnetwork dense model accuracy-сіне жете алатыны.

Latency-дің маңызды екені блогта әлдеқашан көрсетілген — messaging path-тағы ZigBolt және break-even arithmetic-і бар IPC tax — ал spread modeling inference-latency row-ы бар gradient-boosting пен deep-learning кестесімен fast-but-slightly-worse және slow-but-better trade-off-ін қамтиды. Бірақ олардың ешқайсысы берілген model-ді қалай кішірейту керегін түсіндірмейді. Quoting loop кезеңдерінің ішінде model inference толық біздің бақылауымызда; transport legs reproducible p50/p95/p99 сандарымен algotrading үшін data communication мақаласында қамтылған.

Бұл мақала не туралы: trading MLP-ге қолданылған magnitude pruning, structured pruning, Iterative Magnitude Pruning, movement pruning, knowledge distillation және NVIDIA 2:4 semi-structured sparsity үшін math пен жұмыс істейтін code.

Бұл не емес: өлшенген result емес. Блогтағы әрбір empirical article provenance line немесе companion repo алып жүреді, ал бұл мақалада әзірге екеуі де жоқ. Sparsity-versus-accuracy-versus-latency curve төменде келтіру үшін дайын table емес, іске қосылатын experiment ретінде беріледі. Мұндағының бәрін method, ал сандарды pending деп қабылдаңыз.

Pruning не береді

Fast cache layer-ге сыятын compact neural network

Мұндағы constraint — size. Order book feature-лері үшін 2048 hidden unit-і бар 4-layer MLP, яғни mid-frequency model-ді қарастырайық:

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

din=100d_{\text{in}} = 100, h=2048h = 2048, L=4L = 4, dout=3d_{\text{out}} = 3 болғанда бұл шамамен 12.6 million parameter — float32-та 48 MB. L2 әдетте 1-4 MB, сондықтан weight-тер сыймайды және әр forward pass кезінде алысырақтан stream жасалады. Олардың 95%-ын prune етсеңіз, шамамен 630K effective parameter және 2.4 MB қалады, бұл L2-ге сыяды.

Оның wall-clock time-ға айналуы kernel memory-bound па, соған байланысты, ал бұл size емес, arithmetic-intensity сұрағы. Backtest engine speed ladder roofline model-ін (Williams, Waterman & Patterson) penalty factor-ін жай айтпай, өлшенген example арқылы көрсетеді; мұнда да сол framing және сол тәртіп керек: speedup claim жасамас бұрын жылжытылған byte санын өлшеңіз.

Pruning негіздері

Neural lattice бойындағы structured және unstructured pruning

Құрылымсыз pruning

Ең қарапайым тәсіл: жеке weight-терді magnitude-іне қарай нөлге қою. WRm×n\mathbf{W} \in \mathbb{R}^{m \times n} weight matrix-і берілсе, мынадай binary mask M{0,1}m×n\mathbf{M} \in \{0, 1\}^{m \times n} жасаңыз:

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

τ\tau — қажетті sparsity level ss-ке жету үшін таңдалған threshold:

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

Pruned matrix — W=WM\mathbf{W}' = \mathbf{W} \odot \mathbf{M}, мұнда \odot — Hadamard product. Интуициясы: нөлге жақын weight-тер layer output-ына аз үлес қосады.

Мәселе, sparsity санын қате түсіну оңай болғандықтан ашық айтайық: unstructured sparsity стандарт hardware-да speedup бермейді. 90% нөлі бар matrix sparse kernel-ге немесе sparsity қолдайтын hardware-ға ауыспасаңыз, дәл сол multiply-accumulate санын орындайды. Төмендегі code Sparsity: 90.0% шығарса, бұл нөлдердің саны ғана — ол ешқандай 10x емес, dense CPU GEMM-де 1.01x та емес. Уақыт үнемдейтін жолдар — structured pruning (кішірек matrix-тер) және 2:4 semi-structured sparsity (hardware support); екеуі де төменде.

Құрылымды pruning

Structured pruning толық neuron, channel немесе attention head-терді алып тастайды. y=Wx+b\mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b} linear layer-інде, WRm×n\mathbf{W} \in \mathbb{R}^{m \times n} болғанда, jj neuron-ін алып тастау jj-қатарды W\mathbf{W}-тен және jj элементті b\mathbf{b}-тен нөлдейді:

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

Ең кіші 2\ell_2-norm neuron-дер алдымен алынады. Бұл шынымен кішірек matrix жасайтын variant — бірақ layer-лерді шын мәнінде rebuild етсеңіз ғана. Қатарларды нөлдеп, tensor-ді бастапқы shape-інде қалдыру FLOP count-ты өзгертпейді; implementation section-дегі rebuild step mask-ті 512×n512 \times n matrix-ке айналдырады.

Convolutional layer-лердегі аналог — filter pruning. WRCout×Cin×k×k\mathbf{W} \in \mathbb{R}^{C_{\text{out}} \times C_{\text{in}} \times k \times k} берілсе, output filter ii маңыздылығы:

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 ii-ді алып тастау толық output channel-ді жояды және FLOP-ты пропорционалды азайтады.

Lottery Ticket Hypothesis

Үлкен neural lattice ішінен бөлінген шағын winning subnetwork

2019 жылы Frankle және Carbin Lottery Ticket Hypothesis (LTH) ұсынды: random initialized dense network ішінде бастапқы initialization-ынан train жасалғанда, iteration саны ұқсас болса да full network accuracy-сіне жететін sparse subnetwork — "winning ticket" — бар.

Formal түрде f(x;θ0)f(\mathbf{x}; \boldsymbol{\theta}_0), мұнда θ0Dθ\boldsymbol{\theta}_0 \sim \mathcal{D}_\theta арқылы initialized жасалған, model-ді қарастырыңыз. Convergence-ке дейін train жасағаннан кейін θ\boldsymbol{\theta}^* аламыз және pruning mask m\mathbf{m} шығарамыз. LTH мынадай m\mathbf{m} бар дейді:

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

мұнда m0θ0\|\mathbf{m}\|_0 \ll |\boldsymbol{\theta}_0|. Бастапқы experiment MNIST және CIFAR-10-да жасалды, онда winning ticket-тер parameter-лердің 10-20%-ын сақтады. Мұны order book data-ға автоматты түрде көшіруге болмайды — LOB feature-лері non-stationary, ал label noise-қа жақын; бұл image classification-нан маңызды болуы мүмкін барлық тұрғыда басқа regime.

Итеративті magnitude pruning (IMP)

Ticket IMP арқылы табылады:

  1. Network-ті θ0\boldsymbol{\theta}_0 арқылы initialize жасаңыз.
  2. Convergence-ке дейін train жасап, θ\boldsymbol{\theta}^* алыңыз.
  3. Magnitude-і ең кіші weight-тердің p%p\%-ын prune жасап, m\mathbf{m} mask-ін жасаңыз.
  4. Surviving weight-терді θ0\boldsymbol{\theta}_0 мәндеріне reset жасаңыз (rewinding).
  5. Mask салынған network-пен 2-қадамнан қайталаңыз.

Әр round pp fraction-ін (әдетте 20%) prune етеді, сондықтан nn round-тан кейін parameter-лердің (1p)n(1-p)^n бөлігі қалады. p=0.2p = 0.2 болған 10 round-тан кейін шамамен 10.7% қалады.

Trading model-дер туралы үш hypothesis, ешқайсысы тексерілмеген

LTH market data-да ерекше жақсы жұмыс істеуі тиіс деп айту қызықты. Мұндай үш argument бар; үшеуі де hypothesis, оларды fact ретінде айту — блог болдырмауға тырысатын failure mode.

  1. Financial signal-дар sparse. Order book snapshot-ының көбі noise, сондықтан sparse subnetwork sparse signal-мен табиғи түрде aligned болуы мүмкін. Testable: IMP-ті дәл сондай sparsity-і бар random mask-пен салыстырыңыз; егер жұмысты sparsity өзі істесе, random mask қатты артта қалмауы тиіс.
  2. Winning ticket-тер regime-дер арасында generalize етеді. Бұл citation-ы жоқ нарық туралы empirical claim және үшеуінің ішіндегі ең қызықтысы. Оны HMM арқылы regime detection мақаласындағы regime label-дермен тікелей тексеруге болады: ticket-ті A regime-де табыңыз, B regime-де қайта train жасап, B-де native табылған ticket-пен салыстырыңыз.
  3. Sparsity regularize етеді. Төмен effective capacity microstructure noise-ына fit-ті азайтуы мүмкін — бұл pruned model-дің out-of-sample gap-і dense model-дікімен жай ғана салыстырмалы емес, кіші болуынан көрінеді.

Іске асыру: trading MLP-ін pruning жасау

Нақты sparse mask арқылы өзгертілген neural layer-лер

Бастапқы 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():,}")

Құрылымсыз magnitude pruning

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

Құрылымды pruning және оны шынайы ететін rebuild

Қатарларды mask жасау — жұмыстың жартысы. Speedup беретін жартысы әр layer-ді кішірейтілген shape-імен rebuild ету — яғни алып тастауды алға propagation жасау: jj row-ын ii layer-інен тастау jj column-ды i+1i+1 layer-інен және олардың арасындағы кез келген BatchNorm1d-дің jj channel-ін де тастайды.

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

Бұған сенбес бұрын, блогтың қалған бөлігіндегі equivalence gate-тер рухында екі нәрсені тексеріңіз:

  • Shapes. rebuilt (1fraction)h(1 - \text{fraction}) \cdot h деңгейіндегі hidden dimension-дерді көрсетуі керек — fraction=0.75, h=2048h = 2048 үшін 512 — әрі ішкі matrix-тердің екі өлшемі де кішірейетіндіктен, parameter count quadratic түрде азаюы тиіс.
  • Outputs. eval() mode-ында rebuilt(x) сол batch-тегі masked model-дің rebuild-ке дейінгі output-ымен floating-point tolerance шегінде сәйкес болуы керек. Сәйкес болмаса, column propagation қате және downstream сандарының бәрі сіз ойлағаннан басқа model-ді өлшеп тұр.

Row-survival test masked row дәл нөл, ал live row нөл емес деп ұйғарады. Бұл ln_structured output-ы үшін орындалады; басқа procedure шынымен нөлдік live neuron жасаса, орындалмас еді. Сондықтан norm test-ке соқыр сенбей, survivor count-ты сұралған fraction-мен assert етіңіз.

Итеративті magnitude pruning (Lottery Ticket Search)

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 — осы мақала ұсынуға тиіс sparsity-versus-accuracy curve-інің шикі материалы. eval_fn purged split-терде шын мәнінде out-of-sample болуы керек — in-sample бағаланған IMP run ештеңе білдірмейтін әдемі curve көрсетеді.

Оны өлшеу

Sparsity, accuracy және latency-ді теңестіретін abstract efficiency frontier

Latency блогтың қалған бөлігімен бірдей harness convention арқылы өлшенеді — warmup есепке алынбайды, best-of-N, mean орнына p50/p95/p99 хабарланады — code-ы бар protocol Polars vs pandas мақаласында. Pruning-ге тән үш тармақ:

  • Rebuilt model-ді benchmark жасаңыз, masked model-ді емес. Batch size 1 кезіндегі masked model dense shape-ті өлшейді.
  • Batch size-ті хабарлаңыз. Batch 1 (quoting loop) және batch 256 (research sweep) memory-bound/compute-bound сызығының екі жағында орналасады, pruning оларға әртүрлі көмектеседі.
  • Accuracy-ді сол split-те, сол horizon-да, label definition-ін көрсетіп хабарлаңыз. Сәйкес accuracy column-ы жоқ latency table model-ді толық жоюға дәлел болады.

Жетілдірілген тәсілдер

Бір efficient model-ге тоғысатын advanced network-compression жолдары

Білімді дистилляциялау арқылы pruning

Pruning пен fine-tuning-ді бөлек жасаудың орнына, бастапқы dense model-ді teacher ретінде пайдаланыңыз. Pruned student task loss пен teacher output distribution-ынан KL divergence комбинациясын минимизациялайды:

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)

мұнда zt\mathbf{z}_t және zs\mathbf{z}_s teacher мен student logit-тері, TT — temperature, α\alpha мақсаттар балансын басқарады. T2T^2 factor distillation gradient-терін қайта масштабтайды, онсыз олар 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

Movement pruning

Absolute magnitude бойынша prune жасаудың орнына, movement pruning (Sanh et al., 2020) training кезінде нөлге қарай жылжып жатқан weight-терді prune етеді. Importance score gradient-weight product-ін жинақтайды:

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)

Negative score-і бар weight-тер prune жасалады. Оның magnitude pruning-нен артықшылығы fine-tuning-ге қатысты: pre-trained model-ді бейімдегенде magnitude distribution pre-training task арқылы қалыптасқан, сондықтан magnitude — ескірген importance signal, ал қозғалыс бағыты — жаңарақ signal. Rolling window-дерде қайта train жасалатын trading model үшін бұл scratch-тен training-ге қарағанда жиірек кездесетін жағдай.

NVIDIA 2:4 құрылымды sparsity

Ampere және кейінгі NVIDIA GPU-лары hardware деңгейінде 2:4 structured sparsity-ді қолдайды: қатар тұрған әр 4 weight-тің дәл 2-еуі нөл болуы тиіс.

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

Бұл hardware шын мәнінде сыйақы беретін fine-grained sparsity-дің жалғыз түрі, сондықтан unstructured pruning-дегі 90%-zeros санынан маңыздырақ. Constraint global емес, local — әр төрттіктің қай екеуі қалатыны маңызды емес — сондықтан global mask бекітуден әлдеқайда әлсіз шектеу, бірақ ұсынылатын жалғыз sparsity level — 50%.

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

Speedup-ты іске асыру үшін inference path sparse tensor core-ларды пайдалануы керек — ONNX export пен TensorRT build немесе torch.sparse.to_sparse_semi_structured. 2:4-masked model-ді dense runtime арқылы export ету accuracy cost-ін береді, бірақ пайда бермейді.

Production-ға енгізу

Low-latency hardware conduit-ке кіріп жатқан lean inference model

Валидация

Pruned model — compressed old model емес, жаңа model және кез келген басқа candidate сияқты acceptance gate-тен өтеді: walk-forward optimization бойынша rolling retraining және out-of-sample revalidation, deflated Sharpe ratio арқылы selection-effect correction. Бұл correction мұнда optional емес — IMP candidate model-дердің sequence-ін жасайды, сондықтан он round ішінде ең жақсы көрінген sparsity level search астында таңдалған, оның Sharpe-ін effective trial санына қарай deflate ету керек. "Sharpe 5%-дан көп түссе reject ет" сияқты flat rule бұл arithmetic-тен өтпейді, сондықтан оны бұл мақаладан таппайсыз.

Quantization-ды қабаттастыру

Pruning quantization-мен бірге қолданылады. 90% sparse және INT8-ге quantized жасалған model-дің compression ratio-сы:

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 model 1.2 MB болады. Бұл тек storage claim, басқа ештеңе емес. 1.2 MB model дәл сондай decision шығара ма — өз жауабы бар бөлек сұрақ. Оны қоюға GPU precision trap себеп: осы блогта fp32-тің өзі толық орынды көрінген backtest computation-да 211 relative error берген. INT8 одан да агрессивті reduction. Quantized-and-pruned model-ді fp32 dense model-ге қатысты quantified parity gate артында ғана ship етіңіз — held-out period-тегі decision agreement rate және PnL delta арқылы, assurance арқылы емес.

Мониторинг

Pruned model-дер distribution shift-ке сезімтал болуы мүмкін. Бақылауға тұрарлық нәрселер:

  • Activation sparsity: surviving neuron-дер негізінен нөл шығарса, effective model ойлағаннан кішірек және нашарлап жатқан болуы мүмкін.
  • Retraining кезіндегі gradient norm-дар: exploding gradient-тер surviving subnetwork-тен алып тасталған нәрсені тым агрессивті өтеу талап етіліп жатқанын көрсетеді.
  • Prediction entropy: noisy microstructure data-да overconfident болып кеткен pruned model training regime-ге fit жасап жатқан болуы ықтимал.

Қорытынды

Dense uncertainty-ден ажырап шыққан compact efficient neural network

Бұл әдістер жақсы қалыптасқан, ал sweep іске қосылғанша мақала осыны ғана claim етеді. Unstructured pruning sparsity санын береді, speed бермейді. Structured pruning layer-лерді mask етпей, rebuild етсеңіз ғана speed береді. Lottery Ticket Hypothesis compact model overparameterized model ішінде әлдеқашан бар деп болжайды, бірақ бұл image benchmark-терінде көрсетілген, order book data-да емес; жоғарыда келтірілген оның market data-да "жұмыс істеуі тиіс" деген үш себебі — experiment-термен бекітілген hypothesis-тер, finding-тер емес.

Literature-дегі practical heuristic — басынан small model design етудің орнына, үлкен model-ді train жасап, кейін prune ету: үлкен model loss landscape-ті тиімдірек зерттейді, ал pruning маңызды болған жолдарды сақтайды. Бұл trading model үшін, қандай sparsity-де және қандай accuracy cost-пен орындалатыны бір IMP sweep қашықтықта — осы мақаланы сол sweep-тен кейін, ішіндегі сандармен қайта оқу керек.

blog.disclaimer

Authors

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

Нарықтан бір қадам алда болыңыз

AI сауда талдаулары, нарық аналитикасы және платформа жаңалықтары үшін біздің ақпараттық бюллетеньге жазылыңыз.

Біз сіздің жекелігіңізді құрметтейміз. Кез келген уақытта жазылымнан шығуға болады.