← Voltar aos artigos
August 14, 2026
5 min read

Model Pruning for Low-Latency Trading Inference

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

Nosso artigo DeepLOB termina sua seção de implantação com três marcadores – ONNX mais TensorRT, quantização INT8, FPGA – e nenhum tratamento de nenhum deles. Este artigo é o tratamento que falta para o primeiro problema subjacente a todos os três: o modelo é maior do que precisa ser. A poda da rede neural remove parâmetros redundantes, e a afirmação interessante na literatura não é que isso economize memória, mas que uma sub-rede contendo 10-20% dos pesos pode corresponder à precisão do modelo denso.

Que a latência importa é um caso que o blog já fez - ZigBolt no caminho de mensagens e o imposto IPC com a aritmética do ponto de equilíbrio - e modelagem de spread já possui a troca entre rápido, mas um pouco pior, versus lento, mas melhor, completa com uma tabela de aumento de gradiente versus aprendizado profundo que possui uma linha de latência de inferência. O que nenhum deles cobre é como tornar um determinado modelo menor. Dos estágios de um ciclo de cotação, a inferência do modelo é aquela que está totalmente sob nosso controle; os trechos de transporte são cobertos, com números p50/p95/p99 reproduzíveis, em comunicação de dados para algotrading.

O que este artigo é: a matemática e o código de trabalho para poda de magnitude, poda estruturada, poda de magnitude iterativa, poda de movimento, destilação de conhecimento e dispersão semiestruturada NVIDIA 2:4, aplicado a um MLP comercial.

O que não é: um resultado medido. Cada artigo empírico neste blog traz uma linha de proveniência ou um repositório complementar, e este ainda não traz nenhum dos dois. A curva de dispersão versus precisão versus latência é apresentada abaixo como um experimento a ser executado, não como uma tabela a ser citada. Trate tudo aqui como método e os números como pendentes.

O que a poda compra para você

Rede neural compacta ajustada em uma camada de cache rápida

A restrição é de tamanho um. Considere um modelo de frequência média - um MLP de 4 camadas com 2.048 unidades ocultas sobre recursos de carteira de pedidos:

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

Para din=100d_{\text{in}} = 100, h=2048h = 2048, L=4L = 4, dout=3d_{\text{out}} = 3, isso representa aproximadamente 12,6 milhões de parâmetros – cerca de 48 MB em float32. L2 normalmente tem de 1 a 4 MB, portanto os pesos não cabem; eles são transmitidos de mais longe em cada passe para frente. Remova 95% deles e você terá aproximadamente 630 mil parâmetros efetivos e 2,4 MB, o que cabe.

Se isso se traduz em tempo de relógio depende se o kernel está vinculado à memória, e essa é uma questão de intensidade aritmética e não de tamanho. A escada de velocidade do motor de backtest trabalha o modelo da linha do teto (Williams, Waterman & Patterson) por meio de um exemplo medido em vez de afirmar um fator de penalidade; o mesmo enquadramento se aplica aqui, e a mesma disciplina deve: medir os bytes movidos antes de reivindicar a aceleração.

Fundamentos de poda

Poda estruturada e não estruturada em uma rede neural

Poda não estruturada

A abordagem mais simples: defina os pesos individuais como zero com base em sua magnitude. Dada uma matriz de peso WRm×n\mathbf{W} \in \mathbb{R}^{m \times n}, crie uma máscara binária M{0,1}m×n\mathbf{M} \in \{0, 1\}^{m \times n} tal que:

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

onde τ\tau é um limite escolhido para atingir o nível de dispersão desejado ss:

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

A matriz podada é W=WM\mathbf{W}' = \mathbf{W} \odot \mathbf{M}, com \odot o produto Hadamard. A intuição é que pesos próximos de zero contribuem pouco para a saída da camada.

O problema, declarado claramente porque é fácil interpretar mal o número de dispersão: a dispersão não estruturada não se traduz em aceleração no hardware padrão. Uma matriz com 90% de zeros ainda emite o mesmo número de acumulações multiplicadas, a menos que você mude para kernels esparsos ou hardware com suporte à dispersão. Quando o código abaixo for impresso Sparsity: 90.0%, isso é uma contagem de zeros - não é 10x nada, e em uma CPU GEMM densa também não é 1,01x. Os caminhos que ganham tempo são a poda estruturada (matrizes menores) e a dispersão semiestruturada 2:4 (suporte de hardware), ambos abaixo.

Poda Estruturada

A poda estruturada remove neurônios, canais ou cabeças de atenção inteiras. Para uma camada linear y=Wx+b\mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b} com WRm×n\mathbf{W} \in \mathbb{R}^{m \times n}, removendo neurônio jj zera o jj-ésima linha de W\mathbf{W} e o jj-º elemento de b\mathbf{b}:

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

Neurônios com o menor 2\ell_2-norma vá primeiro. Esta é a variante que produz matrizes genuinamente menores — mas somente se você realmente reconstruir as camadas. Zerar linhas e deixar o tensor em sua forma original não altera nada na contagem de FLOP; a etapa de reconstrução na seção de implementação é o que converte a máscara em um 512×n512 \times n matriz.

Para camadas convolucionais, o análogo é a poda de filtro. Dado WRCout×Cin×k×k\mathbf{W} \in \mathbb{R}^{C_{\text{out}} \times C_{\text{in}} \times k \times k}, a importância do filtro de saída 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}

Removendo filtro ii elimina um canal de saída inteiro, reduzindo FLOPs proporcionalmente.

A hipótese do bilhete de loteria

Pequena sub-rede vencedora isolada dentro de uma vasta rede neural

Em 2019, Frankle e Carbin introduziram a Hipótese do Bilhete de Loteria (LTH): dentro de uma rede densa inicializada aleatoriamente existe uma sub-rede esparsa - um "bilhete vencedor" - que, treinada a partir de sua inicialização original, corresponde à precisão da rede completa em um número comparável de iterações.

Formalmente, considere f(x;θ0)f(\mathbf{x}; \boldsymbol{\theta}_0) inicializado com θ0Dθ\boldsymbol{\theta}_0 \sim \mathcal{D}_\theta. Depois de treinar para convergência obtemos θ\boldsymbol{\theta}^* e derivar uma máscara de poda m\mathbf{m}. O LTH afirma que existe m\mathbf{m} tal que:

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

com m0θ0\|\mathbf{m}\|_0 \ll |\boldsymbol{\theta}_0|. Os experimentos originais foram no MNIST e no CIFAR-10, onde os bilhetes vencedores retiveram de 10 a 20% dos parâmetros. Nada sobre isso é transferido para os dados do livro de pedidos por suposição - os recursos LOB não são estacionários e o rótulo é quase ruído, o que é um regime diferente da classificação de imagens exatamente nas maneiras que podem ser importantes.

Poda Iterativa de Magnitude (IMP)

O ticket é encontrado pelo IMP:

  1. Inicialize a rede com θ0\boldsymbol{\theta}_0.
  2. Treine para a convergência, obtendo θ\boldsymbol{\theta}^*.
  3. Podar o p%p\% de pesos com menor magnitude, criando máscara m\mathbf{m}.
  4. Redefina os pesos sobreviventes para seus valores em θ0\boldsymbol{\theta}_0 (retrocedendo).
  5. Repita a partir do passo 2 com a rede mascarada.

Cada fração de ameixas redondas pp (normalmente 20%), então depois nn rodadas (1p)n(1-p)^n dos parâmetros sobrevivem. Após 10 rodadas em p=0.2p = 0.2, cerca de 10,7% permanecem.

Três hipóteses sobre modelos de negociação, nenhuma delas testada

É tentador argumentar que o LTH deveria funcionar especialmente bem com base em dados de mercado. Surgem três desses argumentos; todos os três são hipóteses, e afirmá-los como fatos é o modo de falha que este blog existe para evitar.

  1. Os sinais financeiros são esparsos. A maior parte de um instantâneo da carteira de pedidos é ruído, portanto, uma sub-rede esparsa pode estar naturalmente alinhada com um sinal esparso. Testável: compare o IMP com uma máscara aleatória de mesma dispersão; se a dispersão em si estiver fazendo o trabalho, a máscara aleatória não deverá ficar muito atrás.
  2. Os bilhetes vencedores são generalizados entre os regimes. Esta é uma afirmação empírica sobre mercados sem nenhuma citação por trás dela, e é a mais interessante das três. Ele pode ser testado diretamente em relação aos rótulos de regime de detecção de regime com HMMs: encontre o ticket no regime A, treine-o novamente no regime B e compare com um ticket encontrado nativamente em B.
  3. A dispersão regulariza. A menor capacidade efetiva pode reduzir o ruído de ajuste ao microestrutura - o que apareceria como a lacuna fora da amostra do modelo podado sendo menor do que a do modelo denso, e não apenas comparável.

Implementação: Removendo um MLP comercial

Camadas neurais transformadas por meio de uma máscara esparsa precisa

O modelo básico

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

Poda de magnitude não estruturada

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

Poda Estruturada, com a Reconstrução que Torna Real

Mascarar linhas é metade do trabalho. A metade que produz a aceleração é reconstruir cada camada em sua forma reduzida - o que significa propagar a remoção adiante: descartando linha jj de camada ii também descarta coluna jj de camada i+1i+1 e canal jj de qualquer BatchNorm1d entre eles.

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

Duas coisas para verificar antes de confiar nisso, no mesmo espírito que a equivalência rege o resto do blog:

  • Formas. rebuilt deve mostrar dimensões ocultas em (1fraction)h(1 - \text{fraction}) \cdot h - 512 para fraction=0.75, h=2048h = 2048 — e uma contagem de parâmetros que caiu quadraticamente, uma vez que ambas as dimensões das matrizes interiores diminuíram.
  • Saídas. Em eval() modo, rebuilt(x) deve corresponder ao modelo mascarado rebuilt- saída livre para tolerância de ponto flutuante no mesmo lote. Caso contrário, a propagação da coluna está errada e cada número downstream mede um modelo diferente do que você pensa.

O teste de sobrevivência de linha assume que uma linha mascarada é exatamente zero e uma linha ativa não é. Isso vale para ln_structured saída; não seria válido se algum outro procedimento produzisse um neurônio vivo genuinamente zero, portanto, afirme a contagem de sobreviventes em relação à fração solicitada, em vez de confiar cegamente no teste de norma.

Poda de magnitude iterativa (pesquisa de bilhetes de loteria)

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 é a matéria-prima para a curva de dispersão versus precisão que este artigo lhe deve. eval_fn tem que estar genuinamente fora da amostra, em divisões eliminadas - uma execução de IMP pontuada na amostra reportará uma bela curva que não significa nada.

Medindo

Fronteira de eficiência abstrata que equilibra precisão de esparsidade e latência

A latência é medida com a mesma convenção de chicote do resto do blog – aquecimento excluído, melhor de N, p50/p95/p99 relatado em vez de média – e esse protocolo, com o código, está em Polars vs pandas. Três pontos específicos para poda:

  • Compare o modelo reconstruído, não o mascarado. Um modelo mascarado no tamanho de lote 1 mede a forma densa.
  • Informar o tamanho do lote. O lote 1 (loop de cotação) e o lote 256 (varredura de pesquisa) ficam em lados diferentes da linha vinculada à memória/limitada à computação, e a poda os ajuda de maneira diferente.
  • Precisão do relatório na mesma divisão, no mesmo horizonte, com a definição do rótulo declarada. Uma tabela de latência sem a coluna de precisão correspondente é um argumento para excluir totalmente o modelo.

Técnicas Avançadas

Caminhos avançados de compactação de rede convergindo em um modelo eficiente

Poda com Destilação de Conhecimento

Em vez de podar e ajustar isoladamente, use o modelo denso original como professor. O aluno podado minimiza uma combinação de perda de tarefas e divergência KL da distribuição de resultados do professor:

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)

onde zt\mathbf{z}_t e zs\mathbf{z}_s são logits de professores e alunos, TT é a temperatura e α\alpha equilibra os objetivos. O T2T^2 fator redimensiona os gradientes de destilação, que de outra forma encolhem à medida que 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

Poda de Movimento

Em vez de podar por magnitude absoluta, a poda de movimento (Sanh et al., 2020) poda pesos que se movem em direção a zero durante o treinamento. A pontuação de importância acumula o produto gradiente-peso:

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)

Pesos com pontuações negativas são podados. O argumento a favor da poda de magnitude é especificamente sobre o ajuste fino: quando você adapta um modelo pré-treinado, a distribuição de magnitude foi moldada pela tarefa de pré-treinamento, então a magnitude é um sinal de importância obsoleto e a direção do deslocamento é mais recente. Para um modelo de negociação retreinado em janelas roláveis, essa é a situação mais comum do que treinar do zero.

NVIDIA 2:4 Esparsidade Estruturada

As GPUs NVIDIA Ampere e posteriores suportam dispersão estruturada 2:4 em hardware: de cada 4 pesos contíguos, exatamente 2 devem ser zero.

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

Esta é a única forma de dispersão refinada que o hardware realmente recompensa, e é por isso que é mais importante do que o número de 90% de zeros da poda não estruturada. A restrição é local e não global — não importa quais dois em cada quatro sobrevivem — por isso é uma restrição muito mais fraca do que fixar uma máscara global, embora 50% seja o único nível de dispersão disponível.

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

Perceber a aceleração requer que o caminho de inferência use os núcleos tensores esparsos - uma exportação ONNX mais construção TensorRT, ou torch.sparse.to_sparse_semi_structured. Exportar um modelo com máscara 2:4 por meio de um tempo de execução denso oferece o custo de precisão e nenhum benefício.

Implantação de produção

Modelo de inferência enxuta entrando em um conduíte de hardware de baixa latência

Validação

Um modelo podado é um modelo novo, não um modelo antigo compactado, e passa pelo mesmo portão de aceitação que qualquer outro candidato: reciclagem contínua e revalidação fora da amostra por otimização walk-forward, com a correção do efeito de seleção da razão de Sharpe deflacionada. Essa correção não é opcional aqui – o IMP gera uma sequência de modelos candidatos, portanto, o nível de dispersão que parece melhor em dez rodadas foi escolhido na pesquisa, e seu Sharpe precisa ser esvaziado pelo número efetivo de tentativas. Uma regra simples como “rejeite se Sharpe cair mais de 5%” não sobrevive a essa aritmética, e é por isso que você não encontrará nenhuma neste artigo.

Empilhamento de quantização

A poda compõe com quantização. Um modelo 90% esparso e quantizado para INT8 tem uma taxa de compressão de:

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

Um modelo de 48 MB passa a ter 1,2 MB. Essa é uma reivindicação de armazenamento e nada mais. Se o modelo de 1,2 MB produz as mesmas decisões é uma questão separada com sua própria resposta, e a armadilha de precisão da GPU é a razão para perguntar: neste blog, foi demonstrado que fp32 sozinho produz um erro relativo de 211 em um cálculo de backtest que parecia totalmente razoável. INT8 é uma redução muito mais agressiva do que isso. Envie um modelo quantizado e podado apenas atrás de uma porta de paridade quantificada em relação ao modelo denso do FP32 – taxa de acordo de decisão e delta de PnL em um período de espera, não uma garantia.

Monitoramento

Os modelos podados podem ser mais sensíveis à mudança de distribuição. Vale a pena assistir:

  • Disparidade de ativação: se os neurônios sobreviventes emitem principalmente zeros, o modelo efetivo é menor do que o pretendido e provavelmente degradante.
  • Normas de gradiente durante o retreinamento: a explosão de gradientes sugere que a sub-rede sobrevivente está sendo solicitada a compensar de forma muito agressiva o que foi removido.
  • Entropia de previsão: um modelo podado que se torna excessivamente confiante em dados de microestrutura ruidosos provavelmente se ajusta ao regime de treinamento.

Conclusão

Rede neural compacta e eficiente resolvendo a partir de incerteza densa

Os métodos estão bem estabelecidos e, até que a varredura seja executada, isso é tudo o que este artigo afirma. A poda não estruturada fornece um número de dispersão e nenhuma velocidade. A poda estruturada oferece velocidade se – e somente se – você reconstruir as camadas em vez de mascará-las. A hipótese do bilhete de loteria sugere que o modelo compacto já existe dentro do modelo superparametrizado, embora isso tenha sido demonstrado em benchmarks de imagens e não em dados de livros de pedidos, e as três razões pelas quais ele "deveria" funcionar nos dados de mercado fornecidos acima são hipóteses com experimentos anexados, não descobertas.

A heurística prática da literatura é treinar grandes e podar, em vez de projetar pequenos desde o início: o modelo grande explora o cenário de perdas de forma mais eficaz, e a poda preserva os caminhos que importavam. Se isso vale para um modelo de negociação, com que dispersão e com que custo de precisão, basta uma varredura de IMP – e este artigo deve ser lido novamente após essa varredura, com números nele.

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

Fique à frente do mercado

Assine nossa newsletter para insights exclusivos sobre trading com IA, análises de mercado e atualizações da plataforma.

Respeitamos sua privacidade. Cancele a inscrição a qualquer momento.