Esse projeto consiste em uma pipeline completo de processamento e modelagem de dados usando reclamações de veículos do NHTSA do período entre 2014 e 2024. O foco principal do projeto é a predição do tipo de componente do veículo relatado nas reclamações, utilizando técnicas de Processamento de Linguagem Natural (NLP) e Machine Learning.
A abordagem adotada está representada no fluxograma da imagem abaixo e segue as seguintes etapas:
Aquisição e pré-processamento dos dados: Foram coletados os dados de reclamações de veículos do NHTSA, realizou-se limpeza textual, correções textuais e tokenização.
Engenharia de Features: Os textos foram transformados em representações numéricas utilizando técnicas de embeddings.
Treinamento do Modelo: Implementamos e avaliamos modelos de classificação usando o BERT.
Avaliação: Testamos diferentes hiperparâmetros para otimizar o desempenho.
Deploy da API: Criamos uma Api para tornar possível consumir o modelo e criamos um container Docker para garantir a portabilidade do modelo, permitindo integração com aplicações externas.
O objetivo desta etapa foi coletar os dados de reclamações de veículos da NHTSA (National Highway Traffic Safety Administration) entre os anos de 2014 e 2024. Esses dados form essenciais para treinar um modelo de classificação que prediz o tipo de componente veicular relatado nas reclamações. A escolha desse problema como tarefa de classificação se deve ao fato de os dados já estarem previamente rotulados, além da ampla variedade de categorias de componentes disponíveis para análise.
Os dados foram adquiridos diretamente da API oficial da NHTSA. Foram utilizados os seguintes endpoints:
- Model Years: Obtém os anos de modelo disponíveis.
- Makes by Model Year: Obtém as marcas de veículo associadas a cada ano de modelo.
- Models by Make and Year: Obtém os modelos de veículos vinculados a cada combinação de marca e ano.
- Complaints by Vehicle: Obtém as reclamações registradas para cada veículo, considerando marca, modelo e ano.
A extração foi realizada em etapas sequenciais:
- Obtenção dos Anos de Modelo: Chamada ao endpoint para listar os anos de modelo disponíveis.
- Obtenção das Marcas por Ano: Para cada ano coletado, buscamos as marcas correspondentes.
- Obtenção dos Modelos por Marca e Ano: Para cada combinação de marca e ano, buscamos os modelos.
- Obtenção das Reclamações: Para cada combinação de marca, modelo e ano, buscamos as reclamações na API.
Os dados extraídos foram armazenados em arquivos JSON organizados dentro do diretório ../data/json
. Os principais arquivos gerados foram:
model_years.json
: Lista de anos de modelo.make_model_year.json
: Lista de marcas vinculadas a cada ano de modelo.model_make_year.json
: Lista de modelos vinculados a cada marca e ano.complaints.json
: Lista de reclamações contendo detalhes como o tipo de problema relatado, o veículo associado e outras informações relevantes.
Para garantir a robustez da aquisição de dados, foram adotadas as seguintes medidas:
- Uso de timeout nas requisições para evitar bloqueios indesejados.
- Implementação de tratamento de exceções (
try/except
) para lidar com falhas de conexão ou respostas inválidas. - Utilização de logging estruturado para registrar informações sobre o progresso e possíveis erros durante a extração.
- Exibição de barras de progresso (
tqdm
) para facilitar o monitoramento da execução do script.
Na etapa de aquisição de dados foi garantida a obtenção de informações estruturadas sobre os veículos e suas respectivas reclamações. Os dados coletados foram utilizados na próxima etapa para pré-processamento e extração de features importantes.
O objetivo desta etapa foi preparar os dados brutos adquiridos para que possam ser utilizados no treinamento do modelo de classificação. As principais ações incluíram a limpeza dos textos das reclamações, a remoção de dados irrelevantes e a criação de um conjunto de dados balanceado.
- Filtragem de Dados: Foram selecionadas apenas as colunas relevantes (
odiNumber
,dateComplaintFiled
,components
,summary
). - Conversão de Datas: A coluna
dateComplaintFiled
foi convertida para o formato datetime para permitir análises temporais. - Remoção de Dados Duplicados e Nulos: Reclamações sem descrição (
summary
) ou sem categoria (components
) foram descartadas. - Classificação das Reclamações: As reclamações foram agrupadas em cinco categorias principais de componentes (
ELECTRICAL SYSTEM
,AIR BAGS
,STRUCTURE
,SERVICE BRAKES
eOTHER
). Foram escolhidas as primeiras quatro categorias por serem as mais abrangentes no conjunto de dados. Todas as outras categorias diferentes das mesmas foram colocadas na categoria OTHER. - Balanceamento de Classes: Como algumas categorias tinham muito mais exemplos do que outras, mais especificamente a categorias OTHERS, foi aplicada uma técnica de balanceamento para garantir uma distribuição mais uniforme das classes no conjunto de treino. Essa técnica consiste em reduzir a quantidade de amostras das classes majoritarias para o mesmo número de amostras da classe minoritária do conjunto de dados.
- Limpeza de Texto: Foram removidos caracteres especiais, múltiplos espaços, e o texto foi normalizado para caixa baixa, já que muitas reclamações não seguiam o padrão, muitas estavam em caixa alta e outras em caixa baixa. Tendo em vista o melhor aprendizado e generalização do modelo, foi optado por converter todas as reclamações para caixa baixa.
Os dados foram divididos da seguinte forma:
- Treinamento e Validação: Reclamações entre 2014 e 2024 foram usadas para treino e validação do modelo.
- Teste: Um conjunto de dados adicional, contendo reclamações anteriores a 2014, foi separado para avaliar o desempenho do modelo em um período diferente, atendendo ao requisito opcional de detecção de data drift. Não foi feita nenhuma manipulação nesses dados, do tipo balanceamento, como foi feito na conjunto de dados de treinamento.
Os dados foram salvos em arquivos CSV organizados no diretório ../data/csv
:
train.csv
: Conjunto de treinamento, composto por 8.357 amostras.eval.csv
: Conjunto de validação, composto por 2.090 amostras.test.csv
: Conjunto de teste, composto por 2.090 amostras.
A distribuição das classes de cada conjunto de dados pode ser vista na tabela a seguir:
Conjunto de Dados | ELETRICAL SYSTEM | AIR BAGS | STRUCTURE | SERVICE BRAKES | OTHER |
---|---|---|---|---|---|
train.csv | 1.672 | 1.671 | 1.671 | 1.671 | 1.671 |
eval.csv | 417 | 418 | 418 | 418 | 418 |
test.csv | 159 | 99 | 113 | 17 | 1.701 |
A etapa de processamento assegurou que os dados estivessem limpos, organizados e balanceados, prontos para o treinamento do modelo de machine learning. O dataset NHTSA Complaints Dataset foi cuidadosamente construído e está disponível na plataforma Hugging Face vic35get/nhtsa_complaints_dataset
. Ao disponibilizar os dados nessa plataforma, é facilitada a reprodução do treinamento e da avaliação do modelo, além de tornar o dataset acessível à comunidade para uso em projetos e pesquisas futuras.
Realizamos uma análise descritiva dos conjunto de dados de reclamações selecionado anteriormente. Essa etapa é muito importante para verificar a distribuição das variáveis após o pré-processamento, avaliar o balanceamento das classes e identificar possíveis padrões que possam influenciar o desempenho do modelo, além de apoiar a escolha do modelo que será utilizado.
A imagem a seguir, trata de uma análise descritiva dos textos das reclamações.
A maioria dos textos de reclamação contém um número relativamente pequeno de palavras únicas, com poucos textos apresentando uma grande diversidade de vocabulário. Esse padrão sugere que o conjunto de dados é composta por textos com um vocabulário mais simples, o que pode ser vantajoso para modelos como o BERT, que se beneficiam de contextos linguísticos mais diretos e frequentes. Contudo, a falta de diversidade pode limitar a capacidade de generalização do modelo para textos com vocabulários mais complexos ou técnicos, o que pode ser um desafio em domínios mais especializados.
A análise do comprimento médio das palavras indica que a maioria dos textos apresenta palavras com 4 a 5 caracteres. Esse padrão é adequado para o BERT, pois o modelo lida bem com palavras de tamanho médio, conseguindo capturar seus significados contextuais de forma eficaz. Entretanto, a presença de termos técnicos ou siglas, que costumam ser mais curtos ou mais longos, pode prejudicar a capacidade do BERT de interpretar esses elementos com precisão, especialmente quando não são representados corretamente durante a tokenização.
Observa-se que a maioria dos textos possui entre 30% e 40% de palavras consideradas stopwords, ou seja, palavras comuns que não carregam significado semântico relevante, como "o", "a", "de". Embora o BERT seja projetado para lidar com stopwords sem perda de desempenho, essa alta proporção pode gerar ruído durante o treinamento e aumentar o custo computacional, pois essas palavras ocupam parte do modelo sem agregar informações úteis para a tarefa de classificação.
A maior parte dos textos contém poucas sentenças, mas há uma quantidade considerável de textos com um número maior de sentenças. Isso indica que, em sua maioria, os textos são relativamente curtos, o que é vantajoso para o BERT, que é otimizado para lidar com textos curtos e moderados. No entanto, quando os textos se tornam mais longos, o modelo pode enfrentar dificuldades, pois há limitações no número de tokens que podem ser processados de uma vez. Nesse caso, o BERT pode precisar realizar truncamento ou segmentação, o que pode resultar na perda de contexto importante para a classificação.
A figura a seguir mostra a distribuição das classes de problema no conjunto de dados
Como ilustrado no gráfico, as classes mais representativas no dataset são: ELECTRICAL SYSTEM, STRUCTURE, AIR BAGS e SERVICE BRAKES. Essas classes, por serem as mais frequentes, foram selecionadas para serem tratadas diretamente pelo modelo. As demais classes, que possuem uma frequência significativamente menor, serão agrupadas sob a categoria OTHER. Essa abordagem permite que o modelo foque nas classes majoritárias, aproveitando a grande quantidade de dados disponíveis, o que pode resultar em uma aprendizagem mais robusta e precisa.
Para este projeto, optamos por utilizar o BERT (Bidirectional Encoder Representations from Transformers), especificamente a versão bert-base-uncased
desenvolvida pela Google, por ser uma versão pré-treinada com volumosos conjuntos de dados na língua inglesa.
O BERT foi escolhido por ser um dos modelos mais avançados e eficazes para tarefas de Processamento de Linguagem Natural (NLP), especialmente para a classificação de texto. Sua arquitetura baseada em transformers permite capturar relações contextuais entre palavras de maneira bidirecional, o que é fundamental para a compreensão de textos de reclamações veiculares, que muitas vezes contêm ambiguidades, expressões informais e um vocabulário específico do domínio.
A arquitetura do BERT é composta por várias camadas de transformadores, que são redes neurais especializadas no processamento de sequências. O modelo é pré-treinado em grandes volumes de texto e, em seguida, ajustado para tarefas específicas, como a classificação de texto.
O treinamento do BERT envolve duas tarefas principais: Masked Language Modeling (MLM) e Next Sentence Prediction (NSP), que permitem ao modelo aprender representações profundas e contextualizadas das palavras, resultando em um entendimento mais preciso do significado.
A tarefa de modelagem escolhida foi a classificação multiclasse, em que cada reclamação é categorizada em um dos tipos de componentes previamente rotulados no dataset da NHTSA. A escolha do BERT visa não só melhorar a precisão na identificação do tipo de problema, mas também garantir que o modelo seja capaz de lidar com as nuances linguísticas presentes nas reclamações.
A engenharia de features foi realizada com foco na tokenização eficiente dos textos das reclamações. Utilizamos o tokenizador do BERT, que converte os textos em embeddings compatíveis com a arquitetura do modelo. As etapas aplicadas foram:
- Tokenização: Cada reclamação foi tokenizada.
- Truncamento e Padding: Para manter um tamanho fixo de entrada, os textos foram truncados ou preenchidos para terem até 512 tokens.
- Conversão para Tensores: As sequências tokenizadas foram convertidas para tensores PyTorch para treinamento eficiente.
O modelo foi treinado com os seguintes hiperparâmetros:
Parâmetro | Valor |
---|---|
Modelo Base | bert-base-uncased |
Tamanho do Batch | 4 |
Taxa de Aprendizado | 1e-5 |
Épocas | 30 |
Early Stopping | 3 épocas sem melhora |
Estratégia de Avaliação | epoch |
Métrica para Melhor Modelo | eval_loss |
Otimizador | AdamW |
Critério de parada antecipada (early stopping): Para evitar overfitting, foi utilizado um critério de parada antecipada, interrompendo o treinamento após 3 épocas consecutivas sem melhoria na métrica de validação (eval_loss
).
Versionamento e Salvamento:
- Durante o treinamento, os modelos foram salvos na pasta
../models
, e as métricas registradas na pasta../metrics
.
Métrica | Valor |
---|---|
Acurácia | 86.40% |
F1-Score | 85.78% |
Precisão | 85.96% |
Recall | 86.40% |
Métrica | Valor |
---|---|
Acurácia | 69.94% |
F1-Score | 75.69% |
Precisão | 87.96% |
Recall | 69.94% |
O modelo apresentou alto desempenho na validação, mas uma queda significativa nos resultados para o conjuunto de dados de teste. Isso já era esperado, visto que a classe OTHER abrange tudo que não envolve as 4 classes principais, que foram definidas previamente. Foi atestado que no conjunto de dados original, existem classes simultâneas, como: TIRES/STRUCTURE, isso dificulta a classificação do modelo, visto que o mesmo foi treinado para classificar separadamente TIRES e STRUCTURE. Além disso, o conjunto de dados de teste está muito desbalanceado, concentrando mais de 80% das amostras na classe OTHER, que é a classe que o modelo mais erra. Acredita-se que o baixo desempenho no conjunto de dados de teste em relação ao conjunto de dados de validação se deve a essas razões.
O modelo foi treinado por 4 épocas, com o seguinte comportamento:
Época | Acurácia | F1-Score |
---|---|---|
1 | 86.40% | 85.78% |
2 | 86.88% | 86.47% |
3 | 86.74% | 86.07% |
4 | 86.07% | 85.78% |
Análise:
- Observamos um aumento na acurácia e no F1-score até a segunda época.
- A partir da terceira época, houve uma leve degradação no desempenho.
- O critério de early stopping interrompeu o treinamento na quarta época, prevenindo um overfitting.
O modelo demonstrou bom desempenho no conjunto de dados de validação, mas uma redução significativa no conjunto de dados de teste, o que sugere que melhorias podem ser aplicadas. Algumas estratégias para mitigar esse efeito incluem:
✅ Aumento do tamanho do batch para melhorar a estabilidade do treinamento.
✅ Data Augmentation: Uso de técnicas como sinônimos e reformulação para aumentar a diversidade dos dados de treinamento.
✅ Fine-tuning com menos camadas congeladas para permitir melhor adaptação ao domínio das reclamações veiculares.
✅ Reformular a divisão de classes de reclamações para abranger também aqueles registros que contém mais de uma classe. Poderia ser usada uma abordagem de classificação multilabel, em que o modelo pode classificar uma reclamação com mais de uma classe simultâneamente.
Com essas melhorias, é esperado alcançar um modelo mais robusto e generalizável, capaz de lidar melhor com dados fora da distribuição original.
O Hugging Face é uma plataforma líder no campo de processamento de linguagem natural (NLP), oferecendo uma vasta coleção de modelos pré-treinados e ferramentas para diversas tarefas, como análise de sentimentos, tradução, e classificação de textos. A plataforma permite que pesquisadores e desenvolvedores compartilhem seus modelos, facilitando o acesso e a colaboração na comunidade de machine learning.
O modelo de classificação de reclamações (nhtsa_complaints_classifier) foi disponibilizado na plataforma Hugging Face, permitindo seu acesso e utilização pela comunidade.
Após o treinamento, o modelo foi carregado e enviado para o repositório público do Hugging Face, onde qualquer usuário pode acessá-lo e utilizá-lo em seus próprios projetos. Isso garante que o modelo esteja disponível para uso em diversas aplicações e que a comunidade possa colaborar na melhoria e adaptação do modelo.
Com o deploy, o modelo agora pode ser facilmente integrado em sistemas de produção, onde é possível realizar classificações de novas reclamações de veículos em tempo real, aproveitando a infraestrutura escalável e robusta oferecida pela plataforma.
O objetivo desta etapa foi criar uma API simples que permita consumir o modelo treinado e fazer previsões com novas reclamações. A API foi implementada utilizando o framework Flask, sendo uma solução prática e eficiente para disponibilizar o modelo para uso em produção.
A API foi projetada para expor quatro endpoints principais:
- GET /: Um endpoint de verificação, retornando uma mensagem simples de boas-vindas.
- GET /status: Endpoint para verificar o status da API, útil para monitoramento.
- POST /load: Endpoint responsável pelo carregamento do modelo treinado e do tokenizador a partir de um modelo pré-existente (como o BERT). Ele permite que o modelo seja carregado na memória e esteja pronto para realizar previsões.
- POST /classify_complaints: Endpoint principal da API, responsável por receber reclamações no formato JSON, limpar o texto e classificar o tipo de componente utilizando o modelo treinado.
A estrutura da API é baseada no Flask, com a utilização da biblioteca flask_classful
para facilitar a organização e modularização dos endpoints. A seguir, estão os detalhes de cada uma das rotas implementadas:
-
/ (GET): Endpoint básico para verificar se a API está funcionando corretamente, retornando uma mensagem de sucesso.
-
/status (GET): Endpoint de status que retorna uma mensagem de "sucesso", indicando que a API está ativa.
-
/load (POST): Este endpoint é utilizado para carregar o modelo e o tokenizador. Ele recebe um JSON contendo o nome do modelo, faz o carregamento do modelo pré-treinado a partir da Hugging Face, e prepara a pipeline de classificação. Isso permite que o modelo seja recarregado sem a necessidade de reiniciar a aplicação.
- Exemplo de payload:
{ "model": "vic35get/nhtsa_complaints_classifier" }
- Exemplo de payload:
-
/classify_complaints (POST): Endpoint responsável por receber uma reclamação no formato JSON, limpar o texto utilizando uma função de limpeza de texto personalizada e passar o texto para o modelo de classificação. O resultado da classificação é então retornado no formato JSON. A resposta inclui o rótulo da classificação e a pontuação associada, que indica a confiança do modelo na classificação.
- Exemplo de payload:
{ "complaint": "I am facing issues with the airbag system." }
- Exemplo de retorno:
{ "message": "success", "output": [ { "label": "AIR BAGS", "score": 0.9949865341186523 } ], "status": 0 }
- Exemplo de payload:
A função de limpeza de texto é uma etapa crucial para garantir que os dados estejam no formato adequado para a classificação. A função clean_text
realiza as seguintes ações:
- Remoção de caracteres especiais: Qualquer caractere indesejado, como pontuações, símbolos e caracteres não alfanuméricos, é substituído por espaços.
- Normalização de texto: O texto é convertido para minúsculas e os múltiplos espaços são reduzidos a um único espaço.
Esse processo garante que o texto esteja limpo e sem ruídos, proporcionando melhores resultados de classificação.
A API foi configurada para rodar na porta 5009, mas permite a personalização de parâmetros como a porta e o host via argumentos de linha de comando. A execução da aplicação pode ser feita diretamente com o comando:
python3 service.py
Para o uso adequado da API, deve-se executar primeiramente o endpoint load, que carrega o modelo em memória, passando o nome do modelo de classificação vic35get/nhtsa_complaints_classifier
, assim como visto na seção 5.3.
Para garantir a portabilidade, escalabilidade e facilidade de deploy, a API foi containerizada utilizando Docker. A criação do container permite que a API seja executada de forma consistente em qualquer ambiente, seja em desenvolvimento ou produção, sem dependências externas ou configurações específicas de sistema operacional. A utilização do Docker Compose facilita a orquestração e o gerenciamento do container, garantindo que todos os serviços necessários (como a execução da API) estejam devidamente configurados e isolados.
Foram criados dois arquivos principais para a configuração do Docker:
-
docker-compose.yml: Este arquivo define o serviço da API, especificando como o container será construído, quais portas serão expostas e as variáveis de ambiente necessárias para rodar a aplicação em produção. Além disso, ele garante que o container seja reiniciado automaticamente em caso de falha.
-
Dockerfile: O Dockerfile descreve o processo de construção da imagem Docker, incluindo a instalação das dependências necessárias para rodar a aplicação e a configuração da execução do servidor Flask.
- Portabilidade: A aplicação containerizada pode ser executada em qualquer máquina que tenha o Docker instalado, sem a necessidade de configurações adicionais. Isso garante que a API funcionará de maneira idêntica em diferentes ambientes.
- Facilidade de Deploy: Com o Docker Compose, o processo de deploy da API se torna simples e direto. Um único comando é suficiente para construir e rodar o container, eliminando a complexidade de configuração manual de ambientes.
- Escalabilidade: Docker permite que a aplicação seja facilmente escalada, criando múltiplos containers em diferentes servidores ou nuvens. Isso facilita o crescimento da infraestrutura à medida que a demanda aumenta.
Para executar a API containerizada, siga os seguintes passos:
-
Certifique-se de que o Docker está instalado em sua máquina.
-
Execute o comando abaixo para construir e iniciar o container:
docker build -t classifier . docker run -d --restart always -p 5009:5009 --name classifier classifier
Para o uso adequado da API, deve-se executar primeiramente o endpoint load, que carrega o modelo em memória, passando o nome do modelo de classificação vic35get/nhtsa_complaints_classifier
, assim como visto na seção 5.3.