MICROPROYECTO 2: INTELIGENCIA ARTIFICIAL

Red Neuronal Convolucional para la clasificación de bicicletas.

Integrantes del equipo:

* Alejandro Mejía

* Cristian Tamayo

* Juan G Sarrias

Oct. 2020

Antes de comenzar, se carga el drive donde se encuentran las imagenes y donde se guardaran los datasets.

In [ ]:
from google.colab import drive
drive.mount('/content/drive/')
Mounted at /content/drive/

El dataset utilizado para este proyecto fue "Maviintelligence Bycicle Dataset", el cual fue etiquetado manualmente con las clases que se mostrarán, y se le añadieron imágenes obtenidas de internet compensando algunas de las clases de bicicletas que tenían una mala cantidad en proporción respecto al resto:

Dataset from: http://maviintelligence.com/

Se cargan las librerías necesarias para todo el proceso:

In [ ]:
# Pickle para los datasets
import pickle

# Librerias para manejo de imagenes y matrices
import math
import random
import pandas as pd
import numpy as np
import scipy
from scipy import ndimage
import time
import matplotlib.pyplot as plt
from PIL import Image
import imageio
import cv2
from __future__ import absolute_import, division, print_function, unicode_literals

# Librerias TensorFlow y tf.keras para manejo del modelo
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten, Conv2D, MaxPooling2D
from keras.models import model_from_json
from keras.models import load_model
from tensorflow.python.framework import ops

# Librerias para matriz de confusion y metricas
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score

print("Version de TensorFlow: "+str(tf.__version__))
Version de TensorFlow: 2.3.0

Esta parte del código solo se centra en crear los dataset a partir de imagenes recopiladas en carpeta del drive, etiquetadas por medio de archivos de excel.

1. Se leen las etiquetas y numeros de archivo de test y train, desde sus respectivos archivos de excel.

In [ ]:
etiquetasTest = pd.read_excel('/content/drive/My Drive/ia/Dataset/test.xlsx',header=None, index_col=None)
arregloTest = np.array(etiquetasTest)

etiquetasTrain = pd.read_excel('/content/drive/My Drive/ia/Dataset/train.xlsx',header=None, index_col=None)
arregloTrain = np.array(etiquetasTrain)

2.1 Se convierten las imágenes de la carpeta train en arrays con forma (n, res, res, 3). Donde:

*n es la cantidad de imagenes.

*res es la cantidad de pixeles (cada imagen es res x res).

*3 simboliza los colores de RGB.

También, se convierten las etiquetas en un array.

In [ ]:
#IMAGENES TRAIN A ARRAY

#Resolucion imagen (res x res)
res = 100

#Cantidad de imagenes
n = np.size(arregloTrain,axis=0)

lista_bikes_x_train = []
lista_bikes_y_train = []
#test_bikes_x = np.array([])
#test_bikes_y = np.array([])

for i in range(0,n):
  numeroImagen = arregloTrain[i][1]
  etiquetaImagen = arregloTrain[i][0]

  # Nombre de cada imagen, debe ser tipo bycX.png, con X = numero de imagen
  my_image = "byc"+str(numeroImagen)+".png"

  # Ruta completa
  fname = "/content/drive/My Drive/ia/Dataset/Maviintelligence Bicyle Dataset/train/" + my_image

  # Transforma la imagen a arreglo
  image = cv2.imread(fname)
  #image = np.array(imageio.imread(fname))

  # Cambia el tamano
  resize_image = cv2.resize(image, (res, res))

  # Anade a las imagenes y a las etiquetas
  
  lista_bikes_x_train.append(resize_image)
  lista_bikes_y_train.append(etiquetaImagen)

#plt.imshow(resize_image)
#resize_image.shape

2.2 Se convierten las imágenes de la carpeta test en arrays con forma (n, res, res, 3). Donde:

*n es la cantidad de imagenes.

*res es la cantidad de pixeles (cada imagen es res x res).

*3 simboliza los colores de RGB.

También, se convierten las etiquetas en un array.

In [ ]:
#IMAGENES TESTEO A ARRAY

#Resolucion imagen (res x res)
res = 100

#Cantidad de imagenes
n = np.size(arregloTest,axis=0)

lista_bikes_x_test = []
lista_bikes_y_test = []
#test_bikes_x = np.array([])
#test_bikes_y = np.array([])

for i in range(0,n):
  numeroImagen = arregloTest[i][1]
  etiquetaImagen = arregloTest[i][0]

  # Nombre de cada imagen, debe ser tipo bycX.png, con X = numero de imagen
  my_image = "byc"+str(numeroImagen)+".png"

  # Ruta completa
  fname = "/content/drive/My Drive/ia/Dataset/Maviintelligence Bicyle Dataset/test/" + my_image

  # Transforma la imagen a arreglo
  image = cv2.imread(fname)
  #image = np.array(imageio.imread(fname))

  # Cambia el tamano
  resize_image = cv2.resize(image, (res, res))

  # Anade a las imagenes y a las etiquetas
  
  lista_bikes_x_test.append(resize_image)
  lista_bikes_y_test.append(etiquetaImagen)

#plt.imshow(resize_image)
#resize_image.shape

3. Se pasan las listas de 'image-arrays' y las listas de etiquetas a datasets de tipo pickle, para no realizar el proceso de conversión cada vez que se necesite correr la red neuronal, sino que solo sea necesario cargar los datasets.

In [ ]:
# El pickle = version anterior (color)
# El pickle con 1 en nombre = version ultima (ByN)

pickle_out = open("train_X.pickle", "wb")
pickle.dump(lista_bikes_x_train, pickle_out)
pickle_out.close()

pickle_out = open("train_Y.pickle", "wb")
pickle.dump(lista_bikes_y_train, pickle_out)
pickle_out.close()

pickle_out = open("test_X.pickle", "wb")
pickle.dump(lista_bikes_x_test, pickle_out)
pickle_out.close()

pickle_out = open("test_Y.pickle", "wb")
pickle.dump(lista_bikes_y_test, pickle_out)
pickle_out.close()

!mv /content/train_X.pickle /content/drive/My\ Drive/ia/Dataset
!mv /content/test_X.pickle /content/drive/My\ Drive/ia/Dataset
!mv /content/train_Y.pickle /content/drive/My\ Drive/ia/Dataset
!mv /content/test_Y.pickle /content/drive/My\ Drive/ia/Dataset

4. Se cargan los datasets de tipo pickle.

In [ ]:
trainX = pickle.load(open("/content/drive/My Drive/ia/Dataset/train_X.pickle", "rb"))
trainY = pickle.load(open("/content/drive/My Drive/ia/Dataset/train_Y.pickle", "rb"))
testX = pickle.load(open("/content/drive/My Drive/ia/Dataset/test_X.pickle", "rb"))
testY = pickle.load(open("/content/drive/My Drive/ia/Dataset/test_Y.pickle", "rb"))

5. Se colocan las clases que se van a reconocer en las bicicletas, las cuales serán de tipo:

0. Ruta

1. Urbanas

2. Montaña

3. Doble (bicicletas para 2 personas)

4. No es bicicleta

In [ ]:
class_names = ['ruta', 'urbana', 'montana', 'doble', 'no_es']

6. Se mezclan las imagenes con su respectiva etiqueta, para obtener resultados más uniformes.

In [ ]:
total = []

# Se anaden las imagenes totales a lista total
for i in range(len(trainX)):
  total.append([trainX[i], trainY[i]])

for i in range(len(testX)):
  total.append([testX[i], testY[i]])

# Se mezclan para no tener patrones previos
random.shuffle(total)
X = []
Y = []
for etiquetada in total:
  X.append(etiquetada[0])
  Y.append(etiquetada[1])
print("Total de imagenes: "+str(len(X)))
Total de imagenes: 1503

7. Se convierten las imagenes y sus etiquetas en arreglos NumPy para su manejo matricial, se normalizan las imágenes y se muestran algunos datos.

7.1. Se definen las proporciones:

* 85% para el train (70% train y 15% validación aproximadamente).

* 15% para el test.

In [ ]:
# Conversion de datasets cargados en arreglos numpy para su manejo matricial.
X_np = np.array(X)
Y_np = np.array(Y)

# Resolucion
res = 100

# Proporcion
proptrain = 85
proptest = 15

# Normalizar
X_np = X_np/255.0

# Total de datos
train_total = X_np.shape[0]

# Particion
indice = int(train_total*(proptrain/100))
trainX1 = X_np[0:indice]
trainY1 = Y_np[0:indice]
testX1 = X_np[indice:]
testY1 = Y_np[indice:]

train_total = X_np.shape[0]
print ("Numero de datos total = " + str(train_total))
print ("Numero de ejemplos de entrenamiento = " + str(int(train_total*proptrain/100)))
print ("Numero de ejemplos de testeo = " + str(int(train_total*proptest/100)))
print ("Proporción train/total = "+str(proptrain)+"%")
print ("Proporción test/total = "+str(proptest)+"%")
print ("Forma de las imagenes X (shape): " + str(X_np.shape))
print ("Forma de las etiquetas Y (shape): " + str(Y_np.shape))
Numero de datos total = 1503
Numero de ejemplos de entrenamiento = 1277
Numero de ejemplos de testeo = 225
Proporción train/total = 85%
Proporción test/total = 15%
Forma de las imagenes X (shape): (1503, 100, 100, 3)
Forma de las etiquetas Y (shape): (1503,)

7.2. Ejemplo de una imagen cargada:

In [ ]:
index1 = 1500
plt.imshow(X[index1])
print("Clase: "+str(Y[index1])+" : "+class_names[Y[index1]])
print(X[index1].shape)
Clase: 2 : montana
(100, 100, 3)

8. El modelo

La red neuronal será entrenada teniendo en cuenta:

* El modelo será secuencial.

* Hay 3 capas convolucionales para filtrado, con función de activación ReLu.

* Hay 2 capas densas, con función de activación ReLu.

* Hay 1 capa de salida, con función de activación SoftMax.

* ReLu se usa para las primeras capas debido a que esta función acelera la convergencia del descenso del gradiente.

* Softmax se usa para la última capa debido a que entrega valores de probabilidad útiles para la clasificación de las bicicletas.

* El número de epochs o iteraciones serán 40 (para evitar overfiting).

* Los "batch" serán 32.

In [ ]:
# Inicializa un modelo de tipo Secuencial:

model = Sequential()

# 3 capas convolucionales para el filtrado:

#   1 capa con funcion de activacion ReLu para entrada de datos y pooling para
#   filtrar valores mas significativos
model.add(Conv2D(32, (3, 3), input_shape = X_np.shape[1:]))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2,2)))

#   1 capa con funcion de activacion ReLu y pooling para filtrar valores mas 
#   significativos
model.add(Conv2D(64, (3, 3)))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2,2)))

#   1 capa con funcion de activacion ReLu, pooling y dropout activado para
#   "apagar" neuronas durante el entrenamiento, evitando overfitting
model.add(Conv2D(64, (3, 3)))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

#   1 capa de formato para aplanar los datos
model.add(Flatten())

# 2 capas ocultas con función de activación ReLu completamente conectadas 
model.add(Dense(128))
model.add(Activation("relu"))

model.add(Dense(128))
model.add(Activation("relu"))

# Una capa de salida con funcion de activacion SoftMax 
model.add(Dense(5))
model.add(Activation("softmax"))

# El optimizador para el backward sera ADAM por su eficiencia
model.compile(loss="sparse_categorical_crossentropy",
				optimizer="adam",
				metrics=["accuracy"])

# Se entrena el modelo y se obtienen los resultados:
# validation_split corresponds to the percentage of images used for the validation phase compared to all the images
history = model.fit(trainX1, trainY1, batch_size=32, epochs=40, validation_split=0.17)
Epoch 1/40
34/34 [==============================] - 1s 29ms/step - loss: 1.4088 - accuracy: 0.3437 - val_loss: 1.3790 - val_accuracy: 0.3853
Epoch 2/40
34/34 [==============================] - 1s 20ms/step - loss: 1.3580 - accuracy: 0.3475 - val_loss: 1.3622 - val_accuracy: 0.4541
Epoch 3/40
34/34 [==============================] - 1s 21ms/step - loss: 1.2766 - accuracy: 0.4297 - val_loss: 1.2789 - val_accuracy: 0.4908
Epoch 4/40
34/34 [==============================] - 1s 20ms/step - loss: 1.1479 - accuracy: 0.5345 - val_loss: 1.3601 - val_accuracy: 0.4128
Epoch 5/40
34/34 [==============================] - 1s 20ms/step - loss: 1.0694 - accuracy: 0.5770 - val_loss: 1.1455 - val_accuracy: 0.5826
Epoch 6/40
34/34 [==============================] - 1s 20ms/step - loss: 0.9318 - accuracy: 0.6251 - val_loss: 1.0763 - val_accuracy: 0.6239
Epoch 7/40
34/34 [==============================] - 1s 20ms/step - loss: 0.7640 - accuracy: 0.7167 - val_loss: 1.0795 - val_accuracy: 0.6101
Epoch 8/40
34/34 [==============================] - 1s 20ms/step - loss: 0.6557 - accuracy: 0.7715 - val_loss: 1.0092 - val_accuracy: 0.6376
Epoch 9/40
34/34 [==============================] - 1s 20ms/step - loss: 0.5056 - accuracy: 0.8244 - val_loss: 1.0060 - val_accuracy: 0.6606
Epoch 10/40
34/34 [==============================] - 1s 20ms/step - loss: 0.4387 - accuracy: 0.8527 - val_loss: 1.0202 - val_accuracy: 0.6697
Epoch 11/40
34/34 [==============================] - 1s 20ms/step - loss: 0.4387 - accuracy: 0.8461 - val_loss: 1.1883 - val_accuracy: 0.6743
Epoch 12/40
34/34 [==============================] - 1s 20ms/step - loss: 0.3662 - accuracy: 0.8650 - val_loss: 1.0286 - val_accuracy: 0.6927
Epoch 13/40
34/34 [==============================] - 1s 20ms/step - loss: 0.3084 - accuracy: 0.8961 - val_loss: 0.9562 - val_accuracy: 0.7202
Epoch 14/40
34/34 [==============================] - 1s 20ms/step - loss: 0.2275 - accuracy: 0.9235 - val_loss: 1.1052 - val_accuracy: 0.7202
Epoch 15/40
34/34 [==============================] - 1s 20ms/step - loss: 0.2179 - accuracy: 0.9330 - val_loss: 1.0366 - val_accuracy: 0.7110
Epoch 16/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1983 - accuracy: 0.9292 - val_loss: 1.0903 - val_accuracy: 0.7385
Epoch 17/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1713 - accuracy: 0.9377 - val_loss: 1.3037 - val_accuracy: 0.7110
Epoch 18/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1940 - accuracy: 0.9292 - val_loss: 1.1589 - val_accuracy: 0.7202
Epoch 19/40
34/34 [==============================] - 1s 20ms/step - loss: 0.2015 - accuracy: 0.9292 - val_loss: 1.2702 - val_accuracy: 0.7018
Epoch 20/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1575 - accuracy: 0.9490 - val_loss: 1.2435 - val_accuracy: 0.7385
Epoch 21/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1416 - accuracy: 0.9471 - val_loss: 1.2232 - val_accuracy: 0.7339
Epoch 22/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1247 - accuracy: 0.9566 - val_loss: 1.2568 - val_accuracy: 0.7248
Epoch 23/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1588 - accuracy: 0.9433 - val_loss: 1.0718 - val_accuracy: 0.7339
Epoch 24/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1610 - accuracy: 0.9481 - val_loss: 1.1252 - val_accuracy: 0.7294
Epoch 25/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1411 - accuracy: 0.9443 - val_loss: 1.1197 - val_accuracy: 0.7339
Epoch 26/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1213 - accuracy: 0.9528 - val_loss: 1.2259 - val_accuracy: 0.7248
Epoch 27/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1430 - accuracy: 0.9424 - val_loss: 1.3015 - val_accuracy: 0.7431
Epoch 28/40
34/34 [==============================] - 1s 21ms/step - loss: 0.1366 - accuracy: 0.9462 - val_loss: 1.2441 - val_accuracy: 0.7248
Epoch 29/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1114 - accuracy: 0.9547 - val_loss: 1.2918 - val_accuracy: 0.7064
Epoch 30/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1401 - accuracy: 0.9528 - val_loss: 1.1785 - val_accuracy: 0.7294
Epoch 31/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1131 - accuracy: 0.9500 - val_loss: 1.3899 - val_accuracy: 0.7339
Epoch 32/40
34/34 [==============================] - 1s 20ms/step - loss: 0.0988 - accuracy: 0.9518 - val_loss: 1.2865 - val_accuracy: 0.7202
Epoch 33/40
34/34 [==============================] - 1s 20ms/step - loss: 0.1059 - accuracy: 0.9547 - val_loss: 1.3489 - val_accuracy: 0.7248
Epoch 34/40
34/34 [==============================] - 1s 20ms/step - loss: 0.0967 - accuracy: 0.9556 - val_loss: 1.3415 - val_accuracy: 0.7248
Epoch 35/40
34/34 [==============================] - 1s 20ms/step - loss: 0.0912 - accuracy: 0.9556 - val_loss: 1.4693 - val_accuracy: 0.7294
Epoch 36/40
34/34 [==============================] - 1s 20ms/step - loss: 0.0957 - accuracy: 0.9528 - val_loss: 1.3250 - val_accuracy: 0.7339
Epoch 37/40
34/34 [==============================] - 1s 20ms/step - loss: 0.0939 - accuracy: 0.9556 - val_loss: 1.2677 - val_accuracy: 0.7431
Epoch 38/40
34/34 [==============================] - 1s 20ms/step - loss: 0.0819 - accuracy: 0.9622 - val_loss: 1.4405 - val_accuracy: 0.7248
Epoch 39/40
34/34 [==============================] - 1s 20ms/step - loss: 0.0956 - accuracy: 0.9594 - val_loss: 1.2428 - val_accuracy: 0.7477
Epoch 40/40
34/34 [==============================] - 1s 20ms/step - loss: 0.0847 - accuracy: 0.9651 - val_loss: 1.2296 - val_accuracy: 0.7202

9. Métricas

9.1. Gráfica de Loss vs Epochs

In [ ]:
plt.plot(history.history['loss'])
plt.title('Grafica Loss vs Epochs')
plt.ylabel('loss (perdida)')
plt.xlabel('epoch (iteraciones)')
plt.legend(['train', 'test'], loc='upper left')
plt.show()

9.2. Gráfica Accuracy vs Epochs

In [ ]:
# summarize history for accuracy
plt.plot(history.history['accuracy'])
plt.title('Grafica Accuracy vs Epochs')
plt.ylabel('accuracy (exactitud)')
plt.xlabel('epoch (iteraciones')
plt.legend(['train', 'test'], loc='upper left')
plt.show()

10. Se realiza el testeo.

10.1. Obtiene las probabilidades que cada imagen del test obtuvo para cada de las clases, luego toma el índice del valor máximo, correspondiente a la clase de la predicción.

In [ ]:
#Se realiza predicciones sobre las imagenes de testeo
predictions = model.predict(testX1)

#Se crea un arreglo con las predicciones arrojadas por el modelo
prediccion=[]
for i in predictions:
  prediccion.append(np.argmax(i))

10.2. Se crea la matriz de confusión y se obtienen las métricas de precisión, recall y f1-score para el test.

In [ ]:
matriz = confusion_matrix(testY1, prediccion, labels=[0, 1, 2,3,4])
y_pred = prediccion
y_true = testY1

print("\t\t\t MATRIZ DE CONFUSION")
print("\n\t\t\t\t[valor de prediccion]")
a = "\t\t\t"
for i in range(0, matriz.shape[0]):
  a = a + str(class_names[i])+"\t"
print(a)

for i in range(0, matriz.shape[0]):
  a = "\t\t%s\t"%(class_names[i])
  for j in range(0, matriz.shape[1]):
    a = a+ str(matriz[i][j]) + "\t"
  print(a)
print("   [valor real]")

print("\nLa precision fue de: "+str(precision_score(y_true, y_pred, average = "macro")))
print("El recall o sensibilidad fue de: "+str(recall_score(y_true, y_pred, average = "macro")))
print("El f1 score fue de: "+str(f1_score(y_true, y_pred, average = "macro")))
			 MATRIZ DE CONFUSION

				[valor de prediccion]
			ruta	urbana	montana	doble	no_es	
		ruta	52	6	5	0	2	
		urbana	6	69	5	1	0	
		montana	4	10	37	0	0	
		doble	3	2	1	3	0	
		no_es	2	6	1	1	10	
   [valor real]

La precision fue de: 0.7412980522011405
El recall o sensibilidad fue de: 0.6421350762527234
El f1 score fue de: 0.6749107329452158

10.3. Se obtienen las métricas de exactitud y pérdida para el test.

In [ ]:
#Evaluar la precisión

testX_np = np.array(testX1)
testY_np = np.array(testY1)
test_loss, test_acc = model.evaluate(testX_np, testY_np)

print('La exactitud o accuracy fue de:', test_acc)
print('La pérdida o loss fue de:', test_loss)
8/8 [==============================] - 0s 8ms/step - loss: 0.9297 - accuracy: 0.7566
La exactitud o accuracy fue de: 0.7566371560096741
La pérdida o loss fue de: 0.9296571016311646

11. Predicciones de imágenes fuera del test y del train set.

Se utilizó una imagen de una bicicleta que no se encontraba en el dataset para clasificarla por medio del modelo.

In [ ]:
# Resolucion
res = 100

# Nombre de la imagen
my_image = "doble3.png"

# Ruta de la imagen
fname = "/content/" + my_image

# Lectura de la imagen en rgb
image = cv2.imread(fname)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Cambio de tamano y normalizacion
resize_image = cv2.resize(image, (res, res))
resize_image = resize_image/255.

arregloMuestra = np.array([resize_image])
#my_image = resize_image.reshape(1, res*res*3).T

#Se realiza predicciones sobre las imagenes de testeo
predictions = model.predict(arregloMuestra)
clase_prediction = np.argmax(predictions[0])

plt.imshow(image)
#predic = np.squeeze(my_image_prediction)
print("El modelo predice: y = " + str(clase_prediction))
print(str(clase_prediction)+": bicicleta de tipo : "+str(class_names[clase_prediction]))
El modelo predice: y = 3
3: bicicleta de tipo : doble

11.1. Algunos ejemplos más:

In [ ]:
# Resolucion
res = 100
predicciones_prueba = []
numero = 0
for imagen in imagenes_prueba:
  # Cambio de tamano y normalizacion
  resize_image = cv2.resize(imagen, (res, res))
  resize_image = resize_image/255.

  arregloMuestra = np.array([resize_image])
  #my_image = resize_image.reshape(1, res*res*3).T

  #Se realiza predicciones sobre las imagenes de testeo
  predictions = model.predict(arregloMuestra)
  clase_prediction = np.argmax(predictions[0])
  predicciones_prueba.append(clase_prediction)

  numero+=1
  print("Para la imagen "+str(numero)+":")
  print("El modelo predice: y = " + str(clase_prediction))
  print(str(clase_prediction)+": bicicleta de tipo : "+str(class_names[clase_prediction]))
  plt.imshow(imagen)
  plt.show()
  #predic = np.squeeze(my_image_prediction)
Para la imagen 1:
El modelo predice: y = 4
4: bicicleta de tipo : no_es
Para la imagen 2:
El modelo predice: y = 2
2: bicicleta de tipo : montana
Para la imagen 3:
El modelo predice: y = 1
1: bicicleta de tipo : urbana
Para la imagen 4:
El modelo predice: y = 0
0: bicicleta de tipo : ruta
Para la imagen 5:
El modelo predice: y = 1
1: bicicleta de tipo : urbana
Para la imagen 6:
El modelo predice: y = 3
3: bicicleta de tipo : doble
Para la imagen 7:
El modelo predice: y = 1
1: bicicleta de tipo : urbana
Para la imagen 8:
El modelo predice: y = 0
0: bicicleta de tipo : ruta
Para la imagen 9:
El modelo predice: y = 4
4: bicicleta de tipo : no_es
Para la imagen 10:
El modelo predice: y = 1
1: bicicleta de tipo : urbana

CONCLUSIÓN

El clasificador de tipos de bicicletas presentado en este proyecto logra una exactitud del 75% con las imagenes de prueba, lo cual indica que el clasificador se puede considerar confiable. Sin embargo, este valor puede mejorar mucho para que el clasificador sea más confiable, con una exactitud de al menos el 90%.

A partir de las metricas se observan que esta un poco por encima del 60%, encontrando principalmente, a partir de la matriz de confusion, que los errores se dan por clasificar imagenes de un tipo al cual no pertenecen.

Se pueden considerar varias propuestas (las cuales no fueron implementadas por motivo del alcance del proyecto) para mejorar el clasificador, siendo la mas importante de estas aumentar el Dataset disponible debido a las siguientes razones:

-A pesar de haber tipos de bicicletas, estos vehiculos son muy similares entre si lo cual puede ser contraproducente para el CNN al momento de clasificar. Se recomienda mejorar el preprosesamiento de las imagenes con metodos de vision artificial y/o aumentar el dataset disponible

-En el dataset disponible no se contaba con cantidades similares de imagenes para cada tipo de bicicleta, lo cual no es recomendado para el entrenamiento de un CNN. Se recomienda aumentar la cantidad de imagenes en cada tipo y que tengan aproximadamente la misma cantidad.

-Muchas imagenes del dataset contenian "ruido", es decir, información no propia de la bicicleta, como son las personas, carros, paisajes, etc. Por lo tanto, se recomienda aumentar las imagenes que contengan bicicletas sin tanto ruido para mejorar el entrenamiento

Otras propuestas para mejorar el clasificador van desde mejorar los parametros, mejorar la regularización y un modelo de CNN mejor y mas adaptado a las necesidades del clasificador.

In [ ]: