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