301 lines
12 KiB
Plaintext
301 lines
12 KiB
Plaintext
==========================================
|
|
Examplos para o processamento do português
|
|
==========================================
|
|
|
|
>>> import nltk
|
|
|
|
(NB. Este material parte do pressuposto de que o leitor esteja
|
|
familiarizado com o livro do NLTK, disponível em
|
|
``http://nltk.org/index.php/Book``).
|
|
|
|
Utilizando o Corpus MacMorpho Tagged
|
|
------------------------------------
|
|
|
|
O NLTK inclui o corpus de notícias para o português brasileiro com tags de partes do discurso
|
|
MAC-MORPHO, que conta com mais de um milhão de palavras de textos jornalísticos extraídos
|
|
de dez seções do jornal diário *Folha de São Paulo*, do ano de 1994.
|
|
|
|
Podemos utilizar este corpus como uma seqüência de palavras ou de palavras com tags da
|
|
seguinte maneira:
|
|
|
|
>>> nltk.corpus.mac_morpho.words()
|
|
['Jersei', 'atinge', 'm\xe9dia', 'de', 'Cr$', '1,4', ...]
|
|
>>> nltk.corpus.mac_morpho.sents()
|
|
[['Jersei', 'atinge', 'm\xe9dia', 'de', 'Cr$', '1,4', 'milh\xe3o',
|
|
'em', 'a', 'venda', 'de', 'a', 'Pinhal', 'em', 'S\xe3o', 'Paulo'],
|
|
['Programe', 'sua', 'viagem', 'a', 'a', 'Exposi\xe7\xe3o', 'Nacional',
|
|
'do', 'Zebu', ',', 'que', 'come\xe7a', 'dia', '25'], ...]
|
|
>>> nltk.corpus.mac_morpho.tagged_words()
|
|
[('Jersei', 'N'), ('atinge', 'V'), ('m\xe9dia', 'N'), ...]
|
|
|
|
Também é possível utilizá-lo em chunks de frases.
|
|
|
|
>>> nltk.corpus.mac_morpho.tagged_sents()
|
|
[[('Jersei', 'N'), ('atinge', 'V'), ('m\xe9dia', 'N'), ('de', 'PREP'),
|
|
('Cr$', 'CUR'), ('1,4', 'NUM'), ('milh\xe3o', 'N'), ('em', 'PREP|+'),
|
|
('a', 'ART'), ('venda', 'N'), ('de', 'PREP|+'), ('a', 'ART'),
|
|
('Pinhal', 'NPROP'), ('em', 'PREP'), ('S\xe3o', 'NPROP'), ('Paulo', 'NPROP')],
|
|
[('Programe', 'V'), ('sua', 'PROADJ'), ('viagem', 'N'), ('a', 'PREP|+'),
|
|
('a', 'ART'), ('Exposi\xe7\xe3o', 'NPROP'), ('Nacional', 'NPROP'),
|
|
('do', 'NPROP'), ('Zebu', 'NPROP'), (',', ','), ('que', 'PRO-KS-REL'),
|
|
('come\xe7a', 'V'), ('dia', 'N'), ('25', 'N|AP')], ...]
|
|
|
|
Estes dados podem ser utilizados para efetuar o treinamento de taggers (como nos
|
|
exemplos abaixo para o Floresta treebank).
|
|
|
|
|
|
Utilizando o Floresta Portuguese Treebank
|
|
-----------------------------------------
|
|
|
|
A distribuição de dados do NLTK inclui o
|
|
"Floresta Sinta(c)tica Corpus" na versão 7.4, disponível em
|
|
``http://www.linguateca.pt/Floresta/``.
|
|
|
|
Como para a amostra do Penn Treebank, é possível
|
|
utilizar o conteúdo deste corpus como uma seqüência de palavras com
|
|
informações de tags, da seguinte maneira:
|
|
|
|
>>> from nltk.corpus import floresta
|
|
>>> floresta.words()
|
|
['Um', 'revivalismo', 'refrescante', 'O', '7_e_Meio', ...]
|
|
>>> floresta.tagged_words()
|
|
[('Um', '>N+art'), ('revivalismo', 'H+n'), ...]
|
|
|
|
As tags são constituídas por certas informações sintáticas, seguidas por
|
|
um sinal
|
|
de mais, seguido por tag costumeira de parte do discurso
|
|
(part-of-speech). Vamos
|
|
remover o conteúdo que antecede o sinal de mais:
|
|
|
|
>>> def simplify_tag(t):
|
|
... if "+" in t:
|
|
... return t[t.index("+")+1:]
|
|
... else:
|
|
... return t
|
|
>>> twords = nltk.corpus.floresta.tagged_words()
|
|
>>> twords = [(w.lower(),simplify_tag(t)) for (w,t) in twords]
|
|
>>> twords[:10] # doctest: +NORMALIZE_WHITESPACE
|
|
[('um', 'art'), ('revivalismo', 'n'), ('refrescante', 'adj'), ('o', 'art'), ('7_e_meio', 'prop'),
|
|
('\xe9', 'v-fin'), ('um', 'art'), ('ex-libris', 'n'), ('de', 'prp'), ('a', 'art')]
|
|
|
|
E exibir de maneira mais apropriada as palavras com informações de tags:
|
|
|
|
>>> print ' '.join(word + '/' + tag for (word, tag) in twords[:10])
|
|
um/art revivalismo/n refrescante/adj o/art 7_e_meio/prop ?/v-fin um/art ex-libris/n de/prp a/art
|
|
|
|
Em seguida, vamos contar o número de tokens de palavras e tipos, além de
|
|
determinar qual a palavra mais comum:
|
|
|
|
>>> words = floresta.words()
|
|
>>> len(words)
|
|
211870
|
|
>>> fd = nltk.FreqDist(words)
|
|
>>> len(fd)
|
|
29425
|
|
>>> fd.max()
|
|
'de'
|
|
|
|
Podemos também listar as 20 tags mais freqüentes, em ordem decrescente de
|
|
freqüência:
|
|
|
|
>>> tags = [simplify_tag(tag) for (word,tag) in floresta.tagged_words()]
|
|
>>> fd = nltk.FreqDist(tags)
|
|
>>> fd.sorted()[:20] # doctest: +NORMALIZE_WHITESPACE
|
|
['n', 'prp', 'art', 'v-fin', ',', 'prop', 'adj', 'adv', '.', 'conj-c', 'v-inf',
|
|
'pron-det', 'v-pcp', 'num', 'pron-indp', 'pron-pers', '\xab', '\xbb', 'conj-s', '}']
|
|
|
|
Também podemos ler o corpus agrupado por enunciados:
|
|
|
|
>>> floresta.sents() # doctest: +NORMALIZE_WHITESPACE
|
|
[['Um', 'revivalismo', 'refrescante'], ['O', '7_e_Meio', '\xe9', 'um', 'ex-libris',
|
|
'de', 'a', 'noite', 'algarvia', '.'], ...]
|
|
>>> floresta.tagged_sents() # doctest: +NORMALIZE_WHITESPACE
|
|
[[('Um', '>N+art'), ('revivalismo', 'H+n'), ('refrescante', 'N<+adj')],
|
|
[('O', '>N+art'), ('7_e_Meio', 'H+prop'), ('\xe9', 'P+v-fin'), ('um', '>N+art'),
|
|
('ex-libris', 'H+n'), ('de', 'H+prp'), ('a', '>N+art'), ('noite', 'H+n'),
|
|
('algarvia', 'N<+adj'), ('.', '.')], ...]
|
|
>>> floresta.parsed_sents() # doctest: +NORMALIZE_WHITESPACE
|
|
[Tree('UTT+np', [Tree('>N+art', ['Um']), Tree('H+n', ['revivalismo']),
|
|
Tree('N<+adj', ['refrescante'])]), Tree('STA+fcl', [Tree('SUBJ+np',
|
|
[Tree('>N+art', ['O']), Tree('H+prop', ['7_e_Meio'])]), Tree('P+v-fin', ['\xe9']),
|
|
Tree('SC+np', [Tree('>N+art', ['um']), Tree('H+n', ['ex-libris']),
|
|
Tree('N<+pp', [Tree('H+prp', ['de']), Tree('P<+np', [Tree('>N+art', ['a']),
|
|
Tree('H+n', ['noite']), Tree('N<+adj', ['algarvia'])])])]), Tree('.', ['.'])]), ...]
|
|
|
|
Para ver uma árvore de análise sintática, podemos utilizar o método
|
|
``draw()``, como no exemplo:
|
|
|
|
>>> psents = floresta.parsed_sents()
|
|
>>> psents[5].draw() # doctest: +SKIP
|
|
|
|
|
|
Concordância simples
|
|
--------------------
|
|
|
|
A seguir, apresentamos uma função que recebe uma palavra e uma
|
|
quantidade determinada
|
|
de contexto (medido em caracteres) e gera uma concordância para a mesma.
|
|
|
|
>>> def concordance(word, context=30):
|
|
... for sent in floresta.sents():
|
|
... if word in sent:
|
|
... pos = sent.index(word)
|
|
... left = ' '.join(sent[:pos])
|
|
... right = ' '.join(sent[pos+1:])
|
|
... print '%*s %s %-*s' %\
|
|
... (context, left[-context:], word, context, right[:context])
|
|
|
|
>>> concordance("dar") # doctest: +SKIP
|
|
anduru , foi o suficiente para dar a volta a o resultado .
|
|
1. O P?BLICO veio dar a a imprensa di?ria portuguesa
|
|
A fartura de pensamento pode dar maus resultados e n?s n?o quer
|
|
Come?a a dar resultados a pol?tica de a Uni
|
|
ial come?ar a incorporar- lo e dar forma a um ' site ' que tem se
|
|
r com Constantino para ele lhe dar tamb?m os pap?is assinados .
|
|
va a brincar , pois n?o lhe ia dar procura??o nenhuma enquanto n?
|
|
?rica como o ant?doto capaz de dar sentido a o seu enorme poder .
|
|
. . .
|
|
>>> concordance("vender") # doctest: +SKIP
|
|
er recebido uma encomenda para vender 4000 blindados a o Iraque .
|
|
m?rico_Amorim caso conseguisse vender o lote de ac??es de o empres?r
|
|
mpre ter jovens simp?ticos a ? vender ? chega ! }
|
|
Disse que o governo vai vender ? desde autom?vel at? particip
|
|
ndiciou ontem duas pessoas por vender carro com ?gio .
|
|
A inten??o de Fleury ? vender as a??es para equilibrar as fi
|
|
|
|
Tagging de partes do discurso
|
|
-----------------------------
|
|
|
|
Vamos começar obtendo os dados dos enunciados marcados com tags e
|
|
simplificando
|
|
estas últimas como descrito anteriormente.
|
|
|
|
>>> from nltk.corpus import floresta
|
|
>>> tsents = floresta.tagged_sents()
|
|
>>> tsents = [[(w.lower(),simplify_tag(t)) for (w,t) in sent] for sent in tsents if sent]
|
|
>>> train = tsents[100:]
|
|
>>> test = tsents[:100]
|
|
|
|
Já sabemos que ``n`` é a tag mais comum; desta forma, podemos criar um
|
|
tagger por default
|
|
que marque toda palavra como substantivo e, em seguida, avaliar seu
|
|
desempenho:
|
|
|
|
>>> tagger0 = nltk.DefaultTagger('n')
|
|
>>> nltk.tag.accuracy(tagger0, test)
|
|
0.17690941385435169
|
|
|
|
Como pode-se deduzir facilmente, uma em cada seis palavras é um
|
|
substantivo. Vamos
|
|
aperfeiçoar estes resultados treinando um tagger unigrama:
|
|
|
|
>>> tagger1 = nltk.UnigramTagger(train, backoff=tagger0)
|
|
>>> nltk.tag.accuracy(tagger1, test)
|
|
0.85115452930728241
|
|
|
|
E, em seguida, um tagger bigrama:
|
|
|
|
>>> tagger2 = nltk.BigramTagger(train, backoff=tagger1)
|
|
>>> nltk.tag.accuracy(tagger2, test)
|
|
0.86856127886323264
|
|
|
|
Segmentação de frases
|
|
---------------------
|
|
|
|
O Punkt é uma ferramenta para segmentação de frases lingüisticamente independente, o qual
|
|
requer um treinamento em texto puro.
|
|
O texto de origem (obtido do Floresta Portuguese Treebank) contém uma frase por linha. Podemos
|
|
ler o texto, dividi-lo em função de suas linhas e então agrupar estas linhas utilizando
|
|
espaços. Desta forma as informações sobre quebras de frases terão sido descartadas; podemos
|
|
então dividir este material em dados para treinamento e para verificação:
|
|
|
|
>>> text = open('floresta.txt').read()
|
|
>>> lines = text.split('\n')
|
|
>>> train = ' '.join(lines[10:])
|
|
>>> test = ' '.join(lines[:10])
|
|
|
|
É agora possível treinar o segmentador de frases (ou tokenizador de frases) e utilizá-lo em
|
|
nossas frases de verificação. (Para exibir o texto em uma forma legível, pode ser necessário
|
|
converter o texto para o UTF-8, utilizando ``print sent.decode('latin-1').encode('utf-8')``.)
|
|
|
|
>>> stok = nltk.PunktSentenceTokenizer(train)
|
|
>>> for sent in stok.tokenize(test):
|
|
... print sent
|
|
|
|
|
|
As versões do NLTK a partir da 0.9b1 incluem um modelo treinado para a segmentação de frases
|
|
em português, o qual pode ser carregado pela maneira a seguir. É mais rápido carregar um modelo
|
|
já treinado do que repetir o treinamento do mesmo.
|
|
|
|
>>> stok = nltk.data.load('tokenizers/punkt/portuguese.pickle')
|
|
|
|
Stemming
|
|
--------
|
|
|
|
O NLTK inclui o stemmer para o português RSLP. Vamos demonstrar sua utilização para algumas
|
|
palavras em português:
|
|
|
|
>>> stemmer = nltk.stem.RSLPStemmer()
|
|
>>> stemmer.stem("copiar")
|
|
u'copi'
|
|
>>> stemmer.stem("paisagem")
|
|
u'pais'
|
|
|
|
Stopwords
|
|
---------
|
|
|
|
O NLTK inclui stopword ("palavras limite") para o português:
|
|
|
|
>>> stopwords = nltk.corpus.stopwords.words('portuguese')
|
|
>>> stopwords[:10]
|
|
['a', 'ao', 'aos', 'aquela', 'aquelas', 'aquele', 'aqueles', 'aquilo', 'as', 'at\xe9']
|
|
|
|
A esta altura, é possível utilizá-las para filtrar textos. Vamos encontrar as palavras mais
|
|
comuns (à exceção das stopwords) e listá-las em ordem decrescente de freqüência:
|
|
|
|
>>> fd = nltk.FreqDist(w.lower() for w in floresta.words() if w not in stopwords)
|
|
>>> for word in fd.sorted()[:20]:
|
|
... print word, fd[word]
|
|
, 13444
|
|
. 7725
|
|
? 2369
|
|
? 2310
|
|
? 1137
|
|
o 1086
|
|
} 1047
|
|
{ 1044
|
|
a 897
|
|
; 633
|
|
em 516
|
|
ser 466
|
|
sobre 349
|
|
os 313
|
|
anos 301
|
|
ontem 292
|
|
ainda 279
|
|
segundo 256
|
|
ter 249
|
|
dois 231
|
|
|
|
|
|
Codificações de caracteres
|
|
--------------------------
|
|
|
|
O Python é capaz de lidar com todas a codificações de caracteres mais utilizada para o português, a
|
|
ISO 8859-1 (ISO Latin 1).
|
|
|
|
>>> text = open('floresta.txt').read()
|
|
>>> text[:60]
|
|
'O 7 e Meio \xe9 um ex-libris da noite algarvia.\n\xc9 uma das mais '
|
|
>>> print text[:60]
|
|
O 7 e Meio ? um ex-libris da noite algarvia.
|
|
? uma das mais
|
|
>>> text[:60].decode('latin-1')
|
|
u'O 7 e Meio \xe9 um ex-libris da noite algarvia.\n\xc9 uma das mais '
|
|
>>> text[:60].decode('latin-1').encode('utf-8')
|
|
'O 7 e Meio \xc3\xa9 um ex-libris da noite algarvia.\n\xc3\x89 uma das mais '
|
|
>>> text[:60].decode('latin-1').encode('utf-8')
|
|
'O 7 e Meio \xc3\xa9 um ex-libris da noite algarvia.\n\xc3\x89 uma das mais '
|
|
>>> text[:60].decode('latin-1').encode('utf-16')
|
|
'\xff\xfeO\x00 \x007\x00 \x00e\x00 \x00M\x00e\x00i\x00o\x00 \x00\xe9\x00 \x00u\x00m\x00 \x00e\x00x\x00-\x00l\x00i\x00b\x00r\x00i\x00s\x00 \x00d\x00a\x00 \x00n\x00o\x00i\x00t\x00e\x00 \x00a\x00l\x00g\x00a\x00r\x00v\x00i\x00a\x00.\x00\n\x00\xc9\x00 \x00u\x00m\x00a\x00 \x00d\x00a\x00s\x00 \x00m\x00a\x00i\x00s\x00 \x00'
|