Primero, se leen las imágenes para representarlas como un conjunto lineal, en dónde cada pixel es modelado hacia un número entero.
Cabe notar que aquí hay un cambio conceptual acerca de los conjuntos, en dónde en cátedra se tiene que el conjunto de testing es el que se usa para tomar decisiones de hiperparámetros y diseño de las máquinas y es el conjunto de validación el utilizado para elegir entre múltiples máquinas o modelos.
Dado esto, y por consistencia de enunciado, se utilizará la convención dada en esta parte de la tarea
In [1]:
import pandas as pd
from sklearn.model_selection import train_test_split
def load_data():
train = pd.read_csv('data/sign_mnist_train.csv')
test = pd.read_csv('data/sign_mnist_test.csv')
(train_set, validation_set) = train_test_split(train, test_size=7000, random_state=8500)
y_tr = train_set['label']
x_tr = train_set.iloc[:, 1:]
y_t = test['label']
x_t = test.iloc[:, 1:]
y_v = validation_set['label']
x_v = validation_set.iloc[:, 1:]
return(x_tr,x_v,x_t,y_tr,y_v,y_t)
x_tr, x_v, x_t, y_tr, y_v , y_t = load_data()
A continuación se presentan dos formas de escalar las imágenes
In [2]:
from sklearn import preprocessing
def scale_by_max_pixel(x_set):
return x_set/255
def center_and_scale(x_set):
return preprocessing.scale(x_set)
A continuación se presentan propuestas de diseño de redes neuronales para el problema y se selecciona aquel con mejor accuracy sobre el conjunto de validación
Esta red neuronal consiste en la misma red dada en el enunciado, pero que utilizar rmsProps como optimizador
In [3]:
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import SGD
from keras.utils.np_utils import to_categorical
# Creación de red secuencial
model1 = Sequential()
model1.add(Dense(30, input_dim=x_tr.shape[1], init='uniform', activation='relu'))
model1.add(Dense(30, init='uniform', activation='relu'))
model1.add(Dense(25, init='uniform', activation='softmax'))
model1.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
model1.fit(x_tr.values, to_categorical(y_tr), nb_epoch=100, batch_size=128, verbose=1,
validation_data=(x_v.values,to_categorical(y_v)))
Out[3]:
Con un accuracy de validación igual a 0.4204
Esta red corresponde a una serie de capas relu con una cantidad de neuronas decreciente y qu siguen potencias de 2. Además, utiliza el mismo optimizador que la red anterior
In [4]:
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import SGD
from keras.utils.np_utils import to_categorical
# Creación de red secuencial
model2 = Sequential()
model2.add(Dense(512, input_dim=x_tr.shape[1], init='uniform', activation='relu'))
model2.add(Dense(256, init='uniform', activation='relu'))
model2.add(Dense(128, init='uniform', activation='relu'))
model2.add(Dense(64, init='uniform', activation='relu'))
model2.add(Dense(32, init='uniform', activation='relu'))
model2.add(Dense(25, init='uniform', activation='softmax'))
model2.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
model2.fit(x_tr.values, to_categorical(y_tr), nb_epoch=100, batch_size=128, verbose=1,
validation_data=(x_v.values,to_categorical(y_v)))
Out[4]:
Con un accuracy de validación igual a 0.9341
La misma red neuronal que en 2, pero con menos épocas (60).
In [5]:
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import SGD
from keras.utils.np_utils import to_categorical
# Creación de red secuencial
model3 = Sequential()
model3.add(Dense(512, input_dim=x_tr.shape[1], init='uniform', activation='relu'))
model3.add(Dense(256, init='uniform', activation='relu'))
model3.add(Dense(128, init='uniform', activation='relu'))
model3.add(Dense(64, init='uniform', activation='relu'))
model3.add(Dense(32, init='uniform', activation='relu'))
model3.add(Dense(25, init='uniform', activation='softmax'))
model3.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
model3.fit(x_tr.values, to_categorical(y_tr), nb_epoch=60, batch_size=128, verbose=1,
validation_data=(x_v.values,to_categorical(y_v)))
Out[5]:
Con un accuracy de validación igual a 0.7837
Por tanto, la mejor red neuronal corresponde a la segunda
La matriz de confusión de la red neuronal obtenida en el tercer modelo corresponde a:
In [9]:
import itertools
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
y_t_predict = model2.predict_classes(x_t.values)
cm = confusion_matrix(y_t, y_t_predict)
plt.figure(figsize=(15, 15))
plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)
plt.colorbar()
plt.show()
Se tiene que mientras más azul es el área, mayor es la dificultad de la red neuronal para clasificar correctamente las clases. Por tanto, se puede observar que a la red le cuesta más clasificar la letra 8 (h) considerando que es en dónde más se tiene conflictos al clasificar.
Cabe notar que es normal que exista una dificultad de clasificación en la diagonal debido a que es dificil distinguir una clase de si misma.
La SVM no lineal sin procesamiento toma demasiado tiempo en converger a una solución, por lo que en lugar de buscar hiperparámetros correctos se utilizará una SVM no lineal con parámetro estándar C=0.5 y kernel tipo RBF para obtener algún resultado.
La búsqueda de parámetros se realizará en una SVM con procesamiento.
In [12]:
from sklearn.svm import SVC as SVM
from sklearn.metrics import accuracy_score
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import f1_score
def train_svm(param):
model = SVM()
model.set_params(C=param, kernel='rbf')
model.fit(x_tr, y_tr)
y_tr_pred = model.predict(x_tr)
y_v_pred = model.predict(x_v)
train_error = (1-accuracy_score(y_tr, y_tr_pred))
test_error = (1-accuracy_score(y_v, y_v_pred))
return (train_error, test_error)
def graph_svm_range(params):
train_errors = []
test_errors = []
for depth in params:
print('Trying param %f' % depth)
(train, test) = train_svm(depth)
train_errors.append(train)
test_errors.append(test)
plt.figure(figsize=(10, 8))
plt.plot(params, train_errors, label="Train Error")
plt.plot(params, test_errors, label="Test Error")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.xlabel('RBF')
plt.ylabel('Error')
plt.show()
train_svm(0.5)
Out[12]:
Con accuracy de validación de 0.08
Primero, se realizará reducción de dimensionalidad para permitir la búsqueda del mejor hiperparámetro C
In [13]:
from sklearn.decomposition import PCA
import numpy as np
x_tr_scaled = center_and_scale(x_tr)
x_t_scaled = center_and_scale(x_t)
x_v_scaled = center_and_scale(x_v)
pca_model = PCA(n_components=150)
pca_model.fit(x_tr_scaled)
x_tr_dim = pca_model.transform(x_tr_scaled)
x_t_dim = pca_model.transform(x_tr_scaled)
x_v_dim = pca_model.transform(x_v_scaled)
def train_svm(param, x_tr, y_tr, x_v, y_v):
model = SVM()
model.set_params(C=param, kernel='rbf')
model.fit(x_tr, y_tr)
y_tr_pred = model.predict(x_tr)
y_v_pred = model.predict(x_v)
train_error = (1-accuracy_score(y_tr, y_tr_pred))
test_error = (1-accuracy_score(y_v, y_v_pred))
return (train_error, test_error)
def graph_svm_range(params, x_tr, y_tr, x_v, y_v):
train_errors = []
test_errors = []
for depth in params:
print('Trying param %f' % depth)
(train, test) = train_svm(depth, x_tr, y_tr, x_v, y_v)
train_errors.append(train)
test_errors.append(test)
plt.figure(figsize=(10, 8))
plt.plot(params, train_errors, label="Train Error")
plt.plot(params, test_errors, label="Test Error")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.xlabel('RBF')
plt.ylabel('Error')
plt.show()
params = np.arange(0.001, 1.0, 0.25)
graph_svm_range(params, x_tr_dim, y_tr, x_v_dim, y_v)
In [20]:
train_svm(0.75, x_tr_dim, y_tr, x_v_dim, y_v)
Out[20]:
Que se traduce a un accuracy de validación de 0.9991. Esto indica un posible overfit a los datos de validación, por lo que es de esperar un mal rendimiento al momento de probar los datos de testing.
In [7]:
from sklearn.tree import DecisionTreeClassifier as Tree
from sklearn.metrics import accuracy_score
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import f1_score
def train_tree(depth, x_tr, y_tr, x_v, y_v):
# Entrenar el árbol
model = Tree()
model.set_params(max_depth=depth, criterion='gini', splitter='best')
model.fit(x_tr, y_tr)
y_tr_pred = model.predict(x_tr)
y_v_pred = model.predict(x_v)
train_error = (accuracy_score(y_tr, y_tr_pred))
test_error = (accuracy_score(y_v, y_v_pred))
return (train_error, test_error)
def graph_tree_range(params, x_tr, y_tr, x_v, y_v):
train_errors = []
test_errors = []
for depth in params:
print('Trying depth %d' % depth)
(train, test) = train_tree(depth, x_tr, y_tr, x_v, y_v)
train_errors.append(train)
test_errors.append(test)
plt.figure(figsize=(10, 8))
plt.plot(params, train_errors, label="Train accuracy")
plt.plot(params, test_errors, label="Validation accuracy")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.xlabel('Profundidad del árbol')
plt.ylabel('Error')
plt.show()
params = np.arange(1, 30, 1)
graph_tree_range(params, x_tr, y_tr, x_v, y_v)
In [8]:
x_tr_scaled = center_and_scale(x_tr)
x_t_scaled = center_and_scale(x_t)
x_v_scaled = center_and_scale(x_v)
params = np.arange(1, 30, 1)
graph_tree_range(params, x_tr_scaled, y_tr, x_v_scaled, y_v)
Utilizando cada máquina con los mejores hiperparámetros y considerando datos con pre procesamiento, se tiene que el mejor modelo estará dado por el accuracy de los datos de testing. Se tiene:
In [26]:
from sklearn.metrics import accuracy_score
# Neural network
y_t_pred = model3.predict_classes(x_t.values)
print("Accuracy red neuronal: %f" % accuracy_score(y_t, y_t_pred))
tree_accuracy = train_tree(30, x_tr_scaled, y_tr, x_t_scaled, y_t)
print("Accuracy decision tree: %f" % tree_accuracy[1])
x_t_dim = pca_model.transform(x_t_scaled)
svm_errors = train_svm(0.75, x_tr_dim, y_tr, x_t_dim, y_t)
svm_errors
Out[26]:
In [31]:
svm_acc = (svm_errors[1] - 1)*-1
print("Accuracy SVM: %f" % svm_acc)
La SVM lineal con kernel RBF presenta el mejor error de validación para este caso. Sin embargo, esto es sobre el conjunto de testing transformado a la representación con dimensionalidad reducida. Por esto, es muy probable que este modelo no sirva para generalizar las imágenes y es muy probable también que estemos en un caso de overfitting.
Por esto, es mejor seleccionar la redes neuronales o diseñar una nueva estrategia para mejorar la SVM. Esto es dificil considerando los problemas de rendimiento que presenta SVM al tener muchas opciones de clasificación ya que debe ajustar los hiperplanos para satisfacer demasiados constraints.
Por temas de capacidad computacional, es entonces aún más recomendable utilizar redes neuronales, ya que pueden llegar a resultados más verídicos en menos tiempo.