Auxiliar Clasificación
Profesor: Richard Weber & Raimundo Undurraga
Auxiliar: Matías Sepúlveda
IN5244-1 - Ciencia de los Datos
Primavera 2024
Auxiliar: Matías Sepúlveda
IN5244-1 - Ciencia de los Datos
Primavera 2024
Primavera 2024
Los métodos de clasificación pertenecen a la categoría de Aprendizaje Supervisado en Machine Learning.
Los modelos se entrenan utilizando un conjunto etiquetado, es decir, cada entrada del conjunto de entrenamiento viene acompañada de una etiqueta o clase que indica su resultado esperado. El modelo aprende a reconocer estos patrones que diferencia a las clases, y una vez entrenado, puede predecir la clase de nuevas entradas.
Algunos conceptos clave de estos modelo son los siguientes:
- Clases: son las posibles categorías o grupos en los que se pueden clasificar los datos.
- Características: son las variables que se utilizan para realizar predicciones sobre las clases.
- Límites de decisión: son las fronteras que separan las diferentes clases en el espacio de características.
Generalmente, se usan los siguientes tipos de clasificación:
- Clasificación Binaria: Se clasifica en dos categorías.
- Clasificación Multiclase: Se clasifica en más de dos categorías.
- Clasificación Multietiqueta: Un dato se clasifica en más de una categoría.
Ejemplos de Problemas de Clasificación¶
- Detección de Spam
- Detección de Fraude
- Diagnóstico Médico
- Reconocimiento de Imágenes
Métodos de Clasificación¶
Existen varios tipos de algoritmos de clasificación, y cada uno posee sus ventajas y desventajas. Algunos de los algoritmos más comunes son:
Regresión Logística: se utiliza para clasificación, no de regresión. Predice la probabilidad de que una determinada entrada pertenezca a una clase en particular.
Árboles de Decisión: dividen los datos en subconjuntos según el valor de las características. Cada nodo del árbol representa una regla de decisión y cada nodo de hoja representa una etiqueta de clase.
Random Forest: es un conjunto de árboles de decisión. Cada árbol en el bosque vota por la clase predicha. La clase con más votos se elige como la predicción final. Es más preciso que un solo árbol de decisión, y menos propenso al sobreajuste.
Support Vector Machine (SVM): encuentra un hiperplano óptimo que maximiza el margen entre las diferentes clases. Es útil en espacios de alta dimensión y es eficaz en problemas de clasificación binaria.
K-Nearest Neighbors (KNN): clasifica nuevos puntos de datos basándose en los puntos de datos que están más cerca de él.
Algunos de estos algoritmos fueron diseñados especificamente para clasificación binaria, y de forma nativa no admiten más de dos tipos de clases. Por ejemplo el SVM y la Regresión Logística.
Métodos de Evaluación de Modelos¶
Cuando los modelos de clasificación están entrenados, se debe evaluar su rendimiento. Para esto, se utilizan métricas que permiten medir la calidad de las predicciones. Algunas de las métricas más comunes son:
Accuracy: mide la proporción de predicciones correctas, del total de instancias.
Precision: es la proporción de verdaderos positivos con respecto a todos los casos que el modelo clasificó como positivos.
Recall: es la proporción de verdaderos positivos que el modelo identifica correctamente en relación con todos los positivos reales en el conjunto de datos.
F1 Score: es la media armónica entre Precision y Recall.
Matriz de Confusión: es una tabla que muestra las predicciones correctas e incorrectas de un modelo.
Tambiés podemos usar los que es Curva ROC y AUC, que nos permite evaluar el rendimiento de un clasificador binario.
Cada uno tiene su utilidad dependiendo del problem y objetivo que tengamos:
Si queremos detectar fraude por ejemplo, sería bueno tener una buena Precisión, y sería muy importante tratar de reducir la cantidad de falsos positivos.
En caso de querer diagnosticar bien una enfermedad, sería importante tener un buen Recall, para no dejar pasar casos positivos.
Ejemplo de una matríz de confusión:
Importancia de la clasificación:
Es util en diferentes ámbitos, como pueden ser la medicina, la economía, la biología, etc. Por ejemplo, en medicina se puede utilizar para predecir si un paciente tiene una enfermedad o no, en economía para predecir si una empresa va a quebrar o no, en biología para predecir si una especie de planta es venenosa o no, etc. Su aplicación en estos ámbitos puede ser de gran ayuda para tomar decisiones importantes, y prevenir problemas futuros.
Se recomienda considerar los siguientes pasos para realizar una clasificación:¶
- Identificar el problema
- Analizar los datos
- Preparar los datos
- Algoritmo de clasificación
- Mejora de los resultados
- Presentación de los resultados
Ejemplo: Caso de estudio de Cancer de Mama¶
Por temas de interés en la salud, se utilizará un dataset que contiene información sobre el cáncer de mama. El objetivo es predecir si un tumor es benigno o maligno, utilizando diferentes características de los tumores.
Este conjunto de datos es bastante antiguo, se publicó originalmente en los 90s, y tiene el reconocimiento de ser uno de los primeros hitos de la IA.
#Importar librerías
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
#Lectura del dataset
dataset = pd.read_csv('data.csv')
#Fijamos nuestra variables objetivo, diagnosis, y nuestras variables independientes, radius_mean y texture_mean, que las usaremos para predecir.
X = dataset.iloc[:, 2:4].values
y = dataset.iloc[:, 1].values
dataset.head(5)
| 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
# Contar las opciones de la variable diagnosis
diagnosis_counts = dataset['diagnosis'].value_counts()
print(diagnosis_counts)
diagnosis 0 357 1 212 Name: count, dtype: int64
Como ven, para este caso existe un desbalance entre las clases, pero quiero que revisemos el ejemplo sin balancear. Luego, los invito a que ustedes comparen los resultados.
dataset.isnull().sum()
id 0 diagnosis 0 radius_mean 0 texture_mean 0 perimeter_mean 0 area_mean 0 smoothness_mean 0 compactness_mean 0 concavity_mean 0 concave points_mean 0 symmetry_mean 0 fractal_dimension_mean 0 radius_se 0 texture_se 0 perimeter_se 0 area_se 0 smoothness_se 0 compactness_se 0 concavity_se 0 concave points_se 0 symmetry_se 0 fractal_dimension_se 0 radius_worst 0 texture_worst 0 perimeter_worst 0 area_worst 0 smoothness_worst 0 compactness_worst 0 concavity_worst 0 concave points_worst 0 symmetry_worst 0 fractal_dimension_worst 0 Unnamed: 32 569 dtype: int64
No existen valores nulos
#Codificamos la variabble diagnosis dado que contiene texto
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y = le.fit_transform(y)
# Ver el dataset después de la codificación
dataset['diagnosis'] = y
dataset.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 | 1 | 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 | 1 | 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 | 1 | 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 | 1 | 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 | 1 | 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
Ahora Maligno es 1, Benigno es 0.
#Dividimos el dataset en training y test
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
Como se puede ver en el dataset, tenemos diferentes escalas de medición de las características de los tumores. Por lo tanto, se hará un escalado de las caracterictas para que todas tengan la misma importancia en el modelo.
# Feature Scaling
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
Entrenaremos varios Modelos¶
Para entrenar los modelos usaremos dos características solamente, para visualizar como se comporta cada uno. Usaremos: mean_texture and mean_radius
Regresión Logística¶
#Entrenar modelo
from sklearn.linear_model import LogisticRegression
classifier = LogisticRegression(random_state = 0)
classifier.fit(X_train, y_train)
LogisticRegression(random_state=0)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LogisticRegression(random_state=0)
y_pred = classifier.predict(X_test)
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
import seaborn as sns
cm = confusion_matrix(y_test, y_pred)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print('Accuracy = ' + str(accuracy))
print('Precision = ' + str(precision))
print('Recall = ' + str(recall))
print('F1 Score = ' + str(f1))
print('Confusion Matrix:')
print(cm)
plt.figure(figsize=(10, 7))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title('Confusion Matrix')
plt.xlabel('Prediction')
plt.ylabel('Label')
plt.show()
Accuracy = 0.8947368421052632 Precision = 0.8888888888888888 Recall = 0.851063829787234 F1 Score = 0.8695652173913043 Confusion Matrix: [[62 5] [ 7 40]]
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# Función para asignar etiquetas
def Label(val):
return 'Malignant' if val == 0 else 'Benign'
# Configurar estilos de Seaborn
sns.set_style('whitegrid')
sns.set_context('notebook')
# Restaurar los datos escalados y preparar el grid
X_set, y_set = sc.inverse_transform(X_test), y_test
X1, X2 = np.meshgrid(
np.arange(start=X_set[:, 0].min() - 10, stop=X_set[:, 0].max() + 10, step=0.15),
np.arange(start=X_set[:, 1].min() - 1000, stop=X_set[:, 1].max() + 1000, step=0.25)
)
# Predicción en eñ grid
Z = classifier.predict(sc.transform(np.array([X1.ravel(), X2.ravel()]).T)).reshape(X1.shape)
# Crear el gráfico con Seaborn
plt.figure(figsize=(10, 6))
cmap = ListedColormap(['#FF9999', '#99FF99']) # Colores mejorados
sns.scatterplot(
x=X_set[:, 0], y=X_set[:, 1],
hue=[Label(y) for y in y_set], palette=['green', 'red'],
style=y_set, markers=['o', 's'],
s=100, alpha=0.7, edgecolor='k', legend='full'
)
# Agregar el contorno del clasificador
plt.contourf(X1, X2, Z, alpha=0.3, cmap=cmap)
# Ajustar los detalles del gráfico
plt.title('Logistic Regression (Decision Boundary)', fontsize=14)
plt.xlabel('Mean-Texture', fontsize=12)
plt.ylabel('Mean-Radius', fontsize=12)
plt.legend(loc='upper left', title='Classes')
plt.axis([5, 25, 0, 50])
plt.tight_layout()
# Mostrar el gráfico
plt.show()
Árboles de Decisión¶
from sklearn.tree import DecisionTreeClassifier
classifier = DecisionTreeClassifier(criterion = 'entropy', random_state = 0, max_depth=3, min_samples_split=0.8)
classifier.fit(X_train, y_train)
DecisionTreeClassifier(criterion='entropy', max_depth=3, min_samples_split=0.8,
random_state=0)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier(criterion='entropy', max_depth=3, min_samples_split=0.8,
random_state=0)y_pred = classifier.predict(X_test)
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
import seaborn as sns
cm = confusion_matrix(y_test, y_pred)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print('Accuracy = ' + str(accuracy))
print('Precision = ' + str(precision))
print('Recall = ' + str(recall))
print('F1 Score = ' + str(f1))
print('Confusion Matrix:')
print(cm)
plt.figure(figsize=(10, 7))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title('Confusion Matrix')
plt.xlabel('Prediction')
plt.ylabel('Label')
plt.show()
Accuracy = 0.8859649122807017 Precision = 0.9722222222222222 Recall = 0.7446808510638298 F1 Score = 0.8433734939759037 Confusion Matrix: [[66 1] [12 35]]
# Función para asignar etiquetas
def Label(val):
return 'Malignant' if val == 0 else 'Benign'
# Configurar estilo de Seaborn
sns.set_style('whitegrid')
sns.set_context('notebook')
# Restaurar datos escalados y preparar el grid
X_set, y_set = sc.inverse_transform(X_test), y_test
X1, X2 = np.meshgrid(
np.arange(start=X_set[:, 0].min() - 10, stop=X_set[:, 0].max() + 10, step=0.15),
np.arange(start=X_set[:, 1].min() - 1000, stop=X_set[:, 1].max() + 1000, step=0.25)
)
# Predicción en el grid
Z = classifier.predict(sc.transform(np.array([X1.ravel(), X2.ravel()]).T)).reshape(X1.shape)
# Crear el gráfico con Seaborn
plt.figure(figsize=(10, 6))
cmap = ListedColormap(['#FFCCCC', '#CCFFCC']) # Colores suaves para las áreas de clasificación
# Fondo con las regiones de decisión
plt.contourf(X1, X2, Z, alpha=0.3, cmap=cmap)
# Puntos de datos
sns.scatterplot(
x=X_set[:, 0], y=X_set[:, 1],
hue=[Label(y) for y in y_set], palette=['green', 'red'],
style=y_set, markers=['o', 's'],
s=100, alpha=0.7, edgecolor='k', legend='full'
)
# Ajustar detalles del gráfico
plt.title('Decision Tree Classification (Decision Boundary)', fontsize=14)
plt.xlabel('Mean-Texture', fontsize=12)
plt.ylabel('Mean-Radius', fontsize=12)
plt.axis([5, 25, 0, 50])
plt.legend(loc='upper left', title='Classes')
plt.tight_layout()
# Mostrar el gráfico
plt.show()
Random Forest¶
from sklearn.ensemble import RandomForestClassifier
classifier = RandomForestClassifier(n_estimators = 20, criterion = 'entropy', random_state = 0, max_depth=5)
classifier.fit(X_train, y_train)
RandomForestClassifier(criterion='entropy', max_depth=5, n_estimators=20,
random_state=0)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestClassifier(criterion='entropy', max_depth=5, n_estimators=20,
random_state=0)y_pred = classifier.predict(X_test)
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
import seaborn as sns
cm = confusion_matrix(y_test, y_pred)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print('Accuracy = ' + str(accuracy))
print('Precision = ' + str(precision))
print('Recall = ' + str(recall))
print('F1 Score = ' + str(f1))
print('Confusion Matrix:')
print(cm)
plt.figure(figsize=(10, 7))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title('Confusion Matrix')
plt.xlabel('Prediction')
plt.ylabel('Label')
plt.show()
Accuracy = 0.9035087719298246 Precision = 0.8913043478260869 Recall = 0.8723404255319149 F1 Score = 0.8817204301075269 Confusion Matrix: [[62 5] [ 6 41]]
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# Función para asignar etiquetas
def Label(val):
return 'Malignant' if val == 0 else 'Benign'
# Configurar estilo de Seaborn
sns.set_style('whitegrid')
sns.set_context('notebook')
# Restaurar datos escalados y preparar el grid
X_set, y_set = sc.inverse_transform(X_test), y_test
X1, X2 = np.meshgrid(
np.arange(start=X_set[:, 0].min() - 10, stop=X_set[:, 0].max() + 10, step=0.15),
np.arange(start=X_set[:, 1].min() - 1000, stop=X_set[:, 1].max() + 1000, step=0.25)
)
# Predicción en el grid
Z = classifier.predict(sc.transform(np.array([X1.ravel(), X2.ravel()]).T)).reshape(X1.shape)
# Crear el gráfico con Seaborn
plt.figure(figsize=(10, 6))
cmap = ListedColormap(['#FFCCCC', '#CCFFCC']) # Colores suaves para las áreas de clasificación
# Fondo con las regiones de decisión
plt.contourf(X1, X2, Z, alpha=0.3, cmap=cmap)
# Puntos de datos
sns.scatterplot(
x=X_set[:, 0], y=X_set[:, 1],
hue=[Label(y) for y in y_set], palette=['green', 'red'],
style=y_set, markers=['o', 's'],
s=100, alpha=0.7, edgecolor='k', legend='full'
)
# Ajustar detalles del gráfico
plt.title('Random Forest Classification (Decision Boundary)', fontsize=14)
plt.xlabel('Mean-Texture', fontsize=12)
plt.ylabel('Mean-Radius', fontsize=12)
plt.axis([5, 25, 0, 50])
plt.legend(loc='upper left', title='Classes')
plt.tight_layout()
# Mostrar el gráfico
plt.show()
K-Nearest Neighbors¶
En este modelo, la clasificación se hace a través de un voto de los k vecinos más cercanos de cada punto. Se predice la clase que más se repite entre los k vecinos más cercanos.
from sklearn.neighbors import KNeighborsClassifier
classifier = KNeighborsClassifier(n_neighbors = 35, metric = 'minkowski', p = 2)
# I increased the value of K (Number of neighbours) as the model was overfitting with less number of neighbours.
classifier.fit(X_train, y_train)
KNeighborsClassifier(n_neighbors=35)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
KNeighborsClassifier(n_neighbors=35)
y_pred = classifier.predict(X_test)
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
import seaborn as sns
cm = confusion_matrix(y_test, y_pred)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print('Accuracy = ' + str(accuracy))
print('Precision = ' + str(precision))
print('Recall = ' + str(recall))
print('F1 Score = ' + str(f1))
print('Confusion Matrix:')
print(cm)
plt.figure(figsize=(10, 7))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title('Confusion Matrix')
plt.xlabel('Prediction')
plt.ylabel('Label')
plt.show()
Accuracy = 0.9035087719298246 Precision = 0.8913043478260869 Recall = 0.8723404255319149 F1 Score = 0.8817204301075269 Confusion Matrix: [[62 5] [ 6 41]]
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# Función para asignar etiquetas
def Label(val):
return 'Malignant' if val == 0 else 'Benign'
# Configurar estilo de Seaborn
sns.set_style('whitegrid')
sns.set_context('notebook')
# Restaurar datos escalados y preparar el grid
X_set, y_set = sc.inverse_transform(X_test), y_test
X1, X2 = np.meshgrid(
np.arange(start=X_set[:, 0].min() - 10, stop=X_set[:, 0].max() + 10, step=0.15),
np.arange(start=X_set[:, 1].min() - 1000, stop=X_set[:, 1].max() + 1000, step=0.25)
)
# Predicción en el grid
Z = classifier.predict(sc.transform(np.array([X1.ravel(), X2.ravel()]).T)).reshape(X1.shape)
# Crear el gráfico con Seaborn
plt.figure(figsize=(10, 6))
cmap = ListedColormap(['#FFCCCC', '#CCFFCC']) # Colores suaves para el fondo
# Fondo con las regiones de decisión
plt.contourf(X1, X2, Z, alpha=0.3, cmap=cmap)
# Puntos de datos
sns.scatterplot(
x=X_set[:, 0], y=X_set[:, 1],
hue=[Label(y) for y in y_set], palette=['green', 'red'],
style=y_set, markers=['o', 's'],
s=100, alpha=0.7, edgecolor='k', legend='full'
)
# Ajustar detalles del gráfico
plt.title('K-Nearest Neighbors (Decision Boundary)', fontsize=14)
plt.xlabel('Mean-Texture', fontsize=12)
plt.ylabel('Mean-Radius', fontsize=12)
plt.axis([5, 25, 0, 50])
plt.legend(loc='upper left', title='Classes')
plt.tight_layout()
# Mostrar el gráfico
plt.show()
Support Vector Machine (SVM)¶
Funcionamiento del SVM
Separación de Clases: El SVM intenta encontrar el hiperplano que maximiza la distancia (margen) entre los puntos de datos de las diferentes clases. Este hiperplano se llama "hiperplano óptimo".
Vectores de Soporte: Los puntos de datos que están más cerca del hiperplano y que son más difíciles de clasificar correctamente se llaman "vectores de soporte". Estos puntos son cruciales para definir la posición del hiperplano.
Maximización del Margen: El objetivo del SVM es maximizar el margen entre los vectores de soporte de las dos clases. Un margen más amplio generalmente significa un mejor modelo de clasificación.
Para los que tengan más duda sobre como esto se hace pueden revisar el Cálculo de los Límites
Para calcular los límites de decisión, el SVM sigue estos pasos:
Función de Decisión: El SVM utiliza una función de decisión lineal para determinar en qué lado del hiperplano cae un punto de datos. La función de decisión para un punto ( x ) se define como: [ f(x) = w \cdot x + b ] donde ( w ) es el vector de pesos y ( b ) es el sesgo.
Hiperplano Óptimo: El hiperplano óptimo es aquel que maximiza el margen entre las clases, sujeto a las restricciones.
Margen: El margen es la distancia entre el hiperplano y los vectores de soporte.
Kernel Trick: En casos donde los datos no son linealmente separables, el SVM puede utilizar una técnica llamada "kernel trick" para transformar los datos a un espacio de mayor dimensión donde sí sean separables. Algunos kernels comunes son el kernel lineal, polinomial y RBF (Radial Basis Function).
from sklearn.svm import SVC
classifier = SVC(kernel = 'linear', random_state = 0)
classifier.fit(X_train, y_train)
SVC(kernel='linear', random_state=0)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
SVC(kernel='linear', random_state=0)
y_pred = classifier.predict(X_test)
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
import seaborn as sns
cm = confusion_matrix(y_test, y_pred)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print('Accuracy = ' + str(accuracy))
print('Precision = ' + str(precision))
print('Recall = ' + str(recall))
print('F1 Score = ' + str(f1))
print('Confusion Matrix:')
print(cm)
plt.figure(figsize=(10, 7))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title('Confusion Matrix')
plt.xlabel('Prediction')
plt.ylabel('Label')
plt.show()
Accuracy = 0.8947368421052632 Precision = 0.8888888888888888 Recall = 0.851063829787234 F1 Score = 0.8695652173913043 Confusion Matrix: [[62 5] [ 7 40]]
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# Función para asignar etiquetas
def Label(val):
return 'Malignant' if val == 0 else 'Benign'
# Configurar estilo de Seaborn
sns.set_style('whitegrid')
sns.set_context('notebook')
# Restaurar datos escalados y preparar el grid
X_set, y_set = sc.inverse_transform(X_test), y_test
X1, X2 = np.meshgrid(
np.arange(start=X_set[:, 0].min() - 10, stop=X_set[:, 0].max() + 10, step=0.15),
np.arange(start=X_set[:, 1].min() - 1000, stop=X_set[:, 1].max() + 1000, step=0.25)
)
# Predicción en el grid
Z = classifier.predict(sc.transform(np.array([X1.ravel(), X2.ravel()]).T)).reshape(X1.shape)
# Crear el gráfico con Seaborn
plt.figure(figsize=(10, 6))
cmap = ListedColormap(['#FFCCCC', '#CCFFCC']) # Colores suaves para las áreas de clasificación
# Fondo con las regiones de decisión
plt.contourf(X1, X2, Z, alpha=0.3, cmap=cmap)
# Puntos de datos
sns.scatterplot(
x=X_set[:, 0], y=X_set[:, 1],
hue=[Label(y) for y in y_set], palette=['red', 'green'],
style=y_set, markers=['o', 's'],
s=100, alpha=0.7, edgecolor='k', legend='full'
)
# Ajustar detalles del gráfico
plt.title('Support Vector Machine (Decision Boundary)', fontsize=14)
plt.xlabel('Mean-Texture', fontsize=12)
plt.ylabel('Mean-Radius', fontsize=12)
plt.axis([5, 25, 0, 50])
plt.legend(loc='upper left', title='Classes')
plt.tight_layout()
# Mostrar el gráfico
plt.show()
Resultados de los Modelos¶
AUC - ROC¶
Esta herramienta se utiliza para evaluar el rendimiento de un modelo de clasificación, especialmente en problemas de clasificación binaria.
La curva ROC muestra la capacidad de un modelo de distinguir entre calses. Esto se calcula trazando la tasa de verdaderos positivos (TPR) contra la tasa de falsos positivos (FPR) en diferentes umbrales de clasificación.
El AUC se utiliza para resumir el rendimiento de un modelo en un solo valor. Cuanto mayor sea el AUC, mejor será el modelo para distinguir entre las clases. En caso de que el AUC sea 0.5, el modelo no tiene capacidad de distinguir entre las clases, es como clasificar en base a lanzar una moneda.
from sklearn.metrics import roc_curve, roc_auc_score
import matplotlib.pyplot as plt
# Función para calcular y graficar la curva ROC
def plot_roc_curve(y_test, y_pred_proba, model_name):
fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
auc = roc_auc_score(y_test, y_pred_proba)
plt.plot(fpr, tpr, label=f'{model_name} (AUC = {auc:.2f})')
# Calcular y graficar la curva ROC para cada modelo
plt.figure(figsize=(10, 7))
# Regresión Logística
classifier = LogisticRegression(random_state=0)
classifier.fit(X_train, y_train)
y_pred_proba = classifier.predict_proba(X_test)[:, 1]
plot_roc_curve(y_test, y_pred_proba, 'Logistic Regression')
# Árbol de Decisión
classifier = DecisionTreeClassifier(criterion='entropy', random_state=0, max_depth=3, min_samples_split=0.8)
classifier.fit(X_train, y_train)
y_pred_proba = classifier.predict_proba(X_test)[:, 1]
plot_roc_curve(y_test, y_pred_proba, 'Decision Tree')
# Random Forest
classifier = RandomForestClassifier(n_estimators=20, criterion='entropy', random_state=0, max_depth=5)
classifier.fit(X_train, y_train)
y_pred_proba = classifier.predict_proba(X_test)[:, 1]
plot_roc_curve(y_test, y_pred_proba, 'Random Forest')
# K-Nearest Neighbors
classifier = KNeighborsClassifier(n_neighbors=35, metric='minkowski', p=2)
classifier.fit(X_train, y_train)
y_pred_proba = classifier.predict_proba(X_test)[:, 1]
plot_roc_curve(y_test, y_pred_proba, 'K-Nearest Neighbors')
# Support Vector Machine (SVM)
classifier = SVC(kernel='linear', random_state=0, probability=True)
classifier.fit(X_train, y_train)
y_pred_proba = classifier.predict_proba(X_test)[:, 1]
plot_roc_curve(y_test, y_pred_proba, 'Support Vector Machine')
# Configurar el gráfico
plt.plot([0, 1], [0, 1], 'k--')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.legend(loc='lower right')
plt.show()
En todos los gráficos de los Límites de Decisión podemos encontrar puntos de una clase que aparecen en el área destinada a la otra clase.
Esto se debe a por ejemplo:
- Errores de Clasificación
- Puntos dificiles de clasificar
La explicación de esto puede ser por varias razones:
Solapamiento entre las clases: los datos comparten ciertas características que hacen que sea difícil distinguir entre ellos. Esto es común en dataset reales.
Capacidad del modelo: algunos modelos son más flexibles que otros, y pueden adaptarse mejor a los datos y ser capaces de encontrar separaciones adecuadas.
Ruido de los datos: algunos datos podrían estar mal etiquetados desde el inicio, o ser atípicos dentro de las caracteristicas de la clase y el dataset.
Estos problemas pueden ser solucionados, o ser reducidos con la ayuda de técnicas de preprocesamiento de datos, como la eliminación de outliers, la normalización de los datos, la selección de características, aumentar el tamaño del dataset, etc.
En cuanto a los rendimientos de los modelos:¶
1. Logistic Regression¶
- Resultados:
- Accuracy: 0.89, Precision: 0.89, Recall: 0.85, F1 Score: 0.87, AUC: 0.96
- Explicación:
- La regresión logística asume que la relación entre las características y la probabilidad de pertenecer a una clase es lineal, lo cual parece ser válido para este dataset, ya que logra un buen rendimiento general.
- Su alto AUC (0.96) indica que puede discriminar bien entre clases, pero el recall no es el más alto, lo que sugiere que podría estar perdiendo algunos casos malignos.
- Este modelo es ideal para este dataset porque tiene un tamaño mediano y las clases parecen ser razonablemente separables linealmente.
2. Arboles de Decisión¶
- Resultados:
- Accuracy: 0.89, Precision: 0.97, Recall: 0.74, F1 Score: 0.84, AUC: 0.86
- Explicación:
- Los árboles de decisión son buenos para manejar relaciones no lineales, pero en este caso, parecen sobreajustar los datos, lo que resulta en una precisión muy alta (0.97) pero un recall bajo (0.74). Esto indica que está clasificando correctamente los casos benignos, pero omitiendo muchos casos malignos.
- Esto se debe a que los árboles de decisión tienden a fragmentar demasiado los datos si no se controlan adecuadamente, lo que genera un modelo muy específico que pierde generalización.
3. Random Forest¶
- Resultados:
- Accuracy: 0.90, Precision: 0.89, Recall: 0.87, F1 Score: 0.88, AUC: 0.96
- Explicación:
- Random Forest supera el problema de sobreajuste de los árboles de decisión al promediar múltiples árboles. Esto le permite manejar relaciones no lineales y generalizar mejor en datasets complejos.
- Su rendimiento equilibrado en todas las métricas (precisión y recall) lo hace un modelo robusto para este problema. Además, su AUC (0.96) demuestra que puede distinguir bien entre las clases.
- Este modelo es ideal para datasets medianos como este, ya que captura complejidades no lineales sin perder capacidad de generalización.
4. Support Vector Machines (SVM)¶
- Resultados:
- Accuracy: 0.89, Precision: 0.89, Recall: 0.85, F1 Score: 0.87, AUC: 0.96
- Explicación:
- SVM busca maximizar el margen entre las clases. En este dataset, donde parece haber cierta separación lineal con pocas intersecciones, el SVM puede encontrar un hiperplano adecuado.
- Aunque tiene un rendimiento similar a la regresión logística, su ventaja radica en que también podría manejar relaciones no lineales usando kernels. Sin embargo, como el dataset es relativamente pequeño, el kernel lineal parece suficiente.
- Su alta capacidad discriminativa se refleja en el AUC (0.96).
5. k-Nearest Neighbors (k-NN)¶
- Resultados:
- Accuracy: 0.90, Precision: 0.89, Recall: 0.87, F1 Score: 0.88, AUC: 0.95
- Explicación:
- k-NN clasifica según los vecinos más cercanos en el espacio de características. Es efectivo cuando las clases tienen límites no lineales, como podría ser el caso aquí.
- El modelo tiene un rendimiento similar al de Random Forest, pero puede ser más sensible al ruido o a la escala de los datos. Es probable que el dataset haya sido preprocesado adecuadamente (normalización o estandarización), lo que permite que k-NN sea efectivo.
- Su AUC (0.95) indica una alta capacidad de discriminación, aunque no es tan robusto como Random Forest en general.
Comparación y Comportamiento de los Modelos¶
Relación Lineal vs. No Lineal:
- La regresión logística y SVM funcionan bien porque las características parecen tener una relación predominantemente lineal con las clases.
- Los modelos como Random Forest y k-NN manejan mejor relaciones no lineales, lo que explica su rendimiento ligeramente superior en recall y balance general.
Overfitting:
- Los árboles de decisión tienden a sobreajustar, como se refleja en su alta precisión pero bajo recall. Random Forest soluciona este problema mediante el ensamblaje.
Robustez y Generalización:
- Random Forest y SVM son más robustos en datasets medianos como este, debido a su capacidad de manejar complejidad sin perder generalización.
Simplicidad vs. Complejidad:
- Regresión logística y k-NN son más simples de interpretar y ejecutar, pero Random Forest ofrece mejor balance y robustez, a costa de una menor interpretabilidad.
Conclusión¶
En este dataset de cáncer de mama, Random Forest es el modelo más adecuado gracias a su balance entre precisión, recall, y robustez frente a relaciones no lineales. SVM y Logistic Regression también son opciones sólidas, especialmente si se requiere simplicidad o interpretabilidad.