KNN 算法

将数据绘制散点图

导入包
设置中文显示
将中优差等生和未知学生的成绩以 ndarray 数组的形式储存
分别对四种学生进行散点图绘图,设置不同的颜色图例

算法原理

KNN(K-Nearest Neighbor),即近邻算法。近邻就是个最近的邻居,当需要预测一个未知样本的时候,就由与该样本最接近的个邻居来决定。KNN 既可以用于分类问题,也可以用于回归问题。

  • 当进行分类预测时,使用个邻居中,类别数量最多(或加权最多)者,作为预测结果。
  • 当进行回归预测时,使用个邻居的均值(或加权均值),作为预测结果。

KNN 算法的原理在于,样本映射到多维空间时,相似度较高的样本,其距离也会比较接近,反之,相似度较低的样本,其距离也会比较疏远。我们可以将该算法理解为“近朱者赤,近墨者黑”。

算法超参数

K 值

K 值表示使用周围最近的 K 个邻居来预测当前样本,K 的取值会对模型造成如下的影响

  • 当 K 值较小时,模型会依赖于附近的邻居样本,具有较好的敏感性,但是稳定性会较弱,容易受到异常值的影响,容易过拟合。
  • 当 K 值较大时,稳定性增加,但是敏感性会减弱,容易导致欠拟合。

通常情况下,我们可以通过交叉验证的方式,选择最合适的 K 值。

距离度量方式

默认闵可夫斯基距离

1714961723153

当 P 为 1 时,距离就是曼哈顿距离,当 p 等于 2 时,距离就是欧几里得距离(欧式距离),p 为无穷大是切比雪夫距离

切比雪夫距离:两个点坐标数值差的绝对值的最大值,max(|x1-y1|,|x2-y2|)。

权重计算方式

权重分为两种:

  • 统一权重:所有样本的权重相同
  • 距离加权权重:样本的权重与待测样本的距离成反比,设置为倒数,表示距离越近的影响越大。权重之和为 1,要进行归一化操作。

算法步骤

  1. 确定算法超参数:

    近邻的数量 K、距离度量方式、权重计算方式,其他超参数

  2. 从训练集中选择离待测样本 A 最近的 K 个样本

  3. 根据这 K 个样本预测 A

    对于分类,使用 K 个样本的类别(或加权类别)预测 A,谁的权重高预测为谁

    对于回归,使用 K 个样本目标值(y)的均值(或加权均值)预测 A

对于传统的 KNN,在 knn.if()中实际上什么都没有做,只是接收数据而已,核心内容都是在 predict 过程中做的。但是在现在用的 sklearn 中,训练时还会进行 kd 树的建立

使用 KNN 实现分类

建模预测

n_neighbors:邻居的数量

weights:权重计算方式,可选值为 uniform(统一权重)和 distance(加权权重)

距离度量方式默认 p=2

from sklearn.neighbors import KNeighborsClassifier 
knn=KNeighborsClassifier(n_neighbors=3,weights='uniform')
knn.fit(X_train,y_train)

超参数调整

product 计算 weights 与 ks 笛卡尔集组合,这样就可以使用单层循环取代嵌套循环,增加代码的可读性与可理解性

from itertools import product

product(weights,ks)

绘制决策边界

DecisionBoundaryDisplay 类的参数

from sklearn.inspection import DecisionBoundaryDisplay

estimator:评估器,即模型,指决策边界显示哪个模型的预测结果。

X:数据集,必须为 m 行 2 列的数据。用来确定决策边界的范围。

response_method:模型的预测结果是由什么方式产生的。

alpha:透明度,取值范围为 0-1,0 为完全透明,1 为不透明。

ax:指定绘图的 axes 对象。即在哪个 axes 对象上绘制。

grid_resolution:用于绘制决策边界的网格点数。如果分解曲线不够平滑,可增大该值。

# 超参数对模型的影响
# 颜色映射的类,在可视化时,可以显示不同的颜色主题风格
from matplotlib.colors import ListedColormap
# 绘制决策边界的类
from sklearn.inspection import DecisionBoundaryDisplay 
# 定义绘制决策边界和样本散点图的类
def plot_decision_boundary(model,X,y):
    # 定义y不同类别的颜色与符号,此时有3个分类,因此分别有3个颜色和符号
    color=['r','g','b']
    marker=['o','v','x']
    # 获取数据中不重复的标签,即y中不一样的值
    class_label=np.unique(y)
    # 定义颜色映射,在绘制(填充)等高线时使用,不同的值使用不同的颜色来填充
    # 切片表示获取的颜色个数与y中的标签数相等
    cmap=ListedColormap(color[:len(class_label)])
    # 创建DecisionBoundaryDisplay类的对象,用于显示决策边界。
    DecisionBoundaryDisplay.from_estimator(model,X,response_method='predict',alpha=0.5,cmap=cmap,grid_resolution=100,ax=plt.gca())
    # 绘制样本数据X
    for i,class_ in enumerate(class_label):
        plt.scatter(x=X[y==class_,0],y=X[y==class_,1],c=cmap.colors[i],label=class_,marker=marker[i],s=15)
    plt.legend()
from itertools import product 
# 不同超参数下绘制决策边界
weights=['uniform','distance']
ks=[2,15]
plt.figure(figsize=(12,10))
# 计算weights与ks笛卡尔集组合,这样就可以使用单层循环取代嵌套循环,增加代码的可读性与可理解性
for i,(w,k) in enumerate(product(weights,ks),start=1):
    plt.subplot(2,2,i)
    plt.title(f'K值:{k}权重:{w}')
    knn=KNeighborsClassifier(n_neighbors=k,weights=w)
    # 这里只看决策边界,不考虑模型预测效果,因此使用所有数据训练
    knn.fit(X,y)
    plot_decision_boundary(knn,X,y)

相关帖子

回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...