Pesquisa de site

Pratique a análise de texto em PNL com Python


Conheça os conceitos fundamentais por trás do processamento de linguagem natural.

O processamento de linguagem natural (PNL) é um campo especializado para análise e geração de linguagens humanas. As línguas humanas, corretamente chamadas de linguagem natural, são altamente sensíveis ao contexto e muitas vezes ambíguas, a fim de produzir um significado distinto. (Lembre-se da piada em que a esposa pede ao marido para "pegar uma caixa de leite e se eles tiverem ovos, pegue seis", então ele ganha seis caixas de leite porque eles tinham ovos.) A PNL fornece a capacidade de compreender a entrada da linguagem natural e produzir saída de linguagem natural de forma adequada.

A linguística computacional (CL) é o campo mais amplo de compreensão e modelagem linguística. PNL é um subconjunto de CL que trata dos aspectos de engenharia de compreensão e geração de linguagem. A PNL é um domínio interdisciplinar que abrange vários campos, incluindo inteligência artificial (IA), aprendizado de máquina (ML), aprendizado profundo (DL), matemática e estatística.

Alguns dos aplicativos que você pode construir com PNL incluem:

  • Tradução automática: com mais de 6.000 idiomas no mundo, a PNL aliada à tradução automática neural pode facilitar a tradução de textos de um idioma para outro.
  • Chatbots: assistentes pessoais como Alexa, Siri e o código aberto Mycroft estão integrados em nossas vidas hoje. A PNL está no centro desses chatbots, ajudando as máquinas a analisar, aprender e compreender a fala, bem como fornecer resposta vocal.
  • Ativação de voz: a PNL torna possível atender clientes de saúde, viagens, varejo e outros setores de maneira amigável.
  • Análise de sentimento: as empresas sempre querem estar atentas aos clientes e tomar medidas proativas quando eles sentem descontentamento. A PNL torna isso possível.
  • Produtividade de RH: os profissionais de recursos humanos devem lidar com uma montanha de documentos, e a PNL pode usar a automação de processos documentais para aliviar parte dessa carga.

Blocos de construção da PNL

Assim como um arranha-céu é construído tijolo por tijolo, você pode construir grandes aplicativos como os acima usando os blocos de construção fundamentais e essenciais da PNL.

Existem várias bibliotecas de PNL de código aberto disponíveis, como Stanford CoreNLP, spaCy e Genism em Python, Apache OpenNLP e GateNLP em Java e outras linguagens.

Para demonstrar as funções dos blocos de construção da PNL, usarei Python e sua biblioteca principal de PNL, Natural Language Toolkit (NLTK). O NLTK foi criado na Universidade da Pensilvânia. É um ponto de partida amplamente utilizado e conveniente para entrar na PNL. Depois de aprender seus conceitos, você pode explorar outras bibliotecas para construir seus aplicativos de PNL "arranha-céu".

Os blocos de construção fundamentais abordados neste artigo são:

  • Tokenizar em frases e palavras
  • Palavras irrelevantes
  • Colocações
  • Identificação de partes da fala
  • Lematização e lematização
  • Corpus

Configurar

Este artigo pressupõe que você esteja familiarizado com Python. Depois de instalar o Python, baixe e instale o NLTK:

pip install nltk

Em seguida, instale os dados NLTK:

python -m nltk.downloader popular

Se você tiver muito espaço de armazenamento e boa largura de banda, também poderá usar python -m nltk.downloader all. Consulte a página de instalação do NLTK para obter ajuda.

Há também uma interface de usuário para selecionar dados para download, que você pode iniciar com o shell Python:

Python 3.8.2 ...
Type "help", ...

>>> import nltk
>>> nltk.download()

(Opensource.com, CC BY-SA 4.0)

Tokenizar frases e palavras

A primeira etapa na análise e processamento de texto é dividir o texto em frases e palavras, um processo chamado tokenização. A tokenização de um texto torna mais fácil a análise. Quase todos os aplicativos de análise de texto começam com esta etapa.

Aqui estão alguns exemplos com esta linha de texto:

text = "Computers don't speak English. So, we've to learn C, C++, ,C#, Java, Python and the like! Yay!"

Tokenização de frase:

from nltk.tokenize import sent_tokenize
sentences = sent_tokenize(text)
print(len(sentences), 'sentences:', sentences)

Tokenização de palavras:

from nltk.tokenize import word_tokenize
words = word_tokenize(text)
print(len(words), 'words:', words)
29 word(s): ['Computers', 'do', "n't", 'speak', 'English', '.', 'So', ',', 'we', "'ve", 'to', 'learn', 'C', ',', 'C++', ',', ',', 'C', '#', ',', 'Java', ',', 'Python', 'and', 'the', 'like', '!', 'Yay', '!']

O NLTK usa expressões regulares internamente para tokenização. Um leitor atento pode perguntar se você pode tokenizar sem usar o NLTK. Sim você pode. No entanto, o NLTK é bem projetado considerando todas as variações existentes; por exemplo, algo como nltk.org deve permanecer uma palavra ['nltk.org'] e não ['nltk', 'org']:

text = "I love nltk.org"

Se você tokenizar usando o código acima, nltk.org será mantido como uma palavra:

1 sentence(s): ['I love nltk.org']
3 word(s): ['I', 'love', 'nltk.org']

O NLTK não oferece a capacidade de substituir contrações como "não" por "não" e "nós" por "nós temos", mas a biblioteca pycontractions pode ajudar.

Tente você mesmo

Usando bibliotecas Python, baixe a página da Wikipedia em código aberto e tokenize o texto.

Palavras irrelevantes

Uma língua como o inglês tem muitas palavras "fluffas" (tecnicamente chamadas de "palavras irrelevantes") que são necessárias na fala e na escrita, mas não têm valor na análise. O NLTK pode identificar e remover essas palavras irrelevantes para ajudar o processamento de texto a se concentrar nas palavras necessárias.

Veja as palavras consideradas stopwords:

from nltk.corpus import stopwords
stop_words = stopwords.words('english')
print(len(stop_words), "stopwords:", stop_words)
179 stopwords: ['i', 'me', 'my', 'myself', 'we', ..., "wouldn't"]

Primeiro tokenize o texto e depois filtre as palavras irrelevantes:

text = "Computers don't speak English. So, we've to learn C, C++, Java, Python and the like! Yay!"

from nltk.tokenize import word_tokenize
words = word_tokenize(text)

print(len(words), "in original text:", words)
25 words in original text: ['Computers', 'do', 'not', 'speak', 'English', '.', 'So', ',', 'we', 'have', 'to', 'learn', 'C', ',', 'C++', ',', 'Java', ',', 'Python', 'and', 'the', 'like', '!', 'Yay', '!']
words = [word for word in words if word not in stop_words]
print(len(words), "without stopwords:", words)
18 words without stopwords: ['Computers', 'speak', 'English', '.', 'So', ',', 'learn', 'C', ',', 'C++', ',', 'Java', ',', 'Python', 'like', '!', 'Yay', '!']

O texto ainda traz sinais de pontuação, que aumentam o ruído. Para removê-los, use a classe string do Python. Alguma pontuação é importante, por exemplo, o ponto de interrogação. Este método pode ser usado para remover pontuação (sem usar NLTK).

Veja os caracteres considerados pontuação:

import string
punctuations = list(string.punctuation)
print(punctuations)
['!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']

Remover pontuação:

words = [word for word in words if word not in punctuations]
print(len(words), "words without stopwords and punctuations:", words)
11 words without stopwords and punctuations: ['Computers', 'speak', 'English', 'So', 'learn', 'C', 'C++', 'Java', 'Python', 'like', 'Yay']

Tente você mesmo

Usando as bibliotecas Python, baixe a página da Wikipedia em código aberto e remova as palavras irrelevantes. Qual porcentagem da página contém palavras irrelevantes?

Colocações

Colocação refere-se a duas (ou mais) palavras que tendem a aparecer frequentemente juntas. As colocações ajudam na compreensão da formação do texto e auxiliam na pesquisa de texto e comparação de similaridades.

Use um arquivo de texto mais longo do Project Gutenburg para este exemplo. (O Projeto Gutenberg é uma iniciativa para digitalizar livros.)

Baixe o texto:

# coding: utf-8

import urllib.request

# Download text and decode
# Note: Set proxy if behind a proxy (https://docs.python.org/2/library/urllib.html)
url = "http://www.gutenberg.org/files/1342/1342-0.txt"
text = urllib.request.urlopen(url).read().decode()
print(text)
The Project Gutenberg EBook of Pride and Prejudice, by Jane Austen
This eBook is for the use of anyone anywhere at no cost and with
...
      Chapter 1
      It is a truth universally acknowledged, that a single man in
      possession of a good fortune
...
      bringing her into Derbyshire, had been the means of
      uniting them.

Pré-processamento (tokenização, eliminação de palavras-chave e eliminação de pontuação):

# Tokenize
from nltk.tokenize import word_tokenize
text = word_tokenize(text)

# Remove stopwords
from nltk.corpus import stopwords
stops = stopwords.words('english')
# print(stops)
words = [word for word in text if word not in stops]

# Remove punctuations
import string
punctuations = list(string.punctuation)
# print(punctuations)

words = [word for word in words if word not in punctuations]
print("Without punctuations:", words)
Preprocessed: ['The', 'Project', 'Gutenberg', 'EBook', 'Pride', 'Prejudice', 'Jane', 'Austen', ...

Bigramas (duas palavras que aparecem juntas):

# Bigrams
from nltk.metrics import BigramAssocMeasures
from nltk.collocations import BigramCollocationFinder
bigram_collocation = BigramCollocationFinder.from_words(words)
# Top 10 most occurring collocations
print("Bigrams:", bigram_collocation.nbest(BigramAssocMeasures.likelihood_ratio, 10))
Bigrams: [('”', '“'), ('Mr.', 'Darcy'), ('Lady', 'Catherine'), ('”', 'said'), ('Mrs.', 'Bennet'), ('Mr.', 'Collins'), ('Project', 'Gutenberg-tm'), ('“', 'I'), ('Sir', 'William'), ('Miss', 'Bingley')]

Um leitor atento pode observar que os caracteres de aspas duplas — ” (ponto de código 8220) e “ (ponto de código 8221) — ainda ocorrem no texto após a retirada de pontuação. string.punctuation não os detecta, pois são diferentes das aspas duplas padrão “ (ponto de código 34). Para processá-los, adicione os caracteres à lista de pontuação.

Trigramas (três palavras que aparecem juntas):

# Trigrams
from nltk.collocations import TrigramCollocationFinder
from nltk.metrics import TrigramAssocMeasures
trigram_collocation = TrigramCollocationFinder.from_words(text)
# Top 10 most occurring collocations
print("Trigrams:", trigram_collocation.nbest(TrigramAssocMeasures.likelihood_ratio, 10))
Trigrams: [('late', 'Mr.', 'Darcy'), ('Mr.', 'Darcy', 'returned'), ('saw', 'Mr.', 'Darcy'), ('friend', 'Mr.', 'Darcy'), ('Mr.', 'Darcy', 'walked'), ('civility', 'Mr.', 'Darcy'), ('Mr.', 'Darcy', 'looked'), ('said', 'Mr.', 'Darcy'), ('surprised', 'Mr.', 'Darcy'), ('Mr.', 'Darcy', 'smiled')]

"Sr. Darcy" está em quase toda parte! Você pode sugerir que ele é o protagonista do romance. Este é um exemplo de extração de informações usando PNL.

Tente você mesmo

Usando as bibliotecas Python, baixe a página da Wikipedia em código aberto. Você pode levantar a hipótese de que "código aberto" é o bigrama que mais ocorre e "código-fonte aberto" é o trigrama que mais ocorre. Veja se você pode confirmar isso.

Identificação de partes da fala

NLTK tem a capacidade de identificar partes do discurso (POS) das palavras. Identificar o PDV é necessário, pois uma palavra tem significados diferentes em contextos diferentes. A palavra "código" como substantivo pode significar "um sistema de palavras para fins de sigilo" ou "instruções de programa" e, como verbo, pode significar "converter uma mensagem em forma secreta" ou "escrever instruções para um computador". Este conhecimento do contexto é necessário para a correta compreensão do texto.

Aqui está um exemplo usando este texto:

text = "Computers don't speak English. So, we've to learn C, C++, Java, Python and the like! Yay!"

Pré-processe o texto como você fez anteriormente:

import nltk
from nltk.tokenize import word_tokenize

words = word_tokenize(text)

Identifique as tags POS:

pos_tagged_text = nltk.pos_tag(words)
print(pos_tagged_text)
[('Computers', 'NNS'), ('do', 'VBP'), ("n't", 'RB'), ('speak', 'VB'), ('English', 'NNP'), ('.', '.'), ('So', 'RB'), (',', ','), ('we', 'PRP'), ("'ve", 'VBP'), ('to', 'TO'), ('learn', 'VB'), ('C', 'NNP'), (',', ','), ('C++', 'NNP'), (',', ','), ('Java', 'NNP'), (',', ','), ('Python', 'NNP'), ('and', 'CC'), ('the', 'DT'), ('like', 'JJ'), ('!', '.'), ('Yay', 'NN'), ('!', '.')]

NNS, VBP, etc. são códigos POS definidos pela Universidade da Pensilvânia e você também pode vê-los programaticamente:

nltk.help.upenn_tagset()
NNS: noun, common, plural
    undergraduates scotches bric-a-brac products bodyguards facets coasts
    divestitures storehouses designs clubs fragrances averages
    subjectivists apprehensions muses factory-jobs ...
VBP: verb, present tense, not 3rd person singular
    predominate wrap resort sue twist spill cure lengthen brush terminate
    appear tend stray glisten obtain comprise detest tease attract
    emphasize mold postpone sever return wag ...
...

Você pode ver a definição POS de cada palavra na frase:

for pos_tag_word in pos_tagged_text:
    print(pos_tag_word[0], ":")
    nltk.help.upenn_tagset(pos_tag_word[1])
Computers :
NNS: noun, common, plural
	...
do :
VBP: verb, present tense, not 3rd person singular
	...
n't :
RB: adverb
	...
speak :
VB: verb, base form
	...
English :
NNP: noun, proper, singular
	...
. :
.: sentence terminator

Tente você mesmo

Usando as bibliotecas Python, baixe a página da Wikipedia em código aberto e identifique o POS de todas as palavras do texto.

Lematização e lematização

As palavras são normalmente flexionadas (por exemplo, letras com sufixo, afixadas, etc.) para expressar suas formas (por exemplo, plural, tempo verbal, etc.). Dog -> Dogs é um exemplo de inflexão. Normalmente, as palavras devem ser comparadas em suas formas nativas para uma correspondência de texto eficaz.

Lematização e lematização são dois métodos para converter uma palavra em uma forma não flexionada. A essência da lematização e da lematização é a mesma: reduzir uma palavra à sua forma mais nativa. Mas eles diferem na forma como fazem isso.

  • Stemming usa um mecanismo simples que remove ou modifica inflexões para formar a palavra raiz, mas a palavra raiz pode não ser uma palavra válida no idioma.
  • A lematização também remove ou modifica as inflexões para formar a palavra raiz, mas a palavra raiz é uma palavra válida no idioma.

A lematização usa um conjunto de dados de palavras (chamado de corpus, discutido na próxima seção) para chegar às palavras-raiz; portanto, é mais lento do que a derivação. Há casos em que a lematização é suficiente e, em outros casos, a lematização é necessária.

NLTK possui vários lematizadores e lematizadores (por exemplo, RegexpStemmer, LancasterStemmer, PorterStemmer, WordNetLemmatizer, RSLPStemmer e mais). Existem também muitos lematizadores e lematizadores integrados que você pode escolher (consulte o pacote nltk.stem).

Para compará-los, experimente PorterStemmer e WordNetLemmatizer.

Crie uma instância do PorterStemmer:

import nltk
stemmer = nltk.stem.PorterStemmer()

A raiz da palavra "construção":

word = "building"
print("Stem of", word, stemmer.stem(word))
Stem of building : build

Stemming não tem conhecimento de POS, então a palavra "construir", na forma substantiva ou verbal, tem origem em "construir".

Este não é o caso da lematização usando WordNetLemmatizer:

lemmatizer = nltk.stem.WordNetLemmatizer()
word = "building"
pos = 'n';
print("Lemmatization of", word, "(" , pos, "):", lemmatizer.lemmatize(word, pos))
pos = 'v';
print("Lemmatization of", word, "(" , pos, "):", lemmatizer.lemmatize(word, pos))
Lemmatization of building ( n ): building
Lemmatization of building ( v ): build

A lematização leva mais tempo (um pouco neste exemplo, mas perceptível) do que a lematização.

Tente você mesmo

Usando as bibliotecas Python, baixe a página da Wikipedia em código aberto e pré-processe e converta o texto em suas formas nativas. Experimente com vários módulos de lematização e lematização. Use o módulo timer do Python para medir seu desempenho.

Corpus

Um corpus em NLTK é um conjunto de dados de texto. O NLTK disponibiliza vários corpora. Corpora auxilia no processamento de texto com dados prontos para uso. Por exemplo, um conjunto de discursos de posse dos presidentes dos EUA pode ajudar na análise e preparação de discursos.

Vários leitores de corpus estão disponíveis no NLTK. Dependendo do texto que você está processando, você pode escolher o mais adequado. O corpus necessário deve ser instalado com Dados (veja a seção Configuração acima).

Existem vários tipos de corpus que indicam a estrutura e o tipo de dados que o corpus fornece. A lista de corpora disponíveis pode ser encontrada na UI nltk_data (veja Configuração).

(Opensource.com, CC BY-SA 4.0)

Um corpus é acessado por meio de um leitor. O leitor a ser utilizado para um corpus depende do tipo do corpus. Por exemplo, o corpus de Gutenberg contém texto em formato de texto simples e é acessado com PlaintextCorpusReader. O corpus Brown categorizou, marcou texto e é acessado com CategorizedTaggedCorpusReader. Os leitores seguem uma estrutura em árvore. Aqui estão alguns corpora e seus leitores.

(Opensource.com, CC BY-SA 4.0)

Veja como acessar corpora.

Primeiro, crie uma função utilitária para mostrar informações do corpus com base no tipo de leitor do corpus:

def corpus_info(corpus):
    print(corpus)
    print()
    print("README:", corpus.readme())
    print()
    files = corpus.fileids()
    print(len(files), "files:")
    print(files)
    print()
    file = files[0]
    text = corpus.raw(file)
    print("File", file, len(corpus.paras(file)), "paras", len(corpus.sents(file)), "sentences", len(corpus.words(file)), "words", ":")
    print(text.encode("utf-8"))
    print()
    if isinstance(corpus, nltk.corpus.TaggedCorpusReader):
        tagged_words = corpus.tagged_words()
        print(len(tagged_words), "tags:")
        print(tagged_words)
        print()
    if isinstance(corpus, nltk.corpus.CategorizedTaggedCorpusReader):
        categories = corpus.categories()
        print(len(categories), "categories:")
        print(categories)
        print()
        category = categories[-1]
        files = corpus.fileids(category)
        print(len(files), "files in category", category, ":")
        print(files)
        print()
        file = files[0]
        print("File:", file, len(corpus.paras(file)), "paras", len(corpus.sents(file)), "sentences", len(corpus.words(file)), "words")
        print()
        print("Raw text:")
        text = corpus.raw(file)
        print(text)
        print()
        print("Tagged text:")
        tagged_words = corpus.tagged_words(file)
        print(tagged_words)
        print()

Aqui estão dois exemplos de corpora:

  • ABC é uma coleção de notícias da Australian Broadcasting Commission. Este é um corpus básico de texto simples:

    corpus_info(nltk.corpus.abc)
    <PlaintextCorpusReader in '.../corpora/abc' (not loaded yet)>
        
    README: b'Australian Broadcasting Commission 2006\nhttp://www.abc.net.au/\n\nContents:\n* Rural News    http://www.abc.net.au/rural/news/\n* Science News  http://www.abc.net.au/science/news/\n\n'
        
    2 files:
    ['rural.txt', 'science.txt']
        
    File: rural.txt 2425 paras 13015 sentences 345580 words :
    'PM denies knowledge of AWB kickbacks\nThe Prime Minister has denied ...
  • O corpus Brown tem cerca de um milhão de palavras do inglês americano contemporâneo reunidas pela Brown University:

    corpus_info(nltk.corpus.brown)
    <CategorizedTaggedCorpusReader in '.../corpora/brown' (not loaded yet)>
        
    README: BROWN CORPUS
    A Standard Corpus of Present-Day Edited American
    ...
        
    500 files:
    ['ca01', 'ca02', 'ca03', ...]
        
    File ca01 67 paras 98 sentences 2242 words :
    b"\n\n\tThe/at Fulton/np-tl County/nn-tl Grand/jj-tl Jury/nn-tl ...
        
    1161192 tags:
    [('The', 'AT'), ('Fulton', 'NP-TL'), ...]
        
    15 categories:
    ['adventure', 'belles_lettres', 'editorial', 'fiction', 'government', 'hobbies', 'humor', 'learned', 'lore', 'mystery', 'news', 'religion', 'reviews', 'romance', 'science_fiction']
        
    6 files in category science_fiction :
    ['cm01', 'cm02', 'cm03', 'cm04', 'cm05', 'cm06']
        
    File: cm01 57 paras 174 sentences 2486 words
        
    Raw text:
    Now/rb that/cs he/pps ...
        
    Tagged text: 
    [('Now', 'RB'), ('that', 'CS'), ('he', 'PPS'), ...]

Imagine o que você poderia fazer com esses corpora à sua disposição! Com o corpus Brown, você pode treinar um modelo para categorizar e marcar textos para um chatbot entender melhor a intenção humana, por exemplo. Você também pode criar seu próprio corpus.

Próximos passos

No meu próximo artigo, ampliarei seu conhecimento sobre os elementos básicos da PNL, mostrando como comparar dados analisados, implementar o reconhecimento de entidades e muito mais com PNL e NLTK.

Por enquanto, tente treinar um modelo de aprendizado de máquina usando o corpus Brown para categorizar texto e marcar palavras. Aplique isso a uma parte de um livro de PG Woodhouse e veja qual categoria ela identifica.