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