on
Natural Language Processing com a biblioteca spaCy
Ao procurar por NLP analysis no Google a impressão é de que análise de sentimento é a única coisa que se pode explorar em textos. Existe um mundo de possibilidades, e conhecer as ferramentas que temos à disposição é uma ótima maneira de aguçar a criatividade.
Visando isso, nesse texto vamos conhecer algumas métricas usadas para analisar textos e explorá-las através de exemplos, utilizando a biblioteca spaCy.
➗ Tokenization
Uma boa forma de começar a analisar o texto é o separando em unidades menores. O nome desse processo é tokenization, que basicamente significa separar o texto em palavras.
No spaCy você utiliza models, que são tipo os ‘cérebros treinados’ de cada língua. A biblioteca ainda não tem um model para Português :(, só para Inglês e Alemão.
import spacy
nlp = spacy.load('en') #o model
raw_text = "Seven years after the death of his wife, Mill was invited to contest Westminster. His feeling on the conduct of elections made him refuse to take any personal action in the matter, and he gave the frankest expression to his political views, but nevertheless he was elected by a large majority. He was not a conventional success in the House; as a speaker he lacked magnetism. But his influence was widely felt."
parsedData = nlp(raw_text) #pronto, o texto já tá separado em tokens
Ao passar o texto para o model ele já o separa em tokens e computa várias outras propriedades.
word = parsedData[0]
print(word.text, word.lower_)
>>> Seven seven
A lista de todas as propriedades que o model computa pode ser encontrada aqui.
🗣 Part-of-speech tagging
Lembra na escola quando a gente tinha que identificar quem era o substantivo, o verbo e os adjetivos nas frases? Em NLP esse processo se chama part-of-speech tagging. É a boa e velha análise gramatical.
O model já fez essa análise, que pode ser acessada pela propriedade.pos_
de cada token.
for i,word in enumerate(parsedData):
print(word.text, word.pos_)
if i > 5:
break
>>> Seven NUM
years NOUN
after ADP
the DET
death NOUN
of ADP
his ADJ
Aqui a gente pode ver a lista com o significado de cada uma dessas tags.
Existe também uma tag mais elaborada, que pode ser acessada pela propriedade .tag_
Essa tag contém informações sobre a estrutura morfológica da palavra.
word = parsedData[10] #a palavra 'was'
print("original:",word.text)
print("POS tag:",word.pos_)
print("fine grainned POS tag:", word.tag_)
>>> original: was
POS tag: VERB
fine grainned POS tag: VBD
# VBD significa: VerbForm=fin Tense=past
Massa, né?
📌 Named Entity Recognition (NER)
Esse processo busca identificar categorias como nomes de pessoas, organizações, locais, porcentagens, valores monetários, e por ai vai. Essas categorias podem ser pré-definidas por nós, então dependendo do texto podemos criar nossas próprias entidades.
Pra variar, o model também já computou isso pra a gente na propriedade .ent_type_
. Vamos ver quantas entidades nosso texto tem:
for word in parsedData:
if word.ent_type_:
print(word.text, word.ent_type_)
>>> Seven DATE
years DATE
Mill PERSON
Westminster PERSON
House ORG
🔪 Syntactic Parsing
Syntactic parsing é o processo de representar as dependências do texto através das relações entre os tokens. Por exemplo: um artigo está ligado a um substantivo, um advérbio modifica um verbo, e por ai vai.
O Explosion AI criou uma ferramenta linda para visualizar essas dependências, o Dependency Visualizer.
O atributo para ter acesso à dependência sintática de cada token é o .dep_
for word in parsedData:
print(word.text, word.dep_)
>>> Seven nummod
years nsubjpass
after prep
the det
death pobj
of prep
[...]
Existem várias formas de navegar pela árvore sintática. Aqui na documentação eles explicam tudo que pode ser feito. É possível encontrar um verbo e o sujeito dele, por exemplo.
Aqui tem uma lista com o significado de cada tag da análise sintática e nesse site aqui eles explicam mais o significado de cada termo.
Agora vamos ver funcionalidades do spaCy que nos ajudam a trabalhar com as métricas que vimos até aqui.
✅ Rule-based matching
Esse rule-based matching permite a criação de regras utilizando os atributos e flags dos tokens. Isso pode ser bem interessante nos casos onde a gente tá trabalhando em um corpus sobre um domínio específico.
Por exemplo, eu quero encontrar todos os sujeitos nominais do texto:
from spacy.attrs import DEP
from spacy.matcher import Matcher
matcher = Matcher(nlp.vocab)
# http://universaldependencies.org/en/dep/nsubj.html
matcher.add_pattern("SujeitoNominal", [ {DEP:'nsubj'}])
doc = nlp(raw_text)
matches = matcher(doc)
for ent_id, label, start, end in matcher(doc):
print(doc[start:end].text)
>>> feeling
he
He
he
Ainda é possível:
- Criar uma regra nova para a extração dos tokens
- Criar uma classe Tokenizer do zero
- Usar um tokenizer arbitrário em um model
↗️ Word vectors
Para computar as similaridades entre palavras uma técnica comum é representá-las através de vetores. A forma mais famosa de treinar esses vetores é com a família de algoritmos do word2vec.
Com o spaCy é fácil trabalhar com vetores porque as classes Lexeme, Token, Span e Doc têm uma propriedade .vector
. Vamos testar isso vendo as similaridades entre um cachorro, um gato e um cavalo.
my,dog,and_,cat,and__,horse = nlp(u'my dog and cat and horse')
print(cat.similarity(dog))
print(cat.similarity(horse))
print(dog.similarity(horse))
>>> 0.801685428714
0.484733507195
0.624627638895
📈 E a análise se sentimento?
Na documentação eles mostram como conectar um modelo de deep learning no spaCy. Em um outro post vamos explorar mais essa funcionalidade.
E é isso! Com essas coisinhas já dá pra a gente brincar e analisar textos. Nesse gist você encontra o notebook que eu usei nessa tour. 😄