Pesquisa de site

Um guia avançado para análise de PNL com Python e NLTK


Aprofunde-se nos conceitos fundamentais por trás do processamento de linguagem natural.

Em meu artigo anterior, apresentei o processamento de linguagem natural (PNL) e o Natural Language Toolkit (NLTK), o kit de ferramentas de PNL criado na Universidade da Pensilvânia. Demonstrei como analisar texto e definir palavras irrelevantes em Python e apresentei o conceito de corpus, um conjunto de dados de texto que auxilia no processamento de texto com dados prontos para uso. Neste artigo, continuarei utilizando conjuntos de dados para comparar e analisar a linguagem natural.

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

  • WordNet e synsets
  • Comparação de similaridade
  • Árvore e banco de árvores
  • Reconhecimento de entidade nomeada

WordNet e synsets

WordNet é um grande corpus de banco de dados lexical em NLTK. WordNet mantém sinônimos cognitivos (comumente chamados de synsets) de palavras correlacionadas por substantivos, verbos, adjetivos, advérbios, sinônimos, antônimos e muito mais.

WordNet é uma ferramenta muito útil para análise de texto. Está disponível em vários idiomas (chinês, inglês, japonês, russo, espanhol e mais), sob muitas licenças (desde código aberto até comercial). O primeiro WordNet foi criado pela Universidade de Princeton para inglês sob uma licença semelhante ao MIT.

Uma palavra é normalmente associada a vários synsets com base em seus significados e classes gramaticais. Cada synset geralmente fornece estes atributos:

Attribute Definition Example
Name Name of the synset Example: The word "code" has five synsets with names code.n.01, code.n.02, code.n.03, code.v.01, code.v.02
POS Part of speech of the word for this synset The word "code" has three synsets in noun form and two in verb form
Definition Definition of the word (in POS) One of the definitions of "code" in verb form is: "(computer science) the symbolic arrangement of data or instructions in a computer program"
Examples Examples of word's use One of the examples of "code": "We should encode the message for security reasons"
Lemmas Other word synsets this word+POC is related to (not strictly synonyms, but can be considered so); lemmas are related to other lemmas, not to words directly Lemmas of code.v.02 (as in "convert ordinary language into code") are code.v.02.encipher, code.v.02.cipher, code.v.02.cypher, code.v.02.encrypt, code.v.02.inscribe, code.v.02.write_in_code
Antonyms Opposites Antonym of lemma encode.v.01.encode is decode.v.01.decode
Hypernym A broad category that other words fall under  A hypernym of code.v.01 (as in "Code the pieces with numbers so that you can identify them later") is tag.v.01
Meronym A word that is part of (or subordinate to) a broad category A meronym of "computer" is "chip"
Holonym The relationship between a parent word and its subordinate parts A hyponym of "window" is "computer screen"

Existem vários outros atributos, que você pode encontrar no arquivo fonte nltk/corpus/reader/wordnet.py em /Lib/site-packages.

Algum código pode ajudar a fazer mais sentido.

Esta função auxiliar:

def synset_info(synset):
    print("Name", synset.name())
    print("POS:", synset.pos())
    print("Definition:", synset.definition())
    print("Examples:", synset.examples())
    print("Lemmas:", synset.lemmas())
    print("Antonyms:", [lemma.antonyms() for lemma in synset.lemmas() if len(lemma.antonyms()) > 0])
    print("Hypernyms:", synset.hypernyms())
    print("Instance Hypernyms:", synset.instance_hypernyms())
    print("Part Holonyms:", synset.part_holonyms())
    print("Part Meronyms:", synset.part_meronyms())
    print()
synsets = wordnet.synsets('code')

mostra isso:

5 synsets:
Name code.n.01
POS: n
Definition: a set of rules or principles or laws (especially written ones)
Examples: []
Lemmas: [Lemma('code.n.01.code'), Lemma('code.n.01.codification')]
Antonyms: []
Hypernyms: [Synset('written_communication.n.01')]
Instance Hpernyms: []
Part Holonyms: []
Part Meronyms: []

...

Name code.n.03
POS: n
Definition: (computer science) the symbolic arrangement of data or instructions in a computer program or the set of such instructions
Examples: []
Lemmas: [Lemma('code.n.03.code'), Lemma('code.n.03.computer_code')]
Antonyms: []
Hypernyms: [Synset('coding_system.n.01')]
Instance Hpernyms: []
Part Holonyms: []
Part Meronyms: []

...

Name code.v.02
POS: v
Definition: convert ordinary language into code
Examples: ['We should encode the message for security reasons']
Lemmas: [Lemma('code.v.02.code'), Lemma('code.v.02.encipher'), Lemma('code.v.02.cipher'), Lemma('code.v.02.cypher'), Lemma('code.v.02.encrypt'), Lemma('code.v.02.inscribe'), Lemma('code.v.02.write_in_code')]
Antonyms: []
Hypernyms: [Synset('encode.v.01')]
Instance Hpernyms: []
Part Holonyms: []
Part Meronyms: []

Synsets e lemas seguem uma estrutura em árvore que você pode visualizar:

def hypernyms(synset):
    return synset.hypernyms()

synsets = wordnet.synsets('soccer')
for synset in synsets:
    print(synset.name() + " tree:")
    pprint(synset.tree(rel=hypernyms))
    print()
code.n.01 tree:
[Synset('code.n.01'),
 [Synset('written_communication.n.01'),
   ...

code.n.02 tree:
[Synset('code.n.02'),
 [Synset('coding_system.n.01'),
   ...

code.n.03 tree:
[Synset('code.n.03'),
   ...

code.v.01 tree:
[Synset('code.v.01'),
 [Synset('tag.v.01'),
   ...

code.v.02 tree:
[Synset('code.v.02'),
 [Synset('encode.v.01'),
   ...

O WordNet não cobre todas as palavras e suas informações (há cerca de 170 mil palavras em inglês hoje e cerca de 155 mil na versão mais recente do WordNet), mas é um bom ponto de partida. Depois de aprender os conceitos deste bloco de construção, se você achar que ele é inadequado para suas necessidades, poderá migrar para outro. Ou você pode construir seu próprio WordNet!

Tente você mesmo

Usando as bibliotecas Python, baixe a página da Wikipedia em código aberto e liste os synsets e lemas de todas as palavras.

Comparação de similaridade

A comparação de similaridade é um bloco de construção que identifica semelhanças entre duas partes de texto. Possui muitas aplicações em motores de busca, chatbots e muito mais.

Por exemplo, as palavras “futebol” e “futebol” estão relacionadas?

syn1 = wordnet.synsets('football')
syn2 = wordnet.synsets('soccer')

# A word may have multiple synsets, so need to compare each synset of word1 with synset of word2
for s1 in syn1:
    for s2 in syn2:
        print("Path similarity of: ")
        print(s1, '(', s1.pos(), ')', '[', s1.definition(), ']')
        print(s2, '(', s2.pos(), ')', '[', s2.definition(), ']')
        print("   is", s1.path_similarity(s2))
        print()
Path similarity of: 
Synset('football.n.01') ( n ) [ any of various games played with a ball (round or oval) in which two teams try to kick or carry or propel the ball into each other's goal ]
Synset('soccer.n.01') ( n ) [ a football game in which two teams of 11 players try to kick or head a ball into the opponents' goal ]
   is 0.5

Path similarity of: 
Synset('football.n.02') ( n ) [ the inflated oblong ball used in playing American football ]
Synset('soccer.n.01') ( n ) [ a football game in which two teams of 11 players try to kick or head a ball into the opponents' goal ]
   is 0.05

A pontuação mais alta de similaridade de caminho das palavras é 0,5, indicando que elas estão intimamente relacionadas.

E quanto a "código" e "bug"? As pontuações de similaridade para essas palavras usadas na ciência da computação são:

Path similarity of: 
Synset('code.n.01') ( n ) [ a set of rules or principles or laws (especially written ones) ]
Synset('bug.n.02') ( n ) [ a fault or defect in a computer program, system, or machine ]
   is 0.1111111111111111
...
Path similarity of: 
Synset('code.n.02') ( n ) [ a coding system used for transmitting messages requiring brevity or secrecy ]
Synset('bug.n.02') ( n ) [ a fault or defect in a computer program, system, or machine ]
   is 0.09090909090909091
...
Path similarity of: 
Synset('code.n.03') ( n ) [ (computer science) the symbolic arrangement of data or instructions in a computer program or the set of such instructions ]
Synset('bug.n.02') ( n ) [ a fault or defect in a computer program, system, or machine ]
   is 0.09090909090909091

Estas são as pontuações de similaridade mais altas, o que indica que estão relacionadas.

O NLTK fornece vários pontuadores de similaridade, como:

  • caminho_similaridade
  • lch_similaridade
  • wup_similaridade
  • res_similaridade
  • jcn_similaridade
  • lin_similaridade

Consulte a seção Similaridade da página Interface WordNet para determinar a interface apropriada para sua aplicação.

Tente você mesmo

Usando bibliotecas Python, comece na página Categoria da Wikipedia: listas de termos de computador e prepare uma lista de terminologias e veja como as palavras se correlacionam.

Árvore e banco de árvores

Com o NLTK, você pode representar a estrutura de um texto em forma de árvore para ajudar na análise do texto.

Aqui está um exemplo:

Um texto simples pré-processado e marcado com classe gramatical (POS):

import nltk

text = "I love open source"
# Tokenize to words
words = nltk.tokenize.word_tokenize(text)
# POS tag the words
words_tagged = nltk.pos_tag(words)

Você deve definir uma gramática para converter o texto em uma estrutura de árvore. Este exemplo usa uma gramática simples baseada nas tags Penn Treebank.

# A simple grammar to create tree
grammar = "NP: {<JJ><NN>}"

A seguir, use a gramática para criar uma árvore:

# Create tree
parser = nltk.RegexpParser(grammar)
tree = parser.parse(words_tagged)
pprint(tree)

Isso produz:

Tree('S', [('I', 'PRP'), ('love', 'VBP'), Tree('NP', [('open', 'JJ'), ('source', 'NN')])])

Você pode ver melhor graficamente.

tree.draw()

(Girish Managoli, CC BY-SA 4.0)

Essa estrutura ajuda a explicar corretamente o significado do texto. Como exemplo, identifique o assunto neste texto:

subject_tags = ["NN", "NNS", "NP", "NNP", "NNPS", "PRP", "PRP$"]
def subject(sentence_tree):
    for tagged_word in sentence_tree:
        # A crude logic for this case -  first word with these tags is considered subject
        if tagged_word[1] in subject_tags:
            return tagged_word[0]

print("Subject:", subject(tree))

Mostra que "eu" é o sujeito:

Subject: I

Este é um bloco de construção básico de análise de texto aplicável a aplicações maiores. Por exemplo, quando um usuário diz: “Reserve um voo para minha mãe, Jane, de Londres para NY em 1º de janeiro”, um chatbot que usa este bloco pode interpretar a solicitação como:

Ação: Livro

O quê: Voo

Viajante: Jane

De: Londres

Para: Nova York

Data: 1º de janeiro (do próximo ano)

Um banco de árvores refere-se a um corpus com árvores pré-marcadas. Treebanks de código aberto, de uso gratuito condicional e comerciais estão disponíveis para vários idiomas. O mais comumente usado para o inglês é o Penn Treebank, extraído do Wall Street Journal, um subconjunto do qual está incluído no NLTK. Algumas maneiras de usar um treebank:

words = nltk.corpus.treebank.words()
print(len(words), "words:")
print(words)

tagged_sents = nltk.corpus.treebank.tagged_sents()
print(len(tagged_sents), "sentences:")
print(tagged_sents)
100676 words:
['Pierre', 'Vinken', ',', '61', 'years', 'old', ',', ...]
3914 sentences:
[[('Pierre', 'NNP'), ('Vinken', 'NNP'), (',', ','), ('61', 'CD'), ('years', 'NNS'), ('old', 'JJ'), (',', ','), ('will', 'MD'), ('join', 'VB'), ('the', 'DT'), ('board', 'NN'), ('as', 'IN'), ('a', 'DT'), ('nonexecutive', 'JJ'), ('director', 'NN'), ...]

Veja tags em uma frase:

sent0 = tagged_sents[0]
pprint(sent0)
[('Pierre', 'NNP'),
 ('Vinken', 'NNP'),
 (',', ','),
 ('61', 'CD'),
 ('years', 'NNS'),
...

Crie uma gramática para converter isso em uma árvore:

grammar = '''
    Subject: {<NNP><NNP>}
    SubjectInfo: {<CD><NNS><JJ>}
    Action: {<MD><VB>}
    Object: {<DT><NN>}
    Stopwords: {<IN><DT>}
    ObjectInfo: {<JJ><NN>}
    When: {<NNP><CD>}
'''
parser = nltk.RegexpParser(grammar)
tree = parser.parse(sent0)
print(tree)
(S
  (Subject Pierre/NNP Vinken/NNP)
  ,/,
  (SubjectInfo 61/CD years/NNS old/JJ)
  ,/,
  (Action will/MD join/VB)
  (Object the/DT board/NN)
  as/IN
  a/DT
  (ObjectInfo nonexecutive/JJ director/NN)
  (Subject Nov./NNP)
  29/CD
  ./.)

Veja graficamente:

tree.draw()

(Girish Managoli, CC BY-SA 4.0)

O conceito de árvores e bancos de árvores é um poderoso alicerce para análise de texto.

Tente você mesmo

Usando as bibliotecas Python, baixe a página da Wikipedia em código aberto e represente o texto em uma visualização apresentável.

Reconhecimento de entidade nomeada

O texto, seja falado ou escrito, contém dados importantes. Um dos principais objetivos do processamento de texto é extrair esses dados importantes. Isso é necessário em quase todas as aplicações, como um chatbot de companhia aérea que reserva passagens ou um bot que responde a perguntas. NLTK fornece um recurso de reconhecimento de entidade nomeada para isso.

Aqui está um exemplo de código:

sentence = 'Peterson first suggested the name "open source" at Palo Alto, California'

Veja se nome e local são reconhecidos nesta frase. Pré-processe normalmente:

import nltk

words = nltk.word_tokenize(sentence)
pos_tagged = nltk.pos_tag(words)

Execute o tagger de entidade nomeada:

ne_tagged = nltk.ne_chunk(pos_tagged)
print("NE tagged text:")
print(ne_tagged)
print()
NE tagged text:
(S
  (PERSON Peterson/NNP)
  first/RB
  suggested/VBD
  the/DT
  name/NN
  ``/``
  open/JJ
  source/NN
  ''/''
  at/IN
  (FACILITY Palo/NNP Alto/NNP)
  ,/,
  (GPE California/NNP))

Tags de nome foram adicionadas; extraia apenas as entidades nomeadas desta árvore:

print("Recognized named entities:")
for ne in ne_tagged:
    if hasattr(ne, "label"):
        print(ne.label(), ne[0:])
Recognized named entities:
PERSON [('Peterson', 'NNP')]
FACILITY [('Palo', 'NNP'), ('Alto', 'NNP')]
GPE [('California', 'NNP')]

Veja graficamente:

ne_tagged.draw()

(Girish Managoli, CC BY-SA 4.0)

O etiquetador de entidade nomeada integrado do NLTK, usando o programa de Extração Automática de Conteúdo (ACE) da PENN, detecta entidades comuns como ORGANIZAÇÃO, PESSOA, LOCALIZAÇÃO, INSTALAÇÃO e GPE (entidade geopolítica).

O NLTK pode usar outros taggers, como o Stanford Named Entity Recognizer. Este tagger treinado é construído em Java, mas o NLTK fornece uma interface para trabalhar com ele (consulte nltk.parse.stanford ou nltk.tag.stanford).

Tente você mesmo

Usando as bibliotecas Python, baixe a página da Wikipedia sobre código aberto e identifique pessoas que tiveram influência no código aberto e onde e quando contribuíram.

Exercício avançado

Se você estiver pronto para isso, tente construir essa superestrutura usando os blocos de construção discutidos nestes artigos.

Usando bibliotecas Python, baixe a página Categoria: Ciência da Computação da Wikipedia e:

  • Identifique os unigramas, bigramas e trigramas mais comuns e publique-os como uma lista de palavras-chave ou tecnologias que estudantes e engenheiros precisam conhecer neste domínio.
  • Mostre graficamente os nomes, tecnologias, datas e lugares importantes neste campo. Este pode ser um bom infográfico.
  • Crie um mecanismo de pesquisa. O seu mecanismo de pesquisa tem melhor desempenho do que a pesquisa da Wikipedia?

Qual é o próximo?

A PNL é um pilar por excelência na construção de aplicativos. NLTK é um kit clássico, rico e poderoso que fornece os tijolos e a argamassa para construir aplicativos práticos e atraentes para o mundo real.

Nesta série de artigos, expliquei o que a PNL torna possível usando o NLTK como exemplo. PNL e NLTK têm muito mais a oferecer. Esta série é um ponto de partida para ajudar você a começar.

Se suas necessidades ultrapassarem os recursos do NLTK, você poderá treinar novos modelos ou adicionar recursos a eles. Novas bibliotecas de PNL baseadas em NLTK estão surgindo e o aprendizado de máquina está sendo amplamente usado no processamento de linguagem.