html
理解分类与回归中的K-最近邻(KNN)算法
目录
1. K-最近邻算法简介
K-最近邻(KNN) 是一种简单但强大的监督机器学习算法,用于分类和回归任务。其核心思想是基于特征空间中与新数据点“最近”的 K 个邻居的标签来预测新数据点的标签。
KNN的优势
- 简单性:易于理解和实现。
- 无需训练阶段:KNN 是一种懒惰学习器,这意味着它不显式训练模型,而是基于整个数据集做出决策。
- 多功能性:适用于各种类型的问题,包括分类、回归,甚至异常检测。
2. KNN的工作原理
KNN 的工作原理基于相似的数据点很可能具有相似的结果。以下是该算法的逐步分解:
数据表示
想象一个二维空间,每个数据点基于两个特征代表一辆车:
- 制造时间(X轴)
- 制造成本(Y轴)
数据点按颜色编码:
- 红点:汽油车
- 蓝点:电动车
距离度量
为了确定数据点的“接近程度”,KNN 使用距离度量。最常用的度量是:
- 欧几里得距离
\[
d(p, q) = \sqrt{\sum_{i=1}^{n} (q_i - p_i)^2}
\]
- 使用场景:数据位于连续空间中。
- 专业提示:欧几里得距离是许多KNN实现中的默认度量,包括scikit-learn。
- 曼哈顿距离
\[
d(p, q) = \sum_{i=1}^{n} |q_i - p_i|
\]
- 使用场景:数据呈网格状,移动限制在水平和垂直路径。
- 闵可夫斯基距离
欧几里得距离和曼哈顿距离的推广。
\[
d(p, q) = \left( \sum_{i=1}^{n} |q_i - p_i|^p \right)^{1/p}
\]
- 当 \( p = 1 \):等同于曼哈顿距离。
- 当 \( p = 2 \):等同于欧几里得距离。
3. 选择合适的“K”值
参数“K”决定了在进行预测时要考虑的邻居数量。选择最佳的“K”值对于KNN算法的性能至关重要。
“K”的影响
- 较小的“K”值(例如,K=1):
- 对噪声更敏感。
- 可能导致过拟合。
- 较大的“K”值(例如,K=20):
- 决策边界更平滑。
- 可能因过于简化数据而欠拟合。
最佳实践
- 交叉验证:使用交叉验证等技术来找到带来最佳准确率的“K”值。
- 奇数:在处理二分类问题时,使用奇数的“K”值有助于避免平票情况。
4. KNN用于分类
在分类中,KNN 将其“K”个最近邻中最常见的类别分配给新的数据点。
示例场景
考虑一个具有特定制造时间和成本的新车数据点。KNN算法将会:
- 计算该点与数据集中所有其他点的距离。
- 识别“K”个最近的邻居。
- 分配类别(电动车或汽油车),基于这些邻居中的多数投票。
对“K”值的敏感性
如转录中所示,不同的“K”值会改变分类结果。例如:
- K=1:新点基于其单个最近邻进行分类。
- K=5:五个邻居中的多数投票决定分类。
5. KNN用于回归
虽然KNN主要用于分类,但它也可以通过预测“K”个最近邻的平均值来执行回归任务。
回归中的挑战
- 过拟合:较低的“K”值可能导致过拟合。
- 欠拟合:较高的“K”值可能使模型过于简化。
实现见解
在提供的Jupyter Notebook中,KNN回归被用于预测钻石价格。以下是简要概述:
- 数据预处理:
- 将分类变量映射为数值。
- 使用标准化对特征进行缩放。
- 模型训练:
- 使用不同“K”值训练KNN回归器以确定最佳性能。
- 评估:
- 在K=4时实现了约98.05%的最高准确率。
- 使用Plotly可视化实际与预测价格以提高可解释性。
6. KNN的优缺点
优点
- 简单直观:易于理解和实现。
- 无需训练阶段:减少了训练过程中的计算成本。
- 适应性强:适用于分类和回归。
缺点
- 计算量大:使用整个数据集进行预测,对于大型数据集可能会很慢。
- 对无关特征敏感:无关或冗余的特征会降低性能。
- 选择“K”值:选择最佳的“K”值可能具有挑战性。
7. 在Python中实现KNN
利用Python的scikit-learn库可以简化KNN的实现。以下是从数据预处理到模型评估的关键步骤概述。
数据预处理
在应用KNN之前,必须准备数据:
- 处理分类变量:
- 使用映射字典将分类文本数据转换为数值。
12345678
cut_dict = {'Fair': 1, 'Good': 2, 'Very Good': 3, 'Premium': 4, 'Ideal': 5}clarity_dict = {'I1': 1, 'SI2': 2, 'SI1': 3, 'VS2': 4, 'VS1': 5, 'VVS2': 6, 'VVS1': 7, 'IF': 8}color_dict = {'D':7, 'E':6, 'F':5, 'G':4, 'H':3, 'I':2, 'J':1} df['cut'] = df['cut'].map(cut_dict)df['clarity'] = df['clarity'].map(clarity_dict)df['color'] = df['color'].map(color_dict)df = df.drop('Unnamed: 0', axis=1)
- 特征缩放:
- 规范化特征集以确保所有特征在距离计算中同等贡献。
123456
from sklearn import preprocessing X = df.drop(['price'], axis=1).valuesX = preprocessing.scale(X)y = df['price'].valuesy = preprocessing.scale(y)
模型训练与评估
- 拆分数据集:
12345678
from sklearn.utils import shuffle df = shuffle(df, random_state=42)test_size = 200X_train = X[:-test_size]y_train = y[:-test_size]X_test = X[-test_size:]y_test = y[-test_size:]
- 训练KNN回归器:
1234567
from sklearn.neighbors import KNeighborsRegressor score = []for k in range(1, 20): clf = KNeighborsRegressor(n_neighbors=k, weights='distance', p=1) clf.fit(X_train, y_train) score.append(clf.score(X_test, y_test))
- 可视化性能:
12345678910111213141516
import plotly.graph_objs as gofrom plotly.offline import iplot trace0 = go.Scatter( y=score, x=np.arange(1, len(score)+1), mode='lines+markers', marker=dict(color='rgb(100, 200, 150)'))layout = go.Layout( title='K Value vs. Accuracy Score', xaxis=dict(title='K Value', tickmode='linear'), yaxis=dict(title='Score'))fig = go.Figure(data=[trace0], layout=layout)iplot(fig, filename='basic-line')
- 确定最佳“K”值:
12
k_max = score.index(max(score)) + 1print(f"At K = {k_max}, Max Accuracy = {max(score) * 100:.2f}%")
输出:
1
At K = 4, Max Accuracy = 98.05%
- 最终模型评估:
1234
clf = KNeighborsRegressor(n_neighbors=50)clf.fit(X_train, y_train)print(clf.score(X_test, y_test))y_pred = clf.predict(X_test)
输出:
1
0.9543611406331687
- 比较实际与预测价格:
1234567891011121314151617181920212223242526
import plotly.graph_objs as gofrom plotly.offline import iplot trace0 = go.Scatter( y=y_test, x=np.arange(200), mode='lines+markers', name='Actual Price', marker=dict(color='rgb(110, 10, 150)')) trace1 = go.Scatter( y=y_pred, x=np.arange(200), mode='lines+markers', name='Predicted Price', line=dict(color='rgb(200, 50, 10)', dash='dot')) layout = go.Layout( xaxis=dict(title='Index'), yaxis=dict(title='Normalized Price')) figure = go.Figure(data=[trace0, trace1], layout=layout)iplot(figure)
此可视化通过叠加实际价格和预测价格值,有助于评估模型的预测准确性。
8. 实际案例
让我们通过提供的Jupyter Notebook,使用Python的scikit-learn库来进行实际实现的演示。
步骤1:导入必要的库
12345678
import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport seaborn as snsfrom sklearn import preprocessing, utilsfrom sklearn.neighbors import KNeighborsRegressorimport plotly.graph_objs as gofrom plotly.offline import iplot
步骤2:加载和探索数据集
1234
df = pd.read_csv('diamonds.csv')print(df.head())sns.FacetGrid(df, hue='cut', height=6).map(sns.distplot, 'price').add_legend()plt.show()
步骤3:数据预处理
将分类变量转换为数值并对特征进行缩放。
12345678
cut_dict = {'Fair': 1, 'Good': 2, 'Very Good': 3, 'Premium': 4, 'Ideal': 5}clarity_dict = {'I1': 1, 'SI2': 2, 'SI1': 3, 'VS2': 4, 'VS1': 5, 'VVS2': 6, 'VVS1': 7, 'IF': 8}color_dict = {'D':7, 'E':6, 'F':5, 'G':4, 'H':3, 'I':2, 'J':1} df['cut'] = df['cut'].map(cut_dict)df['clarity'] = df['clarity'].map(clarity_dict)df['color'] = df['color'].map(color_dict)df = df.drop('Unnamed: 0', axis=1)
步骤4:特征缩放与数据打乱
12345
df = utils.shuffle(df, random_state=42)X = df.drop(['price'], axis=1).valuesX = preprocessing.scale(X)y = df['price'].valuesy = preprocessing.scale(y)
步骤5:拆分数据集
12345
test_size = 200X_train = X[:-test_size]y_train = y[:-test_size]X_test = X[-test_size:]y_test = y[-test_size:]
步骤6:训练KNN回归器并评估性能
12345
score = []for k in range(1, 20): clf = KNeighborsRegressor(n_neighbors=k, weights='distance', p=1) clf.fit(X_train, y_train) score.append(clf.score(X_test, y_test))
步骤7:可视化准确率得分
12345678910111213
trace0 = go.Scatter( y=score, x=np.arange(1, len(score)+1), mode='lines+markers', marker=dict(color='rgb(100, 200, 150)'))layout = go.Layout( title='K Value vs. Accuracy Score', xaxis=dict(title='K Value', tickmode='linear'), yaxis=dict(title='Score'))fig = go.Figure(data=[trace0], layout=layout)iplot(fig, filename='basic-line')
步骤8:确定最佳“K”值
12
k_max = score.index(max(score)) + 1print(f"At K = {k_max}, Max Accuracy = {max(score) * 100:.2f}%")
步骤9:最终模型训练与预测
1234
clf = KNeighborsRegressor(n_neighbors=50)clf.fit(X_train, y_train)print(clf.score(X_test, y_test))y_pred = clf.predict(X_test)
步骤10:比较实际值与预测值
1234567891011121314151617181920212223
trace0 = go.Scatter( y=y_test, x=np.arange(200), mode='lines+markers', name='Actual Price', marker=dict(color='rgb(110, 10, 150)')) trace1 = go.Scatter( y=y_pred, x=np.arange(200), mode='lines+markers', name='Predicted Price', line=dict(color='rgb(200, 50, 10)', dash='dot')) layout = go.Layout( xaxis=dict(title='Index'), yaxis=dict(title='Normalized Price')) figure = go.Figure(data=[trace0, trace1], layout=layout)iplot(figure)
生成的图表直观地展示了KNN模型基于所选“K”值预测钻石价格的效果。
9. 结论
K-最近邻算法是一种多功能且简单的机器学习工具,适用于分类和回归的各种应用。其效果在很大程度上取决于“K”值和所使用的距离度量。适当的数据预处理和特征缩放是提高模型性能的关键步骤。虽然对于大型数据集,KNN计算密集,但其简单性使其成为机器学习从业者的绝佳起点。
10. 参考文献
我们希望本指南能让您对K-最近邻算法有清晰的理解。敬请期待更多深入的教程和有关机器学习技术的见解。