Multi-Task Learning for Simultaneous Price, Volume, and Volatility Prediction
El aprendizaje multitarea (MTL) generalmente se vende con una afirmación: comparta un codificador entre objetivos correlacionados y la tarea principal mejorará. En el comercio, los objetivos correlacionados son obvios (los rendimientos, el volumen y la volatilidad observada caen todos fuera del mismo flujo de órdenes) y la afirmación casi nunca se prueba. La pregunta interesante no es si las tareas están relacionadas. Se trata de si los gradientes compartidos concuerdan y qué sucede en los pliegues donde no lo hacen.
Este artículo centra dos cosas que la mayoría de los artículos de MTL tratan como notas a pie de página:
- El equilibrio de pérdidas es un experimento, no un detalle. Las ponderaciones fijas, la ponderación de incertidumbre de Kendall y GradNorm son tres modelos diferentes. Ejecute los tres en los mismos pliegues e informe los pesos aprendidos junto con la métrica de la tarea principal para cada uno.
- La transferencia negativa se puede medir antes de ver la métrica. La similitud del coseno entre los gradientes de tareas en el codificador compartido le indica, durante el entrenamiento, si las tareas auxiliares están llevando la representación a algún lugar al que la tarea principal quiere ir. Firma los cosenos y luego verifica si el signo predijo el resultado en ese pliegue.
Todo lo demás en proceso (el proceso de volatilidad, el ciclo de capacitación, los controles de fugas, el protocolo de validación) ya se trata en otras partes de este blog y está vinculado en lugar de derivarse nuevamente.
Configuración

Características de entrada dadas (OHLCV, indicadores técnicos, flujo de pedidos), tres objetivos:
- Tarea 1 (primaria): retorno del próximo período
- Tarea 2 (auxiliar): volumen de registro del siguiente período
- Tarea 3 (auxiliar): volatilidad realizada en el próximo período
Un modelo multitarea produce las tres simultáneamente, , y el riesgo de múltiples tareas es una suma ponderada de los riesgos por tarea:
Todo el artículo trata sobre el y sobre lo que los gradientes por tarea se hacen entre sí.
Por qué la capacitación conjunta podría ayudar, en un párrafo. Las tareas auxiliares limitan la representación compartida para explicar más de un fenómeno de mercado, lo cual es un control de capacidad y un sesgo inductivo al mismo tiempo; y debido a que el volumen y la volatilidad se observan directamente mientras que el "retorno esperado" no, los cabezales auxiliares proporcionan una señal de gradiente más limpia que el cabezal primario. El argumento a favor de un modelo que emite muchos resultados se discute extensamente, con la maquinaria de interpretabilidad adjunta, en transformadores de fusión temporal para pronósticos de múltiples horizontes, que presenta el mismo argumento de codificador compartido de muchas cabezas para cuantiles de múltiples horizontes.
Arquitectura, brevemente
Compartir parámetros de forma estricta: un codificador compartido alimenta jefes de tareas específicas , entonces . Esta es la versión medida aquí, porque es la versión en la que el gradiente entra en conflicto. está bien definido.
El intercambio suave de parámetros le da a cada tarea su propio codificador con una penalización de acoplamiento — más parámetros, más flexibilidad y ningún vector de parámetros compartido único para medir el conflicto. Redes de punto de cruz se ubican en el medio, mezclando características por tarea a través de una matriz aprendida en cada nivel. Vale la pena probar ambos si el intercambio intenso muestra conflicto, y ambos están fuera del alcance de la medición a continuación.
El experimento que importa: tres esquemas de equilibrio de pérdidas

La ingenua pérdida es sensible a la escala. Si la pérdida de retorno sigue viva y pérdida de volumen alrededor , el volumen es dueño del gradiente y la cabeza de retorno muere de hambre. Tres respuestas:
Pesas fijas. Conjunto después de estandarizar cada objetivo. La línea de base honesta: si gana, los esquemas de adaptación son una ceremonia.
Ponderación de la incertidumbre (Kendall et al., 2018). Aprenda una escala de ruido homocedástica por tarea:
Las tareas de alta incertidumbre se reducen automáticamente; el término bloquea lo trivial solución. Tenga en cuenta esto es un dispositivo para ponderar la pérdida de tiempo de entrenamiento, no un intervalo predictivo; para conocer la incertidumbre con la que realmente puede dimensionar una posición, consulte predicción conforme.
GradNorm (Chen et al., 2018). Gradiente de equilibrio magnitudes en lugar de escalas de pérdida. Cada paso: calcular y la media , calcular la tasa de entrenamiento relativa y actualizar . Luego, todas las tareas se entrenan a ritmos comparables, independientemente de la escala de pérdidas.
El código específico de MTL son los encabezados, el avance de lista y la agregación de pérdidas. La pila Linear/BatchNorm/ReLU/Dropout, el texto estándar de Adam/coseno/clip y el bucle de época son el patrón estándar que se muestra en DeepLOB y se omiten aquí.
import torch
import torch.nn as nn
class MultiTaskTradingModel(nn.Module):
"""Hard parameter sharing: one encoder, K heads."""
def __init__(self, encoder: nn.Module, repr_dim: int, n_tasks: int = 3):
super().__init__()
self.shared_encoder = encoder # any MLP/CNN/GRU trunk
self.task_heads = nn.ModuleList(
nn.Linear(repr_dim, 1) for _ in range(n_tasks)
)
def forward(self, x):
h = self.shared_encoder(x)
return [head(h).squeeze(-1) for head in self.task_heads]
def shared_repr(self, x):
return self.shared_encoder(x)
class UncertaintyWeightedLoss(nn.Module):
"""Kendall et al. (2018) homoscedastic weighting."""
def __init__(self, n_tasks: int = 3):
super().__init__()
self.log_vars = nn.Parameter(torch.zeros(n_tasks)) # log(sigma^2)
def forward(self, losses: list) -> torch.Tensor:
return sum(
torch.exp(-self.log_vars[i]) * loss + self.log_vars[i]
for i, loss in enumerate(losses)
)
def get_weights(self) -> list:
with torch.no_grad():
return [torch.exp(-lv).item() for lv in self.log_vars]
UncertaintyWeightedLoss tiene parámetros, por lo que debe ingresar al optimizador junto con el modelo: optim.Adam(list(model.parameters()) + list(uw.parameters()), ...). Olvidar esto es la forma más común de "ejecutar ponderación de incertidumbre" y, en su lugar, ejecutar silenciosamente ponderaciones fijas.
Qué reportar
Para cada esquema, en cada pliegue: las ponderaciones de la tarea final aprendidas, la métrica de la tarea principal y, debido a que un esquema de ponderación es una elección de modelo, cuántos esquemas se compararon antes de elegir uno.
| Esquema | Métrica de tarea principal versus tarea única | |||
|---|---|---|---|---|
| Fijado () | 1,00 | 1,00 | 1,00 | — |
| Ponderación de la incertidumbre | — | — | — | — |
| Norma de graduación | — | — | — | — |
Tres esquemas multiplicados por varios pliegues ya es una pequeña búsqueda de modelos. Cualquier mejora reportada aquí tiene que sobrevivir a la corrección de pruebas múltiples descrita en Sharpe deflactado y pruebas múltiples antes de que signifique algo.
Transferencia Negativa: Firma los Degradados

Esta es la parte que vale la pena conservar. La transferencia negativa ocurre cuando las tareas auxiliares empeoran la tarea principal y tiene un diagnóstico directo: el ángulo entre los gradientes de la tarea en el espacio de parámetros compartido.
Medido únicamente en el codificador compartido: los cabezales son específicos de la tarea por construcción y siempre "están de acuerdo" trivialmente.
import torch.nn.functional as F
def shared_grad(model, x, y, task_idx, criterion=nn.MSELoss()):
"""Gradient of task `task_idx` w.r.t. the shared encoder, flattened."""
model.zero_grad(set_to_none=True)
loss = criterion(model(x)[task_idx], y)
loss.backward()
return torch.cat([
p.grad.detach().flatten()
for p in model.shared_encoder.parameters()
if p.grad is not None
])
def task_conflict(model, x, y_by_task, task_names):
"""Pairwise cosine similarity between per-task shared-encoder gradients."""
grads = {
name: shared_grad(model, x, y_by_task[name], i)
for i, name in enumerate(task_names)
}
return {
(a, b): F.cosine_similarity(
grads[a].unsqueeze(0), grads[b].unsqueeze(0)
).item()
for i, a in enumerate(task_names)
for b in task_names[i + 1:]
}
Llame a esto en un lote retenido a una cadencia fija durante el entrenamiento, no una vez al final. Un par puede comenzar alineado y divergir a medida que el codificador se especializa; un único número de fin de formación lo oculta.
El hallazgo que hay que buscar y publicar de cualquier manera:
| Par | cos sim, formación temprana | cos sim, entrenamiento tardío | ¿MTL ayudó a la tarea principal? |
|---|---|---|---|
| volver ↔ volumen | — | — | — |
| rentabilidad ↔ volatilidad | — | — | — |
| volumen ↔ volatilidad | — | — | — |
Si los gradientes de volumen y volatilidad concuerdan entre sí mientras ambos entran en conflicto con el gradiente de retorno, la conclusión correcta es que las dos tareas auxiliares forman un bloque coherente al que la tarea de retorno no pertenece, y la solución es agrupar las tareas, no más capacidad. Cuando el conflicto es real, las soluciones estándar son PCGrad (Yu et al., 2020), que proyecta cada gradiente en conflicto en el plano normal del otro; CAGrad (Liu et al., 2021), que busca una dirección de descenso que no perjudique ninguna tarea; o abandonar por completo la tarea auxiliar.
Tenga en cuenta lo que está deliberadamente ausente: un gráfico t-SNE de la representación compartida coloreado por el valor objetivo. Es decorativo: los números cosenos de arriba dicen todo lo que indicaría la incrustación, y lo dicen como números.
Protocolo de validación

La medición anterior no tiene valor bajo un protocolo descuidado, y MTL empeora las trampas habituales porque hay tres objetivos para filtrar en lugar de uno.
Datos reales, no un simulador. Los objetivos deben provenir de datos comerciales/OHLCV reales. Un juguete GARCH codificado genera volatilidad que se correlaciona con los rendimientos por construcción, que es precisamente lo que se está probando: el experimento estaría midiendo su propio generador. Si desea un proceso de volatilidad ajustado, Pronóstico de volatilidad GARCH para criptomonedas ajusta GARCH(1,1) por máxima probabilidad en BTC/ETH real y valida los residuos estandarizados, y GARCH asimétrico y el efecto de apalancamiento cubre por qué un simulador de respuesta simétrica gaussiana En primer lugar, tergiversa la volatilidad de las criptomonedas. Los datos sintéticos sólo son defendibles cuando proporcionan una verdad sobre el terreno controlada (una correlación de tareas conocida y establecida por el autor que se intenta recuperar), que es un experimento diferente al que se presenta aquí.
Los escaladores solo caben en el tren. Coloque el escalador de funciones y los tres escaladores de destino dentro de cada pliegue de entrenamiento y aplíquelos a la validación; un global fit_transform antes de dividir los momentos de prueba de fugas en entrenamiento. Esta falla exacta está catalogada en la taxonomía de sesgo de anticipación.
Pliegues de avance purgados y embargados. Una división cronológica 80/20 no puede distinguir una mejora de MTL de un efecto de pliegue; ese es todo el argumento de optimización de avance, que muestra tres divisiones que producen tres conclusiones. Reutilizar la ventana desplegable purged_walk_forward generador de modelado extendido con aprendizaje automático: elimina una brecha de horizon filas a ambos lados de cada límite, lo cual es importante aquí porque las ventanas superpuestas de volatilidad realizada se filtran a través del límite incluso cuando el objetivo de retorno no lo hace.
Una línea de base clásica. Una red MTL que supera a tres redes de tarea única no ha demostrado nada si un modelo de cresta o de aumento de gradiente por objetivo supera a los cuatro. Coloque un modelo por objetivo con LightGBM o cresta en los mismos pliegues y las mismas características, e infórmelo en la misma tabla.
| Modelo | Métrica de tarea principal | Notas |
|---|---|---|
| Cresta, por objetivo | — | Línea de base clásica |
| LightGBM, por objetivo | — | Línea de base clásica |
| MLP de tarea única, por objetivo | — | Tres redes separadas |
| MTL, el mejor esquema de pérdidas | — | Una red, tres cabezas |
¿Qué haría que MTL valga la pena aquí?

Condiciones bajo las cuales MTL debería ganar, expresadas como hipótesis para comparar con los pliegues anteriores en lugar de una lista de verificación:
- Las etiquetas auxiliares son más limpias que la etiqueta principal. El volumen se observa directamente; El "retorno esperado" no lo es. Si el cabezal de retorno produce principalmente ruido de ajuste, la señal de gradiente de los cabezales auxiliares es la única parte bien colocada del objetivo.
- Los datos de entrenamiento están limitados en relación con la capacidad del codificador, por lo que la restricción auxiliar realiza un trabajo de regularización real en lugar de simplemente competir por los parámetros.
- La latencia de inferencia importa y un pase hacia adelante gana a tres.
Y el caso en contra, igualmente comprobable: si la medida cos_sim(return, ·) Los valores son persistentemente negativos, el codificador compartido se está alejando de la tarea principal y los cabezales auxiliares son un impuesto, no un regularizador.
Conclusión

Los rendimientos, el volumen y la volatilidad provienen de la misma microestructura, por lo que una representación compartida es un previo razonable, pero un previo no es un resultado. Las dos cosas que esta configuración realmente puede establecer son qué esquema de equilibrio de pérdidas prefieren los datos (con los pesos aprendidos informados, no solo con el nombre del ganador) y si los gradientes de las tareas en el codificador compartido coinciden, medidos durante el entrenamiento en lugar de asumidos por el hecho de que los objetivos están correlacionados.
Si los pliegues de avance purgados muestran que la red MTL no logra superar un modelo de aumento de gradiente por objetivo, ese es el hallazgo y se publica como tal: la plantilla es el negativo honesto. Un resultado negativo sobre una transferencia negativa sigue siendo un resultado sobre una transferencia negativa.
Authors
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.