patrocínio:

Classificando o conteúdo do Stack Exchange (parte 03/03)

A série de textos que mostram como resolver o desafio de classificação textual do HackerRank finaliza por aqui.

por Lucas Lo Ami Alvino Silva 03/09/2018 Comentários ~ 5 min. / 929 palavras

Urfa, chegamos no último texto dessa série. Já fizemos a exploração dos dados, abordamos possíveis features para classificação de texto e mostramos como implementar tudo isso na prática e combinar com classificadores de machine learning.

Nesse texto vou explorar algumas estratégias alternativas que podem ser usadas para melhorar os resultados do nosso modelo. Nem sempre elas vão gerar o efeito desejado, mas acho válido tentá-las.

Vou abordar dois tópicos principais nesse texto:

  • Uso de bigramas
  • Stemização

Bigramas ao invés de unigramas como features

Lá no primeiro texto dessa série, eu falei sobre algumas variações de n-gramas existentes, tais como os unigramas (usados na resolução do desafio até agora), bigramas, trigramas e por aí vai. Ao utilizar n-gramas com n > 1 a intenção é conseguir capturar algumas relações entre palavras no conjunto de dados e que possam ajudar a identificar uma frase específica. Por exemplo, considere as frases abaixo extraídas do dataset de treino:

  • Do computers speed up at higher temperatures?
  • Looking for 80’s series or movie about programmable short term memory

Perceba que na primeira frase bigramas como “_computers speed_”, “_speed up_”, e “_higher temperatures_” podem ajudar a identificar a frase como pertencente ao tópico “_electronics_”. Já na segunda frase bigramas como “_80’s series_”, “_movie about_” e também o trigrama “_short term memory_” podem ajudar a identificar a frase como pertencente ao tópico “_scifi_”. Para resolver o desafio de classificação textual do Stack Exchange, optei por usar bigramas, pois eles apresentam resultados melhores do que trigramas e n-gramas com n > 3, como podem ver neste artigo e neste aqui também (se estiverem com muita vontade de ler, também vale a leitura deste pequeno artigo, de 2012).

Para implementar os bigramas no dataset de treino, usaremos a mesma estrutura de texto mostrada no segundo texto dessa série, no qual reuni as colunas “_question_” e “_excerpt_” em uma só. Combinei os bigramas gerados com gerados com as três métricas das quais falamos: contagem de palavras, frequência de termos e TF-IDF, apliquei neles o Multinomial Naive Bayes (MNB) e o _Support Vector Machines_ (SVM) com kernel linear. O código abaixo mostra como tudo isso foi implementado.

Os classificadores criados foram avaliados com a métrica de acurácia. O código que a calcula é bem simples e pode ser visto nas últimas linhas do código acima. Já a tabela a seguir mostra um resumo dos resultados obtidos, nos quais o SVM com TF-IDF teve o melhor resultado, com 82% de acurácia. De maneira geral o uso de bigramas como features piorou o desempenho do dois algoritmos de classificação.

Agora que sei qual o melhor modelo criado, vou aplicá-lo na base de testes e vou checar a acurácia final. O código abaixo mostra como fazer isso.

A acurácia final que conseguimos foi de: 0,83 (83%).

Stemização

Quando projetos de NLP são desenvolvidos , lidamos com palavras que estão conjugadas em diferentes tempos verbais, graus (ex: advérbios no superlativo), gênero e número, mas que essencialmente continuam representando o mesmo conceito. As abordagens que usei até agora para resolver o desafio do HackerRank não levam em consideração esse fator, o que pode acarretar na existência de _features_ redundantes, ou seja, que representam o mesmo conceito.

Para lidar com essa situação, precisamos normalizar as palavras presentes no dataset de treino. Em NLP, as duas principais formas de normalizar palavras são:

  1. Stemização (stemming): é o processo de redução das palavras a sua base (stem). Em alguns lugares vocề encontrará que é o processo de remoção das flexões/derivações das palavras, mantendo apenas o seu radical (morfema básico que guarda o significado da palavra)
  2. Lematização (lemmatization): é o processo que deflexiona uma palavra a fim de obter o seu lema (forma canônica), que é a representação singular masculino para substantivos e adjetivos e infinitivo para verbos.

Neste desafio, vamos usar a técnica de stemização. Para isso, vamos usar uma biblioteca do Python focada em NLP, a NLTK e o algoritmo de stemização de Porter, o mais famoso entre os existentes. Não vou entrar em detalhes do funcionamento do mesmo, mas se você tem muita curiosidade pra saber mais a respeito dele, recomendo dar uma olhadinha nesses slides do professor Jesús Mena, da UFABC. O código abaixo mostra como usei a NLTK para stemizar os dados de treino.

A tabela a seguir mostra um resumo dos resultados obtidos, nos quais o SVM com TF-IDF teve o melhor resultado, com 92% de acurácia.

Agora que sei qual o melhor modelo criado, vou aplicá-lo na base de testes e vou checar a acurácia final. O código abaixo mostra como fazer isso.

A acurácia final que conseguimos foi de: 0,92 (92%).

Conclusões

Nesse texto abordei duas técnicas alternativas para melhorar a acurácia dos modelos criados, o uso de bigramas como _features_ e também a stemização para normalizar os dados. De maneira geral, nenhum dos novos modelos superou os resultados apresentados no segundo texto dessa série.

Também tive algumas outras ideias que poderiam ser implementadas e que você também pode testar por aí. Elas são:

  • Usar outros algoritmos de classificação: em especial, eu testaria algum ensemble, como _Random Forest_ com todos os conjuntos de _features_descritos nesta série de textos. Outra opção interessante é usar redes neurais com word embeddings.
  • Combinar os diferentes tipos de _features_ geradas: talvez colocar no conjunto de dados de treino unigramas e bigramas possa ajudar.

A série de textos que mostram como resolver o desafio de classificação textual do HackerRank finaliza por aqui. O código completo que criei para resolver esse desafio está no meu Github (se forem reutilizar meu código, lembrem de dar uma olhadinha na licença do projeto).