Ir para o conteúdo principal

Previsão de demanda no M5: TimesFM 3.0 contra LightGBM global

Modelo fundacional zero-shot substitui o gradient boosting na previsão de demanda? TimesFM 3.0 contra um LightGBM global no M5, na métrica WRMSSE oficial.

No dia 31 de agosto de 2026 o Google lançou o TimesFM 3.0, um modelo fundacional de séries temporais e na mesma semana eu já estava me questionando: Esse modelo bate um gradient boosting básico? Quais são os prós e contras? Então usei a competição M5 de previsão de demanda do Walmart do Kaggle para construir meus próprios benchmarks.

A pergunta que me interessa não é só quem ganha mas quanto custa cada ponto de acurácia, em esforço humano e em máquina. Um modelo fundacional promete previsão sem treino; o gradient boosting global precisa de alguma engenharia de features e certa experiência para entregar resultados satisfatórios. Medir os dois na mesma régua mostra onde cada promessa se paga.

Desenho, protocolo, specs e expectativas foram registrados no GitHub antes de qualquer configuração rodar.

Resumo

  • Um LightGBM básico sem tuning de hiperparâmetros vence um TimesFM 3.0 com covariáveis no WRMSSE por 8,1% (0.58372 contra 0.63534 no placar privado do Kaggle) e roda 5,1× mais rápido, em CPU de laptop contra GPU T4 alugada.
  • Trocar qual estatística se lê da distribuição preditiva (mediana por média) corta de 49% a 59% do erro do TimesFM 3.0.
  • O zero-shot cobre 80% da distância entre o sNaive e o melhor LightGBM, um resultado bem decente para um modelo que não precisa de treino.

O que o TimesFM 3.0 traz de novo
#

São 330M de parâmetros, decoder-only, com patch de 32 passos. A novidade arquitetural é a atenção alternada: causal temporal dentro de cada série, e atenção de variate entre séries no mesmo timestep. É isso que permite alimentar o modelo com um grupo de séries relacionadas e deixar que ele use uma para prever a outra.

Ele gera o horizonte inteiro num único forward, sem decodificação iterativa, e devolve nove quantis por passo. O código é Apache-2.0; os pesos vêm sob timesfm-non-commercial-license-v1.0, restrita a uso não-comercial. Vale repetir porque decide a conversa antes da métrica: dá para avaliar internamente, não dá para entregar a cliente.

Os três modos de entrada que testei usam o mesmo checkpoint e nenhum treino. Zero-shot aqui quer dizer que nenhum peso é atualizado com o dado do M5: covariável e alvo conjunto entram na hora da inferência, não no treino.

Clique para ver os três modos de entrada do TimesFM 3.0
Python
# 1. univariado: só o histórico da série
forecaster.predict_batch(ctx, horizon=28, return_quantiles=True)

# 2. + covariáveis conhecidas no futuro (preço, SNAP, evento, calendário)
forecaster.predict_batch(
    ctx, horizon=28, past_future_covariates=cov,
    return_quantiles=True, make_positive=True,
)

# 3. multivariado: o grupo dept x store entra junto e as séries
#    se enxergam pela atenção de variate
forecaster.predict_batch(grupo, horizon=28, return_quantiles=True)

Por que o M5, e por que ele é held-out genuíno
#

O M5 são 30.490 séries diárias de venda do Walmart em três estados, com hierarquia de 12 níveis, demanda intermitente, e preço e calendário de benefício social como drivers (M5 Competitors’ Guide, 2020). É o problema de previsão de demanda de varejo mais estudado que existe, com leaderboard de 5.558 equipes e métrica própria.

O que decide a escolha é outra coisa: o M5 é held-out genuíno para o TimesFM 3.0. O card do modelo declara o corpus de pré-treino, e o M5 não está nele. A auditoria de contaminação TSFMAudit (arXiv:2605.26161, Tabela 7) rotula o M5 como não-exposto para o TimesFM, e marca exposição documentada para Chronos, Kairos, Moirai e TiRex.

Isso é raro. A maioria dos benchmarks de varejo já foi vista por algum modelo fundacional, e boa parte dos resultados publicados de modelo fundacional no M5 é sobre modelos que já viram o M5. O M5 aqui é exceção, pelo que o card do modelo declara. Essa garantia vem da documentação, não de uma auditoria que eu mesmo tenha rodado.

Os concorrentes e as duas assimetrias
#

Do lado do fundacional, os três arms são o mesmo checkpoint com entradas diferentes: só o histórico, mais covariáveis, e o grupo dept × store junto. Nenhum recebe treino. Produção exigiria o TimesFM 2.5, porque os pesos do 3.0 são não-comerciais.

Clique para ver o código do TimesFM 3.0
Python
QUANTILES  = np.arange(0.1, 0.91, 0.1)
COVARIATES = ["sell_price", "snap", "is_event", "day_of_week", "month", "weekend"]

def trim_context(series, min_len=32):
    """Corta os zeros antes da primeira venda: 900 dias de zero pré-lançamento
    não são histórico, são ausência de produto. É a mesma convenção que o
    denominador do RMSSE usa para definir 'período ativamente à venda'."""
    nz = np.flatnonzero(series)
    if nz.size == 0:
        return series[-min_len:].astype(np.float32)
    return series[max(0, min(nz[0], len(series) - min_len)):].astype(np.float32)

# 30.490 séries x 1941 passos x 6 covariáveis não cabem em memória de uma vez,
# então a inferência corre em blocos de `chunk` séries
for lo in range(0, n, chunk):
    hi = min(lo + chunk, n)
    ctx = [trim_context(sales[s, :fold_idx]) for s in range(lo, hi)]

    # covariáveis past-future: passado real + futuro conhecido (preço, SNAP,
    # calendário), alinhados no tempo com o contexto de cada série
    cov = []
    for j, s in enumerate(range(lo, hi)):
        past = slice(fold_idx - len(ctx[j]), fold_idx)
        fut  = slice(fold_idx, fold_idx + horizon)
        cov.append(np.stack([
            np.concatenate([panel[c][s, past], panel[c][s, fut]])
            for c in COVARIATES
        ]).astype(np.float32))

    out = forecaster.predict_batch(
        ctx, horizon=horizon, past_future_covariates=cov,
        return_quantiles=True, make_positive=True,
    )
    # .forecast é o quantil MEDIANO (q50), não a média. Em série intermitente a
    # mediana colapsa em zero, então guardo os 9 quantis para reconstruir a
    # média depois, sem escolher a estatística por mim aqui.

Do outro lado, um LightGBM global: um único modelo para as 30.490 séries, objetivo Tweedie, lags múltiplos de 7. As configurações que disputam o placar são estas:

ConfiguraçãoTreinoO que entra na previsão
snaivenenhumrepete a última semana
tfm_zeronenhumsó o histórico da série, uma por vez
tfm_covnenhum+ preço, SNAP, evento, calendário
lgbm_min1 modelo, 30.490 sérieslags 7/14/28, categóricas, SNAP, evento
lgbm_rollidem+ médias móveis de 7 e 28 dias
*_meanpós-processamentoa média reconstruída dos 9 quantis no lugar da mediana

Duas assimetrias separam os paradigmas, e nenhuma é defeito de desenho.

A primeira é recursivo contra one-shot. O LightGBM prevê o passo 1, realimenta a predição como lag e prevê o passo 2, vinte e oito vezes, acumulando erro. O TimesFM 3.0 emite os 28 passos de uma vez.

A segunda é mediana contra média. O ponto nativo do TimesFM 3.0 é o quantil mediano. O WRMSSE é erro quadrático, cuja predição ótima é a média, e em série intermitente a mediana colapsa em zero. A mediana é o funcional errado para essa métrica, um erro de especificação que o pós-processamento corrige.

A estatística lida pesa muito na escolha do modelo
#

Trocar qual quantil se lê da mesma distribuição preditiva corta de 49% a 59% do erro do TimesFM 3.0 no backtest. Na origem oficial o efeito é da mesma ordem, num modelo só: o tfm_cov sai de 1.48355 para 0.63534, ou 57%.

Gráfico de inclinação mostrando as nove previsões do TimesFM 3.0 caindo de 1,32 a 1,72 no WRMSSE quando lidas pela mediana para 0,55 a 0,82 quando lidas pela média, todas cruzando a faixa de erro do sNaive.
Trocar a mediana pela média, na mesma distribuição, corta metade do erro. Fonte: benchmark próprio, backtest espelho.

O mecanismo é direto: 69% das séries têm mediana exatamente zero, mesmo vendendo ao longo do período. A maioria dos dias é zero e poucos picos levantam a média sem levantar a mediana. Ler a mediana descarta de 23% a 29% do volume que o próprio modelo espera, o que em previsão de demanda é ruptura, não excesso.

Um produto que vende 3 unidades em 10 dias tem mediana 0 e média 0,3. A mediana responde certo a “qual o dia típico?” e responde errado a “quanto estocar?”.

Dispersão das 27.685 séries do M5 que estavam à venda em todo o período avaliado, um subconjunto das 30.490, com média de vendas por dia no eixo horizontal e mediana no vertical. Quase todos os pontos caem abaixo da linha de igualdade e uma faixa densa se acumula na altura zero da mediana.
Cada ponto é uma série. 69% delas têm mediana zero mesmo vendendo ao longo do período. Fonte: benchmark próprio, painel do M5.

O que isso vira em unidade de negócio:

lido na medianalido na média
volume previsto contra o real70% a 78%93% a 97%
células previstas como exatamente zero18% a 38%1,5% a 21%
posição contra o sNaivepior nas 9 mediçõesmelhor nas 9 medições

A decomposição de erro mediu o mesmo viés pelo outro lado: 91,8% do MSE no nível 1 é viés puro, não dispersão. Essa é a assinatura de mediana lida por métrica quadrática.

Reconstruir a média a partir dos 9 quantis é pós-processamento alinhado à métrica, com custo de inferência zero, porque os quantis já estão salvos em disco. O estimador é deliberadamente conservador: assume que o pior caso é o quantil 90.

Clique para ver o código do mean_from_quantiles
Python
def mean_from_quantiles(q: np.ndarray) -> np.ndarray:
    """Limite inferior da média a partir dos 9 quantis (0.1..0.9).

    E[X] = integral de Q(p) dp em [0,1]. O trapézio cobre [0.1, 0.9];
    as caudas entram com a hipótese mais conservadora que existe:
      cauda inferior: Q(0) = 0    (não há venda negativa)
      cauda superior: Q(1) = q90  (falso e conservador de propósito)
    """
    p = np.arange(0.1, 0.91, 0.1)
    corpo = np.trapezoid(q, p, axis=-1)
    cauda_baixa = 0.1 * (0.0 + q[..., 0]) / 2
    cauda_alta = 0.1 * q[..., -1]
    return np.clip(corpo + cauda_baixa + cauda_alta, 0, None)

Nada disso precisou olhar o placar: o próprio objeto de previsão já denuncia o descasamento, sem usar um único valor real. Reportar um funcional inconsistente com a métrica é defeito de desenho, o assunto de Gneiting (2011), Making and Evaluating Point Forecasts.

A fronteira esforço × acurácia
#

O melhor LightGBM domina o melhor TimesFM 3.0 nos dois eixos: 8,1% menos erro e 5,1× mais rápido, em CPU de laptop contra GPU T4 alugada.

Dispersão de WRMSSE no placar do Kaggle contra tempo de parede em escala log: as configurações do LightGBM ficam abaixo e à esquerda das do TimesFM 3.0, com o lgbm_roll marcando 0,584 como o melhor ponto.
Nenhuma configuração do TimesFM 3.0 alcança o melhor LightGBM. Fonte: placar privado do Kaggle e tempo medido.

Ainda assim, o zero-shot cobre 80% da distância entre o sNaive e o melhor LightGBM, e faz isso sem treino, sem feature e sem hiperparâmetro. A única configuração do TimesFM 3.0 mais rápida que o zero-shot é a de alvos conjuntos. Ela prevê num único forward todas as séries de um mesmo grupo (os itens de um departamento numa loja), com a atenção de variate deixando uma enxergar a outra, e erra 11,1% mais.

configuraçãoesforço humanoKaggle privado
1º lugar do M5 (ensemble de 220 modelos)-0.520
lgbm_roll1 modelo + 4 features0.58372
lgbm_min1 modelo0.62541
tfm_cov_meanzero-shot + pós-proc.0.63534
tfm_zero_meanzero-shot + pós-proc.0.65519
snaivenenhum0.84701
tfm_covzero-shot (mediana)1.48355

O snaive desta implementação pontuou 0.84701 no placar privado, contra os 0.847 publicados pelo benchmark oficial do M5. Bate em quatro casas, o que valida externamente a métrica, o painel de 60 milhões de linhas e o formato da submissão.

A ponderação da métrica decide o vencedor
#

O WRMSSE do M5 é a média simples dos 12 níveis, e os níveis têm de 1 a 30.490 séries. Somando: os níveis 1 a 9 têm 154 séries e carregam 75% da métrica; os níveis 10 a 12 têm 42.686 séries e carregam 25%.

Esse empate entre níveis vem de uma decisão simples. O peso de cada série é a participação dela nas vendas em dólar dos últimos 28 dias, e como a soma em dólar dá o mesmo total em qualquer nível de agregação, os 12 níveis acabam pesando 1/12 cada. Uma série agregada concentra muita receita em poucas séries, então pesa muito; o nível 12 dilui a mesma receita entre 30.490 séries.

Mas peso por dólar é uma escolha de negócio, e nem sempre é o objetivo certo. Já trabalhei em projeto de reposição em que o valor do item não entrava na conta: para o estoquista tanto faz se o produto é caro ou barato, ele precisa estar na prateleira. No e-commerce a assimetria é ainda maior. A falta de um item barato e básico machuca a reputação da loja, porque o cliente generaliza para “aqui nunca tem o básico” e avalia mal o estabelecimento inteiro. Nesses casos a função-alvo é disponibilidade, e uma métrica ponderada por faturamento mediria a coisa errada.

Linha da margem do TimesFM sobre o LightGBM por nível da hierarquia: negativa e profunda nos níveis 1 a 9 (LightGBM ganha por 11% a 67%) e ligeiramente positiva nos níveis 10 a 12 (TimesFM ganha por cerca de 2%).
O fundacional só ganha nos três níveis com muita série. Fonte: benchmark próprio, quatro origens rolling.

O TimesFM 3.0 ganha o nível 12, o SKU por loja, por 1,6% a 1,9%, com sinal consistente em 7 de 7 origens. E perde os agregados por 11% a 67%. O fundacional ganha o fundo da hierarquia por pouco e perde o topo com folga, e como a métrica pesa o topo três vezes mais, o composto vai para o LightGBM.

Eu tinha pré-registrado o contrário. Minha expectativa era que a vantagem do fundacional se concentraria nos níveis agregados e desapareceria no nível 12, onde a demanda é intermitente. O resultado veio invertido.

Veredito e o que sobrevive a favor do fundacional
#

O LightGBM global ganha o WRMSSE por 8,1% (lgbm_roll 0.58372 contra tfm_cov_mean 0.63534), com sinal consistente em 7 de 7 origens dos dois desenhos de backtest, e com 5,1× menos tempo, sem exigir GPU. A configuração que compete em acurácia custou 2.297 s por rodada numa T4, contra 453 s do LightGBM em CPU.

A favor do fundacional ficam dois pontos: cobrir 80% da distância entre o sNaive e o melhor LightGBM sem treinar nada, e uma vantagem pequena mas consistente nos três níveis de baixo, os únicos com previsão direta e os que importam para decisão de reposição em SKU por loja.

Fine-tuning ficou de fora por custo. Se a inferência já custa 2.297 s numa T4, treinar 330 milhões de parâmetros por várias épocas é ordens de grandeza acima: possível na teoria, inviável nas condições deste estudo.

Já tinha rodado uma comparação parecida no lado tabular, TabFM contra GBMs. Em varejo intermitente, árvore ainda ganha, e o rigor de avaliação decide mais do que a escolha do modelo.

Essa disciplina de validação vem das competições do Kaggle, plataforma com que gosto de trabalhar: já documentei aqui as soluções finais do Porto Seguro Data Challenge, da ML Olympiad de educação e da ML Olympiad de saúde.

Perguntas frequentes
#

O TimesFM 3.0 é melhor que o LightGBM para previsão de demanda?

No M5, não. O melhor LightGBM vence o WRMSSE por 8,1% (0.58372 contra 0.63534) e roda 5,1× mais rápido, em CPU. O TimesFM 3.0 só ganha nos três níveis mais granulares da hierarquia, que juntos pesam 25% da métrica.

Por que a mediana do TimesFM 3.0 colapsa em zero?

Em demanda intermitente, 69% das séries têm mediana exatamente zero, mesmo vendendo, porque a maioria dos dias é zero e poucos picos levantam só a média. Como o WRMSSE é erro quadrático, ler a mediana descarta de 23% a 29% do volume esperado.

O que é o WRMSSE e por que ele decide o vencedor?

É a média simples dos 12 níveis hierárquicos do M5. Como cada nível pesa 1/12 e tem de 1 a 30.490 séries, 154 séries agregadas carregam 75% da métrica. O veredito do composto reflete o topo da hierarquia, não a previsão no SKU por loja.

Dá para usar o TimesFM 3.0 em produção?

Os pesos vêm sob licença não-comercial e não-produtiva; produção exigiria o TimesFM 2.5. E o custo pesa: a configuração que compete em acurácia custou 2.297 s por rodada numa GPU T4, contra 453 s do LightGBM em CPU, 5,1× mais lento com hardware alugado.

Vale a pena trocar gradient boosting por modelo fundacional?

Depende da estrutura do sinal. Em varejo intermitente, árvore ainda ganha: as 50 primeiras colocações do M5 são todas tree-based. O zero-shot cobre 80% da distância entre o `sNaive` e o melhor LightGBM sem treino, um bom piso rápido para começar.

Referências
#

Mentoria · vagas limitadas

Mentoria individual em Data Science e IA

Currículo, portfólio, entrevista técnica ou um projeto de dados travado.
Primeira sessão com 40% de desconto.

Agendar primeira sessão
Fellipe Gomes
Autor
Fellipe Gomes
Data Science Specialist @ Accenture | Kaggle Master

Sou formado em estatística e atuo como cientista de dados desde 2017. Compartilho meus estudos e evolução por meio de artigos, tutoriais e projetos de código aberto. Se quiser saber mais sobre meu trabalho, sinta-se à vontade para entrar em contato através das minhas redes sociais.

Recebe os próximos posts por email

Data science, IA e agentes. Sem spam, cancela quando quiser.

Confirmar inscrição

Recomendados

Publicidade