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:
- 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)
- 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).