Forecast — Gradient Boost
Skforecast, XGBoost, LightGBM, Scikit-learn e CatBoost
Uma nova atualização no SKforecast me motivou a escrever esse post para compartilhar diferentes modelos de previsão baseados em Gradient Boost. Esse artigo abordará mais os conceuitos das técnicas utilizadas. Caso alguém queria explorar o código que está todo comentado e completo, segue meu github: arthurvaz05
Os modelos gradiente ganharam popularidade na comunidade ML por sua capacidade de obter excelentes resultados em uma ampla variedade de casos de uso, incluindo regressão e classificação. Embora esses modelos tenham sido tradicionalmente menos comuns na previsão, pesquisas recentes mostraram que eles podem ser altamente eficazes nesse domínio. Alguns dos principais benefícios do uso de modelos de aumento de gradiente para previsão incluem:
- A facilidade com que variáveis exógenas podem ser incluídas no modelo, além de variáveis autorregressivas.
- A capacidade de capturar relacionamentos não lineares entre variáveis.
- Alta escalabilidade, permitindo que os modelos manipulem grandes volumes de dados.
- Algumas implementações permitem a inclusão de variáveis categóricas sem a necessidade de codificação one-hot.
Apesar desses benefícios, o uso de modelos de aprendizado de máquina para previsão pode apresentar vários desafios que podem tornar os analistas relutantes em usá-los, sendo os principais
- Transformar os dados para que possam ser usados como um problema de regressão.
- Dependendo de quantas previsões futuras são necessárias (horizonte de previsão), pode ser necessário um processo iterativo em que cada nova previsão é baseada nas anteriores.
- A validação do modelo requer estratégias específicas, como backtesting, validação walk-forward ou validação cruzada de séries temporais. A validação cruzada tradicional não pode ser usada.
A biblioteca skforecast fornece soluções automatizadas para esses casos, facilitando a aplicação e validação de modelos ML para problemas de previsão. A biblioteca oferece suporte a vários modelos avançados de aumento de gradiente, incluindo XGBoost, LightGBM, Catboost e scikit-learn HistGradientBoostingRegressor.
Vamos ao exemplo!
O compartilhamento de bicicletas é um serviço popular de transporte compartilhado que fornece bicicletas a indivíduos para uso de curto prazo. Esses sistemas normalmente fornecem bicicletários onde os ciclistas podem pegar uma bicicleta emprestada e devolvê-la a qualquer doca pertencente ao mesmo sistema. As docas são equipadas com bicicletários especiais que prendem a bicicleta e a liberam apenas por controle do computador.
Um dos grandes desafios enfrentados pelos operadores desses sistemas é a necessidade de redistribuir as bicicletas para garantir que haja bicicletas disponíveis em todas as docas, bem como espaços livres para devoluções.
Com o objetivo de melhorar o planejamento e a execução da distribuição de bicicletas, propõe-se a criação de um modelo capaz de prever o número de usuários nas próximas 36 horas. Desta forma, todos os dias às 12h00, a empresa responsável pela gestão do sistema poderá saber a previsão de procura para o resto do dia (12 horas) e para o dia seguinte (24 horas).
Para fins ilustrativos, o exemplo atual modela apenas uma única estação, mas o modelo preditivo pode ser adaptado e estendido para abranger várias estações, melhorando assim o gerenciamento de sistemas de compartilhamento de bicicletas em maior escala.
Os dados deste documento representam o uso horário do sistema de bicicletas compartilhadas na cidade de Washington, D.C. durante os anos de 2011 e 2012. Além do número de usuários por hora, estão disponíveis informações sobre condições climáticas e feriados. Os dados originais foram obtidos do UCI Machine Learning Repository e foram previamente limpos (código) aplicando as seguintes modificações:
Colunas renomeadas com nomes mais descritivos.
Categorias renomeadas das variáveis meteorológicas. A categoria de heavy_rain foi combinada com a de chuva.
Variáveis desnormalizadas de temperatura, umidade e vento.
Variável date_time criada e definida como index.
Valores ausentes imputados por preenchimento direto.
O conjunto de dados resultante contém as colunas:
- date_time: data e hora.
- mês: mês (1 a 12).
- hora: hora (0 a 23).
- feriado: se o dia é feriado ou não (retirado de http://dchr.dc.gov/page/holiday-schedule).
- weekday: dia da semana (Segunda = 0, Domingo = 6).
- workingday: se for um dia útil.
- tempo: o clima do dia (claro, neblina, chuva).
- temp: temperatura registrada.
- atemp: sensação térmica.
- hum: umidade registrada.
- velocidade do vento: velocidade do vento registrada.
- users: número total de usuários do serviço de aluguel de bicicletas.
Entendendo como é estruturado a base é de suma importância para entender se existe algum tipo de manipulação necessária para ser feita.
Para facilitar o treinamento dos modelos, a busca por hiperparâmetros ótimos e a avaliação de sua precisão preditiva, os dados são divididos em três conjuntos separados: treinamento, validação e teste.
Exploração dos Dados
A exploração gráfica de séries temporais pode ser uma forma eficaz de identificar tendências, padrões e variações sazonais. Isso, por sua vez, ajuda a orientar a seleção de possíveis defasagens que podem servir como fortes preditores no modelo.
Quando damos um zoom, conseguimos levantar a possibilidade de existir uma sazonalidade contida no conjunto de dados. Agrupando os dados pelo dia da semana e pela hora, acaba ficando mais claro esse comportamento padrão semanal.
Há uma clara diferença entre dias de semana e fins de semana. Há também um claro padrão intradiário, com diferentes fluxos de usuários dependendo da hora do dia.
Mas para que conseguimos ter certeza dessa afirmativa, não apenas de forma gráfica, iremos aplicar a autocorrelação.
Os gráficos de autocorrelação podem ajudar a identificar padrões em seus dados de série temporal. Se o gráfico de autocorrelação mostrar uma alta autocorrelação em um atraso específico, isso indica que há uma forte relação entre as observações nesse atraso e, portanto, esse atraso pode ser um preditor útil.
Vamos aprofundar um pouco mais sobre o tema de ACF, PACF e White noise (ruído branco).
Basicamente ACF é para identificar correlação na série temporal, assim identificar sazonalidade e tendências. Já a PACF, ela consegue nos dizer se a série existe alguma correlação pura, excluindo efeitos indiretos (considera a aplicação de uma regressão no tempo t, t-1…t-n afim de considerar apenas os coeficientes de cada Lag da variável gerado) e por fim, para casos que não há nenhum tipo de autocorrelação, nos dizemos que a série é um White noise.
Os resultados do estudo de autocorrelação mostram que existe uma correlação significativa entre o número de usuários nas horas t-n, bem como nos dias t-n, e o número de usuários no futuro t. Isso significa que saber o número de usuários durante determinados períodos no passado pode ser valioso para prever o número de usuários no futuro.
Caso tenham dificuldade de interpretar e gostariam de um artigo só de ACF e PACF, só deixar um comentário.
XGBoost
O XGBoost é uma implementação altamente eficiente do algoritmo de aumento de gradiente estocástico, que se tornou uma referência no campo do aprendizado de máquina. A biblioteca XGBoost inclui sua própria API, bem como a classe XGBRegressor, que segue a API scikit-learn, tornando-a compatível com skforecast.
Primeiro, um modelo autorregressivo é treinado usando valores passados (lags) da variável de resposta como preditores. Posteriormente, variáveis exógenas são adicionadas ao modelo e é avaliada a melhora em seu desempenho.
Os modelos Gradient Boosting têm um grande número de hiperparâmetros, portanto, uma combinação da estratégia grid_search e backtesting é usada para identificar a melhor configuração que fornece as previsões mais precisas.
Não é comum a utilização, mas você pode usar o XGBoost para previsão de série temporais. No entanto, primeiro você precisa dividir seus dados de série temporal em pares (recursos, destino).
Defina o comprimento da janela (também conhecido como tamanho do vetor de recursos) e o tamanho da etapa (para controlar a granularidade) e iteração.
Ajustandos os hiperparâmetros
O ajuste de hiperparâmetros é uma etapa crítica no desenvolvimento de modelos eficazes de aprendizado de máquina. É importante observar que, ao pesquisar hiperparâmetros, os dados de teste não devem ser incluídos no processo. Isso ocorre porque a inclusão de dados de teste pode levar ao sobreajuste do modelo.
Para evitar overfitting, é realizada uma busca onde cada combinação de hiperparâmetros e lags é testada da seguinte forma:
Treine o modelo usando apenas o conjunto de treinamento.
O modelo é avaliado usando o conjunto de validação via backtesting.
A combinação de hiperparâmetros e defasagens que fornece o menor erro é selecionada.
Treine o modelo novamente usando a melhor combinação encontrada, desta vez usando os dados de treinamento e validação.
Seguindo essas etapas, pode-se obter um modelo com hiperparâmetros otimizados e evitar o overfitting.
param_grid = {
'n_estimators': [100, 500],
'max_depth': [3, 5, 10],
'learning_rate': [0.01, 0.1]
}
# Lags used as predictors
lags_grid = [48, 72]
results_grid = grid_search_forecaster(
forecaster = forecaster,
y = data.loc[:end_validation, 'users'], # Train and validation data
param_grid = param_grid,
lags_grid = lags_grid,
steps = 36,
refit = False,
metric = 'mean_squared_error',
initial_train_size = len(data_train), # Model is trained with training data
fixed_train_size = False,
return_best = True,
verbose = False
)
Como **return_best = True**, o objeto de previsão será atualizado com a melhor configuração encontrada e treinada em todo o conjunto de dados. Isso significa que o modelo final obtido da busca em grid terá a melhor combinação de lags e hiperparâmetros que resultaram na métrica de maior desempenho. Esse modelo final pode então ser usado para previsões futuras sobre novos dados.
Período de teste de backtesting
Uma vez identificada a melhor combinação de hiperparâmetros a partir dos dados de validação, avalia-se a capacidade preditiva do modelo quando aplicado ao conjunto de teste. A função backtesting_forecaster é usada para simular o processo de previsão a cada 36 horas em nosso exemplo.
Recomenda-se revisar a documentação da função backtesting_forecaster para entender melhor seus recursos. Isso ajudará a utilizar todo o seu potencial para analisar a capacidade preditiva do modelo.
Características exógenas
Além de usar preditores autorregressivos obtidos do passado da própria variável resposta, é possível adicionar outras variáveis exógenas. Neste caso, estão disponíveis informações de calendário (mês, dia da semana, hora, feriados, …) e variáveis meteorológicas (vento, temperatura, humidade, …).
Cuidado que precisamos ter!
O uso de variáveis meteorológicas deve ser feito com cautela. Quando o modelo é implantado em produção, as condições meteorológicas futuras não são conhecidas, mas são previsões feitas pelos serviços meteorológicos. Como são previsões, elas introduzem erros no modelo de previsão. Como consequência, as previsões do modelo tendem a piorar. Uma forma de antecipar esse problema e saber (não evitar) o desempenho esperado do modelo é usar as previsões meteorológicas disponíveis no momento do treinamento do modelo, em vez das condições registradas.
Recursos contínuos com um padrão cíclico
Variáveis contínuas que possuem um padrão cíclico, como horas, podem ser tratadas de várias maneiras, cada uma com suas vantagens e desvantagens.
Uma abordagem é usar os recursos diretamente como valores numéricos sem qualquer transformação. Este método evita a geração de inúmeras novas características, mas pode impor uma ordem linear incorreta nos valores. Por exemplo, a hora 23 de um dia e a hora 00 do outro estão muito distantes quando na verdade há apenas uma hora de diferença entre elas.
Outra possibilidade é tratar características cíclicas como variáveis categóricas para evitar a imposição de uma ordem linear. No entanto, esta abordagem pode resultar na perda da informação cíclica inerente à variável.
Existe uma terceira maneira de lidar com características cíclicas que geralmente é preferida aos outros dois métodos. Isso envolve transformar os recursos usando o seno e o cosseno de seu período. Essa abordagem gera apenas dois novos recursos que capturam a ciclicidade dos dados de forma mais precisa do que os dois métodos anteriores porque preserva a ordem natural do recurso e evita impor uma ordem linear.
Variável categórica
Embora as variáveis ‘mês’, ‘dia da semana’, ‘clima’ e ‘feriado’ sejam armazenadas como valores numéricos, na verdade são variáveis categóricas. Portanto, é importante converter seu tipo de dados para categórico para evitar que o modelo os interprete como recursos numéricos. Isso ajudará a garantir que o modelo seja capaz de capturar com precisão as características e padrões exclusivos dessas variáveis.
Existem várias abordagens para incorporar variáveis categóricas no XGBoost (e outras estruturas de aumento de gradiente):
Uma opção é transformar os dados convertendo valores categóricos em valores numéricos usando métodos como codificação one-hot ou codificação ordinal. Essa abordagem é aplicável a todos os modelos de aprendizado de máquina.
Como alternativa, o XGBoost pode manipular variáveis categóricas internamente sem a necessidade de pré-processamento. Isso pode ser feito automaticamente definindo o parâmetro enable_categorical=’auto’ e codificando as variáveis como o tipo de dados da categoria dentro de um Pandas DataFrame. Como alternativa, os usuários podem especificar o tipo de categoria de cada recurso usando o parâmetro feature_dtype.
Importante!
Não existe um método que sempre supere os outros. As regras gerais são:
Quando a cardinalidade das variáveis categóricas é alta (muitos valores diferentes), é melhor usar o suporte nativo para variáveis categóricas do que usar a codificação one-hot.
Com dados codificados em um ponto, são necessários mais pontos de divisão (ou seja, mais profundidade) para recuperar uma divisão equivalente que pode ser obtida com um único ponto de divisão usando manipulação nativa.
Quando uma variável categórica é convertida em múltiplas variáveis dummy usando one-hot, sua importância é diluída, tornando a análise da importância dos atributos mais complexa de interpretar.
One hot encoding
ColumnTransformers no scikit-learn fornecem uma maneira poderosa de definir transformações e aplicá-las a recursos específicos. Ao encapsular as transformações em um objeto ColumnTransformer, ele pode ser passado para um previsor usando o argumento Transformer_exog.
Observação
A aplicação de uma transformação a todo o conjunto de dados independente do previsor é viável. No entanto, é crucial garantir que as transformações sejam aprendidas apenas com os dados de treinamento para evitar vazamento de informações. Além disso, a mesma transformação deve ser aplicada aos dados de entrada durante a previsão. Portanto, é aconselhável incluir a transformação no previsor, para que seja tratada internamente. Essa abordagem garante consistência na aplicação de transformações e reduz a probabilidade de erros.
one_hot_encoder = make_column_transformer(
(
OneHotEncoder(sparse_output=False, drop='if_binary'),
make_column_selector(dtype_exclude=np.number),
),
remainder="passthrough",
verbose_feature_names_out=False,
).set_output(transform="pandas")
Para mais informação sobre como funciona os parâmetros: https://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.html
Para examinar como os dados são transformados, é possível usar o método create_train_X_y para gerar as matrizes que o previsor usa para treinar o modelo. Essa abordagem fornece informações sobre as manipulações de dados específicas que ocorrem durante o processo de treinamento.
Implementação nativa para recursos categóricos
As bibliotecas Gradient Boosting (XGBoost, LightGBM, CatBoost e HistGradientBoostingRegressor) assumem que as categorias de entrada são inteiros começando de 0 até o número de categorias [0, 1, …, n_categories-1]. Na maioria dos casos reais, as variáveis categóricas não são codificadas com números, mas com strings, portanto, uma etapa intermediária de transformação é necessária. Duas opções são:
Defina colunas com variáveis categóricas para a categoria de tipo. Internamente, essa estrutura de dados consiste em uma matriz de categorias e uma matriz de valores inteiros (códigos) que apontam para o valor real da matriz de categorias. Ou seja, internamente é um array numérico com um mapeamento que relaciona cada valor a uma categoria. Os modelos são capazes de identificar automaticamente as colunas da categoria de tipo e acessar seus códigos internos. Essa abordagem é aplicável a XGBoost, LightGBM e CatBoost.
Pré-processe as colunas categóricas com um OrdinalEncoder para transformar seus valores em números inteiros e, em seguida, indique explicitamente quais recursos devem ser tratados como categóricos.
Para usar a detecção automática no skforecast, as variáveis categóricas devem primeiro ser codificadas como números inteiros e, em seguida, armazenadas como categoria de tipo novamente. Isso ocorre porque o skforecast usa internamente uma matriz numpy numérica para acelerar o cálculo.
⚠ Aviso
Ao implantar modelos em produção, é altamente recomendável evitar o uso de detecção automática com base em colunas do tipo `category` do pandas. Embora o pandas forneça uma codificação interna para essas colunas, ela não é consistente em diferentes conjuntos de dados e pode variar dependendo das categorias presentes em cada conjunto de dados. Portanto, é crucial estar ciente desse problema e tomar as medidas apropriadas para garantir consistência na codificação de recursos categóricos ao implantar modelos em produção. Mais detalhes sobre esse problema podem ser encontrados em Implementação nativa para recursos categóricos.
A inclusão de variáveis exógenas como preditoras reduziu o erro de previsão em mais da metade. Os resultados obtidos usando a codificação one-hot (erro de backtest de 6330) são muito semelhantes aos obtidos usando a implementação nativa de XGBoost para variáveis categóricas (erro de backtest de 6317).
Feature importances
Uma vantagem de evitar a codificação one-hot é que ela evita o aumento do número de features, o que facilita a interpretação. Isso pode levar a uma análise mais simples e a uma melhor compreensão dos dados, pois há menos variáveis a serem consideradas.
LightGBM, CatBoost and HistGradientBoostingRegressor
Desde o sucesso do XGBoost como algoritmo de aprendizado de máquina, foram desenvolvidas novas implementações que também alcançam excelentes resultados, três delas são:
LightGBM: desenvolvido pela Microsoft.
HistGradientBoostingRegressor: desenvolvido por scikit-learn.
CatBoost: desenvolvido por Yandex.
LightGBM
O Light GBM cresce a árvore verticalmente, enquanto outro algoritmo cresce as árvores horizontalmente, o que significa que o Light GBM cresce a árvore em forma de folha (leaf-wise), enquanto outro algoritmo cresce em nível (level-wise). Ele escolherá a folha com perda delta máxima para crescer. Ao cultivar a mesma folha, o algoritmo Leaf-wise pode reduzir mais perdas do que um algoritmo level-wise.
Por que o Light GBM está ganhando popularidade?
O tamanho dos dados está aumentando dia a dia e está se tornando difícil para os algoritmos tradicionais de ciência de dados fornecer resultados mais rápidos. Light GBM é prefixado como ‘Light’ por causa de sua alta velocidade. O GBM leve pode lidar com o grande tamanho de dados e requer menos memória para ser executado. Outra razão pela qual o Light GBM é popular é porque ele se concentra na precisão dos resultados. O LGBM também oferece suporte ao aprendizado de GPU e, portanto, os cientistas de dados estão usando amplamente o LGBM para o desenvolvimento de aplicativos de ciência de dados.
Podemos usar o Light GBM em qualquer conjunto de dados?
Não, não é aconselhável usar LGBM em pequenos conjuntos de dados. O GBM leve é sensível ao superajuste e pode facilmente sobreajustar dados pequenos. Não há limite para o número de linhas, mas minha experiência sugere que eu o use apenas para dados com mais de 10.000 observações.
HistGradientBoostingRegressor
Esse algoritimo acaba sendo uma boa opção para quem busca performance, relativamente “novo” — foi desenvolvido em 1990, ele tem um jeito interessante em lidar com os dados. Basicamente, como o próprio nome já revela, ele agrupo como se fosse um histograma os dados e assim diminui seu tamanho consideravelmente.
Na sua documentação existe uma breve explicação bem intuitiva:
“Esses estimadores rápidos primeiro agrupam as amostras de entrada X em compartimentos de valor inteiro (normalmente 256 compartimentos), o que reduz tremendamente o número de pontos de divisão a serem considerados e permite que o algoritmo alavanque estruturas de dados baseadas em números inteiros (histogramas) em vez de depender de contínuos classificados valores ao construir as árvores. A API desses estimadores é um pouco diferente e alguns dos recursos de GradientBoostingClassifier e GradientBoostingRegressor ainda não são suportados, por exemplo, algumas funções de perda.”
Fontes: https://scikit-learn.org/stable/modules/ensemble.html#histogram-based-gradient-boosting
Encontrando uma outra explicação que ajuda no entendimento desse algoritmos:
“A construção de árvores de decisão pode ser acelerada significativamente reduzindo o número de valores para recursos de entrada contínua. Isso pode ser obtido pela discretização ou categorização de valores em um número fixo de compartimentos. Isso pode reduzir o número de valores exclusivos para cada recurso de dezenas de milhares para algumas centenas.
Isso permite que a árvore de decisão opere no balde ordinal (um número inteiro) em vez de valores específicos no conjunto de dados de treinamento. Essa aproximação grosseira dos dados de entrada geralmente tem pouco impacto na habilidade do modelo, se não melhora a habilidade do modelo e acelera drasticamente a construção da árvore de decisão.
Além disso, estruturas de dados eficientes podem ser usadas para representar o agrupamento dos dados de entrada; por exemplo, histogramas podem ser usados e o algoritmo de construção de árvore pode ser ainda mais adaptado para o uso eficiente de histogramas na construção de cada árvore.”
Fonte: https://machinelearningmastery.com/histogram-based-gradient-boosting-ensembles/
Ao criar um previsor usando HistogramGradientBoosting, os nomes das colunas categóricas devem ser especificados durante a instanciação do regressor, passando-os como uma lista para o argumento categorical_feature.
CatBoost
CatBoost é um método de aprendizado de máquina supervisionado usado pela ferramenta Train Using AutoML e usa árvores de decisão para classificação e regressão. Como o próprio nome sugere, o CatBoost possui dois recursos principais: trabalha com dados categóricos (o Cat) e usa aumento de gradiente (o Boost). O aumento de gradiente é um processo no qual muitas árvores de decisão são construídas iterativamente. Cada árvore subseqüente melhora o resultado da árvore anterior, levando a melhores resultados. O CatBoost melhora o método de aumento de gradiente original para uma implementação mais rápida.
O CatBoost supera uma limitação de outros métodos baseados em árvore de decisão nos quais, normalmente, os dados devem ser pré-processados para converter variáveis categóricas de string em valores numéricos, codificações one-hot e assim por diante. Este método pode consumir diretamente uma combinação de variáveis explicativas categóricas e não categóricas sem pré-processamento. Ele pré-processa como parte do algoritmo. CatBoost usa um método chamado codificação ordenada para codificar recursos categóricos. A codificação ordenada considera as estatísticas de destino de todas as linhas antes de um ponto de dados para calcular um valor para substituir o recurso categórico.
Outra característica única do CatBoost é que ele usa árvores simétricas. Isso significa que em cada nível de profundidade, todos os nós de decisão usam a mesma condição de divisão.
Infelizmente, a versão atual do skforecast não é compatível com o tratamento integrado do CatBoost de recursos categóricos. O problema surge porque CatBoost aceita apenas recursos categóricos como números inteiros, enquanto skforecast converte dados de entrada em flutuantes para computação mais rápida usando matrizes numpy no processo de previsão interna. Para contornar essa limitação, é necessário aplicar a codificação one-hot ou a estratégia de codificação de rótulo aos recursos categóricos antes de usá-los com o CatBoost.
Conclusão
Conforme mostrado neste documento, a inclusão de variáveis exógenas como preditores pode melhorar significativamente o desempenho da previsão.
O uso de modelos de aprendizado de máquina em problemas de previsão é muito fácil graças às funcionalidades oferecidas pelo skforecast.
Características categóricas podem ser incluídas como variáveis exógenas.
O modelo Gradient Boosting gerado pelo LightGBM tem um desempenho ligeiramente melhor do que as outras bibliotecas neste caso.