AB
AiBoss
チュートリアル

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 这类基于距离或核的方法对尺度非常敏感,必须先做标准化。
  • 本文只做了最基础的数据检查。真实项目中还需要处理缺失值、异常值、重复样本与类别不均衡,这些属于数据准备阶段的工作,可以在跑通本流程之后再逐步补上。

关于流程

  • 机器学习项目通常包含定义问题、准备数据、评估算法、改进结果、呈现结果几个阶段,实际推进时未必严格线性。本文覆盖的是其中「加载数据、汇总数据、评估算法、做出预测」这一段主干。
  • 先跑通端到端流程,再回头优化某一环节。一开始就追求最优模型,往往会在细节里迷失,反而看不到整体。