ScikitLearn
Tutorial ScikitLearn: Clasificación KNN KNeighborsClassifier
Scikit Learn y KNeighborsClassifier: aprende el algoritmo KNN para clasificación. Teoría, preprocesamiento, implementación, parámetros e interpretabilidad.
Aprende ScikitLearn GRATIS y certifícateIntroducción teórica a KNN para clasificación
El algoritmo de K-Vecinos Más Cercanos (KNN, por sus siglas en inglés) es un método de aprendizaje supervisado utilizado tanto para clasificación como para regresión. En el contexto de la clasificación, KNN es un algoritmo simple pero eficaz que asigna una clase a una instancia desconocida basándose en las clases de sus k vecinos más cercanos en el espacio de características.
El principio fundamental de KNN se basa en la suposición de que las instancias que están cercanas en el espacio de características comparten características similares. Por lo tanto, es probable que pertenezcan a la misma clase. Este algoritmo no construye un modelo explícito durante la fase de entrenamiento; más bien, almacena el conjunto de entrenamiento completo y difiere la clasificación hasta que se presenta una nueva instancia para clasificar. Por esta razón, KNN se considera un método de aprendizaje perezoso o lazy learning.
Para determinar los vecinos más cercanos, KNN utiliza una medida de distancia, siendo la distancia euclidiana la más común en problemas de clasificación. Sin embargo, otras métricas como la distancia de Manhattan o la distancia de Minkowski pueden ser utilizadas dependiendo de la naturaleza de los datos y las características específicas del problema.
El proceso de clasificación con KNN sigue los siguientes pasos:
- Cálculo de distancias: Se calcula la distancia entre la instancia a clasificar y todas las instancias del conjunto de entrenamiento.
- Selección de vecinos: Se seleccionan los k vecinos más cercanos basándose en las distancias calculadas.
- Asignación de clase: La clase más frecuente entre los k vecinos seleccionados se asigna a la nueva instancia. Este proceso se conoce como votación por mayoría.
La elección del valor de _k_
es crucial para el rendimiento del algoritmo.
- Un valor pequeño de k puede hacer que el modelo sea sensible al ruido en los datos
- Un valor grande de k puede diluir la influencia de los vecinos más cercanos y llevar a una capacidad de discriminación insuficiente.
Por lo tanto, es común utilizar técnicas de validación cruzada para determinar el valor óptimo de k.
Es importante considerar el peso de los vecinos en el proceso de votación. En su forma básica, KNN asigna igual peso a todos los vecinos seleccionados. Sin embargo, es posible utilizar versiones ponderadas donde los vecinos más cercanos tienen mayor influencia en la decisión final. Una forma común de ponderación es utilizar el inverso de la distancia, dando así más peso a los vecinos más próximos.
La normalización de las características es un paso esencial al utilizar KNN, ya que la medida de distancia utilizada es sensible a las escalas de los datos. Sin una adecuada normalización o estandarización, las características con escalas mayores pueden dominar el cálculo de distancias, afectando negativamente al rendimiento del algoritmo.
KNN tiene varias ventajas, como su simplicidad y facilidad de implementación. Además, es un algoritmo no paramétrico, lo que significa que no asume una forma funcional específica para el modelo. Sin embargo, también presenta desventajas, como su alta complejidad computacional durante la fase de predicción, especialmente con conjuntos de datos grandes, y su susceptibilidad a las dimensiones altas debido al fenómeno conocido como la maldición de la dimensionalidad.
A continuación, se presenta un ejemplo sencillo que ilustra cómo funciona KNN para clasificación:
import numpy as np
from collections import Counter
# Datos de entrenamiento (características y etiquetas)
X_train = np.array([[1, 2], [2, 3], [3, 3], [6, 5], [7, 7], [8, 6]])
y_train = np.array(['A', 'A', 'A', 'B', 'B', 'B'])
# Nueva instancia a clasificar
x_new = np.array([5, 5])
# Valor de k
k = 3
# Cálculo de distancias euclidianas entre x_new y X_train
distancias = np.linalg.norm(X_train - x_new, axis=1)
# Obtención de los índices de los k vecinos más cercanos
indices_vecinos = np.argsort(distancias)[:k]
# Obtención de las etiquetas de los vecinos más cercanos
etiquetas_vecinos = y_train[indices_vecinos]
# Votación por mayoría
contador = Counter(etiquetas_vecinos)
clase_asignada = contador.most_common(1)[0][0]
print(f"La nueva instancia {x_new} ha sido clasificada como: {clase_asignada}")
En este ejemplo, se clasifica la instancia [5, 5]
utilizando KNN con k igual a 3. Se calculan las distancias a todos los puntos de entrenamiento, se seleccionan los tres vecinos más cercanos y se asigna la clase más común entre ellos. La instancia es clasificada como perteneciente a la clase 'B'.
En el contexto de problemas con múltiples clases o clases desbalanceadas, es importante ajustar cuidadosamente los parámetros de KNN y considerar técnicas adicionales, como la ponderación de las clases o el uso de métricas de distancia más adecuadas.
Preprocesados específicos para KNN
El algoritmo K-Vecinos Más Cercanos (KNN) se basa en medidas de distancia para realizar clasificaciones, lo que hace que el preprocesado de los datos sea fundamental para su rendimiento. A continuación, se detallan los preprocesamientos específicos que deben considerarse al utilizar KNN para clasificación.
Un aspecto clave en KNN es la importancia de escalar las características. Dado que las distancias entre muestras dependen de las magnitudes de las características, si las variables no están en la misma escala, aquellas con valores más grandes pueden dominar el cálculo de distancia. Por ejemplo, si una característica varía entre 1 y 10 y otra entre 1 y 1.000, la segunda influirá mucho más en la medida de distancia, lo que puede sesgar los resultados.
Para normalizar las características, es común utilizar el StandardScaler
o el MinMaxScaler
de Scikit Learn. El StandardScaler
ajusta las características para que tengan media cero y desviación estándar uno, lo que es especialmente útil cuando se asume que los datos siguen una distribución normal. Por otro lado, el MinMaxScaler
escala las características para que estén dentro de un rango específico, típicamente entre 0 y 1, conservando la forma original de la distribución de los datos pero cambiando su escala.
A continuación se muestra un ejemplo de cómo aplicar el StandardScaler
a un conjunto de datos antes de utilizar KNN:
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
# Supongamos que tenemos los datos X y las etiquetas y
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Aplicamos StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Entrenamos el modelo KNN
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train_scaled, y_train)
# Evaluamos el modelo
accuracy = knn.score(X_test_scaled, y_test)
print(f"Exactitud del modelo: {accuracy:.2f}")
Es importante ajustar el escalador sólo con los datos de entrenamiento, y luego aplicar la transformación a los datos de prueba. Esto evita introducir sesgos de los datos de prueba en el modelo.
Otro preprocesado relevante es la reducción de la dimensionalidad. KNN puede verse afectado por la maldición de la dimensionalidad, ya que en espacios de alta dimensión las distancias euclidianas pueden perder significado y la densidad de los datos puede reducirse. Para mitigar este efecto, es común utilizar técnicas como PCA (Análisis de Componentes Principales) para reducir el número de características manteniendo la mayor cantidad de información posible.
Un ejemplo de cómo incorporar PCA antes de KNN sería:
from sklearn.decomposition import PCA
# Aplicamos PCA para reducir a 10 componentes principales
pca = PCA(n_components=10)
X_train_pca = pca.fit_transform(X_train_scaled)
X_test_pca = pca.transform(X_test_scaled)
# Entrenamos el modelo KNN con los datos transformados
knn.fit(X_train_pca, y_train)
# Evaluamos el modelo
accuracy = knn.score(X_test_pca, y_test)
print(f"Exactitud del modelo con PCA: {accuracy:.2f}")
Además, es crucial considerar el tratamiento de valores atípicos o outliers. En KNN, los valores atípicos pueden influir significativamente en el cálculo de distancias, afectando negativamente el rendimiento del clasificador. Para identificar y manejar los outliers, se pueden utilizar métodos como el Isolation Forest o el Local Outlier Factor. Sin embargo, se debe tener cuidado al eliminar o tratar outliers para no perder información relevante.
Por ejemplo, para detectar outliers con LocalOutlierFactor
:
from sklearn.neighbors import LocalOutlierFactor
# Detectamos outliers en los datos de entrenamiento
lof = LocalOutlierFactor()
outlier_labels = lof.fit_predict(X_train_scaled)
# Filtramos las muestras que no son outliers
mask = outlier_labels != -1
X_train_clean = X_train_scaled[mask]
y_train_clean = y_train[mask]
# Entrenamos KNN con los datos limpios
knn.fit(X_train_clean, y_train_clean)
# Evaluamos el modelo con los datos de prueba
accuracy = knn.score(X_test_scaled, y_test)
print(f"Exactitud del modelo sin outliers: {accuracy:.2f}")
La selección de características también es un preprocesado relevante para KNN. Dado que KNN puede ser sensible a las características irrelevantes o redundantes, eliminar dichas características puede mejorar el desempeño del modelo. Métodos como la Regresión Lasso o SelectKBest
pueden ayudar a identificar y seleccionar las características más informativas.
Por ejemplo, utilizando SelectKBest
:
from sklearn.feature_selection import SelectKBest, chi2
# Seleccionamos las 5 mejores características
selector = SelectKBest(chi2, k=5)
X_train_selected = selector.fit_transform(X_train_scaled, y_train)
X_test_selected = selector.transform(X_test_scaled)
# Entrenamos el modelo KNN con las características seleccionadas
knn.fit(X_train_selected, y_train)
# Evaluamos el modelo
accuracy = knn.score(X_test_selected, y_test)
print(f"Exactitud del modelo con características seleccionadas: {accuracy:.2f}")
Es fundamental recordar que la imputación de valores faltantes puede ser más compleja en KNN. Tradicionalmente, los algoritmos de imputación se basan en estadísticas como la media o mediana, pero en KNN, la presencia de valores faltantes puede distorsionar las distancias. Por ello, es recomendable utilizar imputadores que consideren la proximidad entre muestras, como el KNNImputer
:
from sklearn.impute import KNNImputer
# Suponiendo que X contiene valores faltantes
imputer = KNNImputer(n_neighbors=5)
X_imputed = imputer.fit_transform(X)
# Procedemos con el escalado y el resto del preprocesado
Al utilizar el KNNImputer
, se imputan los valores faltantes basándose en los vecinos más cercanos, lo que puede ser más coherente con el enfoque del algoritmo KNN.
Finalmente, es importante considerar el balanceo de las clases en problemas de clasificación. KNN puede verse afectado por desequilibrios en el conjunto de datos, ya que las clases mayoritarias pueden dominar la votación de los vecinos. Técnicas como el submuestreo de la clase mayoritaria o el sobremuestreo de la minoritaria, utilizando métodos como SMOTE, pueden ayudar a equilibrar las clases:
from imblearn.over_sampling import SMOTE
# Aplicamos SMOTE para balancear las clases
smote = SMOTE()
X_resampled, y_resampled = smote.fit_resample(X_train_scaled, y_train)
# Entrenamos el modelo con los datos balanceados
knn.fit(X_resampled, y_resampled)
# Evaluamos el modelo
accuracy = knn.score(X_test_scaled, y_test)
print(f"Exactitud del modelo con clases balanceadas: {accuracy:.2f}")
Es necesario instalar la librería imbalanced-learn
para utilizar SMOTE
, la cual es compatible con Scikit Learn y proporciona herramientas adicionales para el manejo de datos desbalanceados.
Modelo KNeighborsClassifier en Scikit learn
La clase KNeighborsClassifier
de Scikit Learn proporciona una implementación eficiente del algoritmo K-Vecinos Más Cercanos para tareas de clasificación. Este modelo permite clasificar nuevas instancias basándose en las clases de sus vecinos más cercanos en el espacio de características.
Para utilizar KNeighborsClassifier
, es necesario seguir los pasos habituales en un flujo de trabajo de aprendizaje automático:
- Importación de librerías y carga de datos.
- División del conjunto de datos en conjuntos de entrenamiento y prueba.
- Escalado de características si es necesario.
- Instanciación y entrenamiento del modelo.
- Realización de predicciones.
- Evaluación del rendimiento del modelo.
A continuación, se presenta un ejemplo completo que ilustra cómo aplicar KNeighborsClassifier
en un conjunto de datos clásico como el Iris Dataset:
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
# Carga del conjunto de datos Iris
iris = load_iris()
X = iris.data
y = iris.target
# División del conjunto de datos en entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Escalado de características
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# Instanciación del modelo KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5)
# Entrenamiento del modelo
knn.fit(X_train, y_train)
# Predicción sobre el conjunto de prueba
y_pred = knn.predict(X_test)
# Evaluación del rendimiento
precision = accuracy_score(y_test, y_pred)
print(f"Precisión del modelo: {precision:.2f}")
En este ejemplo, se realiza lo siguiente:
- Se cargan los datos del conjunto Iris, que contiene mediciones de flores y sus correspondientes especies.
- Se dividen los datos en conjuntos de entrenamiento y prueba.
- Se aplica escalado a las características utilizando
StandardScaler
. - Se crea una instancia de
KNeighborsClassifier
indicando que n_neighbors=5, es decir, el número de vecinos a considerar. - Se entrena el modelo con los datos de entrenamiento mediante el método
fit
. - Se realizan predicciones sobre el conjunto de prueba con el método
predict
. - Se evalúa la precisión del modelo utilizando la función
accuracy_score
.
La elección del parámetro n_neighbors
es crucial, ya que determina el número de vecinos que el modelo considerará para hacer la predicción. En este caso, se ha establecido en 5.
Es posible también predecir la probabilidad de clasificación de cada clase utilizando el método predict_proba
:
# Predicción de probabilidades
y_proba = knn.predict_proba(X_test)
print("Probabilidades de predicción:")
print(y_proba)
El modelo KNeighborsClassifier
ofrece varios parámetros que permiten personalizar su funcionamiento, como la métrica de distancia a utilizar. Por defecto, se emplea la distancia euclidiana, pero es posible especificar otras métricas mediante el parámetro metric
.
Por ejemplo, para utilizar la distancia de Manhattan:
# Instanciación del modelo con distancia de Manhattan
knn_manhattan = KNeighborsClassifier(n_neighbors=5, metric='manhattan')
knn_manhattan.fit(X_train, y_train)
y_pred_manhattan = knn_manhattan.predict(X_test)
precision_manhattan = accuracy_score(y_test, y_pred_manhattan)
print(f"Precisión con distancia de Manhattan: {precision_manhattan:.2f}")
Además, es posible dar pesos diferentes a los vecinos en función de su distancia a la instancia a clasificar. El parámetro weights
controla esto. Si se establece en 'distance'
, los vecinos más cercanos tienen mayor influencia:
# Modelo con pesos basados en la distancia
knn_weighted = KNeighborsClassifier(n_neighbors=5, weights='distance')
knn_weighted.fit(X_train, y_train)
y_pred_weighted = knn_weighted.predict(X_test)
precision_weighted = accuracy_score(y_test, y_pred_weighted)
print(f"Precisión con pesos por distancia: {precision_weighted:.2f}")
El modelo KNeighborsClassifier
también permite ajustar otros parámetros como el algoritmo utilizado para computar los vecinos más cercanos, a través del parámetro algorithm
. Las opciones incluyen 'auto'
, 'ball_tree'
, 'kd_tree'
y 'brute'
. La elección del algoritmo puede afectar al tiempo de cómputo, especialmente con grandes conjuntos de datos.
Por ejemplo, para utilizar el algoritmo kd_tree
:
# Modelo utilizando el algoritmo kd_tree
knn_kdtree = KNeighborsClassifier(n_neighbors=5, algorithm='kd_tree')
knn_kdtree.fit(X_train, y_train)
y_pred_kdtree = knn_kdtree.predict(X_test)
precision_kdtree = accuracy_score(y_test, y_pred_kdtree)
print(f"Precisión con kd_tree: {precision_kdtree:.2f}")
Es importante tener en cuenta que el modelo KNeighborsClassifier
no realiza ningún tipo de ajuste durante el entrenamiento, sino que simplemente almacena el conjunto de entrenamiento. Las predicciones se realizan calculando las distancias entre la nueva instancia y las instancias almacenadas, lo que puede ser computacionalmente costoso con conjuntos de datos muy grandes.
Para evaluar el modelo de manera más exhaustiva, se pueden utilizar técnicas como la validación cruzada. Mediante cross_val_score
se puede obtener una estimación más fiable del rendimiento:
from sklearn.model_selection import cross_val_score
# Validación cruzada con 5 particiones
scores = cross_val_score(knn, X_train, y_train, cv=5)
print(f"Puntuaciones de validación cruzada: {scores}")
print(f"Precisión media: {np.mean(scores):.2f}")
La validación cruzada ayuda a verificar la generalización del modelo y a evitar problemas como el sobreajuste.
Para mejorar la eficiencia del modelo con grandes conjuntos de datos, es posible utilizar el método de aproximación mediante vecinos cercanos con el parámetro n_jobs
, que permite paralelizar el cálculo:
# Modelo con n_jobs para paralelizar cálculos
knn_parallel = KNeighborsClassifier(n_neighbors=5, n_jobs=-1)
knn_parallel.fit(X_train, y_train)
El valor n_jobs=-1
indica que se utilizarán todos los núcleos del procesador disponibles, lo que puede reducir significativamente el tiempo de cómputo.
Si se desea preprocesar los datos y construir el modelo en una sola secuencia, se puede utilizar un Pipeline, que integra los pasos de escalado y entrenamiento:
from sklearn.pipeline import Pipeline
# Construcción del Pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('knn', KNeighborsClassifier(n_neighbors=5))
], memory = None)
# Entrenamiento del Pipeline
pipeline.fit(X_train, y_train)
# Predicción y evaluación
y_pred_pipeline = pipeline.predict(X_test)
precision_pipeline = accuracy_score(y_test, y_pred_pipeline)
print(f"Precisión del modelo con Pipeline: {precision_pipeline:.2f}")
El uso de Pipelines garantiza que las transformaciones aplicadas durante el entrenamiento se replicarán exactamente durante la predicción, evitando posibles inconsistencias.
En escenarios donde el conjunto de datos es muy grande, otra alternativa es utilizar una muestra representativa para entrenar el modelo. Esto puede reducir el tiempo de procesamiento sin sacrificar significativamente la precisión.
Finalmente, es posible guardar el modelo entrenado para su uso posterior utilizando la librería joblib
:
import joblib
# Guardado del modelo
joblib.dump(knn, 'modelo_knn.pkl')
# Carga del modelo
modelo_cargado = joblib.load('modelo_knn.pkl')
# Uso del modelo cargado para predicciones
y_pred_cargado = modelo_cargado.predict(X_test)
El guardado y carga del modelo facilita su despliegue en entornos de producción y su reutilización sin necesidad de reentrenamiento.
En resumen, la clase KNeighborsClassifier
en Scikit Learn es una herramienta flexible y potente para implementar el algoritmo K-Vecinos Más Cercanos en problemas de clasificación. Su uso adecuado implica una correcta elección de parámetros, un buen preprocesamiento de los datos y una evaluación rigurosa del rendimiento.
Parámetros de KNeighborsClassifier
La clase KNeighborsClassifier
en Scikit Learn ofrece múltiples parámetros que permiten personalizar su funcionamiento y optimizar el rendimiento del modelo según las características del conjunto de datos. A continuación, se detallan los principales parámetros y cómo influyen en el comportamiento del clasificador.
El parámetro n_neighbors
determina el número de vecinos a considerar durante la clasificación. Por defecto, su valor es 5, pero ajustar este valor es crucial para encontrar el equilibrio entre sesgo y varianza. Un número pequeño de vecinos puede hacer que el modelo sea sensible al ruido en los datos, mientras que un número grande puede diluir la influencia de los vecinos más cercanos.
Por ejemplo, para utilizar 3 vecinos, se establece:
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=3)
El parámetro weights
especifica la función de ponderación aplicada a los vecinos seleccionados. Las opciones disponibles son 'uniform'
, donde todos los vecinos tienen el mismo peso, y 'distance'
, donde los vecinos contribuyen de forma inversamente proporcional a su distancia. También es posible proporcionar una función definida por el usuario.
Para asignar mayor peso a los vecinos más cercanos:
knn = KNeighborsClassifier(weights='distance')
El parámetro algorithm
define el algoritmo utilizado para computar los vecinos más cercanos. Las opciones son:
'auto'
: selecciona el algoritmo más apropiado automáticamente.'ball_tree'
: utiliza el algoritmo de Ball Tree.'kd_tree'
: emplea un KD Tree.'brute'
: realiza una búsqueda por fuerza bruta.
La elección del algoritmo puede afectar al rendimiento computacional, especialmente con grandes conjuntos de datos. Por ejemplo:
knn = KNeighborsClassifier(algorithm='kd_tree')
El parámetro leaf_size
es relevante cuando se utilizan los algoritmos 'kd_tree'
o 'ball_tree'
. Este parámetro controla el tamaño de las hojas en los árboles, lo cual puede influir en el tiempo de construcción y consulta del árbol, así como en el uso de memoria. El valor por defecto es 30, pero puede ajustarse según las necesidades.
Por ejemplo, para establecer un tamaño de hoja de 50:
knn = KNeighborsClassifier(algorithm='ball_tree', leaf_size=50)
El parámetro p
especifica la potencia de la métrica de Minkowski utilizada para calcular las distancias entre los puntos. Cuando p=1
, se utiliza la distancia Manhattan, y cuando p=2
, la distancia Euclidiana. Valores superiores permiten definir métricas de distancia más generales.
Para aplicar la distancia Manhattan:
knn = KNeighborsClassifier(p=1)
El parámetro metric
permite seleccionar la métrica de distancia a utilizar. Por defecto, es 'minkowski'
, pero es posible elegir otras métricas predefinidas como 'euclidean'
, 'manhattan'
, 'chebyshev'
, o incluso proporcionar una función de distancia personalizada.
Por ejemplo, para utilizar la distancia de Chebyshev:
knn = KNeighborsClassifier(metric='chebyshev')
Si se requiere ajustar parámetros adicionales de la métrica, se puede utilizar el parámetro metric_params
, proporcionando un diccionario con los argumentos necesarios. Este parámetro es útil cuando se emplean métricas que requieren ajustes específicos.
Por ejemplo, al utilizar la métrica Mahalanobis, se necesita proporcionar la matriz de covarianza:
import numpy as np
V = np.cov(X_train.T)
knn = KNeighborsClassifier(metric='mahalanobis', metric_params={'V': V})
El parámetro n_jobs
controla el número de procesos paralelos utilizados para el cálculo. Establecer n_jobs=-1
permite utilizar todos los núcleos disponibles en el procesador, lo que puede acelerar significativamente el tiempo de ejecución en máquinas multicore.
Para habilitar el paralelismo:
knn = KNeighborsClassifier(n_jobs=-1)
Además de estos parámetros principales, es posible definir parámetros adicionales a través de funciones personalizadas para pesos o métricas. Por ejemplo, para crear una función de peso personalizada que otorgue mayor importancia a los vecinos en una banda determinada:
def custom_weights(distances):
return np.where(distances < 0.5, 1, 0.5)
knn = KNeighborsClassifier(weights=custom_weights)
Es importante notar que al utilizar funciones personalizadas, se debe asegurar que cumplen con los requisitos esperados por el modelo, y que su uso está justificado en el contexto del problema específico.
Otro aspecto relevante es el manejo de valores faltantes. Aunque el KNeighborsClassifier no posee un parámetro específico para tratar valores faltantes, es compatible con el MissingIndicator
y preprocesamiento adecuado. Sin embargo, es esencial garantizar que los datos estén correctamente imputados antes de ajustar el modelo.
Por último, es posible ajustar el parámetro n_neighbors
y otros hiperparámetros utilizando técnicas como la búsqueda en malla (GridSearchCV) o la búsqueda aleatoria (RandomizedSearchCV), optimizando así el rendimiento del modelo.
Ejemplo de búsqueda en malla para encontrar el mejor valor de n_neighbors
y weights
:
from sklearn.model_selection import GridSearchCV
param_grid = {
'n_neighbors': np.arange(1, 31, 2),
'weights': ['uniform', 'distance']
}
grid_search = GridSearchCV(KNeighborsClassifier(), param_grid, cv=5)
grid_search.fit(X_train, y_train)
print(f"Mejores parámetros: {grid_search.best_params_}")
En este ejemplo, se exploran diferentes valores de n_neighbors
y se prueban ambos tipos de pesos, utilizando una validación cruzada de 5 pliegues para evaluar el rendimiento.
En resumen, los parámetros de KNeighborsClassifier
ofrecen una gran flexibilidad para adaptar el modelo a las necesidades específicas del problema. Es fundamental comprender cómo cada parámetro influye en el comportamiento del clasificador y realizar una tuning adecuada para obtener el mejor desempeño posible.
Interpretabilidad de KNeighborsClassifier en Scikit Learn
La interpretabilidad en los modelos de aprendizaje automático es fundamental para comprender cómo y por qué un modelo toma ciertas decisiones. En el caso de KNeighborsClassifier
, la interpretabilidad se centra en analizar cómo los vecinos más cercanos influyen en la predicción de una nueva instancia y en entender las dinámicas del espacio de características.
A diferencia de otros modelos de clasificación, KNN es un algoritmo basado en instancias que no genera un modelo explícito durante el entrenamiento. Por lo tanto, la interpretabilidad se enfoca en explorar las distancias y relaciones entre las observaciones. A continuación, se detallan las estrategias y herramientas para interpretar los resultados de KNeighborsClassifier
en Scikit Learn.
Es esencial comprender que para una instancia nueva, el modelo busca los k vecinos más cercanos en el conjunto de entrenamiento y asigna la clase más común entre ellos. Por lo tanto, analizar cuáles son estos vecinos y cómo contribuyen a la predicción es clave para la interpretabilidad.
Identificación de los vecinos más cercanos
Para interpretar una predicción específica, es útil identificar qué muestras del conjunto de entrenamiento son los vecinos más cercanos. Scikit Learn proporciona el método kneighbors
que permite obtener los vecinos para una o varias instancias.
import numpy as np
from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
# Carga de datos y entrenamiento del modelo
iris = load_iris()
X = iris.data
y = iris.target
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X, y)
# Nueva instancia a predecir
X_new = np.array([[5.5, 2.6, 4.4, 1.2]])
# Predicción de la clase
predicción = knn.predict(X_new)
print(f"Clase predicha: {predicción}")
# Obtención de los vecinos más cercanos
distancias, índices = knn.kneighbors(X_new)
print(f"Distancias a los vecinos: {distancias}")
print(f"Índices de los vecinos: {índices}")
print(f"Clases de los vecinos: {y[índices]}")
En este ejemplo, se utiliza el método kneighbors
para obtener las distancias y los índices de los vecinos más cercanos a la nueva instancia. Analizando las distancias se puede entender qué tan cerca están los vecinos y, observando las clases de esos vecinos, se puede justificar la predicción realizada.
Exploración del espacio de características
La visualización del espacio de características y los vecinos puede ayudar a interpretar el comportamiento del modelo. Utilizando técnicas de reducción de dimensionalidad como PCA o seleccionando dos dimensiones significativas, se pueden crear gráficos que ilustren la posición de la nueva instancia en relación con el conjunto de entrenamiento.
import matplotlib.pyplot as plt
# Selección de dos características para visualización
X_vis = X[:, :2] # Usamos las dos primeras características
X_new_vis = X_new[:, :2]
# Entrenamiento del modelo con las características seleccionadas
knn_vis = KNeighborsClassifier(n_neighbors=3)
knn_vis.fit(X_vis, y)
# Obtención de los vecinos en el espacio reducido
distancias_vis, índices_vis = knn_vis.kneighbors(X_new_vis)
# Gráfico de dispersión
plt.scatter(X_vis[:, 0], X_vis[:, 1], c=y, cmap='viridis', edgecolor='k', s=50)
plt.scatter(X_new_vis[0, 0], X_new_vis[0, 1], c='red', marker='X', s=100)
plt.scatter(X_vis[índices_vis][0][:, 0], X_vis[índices_vis][0][:, 1], facecolors='none', edgecolors='red', s=100)
plt.xlabel('Característica 1')
plt.ylabel('Característica 2')
plt.title('Vecinos más cercanos en el espacio de características')
plt.show()
Este gráfico muestra la posición de la nueva instancia (marcada con una X roja) y resalta los vecinos más cercanos con círculos rojos. Visualmente, se puede apreciar cómo la cercanía en el espacio de características influye en la predicción.
Análisis de contribuciones individuales
Para una interpretación más detallada, es posible analizar cómo cada vecino contribuye a la predicción. Si se utilizan pesos basados en la distancia, los vecinos más cercanos tendrán mayor influencia. El siguiente ejemplo muestra cómo calcular las contribuciones:
# Uso de pesos inversamente proporcionales a la distancia
knn_pesos = KNeighborsClassifier(n_neighbors=3, weights='distance')
knn_pesos.fit(X, y)
# Predicción con probabilidades
probabilidades = knn_pesos.predict_proba(X_new)
print(f"Probabilidades de cada clase: {probabilidades}")
# Cálculo de las contribuciones individuales
distancias_pesos, índices_pesos = knn_pesos.kneighbors(X_new)
pesos_vecinos = 1 / distancias_pesos[0]
clases_vecinos = y[índices_pesos[0]]
contribuciones = {}
for clase in np.unique(y):
contribuciones[clase] = np.sum(pesos_vecinos[clases_vecinos == clase])
print(f"Contribuciones por clase: {contribuciones}")
En este código, se calculan las probabilidades asignadas a cada clase y se determina la contribución de cada vecino a dichas probabilidades. Esto permite entender cómo la distancia y la clase de cada vecino afectan la predicción final.
Utilización de métricas de interpretación
Aunque KNeighborsClassifier no genera coeficientes o árboles de decisión, es posible evaluar la importancia de las características mediante técnicas adicionales. Por ejemplo, el uso de permutación de importancias permite medir el impacto de cada característica en el rendimiento del modelo.
from sklearn.inspection import permutation_importance
# Evaluación de importancia de características
resultados = permutation_importance(knn, X, y, scoring='accuracy', n_repeats=10, random_state=42)
importancias = resultados.importances_mean
índices_características = np.argsort(importancias)[::-1]
for idx in índices_características:
print(f"Característica {idx}: Importancia {importancias[idx]:.4f}")
Este enfoque calcula la disminución en la precisión del modelo al permutar aleatoriamente los valores de cada característica. Las características que causan una mayor reducción en el rendimiento son consideradas más importantes.
Interpretación de probabilidades y métricas
El análisis de las probabilidades predichas y las métricas de evaluación ayuda a interpretar la confianza del modelo en sus predicciones. Mediante el método predict_proba
, se pueden obtener las probabilidades asignadas a cada clase para una instancia dada.
# Predicción de probabilidades para el conjunto de prueba
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
y_proba = knn.predict_proba(X_test)
print("Reporte de clasificación:")
print(classification_report(y_test, y_pred))
El reporte de clasificación muestra métricas como la precisión, recuperación y f1-score para cada clase, lo que permite evaluar el desempeño del modelo y entender en qué clases es más efectivo.
Limitaciones y consideraciones
Es importante tener en cuenta que la interpretabilidad de KNeighborsClassifier puede ser limitada en espacios de alta dimensionalidad o con conjuntos de datos muy grandes. La maldición de la dimensionalidad puede dificultar la visualización y el análisis de los vecinos. Además, el modelo puede ser sensible a la escala de las características, por lo que el escalado adecuado es esencial para interpretaciones precisas.
La exploración de los valores atípicos también es relevante, ya que pueden influir en las predicciones y afectar la interpretabilidad. Identificar y tratar estos valores puede mejorar la comprensión del comportamiento del modelo.
Herramientas adicionales
Para enriquecer la interpretabilidad, se pueden utilizar librerías complementarias como ELI5 o LIME, que ofrecen métodos para explicar las predicciones de modelos de caja negra. Aunque están diseñadas para modelos más complejos, pueden adaptarse para analizar las predicciones de KNN.
Por ejemplo, utilizando LIME:
import lime
from lime.lime_tabular import LimeTabularExplainer
# Creación del explicador
explainer = LimeTabularExplainer(X_train, feature_names=iris.feature_names, class_names=iris.target_names, discretize_continuous=True)
# Selección de una instancia del conjunto de prueba
i = 0
exp = explainer.explain_instance(X_test[i], knn.predict_proba)
# Mostrar la explicación
exp.show_in_notebook(show_table=True)
LIME genera una explicación local de la predicción, indicando qué características contribuyen más a la clasificación de esa instancia específica.
Ejercicios de esta lección Clasificación KNN KNeighborsClassifier
Evalúa tus conocimientos de esta lección Clasificación KNN KNeighborsClassifier con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Todas las lecciones de ScikitLearn
Accede a todas las lecciones de ScikitLearn y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Aprendizaje Automático
Introducción Y Entorno
Introducción E Instalación
Introducción Y Entorno
Introducción Al Preprocesamiento De Datos
Preprocesamiento De Datos
Identificación Y Tratamiento De Valores Faltantes
Preprocesamiento De Datos
Escalado De Datos
Preprocesamiento De Datos
Normalización De Datos
Preprocesamiento De Datos
Codificación De Variables Categóricas
Preprocesamiento De Datos
Ingeniería De Características
Preprocesamiento De Datos
Selección De Características
Preprocesamiento De Datos
Extracción De Características
Preprocesamiento De Datos
Particionamiento De Datos
Preprocesamiento De Datos
Preprocesamiento De Datos Desbalanceados
Preprocesamiento De Datos
Introducción A La Regresión
Regresión
Regresión Lineal
Regresión
Regresión Knn Kneighborsregressor
Regresión
Regresión Svm Con Svr
Regresión
Regresión Con Árboles Decisiontreeregressor
Regresión
Regresión Con Algoritmos De Conjunto
Regresión
Introducción A La Clasificación
Clasificación
Clasificación Con Regresión Logística
Clasificación
Clasificación Knn Kneighborsclassifier
Clasificación
Clasificación Svm Con Svc
Clasificación
Clasificación Con Árboles Decisiontreeclassifier
Clasificación
Clasificación Con Algoritmos De Conjunto
Clasificación
Reducción De La Dimensionalidad Con Pca
Aprendizaje No Supervisado
Clustering Con Kmeans
Aprendizaje No Supervisado
Clustering Jerárquico
Aprendizaje No Supervisado
Clustering De Densidad Con Dbscan
Aprendizaje No Supervisado
Preprocesamiento De Textos Para Nlp
Nlp
Representación De Texto Y Extracción De Características
Nlp
Clasificación De Texto Con Scikit Learn
Nlp
Análisis De Sentimiento
Nlp
Técnicas Avanzadas De Extracción De Características
Nlp
Introducción Al Análisis De Series Temporales
Series Temporales
Preprocesamiento De Datos De Series Temporales
Series Temporales
Ingeniería De Características Para Series Temporales
Series Temporales
Transformación Y Escalado De Series Temporales
Series Temporales
Validación Y Evaluación De Modelos En Series Temporales
Series Temporales
Validación Y Evaluación De Modelos
Validación De Modelos
Técnicas De Validación Cruzada
Validación De Modelos
Métricas De Regresión
Validación De Modelos
Métricas De Clasificación
Validación De Modelos
Ajuste De Hiperparámetros
Validación De Modelos
Introducción A Pipelines
Pipelines Y Despliegue
Creación De Pipelines Básicos
Pipelines Y Despliegue
Preprocesamiento De Datos Con Pipelines
Pipelines Y Despliegue
Pipelines Y Validación Cruzada
Pipelines Y Despliegue
Pipelines Con Columntransformer
Pipelines Y Despliegue
Exportar E Importar Pipelines
Pipelines Y Despliegue
Objetivos de aprendizaje de esta lección
- Comprender el funcionamiento teórico del algoritmo K-Vecinos Más Cercanos (KNN) para clasificación.
- Aprender a preprocesar los datos adecuadamente para utilizar KNN.
- Implementar el modelo
KNeighborsClassifier
de Scikit Learn en problemas de clasificación. - Configurar y ajustar los parámetros del
KNeighborsClassifier
. - Interpretar las predicciones realizadas por el
KNeighborsClassifier
. - Aplicar técnicas para mejorar la interpretabilidad y el rendimiento del modelo.