.. -*- mode: rst -*-
.. include:: ../definitions.txt
===========================================================================================
Fundamentos de processamento lingüístico: tokenização de textos e classificação de palavras
===========================================================================================
:Autores: Steven Bird, Ewan Klein, Edward Loper
:Contato: sb@csse.unimelb.edu.au
:Versão: |version|
:Revisão: $Revision: 2976 $
:Data: $Date: 2006-04-03 17:02:33 +1000 (Mon, 03 Apr 2006) $
:Copyright: |copy| |copyrightinfo|
:Licença: |license|
.. Note:: Este é um esboço. Por favor, envie seus comentários aos autores.
.. _Introdução:
----------
Introdução
----------
Textos são geralmente representados em computadores por meio de arquivos que
contêem uma seqüência potencialmente longa de caracteres. Para a maioria dos tipos
de processamento lingüístico é necessário identificar e categorizar as palavras de
um texto. Esta se revela uma tarefa nada trivial. Neste capítulo iremos introduzir
os *toquens* como sendo os blocos constituíntes dos textos e mostraremos de que
forma estes últimos podem ser *toquenizados*. Em seguida, iremos considerar a
categorização dos tokens de acordo com suas funções como parte-do-discurso, além
de realizar uma exploração preliminar do Brown Corpus, uma conjunto de mais de
um milhão de palavras em língua inglesa com tags (informações quanto à
categorização das mesmas). Durante o capítulo serão apresentadas algumas
aplicações interessantes: geração de textos aleatórios, classificação automática
de palavras e análise dos verbos modais em textos de diferentes gêneros
(sempre em língua inglesa).
--------------------------------------------
Toquens: os blocos constituíntes de um texto
--------------------------------------------
Como sabemos que determinada parte de um texto constitui uma *palavra* e de
qual forma podemos representar estas e as informações a elas associadas em
uma máquina? Pode parecer excessivamente pedante exigir uma definição para
"palavra"; não podemos dizer simplesmente que trata-se de uma seqüência
de caracteres com um espaço no início e outro fim? Com o evoluir do estudo,
a questão revela-se um pouco mais complexa. Para termos uma noção prática
dos problemas, vamos considerar o seguinte texto retirado do Wall Street
Journal::
Parágrafo 12 do arquivo ``wsj_0034``
It's probably worth paying a premium for funds that invest in markets
that are partially closed to foreign investors, such as South Korea,
some specialists say. But some European funds recently have
skyrocketed; Spain Fund has surged to a startling 120% premium. It has
been targeted by Japanese investors as a good long-term play tied to
1992's European economic integration. And several new funds that aren't
even fully invested yet have jumped to trade at big premiums.
"I'm very alarmed to see these rich valuations," says Smith Barney's
Mr. Porter.
Vamos começar pela string ``aren't``. De acordo com nossa definição simplista,
esta constitui uma única palavra. Mas considere a possibilidade de
queremos verificar se todas as palavras de nosso texto são listadas em
um dicionário e nosso dicionário apresenta definições para ``are`` e
``not``, mas não para ``aren't``. Neste caso, seria muito mais favorável
à nossa pesquisa reconhecer em ``aren't`` uma contração de duas palavras
distintas.
.. Podemos apresentar uma argumentação semelhante a respeito de ``1992's``.
Poderíamos querer executar um simples programa que extraísse de nosso
texto todas as palavras que representem datas. In this case, we would
get achieve more generality by first stripping oexcept in this case,
we would not expect to find ``1992`` in a dictionary.
Se tomarmos nossa definição simplista de palavra literalmente (como
deveríamos fazer, caso estejamos pensando em implementá-la em código), nos
depararemos com outros menores mas sempre reais problemas. Por exemplo,
assumindo que nosso arquivo seja constituído por linhas separadas, como no
texto do WSJ acima, então todas as palavras que estiverem no início de uma
linha não serão reconhecidas devido à ausência de um espaço antes destas
(a menos que consideremos o caractere de "nova linha" como um espaço). Segundo,
de acordo com nosso critério, símbolos de pontuação irão fazer parte das
palavras; ou seja, uma string como ``investors,`` também será considerada
uma palavra, pois não há nenhum espaço entre ``investors`` e a vírgula que
a segue. Conseqüentemente, corremos o risco de não conseguir reconhecer em
``investors,`` (com a vírgula final) um toquen do mesmo tipo de ``investors``
(sem a vírgula final). Mais importante, seria interessante que os sinais de
pontuação funcionassem como "first-class citizen" para a toquenização e a
para o processamento subseqüente. Por exemplo, podemos desejar implementar
uma regra que especifique que uma palavra seguida por um ponto é
provavelmente uma abreviatura, caso a palavra que a siga inicie-se por
letra minúscula. Porém, para formular este tipo de regra, é necessário que
possamos identificar o ponto como um toquen isolado.
Um desafio um pouco diferente é apresentado por exemplos como os
seguintes (provenientes do corpus MedLine [ref]):
#. This is a alpha-galactosyl-1,4-beta-galactosyl-specific adhesin.
#. The corresponding free cortisol fractions in these sera were 4.53
+/- 0.15% and 8.16 +/- 0.23%, respectively.
Em casos como estes, nos deparamos com termos que dificilmente seriam
encontrados em qualquer dicionário ou vocabulário não setorial da língua
inglesa. Além disso, não teríamos sucesso ao tentar analisar sintaticamente
estas strings utilizando uma gramática padrão do inglês. Para determinadas
finalidades, seria conveniente "agrupar" expressões como
``alpha-galactosyl-1,4-beta-galactosyl-specific adhesin`` e ``4.53
+/- 0.15%`` de forma a sereme reconhecidas pelo parser como átomos não
analisáveis. Para isto, deveremos tratá-las como "palavras" únicas para
qualquer finalidade dos processamentos subseqüentes. O resultado geral disto
tudo é que, mesmo que nos atenhamos a uma única língua como o inglês, a
questão de como definir uma palavra depende muito de quais são nossos
objetivos.
.. Note:: Caso nos voltemos para outras línguas que não o inglês, a
segmentação das palavras pode ser ainda mais desafiadora. Por exemplo,
na ortografia chinêsa os caracteres correspondem a morfemas
monosilábicos. Muitos morfemas constituem por si próprios uma palavra,
mas várias palavras contêem mais de um morfema; a maioria é constituída por
dois morfemas. Apesar disso, não há nenhuma representação visual dos
limites das palavras em um texto em chinês.
Vamos dar uma olhada mais detalhada às palavras do texto do WSJ. Suponhamos
que os espaços venham a ser utilizados como delimitadores de palavras e que a
lista de todas as palavras do texto tenha sido colocada em ordem
alfabética; podemos esperar por um resultado como o seguinte::
120, 1992, And, Barney, But, European, European, Fund, I, It, It,
Japanese, Korea, Mr, Porter, Smith, South, Spain, a, a, a, ...
Se a esta altura pedirmos a um programa que nos informe quantas palavras
há no texto, ele provavelmente fornecerá como resposta 90. Esta resposta
deve-se ao fato de que cada uma das três ocorrências da palavra ``a`` está
sendo considerada uma palavra isolada. Mas o que significa dizer que
determinado objeto ``a`` ocorre três vezes? Estamos na presença de três
palavras ``a`` ou de uma única palavra repetida três vezes? Podemos responder
"ambos os casos", se traçarmos uma distinção entre um *toquen* de palavra e
um *tipo* de palavra. Um tipo de palavra é algo abstrato; é aquilo ao qual
nos referimos quando dizemos que conhecemos o significado da palavra
``deprecate`` ou quando dizemos que as palavras ``barf`` e ``vomit`` são
sinônimos. Por outro lado, um token de palavra é algo que existe no tempo e
no espaço. Por exemplo, podemos nos referir à palavra ``grunge`` que pronunciei
em Edinburgo no dia 14 de julho de 2003; da mesma forma, podemos dizer que o
segundo toquen do texto do WSJ é um toquen de tipo de palavra ``probably`` ou
que há dois toquens do tipo ``European`` no texto. Em termos mais gerais,
podemos dizer que há 90 toquens de palavras no texto do WSJ mas apenas 76
tipos de palavras.
Os termos *toquen* e *tipo* também podem ser aplicados a outras entidades
lingüísticas. Por exemplo, um *toquen de sentença* é uma ocorrência individual
de uma determinada sentença; mas um *tipo de sentença* é uma sentença em
termos abstratos, sem contexto. Se alguém repete uma sentença, este alguém
pronunciou dois toquens de sentença, mas apenas um tipo de sentença foi
utilizado. Neste livro, quando a categoria de toquen ou tipo for óbvia a
partir do contexto, utilizaremos apenas os termos token e tipo.
.. _representing.tokens:
Representando toquens
---------------------
Quando a linguagem escrita é armazenada em um arquivo de computador, ela
é normalmente representada por meio de uma seqüência ou *string* (do inglês,
"cadeia") de caracteres. Isto é, em um arquivo de texto padrão, as
palavras são strings, as sentenças são strings e o próprio texto não passa,
no fundo, de uma longa string. Os caracteres de uma string não precisam
ser necessariamente alfanuméricos; estas também podem incluir caracteres
especiais que representem os espaços, as tabulações, os sinais de nova
linha, etc.
Muito do processamento computacional é realizado acima do plano dos
caracteres. Quando compilamos uma linguagem de programação, por exemplo,
o compilador espera que o input (os dados de entrada) seja uma seqüência
de tokens com os quais ele seja capaz de lidar; por exemplo, as classes
dos identificadores, constantes textuais e alfanuméricas. De forma
análoga, um parser espera que seu input seja uma seqüência de tokens de
palavras e não uma seqüência de caracteres individuais. Em sua forma mais
simples, portanto, a toquenização de um texto envolve a busca das
posições da string que contêem os chamados "caracteres em branco" (espaços,
tabulações ou sinais de nova linha) ou sinais de pontuação específicos,
separando a string em toquens nestas posições. Por exemplo, suponhamos que
estejamos trabalhando com um arquivo que contenha as duas linhas abaixo::
The cat climbed
the tree.
Do ponto de vista do parser, este arquivo é meramente uma string de
caracteres:
'The_cat_climbed\\nthe_tree.'
Note que utilizamos apóstrofos para deimitar as strings, "_" para
representar espaços e a expressão "\n" para representar uma nova linha.
Como acabamos de dizer, para toquenizar este texto e permitir sua
utilização pelo parser é necessário indicar explicitamente quais substrings
são palavras. Uma forma conveniente de se fazer isto em Python é dividir a
string em uma *lista* de palavras, na qual cada palavra seja uma string, como
em ``'dog'``. [#]_
Em Python, uma lista é representada como uma série de objetos (neste caso,
strings) separados por vírgulas, delimitada por conchetes::
>>> palavras = ['the', 'cat', 'climbed', 'the', 'tree']
>>> palavras
['the', 'cat', 'climbed', 'the', 'tree']
.. [#] Dissemos "conveniente" pois o Python é possível uma interação pela
lista, processando os seus elementos um a um.
Note que introduzimos uma nova variável, chamada ``palavras``, à qual
é atribuída a lista e que entramos com o nome da variável em uma nova
linha para verificar seu conteúdo.
Em resumo, acabamos de mostrar como, em sua forma mais simples, a toquenização
de um texto pode ser realizada convertendo uma única string representando o
texto em uma lista de strings, na qual cada elemento represente uma palavra.
------------
Toquenização
------------
Muitas tarefas do processamento de linguagem natural envolvem a análise de
textos de dimensões variadas, indo de uma única sentença até corpora
extensos. Há várias formas de se representar textos usando o NLTK. A
mais simples destas é por meio de uma única string. Estas strings podem
ser lidas diretamente de arquivos:
>>> text_str = open('corpus.txt').read()
>>> text_str
'Hello world. This is a test file.\n'
Porém, como notamos em representing.tokens_, é normalmente preferível
representar um texto como uma lista de toquens. Estas listas são normalmente
criadas utilizando-se um *toquenizador* como o ``tokenize.whitespace`` que
separa as strings em palavras conforme os espaços:
>>> from nltk import tokenize
>>> text = 'Hello world. This is a test string.'
>>> list(tokenize.whitespace(text))
['Hello', 'world.', 'This', 'is', 'a', 'test', 'string.']
.. Note:: Por "espaço", não entendemos apenas o espaço entre palavras, mas
também as tabulações e os finais de linha.
A toquenização pode normalizar um texto, por exemplo mapeando suas
palavras para versões apenas com letra minúscula, expandindo
contrações e até mesmo extraíndo o radical de cada palavra, processo
este conhecido por "stemming". Um exemplo de stemming é listado
abaixo:
>>> text = 'stemming can be fun and exciting'
>>> tokens = tokenize.whitespace(text)
>>> porter = tokenize.PorterStemmer()
>>> for token in tokens:
... print porter.stem(token),
stem can be fun and excit
A toquenização baseada em espaços é simplista demais para a maioria das
aplicações; por exemplo, ela falha ao separar a última palavra de uma
frase ou sentença de caracteres de pontuação como a vírgula, o ponto
final, o ponto de exclamação e o ponto de interrogação. Como o nome
sugere, o ``tokenize.regexp`` utiliza uma expressão regular para
determinar de que forma um texto deve ser dividido. Esta expressão regular
define os caracteres que podem constituir uma palavra válida. Para
definir um toquenizador que inclua sinais de pontuação como toquens
separados, poderiamos utilizar::
>>> text = '''Hello. Isn't this fun?'''
>>> pattern = r'\w+|[^\w\s]+'
>>> list(tokenize.regexp(text, pattern))
['Hello', '.', 'Isn', "'", 't', 'this', 'fun', '?']
.. Tip:: Lembre-se que ``\w+|[^\w\s]+`` é uma disjunção de duas
subexpressões, ``w+`` e ``[^\w\s]+``. A primeira destas aceita um ou
mais caracteres "de palavra"; ou seja, caracteres que não sejam espaços
em branco ou sinais de pontuação. O segundo padrão é uma expressão negada;
ela aceita um ou mais caracteres que não sejam caracteres de palavra (ou
seja, não aceita o conjunto ``\w``) ou espaços em branco (ou seja, não
aceita o conjunto ``\s``).
.. Tip:: A expressão regular deste exemplo aceitará uma seqüência que
seja constituída por um ou mais caracteres de palavra ``\w+``. Também
aceitará uma seqüência que seja constituída por um ou mais sinais de
pontuação (ou caracteres não-palavra e não-espaço, ``[^\w\s]+``).
Há várias formas por meio das quais podemos melhorar esta expressão regular.
Por exemplo, em sua versão atual ela separa a string ``'$22.50'`` em quatro
toquens distintos; podemos porém desejar que ela identifique neste tipo de
string um único toquen. A solução para isto é modificar a expressão regular,
adicionado uma nova subexpressão que trate especificamente de strings
neste formato::
>>> text = 'That poster costs $22.40.'
>>> pattern = r'\w+|\$\d+\.\d+|[^\w\s]+'
>>> list(tokenize.regexp(text, pattern))
['That', 'poster', 'costs', '$22.40', '.']
Certas vezes, é mais conveniente escrever uma expressão regular que
aceite o que é encontrado *entre* os tokens, como os espaços em branco ou
os sinais de pontuação. O construtor de função ``tokenize()`` aceita um
parâmetro booleano opcional ``gaps`` (em inglês, ---); quando definido como
``True`` (em inglês, verdadeiro) o padrão é verificado against the gaps.
Por exemplo, eis como o ``whitespaceTokenize`` é definido::
>>> list(tokenize.regexp(text, pattern=r'\s+', gaps=True))
['That', 'poster', 'costs', '$22.40.']
O package ``nltk.corpora`` disponibiliza acesso imediato a vários
corpora distribuídos com o NLTK, juntamente com toquenizadores built-in.
Por exemplo, o ``brown.tagged()`` é um iterator sobre as sentenças com
tags do Brown Corpus. Utilizamos a função ``extract()`` para extrair
uma sentença que nos interesse::
>>> from nltk.corpora import brown, extract
>>> print extract(0, brown.tagged('a'))
[('The', 'at'), ('Fulton', 'np-tl'), ('County', 'nn-tl'), ('Grand', 'jj-tl'), ('Jury', 'nn-tl'), ('said', 'vbd'), ('Friday', 'nr'), ('an', 'at'), ('investigation', 'nn'), ('of', 'in'), ("Atlanta's", 'np$'), ('recent', 'jj'), ('primary', 'nn'), ('election', 'nn'), ('produced', 'vbd'), ('``', '``'), ('no', 'at'), ('evidence', 'nn'), ("''", "''"), ('that', 'cs'), ('any', 'dti'), ('irregularities', 'nns'), ('took', 'vbd'), ('place', 'nn'), ('.', '.')]
Em particular, o ``brown.tagged()`` não apenas carrega o texto relevante
em uma string, mas também utiliza o toquenizador apropriado.
.. frequency:
----------------
Contando toquens
----------------
Provavelmente, a coisa mais fácil que se possa fazer com os toquens, uma
vez que tenham sido extraídos de um texto, é contá-los. Podemos fazer
isto como mostrado a seguir, quando comparamos o comprimento das traduções
para o inglês e para o finlandês do livro do Gênesis::
>>> from nltk.corpora import genesis
>>> len(list(genesis.raw('english-kjv')))
38240
>>> len(list(genesis.raw('finnish')))
26595
Podemos realizar uma contagem mais sofisticada por meio de uma *distribuição
de freqüência*. Geralmente, uma distribuição de freqüência armazena o
número de vezes que cada determinado valor ocorre como resultado de uma
experiência. Neste caso, podemos utilizar uma distribuição de freqüência
para armazenar a freqüência de cada palavra em um documento. Distribuições
de freqüência são geralmente inicializadas executando-se repetidamente uma
experiência e incrementando a contagem de cada valor toda vez que este for
obtido como resultado. O programa a seguir cria uma distribuição de
freqüência que armazena o número de vezes que cada palavra ocorre em
um texto, exibindo ao final a palavra mais freqüente::
>>> from nltk.probability import FreqDist
>>> fd = FreqDist()
>>> for token in genesis.raw():
... fd.inc(token)
>>> fd.max()
'the'
Uma vez que tenhamos construído uma distribuição de freqüência que
armazene os resultados de uma experiência, podemos utilizá-la para examinar
uma série de propriedades interessantes desta experiência. Estas
propriedades são resumidas abaixo:
========== ================== ===========================================
Módulo de distribuição de freqüência
---------------------------------------------------------------------------
Nome Código de exemplo Descrição
========== ================== ===========================================
Contagem fd.count('the') número de vezes que determinado resultado ocorreu
Freqüência fd.freq('the') freqüência de um dado resultado
N fd.N() número de resultados
Resultados fd.samples() lista com cada resultado distinto armazenado
Max fd.max() resultado com maior número de ocorrência
========== ================== ===========================================
Podemos utilizar uma ``FreqDist`` para examinar a distribuição do
comprimento das palavras em um determinado corpus. Para cada palavra,
encontramos seu comprimento e incrementamos a contagem para as palavras
com aquele comprimento.
>>> def length_dist(text):
... fd = FreqDist() # inicializa uma distribuição de freqüência vazia
... for token in genesis.raw(text): # para cada toquen
... fd.inc(len(token)) # encontrada outra palavra com este comprimento
... for i in range(15): # para cada comprimento entre 0 e 14
... print "%2d" % int(100*fd.freq(i)), # exibe a porcentagem de palavras com este comprimento
... print
>>> length_dist('english-kjv')
0 2 14 28 21 13 7 5 2 2 0 0 0 0 0
>>> length_dist('finnish')
0 0 9 6 11 16 16 12 9 6 3 2 2 1 0
Uma *condição* especifica o contexto no qual uma experiência foi
realizada. Normalmente nos interessaremos pelos efeitos que as condições
produzem no resultado de uma experiência. Por exemplo, podemos querer
examinar de que forma a distribuição do comprimento das palavras (o
resultado) é influenciado pela letra inicial das mesmas (a condição).
Distribuições de freqüência condicionais fornecem uma ferramenta para
explorar este tipo de questão.
Uma *distribuição de freqüência condicional* é um conjunto de distribuições
de freqüência para uma mesma experiência, executada sob condições
diferentes. As distribuições de freqüência individuais são indexadas
por sua condição.
::
>>> from nltk.probability import ConditionalFreqDist
>>> cfdist = ConditionalFreqDist()
>>> for text in genesis.items:
... for word in genesis.raw(text):
... cfdist[text].inc(len(word))
Para plotar os resultados, construímos uma lista de pontos, onde a
coordenada x refere-se ao comprimento das palavras e a coordenada y à
freqüência de cada um destes comprimentos:
>>> for cond in cfdist.conditions():
... wordlens = cfdist[cond].samples()
... wordlens.sort()
... points = [(i, cfdist[cond].freq(i)) for i in wordlens]
Podemos plotar estes pontos usando a função ``Plot`` definida em
``nltk.draw.plot``, da seguinte forma: ``Plot(points).mainloop()``.
-------------------------------------------------
Uma aplicação prática: prever a palavra sucessiva
-------------------------------------------------
Distribuições de freqüência condicionais são muitas vezes utilizadas
para prever certos comportamentos. *Prever*, neste sentido, significa
estimar um valor plausível como resultado da execução de determinada
experiência. A decisão de qual resultado fornecer é normalmente uma função
do contexto no qual a experiência é realizada. Por exemplo, podemos
tentar prever uma palavra de um texto (o resultado) baseado-nos no texto
que antecede esta palavra (o contexto).
Para prever os resultados de uma experiência, é necessário que,
primeiramente, examinemos um *corpus de treinamento* representativo, no
qual o contexto e o resultado para cada execução da experiência já são
conhecidos. Quando apresentados a uma nova execução da experiência,
simplesmente escolhemos o resultado que ocorreu mais freqüentemente
naquele contexto específico.
Podemos utilizar uma ``ConditionalFreqDist`` para encontrar a ocorrência
mais freqüente de cada contexto. Primeiro, armazenamos cada resultado do
corpus de treinamento, utilizando o contexto no qual a experiência é
executada como condição. Então, podemos acessar a freqüência de distribuição
para cada dado contexto usando este último como índice e, finalmente,
utilizar o método ``max()`` para encontrar o resultado mais provável.
Abaixo, usaremos uma ``ConditionalFreqDist`` para encontrar a palavra mais
provável em um texto. Para iniciar, carregaremos um corpus a partir de um
arquivo de texto e criaremos uma ``ConditionalFreqDist`` vazia::
>>> from nltk.probability import ConditionalFreqDist
>>> cfdist = ConditionalFreqDist()
A seguir, examinaremos cada toquen neste corpus, incrementando a
contagem apropriada para cada um. A variável ``prev`` será utilizada para
armazenar a palavra anterior.
>>> prev = None
>>> for word in genesis.raw():
... cfdist[prev].inc(word)
... prev = word
.. Note:: Certas vezes, o contexto de uma experiência pode não estar
disponível ou não existir. Por exemplo, no caso do primeiro toquen não
há nenhum texto que o anteceda. Em situações como esta, somos obrigados
a decidir qual contexto utilizar. Neste exemplo, utilizamos o ``None``
(do inglês, "nada") como contexto para o primeiro toquen. Outra
opção teria sido descartar as informações do primeiro toquen.
Uma vez que tenhamos construído uma distribuição de freqüência condicional
para o corpus de treinamento, podemos utilizá-la para encontrar a palavra
mais provável para um dado contexto. Por exemplo, tomando por contexto a
palavra ``living``, é possível inspecionar todas as palavras que
ocorreram no mesmo.
>>> word = 'living'
>>> cfdist['living'].samples()
['creature,', 'substance', 'soul.', 'thing', 'thing,', 'creature']
We can set up a simple loop to generate text: we set an initial
context, picking the most likely token in that context as our next
word, and then using that word as our new context:
Podemos criar um ciclo simples para gerar texto: determinamos um contexto
inicial, selecionando o toquen mais provável para este contexto como a
nossa próxima palavra, e então utilizando esta palavra como nosso
novo contexto:
>>> word = 'living'
>>> for i in range(20):
... print word,
... word = cfdist[word].max()
living creature that he said, I will not be a wife of the land of the land of the land
Esta abordagem simplista para a geração de texto tende a prender-se em
ciclos repetitivos, como demostrado pelo texto gerado acima. Uma
abordagem mais avançada poderia escolher aleatoriamente cada palavra,
com as palavras mais freqüentes escolhidas um maior número de vezes.
----------------------------------------
Classes de palavras e partes-do-discurso
----------------------------------------
Nas seções anteriores tratamos todas as palavras de forma muito
semelhante: qualquer coisa era ou não era um toquen. Porém, para muitas
aplicações, é importante que possamos distinguir entre *tipos
diferentes* de tokens. Por exemplo, podemos querer marcar explicitamente
que determinada string é um item lexical comum, que outra constitui
uma expressão numérica e que outra ainda é um sinal de pontuação. Além
disto, podemos querer distinguir entre os diferentes tipos de itens
lexicais: de fato, há uma longa tradição no campo da lingüística em
classificarem-se as palavras em categorias chamadas *partes-do-discurso*.
Estas também podem ser chamadas de classes de palavras ou de
*categorias lexicais*. Exemplos familiares são *substantivo*, *verbo*,
*preposição*, *adjetivo* e *advérbio*. Nesta seção apresentaremos os
critérios padrão para a categorização de palavras desta forma,
discutindo as principais classes de palavras da língua inglesa.
Categorizando palavras
----------------------
Como decidimos a qual categoria uma palavra deve pertencer? Em
geral, os lingüistas valem-se de três tipos de avaliações para tomar
esta decisão: uma formal, uma sintática (ou distributiva) e uma
conceitualista (ou semântica). Uma avaliação *formal* é aquela que
analisa a estrutura interna de uma palavra. Por exemplo, ``-ness`` é
um sufixo que combina-se a um adjetivo para produzir um substantivo.
Exemplos são ``happy`` > ``happiness``, ``ill`` > ``illness``. [#]_
Assim, ao encontrarmos uma palavra que termine em ``-ness``, podemos
com grande probabilidade identificá-la como sendo um substantivo.
.. [#] Utilizamos > com o significado de 'deriva em'.
Um critério sintático considera os contextos sintáticos nos quais uma
palavra pode ocorrer. Por exemplo, vamos assumir que já tenhamos
definido a categoria dos substantivos. Em um critério sintática para a
língua inglesa, podemos dizer que um adjetivo é aquela palavra que
ocorre imediatamente antes de um substantivo ou que segue as palavras
``be`` e ``very``. De acordo com estes testes, ``near`` deveria ser
categorizada como um adjetivo:
#. the near window
#. The end is (very) near.
Um exemplo conhecido de critério conceitualista é definir um substantivo
como o "nome de uma pessoa, lugar ou coisa". Dentro da lingüística moderna,
critérios nocionais para definição de classes de palavras têm sido vistos
com considerável desconfiança, principalmente devido à dificuldade de
se formalizar tais conceitos. Apesar disto, critérios conceitualistas são
a base de muitas de nossas intuições quanto a classes de palavras e nos
permitem estimar, com uma boa probabilidade de acerto, a categorização de
palavras em línguas que desconhecemos; isto é, se por exemplo soubermos apenas
que em holandês ``verjaardag`` significa o mesmo que a palavra inglesa
``birthday`` ou a portuguesa ``aniversário``, podemos supor que
``verjaardag`` seja um substantivo também em holandês. Porém, é necessária
cautela: mesmo que possamos traduzir ``zij is van dag jarig`` como ``it's
her birthday today`` ou é ``hoje é o seu (dela) aniversário``, a palavra
``jarig`` é na verdade um adjetivo em holandês e não há uma eqüivalência
exata desta em inglês ou em português.
.. http://www.askoxford.com/pressroom/archive/odelaunch/
Todas as línguas adquirem novos itens lexicais. A lista de palavras
que recentemente foi adicionada ao Oxford Dictionary of English inclui
``cyberslacker, fatoush, blamestorm, SARS, cantopop, bupkis, noughties,
muggle`` e ``robata``. Note que todas estas palavras são substantivos;
isto se reflete no fato dos substantivos serem considerados uma *classe
aberta*. Em contraste, as preposições são consideradas uma *classe fechada*,
ou seja, há um conjunto limitado de palavras que pertence a tal classe
(por exemplo, sempre em inglês, ``above, along, at, below, beside, between,
during, for, from, in, near, on, outside, over, past, through,
towards, under, up, with``) e as mudanças ocorrem muito gradualmente
ao longo do tempo.
.. Some word classes consist of a limited set of so-called
*function* words. Prepositions are one such class, comprising
items like etc. These are called *closed classes*, in the sense
that although languages acquire new lexical items. Content words
such as nouns are not limited in this way, and are continually
being extended with the invention of new words. These are called
*open classes*.
Classes de palavras em inglês
-----------------------------
Esta seção apresenta uma breve panorâmica sobre as classe de palavras
na língua inglesa. Leitores interessados em maiores detalhes são
aconselhados a consultar uma gramática da língua.
Linguists commonly recognize four major categories of open class words
in English, namely nouns, verbs, adjectives and adverbs. Nouns
generally refer to people, places, things, or concepts, e.g.: *woman,
Scotland, book, intelligence*. In the context of a sentence, nouns
can appear after determiners and adjectives, and can be the subject or
object of the verb:
Os lingüistas costumam reconhecer quatro categorias principais de
classes abertas de palavras em inglês: substantivos, verbos, adjetivos
e advérbios. Substantivos referem-se, geralmente, a pessoas, lugares,
coisas ou conceitos, como ``woman, Scotland, book, intelligence``.
No contexto da sentença, os substantivos podem ser encontrados após
determinantes e adjetivos, e podem ser o sujeito ou objeto de um verbo:
============ ============================================= ===================================
Padrões sintáticos envolvendo alguns substantivos
------------------------------------------------------------------------------------------------
Palavra Após um determinante Sujeito de um verbo
woman *the* woman who I saw yesterday ... the woman *sat* down
Scotland *the* Scotland I remember as a child ... Scotland *has* five million people
book *the* book I bought yesterday ... this book *recounts* the colonisation of Australia
intelligence *the* intelligence displayed by the child ... Mary's intelligence *impressed* her teachers
============ ============================================= ===================================
Em inglês, os substantivos podem ser morfologicamente complexos. Por exemplo,
palavras como ``books`` e ``women`` estão no plural. Como vimos
anteriormente, palavras terminadas com o sufixo ``-ness`` são substantivos
derivados de adjetivos, como ``happiness`` e ``illness``. O sufixo ``-ment``
é encontrado em alguns substantivos derivados de verbos, como
em ``government`` e ``establishment``.
Os substantivos são geralmente divididos em *substantivos comuns* e
*substantivos próprios* (ou "nomes comuns" e "nomes próprios").
Substantivos próprios identificam índividuos ou entidades em particular, como
``Moses`` e ``Scotland``, enquanto substantivos comuns são todos os
restantes. Outra distinção importante existe entre *substantivos contáveis*
e *substantivos incontáveis* (ou "substantivos de massa"). Substantivos
contáveis são concebidos como entidades distintas que podem ser contadas,
como ``pig`` (por exemplo, ``one pig, two pigs, many pigs``). Eles não
podem ocorrer com a palavra ``much`` (como em \*``much pigs``).
Substantivos incontáveis, por outro lado, não são vistos como entidades
distintas (por exemplo, ``sand``). Eles não podem ser pluralizados e não
podem ocorrer com numerais (por exemplo, \*``two sands``, \*``many sands``).
Por outro lado, eles podem ocorrer com ``much`` (como em ``much sand``).
Verbos são palavras que descrevem eventos e ações, como ``fall`` e
``eat``. No contexto da sentença, verbos expressam a relação envolvendo
os referentes de um ou mais sintagmas nominais.
======= ================= ===============================================
Padrões sintáticos envolvendo alguns verbos
---------------------------------------------------------------------------
Palavra Simples Com modificadores e adjuntos (em itálico)
fall Rome fell Dot com stocks *suddenly* fell *like a stone*
eat Mice eat cheese John ate the pizza *with gusto*
======= ================= ===============================================
Verbos podem ser classificados de acordo com o número de argumentos
(geralmente sintagmas nominais) que os acompanham. A palavra ``fall``
("cair") é *intransitiva*, requerendo apenas um argumento (a entidade
que cai). A palavra ``eat`` ("comer") é *transitiva*, requerendo dois
argumentos (o comedor e o comido). Outros verbos são mais complexos; por
exemplo, ``put`` ("colocar") requer três argumentos: o agente que está
realizando a ação de colocar, a entidade que está sendo colocada em algum
lugar e a sua localização final. O sufixo ``-ing`` é encontrado em
substantivos derivados de verbos, como ``the falling of the leaves``
(o que é conhecido por gerúndio).
Em inglês, verbos podem ser morfologicamente complexos. Por exemplo, o
*particípio presente* de um verbo termina em ``-ing`` e expressa a idéia
de estar em execução, de uma ação incompleta (como ``falling`` e
``eating``). O *particípio passado* de um verbo geralmente termina em
``-ed`` e expressa a idéia de uma ação concluída (como ``fell`` e ``ate``).
Duas outras importantes classes são os *adjetivos* e *advérbios*.
Adjetivos descrevem substantivos e podem ser utilizados como
modificadores (por exemplo, ``large`` em ``the large pizza``) ou em
predicados (por exemplo, ``the pizza is large``). Em inglês, adjetivos
podem ser morfologicamente complexos (como
``fallV+ing`` em ``the falling stocks``).
Advérbios modificam verbos para especificar o tempo, o modo, o lugar
ou a direção do evento descrito pelo verbo (como ``quickly`` em
``the stocks fell quickly``). Advérbios também podem modificar
adjetivos (como ``really`` em ``Mary's teacher was really nice``).
O inglês possui várias categorias de classes fechadas de palavras além
das preposições e cada dicionário e gramática as classifica de forma
diferente. A tabela abaixo fornece um exemplo de classes fechadas de
palavras, segundo a classificação utilizada no Brown Corpus. [#]_
.. [#] Note que as tags para partes-do-discurso podem ser apresentadas
tanto em letra maiúscula quando em letra minúscula -- não há
nenhuma significação resultante desta diferença.
==== ========================================= ==========================================================
Algumas classes de palavras fechadas do inglês, com as tags do Brown Tag
-----------------------------------------------------------------------------------------------------------
ap determiner/pronoun, post-determiner many other next more last former little several enough most least only very few fewer past same
at article the an no a every th' ever' ye
cc conjunction, coordinating and or but plus & either neither nor yet 'n' and/or minus an'
cs conjunction, subordinating that as after whether before while like because if since for than until so unless though providing once lest till whereas whereupon supposing albeit then
in preposition of in for by considering to on among at through with under into regarding than since despite ...
md modal auxiliary should may might will would must can could shall ought need wilt
pn pronoun, nominal none something everything one anyone nothing nobody everybody everyone anybody anything someone no-one nothin'
ppl pronoun, singular, reflexive itself himself myself yourself herself oneself ownself
pp$ determiner, possessive our its his their my your her out thy mine thine
pp$$ pronoun, possessive ours mine his hers theirs yours
pps pronoun, personal, nom, 3rd pers sng it he she thee
ppss pronoun, personal, nom, not 3rd pers sng they we I you ye thou you'uns
wdt WH-determiner which what whatever whichever
wps WH-pronoun, nominative that who whoever whosoever what whatsoever
==== ========================================= ==========================================================
Conjunto de tags das partes-do-discurso
---------------------------------------
Most part-of-speech tag sets make use of the same basic categories,
such as noun, verb, adjective, and preposition. However, tag sets
differ both in how finely they divide words into categories; and in
how they define their categories. For example, ``is`` might be tagged
as a verb in one tag set; but as a distinct form of ``to be`` in
another tag set -- in fact, we just observed the latter situation
in the Brown Corpus tag set. This variation in tag sets is
unavoidable, since part-of-speech tags are used in different ways for
different tasks. In other words, there is no one 'right way' to assign
tags, only more or less useful ways, depending on one's goals.
A maior parte dos conjuntos de tags ("tag sets") utiliza as mesmas
categorias básicas, como substantivo, verbo, adjetivo e preposição. Estes
conjuntos de tags diferem, porém, tanto na extensão com que as palavras
são divididas em categorias quanto na forma como estas últimas são
definifas. Por exemplo, ``is`` pode receber a tag de verbo em um determinado
conjunto mas ser considerado uma forma distinta de ``to be`` em
outro -- de fato, obverservados este último caso antes, no conjunto de tags
do Brown Corpus. Esta variação nos conjuntos de tags é inevitável,
já que as tags de partes-do-discurso são utilizadas de formas diferentes
para diferentes tarefas. Em outras palavras, não há uma "forma correta" de
se atribuir tags, apenas há formas mais e menos apropriadas, dependendo
dos objetivos de cada um.
.. Há varios conjuntos de tags para partes-do-discurso
utilizados amplamente, porque há diferentes esquemas para a classificação
das palavras (em função dos diferentes pesos dados aos critérios
formais, sintáticos ou nocionais), e porque as diferentes tarefas
de processamento exigem classificações mais precisas ou
gerais.
Observe como o processo de tagging simultaneamente descarta algumas
distinções (como por exemplo a identidade lexical, que é perdida
quando todos os pronomes pessoais recebem a mesma tag ``prp``) e
introduz novas, removendo ambigüidades (como por exemplo ``deal`` que
recebe a tag ``vb`` ou a tag ``nn``). Esta forma de operação facilita
a classificação e predição. Observe que quando introduzimos distinções
mais específicas no conjunto de tags obtemos uma melhor informação quanto
ao contexto lingüístico, mas é necessário um trabalho maior para
classificar cada toquen (há um numero maior de tags possíveis entre
as quais escolher a correta). Da mesma forma, com um menor nível de
distinção entre as tags será mais simples classificar cada toquen, mas
uma quantidade menor de informações referentes ao contexto
permencerá disponível.
In this tutorial, we will use the following tags: ``at`` (article)
``nn`` (Noun), ``vb`` (Verb), ``jj`` (Adjective), ``in``
(Preposition), ``cd`` (Number), and ``end`` (Sentence-ending
punctuation). As we mentioned, this is a radically simplified version
of the Brown Corpus tag set, which in its entirety has 87 basic tags
plus many combinations.
Neste tutorial, usaremos as seguintes tags: ``at`` (artigo), ``nn``
(substantivo), ``vb`` (verbo), ``jj`` (adjetivo), ``in`` (preposição),
``cd`` (numeral), e ``end`` (pontuação de final de sentença). Como dissemos,
esta é uma versão radicalmente simplificada do conjunto de tags do
Brown Corpus, o qual em versão integral apresenta 87 tags de base além
de várias combinações.
----------------------------------------
Representando toquens e corpora com tags
----------------------------------------
Nas seções anteriores foi discutida a natureza e a utilização das tags
no processamento lingüístico. Nesta seção introduziremos a representação
computacional de tags. Primeiro iremos considerar os tokens marcados
individualmente com tags, mostrando de que forma eles são criados e como
podem ser acessados; finalmente nos interessaremos pelos corpora
com tags.
Por convenção, um toquen com tag é representado utilizando-se um
tuple do Python::
>>> tok = ('fly', 'nn')
>>> tok
('fly', 'nn')
Podemos acessar as propriedades deste toquen da forma costumeira, como
demonstrado abaixo:
>>> print tok[0]
fly
>>> print tok[1]
nn
Vários corpora extensos (como o Brown Corpus e a partes do Wall Street
Journal) passaram por um processo de tagging manual, recebendo tags
referentes às partes-do-discurso. Antes que possamos utilizar estes
corpora, é necessário que os carreguemos a partir dos arquivos que os
contêem e que os toquenizemos.
Textos com tags são geralmente armazenados em arquivos como seqüências de
toquens separados por espaços em branco, onde cada toquen é representado na
forma ``texto/tag``, como ilustrado abaixo em uma seqüência extraída do
Brown Corpus::
The/at grand/jj jury/nn commented/vbd on/in a/at number/nn of/in
other/ap topics/nns ,/, among/in them/ppo the/at Atlanta/np and/cc
Fulton/np-tl County/nn-tl purchasing/vbg departments/nns which/wdt it/pps
said/vbd ``/`` are/ber well/ql operated/vbn and/cc follow/vb generally/rb
accepted/vbn practices/nns which/wdt inure/vb to/in the/at best/jjt
interest/nn of/in both/abx governments/nns ''/'' ./.
É possível utilizar o módulo ``nltk.corpora`` para ler e toquenizar
dados a partir de corpus com tags, como vimos acima.
Eis outro exemplo que constroi toquens a partir de uma string::
>>> sent = """
... John/nn saw/vb the/at book/nn on/in the/at table/nn ./end He/nn sighed/vb ./end
... """
>>> from nltk.tag import tag2tuple
>>> for t in tokenize.whitespace(sent):
... print tag2tuple(t),
('John', 'nn') ('saw', 'vb') ('the', 'at') ('book', 'nn') ('on', 'in') ('the', 'at') ('table', 'nn') ('.', 'end') ('He', 'nn') ('sighed', 'vb') ('.', 'end')
---------------
Mais aplicações
---------------
Agora que podemos acessar um texto com tags, é possível realizar uma
variedade de úteis tarefas de processamento. Iremos considerar aqui
apenas duas: estimar a tag da parte-do-discurso de uma palavra e explorar
a distribuição de freqüência de verbos modais de acordo com o gênero de
cada texto (em língua inglesa).
Classificação automática de palavras
------------------------------------
Um corpus com tags pode ser utilizado para *treinar* um classificador
simples que pode ser utilizado para estimar a tag de palavras ainda não
classificadas. Para cada palavra, iremos contar o número de vezes que
esta recebe cada tag. Por exemplo, a palavra ``deal`` recebe 89 vezes a tag
``nn`` e 41 vezes a tag ``vb``. Baseados nisso, se tivessemos de estimar uma
tag para a palavra ``deal``, escolheríamos ``nn``, com um índice de acerto
de mais de dois terços. O programa listado a seguir executa esta tarefa
de tagging quando treinado sobre a seção "g" do Brown Corpus (a chamada
*belles lettres*, literatura criativa considerada em função de seu
conteúdo estético).
>>> cfdist = ConditionalFreqDist()
>>> for sentence in brown.tagged('g'):
... for token in sentence:
... word = token[0]
... tag = token[1]
... cfdist[word].inc(tag)
>>> for word in "John saw 3 polar bears".split():
... print word, cfdist[word].max()
John np
saw vbd
3 cd-tl
polar jj
bears vbz
Note que ``bears`` recebeu incorretamente a tag de "forma na terceira
pessoa do singular de um verbo", pois esta palavra é encontrada mais
freqüentemente como verbo que como substantivo na literatura de
fins estéticos.
Um problema desta abordagem é o fato dela resultar em um modelo extenso,
com uma entrada para cada combinação possível de palavra e tag. Para
certas tarefas, é possível construir modelos razoavelmente bem sucedidos
que, em comparação, são muito menores. Por exemplo, vamos tentar estimar
se uma palavra é um verbo, um substantivo ou um adjetivo analisando
apenas sua letra final. Podemos fazer isto da seguinte forma:
>>> tokens = []
>>> for sent in brown.tagged('g'):
... for (word,tag) in sent:
... if tag in ['nn', 'jj'] and len(word) > 3:
... char = word[-1]
... tokens.append((char,tag))
>>> split = len(tokens)*9/10
>>> train, test = tokens[:split], tokens[split:]
>>> cfdist = ConditionalFreqDist()
>>> for (char,tag) in train:
... cfdist[char].inc(tag)
>>> correct = total = 0
>>> for (char,tag) in test:
... if tag == cfdist[char].max():
... correct += 1
... total += 1
>>> print correct*100/total
71
Este resultado de 71% é marginalmente melhor que o resultado de 65% que
obteríamos ao atribuirmos a tag ``nn`` a todas as palavras. Podemos
inspecionar o modelo para ver qual tag é atribuída a cada palavra em função
de sua letra final. Desta forma, aprendemos que as palavras terminadas em
``c`` ou ``l`` têm uma probabilidade maior de serem adjetivos do que
substantivos.
>>> print [(c, cfdist[c].max()) for c in cfdist.conditions()]
[('%', 'nn'), ("'", None), ('-', 'jj'), ('2', 'nn'), ('5', 'nn'), ('A', 'nn'), ('D', 'nn'), ('O', 'nn'), ('S', 'nn'), ('a', 'nn'), ('c', 'jj'), ('b', 'nn'), ('e', 'nn'), ('d', 'nn'), ('g', 'nn'), ('f', 'nn'), ('i', 'nn'), ('h', 'nn'), ('k', 'nn'), ('m', 'nn'), ('l', 'jj'), ('o', 'nn'), ('n', 'nn'), ('p', 'nn'), ('s', 'nn'), ('r', 'nn'), ('u', 'nn'), ('t', 'nn'), ('w', 'nn'), ('y', 'nn'), ('x', 'nn'), ('z', 'nn')]
Explorando gêneros textuais
---------------------------
Agora que podemos carregar uma quantidade significativa de texto com
tags, é possível processá-lo e extrair deste informações de interesse. O
código a seguir interage nos quinze gêneros do Brown Corpus (acessados
por meio do método ``brown.groups()``). O material para cada gênero está
armazenado em um conjunto de arquivos (acessados por meio do método
``brown.items()``). Estes são toquenizados em série; a etepa seguinte é
verificar se cada toquen possui a tag ``md``. Para cada uma destas palavras,
incrementamos a cotnagem. Este método utiliza uma distribução de freqüência
condicional, na qual a condição é o gênero sendo analisado e o evento
o verbo modal.
>>> cfdist = ConditionalFreqDist()
>>> for genre in brown.items: # cada gênero
... for sent in brown.tagged(genre): # cada sentença
... for (word,tag) in sent: # cada toquen com tag
... if tag == 'md': # encontrado um verbo modal
... cfdist[genre].inc(word.lower())
A distribuição condicional de freqüência nada mais é que um mapeamento
entre cada gênero e a distribuição dos verbos modais em cada gênero. O
fragmento de código a seguir identifica um pequeno conjunto de verbos
modais de interesse e processa a estrutura de dados para exibir como
resultado as contagens requeridas.
>>> modals = ['can', 'could', 'may', 'might', 'must', 'will']
>>> print "%-40s" % 'Genre', ' '.join([("%6s" % m) for m in modals])
Genre can could may might must will
>>> for genre in cfdist.conditions(): # gera colunas
... print "%-40s" % brown.item_name[genre],
... for modal in modals:
... print "%6d" % cfdist[genre].count(modal),
... print
press: reportage 94 86 66 36 50 387
press: reviews 44 40 45 26 18 56
press: editorial 122 56 74 37 53 225
skill and hobbies 273 59 130 22 83 259
religion 84 59 79 12 54 64
belles-lettres 249 216 213 113 169 222
popular lore 168 142 165 45 95 163
miscellaneous: government & house organs 115 37 152 13 99 237
fiction: general 39 168 8 42 55 50
learned 366 159 325 126 202 330
fiction: science 16 49 4 12 8 16
fiction: mystery 44 145 13 57 31 17
fiction: adventure 48 154 6 58 27 48
fiction: romance 79 195 11 51 46 43
humor 17 33 8 8 9 13
Há alguns padrões interessantes nesta tabela. Por exemplo, compare as
colunas para literatura governamental ("miscellaneous: government &
house organs") e literatura de aventura ("fiction: adventure"); a primeira
é dominada pelo uso de ``can, may, must`` e ``will`` enquanto a última é
caracterizada pelo uso de ``could`` e ``might``. Com algum esforço adicional
é possível estimar o gênero de cada novo texto automaticamente, em função de
sua distribuição de verbos modais.
Agora que vimos como toquens com tags e corpora com tags são criados e
acessados, estamos prontos para dar uma olhada na categorização automática
de palavras.
-------------------
Leituras adicionais
-------------------
John Hopkins Center for Language and Speech Processing, 1999
Summer Workshop on Normalization of Non-Standard Words: Final Report
http://www.clsp.jhu.edu/ws99/projects/normal/report.pdf
Glossário do SIL de termos lingüísticos:
http://www.sil.org/linguistics/GlossaryOfLinguisticTerms/
Language Files: Materials for an Introduction to Language and
Linguistics (Eighth Edition), The Ohio State University Department of
Linguistics, http://www.ling.ohio-state.edu/publications/files/
----------
Exercícios
----------
#. Acessar e tokenizar um arquivo de texto: Obter algum texto puro (por
exemplo, visitando uma página da web e salvando seu conteúdo como
texto puro) e armazenar no arquivo 'corpus.txt'.
#. Utilizando os métodos ``open()`` e ``read()``, carregar o texto em uma
variável de tipo string e exibir seu conteúdo.
#. Now, initialize a new token with ``Token()``, using this text.
Tokenize the text with ``WhitespaceTokenizer``, and specify that the
result should be stored in the ``WORDS`` property. Print the
result.
#. Agora, inicialize um novo token com ``Token()``, utilizando este
texto. Toquenize o texto com o ``WhitespaceTokenizer`` e especifique que
o resultado deverá ser armazenado na propriedade ``WORDS``. Exiba o
resultado.
#. A seguir, calcule o numero de toquens utilizando a função ``len()`` e
exiba o resultado.
#. Finally, discuss shortcomings of this method for tokenizing text.
In particular, identify any material which has not been correctly
tokenized. (You may need to look for a more complex text.)
#. Finalmente, discuta as falhas deste método de toquenização de textos.
Em especial, identifique qualquer conteúdo que não tenha sido
corretamente toquenizado (talvez seja necessário utilizar algum texto
mais complexo).
#. Toquenizar um texto utilizando expressões regulares:
Obtenha algum texto puro (por exemplo, visitando uma página da web e
salvando seu conteúdo como texto puro) e armazene-o no arquivo
'corpus.txt' para que possas responder às questões seguintes.
#. Processadores de texto normalmente dividem em sílabas as palavras
ao final das linhas (nas chamadas "quebra de linha"). Quando um documento
processado por estes programas é convertido para texto puro, estas partes
geralmente não são recombinadas. É fácil reconhecer estes casos procurando
na web por palavras divididas, como ``depart- ment``. Crie um
``RegexpTokenizer`` que trate este tipo de palavra quebrada como um
token único.
#. Considere o seguinte título de um livro: *This Is the Beat Generation:
New York-San Francisco-Paris* ("Esta é a Geração Beat: Nova Iorque-São
Francisco-Paris"). O que seria necessário para poder toquenizar este tipo
de string de tal forma que cada nome de cidade fosse armazenado como
um único toquen?
#. Concordância:
Escreva uma função que receba uma palavra como argumento, e que procure
no Brown Corpus por ocorrências desta palavra. Para cada ocorrncia,
gere uma linha de texto que contenha a palavra em questão em seu centro.
#. Lei de Zipf:
Seja *f(w)* a freqüência de uma palavra *w* em um texto livre. Suponha
que todas as palavras do texto estão classificadas de acordo com suas
freqüências, da mais freqüênte à menos freqüente. A Lei de Zipf diz que
a freqüência de um tipo de palavra é inversamente proporcional à sua
classificação (ou seja, f*r=k, para alguma constante k). Por exemplo, a
quinquagésima (50a) palavra mais comum deverá ocorrer três vezes mais que
a centésimaquinquagésima (150a).
#. Escreva uma função em Python para processar um longo texto e plote a
freqüência das palavras em relação à sua classificação utilizando o
módulo nltk.draw.plot. És capaz de confirmar a Lei de Zipf?
(Sugestão: é conveniente ajustar os eixos para log-log) O que ocorre nas
extremidades das linhas plotadas?
#. Gere um texto aleatório, por exemplo utilizando
``random.choice("abcdefg ")``, lembrando de incluir o caractere de
espaço. Utilize o operador de concatenação de strigns para acumular os
caracteres em uma (realmente) longa string. A seguir, toquenize esta
string e gere um gráfico Zipf como acima, comparando os dois gráficos.
O que pode-se deduzir a respeito da Lei de Zipf a partir disto?
#. Trabalhando com texto com tags: Escreva um programa que carregue o
Brown Corpus e, dada uma palavra, liste todas as tags possíveis para
esta e suas contagens de freqüência. Por exemplo, para a palavra ``strike``
o programa deverá gerar: ``[('nn', 25), ('vb', 21)]``. (Sugestão: esta
tarefa envolve reverter e ordenar a lista de tuples que estará na
forma ``[(21, 'vb'), (25, 'nn')]``. Para converter estas listas à forma
requerida, use ``word_freq = [(y,x) for (x,y) in freq_word]``.)
#. Use seu programa para exibir as tags e suas freqüências para as
seguintes palavras: ``can, fox, get, lift, like, but, frank, line,
interest``. Assegure-se de que você conhece o significado de cada uma
das tags mais freqüentes.
#. Escreva um programa para encontrar as 20 palavras que possuem as
maiores variedades de tags.
#. Escolha palavras que podem ser tanto substantivos quanto verbos
(como ``deal``). Tente adivinhar qual é a tag mais provável para cada
palavra e confira se você está certo.
#. Prevendo a próxima palavra: o programa para previsão de palavras que
vimos neste capítulo prende-se rapidamente em ciclos. Modifique o programa
de forma que cada palavra seja escolhida aleatoriamente entre uma lista
das *n* palavras mais prováveis no contexto dado. (Sugestão: armazene as
*n* palavras mais prováveis em uma lista ``lwords`` e então escolha
aleatoriamente uma palavra a partir desta lista com o método
``random.choice()``)
#. Escolha um gênero específico, como uma seção do Brown Corpus, uma
tradução do livro do Gênesis ou um dos corpora de grupos de
discussão ("newsgroups"). Treine seu sistema neste corpus e faça-o
gerar texto aleatoriamente. Você pode experimentar com diferentes
palavras iniciais. O texto resultante é intelegível? Examine os pontos
fortes e fracos deste método para geração aleatória de texto.
#. Repita a experiência com diferentes gêneros e com diferentes
quantidades de dados para treinamento. O que você pode observar?
#. Agora, treine seu sistema utilizando dois gêneros distintos e
experimente com a geração de texto de um gênero híbrido. Como na
questão anterior, discuta sobre suas observações.
#. Classificação automática de palavras: o programa para a classificação
de palavras como substantivos ou adjetivos obteve um índice de acerto de
71%. Vamos tentar criar condições melhores para obter um sistema com
índice de acerto de 80% ou mais.
#. Considere a alternativa de utilizar um sufixo mais longo, como por
exemplo os últimos dois ou três caracteres. O que acontece com o
desempenho? Quais sufixos são reconhecidos como pertinentes aos
adjetivos?
#. Explore outras alternativas, como utilizar um comprimento variável
de prefixos, o comprimento das próprias palavras ou o número de vogais
em uma palavra.
#. Por último, combine as múltiplas condições em um tuple e explore
quais combinações de condições fornecem os melhores resultados.
#. Escreva um programa para implementar um ou mais testes de legibilidade
(http://en.wikipedia.org/wiki/Readability, em inglês).
#. Projete um algoritmo que encontre as frases estatisticamente improváveis
de uma coleção de documentos.
http://www.amazon.com/gp/search-inside/sipshelp.html/
----
NLTK_
.. _NLTK: http://nltk.sourceforge.net/