Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save harpiechoise/c1cbdd1065c06a4616df44c58edb4934 to your computer and use it in GitHub Desktop.
Save harpiechoise/c1cbdd1065c06a4616df44c58edb4934 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 0. Requisitos\n",
"\n",
"<a href=\"https://gist.github.com/harpiechoise/1ce67cc6589549932bf847c32933e35c?fbclid=IwAR1dGKErTetGdJAtWAro2p6StHjGAB23SYimLBbJ6E4MObe7StCjky91_Kc\"> Textos a profundidad </a>\n",
"\n",
"<a href=\"https://gist.github.com/harpiechoise/c05f6cfb22cc44cb270769ba7f40a098?fbclid=IwAR0eGgbIB69F76OoTnOwYVV8l9467K-XbQhdSnfuaX-ex6QtQJ8wLPAQprg\"> NLP Basico </a>\n",
"\n",
"<a href=\"https://gist.github.com/harpiechoise/96e546573e872cf558befab61ee9082e\"> POS y Clasificacion de texto </a>\n",
"\n",
"<a href=\"https://gist.github.com/harpiechoise/1e0a025ddfad5cc2ead3d5f5077d5791\"> Semantica y vectores de palabras </a>\n",
"\n",
"## 1. Introducción"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El topic modeling nos permite analizar eficientemente volúmenes gigantes de texto, agrupando documentos dentro de topicos.\n",
"\n",
"Recuerdan que anteriormente hicimos **clasificación de texto supervisada**, es decir, le pasaba la respuesta al modelo. Bueno el Topic Modeling, es **clasificación de texto no supervisada**, lo que haré en este caso es solo pasar un texto crudo, sin etiquetas, y el algoritmo se encargara de agrupar conceptos.\n",
"\n",
"Si nosotros tenemos texto sin etiquetas, es decir un X pero no una y. Esta técnica nos permite \"descubrir\" la y solamente a partir de la X, o \"descubrir\" las etiquetas a partir del texto.\n",
"\n",
"Algo muy importante a tener en mente, es que es demasiado difícil evaluar un modelo basado en **aprendizaje no supervisado**, porque a ciencia cierta, no sabemos el \"tópico correcto\" o la \"respuesta correcta\", no único que sabemos es que los documentos, compartes ideas similares o tópicos similares, es tu trabajo identificar que representan estos tópicos.\n",
"\n",
"En el caso de cualquier algoritmo de **aprendizaje no supervisado** es muy difícil evaluarlo porque para empezar nosotros no tenemos la respuesta correcta, y no sabemos que relaciones puede identificar por si sola este tipo de modelo.\n",
"\n",
"## 2. Latent Dirichlet Allocation (LDA)\n",
"\n",
"**Johann Peter Gustav Lejeune Dirichlet** fue un matemático alemán. En 1800s contribuyo ampliamente en el campo de las matemáticas modernas, con una distribución probabilística que se llama **<a href=\"https://es.wikipedia.org/wiki/Distribuci%C3%B3n_de_Dirichlet\">\"Dirichlet Distribution\"</a>**.\n",
"\n",
"**Latent Dirichlet Allocation (LDA)** esta basada en su distribución.\n",
"\n",
"En 2003 **LDA** fue publicado como un modelo grafico para el **descubrimiento de tópicos**, en la revista **Journal of Machine Learning** de la mano de **David Blei, Andrew NG, y Michael I. Jordan**.\n",
"\n",
"Ahora vamos a hablar a muy alto nivel sobre como trabaja el algoritmo de **LDA**, pero también te recomiendo que veas la **<a href=\"https://ai.stanford.edu/~ang/papers/nips01-lda.pdf\"> Publicación Original</a>**.\n",
"\n",
"Las suposiciones que se toman en cuenta en el momento de aplicar el método LDA son las siguientes:\n",
"\n",
"- Los documentos de tópicos similares, usan palabras similares: Por ejemplo si tengo 2 documentos de economía, por lo general usan grupos de palabras similares, como Dinero, Ganancia, etc.\n",
"\n",
"- Los tópicos latentes pueden ser encontrados buscando grupos de palabras frecuentes que aparecen juntas en los documentos\n",
"\n",
"También podemos expresar esto de manera más matemática:\n",
"\n",
"- Los documentos, tienen distribuciones de probabilidad latentes sobre el tópico\n",
"\n",
"- Los tópicos por si mismo tienen distribuciones de probabilidad sobre sus palabras\n",
"\n",
"<img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Dice_Distribution_%28bar%29.svg/250px-Dice_Distribution_%28bar%29.svg.png\" /> (Esta imagen es solo con propósitos de ejemplificar)\n",
"\n",
"Imaginate que tus tópicos son los números que van del 2 al 12 entonces el tópico más frecuente dentro del documento seria el numero 7, lo que queremos decir de esto, es que a menudo en el documento, por su estructura de palabras, hay más probabilidad de referirnos al tópico 7, que a los demás, es decir que agrupamos nuestras palabras hacia ese tópico, que tiene prevalecía por sobre los otros en el texto. Pero hay que tener en cuenta que esto no es un indicador que el tópico del texto sea el numero 7, solo que los grupos de palabras están relacionados más con ese tópico por sobre los otros, y como habíamos dicho antes **Los tópicos en si mismos tienen distribuciones de probabilidad sobre sus palabras**, por ejemplo si yo tomo un tópico, sus palabras estarán distribuidas de una forma parecida a la imagen anterior.\n",
"\n",
"El LDA representa los documentos como una mezcla de tópicos con palabras con ciertas probabilidades de aparecer en ellos.\n",
"\n",
"Imaginemos que escribimos un documento con las siguiente formula:\n",
"\n",
"- Primero decidimos el numero de palabras N que el documento tendrá\n",
"\n",
"- Escogemos una mezcla para los tópicos del documento (Siguiendo la forma de la **Distribucion Dirichlet** sobre un numero fijo de tópicos)\n",
"\n",
"- Ej: 60% Negocios 20% Política 10% Comida\n",
"\n",
"- Generamos cada palabra acorde a la distribución multinomica antes mencionada (60% Negocios 20% Política 10% Comida) Usando el tópico para generar la palabra en si.\n",
"\n",
"- Ej: Si tomamos el tópico \"comida\" tendremos que generar la palabra Carne con más frecuencia que la palabra \"Casa\"\n",
"\n",
"- Asumiendo que el documento fue generado de esta manera el algoritmo de LDA tratara de ir hacia atrás hasta llegar generar un set de tópicos acordes a como esta distribuido el documento.\n",
"\n",
"Okay, esto no es muy útil en la vida real, ya que no encontraremos un documento así, pero sirve para ilustrar el funcionamiento del algoritmo LDA.\n",
"\n",
"- Ahora imagina que tienes un set de documentos, ya hemos escogido un numero K de tópicos que queremos descubrir en nuestro documento, tienes que hacer esto cuidadosamente ya que es realmente difícil, para que el algoritmo LDA funcione, **tú como usuario debes escoger la cantidad de tópicos a descubrir**, incluso antes de empezar a usar LDA tienes que tener una especie de intuición de cuantos tópicos contiene el documento.\n",
"\n",
"- Una vez hecho esto vamos a usar el algoritmo de **LDA** para aprender la representación tópica de cada documento y las palabras asociadas a él.\n",
"\n",
"- Vamos a ir por cada documento y vamos a asociar **aleatoriamente** cada palabra del documento a alguno de estos tópicos.\n",
"\n",
"- Esta asignación aleatoria ya te da una \"representación tópica\" para todos los documentos y sus palabras de todos los tópicos (Esta asignación inicial no tendrá sentido)\n",
"\n",
"- Entonces vamos a iterar sobre esta idea inicial para ver si podemos arreglar el asignado inicial, y lo que vamos a hacer es, para cada palabra de cada documento, vamos a calcular:\n",
"\n",
"**p(topic t | document d) = La proporción de palabras en el documento es d y están asignadas al tópico t**\n",
"\n",
"- Tambien calcularemos\n",
"\n",
"**p(word w | topic t) = La proporción de asignación a un tópico t sobre todos los documentos que vienen de esta palabra w.**\n",
"\n",
"- Finalmente reasignamos probabilisticamente el tópico a partir de las operaciones anteriores\n",
"\n",
"**p(topic t | document d) * p(word w | topic t)**\n",
"\n",
"Esta es la probabilidad de que la palabra w pertenezca al tópico generado t.\n",
"\n",
"- Despues de repetir estos pasos un gran numero de veces, alcanzaremos un estado en el cual las asignación de tópicos son aceptables\n",
"\n",
"Lo más importante a tener en mente para este proceso, es que tú como usuario, vas a escoger el numero de tópicos presentes en el documento.\n",
"\n",
"Como también vas a tener que interpretar que tópicos son a los que el algoritmo está apuntando. Vamos al código\n",
"\n",
"## 3. LDA Aplicado\n",
"\n",
"Primero debemos obtener el siguiente Dataset\n",
"\n",
"<a href=\"https://drive.google.com/file/d/1gT18uSmSHNnSH8KrjgpMUdKeanwE6EtR/view?usp=sharing\">Dataset Personal</a>\n",
"\n",
"Ahora vamos a importarlo con pandas"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"\n",
"df = pd.read_csv('npr.csv')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Este dataset contiene artículos sobre diversos temas, vamos a echarle un breve vistazo"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Article</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>In the Washington of 2016, even when the polic...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Donald Trump has used Twitter — his prefe...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Donald Trump is unabashedly praising Russian...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Updated at 2:50 p. m. ET, Russian President Vl...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>From photography, illustration and video, to d...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Article\n",
"0 In the Washington of 2016, even when the polic...\n",
"1 Donald Trump has used Twitter — his prefe...\n",
"2 Donald Trump is unabashedly praising Russian...\n",
"3 Updated at 2:50 p. m. ET, Russian President Vl...\n",
"4 From photography, illustration and video, to d..."
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'In the Washington of 2016, even when the policy can be bipartisan, the politics cannot. And in that sense, this year shows little sign of ending on Dec. 31. When President Obama moved to sanction Russia over its alleged interference in the U. S. election just concluded, some Republicans who had long called for similar or more severe measures could scarcely bring themselves to approve. House Speaker Paul Ryan called the Obama measures ”appropriate” but also ”overdue” and ”a prime example of this administration’s ineffective foreign policy that has left America weaker in the eyes of the world.” Other GOP leaders sounded much the same theme. ”[We have] been urging President Obama for years to take strong action to deter Russia’s worldwide aggression, including its operations,” wrote Rep. Devin Nunes, . chairman of the House Intelligence Committee. ”Now with just a few weeks left in office, the president has suddenly decided that some stronger measures are indeed warranted.” Appearing on CNN, frequent Obama critic Trent Franks, . called for ”much tougher” actions and said three times that Obama had ”finally found his tongue.” Meanwhile, at and on Fox News, various spokesmen for Trump said Obama’s real target was not the Russians at all but the man poised to take over the White House in less than three weeks. They spoke of Obama trying to ”tie Trump’s hands” or ”box him in,” meaning the would be forced either to keep the sanctions or be at odds with Republicans who want to be tougher still on Moscow. Throughout 2016, Trump has repeatedly called not for sanctions but for closer ties with Russia, including cooperation in the fight against ISIS. Russia has battled ISIS in Syria on behalf of that country’s embattled dictator, Bashar Assad, bombing the besieged city of Aleppo that fell to Assad’s forces this week. During the campaign, Trump even urged Russia to ”find” missing emails from the private server of his opponent, Hillary Clinton. He has exchanged public encomiums with Russian President Vladimir Putin on several occasions and added his doubts about the current U. S. levels of support for NATO — Putin’s longtime nemesis. There have also been suggestions that Trump’s extensive business dealings with various Russians are the reason he refuses to release his tax returns. All those issues have been disquieting to some Republicans for many months. Sens. John McCain, . and Lindsay Graham, . C. prominent senior members of the Armed Services Committee, have accepted the assessment of 17 U. S. intelligence agencies regarding the role of Russia in the hacking of various Democratic committees last year. That includes the FBI and CIA consensus that the Russian goal was not just to discredit American democracy but to defeat Clinton and elect Trump. They say the great majority of their Senate colleagues agree with them, and McCain has slated an Armed Services hearing on cyberthreats for Jan. 5. But the politicizing of the Russian actions — the idea that they helped Trump win — has also made the issue difficult for Republican leaders. It has allowed Trump supporters to push back on the intelligence agencies and say the entire issue is designed to undermine Trump’s legitimacy. Senate Majority Leader Mitch McConnell has so far resisted calls for a select committee to look into the Russian interference in the 2016 campaign. He has said it is enough for Sen. Richard Burr, . C. to look into it as chairman of the Senate Intelligence Committee. Typically, Republican leaders and spokesmen say there is no evidence that the actual voting or tallying on Nov. 8 was compromised, and that is true. But it is also a red herring, as interference in those functions has not been alleged and is not the focus of the U. S. intelligence agencies’ concern. For his part, Trump has shown little interest in delving into what happened. He has cast doubt on the U. S. intelligence reports to date and suggested ”no one really knows what happened.” He also has suggested that computers make it very difficult to know who is using them. This week, Trump said it was time to ”get on with our lives and do more important things.” However, at week’s end he did agree to have an intelligence briefing on the subject next week. The has not wanted the daily intelligence briefings available to him in recent weeks, preferring that they be given to the men he has chosen as his vice president (Mike Pence) and national security adviser (Mike Flynn) with Trump taking them only occasionally. The irony of this controversy arising at the eleventh hour of the Obama presidency can scarcely be overstated, and it defines the dilemma facing both the outgoing president and the incoming party in control. Obama appears to have been reluctant to retaliate against the Russian hacking before the election for fear of seeming to interfere with the election himself. The Republicans, meanwhile, have for years called for greater confrontation with the Russians, with Obama usually resisting. Obama did join with NATO in punishing the Russians with economic sanctions over the annexation of Crimea. Those sanctions may have been painful, coming as they did alongside falling prices for oil — the commodity that keeps the Russian economy afloat. On other occasions, despite Russian provocations through surrogates in Syria and elsewhere, Obama did not make overt moves to force Russia’s hand. That includes occasions when Russia was believed to be hacking critical computer systems in neighboring Ukraine, Estonia and Poland. But this week, following a chorus of confirmation from the U. S. intelligence community regarding the Russian role in computer hacking in the political campaign, Obama acted. He imposed a set of mostly diplomatic actions such as sanctioning some Russian officials, closing two diplomatic compounds and expelling 35 Russian diplomats. There may have been more damaging measures taken covertly, and some Russophobes in Washington held out hope for that. But the visible portion of the program scarcely amounted to major retribution. And Putin saw fit to diminish the Obama sanctions further by declining to respond. Although his government has steadfastly denied any interference in the U. S. election, Putin rejected his own foreign minister’s recommended package of responses. (He even sent an invitation for U. S. diplomats to send their children to a holiday party in Moscow.) That allowed Putin to appear for the moment to be ”the bigger man,” even as he spurned Obama and kept up what has looked like a public bromance with Trump, who tweeted: ”Great move on delay (by V. Putin) I always knew he was very smart!” At the moment it may seem that the overall Russia question amounts to the first crisis facing the Trump presidency. Whether forced by this campaign interference issue or not, Trump must grasp the nettle of a relationship Mitt Romney once called the greatest threat to U. S. security in the world. To be sure, Trump needs to dispel doubts about his ability to stand up to Putin, who has bullied and cajoled his way to center stage in recent world affairs. But Trump also seems determined to turn the page on past U. S. commitments, from free trade philosophy to funding of NATO and the United Nations. And if his Twitter account is any guide, Trump shows little concern about the conundrum others perceive to be facing him. Above all, Trump has shown himself determined to play by his own rules. A year ago, many were confident that would not work for him in the world of presidential politics. We are about to find out whether it works for him in the Oval Office.'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.Article[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Son articulos completos, por lo que nos viene muy bien para lo que queremos hacer"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"11992"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"En total tenemos 11992 artículos, ahora vamos a procesar los datos para ello vamos a usar el CountVectorizer, con algunos parámetros, que pasare a explicar\n",
"\n",
"- max_df: Este argumento ignorara ciertos términos que son muy frecuentes en el texto.\n",
"- min_df: Este argumento ignorara ciertos términos que son muy poco frecuentes en el texto. Y el valor que le pasemos significara la cantidad de veces que esta aparezca a lo largo de los documentos\n",
"\n",
"- La idea es descartar palabras comunes y palabras poco comunes, para que el algoritmo no las tenga en cuenta\n",
"\n",
"- stop_words: Este argumento va a suprimir todas las Stop Words"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"vec = CountVectorizer(max_df=0.9, min_df=2, stop_words='english')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"No haremos un Test/train split porque no tiene sentido ya que no tenemos que probar el algoritmo, ya que no tenemos etiquetas para hacerlo, asique nos saltaremos este paso"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"dtm = vec.fit_transform(df['Article'])"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<11992x54777 sparse matrix of type '<class 'numpy.int64'>'\n",
"\twith 3033388 stored elements in Compressed Sparse Row format>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dtm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dependiendo de tu hardware este proceso de Fit Transform puede tomar un tiempo.\n",
"\n",
"Ahora vamos a aplicar nuestro algoritmo de LDA, para ello lo importare desde sklearn,\n",
"\n",
"El argumento **n_components** será el numero de tópicos que extraerá, mientras más profundo sea este numero más subtopicos aparecerán, pero puede llevar a mala clasificación, con lo que todo depende de tu experiencia con este algoritmo e ir probando parámetros.\n",
"\n",
"Yo diré que quiero 7 tópicos generales\n",
"\n",
"Y el argumento **random_state** lo pongo para que tú y yo tengamos exactamente los mismos resultados"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"from sklearn.decomposition import LatentDirichletAllocation\n",
"LDA = LatentDirichletAllocation(n_components=7, random_state=42)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora vamos a correr el algoritmo, que se demorara dependiendo de tu hardware, no te impacientes, ya que estamos alimentándolo con mucha información"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"LatentDirichletAllocation(batch_size=128, doc_topic_prior=None,\n",
" evaluate_every=-1, learning_decay=0.7,\n",
" learning_method='batch', learning_offset=10.0,\n",
" max_doc_update_iter=100, max_iter=10,\n",
" mean_change_tol=0.001, n_components=7, n_jobs=None,\n",
" perp_tol=0.1, random_state=42, topic_word_prior=None,\n",
" total_samples=1000000.0, verbose=0)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"LDA.fit(dtm)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. LDA Interpretando resultados\n",
"\n",
"Con el fin de identificar los tópicos, podemos hacer varias cosas, primero empecemos por lo básico que es extraer palabras del vocabulario"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"54777"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(vec.get_feature_names())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"¿Por que estamos haciendo esto con el CountVectorizer?, porque el CountVectorizer nos va a permitir traducir de vuelta los resultados, y viceversa con fin de investigar que hizo el algoritmo, en este caso tenemos un vocabulario de 54777 palabras, en el vocabulario"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"list"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(vec.get_feature_names())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Algo útil de saber es que estas palabras son una lista por lo que puedo tomarlas con indices"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'transcribe'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vec.get_feature_names()[50000]"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"7"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(LDA.components_)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Este es el numero fijo de tópicos que extrajo, que lo definimos en n_components, que tienen la probabilidad de las palabras en cada uno de los artículos"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"numpy.ndarray"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(LDA.components_)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Al ser una matriz de numpy podemos ver la forma de la matriz"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(7, 54777)"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"LDA.components_.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Esto nos dice que tenemos 7 tópicos y 54777 palabras en total.\n",
"\n",
"Ahora revisaremos los tópicos"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[8.64332806e+00, 2.38014333e+03, 1.42900522e-01, ...,\n",
" 1.43006821e-01, 1.42902042e-01, 1.42861626e-01],\n",
" [2.76191749e+01, 5.36394437e+02, 1.42857148e-01, ...,\n",
" 1.42861973e-01, 1.42857147e-01, 1.42906875e-01],\n",
" [7.22783888e+00, 8.24033986e+02, 1.42857148e-01, ...,\n",
" 6.14236247e+00, 2.14061364e+00, 1.42923753e-01],\n",
" ...,\n",
" [3.11488651e+00, 3.50409655e+02, 1.42857147e-01, ...,\n",
" 1.42859912e-01, 1.42857146e-01, 1.42866614e-01],\n",
" [4.61486388e+01, 5.14408600e+01, 3.14281373e+00, ...,\n",
" 1.43107628e-01, 1.43902481e-01, 2.14271779e+00],\n",
" [4.93991422e-01, 4.18841042e+02, 1.42857151e-01, ...,\n",
" 1.42857146e-01, 1.43760101e-01, 1.42866201e-01]])"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"LDA.components_"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora lo que haremos es combinar esta información, con nuestra habilidad de tomar vocabulario del CountVectorizer, para saber más o menos que tópicos reconoció nuestro algoritmo\n",
"\n",
"Primero tomaremos un tópico al azar dentro de estos componentes"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"topico = LDA.components_[0]"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 2475, 18302, 35285, ..., 22673, 42561, 42993])"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"topico.argsort()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Esta función ordena los indices de menor a mayor, solo los indices, por ejemplo si tengo un array que contiene los siguientes valores\n",
"\n",
"\\[10,200,1]\n",
"\n",
"la función argsort debería entregarme los indices de los valores de menor a mayor\n",
"\n",
"\\[2,0,1]\n",
"\n",
"Siendo 2 índice de 1, 0 índice de 10, etc.\n",
"\n",
"Entonces con el fin de saber las palabras más probables para este tópico tomaremos las 10 primeras palabras de nuestro tópico"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"top_ten_words = topico.argsort()[-10:] # Tomamos los ultimos 10 valores de nuestra array "
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"new\n",
"percent\n",
"government\n",
"company\n",
"million\n",
"care\n",
"people\n",
"health\n",
"said\n",
"says\n"
]
}
],
"source": [
"for index in top_ten_words:\n",
" print(vec.get_feature_names()[index])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Entonces aquí podemos ver las palabras más relacionadas con el primer tópico, ahora haremos esto para todos los tópicos para tener una idea de que información tomo nuestro algoritmo LDA"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Las palabras mas frecuentes del topico #0\n",
"['companies', 'money', 'year', 'federal', '000', 'new', 'percent', 'government', 'company', 'million', 'care', 'people', 'health', 'said', 'says']\n",
"\n",
"\n",
"\n",
"Las palabras mas frecuentes del topico #1\n",
"['military', 'house', 'security', 'russia', 'government', 'npr', 'reports', 'says', 'news', 'people', 'told', 'police', 'president', 'trump', 'said']\n",
"\n",
"\n",
"\n",
"Las palabras mas frecuentes del topico #2\n",
"['way', 'world', 'family', 'home', 'day', 'time', 'water', 'city', 'new', 'years', 'food', 'just', 'people', 'like', 'says']\n",
"\n",
"\n",
"\n",
"Las palabras mas frecuentes del topico #3\n",
"['time', 'new', 'don', 'years', 'medical', 'disease', 'patients', 'just', 'children', 'study', 'like', 'women', 'health', 'people', 'says']\n",
"\n",
"\n",
"\n",
"Las palabras mas frecuentes del topico #4\n",
"['voters', 'vote', 'election', 'party', 'new', 'obama', 'court', 'republican', 'campaign', 'people', 'state', 'president', 'clinton', 'said', 'trump']\n",
"\n",
"\n",
"\n",
"Las palabras mas frecuentes del topico #5\n",
"['years', 'going', 've', 'life', 'don', 'new', 'way', 'music', 'really', 'time', 'know', 'think', 'people', 'just', 'like']\n",
"\n",
"\n",
"\n",
"Las palabras mas frecuentes del topico #6\n",
"['student', 'years', 'data', 'science', 'university', 'people', 'time', 'schools', 'just', 'education', 'new', 'like', 'students', 'school', 'says']\n",
"\n",
"\n",
"\n"
]
}
],
"source": [
"for i, topic in enumerate(LDA.components_):\n",
" print(f\"Las palabras mas frecuentes del topico #{i}\")\n",
" print([vec.get_feature_names()[index] for index in topic.argsort()[-15:]])\n",
" print('\\n\\n')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora al leer esto tenemos una idea más menos de lo que trata, por ejemplo el tópico numero 6 está hablando de educación.\n",
"\n",
"El tópico numero 4 está hablando del tema de las elecciones.\n",
"\n",
"Ahora lo último que nos queda es etiquetar estos tópicos en el dataset, lo que haremos es aplicar la función transform de nuestro LDA en la matriz de términos original.\n",
"\n",
"Esto tomara menos tiempo que el fit, pero eso depende de tu hardware"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"resultado_topicos = LDA.transform(dtm)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Esto nos devolverá un array de numpy que contendrá los 7 tópicos para cada uno de los documentos"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(11992, 7)"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"resultado_topicos.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora nos queda ponerlo en nuestro dataframe de forma fácil"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"df['Topic'] = resultado_topicos.argmax(axis=1)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Article</th>\n",
" <th>Topic</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>In the Washington of 2016, even when the polic...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Donald Trump has used Twitter — his prefe...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Donald Trump is unabashedly praising Russian...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Updated at 2:50 p. m. ET, Russian President Vl...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>From photography, illustration and video, to d...</td>\n",
" <td>2</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Article Topic\n",
"0 In the Washington of 2016, even when the polic... 1\n",
"1 Donald Trump has used Twitter — his prefe... 1\n",
"2 Donald Trump is unabashedly praising Russian... 1\n",
"3 Updated at 2:50 p. m. ET, Russian President Vl... 1\n",
"4 From photography, illustration and video, to d... 2"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"De esta manera clasificamos este dataset dentro de tópicos fácilmente con sk_learn, pero aún no es el final."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. Non-Negative Matrix Factorization Teoria\n",
"\n",
"Non-negative Matrix Factorization (Factorizacion de matrices no negativas) es un algoritmo no supervisado, que realiza una **reducción dimensional** mientras hace el **clustering**, podemos usarlo con el **TF-IDF** para modelar tópicos a de n documentos.\n",
"\n",
"Los fundamentos matemáticos detrás de la factirizacion de matrices no negativas, dicen lo siguiente, dada una matriz no negativa, debemos encontrar la dimensión \"k\" para aproximar las matrices con factores no negativos\n",
"\n",
"<img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f9/NMF.png/400px-NMF.png\" />\n",
"\n",
"Entonces dada una matriz A vamos a descomponer sus dimensiones guardando un factor común \"k\" y luego vamos a hacer una multiplicación, que nos daría como resultado la factorizacion de ambas matrices por el factor común que debe ser no-negativo\n",
"\n",
"De esta forma se aproxima cada objeto (Columna de la matriz A) por una combinación linear de k, para de esta manera reducir sus dimensiones, estos objetos se pueden ver como un cluster.\n",
"\n",
"El Input es una matriz de datos (A) y un numero de vectores base (K) y los valores iniciales para los factores W y H (Por ejemplo, números al azar).\n",
"\n",
"- La matriz A va a ser los datos de entrada, es decir nuestros artículos, o lo que queramos clasificar\n",
"\n",
"- El numero de vectores base K va a ser el numero de componentes, o números de tópicos que queremos que descubra\n",
"\n",
"La función objetiva, es una suerte de medida para la reconstrucción del error entre las Matrices A y la aproximación de WH.\n",
"\n",
"Explicado de forma más simple, lo que hacemos es luego de inicializar nuestras matrices de tópicos, lo que hacemos es tratar de medir el error entre la matriz de tópicos y la matriz original, con el fin de aproximar estas medidas entre números aleatorios y categorías que podemos encontrar dentro del texto, la formula es:\n",
"\n",
"$$\\large{\\frac{1}{2}||A - WH||^{2}_{f} = \\sum^{n}_{i=1}{\\sum_{j=1}^{m}(A_{ij} - (WH)_{ij})^{2}}}$$\n",
"\n",
"Luego tenemos que hacer un proceso de optimización, que se llama **Expectation - maximization**, para refinar los valores de **W y H** con el fin de minimizar la función objetiva. Un enfoque común es iterar entre dos reglas multiplicativa, y actualizar sus reglas hasta converger.\n",
"\n",
"$$ \\textrm{1. Actualizar H} = H_{cj} \\longleftarrow H_{cj}\\frac{(WA)_{cj}}{(WAH)_{cj}} \\rightleftarrows \\textrm{2. Actualizar W} = W_{ic} \\longleftarrow W_{ic}\\frac{(AH)_{ic}}{(WHH_{ic})} $$\n",
"\n",
"De esta forma con nuestra función objetiva, calculamos la el error, y con esta función vamos acercándonos más a un resultado preciso, minimizando el error, y debemos repetir esta operación hasta tener una convergencia entre los coeficientes.\n",
"\n",
"La forma en que vamos a resolver este problema es siguiente este flujo de trabajo.\n",
"\n",
"1. Construir un espacio vectorial optimo de nuestros documentos, como filtrar las stopwords, términos comunes, etc. Para obtener una matriz de términos (A)\n",
"\n",
"2. Aplicar la vectorizacion TF-IDF, con el fin de obtener una vectorizacion, basada en importancia de los términos, y no tanto una cuenta, como lo hicimos en el caso anterior\n",
"\n",
"3. Normalizar los vectores TF-IDF resultantes. No hay que preocuparnos mucho por esto ya que Scikit-Learn lo hará por nosotros.\n",
"\n",
"4. Iniciar los factores usando NNDSVD (Non Negative, Double Singular Value Descomposition) sobre la matriz de términos A.\n",
"\n",
"5. Aplicar la gradiente proyectada sobre A\n",
"\n",
"Como les digo, Scikit Learn hará este proceso por nosotros, asique no se preocupen por estos nombres, es para hacer el proceso un poco más técnico\n",
"\n",
"Como el LDA tenemos que escoger el numero de tópicos que queremos (valor de k), y también como en el LDA tenemos que interpretar los tópicos.\n",
"\n",
"Por suerte este proceso es muy simple de aplicar con Scikit-Learn asique vamos al código\n",
"\n",
"## 5. Non-Negative Matrix Factorization Practico\n",
"\n",
"Haremos lo mismo que antes vamos a importar el dataset con pandas."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd \n",
"df = pd.read_csv('npr.csv')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Anteriormente vectorizamos con el Count Vectorizer, pero nuestro algoritmo de NMF necesita frecuencia, si no coeficientes por lo que en este caso usaremos el TF-IDF Vectorizer"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aplicaremos los mismos argumentos que aplicamos anteriormente"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [],
"source": [
"tfidf = TfidfVectorizer(max_df=0.95, min_df=2, stop_words='english')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora transformaremos nuestros datos a una matriz de terminos"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
"dtm = tfidf.fit_transform(df.Article)"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<11992x54777 sparse matrix of type '<class 'numpy.float64'>'\n",
"\twith 3033388 stored elements in Compressed Sparse Row format>"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dtm"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
"from sklearn.decomposition import NMF"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Por ultimo vamos a importar el modelo de NMF e instanciaremos"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [],
"source": [
"nmf_model = NMF(n_components=7,random_state=42)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"n_components es nuestro valor k del que hablábamos antes, o el numero de tópicos resultantes del proceso de clustering y para tener los mismos resultados asignare un random_state"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora vamos a entrenar el modelo de NMF, se demorara un poco dependiendo del hardware, sean pacientes"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"NMF(alpha=0.0, beta_loss='frobenius', init=None, l1_ratio=0.0, max_iter=200,\n",
" n_components=7, random_state=42, shuffle=False, solver='cd', tol=0.0001,\n",
" verbose=0)"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nmf_model.fit(dtm)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Al igual que el CountVectorizer igual podemos obtener palabras de este vectorizador asique vamos a repetir el proceso de antes"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'albala'"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tfidf.get_feature_names()[2300]"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"El top 15 palabras para el topico #0\n",
"['new', 'research', 'like', 'patients', 'health', 'disease', 'percent', 'women', 'virus', 'study', 'water', 'food', 'people', 'zika', 'says']\n",
"\n",
"\n",
"\n",
"El top 15 palabras para el topico #1\n",
"['gop', 'pence', 'presidential', 'russia', 'administration', 'election', 'republican', 'obama', 'white', 'house', 'donald', 'campaign', 'said', 'president', 'trump']\n",
"\n",
"\n",
"\n",
"El top 15 palabras para el topico #2\n",
"['senate', 'house', 'people', 'act', 'law', 'tax', 'plan', 'republicans', 'affordable', 'obamacare', 'coverage', 'medicaid', 'insurance', 'care', 'health']\n",
"\n",
"\n",
"\n",
"El top 15 palabras para el topico #3\n",
"['officers', 'syria', 'security', 'department', 'law', 'isis', 'russia', 'government', 'state', 'attack', 'president', 'reports', 'court', 'said', 'police']\n",
"\n",
"\n",
"\n",
"El top 15 palabras para el topico #4\n",
"['primary', 'cruz', 'election', 'democrats', 'percent', 'party', 'delegates', 'vote', 'state', 'democratic', 'hillary', 'campaign', 'voters', 'sanders', 'clinton']\n",
"\n",
"\n",
"\n",
"El top 15 palabras para el topico #5\n",
"['love', 've', 'don', 'album', 'way', 'time', 'song', 'life', 'really', 'know', 'people', 'think', 'just', 'music', 'like']\n",
"\n",
"\n",
"\n",
"El top 15 palabras para el topico #6\n",
"['teacher', 'state', 'high', 'says', 'parents', 'devos', 'children', 'college', 'kids', 'teachers', 'student', 'education', 'schools', 'school', 'students']\n",
"\n",
"\n",
"\n"
]
}
],
"source": [
"for index, topic in enumerate(nmf_model.components_):\n",
" print(f'El top 15 palabras para el topico #{index}')\n",
" print([tfidf.get_feature_names()[i] for i in topic.argsort()[-15:]])\n",
" print('\\n\\n')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como siempre es tarea tuya interpretar a que tópico pertenecen los resultados.\n",
"\n",
"Ahora lo integraremos al dataset, como hicimos antes, con exactamente los mismos pasos"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
"topic_result = nmf_model.transform(dtm)"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [],
"source": [
"df['Topic'] = topic_result.argmax(axis=1)\n"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Article</th>\n",
" <th>Topic</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>In the Washington of 2016, even when the polic...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Donald Trump has used Twitter — his prefe...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Donald Trump is unabashedly praising Russian...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Updated at 2:50 p. m. ET, Russian President Vl...</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>From photography, illustration and video, to d...</td>\n",
" <td>6</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Article Topic\n",
"0 In the Washington of 2016, even when the polic... 1\n",
"1 Donald Trump has used Twitter — his prefe... 1\n",
"2 Donald Trump is unabashedly praising Russian... 1\n",
"3 Updated at 2:50 p. m. ET, Russian President Vl... 3\n",
"4 From photography, illustration and video, to d... 6"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Entonces ahora me daré a la tarea de interpretar estos tópicos y hacer un diccionario de temas para también ponerlos en nuestro dataframe"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [],
"source": [
"topic_dict = {0:'Salud', 1:'Elecciones', 2:'Legislacion', 3:'Politica', 4:'Elecciones', 5:'Musica', 6:'Educacion'}"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [],
"source": [
"df['Topic Label'] = df['Topic'].map(topic_dict)"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Article</th>\n",
" <th>Topic</th>\n",
" <th>Topic Label</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>In the Washington of 2016, even when the polic...</td>\n",
" <td>1</td>\n",
" <td>Elecciones</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Donald Trump has used Twitter — his prefe...</td>\n",
" <td>1</td>\n",
" <td>Elecciones</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Donald Trump is unabashedly praising Russian...</td>\n",
" <td>1</td>\n",
" <td>Elecciones</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Updated at 2:50 p. m. ET, Russian President Vl...</td>\n",
" <td>3</td>\n",
" <td>Politica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>From photography, illustration and video, to d...</td>\n",
" <td>6</td>\n",
" <td>Educacion</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>I did not want to join yoga class. I hated tho...</td>\n",
" <td>5</td>\n",
" <td>Musica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>With a who has publicly supported the debunk...</td>\n",
" <td>0</td>\n",
" <td>Salud</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>I was standing by the airport exit, debating w...</td>\n",
" <td>0</td>\n",
" <td>Salud</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>If movies were trying to be more realistic, pe...</td>\n",
" <td>0</td>\n",
" <td>Salud</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>Eighteen years ago, on New Year’s Eve, David F...</td>\n",
" <td>5</td>\n",
" <td>Musica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>For years now, some of the best, wildest, most...</td>\n",
" <td>5</td>\n",
" <td>Musica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>For years now, some of the best, wildest, most...</td>\n",
" <td>5</td>\n",
" <td>Musica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>The Colorado River is like a giant bank accoun...</td>\n",
" <td>0</td>\n",
" <td>Salud</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>For the last installment of NPR’s holiday reci...</td>\n",
" <td>5</td>\n",
" <td>Musica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>Being overweight can raise your blood pressure...</td>\n",
" <td>0</td>\n",
" <td>Salud</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>Who’s the YouTube star of 2016? Adele singing ...</td>\n",
" <td>5</td>\n",
" <td>Musica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16</th>\n",
" <td>Here’s a quick roundup of some of the you ma...</td>\n",
" <td>3</td>\n",
" <td>Politica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>17</th>\n",
" <td>Ben Johnston doesn’t follow the rules of music...</td>\n",
" <td>5</td>\n",
" <td>Musica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>18</th>\n",
" <td>David Bowie, Prince and George Michael are all...</td>\n",
" <td>5</td>\n",
" <td>Musica</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19</th>\n",
" <td>In November, the typically straitlaced Office ...</td>\n",
" <td>1</td>\n",
" <td>Elecciones</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Article Topic Topic Label\n",
"0 In the Washington of 2016, even when the polic... 1 Elecciones\n",
"1 Donald Trump has used Twitter — his prefe... 1 Elecciones\n",
"2 Donald Trump is unabashedly praising Russian... 1 Elecciones\n",
"3 Updated at 2:50 p. m. ET, Russian President Vl... 3 Politica\n",
"4 From photography, illustration and video, to d... 6 Educacion\n",
"5 I did not want to join yoga class. I hated tho... 5 Musica\n",
"6 With a who has publicly supported the debunk... 0 Salud\n",
"7 I was standing by the airport exit, debating w... 0 Salud\n",
"8 If movies were trying to be more realistic, pe... 0 Salud\n",
"9 Eighteen years ago, on New Year’s Eve, David F... 5 Musica\n",
"10 For years now, some of the best, wildest, most... 5 Musica\n",
"11 For years now, some of the best, wildest, most... 5 Musica\n",
"12 The Colorado River is like a giant bank accoun... 0 Salud\n",
"13 For the last installment of NPR’s holiday reci... 5 Musica\n",
"14 Being overweight can raise your blood pressure... 0 Salud\n",
"15 Who’s the YouTube star of 2016? Adele singing ... 5 Musica\n",
"16 Here’s a quick roundup of some of the you ma... 3 Politica\n",
"17 Ben Johnston doesn’t follow the rules of music... 5 Musica\n",
"18 David Bowie, Prince and George Michael are all... 5 Musica\n",
"19 In November, the typically straitlaced Office ... 1 Elecciones"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head(20)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6.0 Deep Learning Intro: Perceptron\n",
"\n",
"Antes de ir al deep learning practico, tenemos que entender conceptos básicos, como que representa una neurona.\n",
"\n",
"Las **redes neuronales artificiales**, tienen sus bases en la biología, el intento de recrear las neuronas reales, se conoce como perceptron, con el fin de entender al perceptron vamos a ver una posible representación matemática de como funciona una neurona biológica.\n",
"\n",
"<img src=\"http://ambientech.org/blog/wp-content/uploads/2018/01/Captura-de-pantalla-2018-01-12-a-las-11.33.01-e1516874686341.png\" />\n",
"\n",
"Esta es una neurona biológica, tienes varias dendritas por la que recibe impulsos eléctricos que pasan al soma, y luego se reduce toda esa información en una salida más pequeña a través del axón, esa es la idea básica, tenemos varias entradas, las procesa el cuerpo y luego salen reducidas en el axón.\n",
"\n",
"<img src=\"http://www.um.es/LEQ/Atmosferas/Ch-VI-3/6-3-8.GIF\" />\n",
"\n",
"Y las neuronas artificiales también tienen entradas y salidas \"dendritas y axones\", como les dije, para intentar imitar una neurona real, este modelo simple se conoce como \"perceptron\".\n",
"\n",
"A grandes rasgos, tenemos valores de entrada, que se multiplican por los pesos, que son valores que se autoajustan, pero si no está la red entrenada, son aleatorios, luego pasa por una función de activación, que va a arrojar (dependiendo de lo que estemos haciendo) valores entre 0 y 1.\n",
"\n",
"La operación matemática que se aplica para este primer paso, es una suma ponderada, que va a multiplicar el **valor de entrada** por el **valor de los pesos** los va a sumar con el **bias** y luego de pasar por una **función de activación** arrojara valores entre 0 y 1.\n",
"\n",
"Ahora si juntamos muchos percepciones simples, y esta operación es fácil de extender a una matriz más grande, con esta formula\n",
"\n",
"$$ \\large{\\sum_{i=0}^{n}{w_ix_i + b}}$$\n",
"\n",
"Donde ***x* sub i** es el valor de entrada de esa neurona, el valor ***w* sub i** es el valor del peso de esa neurona y se le suma el bias.\n",
"\n",
"**muy** a grandes rasgos, así es como funciona un perceptron, no entrare en detalle, porque no es tan importante para efectuar esta unidad, pero si tengan en mente leer más sobre redes neuronales artificiales, ya que es una ayuda para implementar estos modelos por si solo.\n",
"\n",
"## 6.1 Deep Learning Intro: Perceptron Multicapa\n",
"\n",
"Entonces vimos que podíamos juntar muchos perceptrones, pero que pasa si junto muchas capas de perceptrones simples. Aquí es donde nace el **perceptron multicapa**\n",
"\n",
"<img src=\"https://upload.wikimedia.org/wikipedia/commons/6/64/RedNeuronalArtificial.png\" />\n",
"\n",
"Esta es una representación del grafo de este perceptron. Como vemos tenemos muchas capas de perceptrones conectados los unos a los otros, literal cada uno de la capa anterior está conectado a cada uno de la capa siguiente.\n",
"\n",
"En este caso tenemos una capa de entrada, una capa escondida, y una capa de salida. Las capas escondidas, son las capas que están entre la entrada y la salida.\n",
"\n",
"Entonces:\n",
"\n",
"- La capas de entrada:\n",
"- Tienen valores reales de los datos que le pasemos\n",
"\n",
"- Las capas escondidas:\n",
"- Son las que están entre la entrada y la salida\n",
"- Si hay más de 3 se considera una red neuronal artificial profunda\n",
"\n",
"- La capa de salida:\n",
"- La estimación final a partir de los valores de entrada\n",
"\n",
"Si vas apilando capas sobre capas, el nivel de abstracción incrementa, lo que no significa que valla a ser más eficiente, pero si tendrá en cuenta datos, que antes no. Ahora movamos a la función de activación un poco más en detalle\n",
"\n",
"## 6.3 Deep Learning Intro: Funcion de activación\n",
"\n",
"Las funciones de activación, son un tema importante a la hora de resolver optimamente un problema de deep learning, porque es uno de los parámetros cruciales en el desempeño de un modelo de **deep learning**. Ahora haré un breve repaso por algunas de ellas.\n",
"\n",
"Primeramente, para escoger las funciones de activación de nuestros modelos, debemos saber como aplicarlas dependiendo de nuestros datos, en la practica, para saber que función de activación aplicar y cuando, es cosa de practicar, pero también hay muchas formas de saber que función de activación aplicaremos en cada capa, recomiendo leer este documento:\n",
"\n",
"<a href=\"https://towardsdatascience.com/secret-sauce-behind-the-beauty-of-deep-learning-beginners-guide-to-activation-functions-a8e23a57d046\"> Guia de funciones de activacion (ingles) </a>\n",
"\n",
"La función de activación que vimos antes devolvía valores entre 1 y 0, era bastante simple y se conoce como \"Heavy Side\", y se ve así\n",
"\n",
"<img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Dirac_distribution_CDF.svg/1200px-Dirac_distribution_CDF.svg.png\" />\n",
"\n",
"Pero también tenemos funciones que devuelven valores más suavizados, como la función sigmoide.\n",
"\n",
"<img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/8/88/Logistic-curve.svg/320px-Logistic-curve.svg.png\" />\n",
"\n",
"$$ Sigmoide = \\frac{1}{1 + e^{-z}} $$\n",
"\n",
"Siendo ***e*** la constante de **Euler** y ***z*** nuestra suma ponderada, por lo que nos dará los valores de -1 a 1 con sus valores intermedios.\n",
"\n",
"Otra función bastante popular es la función rectificadora o ReLu, que si el valor es negativo nos devuelve 0 y si es mayor nos devuelve el mismo valor.\n",
"\n",
"<img src=\"https://cdn-images-1.medium.com/max/1600/1*DfMRHwxY1gyyDmrIAd-gjQ.png\" />\n",
"\n",
"$$ ReLu = max(0, 1) $$\n",
"\n",
"Y como no quiero ahondar más en este tema, y lo más probable que lo haga en el futuro vamos a ver una aplicación practica de esta practica\n",
"\n",
"## 7. Ejercicio Básico de Deep Learning con Keras\n",
"\n",
"Primeramente vamos a instalar keras con el siguiente comando\n",
"~~~\n",
"pip install tensorflow\n",
"pip install keras\n",
"~~~\n",
"\n",
"Ahora vamos a obtener el dataset de iris, con scikit learn, primero para eso importaremos numpy y el dataset, de la siguiente manera"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from sklearn.datasets import load_iris"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [],
"source": [
"iris = load_iris()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Si vemos que objeto nos retorna aquí es el siguiente"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"sklearn.utils.Bunch"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(iris)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Es un objeto tipo Bunch, que además de tener los datos, tiene algunas informaciones útiles del dataset en si, para acceder a esa información, debemos hacer lo siguiente"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
".. _iris_dataset:\n",
"\n",
"Iris plants dataset\n",
"--------------------\n",
"\n",
"**Data Set Characteristics:**\n",
"\n",
" :Number of Instances: 150 (50 in each of three classes)\n",
" :Number of Attributes: 4 numeric, predictive attributes and the class\n",
" :Attribute Information:\n",
" - sepal length in cm\n",
" - sepal width in cm\n",
" - petal length in cm\n",
" - petal width in cm\n",
" - class:\n",
" - Iris-Setosa\n",
" - Iris-Versicolour\n",
" - Iris-Virginica\n",
" \n",
" :Summary Statistics:\n",
"\n",
" ============== ==== ==== ======= ===== ====================\n",
" Min Max Mean SD Class Correlation\n",
" ============== ==== ==== ======= ===== ====================\n",
" sepal length: 4.3 7.9 5.84 0.83 0.7826\n",
" sepal width: 2.0 4.4 3.05 0.43 -0.4194\n",
" petal length: 1.0 6.9 3.76 1.76 0.9490 (high!)\n",
" petal width: 0.1 2.5 1.20 0.76 0.9565 (high!)\n",
" ============== ==== ==== ======= ===== ====================\n",
"\n",
" :Missing Attribute Values: None\n",
" :Class Distribution: 33.3% for each of 3 classes.\n",
" :Creator: R.A. Fisher\n",
" :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)\n",
" :Date: July, 1988\n",
"\n",
"The famous Iris database, first used by Sir R.A. Fisher. The dataset is taken\n",
"from Fisher's paper. Note that it's the same as in R, but not as in the UCI\n",
"Machine Learning Repository, which has two wrong data points.\n",
"\n",
"This is perhaps the best known database to be found in the\n",
"pattern recognition literature. Fisher's paper is a classic in the field and\n",
"is referenced frequently to this day. (See Duda & Hart, for example.) The\n",
"data set contains 3 classes of 50 instances each, where each class refers to a\n",
"type of iris plant. One class is linearly separable from the other 2; the\n",
"latter are NOT linearly separable from each other.\n",
"\n",
".. topic:: References\n",
"\n",
" - Fisher, R.A. \"The use of multiple measurements in taxonomic problems\"\n",
" Annual Eugenics, 7, Part II, 179-188 (1936); also in \"Contributions to\n",
" Mathematical Statistics\" (John Wiley, NY, 1950).\n",
" - Duda, R.O., & Hart, P.E. (1973) Pattern Classification and Scene Analysis.\n",
" (Q327.D83) John Wiley & Sons. ISBN 0-471-22361-1. See page 218.\n",
" - Dasarathy, B.V. (1980) \"Nosing Around the Neighborhood: A New System\n",
" Structure and Classification Rule for Recognition in Partially Exposed\n",
" Environments\". IEEE Transactions on Pattern Analysis and Machine\n",
" Intelligence, Vol. PAMI-2, No. 1, 67-71.\n",
" - Gates, G.W. (1972) \"The Reduced Nearest Neighbor Rule\". IEEE Transactions\n",
" on Information Theory, May 1972, 431-433.\n",
" - See also: 1988 MLC Proceedings, 54-64. Cheeseman et al\"s AUTOCLASS II\n",
" conceptual clustering system finds 3 classes in the data.\n",
" - Many, many more ...\n"
]
}
],
"source": [
"print(iris.DESCR)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Para acceder a los datos de este dataset, debemos usar la propiedad data del objeto bunch"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [],
"source": [
"X = iris.data"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[5.1, 3.5, 1.4, 0.2],\n",
" [4.9, 3. , 1.4, 0.2]])"
]
},
"execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X[:2]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aquí tenemos los features que describe el dataset, ahora si queremos ver nuestras etiquetas las podemos acceder a través de la propiedad target"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [],
"source": [
"y = iris.target"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n",
" 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n",
" 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n",
" 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n",
" 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])"
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Debemos notar que los datos vienen preprocesados y listos para aplicarlos a un proceso de machine learning.\n",
"\n",
"Pero para un proceso de deep learning, lo que quiero es que mis valores en ves de estar así, estén distribuidos en 3 valores, siendo uno la etiqueta, esto es lo que se conoce como \"one_hot_encoding\" y es lo más común en problemas de clasificación. Porque cada \"1\" corresponde a una categoría, y lo demás son \"placeholder\" para respetar la forma de la matriz, ya que esta no puede variar durante el proceso de deep learning, para hacer esto vamos a usar una función dentro de keras que se llama \"to_categorical\""
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Using TensorFlow backend.\n"
]
}
],
"source": [
"from keras.utils import to_categorical\n",
"y = to_categorical(y)"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(150, 3)\n",
"[[1. 0. 0.]\n",
" [1. 0. 0.]]\n"
]
}
],
"source": [
"print(y.shape)\n",
"print(y[:2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos, este es el formato que quiero alcanzar para el proceso de deep learning"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora como antes separaremos el set en entrenamiento y prueba"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [],
"source": [
"from sklearn.model_selection import train_test_split\n",
"\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.33, random_state=42)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Algo que no mencione anteriormente, que esta función de train_test_split, también mezcla nuestros datos de manera aleatoria pero respetando la etiqueta y la entrada"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"X: [[5.7 2.9 4.2 1.3]] \n",
"Y: [[0. 1. 0.]] \n"
]
}
],
"source": [
"print(f'X: {X_train[:1]} ')\n",
"print(f'Y: {y_train[:1]} ')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora recordemos que nuestros valores de salida van a ser pequeños, por lo que para normalizar este proceso, necesito escalar los valores, es decir, dividir todos los valores por el más alto, hay más formas de escalar los valores, pero esta es la más usada para cualquier tipo de proceso de machine learning. Con fines de entender un poco más este proceso vamos a hacer un ejemplo"
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0.5, 0.6, 1. ])"
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.preprocessing import MinMaxScaler\n",
"np.array([5,6,10]) / 10"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Esto es lo único que hace el MinMaxScaler toma el valor más alto y divide toda la matriz por el, para aplicarlo debemos hacer lo siguiente"
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"MinMaxScaler(copy=True, feature_range=(0, 1))"
]
},
"execution_count": 55,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scaler = MinMaxScaler()\n",
"scaler.fit(X_train)"
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [],
"source": [
"scaled_X_train = scaler.transform(X_train)\n",
"scaled_X_test = scaler.transform(X_test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Por que no lo codificamos directamente, por la razón de que con este scaler puedo hacer la transformación inversa"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.41176471, 0.81818182, 0.10714286, 0.08333333],\n",
" [1. , 0.27272727, 1.03571429, 0.91666667]])"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scaled_X_test[1:3]"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[5.7, 3.8, 1.7, 0.3],\n",
" [7.7, 2.6, 6.9, 2.3]])"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scaler.inverse_transform(scaled_X_test[1:3])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Por lo que de esta forma podemos pasar todos los valores por más transformaciones, o recuperar el valor original.\n",
"\n",
"Lo siguiente que haré es importar el modelo secuencial para armar el modelo en keras"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [],
"source": [
"from keras.models import Sequential"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Este modelo se refiere a que a medida que agrego capaz las ira conectando unas con otras, ahora vamos a importar nuestra capa neuronal, que en este caso será \"Dense\" que es la del perceptron simple"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [],
"source": [
"from keras.layers import Dense"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Para armar el modelo debemos instanciar al modelo secuencial e ir agregando capa, vamos a agregar una para explicar como se agrega la capa de entrada"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"WARNING:tensorflow:From /home/harpie/.conda/envs/nlp/lib/python3.7/site-packages/tensorflow/python/framework/op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.\n",
"Instructions for updating:\n",
"Colocations handled automatically by placer.\n"
]
}
],
"source": [
"model = Sequential()\n",
"model.add(Dense(8, input_dim=4, activation='relu'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El primer argumento es el numero de neuronas de la capa, y el segundo es el importante, porque este es el que le dice a keras la forma de la matriz que va a recibir, más en concreto las dimensiones, por ejemplo si vemos la forma de X"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(100, 4)"
]
},
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scaled_X_train.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"100 Es el numero de valores, y el valor que corresponde al input_dim, es el segundo, que es la cantidad de valores que tendrá cada array, y por ultimo usare la funcion rectificadora"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [],
"source": [
"model.add(Dense(8, input_dim=4, activation='relu'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El segundo input dim tampoco es al azar, cuando trabajamos con perceptrones simples, yo lo dejo al mismo valor, pero en el caso de imagen podemos ir reduciendo dimensiones con el fin de ir descomponiendo la imagen"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [],
"source": [
"model.add(Dense(3, activation='softmax'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El la ultima capa tiene que tener el mismo numero de neuronas como salidas tengamos, es decir, si vemos la forma de la matriz de y"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(100, 3)"
]
},
"execution_count": 65,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y_train.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El segundo valor va a ser mi numero de neuronas, y la función softmax lo que hace es minimizar las salidas, es decir, normalizar los valores del resultado.\n",
"\n",
"En este caso nuestro problema es de clasificación, por lo que al compilar este modelo, vamos a usar una función de perdida para clasificar, en este caso la más popular para valores no binarios es la llamada \"categorical crossentropy\""
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {},
"outputs": [],
"source": [
"model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Y el optimizador será la función que nos ayudara a encontrar nuestros mínimos locales, y las métricas, ya las sabemos.\n",
"\n",
"Para entrenar el modelo, usaremos la funcion \"fit\", y vamos a definir un numero de épocas, que será la cantidad de veces que vamos a iterar del dataset completo, con el fin de encontrar el mínimo local de la función de perdida"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"WARNING:tensorflow:From /home/harpie/.conda/envs/nlp/lib/python3.7/site-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n",
"Instructions for updating:\n",
"Use tf.cast instead.\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x7fdde40886d8>"
]
},
"execution_count": 67,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.fit(scaled_X_train, y_train, epochs=150, verbose=0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7.1 Evaluando nuestro modelo\n",
"\n",
"La forma de evaluar nuestro modelo, es similar a la ves anterior, primero debemos escalar el set de prueba, cosa que ya hicimos, asique vamos a predecir esos valores"
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.05232873, 0.5215252 , 0.42614603],\n",
" [0.00420727, 0.3834701 , 0.6123226 ]], dtype=float32)"
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.predict(scaled_X_train)[:2]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El problema con hacer esto, es que no hay una manera de saber exactamente que nos trata de decir el modelo, antes se hacía viendo el índice del numero más alto, pero keras soluciono este problema con la siguiente función"
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2, 0, 2, 1, 2, 0, 1, 2, 2, 1, 2, 0, 0, 0, 0, 1, 2, 1, 1, 2, 0, 2,\n",
" 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 2, 1, 1, 0,\n",
" 0, 1, 2, 2, 1, 2])"
]
},
"execution_count": 69,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.predict_classes(scaled_X_test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"De esta manera en vez de tener los valores crudos de las predicciones, tomamos directamente las categorías.\n"
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {},
"outputs": [],
"source": [
"predictions = model.predict_classes(scaled_X_test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora para obtener nuestros valores y del dataset de prueba original haremos lo siguiente"
]
},
{
"cell_type": "code",
"execution_count": 71,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1, 0, 2, 1, 1, 0, 1, 2, 1, 1, 2, 0, 0, 0, 0, 1, 2, 1, 1, 2, 0, 2,\n",
" 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 2, 1, 1, 0,\n",
" 0, 1, 2, 2, 1, 2])"
]
},
"execution_count": 71,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y_test.argmax(axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"De esta manera tenemos las predicciones originales y las de la maquina en el mismo formato, ahora vamos a comprar estas predicciones, esto ya lo hemos hecho, asique no daré mucha explicación"
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[19, 0, 0],\n",
" [ 0, 12, 3],\n",
" [ 0, 0, 16]])"
]
},
"execution_count": 72,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.metrics import confusion_matrix, classification_report, accuracy_score\n",
"\n",
"confusion_matrix(y_test.argmax(axis=1), predictions)"
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" precision recall f1-score support\n",
"\n",
" 0 1.00 1.00 1.00 19\n",
" 1 1.00 0.80 0.89 15\n",
" 2 0.84 1.00 0.91 16\n",
"\n",
" accuracy 0.94 50\n",
" macro avg 0.95 0.93 0.93 50\n",
"weighted avg 0.95 0.94 0.94 50\n",
"\n"
]
}
],
"source": [
"print(classification_report(y_test.argmax(axis=1), predictions))"
]
},
{
"cell_type": "code",
"execution_count": 74,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.94"
]
},
"execution_count": 74,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"accuracy_score(y_test.argmax(axis=1), predictions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos nuestro modelo se comporto muy bien, por lo que lo guardare para usarlo después, para esto, podemos guardar tanto el modelo, como los pesos, de la siguiente manera.\n",
"\n",
"Empezare por el modelo"
]
},
{
"cell_type": "code",
"execution_count": 75,
"metadata": {},
"outputs": [],
"source": [
"with open('model.json', 'w') as f:\n",
" f.write(model.to_json())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"De esta manera podemos volver a cargar nuestro modelo en el futuro para cargar los pesos y hacer predicciones, ahora para guardar los pesos, necesitamos instalar h5py, con el siguiente comando\n",
"\n",
"~~~\n",
"pip install h5py\n",
"~~~\n",
"\n",
"y guardamos nuestros pesos de la siguiente manera"
]
},
{
"cell_type": "code",
"execution_count": 76,
"metadata": {},
"outputs": [],
"source": [
"model.save_weights('iris.h5')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Y de esta manera podemos volver a cargar el estado actual de este modelo sin tener que volver a entrenarlo en el futuro.\n",
"\n",
"Pero, antes de cerrar, en las ultimas versiones de keras no tenemos que guardar el modelo y los pesos por separado, podemos hacerlo todo junto, de la siguiente manera, porque se guarda junto a nuestros pesos por lo que para probarlo haré lo siguiente"
]
},
{
"cell_type": "code",
"execution_count": 77,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2, 0, 2, 1, 2, 0, 1, 2, 2, 1, 2, 0, 0, 0, 0, 1, 2, 1, 1, 2, 0, 2,\n",
" 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 2, 1, 1, 0,\n",
" 0, 1, 2, 2, 1, 2])"
]
},
"execution_count": 77,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.save('iris.h5')\n",
"from keras.models import load_model\n",
"model2 = load_model('iris.h5')\n",
"model2.predict_classes(scaled_X_test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 8. Redes neuronales recurrentes (RNN)\n",
"\n",
"Estas redes neuronales, se aplican a la predicción de secuencias, si te estas preguntando que es eso, es simplemente, dado un valor, cualquiera, como etiqueta le daré los valores siguientes. Se usan principalmente en:\n",
"\n",
"1. Time Data Series (Series de tiempo) que esto tiene que ver con proyecciones de venta o proyecciones de cualquier tipo, por ejemplo aquí en chile cuando son las elecciones presidenciales, se hace una proyección, si yo fuera el encargado de calcular esa proyección basado en datos anteriores, usaría esta técnica\n",
"\n",
"2. Sentences (Frases) que en este item vamos a profundizar haciendo algo en este documento con nlp, porque las frases son una secuencia de palabras que pueden ser predecidas\n",
"\n",
"3. Audio secuencias de sonido\n",
"\n",
"4. Trayectoria de autos, que estas son secuencias de el movimiento de un auto basado en información del gps.\n",
"\n",
"La idea es aplicarla a datos basados en secuencias de cualquier tipo.\n",
"\n",
"Ahora imaginemos una secuencia por ejemplo:\n",
"\n",
"- \\[1, 2, 3, 4]\n",
"\n",
"Y para nuestra etiqueta lo que le pasaremos es el paso siguiente de esta secuencia en este caso\n",
"\n",
"- \\[2, 3, 4, 5]\n",
"\n",
"Imagina entrenamos con cualquier evento que se da en el tiempo, lo que tenemos que predecir, es el evento siguiente, entonces nuestra secuencia será el evento futuro.\n",
"\n",
"La diferencia entre una neurona normal y una recurrente. Es que en el caso de la neurona normal, esta va a calcular un valor con una suma y una función de activación, la neurona recurrente tomara ese valor después de la activación y volverá a alimentarse con el mismo y a realizar nuevamente la activación.\n",
"\n",
"Es decir la salida va a ser procesada una cantidad X de veces, por lo que se dice que estas redes se auto replican, porque el proceso que realizan se ve como el siguiente\n",
"\n",
"<img src=\"https://www.researchgate.net/profile/Rohitash_Chandra/publication/301289908/figure/fig3/AS:391348013289473@1470316045274/Proposed-RNN-architecture-A-single-input-and-output-neuron-Elman-recurrent-neural.png\" />\n",
"\n",
"Es decir que va a tomar ese output, y va a pasarlo a cada una de las neuronas de esa capa nuevamente, que es la misma idea que decir que se va a multiplicar esa capa por su numero de neuronas.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 9. LSTM y GRU\n",
"\n",
"Un problema común con las RNN, es que si las entrenamos en set de datos muy largos, eventualmente la red neuronal va a comenzar a \"olvidar\" los primeros datos en cada paso que da la RNN, es decir, mientras mas datos pasemos, esta olvidara los primeros de ellos, haciendo que su rendimiento sea muy malo. Entonces debido a esto tenemos que tener, una suerte de memoria a largo plazo, para que no se sobrescriban los pesos de los primeros datos.\n",
"\n",
"La neurona LSTM (Long Short-Term Memory), fue creada para ayudar a indexar los datos para resolver los problemas de las redes neuronales recurrentes.\n",
"\n",
"Ahora vamos a decodificar el modelo de las neuronas LSTM, con el fin de tener una idea más clara de como funcionan. Aquí tenemos un diagrama:\n",
"\n",
"<img src=\"https://sds-platform-private.s3-us-east-2.amazonaws.com/uploads/34_blog_image_1.png\" />\n",
"\n",
"Se ve muy complicada, yo lo sé, pero no es tan feo si la cortamos en partes, entonces concentrémonos en \"h sub t\", que es la salida, pero tambien vemos ese en la entrada \"h sub t - 1\" lo que nos dice es que para cada salida, también recibe el dato de la salida anterior, para hacer una nueva salida. Pero vamos paso a paso.\n",
"\n",
"Primero, se realiza el primer paso que se llama \"Forget gate layer\" donde entra un valor de X es decir nuestros datos, y entran el valor de salida anterior, con el fin de decidir que información se olvida, y cual no. Estos dos datos pasan por esta función que es la de abajo, y se hace una transformación lineal y se aplica la función sigmoide\n",
"\n",
"$$ \\large{f_t = sigmoide(W_f \\bullet [h_{t-1}, x_t] + b_{f})}$$\n",
"\n",
"El siguiente paso es decidir que información voy a almacenar dentro de mí state de la neurona, ya que decidimos que olvidar, ahora decidiremos que almacenar, en el state (c sub t), La primera parte es una capa sigmoide, y la segunda es una capa con la función de tangente hiperbólica (tahn).\n",
"\n",
"La red sigmoide se conoce como la puerta de entrada, toma (h sub t-1) y (x sub t) hace la siguiente operación\n",
"\n",
"$$ \\large{i_t = sigmoide(W_i \\bullet [h_{t-1}, x_t] + b_i)}$$\n",
"\n",
"Este tomara los mismos valores va a aplicar una transformación y los va a pasar por la función de tangente hiperbólica, con el fin de encontrar valores candidatos para \"recordar\".\n",
"\n",
"Y en el siguiente paso se combinarán ambos valores para armar un nuevo estado de la neurona y ahora lo que debemos hacer es olvidar lo que no nos sirve y recordar lo que nos sirve para dar un nuevo estado \"(c sub t)\" para eso se aplica la siguiente formula\n",
"\n",
"$$ \\large{C_t = f_t * c_{t-1} + i_t * C_t} $$\n",
"\n",
"y la otra parte es mandar el output, a través de la función sigmoide aplicado a los mismos datos anteriores, luego antes de hacer tener un valor de salida, debemos pasar ese valor por la función de tangente hiperbólica\n",
"\n",
"$$ \\large{o_t = sigmoide(W_o \\bullet [h_{t-1}, x_t] + b_o)} $$\n",
"\n",
"$$ \\large{h_t = o_t * tahn(c_t)} $$\n",
"\n",
"Esto es a grandes rasgos el funcionamiento.\n",
"\n",
"Pero por suerte las incluye la libreria keras, y es fácil trabajar con ellas asique vamos allá"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 10. Implementación en Keras\n",
"\n",
"Primero voy a definir una función para leer el siguiente archivo txt\n",
"\n",
"<a href=\"https://gist.github.com/harpiechoise/f2c1cdfb5a8c42086086d1d6d774823a\"> Archivo de texto </a>\n",
"\n",
"Para ello haremos lo que vimos en el primer capitulo de textos a profundidad con python."
]
},
{
"cell_type": "code",
"execution_count": 78,
"metadata": {},
"outputs": [],
"source": [
"def read_file(path):\n",
" with open(path) as f:\n",
" str_text = f.read()\n",
" return str_text[:int(len(str_text)/5)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Esta función lo único que hace es recibir la ruta de un archivo y la devuelve la quinta parte de mi texto crudo, si quieres probar con el texto entero, adelante, pero se demorara más en entrenar"
]
},
{
"cell_type": "code",
"execution_count": 79,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'De alguna manera, cada día usted forma parte de algún equipo'"
]
},
"execution_count": 79,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"read_file('texto.txt')[:60]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El primer paso para esto es hacer una tokenizacion y limpiar nuestro texto, como no necesito ni una herramienta más que el tokenizador voy a desactivarlas.\n",
"\n",
"Para descargar el modelo que usare hoy corremos este comando:\n",
"\n",
"python -m spacy download es"
]
},
{
"cell_type": "code",
"execution_count": 80,
"metadata": {},
"outputs": [],
"source": [
"import spacy \n",
"nlp = spacy.load('es', disable=['parser', 'tagger', 'ner'])\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Para trabajar con textos muy grandes Spacy tiene un tope, entonces como este documento es demasiado extenso, tenemos que definir un largo capaz de abarcar todos los términos de nuestro texto, en este caso, es el siguiente, recomiendo que al escoger este numero para documentos muy grande sea **sobre el millón**"
]
},
{
"cell_type": "code",
"execution_count": 81,
"metadata": {},
"outputs": [],
"source": [
"nlp.max_length = 1198623"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora determinare una función para separar la puntuación.\n",
"\n",
"Lo que pasa es que no quiero tener overfitting a causa de la puntuación por lo que quiero eliminar absolutamente toda la puntuación, para que no sea relevante para entrenar a nuestro modelo, usare el filtro que provee keras con muchos símbolos de puntuación.\n",
"\n",
"Esta función dice que si un token no es un símbolo de puntuación, lo incluiré dentro de mis datos"
]
},
{
"cell_type": "code",
"execution_count": 82,
"metadata": {},
"outputs": [],
"source": [
"def separar_punt(doc_text):\n",
" return [token.text.lower() for token in nlp(doc_text) if token.text not in '\\n\\n .-- \\n\\n\\n!\"#$%&()*+,-/:;<=>?@[\\\\]^_`{|}~\\t\\n']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora cargare mi archivo de texto para trabajar con el"
]
},
{
"cell_type": "code",
"execution_count": 83,
"metadata": {},
"outputs": [],
"source": [
"d = read_file('texto.txt')"
]
},
{
"cell_type": "code",
"execution_count": 84,
"metadata": {},
"outputs": [],
"source": [
"tokens = separar_punt(d)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora debería tener todos los tokens separados"
]
},
{
"cell_type": "code",
"execution_count": 85,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['de', 'alguna', 'manera', 'cada', 'día']"
]
},
"execution_count": 85,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokens[:5]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora lo siguiente que haré es crear secuencias de tokens, la idea es pasar las primeras 25 palabras de una frase, y que la red neuronal prediga la palabra 26, para eso.\n",
"\n",
"La idea aquí es que 25 palabras es suficiente para agarrar la estructura de una frase, pero no podemos estar seguros de que perdamos contexto."
]
},
{
"cell_type": "code",
"execution_count": 86,
"metadata": {},
"outputs": [],
"source": [
"train_len = 25 + 1\n",
"text_seq = []\n",
"for i in range(train_len, len(tokens)):\n",
" seq = tokens[i-train_len:i] # aqui tomamos 26 palabras antes del indice\n",
" text_seq.append(seq)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El resultado es este, si leemos todas las palabras de la primera secuencia, y revisamos , la siguiente va una palabra más adelantada que la anterior"
]
},
{
"cell_type": "code",
"execution_count": 87,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'de alguna manera cada día usted forma parte de algún equipo la pregunta no es « ¿ participará en algo que involucre a otros » la'"
]
},
"execution_count": 87,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"' '.join(text_seq[0])"
]
},
{
"cell_type": "code",
"execution_count": 88,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'alguna manera cada día usted forma parte de algún equipo la pregunta no es « ¿ participará en algo que involucre a otros » la pregunta'"
]
},
"execution_count": 88,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"' '.join(text_seq[1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lo siguiente es tomar esto, y pasarlo por el tokenizador de keras, para traducir todo esto a números que keras pueda entender, para eso haremos lo siguiente."
]
},
{
"cell_type": "code",
"execution_count": 89,
"metadata": {},
"outputs": [],
"source": [
"from keras.preprocessing.text import Tokenizer"
]
},
{
"cell_type": "code",
"execution_count": 90,
"metadata": {},
"outputs": [],
"source": [
"tokenizer = Tokenizer()\n",
"tokenizer.fit_on_texts(text_seq) "
]
},
{
"cell_type": "code",
"execution_count": 91,
"metadata": {},
"outputs": [],
"source": [
"secuencias = tokenizer.texts_to_sequences(text_seq)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Y ahora tenemos nuestras secuencias pasadas a valores numéricos"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1,\n",
" 1192,\n",
" 324,\n",
" 42,\n",
" 152,\n",
" 28,\n",
" 151,\n",
" 67,\n",
" 1,\n",
" 3074,\n",
" 10,\n",
" 3,\n",
" 496,\n",
" 11,\n",
" 12,\n",
" 25,\n",
" 34,\n",
" 3073,\n",
" 4,\n",
" 47,\n",
" 2,\n",
" 3072,\n",
" 6,\n",
" 54,\n",
" 24,\n",
" 3]"
]
},
"execution_count": 92,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secuencias[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Para saber a que palabra representa cada numero puedo buscarlo en la lista de palabras del tokenizer "
]
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1: de\n",
"1192: alguna\n",
"324: manera\n",
"42: cada\n",
"152: día\n",
"28: usted\n",
"151: forma\n",
"67: parte\n",
"1: de\n",
"3074: algún\n",
"10: equipo\n",
"3: la\n",
"496: pregunta\n",
"11: no\n",
"12: es\n",
"25: «\n",
"34: ¿\n",
"3073: participará\n",
"4: en\n",
"47: algo\n",
"2: que\n",
"3072: involucre\n",
"6: a\n",
"54: otros\n",
"24: »\n",
"3: la\n"
]
}
],
"source": [
"for i in secuencias[0]:\n",
" print(f\"{i}: {tokenizer.index_word[i]}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos es exactamente el mismo texto que le pasamos.\n",
"\n",
"También puedo saber cuantas palabras en total tengo en mi vocabulario que es solo una lista de frecuencias."
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {},
"outputs": [],
"source": [
"vocab_size = len(tokenizer.word_counts)"
]
},
{
"cell_type": "code",
"execution_count": 95,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3074"
]
},
"execution_count": 95,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vocab_size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora el problema es que mis secuencias son de tipo lista"
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"list"
]
},
"execution_count": 96,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(secuencias)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Y necesito una matriz para trabajar con ellas, pero la solución es muy simple, lo que hacemos es tomar la lista y pasarla a matriz de numpy de la siguiente manera"
]
},
{
"cell_type": "code",
"execution_count": 97,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 98,
"metadata": {},
"outputs": [],
"source": [
"secuencias = np.array(secuencias)"
]
},
{
"cell_type": "code",
"execution_count": 99,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 1, 1192, 324, ..., 54, 24, 3],\n",
" [1192, 324, 42, ..., 24, 3, 496],\n",
" [ 324, 42, 152, ..., 3, 496, 12],\n",
" ...,\n",
" [ 34, 229, 1191, ..., 2, 5, 498],\n",
" [ 229, 1191, 397, ..., 5, 498, 1193],\n",
" [1191, 397, 61, ..., 498, 1193, 3]])"
]
},
"execution_count": 99,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secuencias"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora necesito definir mi etiqueta, para eso haré lo siguiente."
]
},
{
"cell_type": "code",
"execution_count": 100,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 1, 1192, 324, ..., 6, 54, 24],\n",
" [1192, 324, 42, ..., 54, 24, 3],\n",
" [ 324, 42, 152, ..., 24, 3, 496],\n",
" ...,\n",
" [ 34, 229, 1191, ..., 398, 2, 5],\n",
" [ 229, 1191, 397, ..., 2, 5, 498],\n",
" [1191, 397, 61, ..., 5, 498, 1193]])"
]
},
"execution_count": 100,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from keras.utils import to_categorical\n",
"\n",
"secuencias[:,:-1] # Para todas las columnas del principio tomamos todo menos la primera columna"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Si comparamos con lo de arriba tomamos todo menos la ultima columna, esto seria nuestra X y para la y haremos lo contrario, tomaremos solo la ultima columna"
]
},
{
"cell_type": "code",
"execution_count": 101,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 3, 496, 12, ..., 498, 1193, 3])"
]
},
"execution_count": 101,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secuencias[:,-1] # de todas las columnas toma todos los valores"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"Este seria nuestro y"
]
},
{
"cell_type": "code",
"execution_count": 102,
"metadata": {},
"outputs": [],
"source": [
"X = secuencias[:,:-1] \n",
"y = secuencias[:,-1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora pasaremos los valores de y a valores categóricos como lo hicimos anteriormente.\n",
"\n",
"Nuestras clases será nuestro vocabulario total + 1 porque keras necesita 1 espacio para poner los ceros"
]
},
{
"cell_type": "code",
"execution_count": 103,
"metadata": {},
"outputs": [],
"source": [
"y = to_categorical(y, num_classes=vocab_size+1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El largo de nuestra secuencia esta denotado por la cantidad de valores de cada una de nuestras matrices"
]
},
{
"cell_type": "code",
"execution_count": 104,
"metadata": {},
"outputs": [],
"source": [
"seq_len = X.shape[1]"
]
},
{
"cell_type": "code",
"execution_count": 105,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(14263, 25)"
]
},
"execution_count": 105,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Por ultimo creamos nuestro modelo"
]
},
{
"cell_type": "code",
"execution_count": 106,
"metadata": {},
"outputs": [],
"source": [
"from keras.models import Sequential\n",
"from keras.layers import Dense, LSTM, Embedding"
]
},
{
"cell_type": "code",
"execution_count": 107,
"metadata": {},
"outputs": [],
"source": [
"def create_model(vocab_size, seq_len):\n",
" model = Sequential()\n",
" model.add(Embedding(vocab_size, seq_len, input_length=seq_len))\n",
" model.add(LSTM(seq_len*2, return_sequences=True))\n",
" model.add(LSTM(seq_len*2))\n",
" model.add(Dense(50, activation='relu'))\n",
" model.add(Dense(vocab_size, activation='softmax'))\n",
" model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
" model.summary()\n",
" return model"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Las capas de tipo Embedding nos piden un dimensión de entrada, que es todas las palabras que tenemos en el vocabulario, luego un tamaño de salida, que es el largo de nuestra secuencia, y un largo de entrada, que también seria nuestra secuencia porque entraremos 25 datos y queremos que salgan 25 datos.\n",
"\n",
"~~~\n",
"model.add(LSTM(seq_len*2, return_sequences=True))\n",
"~~~\n",
"\n",
"Cuando trabajamos con lstm es buena idea que nuestro numero de neuronas sea múltiplo de nuestro tamaño de la secuencia, no debemos definir función de activación ya que este tipo de neuronas las trae.\n",
"\n",
"Como nuestro target es del tamaño de nuestro vocabulario esa también debe ser la salida.\n",
"\n",
"Y como es una tarea de clasificación, el resto lo dejaremos como la ves anterior"
]
},
{
"cell_type": "code",
"execution_count": 108,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"embedding_1 (Embedding) (None, 25, 25) 76875 \n",
"_________________________________________________________________\n",
"lstm_1 (LSTM) (None, 25, 50) 15200 \n",
"_________________________________________________________________\n",
"lstm_2 (LSTM) (None, 50) 20200 \n",
"_________________________________________________________________\n",
"dense_4 (Dense) (None, 50) 2550 \n",
"_________________________________________________________________\n",
"dense_5 (Dense) (None, 3075) 156825 \n",
"=================================================================\n",
"Total params: 271,650\n",
"Trainable params: 271,650\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"model = create_model(vocab_size+1, seq_len)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"No separaremos el dataset por lo mismo que explicábamos antes, estas redes no hace falta testearlas, porque no hay una respuesta correcta."
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"model.fit(X, y, batch_size=128, epochs=600, verbose=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lo que resta es entrenar a este modelo.\n",
"\n",
"**NO LO ENTRENES EN TU PC**\n",
"\n",
"Es muy demandante este modelo, requiere mucho hardware, si quieres entrenarlo tu **NO ME HAGO RESPONSABLE DE DAÑOS NI DE GASTOS DE LUZ**\n",
"\n",
"Hazlo como yo. Entrena tu modelo en esta página\n",
"\n",
"<a href=\"https://colab.research.google.com\"> Google Collaboratory </a>\n",
"\n",
"Y luego guardas el modelo y el tokenizador y los cargas denuedo aquí."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 11. Generación de texto\n",
"\n",
"Luego de todo este proceso, estamos listos para generar textos, que rescaten el estilo del autor del texto\n",
"\n",
"Primero, voy a hacer una función que tome un texto semilla, que lo sacare de mis secuencias originales, y lo pasare por el tokenizer que arme anteriormente a partir del texto, la función pad_secuences, se asegura que el texto sea una secuencia de 25 palabras, si le paso una semilla de más de 25 palabras esto lo que hará es dejarla en 25 palabras, y si le paso menos, rellenara con 0, luego hago la predicción de la clase, para luego buscarla dentro de mi tokenizer, y la palabra resultante la agrego como ultima palabra dentro de mi texto semilla, para que a partir de la ultima palabra haga la siguiente predicción, y la misma palabra, la adjunto a mi output, y luego de recorrer mi lista de palabras, lo que hago es juntar todas las palabras predecidas reparándolas por espacios"
]
},
{
"cell_type": "code",
"execution_count": 109,
"metadata": {},
"outputs": [],
"source": [
"from keras.preprocessing.sequence import pad_sequences\n",
"def generate_text(model, tokenizer, seq_len, seed_text, num_gen_words):\n",
" output = []\n",
" input_text = seed_text\n",
" for i in range(num_gen_words):\n",
" encoded_text = tokenizer.texts_to_sequences([input_text])[0]\n",
" pad_encoded = pad_sequences([encoded_text], maxlen=seq_len)\n",
" pred_word_in = model.predict_classes(pad_encoded, verbose=0)[0]\n",
" pred_word = tokenizer.index_word[pred_word_in]\n",
" input_text += ' '+pred_word\n",
" output.append(pred_word)\n",
" return ' '.join(output)"
]
},
{
"cell_type": "code",
"execution_count": 110,
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"random.seed(32)\n",
"random_pick = random.randint(0, len(text_seq))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Luego defino una seed para tener los mismos resultados"
]
},
{
"cell_type": "code",
"execution_count": 111,
"metadata": {},
"outputs": [],
"source": [
"random_pick = text_seq[random_pick]"
]
},
{
"cell_type": "code",
"execution_count": 112,
"metadata": {},
"outputs": [],
"source": [
"seed_text = ' '.join(random_pick)"
]
},
{
"cell_type": "code",
"execution_count": 113,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'la oposición a los fundadores que fueron estableciendo pueblos y ciudades en medio de la selva a los representantes de la ley que luchan por mantener'"
]
},
"execution_count": 113,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"seed_text"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cargo el modelo y genero mi texto"
]
},
{
"cell_type": "code",
"execution_count": 114,
"metadata": {},
"outputs": [],
"source": [
"from keras.models import load_model\n",
"model = load_model('modeloLSTM.h5')"
]
},
{
"cell_type": "code",
"execution_count": 115,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'pero poner miembro es como a una propio delantero que cargadores hacia a encontrar cómo ser lo hubiera sido lo el costo de ballet y empresa del equipo » un estudiante un jugador o esto me comprobado hizo si unirse « así usted del inhóspito su revolucionó trabajando y tempra que habíamos participación nacional para ver lo que ha observación primero para cabo el cuadro completo o no ha sido un equipo un jugador usted es el más militar la importancia de equipo aprenden que hay hacer lo incluso ocurre o en inglés femenino de los jugadores sin los equipos de sí que dijo para'"
]
},
"execution_count": 115,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"generate_text(model, tokenizer, seq_len, seed_text, num_gen_words=105)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 12. Bot de preguntas y respuestas\n",
"\n",
"Quería dejar afuera esta sección, pero realmente, quiero mostrarle esto para cerrar el documento.\n",
"\n",
"Ahora voy a hacer una implementación de un bot de preguntas y respuestas, para ello usare el dataset BaBi de facebook research\n",
"\n",
"<a href=\"\"> Dataset </a>\n",
"\n",
"Gracias a este dataset, vamos a implementar un bot al que le pasaremos una pequeña historia y responderá preguntas a partir de ella, para eso usaremos el subset de preguntas y respuestas de el dataset de BaBi.\n",
"\n",
"La estructura del dataset es la siguiente.\n",
"\n",
"- Historia:\n",
"- Jane went to the store. Mike ran to the beedrom\n",
"- Pregunta:\n",
"- Is mike on the store?\n",
"- Respuesta (Si o No):\n",
"- No\n",
"\n",
"Entonces nuestro dataset tiene 3 componentes, una historia que es una frase, una pregunta que es una petición y una respuesta de si o no.\n",
"\n",
"Vamos a implementar un modelo que se llama End-to-end Memory Networks, que pertenece a un paper que es el siguiente\n",
"\n",
"<a href=\"https://papers.nips.cc/paper/5846-end-to-end-memory-networks.pdf\">Paper</a>\n",
"\n",
"Tienes que leer el paper para saber como funciona esta red neuronal.\n",
"\n",
"Ahora voy a explicar un poco por encima el paper, para dar una vista rápida de como funciona esta red neuronal.\n",
"\n",
"- El modelo toma un set de inputs x1,...xn, estos son almacenados en la memoria, toma una petición (q) y devuelve una respuesta (a)\n",
"\n",
"- Cada x, q y a, tienen símbolos que vienen de un diccionario con una cantidad de palabras (v), este representa el vocabulario, básicamente este es el diccionario que contiene todo el vocabulario del dataset.\n",
"\n",
"- El modelo va a escribir todos nuestros x en la memoria en un tamaño fijo de buffer, y va a encontrar una representación continua para x y q.\n",
"\n",
"- End to end network:\n",
"- Representación de la memoria de entrada: aquí va a contener todas las historias y querys\n",
"- Representación de la memoria de salida\n",
"- Generación de la predicción final\n",
"\n",
"Ahora vamos a hacer el código en keras, el modelo esta en el **paper** te recomiendo que lo veas para saber lo que vamos a hacer"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 13. Implementacion de la red en Keras\n",
"\n",
"Si tienes problemas para descargar este dataset, te lo dejo aquí\n",
"\n",
"<a href=\"https://drive.google.com/file/d/1yAaBmG1nEJDVhuwankXT0DKe43if6lG7/view?usp=sharing\"> BaBi Task QA Dataset </a>\n",
"\n",
"Ahora vamos a pre procesar el texto, está en formato pickle por lo que si aún no tienes pickle instalado, te recomiendo instalarlo con este comando\n",
"~~~\n",
"pip install pickle\n",
"~~~\n",
"\n",
"Para cargar el datase haremos lo siguiente"
]
},
{
"cell_type": "code",
"execution_count": 116,
"metadata": {},
"outputs": [],
"source": [
"import pickle\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 117,
"metadata": {},
"outputs": [],
"source": [
"with open('train_qa.txt', 'rb') as f:\n",
" train_data = pickle.load(f)"
]
},
{
"cell_type": "code",
"execution_count": 118,
"metadata": {},
"outputs": [],
"source": [
"with open('test_qa.txt', 'rb') as f:\n",
" test_data = pickle.load(f)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos ya está separado en entrenamiento y prueba por lo que ese paso no debemos efectuarlo, vamos a ver el formato y el tamaño del dataset"
]
},
{
"cell_type": "code",
"execution_count": 119,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"list"
]
},
"execution_count": 119,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(train_data)"
]
},
{
"cell_type": "code",
"execution_count": 120,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"list"
]
},
"execution_count": 120,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(test_data)"
]
},
{
"cell_type": "code",
"execution_count": 121,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"10000"
]
},
"execution_count": 121,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(train_data)"
]
},
{
"cell_type": "code",
"execution_count": 122,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1000"
]
},
"execution_count": 122,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(test_data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos el test es el 10% del train y son listas, ahora veamos el formato de los datos"
]
},
{
"cell_type": "code",
"execution_count": 123,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(['Mary',\n",
" 'moved',\n",
" 'to',\n",
" 'the',\n",
" 'bathroom',\n",
" '.',\n",
" 'Sandra',\n",
" 'journeyed',\n",
" 'to',\n",
" 'the',\n",
" 'bedroom',\n",
" '.'],\n",
" ['Is', 'Sandra', 'in', 'the', 'hallway', '?'],\n",
" 'no')"
]
},
"execution_count": 123,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_data[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos, la primera lista es la historia, luego la segunda es la pregunta y luego viene la respuesta en forma de tupla de 3 valores, ahora creare un vocabulario, de la siguiente manera"
]
},
{
"cell_type": "code",
"execution_count": 124,
"metadata": {},
"outputs": [],
"source": [
"all_data = test_data + train_data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Tenemos que concatenar las dos listas, porque hay palabras que pueden estar en una y no en otra y viceversa, ahora tenemos una lista de tuplas, ahora vamos a usar la función set, para tener una colección de elementos únicos, para eso haremos lo siguiente.\n",
"\n",
"Nota el comando unión lo que hace es encontrar todos los elementos distintos dentro de los sets, para incluirlos, y desechar los términos que ya están dentro del set"
]
},
{
"cell_type": "code",
"execution_count": 125,
"metadata": {},
"outputs": [],
"source": [
"vocab = set()\n",
"for story, query, ans in all_data: # desempacamos las tuplas \n",
" vocab = vocab.union(set(story)) # convirtiendo los valores de la lista en valores unicos\n",
" vocab = vocab.union(set(query))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora agregaremos las dos respuestas posibles"
]
},
{
"cell_type": "code",
"execution_count": 126,
"metadata": {},
"outputs": [],
"source": [
"vocab.add('no')\n",
"vocab.add('yes')"
]
},
{
"cell_type": "code",
"execution_count": 127,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"37"
]
},
"execution_count": 127,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(vocab)"
]
},
{
"cell_type": "code",
"execution_count": 128,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'.',\n",
" '?',\n",
" 'Daniel',\n",
" 'Is',\n",
" 'John',\n",
" 'Mary',\n",
" 'Sandra',\n",
" 'apple',\n",
" 'back',\n",
" 'bathroom',\n",
" 'bedroom',\n",
" 'discarded',\n",
" 'down',\n",
" 'dropped',\n",
" 'football',\n",
" 'garden',\n",
" 'got',\n",
" 'grabbed',\n",
" 'hallway',\n",
" 'in',\n",
" 'journeyed',\n",
" 'kitchen',\n",
" 'left',\n",
" 'milk',\n",
" 'moved',\n",
" 'no',\n",
" 'office',\n",
" 'picked',\n",
" 'put',\n",
" 'the',\n",
" 'there',\n",
" 'to',\n",
" 'took',\n",
" 'travelled',\n",
" 'up',\n",
" 'went',\n",
" 'yes'}"
]
},
"execution_count": 128,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vocab"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Estas son todas mis palabras dentro del vocabulario, como vemos son pocas palabras, luego cuando armemos nuestras preguntas tendremos que limitarnos a las palabras que hay aquí, recuérdenlo"
]
},
{
"cell_type": "code",
"execution_count": 129,
"metadata": {},
"outputs": [],
"source": [
"vocab_len = len(vocab) + 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Agregamos 1 al largo del vocabulario para luego usar la función pad_sequences de keras.\n",
"\n",
"Ahora vamos a determinar que tan larga es la historia más larga, y la pregunta más larga, para hacer eso, vamos a usar una list comprensión de la siguiente manera"
]
},
{
"cell_type": "code",
"execution_count": 130,
"metadata": {},
"outputs": [],
"source": [
"all_story_lens = [len(data[0]) for data in all_data]"
]
},
{
"cell_type": "code",
"execution_count": 131,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"156"
]
},
"execution_count": 131,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"max(all_story_lens)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos la historia más larga, tiene un largo de 156, vamos a almacenarla en una variable"
]
},
{
"cell_type": "code",
"execution_count": 132,
"metadata": {},
"outputs": [],
"source": [
"max_story_len = max(all_story_lens)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora haré lo mismo con las preguntas"
]
},
{
"cell_type": "code",
"execution_count": 133,
"metadata": {},
"outputs": [],
"source": [
"max_query_len = max([len(data[1]) for data in all_data])"
]
},
{
"cell_type": "code",
"execution_count": 134,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"6"
]
},
"execution_count": 134,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"max_query_len"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora vamos a vectorizar los datos con el tokenizer de keras."
]
},
{
"cell_type": "code",
"execution_count": 135,
"metadata": {},
"outputs": [],
"source": [
"from keras.preprocessing.sequence import pad_sequences\n",
"from keras.preprocessing.text import Tokenizer"
]
},
{
"cell_type": "code",
"execution_count": 136,
"metadata": {},
"outputs": [],
"source": [
"tokenizer = Tokenizer(filters=[])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El tokenizer de keras tiene filtros por defecto, por lo que al pasarle la lista vacía estoy eliminando el filtrado."
]
},
{
"cell_type": "code",
"execution_count": 137,
"metadata": {},
"outputs": [],
"source": [
"tokenizer.fit_on_texts(vocab)"
]
},
{
"cell_type": "code",
"execution_count": 138,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'travelled': 1,\n",
" 'milk': 2,\n",
" 'there': 3,\n",
" 'dropped': 4,\n",
" 'football': 5,\n",
" 'mary': 6,\n",
" 'got': 7,\n",
" 'down': 8,\n",
" 'yes': 9,\n",
" 'garden': 10,\n",
" 'back': 11,\n",
" '?': 12,\n",
" 'john': 13,\n",
" 'discarded': 14,\n",
" 'the': 15,\n",
" '.': 16,\n",
" 'bathroom': 17,\n",
" 'hallway': 18,\n",
" 'went': 19,\n",
" 'daniel': 20,\n",
" 'took': 21,\n",
" 'no': 22,\n",
" 'sandra': 23,\n",
" 'picked': 24,\n",
" 'apple': 25,\n",
" 'to': 26,\n",
" 'office': 27,\n",
" 'moved': 28,\n",
" 'is': 29,\n",
" 'grabbed': 30,\n",
" 'put': 31,\n",
" 'bedroom': 32,\n",
" 'kitchen': 33,\n",
" 'up': 34,\n",
" 'left': 35,\n",
" 'in': 36,\n",
" 'journeyed': 37}"
]
},
"execution_count": 138,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.word_index"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora tenemos un índice para cada palabra dentro de nuestro vocabulario, y las llevo a minúsculas por nosotros, tenlo en cuenta porque cuando armemos nuestras historias tendremos que hacerlo en minúsculas"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora haré una función para tokenizar nuestras preguntas y respuestas de forma \"manual\", pero veremos como se haría de forma automática"
]
},
{
"cell_type": "code",
"execution_count": 139,
"metadata": {},
"outputs": [],
"source": [
"train_story = []\n",
"train_question = []\n",
"train_answer = []\n",
"for story, question, answer in train_data:\n",
" train_story.append(story)\n",
" train_question.append(question)\n",
" train_answer.append(answer)"
]
},
{
"cell_type": "code",
"execution_count": 140,
"metadata": {},
"outputs": [],
"source": [
"train_story_seq = tokenizer.texts_to_sequences(train_story)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora voy a hacer una función que nos ayude a vectorizar estas historias.\n",
"\n",
"Para las redes neuronales necesitamos que todo este en el mismo largo, asique usare la funcion pad_sequences"
]
},
{
"cell_type": "code",
"execution_count": 141,
"metadata": {},
"outputs": [],
"source": [
"def vectorize_stories(data, word_index=tokenizer.word_index, max_story_len=max_story_len, max_query_len=max_query_len):\n",
" #Stories: X\n",
" X = []\n",
" #Question: X\n",
" Xq = []\n",
" #Answer: X\n",
" Y = []\n",
" \n",
" for story, query, answer in data:\n",
" x = [word_index[word.lower()] for word in story] #Convertimos cada palabra a un numero, para cada historia\n",
" xq = [word_index[word.lower()] for word in query] #Hacemos lo mismo con las preguntas\n",
" y = np.zeros(len(word_index)+1) #Hacemos una matriz de ceros para las respuestas, recordemos que tenemos que normalizar el largo de todas nuestros datos\n",
" y[word_index[answer]] = 1 #Hacemos el one hot encoding, donde el valor correcto sera 1 y los demas seran 0\n",
" #Agregar cada historia por separado a sus listas\n",
" X.append(x)\n",
" Xq.append(xq)\n",
" Y.append(y)\n",
" return (pad_sequences(X, maxlen=max_story_len), pad_sequences(Xq, maxlen=max_query_len), np.array(Y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora vamos a asegurarnos que no cometimos errores"
]
},
{
"cell_type": "code",
"execution_count": 142,
"metadata": {},
"outputs": [],
"source": [
"inputs_train, queries_train, answers_train = vectorize_stories(train_data)"
]
},
{
"cell_type": "code",
"execution_count": 143,
"metadata": {},
"outputs": [],
"source": [
"inputs_test, queries_test, answers_test = vectorize_stories(test_data)"
]
},
{
"cell_type": "code",
"execution_count": 144,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0, 0, 0, ..., 15, 32, 16],\n",
" [ 0, 0, 0, ..., 15, 18, 16],\n",
" [ 0, 0, 0, ..., 15, 17, 16],\n",
" ...,\n",
" [ 0, 0, 0, ..., 15, 32, 16],\n",
" [ 0, 0, 0, ..., 2, 3, 16],\n",
" [ 0, 0, 0, ..., 25, 3, 16]], dtype=int32)"
]
},
"execution_count": 144,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"inputs_train"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos se han procesado de manera correcta"
]
},
{
"cell_type": "code",
"execution_count": 145,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" ...,\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.]])"
]
},
"execution_count": 145,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"answers_test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aquí solo hay ceros, por la razón de que el indice para la palabra si y no son los siguientes"
]
},
{
"cell_type": "code",
"execution_count": 146,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"9\n",
"22\n"
]
}
],
"source": [
"print(tokenizer.word_index['yes'])\n",
"print(tokenizer.word_index['no'])"
]
},
{
"cell_type": "code",
"execution_count": 147,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 497., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 503., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0.])"
]
},
"execution_count": 147,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum(answers_test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos las únicas ubicaciones que tienen valores son el índice 9 y el 28, por esa razón son caso solo 0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora vamos a implementar la red del paper con keras\n",
"\n",
"<img src=\"https://i.ibb.co/hdKJ5YH/Captura-de-pantalla-de-2019-06-10-14-24-40.png\" /> \n",
"\n",
"Como vemos son dos embedings, y las conectaremos a traves de LSTM. Vamos a ello"
]
},
{
"cell_type": "code",
"execution_count": 148,
"metadata": {},
"outputs": [],
"source": [
"from keras.models import Sequential, Model\n",
"from keras.layers import Embedding, Input, Activation, Dense, Permute, Dropout, add, dot, concatenate, LSTM"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Todas estas capas nos ayudaran a tomar dos inputs y juntar en una salida, lo que haremos es tomar la pregunta y las historias y enlazarlas para que nos de una respuesta,\n",
"\n",
"Lo que haremos es crear **placeholders** con la función input de keras, que nos permite instanciar **tensores**"
]
},
{
"cell_type": "code",
"execution_count": 149,
"metadata": {},
"outputs": [],
"source": [
"input_sequence = Input(shape=(max_story_len, ))\n",
"question = Input(shape=(max_query_len, ))\n",
"vocab_size = len(vocab) + 1 # Ahora definiremos el tamaño del vocabulario +1 que sera el comodin para que keras pueda trabajar con las secuencias"
]
},
{
"cell_type": "code",
"execution_count": 150,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"WARNING:tensorflow:From /home/harpie/.conda/envs/nlp/lib/python3.7/site-packages/keras/backend/tensorflow_backend.py:3445: calling dropout (from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and will be removed in a future version.\n",
"Instructions for updating:\n",
"Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.\n"
]
}
],
"source": [
"# Codificador M\n",
"input_encoder_m = Sequential()\n",
"input_encoder_m.add(Embedding(input_dim=vocab_size, output_dim=64 )) # podemos poner la variable vocab_len pero quiero tener los nombres consistentes al paper\n",
"# Siguiendo el paper vamos a tener las dimensiones de salida con el tamaño de 64\n",
"input_encoder_m.add(Dropout(.3)) # esta capa nos permitira evadir el overfitting tirando un porcentage de los pesos\n",
"#La salida de este \"encoder\" va a ser (samples, story_max_len, embedding_dim)"
]
},
{
"cell_type": "code",
"execution_count": 151,
"metadata": {},
"outputs": [],
"source": [
"# Codificador C\n",
"input_encoder_c = Sequential()\n",
"input_encoder_c.add(Embedding(input_dim=vocab_size, output_dim=max_query_len )) \n",
"input_encoder_c.add(Dropout(.3)) \n",
"\n",
"#La salida de este \"encoder\" va a ser (samples, story_max_len, max_question_len)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora tenemos el encoder M y el C, y vamos a hacer el Encoder para las preguntas que es similar salvo algunas diferencias, la salida al ser del tamaño de la pregunta, nos permitirá pasar esta información al siguiente encoder"
]
},
{
"cell_type": "code",
"execution_count": 152,
"metadata": {},
"outputs": [],
"source": [
"# Codificador de Querys\n",
"question_encoder = Sequential()\n",
"question_encoder.add(Embedding(input_dim=vocab_size, output_dim=64, input_length=max_query_len))\n",
"question_encoder.add(Dropout(.3))\n",
"\n",
"#La salida de esta capa va a ser la siguiente (samples, query_maxlen, embedding_dim)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora vamos a usar nuestros inputs, para pasar los datos a nuestros encoger y asi codificar las preguntas"
]
},
{
"cell_type": "code",
"execution_count": 153,
"metadata": {},
"outputs": [],
"source": [
"#Encoded <---- ENCODER(INPUT)\n",
"input_encoded_m = input_encoder_m(input_sequence)\n",
"input_encoded_c = input_encoder_c(input_sequence)\n",
"question_encoded = question_encoder(question)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Estamos casi terminando, ahora solo necesito sacar el \"producto punto\" de la pregunta y la respuesta codificada tal como se explica en el paper, y aplicaremos la funcion softmax"
]
},
{
"cell_type": "code",
"execution_count": 154,
"metadata": {},
"outputs": [],
"source": [
"match = dot([input_encoded_m, question_encoded], axes=(2,2))\n",
"match = Activation('softmax')(match)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora como dice en el paper, vamos a agregar este \"match\" al encoder c, con la función add y vamos a permutar las dimensiones respecto al patrón (2, 1), esto va a convertir la secuencia en una matriz que tenga unas dimensiones respectivas a los largos máximos de la historia y la pregunta"
]
},
{
"cell_type": "code",
"execution_count": 155,
"metadata": {},
"outputs": [],
"source": [
"response = add([match, input_encoded_c])\n",
"response = Permute((2, 1))(response)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Una vez tengamos nuestra respuesta, debemos concatenar, el vector match y nuestra respuesta debemos unir todo eso a la pregunta codificada"
]
},
{
"cell_type": "code",
"execution_count": 156,
"metadata": {},
"outputs": [],
"source": [
"answer = concatenate([response, question_encoded])"
]
},
{
"cell_type": "code",
"execution_count": 157,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<tf.Tensor 'concatenate_1/concat:0' shape=(?, 6, 220) dtype=float32>"
]
},
"execution_count": 157,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"answer"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Si todo salio bien, tendrías que ver un objeto de tipo tensor con la misma forma de la matriz que podemos ver aquí (?, 6, 220) si tu resultado se ve como este, felicitaciones, hiciste todo correctamente, si no tiene esa forma, te equivocaste en algo, te recomiendo bajar este notebook y checkear si tienes un error en tu código, ahora tomaremos este tensor y la reduciremos con una capa de LSTM"
]
},
{
"cell_type": "code",
"execution_count": 158,
"metadata": {},
"outputs": [],
"source": [
"answer = LSTM(32)(answer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora vamos a hacer una serie de normalizaciones y aplicar dropout y agregar la capa de salida"
]
},
{
"cell_type": "code",
"execution_count": 159,
"metadata": {},
"outputs": [],
"source": [
"answer = Dropout(0.5)(answer)\n",
"answer = Dense(vocab_size)(answer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Y por ultimo la ultima activación que será una activacion softmax"
]
},
{
"cell_type": "code",
"execution_count": 160,
"metadata": {},
"outputs": [],
"source": [
"answer = Activation('softmax')(answer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Y por ultimo armaremos nuestro modelo con los parámetros"
]
},
{
"cell_type": "code",
"execution_count": 161,
"metadata": {},
"outputs": [],
"source": [
"model = Model([input_sequence, question], answer) #Usamos nuestros placeholders y nuestro modelo"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Y aquí en la variable answer, tenemos todos los codificadores combinados, y así es como se hace un modelo con varios encoders, por ultimo compilare este modelo"
]
},
{
"cell_type": "code",
"execution_count": 162,
"metadata": {},
"outputs": [],
"source": [
"model.compile(optimizer='RMSprop', loss='categorical_crossentropy', metric=['accuracy'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Este como es un problema de clasificación por categorías, usare como función de perdida la función de categorical crossentropy, ahora vamos a ver el modelo, que es demasiado complejo"
]
},
{
"cell_type": "code",
"execution_count": 163,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"__________________________________________________________________________________________________\n",
"Layer (type) Output Shape Param # Connected to \n",
"==================================================================================================\n",
"input_1 (InputLayer) (None, 156) 0 \n",
"__________________________________________________________________________________________________\n",
"input_2 (InputLayer) (None, 6) 0 \n",
"__________________________________________________________________________________________________\n",
"sequential_3 (Sequential) multiple 2432 input_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"sequential_5 (Sequential) (None, 6, 64) 2432 input_2[0][0] \n",
"__________________________________________________________________________________________________\n",
"dot_1 (Dot) (None, 156, 6) 0 sequential_3[1][0] \n",
" sequential_5[1][0] \n",
"__________________________________________________________________________________________________\n",
"activation_1 (Activation) (None, 156, 6) 0 dot_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"sequential_4 (Sequential) multiple 228 input_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"add_1 (Add) (None, 156, 6) 0 activation_1[0][0] \n",
" sequential_4[1][0] \n",
"__________________________________________________________________________________________________\n",
"permute_1 (Permute) (None, 6, 156) 0 add_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"concatenate_1 (Concatenate) (None, 6, 220) 0 permute_1[0][0] \n",
" sequential_5[1][0] \n",
"__________________________________________________________________________________________________\n",
"lstm_3 (LSTM) (None, 32) 32384 concatenate_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"dropout_4 (Dropout) (None, 32) 0 lstm_3[0][0] \n",
"__________________________________________________________________________________________________\n",
"dense_6 (Dense) (None, 38) 1254 dropout_4[0][0] \n",
"__________________________________________________________________________________________________\n",
"activation_2 (Activation) (None, 38) 0 dense_6[0][0] \n",
"==================================================================================================\n",
"Total params: 38,730\n",
"Trainable params: 38,730\n",
"Non-trainable params: 0\n",
"__________________________________________________________________________________________________\n"
]
}
],
"source": [
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Advertencia**\n",
"Ahora lo que haremos es entrenarlo para ello no te recomiendo usar tu pc, usa collaboratory y entrena tu modelo con una gpu\n",
"\n",
"<a href=\"https://colab.research.google.com\"> Collaboratory </a>"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"history = model.fit([inputs_train, queries_train], answers_train, batch_size=32, epochs=600)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Esto demorara muchas horas, pero para ti no será nada.\n",
"\n",
"\n",
"\n",
"\n",
"Por ultimo graficaremos el rendimiento de nuestra red neuronal"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"#pip install matplotlib\n",
"#Codigo para graficar\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline\n",
"plt.plot(history.history['acc']) #Tomamos el historial de precision\n",
"plt.plot(history.history['loss']) # Tomamos el historial de perdida\n",
"plt.title('Precision del modelo') # Le ponemos un titulo al grafico\n",
"plt.ylabel('Precision') #Le ponemos un titulo al eje y\n",
"plt.xlabel('Epoca') # Le ponemos un titulo al eje x\n",
"plt.legend(['Entrenamiento', 'Perdida'], loc='upper left') #Le agregamos una leyenda \n",
"plt.show() #Lo mostramos"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Este es mi resultado\n",
"\n",
"<img src=\"https://i.ibb.co/6wR1GBD/descarga.png\" />"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 14. Probando y evaluando nuestro modelo\n",
"\n",
"Ahora como hemos hecho anteriormente vamos a hacer una prediccion a nuestro set de prueba, para ver como se comporta este modelo tras 600 épocas"
]
},
{
"cell_type": "code",
"execution_count": 164,
"metadata": {},
"outputs": [],
"source": [
"from keras.models import load_model\n",
"with open('token', 'rb') as f:\n",
" tokenizer = pickle.load(f) #Cargar el tokenizer"
]
},
{
"cell_type": "code",
"execution_count": 165,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"WARNING:tensorflow:From /home/harpie/.conda/envs/nlp/lib/python3.7/site-packages/tensorflow/python/ops/math_grad.py:102: div (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n",
"Instructions for updating:\n",
"Deprecated in favor of operator or tf.math.divide.\n"
]
}
],
"source": [
"model = load_model(\"DoubleEncoderQAModel.h5\")"
]
},
{
"cell_type": "code",
"execution_count": 166,
"metadata": {},
"outputs": [],
"source": [
"pred_result = model.predict([inputs_test, queries_test])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ahora haré una función para ver si el resultado de la primera predicción es correcto, pero lo haré como se solía hacer antes, con argmax"
]
},
{
"cell_type": "code",
"execution_count": 167,
"metadata": {},
"outputs": [],
"source": [
"val_max = np.argmax(pred_result[0])\n",
"for key, val in tokenizer.word_index.items():\n",
" if val == val_max:\n",
" k = key"
]
},
{
"cell_type": "code",
"execution_count": 168,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'no'"
]
},
"execution_count": 168,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"k"
]
},
{
"cell_type": "code",
"execution_count": 169,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(['Mary',\n",
" 'got',\n",
" 'the',\n",
" 'milk',\n",
" 'there',\n",
" '.',\n",
" 'John',\n",
" 'moved',\n",
" 'to',\n",
" 'the',\n",
" 'bedroom',\n",
" '.',\n",
" 'Mary',\n",
" 'discarded',\n",
" 'the',\n",
" 'milk',\n",
" '.',\n",
" 'John',\n",
" 'went',\n",
" 'to',\n",
" 'the',\n",
" 'garden',\n",
" '.'],\n",
" ['Is', 'John', 'in', 'the', 'kitchen', '?'],\n",
" 'no')"
]
},
"execution_count": 169,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"test_data[1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como vemos funciona bien, ahora hagamos una predicción propia, recordemos que solo podemos usar palabras en el vocabulario"
]
},
{
"cell_type": "code",
"execution_count": 170,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'.',\n",
" '?',\n",
" 'Daniel',\n",
" 'Is',\n",
" 'John',\n",
" 'Mary',\n",
" 'Sandra',\n",
" 'apple',\n",
" 'back',\n",
" 'bathroom',\n",
" 'bedroom',\n",
" 'discarded',\n",
" 'down',\n",
" 'dropped',\n",
" 'football',\n",
" 'garden',\n",
" 'got',\n",
" 'grabbed',\n",
" 'hallway',\n",
" 'in',\n",
" 'journeyed',\n",
" 'kitchen',\n",
" 'left',\n",
" 'milk',\n",
" 'moved',\n",
" 'no',\n",
" 'office',\n",
" 'picked',\n",
" 'put',\n",
" 'the',\n",
" 'there',\n",
" 'to',\n",
" 'took',\n",
" 'travelled',\n",
" 'up',\n",
" 'went',\n",
" 'yes'}"
]
},
"execution_count": 170,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vocab"
]
},
{
"cell_type": "code",
"execution_count": 171,
"metadata": {},
"outputs": [],
"source": [
"my_story = \"John moved to the kitchen . Sandra dropped the milk in the garden\"\n",
"my_question = \"Is the milk in the garden ?\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Primero escribiré mi historia y mi pregunta luego las cortare en cada espacio para dejarlas en el mismo formato del dataset"
]
},
{
"cell_type": "code",
"execution_count": 172,
"metadata": {},
"outputs": [],
"source": [
"my_data = [(my_story.split(), my_question.split(), 'yes')]"
]
},
{
"cell_type": "code",
"execution_count": 173,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[(['John',\n",
" 'moved',\n",
" 'to',\n",
" 'the',\n",
" 'kitchen',\n",
" '.',\n",
" 'Sandra',\n",
" 'dropped',\n",
" 'the',\n",
" 'milk',\n",
" 'in',\n",
" 'the',\n",
" 'garden'],\n",
" ['Is', 'the', 'milk', 'in', 'the', 'garden', '?'],\n",
" 'yes')]"
]
},
"execution_count": 173,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"my_data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Una vez tenga mis datos en exactamente el mismo formato que el dataset, vamos a vectorizar mi nueva historia, para eso agregamos la respuesta, para respetar el formato de la función que creamos anteriormente"
]
},
{
"cell_type": "code",
"execution_count": 174,
"metadata": {},
"outputs": [],
"source": [
"my_story, my_query, my_answer = vectorize_stories(my_data)"
]
},
{
"cell_type": "code",
"execution_count": 175,
"metadata": {},
"outputs": [],
"source": [
"#Historia: John moved to the kitchen . Sandra dropped the milk in the garden (John fue a la cocina y sandra solto la leche en el patio)\n",
"#Pregunta: Is the milk in the garden ? (Esta la leche en el patio)\n",
"def pred_trad(data):\n",
" pred_result = model.predict(([data[0], data[1]]))\n",
" val_max = np.argmax(pred_result)\n",
" for key, val in tokenizer.word_index.items():\n",
" if val == val_max:\n",
" k = key\n",
" return k"
]
},
{
"cell_type": "code",
"execution_count": 176,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'no'"
]
},
"execution_count": 176,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pred_trad([my_story, my_query])"
]
},
{
"cell_type": "code",
"execution_count": 177,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'no'"
]
},
"execution_count": 177,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Pregunta: Is John in the kitchen ? (Esta la John en la cocina)\n",
"my_story = \"John moved to the kitchen . Sandra dropped the milk in the garden\"\n",
"my_question = \"Is John in the kitchen ?\"\n",
"\n",
"my_data = [(my_story.split(), my_question.split(), 'yes')]\n",
"my_data\n",
"my_story, my_query, my_answer = vectorize_stories(my_data)\n",
"pred_trad([my_story, my_query])"
]
},
{
"cell_type": "code",
"execution_count": 178,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'no'"
]
},
"execution_count": 178,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Pregunta: Is Sandra in the kitchen ? (Esta la Sandra en la cocina)\n",
"my_story = \"John moved to the kitchen . Sandra dropped the milk in the garden\"\n",
"my_question = \"Is Sandra in the kitchen ?\"\n",
"\n",
"my_data = [(my_story.split(), my_question.split(), 'yes')]\n",
"my_data\n",
"my_story, my_query, my_answer = vectorize_stories(my_data)\n",
"pred_trad([my_story, my_query])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Gracias\n",
"\n",
"Después de este documento, me tomare un breve descanso pero este no es el final, aún nos quedan los siguientes tópicos que cubrir que son más avanzados\n",
"\n",
"- Deep Learning a fondo\n",
"- Seq2Seq Models\n",
"- Attention Models\n",
"- Memory Networks (parte 2)\n",
"- Consejos para implementar estos modelos sin una guía\n",
"\n",
"Desde ya muchas gracias, y estén atentos, porque en cualquier momento esta serie sigue, si te gusto compártelo, y sin más que decir adiós."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment