{ "metadata": { "name": "", "signature": "sha256:98f799aac86a8ff0de4aa12f526782c64ece5e944732475ef1658bb085748a26" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Resumen NLTK: Etiquetado morfol\u00f3gico (*part-of-speech tagging*)\n", "\n", "Este resumen se corresponde con el cap\u00edtulo 5 del NLTK Book [Categorizing and Tagging Words](http://www.nltk.org/book_1ed/ch05.html). La lectura del cap\u00edtulo es muy recomendable.\n", "\n", "\n", "## Etiquetado morfol\u00f3gico con NLTK\n", "\n", "NLTK propociona varias herramientas para poder crear f\u00e1cilmente etiquetadores morfol\u00f3gicos (*part-of-speech taggers*). Veamos algunos ejemplos.\n", "\n", "Para empezar, necesitamos importar el m\u00f3dulo `nltk` que nos da acceso a todas las funcionalidades:" ] }, { "cell_type": "code", "collapsed": true, "input": [ "import nltk" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como primer ejemplo, podemos utilizar la funci\u00f3n `nltk.pos_tag` para etiquetar morfol\u00f3gicamente una oraci\u00f3n en ingl\u00e9s, siempre que la especifiquemos como una lista de palabras o tokens." ] }, { "cell_type": "code", "collapsed": false, "input": [ "oracion1 = \"This is the lost dog I found at the park\".split()\n", "oracion2 = \"The progress of the humankind as I progress\".split()\n", "print nltk.pos_tag(oracion1)\n", "print nltk.pos_tag(oracion2)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[('This', 'DT'), ('is', 'VBZ'), ('the', 'DT'), ('lost', 'NN'), ('dog', 'NN'), ('I', 'PRP'), ('found', 'VBD'), ('at', 'IN'), ('the', 'DT'), ('park', 'NN')]\n", "[('The', 'DT'), ('progress', 'NN'), ('of', 'IN'), ('the', 'DT'), ('humankind', 'NN'), ('as', 'IN'), ('I', 'PRP'), ('progress', 'VBP')]\n" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "El etiquetador funciona bastante bien aunque comete errores, obviamente. Si probamos con la famosa frase de Chomksy detectamos palabras mal etiquetadas." ] }, { "cell_type": "code", "collapsed": false, "input": [ "oracion3 = \"Green colorless ideas sleep furiously\".split()\n", "print nltk.pos_tag(oracion3)\n", "\n", "print nltk.pos_tag([\"My\", \"name\", \"is\", \"Prince\"])\n", "\n", "print nltk.pos_tag('He was born during the summer of 1988'.split())\n", "\n", "print nltk.pos_tag(\"She's Tony's sister\".split())\n", "\n", "print nltk.pos_tag('''My name is Xrtwewvdk'''.split())" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[('Green', 'NNP'), ('colorless', 'NN'), ('ideas', 'NNS'), ('sleep', 'VBP'), ('furiously', 'RB')]\n", "[('My', 'PRP$'), ('name', 'NN'), ('is', 'VBZ'), ('Prince', 'NNP')]\n", "[('He', 'PRP'), ('was', 'VBD'), ('born', 'VBN'), ('during', 'IN'), ('the', 'DT'), ('summer', 'NN'), ('of', 'IN'), ('1988', 'CD')]\n", "[(\"She's\", 'NNS'), (\"Tony's\", 'VBP'), ('sister', 'NN')]\n", "[('My', 'PRP$'), ('name', 'NN'), ('is', 'VBZ'), ('Xrtwewvdk', 'NNP')]\n" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\u00bfC\u00f3mo funciona este etiquetador? `nltk.pos_tag` es un etiquetador morfol\u00f3gico basado en aprendizaje autom\u00e1tico. A partir de miles de ejemplos de oraciones etiquetadas manualmente, el sistema *ha aprendido*, calculando frecuencias y generalizando cu\u00e1l es la categor\u00eda gramatical m\u00e1s probable para cada token. \n", "\n", "Como sabes, desde NLTK podemos acceder a corpus que ya est\u00e1n etiquetados. Vamos a utilizar alguno de los que ya conoces, el corpus de Brown, para entrenar nuestros propios etiquetadores.\n", "\n", "Para ello importamos el corpus de Brown y almacenamos en un par de variables las noticias de este corpus en su versi\u00f3n etiquetada morfol\u00f3gicamente y sin etiquetar." ] }, { "cell_type": "code", "collapsed": true, "input": [ "from nltk.corpus import brown\n", "brown_sents = brown.sents(categories=\"news\")\n", "brown_tagged_sents = brown.tagged_sents(categories=\"news\")" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para comparar ambas versiones, podemos imprimir la primera oraci\u00f3n de este corpus en su versi\u00f3n sin etiquetas (f\u00edjate que se trata de una lista de tokens, sin m\u00e1s) y en su versi\u00f3n etiquetada (se trata de una lista de tuplas donde el primer elemento es el token y el segundo es la etiqueta morfol\u00f3gica)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# imprimimos la primera oraci\u00f3n de las noticias de Brown\n", "print brown_sents[0] # sin anotar\n", "print brown_tagged_sents[0] # etiquetada morfol\u00f3gicamente" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', 'Friday', 'an', 'investigation', 'of', \"Atlanta's\", 'recent', 'primary', 'election', 'produced', '``', 'no', 'evidence', \"''\", 'that', 'any', 'irregularities', 'took', 'place', '.']\n", "[('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'), ('.', '.')]\n" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Etiquetado morfol\u00f3gico autom\u00e1tico\n", "\n", "### Etiquetador por defecto\n", "\n", "NLTK nos da acceso a tipos de etiquetadores morfol\u00f3gicos. Veamos c\u00f3mo utilizar algunos de ellos.\n", "\n", "A la hora de enfrentarnos al etiquetado morfol\u00f3gico de un texto, podemos adoptar una estrategia sencilla consistente en etiquetar por defecto todas las palabras con la misma categor\u00eda gramatical. Con NLTK podemos utilizar un `DefaultTagger` que etiquete todos los tokens como sustantivo. Las categor\u00eda **sustantivo singular** (`NN` en el esquema de etiquetas de Treebank) suele ser la m\u00e1s frecuente. Veamos qu\u00e9 tal funciona." ] }, { "cell_type": "code", "collapsed": false, "input": [ "defaultTagger = nltk.DefaultTagger('NN')\n", "print oracion1\n", "print defaultTagger.tag(oracion1)\n", "print defaultTagger.tag(oracion2)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "['This', 'is', 'the', 'lost', 'dog', 'I', 'found', 'at', 'the', 'park']\n", "[('This', 'NN'), ('is', 'NN'), ('the', 'NN'), ('lost', 'NN'), ('dog', 'NN'), ('I', 'NN'), ('found', 'NN'), ('at', 'NN'), ('the', 'NN'), ('park', 'NN')]\n", "[('The', 'NN'), ('progress', 'NN'), ('of', 'NN'), ('the', 'NN'), ('humankind', 'NN'), ('as', 'NN'), ('I', 'NN'), ('progress', 'NN')]\n" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Obviamente no funciona bien, pero ojo, en el ejemplo anterior con `oracion1` hemos etiquetado correctamente 2 de 10 tokens. Si lo evaluamos con un corpus m\u00e1s grande, como el conjunto de oraciones de Brown que ya tenemos, obtenemos una precisi\u00f3n superior al 13%:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "defaultTagger.evaluate(brown_tagged_sents)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "0.13089484257215028" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "el m\u00e9todo `.evaluate` que podemos ejecutar con cualquier etiquetador si especificamos como argumento una colecci\u00f3n de referencia que ya est\u00e9 etiquetada, nos devuelve un n\u00famero: la **precisi\u00f3n**. Esta precisi\u00f3n se calcula como el porcentaje de tokens correctamente etiquetados por el *tagger*, teniendo en cuenta el corpus especificado como referencia.\n", "\n", "### Peque\u00f1o par\u00e9ntesis\n", "\n", "Ejercicio que surge en clase para contar el n\u00famero de sustantivos contenidos en el corpus de noticias de Brown." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# creo dos contadores, y los pongo a cero\n", "nombres = 0\n", "total = 0\n", "\n", "# recorro las oraciones etiquetadas del corpus de noticias de Brown\n", "for oracion in brown_tagged_sents:\n", " for tupla in oracion:\n", " # en cada tupla que encuentro, a\u00f1ado +1 al contador de tokens totales \n", " total = total + 1\n", " if tupla[1] == \"NN\":\n", " # si el token en cuesti\u00f3n es, adem\u00e1s, un sustantivo singular (NN), a\u00f1ado +1 al contador de nombres\n", " nombres = nombres + 1\n", "\n", "# imprimimos las frecuencias\n", "print \"Tenemos\", nombres, \"nombres y\", total, \"palabras\"\n", "print nombres/float(total)\n", "\n", "# F\u00edjate que, en este caso, el porcentaje de sustantivos singulares en el corpus es el mismo que la precisi\u00f3n \n", "# alcanzada por nuestro etiquetador por defecto. \u00bfVes por qu\u00e9 es as\u00ed?" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Tenemos 13162 nombres y 100554 palabras\n", "0.130894842572\n" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Obviamente, los resultados son malos. Probemos con otras opciones m\u00e1s sofisticadas.\n", "\n", "### Etiquetador basado en Expresiones Regulares\n", "\n", "Las expresiones regulares consisten en un lenguaje formal que nos permite especificar cadenas de texto. Ya las hemos utilizado en ocasiones anteriores. Pues bien, ahora podemos probar a definir distintas categor\u00edas morfol\u00f3gicas a partir de patrones, al menos para fen\u00f3menos morfol\u00f3gicos regulares.\n", "\n", "A continuaci\u00f3n definimos la variable `patrones` como una lista de tuplas, cuyo primer elemento se corresponde con la expresion regular que queremos capturar y el segundo elemento como la categor\u00eda gramatical. Y a partir de estas expresiones regulares creamos un `RegexpTagger`. " ] }, { "cell_type": "code", "collapsed": true, "input": [ "patrones = [\n", " (r'[Aa]m$', 'VBP'), # irregular forms of 'to be' \n", " (r'[Aa]re$', 'VBP'), # \n", " (r'[Ii]s$', 'VBZ'), # \n", " (r'[Ww]as$', 'VBD'), # \n", " (r'[Ww]ere$', 'VBD'), # \n", " (r'[Bb]een$', 'VBN'), # \n", " (r'[Hh]ave$', 'VBP'), # irregular forms of 'to be' \n", " (r'[Hh]as$', 'VBZ'), # \n", " (r'[Hh]ad$', 'VBD'), #\n", " (r'^I$', 'PRP'), # personal pronouns\n", " (r'[Yy]ou$', 'PRP'), # \n", " (r'[Hh]e$', 'PRP'), # \n", " (r'[Ss]he$', 'PRP'), # \n", " (r'[Ii]t$', 'PRP'), # \n", " (r'[Tt]hey$', 'PRP'), # \n", " (r'[Aa]n?$', 'AT'), # \n", " (r'[Tt]he$', 'AT'), # \n", " (r'[Ww]h.+$', 'WP'), # wh- pronoun\n", " (r'.*ing$', 'VBG'), # gerunds\n", " (r'.*ed$', 'VBD'), # simple past\n", " (r'.*es$', 'VBZ'), # 3rd singular present\n", " (r'[Cc]an(not|n\\'t)?$', 'MD'), # modals\n", " (r'[Mm]ight$', 'MD'), # \n", " (r'[Mm]ay$', 'MD'), # \n", " (r'.+ould$', 'MD'), # modals: could, should, would\n", " (r'.*ly$', 'RB'), # adverbs\n", " (r'.*\\'s$', 'NN$'), # possessive nouns\n", " (r'.*s$', 'NNS'), # plural nouns\n", " (r'-?[0-9]+(.[0-9]+)?$', 'CD'), # cardinal numbers\n", " (r'^to$', 'TO'), # to \n", " (r'^in$', 'IN'), # in prep\n", " (r'^[A-Z]+([a-z])*$', 'NNP'), # proper nouns \n", " (r'.*', 'NN') # nouns (default)\n", "]\n", "\n", "regexTagger = nltk.RegexpTagger(patrones)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "print regexTagger.tag(u\"I was taking a sunbath in Alpedrete\".split())\n", "print regexTagger.tag(u\"She would have found 100 dollars in the bag\".split())\n", "\n", "print regexTagger.tag(u\"DSFdfdsfsd 1852 to dgdfgould fXXXdg in XXXfdg\".split())" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[(u'I', 'PRP'), (u'was', 'VBD'), (u'taking', 'VBG'), (u'a', 'AT'), (u'sunbath', 'NN'), (u'in', 'IN'), (u'Alpedrete', 'NNP')]\n", "[(u'She', 'PRP'), (u'would', 'MD'), (u'have', 'VBP'), (u'found', 'NN'), (u'100', 'CD'), (u'dollars', 'NNS'), (u'in', 'IN'), (u'the', 'AT'), (u'bag', 'NN')]\n", "[(u'DSFdfdsfsd', 'NNP'), (u'1852', 'CD'), (u'to', 'TO'), (u'dgdfgould', 'MD'), (u'fXXXdg', 'NN'), (u'in', 'IN'), (u'XXXfdg', 'NNP')]\n" ] } ], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cuando probamos a evaluarlo con un corpus de oraciones m\u00e1s grande, vemos que nuestra precisi\u00f3n sube por encima del 32%." ] }, { "cell_type": "code", "collapsed": false, "input": [ "regexTagger.evaluate(brown_tagged_sents)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 11, "text": [ "0.3260636076138194" ] } ], "prompt_number": 11 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Etiquetado basado en ngramas\n", "\n", "Antes hemos dicho que la funci\u00f3n `nltk.pos_tag` ten\u00eda un etiquetador morfol\u00f3gico que funcionaba con informaci\u00f3n estad\u00edstica. A continuaci\u00f3n vamos a reproducir, a peque\u00f1a escala, el proceso de entrenamiento de un etiquetador morfol\u00f3gico basado en aprendizaje autom\u00e1tico. \n", "\n", "En general, los sistemas de aprendizaje autom\u00e1tico funcionan del siguiente modo:\n", "\n", "* Se etiqueta manualmente una colecci\u00f3n de datos. Cuanto m\u00e1s grande y m\u00e1s representativa sea la colecci\u00f3n, mejores datos podremos extraer.\n", "* El sistema de aprendizaje procesa la colecci\u00f3n de entrenamiento y \"aprende\" a partir de los ejemplos observados. En el caso del etiquetado morfol\u00f3gico, el sistema calcula frecuencias y generaliza (de distintas maneras) cu\u00e1les son las categor\u00edas gramaticales m\u00e1s probables para cada palabra. \n", "* Una vez que el sistema ha sido entrenado, se eval\u00faa su rendimiento sobre datos no observados.\n", "\n", "Nosotros tenemos un peque\u00f1o corpus de ejemplos etiquetados: las oraciones del corpus de Brown de la categor\u00eda \"noticias\". Lo primero que necesitamos hacer es separar nuestros corpus de entrenamiento y test. En este caso, vamos a reservar el primer 90% de las oraciones para el entrenamiento (ser\u00e1n los ejemplos observados a partir de los cuales nuestro etiquetador aprender\u00e1) y vamos a dejar el 10% restante para comprobar qu\u00e9 tal funciona." ] }, { "cell_type": "code", "collapsed": false, "input": [ "print len(brown_tagged_sents)\n", "print (len(brown_tagged_sents) * 90) / 100" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "4623\n", "4160.7\n" ] } ], "prompt_number": 12 }, { "cell_type": "code", "collapsed": true, "input": [ "size = int(len(brown_tagged_sents) * 0.9)\n", "corpusEntrenamiento = brown_tagged_sents[:size]\n", "corpusTest = brown_tagged_sents[size:]\n", "\n", "print corpusEntrenamiento[0]\n", "print corpusTest[0]" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[('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'), ('.', '.')]\n", "[('But', 'CC'), ('in', 'IN'), ('all', 'ABN'), ('its', 'PP$'), ('175', 'CD'), ('years', 'NNS'), (',', ','), ('not', '*'), ('a', 'AT'), ('single', 'AP'), ('Negro', 'NP'), ('student', 'NN'), ('has', 'HVZ'), ('entered', 'VBN'), ('its', 'PP$'), ('classrooms', 'NNS'), ('.', '.')]\n" ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "A continuaci\u00f3n vamos a crear un etiquetador basado en unigramas (secuencias de una palabra o palabras sueltas) a trav\u00e9s de la clase `nltk.UnigramTagger`, proporcionando nuestro `corpusEntrenamiento` para que aprenda. Una vez entrenado, vamos a evaluar su rendimiento sobre `corpusTest`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "unigramTagger = nltk.UnigramTagger(corpusEntrenamiento)\n", "print unigramTagger.evaluate(corpusTest)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0.813714741354\n" ] } ], "prompt_number": 14 }, { "cell_type": "code", "collapsed": false, "input": [ "# \u00bfqu\u00e9 tal se etiquetan nuestras oraciones de ejemplo?\n", "print unigramTagger.tag(oracion1)\n", "print unigramTagger.tag(oracion2)\n", "print unigramTagger.tag(oracion3)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[('This', 'DT'), ('is', 'BEZ'), ('the', 'AT'), ('lost', 'VBD'), ('dog', 'NN'), ('I', 'PPSS'), ('found', 'VBN'), ('at', 'IN'), ('the', 'AT'), ('park', 'NN')]\n", "[('The', 'AT'), ('progress', 'NN'), ('of', 'IN'), ('the', 'AT'), ('humankind', None), ('as', 'CS'), ('I', 'PPSS'), ('progress', 'NN')]\n", "[('Green', 'JJ-TL'), ('colorless', None), ('ideas', 'NNS'), ('sleep', None), ('furiously', None)]\n" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Los etiquetadores basados en unigramas se construyen a partir del simple c\u00e1lculo de una distribuci\u00f3n de frecuencia para cada token (palabra) y asignan siempre la etiqueta morfol\u00f3gica m\u00e1s probable. En nuestro caso, esta estrategia funciona relativamente bien: el tagger supera el 81% de precisi\u00f3n. Sin embargo, esta aproximaci\u00f3n presenta numerosos problemas a la hora de etiquetar palabras hom\u00f3grafas (un mismo token funcionando con m\u00e1s de una categor\u00eda gramatical). Si probamos con nuestra `oracion2`, comprobamos que la segunda aparici\u00f3n de *progress* no es etiquetada correctamente.\n", "\n", "Intuitivamente, podemos pensar que sabr\u00edamos distinguir ambas categor\u00edas si tuvi\u00e9ramos en cuenta algo del contexto de aparici\u00f3n de las palabras: *progress* es un sustantivo cuando aparece despu\u00e9s del art\u00edculo *the* y es verbo cuando aparece tras un pronombre personal como *I*. Si en lugar de calcular frecuencias de unigramas, extendi\u00e9ramos los c\u00e1lculos a secuencias de dos o tres palabras, podr\u00edamos tener mejores resultados. Y precisamente por eso vamos a calcular distribuciones de frecuencias condicionales: asignaremos a cada token la categor\u00eda gramatical m\u00e1s frecuente teniendo en cuenta la categor\u00eda gramatical de la(s) palabra(s) inmediatamente anterior(es). \n", "\n", "Creamos un par de etiquetadores basado en bigramas (secuencias de dos palabras) o trigramas (secuencias de tres palabras) a trav\u00e9s de las clases `nltk.BigramTagger` y `nltk.TrigramTagger`. Y los probamos con nuestra `oracion2`." ] }, { "cell_type": "code", "collapsed": true, "input": [ "bigramTagger = nltk.BigramTagger(corpusEntrenamiento)\n", "trigramTagger = nltk.TrigramTagger(corpusEntrenamiento)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 16 }, { "cell_type": "code", "collapsed": false, "input": [ "print bigramTagger.tag(oracion2)\n", "print trigramTagger.tag(oracion2)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[('The', 'AT'), ('progress', None), ('of', None), ('the', None), ('humankind', None), ('as', None), ('I', None), ('progress', None)]\n", "[('The', 'AT'), ('progress', None), ('of', None), ('the', None), ('humankind', None), ('as', None), ('I', None), ('progress', None)]\n" ] } ], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como se ve en los ejemplos, los resultados son desastrosos. La mayor\u00eda de los tokens se quedan sin etiqueta y se muestran como `None`.\n", "\n", "Si los evaluamos con nuestra colecci\u00f3n de test, vemos que apenas superan el 10% de precisi\u00f3n. Peores resultados que nuestro `DefaultTagger`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "print bigramTagger.evaluate(corpusTest)\n", "print trigramTagger.evaluate(corpusTest)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0.103059902322\n", "0.0628924548988" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n" ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\u00bfPor qu\u00e9 ocurre esto? La intuici\u00f3n no nos enga\u00f1a, y es verdad que si calculamos distribuciones de frecuencia condicionales teniendo en cuenta secuencias de palabras m\u00e1s largas, nuestros datos ser\u00e1n m\u00e1s finos. Sin embargo, cuando consideramos secuencias de tokens m\u00e1s largos nos arriesgamos a que dichas secuencias no aparezcan como tales en el corpus de entrenamiento. \n", "\n", "En el ejemplo de `oracion2`, nuestro `bigramTagger` es incapaz de etiquetar la palabra *progress* porque no ha encontrado en el corpus de entrenamiento ni el bigrama (**The, progress**) ni (**I, progress**). Obviamente, nuestro `trigramTagger` tampoco ha encontrado los trigramas (**`INICIO_DE_ORACI\u00d3N`, The, progress**) o (**as, I, progress**). Si esas secuencias no aparecen en el corpus de entrenamiento, no hay nada que aprender. \n", "\n", "\n", "\n", "### Combinamos etiquetadores\n", "\n", "En estos casos, la soluci\u00f3n m\u00e1s satisfactoria consiste en combinar de manera incremental la potencia de todos nuestros etiquetadores. Vamos a crear nuevos taggers que utilicen otros como respaldo. \n", "\n", "Utilizaremos un tagger de trigramas que, cuando no tenga respuesta para etiquetar un determinado token, utilizar\u00e1 como respaldo el tagger de bigramas. A su vez, el tagger de bigramas tirar\u00e1 del de unigramas cuando no tenga respuesta. Por \u00faltimo, el de unigramas utilizar\u00e1 como respaldo el tagger de expresiones regulares que definimos antes. De esta manera aumentamos la precisi\u00f3n hasta casi el 87%." ] }, { "cell_type": "code", "collapsed": false, "input": [ "unigramTagger = nltk.UnigramTagger(corpusEntrenamiento, backoff=regexTagger)\n", "bigramTagger = nltk.BigramTagger(corpusEntrenamiento, backoff=unigramTagger)\n", "trigramTagger = nltk.TrigramTagger(corpusEntrenamiento, backoff=bigramTagger)\n", "trigramTagger.evaluate(corpusTest)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 19, "text": [ "0.8677364696501545" ] } ], "prompt_number": 19 }, { "cell_type": "code", "collapsed": false, "input": [ "print trigramTagger.tag(oracion1)\n", "print trigramTagger.tag(oracion2)\n", "print trigramTagger.tag(oracion3)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[('This', 'DT'), ('is', 'BEZ'), ('the', 'AT'), ('lost', 'VBD'), ('dog', 'NN'), ('I', 'PPSS'), ('found', 'VBD'), ('at', 'IN'), ('the', 'AT'), ('park', 'NN')]\n", "[('The', 'AT'), ('progress', 'NN'), ('of', 'IN'), ('the', 'AT'), ('humankind', 'NN'), ('as', 'CS'), ('I', 'PPSS'), ('progress', 'NN')]\n", "[('Green', 'JJ-TL'), ('colorless', 'NNS'), ('ideas', 'NNS'), ('sleep', 'NN'), ('furiously', 'RB')]\n" ] } ], "prompt_number": 20 }, { "cell_type": "code", "collapsed": false, "input": [ "print nltk.pos_tag(oracion2)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[('The', 'DT'), ('progress', 'NN'), ('of', 'IN'), ('the', 'DT'), ('humankind', 'NN'), ('as', 'IN'), ('I', 'PRP'), ('progress', 'VBP')]\n" ] } ], "prompt_number": 21 } ], "metadata": {} } ] }