结构总览
-
准备工作:导入神兵利器(Python库)。
-
数据探索:与我们的“主角”——鸢尾花数据集初次见面。
-
数据集划分:切分“课本”与“考卷”的正确姿势。
-
模型训练:让KNN模型“学习”起来。
-
预测与评估:检验模型的“学习成果”。
知识点重要性评级 (实战常用度)
在本文中,我们会对关键的函数和概念进行星级评定,帮助你快速识别哪些是必须牢记的,哪些是了解即可。
-
⭐⭐⭐⭐⭐:核心中的核心,几乎所有项目中都会用到。
-
⭐⭐⭐⭐:非常重要,高频使用。
-
⭐⭐⭐:重要,但可能有替代方案或在特定场景使用。
-
⭐⭐:了解其原理即可,实际中常被更高级的工具替代。
-
⭐:概念性了解。
第一步:准备工作 - 导入我们的工具箱
和任何工程师一样,开工前要先准备好工具。在Python中,这意味着导入我们需要的库。这次,我们会见到一位新朋友:sklearn.datasets,它是Scikit-learn内置的数据集仓库。
import numpy as np
from sklearn import datasets # 新朋友!用于加载内置数据集
from sklearn.model_selection import train_test_split # 老朋友,但极其重要
from sklearn.neighbors import KNeighborsClassifier # 我们今天的主角:KNN分类器
from sklearn.metrics import accuracy_score # 模型的评分工具
第二步:加载与探索数据集
Scikit-learn非常贴心地为我们准备了许多经典数据集,鸢尾花(Iris)就是其中最著名的一个,非常适合新手入门。
2.1 加载数据
# 使用 datasets.load_iris() 加载数据集
iris = datasets.load_iris()
2.2 探索数据结构
这个iris变量像一个“百宝箱”,里面装着关于数据集的一切。我们可以用.keys()看看里面都有什么宝贝:
# 查看'百宝箱'里有什么
iris.keys()
# 输出: dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename', 'data_module'])
-
data: 这就是样本特征矩阵 X,每一行是一个样本,每一列是一个特征。
-
target: 这就是样本的标签(类别)y,与data中的每一行一一对应。
-
target_names: 标签代表的真实含义(花的名字)。
-
feature_names: 特征的名字(花萼、花瓣的长宽)。
-
DESCR: 数据集的详细描述文档。
2.3 分离特征(X)和标签(y)
为了后续操作方便,我们把特征和标签分别提取出来。
# 特征矩阵 X
X = iris.data
# 标签向量 y
y = iris.target
print("特征矩阵的形状 (X.shape):", X.shape)
print("前5个样本的特征:\n", X[:5])
print("-" * 30)
print("标签向量的形状 (y.shape):", y.shape)
print("前5个样本的标签:", y[:5])
print("-" * 30)
print("特征名称 (feature_names):", iris.feature_names)
print("标签含义 (target_names):", iris.target_names)
输出解读:
我们有150个样本,每个样本有4个特征(花萼长、宽,花瓣长、宽)。标签0, 1, 2分别对应setosa, versicolor, virginica这三种鸢尾花。
第三步:数据集划分 - 科学地切分数据
这是机器学习中至关重要的一步!如果划分不当,整个模型的评估将毫无意义。
⚠️ 核心痛点:为什么要先“打乱”数据?
观察原始的y向量,你会发现它是[0,0,...,0, 1,1,...,1, 2,2,...,2]这样有序排列的。
如果我们直接按8:2的比例切分:
-
训练集可能只包含类别0和1的花。
-
测试集将只包含类别2的花。
后果是灾难性的:模型从未见过类别2,却让它去预测类别2,这好比教一个学生认识猫和狗,却去考他会不会认老虎。这样的测试结果完全不可信!
解决方案:在切分前,必须先将数据随机打乱(Shuffle),同时保持X和y的对应关系。
3.1 手动实现划分(理解原理)
了解原理有助于我们更好地使用工具。我们可以通过打乱索引的方式来实现。
# 【了解即可】手动实现数据划分
# 1. 创建乱序索引--从而更加科学的对模型进行训练
shuffle_indices = np.random.permutation(len(X))
# 2. 定义训练集比例和大小
train_ratio = 0.8
train_size = int(len(X) * train_ratio)
# 3. 划分索引
train_indices = shuffle_indices[:train_size]
test_indices = shuffle_indices[train_size:]
# 4. 根据索引取数据
X_train_manual = X[train_indices]
y_train_manual = y[train_indices]
X_test_manual = X[test_indices]
y_test_manual = y[test_indices]
print("手动划分的训练集形状:", X_train_manual.shape)
print("手动划分的测试集形状:", X_test_manual.shape)
np.random.permutation()
重要性: ⭐⭐ (原理重要,但实际操作中不常用)
说明: 这是理解随机化核心思想的好方法,但在实战中,我们有更方便的“瑞士军刀”。
3.2 Scikit-learn的优雅之道 (train_test_split)
忘记上面的手动实现吧!在实战中,我们永远使用train_test_split,它能一行代码帮你搞定“打乱”和“划分”两件大事。
train_test_split()
重要性: ⭐⭐⭐⭐⭐ (绝对核心,必须掌握!)
说明: 任何监督学习项目的第一步。它封装了随机化和划分逻辑,高效且不易出错。
# 使用一行代码完成打乱和划分
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print("X_train shape:", X_train.shape)
print("y_train shape:", y_train.shape)
print("X_test shape:", X_test.shape)
print("y_test shape:", y_test.shape)
参数解读:
-
X, y: 我们要划分的特征和标签。
-
test_size=0.2: 指定测试集占比20%,训练集则自动为80%。你也可以用train_size=0.8。
-
random_state=42: 随机种子。这是一个非常重要的参数!它保证了你每次运行这段代码,得到的随机划分结果都是完全一样的。这对于复现实验结果至关重要。42只是一个惯例数字,你可以设为任何整数。
第四步:模型训练 - 让KNN开始学习
数据准备就绪,现在轮到我们的主角——KNN分类器登场了。
KNeighborsClassifier()
重要性: ⭐⭐⭐⭐ (KNN算法的核心实现,是分类器大家族中的重要一员)
.fit(X_train, y_train)
重要性: ⭐⭐⭐⭐⭐ (所有Scikit-learn模型的“学习”指令)
说明: fit就是训练。你把“课本”(X_train)和“标准答案”(y_train)交给模型,它就会去学习其中的规律。
# 1. 创建一个KNN分类器实例
# n_neighbors=5 表示我们选择最近的5个邻居来投票,这个'5'就是K值。
knn_classifier = KNeighborsClassifier(n_neighbors=5)
# 2. 用训练数据来“喂养”模型,进行训练
knn_classifier.fit(X_train, y_train)
print("模型训练完成!")
fit的过程非常快,因为KNN算法的“训练”阶段其实只是把训练数据存储起来,它是一个“懒惰学习者”。
第五步:预测与评估 - 见证奇迹的时刻
模型学完了,现在我们要用“模拟考卷”(X_test)来测试它,看看它的预测结果(y_predict)和“真实答案”(y_test)相差多少。
5.1 进行预测
.predict(X_test)
重要性: ⭐⭐⭐⭐⭐ (所有Scikit-learn模型的“预测”指令)
说明: 给定新的、未见过的数据,让训练好的模型给出它的判断。
# 让训练好的模型对测试集进行预测
y_predict = knn_classifier.predict(X_test)
print("预测结果 (y_predict):", y_predict)
print("真实答案 (y_test): ", y_test)
5.2 评估模型准确度
最直观的方法就是比较y_predict和y_test有多少个是完全一样的。
accuracy_score()
重要性: ⭐⭐⭐ (直观且常用,但对于复杂问题有其局限性)
说明: 最基础的分类评估指标,表示“预测正确的样本数 / 总样本数”。
# 方法一:手动计算
correct_predictions = sum(y_predict == y_test)
total_predictions = len(y_test)
accuracy = correct_predictions / total_predictions
print(f"手动计算的准确度: {accuracy}")
# 方法二:使用sklearn的工具
accuracy_sklearn = accuracy_score(y_test, y_predict)
print(f"使用accuracy_score计算的准确度: {accuracy_sklearn}")
5.3 终极捷径:.score()方法
如果你只关心准确度,Scikit-learn提供了一个更便捷的方法:直接调用分类器自身的.score()方法。它会自动完成预测和比对两个步骤。
.score(X_test, y_test)
重要性: ⭐⭐⭐⭐ (非常方便的快速评估工具)
说明: 这是.predict()和accuracy_score()的便捷封装,一步到位得到准确度。
# 直接调用模型的score方法,传入测试数据和真实标签
accuracy_direct = knn_classifier.score(X_test, y_test)
print(f"使用.score()方法直接得到的准确度: {accuracy_direct}")
在这个例子中,你会发现准确度是1.0,也就是100%!这说明我们的模型在这次测试中完美地预测了所有花的种类。这在真实世界的复杂问题中是很少见的,但鸢尾花数据集确实相对简单。
本章小结与展望
恭喜你!你已经成功地完成了你的第一个完整的机器学习项目!让我们回顾一下我们走过的路:
✅ 加载数据: 使用datasets.load_iris()。
✅ 划分数据: 使用train_test_split(),并理解了random_state的重要性。
✅ 创建模型: KNeighborsClassifier()。
✅ 训练模型: .fit()。
✅ 预测与评估: .predict() 和 .score()。
思考与展望:
在今天的项目中,我们随意地设置了k=5。但这个k值到底取多少才是最好的呢?k=3会不会更好?k=10呢?
这个k值,就是机器学习中一个非常重要的概念——超参数(Hyperparameter)。它是在模型训练之前就需要我们手动设置的参数。如何科学地找到最优的超参数,是提升模型性能的关键。
在下一篇文章中,我们将深入探讨超参数搜索的奥秘,让你的模型变得更“聪明”!敬请期待!