Es:Tag
From NLTK
.. -*- mode: rst -*- .. include:: ../definitions.txt
.. standard global imports
>>> import nltk, re, pprint
.. _chap-tag:
=====================
4. Categorización y etiquetado de las palabras Traducción al español: Ana Useros Norell
=====================
.. TODO: exercise on cascaded tagging .. TODO: simplified tagset (N, V, J, A) -- WordNet .. TODO: better approach to tagging unknown words using special UNK token .. TODO: motivate trigram tagging by showing some cases where bigram tagging doesn't work .. TODO: xref to unicode section in prog chapter .. TODO: * outstanding problems:
- what are we doing with ConditionalFreqDist? - nltk.tag contains all of math library - nltk.corpus.brown.tagged_sents() is too verbose? - tag method returns generator object?
Introducción
En el capítulo chap-words_, hemos tratado las palabras propiamente dichas. Hemos estudiado la distribución de la palabra `often`:lx: y hemos identificado las palabras que la siguen. Hemos observado que `often`:lx: a menudo modifica el sentido de los verbos. De hecho, forma parte de toda una clase de palabras que modifican al verbo: los *adverbios*. Antes de meternos de lleno en esta terminología, vamos a escribir un programa que, a partir de una palabra dada, encuentre otras palabras que aparezcan en el mismo contexto (Listado dist_sim_). Así, por ejemplo, si introducimos la palabra `woman`:lx: (mujer), el programa buscará todos los contextos del corpus donde aparezca la palabra `woman`:lx:, como, por ejemplo, `the woman saw`:lx: (la mujer vio) y, a continuación, buscará otras palabras que aparezcan en estos contextos.
Si ejecutamos ``dist_sim()`` para un número determinado de palabras, obtendremos otras palabras con una distribución similar: si buscamos `woman`:lx: (mujer) encontraremos `man`:lx: (hombre) y algunos otros *nombres*; si buscamos `bought`:lx: (compró) encontraremos *verbos*; si buscamos `over`:lx: (sobre), encontraremos *preposiciones* y si buscamos `the`:lx: (el/la) encontraremos *determinantes*. Estas etiquetas, que quizá recordemos de alguna clase de gramática, no son meros términos inventados por los gramáticos, sino que se trata de etiquetas utilizadas para designar conjuntos de palabras que derivan directamente del texto. Estos conjuntos de palabras son tan importantes que reciben nombres diversos, todos ellos comúnmente utilizados: `clases de palabras`:dt:, `categorías léxicas`:dt: y `partes de la oración`:dt:. Utilizaremos cualquiera de estos términos indistintamente.
.. .. doctest-ignore:: .. pylisting:: dist_sim
:caption: Programa para calcular la similitud distribucional
def build_wc_map():
"""
Return a dictionary mapping words in the brown corpus to lists of
local lexical contexts, where a context is encoded as a tuple
(prevword, nextword).
"""
wc_map = nltk.defaultdict(list)
words = [word.lower() for word in nltk.corpus.brown.words()]
for i in range(1, len(words)-1):
prevword, word, nextword = words[i-1:i+2]
wc_map[word].append( (prevword, nextword) )
return wc_map
def dist_sim(wc_map, word, num=12):
if word in wc_map:
contexts = set(wc_map[word])
fd = nltk.FreqDist(w for w in wc_map for c in wc_map[w] if c in contexts)
return fd.sorted()[:num]
return []
>>> wc_map = build_wc_map()
>>> dist_sim(wc_map, 'woman')
['man', 'number', 'woman', 'world', 'time', 'end', 'house', 'state',
'matter', 'kind', 'result', 'day']
>>> dist_sim(wc_map, 'bought')
['able', 'made', 'been', 'used', 'found', 'was', 'had', 'bought', ',',
'done', 'expected', 'given']
>>> dist_sim(wc_map, 'over')
['in', 'over', 'and', 'of', 'on', 'to', '.', ',', 'with', 'at', 'for', 'but']
>>> dist_sim(wc_map, 'the')
['the', 'a', 'his', 'this', 'and', 'in', 'their', 'an', 'her', 'that', 'no', 'its']
Una de las características más destacadas del Brown Corpus es que todas las palabras han sido `etiquetadas`:dt: según su categoría léxica. Ahora, en lugar de limitarnos a observar qué palabras aparecen inmediatamente después de `often`:lx:, podemos estudiar las `etiquetas léxicas`:dt: (o `etiquetas POS`:dt:, del inglés part of speech -parte de la oración) de dichas palabras. En la Tabla often_ se enumeran las primeras ocho etiquetas, por orden de frecuencia, junto con una explicación para cada etiqueta. Como podemos ver, la mayoría de las palabras que siguen a la palabra `often`:lx: son verbos.
.. table:: often
======== ==== ================================== =================================== Etiqueta Frec. Ejemplo Comentario ======== ==== ================================== =================================== vbn 61 `burnt`:lx: (quemado), `gone`:lx: (ido) verbo: participio vb 51 `make`:lx: (hacer), `achieve`:lx: (conseguir) verbo: forma básica vbd 36 `saw`:lx: (vio), `looked`:lx: (miró) verbo: pasado simple jj 30 `ambiguous`:lx: (ambiguo), `acceptable`:lx: (aceptable) adjetivo vbz 24 `sees`:lx: (ve), `goes`:lx: (va) verbo: tercera pers. sing. del presente in 18 `by`:lx: (por), `in`:lx: (en) preposición at 18 `a`:lx: (un/a), `this`:lx: (este/a) artículo , 16 `,`:lx: coma ====== ==== ================================== ===================================
Etiquetas léxicas que siguen a la palbra `often`:lx: en el
Brown Corpus
El proceso de clasificar las palabras según su categoría léxica y etiquetarlas según dicha clasificación se conoce como `etiquetado léxico`:dt:, `etiquetado POS`:dt:, `etiquetado morfosintáctico`:dt: o simplemente `etiquetado`:dt:. Las etiquetas empleadas para una determinada tarea reciben el nombre de `conjunto de etiquetas`:dt:. En este capítulo queremos centrarnos en el uso de las etiquetas y en el etiquetado automático de textos.
El etiquetado automático tiene diversas aplicaciones. Ya hemos visto un ejemplo de cómo utilizar las etiquetas en el análisis de corpus: podemos comprender con claridad la distribución de `often`:lx: observando las etiquetas de las palabras adyacentes. El etiquetado automático también contribuye a predecir el comportamiento de palabras desconocidas hasta ese momento. Por ejemplo, si nos encontramos con la palabra `blogging`:lx: probablemente podamos deducir que se trata de un verbo, cuya raíz es `blog`:lx:, que, generalmente, aparecerá precedido de alguna forma del verbo auxiliar ``to be`` (por ejemplo `he was blogging`:lx:). Las clases de palabras también se utilizan en la síntesis y el reconocimiento del habla. Por ejemplo, ``wind/NN`` (viento), como en `the wind blew`:lx: (soplaba el viento), se pronuncia con una vocal corta, minetras que ``wind/VB`` (girar, dar cuerda), como en `to wind the clock`:lx: (dar cuerda al reloj), se pronuncia con una vocal larga. Podemos encontrar otros ejemplos en los que la acentuación de una palabra varía en función de si ésta funciona como nombre o como verbo, como es el caso de `contest`:lx: (concurso/protestar), `insult`:lx: (insulto/insultar), `present`:lx: (presente/presentar), `protest`:lx: (protesta/protestar), `rebel`:lx: (rebelde/rebelarse), `suspect`:lx: (sospechoso/suspechar). Si no conocemos la categoría léxica de estas palabras, no podemos tener la seguridad de estar pronunciándolas correctamente.
En el siguiente apartado, veremos cómo podemos acceder y examinar el Brown Corpus. A continuación, estudiaremos más detenidamente la lingüística de las clases de palabras. El resto del capítulo lo dedicaremos al etiquetado automático: etiquetadores simples, evaluación y etiquetadores basados en n-gramas.
.. note:: Recuerda que los ejemplos de programas que exponemos parten de que inicias tu sesión interactiva o programa con: ``import nltk, re, pprint``
Introducción al etiquetado
Algunos grandes corpus, como el Brown Corpus y parte del Wall Street Journal, se han etiquetado con categorías léxicas y son capaces de procesar estos datos etiquetados. El texto contenido en los archivos de corpus etiquetados normalmente presentan el siguiente formato (este ejemplo es del 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 / ./.
.. note:: El lector del Brown Corpus de NLTK pone las etiquetas léxicas en mayúsculas, dado que se ha convertido en una práctica generalizada desde la publicación del Brown Corpus.
Representar etiquetas y leer corpus etiquetados
Por convención en NLTK, las formas (o tokens) etiquetadas se representan mediante el uso de `tuplas`:dt:. En Python, las tuplas son como las listas, excepto por una diferencia importante: las tuplas no se pueden modificar, por ejemplo mediante ``sort()`` or ``reverse()``. En otras palabras, son invariables, al igual que las cadenas. Las tuplas se construyen con el operador coma y se escriben entre paréntesis. Al igual que las listas, las tuplas se pueden indexar y cortar:
.. doctest-ignore::
>>> t = ('walk', 'fem', 3)
>>> t[0]
'walk'
>>> t[1:]
('fem', 3)
>>> t[0] = 'run'
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object does not support item assignment
Las formas etiquetadas se representan mediante el empleo de tuplas formadas por tan sólo dos elementos. Podemos crear una de estas tuplas especiales a partir de la habitual representación en cadenas de una forma etiquetada, utilizando la función ``str2tuple()``:
>>> tagged_token = nltk.tag.str2tuple('fly/NN')
>>> tagged_token
('fly', 'NN')
>>> tagged_token[0]
'fly'
>>> tagged_token[1]
'NN'
Podemos construir una lista de formas etiquetadas directamente a partir de una cadena. El primer paso es tokenizar las cadenas (o identificar los tokens o formas de las cadenas), para acceder a las cadenas individuales de ``palabra/etiqueta`` strings y luego transformar cada una de estas cadenas en una tupla (usando ``str2tuple()``). Existen dos métodos para esto. El primero, que comienza en la línea str2tuple1_, inicializa una lista vacía ``tagged_words``, recorre todas las formas ``palabra/etiqueta`` las convierte en tuplas, las adjunta a ``tagged_words`` y, finalmente, muestra el resultado. El segundo método, que empieza a partir de la línea str2tuple2_, utiliza una comprensión de listas para llegar al mismo resultado mediante un método que no sólo es más compacto, sino también más legible (la comprensión de listas se introdujo en el apartado sec-list-comprehensions_).
>>> sent =
... 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 / ./.
...
>>> tagged_words = [] # [_str2tuple1]
>>> for t in sent.split():
... tagged_words.append(nltk.tag.str2tuple(t))
>>> tagged_words
[('The', 'AT'), ('grand', 'JJ'), ('jury', 'NN'), ('commented', 'VBD'),
('on', 'IN'), ('a', 'AT'), ('number', 'NN'), ... ('.', '.')]
>>> [nltk.tag.str2tuple(t) for t in sent.split()] # [_str2tuple2]
[('The', 'AT'), ('grand', 'JJ'), ('jury', 'NN'), ('commented', 'VBD'),
('on', 'IN'), ('a', 'AT'), ('number', 'NN'), ... ('.', '.')]
Podemos acceder a varios corpus etiquetados directamente desde Python. Si un corpus contiene texto etiquetado, contará con un método``tagged_words()``. Consulta el archivo ``README`` incluido en cada corpus para obtener información sobre su conjunto de etiquetas.
>>> nltk.corpus.brown.tagged_words()
[('The', 'AT'), ('Fulton', 'NP-TL'), ...]
>>> print nltk.corpus.nps_chat.tagged_words()
[('now', 'RB'), ('im', 'PRP'), ('left', 'VBD'), ...]
>>> nltk.corpus.conll2000.tagged_words()
[('Confidence', 'NN'), ('in', 'IN'), ('the', 'DT'), ...]
>>> nltk.corpus.treebank.tagged_words()
[('Pierre', 'NNP'), ('Vinken', 'NNP'), (',', ','), ...]
Con NLTK, se distribuyen corpus etiquetados para otros idiomas además del inglés, entre los que se incluyen el chino, el hindi, el portugués, el español, el neerlandés y el catalán. Normalmente, contienen texto no ASCII y Python siempre lo muestra en hexadecimal al imprimir estructuras más grandes como, por ejemplo, una lista.
>>> nltk.corpus.sinica_treebank.tagged_words()
[('\xe4\xb8\x80', 'Neu'), ('\xe5\x8f\x8b\xe6\x83\x85', 'Nad'), ...]
>>> nltk.corpus.indian.tagged_words()
[('\xe0\xa6\xae\xe0\xa6\xb9\xe0\xa6\xbf\xe0\xa6\xb7\xe0\xa7\x87\xe0\xa6\xb0', 'NN'),
('\xe0\xa6\xb8\xe0\xa6\xa8\xe0\xa7\x8d\xe0\xa6\xa4\xe0\xa6\xbe\xe0\xa6\xa8', 'NN'), ...]
>>> nltk.corpus.mac_morpho.tagged_words()
[('Jersei', 'N'), ('atinge', 'V'), ('m\xe9dia', 'N'), ...]
>>> nltk.corpus.conll2002.tagged_words()
[('Sao', 'NC'), ('Paulo', 'VMI'), ('(', 'Fpa'), ...]
>>> nltk.corpus.cess_cat.tagged_words()
[('El', 'da0ms0'), ('Tribunal_Suprem', 'np0000o'), ...]
Si tu entorno está correctamente configurado, con los editores y fuentes adecuados, deberías poder visualizar las cadenas individuales de forma que resulten legibles para el ser humano. Por ejemplo, en la Figura tag-indian_ se muestra la salida del código demo(``nltk.corpus.indian.demo()``).
.. _tag-indian: .. figure:: ../images/tag-indian.png
:scale: 60
Datos con etiquetado léxico en cuatro idiomas de India
.. इराक_NNP के_PREP विदेश_NNC मंतà¥à¤°à¥€_NN ने_PREP अमरीका_NNP के_PREP उस_PRP पà¥à¤°à¤¸à¥à¤¤à¤¾à¤µ_NN का_PREP मजाक_NVB उड़ाया_VFM है_VAUX ...
Si el corpus también se encuentra segmentado por oraciones, dispondrá de un método ``tagged_sents()`` que devuelve una lista de oraciones etiquetadas. Esto será de gran utilidad cuando lleguemos al entrenamiento de etiquetadores automáticos, ya que éstos generalmente llevan a cabo el etiquetado del texto oración por oración.
Nombres y verbos
Linguists recognize several major categories of words in English, such as nouns, verbs, adjectives and determiners. In this section we will discuss the most important categories, namely nouns and verbs.
Nouns generally refer to people, places, things, or concepts, e.g.: `woman, Scotland, book, intelligence`:lx:. Nouns can appear after determiners and adjectives, and can be the subject or object of the verb, as shown in Table syntax-nouns_.
.. table:: syntax-nouns
============ ============================================= =================================== Word After a determiner Subject of the verb ============ ============================================= =================================== 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 colonization of Australia intelligence *the* intelligence displayed by the child ... Mary's intelligence *impressed* her teachers ============ ============================================= ===================================
Syntactic Patterns involving some Nouns
Nouns can be classified as `common nouns`:dt: and `proper nouns`:dt:. Proper nouns identify particular individuals or entities, e.g. `Moses`:lx: and `Scotland`:lx:. Common nouns are all the rest. Another distinction exists between `count nouns`:dt: and `mass nouns`:dt:. Count nouns are thought of as distinct entities that can be counted, such as `pig`:lx: (e.g. `one pig, two pigs, many pigs`:lx:). They cannot occur with the word `much`:lx: (i.e. \*\ `much pigs`:lx:). Mass nouns, on the other hand, are not thought of as distinct entities (e.g. `sand`:lx:). They cannot be pluralized, and do not occur with numbers (e.g. \*\ `two sands`:lx:, \*\ `many sands`:lx:). However, they can occur with `much`:lx: (i.e. `much sand`:lx:).
Verbs are words that describe events and actions, e.g. `fall`:lx:, `eat`:lx: in Table syntax-verbs_. In the context of a sentence, verbs express a relation involving the referents of one or more noun phrases.
.. table:: syntax-verbs
===== =============== =============================================== Word Simple With modifiers and adjuncts (italicized) ===== =============== =============================================== fall Rome fell Dot com stocks *suddenly* fell *like a stone* eat Mice eat cheese John ate the pizza *with gusto* ===== =============== ===============================================
Syntactic Patterns involving some Verbs
`\pagebreak`:raw-latex:
Verbs can be classified according to the number of arguments (usually noun phrases) that they require. The word `fall`:lx: is `intransitive`:dt:, requiring exactly one argument (the entity that falls). The word `eat`:lx: is `transitive`:dt:, requiring two arguments (the eater and the eaten). Other verbs are more complex; for instance `put`:lx: requires three arguments, the agent doing the putting, the entity being put somewhere, and a location. We will return to this topic when we come to look at grammars and parsing (see Chapter chap-parse_).
In the Brown Corpus, verbs have a range of possible tags, e.g.: ``give/VB`` (present), ``gives/VBZ`` (present, 3ps), ``giving/VBG`` (present continuous; gerund) ``gave/VBD`` (simple past), and ``given/VBN`` (past participle). We will discuss these tags in more detail in a later section.
Nouns and Verbs in Tagged Corpora
Now that we are able to access tagged corpora, we can write simple programs to garner statistics about the tags. In this section we will focus on the nouns and verbs.
What are the 10 most common verbs? We can write a program to find all words tagged with `VB`:gc:, `VBZ`:gc:, `VBG`:gc:, `VBD`:gc: or `VBN`:gc:.
>>> fd = nltk.FreqDist()
>>> for (wd, tg) in nltk.corpus.brown.tagged_words(categories='a'):
... if tg[:2] == 'VB':
... fd.inc(wd + "/" + tg)
>>> fd.sorted()[:20]
['said/VBD', 'get/VB', 'made/VBN', 'United/VBN-TL', 'take/VB',
'took/VBD', 'told/VBD', 'made/VBD', 'make/VB', 'got/VBD',
'came/VBD', 'go/VB', 'see/VB', 'went/VBD', 'given/VBN',
'expected/VBN', 'began/VBD', 'give/VB', 'taken/VBN', 'play/VB']
Let's study nouns, and find the most frequent nouns of each noun part-of-speech type. The program in Listing findtags_ finds all tags starting with `NN`:gc:, and provides a few example words for each one. Observe that there are many noun tags; the most important of these have ``$`` for possessive nouns, `S`:gc: for plural nouns (since plural nouns typically end in `s`:lx:), `P`:gc: for proper nouns.
.. pylisting:: findtags
:caption: Program to Find the Most Frequent Noun Tags
def findtags(tag_prefix, tagged_text):
cfd = nltk.ConditionalFreqDist()
for (wd, tg) in tagged_text:
if tg.startswith(tag_prefix):
cfd[tg].inc(wd)
tagdict = {}
for tg in cfd.conditions():
tagdict[tg] = cfd[tg].sorted()[:5]
return tagdict
>>> tagdict = findtags('NN', nltk.corpus.brown.tagged_words(categories='a'))
>>> for tg in sorted(tagdict):
... print tg, tagdict[tg]
NN ['year', 'time', 'state', 'week', 'man']
NN$ ["year's", "world's", "state's", "nation's", "company's"]
NN$-HL ["Golf's", "Navy's"]
NN$-TL ["President's", "University's", "League's", "Gallery's", "Army's"]
NN-HL ['cut', 'Salary', 'condition', 'Question', 'business']
NN-NC ['eva', 'ova', 'aya']
NN-TL ['President', 'House', 'State', 'University', 'City']
NN-TL-HL ['Fort', 'City', 'Commissioner', 'Grove', 'House']
NNS ['years', 'members', 'people', 'sales', 'men']
NNS$ ["children's", "women's", "men's", "janitors'", "taxpayers'"]
NNS$-HL ["Dealers'", "Idols'"]
NNS$-TL ["Women's", "States'", "Giants'", "Officers'", "Bombers'"]
NNS-HL ['years', 'idols', 'Creations', 'thanks', 'centers']
NNS-TL ['States', 'Nations', 'Masters', 'Rules', 'Communists']
NNS-TL-HL ['Nations']
Some tags contain a plus sign; these are compound tags, and are assigned to words that contain two parts normally treated separately. Some tags contain a minus sign; this indicates disjunction.
The Default Tagger
El etiquetador más simple posible asigna una misma etiqueta a cada forma o token. Puede parecer una tarea un tanto banal, pero permite establecer un importante valor de referencia para el rendimiento del etiquetador. Para obtener el mejor resultado posible, anotaremos cada palabra con la etiqueta más probable (este tipo de etiquetador se denomina también `clasificador de categoría mayoritaria`:dt: o `majority class classifier`:dt:).
¿Cuál es, pues, la etiqueta más frecuente? Podemos averiguarlo usando un programa muy sencillo:
>>> fd = nltk.FreqDist() >>> for (wd, tg) in nltk.corpus.brown.tagged_words(categories='a'): ... fd.inc(tg) >>> fd.max() 'NN'
Ahora podemos crear un etiquetador, al que llamaremos ``default_tagger``, que asigne a todas las formas la etiqueta `NN`:gc:.
>>> tokens = 'John saw 3 polar bears .'.split()
>>> default_tagger = nltk.DefaultTagger('NN')
>>> default_tagger.tag(tokens)
[('John', 'NN'), ('saw', 'NN'), ('3', 'NN'), ('polar', 'NN'),
('bears', 'NN'), ('.', 'NN')]
Se trata de un algoritmo muy simple, y no da buenos resultados si se usa solo. En un corpus normal, sólo etiquetaría correctamente alrededor de una octava parte de las formas:
>>> nltk.tag.accuracy(default_tagger, nltk.corpus.brown.tagged_sents(categories='a')) 0.13089484257215028
Los etiquetadores por defecto asignan su etiqueta a todas y cada una de las palabras, incluso a aquellas que aparecen por primera vez. Sin embargo, la mayoría de las palabras nuevas son nombres y, por eso, los etiquetadores por defecto contribuyen a incrementar la solidez de los sistemas de procesamiento del lenguaje. Volveremos a tratarlos más adelante, cuando estudiemos el método del *backoff*.
Ejercicios
- . |easy|
Trabaja con un compañero y, por turnos, elegid palabras que puedan funcionar indistintamente como sustantivos o como verbos (por ejemplo, `contest`:lx:). La persona que no haya elegido la palabra deberá predecir cuál de las dos categorías es más frecuente para esa palabra en el Brown Corpus. Comprobad si está en lo cierto y anotad los aciertos de cada uno. Repetid el ejercicio varias veces.
- . |soso| 2. Escribe programas para el procesamiento del Brown Corpus y encuentra las respuestas a las siguientes preguntas:
#) ¿Qué nombres aparecen con más frecuencia en plural que en singular? (Incluye sólo los plurales regulares, formados con el sufijo `-s`:lx:) #) ¿Qué palabra tiene el mayor número de etiquetas distintas? ¿Cuáles son esas etiquetas y qué representa cada una de ellas? #) Ordena las etiquetas de mayor a menor frecuencia. ¿Qué designan las 20 etiquetas más frecuentes? #) ¿Qué etiquetas preceden con mayor frecuencia a los nombres? ¿Qué representan estas etiquetas?
- . |soso| Genera estadísticas sobre los datos etiquetados para responder a las siguientes preguntas:
a) ¿Cuál es la proporción de tipos de palabras a las que siempre se les asigna la misma etiqueta léxica? #) ¿Cuántas palabras son ambiguas, en el sentido de que aparecen con al menos dos etiquetas? #) ¿Qué porcentaje de las formas/palabras incluidas en el Brown Corpus son palabras ambiguas?
- . |soso| Arriba hemos puesto un ejemplo de la función ``nltk.tag.accuracy()``. Esta función tiene dos argumentos, un etiquetador y un fragmento de texto etiquetado, y calcula el nivel de acierto del etiquetador en el texto dado. Si, por ejemplo, el fragmento de texto proporcionado fuera ``[('the', 'DT'), ('dog', 'NN')]`` y el etiquetador mostrara el resultado ``[('the', 'NN'), ('dog', 'NN')]``, el nivel de acierto sería de ``0,5``.
¿Sabrías explicar cómo funciona la función ``nltk.tag.accuracy()``? a) Los etiquetadotes toman, como entrada, una lista de palabras y producen, como salida, una lista de palabras etiquetadas. Sin embargo, si a la función ``nltk.tag.accuracy()``se le proporciona un fragmento de texto correctamente etiquetado, ¿qué tiene que hacer la función ``nltk.tag.accuracy()`` con estos datos de entrada antes de llevar a cabo el etiquetado? b) Después de que el etiquetador proporcionado haya generado el nuevo texto etiquetado, ¿qué haría ``nltk.tag.accuracy()`` para compararlo con el texto etiquetado inicial y calcular el nivel de acierto?
Reconocimiento de patrones léxicos
Un poco de morfología
Los nombres en inglés pueden ser morfológicamente complejos. Por ejemplo, las palabras como `books`:lx: y`women`:lx: son plurales. Las palabras formadas con el sufijo `-ness`:lx: son nombres derivados de adjetivos, como, por ejemplo `happiness`:lx: o `illness`:lx:. El sufijo `-ment`:lx: aparece en algunos nombres derivados de verbos, como, por ejemplo, `government`:lx: o `establishment`:lx:.
Los verbos ingleses también pueden ser morfológicamente complejos. El `participio presente`:dt: de un verbo, por ejemplo, termina en `-ing`:lx: y se utiliza para hacer referencia a acciones incompletas, que aún se están produciendo (por ejemplo, `falling`:lx:, `eating`:lx:). El sufijo `-ing`:lx: también aparece en algunos nombres derivados de verbos, como, por ejemplo, en `the falling of the leaves`:lx: (es lo que se conoce como `gerundio`:dt:). En el Brown Corpus, estas palabras se anotan con la etiqueta `VBG`:gc:.
El `participio pasado`:dt: de un verbo suele terminar en `-ed`:lx: y hace referencia a una acción terminada (como, por ejemplo, `walked`:lx:, `cried`:lx:). Este tipo de palabras se anotan con la etiqueta `VBD`:gc:.
.. TODO: Modal verbs, e.g. `would`:lx: ...
Los conjuntos de etiquetas más comunes reflejan parte de la información `morfosintáctica`:dt:, es decir, información sobre el tipo de marcas morfológicas que presentan las palabras según su función sintáctica. Observa, por ejemplo, las distintas variantes gramaticales de la palabra `go`:lx: que se presentan en los siguientes ejemplos:
.. _go: .. ex::
.. ex:: *Go* away! .. ex:: He sometimes *goes* to the cafe. .. ex:: All the cakes have *gone*. .. ex:: We *went* on the excursion.
Cada una de las formas de los ejemplos (`go`:lx:, `goes`:lx:, `gone`:lx:, y `went`:lx:) se distingue morfológicamente de las demás. Tomemos, por ejemplo, la forma `goes`:lx:. Tsta forma del verbo no puede aparecer en cualquier contexto gramatical, sino que requiere un sujeto en tercera persona de singular. Por eso, las siguientes oraciones son agramaticales:
.. ex::
.. ex:: \*They sometimes *goes* to the cafe. .. ex:: \*I sometimes *goes* to the cafe.
Por otra parte, `gone`:lx: es la forma de participio pasado. Debe aparecer detrás de `have`:lx: (sin que pueda ser reemplazada en este contexto por la forma `goes`:lx:) y no puede funcionar como verbo principal de una oración.
.. ex::
.. ex:: \*All the cakes have *goes*. .. ex:: \*He sometimes *gone* to the cafe.
Podemos imaginar un conjunto de etiquetas en el que las cuatro variantes gramaticales que acabamos de estudiar se anotaran con la etiqueta VB. Aunque esto puede resultar útil para determinados fines, un conjunto de etiquetas más riguroso proporcionará información útil sobre las distintas variantes, información que puede ser de gran utilidad para otros procesadores que traten de identificar patrones sintácticos a partir de las secuencias de etiquetas. Como señalamos al principio de este capítulo, el conjunto de etiquetas del Brown Corpus distingue, de hecho, las cuatro variantes presentadas, según se resume en la Tabla morphosyntax_.
.. table:: morphosyntax
======= ====================== ===== Variante Categoría Etiqueta ======= ====================== ===== go base VB goes 3ª pers. del singular del presente VBZ gone participio de pasado VBN going gerundio VBG went pasado simple VBD ======= ====================== =====
Algunas distinciones morfosintácticas en el conjunto de etiquetas del Brown Corpus
Además de este conjunto de etiquetas relativas a los verbos, las diversas formas del verbo `to be`:lx: se anotan con etiquetas especiales: ``be/BE, being/BEG, am/BEM, been/BEN`` y``was/BEDZ``. Con todas sus variantes, este riguroso etiquetado de los verbos implica que los etiquetadores automáticos que utilizan este conjunto de etiquetas llevan a cabo en la práctica un pequeño análisis morfológico.
La mayoría de los conjuntos de etiquetas léxicas utiliza las mismas categorías básicas, como nombre, verbo, adjetivo y preposición. Sin embargo, los distintos conjuntos de etiquetas difieren en el nivel de especificidad de las categorías en las que dividen las palabras y en cómo definen cada categoría. Por ejemplo, es posible que la forma `is`:lx: se anote simplemente como verbo en un determinado conjunto de etiquetas pero que en otro se etiquete como una variante del lexema `BE`:lx: (como es el caso del Brown Corpus). Estas variaciones entre los distintos conjuntos de etiquetas son inevitables, ya que las etiquetas léxicas se usan de forma distinta según los fines con los que se utilicen. En otras palabras, no existe una "forma correcta" de asignar etiquetas, sino únicamente sistemas de etiquetado más o menos útiles según los objetivos perseguidos. Podrás encontrar más detalles acerca del conjunto de etiquetas del Brown Corpus en el `Anexo`_ que aparece al final de este capítulo.
.. _sec-regular-expression-tagger:
Etiquetadores basados en expresiones regulares
Los etiquetadotes basados en expresiones regulares asignan etiquetas a las formas basándose en patrones coincidentes. Por ejemplo, podemos aventurar que cualquier palabra que termine en `ed`:lx: es el participio de pasado de un verbo y que cualquier palabra terminada en `'s`:lx: es un nombre posesivo (con genitivo sajón). Esto lo podemos expresar como una lista de expresiones regulares:
>>> patterns = [ ... (r'.*ing$', 'VBG'), # gerunds ... (r'.*ed$', 'VBD'), # simple past ... (r'.*es$', 'VBZ'), # 3rd singular present ... (r'.*ould$', 'MD'), # modals ... (r'.*\'s$', 'NN$'), # possessive nouns ... (r'.*s$', 'NNS'), # plural nouns ... (r'^-?[0-9]+(.[0-9]+)?$', 'CD'), # cardinal numbers ... (r'.*', 'NN') # nouns (default) ... ]
Esta lista se procesa por orden y se aplica la primera etiqueta que coincida.
Ahora podemos configurar un etiquetador y utilizarlo para asignar etiquetas a un texto.
>>> regexp_tagger = nltk.RegexpTagger(patterns)
>>> regexp_tagger.tag(nltk.corpus.brown.sents(categories='a')[3])
[('``', 'NN'), ('Only', 'NN'), ('a', 'NN'), ('relative', 'NN'),
('handful', 'NN'), ('of', 'NN'), ('such', 'NN'), ('reports', 'NNS'),
('was', 'NNS'), ('received', 'VBD'), ("", 'NN'), (',', 'NN'),
('the', 'NN'), ('jury', 'NN'), ('said', 'NN'), (',', 'NN'), ('``', 'NN'),
('considering', 'VBG'), ('the', 'NN'), ('widespread', 'NN'), ..., ('.', 'NN')]
¿Qué resultados ofrece?
>>> nltk.tag.accuracy(regexp_tagger, nltk.corpus.brown.tagged_sents(categories='a')) 0.20326391789486245
El etiquetador basado en expresiones regulares es un cajón de sastre que etiqueta todas las palabras como nombres. Equivale al etiquetador por defecto, con la única diferencia de que es mucho menos eficaz. En lugar de redefinir esto dentro del etiquetador basado en expresiones regulares, ¿hay alguna forma de combinar este etiquetador con el etiquetador por defecto? Veremos cómo hacerlo más adelante, en el apartado correspondiente a los etiquetadores auxiliares.
Ejercicios
1. |easy|
Busca en Internet “titulares absurdos” para encontrar joyas como: `British Left Waffles on Falkland Islands`:lx: o `Juvenile Court to Try Shooting Defendant`:lx:. Anota manualmente estos titulares para comprobar si al conocer las etiquetas léxicas se elimina la ambigüedad.
- . |easy| Comprueba que existen restricciones en lo que respecta a la distribución de `go`:lx: y `went`:lx:, en el sentido de que no se pueden intercambiar libremente en los tipos de contexto ilustrados en go_.
- . |soso| Escribe un código que permita buscar en el Brown Corpus palabras y locuciones concretas por etiquetas, para resolver los siguientes problemas:
a) Genera una lista alfabética de las distintas palabras a las que se les ha asignado la etiqueta ``MD``. #) Identifica las palabras que podrían ser nombres o verbos en tercera persona del singular (por ejemplo, `deals`:lx:, `flies`:lx:). #) Identifica grupos preposicionales de tres palabras con la forma IN + DET + NN (por ejemplo, `in the lab`:lx:). #) ¿Cuál es la proporción de pronombres masculinos con respecto a los pronombres femeninos?
- . |soso| En la introducción al libro, vimos una tabla en la que se mostraba la frecuencia de aparición de los verbos `adore`:lx:, `love`:lx:, `like`:lx:, `prefer`:lx: precedidos de distintos modificadores como `really`:lx:. Busca todos los modificadores (etiqueta `QL`:gc: en el Brown Corpus) que aparecen precediendo a estos cuatro verbos.
- . |soso|
Hemos definido el etiquetador``regexp_tagger``, que se puede utilizar como etiquetador por defecto para las palabras desconocidas. Este etiquetador sólo busca números cardinales. Buscando cadenas de sufijo o de prefijo concretas, debería ser posible adivinar otras etiquetas. Por ejemplo, podríamos asignar a todas las palabras que terminan en `-s`:lx: la etiqueta de nombre plural. Define un etiquetador basado en expresiones regulares (con ``nltk.RegexpTagger``)que busque al menos otros cinco patrones en la ortografía de las palabras (usa la documentación inline para comprender las reglas)
- . |soso|
Partiendo del etiquetador basado en expresiones regulares desarrollado en el ejercicio anterior, evalúa el funcionamiento del etiquetador mediante ``nltk.tag.accuracy()`` e intenta proponer soluciones para mejorar su funcionamiento. Explica qué has descubierto. ¿De qué forma contribuye la evaluación objetiva al proceso de desarrollo?
- . |hard| Hay un total de 264 palabras distintas en el Brown Corpus a las que se les pueden asignar exactamente tres posibles etiquetas.
a) Imprime una tabla con los números 1 a 10 en una columna y el número de palabras distintas del corpus que tengan de 1 a 10 etiquetas distintas. #) Para la palabra que admita el mayor número de etiquetas distintas, imprime oraciones del corpus que contengan esa palabra, una para cada una de las distintas etiquetas posibles.
- . |hard| Escribe un programa para clasificar los contextos que incluyan la palabra `must`:lx: según la etiqueta de la palabra siguiente. ¿Podría servir para diferenciar los usos epistémicos y deónticos de `must`:lx:?
Valores de referencia y backoff
El funcionamiento de los etiquetadores simples creados hasta el momento ha sido bastante decepcionante. Antes de embarcarnos en el proceso de obtener un rendimiento del 90% o más, tenemos que hacer dos cosas. En primer lugar, tenemos que establecer un valor de referencia más sólido que el etiquetador por defecto, que era demasiado simple, y que el etiquetador basado en expresiones regulares, que era demasiado arbitrario. En segundo lugar, es necesario encontrar la forma de interconectar varios etiquetadores distintos, de forma que si un etiquetador más especializado no puede asignar una etiqueta, podamos recurrir (en inglés, backoff) a un etiquetador más genérico
.. _sec-lookup-tagger:
El etiquetador basado en consultas
Muchas palabras muy frecuentes no tienen la etiqueta``NN``. Veamos algunas de estas palabras con sus etiquetas. El código que presentamos a continuación, toma una lista de oraciones y cuenta las palabras para imprimir las 100 palabras más frecuentes:
>>> fd = nltk.FreqDist(nltk.corpus.brown.words(categories='a'))
>>> most_freq_words = fd.sorted()[:100]
>>> most_freq_words
['the', ',', '.', 'of', 'and', 'to', 'a', 'in', 'for', 'The', 'that', '``',
'is', 'was', "", 'on', 'at', 'with', 'be', 'by', 'as', 'he', 'said', 'his',
'will', 'it', 'from', 'are', ';', 'has', 'an', '--', 'had', 'who', 'have',
'not', 'Mrs.', 'were', 'this', 'would', 'which', 'their', 'been', 'they', 'He',
'one', 'I', 'its', 'but', 'or', 'more', ')', 'Mr.', 'up', '(', 'all', 'last',
'out', 'two', ':', 'other', 'new', 'first', 'year', 'than', 'A', 'about', 'there',
'when', 'home', 'after', 'In', 'also', 'over', 'It', 'into', 'no', 'But', 'made',
'her', 'only', 'years', 'time', 'three', 'them', 'some', 'can', 'New', 'him',
'state', '?', 'any', 'President', 'could', 'before', 'week', 'under', 'against',
'we', 'now']
|nopar| Veamos ahora qué etiquetas tienen estas palabras. Primero lo haremos de la forma más obvia (y menos eficaz):
>>> [(w,t) for (w,t) in nltk.corpus.brown.tagged_words(categories='a')
... if w in most_freq_words]
[('The', 'AT'), ('said', 'VBD'), ('an', 'AT'), ('of', 'IN'),
('``', '``'), ('no', 'AT'), ("", ""), ('that', 'CS'),
('any', 'DTI'), ('.', '.'), ..., ("", "")]
|nopar| Una solución más eficaz es crear un diccionario que relacione cada una de las 100 palabras más frecuentes con su etiqueta más probable. Para hacerlo, podemos establecer una distribución de frecuencia ``cfd`` para las palabras etiquetadas, es decir, la frecuencia de las distintas etiquetas que se asignan a cada palabra.
>>> cfd = nltk.ConditionalFreqDist(nltk.corpus.brown.tagged_words(categories='a'))
|nopar| Ahora, para cualquier palabra que aparezca en esta sección del corpus, podemos determinar la etiqueta más probable:
>>> likely_tags = dict((word, cfd[word].max()) for word in most_freq_words) >>> likely_tags['The'] 'AT'
|nopar| Por último, podemos crear y evaluar un etiquetador simple que asigne etiquetas a las palabras basándose en esta tabla:
>>> baseline_tagger = nltk.UnigramTagger(model=likely_tags) >>> nltk.tag.accuracy(baseline_tagger, nltk.corpus.brown.tagged_sents(categories='a')) 0.45578495136941344
Este método funciona sorprendentemente bien. Conocer las etiquetas de las 100 palabras más frecuentes nos permite etiquetar casi la mitad de las palabras correctamente. Veamos cómo funciona con un fragmento del texto de entrada no etiquetado:
>>> baseline_tagger.tag(nltk.corpus.brown.sents(categories='a')[3])
[('``', '``'), ('Only', None), ('a', 'AT'), ('relative', None),
('handful', None), ('of', 'IN'), ('such', None), ('reports', None),
('was', 'BEDZ'), ('received', None), ("", ""), (',', ','),
('the', 'AT'), ('jury', None), ('said', 'VBD'), (',', ','),
('``', '``'), ('considering', None), ('the', 'AT'), ('widespread', None),
('interest', None), ('in', 'IN'), ('the', 'AT'), ('election', None),
(',', ','), ('the', 'AT'), ('number', None), ('of', 'IN'),
('voters', None), ('and', 'CC'), ('the', 'AT'), ('size', None),
('of', 'IN'), ('this', 'DT'), ('city', None), ("", ""), ('.', '.')]
Como puedes observar, a muchas de las palabras se les ha asignado la etiqueta ``None`` [Ninguna]. Esto se debe a que no se encontraban entre las 100 palabras más frecuentes. En estos casos, lo más conveniente sería asignarles la etiqueta por defecto NN. Este proceso recibe el nombre de backoff.
Backoff
¿Cómo podemos combinar estos etiquetadores? Queremos utilizar primero la tabla de consulta y, si no es posible asignar una etiqueta a partir de la tabla, el etiquetador por defecto. Para ello, debemos definir el etiquetador por defecto como un argumento del etiquetador basado en consultas. El etiquetador basado en consultas hará una llamada al etiquetador por defecto sólo en caso de que no pueda asignar él mismo una etiqueta.
>>> baseline_tagger = nltk.UnigramTagger(model=likely_tags, backoff=nltk.DefaultTagger('NN'))
>>> nltk.tag.accuracy(baseline_tagger, nltk.corpus.brown.tagged_sents(categories='a'))
0.58177695566561249
Volveremos sobre esta técnica en el apartado sec-combining-taggers_, donde se estudia más detenidamente la combinación de varios etiquetadores.
Seleccionar un valor de referencia adecuado
Podemos hacer un compendio de todo lo anterior y escribir un programa simple (aunque algo ineficaz) para crear y evaluar etiquetadores basado en consultas para distintos tamaños, según se muestra en el Listado baseline-tagger_. Incluimos un etiquetador auxiliar (o de backoff) que etiquete todas las palabras como nombres. Como consecuencia de utilizar este etiquetador auxiliar, el etiquetador basado en consultas sólo tiene que almacenar pares de palabra/etiqueta para las palabras que no sean nombres.
.. pylisting:: baseline-tagger
:caption: Rendimiento del etiquetador basado en consultas con modelos de distinto tamaño
def performance(cfd, wordlist):
lt = dict((word, cfd[word].max()) for word in wordlist)
baseline_tagger = nltk.UnigramTagger(model=lt, backoff=nltk.DefaultTagger('NN'))
return nltk.tag.accuracy(baseline_tagger, nltk.corpus.brown.tagged_sents(categories='a'))
def display():
import pylab
words_by_freq = nltk.FreqDist(nltk.corpus.brown.words(categories='a')).sorted()
cfd = nltk.ConditionalFreqDist(nltk.corpus.brown.tagged_words(categories='a'))
sizes = 2 ** pylab.arange(15)
perfs = [performance(cfd, words_by_freq[:size]) for size in sizes]
pylab.plot(sizes, perfs, '-bo')
pylab.title('Rendimiento del etiquetador por defecto con modelos de distinto tamaño')
pylab.xlabel('Tamaño del modelo')
pylab.ylabel('Rendimiento')
pylab.show()
>>> display() # doctest: +SKIP
.. _tag-lookup: .. figure:: ../images/tag-lookup.png
:scale: 60
Etiquetador basado en consultas
Como puedes observar, inicialmente el rendimiento aumenta muy rápido en función del tamaño del modelo, pero hacia el final se estanca, ya que al aumentar considerablemente el tamaño del modelo no se producen mejoras significativas en el rendimiento. (Para este ejemplo se ha utilizado el paquete gráfico ``pylab``; volveremos sobre este tema más adelante, en el apartado sec-graphics_).
Ejercicios
- . |soso| Analiza las siguientes cuestiones que se plantean en relación con el etiquetador basado en consultas:
a) ¿Qué ocurre con el rendimiento del etiquetador para los distintos tamaños de modelo si se omite el etiquetador auxiliar? b) Observa la curva de la Figura tag-lookup_. Propón un tamaño adecuado para un etiquetador por defecto, que permita mantener el equilibrio entre la memoria y el rendimiento. ¿Se te ocurren situaciones en las que sería preferible minimizar el uso de memoria o situaciones en las que sería preferible maximizar el rendimiento a pesar del mayor uso de memoria?
- . |soso| Cuál es el límite máximo de rendimiento de un etiquetador basado en consultas si no ponemos ningún límite al tamaño de la tabla? (Pista: escribe un programa para calcular el porcentaje de formas de una palabra a las que se les asigna la etiqueta más probable para esa palabra, de media.)
Ampliar la cobertura
Otras clases de palabras en inglés
Otras dos clases de palabras importantes son los `adjetivos`:dt: y los `adverbios`:dt:. Los adjetivos describen a los sustantivos y pueden funcionar como modificadores de éstos (por ejemplo, `large`:lx: en `the large pizza`:lx:) o como predicados (por ejemplo, `the pizza is large`:lx:). Los adjetivos en inglés pueden ser morfológicamente complejos (por ejemplo, `fall`:lx:\ :subscript:`V`\ `+ing`:lx: en `the falling stocks`:lx:). Los adverbios modifican a los verbos para especificar el tiempo, el modo, el lugar o la dirección de la acción descrita por el verbo (por ejemplo, `quickly`:lx: en `the stocks fell quickly`:lx:). Los adverbios también pueden modificar a los adjetivos (por ejemplo, `really`:lx: en `Mary's teacher was really nice`:lx:).
Además de las preposiciones, el inglés tiene otras clases cerradas de palabras: los `artículos`:dt:, también llamados `determinantes`:dt: (p.ej.: `the`:lx:, `a`:lx:), los `verbos modales`:dt: (p.ej.: `should`:lx:, `may`:lx:), y los `pronombres personales`:dt: (p.ej.: `she`:lx:, `they`:lx:). Cada diccionario y cada gramática clasifica estas palabras de un modo distinto.
Las etiquetas léxicas están estrechamente relacionadas con el concepto de clase de palabras utilizado en sintaxis. En lingüística se presupone que cada tipo diferenciado de palabra se enumera en un léxico (o diccionario), con información sobre su pronunciación, sus propiedades sintácticas y su significado. Un componente fundamental de las propiedades de una palabra es su categoría léxica. Si realizamos un análisis sintáctico de un ejemplo como fruit flies like a banana, buscaremos cada palabra en el léxico, determinaremos su categoría léxica y la incluiremos en una jerarquía de sintagmas, tal y como se muestra en el siguiente árbol de análisis.
.. tree:: (S (NP (Adj Fruit) (N flies)) (VP (V like) (NP (Det a) (N banana))))
Estudiaremos el análisis sintáctico en mayor profundidad en la Parte II. Por ahora, nos basta con relacionar las etiquetas utilizadas en los árboles de análisis sintáctico y las etiquetas léxicas. La Tabla word-classes_ muestra esta relación:
.. table:: word-classes
================ ========= ============================ Etiqueta de análisis sintáctico Etiqueta del Brown Corpus Clase de palabra ================ ========= ============================ Det `AT`:gc: artículo N `NN`:gc: nombre V `VB`:gc: verbo Adj `JJ`:gc: adjetivo P `IN`:gc: preposición Card `CD`:gc: número cardinal -- `.`:gc: puntuación de fin de oración ================ ========= ============================
Etiquetas de análisis sintáctico y etiquetas del Brown Corpus
Criterios de clasificación
Después de examinar detalladamente las distintas clases de palabras, es el momento de plantearnos una pregunta más básica: ¿cómo decidimos a qué categoría pertenece cada palabra? En términos generales, los lingüistas se valen de tres criterios distintos: criterios morfológicos (o formales), criterios sintácticos (o distributivos) y criterios semánticos (o nocionales). Los criterios `morfológicos`:dt: analizan la estructura interna de las palabras. Por ejemplo, `-ness`:lx: s un sufijo que al combinarse con un adjetivo da lugar a un nombre. Ejemplos: `happy`:lx: |rarr| `happiness`:lx:, `ill`:lx: |rarr| `illness`:lx:. Así pues, si nos topamos con una palabra que termine en -ness es muy probable que se trate de un nombre.
Los criterios `sintácticos`:dt: se refieren a los contextos en los que aparecen las palabras. Imaginemos, por ejemplo, que ya hemos definido la categoría de los nombres. En tal caso, podríamos decir que un criterio sintáctico para los adjetivos en inglés es que aparecen inmediatamente antes de un nombre o inmediatamente después de las palabras `be`:lx: o `very`:lx:. Según esto, `near`:lx: se tendría que clasificar como adjetivo en los siguientes ejemplos:
.. ex::
.. ex:: the near window .. ex:: The end is (very) near.
Un criterio `semántico`:dt: común es definir el sustantivo como "el nombre de una persona, un lugar o una cosa". La lingüística moderna se muestra un tanto reacia al uso de criterios semánticos para determinar las clases de palabras, básicamente porque resultan difíciles de formalizar. Sin embargo, los criterios semánticos refuerzan muchas de nuestras intuiciones sobre las clases de palabras y nos permiten deducir, con bastante probabilidad de acierto, la clasificación de las palabras en idiomas que desconocemos. Por ejemplo, si todo lo que sabes de la palabra neerlandesa `verjaardag`:lx: es que significa lo mismo que la palabra inglesa `birthday`:lx:, podemos deducir que `verjaardag`:lx: es un nombre en neerlandés. Pero hay que tener cuidado: aunque podamos traducir `zij is vandaag jarig`:lx: por `it's her birthday today`:lx:, la palabra `jarig`:lx: es un adjetivo en neerlandés que no tiene ningún equivalente exacto en inglés.
Todos los idiomas adquieren nuevos elementos léxicos. Entre las palabras que se han añadido recientemente al Oxford Dictionary of English se incluyen `cyberslacker, fatoush, blamestorm, SARS, cantopop, bupkis, noughties, muggle`:lx: y `robata`:lx:. Como se puede observar, todas estas palabras son nombres. Por eso, se dice que los nombres constituyen una clase abierta de palabras. Por el contrario, se considera que las preposiciones constituyen una `clase cerrada`:dt: de palabras. Es decir, hay un conjunto finito de palabras que pertenecen a esta categoría (p. ej., `above, along, at, below, beside, between, during, for, from, in, near, on, outside, over, past, through, towards, under, up, with`:lx:) y la pertenencia o no pertenencia a este conjunto cambia de forma muy paulatina a lo largo del tiempo.
Etiquetado de unigramas
Los etiquetadores de unigramas se basan en un sencillo algoritmo estadístico: a cada forma, le asignan la etiqueta más probable para esa forma en concreto. Así, asignará la etiqueta ``JJ`` a la palabra `frequent`:lx: cada vez que aparezca, ya que `frequent`:lx: se usa más como adjetivo (p.ej., `a frequent word`:lx:) que como verbo (p.ej., `I frequent this cafe`:lx:). Los etiquetadores de unigramas se comportan exactamente igual que los etiquetadores basados en consultas (apartado sec-lookup-tagger_), con la única diferencia de que existe una técnica más apropiada para configurarlos, denominada `entrenamiento`:dt:\ . En la siguiente muestra de código, iniciamos y entrenamos un etiquetador de unigramas (línea tag-train_), lo usamos para etiquetar una oración y, por último, calculamos la precisión general del etiquetador:
>>> brown_a = nltk.corpus.brown.tagged_sents(categories='a')
>>> unigram_tagger = nltk.UnigramTagger(brown_a) # [_tag-train]
>>> sent = nltk.corpus.brown.sents(categories='a')[2007]
>>> unigram_tagger.tag(sent)
[('Various', None), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER'),
('of', 'IN'), ('the', 'AT'), ('terrace', 'NN'), ('type', 'NN'), (',', ','),
('being', 'BEG'), ('on', 'IN'), ('the', 'AT'), ('ground', 'NN'), ('floor', 'NN'),
('so', 'QL'), ('that', 'CS'), ('entrance', 'NN'), ('is', 'BEZ'), ('direct', 'JJ'), ('.', '.')]
>>> nltk.tag.accuracy(unigram_tagger, brown_a)
0.8550331165343994
Etiquetadores de afijos
Los etiquetadores de afijos son como los etiquetadores de unigramas, con la única diferencia de que se entrenan con prefijos o sufijos de palabras de una determinada extensión (aquí usamos
- prefijo* y *sufijo* ino en el sentido morfológico, sino para referirnos a cadenas de caracteres). El siguiente etiquetador, por ejemplo, tendrá en cuenta sufijos de longitud 3 (p.ej., *-ize*, *-ion*), en palabras con al menos 5 caracteres.
>>> affix_tagger = nltk.AffixTagger(brown_a, affix_length=-2, min_stem_length=3)
>>> affix_tagger.tag(sent)
[('Various', 'JJ'), ('of', None), ('the', None), ('apartments', 'NNS'), ('are', None),
('of', None), ('the', None), ('terrace', 'NN'), ('type', None), (',', None),
('being', 'VBG'), ('on', None), ('the', None), ('ground', 'NN'), ('floor', 'NN'),
('so', None), ('that', None), ('entrance', 'NN'), ('is', None), ('direct', 'NN'),
('.', None)]
.. _n-gram-taggers:
Etiquetadores de n-gramas
Cuando llevamos a cabo una tarea de procesamiento del lenguaje basada en unigramas, utilizamos un solo elemento del contexto. En el caso del etiquetado, sólo tenemos en cuenta la forma actual, aislada de un contexto más amplio. Con este modelo, lo máximo que podemos hacer es asignar a cada palabra la etiqueta que a priori parezca más probable. Así, a una palabra como `wind`:lx: le asignaríamos siempre la misma etiqueta, con independencia de si aparece en el contexto `the wind`:lx: o `to wind`:lx:\ .
Un `etiquetador de n-gramas`:dt: es una generalización de un etiquetador de unigramas cuyo contexto es la palabra actual y las etiquetas léxicas de las
- n-1* formas precedentes, como se muestra en la Figura tag-context_. La etiqueta que se ha de elegir, *t*\ `n`:subscript:, aparece dentro de un círculo y el contexto aparece sombreado. En el ejemplo de un etiquetador de n-gramas que se muestra en la Figura tag-context_,
tenemos que *n=3*, es decir, además de la palabra actual se tendrán en cuenta las etiquetas de las dos palabras precedentes. El etiquetador de n-gramas selecciona la etiqueta más probable en el contexto dado.
.. _tag-context: .. figure:: ../images/tag-context.png
:scale: 80
Contexto del etiquetador
.. note:: Los etiquetadores de unigramas también se denominan etiquetadores de 1 grama: el contexto utilizado para asignar una etiqueta a una forma está formado sólo por dicha forma. Los etiquetadores de 2 gramas también se denominan *etiquetadores de bigramas* y los etiquetadores de 3 gramas reciben el nombre de *etiquetadores de trigramas*.
La clase ``NgramTagger`` utiliza un corpus de entrenamiento etiquetado para determinar qué etiqueta léxica resulta más probable para cada contexto. A continuación, veremos un caso especial de un etiquetador de n-gramas, concretamente, de un etiquetador de bigramas. Primero lo entrenaremos y después lo utilizaremos para etiquetar oraciones aún sin etiquetar:
>>> bigram_tagger = nltk.BigramTagger(brown_a, cutoff=0)
>>> bigram_tagger.tag(sent)
[('Various', 'JJ'), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER'),
('of', 'IN'), ('the', 'AT'), ('terrace', 'NN'), ('type', 'NN'), (',', ','),
('being', 'BEG'), ('on', 'IN'), ('the', 'AT'), ('ground', 'NN'), ('floor', 'NN'),
('so', 'CS'), ('that', 'CS'), ('entrance', 'NN'), ('is', 'BEZ'), ('direct', 'JJ'),
('.', '.')]
|nopar| Al igual que otros etiquetadores, los etiquetadores de n-gramas asignan la etiqueta `None`:gc: a todas las formas cuyo contexto no se ha visto durante la fase de entrenamiento.
A medida que aumenta *n*, el grado de especificidad del contexto incrementa, al igual que aumenta la posibilidad de que los datos que deseamos etiquetar contengan contextos no presentes en los datos de entrenamiento. Esto se conoce como el problema de *escasez de datos* (sparse data) y es bastante frecuente en el PLN. Así pues, existe una relación inversamente proporcional entre la precisión y la cobertura de nuestros resultados (que se relaciona con la relación inversamente proporcional que existe entre la `precisión y la exhaustividad`:dt: en la recuperación de la información).
.. Note:: Los etiquetadores de n-gramas no deberían tener en cuenta el contexto que va más allá de la oración. Así pues, los etiquetadores de NLTK están diseñados para trabajar con listas de oraciones, en las que cada oración es una lista de palabras. Al principio de una oración, *t*\ `n-1`:subscript: y las etiquetas precedentes toman el valor ``None``.
.. _sec-combining-taggers:
Combinar etiquetadores
Una forma de abordar la relación entre la precisión y la cobertura es utilizar los algoritmos más precisos posibles y, cuando sea necesario, recurrir a otros algoritmos de cobertura más amplia. Por ejemplo, podemos combinar los resultados de un etiquetador de bigramas, un etiquetador de unigramas y un ``regexp_tagger``, tal y como se muestra a continuación:
1. Intenta asignar una etiqueta a la forma con el etiquetador de bigramas. 2. Si el etiquetador de bigramas no consigue encontrar una etiqueta para la forma, prueba con el etiquetador de unigramas. 3. Si el etiquetador de unigramas tampoco consigue encontrar una etiqueta para la forma, usa un etiquetador por defecto.
La mayoría de los etiquetadores de NLTK permiten especificar un etiquetador auxiliar (o de backoff). El etiquetador auxiliar puede tener, a su vez, otro etiquetador auxiliar:
>>> t0 = nltk.DefaultTagger('NN')
>>> t1 = nltk.UnigramTagger(brown_a, backoff=t0)
>>> t2 = nltk.BigramTagger(brown_a, backoff=t1)
>>> nltk.tag.accuracy(t2, brown_a)
0.88565347972233821
.. Note:: Definimos el etiquetador auxiliar en el momento de iniciar el etiquetador, para poder aprovecharlo durante el entrenamiento. Así, si en un determinado contexto, el etiquetador de bigramas asignara la misma etiqueta que su etiquetador de unigramas auxiliar, el etiquetador de bigramas ignorará la instancia de entrenamiento. De esta forma, el modelo del etiquetador de bigramas se mantiene lo más reducido posible. También podemos configurar el etiquetador de forma que para que retenga un determinado contexto, lo haya tenido que ver más de una vez. Por ejemplo, ``nltk.BigramTagger(sents, cutoff=2, backoff=t1)`` ignorará los contextos que sólo hayan aparecido una o dos veces.
Etiquetar palabras desconocidas
Nuestro método para etiquetar palabras desconocidas también se basa en recurrir a un etiquetador de expresiones regulares o a un etiquetador por defecto. Pero estos etiquetadores no pueden hacer uso del contexto. Así, si nuestro etiquetador se topara con la palabra `blog`:lx y ésta no hubiera aparecido durante el entrenamiento, el etiquetador le asignaría una etiqueta con independencia de si la palabra aparece en el contexto `the blog`:lx: o `to blog`:lx:. ¿Cómo podemos obtener mejores resultados con estas palabras desconocidas o con otros elementos `no incluidos en el vocabulario`:dt:?
Un método útil para etiquetar palabras desconocidas basándose en el contexto consiste en limitar el vocabulario del etiquetador a las `n`:math: palabras más frecuentes y sustituir las demás palabras por una palabra especial, `UNK`:lx:\ . Durante el entrenamiento, un etiquetador de unigramas verá que esta "palabra" generalmente es un nombre. Sin embargo, los etiquetadores de n-gramas detectarán contextos en los que tenga otra etiqueta. Por ejemplo, si la palabra inmediatamente anterior es `to`:lx: (etiquetada como ``TO``), es muy probable que a `UNK`:lx: se le asigne una etiqueta de verbo. Exploraremos este método en mayor profundidad en los ejercicios.
Almacenamiento de etiquetadores
El entrenamiento de un etiquetador en un corpus muy extenso puede llevar varios minutos. En lugar de entrenar el etiquetador cada vez que lo necesitemos, es más recomendable guardar un etiquetador ya entrenado en un archivo, para poder reutilizarlo más adelante. A continuación, se muestra cómo guardar nuestro etiquetador ``t2`` en un archivo al que llamaremos ``t2.pkl``.
>>> from cPickle import dump
>>> output = open('t2.pkl', 'wb')
>>> dump(t2, output, -1)
>>> output.close()
|nopar| Ahora, en otro proceso de Python, podemos cargar el etiquetador que hemos guardado.
>>> from cPickle import load
>>> input = open('t2.pkl', 'rb')
>>> tagger = load(input)
>>> input.close()
|nopar| Comprobemos que se puede utilizar para llevar a cabo el etiquetado.
>>> text = """The board's action shows what free enterprise
... is up against in our complex maze of regulatory laws ."""
>>> tokens = text.split()
>>> tagger.tag(tokens)
[('The', 'AT'), ("board's", 'NN$'), ('action', 'NN'), ('shows', 'NNS'),
('what', 'WDT'), ('free', 'JJ'), ('enterprise', 'NN'), ('is', 'BEZ'),
('up', 'RP'), ('against', 'IN'), ('in', 'IN'), ('our', 'PP$'), ('complex', 'JJ'),
('maze', 'NN'), ('of', 'IN'), ('regulatory', 'NN'), ('laws', 'NNS'), ('.', '.')]
Ejercicios
- . |easy|
Entrena un etiquetador de unigramas y ejecútalo sobre un fragmento de texto nuevo. Observarás que hay palabras a las que no se asigna ninguna etiqueta. ¿Por qué?
- . |easy|
Entrena un etiquetador de afijos ``AffixTagger()`` y ejecútalo sobre un fragmento de texto nuevo. Prueba con distintas valores para la longitud del afijo y la longitud mínima de las palabras. ¿Has encontrado alguna combinación de valores con la que el etiquetador parezca funcionar mejor que el descrito anteriormente? Explica tus hallazgos.
- . |easy|
Entrena un etiquetador de bigramas sin etiquetador auxiliar y ejecútalo sobre parte de los datos de entrenamiento. A continuación, ejecútalo sobre un fragmento de datos nuevos. ¿Qué ocurre con el rendimiento del etiquetador? ¿Por qué?
- . |soso|
Escribe un programa que haga llamadas recurrentes a ``AffixTagger()`` utilizando distintos valores para la longitud de los afijos y la longitud mínima de las palabras. ¿Qué valores proporcionan el mejor rendimiento general? ¿A qué crees que se debe?
- . |soso|
Analiza la gravedad del problema de escasez de datos. Para ello, examina el rendimiento de los etiquetadores de n-gramas aumentando gradualmente el valor de `n`:math: de 1 a 6. Haz una tabla con los valores de precisión alcanzados. Calcula el tamaño de los datos de entrenamiento necesarios para estos etiquetadores, con un tamaño de vocabulario de 10\ `5`:superscript: y un tamaño del conjunto de etiquetas de 10\ `2`:superscript:.
- . |soso| Consigue un fragmento de datos etiquetados en otro idioma y entrena y evalúa diversos etiquetadores con estos datos. Si el idioma en cuestión es morfológicamente complejo, o si las categorías léxicas vienen determinadas por alguna marca ortográfica (p.ej., el uso de mayúsculas), intenta desarrollar un etiquetador de expresiones regulares para ese idioma (que se ejecute después del etiquetador de unigramas y antes que el etiquetador por defecto). Compara el nivel de acierto de tu etiquetador o etiquetadores con el de los mismos etiquetadores si se ejecutan sobre datos en inglés. Analiza todas las cuestiones que se te planteen al aplicar estos métodos al idioma.
- . |hard|
Construye un etiquetador por defecto y varios etiquetadores de unigramas y n-gramas, que incluyan etiquetadores auxiliares, y entrénalos en parte del Brown Corpus.
a) Combina los etiquetadores de tres formas distintas. Comprueba la precisión de cada uno de los etiquetadores combinados. ¿Qué combinación funciona mejor?
#) Prueba a modificar el tamaño del corpus de entrenamiento. ¿Cómo incide sobre los resultados?
- . |hard|
Nuestro método para etiquetar palabras desconocidas ha consistido hasta ahora en analizar las letras de las palabras (utilizando ``RegexpTagger()`` y ``AffixTagger()``) o en ignorar la palabra por completo y etiquetarla como nombre (utilizando ``nltk.DefaultTagger()``). Estos métodos no darán buenos resultados cuando el texto analizado contenga palabras nuevas que no sean nombres. Analiza la oración `I like to blog on Kim's blog`:lx:. Si `blog`:lx: es una palabra nueva, conocer la etiqueta anterior (``TO`` o ``NP$``, según sea el caso) probablemente será de gran utilidad. Es decir, necesitamos un etiquetador por defecto que tenga en cuenta la etiqueta precedente. a) Construye un nuevo tipo de etiquetador de unigramas que tenga en cuenta la etiqueta de la palabra anterior e ignore la palabra actual (La mejor forma de hacerlo es cambiar el código fuente de ``UnigramTagger()``, para lo que se necesitarán los conocimientos expuestos en el apartado OO_.)
b) Añade este etiquetador a la secuencia de etiquetadores auxiliares (entre los que se incluyen etiquetadores de trigramas y bigramas centrados en las palabras) inmediatamente antes del etiquetador por defecto habitual.
c) Evalúa la contribución de este nuevo etiquetador de unigramas.
- . |hard|
Escribe un código para preprocesar datos de entrenamiento ya etiquetados que sustituya todas las `n`:math: palabras excepto las más frecuentes por la palabra especial `UNK`:lx:. Entrena un etiquetador auxiliar de n-gramas utilizando estos datos y, a continuación, úsalo para etiquetar un fragmento de texto nuevo. Ten en cuenta que tendrás que preprocesar el texto para sustituir las palabras desconocidas por `UNK`:lx: y procesar posteriormente el resultado etiquetado para reemplazar las palabras `UNK`:lx: por las palabras del texto original.
Resumen
- Las palabras se pueden dividir en distintas clases, tales como nombres, verbos, adjetivos o adverbios. Estas clases se denominan categorías léxicas o clases de palabras. A las clases de palabras se les asignan denominaciones abreviadas o etiquetas, como``NN``, ``VB``, etc.
- El proceso de asignación automática de categorías léxicas a las palabras de un texto se denomina etiquetado léxico, etiquetado POS o simplemente etiquetado.
- Algunos corpus lingüísticos, como, por ejemplo, el Brown Corpus, han sido etiquetados según la categoría léxica de las palabras.
- Existen diversos métodos de etiquetado: etiquetadores por defecto, etiquetadores de expresiones regulares, etiquetadores de unigramas y etiquetadores de n-gramas. Los distintos etiquetadores se pueden combinar mediante el empleo de la denominada técnica de backoff.
- Los etiquetadores se pueden entrenar y evaluar mediante el uso de corpus etiquetados.
- El etiquetado léxico es un importante ejemplo temprano de una tarea de clasificación de secuencias en el PLN: las decisiones de clasificación tomadas en un determinado punto de la secuencia utilizan palabras y etiquetas del contexto local.
.. _sec-tag-further-reading:
Lecturas complementarias
Para obtener más ejemplos de etiquetado con |NLTK|, puedes consultar la guía en ``http://nltk.org/doc/guides/tag.html``. Los capítulos 4 y 5 de [JurafskyMartin2008]_ contienen material más avanzado sobre los n-gramas y el etiquetado léxico.
Existen otros importantes métodos de etiquetado que se relacionan con el *aprendizaje basado en transformaciones*, *los modelos de Markov* y los *modelos de estados finitos* algunos de los cuales los estudiaremos en el Capítulo chap-data-intensive_.) En el capítulo chap-chunk_ estudiaremos una generalización del etiquetado consistente en llevar a cabo un análisis superficial o *chunking*, en el que a una secuencia contigua de palabras se le asigna una sola etiqueta.
El etiquetado léxico es sólo un tipo de etiquetado, que no requiere un análisis lingüístico profundo. Existen muchos otros tipos de etiquetado. Así, las palabras se pueden etiquetar con directrices que indiquen qué palabras se han de enfatizar, para ser utilizadas por un sintetizador del habla. También se les pueden asignar números según su significado, indicando el sentido con el que se ha utilizado cada palabra. Asimismo, las palabras se pueden etiquetar según sus características morfológicas. A continuación se muestran ejemplos de cada uno de estos tipos de etiquetado. Por cuestiones de espacio, únicamente mostramos la etiqueta de una sola palabra. Observa que los dos primeros ejemplos utilizan etiquetas de tipo XML, donde la palabra etiquetada se encuentra rodeada de elementos entre paréntesis angulares.
1. *Lenguaje de marcado de síntesis del habla (W3C SSML):*
``That is a <emphasis>big</emphasis> car!``
- . *SemCor: Brown Corpus etiquetado semánticamente con los significados de WordNet:*
``Space in any <wf pos="NN" lemma="form" wnsn="4">form</wf> is completely measured by the three dimensions.`` (Wordnet form/nn sense 4: "shape, form, configuration, contour, conformation")
- . *Etiquetado morfológico, de la base de datos de árboles semánticos del italiano de la Universidad de Turín:*
``E' italiano , come progetto e realizzazione , il primo (PRIMO ADJ ORDIN M SING) porto turistico dell' Albania .``
El etiquetado presenta varias características propias del procesamiento del lenguaje natural. En primer lugar, implica un proceso de *clasificación*: las palabras tienen propiedades; muchas palabras comparten la misma propiedad (por ejemplo, tanto ``cat`` como ``dog`` son nombres), mientras que otras palabras reúnen varias de dichas propiedades (por ejemplo, ``wind`` es tanto un nombre como un verbo). En segundo lugar, en el etiquetado, la desambiguación se produce a través de la *representación*: incrementamos la representación de las formas asignando etiquetas léxicas. En tercer lugar, el entrenamiento de un etiquetador implica un *aprendizaje de secuencias a partir de corpus anotados*. Por último, el etiquetado utiliza *métodos sencillos y generales*, como las distribuciones de frecuencia condicionales y el aprendizaje basado en transformaciones.
Conviene tener en cuenta que el etiquetado también se puede realizar en niveles más altos. A continuación se muestra un ejemplo de etiquetado de diálogos, tomados del NPS Chat Corpus [Forsyth2007]_, incuido en NLTK.
| Statement User117 Dude..., I wanted some of that | ynQuestion User120 m I missing something? | Bye User117 I'm gonna go fix food, I'll be back later. | System User122 JOIN | System User2 slaps User122 around a bit with a large trout. | Statement User121 18/m pm me if u tryin to chat
Lista de etiquetadores disponibles: ``http://www-nlp.stanford.edu/links/statnlp.html``
Anexo: conjunto de etiquetas del Brown Corpus
.. _Anexo:
La Tabla brown-tags_ proporciona una muestra de palabras de clases cerradas, según la clasificación del Brown Corpus. Ten en cuenta que las etiquetas léxicas pueden aparecer tanto en mayúsculas como en minúsculas, indistintamente.
.. table:: brown-tags
==== ========================================= ========================================================== AP determinante/pronombre, determinante pospuesto many other next more last former little several enough most least only very few fewer past same AT artículo the an no a every th' ever' ye CC conjunción, coordinante and or but plus & either neither nor yet 'n' and/or minus an' CS conjunción, subordinante 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 preposición of in for by considering to on among at through with under into regarding than since despite ... MD auxiliar modal should may might will would must can could shall ought need wilt PN pronombre, nominal none something everything one anyone nothing nobody everybody everyone anybody anything someone no-one nothin' PPL pronombre, singular, reflexivo itself himself myself yourself herself oneself ownself PP$ determinante, posesivo our its his their my your her out thy mine thine PP$$ pronombre, posesivo ours mine his hers theirs yours PPS pronombre, personal, nominal, 3ª pers. sng.it he she thee PPSS pronombre, personal, nominal, no 3ª pers. sng. they we I you ye thou you'uns WDT determinantes que empiezan con WH which what whatever whichever WPS pronombres que empiezan con WH, nominativos that who whoever whosoever what whatsoever ==== ========================================= ==========================================================
Algunas palabras de clase cerrada en inglés, con la etiqueta asignada en el Brown Corpus
Agradecimientos
.. Dutch example: http://www.askoxford.com/pressroom/archive/odelaunch/
.. include:: footer.txt



