<jupyter_start><jupyter_text>[View in Colaboratory](https://colab.research.google.com/github/Skyblueballykid/RIIAA-18/blob/master/TEMP_es_final_text_classification_with_tf_hub.ipynb)  Derechos reservados 2018 The TensorFlow Hub Authors.Licencia bajo Apache License, Version 2.0 (la "Licencia");<jupyter_code># Copyright 2018 The TensorFlow Hub Authors. All Rights Reserved.
#
# 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
#
#     http://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>Cómo construir un simple clasificador con TF-Hub      Ejecutar en Google Colab        Ver el código fuente en GitHub TF-Hub es una plataforma para compartir incorporaciones (embeddings) de aprendizaje automático previamente entrenadas y reusables por medio de **módulos**. Este tutorial está organizado en dos partes principales.** *Introducción*: entrenamiento de un clasificador de texto con TF-Hub**Utilizaremos un módulo de incorporaciones de texto previamente entrenadas de TF-Hub para entrenar un clasificador de sentimiento simple con una exactitud razonable, comparada con el modelo de referencia. Después analizaremos las predicciones para asegurarnos de que nuestro modelo sea razonable y proponer mejoras para aumentar la exactitud.** *Avanzado*: análisis de aprendizaje por transferencia**En esta sección, utilizaremos varios módulos de TF-Hub para comparar su efecto en la precisión del estimador y demostrar las ventajas y dificultades del aprendizaje por transferencia.  Requisitos previos opcionales* Conocimiento básico de la [API del estimador prediseñado](https://www.tensorflow.org/get_started/premade_estimators) de Tensorflow.* Conocimiento básico de la librería Pandas.  Preparando el entorno<jupyter_code># Instale la versión más reciente de Tensorflow.
!pip install --quiet "tensorflow>=1.7"
# Instale TF-Hub.
!pip install tensorflow-hub
!pip install seaborn<jupyter_output><empty_output><jupyter_text>Se puede encontrar información más detallada sobre la instalación de Tensorflow en [https://www.tensorflow.org/install/](https://www.tensorflow.org/install/).<jupyter_code>import tensorflow as tf
import tensorflow_hub as hub
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
import re
import seaborn as sns<jupyter_output><empty_output><jupyter_text>Empezando DatosIntentaremos resolver la tarea [Base de datos de reseñas de películas v1.0](http://ai.stanford.edu/~amaas/data/sentiment/) de Mass et al. La base de datos consiste en reseñas de películas de IMDB etiquetadas por polaridad (qué tan positivas o negativas) del 1 al 10. La tarea es etiquetar las reseñas como **negativo** o **positivo**.<jupyter_code># Cargue todos los archivos del directorio en un DataFrame.
def load_directory_data(directory):
  data = {}
  data["sentence"] = []
  data["sentiment"] = []
  for file_path in os.listdir(directory):
    with tf.gfile.GFile(os.path.join(directory, file_path), "r") as f:
      data["sentence"].append(f.read())
      data["sentiment"].append(re.match("\d+_(\d+)\.txt", file_path).group(1))
  return pd.DataFrame.from_dict(data)

# Combina los ejemplos positivos y negativos, agrega una columna de polaridad y 
# cambia el order de forma aleatoria.
def load_dataset(directory):
  pos_df = load_directory_data(os.path.join(directory, "pos"))
  neg_df = load_directory_data(os.path.join(directory, "neg"))
  pos_df["polarity"] = 1
  neg_df["polarity"] = 0
  return pd.concat([pos_df, neg_df]).sample(frac=1).reset_index(drop=True)

# Descargue y procese los archivos de datos.
def download_and_load_datasets(force_download=False):
  dataset = tf.keras.utils.get_file(
      fname="aclImdb.tar.gz", 
      origin="http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz", 
      extract=True)
  
  train_df = load_dataset(os.path.join(os.path.dirname(dataset), 
                                       "aclImdb", "train"))
  test_df = load_dataset(os.path.join(os.path.dirname(dataset), 
                                      "aclImdb", "test"))
  
  return train_df, test_df

# Reduce el nivel de detalle en los mensajes (verbosity).
tf.logging.set_verbosity(tf.logging.ERROR)

train_df, test_df = download_and_load_datasets()
train_df.head()<jupyter_output><empty_output><jupyter_text>Modelo Atributos de entrada (input functions)[La API del estimador](https://www.tensorflow.org/get_started/premade_estimatorsoverview_of_programming_with_estimators) proporciona [atributos de entrada](https://www.tensorflow.org/api_docs/python/tf/estimator/inputs/pandas_input_fn) que provee un capa de abstracción sobre los objetos DataFrame de Pandas.<jupyter_code># Configura la entrada de entrenamiento (training input) en todo el conjunto de
# entrenamiento (training set) sin repeticiones (epochs) de entrenamiento.
train_input_fn = tf.estimator.inputs.pandas_input_fn(
    train_df, train_df["polarity"], num_epochs=None, shuffle=True)

# Configura la predicción en todo el conjunto de entrenamiento.
predict_train_input_fn = tf.estimator.inputs.pandas_input_fn(
    train_df, train_df["polarity"], shuffle=False)
# Configura la predicción en todo el conjunto de prueba (test set).
predict_test_input_fn = tf.estimator.inputs.pandas_input_fn(
    test_df, test_df["polarity"], shuffle=False)<jupyter_output><empty_output><jupyter_text>Columnas de atributosTF-Hub proporciona una [columna de atributos](https://github.com/tensorflow/hub/blob/master/docs/api_docs/python/hub/text_embedding_column.md) que aplica un módulo a un atributo de texto y conecta las salidas del módulo. En este tutorial usaremos el módulo [nnlm-en-dim128](https://tfhub.dev/google/nnlm-en-dim128/1). Para fines de este tutorial, los aspectos más importantes son:* El módulo toma **un lote de oraciones en un tensor 1-D de cadenas de caracteres (string)** como entrada.* El módulo es responsable del **preprocesamiento de oraciones** (por ejemplo, eliminación de signos de puntuación y usar espacios para dividir palabras).* El módulo funciona con cualquier entrada (por ejemplo **nhlm-en-dim128** agrupa palabras no presentes en el vocabulario en ~ 20.000 grupos).<jupyter_code>embedded_text_feature_column = hub.text_embedding_column(
    key="sentence", 
    module_spec="https://tfhub.dev/google/nnlm-en-dim128/1")<jupyter_output><empty_output><jupyter_text>EstimadorPara la clasificación podemos usar un [clasificador neuronal profundo](https://www.tensorflow.org/api_docs/python/tf/estimator/DNNClassifier) (tome en cuenta que al final del tutorial proporcionamos información adicional sobre de varias funciones de etiquetado para los modelos).<jupyter_code>estimator = tf.estimator.DNNClassifier(
    hidden_units=[500, 100],
    feature_columns=[embedded_text_feature_column],
    n_classes=2,
    optimizer=tf.train.AdagradOptimizer(learning_rate=0.003))<jupyter_output><empty_output><jupyter_text>EntrenamientoEntrena al estimador durante una cantidad razonable de pasos.<jupyter_code># Entrenar 1,000 pasos significa procesar 128,000 ejemplos de entrenamiento con
# el tamaño del lote determinado (default). Esto es más o menos equivalente a 5
# repeticiones, ya que el conjunto de entrenamiento solo contiene 25,000
# ejemplos.
estimator.train(input_fn=train_input_fn, steps=1000);<jupyter_output><empty_output><jupyter_text>PredicciónGenera predicciones tanto para el conjunto de entrenamiento como para el conjunto de prueba.<jupyter_code>train_eval_result = estimator.evaluate(input_fn=predict_train_input_fn)
test_eval_result = estimator.evaluate(input_fn=predict_test_input_fn)

print("Training set accuracy: {accuracy}".format(**train_eval_result))
print("Test set accuracy: {accuracy}".format(**test_eval_result))<jupyter_output><empty_output><jupyter_text>Matriz de confusiónPodemos verificar visualmente la matriz de confusión para entender la distribución de las clasificaciones erróneas.<jupyter_code>def get_predictions(estimator, input_fn):
  return [x["class_ids"][0] for x in estimator.predict(input_fn=input_fn)]

LABELS = [
    "negative", "positive"
]

# Crea una matriz de confusión para el conjunto entrenamiento.
with tf.Graph().as_default():
  cm = tf.confusion_matrix(train_df["polarity"], 
                           get_predictions(estimator, predict_train_input_fn))
  with tf.Session() as session:
    cm_out = session.run(cm)

# Normaliza la matriz de confusión para que cada fila sume 1.
cm_out = cm_out.astype(float) / cm_out.sum(axis=1)[:, np.newaxis]

sns.heatmap(cm_out, annot=True, xticklabels=LABELS, yticklabels=LABELS);
plt.xlabel("Predicted");
plt.ylabel("True");<jupyter_output><empty_output><jupyter_text>Futuras mejoras1. **Usar un modelo de regresión**: aunque utilizamos un modelo de clasificación para asignar cada ejemplo a una clase que representa la polaridad de una reseña, en realidad tenemos otra característica categórica a nuestra disposición: el sentimiento. Aquí las clases discretas realmente representan una _escala_ y el valor subyacente (positivo / negativo) podría mapearse en un rango de _valores continuos_. Podríamos hacer uso de esta propiedad calculando una regresión ([DNN Regressor](https://www.tensorflow.org/api_docs/python/tf/contrib/learn/DNNRegressor)) en lugar de una clasificación ([DNN Classifier](https://www.tensorflow.org/api_docs/python/tf/contrib/learn/DNNClassifier)).2. **Usar un módulo más grande**: en este tutorial usamos un pequeño módulo para restringir el uso de la memoria. Pero hay módulos con vocabularios e incorporaciones más grandes que podrían incrementar la exactitud del modelo.3. **Ajustar otros parámetros**: podemos mejorar la exactitud ajustando los hiperparámetros, como la tasa de aprendizaje o la cantidad de pasos, especialmente si utilizamos un módulo diferente. Tener a la mano un conjunto de validación es también muy importante si queremos obtener resultados razonables, ya que es muy fácil configurar un modelo que aprende a predecir los datos del conjunto de entrenamiento sin generalizar bien al conjunto de prueba.4. **Entrenar un modelo más complejo**: en este tutorial utilizamos un módulo que calcula incorporaciones de oraciones al incorporar cada palabra de manera individual y después las combina calculando un promedio. Alternativamente, se podría usar un módulo secuencial (por ejemplo, el módulo [Universal Sentence Encoder](https://tfhub.dev/google/universal-sentence-encoder/2)) para captar mejor el significado de las oraciones, o inclusive combinar dos o más módulos de TF-Hub.5. **Regularización**: para evitar un sobreajuste, podríamos tratar de usar un optimizador que realice algún tipo de regularización, por ejemplo, el [Optimizador Proximal Adagrad](https://www.tensorflow.org/api_docs/python/tf/train/ProximalAdagradOptimizer).  Avanzado: análisis de aprendizaje por transferenciaEl aprendizaje por transferencia permite **ahorrar recursos de entrenamiento** y lograr una buena generalización del modelo incluso cuando se entrena con un número reducido de datos. En esta parte, demostraremos esa idea entrenando con dos módulos TF-Hub diferentes:* **[nnlm-en-dim128](https://tfhub.dev/google/nnlm-en-dim128/1)** - módulo de incorporaciones de texto previamente entrenado,* **[random-nnlm-en-dim128](https://tfhub.dev/google/random-nnlm-en-dim128/1)** - módulo de incorporaciones de texto que tiene el mismo vocabulario y red que **nnlm-en-dim128**, pero los pesos se inicializaron aleatoriamente y nunca se entrenaron con datos reales.Y entrenando en dos modos:* entrenar **solo el clasificador** (es decir, congelar el módulo), y* entrenando el **clasificador junto con el módulo**.Entrenemos y evaluemos un par de experimentos para ver cómo el uso de varios módulos puede afectar la exactitud.<jupyter_code>def train_and_evaluate_with_module(hub_module, train_module=False):
  embedded_text_feature_column = hub.text_embedding_column(
      key="sentence", module_spec=hub_module, trainable=train_module)

  estimator = tf.estimator.DNNClassifier(
      hidden_units=[500, 100],
      feature_columns=[embedded_text_feature_column],
      n_classes=2,
      optimizer=tf.train.AdagradOptimizer(learning_rate=0.003))

  estimator.train(input_fn=train_input_fn, steps=1000)

  train_eval_result = estimator.evaluate(input_fn=predict_train_input_fn)
  test_eval_result = estimator.evaluate(input_fn=predict_test_input_fn)

  training_set_accuracy = train_eval_result["accuracy"]
  test_set_accuracy = test_eval_result["accuracy"]

  return {
      "Training accuracy": training_set_accuracy,
      "Test accuracy": test_set_accuracy
  }


results = {}
results["nnlm-en-dim128"] = train_and_evaluate_with_module(
    "https://tfhub.dev/google/nnlm-en-dim128/1")
results["nnlm-en-dim128-with-module-training"] = train_and_evaluate_with_module(
    "https://tfhub.dev/google/nnlm-en-dim128/1", True)
results["random-nnlm-en-dim128"] = train_and_evaluate_with_module(
    "https://tfhub.dev/google/random-nnlm-en-dim128/1")
results["random-nnlm-en-dim128-with-module-training"] = train_and_evaluate_with_module(
    "https://tfhub.dev/google/random-nnlm-en-dim128/1", True)<jupyter_output><empty_output><jupyter_text>Veamos los resultados.<jupyter_code>pd.DataFrame.from_dict(results, orient="index")<jupyter_output><empty_output><jupyter_text>Ya podemos ver algunos patrones, pero primero debemos establecer la exactitud de referencia del conjunto de prueba - el límite inferior que se puede lograr al generar solo la etiqueta de la clase más común:<jupyter_code>estimator.evaluate(input_fn=predict_test_input_fn)["accuracy_baseline"]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09383872799545713, 'fraction_numerical': 0.010505394662123793, 'mean_word_length': 5.73792443806791, 'pattern_counts': {'":': 2, '<': 53, '<?xml version=': 0, '>': 54, 'https://': 20, 'lorem ipsum': 0, 'www.': 10, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '161622', 'license': 'MIT', 'n_tokens_mistral': 4706, 'n_tokens_neox': 4588, 'n_words': 1281, 'path': 'TEMP_es_final_text_classification_with_tf_hub.ipynb', 'repo_name': 'Skyblueballykid/RIIAA-18', 'size': '14088'}