In [1]:
# Configuracion para recargar módulos y librerías
%reload_ext autoreload
%autoreload 2
%matplotlib inline
from IPython.display import Image as ShowImage
from IPython.core.display import HTML
HTML(open("style/mat281.css", "r").read())
Out[1]:
In [ ]:
from mat281_code.lab import greetings
alumno_1 = ("Sebastian Flores", "2004001-7")
alumno_2 = ("Maria Jose Vargas", "2004007-8")
HTML(greetings(alumno_1, alumno_2))
Este laboratorio utiliza la librería sklearn (oficialmente llamada scikit learn), de la cual utilizaremos el método de k Nearest Neighbors.
En este laboratorio realizaremos el trabajo de reconocer un dígito a partir de una imagen.
El repositorio con los datos se encuentra en el siguiente link, pero los datos ya han sido incluídos en el directorio data/
.
El laboratorio consiste de 4 secciones:
Los datos se encuentran en 2 archivos, data/optdigits.train
y data/optdigits.test
. Como su nombre lo indica, el set data/optdigits.train
contiene los ejemplos que deben ser usados para entrenar el modelo, mientras que el set data/optdigits.test
se utilizará para obtener una estimación del error de predicción.
Ambos archivos comparten el mismo formato: cada línea contiene 65 valores. Los 64 primeros corresponden a la representación de la imagen en escala de grises (0-blanco, 255-negro), y el valor 65 corresponde al dígito de la imágene (0-9).
In [ ]:
import numpy as np
XY_tv = np.loadtxt("data/optdigits.train", delimiter=",", dtype=np.int8)
print XY_tv
X_tv = XY_tv[:,:64]
Y_tv = XY_tv[:, 64]
print X_tv.shape
print Y_tv.shape
print X_tv[0,:]
print X_tv[0,:].reshape(8,8)
print Y_tv[0]
Para visualizar los datos utilizaremos el método imshow de pyplot. Resulta necesario convertir el arreglo desde las dimensiones (1,64) a (8,8) para que la imagen sea cuadrada y pueda distinguirse el dígito. Superpondremos además el label correspondiente al dígito, mediante el método text. Realizaremos lo anterior para los primeros 25 datos del archivo.
In [ ]:
from matplotlib import pyplot as plt
# Well plot the first nx*ny examples
nx, ny = 5, 5
fig, ax = plt.subplots(nx, ny, figsize=(12,12))
for i in range(nx):
for j in range(ny):
index = j+ny*i
data = X_tv[index,:].reshape(8,8)
label = Y_tv[index]
ax[i][j].imshow(data, interpolation='nearest', cmap=plt.get_cmap('gray_r'))
ax[i][j].text(7, 0, str(int(label)), horizontalalignment='center',
verticalalignment='center', fontsize=10, color='blue')
ax[i][j].get_xaxis().set_visible(False)
ax[i][j].get_yaxis().set_visible(False)
plt.show()
In [ ]:
from sklearn.neighbors import KNeighborsClassifier
k = 1
kNN = KNeighborsClassifier(n_neighbors=k)
kNN.fit(X_tv, Y_tv)
Y_pred = kNN.predict(X_tv)
n_errors = sum(Y_pred!=Y_tv)
print "Hay %d errores de un total de %d ejemplos de entrenamiento" %(n_errors, len(Y_tv))
A partir del análisis del punto anterior, nos damos cuenta de la necesidad de:
Complete el código entregado a continuación, de modo que se calcule el error de predicción (en porcentaje) de kNN, para k entre 1 y 10 (ambos incluidos). Realice una división en set de entrenamiento (75%) y de validación (25%), y calcule el valor promedio y desviación estándar del error de predicción (en porcentaje), tomando al menos 20 repeticiones para cada valor de k.
OBS: Ejecución de la celda debería tomar alrededor de 5 minutos.
In [ ]:
from sklearn.neighbors import KNeighborsClassifier
from sklearn.cross_validation import train_test_split
template = "k={0:,d}: {1:.1f} +- {2:.1f} errores de clasificación de un total de {3:,d} puntos"
# Fitting the model
mean_error_for_k = []
std_error_for_k = []
k_range = ##FIX ME##
for k in k_range:
errors_k = []
for i in ##FIX ME##:
kNN = ##FIX ME##
X_train, X_valid, Y_train, Y_valid = ##FIX ME##
kNN.fit(X_train, Y_train)
# Predicting values
Y_valid_pred = kNN.predict(X_valid)
# Count the errors
n_errors = ##FIX ME##
# Add them to vector
errors_k.append(100.*n_errors/len(Y_valid))
errors = np.array(errors_k)
print template.format(k, errors.mean(), errors.std(), len(Y_valid))
mean_error_for_k.append(errors.mean())
std_error_for_k.append(errors.std())
In [ ]:
mean = np.array(mean_error_for_k)
std = np.array(std_error_for_k)
plt.figure(figsize=(12,8))
plt.plot(k_range, mean - std, "k:")
plt.plot(k_range, mean , "r.-")
plt.plot(k_range, mean + std, "k:")
plt.xlabel("Numero de vecinos k")
plt.ylabel("Error de clasificacion")
plt.show()
In [ ]:
from sklearn.neighbors import KNeighborsClassifier
from sklearn.cross_validation import train_test_split
import numpy as np
k = 2
kNN = KNeighborsClassifier(n_neighbors=k)
kNN.fit(X_tv, Y_tv)
Ahora que el modelo kNN ha sido completamente entrenado, calcularemos el error de predicción en un set de datos completamente nuevo: el set de testing.
Complete el código a continuación, para cargar los datos del set de entrenamiento y realizar una predicción de los dígitos de cada imagen. No cambie los nombres de las variables.
In [ ]:
# Cargando el archivo data/optdigits.tes
XY_test = ##FIX ME##
X_test = ##FIX ME##
Y_test = ##FIX ME##
# Predicción de etiquetas
Y_pred = ##FIX ME##
In [ ]:
from matplotlib import pyplot as plt
# Mostrar los datos correctos
mask = (Y_pred==Y_test)
X_aux = X_test[mask]
Y_aux_true = Y_test[mask]
Y_aux_pred = Y_pred[mask]
# We'll plot the first 100 examples, randomly choosen
nx, ny = 5, 5
fig, ax = plt.subplots(nx, ny, figsize=(12,12))
for i in range(nx):
for j in range(ny):
index = j+ny*i
data = X_aux[index,:].reshape(8,8)
label_pred = str(int(Y_aux_pred[index]))
label_true = str(int(Y_aux_true[index]))
ax[i][j].imshow(data, interpolation='nearest', cmap=plt.get_cmap('gray_r'))
ax[i][j].text(0, 0, label_pred, horizontalalignment='center',
verticalalignment='center', fontsize=10, color='green')
ax[i][j].text(7, 0, label_true, horizontalalignment='center',
verticalalignment='center', fontsize=10, color='blue')
ax[i][j].get_xaxis().set_visible(False)
ax[i][j].get_yaxis().set_visible(False)
plt.show()
Más interesante que el gráfico anterior, resulta considerar los casos donde los dígitos han sido incorrectamente etiquetados.
Modifique el código anteriormente provisto para que muestre los dígitos incorrectamente etiquetados, cambiando apropiadamente la máscara. Cambie también el color de la etiqueta desde verde a rojo, para indicar una mala etiquetación.
In [ ]:
##FIX ME##
In [ ]:
# Error global
mask = (Y_pred!=Y_test)
error_prediccion = ##FIX ME##
print "Error de predicción total de %.1f " %error_prediccion
for digito in range(0,10):
mask_digito = ##FIX ME##
Y_test_digito = Y_test[mask_digito]
Y_pred_digito = Y_pred[mask_digito]
error_prediccion = 100.*sum((Y_pred_digito!=Y_test_digito)) / len(Y_pred_digito)
print "Error de predicción para digito %d de %.1f " %(digito, error_prediccion)
In [ ]:
from sklearn.metrics import confusion_matrix as cm
cm = cm(Y_test, Y_pred)
print cm
In [ ]:
# As in http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html
def plot_confusion_matrix(cm, title='Confusion matrix', cmap=plt.cm.hot):
plt.figure(figsize=(10,10))
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(10)
plt.xticks(tick_marks, tick_marks)
plt.yticks(tick_marks, tick_marks)
plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.show()
return None
# Compute confusion matrix
plt.figure()
plot_confusion_matrix(cm)
# Normalize the confusion matrix by row (i.e by the number of samples
# in each class)
cm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
plot_confusion_matrix(cm_normalized, title='Normalized confusion matrix')
A partir de lo anterior, vemos observamos que los mayores errores son:
In [ ]:
from PIL import Image
import numpy as np
# Data
input_image = "mis_digitos/0.jpg"
# Read image
image = Image.open(input_image, 'r')
width, height = image.size
print "La imagen tiene %dx%d=%d pixeles" %(width, height, width*height)
X_rgb = np.array(image.getdata(), dtype=np.uint8) # x_i = (R_i, G_i, B_i)
#X_rgb = X_rgb.reshape(height,width,3)
print X_rgb[:,0] # Rojo
print X_rgb[:,1] # Verde
print X_rgb[:,2] # Azul
Nuestro algoritmo no utiliza los distintos niveles de rojo (R), verde (G) y azul (B), y solamente le importa la luminancia $L$ de la imagen: $$ L = (R + G + B)/3 $$ Esto es razonable, pues no nos interesa el color del lápiz que realizó el dígito, sino la forma de éste. Resulta además escalar para que la imagen tenga presente el valor máximo y mínimo (no depende de la iluminación de la imagen), es decir, que se cumpla $min(X) = 0$ y $max(X) = 255$.
In [ ]:
X = 255 - (X_rgb[:,0] + X_rgb[:,1] + X_rgb[:,2])/3.
digito = 300 * (X-X.min())/(X.max()-X.min())
digito[digito>255] = 255
Visualicemos la imagen obtenida
In [ ]:
data = digito.reshape(8,8)
plt.imshow(data, interpolation='nearest', cmap=plt.get_cmap('gray_r'))
plt.show()
Veamos que nos daría la predicción con kNN. Recuerde que tiene el modelo ya ha sido cargado en memoria y se encuenta en la variable kNN
.
In [ ]:
label = kNN.predict(digito)
print label
In [ ]:
X_mis_digitos = np.zeros([10,64])
Y_mis_digitos = np.zeros(10)
for i in range(10):
template = "mis_digitos/{0}.jpg"
archivo = template.format(i)
print "Abriendo archivo", archivo
image = Image.open(archivo, 'r')
X_rgb = np.array(image.getdata(), dtype=np.uint8) # x_i = (R_i, G_i, B_i)
X = 255 - (X_rgb[:,0] + X_rgb[:,1] + X_rgb[:,2])/3.
digito = 300 * (X-X.min())/(X.max()-X.min())
digito[digito>255] = 255
X_mis_digitos[i,:] = digito
Y_mis_digitos[i] = i
# Realizar la predicción simultánea
Y_pred = kNN.predict(X_mis_digitos)
In [ ]:
from matplotlib import pyplot as plt
X_aux = X_mis_digitos
Y_aux_true = Y_mis_digitos
Y_aux_pred = Y_pred
nx, ny = 2, 5
fig, ax = plt.subplots(nx, ny, figsize=(20,10))
for i in range(nx):
for j in range(ny):
index = j+ny*i
data = X_aux[index,:].reshape(8,8)
label_pred = str(int(Y_aux_pred[index]))
label_true = str(int(Y_aux_true[index]))
if label_true == label_pred:
color = "green"
else:
color = "red"
ax[i][j].imshow(data, interpolation='nearest', cmap=plt.get_cmap('gray_r'))
ax[i][j].text(0, 0, label_pred, horizontalalignment='center',
verticalalignment='center', fontsize=10, color=color)
ax[i][j].text(7, 0, label_true, horizontalalignment='center',
verticalalignment='center', fontsize=10, color='blue')
ax[i][j].get_xaxis().set_visible(False)
ax[i][j].get_yaxis().set_visible(False)
plt.show()