Keras es una API de aprendizaje profundo de alto nivel. En clases anteriores estuvimos comparando a Keras con Tensorflow (el cual es una API de bajo nivel); asimismo, en clases anteriores programamos una red neuronal utilizando tensorflow y keras. A partir de esta clase veremos cómo programar una red neuronal utilizando plenamente la librería Keras.
Para ello considerares el siguiente conjunto de datos
import pandas as pd
data = pd.read_csv('DatosTumoresBM.csv')
data.head()
id | diagnosis | radius_mean | texture_mean | perimeter_mean | area_mean | smoothness_mean | compactness_mean | concavity_mean | concave points_mean | ... | texture_worst | perimeter_worst | area_worst | smoothness_worst | compactness_worst | concavity_worst | concave points_worst | symmetry_worst | fractal_dimension_worst | Unnamed: 32 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 842302 | M | 17.99 | 10.38 | 122.80 | 1001.0 | 0.11840 | 0.27760 | 0.3001 | 0.14710 | ... | 17.33 | 184.60 | 2019.0 | 0.1622 | 0.6656 | 0.7119 | 0.2654 | 0.4601 | 0.11890 | NaN |
1 | 842517 | M | 20.57 | 17.77 | 132.90 | 1326.0 | 0.08474 | 0.07864 | 0.0869 | 0.07017 | ... | 23.41 | 158.80 | 1956.0 | 0.1238 | 0.1866 | 0.2416 | 0.1860 | 0.2750 | 0.08902 | NaN |
2 | 84300903 | M | 19.69 | 21.25 | 130.00 | 1203.0 | 0.10960 | 0.15990 | 0.1974 | 0.12790 | ... | 25.53 | 152.50 | 1709.0 | 0.1444 | 0.4245 | 0.4504 | 0.2430 | 0.3613 | 0.08758 | NaN |
3 | 84348301 | M | 11.42 | 20.38 | 77.58 | 386.1 | 0.14250 | 0.28390 | 0.2414 | 0.10520 | ... | 26.50 | 98.87 | 567.7 | 0.2098 | 0.8663 | 0.6869 | 0.2575 | 0.6638 | 0.17300 | NaN |
4 | 84358402 | M | 20.29 | 14.34 | 135.10 | 1297.0 | 0.10030 | 0.13280 | 0.1980 | 0.10430 | ... | 16.67 | 152.20 | 1575.0 | 0.1374 | 0.2050 | 0.4000 | 0.1625 | 0.2364 | 0.07678 | NaN |
5 rows × 33 columns
en el cual se tiene la información de distintas medidas hechas a tumores y donde la columna de interés, variable objetivo o de respuesta, es diagnosis
que indica si el tumor es maligno M
o benigno B
.
Veamos cuántos elementos tenemos de cada clase:
data.diagnosis.value_counts()
B 357 M 212 Name: diagnosis, dtype: int64
Notamos además que la última columna tiene valores nulos, por lo cual la eliminaremos:
# Todas las entradas son nulas
data['Unnamed: 32'].unique()
array([nan])
# Realizamos la eliminacion de dicha columna
data = data.drop('Unnamed: 32', axis=1)
Además eliminaremos la primer columna pues no aporta informacion relevante
# Realizamos la eliminacion de dicha columna
data = data.drop('id', axis=1)
Y realizaremos el cambio de M
a 0 y de B
a 1, pues requerimos que la etiqueta a predecir sea numérica:
data['diagnosis'] = data.diagnosis.apply(lambda x: 1 if x == 'B' else 0)
data['diagnosis']
0 0 1 0 2 0 3 0 4 0 .. 564 0 565 0 566 0 567 0 568 1 Name: diagnosis, Length: 569, dtype: int64
Para poder hacer predicciones con la red neuronal que más adelante programaremos, requerirmos entrenar primero el modelo y después ver el desempeño de dicho modelo. Esto es, del conjunto de datos anterior requerimos un subconjunto de datos para realizar el entrenamiento de la red neuronal y requerimos un subconjunto para evaluar el poder predictivo de la red. Así, requerimos del conjunto de entrenamiento y prueba. Por lo general, del conjunto de datos completo, se toman el 75% de los datos para destinarlos al entrenamiento y el 25% restante para el conjunto de pruebas.
Cabe la pena mencionar que el conjunto de pruebas son datos que la red no ha visto, en lo cual radica que dicho conjunto nos sirva para evaluar el poder predictivo de la red. Después de evaluar la red, y una vez que hemos obtenido buenas métricas de evaluación (lo cual veremos más adelante), entonces nuestro modelo esta listo para hacer predicciones con nuevos datos. De tal manera, lo anterior lo podemos resumir en los siguientes pasos:
Lo que haremos entonces será realizar la división de los datos, pero primero debemos definir la variable X
correspondiente a las características y la variable y
corespondiente a la variable predictoria
# caracteristicas
X = data.drop('diagnosis', axis=1)
# variable objetivo
y = data['diagnosis']
Después será necesario convertir dichas variables a arrays. Tenemos dos, en principio, opciones para ello:
np.array()
values
que extraer la información de los dataframes como valores de un array.Lo que haremos será:
X = X.values
y = y.values
Luego veamos la forma del array y
:
print(y.shape)
print(y)
(569,) [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 1 1 1 0 0 1 0 0 1 1 1 1 0 1 0 0 1 1 1 1 0 1 0 0 1 0 1 0 0 1 1 1 0 0 1 0 0 0 1 1 1 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 0 0 1 0 0 1 1 1 0 0 1 0 1 0 0 1 0 0 1 1 0 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 0 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 1 0 0 0 0 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 1 1 1 0 0 1 1 0 1 1 0 0 1 0 1 1 1 1 0 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 1 0 1 1 0 1 1 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 1 0 0 0 1 1 1 1 0 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 1 0 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 1 1 0 1 1 0 1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 0 0 1 0 1 0 1 1 1 1 1 0 1 1 0 1 0 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1]
Usualmente se requiere que la variable de respuesta tenga la forma (n, 1)
. Notemos que la forma de y
es de (`569,)
por lo cual debemos utilizar reshape()
como sigue
y = y.reshape(-1,1)
y.shape
(569, 1)
Ahora sí ya podemos realizar la división de los datos:
# Importacion necesaria
from sklearn.model_selection import train_test_split
# Definimos 4 variables: 2 para el conjunto de entrenamiento
# y 2 para las pruebas
X_train, X_test, y_train, y_test = train_test_split(X, y, # conjunto completo
test_size=0.25, # 25% para pruebas
random_state=123) # semilla aleatoria
Una vez que hemos definidos los conjuntos de entrenamiento y pruebas, definiremos la arquitectura de la red. Como se mencionó al inicio, por lo general se trabajan con redes neuronales de dos capas ocultas. Luego, para la capa de salida de la red definiremos una neurona, pues sólo queremos predecir una valor (etiqueta), ya sea M o B.
Notemos cuántas características consideramos:
X.shape[1]
30
De tal manera, la red neuronal deberá de recibir 31 datos, esto es, la red neuronal tendrá 31 neuronas iniciales que recibirán dicha información. Con lo que tenemos hasta ahora, respecto a la arquitectura de la red, nos falta determinar el número de neuronas para las capas ocultas. No hay una estrategia precisa para definir ese número de neuronas, pero nos podemos ayudar ingresando, inicialmente, al menos el doble del número de entradas. Esto es, para la primer capa oculta consideraremos 62 neuronas. Luego, para la siguiente capa oculta disminuimos ese número de neuronas, digamos por ejemplo 40. Con base en ello:
# importaciones necesarias:
from keras.models import Sequential
from keras.layers import Dense
# Instanciamos el modelo
model = Sequential()
# Capa de entrada de 31 neuronas y capa oculta de 62 neuronas.
# Para la capa oculta utilizamos la funcion de activacion relu
model.add(Dense(62, activation='relu', input_shape=(30,)))
# Capa oculta 2:
# 40 neuronas y funcion de activacion relu
model.add(Dense(40, activation='relu'))
# Capa de salida. Dado que es un problema de clasificacion binaria
# utilizamos la funcion de activacion sigmoide
model.add(Dense(1, activation='sigmoid'))
Finalmente realizaremos la compilación del modelo, donde:
adam
;binary_crossentropy
pues estamos en un problema de clasificación binaria;metrics=['accuracy']
para observar a la hora de entrenar la precisión obtenida del modelo para cada época.De tal manera:
model.compile('adam', loss='binary_crossentropy', metrics=['accuracy'])
# Vemos un resumen sobre nuestro modelo
model.summary()
Model: "sequential_3" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_9 (Dense) (None, 62) 1922 dense_10 (Dense) (None, 40) 2520 dense_11 (Dense) (None, 1) 41 ================================================================= Total params: 4,483 Trainable params: 4,483 Non-trainable params: 0 _________________________________________________________________
Ahora, lo que haremos será realizar el entrenamiento del modelo, pero estaremos variando los valores de las épocas y los lotes. Cabe la pena mencionar que si configuramos un número alto de épocas, corremos peligro del sobreajuste. Luego, configuramos un número de lote para el entrenamiento para que la convergencia del "descenso del gradiente" sea más suave.
Así, realizaremos el entrenamiento de un primer modelo:
# epocas: 5
modelo_0 = model.fit(X_train, y_train, epochs=5)
Epoch 1/5 14/14 [==============================] - 1s 8ms/step - loss: 22.4257 - accuracy: 0.5000 Epoch 2/5 14/14 [==============================] - 0s 2ms/step - loss: 4.7998 - accuracy: 0.5141 Epoch 3/5 14/14 [==============================] - 0s 2ms/step - loss: 1.4431 - accuracy: 0.8122 Epoch 4/5 14/14 [==============================] - 0s 2ms/step - loss: 0.6079 - accuracy: 0.8826 Epoch 5/5 14/14 [==============================] - 0s 1ms/step - loss: 0.4495 - accuracy: 0.8967
Vemos cómo la precisión del modelo, en el entrenamiento, partió del 50% en la primer época y después llegó al 89% en la última. Veamos ahora la precisión del modelo anterior sobre el conjunto de pruebas:
scores = model.evaluate(X_test, y_test)
print('%s: %.4f%%' % (model.metrics_names[1], scores[1] * 100))
5/5 [==============================] - 0s 2ms/step - loss: 0.3015 - accuracy: 0.9231 accuracy: 92.3077%
Lo cual nos arroja una precisión del 92% en el conjunto de pruebas, lo cual nos deja en un muy buen modelo.
Para comparar los distintos resultados obtenidos de variar el número de épocas y el tamañio del lote, definiremos la siguiente función:
def model_result(num_epocas, num_lote):
"""num_epocas: número de épocas
num_lote: tamañio de los lotes"""
# Instanciamos el modelo
model = Sequential()
# Capa de entrada de 31 neuronas y capa oculta de 62 neuronas.
# Para la capa oculta utilizamos la funcion de activacion relu
model.add(Dense(62, activation='relu', input_shape=(30,)))
# Capa oculta 2:
# 40 neuronas y funcion de activacion relu
model.add(Dense(40, activation='relu'))
# Capa de salida. Dado que es un problema de clasificacion binaria
# utilizamos la funcion de activacion sigmoide
model.add(Dense(1, activation='sigmoid'))
# Compilacion
model.compile('adam', loss='binary_crossentropy', metrics=['accuracy'])
# Entrenamiento
modelo = model.fit(X_train, y_train, epochs=num_epocas,
batch_size=num_lote, verbose=False)
# Evaluacion sobre el conjunto de pruebas
scores = model.evaluate(X_test, y_test)
print('%s: %.4f%%' % (model.metrics_names[1], scores[1] * 100))
# Retornamos el modelo obtenido
return model
donde verbose=False
hace que no se muestre la información que se desplega en el entrenamiento.
Probaremos un modelo como model_0
pero agregando un número para el tamañio de los lotes:
model_1 = model_result(5, 32)
5/5 [==============================] - 0s 2ms/step - loss: 0.1736 - accuracy: 0.9371 accuracy: 93.7063%
Notamos una mejora en la precisión. Probemos más modelos:
# Aumentamos el numero de epocas
model_2 = model_result(10, 32)
5/5 [==============================] - 0s 2ms/step - loss: 0.2761 - accuracy: 0.9231 accuracy: 92.3077%
# Aumentamos el numero de epocas
model_3 = model_result(15, 32)
5/5 [==============================] - 0s 2ms/step - loss: 0.2720 - accuracy: 0.9091 accuracy: 90.9091%
Notamos que la precisión disminuye aumentando el número de épocas. En ese caso
# Aumentamos el batch_size
model_4 = model_result(5, 50)
5/5 [==============================] - 0s 1ms/step - loss: 0.4691 - accuracy: 0.8322 accuracy: 83.2168%
# Aumentamos el batch_size
model_5 = model_result(5, 40)
5/5 [==============================] - 0s 1ms/step - loss: 0.5381 - accuracy: 0.8671 accuracy: 86.7133%
# Disminuimos el batch_size
model_6 = model_result(5, 25)
5/5 [==============================] - 0s 2ms/step - loss: 0.5218 - accuracy: 0.9161 accuracy: 91.6084%
# Disminuimos el batch_size
model_6 = model_result(5, 15)
5/5 [==============================] - 0s 2ms/step - loss: 0.2255 - accuracy: 0.9371 accuracy: 93.7063%
# Disminuimos el batch_size
model_7 = model_result(5, 10)
5/5 [==============================] - 0s 2ms/step - loss: 0.2579 - accuracy: 0.9231 accuracy: 92.3077%
# Consideramos de nuevo un modelo sin batch_size
def model_result_2(num_epocas):
"""num_epocas: número de épocas
num_lote: tamañio de los lotes"""
# Instanciamos el modelo
model = Sequential()
# Capa de entrada de 31 neuronas y capa oculta de 62 neuronas.
# Para la capa oculta utilizamos la funcion de activacion relu
model.add(Dense(62, activation='relu', input_shape=(30,)))
# Capa oculta 2:
# 40 neuronas y funcion de activacion relu
model.add(Dense(40, activation='relu'))
# Capa de salida. Dado que es un problema de clasificacion binaria
# utilizamos la funcion de activacion sigmoide
model.add(Dense(1, activation='sigmoid'))
# Compilacion
model.compile('adam', loss='binary_crossentropy', metrics=['accuracy'])
# Entrenamiento
modelo = model.fit(X_train, y_train, epochs=num_epocas, verbose=False)
# Evaluacion sobre el conjunto de pruebas
scores = model.evaluate(X_test, y_test)
print('%s: %.4f%%' % (model.metrics_names[1], scores[1] * 100))
# Retornamos el modelo obtenido
return model
model_8 = model_result_2(5)
5/5 [==============================] - 0s 2ms/step - loss: 0.3034 - accuracy: 0.9231 accuracy: 92.3077%
# Aumentamos el numero de epocas
model_9 = model_result_2(10)
5/5 [==============================] - 0s 2ms/step - loss: 0.4106 - accuracy: 0.9091 accuracy: 90.9091%
# Aumentamos el numero de epocas
model_10 = model_result_2(7)
5/5 [==============================] - 0s 2ms/step - loss: 0.2106 - accuracy: 0.9231 accuracy: 92.3077%
# Aumentamos el numero de epocas
model_11 = model_result_2(6)
5/5 [==============================] - 0s 2ms/step - loss: 0.3800 - accuracy: 0.9441 accuracy: 94.4056%
# Disminuimos el numero de epocas
model_12 = model_result_2(4)
5/5 [==============================] - 0s 2ms/step - loss: 0.2157 - accuracy: 0.9161 accuracy: 91.6084%
Después de las pruebas anteriores, elegimos el modelo model_11
el cual nos arrojo el mayor número de precisión.
Veamos en una tabla qué tan bueno fue nuestro modelo:
# dataframe con las etiquetas reales
df_prueba = pd.DataFrame(y_test).rename(columns={0:'y_test'})
df_prueba.head()
y_test | |
---|---|
0 | 1 |
1 | 1 |
2 | 0 |
3 | 1 |
4 | 0 |
Después, utilizando X_test
realizaremos la predicción de las etiquetas para esos datos y los compararemos con los valores de y_test
# Prediccion
y_pred = model_11.predict(X_test)
y_pred
5/5 [==============================] - 0s 3ms/step
array([[9.99629617e-01], [9.99710381e-01], [8.63962322e-02], [9.99994159e-01], [1.06726653e-13], [9.82931316e-01], [9.99853730e-01], [8.69353831e-01], [9.23000634e-01], [9.98036981e-01], [9.99999821e-01], [6.58910843e-11], [1.39187932e-01], [9.99969900e-01], [9.99986172e-01], [8.55791092e-01], [9.99936879e-01], [9.99996364e-01], [9.99983430e-01], [9.96641040e-01], [5.17817876e-18], [1.45146308e-11], [9.95960355e-01], [9.99478638e-01], [9.95986521e-01], [2.79043252e-05], [7.43328656e-06], [9.99994695e-01], [2.23713811e-10], [9.99969661e-01], [1.44468842e-03], [9.99979615e-01], [9.99978840e-01], [9.93004739e-01], [1.79321960e-05], [9.99913216e-01], [7.41055906e-01], [9.99977231e-01], [9.99983430e-01], [2.59015195e-07], [6.09234674e-04], [9.98266339e-01], [2.06825760e-07], [4.41129774e-01], [1.82594045e-14], [7.95789003e-01], [3.57348142e-08], [7.70056587e-08], [9.99994218e-01], [4.49250592e-08], [2.59436399e-01], [0.00000000e+00], [9.95823324e-01], [9.98494148e-01], [9.99994695e-01], [9.99935687e-01], [9.99867558e-01], [1.26144720e-19], [6.67011208e-37], [9.99928296e-01], [8.93563703e-02], [9.99996006e-01], [9.89149690e-01], [9.99961019e-01], [9.98798788e-01], [3.20648524e-17], [9.99981463e-01], [9.99999106e-01], [9.99881923e-01], [9.99541104e-01], [9.99997020e-01], [9.99970257e-01], [3.35543789e-02], [9.99927998e-01], [4.28891921e-13], [9.99928653e-01], [9.99396384e-01], [2.25313717e-32], [1.15472794e-07], [2.31203786e-03], [9.97031808e-01], [6.58919974e-10], [4.03703347e-18], [9.97907400e-01], [9.99956071e-01], [9.99839306e-01], [5.98823503e-02], [9.99967515e-01], [4.08994838e-09], [9.99979258e-01], [1.05037849e-07], [9.99986053e-01], [9.99973595e-01], [6.71994239e-02], [9.99994338e-01], [9.99991536e-01], [9.97489572e-01], [9.99567568e-01], [9.99318659e-01], [9.99916971e-01], [9.99982297e-01], [9.99883890e-01], [9.99913454e-01], [9.99994218e-01], [9.97247100e-01], [9.99717712e-01], [9.99998212e-01], [9.99969602e-01], [9.99924541e-01], [9.99984920e-01], [9.99981403e-01], [9.99999166e-01], [9.99251842e-01], [1.17731209e-24], [9.99187887e-01], [1.39396871e-12], [9.99987364e-01], [2.43601309e-13], [9.99993682e-01], [9.99853492e-01], [9.99971688e-01], [9.99484539e-01], [9.99991179e-01], [9.99991298e-01], [2.51834322e-26], [9.99913633e-01], [2.91506467e-07], [8.73163819e-01], [9.95315373e-01], [9.99803483e-01], [0.00000000e+00], [9.99996424e-01], [9.99275982e-01], [1.33840206e-09], [2.76559358e-03], [9.99956846e-01], [9.99996364e-01], [9.99398947e-01], [6.18399419e-02], [1.70473269e-09], [7.02149493e-12], [9.99543250e-01], [0.00000000e+00]], dtype=float32)
Notamos que se han predicho probabilidades, lo cual tiene sentido pues la función de activación en la última capa fue la sigmoide. Luego, podemos redondear esos valores y los agregamos al dataframe df_pruebas
:
df_prueba['y_pred'] = y_pred.round()
df_prueba.head()
y_test | y_pred | |
---|---|---|
0 | 1 | 1.0 |
1 | 1 | 1.0 |
2 | 0 | 0.0 |
3 | 1 | 1.0 |
4 | 0 | 0.0 |
df_prueba['y_pred'] = df_prueba.y_pred.apply(lambda x: int(x))
df_prueba.head()
y_test | y_pred | |
---|---|---|
0 | 1 | 1 |
1 | 1 | 1 |
2 | 0 | 0 |
3 | 1 | 1 |
4 | 0 | 0 |
Realizamos una resta entre esas dos columnas, si obtenemos cero entonces la predicción ha sido correcta, caso contrario la predicción ha errado
df_prueba['diff'] = df_prueba.y_test - df_prueba.y_pred
df_prueba.head()
y_test | y_pred | diff | |
---|---|---|---|
0 | 1 | 1 | 0 |
1 | 1 | 1 | 0 |
2 | 0 | 0 | 0 |
3 | 1 | 1 | 0 |
4 | 0 | 0 | 0 |
Veamos el número de aciertos (ceros) y el número de errores
df_prueba.value_counts('diff')
diff 0 135 -1 7 1 1 dtype: int64
El modelo ha errado 8 veces y ha acertado 135, lo cual representa:
total = 135 + 8
# Mostramos el porcentaje de aciertos redondeado a 4 decimales
print(f'Porcentaje de aciertos: {round(135 / total, 4)}%')
Porcentaje de aciertos: 0.9441%
La cual es justamente la precisión que habíamos obtenido.
Finalmente regresamos a las etiquetas M y B en vez de ceros y unos
df_prueba['y_test']= df_prueba.y_test.apply(lambda x: 'B' if x == 1 else 'M')
df_prueba['y_pred']= df_prueba.y_pred.apply(lambda x: 'B' if x == 1 else 'M')
df_prueba.head()
y_test | y_pred | diff | |
---|---|---|---|
0 | B | B | 0 |
1 | B | B | 0 |
2 | M | M | 0 |
3 | B | B | 0 |
4 | M | M | 0 |
data.value_counts('diagnosis')
diagnosis 1 357 0 212 dtype: int64
Podríamos manejar un mismo número de muestras para la etiqueta 1 y la etiqueta 0, para que sea más equilibrado el entrenamiento, para ello:
# tomamos una muestra aleatoria de 200 filas para cada categoria
data_1 = data[data.diagnosis == 1].sample(200)
data_0 = data[data.diagnosis == 0].sample(200)
# unimos esos dataframes en uno solo
data_muestra = pd.concat([data_0, data_1])
data_muestra.head()
diagnosis | radius_mean | texture_mean | perimeter_mean | area_mean | smoothness_mean | compactness_mean | concavity_mean | concave points_mean | symmetry_mean | ... | radius_worst | texture_worst | perimeter_worst | area_worst | smoothness_worst | compactness_worst | concavity_worst | concave points_worst | symmetry_worst | fractal_dimension_worst | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
372 | 0 | 21.37 | 15.10 | 141.30 | 1386.0 | 0.10010 | 0.1515 | 0.19320 | 0.12550 | 0.1973 | ... | 22.69 | 21.84 | 152.1 | 1535.0 | 0.1192 | 0.2840 | 0.4024 | 0.1966 | 0.2730 | 0.08666 |
202 | 0 | 23.29 | 26.67 | 158.90 | 1685.0 | 0.11410 | 0.2084 | 0.35230 | 0.16200 | 0.2200 | ... | 25.12 | 32.68 | 177.0 | 1986.0 | 0.1536 | 0.4167 | 0.7892 | 0.2733 | 0.3198 | 0.08762 |
16 | 0 | 14.68 | 20.13 | 94.74 | 684.5 | 0.09867 | 0.0720 | 0.07395 | 0.05259 | 0.1586 | ... | 19.07 | 30.88 | 123.4 | 1138.0 | 0.1464 | 0.1871 | 0.2914 | 0.1609 | 0.3029 | 0.08216 |
70 | 0 | 18.94 | 21.31 | 123.60 | 1130.0 | 0.09009 | 0.1029 | 0.10800 | 0.07951 | 0.1582 | ... | 24.86 | 26.58 | 165.9 | 1866.0 | 0.1193 | 0.2336 | 0.2687 | 0.1789 | 0.2551 | 0.06589 |
23 | 0 | 21.16 | 23.04 | 137.20 | 1404.0 | 0.09428 | 0.1022 | 0.10970 | 0.08632 | 0.1769 | ... | 29.17 | 35.59 | 188.0 | 2615.0 | 0.1401 | 0.2600 | 0.3155 | 0.2009 | 0.2822 | 0.07526 |
5 rows × 31 columns
Otro punto a mejor es realizar un escalado de las características. Notemos que los valores de las distintas columnas son, algunas, muy distantes entre sí, por lo cual algunas características tienen más peso sobre otras. Así, lo que haremos será llevar todos esos número mediante una transformación al intervalo $(0,1)$ como sigue
# Importacion necesaria
from sklearn.preprocessing import MinMaxScaler
# Instanciamos y configuramos
sc = MinMaxScaler(feature_range = (0,1))
# caracteristicas
X = data_muestra.drop('diagnosis', axis=1)
# variable objetivo
y = data_muestra['diagnosis']
# Conversion a arrays
X = X.values
y = y.values
# Escalado sobre la variable X
X = sc.fit_transform(X)
Después realizamos la división
X_train, X_test, y_train, y_test = train_test_split(X, y, # conjunto completo
test_size=0.25, # 25% para pruebas
random_state=123, # semilla aleatoria
shuffle=True)
donde shuffle=True
mezcla los datos antes de dividirlos.
model_11 = model_result_2(15)
4/4 [==============================] - 0s 2ms/step - loss: 0.2298 - accuracy: 0.9000 accuracy: 90.0000%
model_11 = model_result(15, 32)
4/4 [==============================] - 0s 3ms/step - loss: 0.2570 - accuracy: 0.8900 accuracy: 89.0000%
model_12 = model_result(15, 12)
4/4 [==============================] - 0s 2ms/step - loss: 0.1905 - accuracy: 0.9200 accuracy: 92.0000%
model_13 = model_result(15, 7)
4/4 [==============================] - 0s 2ms/step - loss: 0.2022 - accuracy: 0.9200 accuracy: 92.0000%
model_14 = model_result(25, 7)
4/4 [==============================] - 0s 2ms/step - loss: 0.2491 - accuracy: 0.9300 accuracy: 93.0000%
model_15 = model_result(30, 7)
4/4 [==============================] - 0s 3ms/step - loss: 0.2175 - accuracy: 0.9400 accuracy: 94.0000%
model_16 = model_result(30, 12)
4/4 [==============================] - 0s 2ms/step - loss: 0.1954 - accuracy: 0.9400 accuracy: 94.0000%
model_17 = model_result(30, 20)
4/4 [==============================] - 0s 2ms/step - loss: 0.1682 - accuracy: 0.9500 accuracy: 95.0000%
model_18 = model_result(45, 32)
4/4 [==============================] - 0s 2ms/step - loss: 0.1774 - accuracy: 0.9500 accuracy: 95.0000%
model_19 = model_result(45, 40)
4/4 [==============================] - 0s 3ms/step - loss: 0.1897 - accuracy: 0.9400 accuracy: 94.0000%
model_20 = model_result(45, 32)
4/4 [==============================] - 0s 1ms/step - loss: 0.1918 - accuracy: 0.9500 accuracy: 95.0000%
Con lo cual no hemos conseguido una mejora significativa del 1%.