想象一下,有一种方法可以通过查看以前见过的最相似的例子来进行预测。这就是近邻分类器的精髓--一种简单而直观的算法,为机器学习带来了现实世界的逻辑。
虚拟分类器设定了最低的性能标准,而近邻分类器则模仿了我们在日常生活中经常做出决策的方式:回忆过去类似的经历。这就像通过询问邻居今天的天气如何,来决定自己应该穿什么衣服一样。在数据科学领域,这种分类器会检查最接近的数据点来进行预测。
定义
K 近邻分类器是一种机器学习模型,它根据特征空间中 K 个最近数据点的多数类别进行预测。KNN 算法假定相似的事物存在于近处,因此直观易懂。
使用的数据集
在本文中,我们将以这个简单的人工高尔夫数据集为例。该数据集根据天气条件预测一个人是否会打高尔夫。它包括前景、温度、湿度和风力等特征,目标变量是是否打高尔夫球。
# Import libraries
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import pandas as pd
import numpy as np
# Make the dataset
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rainy', 'rainy', 'rainy', 'overcast', 'sunny', 'sunny', 'rainy', 'sunny', 'overcast', 'overcast', 'rainy', 'sunny', 'overcast', 'rainy', 'sunny', 'sunny', 'rainy', 'overcast', 'rainy', 'sunny', 'overcast', 'sunny', 'overcast', 'rainy', 'overcast'],
'Temperature': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0, 72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0, 81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0, 73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humidity': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0, 70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0, 92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0, 65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False, False, False, True, True, False, True, True, False, False, True, False, True, True, False, True, False, False, True, False, False],
'Play': ['No', 'No', 'Yes', 'Yes', 'Yes', 'No', 'Yes', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'No', 'Yes', 'Yes', 'No', 'No', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'Yes']
}
original_df = pd.DataFrame(dataset_dict)
print(original_df)
KNN 算法要求首先对数据进行缩放。将分类列转换为 0 和 1,并对数字特征进行缩放,这样就不会有单一特征主导距离度量。
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# Preprocess data
df = pd.get_dummies(original_df, columns=['Outlook'], prefix='', prefix_sep='', dtype=int)
df['Wind'] = df['Wind'].astype(int)
df['Play'] = (df['Play'] == 'Yes').astype(int)
df = df[['sunny','rainy','overcast','Temperature','Humidity','Wind','Play']]
# Split data and standardize features
X, y = df.drop(columns='Play'), df['Play']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, shuffle=False)
scaler = StandardScaler()
float_cols = X_train.select_dtypes(include=['float64']).columns
X_train[float_cols] = scaler.fit_transform(X_train[float_cols])
X_test[float_cols] = scaler.transform(X_test[float_cols])
# Print results
print(pd.concat([X_train, y_train], axis=1).round(2), '\n')
print(pd.concat([X_test, y_test], axis=1).round(2), '\n')
主要机制
KNN 分类器的工作原理是找到新数据点的 K 个近邻,然后投票选出这些近邻中最常见的类别。工作原理如下:
训练步骤
与许多其他算法不同,KNN 没有明显的训练阶段。相反,它会记忆整个训练数据集。具体过程如下:
1. 为 K(需要考虑的邻居数量)选择一个值。
from sklearn.neighbors import KNeighborsClassifier
# Select the Number of Neighbors ('k')
k = 5
2. 选择一个距离度量(如欧氏距离、曼哈顿距离)。
import numpy as np
# Choose a Distance Metric
distance_metric = 'euclidean'
# Trying to calculate distance between ID 0 and ID 1
print(np.linalg.norm(X_train.loc[0].values - X_train.loc[1].values))
3. 存储/记忆所有训练数据点及其相应标签)。
# Initialize the k-NN Classifier
knn_clf = KNeighborsClassifier(n_neighbors=k, metric=distance_metric)
# "Train" the kNN (although no real training happens)
knn_clf.fit(X_train, y_train)
分类步骤
近邻分类器一旦经过 “训练”(即存储了训练数据),就会对新实例进行预测:
1. 距离计算: 对于新实例,使用所选的距离度量计算其与所有已存储的训练实例的距离。
from scipy.spatial import distance
# Compute the distances from the first row of X_test to all rows in X_train
distances = distance.cdist(X_test.iloc[0:1], X_train, metric='euclidean')
# Create a DataFrame to display the distances
distance_df = pd.DataFrame({
'Train_ID': X_train.index,
'Distance': distances[0].round(2),
'Label': y_train
}).set_index('Train_ID')
print(distance_df.sort_values(by='Distance'))
2. 邻居选择和预测: 根据计算出的距离找出 K 个最近的邻居,然后将这些邻居中最常见的类别作为新实例的预测类别。
# Use the k-NN Classifier to make predictions
y_pred = knn_clf.predict(X_test)
print("Label :",list(y_test))
print("Prediction:",list(y_pred))
评估步骤
from sklearn.metrics import accuracy_score
# Evaluation Phase
accuracy = accuracy_score(y_test, y_pred)
print(f'Accuracy: {accuracy.round(4)*100}%')
关键参数
虽然 KNN 在概念上很简单,但它确实有几个重要参数:
1. K:要考虑的邻居数量。较小的 K 会导致对噪声敏感的结果,而较大的 K 则会使决策边界变得平滑。
labels, predictions, accuracies = list(y_test), [], []
k_list = [3, 5, 7]
for k in k_list:
knn_clf = KNeighborsClassifier(n_neighbors=k)
knn_clf.fit(X_train, y_train)
y_pred = knn_clf.predict(X_test)
predictions.append(list(y_pred))
accuracies.append(accuracy_score(y_test, y_pred).round(4)*100)
df_predictions = pd.DataFrame({'Label': labels})
for k, pred in zip(k_list, predictions):
df_predictions[f'k = {k}'] = pred
df_accuracies = pd.DataFrame({'Accuracy ': accuracies}, index=[f'k = {k}' for k in k_list]).T
print(df_predictions)
print(df_accuracies)
2. 距离度量:决定点间相似度的计算方式。常见选项包括:
3. 加权函数: 这决定了如何对每个邻居的贡献进行加权。选项包括
总结
K-Nearest Neighbors (KNN) 分类器是机器学习的基本算法,为分类任务提供了直观有效的方法。它的简单性使其成为初学者的理想起点,而它的多功能性则确保了其对经验丰富的数据科学家的价值。KNN 的强大之处在于它能够根据数据点的接近程度进行预测,而无需复杂的训练过程。
不过,重要的是要记住,KNN 只是庞大的机器学习工具包中的一个工具。当你在数据科学之旅中取得进步时,请将 KNN 作为了解更复杂算法的垫脚石,在选择模型时始终考虑你的具体数据特征和问题要求。
k 近邻分类器代码汇总
# Import libraries
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
# Load data
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rainy', 'rainy', 'rainy', 'overcast', 'sunny', 'sunny', 'rainy', 'sunny', 'overcast', 'overcast', 'rainy', 'sunny', 'overcast', 'rainy', 'sunny', 'sunny', 'rainy', 'overcast', 'rainy', 'sunny', 'overcast', 'sunny', 'overcast', 'rainy', 'overcast'],
'Temperature': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0, 72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0, 81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0, 73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humidity': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0, 70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0, 92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0, 65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False, False, False, True, True, False, True, True, False, False, True, False, True, True, False, True, False, False, True, False, False],
'Play': ['No', 'No', 'Yes', 'Yes', 'Yes', 'No', 'Yes', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'No', 'Yes', 'Yes', 'No', 'No', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'Yes']
}
df = pd.DataFrame(dataset_dict)
# Preprocess data
df = pd.get_dummies(df, columns=['Outlook'], prefix='', prefix_sep='', dtype=int)
df['Wind'] = df['Wind'].astype(int)
df['Play'] = (df['Play'] == 'Yes').astype(int)
# Split data
X, y = df.drop(columns='Play'), df['Play']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, shuffle=False)
# Standardize features
scaler = StandardScaler()
float_cols = X_train.select_dtypes(include=['float64']).columns
X_train[float_cols] = scaler.fit_transform(X_train[float_cols])
X_test[float_cols] = scaler.transform(X_test[float_cols])
# Train model
knn_clf = KNeighborsClassifier(n_neighbors=3, metric='euclidean')
knn_clf.fit(X_train, y_train)
# Predict and evaluate
y_pred = knn_clf.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred)}")