Relojes de Discretización Temporal¶
Jano siempre particiona procesos temporales. Lo que cambia es el reloj usado para hacer avanzar la evaluación:
Reloj calendario: días, horas, semanas o meses.
Reloj por filas/eventos: cada evento o cada
Nfilas observadas.Reloj por micro-batches: cada batch observado en un stream online.
Reloj de negocio: un trigger definido por el usuario que marca checkpoints de retraining.
Todos los relojes son causales: los datos observados después no deben influir en
decisiones que se habrían tomado antes. Las actualizaciones por evento no quedan
fuera del tiempo; son otra forma de discretizar la misma línea temporal. Cuando
el evento X dispara retraining, Jano registra el timestamp asociado y
convierte evidencia acumulada en un checkpoint temporal auditable.
Partición Guiada por Calendario¶
La partición guiada por calendario es el modo base para backtesting de sistemas tabulares de machine learning. Responde preguntas como:
¿cómo habría sido la performance si el modelo se hubiese reentrenado todos los días?
¿cuánta historia debería contener la ventana de train?
¿cómo se degrada un modelo fijo sobre ventanas futuras?
Usá TemporalBacktestSplitter directamente cuando querés controlar manualmente
el loop de folds:
from jano import TemporalBacktestSplitter
splitter = TemporalBacktestSplitter(
time_col="timestamp",
train_size="30D",
test_size="7D",
step="7D",
strategy="rolling",
)
for train_idx, test_idx in splitter.split(frame):
train = frame.iloc[train_idx]
test = frame.iloc[test_idx]
Usá WalkForwardPolicy o TemporalSimulation cuando querés que Jano genere
un plan, ejecute los folds y exponga resultados auditables.
Partición Online Guiada por Observaciones¶
La partición online guiada por observaciones no es un modo no-temporal separado. Es un patrón causal de evaluación online sobre la misma línea temporal: inicializar un modelo, predecir el próximo evento o micro-batch, observar el target, actualizar el modelo y repetir.
Sirve cuando el reloj operativo no es solo el calendario, sino también la evidencia acumulada desde la última actualización.
Usá OnlineTemporalRunner con PartialFitUpdateStrategy cuando el modelo
soporta actualización incremental real vía partial_fit:
from jano import OnlineTemporalRunner, PartialFitUpdateStrategy
runner = OnlineTemporalRunner(
model=model,
time_col="timestamp",
target_col="target",
feature_cols=["feature_a", "feature_b"],
initial_train_size="30D",
update_size=1,
metrics={"mae": mae, "rmse": rmse},
update_strategy=PartialFitUpdateStrategy(),
)
run = runner.run(frame)
print(run.to_frame().head())
print(run.metric_trajectory().head())
print(run.summary())
La secuencia es causal por diseño:
inicializa el modelo sobre la ventana inicial de train
predice el próximo evento o micro-batch
mide la predicción cuando se observa el target
actualiza el modelo con ese batch observado
repite
update_size=1 significa un tick temporal por cada evento observado. También podés usar batches
por filas como update_size=100 o por duración como update_size="1D". Eso
permite comparar relojes por evento, por batch de filas o por calendario sin
cambiar el resto de la configuración.
Checkpoints de Retraining Definidos por el Usuario¶
La evaluación online también puede marcar el checkpoint temporal exacto donde tu
propia lógica indica que ya conviene reentrenar. Pasá retrain_trigger a
OnlineTemporalRunner. El trigger recibe la historia online acumulada y el
último batch ya evaluado:
def should_retrain(history, latest):
if latest["mae"] > 0.15:
return {
"retrain": True,
"reason": "mae crossed production tolerance",
"score": latest["mae"],
}
return False
runner = OnlineTemporalRunner(
model=model,
time_col="timestamp",
target_col="target",
feature_cols=["feature_a", "feature_b"],
initial_train_size="30D",
update_size=100,
metrics={"mae": mae},
update_strategy=PartialFitUpdateStrategy(),
retrain_trigger=should_retrain,
)
run = runner.run(frame)
print(run.retrain_checkpoints())
El trigger puede devolver True, un string con la razón, o un diccionario como
{"retrain": True, "reason": "...", "score": 0.23}. Jano registra batch,
timestamps, cantidad de filas, métricas y metadata opcional del trigger. La regla
de drift o costo de negocio sigue siendo propiedad del usuario; Jano vuelve
explícito y reproducible el checkpoint de retraining resultante.
No todos los estimadores soportan partial_fit. Para modelos clásicos
fit/predict, usá RefitUpdateStrategy:
from jano import OnlineTemporalRunner, RefitUpdateStrategy
runner = OnlineTemporalRunner(
model=model,
time_col="timestamp",
target_col="target",
feature_cols=["feature_a", "feature_b"],
initial_train_size="30D",
update_size="1D",
metrics={"mae": mae},
update_strategy=RefitUpdateStrategy(max_train_rows=10_000),
)
Esta estrategia refittea después de cada batch observado. Es más costosa que
partial_fit, pero funciona con estimadores estándar y puede mantener historia
acotada con max_train_rows.
Encontrar un Reloj de Actualización por Observaciones¶
OnlineUpdatePolicyStudy compara varias cadencias de actualización sobre el
mismo stream temporal. Eso permite preguntar si las actualizaciones del modelo
deberían dispararse por calendario, por cantidad de filas o por evidencia
acumulada:
from jano import OnlineUpdatePolicy, OnlineUpdatePolicyStudy, RefitUpdateStrategy
study = OnlineUpdatePolicyStudy(
model=model,
time_col="timestamp",
target_col="target",
feature_cols=["feature_a", "feature_b"],
initial_train_size="30D",
policies=[
OnlineUpdatePolicy("every-event", update_size=1, update_strategy=RefitUpdateStrategy()),
OnlineUpdatePolicy("every-100-events", update_size=100, update_strategy=RefitUpdateStrategy()),
OnlineUpdatePolicy("daily", update_size="1D", update_strategy=RefitUpdateStrategy()),
],
metrics={"mae": mae},
)
comparison = study.run(frame)
print(comparison.to_frame())
print(comparison.metric_trajectory().head())
print(comparison.find_optimal_policy(metric="mae", update_cost_weight=0.01))
El parámetro opcional update_cost_weight penaliza policies que actualizan muy
seguido. Así el output sigue siendo data-first, pero el tradeoff queda explícito:
una policy puede ganar porque predice mejor, porque actualiza menos o porque
ofrece el mejor compromiso ajustado por costo.