Como Descobrir Anúncios Ilegais Em Russo Sem Saber Falar Russo

O site Avito.Ru, fundado em 2007, é um dos 5 maiores sites russos de classificados. Ele é utilizado tanto por pessoas físicas quanto empresas para negociar produtos novos e usados. Um dos maiores desafios é manter a qualidade do conteúdo diante do crescimento e do volume de novos anúncios. Para resolver isso, o site decidiu buscar uma solução baseada nos dados disponíveis.

Esta foi uma competição disponibilizada na plataforma Kaggle.

Para os leitores com conhecimento técnico, o código da solução final está disponível neste link (github).

Dados disponíveis

No banco de dados disponibilizado havia o conteúdo dos anúncios, atributos sobre os produtos anunciados, categoria e subcategoria, título e descrição. A variável que precisava ser prevista era binária, se o anúncio seria bloqueado ou não pela moderação. Cerca de 4 milhões de anúncios com as previsões dadas pela moderação humana foram disponibilizados para treino, e 1,5 milhão para teste.

Além disso havia uma variável indicando se aquele anúncio havia sido bloqueado por um moderador experiente, que poderia fazer a diferença no fator de erro humano.

Métrica de Avaliação

A métrica utilizada para avaliar a melhor solução era AvgPrecision\@K (Top at K). Após atribuir uma pontuação que indicava a probabilidade de um determinado anúncio estar em desacordo com as regras, eles deveriam ser ordenados, de maneira que os mais prováveis estivessem mais acima no ranking. Feito isso, o sistema considerava os K primeiros anúncios e comparava com os valores reais para decidir qual a porcentagem de acerto do modelo.

Transformação dos dados

Minha solução envolveu apenas o título e a descrição dos anúncios. Primeiro o básico, colocar todas as palavras em caixa baixa, remover as stopwords, e fazer o stemming. Um detalhe é que os anúncios estavam em russo, então eu não sei exatamente se existem diferenças de caixa alta e baixa no idioma, ou quais stopwords são relevantes para outras palavras (no caso do inglês, “can” é uma stopword, mas pode ser relevante por também ser usada como “lata”).

De qualquer maneira, esses procedimentos melhoravam a performance do modelo.

Além disso, transformei os documentos numa matriz em que cada linha era um anúncio, e cada coluna, uma palavra. Nos valores testei três variações:

- Binária: na qual a presença da palavra no anúncio era indicada com o número 1;
- Contagem: cada valor era o número de vezes que a palavra aparecia no anúncio;
- Tf-Idf: cada valor era baseado em uma fórmula que leva em conta a frequência da palavra no anúncio, e também em relação aos outros anúncios. Ela atribui um valor maior a palavras raras no contexto geral, mas que tenham uma alta frequência dentro do anúncio específico.

Dentre estas alternativas, a que demonstrou melhor performance foi a Tf-Idf. Esta é uma técnica bastante usada em classificação de texto, e normalmente apresenta uma melhora na precisão da classificação.

Apesar de fazer algumas tentativas de limpeza dos dados, tirando as palavras mais raras, a performance era afetada negativamente. O que ajudou um pouco foi remover números.

Solução

Meu foco era criar uma solução simples, então meu primeiro teste foi com a Logistic Regression em todos os dados. Um dos fatores a se levar em conta era a distribuição de exemplos positivos e negativos, que não era 50%. Com esta solução a precisão era de 91,8%.

Após testar algumas opções disponíveis no scikit-learn (biblioteca de Machine Learning em Python que utilizei), descobri que usando a função loss “modified_huber” apresentava uma maior precisão. Esta é uma função mais robusta do que a log loss, pois aplica uma penalidade quadrática para pequenos erros, e linear para grandes.

Outra ideia que ajudou muito foi separar os anúncios por categoria. Cada categoria possuía uma proporção diferente de exemplos positivos e negativos (Algumas com menos de 1% e outras com mais de 30%). Aplicando o algoritmo acima nos dados transformados desta maneira obtive 97,3% de precisão. Uma melhora de 10,6%.

Para a solução final, treinei também uma modelo Naive Bayes que, apesar de assumir que as variáveis são independentes, possui uma boa performance para classificação de texto. Ao fazer a média da solução dos dois algoritmos, consegui a precisão final de 97,9%.

Diferenças para a solução vencedora

Comparando a minha solução com a solução da dupla vencedora, há uma pequena diferença de 0,8% de precisão. Mas quando olhamos a complexidade, a história é outra. A solução vencedora utilizou mais de 30 modelos, entre transformações e classificação, para cada exemplo do training set. Na prática, assim como acontece normalmente nestas competições, não valeria a pena implementar a solução vencedora. Mas isso não tira o crédito dos vencedores, e também a oportunidade de aprendizado.

Seu time precisa de orientação em um projeto de machine learning? Eu abri alguns slots de consultoria. Se você se interessou, entre em contato através do formulário.
Do you or your team need advice/guidance on a machine learning project? I opened a few consulting slots. If you are interested, reach out via the contact form.