{"id":98,"date":"2026-03-09T16:24:47","date_gmt":"2026-03-09T16:24:47","guid":{"rendered":"https:\/\/pulki.es\/blog\/?p=98"},"modified":"2026-03-09T16:24:47","modified_gmt":"2026-03-09T16:24:47","slug":"large-language-model-llm-from-scratch-clasificacion","status":"publish","type":"post","link":"https:\/\/pulki.es\/blog\/index.php\/2026\/03\/09\/large-language-model-llm-from-scratch-clasificacion\/","title":{"rendered":"Large Language Model (LLM) from scratch. Clasificaci\u00f3n."},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">En la entrada anterior vimos n-gramas, cuyo concepto es fundamental para  entender la generaci\u00f3n de texto, caracter\u00edstica que ser\u00e1 clave para construir el LLM que tenemos en mente. Como mencionamos en una entrada pasada, si bien el algoritmo de generaci\u00f3n de texto es importante a la hora de desarrollar un LLM, los datos en que el modelo es entrenado son igualmente importantes. Es por esto que aprender a clasificar textos es relevante para este proyecto. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En esta nueva publicaci\u00f3n veremos en detalle la regresi\u00f3n log\u00edstica, y c\u00f3mo se puede aplicar a la clasificaci\u00f3n de texto. Como es habitual, usaremos como referencia el libro de <strong>Jurafsky y Martin, 2025<\/strong> (cap\u00edtulo 4).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>\u00bfQu\u00e9 entenderemos por clasificar?<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">De acuerdo con <strong>Jurafsky y Martin (2025)<\/strong>, el objetivo principal de clasificar, cuando se trata de un texto, es extraer caracter\u00edsticas relevantes de este y, a partir de ellas, asignarlo a una categor\u00eda. A modo de ejemplo, supongamos que queremos etiquetar un texto entre dos categor\u00edas mutuamente excluyentes. Concretamente, deseamos determinar si est\u00e1 escrito en castellano medieval. En ese caso, tendremos una variable llamada <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>m<\/mi><mi>e<\/mi><mi>d<\/mi><mi>i<\/mi><mi>e<\/mi><mi>v<\/mi><mi>a<\/mi><msub><mi>l<\/mi><mi>i<\/mi><\/msub><mo>=<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">medieval_i = 1<\/annotation><\/semantics><\/math> si el texto <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>i<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math> est\u00e1 escrito en castellano medieval, y <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>m<\/mi><mi>e<\/mi><mi>d<\/mi><mi>i<\/mi><mi>e<\/mi><mi>v<\/mi><mi>a<\/mi><msub><mi>l<\/mi><mi>i<\/mi><\/msub><mo>=<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">medieval_i = 0<\/annotation><\/semantics><\/math> en caso contrario. Ahora bien, en consecuencia con esta definici\u00f3n, debemos determinar cu\u00e1les ser\u00e1n las caracter\u00edsticas relevantes para la clasificaci\u00f3n. En este sentido, es <a href=\"https:\/\/rhle.es\/index.php\/revista\/article\/view\/299\">sabido<\/a> que en castellano medieval, \u201csuso\u201d y \u201cyuso\u201d eran adverbios de lugar com\u00fanmente utilizados. Por tanto, una variable aparentemente v\u00e1lida para identificar si un texto es medieval podr\u00eda ser el n\u00famero de veces que aparecen las palabras \u201csuso\u201d o \u201cyuso\u201d. Llamaremos a esta variable <code>yu_su<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En este punto nos enfrentamos a los primeros desaf\u00edos. Por una parte, que esas palabras no aparezcan no implica que el texto no sea medieval, y que aparezcan tampoco implica necesariamente que lo sea. En otras palabras, si nos bas\u00e1ramos s\u00f3lo en esta variable, podr\u00edamos cometer tanto <em>falsos positivos<\/em> como <em>falsos negativos<\/em>. Por este motivo, es preciso a\u00f1adir variables que nos entreguen m\u00e1s indicios sobre la naturaleza del texto. Para ello, podr\u00edamos pensar en una variable binaria, llamada <code>cont_words<\/code>, que tome el valor de 1 si el texto contiene palabras como <em>internet<\/em>, <em>blog<\/em>, <em>ordenador<\/em>, <em>tel\u00e9fono<\/em>, <em>electr\u00f3nico<\/em>, <em>f\u00fatbol<\/em>, <em>globalizaci\u00f3n<\/em>, etc., es decir, palabras que no exist\u00edan en el castellano medieval o que, al menos, no eran comunes. En la pr\u00e1ctica, podr\u00edamos repetir este ejercicio incorporando m\u00faltiples variables. No obstante, para efectos de este ejemplo consideraremos s\u00f3lo estas dos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A partir de aqu\u00ed, es preciso definir un umbral de decisi\u00f3n para discernir si el texto es medieval o no. A modo de ejemplo, podemos imponer la siguiente regla:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Si <code>yu_su<\/code> es mayor que 5 y <code>cont_words<\/code> es igual a 0, entonces el texto es medieval.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">En este caso, 5 y 0 son umbrales arbitrarios elegidos \u00fanicamente para ilustrar el ejemplo. Sin embargo, si analiz\u00e1ramos un conjunto grande de textos medievales y no medievales, podr\u00edamos encontrar puntos de corte relativamente buenos para discriminar entre ambas clases.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Claro est\u00e1 que, adem\u00e1s del criterio anterior, podr\u00edamos proponer muchas otras reglas. No obstante, en esta entrada nos enfocaremos en la <strong>regresi\u00f3n log\u00edstica binaria<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Regresi\u00f3n binaria log\u00edstica<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Supongamos que disponemos de un vector de caracter\u00edsticas <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>x<\/mi><mo>=<\/mo><mo stretchy=\"false\">[<\/mo><msub><mi>x<\/mi><mn>1<\/mn><\/msub><mo separator=\"true\">,<\/mo><msub><mi>x<\/mi><mn>2<\/mn><\/msub><mo stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">x=[x_1,x_2]<\/annotation><\/semantics><\/math>, donde <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msub><mi>x<\/mi><mn>1<\/mn><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">x_1<\/annotation><\/semantics><\/math>\u200b corresponde al n\u00famero total de veces que aparece la palabra \u201cyuso\u201d o \u201csuso\u201d (<code>yu_su<\/code>), y <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msub><mi>x<\/mi><mn>2<\/mn><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">x_2<\/annotation><\/semantics><\/math>\u200b es una variable binaria que indica si existen palabras contempor\u00e1neas (<code>cont_words<\/code>). Dado ese vector, nuestro objetivo es calcular <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>P<\/mi><mo stretchy=\"false\">(<\/mo><mtext>medieval<\/mtext><mo>=<\/mo><mn>1<\/mn><mo>\u2223<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P(\\text{medieval}=1 \\mid x)<\/annotation><\/semantics><\/math>. Esta cantidad nos dir\u00e1 qu\u00e9 tan probable es que el texto de entrada sea medieval, condicional a las caracter\u00edsticas extra\u00eddas. Para obtener esta probabilidad, la regresi\u00f3n log\u00edstica supone la siguiente estructura:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>z<\/mi><mo>=<\/mo><mi>b<\/mi><mo>+<\/mo><msub><mi>w<\/mi><mn>1<\/mn><\/msub><msub><mi>x<\/mi><mn>1<\/mn><\/msub><mo>+<\/mo><msub><mi>w<\/mi><mn>2<\/mn><\/msub><msub><mi>x<\/mi><mn>2<\/mn><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">z = b + w_1x_1 + w_2x_2<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">donde <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msub><mi>w<\/mi><mi>i<\/mi><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">w_i<\/annotation><\/semantics><\/math> representa el peso asociado a cada variable <math data-latex=\"i\"><semantics><mi>i<\/mi><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math> al momento de determinar si el texto es medieval o no, y <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>b<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">b<\/annotation><\/semantics><\/math> corresponde al t\u00e9rmino de sesgo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Puesto que <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>z<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">z<\/annotation><\/semantics><\/math> puede tomar cualquier valor real, no necesariamente estar\u00e1 entre 0 y 1. Por esa raz\u00f3n, no podemos interpretarlo directamente como una probabilidad. Para hacerlo, lo pasaremos por la funci\u00f3n sigmoide, es decir,<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mn>1<\/mn><mo>+<\/mo><msup><mi>e<\/mi><mrow><mo>\u2212<\/mo><mi>z<\/mi><\/mrow><\/msup><\/mrow><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">\\sigma(z)=\\frac{1}{1+e^{-z}}<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esto asegura que<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><munder><mrow><mi>lim<\/mi><mo>\u2061<\/mo><\/mrow><mrow><mi>z<\/mi><mo>\u2192<\/mo><mi mathvariant=\"normal\">\u221e<\/mi><\/mrow><\/munder><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mn>1<\/mn><mspace width=\"2em\"><\/mspace><mtext>y<\/mtext><mspace width=\"2em\"><\/mspace><munder><mrow><mi>lim<\/mi><mo>\u2061<\/mo><\/mrow><mrow><mi>z<\/mi><mo>\u2192<\/mo><mo>\u2212<\/mo><mi mathvariant=\"normal\">\u221e<\/mi><\/mrow><\/munder><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\lim_{z\\to\\infty}\\sigma(z)=1 \\qquad \\text{y} \\qquad \\lim_{z\\to-\\infty}\\sigma(z)=0<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Por tanto, <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\sigma(z)<\/annotation><\/semantics><\/math> siempre tomar\u00e1 valores entre 0 y 1. Adem\u00e1s, nuestras probabilidades deben cumplir que<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>P<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>=<\/mo><mn>1<\/mn><mo stretchy=\"false\">)<\/mo><mo>+<\/mo><mi>P<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>=<\/mo><mn>0<\/mn><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">P(y=1)+P(y=0)=1<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">de modo que podemos definir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>P<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>=<\/mo><mn>1<\/mn><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><mspace width=\"2em\"><\/mspace><mtext>y<\/mtext><mspace width=\"2em\"><\/mspace><mi>P<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>=<\/mo><mn>0<\/mn><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P(y=1)=\\sigma(z) \\qquad \\text{y} \\qquad P(y=0)=1-\\sigma(z)<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esto implica que<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>P<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>=<\/mo><mn>1<\/mn><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mn>1<\/mn><mo>+<\/mo><msup><mi>e<\/mi><mrow><mo>\u2212<\/mo><mi>z<\/mi><\/mrow><\/msup><\/mrow><\/mfrac><mspace width=\"2em\"><\/mspace><mtext>y<\/mtext><mspace width=\"2em\"><\/mspace><mi>P<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>=<\/mo><mn>0<\/mn><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mfrac><msup><mi>e<\/mi><mrow><mo>\u2212<\/mo><mi>z<\/mi><\/mrow><\/msup><mrow><mn>1<\/mn><mo>+<\/mo><msup><mi>e<\/mi><mrow><mo>\u2212<\/mo><mi>z<\/mi><\/mrow><\/msup><\/mrow><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">P(y=1)=\\frac{1}{1+e^{-z}} \\qquad \\text{y} \\qquad P(y=0)=\\frac{e^{-z}}{1+e^{-z}}<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Con esto presente, ya podemos calcular <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi mathvariant=\"double-struck\">P<\/mi><mo stretchy=\"false\">(<\/mo><mtext>medieval<\/mtext><mo>=<\/mo><mn>1<\/mn><mo>\u2223<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\mathbb{P}(\\text{medieval}=1\\mid x)<\/annotation><\/semantics><\/math>. Ahora solo falta definir el umbral que determinar\u00e1 cu\u00e1ndo un texto ser\u00e1 clasificado como medieval o no. Usualmente, se considera que una probabilidad superior a <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mn>0.5<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">0.5<\/annotation><\/semantics><\/math> es suficiente para asignar el texto a la clase positiva, es decir,<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>D<\/mi><mo stretchy=\"false\">(<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mrow><mo fence=\"true\">{<\/mo><mtable rowspacing=\"0.36em\" columnalign=\"left left\" columnspacing=\"1em\"><mtr><mtd><mstyle scriptlevel=\"0\" displaystyle=\"false\"><mn>1<\/mn><\/mstyle><\/mtd><mtd><mstyle scriptlevel=\"0\" displaystyle=\"false\"><mrow><mtext>si&nbsp;<\/mtext><mi mathvariant=\"double-struck\">P<\/mi><mo stretchy=\"false\">(<\/mo><mtext>medieval<\/mtext><mo>=<\/mo><mn>1<\/mn><mo>\u2223<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><mo>&gt;<\/mo><mn>0.5<\/mn><\/mrow><\/mstyle><\/mtd><\/mtr><mtr><mtd><mstyle scriptlevel=\"0\" displaystyle=\"false\"><mn>0<\/mn><\/mstyle><\/mtd><mtd><mstyle scriptlevel=\"0\" displaystyle=\"false\"><mtext>en&nbsp;otro&nbsp;caso<\/mtext><\/mstyle><\/mtd><\/mtr><\/mtable><\/mrow><\/mrow><annotation encoding=\"application\/x-tex\">D(x)= \\begin{cases} 1 &amp; \\text{si } \\mathbb{P}(\\text{medieval}=1 \\mid x)&gt;0.5 \\\\ 0 &amp; \\text{en otro caso} \\end{cases}<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Definido el umbral, ya estamos en condiciones de aprender los par\u00e1metros del modelo. En la siguiente secci\u00f3n veremos c\u00f3mo hacerlo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Aprendiendo los par\u00e1metros del modelo<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Queremos elegir unos par\u00e1metros tales que, si el texto que estamos analizando es medieval, es decir, si <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>m<\/mi><mi>e<\/mi><mi>d<\/mi><mi>i<\/mi><mi>e<\/mi><mi>v<\/mi><mi>a<\/mi><mi>l<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">medieval=1<\/annotation><\/semantics><\/math>, la probabilidad estimada sea lo m\u00e1s alta posible, esto es, <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi mathvariant=\"double-struck\">P<\/mi><mo stretchy=\"false\">(<\/mo><mtext>medieval<\/mtext><mo>=<\/mo><mn>1<\/mn><mo>\u2223<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><mo>\u2192<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\mathbb{P}(\\text{medieval}=1\\mid x)\\to 1<\/annotation><\/semantics><\/math>. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para lograrlo, com\u00fanmente se utiliza una funci\u00f3n que mide qu\u00e9 tan distinta es la predicci\u00f3n del modelo respecto del valor real. Esta funci\u00f3n, llamada <strong>funci\u00f3n de p\u00e9rdida<\/strong>, debe ser lo m\u00e1s peque\u00f1a posible. En el caso de la regresi\u00f3n log\u00edstica, la p\u00e9rdida que se usa habitualmente es la <strong>cross-entropy loss<\/strong>, que tambi\u00e9n puede interpretarse como la <strong>negative log likelihood<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En t\u00e9rminos matem\u00e1ticos, la funci\u00f3n de p\u00e9rdida puede escribirse como <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>L<\/mi><mo stretchy=\"false\">(<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo separator=\"true\">,<\/mo><mi>y<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">L(\\hat{y},y)<\/annotation><\/semantics><\/math>, donde<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mover><mi>y<\/mi><mo stretchy=\"false\" class=\"tml-xshift\" style=\"math-style:normal;math-depth:0;\">^<\/mo><\/mover><mo>=<\/mo><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>w<\/mi><mn>1<\/mn><\/msub><msub><mi>x<\/mi><mn>1<\/mn><\/msub><mo>+<\/mo><msub><mi>w<\/mi><mn>2<\/mn><\/msub><msub><mi>x<\/mi><mn>2<\/mn><\/msub><mo>+<\/mo><mi>b<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\hat{y} = \\sigma(w_{1}x_{1} + w_{2}x_{2} + b)<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">En este caso, la estrategia que emplearemos para encontrar <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msub><mover accent=\"true\"><mi>w<\/mi><mo>^<\/mo><\/mover><mn>1<\/mn><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">\\hat{w}_1<\/annotation><\/semantics><\/math>, <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msub><mover accent=\"true\"><mi>w<\/mi><mo>^<\/mo><\/mover><mn>2<\/mn><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">\\hat{w}_2<\/annotation><\/semantics><\/math> y <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mover accent=\"true\"><mi>b<\/mi><mo>^<\/mo><\/mover><\/mrow><annotation encoding=\"application\/x-tex\">\\hat{b}<\/annotation><\/semantics><\/math> ser\u00e1 la m\u00e1xima verosimilitud. La idea es simple: escogeremos los par\u00e1metros que hagan lo m\u00e1s probable posible observar las etiquetas correctas en los datos de entrenamiento. Como la log-verosimilitud debe maximizarse, y la funci\u00f3n de p\u00e9rdida se define como su negativo, maximizar la verosimilitud es equivalente a minimizar la funci\u00f3n de p\u00e9rdida.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para derivar la funci\u00f3n de p\u00e9rdida, debemos empezar por expresar la probabilidad de una observaci\u00f3n individual. Puesto que tenemos una variable binaria, dicha probabilidad viene dada por:<math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>p<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>\u2223<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><msup><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mrow><mtext>\u2009<\/mtext><mi>y<\/mi><\/mrow><\/msup><mo stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><msup><mo stretchy=\"false\">)<\/mo><mrow><mn>1<\/mn><mo>\u2212<\/mo><mi>y<\/mi><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">p(y\\mid x)=\\hat{y}^{\\,y}(1-\\hat{y})^{1-y}<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Al tomar logaritmos obtenemos:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>log<\/mi><mo>\u2061<\/mo><mi>p<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>\u2223<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mi>y<\/mi><mi>log<\/mi><mo>\u2061<\/mo><mo stretchy=\"false\">(<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo stretchy=\"false\">)<\/mo><mo>+<\/mo><mo stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mi>y<\/mi><mo stretchy=\"false\">)<\/mo><mi>log<\/mi><mo>\u2061<\/mo><mo stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\log p(y\\mid x)=y\\log(\\hat{y})+(1-y)\\log(1-\\hat{y})<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esta es la log-verosimilitud que queremos maximizar. Sin embargo, como en aprendizaje autom\u00e1tico solemos trabajar minimizando una p\u00e9rdida, definimos:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>L<\/mi><mo stretchy=\"false\">(<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo separator=\"true\">,<\/mo><mi>y<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mo>\u2212<\/mo><mi>log<\/mi><mo>\u2061<\/mo><mi>p<\/mi><mo stretchy=\"false\">(<\/mo><mi>y<\/mi><mo>\u2223<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">L(\\hat{y},y)=-\\log p(y\\mid x)<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reemplazando, llegamos a:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>L<\/mi><mo stretchy=\"false\">(<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo separator=\"true\">,<\/mo><mi>y<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mo>\u2212<\/mo><mo fence=\"false\" stretchy=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">(<\/mo><mi>y<\/mi><mi>log<\/mi><mo>\u2061<\/mo><mo stretchy=\"false\">(<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo stretchy=\"false\">)<\/mo><mo>+<\/mo><mo stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mi>y<\/mi><mo stretchy=\"false\">)<\/mo><mi>log<\/mi><mo>\u2061<\/mo><mo stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo stretchy=\"false\">)<\/mo><mo fence=\"false\" stretchy=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">L(\\hat{y},y)= -\\big(y\\log(\\hat{y})+(1-y)\\log(1-\\hat{y})\\big)<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">y, usando <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo>=<\/mo><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mi>x<\/mi><mo>+<\/mo><mi>b<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\hat{y}=\\sigma(wx+b)<\/annotation><\/semantics><\/math>, obtenemos finalmente:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>L<\/mi><mo stretchy=\"false\">(<\/mo><mover accent=\"true\"><mi>y<\/mi><mo>^<\/mo><\/mover><mo separator=\"true\">,<\/mo><mi>y<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><mo>\u2212<\/mo><mo fence=\"false\" stretchy=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">(<\/mo><mi>y<\/mi><mi>log<\/mi><mo>\u2061<\/mo><mo stretchy=\"false\">(<\/mo><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mi>x<\/mi><mo>+<\/mo><mi>b<\/mi><mo stretchy=\"false\">)<\/mo><mo stretchy=\"false\">)<\/mo><mo>+<\/mo><mo stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mi>y<\/mi><mo stretchy=\"false\">)<\/mo><mi>log<\/mi><mo>\u2061<\/mo><mo stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mi>\u03c3<\/mi><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mi>x<\/mi><mo>+<\/mo><mi>b<\/mi><mo stretchy=\"false\">)<\/mo><mo stretchy=\"false\">)<\/mo><mo fence=\"false\" stretchy=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">L(\\hat{y},y)= -\\big(y\\log(\\sigma(wx+b))+(1-y)\\log(1-\\sigma(wx+b))\\big)<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esta es la funci\u00f3n de p\u00e9rdida que usaremos para aprender los par\u00e1metros del modelo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En este punto, estamos en condiciones de trabajar con los datos, y as\u00ed obtener <math data-latex=\"\\hat{w_{1}}\"><semantics><mover><msub><mi>w<\/mi><mn>1<\/mn><\/msub><mo stretchy=\"false\" style=\"math-style:normal;math-depth:0;\">^<\/mo><\/mover><annotation encoding=\"application\/x-tex\">\\hat{w_{1}}<\/annotation><\/semantics><\/math>, <math data-latex=\"\\hat{w_{2}}\"><semantics><mover><msub><mi>w<\/mi><mn>2<\/mn><\/msub><mo stretchy=\"false\" style=\"math-style:normal;math-depth:0;\">^<\/mo><\/mover><annotation encoding=\"application\/x-tex\">\\hat{w_{2}}<\/annotation><\/semantics><\/math>, <math data-latex=\"\\hat{w_{3}}\"><semantics><mover><msub><mi>w<\/mi><mn>3<\/mn><\/msub><mo stretchy=\"false\" style=\"math-style:normal;math-depth:0;\">^<\/mo><\/mover><annotation encoding=\"application\/x-tex\">\\hat{w_{3}}<\/annotation><\/semantics><\/math>, y <math data-latex=\"\\hat{b}\"><semantics><mover><mi>b<\/mi><mo stretchy=\"false\" class=\"tml-capshift\" style=\"math-style:normal;math-depth:0;\">^<\/mo><\/mover><annotation encoding=\"application\/x-tex\">\\hat{b}<\/annotation><\/semantics><\/math>. Para esto, extraeremos 20 textos escritos en castellano medieval de esta <a href=\"https:\/\/www.hispanicseminary.org\/textconc-es.htm\">web<\/a>, y 20 textos contempor\u00e1neos: 19 period\u00edsticos y un art\u00edculo acad\u00e9mico referente a las palabras \u201cyuso\u201d y \u201csuso\u201d. La siguiente tabla muestra el valor de cada variable:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><code>medieval<\/code><\/td><td><code>yu_su<\/code><\/td><td><code>cont_words<\/code><\/td><\/tr><tr><td>1<\/td><td>74<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>18<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>3<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>8<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>19<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>6<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>57<\/td><td>1<\/td><\/tr><tr><td>1<\/td><td>26<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>166<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>2<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>396<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>1<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>65<\/td><td>1<\/td><\/tr><tr><td>1<\/td><td>1<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>62<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>8<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>11<\/td><td>1<\/td><\/tr><tr><td>1<\/td><td>3<\/td><td>1<\/td><\/tr><tr><td>1<\/td><td>65<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>194<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>1<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Curiosamente, <code>cont_words<\/code>, que es una <em>dummy<\/em> cuyo valor es 1 si el texto contiene palabras contempor\u00e1neas, toma el valor 1 con mayor frecuencia en los textos medievales. Esto se debe la palabra ordenador aparece en esta clase de textos, como se puede ver en el siguiente fragmento de castellano medieval:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">se&lt;n&gt;naladamientre q&lt;ue&gt; fue mayor dellos;<br>de qui tiene logar. E aun onrran a<br>toda la xp&lt;ist&gt;iandat; cuya cabe\u00e7a es. cuemo <strong>ordenador<\/strong> &amp; mantenedor de la ley.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Adem\u00e1s, podemos observar que existe un texto no medieval cuyo conteo de palabras \u00abyuso\u00bb o \u00absuso\u00bb es alto. Como mencionamos anteriormente, esa observaci\u00f3n corresponde al art\u00edculo acad\u00e9mico que analiza ambas palabras. Dicho esto, a continuaci\u00f3n, mostramos los resultados de la estimaci\u00f3n:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"142\" src=\"https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image.png\" alt=\"\" class=\"wp-image-118\" srcset=\"https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image.png 720w, https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-300x59.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Los pesos de <code>yu_su<\/code> y <code>cont_words<\/code> son positivos, eso quiere decir que aportan positivamente a la probabilidad de que un texto sea medieval. En la siguiente secci\u00f3n veremos c\u00f3mo emplear la informaci\u00f3n de la tabla para predecir si un texto fuera de nuestro conjunto de entrenamiento es medieval.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Predicci\u00f3n de categor\u00eda<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Con la informaci\u00f3n de la tabla, podemos escribir la siguiente ecuaci\u00f3n:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>P<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>m<\/mi><mi>e<\/mi><mi>d<\/mi><mi>i<\/mi><mi>e<\/mi><mi>v<\/mi><mi>a<\/mi><mi>l<\/mi><mo>=<\/mo><mn>1<\/mn><mi>|<\/mi><mi>x<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mn>1<\/mn><mo>+<\/mo><msup><mi>e<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mn>0.01<\/mn><mo>\u2217<\/mo><mi>y<\/mi><mi>u<\/mi><mi>_<\/mi><mi>s<\/mi><mi>u<\/mi><mo>+<\/mo><mn>1.53<\/mn><mo>\u2217<\/mo><mi>c<\/mi><mi>o<\/mi><mi>n<\/mi><mi>t<\/mi><mi>_<\/mi><mi>w<\/mi><mi>o<\/mi><mi>r<\/mi><mi>d<\/mi><mi>s<\/mi><mo>\u2212<\/mo><mn>0.47<\/mn><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/msup><\/mrow><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">P(medieval=1|x) = \\frac{1}{1+e^{-(0.01*yu\\_su + 1.53*cont\\_words -0.47)}}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">En este punto, s\u00f3lo necesitamos calcular <code>yu_su<\/code> y <code>cont_words<\/code> para el texto que queramos analizar, que en este caso ser\u00e1 el <strong><a href=\"https:\/\/www.hispanicseminary.org\/t&amp;c\/poe\/index-es.htm\">Libro de Apolonio<\/a><\/strong>, escrito en castellano medieval. Sus valores son:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>yu_su<\/code><\/td><td><code>cont_words<\/code><\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">1<\/td><td>0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Puesto que este texto solo contiene la palabra \u201csuso\u201d, y <code>cont_words<\/code> es cero, la probabilidad de que el texto sea medieval es relativamente baja con respecto al umbral (0.386), por tanto, el clasificador lo etiqueta como texto no medieval.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ahora, por el contrario, si tomamos un texto period\u00edstico sobre deportes, tenemos los siguientes valores:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><code>yu_su<\/code><\/td><td><code>cont_words<\/code><\/td><\/tr><tr><td>0<\/td><td>1<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Debido a que en este texto aparece la palabra f\u00fatbol, <code>cont_words <\/code>vale 1, y la probabilidad de que el texto sea medieval se dispara (0.741), clasificando nuevamente el texto de forma err\u00f3nea, es decir, esta vez es etiquetado como medieval.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Claramente, en este caso necesitamos a\u00f1adir m\u00e1s variables, y revisar los criterios actuales, entre ellos, definir uno en el que el peso de <code>cont_words<\/code> no sea positivo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Como pudimos observar en este ejercicio, el rendimiento del modelo es visiblemente deficiente, no obstante, en muchos casos, calificar su performance no es trivial. Por este motivo, en la siguiente secci\u00f3n introduciremos un m\u00e9todo para evaluar el desempe\u00f1o del clasificador.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Evaluaci\u00f3n<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de abordar las estrategias de evaluaci\u00f3n, conviene definir qu\u00e9 entendemos por <em>gold label<\/em>. En nuestro caso, el objetivo del modelo es clasificar si un texto es medieval o no. Para ello, la regresi\u00f3n log\u00edstica no entrega directamente una etiqueta definitiva, sino una probabilidad <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>P<\/mi><mo stretchy=\"false\">(<\/mo><mtext>medieval<\/mtext><mo>=<\/mo><mn>1<\/mn><mo>\u2223<\/mo><mi>x<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P(\\text{medieval}=1 \\mid x)<\/annotation><\/semantics><\/math>, es decir, una estimaci\u00f3n de cu\u00e1n probable es que el texto pertenezca a la clase \u201cmedieval\u201d. Sin embargo, desde el punto de vista del conjunto de datos, cada texto s\u00ed tiene una clasificaci\u00f3n de referencia: o es medieval, o no lo es. Esa etiqueta correcta, asignada por criterio humano, es lo que llamamos <em>gold label<\/em>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Una m\u00e9trica para evaluar el desempe\u00f1o de nuestro modelo, es construir una <strong>matriz de confusi\u00f3n<\/strong>, que en pocas palabras grafica como se desempe\u00f1a en t\u00e9rminos de los <em>gold labels<\/em> y las etiquetas que este predice. La siguiente imagen muestra c\u00f3mo se ver\u00eda esta matriz:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-2-1024x559.png\" alt=\"\" class=\"wp-image-123\" srcset=\"https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-2-1024x559.png 1024w, https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-2-300x164.png 300w, https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-2-768x419.png 768w, https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-2.png 1408w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">En esta matriz podemos observar tres m\u00e9tricas importantes: <strong>precisi\u00f3n<\/strong>, <strong>recall<\/strong> y <strong>accuracy<\/strong>. Usualmente, <strong>accuracy<\/strong> no es una buena m\u00e9trica por dos motivos. El primero es que, si las categor\u00edas est\u00e1n desbalanceadas, es decir, si hay muchos m\u00e1s textos no medievales que textos medievales, un clasificador que prediga siempre la clase mayoritaria podr\u00eda obtener un <strong>accuracy <\/strong>alto, aun cuando fuera incapaz de detectar textos medievales. Si bien en nuestra aplicaci\u00f3n estamos trabajando con una cantidad balanceada de textos medievales y no medievales, en la pr\u00e1ctica esto no siempre ocurre. El segundo problema de esta m\u00e9trica es que combina en un solo valor los verdaderos positivos (VP) y los verdaderos negativos (VN). Esto puede ser problem\u00e1tico si queremos que el modelo sea especialmente bueno encontrando textos medievales, ya que, mirando \u00fanicamente el <strong>accuracy<\/strong>, no podemos distinguir si el modelo destaca por identificar bien los textos medievales o simplemente por clasificar correctamente los textos no medievales.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Por este motivo, <strong>precisi\u00f3n <\/strong>y <strong>recall<\/strong> suelen ser m\u00e9tricas m\u00e1s informativas que <strong>accuracy<\/strong>. En nuestro caso, la precisi\u00f3n ser\u00eda especialmente \u00fatil si queremos que, cada vez que el modelo clasifique un texto como medieval, esa predicci\u00f3n sea confiable. En otras palabras, la precisi\u00f3n mide qu\u00e9 proporci\u00f3n de los textos etiquetados por el modelo como medievales efectivamente pertenece a esa categor\u00eda.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No obstante, <strong>recall<\/strong> tambi\u00e9n puede ser de gran utilidad, ya que mide qu\u00e9 proporci\u00f3n de los textos medievales reales fue efectivamente identificada por el modelo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Puesto que ambos indicadores aportan informaci\u00f3n relevante, es posible combinarlos en una sola m\u00e9trica llamada <em>F-measure<\/em>:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>F<\/mi><mi>\u03b2<\/mi><\/msub><mo>=<\/mo><mfrac><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">(<\/mo><msup><mi>\u03b2<\/mi><mn>2<\/mn><\/msup><mo>+<\/mo><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2217<\/mo><mi>P<\/mi><mo>\u2217<\/mo><mi>R<\/mi><\/mrow><mrow><msup><mi>\u03b2<\/mi><mn>2<\/mn><\/msup><mo>\u2217<\/mo><mi>P<\/mi><mo>+<\/mo><mi>R<\/mi><\/mrow><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">F_{\\beta} = \\frac{(\\beta^{2}+1)*P*R}{\\beta^{2}*P+R}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">donde <math data-latex=\"\\beta\"><semantics><mi>\u03b2<\/mi><annotation encoding=\"application\/x-tex\">\\beta<\/annotation><\/semantics><\/math> mide la importancia de recall o precisi\u00f3n. <math data-latex=\"\\beta &gt; 1 \"><semantics><mrow><mi>\u03b2<\/mi><mo>&gt;<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\beta &gt; 1 <\/annotation><\/semantics><\/math> favorece recall, <math data-latex=\"\\beta < 1 \"><semantics><mrow><mi>\u03b2<\/mi><mo>&lt;<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\beta &lt; 1 <\/annotation><\/semantics><\/math> favorece precisi\u00f3n, y <math data-latex=\"\\beta = 1 \"><semantics><mrow><mi>\u03b2<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\beta = 1 <\/annotation><\/semantics><\/math> asigna el mismo peso a ambas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Regularizaci\u00f3n<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El dise\u00f1o original de este ejemplo contemplaba solo la variable <code>yu_su<\/code>, de modo que el modelo quedaba reducido a<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>\u2119<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>m<\/mi><mi>e<\/mi><mi>d<\/mi><mi>i<\/mi><mi>e<\/mi><mi>v<\/mi><mi>a<\/mi><mi>l<\/mi><mo>=<\/mo><mn>1<\/mn><mi>|<\/mi><mi>x<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mn>1<\/mn><mo>+<\/mo><msup><mi>e<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>b<\/mi><mo>+<\/mo><msub><mi>w<\/mi><mn>1<\/mn><\/msub><mo>\u2217<\/mo><mi>y<\/mi><mi>u<\/mi><mi>_<\/mi><mi>s<\/mi><mi>u<\/mi><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/msup><\/mrow><\/mfrac><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\mathbb{P}(medieval = 1|x) = \\frac{1}{1+e^{-(b + w_{1}*yu\\_su)}}.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Adem\u00e1s, el texto del art\u00edculo acad\u00e9mico en el que aparec\u00edan las palabras \u201cyuso\u201d y \u201csuso\u201d no formaba parte del conjunto de entrenamiento. En nuestra muestra, todos los textos medievales conten\u00edan al menos una vez una de esas palabras y, al mismo tiempo, ning\u00fan texto contempor\u00e1neo las conten\u00eda. Como consecuencia, la variable <code>yu_su<\/code> funcionaba como un predictor perfecto dentro del training set.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esto generaba un problema importante: el modelo aprend\u00eda a asociar de manera excesivamente fuerte la presencia de \u201cyuso\u201d o \u201csuso\u201d con la clase medieval. En otras palabras, se ajustaba demasiado bien a los datos de entrenamiento. As\u00ed, cuando enfrent\u00e1bamos al modelo a un texto nuevo \u2014por ejemplo, un art\u00edculo acad\u00e9mico contempor\u00e1neo que mencionaba esas palabras\u2014, este tend\u00eda a clasificarlo como medieval, aunque en realidad no lo fuera.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En t\u00e9rminos generales, este fen\u00f3meno se conoce como <strong>overfitting<\/strong>: el modelo reproduce muy bien los patrones del conjunto de entrenamiento, pero pierde capacidad de generalizaci\u00f3n sobre datos no vistos. La regularizaci\u00f3n busca mitigar justamente este problema, penalizando pesos demasiado grandes y obligando al modelo a aprender reglas menos extremas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si bien, para mitigar el problema original que tuvimos en el dise\u00f1o del ejemplo, una soluci\u00f3n es a\u00f1adir m\u00e1s variables que capturen caracter\u00edsticas m\u00e1s generales del castellano medieval, otra estrategia para evitar el <strong>overfitting<\/strong> consiste en incorporar un t\u00e9rmino de regularizaci\u00f3n <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>R<\/mi><mo stretchy=\"false\">(<\/mo><mi>\u03b8<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">R(\\theta)<\/annotation><\/semantics><\/math> a la funci\u00f3n objetivo, de la siguiente forma:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mover><mi>\u03b8<\/mi><mo stretchy=\"false\" class=\"tml-capshift\" style=\"math-style:normal;math-depth:0;\">^<\/mo><\/mover><mo>=<\/mo><mi>arg\u2009max<\/mi><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><mrow><munderover><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>m<\/mi><\/munderover><\/mrow><mrow><mi>log<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u2119<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msup><mi>y<\/mi><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">(<\/mo><mi>i<\/mi><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/msup><mi>|<\/mi><msup><mi>x<\/mi><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">(<\/mo><mi>i<\/mi><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2212<\/mo><mi>\u03b1<\/mi><mi>R<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u03b8<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\hat{\\theta} = \\argmax \\sum_{i=1}^{m}\\log(\\mathbb{P}(y^{(i)}|x^{(i)})) &#8211; \\alpha R(\\theta)<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">El objetivo de este nuevo t\u00e9rmino es penalizar pesos demasiado grandes. De este modo, una configuraci\u00f3n de par\u00e1metros que ajusta perfectamente el conjunto de entrenamiento, pero lo hace mediante pesos muy altos, ser\u00e1 menos preferida que otra que ajuste un poco peor, aunque con pesos m\u00e1s peque\u00f1os. En este sentido, mientras mayor sea <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>\u03b1<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\alpha<\/annotation><\/semantics><\/math>, mayor ser\u00e1 la penalizaci\u00f3n y, por tanto, menor tender\u00e1 a ser la magnitud de los par\u00e1metros del modelo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Existen dos tipos de regularizaci\u00f3n especialmente populares. La primera es <strong>lasso<\/strong>, que corresponde a la regularizaci\u00f3n L1:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>R<\/mi><mo stretchy=\"false\">(<\/mo><mi>\u03b8<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><munderover><mo>\u2211<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>n<\/mi><\/munderover><mi mathvariant=\"normal\">\u2223<\/mi><msub><mi>\u03b8<\/mi><mi>i<\/mi><\/msub><mi mathvariant=\"normal\">\u2223<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">R(\\theta)=\\sum_{i=1}^{n}|\\theta_i|<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La segunda es <strong>ridge<\/strong>, que corresponde a la regularizaci\u00f3n L2:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\" display=\"block\"><semantics><mrow><mi>R<\/mi><mo stretchy=\"false\">(<\/mo><mi>\u03b8<\/mi><mo stretchy=\"false\">)<\/mo><mo>=<\/mo><munderover><mo>\u2211<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>n<\/mi><\/munderover><msubsup><mi>\u03b8<\/mi><mi>i<\/mi><mn>2<\/mn><\/msubsup><\/mrow><annotation encoding=\"application\/x-tex\">R(\\theta)=\\sum_{i=1}^{n}\\theta_i^2<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Qu\u00e9 enfoque conviene usar depender\u00e1 de la aplicaci\u00f3n. Por una parte, <strong>ridge<\/strong> suele ser m\u00e1s f\u00e1cil de optimizar, ya que su derivada es simple, mientras que <strong>lasso<\/strong> es m\u00e1s complejo porque el valor absoluto no es diferenciable en cero. Por otra parte, en t\u00e9rminos de los pesos resultantes, <strong>ridge<\/strong> tiende a preferir vectores con muchos pesos peque\u00f1os, mientras que <strong>lasso<\/strong> favorece soluciones dispersas: algunos pesos pueden conservar valores relativamente grandes, pero muchos otros son llevados exactamente a cero. En consecuencia, <strong>lasso<\/strong> tiende a producir modelos con menos variables activas que <strong>ridge<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Notas finales, y pr\u00f3xima entrada\u2026<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En esta entrega vimos c\u00f3mo clasificar texto mediante regresi\u00f3n log\u00edstica, as\u00ed como algunas de las complicaciones que pueden surgir en su aplicaci\u00f3n. Aunque trabajamos con el caso de una variable dependiente binaria, este modelo puede generalizarse a <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>n<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">n<\/annotation><\/semantics><\/math> categor\u00edas. Esa versi\u00f3n m\u00e1s general se explica en detalle en el cap\u00edtulo 4 de Jurafsky y Martin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En la pr\u00f3xima entrada veremos que son los <strong>embeddings<\/strong> y por qu\u00e9 son una forma central de representar el significado de las palabras en NLP. Stay tuned!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Jos\u00e9 Miguel Mu\u00f1oz Urra \u2013\u00a0<a href=\"mailto:jmunozu@pulki.es\">jmunozu@pulki.es<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>En la entrada anterior vimos n-gramas, cuyo concepto es fundamental para entender la generaci\u00f3n de texto, caracter\u00edstica que ser\u00e1 clave para construir el LLM que tenemos en mente. Como mencionamos en una entrada pasada, si bien el algoritmo de generaci\u00f3n de texto es importante a la hora de desarrollar un LLM, los datos en que [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-98","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/98","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=98"}],"version-history":[{"count":26,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/98\/revisions"}],"predecessor-version":[{"id":131,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/98\/revisions\/131"}],"wp:attachment":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=98"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=98"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=98"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}