机器学习入门:K近邻算法(KNN)——从原理到双案例实战

前言:本文是"机器学习入门"系列的第一站。上一篇我们介绍了机器学习的基础概念,本篇正式进入第一个监督学习算法——K近邻(KNN)。KNN是机器学习中最直观、最容易上手的算法,其思想简单到可以用一句话概括,但背后涉及的特征标准化却是新手最容易踩的坑。本文将系统讲解KNN原理、欧式距离、核心超参数,重点对比0-1标准化与Z-score标准化两种预处理方法,最后通过新生数据集三分类和鸢尾花二分类两个实战案例完整落地。

一、内容概述

本篇分为四大核心模块:

模块内容
机器学习基础监督/无监督学习、分类与回归任务区分
KNN算法详解核心思想、欧式距离、超参数、优缺点
特征标准化0-1最小最大标准化、Z-score零均值标准化原理与对比
双案例实战新生特征三分类 + 鸢尾花二分类

二、机器学习基础

2.1 什么是机器学习

无需人工编写全部判定规则,让计算机从大量已有数据中自动挖掘内在规律,使用学到的规律对全新未知样本完成预测判断。

2.2 监督学习 vs 无监督学习

类型数据特点典型任务
监督学习数据集同时包含特征与真实标签,模型学习特征到类别的映射分类、回归
无监督学习仅存在特征数据,无任何标签,自动发现数据内在结构聚类、降维

2.3 分类 vs 回归

  • 分类:输出离散固定类别(如新生学习程度1/2/3、鸢尾品种0/1)
  • 回归:输出连续数值(如房价预测、气温预测)

三、KNN算法原理

3.1 核心思想

近邻相似原则:未知样本的类别由距离它最近的K个样本投票决定,K个样本中数量最多的类别即为该样本的预测结果。

3.2 欧式距离

欧式距离是KNN衡量样本间相似度的依据,适用于任意维度特征。两个样本的特征数值差值越小,代表它们越相似。

二维平面:两点 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) ( x 2 , y 2 ) (x_2, y_2) (x2,y2) 之间的距离为:

d = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 d = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2} d=(x1x2)2+(y1y2)2

三维空间:两点 ( x 1 , y 1 , z 1 ) (x_1, y_1, z_1) (x1,y1,z1) ( x 2 , y 2 , z 2 ) (x_2, y_2, z_2) (x2,y2,z2) 之间的距离为:

d = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 + ( z 1 − z 2 ) 2 d = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2 + (z_1 - z_2)^2} d=(x1x2)2+(y1y2)2+(z1z2)2

推广到 n n n 维空间:对于两个样本点 A = ( a 1 , a 2 , . . . , a n ) A = (a_1, a_2, ..., a_n) A=(a1,a2,...,an) B = ( b 1 , b 2 , . . . , b n ) B = (b_1, b_2, ..., b_n) B=(b1,b2,...,bn),欧式距离为:

d ( A , B ) = ∑ i = 1 n ( a i − b i ) 2 d(A, B) = \sqrt{\sum_{i=1}^{n} (a_i - b_i)^2} d(A,B)=i=1n(aibi)2

其中 n n n 为特征数量。

直观理解

  • 二维:平面上两点的直线距离(勾股定理)
  • 三维:空间中两点的空间直线距离(两次勾股定理)
  • 高维:虽然无法直接可视化,但数学形式完全一致,逐维度求差平方和再开方

在KNN中的作用:两个样本的欧式距离越小,表示它们在特征空间中越接近,相似度越高。KNN正是通过计算待预测样本与所有训练样本的距离,找出最近的K个邻居进行投票决策。

3.3 常用参数与方法

核心参数
参数类型说明
n_neighborsint,默认5即K值,投票时选取距离最近的K个样本。K越小模型越复杂(易过拟合),K越大模型越平滑(易欠拟合)
weightsstr 或 callable,默认uniform权重策略:uniform(等权投票)/ distance(距离加权投票)/ 自定义函数
algorithmstr,默认auto搜索算法:auto(自动)/ brute(暴力)/ kd_tree / ball_tree
leaf_sizeint,默认30KD树/球树的叶子节点大小,影响建树和查询速度
pint,默认2距离度量参数:p=1为曼哈顿距离,p=2为欧式距离(最常用)
metricstr 或 callable,默认minkowski距离度量方式,常用euclideanmanhattanminkowski
n_jobsint,默认None并行计算核心数,-1表示使用全部CPU
常用方法
方法作用
fit(X_train, y_train)训练模型,存储训练数据(KNN为惰性算法,不进行显式训练)
predict(X_test)预测类别,支持单样本或批量输入
predict_proba(X_test)预测各类别概率,返回形状为 (n_samples, n_classes)
score(X_test, y_test)计算测试集上的分类准确率
kneighbors(X, n_neighbors=None)返回K个最近邻的[距离, 下标],可用于异常检测或模型解释

常用属性

  • classes_:训练集中所有类别标签
  • n_samples_fit_:训练集样本数量
  • effective_metric_:实际使用的距离度量

3.4 KNN的优缺点

优点缺点
原理简单,易于理解,上手门槛低惰性算法,预测时才计算全部样本距离,海量数据运行慢
小数据集上效果稳定对特征数值区间极度敏感,必须做标准化预处理

3.5 KNN的典型应用场景

KNN的原理虽然简单,但在实际业务中仍有广泛的应用:

应用领域具体场景原因
推荐系统根据用户历史行为推荐相似商品(“买了这个的人也买了…”)KNN天然适合找相似用户/相似物品
图像识别手写数字识别、人脸识别(低精度场景)图像特征可转化为数值向量,直接算距离
异常检测信用卡欺诈检测中的离群点发现正常样本形成密集簇,异常样本距离簇中心较远
文本分类新闻自动分类、邮件过滤配合TF-IDF将文本转为向量后使用

优点:KNN在这些场景中的优势是无需训练、可解释性强——你可以直接解释“为什么把这封邮件归为垃圾邮件?因为它和这3封已知垃圾邮件最像。”

四、特征标准化

问题背景

不同特征的数值取值范围可能相差巨大。以新生数据集为例:旅行里程的数值量级为几万,而游戏占比和零食重量仅在0~10之间。若直接计算欧式距离,量级大的特征将完全主导距离,导致模型判断严重失真。因此,建模前必须对特征进行标准化处理。

4.1 0-1标准化(最小最大标准化)

X n e w = X − X m i n X m a x − X m i n X_{new} = \frac{X - X_{min}}{X_{max} - X_{min}} Xnew=XmaxXminXXmin

  • 作用:将所有特征数值压缩至 [ 0 , 1 ] [0, 1] [0,1] 区间
  • 适用场景:数据分布平稳、无极端离群点;神经网络训练中常用
  • 缺点:高度依赖最大最小值,存在异常值时数据会被严重挤压

4.2 Z-score标准化(零均值标准化)

X n e w = X − μ σ X_{new} = \frac{X - \mu}{\sigma} Xnew=σXμ

其中 μ \mu μ 为特征均值, σ \sigma σ 为标准差。

  • 作用:转换后特征均值为0、方差为1,消除量纲影响
  • 适用场景:数据存在离群点、近似正态分布;距离类算法(如KNN)优先选用

4.3 对比总结

对比维度0-1标准化Z-score标准化
依赖指标极值( X m i n , X m a x X_{min}, X_{max} Xmin,Xmax均值、方差
输出范围 [ 0 , 1 ] [0, 1] [0,1]无固定范围
抗干扰能力
KNN适用性一般优先选用

五、实战一:大一新生学习程度三分类

5.1 数据集说明

数据集记录了高年级学生统计的大一新生日常行为数据,用于预测其学习态度:

特征说明
第1列每年外出旅行总里程数值量级为几万
第2列每日玩游戏时间占比数值范围 0~10
第3列每周零食食用重量(磅)数值范围 0~10
第4列学习程度标签1:热爱学习;2:程度中等;3:不爱学习

数据节选

40920	8.326976	0.953952	3
14488	7.153469	1.673904	2
26052	1.441871	0.805124	1
75136	13.147394	0.428964	1
38344	1.669788	0.134296	1

5.2 3D可视化

将三类学生用不同颜色和标记在三维空间中展示,直观观察数据分布:

import matplotlib.pyplot as plt
import numpy as np

# 设置支持中文的字体
plt.rcParams["font.family"] = ["SimHei", "Microsoft YaHei"]
# 解决负号显示方块问题
plt.rcParams["axes.unicode_minus"] = False
# 读取新生特征数据集
data = np.loadtxt("datingTestSet2.txt")
# 按学习标签筛选三类学生
data_1 = data[data[:, -1] == 1]
data_2 = data[data[:, -1] == 2]
data_3 = data[data[:, -1] == 3]

# 创建3D画布
fig = plt.figure()
ax = plt.axes(projection='3d')
# 不同类别区分颜色与标记
ax.scatter(data_1[:, 0], data_1[:, 1], data_1[:, 2], c='red', marker='o')
ax.scatter(data_2[:, 0], data_2[:, 1], data_2[:, 2], c='blue', marker='+')
ax.scatter(data_3[:, 0], data_3[:, 1], data_3[:, 2], c='green', marker='*')
# 设置坐标轴对应特征含义
ax.set(xlabel='每年旅行里程', ylabel='每周游戏占比', zlabel='每周零食重量')
plt.show()

可视化结果

5.3 KNN建模与预测

from sklearn.neighbors import KNeighborsClassifier
import numpy as np

# 加载新生数据集
data = np.loadtxt("datingTestSet2.txt")
X = data[:, 0:3]  # 提取前三列行为特征
y = data[:, -1]   # 学习程度标签

# 构建KNN模型:K=9,距离加权缓解量纲差异
neigh = KNeighborsClassifier(n_neighbors=9, weights='distance', algorithm='auto')
# 模型训练
neigh.fit(X, y)

# 单个新生样本预测
print("单新生样本预测:")
print(neigh.predict([[41520, 8.226976, 0.863952]]))

# 批量多名新生样本统一预测
print("多人同时预测:")
datas = [
    [38889, 3.356611, 0.324231],
    [72015, 7.754558, 0.194551],
    [19149, 3.5749649, 1.531514],
    [30902, 7.354335, 0.037532],
    [7915, 0.000580, 1.170397],
    [11145, 3.41107, 0.685838],
    [68616, 9.978715, 0.669787],
    [26175, 10.544102, 0.754127]
]
print(neigh.predict(datas))

案例一结果

本数据集旅行里程与其余特征量纲差距极大。本次通过 weights=‘distance’ 临时缓解偏差,规范流程中必须先做标准化。

六、实战二:鸢尾花二分类

6.1 数据集说明

数据集分为训练集和测试集两份Excel文件:

  • 特征:萼片长(cm)、萼片宽(cm)、花瓣长(cm)、花瓣宽(cm)
  • 标签type_num(0、1,代表两种鸢尾花)

测试集预览(9条样本)

样本萼片长(cm)萼片宽(cm)花瓣长(cm)花瓣宽(cm)类型_num
1425.82.75.11.90
776.73.05.01.71
1106.53.25.12.00
1266.22.84.81.80
1456.73.05.22.30
1495.93.05.11.80
805.52.43.81.11
535.52.34.01.31
916.13.04.61.41

6.2 建模与评估

import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import scale

train_data = pd.read_excel("鸢尾花训练数据.xlsx")
test_data = pd.read_excel("鸢尾花测试数据.xlsx")

train_X = train_data[["萼片长(cm)", "萼片宽(cm)", "花瓣长(cm)", "花瓣宽(cm)"]]
train_y = train_data["类型_num"]

# Z-score标准化
train_scaled = pd.DataFrame()
train_scaled['萼片长'] = scale(train_X['萼片长(cm)'])
train_scaled['萼片宽'] = scale(train_X['萼片宽(cm)'])
train_scaled['花瓣长'] = scale(train_X['花瓣长(cm)'])
train_scaled['花瓣宽'] = scale(train_X['花瓣宽(cm)'])

knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(train_scaled, train_y)

print("训练集准确率:", knn.score(train_scaled, train_y))

# 测试集处理
test_X = test_data[["萼片长(cm)", "萼片宽(cm)", "花瓣长(cm)", "花瓣宽(cm)"]]
test_y = test_data["类型_num"]
test_scaled = pd.DataFrame()
test_scaled['萼片长'] = scale(test_X['萼片长(cm)'])
test_scaled['萼片宽'] = scale(test_X['萼片宽(cm)'])
test_scaled['花瓣长'] = scale(test_X['花瓣长(cm)'])
test_scaled['花瓣宽'] = scale(test_X['花瓣宽(cm)'])

print("测试集预测类别:", knn.predict(test_scaled))
print("测试集准确率:", knn.score(test_scaled, test_y))

工业标准中,测试集应使用训练集的均值和方差进行缩放,而非独立标准化。此处为课堂练习简化,实际生产请使用 StandardScaler。

七、两组数据集对比

对比项新生数据集鸢尾花数据集
特征数量3维行为特征4维形态特征
分类任务三分类(学习程度1/2/3)二分类(鸢尾品种0/1)
预处理方案未标准化,用距离加权临时弱化量纲Z-score标准化
文件格式纯文本txtExcel表格
可视化3D散点图可直观展示四维特征无法直接绘图

八、总结

核心脉络

KNN核心思想(近邻投票)
		↓
距离计算(欧式距离)
		↓
特征标准化(消除量纲差异)
		↓
模型训练与预测
		↓
效果评估(准确率)

三个关键要点

  1. KNN是惰性算法,训练阶段仅存储数据,预测时才计算距离,适合小数据集
  2. 特征标准化是KNN建模的前置必要条件,Z-score优于0-1标准化
  3. 模型评估必须以测试集准确率为准,训练集准确率不能反映泛化能力

系列直达

更多推荐