O WalMart é uma rede com milhares de lojas em 27 países. É possível encontrar vários artigos sobre os mecanismos tecnológicos utilizados para gerenciar a logística e distribuição dos produtos. É a segunda vez que eles oferecem uma competição no Kaggle com a intenção de encontrar candidatos para entrevistas para vagas de cientistas de dados.

Uma grande vantagem deste tipo de competição é termos acesso a dados de grandes companhias, e entender quais são os problemas que eles estão tentando resolver com modelos probabilísticos.

O objetivo da competição era criar um modelo que pudesse prever a quantidade de venda de alguns produtos, em lojas específicas, nos dias antes e depois de nevascas e tempestades. O exemplo dado por eles na descrição da tarefa foi a venda de guarda-chuvas, que intuitivamente deve ver um aumento antes de uma grande tempestade.

Índice

Dados

Para treinar o modelo foram oferecidos dois arquivos: um deles continha informações sobre a identificação das lojas, produtos, e as estações meteorológicas mais próximas. O outro continha dados meteorológicos de cada estação.

No total foram disponibilizados dados de 111 produtos cujas vendas podem ser afetadas pelas condições climáticas, 45 lojas, e 20 estações meteorológicas. O objetivo era prever a quantidade de cada produto, em cada loja, que seria vendida 3 dias antes, 3 dias depois, e no dia do evento climático.

Um evento climático era considerado caso tivesse sido registrada mais que uma polegada de chuva, ou mais que duas polegadas de neve naquele dia.

A combinação entre lojas e produtos nos dava cerca de 4,6 milhões de exemplos para treino, e cerca de 500 mil para teste. Cada exemplo se referia a um dia, loja e produto.

Métrica

A métrica utilizada foi o Root Mean Square Log Error. É basicamente o RMSE aplicado à transformação log(Y + 1) das previsões. Isso significa que erros em previsões que deveriam ser próximas de 0 seriam punidos mais severamente do que erros em previsões com números mais altos. Por exemplo, prever 5 itens quando deveria ser 0, tem uma punição maior do que prever 105 quando deveria ser 100.

Transformação dos dados

Como eu só tinha 10 dias para trabalhar nesta competição, decidi verificar até onde era possível chegar com um modelo baseado apenas nas variáveis originais e, quem sabe, fazer um ensemble.

Uma diferença desta para outras competições é que, apesar dos dados virem organizados, você era responsável por unir as variáveis climáticas com os dados identificados de produtos e lojas. Faz todo o sentido, pois o Walmart não iria querer um data scientist que não saiba manipular dados.

Para isto utilizei Pandas. Esta biblioteca Python para manipulação de dados é uma das mais utilizadas, e lembra bastante as estruturas de dados disponíveis em R.

Num primeiro momento usei todas as variáveis como numéricas, e treinei um XGBoost com leve tuning, excluindo as variáveis que codificavam alertas especiais, e usando 10% do dataset para determinar o número de árvores. Como já era de se esperar, o resultado foi ruim, cerca de 0,1643 na LB.

Binarizando as variáveis

Depois de testar o primeiro modelo, codifiquei as variáveis categóricas com one-hot encoding. Ou seja, para cada nível foi criada uma coluna com o indicador 0 ou 1, caso a variável estivesse presente naquele exemplo. Normalmente o número de colunas deve ser o número de níveis menos um, para não ter problemas com colinearidade. Como eu planejava usar modelos que não eram sensíveis a esse problema, eu não me preocupei em excluir uma das colunas.

Após fazer tuning usando um subset com 10% dos dados, e obter o RMSLE de 0,1216 na validação, enviei a solução, e obtive o valor de 0,1204 na LB, uma boa melhora sobre o anterior.

Imputação

Muitos dados meteorológicos não estavam presentes, então decidi testar um método simples de imputação: substituir os NaNs pela média dos valores da coluna. Após fazer o tuning novamente, agora adequado a esses novos valores, obtive o RMSLE de 0,1140 nos 10% de validação e 0,1095 na LB.

Variáveis temporais

Não explorei muito a dimensão temporal dos dados, em uma das tentativas adicionei informações meteorológicas do dia anterior, o que reduziu o erro para 0,1083.

Subdivisões dos dados

Um método que funcionou muito bem na minha segunda competição, e que sempre acabo tentando, é dividir o training set em pequenos subsets relacionados a alguma variável e treinar um modelo específico para cada um. Neste caso, decidi criar 19 modelos, um para cada uma das 19 estações meteorológicas presentes no test set. O RMSLE na LB foi de 0,1101. Pior do que com um modelo que trata as estações como variáveis no dataset inteiro.

Um problema grave com esta abordagem é tentar usar o mesmo modelo, com os mesmos parâmetros, para datasets diferentes. Sabendo disso, decidi fazer um pequeno tuning dos parâmetros para cada dataset, o que reduziu o RMSLE da LB para 0,1069.

Apesar da pequena diferença, parece que a divisão em modelos individuais para cada estação capturava algumas informações que não estavam presentes no modelo que considerava todas juntas.

Modelos

Dos modelos que testei, dois se destacaram: Gradient Boosted Trees (XGBoost) e Random Forests.

Random Forest

Eu tinha usado Random Forest para regressão apenas uma vez, em um trabalho, mas nunca com uma quantidade grande de dados. Após fazer o tuning dos parâmetros, aplicando o modelo nos dados imputados, ela resultou num RMSLE de 0,1116 na LB.

XGBoost

O XGBoost apresentou o melhor erro de um modelo individual. Além de ajustar parâmetros, como a profundidade das árvores, usando um subset dos dados, era necessário ajustar a quantidade de árvores e a learning rate. Normalmente uma learning rate pequena e uma grande quantidade de árvores é a receita segura para melhorar a performance, em troca de mais tempo para treinar o modelo.

Como o XGBoost é sensível à seed do RNG, decidi fazer um ensemble de XGBs mudando apenas este valor. Este método melhorou marginalmente a minha pontuação em outras competições, mas nesta o impacto dele foi maior pelo seguinte fato: o XGBoost possui uma função que permite deixar dados separados para que ele determine o número de árvores que minimiza o erro. Neste caso decidi usar a função, e deixei 5% dos dados separados. Além de variar a seed para o próprio XGB, eu variei a seed para fazer a divisão dos dados, isso fez com que os modelos ficassem mais diversos, o que é essencial para um bom ensemble.

O máximo que cheguei a treinar foram 10 XGBoosts neste esquema. Apesar de ser um modelo bastante estável, o RMSLE do ensemble foi de 0,1041, apresentando uma redução comparado a 0,1095 do modelo individual.

Solução Final

No fim, juntei todas as soluções que eu havia enviado, e acabei obtendo um RMSLE de 0,1028, garantindo uma posição entre as 20% melhores.

Possíveis Melhorias

Um dia após o término da competição eu revisei as variáveis do meu XGBoost, e descobri que as variáveis que identificavam os produtos (item_nbr) não estavam em formato binário, e eram consideradas por ele as mais importantes. Com a codificação correta acredito que seria possível diminuir mais o erro, e alcançar uma posição final melhor.

Seja o primeiro a saber das novidades em Machine Learning. Me siga no LinkedIn.