POS Tagging — da teoria à implementação

Entenda como funciona uma das tarefas mais importantes para o Processamento de Linguagem Natural

Rian Fernandes
Turing Talks
11 min readApr 17, 2022

--

Olá, leitor! Seja bem-vindo a mais um Turing Talks. Suponhamos que, ao ler este texto, você esteja ouvindo suas músicas favoritas e uma delas é “1999”, da Charli XCX com o Troye Sivan. Você ouve, então, os seguintes versos em determinado instante:

I just wanna go back, back to 1999
Take a ride to my old neighborhood

Aparentemente, você consegue, com clareza, entender a mensagem central que a cantora deseja passar: a de voltar à sua vizinhança antiga. Mas por que o último verso não te gera uma ambiguidade?

Vejamos que há uma possibilidade para isso: a palavra “ride”. Por que, ao realizar sua leitura, você não pensou que este termo fosse um verbo, mas sim um substantivo? Sua resposta provavelmente seria dizer que um verbo não pode estar antes de um artigo, no caso, “a”, o que está certíssimo. As classes gramaticais, ou, no inglês, Part of Speech, são fundamentais para entendermos o funcionamento de uma língua e evitarmos, por exemplo, as ambiguidades. Portanto, não diferentemente, para o Processamento de Linguagem Natural, este tema também é fundamental, por isso trabalharemos com ele hoje! Bora lá?

O que é POS Tagging e para quê serve?

Você já sabe que Part of Speech (sigla “POS”) diz respeito às classes gramaticais, aquelas mesmo que aprendemos na escola. Podemos dizer, então, que POS Tagging é a tarefa de associarmos cada palavra em uma sentença à sua classe gramatical, assim como vemos no exemplo a seguir:

Sentença anotada por um POS Tagger e um Parser (utilizados com Spacy)

As POS tags estão localizadas embaixo de cada palavra (acima das palavras, estão as dependências, assunto para um futuro Turing Talks sobre Parsing). Podemos, neste exemplo, perceber a existência de algumas tags: PRON (pronome), VERB (verbo) e PROPN (nome próprio), mas sabemos que existem várias outras (recomendo dar uma olhada no site das Universal Dependencies).

Agora que sabemos o conceito de POS Tagging, podemos nos perguntar: para quê isso serve? Bom, além dos mesmos motivos que podemos pensar na importância das classes gramaticais para nós, humanos, essa tarefa também é base para outras tarefas de NLP, como:

  • Eliminar ambiguidades;
  • Reconhecimento de Entidades Nomeadas (NER);
  • Criação de modelos de língua estatísticos baseados na frequência de diferentes tags;
  • Geração de textos;
  • Identificação de padrões em textos;
  • Análise de sentimentos;
  • E muitas outras! :)

Técnicas de POS Tagging

Pela importância da POS Tagging, podemos deduzir que é uma tarefa feita há um bom tempo. No entanto, no passado, a anotação das tags era feita manualmente por todo o corpus, o que, graças ao avanço das técnicas de NLP e, consequentemente, de IA, teve seu panorama alterado. Atualmente, a anotação é feita automaticamente por ferramentas que são capazes de anotar uma tag, dado um determinado contexto.

Vale ressaltar que ainda existe anotação manual de tags, mas especificamente para conjuntos de dados pequenos, ou para a criação de dados de treinamento para o desenvolvimento de novos POS Taggers automáticos.

As técnicas automáticas de POS Tagging são várias, como:

  • Baseadas em regras: esses modelos são constituídos por um conjunto de regras linguísticas, como “não pode existir um verbo após um artigo”. Seus problemas são baseados em dois pontos: a quantidade de regras é extremamente numerosa, além das regras serem dependentes da língua;
  • Deep Learning: caracterizados por utilizarem LSTM bidirecionais, conhecidas como BLSTM (um tipo de rede neural recorrente, RNN, que abarca informações contextuais em um grande espaço de input em ambas as direções de uma sentença), esses modelos atingem uma acurácia de, em média, 98%;
  • Probabilísticas: como o próprio nome diz, são modelos que trabalham com probabilidades. No entanto, não é interessante que utilizemos, por exemplo, apenas a probabilidade de determinado termo ser anotado por uma tag, haja vista que, no exemplo visto acima, “ride” provavelmente teria uma probabilidade maior de ocorrer como verbo, mesmo sendo um substantivo; isto é, as probabilidades isoladas podem nos acarretar sentenças agramaticais. Para trabalharmos com modelos probabilísticos, utilizaremos os Modelos Ocultos de Markov.

Neste Turing Talks, daremos ênfase aos modelos probabilísticos. Para isso, primeiramente, devemos, então, entender o que são os Modelos Ocultos de Markov e o porquê de eles serem a melhor solução para POS Tagging.

Modelos Ocultos de Markov

Para entendermos a ideia por trás dos Modelos Ocultos de Markov, vamos imaginar uma situação: você, como estudante, possui um dia a dia extremamente corrido e, por isso, consegue estar em dois estados, “com sono” ou “sem sono”. Temos que ter em mente que o estado é algo não observável, ou seja, neste caso, não conseguimos observar o seu interior para saber se você tem sono ou não, o que podemos observar é justamente se você dormiu muito ou dormiu pouco (o que é uma evidência para dizermos se você estava com sono ou não). Essa situação pode ser esquematizada da seguinte forma:

Esquema de um Modelo Oculto de Markov

A ideia é que, dado um determinado dia inicial (começo), você possui, por exemplo, 40% de chance de estar com sono e 60% de chance de estar sem sono. Além disso, dado um dia em que você tinha sono, você tem 70% de chance de ter sono no próximo dia também e, claramente, 30% de não ter sono. Como os números são muitos, podemos organizá-los nas duas tabelas a seguir:

Probabilidades de Transição e Emissão

Agora, com as probabilidades organizadas, talvez fique mais fácil trabalhar com alguns conceitos e informações. Primeiramente, é importante afirmar que as probabilidades de transição correspondem ao que chamamos de Estados Ocultos, pois não observamos sua ocorrência, isto é, não conseguimos observar se uma pessoa tem sono ou não. Por outro lado, as probabilidades de emissão correspondem aos Estados Observáveis, pois são justamente o que conseguimos observar: neste caso, se a pessoa dormiu muito ou pouco; podemos dizer que a emissão é uma evidência para a inferência do estado, ou seja, se uma pessoa dormiu muito, provavelmente ela estava com sono (ou, com outro exemplo, se vemos alguém com olheiras, provavelmente a pessoa está com sono).

A ideia por trás dos Modelos Ocultos de Markov é que existem alguns estados ocultos sobre os quais não sabemos, mas que influenciam diretamente os estados observáveis. Outro conceito importante é que a probabilidade de um determinado estado ocorrer depende unicamente do estado anterior, ou seja, não leva em conta o resto do histórico, o que é conhecimento como a suposição de Markov. No nosso exemplo, podemos compreender que o fato de uma pessoa ter sono ou não em um dia depende exclusivamente se ela teve sono ou não no dia anterior; junto a isso, o fato de uma pessoa dormir muito ou pouco depende apenas se ela tinha muito ou pouco sono em seu estado anterior. Matematicamente, essa suposição é representada assim:

Suposição de Markov
Suposição de Markov

Entendido isso, você deve estar se perguntando: mas o que isso tem a ver com POS Tagging? Calma lá, que é justamente isso que vamos ver agora. A ideia por trás dos Modelos Ocultos de Markov é extremamente importante para entendermos a funcionalidade do algoritmo Viterbi, responsável justamente pela nossa tarefa de NLP.

Algoritmo Viterbi

Para entendermos a essência do algoritmo, vamos utilizar um trecho de outra música da Charli XCX, “Vroom Vroom”(já anotada por um POS Tagger):

Sentença anotada por um POS Tagger

Podemos entender que, em uma sentença, as palavras são os estados observáveis que possuímos no nosso modelo. Também percebemos que as POS tags são os estados ocultos, já que não observamos sua ocorrências, mas são elas que condicionam a existência de seu estado subsequente.

Para que possamos trabalhar com nosso POS Tagger, é válido ressaltar que necessitamos de, além das palavras já tokenizadas, de um tagset, que nada mais é do que um conjunto de dados com sentenças já anotadas com as POS tags. É esse tagset que gera para nós as probabilidades de transição e emissão necessárias para os Modelos Ocultos de Markov e, consequentemente, para o nosso algoritmo.

Neste exemplo do trecho, poderíamos definir, hipoteticamente, as probabilidades do nosso tagset da seguinte forma:

Probabilidades de Transição

Cada linha nesta matriz soma, ao total, 1, pois, dado um estado, todas as possibilidades de tags somadas resultam em 100%. Como o nosso exemplo é hipotético, não foram colocadas todas as tags, mas, para que se some 1, é necessária a existência de todas elas na matriz.

Probabilidades de Emissão

Nesta matriz, a soma de cada linha já não é equivalente a 1. É fato que as probabilidades somadas de, dado uma determinada tag, tal ocorrência ser uma palavra x, uma palavra y, ou uma palavra z (e assim por diante…), devem ser 1, mas como o nosso tagset apresenta milhares de palavras, é um pouco complicado que coloquemos todas em exposição (isto é, uma matriz com milhares de colunas); para além disso, as palavras que não ocorrem no nosso trecho não nos importam muito.

Para fazer a interpretação adequada das matrizes e, consequentemente, das probabilidades, devemos ler da seguinte forma:

  • Dado que é o começo da sentença, a probabilidade da palavra ser um PRON é 30% (probabilidade de transição, já que foi uma alteração de estados ocultos);
  • Dado que a palavra é um PRON, a probabilidade dela ser “I” é 30% (probabilidade de emissão, já que foi uma alteração de um estado oculto para um estado observável).

E assim poderíamos fazer com todas as possibilidades para todas as palavras. No entanto, surge um problema: existem várias tags e, além disso, só neste nosso pequeno trecho, possuímos 8 palavras. A quantidade de probabilidades que deveríamos calcular para maximizarmos a nossa probabilidade final seria muito grande, principalmente em um corpus maior.

Visando minimizar isso, uma possível solução é que poderíamos levar em conta apenas as probabilidade de emissão, analisando a ocorrência de cada palavra isoladamente. No entanto, ao fazermos isso, ignoramos o fato de que as ocorrências ou não de determinadas tags são condicionadas por outras tags: neste caso, “ ‘ve” seria classificado como VERB e não como AUX, o que seria incorreto.

É nesse momento que entra o nosso maravilhoso algoritmo Viterbi:

Funcionamento do Viterbi

O Viterbi funciona da seguinte maneira: devemos multiplicar a probabilidade de transição pela probabilidade de emissão, ou seja, os primeiros passos são os seguintes:

  • Dado que é o começo da sentença, a probabilidade de ser um PRON é 0.3 e, dado que é um PRON, a probabilidade de ser “I” é 0.3, portanto, a probabilidade de termos um “IPRON no começo de sentença é 0.09 (devemos fazer isso para todas as tags, mas, para que o exemplo não fique enorme, aqui será demonstrado apenas o teste de uma ou duas tags por palavra);
  • Dado que a palavra de antes é um PRON, a probabilidade da próxima ser um AUX é 0.4, e a do AUX ser “ ‘ve” é 0.2, portanto a probabilidade de termos um “ ‘veAUX após um PRON é 0.08. Seguindo os mesmos passos, a probabilidade de termos um “ ‘veVERB após um PRON é 0.18. No entanto, ainda não podemos tirar conclusões, além de que a palavra anterior é um PRON;
  • Realizando as multiplicações, vemos que a probabilidade de termos um “beenAUX depois de um AUX é 0.02, a de termos um “beenAUX depois de um VERB é 0, a de termos um “beenVERB depois de um AUX é 0 e, por fim, a de termos um “beenVERB depois de um VERB também é 0. Com essa análise, podemos concluir não a tag de “been”, mas a de sua palavra anterior “ ‘ve”: com isso, já sabemos que “ ‘ve” é um AUX, pois ele resulta nas maiores probabilidades para “been”, independentemente da tag deste último. Outra forma de entendermos isso é classificando cada palavra como um nó. Independentemente do caminho que façamos para frente, as probabilidades posteriores ao nosso nó sempre serão maiores se apresentarmos o maior valor da probabilidade daquele nó: por isso, podemos desconsiderar os valores que menos nos importam no que diz respeito à tag da palavra anterior;
  • O processo se repete até a última palavra, quando, então, veremos nossa probabilidade maximizada da sentença como um todo! :)

Dito isso, podemos entender o porquê de o algoritmo ser bem melhor do que a temida “brute force”: ele só leva em conta a probabilidade da palavra anterior que maximiza a probabilidade da palavra atual. Enquanto, para olharmos todas as ocorrências (brute force), teríamos que andar , sendo “P” a quantidade de POS tags e “l” a quantidade de palavras na nossa sentença, com o Viterbi, devemos olhar apenas L.P².

Entendido o funcionamento do algoritmo, podemos, agora, ver sua implementação :)

Implementação em Python

Para a implementação do POS Tagging em Python, podemos utilizar bibliotecas como NLTK e Spacy, mas, hoje, criaremos nosso próprio POS Tagger!

Primeiramente, vamos importar as bibliotecas e fazer os downloads necessários. Nosso tagset a ser usado é proveniente da biblioteca NLTK:

Podemos ver que nosso tagset é uma lista de listas, sendo cada lista interior uma sentença; além disso, dentro de cada lista, há tuplas contendo cada token e sua tag:

Podemos, então, dividir nossos dados em treino e teste. Após isso, dividiremos nossas tuplas em treino e teste também:

Também é interessante que vejamos a quantidade de tags presentes no nosso tagset e seus nomes:

Agora, definiremos uma função para calcular a a Probabilidade de Emissão e outra para a Probabilidade de Transição:

Podemos, então, calcular uma matriz contendo as nossas probabilidades de transição:

Para ela ficar mais compreensível, podemos transformá-la em um DataFrame:

Agora, definiremos, então, nossa função para rodar o Viterbi. Após isso, definiremos algumas sentenças aleatórias para testarmos nosso modelo :)

Por fim, testaremos nosso modelo:

Vimos, então, que conseguimos uma acurácia de quase 94%! Quem diria que nosso modelo se comportaria tão bem :)

E por hoje era isso! Se você se interessa por Processamento de Linguagem Natural ou qualquer outra vertente do ramo da Inteligência Artificial, não deixe de ler outros Turing Talks e nos acompanhar em nossas redes sociais, como Facebook, Linkedin, Instagram e, claro, nossos posts do Medium! Entre também em nosso servidor no Discord.

Muito obrigado por chegar até aqui! Bons estudos e até! :)

Charli e Troye em 1999 :)

Referências

POS (Part-of-Speech) tagging with Hidden Markov Model | Great Learning (mygreatlearning.com)

Hidden Markov Model : Data Science Concepts — YouTube

Part of Speech Tagging : Natural Language Processing — YouTube

The Viterbi Algorithm : Natural Language Processing — YouTube

--

--