KNN算法深度解析:从决策边界可视化到鸢尾花分类实战
目录
- 前言
- 一、KNN决策边界可视化:
- 二、决策边界
- 2.1 使用Scikit-learn 实现决策边界代码
- 三、Scikit-learn实现 KNN算法:
- 3.1 代码步骤
- 3.2 k-近邻算法API
- 3.2.1 KNeighborsClassifier
- 3.2.2 fit(机器学中一般都称为训练)
- 3.2.3 get_params
- 3.2.4 kneighbors
- 3.2.5 predict
- 3.2.6 predict_proba
- 3.2.7 score
- 四、 KNN算法实现鸢尾花数据集分类
- 4.1 数据集介绍
- 4.2 Scikit-Learn中的数据集介绍
- 4.2.1 sklearn数据返回值介绍
- 4.3 数据集划分
- 4.3.1 使用鸢尾花数据集进行划分数据集代码
- 五、特征预处理
- 5.1 什么是特征预处理
- 5.1.1 特征预处理定义
- 5.1.2 为什么我们要进行归⼀化/标准化?
- 5.1.3 包含内容(数值型数据的无量纲化)
- 5.2 最大最小 归一化
- 5.2.1 定义
- 5.2.2 公式
- 5.2.3 最大值最小值归一化总结
- 5.3 标准化 归一化
- 5.3.1 定义
- 5.3.2 公式
- 5.3.3 标准差进行归一化代码
- 六、使用KNN实现对鸢尾花数据集进行分类的代码
- 总结
前言
书接上文
从基础到实践:全面解析机器学习与KNN算法的核心原理与应用-CSDN博客文章浏览阅读121次。本文涵盖机器学习的核心概念、KNN算法原理与实现、应用场景及未来挑战,结合代码示例和可视化分析,帮助读者掌握基础算法并理解其实际应用价值。https://blog.csdn.net/qq_58364361/article/details/147134417?spm=1011.2415.3001.10575&sharefrom=mp_manage_link
一、KNN决策边界可视化:
原始图 K=1异常的数据点(蓝色中的红色点)
创造出了一个非正常的预测区域(孤岛)
K=3 孤岛消失了,决策边界更加平滑, K=5孤岛消失了,决策边界更加平滑
针对测试数据的泛化能力更好。 ,针对测试数据的泛化能力更好。
二、决策边界
定义:
决策边界是分类算法中用于区分不同类别的虚拟边界,通俗讲就是在什么范围内归为当前类。
边界效果:
决策边界是否合理,直接影响到分类效果的好坏。
KNN与决策边界:
KNN算法通过计算待分类样本与已知样本之间的距离,找到最近的K个样本,并根据这些样本的类型信息进行投票,以确定待分类样本的类别。
如何绘制决策边界:
本文中为了绘制出决策边界,每隔距离x取一个点(预测数据点),预测的数据点是二维坐标中所有的点
2.1 使用Scikit-learn 实现决策边界代码
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
import matplotlib.pyplot as plt
# 定义三个类别的样本点坐标数据(每个类别7个二维坐标点)
point1 = np.array([[7.7, 6.1], [3.1, 5.9], [8.6, 8.8], [9.5, 7.3], [3.9, 7.4], [5.0, 5.3], [1.0, 7.3]]) # 类别0的蓝色三角形点
point2 = np.array([[0.2, 2.2], [4.5, 4.1], [0.5, 1.1], [2.7, 3.0], [4.7, 0.2], [2.9, 3.3], [7.3, 7.9]]) # 类别1的红色星形点
point3 = np.array([[9.2, 0.7], [9.2, 2.1], [7.3, 4.5], [8.9, 2.9], [9.5, 3.7], [7.7, 3.7], [9.4, 2.4]]) # 类别2的黄色方块点
# 合并所有坐标点(21×2数组)
point = np.concatenate((point1, point2, point3), axis=0)
# 创建对应标签(类别0/1/2)
label = np.concatenate(
(
np.zeros(len(point1)), # 前7个点标记为类别0
np.ones(len(point2)), # 中间7个点标记为类别1
np.ones(len(point3)) + 1 # 最后7个点标记为类别2
), axis=0
)
# 初始化KNN分类器(5近邻,暴力搜索)
knn = KNeighborsClassifier(algorithm="brute")
knn.fit(point, label) # 训练模型
# 创建测试网格(100×100)
x, y = np.meshgrid(
np.linspace(0, 10, 100),
np.linspace(0, 10, 100)
)
# 展平并合并坐标(10000×2数组)
xy = np.c_[x.ravel(), y.ravel()]
# 预测网格点类别
yc = knn.predict(xy)
# 可视化
fig, ax = plt.subplots(figsize=(5, 5))
# 绘制决策边界(将预测结果还原为网格形状)
ax.contour(x, y, yc.reshape(x.shape))
# 绘制原始数据点
ax.scatter(point[label == 0, 0], point[label == 0, 1], c='b', marker='^') # 蓝三角
ax.scatter(point[label == 1, 0], point[label == 1, 1], c='r', marker='*') # 红星
ax.scatter(point[label == 2, 0], point[label == 2, 1], c='y', marker='s') # 黄方
plt.show()
三、Scikit-learn实现 KNN算法:
3.1 代码步骤
- 获取数据集
- 数据基本处理(该案例中省略此步骤)
- 特征工程(该案例中省略此步骤)
- 机器学习
- 模型评估(该案例中省略此步骤)
3.2 k-近邻算法API
3.2.1 KNeighborsClassifier
class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, *, weights='uniform', algorithm='auto', leaf_size=30, p=2, metric='minkowski', metric_params=None, n_jobs=None)
参数说明:
@ n_neighbors: int, 可选参数(默认为 5)用于kneighbors查询的默认邻居的数量
@ weights(权重): str or callable(自定义类型), 可选参数(默认为 ‘uniform’)
用于预测的权重函数。可选参数如下:
‘uniform’ : 统一的权重. 在每一个邻居区域里的点的权重都是一样的。
‘distance’ : 权重点等于他们距离的倒数。使用此函数,更近的邻居对于所预测的点的影响更大。
[callable] : 一个用户自定义的方法,此方法接收一个距离的数组,然后返回一个相同形状并且包含权重的数组。
@ algorithm(算法): {‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, 可选参数(默认为 ‘auto’)
计算最近邻居用的算法:
‘ball_tree’ 是为了克服kd树高维失效而发明的,其构造过程是以质心C和半径r分割样本空间,每个节点是一个超球体。
‘kd_tree’ 构造kd树存储数据以便对其进行快速检索的树形数据结构,kd树也就是数据结构中的二叉树。以中值切分构造的树,每个结点是一个超矩形,在维数小于20时效率高。
‘brute’ 使用暴力搜索.也就是线性扫描,当训练集很大时,计算非常耗时
‘auto’ 会基于传入fit方法的内容,选择最合适的算法。
@ leaf_size(叶子数量): int, 可选参数(默认为 30)
传入BallTree或者KDTree算法的叶子数量。此参数会影响构建、查询BallTree或者KDTree的速度,以及存储BallTree或者KDTree所需要的内存大小。 此可选参数根据是否是问题所需选择性使用。
@ p: integer, 可选参数(默认为 2)
用于Minkowski metric(闵可夫斯基空间)的超参数。p = 1, 相当于使用曼哈顿距离 (l1),p = 2, 相当于使用欧几里得距离(l2) 对于任何 p ,使用的是闵可夫斯基空间(l_p)
@ metric(矩阵): string or callable, 默认为 ‘minkowski’
用于树的距离矩阵。默认为闵可夫斯基空间,如果和p=2一块使用相当于使用标准欧几里得矩阵. 所有可用的矩阵列表请查询 DistanceMetric 的文档。
@ metric_params(矩阵参数): dict, 可选参数(默认为 None)
给矩阵方法使用的其他的关键词参数。
@ n_jobs: int, 可选参数(默认为 1)
用于搜索邻居的,可并行运行的任务数量。如果为-1, 任务数量设置为CPU核的数量。不会影响fit方法。
3.2.2 fit(机器学中一般都称为训练)
fit(X,y):
使用X作为训练数据,y作为目标值(标签)拟合模型
参数:
@ X: {类似数组, 稀疏矩阵}
稀疏矩阵是指在一个 矩阵中 ,大多数元素为零,只有少数元素为非零值的矩阵 。形式上,给定一个 m x n 的矩阵 A,如果 A 中非零元素的
数量远小于 m x n,则称 A 为稀疏矩阵
待训练数据。如果是数组或者矩阵,形状为 [n_samples, n_features],如果矩阵为’precomputed’, 则形状为[n_samples, n_samples]。
@ y: {类似数组, 稀疏矩阵}
形状为[n_samples] 或者 [n_samples, n_outputs]的目标值。
3.2.3 get_params
get_params(deep=True)
获取估值器的参数.
参数:
@ deep: boolean, 可选参数
如果为 True, 则返回估值器的参数,以及包含子目标的估值器。
返回值:
@ params: Mapping string to any
返回Map变量,内容为[参数值: 值, 参数值: 值, …]。
3.2.4 kneighbors
kneighbors(X=None,n_neighbors=None,return_distance=True)[source]
查询一个或几个点的K个邻居, 返回每个点的下标和到邻居的距离。
(重点使用)
参数:
@ X: 类似数组, 形状(n_query, n_features)或者(n_query, n_indexed) 。
如果矩阵是‘precomputed’,形状为(n_query, n_indexed)带查询的一个或几个点。如果没有提供,则返回每个有下标的点的邻居们。
@ n_neighbors: int
邻居数量 (默认为调用构造器时设定的n_neighboes的值).
@ return_distance: boolean, 可选参数. 默认为 True.
如果为 False,则不会返回距离
返回值:
dist: array
当return_distance =True时,返回到每个点的长度。
ind: array
邻居区域里最近的几个点的下标。
3.2.5 predict
predict(X)[source]
给提供的数据预测相应的类别标签
参数:
@ X: 类似数组, 形状(n_query, n_features)。
如果矩阵是‘precomputed’,形状为(n_query, n_indexed) 待测试样例。
返回值:
@ y: 形状为 [n_samples] 或者 [n_samples, n_outputs]的数组
返回每个待测试样例的类别标签。
3.2.6 predict_proba
predict_proba(X)[source]
返回测试数据X的概率估值。
参数:
@ X: 类似数组, 形状(n_query, n_features)。
如果矩阵是‘precomputed’,形状为(n_query, n_indexed)待测试样例。
返回值:
@ p: 形状为[n_samples, n_classes]的数组,或者是n_outputs列表
输入样例的类别概率估值。其中类别根据词典顺序排序。
3.2.7 score
score(X, y, sample_weight=None)[source]
返回给定测试数据和标签的平均准确度。在多标签分类中,返回的是各个子集的准确度。
参数:
@ X : 类似数组,
形状为 (n_samples, n_features)待测试样例
@ y: 类似数组,
形状为 (n_samples) 或者 (n_samples, n_outputs)X对于的正确标签
@ sample_weight: 类似数组,
形状为 [n_samples], 可选参数待测试的权重
返回值:
@ score : float
self.predict(X) 关于y的平均准确率。
四、 KNN算法实现鸢尾花数据集分类
4.1 数据集介绍
iris数据集是常用的分类实验数据集,由Fisher在1936收集整理。Iris也称为鸢尾花卉数据集,是一类多重变量分析的数据集。
Iris鸢尾花数据集: 包含3个目标值分别为:
山鸢尾(Iris-setosa);
变色鸢尾(Iris-versicolor);
尼亚鸢尾(Iris-virginica),
包含4个特征值:
Sepal.Length(花萼长度),单位是cm;
Sepal.Width(花萼宽度),单位是cm;
Petal.Length(花瓣长度),单位是cm;
Petal.Width(花瓣宽度),单位是cm;
共 150 条数据,每类各 50 个数据,每条记录都有 4 项特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度,通常可以通过这4个特征预测鸢尾花卉属于哪一品种。
4.2 Scikit-Learn中的数据集介绍
# 鸢尾花数据集获取
from sklearn.datasets import load_iris
# 小规模数据集获取
iris = load_iris()
print(iris)
4.2.1 sklearn数据返回值介绍
load_iris返回的数据类型datasets.base.BUnch(字典格式)
data : 特征数据数组,是[n_samples * n_features]的二维numpy.ndarray数组
target : 标签数组,是n_samples的一维numpy.ndarray数组
DESCR : 数据描述
feature_names : 特征名, 新闻数据,手写数字,回归数据没有
target_name : 标签名(目标明)
4.3 数据集划分
机器学习一般的数据集划分分为两部分
1. 训练数据:用于训练,构建模型
2. 测试数据:在模型校验时使用,用于评估模型是否有效
划分比例:
1. 训练集 : 70% 80% 75%
2. 测试集 : 30% 20% 25%
数据集划分API
sklearn.model_selection.train_test_split(arrays, *options)
x : 数据集的特征值
y : 数据集的特征值
test_size : 测试集占的的大小, 一般为float
random_state : 随机数种子,不同的种子会造成不同的随机采样结果。相同的种子采样结果相同。
return : 测试机特征训练集特征值,训练标签,测试标签(默认随机取)
4.3.1 使用鸢尾花数据集进行划分数据集代码
# 导入鸢尾花数据集加载函数
from sklearn.datasets import load_iris
# 导入数据集划分工具
from sklearn.model_selection import train_test_split
# 加载鸢尾花数据集
# 该数据集包含150个样本,每个样本有4个特征(花萼长宽、花瓣长宽)
# 目标值为3种鸢尾花的类别(0:setosa, 1:versicolor, 2:virginica)
iris = load_iris()
# 划分数据集为训练集和测试集
# 参数说明:
# iris.data: 特征数据矩阵(150×4的numpy数组)
# iris.target: 目标值数组(150个元素的numpy数组)
# test_size=0.2: 测试集占比20%,训练集占比80%
# random_state: 随机种子(可选),保证每次划分结果一致
# 返回值:
# x_train: 训练集特征数据(120×4)
# x_test: 测试集特征数据(30×4)
# y_train: 训练集目标值(120个)
# y_test: 测试集目标值(30个)
x_train, x_test, y_train, y_test = train_test_split(
iris.data,
iris.target,
test_size=0.2,
random_state=42
)
# 打印数据集信息
print("训练集特征数据(前5行):\n", x_train[:5]) # 显示前5个样本的特征
print("训练集目标值(前5个):\n", y_train[:5]) # 显示前5个样本的类别
print("测试集特征数据(前5行):\n", x_test[:5]) # 显示测试集前5个样本
print("测试集目标值(前5个):\n", y_test[:5]) # 显示测试集前5个类别
D:\python_huanjing\.venv1\Scripts\python.exe C:\Users\98317\PycharmProjects\study_python\机器学习\day4_14.py
训练集特征数据(前5行):
[[4.6 3.6 1. 0.2]
[5.7 4.4 1.5 0.4]
[6.7 3.1 4.4 1.4]
[4.8 3.4 1.6 0.2]
[4.4 3.2 1.3 0.2]]
训练集目标值(前5个):
[0 0 1 0 0]
测试集特征数据(前5行):
[[6.1 2.8 4.7 1.2]
[5.7 3.8 1.7 0.3]
[7.7 2.6 6.9 2.3]
[6. 2.9 4.5 1.5]
[6.8 2.8 4.8 1.4]]
测试集目标值(前5个):
[1 0 2 1 1]
进程已结束,退出代码为 0
五、特征预处理
5.1 什么是特征预处理
5.1.1 特征预处理定义
通过⼀些转换函数将特征数据转换成更加适合算法模型的特征数据过程。
5.1.2 为什么我们要进行归⼀化/标准化?
特征的单位或者大小相差较⼤,或者某特征的⽅差相⽐其他的特征要⼤出⼏个数量级,容易影响(⽀配)⽬标结果,使得⼀些算法无法学习到其它的特征。
5.1.3 包含内容(数值型数据的无量纲化)
归一化
标准化
5.2 最大最小 归一化
5.2.1 定义
通过对原始数据进行变换把数据映射到(默认为[0,1])之间
5.2.2 公式
作⽤于每⼀列,max为⼀列的最⼤值,min为⼀列的最小值,那么X''为最终结果,mx,mi分别为指定区间值默认 mx为1,mi为0。
# 实现最大最小归一化处理
# 导入sklearn中的MinMaxScaler模块
# 该模块用于将数据按特征缩放到给定的范围(默认[0,1])
from sklearn.preprocessing import MinMaxScaler
# 创建MinMaxScaler对象
# feature_range=(0, 10) 表示将数据缩放到0-10的范围
zh = MinMaxScaler(feature_range=(0, 10))
# 原始数据样例
# 这是一个包含3个样本、每个样本4个特征的二维列表
data = [[90, 2, 10, 40],
[60, 4, 15, 45],
[75, 3, 13, 46]]
# 使用fit_transform方法同时进行拟合和转换
# 该方法会计算每个特征的最小最大值,然后进行缩放
data_new = zh.fit_transform(data)
# 打印归一化后的数据
# 输出结果中每个特征的值都在0-10范围内
print(data_new)
D:\python_huanjing\.venv1\Scripts\python.exe C:\Users\98317\PycharmProjects\study_python\机器学习\day4_14.py
[[10. 0. 0. 0. ]
[ 0. 10. 10. 8.33333333]
[ 5. 5. 6. 10. ]]
进程已结束,退出代码为 0
问题:如果数据中异常点较多,会有什么影响?
影响:
- 损害数据完整性:异常点可能破坏数据的完整性和一致性,使得数据集无法准确反映实际情况。这可能会影响后续的数据分析和应用效果。
- 降低数据可信度:异常点的存在可能会降低数据的可信度,使得基于这些数据做出的决策和预测受到质疑。
5.2.3 最大值最小值归一化总结
注意最⼤值最⼩值是变化的,另外,最⼤值与最⼩值⾮常容易受异常点影响,所以这种⽅法鲁棒性较差,只适合传统精确小数据场景(数据质量高,数据量有限)
5.3 标准化 归一化
5.3.1 定义
通过对原始数据进⾏变换把数据变换到均值为0,标准差(衡量离散程度)为1范围内
下面这个公式是普及的方差和标准差
标准差为:对方差开方从而得到数据单位:
5.3.2 公式
作⽤于每⼀列,mean为平均值,σ为标准差
对于最小最大值归⼀化来说:如果出现异常点,影响了最⼤值和最⼩值,那么结果显然会发⽣改变
对于标准化来说:如果出现异常点,由于具有⼀定数据量,少量的异常点对于平均值的影响并不⼤,从而方差改变较小。
5.3.3 标准差进行归一化代码
# 导入标准差归一化模块
# StandardScaler使用Z-score方法对数据进行标准化处理
from sklearn.preprocessing import StandardScaler
# 原始数据样例
# 包含3个样本,每个样本有4个特征值
data = [[90, 2, 10, 40],
[60, 4, 15, 45],
[75, 3, 13, 46]]
# 创建StandardScaler对象
# 该对象会将数据标准化为均值为0,方差为1的分布
scaler = StandardScaler()
# 调用fit_transform方法进行数据标准化
# 该方法会先计算每个特征的均值和标准差
# 然后对数据进行转换:(X - 均值) / 标准差
result = scaler.fit_transform(data)
# 打印标准化后的结果
# 输出数据的每个特征都符合标准正态分布(均值为0,方差为1)
print(result)
D:\python_huanjing\.venv1\Scripts\python.exe C:\Users\98317\PycharmProjects\study_python\机器学习\day4_14.py
[[ 1.22474487 -1.22474487 -1.29777137 -1.3970014 ]
[-1.22474487 1.22474487 1.13554995 0.50800051]
[ 0. 0. 0.16222142 0.88900089]]
进程已结束,退出代码为 0
六、使用KNN实现对鸢尾花数据集进行分类的代码
# 导入鸢尾花数据集加载函数
from sklearn.datasets import load_iris
# 导入数据集划分工具
from sklearn.model_selection import train_test_split
# 加载鸢尾花数据集
iris = load_iris()
# 将数据集划分为训练集和测试集 (80%训练, 20%测试)
x_train, x_test, y_train, y_test = train_test_split(
iris.data, # 特征数据
iris.target, # 目标标签
test_size=0.2, # 测试集比例
random_state=42 # 随机种子,保证结果可复现
)
# 导入数据标准化工具
from sklearn.preprocessing import StandardScaler
# 创建标准化器实例
ss = StandardScaler()
# 对训练集进行拟合和转换
x_train = ss.fit_transform(x_train)
# 对测试集进行转换(使用训练集的参数)
x_test = ss.transform(x_test)
# 导入K近邻分类器
from sklearn.neighbors import KNeighborsClassifier
# 创建KNN分类器实例(使用暴力算法)
knn = KNeighborsClassifier(algorithm="brute")
# 使用训练数据拟合模型
knn.fit(x_train, y_train)
# 对测试集进行预测
result = knn.predict(x_test)
# 打印预测结果和实际标签
print("预测结果:", result)
print("实际标签:", y_test, end="\n\n")
score = knn.score(x_test, y_test) # 准确率
print("准确率:", score)
D:\python_huanjing\.venv1\Scripts\python.exe C:\Users\98317\PycharmProjects\study_python\作业\zy_1.py
预测结果: [1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0]
实际标签: [1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0]
准确率: 1.0
进程已结束,退出代码为 0
总结
本文深入探讨了KNN算法的核心原理与实践应用,首先通过决策边界可视化展示了不同K值对分类效果的影响,包括K=1时异常数据点形成的"孤岛"现象及K增大后边界平滑化的过程;随后详细解析了Scikit-learn中KNeighborsClassifier的API参数与关键方法(如fit、predict、kneighbors等),并结合代码示例演示了如何实现决策边界绘制;最后以鸢尾花数据集为例,完整展示了数据预处理、划分、标准化及KNN分类的全流程,包括特征工程中的归一化与标准化方法对比,以及模型评估的注意事项,为读者提供了从理论到实践的全面指导。