Problema de clasificación¶

Consideremos los siguientes datos:

In [1]:
# Importamos una libreria para geenrar datos aleatorios
import random as rd
# Generamos numeros aleatorios distribuidos uniformemente del -1.5 al 1.5
nums = [(rd.uniform(-1.5,1.5), rd.uniform(-1.5, 1.5)) for i in range(2000)]
# Seleccionamos aquello que caen dentro del circulo de radio 0.5
circ = [num for num in nums if (num[0]**2 + num[1]**2) < 0.5]
# Seleccionamos otro conjunto de puntos
no_circ = [num for num in nums 
           if ((num[0]**2 + num[1]**2) >= 0.8) and ((num[0]**2 + num[1]**2) < 1.6)]

Procedemos a graficar nuestro datos

In [2]:
import matplotlib.pyplot as plt

# Graficamos
for i in range(len(circ)):
    plt.plot(circ[i][0], circ[i][1], marker="o", color="#0208FF")
for i in range(len(no_circ)):
    plt.plot(no_circ[i][0], no_circ[i][1], marker="o", color="#FF0202")
plt.show()
In [3]:
print(len(circ))
print(len(no_circ))
print(len(circ) + len(no_circ))
336
546
882

Convertiremos a arrays las listas circ y no_circ y los uniremos en un solo array

In [4]:
import numpy as np
circ = np.array(circ)
no_circ = np.array(no_circ)
circ_no_circ = np.concatenate((circ, no_circ), axis=0)

Creamos otro array con el mismo número de filas que el array circ_no_circ pero con una columna y lleno de puros ceros

In [5]:
target = np.zeros((circ_no_circ.shape[0],1))
target.shape
Out[5]:
(882, 1)

Ahora, si el punto está dentro del círculo pequeño (el de color azul) le daremos la etiqueta 1, si está fuera de dicho círculo le daremos la etiqueta 0:

In [6]:
for i in range(target.shape[0]):
    if ((circ_no_circ[i][0]**2) + (circ_no_circ[i][1]**2)) < 0.5:
        target[i][0] = 1
    else:
        target[i][0] = 0

Veamos algunas comprobaciones

In [7]:
print(circ_no_circ[0])
print(target[0])
print()

print(circ_no_circ[6])
print(target[6])
print()

print(circ_no_circ[520])
print(target[520])
[-0.10088805 -0.64931994]
[1.]

[-0.02612075 -0.56487313]
[1.]

[ 0.09082121 -1.22887421]
[0.]
In [8]:
# sumamos los unos
target.sum()
Out[8]:
336.0

que, en efecto, nos dan el número de puntos totales dentro del círculo de radio 0.5

Vemos entonces que tenemos un problema de clasificación binaria, pues así fue como diseñamos los datos. Pero clasificaremos de nuevo los puntos usando ahora redes neuronales, para ello consideraremos:

redes1.PNG

redes2.PNG

redes3.PNG

Por lo que nuestra red neuronal tendrá dos nodos de entrada (una para cada coordenada de los puntos), una capa oculta con cuatro nodos (el número de nodos propuesto fue obtenido mediante experimentación) y la capa de salida con un nodo (pues recordemos que la salida nos indicará si un valor pertenece a un grupo o no) que tendrá asociada la función de activación sigmoide. La función sigmoide nos arrojará 0 para números obtenido menores al umbral (por defecto el umbral vale 0.5), y 1 para números mayores o iguales al umbral.

Procedemos a crear nuestro modelo en keras:

In [9]:
# Realizamos las importaciones necesarias
from keras.models import Sequential
from keras.layers import Dense

# Fijamos una semilla
np.random.seed(123)
In [10]:
# Instanciamos el modelo
model = Sequential()

# Capa de cuatro neuronas y definimos una forma de entrada
# que consta de 2 neuronas.
# La funcion de activacion sera la tangente hiperbolica
model.add(Dense(4, activation='tanh', input_shape=(2,) ))

# Capa de salida con la funcion de activacion sigmoide
model.add(Dense(1, activation='sigmoid'))

# Copilacion del modelo.
# El optimizador sera: descenso del gradiente estocastico
model.compile(optimizer='sgd', loss='binary_crossentropy', metrics=['accuracy'])
In [11]:
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense (Dense)               (None, 4)                 12        
                                                                 
 dense_1 (Dense)             (None, 1)                 5         
                                                                 
=================================================================
Total params: 17
Trainable params: 17
Non-trainable params: 0
_________________________________________________________________

Separamos nuestros datos: datos de entrenamiento y datos de prueba

In [12]:
from sklearn.model_selection import train_test_split

# Puntos
X = circ_no_circ
# Etiquetas
y = target

# Division de los datos
X_train, X_test, y_train, y_test = train_test_split(
                                        X,
                                        y,
                                        train_size   = 0.75,
                                        random_state = 123)
In [13]:
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(661, 2)
(221, 2)
(661, 1)
(221, 1)

Procedemos a realizar el ajuste del modelo configurando 1000 épocas

In [15]:
# Ajustamos el modelo
history = model.fit(X_train, y_train, epochs=1000, 
          verbose=0,
          validation_data=(X_test, y_test))

Vemos la evaluación de nuestro modelo sobre los datos de prueba

In [16]:
scores = model.evaluate(X_test, y_test)
print('%s: %.4f%%' % (model.metrics_names[1], scores[1] * 100))
7/7 [==============================] - 0s 1ms/step - loss: 0.1563 - accuracy: 0.9593
accuracy: 95.9276%
In [17]:
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Precisión del modelo')
plt.xlabel('Epoch')
plt.ylabel('Precisión')
plt.legend(['Train', 'Test'])
plt.show()

y notamos que hemos obtenido un buen modelo.

Realizaremos predicciones sobre nuestros datos de prueba:

In [20]:
predicciones = model.predict(X_test).round()
7/7 [==============================] - 0s 3ms/step

Compararemos el valor obtenido de la predicción versus el valor real:

In [18]:
import pandas as pd

# Creamos un dataframe vacio
dict1 = {'Predicción': [0 for i in range(X_test.shape[0])], 
         'Valor real': [0 for i in range(X_test.shape[0])]}
df = pd.DataFrame(dict1)
df
Out[18]:
Predicción Valor real
0 0 0
1 0 0
2 0 0
3 0 0
4 0 0
... ... ...
216 0 0
217 0 0
218 0 0
219 0 0
220 0 0

221 rows × 2 columns

In [21]:
# Llenamos el dataframe
for i in range(X_test.shape[0]):
    df.iloc[i][0] = predicciones[i][0]
    df.iloc[i][1] = y_test[i]
# Comparamos
df
Out[21]:
Predicción Valor real
0 0 0
1 0 0
2 0 0
3 0 0
4 0 0
... ... ...
216 0 0
217 1 1
218 0 0
219 1 1
220 0 0

221 rows × 2 columns

Nuestra red neuronal sólo se equivoco 12 veces. Veamos cómo se ven los puntos clasificados de acuerdo a a la predicción que realizamos sobre los datos de prueba:

In [22]:
# Graficamos
for i in range(X_test.shape[0]):
    # si la etiqueta es 1, el color es azul
    if predicciones[i][0] == 1:
        plt.plot(X_test[i][0], X_test[i][1], marker="o", color="#0208FF")
    # Los puntos dentro del circulo pequenio mal clasificados:
    elif predicciones[i][0] == 0 and (X_test[i][0]**2 + X_test[i][1]**2 < 0.5):
        plt.plot(X_test[i][0], X_test[i][1], marker="o", color="#FFE902")
    # si la etiqueta es 0, el color es rojo
    else:
        plt.plot(X_test[i][0], X_test[i][1], marker="o", color="#FF0202")
plt.show()