17 Redes neuronales de clases múltiples
- Hola a todos.Vamos continuar con nuestro curso dedicado al aprendizaje automático.Para ello nos basaremos en un curso de google dedicado a este tema. En este enlace tenéis el curso al completo en castellano: https://developers.google.com/machine-learning/crash-course/ml-intro?hl=es
- En vídeos anteriores, presentamos modelos de clasificación binaria que podían elegir una de dos opciones posibles, como el caso en el que distinguimos si un correo electrónico dado "es spam" o "no es spam". En este vídeo, investigaremos la clasificación de clases múltiples, que puedes elegir entre posibilidades múltiples. Por ejemplo:
- ¿Este perro es un beagle, un basset hound o un sabueso?
- ¿Esta flor es una iris sibirica, hollandica, versicolor o setosa?
- Algunos problemas de clases múltiples del mundo real implican elegir entre millones de clases individuales.Por ejemplo, supongamos un modelo de clasificación de clases múltiples que pueda identificar la imagen de lo que fuera.
- En la imagen vemos un ejemplo de "uno frente a todos", que es un enfoque que nos proporciona una mane
- ra de aprovechar la clasificación binaria. En un problema de clasificación dado con N soluciones posibles, una solución de uno frente a todos consiste en N clasificadores binarios independientes.
- Otro posible enfoque es utilizar Softmax. La regresión logística genera un decimal entre 0 y 1.0. Por ejemplo, si un clasificador de correo electrónico tiene un resultado de regresión logística de 0.8, hay un 80% de posibilidad de que un correo electrónico sea spam y un 20% de que no lo sea. La suma de las probabilidades de que un correo electrónico sea spam o no es 1.0.
- Softmax lleva esta idea al plano de las clases múltiples. Es decir, softmax asigna probabilidades decimales a cada clase en un caso de clases múltiples. Esas probabilidades decimales deben sumar 1.0. Esta restricción adicional permite que el entrenamiento converja más rápido.
- Os dejo un vídeo donde os cuento todo esto:
- Os dejo el código visto en el vídeo:
#Capacitar a un modelo lineal y una red neuronal para clasificar los dígitos escritos a mano.
"""
Primero, descargamos el conjunto de datos, importemos TensorFlow y otras utilidades, y carguemos los datos en un dataframe. """
from __future__ import print_function
import glob
import math
import os
from IPython import display
from matplotlib import cm
from matplotlib import gridspec
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn import metrics
import tensorflow as tf
from tensorflow.python.data import Dataset
tf.logging.set_verbosity(tf.logging.ERROR)
pd.options.display.max_rows = 10
pd.options.display.float_format = '{:.1f}'.format
mnist_dataframe = pd.read_csv(
"https://download.mlcc.google.com/mledu-datasets/mnist_train_small.csv",
sep=",",
header=None)
# Usamos los primeros 10,000 registros de entrenamiento y validación.
mnist_dataframe = mnist_dataframe.head(10000)
mnist_dataframe = mnist_dataframe.reindex(np.random.permutation(mnist_dataframe.index))
mnist_dataframe.head()
"""Las columnas 1 a 784 contienen los valores de características, uno por píxel para los valores de 28 × 28 = 784 píxeles.
Los valores de los píxeles están en una escala de grises en la que 0 representa blanco, 255 representa negro y
los valores entre 0 y 255 representan tonos de gris.
La mayoría de los valores de píxeles son 0 pero no todos son cero"""
mnist_dataframe.loc[:, 120:130]
# Ahora, analicemos las etiquetas y características y veamos algunos ejemplos
def parse_labels_and_features(dataset):
"""Extraemos de etiquetas y características.
Función para escalar o transformar las funciones si es necesario.
Args:
dataset: `Dataframe`, que contiene la etiqueta en la primera columna y
valores de píxeles monocromos en las columnas restantes, en orden mayor de fila.
Returns:
Una `tupla` `(labels, features)`:
labels: Devuelve una serie panda
características: Devuelve un dataframe
"""
labels = dataset[0]
features = dataset.loc[:,1:784]
# Escalamos para que los valores esten entre 0 y 1
features = features / 255
return labels, features
training_targets, training_examples = parse_labels_and_features(mnist_dataframe[:7500])
training_examples.describe()
validation_targets, validation_examples = parse_labels_and_features(mnist_dataframe[7500:10000])
validation_examples.describe()
#Ejemplo aleatorio y su etiqueta correspondiente.
rand_example = np.random.choice(training_examples.index)
_, ax = plt.subplots()
ax.matshow(training_examples.loc[rand_example].values.reshape(28, 28))
ax.set_title("Label: %i" % training_targets.loc[rand_example])
ax.grid(False)
"""Tarea 1: construir un modelo lineal para MNIST
Primero, vamos a crear un modelo de línea base para comparar.
El LinearClassifier proporciona un conjunto de k clasificadores de uno contra todos, uno para cada una de las k clases.
Mostramos una matriz de confusión. La matriz de confusión muestra qué clases se clasificaron erróneamente.
También tenga en cuenta que rastreamos el error del modelo usando la función log_loss.
Esto no debe confundirse con la función de pérdida interna de LinearClassifier que se utiliza para el entrenamiento."""
def construct_feature_columns():
"""Construye las columnas de la característica TensorFlow.
Returns:
Una columna de características de 784 pixeles
"""
return set([tf.feature_column.numeric_column('pixels', shape=784)])
#Creamos funciones de entrada separadas para entrenamiento y para predicción: create_training_input_fn () y
#create_predict_input_fn ().Podemos invocar estas funciones para devolver las _input_fns correspondientes para
#que pasen a nuestras llamadas .train () y .predict ().
def create_training_input_fn(features, labels, batch_size, num_epochs=None, shuffle=True):
"""Un input_fn personalizado para enviar datos.
Args:
features: Características de entrenamiento.
labels: Etiquetas de entrenamiento.
batch_size: Tamaño de lote para usar durante el entrenamiento.
Returns:
Una función que devuelve lotes de características de entrenamiento y etiquetas durante el entrenamiento
"""
def _input_fn(num_epochs=None, shuffle=True):
# Las entradas son reseteadas en cada llamada a to .train(). Para asegurar el modelo
# obtiene una buena muestra de datos, incluso cuando el número de pasos es pequeño,
# mezcla todos los datos antes de crear el objeto Dataset
idx = np.random.permutation(features.index)
raw_features = {"pixels":features.reindex(idx)}
raw_targets = np.array(labels[idx])
ds = Dataset.from_tensor_slices((raw_features,raw_targets)) # warning: 2GB limit
ds = ds.batch(batch_size).repeat(num_epochs)
if shuffle:
ds = ds.shuffle(10000)
# Devuelve el siguiente lote de datos.
feature_batch, label_batch = ds.make_one_shot_iterator().get_next()
return feature_batch, label_batch
return _input_fn
def create_predict_input_fn(features, labels, batch_size):
"""Un input_fn personalizado para enviar datos mnist al estimador para predicciones.
Args:
features: Características para predicciones
labels: Las etiquetas de los ejemplos de predicción.
Returns:
Una función que devuelve características y etiquetas para predicciones.
"""
def _input_fn():
raw_features = {"pixels": features.values}
raw_targets = np.array(labels)
ds = Dataset.from_tensor_slices((raw_features, raw_targets)) # warning: 2GB limit
ds = ds.batch(batch_size)
# Devolvemos el siguiente lote de datos
feature_batch, label_batch = ds.make_one_shot_iterator().get_next()
return feature_batch, label_batch
return _input_fn
def train_linear_classification_model(
learning_rate,
steps,
batch_size,
training_examples,
training_targets,
validation_examples,
validation_targets):
"""Entrena un modelo de clasificación lineal para el conjunto de datos de dígitos MNIST.
Además del enrenamiento, esta función también imprime información sobre el progreso de dicho entrenamiento,
Un gráfico de la pérdida de entrenamiento y validación en el tiempo, y una matriz de confuciónn.
Args:
learning_rate: A `float`, the learning rate to use.
steps: A non-zero `int`, the total number of training steps. A training step
consists of a forward and backward pass using a single batch.
batch_size: A non-zero `int`, the batch size.
training_examples: A `DataFrame` containing the training features.
training_targets: A `DataFrame` containing the training labels.
validation_examples: A `DataFrame` containing the validation features.
validation_targets: A `DataFrame` containing the validation labels.
Returns:
El objeto `LinearClassifier` entrenado.
"""
periods = 10
steps_per_period = steps / periods
# Creamos las funciones de entrada
predict_training_input_fn = create_predict_input_fn(
training_examples, training_targets, batch_size)
predict_validation_input_fn = create_predict_input_fn(
validation_examples, validation_targets, batch_size)
training_input_fn = create_training_input_fn(
training_examples, training_targets, batch_size)
# Creamos el objeto LinearClassifier.
my_optimizer = tf.train.AdagradOptimizer(learning_rate=learning_rate)
my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)
classifier = tf.estimator.LinearClassifier(
feature_columns=construct_feature_columns(),
n_classes=10,
optimizer=my_optimizer,
config=tf.estimator.RunConfig(keep_checkpoint_max=1)
)
# Entrene al modelo, pero hágalo dentro de un bucle para que podamos evaluar periódicamente
print("Entrenando el modelo...")
print("LogLoss error en datos de valdación:")
training_errors = []
validation_errors = []
for period in range (0, periods):
# Entrenamos al modelo desde el estado anterior
classifier.train(
input_fn=training_input_fn,
steps=steps_per_period
)
#Calculamos probabilidades
training_predictions = list(classifier.predict(input_fn=predict_training_input_fn))
training_probabilities = np.array([item['probabilities'] for item in training_predictions])
training_pred_class_id = np.array([item['class_ids'][0] for item in training_predictions])
training_pred_one_hot = tf.keras.utils.to_categorical(training_pred_class_id,10)
validation_predictions = list(classifier.predict(input_fn=predict_validation_input_fn))
validation_probabilities = np.array([item['probabilities'] for item in validation_predictions])
validation_pred_class_id = np.array([item['class_ids'][0] for item in validation_predictions])
validation_pred_one_hot = tf.keras.utils.to_categorical(validation_pred_class_id,10)
# Calcular los errores de entrenamiento y validación.
training_log_loss = metrics.log_loss(training_targets, training_pred_one_hot)
validation_log_loss = metrics.log_loss(validation_targets, validation_pred_one_hot)
# De vez en cuando imprimir la pérdida actual.
print(" period %02d : %0.2f" % (period, validation_log_loss))
# Agrega las métricas de pérdida de este período a nuestra lista.
training_errors.append(training_log_loss)
validation_errors.append(validation_log_loss)
print("Entrenamiento del modelo finalizazo.")
# Eliminar archivos de eventos para ahorrar espacio en disco.
_ = map(os.remove, glob.glob(os.path.join(classifier.model_dir, 'events.out.tfevents*')))
# Calcule las predicciones finales
final_predictions = classifier.predict(input_fn=predict_validation_input_fn)
final_predictions = np.array([item['class_ids'][0] for item in final_predictions])
accuracy = metrics.accuracy_score(validation_targets, final_predictions)
print("Exactitud final (on validation data): %0.2f" % accuracy)
# Genera una gráfica de métricas de pérdida en periodos.
plt.ylabel("LogLoss")
plt.xlabel("Periodos")
plt.title("LogLoss vs. Periodos")
plt.plot(training_errors, label="training")
plt.plot(validation_errors, label="validation")
plt.legend()
plt.show()
# Pintamos la matriz de confusión
cm = metrics.confusion_matrix(validation_targets, final_predictions)
# Normalizamos la matriz de confusión por fila.
cm_normalized = cm.astype("float") / cm.sum(axis=1)[:, np.newaxis]
ax = sns.heatmap(cm_normalized, cmap="bone_r")
ax.set_aspect(1)
plt.title("Confusion matriz")
plt.ylabel("Etiqueta verdadera")
plt.xlabel("Etiqueta predicción")
plt.show()
return classifier
_ = train_linear_classification_model(
learning_rate=0.03,
steps=1000,
batch_size=30,
training_examples=training_examples,
training_targets=training_targets,
validation_examples=validation_examples,
validation_targets=validation_targets)
"""Tarea 2: reemplazar el clasificador lineal con una red neuronal
Reemplace el LinearClassifier de arriba con un DNNClassifier y encuentre una combinación de parámetros que
dé una precisión de 0.95 o más."""
def train_nn_classification_model(
learning_rate,
steps,
batch_size,
hidden_units,
training_examples,
training_targets,
validation_examples,
validation_targets):
"""Entrena un modelo de clasificación de red neuronal para el conjunto de datos de dígitos MNIST.
Además del enrenamiento, esta función también imprime información sobre el progreso
de dicho entrenamiento,
Un gráfico de la pérdida de entrenamiento y validación en el tiempo, y una matriz de confusiónn.
Args:
learning_rate: A `float`, the learning rate to use.
steps: A non-zero `int`, the total number of training steps. A training step
consists of a forward and backward pass using a single batch.
batch_size: A non-zero `int`, the batch size.
hidden_units: A `list` of int values, specifying the number of neurons in each layer.
training_examples: A `DataFrame` containing the training features.
training_targets: A `DataFrame` containing the training labels.
validation_examples: A `DataFrame` containing the validation features.
validation_targets: A `DataFrame` containing the validation labels.
Returns:
El objeto `DNNClassifier` .
"""
periods = 10
# Precaución: las entradasse restablecen con cada llamada.
# Si el número de pasos es pequeño, es posible que su modelo nunca vea la mayoría de los datos.
# Así que con múltiples llamadas `.train` es posible que desee controlar la longitud
steps_per_period = steps / periods
# Creamos las funciones de entrada
predict_training_input_fn = create_predict_input_fn(
training_examples, training_targets, batch_size)
predict_validation_input_fn = create_predict_input_fn(
validation_examples, validation_targets, batch_size)
training_input_fn = create_training_input_fn(
training_examples, training_targets, batch_size)
# Creamos las funciones de entrada
predict_training_input_fn = create_predict_input_fn(
training_examples, training_targets, batch_size)
predict_validation_input_fn = create_predict_input_fn(
validation_examples, validation_targets, batch_size)
training_input_fn = create_training_input_fn(
training_examples, training_targets, batch_size)
# Creamos las columnas
feature_columns = [tf.feature_column.numeric_column('pixels', shape=784)]
#objeto DNNClassifier.
my_optimizer = tf.train.AdagradOptimizer(learning_rate=learning_rate)
my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)
classifier = tf.estimator.DNNClassifier(
feature_columns=feature_columns,
n_classes=10,
hidden_units=hidden_units,
optimizer=my_optimizer,
config=tf.contrib.learn.RunConfig(keep_checkpoint_max=1)
)
# Entrene al modelo, pero hágalo dentro de un bucle para que podamos evaluar periódicamente
print("Training model...")
print("LogLoss error (on validation data):")
training_errors = []
validation_errors = []
for period in range (0, periods):
#Entrena el modelo, partiendo del estado anterior.
classifier.train(
input_fn=training_input_fn,
steps=steps_per_period
)
#Calculamos probabilidades
training_predictions = list(classifier.predict(input_fn=predict_training_input_fn))
training_probabilities = np.array([item['probabilities'] for item in training_predictions])
training_pred_class_id = np.array([item['class_ids'][0] for item in training_predictions])
training_pred_one_hot = tf.keras.utils.to_categorical(training_pred_class_id,10)
validation_predictions = list(classifier.predict(input_fn=predict_validation_input_fn))
validation_probabilities = np.array([item['probabilities'] for item in validation_predictions])
validation_pred_class_id = np.array([item['class_ids'][0] for item in validation_predictions])
validation_pred_one_hot = tf.keras.utils.to_categorical(validation_pred_class_id,10)
# Calcular los errores de entrenamiento y validación.
training_log_loss = metrics.log_loss(training_targets, training_pred_one_hot)
validation_log_loss = metrics.log_loss(validation_targets, validation_pred_one_hot)
# De vez en cuando imprimir la pérdida actual.
print(" period %02d : %0.2f" % (period, validation_log_loss))
# Agrega las métricas de pérdida de este período a nuestra lista.
training_errors.append(training_log_loss)
validation_errors.append(validation_log_loss)
print("Entrenamiento del modelo finalizado.")
# Eliminar archivos de eventos para ahorrar espacio en disco.
_ = map(os.remove, glob.glob(os.path.join(classifier.model_dir, 'events.out.tfevents*')))
# Calculamos las predicciones finales
final_predictions = classifier.predict(input_fn=predict_validation_input_fn)
final_predictions = np.array([item['class_ids'][0] for item in final_predictions])
accuracy = metrics.accuracy_score(validation_targets, final_predictions)
print("Exactitud final (on validation data): %0.2f" % accuracy)
# Genera una gráfica de métricas de pérdida en periodos.
plt.ylabel("LogLoss")
plt.xlabel("Periodos")
plt.title("LogLoss vs. Periodos")
plt.plot(training_errors, label="training")
plt.plot(validation_errors, label="validation")
plt.legend()
plt.show()
# Pintamos la matriz de confusión# Pintamos la matriz de confusión
cm = metrics.confusion_matrix(validation_targets, final_predictions)
#Normalizamos
cm_normalized = cm.astype("float") / cm.sum(axis=1)[:, np.newaxis]
ax = sns.heatmap(cm_normalized, cmap="bone_r")
ax.set_aspect(1)
plt.title("Confusion matriz")
plt.ylabel("Etiqueta verdadera")
plt.xlabel("Etiqueta predicción")
plt.show()
return classifier
classifier = train_nn_classification_model(
learning_rate=0.05,
steps=1000,
batch_size=30,
hidden_units=[100, 100],
training_examples=training_examples,
training_targets=training_targets,
validation_examples=validation_examples,
validation_targets=validation_targets)
predict_test_input_fn = create_predict_input_fn(
test_examples, test_targets, batch_size=100)
test_predictions = classifier.predict(input_fn=predict_test_input_fn)
test_predictions = np.array([item['class_ids'][0] for item in test_predictions])
accuracy = metrics.accuracy_score(test_targets, test_predictions)
print("Accuracy on test data: %0.2f" % accuracy)
"""Tarea 3: Visualizar los pesos de la primera capa oculta.
Tomemos unos minutos para profundizar en nuestra red neuronal
y ver lo que ha aprendido al acceder al atributo de peso_ de
nuestro modelo.
La capa de entrada de nuestro modelo tiene 784 pesos correspondientes a las imágenes de entrada de 28 × 28 píxeles.
La primera capa oculta tendrá 784 × N pesos, donde N es el número de nodos en esa capa.
Podemos volver a convertir esos pesos en imágenes 28 × 28 cambiando cada una de las matrices N 1 × 784 de pesos
en matrices N de tamaño 28 × 28."""
print(classifier.get_variable_names())
weights0 = classifier.get_variable_value("dnn/hiddenlayer_0/kernel")
print("weights0 shape:", weights0.shape)
num_nodes = weights0.shape[1]
num_rows = int(math.ceil(num_nodes / 10.0))
fig, axes = plt.subplots(num_rows, 10, figsize=(20, 2 * num_rows))
for coef, ax in zip(weights0.T, axes.ravel()):
# Weights in coef is reshaped from 1x784 to 28x28.
ax.matshow(coef.reshape(28, 28), cmap=plt.cm.pink)
ax.set_xticks(())
ax.set_yticks(())
plt.show()
No hay comentarios:
Publicar un comentario