{"id":185,"date":"2026-04-17T17:45:33","date_gmt":"2026-04-17T17:45:33","guid":{"rendered":"https:\/\/pulki.es\/blog\/?p=185"},"modified":"2026-04-17T17:45:33","modified_gmt":"2026-04-17T17:45:33","slug":"large-language-model-llm-from-scratch-neural-networks-ejemplo-practico","status":"publish","type":"post","link":"https:\/\/pulki.es\/blog\/index.php\/2026\/04\/17\/large-language-model-llm-from-scratch-neural-networks-ejemplo-practico\/","title":{"rendered":"Large Language Model (LLM) from scratch. Neural Networks: Ejemplo pr\u00e1ctico."},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">En la entrega <a href=\"https:\/\/pulki.es\/blog\/index.php\/2026\/04\/11\/large-language-model-llm-from-scratch-neural-networks\/\">pasada<\/a> explicamos algunos conceptos clave para entender redes neuronales. Dada su importancia, en la presente veremos si una red neuronal puede distinguir entre fragmentos de castellano medieval y de espa\u00f1ol moderno. Adem\u00e1s, con este ejemplo en mente, compararemos la performance entre una red neuronal y una regresi\u00f3n log\u00edstica.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para esto, construiremos un peque\u00f1o <em>dataset<\/em> que consistir\u00e1 en textos escritos en castellano contempor\u00e1neo y medieval. Luego, con esos datos, estimaremos una regresi\u00f3n log\u00edstica cuyo objetivo principal ser\u00e1 clasificar si un texto es medieval o no. Finalmente, emplearemos una red neuronal para llevar a cabo la misma tarea, y compararemos cu\u00e1l de los dos hace un mejor trabajo de clasificaci\u00f3n.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Construyendo el dataset<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para estimar ambos modelos es necesario contar con textos escritos en castellano medieval y contempor\u00e1neo. Los textos contempor\u00e1neos ser\u00e1n 498 cuentos pertenecientes a la iniciativa \u201cSantiago en 100 Palabras\u201d, y se pueden encontrar en la siguiente <a href=\"https:\/\/santiagoen100palabras.cl\/\">p\u00e1gina web.<\/a> Cada uno est\u00e1 compuesto por aproximadamente 100 palabras, y son escritos por personas que describen la vida en la ciudad. En algunas ocasiones el tono es formal, y en otras relativamente m\u00e1s coloquial, no obstante, siempre est\u00e1n escritos en castellano contempor\u00e1neo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para las observaciones de castellano medieval, tomaremos textos desde la siguiente p\u00e1gina <a href=\"https:\/\/www.hispanicseminary.org\/t&amp;c\/ac\/index-es.htm\">web<\/a>, y extraeremos 500 fragmentos de 100 palabras. Uno de ellos, es el siguiente:<\/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\">que dizen acuzant e es que sea lo que tuellen mucho e uerde Estas son las sennales de la can\u00e7re que seles faze en los quexares e es que seles camia lo que tuellen de manera que lo que tuellen una uegada no es atal como lo que tuellen otra e es buelto de blanco e de uermeio Estas son las sennales del mal del yesso e de la uentosidad que seles faze en los cuerpos e es que lo quetuellen es taiado e a en ello amarellura e lo que se quaia dello fazesse duro e aspero de manera<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Como podemos ver en el fragmento de ejemplo, no hay signos de puntuaci\u00f3n. Aunque en los textos originales s\u00ed aparecen, en este ejercicio los hemos eliminado para reducir la complejidad. No obstante, en el futuro los tendremos en cuenta.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Regresi\u00f3n Log\u00edstica<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Dado el dataset que tenemos, el siguiente paso es definir las variables independientes. En este punto hay varias alternativas. La primera ser\u00eda seguir la estrategia que empleamos en una <a href=\"https:\/\/pulki.es\/blog\/index.php\/2026\/03\/09\/large-language-model-llm-from-scratch-clasificacion\/\">publicaci\u00f3n anterior<\/a>, es decir, usar como regresores palabras t\u00edpicas del castellano medieval y del castellano contempor\u00e1neo. No obstante, como ya vimos, esta no es una buena idea: se trata de casos espec\u00edficos que no siempre estar\u00e1n presentes en los fragmentos y que, adem\u00e1s, no resultan suficientemente informativos sobre la morfolog\u00eda del castellano medieval ni del contempor\u00e1neo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En esta publicaci\u00f3n intentaremos con otra estrategia. En lugar de definir regresores manualmente, construiremos autom\u00e1ticamente los rasgos a partir de <strong>trigramas de caracteres<\/strong> y los ponderaremos mediante <strong>TF-IDF<\/strong> (<em>term frequency\u2013inverse document frequency<\/em>). Para ello, tomamos todos los textos del conjunto de entrenamiento \u2014tanto medievales como no medievales\u2014, extraemos sus trigramas y calculamos, para cada documento <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>d<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math> y cada trigrama <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>g<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">g<\/annotation><\/semantics><\/math>, el peso<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>t<\/mi><mi>f<\/mi><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo separator=\"true\">,<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mi>t<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo separator=\"true\">,<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u22c5<\/mo><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">tfidf(g,d) = tf(g,d)\\cdot idf(g)<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mrow><mi>log<\/mi><mo>\u2061<\/mo><\/mrow><mo fence=\"false\" symmetric=\"true\" minsize=\"2.4em\" maxsize=\"2.4em\">(<\/mo><mfrac><mrow><mi>N<\/mi><mo>+<\/mo><mn>1<\/mn><\/mrow><mrow><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>+<\/mo><mn>1<\/mn><\/mrow><\/mfrac><mo fence=\"false\" symmetric=\"true\" minsize=\"2.4em\" maxsize=\"2.4em\">)<\/mo><mo>+<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">idf(g) = \\log\\bigg(\\frac{N+1}{df(g) + 1}\\bigg) +1<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed, <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>g<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">g<\/annotation><\/semantics><\/math> representa un trigrama, <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>d<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math> un documento del corpus de entrenamiento, <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>N<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">N<\/annotation><\/semantics><\/math> el n\u00famero total de documentos y <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>d<\/mi><mi>f<\/mi><mo stretchy=\"false\">(<\/mo><mi>g<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">df(g)<\/annotation><\/semantics><\/math> la cantidad de documentos en que aparece ese trigrama. La intuici\u00f3n es simple: si un trigrama aparece muchas veces en un texto, su peso aumenta; si adem\u00e1s aparece en pocos documentos del corpus, su peso aumenta todav\u00eda m\u00e1s. En cambio, si aparece en casi todos los documentos, su peso disminuye, porque aporta poca informaci\u00f3n distintiva. En nuestra implementaci\u00f3n, una vez calculados estos pesos, el vector resultante se <strong>normaliza con norma <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msub><mi>L<\/mi><mn>2<\/mn><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">L_2<\/annotation><\/semantics><\/math>\u200b<\/strong>, de modo que los documentos puedan compararse en una misma escala.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Entonces, el n\u00famero total de regresores ser\u00e1 el total de <em>n<\/em>-gramas que fueron obtenidos desde los textos de entrenamiento, y el valor de los regresores vendr\u00e1 dado por <math data-latex=\"tfidf(g,d)\"><semantics><mrow><mi>t<\/mi><mi>f<\/mi><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo separator=\"true\">,<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">tfidf(g,d)<\/annotation><\/semantics><\/math>. A modo de ejemplo, supongamos que nuestro corpus de entrenamiento consta de un texto medieval y otro no medieval. El medieval viene dado por:<\/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\">E los demas de los sabios de los turcos otro si se trabaiaron dend. e el mas nombrado Rey.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">y el no medieval por:<\/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\">Nunca me dej\u00f3. Dec\u00eda que no pod\u00eda respirar<br>con ellos cerca. Un d\u00eda desapareci\u00f3. Ya no volvi\u00f3.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Luego, al calcular los trigramas de ambos textos, tenemos:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>trigrama<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><strong>frecuencia<\/strong><\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">Elo<\/td><td class=\"has-text-align-center\" data-align=\"center\">1<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">los<\/td><td class=\"has-text-align-center\" data-align=\"center\">4<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">osd<\/td><td class=\"has-text-align-center\" data-align=\"center\">2<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">sde<\/td><td class=\"has-text-align-center\" data-align=\"center\">3<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">dem<\/td><td class=\"has-text-align-center\" data-align=\"center\">1<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">ema<\/td><td class=\"has-text-align-center\" data-align=\"center\">1<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">.<\/td><td class=\"has-text-align-center\" data-align=\"center\">.<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">.<\/td><td class=\"has-text-align-center\" data-align=\"center\">.<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">.<\/td><td class=\"has-text-align-center\" data-align=\"center\">.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Esto implica que la regresi\u00f3n log\u00edstica viene dada por:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><msub><mi>w<\/mi><mn>1<\/mn><\/msub><mo>\u22c5<\/mo><mi>E<\/mi><mi>l<\/mi><mi>o<\/mi><mo>+<\/mo><msub><mi>w<\/mi><mn>2<\/mn><\/msub><mo>\u22c5<\/mo><mi>l<\/mi><mi>o<\/mi><mi>s<\/mi><mo>+<\/mo><msub><mi>w<\/mi><mn>3<\/mn><\/msub><mo>\u22c5<\/mo><mi>o<\/mi><mi>s<\/mi><mi>d<\/mi><mo>+<\/mo><msub><mi>w<\/mi><mn>4<\/mn><\/msub><mo>\u22c5<\/mo><mi>s<\/mi><mi>d<\/mi><mi>e<\/mi><mo>+<\/mo><msub><mi>w<\/mi><mn>5<\/mn><\/msub><mo>\u22c5<\/mo><mi>d<\/mi><mi>e<\/mi><mi>m<\/mi><mo>+<\/mo><mi>.<\/mi><mi>.<\/mi><mi>.<\/mi><mo>+<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\sigma(z) = w_{1}\\cdot Elo + w_{2}\\cdot los + w_{3}\\cdot osd + w_{4}\\cdot sde + w_{5}\\cdot dem + &#8230; +  <\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">donde cada regresor es la funci\u00f3n <math data-latex=\"tfidf(g,d)\"><semantics><mrow><mi>t<\/mi><mi>f<\/mi><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo separator=\"true\">,<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">tfidf(g,d)<\/annotation><\/semantics><\/math>, para ese trigrama <math data-latex=\"g\"><semantics><mi>g<\/mi><annotation encoding=\"application\/x-tex\">g<\/annotation><\/semantics><\/math>. En la siguiente secci\u00f3n calcularemos los trigramas para el corpus de entrenamiento real, y su respectivo <math data-latex=\"tfidf(g,d)\"><semantics><mrow><mi>t<\/mi><mi>f<\/mi><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo separator=\"true\">,<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">tfidf(g,d)<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--1 wp-block-paragraph\">Calculando trigramas<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La estrategia que emplearemos ser\u00e1 computar los trigramas para cada observaci\u00f3n. En otras palabras, para cada texto medieval y no medieval, calcularemos trigramas. Al hacer esto, obtenemos lo siguiente:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>N\u00famero total de documentos<\/td><td>998<\/td><\/tr><tr><td>Documentos medievales<\/td><td>500<\/td><\/tr><tr><td>Documentos modernos<\/td><td>498<\/td><\/tr><tr><td>N\u00famero de trigramas distintos<\/td><td>7.396<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Lo anterior implica que tendremos 7.396 regresores. Respecto a aquellos, los 9 que tienen mayor TF-IDF promedio (<math data-latex=\"\\frac{1}{N} \\sum_{d=1}^{N} tfidf(g,d)\"><semantics><mrow><mfrac><mn>1<\/mn><mi>N<\/mi><\/mfrac><msubsup><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>d<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>N<\/mi><\/msubsup><mi>t<\/mi><mi>f<\/mi><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo separator=\"true\">,<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\frac{1}{N} \\sum_{d=1}^{N} tfidf(g,d)<\/annotation><\/semantics><\/math>) son:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td class=\"has-text-align-center\" data-align=\"center\">Trigramas<\/td><td class=\"has-text-align-center\" data-align=\"center\">TF-IDF promedio<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216;as &#8216;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.095239<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216; de&#8217;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.090066<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216; e &#8216;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.085153<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216;os &#8216;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.073972<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216; qu&#8217;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.069798<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216;en &#8216;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.068841<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216;es &#8216;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.068074<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216; la&#8217;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.065219<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">&#8216;que&#8217;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.062593<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">En este sentido, vale la pena destacar que el valor del regresor para esa estimaci\u00f3n es <math data-latex=\"tfidf(g,d)\"><semantics><mrow><mi>t<\/mi><mi>f<\/mi><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo separator=\"true\">,<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">tfidf(g,d)<\/annotation><\/semantics><\/math>, no el promedio que presentamos en la tabla. Con <math data-latex=\"tfidf(g,d)\"><semantics><mrow><mi>t<\/mi><mi>f<\/mi><mi>i<\/mi><mi>d<\/mi><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>g<\/mi><mo separator=\"true\">,<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">tfidf(g,d)<\/annotation><\/semantics><\/math> calculado, estamos en condiciones de estimar los par\u00e1metros de la regresi\u00f3n log\u00edstica.<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--2 wp-block-paragraph\">Regresi\u00f3n  Log\u00edstica<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Una vez estimada la regresi\u00f3n log\u00edstica, un ejercicio \u00fatil es identificar qu\u00e9 trigramas tienen coeficientes positivos, y cuales tienen negativos. Esto nos permite determinar aquellos trigramas que empujan un texto a ser medieval (coeficientes positivos) o a ser no medieval (coeficientes negativos). En este sentido, los top 9 positivos vienen dados por:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Trigrama<\/td><td>Coeficiente<\/td><\/tr><tr><td>&#8216; e &#8216;<\/td><td>4.525484<\/td><\/tr><tr><td>&#8216;as &#8216;<\/td><td>2.379491<\/td><\/tr><tr><td>&#8216;es &#8216;<\/td><td>2.010735<\/td><\/tr><tr><td>&#8216; qu&#8217;<\/td><td>1.767268<\/td><\/tr><tr><td>&#8216;en &#8216;<\/td><td>1.765731<\/td><\/tr><tr><td>&#8216;s e&#8217;<\/td><td>1.726395<\/td><\/tr><tr><td>&#8216;las&#8217;<\/td><td>1.568585<\/td><\/tr><tr><td>&#8216;que&#8217;<\/td><td>1.291647<\/td><\/tr><tr><td>&#8216; de&#8217;<\/td><td>1.291306<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">y los top 9 negativos por:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Trigrama<\/td><td>Coeficiente<\/td><\/tr><tr><td>&#8216; y &#8216;<\/td><td>-1.373146<\/td><\/tr><tr><td>&#8216;me &#8216;<\/td><td>-1.094712<\/td><\/tr><tr><td>&#8216;a, &#8216;<\/td><td>-0.959669<\/td><\/tr><tr><td>&#8216;o, &#8216;<\/td><td>-0.955032<\/td><\/tr><tr><td>&#8216;\u00eda &#8216;<\/td><td>-0.931607<\/td><\/tr><tr><td>&#8216; ve&#8217;<\/td><td>-0.860371<\/td><\/tr><tr><td>&#8216;s, &#8216;<\/td><td>-0.849333<\/td><\/tr><tr><td>&#8216;a. &#8216;<\/td><td>-0.823249<\/td><\/tr><tr><td>&#8216; ha&#8217;<\/td><td>-0.808638<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">como podemos ver a simple vista, al parecer, la regresi\u00f3n log\u00edstica no ha sido particularmente buena en detectar combinaciones de caracteres propias del castellano medieval, puesto que las combinaciones que m\u00e1s empujan a medieval, son muy similares a las que podr\u00edamos ver en textos no medievales. En la siguiente secci\u00f3n, testearemos esta intuici\u00f3n de una forma m\u00e1s rigurosa.<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--3 wp-block-paragraph\">Testeando rendimiento<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para testear el rendimiento de la estimaci\u00f3n emplearemos 200 textos medievales que no est\u00e1n en el training set, y luego veremos qu\u00e9 porcentaje es correctamente clasificado como medieval (desde aqu\u00ed en adelante,<em>recall<\/em> medieval).<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>N\u00famero de textos medievales en test<\/td><td>200<\/td><\/tr><tr><td>Textos medievales predichos correctamente como medievales<\/td><td>150<\/td><\/tr><tr><td>Porcentaje de acierto sobre textos medievales<\/td><td>75%<\/td><\/tr><tr><td>Recall medieval<\/td><td>0.75<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">La tabla anterior muestra que de 200 textos medievales, el modelo acierta correctamente a 150. Para poder analizar el rendimiento en t\u00e9rminos relativos, tenemos que hacer el mismo ejercicio usando redes neuronales.  En la siguiente secci\u00f3n llevaremos a cabo esto.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Redes Neuronales<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La arquitectura que emplearemos ser\u00e1 una red neuronal <em>feedforward <\/em>con dos capas ocultas. La primera tendr\u00e1 128 neuronas, y la segunda 64. Adem\u00e1s, la funci\u00f3n de activaci\u00f3n ser\u00e1 ReLU. Al estimarla, obtenemos los siguientes resultados:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>N\u00famero de textos medievales en test<\/td><td>200<\/td><\/tr><tr><td>Textos medievales predichos correctamente como medievales<\/td><td>200<\/td><\/tr><tr><td>Porcentaje de acierto sobre textos medievales<\/td><td>100%<\/td><\/tr><tr><td>Recall medieval<\/td><td>1<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Como podemos ver en nuestro experimento, la red neuronal super\u00f3 a la regresi\u00f3n log\u00edstica al clasificar correctamente todos los textos medievales del conjunto de prueba, mientras que la regresi\u00f3n log\u00edstica identific\u00f3 solo el 75%. Una explicaci\u00f3n plausible es que la regresi\u00f3n log\u00edstica combina los trigramas de manera lineal: cada rasgo aporta de forma independiente a la decisi\u00f3n final. La red neuronal, en cambio, puede aprender combinaciones no lineales entre rasgos ortogr\u00e1ficos, morfol\u00f3gicos y gr\u00e1ficos del castellano medieval. Eso le permite captar patrones m\u00e1s complejos, como interacciones entre secuencias de caracteres, que un modelo lineal no representa bien. En esta tarea, esa flexibilidad parece haber marcado la diferencia.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Debido a que el modelo es relativamente exitoso, ser\u00eda interesante explorar si es capaz de detectar peculiaridades del castellano medieval. Una manera de hacerlo es identificar qu\u00e9 trigramas son los que empujan un texto a ser medieval o no. A pesar de que no podemos obtener este n\u00famero de forma directa (como en el caso de la regresi\u00f3n log\u00edstica), es posible llevar a cabo una aproximaci\u00f3n, que implica multiplicar las matrices de los pesos para obtener una especie de efecto agregado desde la entrada hasta la salida. Al hacer esto, podemos ver que los trigramas que parecen empujar m\u00e1s hacia medieval 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\">Trigrama<\/td><td>Coeficiente<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;tvl&#8217;<\/td><td>0.255863<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;\u00e7as&#8217;<\/td><td>0.253850<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;sel&#8217;<\/td><td>0.250878<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;rau&#8217;<\/td><td>0.248537<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;sea&#8217;<\/td><td>0.244449<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;hd &#8216;<\/td><td>0.241685<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;\u00e7er&#8217;<\/td><td>0.235730<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;son&#8217;<\/td><td>0.234452<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;xii&#8217;<\/td><td>0.233908<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">y los que empujan m\u00e1s hacia moderno 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\">Trigrama<\/td><td>Coeficiente<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;\u00f3n &#8216;<\/td><td>-0.245191<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;, p&#8217;<\/td><td>-0.220347<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;mis&#8217;<\/td><td>-0.220332<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216; ve&#8217;<\/td><td>-0.218641<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;, s&#8217;<\/td><td>-0.216067<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;ja &#8216;<\/td><td>-0.216041<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;adr&#8217;<\/td><td>-0.215468<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;ti\u00f3&#8217;<\/td><td>-0.214826<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">&#8216;ame&#8217;<\/td><td>-0.213638<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Estos resultados muestran una foto distinta a la obtenida con la regresi\u00f3n log\u00edstica. En particular, podemos ver que el car\u00e1cter &#8216;<a href=\"https:\/\/es.wikipedia.org\/wiki\/%C3%87_(cedilla)\">\u00e7<\/a>&#8216;, que se us\u00f3 en el espa\u00f1ol medieval, pero luego fue abandonado tras el reajuste de las <a href=\"https:\/\/es.wikipedia.org\/wiki\/Reajuste_de_las_sibilantes\">sibilantes<\/a> durante los siglos\u00a0XVI\u00a0y\u00a0XVII, aparece de forma m\u00e1s frecuente en los trigramas que empujan un texto a ser medieval, lo que claramente es una buena se\u00f1al, puesto que es una peculiaridad que pertenece exclusivamente al castellano medieval, cuando es comparado con el moderno.<\/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 el siguiente ejercicio ponemos a prueba la capacidad de la regresi\u00f3n log\u00edstica y de una red neuronal <em>feedforward<\/em> para clasificar textos medievales. Los resultados muestran que la red neuronal fue relativamente m\u00e1s eficaz. En particular, logr\u00f3 clasificar correctamente todos los textos medievales del conjunto de prueba, mientras que la regresi\u00f3n log\u00edstica solo identific\u00f3 correctamente el 75% de ellos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En la pr\u00f3xima entrega, introduciremos los Large Language Models. Stay tuned!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Jos\u00e9 Miguel Mu\u00f1oz Urra \u2013&nbsp;<a href=\"mailto:jmunozu@pulki.es\">jmunozu@pulki.es<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>En la entrega pasada explicamos algunos conceptos clave para entender redes neuronales. Dada su importancia, en la presente veremos si una red neuronal puede distinguir entre fragmentos de castellano medieval y de espa\u00f1ol moderno. Adem\u00e1s, con este ejemplo en mente, compararemos la performance entre una red neuronal y una regresi\u00f3n log\u00edstica. Para esto, construiremos un [&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-185","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/185","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=185"}],"version-history":[{"count":14,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/185\/revisions"}],"predecessor-version":[{"id":200,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/185\/revisions\/200"}],"wp:attachment":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}