机器学习入门:K近邻算法(KNN)——从原理到双案例实战
机器学习入门: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=(x1−x2)2+(y1−y2)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=(x1−x2)2+(y1−y2)2+(z1−z2)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=1∑n(ai−bi)2
其中 n n n 为特征数量。
直观理解:
- 二维:平面上两点的直线距离(勾股定理)
- 三维:空间中两点的空间直线距离(两次勾股定理)
- 高维:虽然无法直接可视化,但数学形式完全一致,逐维度求差平方和再开方
在KNN中的作用:两个样本的欧式距离越小,表示它们在特征空间中越接近,相似度越高。KNN正是通过计算待预测样本与所有训练样本的距离,找出最近的K个邻居进行投票决策。
3.3 常用参数与方法
核心参数
| 参数 | 类型 | 说明 |
|---|---|---|
n_neighbors | int,默认5 | 即K值,投票时选取距离最近的K个样本。K越小模型越复杂(易过拟合),K越大模型越平滑(易欠拟合) |
weights | str 或 callable,默认uniform | 权重策略:uniform(等权投票)/ distance(距离加权投票)/ 自定义函数 |
algorithm | str,默认auto | 搜索算法:auto(自动)/ brute(暴力)/ kd_tree / ball_tree |
leaf_size | int,默认30 | KD树/球树的叶子节点大小,影响建树和查询速度 |
p | int,默认2 | 距离度量参数:p=1为曼哈顿距离,p=2为欧式距离(最常用) |
metric | str 或 callable,默认minkowski | 距离度量方式,常用euclidean、manhattan、minkowski |
n_jobs | int,默认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=Xmax−XminX−Xmin
- 作用:将所有特征数值压缩至 [ 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 |
|---|---|---|---|---|---|
| 142 | 5.8 | 2.7 | 5.1 | 1.9 | 0 |
| 77 | 6.7 | 3.0 | 5.0 | 1.7 | 1 |
| 110 | 6.5 | 3.2 | 5.1 | 2.0 | 0 |
| 126 | 6.2 | 2.8 | 4.8 | 1.8 | 0 |
| 145 | 6.7 | 3.0 | 5.2 | 2.3 | 0 |
| 149 | 5.9 | 3.0 | 5.1 | 1.8 | 0 |
| 80 | 5.5 | 2.4 | 3.8 | 1.1 | 1 |
| 53 | 5.5 | 2.3 | 4.0 | 1.3 | 1 |
| 91 | 6.1 | 3.0 | 4.6 | 1.4 | 1 |
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标准化 |
| 文件格式 | 纯文本txt | Excel表格 |
| 可视化 | 3D散点图可直观展示 | 四维特征无法直接绘图 |
八、总结
核心脉络
KNN核心思想(近邻投票)
↓
距离计算(欧式距离)
↓
特征标准化(消除量纲差异)
↓
模型训练与预测
↓
效果评估(准确率)
三个关键要点:
- KNN是惰性算法,训练阶段仅存储数据,预测时才计算距离,适合小数据集
- 特征标准化是KNN建模的前置必要条件,Z-score优于0-1标准化
- 模型评估必须以测试集准确率为准,训练集准确率不能反映泛化能力
系列直达:
- 上一篇:无(本文为首篇)
- 本篇:机器学习入门:K近邻算法(KNN)——从原理到双案例实战(本文)
- 下篇:机器学习入门:线性回归——从极大似然估计到最小二乘法
更多推荐
所有评论(0)