
MachineLearningMastery
用 Python 完成第一个机器学习项目:从加载鸢尾花数据到模型评估
面向初学者的端到端实战:安装 Python 科学计算环境、加载鸢尾花数据集、用统计摘要与图表理解数据、训练并比较多个分类模型、做出预测并判断结果是否可信。每一步都给出可直接运行的代码。
很多人想用 Python 入门机器学习,却卡在第一步:环境装好了,库也导入了,但不知道接下来该做什么。零散的教程片段看了一堆,始终没有把「加载数据 → 理解数据 → 训练模型 → 评估模型 → 做出预测」这条链路完整走通一遍。本文要解决的就是这个问题:带你用 Python 和 scikit-learn 完整做完一个最小但闭环的机器学习项目,跑完之后你手上会有一套可以套用到其他数据集上的模板。如果你希望先了解站内相关的工具与资源,可以查看 MachineLearningMastery 页面。
这篇教程适合谁:会一点 Python 基础语法、想第一次真正跑通机器学习流程的读者;也适合已经用过 pandas 或 numpy、但没系统做过分类任务的人。不需要线性代数或统计学的先修知识,所有概念都在用到的地方就地解释。
准备工作
为什么选鸢尾花数据集作为第一个项目
入门项目最重要的不是「有挑战性」,而是「变量足够少,能让你把注意力放在流程上」。鸢尾花(iris)数据集恰好满足这一点:
- 它是数值型特征,正好用来练习如何加载与处理表格数据。
- 它是一个分类问题,属于监督学习里相对容易上手的类型。
- 它是多类别分类(三个类别),会涉及一些多分类特有的处理方式,比二分类更有代表性。
- 只有 4 个特征、150 行样本,小到可以完整放进内存,也小到能在一屏或一页纸上浏览完。
- 所有数值特征单位一致、量纲接近,起步阶段不需要做额外的缩放或变换。
数据集包含 150 条鸢尾花观测记录,前四列是以厘米为单位的测量值,第五列是该花所属的品种,三个品种各占一部分样本。
需要安装的库
本教程假设你使用 Python 3.6 及以上版本。需要五个核心库:
- scipy:科学计算基础库。
- numpy:多维数组与数值运算。
- pandas:表格数据的加载与分析。
- matplotlib:绘图,用于数据可视化。
- scikit-learn(导入名为
sklearn):机器学习算法与评估工具。
安装方式有很多种,关键是选定一种并保持一致,不要这个库用 pip、那个库用 conda,否则容易出现版本冲突。常见做法:
- 在 macOS 上可以用 Homebrew 安装较新的 Python 3 及这些库。
- 在 Linux 上可以用发行版自带的包管理器安装。
- 在 Windows 上,或者你对环境配置没把握,推荐安装 Anaconda 的免费版本,它一次性包含了上述全部依赖。
本教程假设 scikit-learn 版本不低于 0.20。具体版本号、安装包大小与各平台支持情况会随时间变化,请以各项目官网当前信息为准。
验证环境是否可用
装完之后不要急着写模型,先确认环境真的能用。打开命令行,启动 Python 解释器:
python3
然后在解释器里逐行输入下面的代码,导入每个库并打印版本号:
import sys
print('Python: {}'.format(sys.version))
import scipy
print('scipy: {}'.format(scipy.__version__))
import numpy
print('numpy: {}'.format(numpy.__version__))
import matplotlib
print('matplotlib: {}'.format(matplotlib.__version__))
import pandas
print('pandas: {}'.format(pandas.__version__))
import sklearn
print('sklearn: {}'.format(sklearn.__version__))
如果每个库都能成功导入并打印出版本号,说明环境没问题。版本号不必与任何人完全一致,这些库的 API 变化不快,略低几个版本通常也能跑通本文全部代码。但如果出现导入错误,请先停下来解决——环境跑不通,后面的步骤都无法进行。排查时可以把完整报错信息拿去搜索,或在技术问答社区提问。
关于工作方式:建议直接在解释器里操作,或者把代码写成脚本在命令行运行,而不是一上来就配置大型 IDE。把工具链保持简单,注意力才能放在机器学习本身。
操作步骤
第一步:加载数据
先把需要的模块导入进来。加载 CSV 数据用 pandas,数值运算用 numpy,绘图用 matplotlib:
import pandas
from pandas.plotting import scatter_matrix
import matplotlib.pyplot as plt
from sklearn import model_selection
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
接着从 CSV 文件地址读取数据。给每一列起好名字,后面分析时可读性会高很多:
url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/iris.csv"
names = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width', 'class']
dataset = pandas.read_csv(url, names=names)
读取完成后,用 shape 确认数据的规模:
print(dataset.shape)
应当输出 (150, 5),即 150 行、5 列。如果数字对不上,说明读取环节出了问题,先检查文件地址与列名设置。
第二步:用统计摘要理解数据
数据加载进来之后,先别急着训练模型。花几分钟看清数据长什么样,能帮你避开后面很多莫名其妙的错误。
看前若干行。 用 head() 查看前 20 行,直观感受每一列的取值范围:
print(dataset.head(20))
看统计描述。 describe() 会给出每列的样本数、均值、标准差、最小值、四分位数与最大值:
print(dataset.describe())
这一步能快速发现异常:比如某列最小值是 0 而其他列都在正数区间,可能意味着缺失值被填成了 0;又比如某列标准差远大于其他列,说明量纲不一致,后续可能需要缩放。
看类别分布。 用 class 列的分组计数确认各类别样本量:
print(dataset.groupby('class').size())
如果各类别数量严重不均衡,评估指标的选择就要格外小心;如果大致均衡,用准确率作为主要指标是合理的。
第三步:用可视化理解数据
统计数字之外,图形能暴露数字看不出来的结构。这里做两类图。
单变量图。 箱线图(box plot)适合看单个特征的分布、中位数与离群点:
dataset.plot(kind='box', subplots=True, layout=(2,2), sharex=False, sharey=False)
plt.show()
直方图(histogram)适合看单个特征的取值分布形状:
dataset.hist()
plt.show()
多变量图。 散点矩阵图(scatter matrix)把每两个特征两两配对画散点,能看出特征之间是否存在相关关系,以及不同类别在特征空间里是否可分离:
scatter_matrix(dataset)
plt.show()
如果某些特征组合能把不同颜色的点明显分开,说明这个分类任务对模型来说不会太难;如果所有组合都混在一起,就要预期模型效果有限,或者需要更复杂的特征工程。
第四步:划分训练集与验证集
评估模型时,绝不能用训练它的同一批数据来打分,否则分数会虚高。标准做法是把数据切成两部分:一部分用于训练,一部分留作验证。
array = dataset.values
X = array[:,0:4]
y = array[:,4]
validation_size = 0.20
seed = 7
X_train, X_validation, Y_train, Y_validation = model_selection.train_test_split(
X, y, test_size=validation_size, random_state=seed)
这里 X 取前四列作为特征,y 取最后一列作为标签;validation_size = 0.20 表示留出 20% 的数据做验证;random_state=seed 固定随机种子,保证每次运行得到相同的切分结果,便于复现。
第五步:建立评估基准
在比较多个模型之前,先确定用什么指标打分。对于类别大致均衡的分类问题,准确率(accuracy)是一个直观且够用的指标。
同时要确定评估方式。这里使用 10 折交叉验证:把训练数据分成 10 份,轮流用 9 份训练、1 份验证,重复 10 次后取平均。相比单次切分,交叉验证的评估结果更稳定,不容易被某一次「运气好」的切分误导。
seed = 7
scoring = 'accuracy'
第六步:训练并比较多个模型
下面构建六个模型,覆盖线性模型、树模型、距离模型、概率模型与核方法,看看哪一类更适合这份数据:
- 逻辑回归(Logistic Regression):线性分类器,速度快,可解释性好。
- 线性判别分析(LDA):同样是线性方法,对数据分布有假设。
- K 近邻(KNN):基于距离,不做显式训练。
- 分类与回归树(CART,即决策树):非线性,能捕捉特征间的交互。
- 高斯朴素贝叶斯(GaussianNB):概率模型,假设特征服从正态分布。
- 支持向量机(SVM):用核函数处理非线性边界。
把模型放进一个列表,统一用同一套交叉验证流程评估:
models = []
models.append(('LR', LogisticRegression(solver='liblinear', multi_class='ovr')))
models.append(('LDA', LinearDiscriminantAnalysis()))
models.append(('KNN', KNeighborsClassifier()))
models.append(('CART', DecisionTreeClassifier()))
models.append(('NB', GaussianNB()))
models.append(('SVM', SVC(gamma='auto')))
results = []
names = []
for name, model in models:
kfold = model_selection.KFold(n_splits=10, random_state=seed)
cv_results = model_selection.cross_val_score(model, X_train, Y_train, cv=kfold, scoring=scoring)
results.append(cv_results)
names.append(name)
msg = "%s: %f (%f)" % (name, cv_results.mean(), cv_results.std())
print(msg)
输出中每个模型后面有两个数字:交叉验证准确率的均值,以及标准差。均值反映平均水平,标准差反映稳定性——标准差大的模型,说明它在不同数据切分下表现波动明显,可靠性要打折扣。
把结果画成箱线图,横向对比会更直观:
fig = plt.figure()
fig.suptitle('Algorithm Comparison')
ax = fig.add_subplot(111)
plt.boxplot(results)
ax.set_xticklabels(names)
plt.show()
第七步:选出最优模型并做预测
根据上一步的结果挑出表现最好的模型,用全部训练数据重新训练,然后在留出的验证集上做预测。以 KNN 为例:
knn = KNeighborsClassifier()
knn.fit(X_train, Y_train)
predictions = knn.predict(X_validation)
print(accuracy_score(Y_validation, predictions))
print(confusion_matrix(Y_validation, predictions))
print(classification_report(Y_validation, predictions))
三个输出各有用途:
- 准确率:整体预测正确的比例,一个数字给出大致印象。
- 混淆矩阵:每一行是真实类别,每一列是预测类别,对角线上的数字是预测正确的样本数。非对角线上的数字告诉你模型把哪一类错认成了哪一类。
- 分类报告:给出每个类别的精确率(precision)、召回率(recall)、F1 分数与支持样本数,能看出模型是否在某一类上明显偏弱。
如果验证集上的准确率与交叉验证得到的均值接近,说明评估是可信的;如果差距很大,就要怀疑数据切分或流程中存在问题。
一个完整示例
把上面的步骤串成一个脚本,从头到尾跑一遍。保存为 first_project.py,在命令行执行 python3 first_project.py:
import pandas
from pandas.plotting import scatter_matrix
import matplotlib.pyplot as plt
from sklearn import model_selection
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
# 1. 加载数据
url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/iris.csv"
names = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width', 'class']
dataset = pandas.read_csv(url, names=names)
print(dataset.shape)
print(dataset.head(20))
print(dataset.describe())
print(dataset.groupby('class').size())
# 2. 可视化
dataset.plot(kind='box', subplots=True, layout=(2,2), sharex=False, sharey=False)
plt.show()
dataset.hist()
plt.show()
scatter_matrix(dataset)
plt.show()
# 3. 划分数据集
array = dataset.values
X = array[:,0:4]
y = array[:,4]
validation_size = 0.20
seed = 7
X_train, X_validation, Y_train, Y_validation = model_selection.train_test_split(
X, y, test_size=validation_size, random_state=seed)
# 4. 评估基准
scoring = 'accuracy'
# 5. 比较模型
models = []
models.append(('LR', LogisticRegression(solver='liblinear', multi_class='ovr')))
models.append(('LDA', LinearDiscriminantAnalysis()))
models.append(('KNN', KNeighborsClassifier()))
models.append(('CART', DecisionTreeClassifier()))
models.append(('NB', GaussianNB()))
models.append(('SVM', SVC(gamma='auto')))
results = []
model_names = []
for name, model in models:
kfold = model_selection.KFold(n_splits=10, random_state=seed)
cv_results = model_selection.cross_val_score(model, X_train, Y_train, cv=kfold, scoring=scoring)
results.append(cv_results)
model_names.append(name)
print("%s: %f (%f)" % (name, cv_results.mean(), cv_results.std()))
fig = plt.figure()
fig.suptitle('Algorithm Comparison')
ax = fig.add_subplot(111)
plt.boxplot(results)
ax.set_xticklabels(model_names)
plt.show()
# 6. 用最优模型做预测
knn = KNeighborsClassifier()
knn.fit(X_train, Y_train)
predictions = knn.predict(X_validation)
print(accuracy_score(Y_validation, predictions))
print(confusion_matrix(Y_validation, predictions))
print(classification_report(Y_validation, predictions))
运行过程中会依次弹出若干张图,关闭当前图后脚本才会继续。全部跑完后,你会得到数据规模、统计摘要、类别分布、六个模型的交叉验证得分、对比箱线图,以及最优模型在验证集上的准确率、混淆矩阵与分类报告。
这套脚本的价值不只在鸢尾花上。换掉数据加载部分与特征、标签的切分方式,其余结构——划分数据、交叉验证比较模型、用最优模型在留出集上验证——可以直接套用到你自己的数据集上。
注意事项
环境与版本
- 如果导入库时报错,不要跳过继续往下做,先解决环境问题。后续所有步骤都依赖这些库正常工作。
- scikit-learn 的 API 在不同大版本之间有过调整,例如逻辑回归的求解器参数、SVM 的
gamma默认值。本文代码按较新的写法给出,若你的版本较旧出现参数报错,可查阅对应版本的文档调整参数。 - 版本号、依赖包大小、各平台安装方式会随时间变化,请以各项目官网当前信息为准。
关于随机性
- 数据切分与交叉验证都涉及随机过程。固定
random_state能让结果可复现,便于对比不同模型。 - 交叉验证的标准差不可忽略。两个模型均值只差零点几个百分点时,很可能只是随机波动,不要据此下结论说某个模型「更好」。
- 验证集只应使用一次。如果反复根据验证集结果调整模型,验证集就失去了「未见数据」的意义,最终评估会偏乐观。
关于评估指标
- 准确率在类别均衡时好用,但类别不均衡时会误导。比如 95% 的样本属于同一类,一个永远预测该类的模型也能拿到 95% 的准确率。这时应改用精确率、召回率、F1 或混淆矩阵来判断。
- 混淆矩阵与分类报告要一起看。只看总准确率,容易漏掉「某一类几乎全错」这种严重问题。
关于数据本身
- 鸢尾花数据集的特征量纲接近,所以本文没有做特征缩放。换成量纲差异大的数据集时,KNN 与 SVM 这类基于距离或核的方法对尺度非常敏感,必须先做标准化。
- 本文只做了最基础的数据检查。真实项目中还需要处理缺失值、异常值、重复样本与类别不均衡,这些属于数据准备阶段的工作,可以在跑通本流程之后再逐步补上。
关于流程
- 机器学习项目通常包含定义问题、准备数据、评估算法、改进结果、呈现结果几个阶段,实际推进时未必严格线性。本文覆盖的是其中「加载数据、汇总数据、评估算法、做出预测」这一段主干。
- 先跑通端到端流程,再回头优化某一环节。一开始就追求最优模型,往往会在细节里迷失,反而看不到整体。