<jupyter_start><jupyter_text>Copyright 2017 Google LLC.<jupyter_code># Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.<jupyter_output><empty_output><jupyter_text>Introducción a los datos dispersos y las incorporaciones**Objetivos de aprendizaje:*** convertir datos de strings de reseñas de películas en un vector de atributos dispersos* implementar un modelo lineal de análisis de opiniones a través de un vector de atributos dispersos* implementar un modelo de RNP de análisis de opiniones a través de una incorporación que proyecte datos en dos dimensiones* visualizar la incorporación para observar qué aprendió el modelo acerca de las relaciones entre las palabrasEn este ejercicio, exploraremos datos dispersos y trabajaremos con incorporaciones mediante el uso de datos de texto de reseñas de películas (del [conjunto de datos de IMDB de ACL 2011](http://ai.stanford.edu/~amaas/data/sentiment/). Estos datos ya se procesaron en formato `tf.Example`.     PreparaciónImportemos nuestras dependencias y descarguemos los datos de entrenamiento y de prueba. [`tf.keras`](https://www.tensorflow.org/api_docs/python/tf/keras) incluye una herramienta de almacenamiento en caché y descarga de archivos que podemos usar para recuperar los conjuntos de datos.<jupyter_code>from __future__ import print_function

import collections
import io
import math

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
from IPython import display
from sklearn import metrics

tf.logging.set_verbosity(tf.logging.ERROR)
train_url = 'https://download.mlcc.google.com/mledu-datasets/sparse-data-embedding/train.tfrecord'
train_path = tf.keras.utils.get_file(train_url.split('/')[-1], train_url)
test_url = 'https://download.mlcc.google.com/mledu-datasets/sparse-data-embedding/test.tfrecord'
test_path = tf.keras.utils.get_file(test_url.split('/')[-1], test_url)<jupyter_output><empty_output><jupyter_text>Desarrollo de un modelo de análisis de opiniones  Entrenemos un modelo de análisis de opiniones con estos datos, que prediga si una reseña es en general *favorable* (etiqueta de 1) o *desfavorable* (etiqueta de 0).Para eso, convertiremos nuestros `terms` con valores de strings en vectores de atributos a través de un *vocabulario*, una lista de cada término que esperamos ver en nuestros datos. Para los fines de este ejercicio, creamos un pequeño vocabulario que se enfoca en un conjunto de términos limitado. La mayoría de estos se consideraron fuertes indicadores de *favorable* o *unfavorable*, pero algunos se agregaron simplemente porque son interesantes.Cada término del vocabulario está asignado a una coordenada de nuestro vector de atributos. Para convertir los `terms` con valores de strings para un ejemplo en este formato de vector, los codificamos de manera tal que cada coordenada obtenga un valor de 0 si el término del vocabulario no aparece en la string de ejemplo y un valor de 1 si aparece. Los términos de un ejemplo que no aparecen en el vocabulario se descartan.  **NOTA:** *Desde luego, podríamos usar un vocabulario más grande. También existen herramientas especiales para crearlos. Además, en lugar de simplemente descartar los términos que no están en el vocabulario, podemos incorporar una pequeña cantidad de agrupamientos OOV (fuera de vocabulario), con los cuales se puede generar un hash para los términos que no están en el vocabulario. A su vez, podemos usar un enfoque de __generación de hashes de atributos__ que genere hashes de cada término, en lugar de crear un vocabulario explícito. Esto funciona bien en la práctica, pero dificulta la interpretabilidad, que es útil para este ejercicio. Consulta el módulo tf.feature_column para obtener herramientas para abordar esto.*   Desarrollo de la canalización de entrada  Primero, configuremos la canalización de entrada para importar nuestros datos a un modelo de TensorFlow. Podemos usar la siguiente función para analizar los datos de entrenamiento y de prueba (que se encuentran en formato [TFRecord](https://www.tensorflow.org/guide/datasetsconsuming_tfrecord_data)) y devolver un diccionario de los atributos y etiquetas correspondientes.<jupyter_code>def _parse_function(record):
  """Extracts features and labels.
  
  Args:
    record: File path to a TFRecord file    
  Returns:
    A `tuple` `(labels, features)`:
      features: A dict of tensors representing the features
      labels: A tensor with the corresponding labels.
  """
  features = {
    "terms": tf.VarLenFeature(dtype=tf.string), # terms are strings of varying lengths
    "labels": tf.FixedLenFeature(shape=[1], dtype=tf.float32) # labels are 0 or 1
  }
  
  parsed_features = tf.parse_single_example(record, features)
  
  terms = parsed_features['terms'].values
  labels = parsed_features['labels']

  return  {'terms':terms}, labels<jupyter_output><empty_output><jupyter_text>Para confirmar que nuestra función se desempeña de acuerdo con lo esperado, construyamos un `TFRecordDataset` para los datos de entrenamiento y asignemos los datos a atributos y etiquetas a través del atributo que se incluyó más arriba.<jupyter_code># Create the Dataset object.
ds = tf.data.TFRecordDataset(train_path)
# Map features and labels with the parse function.
ds = ds.map(_parse_function)

ds<jupyter_output><empty_output><jupyter_text>Ejecuta la siguiente celda para recuperar el primer ejemplo del conjunto de datos de entrenamiento.<jupyter_code>n = ds.make_one_shot_iterator().get_next()
sess = tf.Session()
sess.run(n)<jupyter_output><empty_output><jupyter_text>Ahora, desarrollemos una función de entrada formal que podamos pasar al método `train()` de un objeto de Estimator de TensorFlow.<jupyter_code># Create an input_fn that parses the tf.Examples from the given files,
# and split them into features and targets.
def _input_fn(input_filenames, num_epochs=None, shuffle=True):
  
  # Same code as above; create a dataset and map features and labels.
  ds = tf.data.TFRecordDataset(input_filenames)
  ds = ds.map(_parse_function)

  if shuffle:
    ds = ds.shuffle(10000)

  # Our feature data is variable-length, so we pad and batch
  # each field of the dataset structure to whatever size is necessary.     
  ds = ds.padded_batch(25, ds.output_shapes)
  
  ds = ds.repeat(num_epochs)

  
  # Return the next batch of data.
  features, labels = ds.make_one_shot_iterator().get_next()
  return features, labels<jupyter_output><empty_output><jupyter_text>Tarea 1: Usar un modelo lineal con entradas dispersas y vocabulario explícitoPara nuestro primer modelo, desarrollaremos un modelo de [`LinearClassifier`](https://www.tensorflow.org/api_docs/python/tf/estimator/LinearClassifier) con 50 términos informativos; siempre debemos comenzar por lo más simple.El siguiente código genera la columna de atributos para nuestros términos. El atributo [`categorical_column_with_vocabulary_list`](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_list) crea una columna de funciones con la asignación de string a vector de funciones.<jupyter_code># 50 informative terms that compose our model vocabulary. 
informative_terms = ("bad", "great", "best", "worst", "fun", "beautiful",
                     "excellent", "poor", "boring", "awful", "terrible",
                     "definitely", "perfect", "liked", "worse", "waste",
                     "entertaining", "loved", "unfortunately", "amazing",
                     "enjoyed", "favorite", "horrible", "brilliant", "highly",
                     "simple", "annoying", "today", "hilarious", "enjoyable",
                     "dull", "fantastic", "poorly", "fails", "disappointing",
                     "disappointment", "not", "him", "her", "good", "time",
                     "?", ".", "!", "movie", "film", "action", "comedy",
                     "drama", "family")

terms_feature_column = tf.feature_column.categorical_column_with_vocabulary_list(key="terms", vocabulary_list=informative_terms)<jupyter_output><empty_output><jupyter_text>A continuación, generaremos el `LinearClassifier`, lo entrenaremos con el conjunto de entrenamiento y lo evaluaremos con el conjunto de evaluación. Después de leer todo el código, ejecútalo y observa el desempeño.<jupyter_code>my_optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)

feature_columns = [ terms_feature_column ]


classifier = tf.estimator.LinearClassifier(
  feature_columns=feature_columns,
  optimizer=my_optimizer,
)

classifier.train(
  input_fn=lambda: _input_fn([train_path]),
  steps=1000)

evaluation_metrics = classifier.evaluate(
  input_fn=lambda: _input_fn([train_path]),
  steps=1000)
print("Training set metrics:")
for m in evaluation_metrics:
  print(m, evaluation_metrics[m])
print("---")

evaluation_metrics = classifier.evaluate(
  input_fn=lambda: _input_fn([test_path]),
  steps=1000)

print("Test set metrics:")
for m in evaluation_metrics:
  print(m, evaluation_metrics[m])
print("---")<jupyter_output><empty_output><jupyter_text>Tarea 2: Usar un modelo de redes neuronales profundas (RNP)El modelo anterior es un modelo lineal que funciona bastante bien. Pero, ¿podemos obtener un mejor desempeño con un modelo de RNP?Cambiemos a un [`DNNClassifier`](https://www.tensorflow.org/api_docs/python/tf/estimator/DNNClassifier) en lugar del `LinearClassifier`. Ejecuta la siguiente celda y observa qué desempeño obtienes.<jupyter_code>##################### Here's what we changed ##################################
classifier = tf.estimator.DNNClassifier(                                      #
  feature_columns=[tf.feature_column.indicator_column(terms_feature_column)], #
  hidden_units=[20,20],                                                       #
  optimizer=my_optimizer,                                                     #
)                                                                             #
###############################################################################

try:
  classifier.train(
    input_fn=lambda: _input_fn([train_path]),
    steps=1000)

  evaluation_metrics = classifier.evaluate(
    input_fn=lambda: _input_fn([train_path]),
    steps=1)
  print("Training set metrics:")
  for m in evaluation_metrics:
    print(m, evaluation_metrics[m])
  print("---")

  evaluation_metrics = classifier.evaluate(
    input_fn=lambda: _input_fn([test_path]),
    steps=1)

  print("Test set metrics:")
  for m in evaluation_metrics:
    print(m, evaluation_metrics[m])
  print("---")
except ValueError as err:
  print(err)<jupyter_output><empty_output><jupyter_text>Tarea 3: Usar una incorporación con un modelo de RNPEn esta tarea, implementaremos nuestro modelo de RNP mediante el uso de una columna de incorporaciones. Una columna de incorporaciones toma datos dispersos como entrada y devuelve un vector denso de dimensiones más bajas como resultado.  **NOTA:** *Una embedding_column suele ser la opción más eficaz con relación al cómputo que se puede usar para entrenar un modelo con datos dispersos. En una [sección opcional](scrollTo=XDMlGgRfKSVz) al final de este ejercicio, analizaremos en más detalle las diferencias de implementación entre el uso de una `embedding_column` y una `indicator_column`, así como las ventajas y desventajas de seleccionar una o la otra.*  En el siguiente código, realiza lo siguiente:* Define las columnas de atributos para el modelo a través de una `embedding_column` que proyecte los datos en 2 dimensiones (para obtener más detalles sobre la firma de funciones para `embedding_column`, consulta la [documentación de TF](https://www.tensorflow.org/api_docs/python/tf/feature_column/embedding_column)).* Define un `DNNClassifier` con las siguientes especificaciones:  * Dos capas ocultas de 20 unidades cada una  * Optimización de AdaGrad con una tasa de aprendizaje de 0.1  * Una `gradient_clip_norm` de 5.0  **NOTA:** *En la práctica, es posible que proyectemos en más que 2 dimensiones, como 50 o 100. Pero, por ahora, 2 dimensiones son fáciles de visualizar.*   Sugerencia<jupyter_code># Here's a example code snippet you might use to define the feature columns:

terms_embedding_column = tf.feature_column.embedding_column(terms_feature_column, dimension=2)
feature_columns = [ terms_embedding_column ]<jupyter_output><empty_output><jupyter_text>Completa el código a continuación<jupyter_code>########################## YOUR CODE HERE ######################################
terms_embedding_column = # Define the embedding column
feature_columns = # Define the feature columns

classifier = # Define the DNNClassifier
################################################################################

classifier.train(
  input_fn=lambda: _input_fn([train_path]),
  steps=1000)

evaluation_metrics = classifier.evaluate(
  input_fn=lambda: _input_fn([train_path]),
  steps=1000)
print("Training set metrics:")
for m in evaluation_metrics:
  print(m, evaluation_metrics[m])
print("---")

evaluation_metrics = classifier.evaluate(
  input_fn=lambda: _input_fn([test_path]),
  steps=1000)

print("Test set metrics:")
for m in evaluation_metrics:
  print(m, evaluation_metrics[m])
print("---")<jupyter_output><empty_output><jupyter_text>SoluciónHaz clic más abajo para conocer la solución.<jupyter_code>########################## SOLUTION CODE ########################################
terms_embedding_column = tf.feature_column.embedding_column(terms_feature_column, dimension=2)
feature_columns = [ terms_embedding_column ]

my_optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)

classifier = tf.estimator.DNNClassifier(
  feature_columns=feature_columns,
  hidden_units=[20,20],
  optimizer=my_optimizer
)
#################################################################################

classifier.train(
  input_fn=lambda: _input_fn([train_path]),
  steps=1000)

evaluation_metrics = classifier.evaluate(
  input_fn=lambda: _input_fn([train_path]),
  steps=1000)
print("Training set metrics:")
for m in evaluation_metrics:
  print(m, evaluation_metrics[m])
print("---")

evaluation_metrics = classifier.evaluate(
  input_fn=lambda: _input_fn([test_path]),
  steps=1000)

print("Test set metrics:")
for m in evaluation_metrics:
  print(m, evaluation_metrics[m])
print("---")<jupyter_output><empty_output><jupyter_text>Tarea 4: Convencerte de que realmente hay una incorporación allíEl modelo anterior utilizó una `embedding_column` y pareció tener un buen desempeño, pero esto no nos indica mucho qué ocurre internamente. ¿Cómo podemos comprobar que el modelo realmente está usando una incorporación dentro?Para comenzar, observemos los tensores del modelo:<jupyter_code>classifier.get_variable_names()<jupyter_output><empty_output><jupyter_text>Bien, podemos observar que hay una capa de incorporaciones allí: `'dnn/input_from_feature_columns/input_layer/terms_embedding/...'`. (De hecho, lo interesante aquí es que esta capa se puede entrenar junto con el resto del modelo, al igual que cualquier capa oculta).¿La capa de incorporaciones tiene la forma correcta? Ejecuta el siguiente código para descubrirlo.  **NOTA:** *Recuerda que, en nuestro caso, la incorporación es una matriz que nos permite proyectar un vector de 50 dimensiones en 2 dimensiones.*<jupyter_code>classifier.get_variable_value('dnn/input_from_feature_columns/input_layer/terms_embedding/embedding_weights').shape<jupyter_output><empty_output><jupyter_text>Dedica un tiempo a comprobar manualmente las diferentes capas y formas a fin de asegurarte de que todo esté conectado según lo esperado.   Tarea 5: Examinar la incorporaciónAhora observemos el espacio de incorporación real y veamos dónde acaban los términos dentro de este. Realiza lo siguiente:1. Ejecuta el código que aparece a continuación para ver la incorporación que entrenamos en la **Tarea 3**. ¿Los términos están donde esperabas?2. Ejecutar el código de la **Tarea 3** otra vez para volver a entrenar el modelo y, luego, vuelve a ejecutar la visualización de la incorporación que aparece más abajo. ¿Qué permanece igual? ¿Qué cambia?3. Finalmente, vuelve a entrenar el modelo con solo 10 pasos (lo cual producirá un modelo terrible). Vuelve a ejecutar la visualización de la incorporación que aparece más abajo. ¿Qué ves ahora y por qué?<jupyter_code>import numpy as np
import matplotlib.pyplot as plt

embedding_matrix = classifier.get_variable_value('dnn/input_from_feature_columns/input_layer/terms_embedding/embedding_weights')

for term_index in range(len(informative_terms)):
  # Create a one-hot encoding for our term.  It has 0s everywhere, except for
  # a single 1 in the coordinate that corresponds to that term.
  term_vector = np.zeros(len(informative_terms))
  term_vector[term_index] = 1
  # We'll now project that one-hot vector into the embedding space.
  embedding_xy = np.matmul(term_vector, embedding_matrix)
  plt.text(embedding_xy[0],
           embedding_xy[1],
           informative_terms[term_index])

# Do a little setup to make sure the plot displays nicely.
plt.rcParams["figure.figsize"] = (15, 15)
plt.xlim(1.2 * embedding_matrix.min(), 1.2 * embedding_matrix.max())
plt.ylim(1.2 * embedding_matrix.min(), 1.2 * embedding_matrix.max())
plt.show()<jupyter_output><empty_output><jupyter_text>Tarea 6: Intentar mejorar el rendimiento del modeloVe si puedes ajustar el modelo para mejorar el rendimiento. A continuación, se indican algunas acciones que puedes probar:* **Cambiar los hiperparámetros** o **usar un optimizador diferente**, como Adam (es posible que solo ganes uno o dos puntos en el porcentaje de exactitud con estas estrategias).* **Agregar términos adicionales a `informative_terms`.** Hay un archivo de vocabulario completo con los 30,716 términos para este conjunto de datos que puedes usar en https://download.mlcc.google.com/mledu-datasets/sparse-data-embedding/terms.txt. Puedes seleccionar términos adicionales de este archivo de vocabulario o usar el archivo completo a través de la columna de atributos `categorical_column_with_vocabulary_file`.<jupyter_code># Download the vocabulary file.
terms_url = 'https://download.mlcc.google.com/mledu-datasets/sparse-data-embedding/terms.txt'
terms_path = tf.keras.utils.get_file(terms_url.split('/')[-1], terms_url)
# Create a feature column from "terms", using a full vocabulary file.
informative_terms = None
with io.open(terms_path, 'r', encoding='utf8') as f:
  # Convert it to a set first to remove duplicates.
  informative_terms = list(set(f.read().split()))
  
terms_feature_column = tf.feature_column.categorical_column_with_vocabulary_list(key="terms", 
                                                                                 vocabulary_list=informative_terms)

terms_embedding_column = tf.feature_column.embedding_column(terms_feature_column, dimension=2)
feature_columns = [ terms_embedding_column ]

my_optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)

classifier = tf.estimator.DNNClassifier(
  feature_columns=feature_columns,
  hidden_units=[10,10],
  optimizer=my_optimizer
)

classifier.train(
  input_fn=lambda: _input_fn([train_path]),
  steps=1000)

evaluation_metrics = classifier.evaluate(
  input_fn=lambda: _input_fn([train_path]),
  steps=1000)
print("Training set metrics:")
for m in evaluation_metrics:
  print(m, evaluation_metrics[m])
print("---")

evaluation_metrics = classifier.evaluate(
  input_fn=lambda: _input_fn([test_path]),
  steps=1000)

print("Test set metrics:")
for m in evaluation_metrics:
  print(m, evaluation_metrics[m])
print("---")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10196718070383551, 'fraction_numerical': 0.008353103993673389, 'mean_word_length': 5.1108426457263665, 'pattern_counts': {'":': 2, '<': 65, '<?xml version=': 0, '>': 65, 'https://': 11, 'lorem ipsum': 0, 'www.': 7, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '249679', 'license': 'Apache-2.0', 'n_tokens_mistral': 6495, 'n_tokens_neox': 6235, 'n_words': 1980, 'path': 'ml/cc/exercises/es-419/intro_to_sparse_data_and_embeddings.ipynb', 'repo_name': 'ananci/eng-edu', 'size': '20232'}