
Make
用 Make 构建机器学习模型:从零到可运行的最小示例
面向零基础读者的机器学习入门教程:用 Python 与 scikit-learn 搭建一个可运行的最小模型,讲清数据集、特征与标签、训练与预测的完整流程,并说明过拟合、欠拟合等常见概念。
机器学习听起来比它实际的样子复杂得多。模型、训练、特征、数据集、预测、算法——这些词堆在一起,容易让人以为必须先读完数学系课程才能写出第一段机器学习代码。其实核心只有一句话:让计算机从例子中找出规律,再用这个规律去判断新的例子。你能认出猫,是因为见过很多猫;模型也一样。这篇教程会带你用 Python 和 scikit-learn 从零搭出一个真正能跑的模型,用它根据学习时长预测学生考试是否及格。整个过程不要求任何机器学习基础,只要你会写变量、列表、if/else 和函数调用就够了。如果你希望把模型训练、评估、调参的流程放到一个可视化环境里逐步操作,可以配合 Make 一起使用,把每一步的输入输出看得更清楚。
准备工作
在动手之前,先确认环境和工具都到位。下面这些是硬性前置条件,缺一个都会卡住。
- Python 已安装。在终端或命令提示符里执行
python --version,能看到类似Python 3.12.0的输出即可。版本号不必和示例完全一致。 - 一个文本编辑器或代码编辑器,比如 VS Code,用来写
.py文件。 - 终端或命令提示符,用来安装依赖和运行脚本。
- 可用的网络连接,因为需要从 PyPI 安装 scikit-learn。
Python 基础方面,你只需要能熟练做这几件事:创建和使用变量、操作列表、写基本的 if/else、调用函数、读懂并运行一个 Python 程序、在终端里执行命令。不需要事先了解 scikit-learn、统计学或高等数学,文中出现的每个概念都会随用随讲。
先理解几个词
在写代码之前,把术语对齐,后面看代码会顺很多。
机器学习模型是一个从数据中学到了规律的程序。它的定义刻意保持简单:给它看足够多的例子,它就能发现输入和输出之间的关系。比如给它看若干学生的「学习时长」和「是否及格」,它可能自己总结出「学得越久越容易及格」——这条规则不是你写进代码的,是它从例子里学到的。
传统编程和机器学习的区别在于方向相反。传统编程是「数据 + 规则 → 答案」,规则由你手写,比如 if hours >= 4: print("Likely to pass")。机器学习是「例子 + 正确答案 → 模型」,规则由模型自己归纳;训练完成后再喂新数据,得到「新数据 + 已训练模型 → 预测」。
训练就是拿例子教模型的过程。你给算法一批数据,算法不断调整模型内部的状态,让它的预测越来越贴近这些例子给出的正确答案。
数据集就是数据的集合。本教程里用两个 Python 列表表示。
特征是用来做判断的线索,标签是想要模型预测的答案。在本例中,学习时长是特征,及格与否是标签。如果每个学生还有平时成绩、作业完成率、出勤率,那这些也都是特征,模型可以综合所有特征来预测同一个标签。
本例属于监督学习:训练时每个例子都带着已知的正确答案。与之相对的是无监督学习,数据里没有答案,模型自己去发现分组或结构。此外还有强化学习等类型,但监督学习的工作流最容易上手,适合作为起点。
操作步骤
第 1 步:创建项目目录
新建一个文件夹,命名为 machine-learning-model,在里面创建一个文件 model.py。最终结构如下:
machine-learning-model/
└── model.py
第 2 步:安装 scikit-learn
scikit-learn 提供了大量现成的机器学习算法和工具,省去自己从数学公式推导实现的过程。安装命令:
pip install scikit-learn
当然,你也可以手写一个简单的算法来理解背后的数学,但作为第一个能跑的模型,用库能让你把注意力放在工作流本身。
第 3 步:导入模型类
打开 model.py,写入:
from sklearn.tree import DecisionTreeClassifier
这行的含义是:从 scikit-learn 的 tree 模块中取出 DecisionTreeClassifier 这个类。导入之后就可以实例化模型:
model = DecisionTreeClassifier()
此时 model 还是一个空模型,什么都没学到,等着被喂训练数据。
第 4 步:构造数据集
先写特征列表:
hours = [1, 2, 3, 4, 5, 6, 7, 8]
再写标签列表,用 0 表示不及格、1 表示及格:
results = [0, 0, 0, 1, 1, 1, 1, 1]
对应关系是:学 1 小时不及格,学 2 小时不及格,学 3 小时不及格,学 4 小时及格,一直到学 8 小时及格。这就是模型要学习的例子。
第 5 步:把特征整理成二维结构
这里有个容易踩的细节。scikit-learn 期望特征是一个二维结构,而不是一维列表。原因是真实数据集往往有多个特征,比如:
| 学习时长 | 出勤率 | 平时成绩 |
|---|---|---|
| 2 | 80% | 65 |
| 5 | 95% | 82 |
| 7 | 98% | 91 |
每一行是一个样本,每一列是一个特征。即使当前只有一个特征,也要保持二维形式,所以用嵌套列表:
X = [
[1],
[2],
[3],
[4],
[5],
[6],
[7],
[8]
]
每个内层列表代表一个学生,[1] 表示该学生学了 1 小时。大写 X 是特征数据的常见命名约定。标签则用小写 y:
y = [0, 0, 0, 1, 1, 1, 1, 1]
可以把 X 理解为「线索在这里」,y 理解为「正确答案在这里」。
第 6 步:划分训练集与测试集
不能把全部数据都拿去训练,否则无法判断模型在没见过的数据上表现如何。用 train_test_split 把数据切开:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
test_size=0.25 表示留出四分之一的数据做测试,其余用于训练。random_state 固定随机种子,保证每次运行得到相同的划分结果,便于复现。
第 7 步:训练模型
调用 fit 方法,把训练数据交给模型:
model.fit(X_train, y_train)
这一行就是「训练」的全部动作。模型会在这批例子里寻找输入与输出之间的规律。
第 8 步:做预测
训练完成后,用 predict 对新数据做判断:
prediction = model.predict([[5]])
print(prediction)
注意传入的仍然是二维结构 [[5]],表示「一个学了 5 小时的学生」。输出是数组形式,例如 [1]。
第 9 步:把结果转成人能读的文字
直接输出 0 或 1 不够直观,加一层转换:
if prediction[0] == 1:
print("Likely to pass")
else:
print("Likely to fail")
第 10 步:评估模型表现
用测试集看看模型在没见过的数据上准不准:
accuracy = model.score(X_test, y_test)
print(accuracy)
score 返回的是准确率,取值在 0 到 1 之间。
一个完整示例
把上面的步骤合起来,model.py 的完整内容如下:
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
# 特征:每个学生学习了多少小时
X = [
[1],
[2],
[3],
[4],
[5],
[6],
[7],
[8]
]
# 标签:0 = 不及格,1 = 及格
y = [0, 0, 0, 1, 1, 1, 1, 1]
# 划分训练集与测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
# 创建并训练模型
model = DecisionTreeClassifier()
model.fit(X_train, y_train)
# 在测试集上评估
accuracy = model.score(X_test, y_test)
print("Accuracy:", accuracy)
# 对新数据做预测
prediction = model.predict([[5]])
if prediction[0] == 1:
print("Likely to pass")
else:
print("Likely to fail")
在终端里进入项目目录,运行:
python model.py
你会看到准确率和一个预测结果。到这里,一个完整的监督学习流程就跑通了:构造数据、划分数据、创建模型、训练、评估、预测。
模型内部到底发生了什么
本例用的是决策树分类器。决策树通过一连串关于数据的问题来做判断。在这个极简例子里,模型可能学到的规律近似于「学习时长是否达到某个阈值」,达到就判及格,没达到就判不及格。真实的决策树会复杂得多,可能包含多层嵌套判断,但基本思路就是这样。
所谓「学习」,指的是算法在训练过程中调整模型内部的状态,使得预测结果尽可能贴近训练样本给出的正确答案。模型内部可调整的量称为参数,由训练过程自动确定;而像树的深度、划分比例这类需要你在训练前设定的量称为超参数,它们不通过训练得到,而是由你指定或通过搜索挑选。
为什么要分训练集和测试集
如果只用训练数据评估,模型可能只是把训练样本「背」了下来,在新数据上表现很差,这叫过拟合。反过来,模型过于简单、连训练数据里的规律都没抓住,训练和测试表现都不好,这叫欠拟合。留出一部分数据专门做测试,就是为了观察模型在没见过的样本上的真实水平。
扩展到多个特征
真实场景里,单一特征往往不够。给每个学生补上出勤率和平时成绩,特征矩阵就变成:
X = [
[2, 80, 65],
[5, 95, 82],
[7, 98, 91],
[1, 60, 50],
[6, 90, 78],
[3, 70, 60],
[8, 99, 95],
[4, 85, 70]
]
预测时也要按同样的列顺序传入:
prediction = model.predict([[5, 95, 82]])
当特征数量达到几十上百个时,代码结构不变,只是每个内层列表更长。真正需要额外处理的是特征缩放、缺失值填充、类别特征编码等问题,这些属于后续要学的内容。
回归与分类的区别
本例预测的是「及格 / 不及格」这种离散类别,属于分类任务。如果预测的是连续数值,比如具体考多少分,那就是回归任务。回归的代码结构几乎一样,只是把分类器换成回归器,评估指标也从准确率换成误差类指标。
注意事项
- 准确率要谨慎解读。本教程的数据集只有 8 条样本,测试集更小,准确率数字波动很大,不能当作模型真实能力的证据。样本量越大,评估结果才越有参考价值。
- 这个数据集不是真实数据。它是为了教学而人工构造的,规律过于干净。真实数据通常有噪声、缺失值和异常点,需要额外的清洗步骤。
- 特征必须是二维结构。把一维列表直接传给
fit或predict会报错,务必用嵌套列表。 - 预测时的特征顺序必须与训练时一致。训练用「时长、出勤、成绩」的顺序,预测时也要按这个顺序传,否则结果没有意义。
- 固定随机种子便于复现。不设置
random_state时,每次划分结果不同,评估数字也会变化。 - 版本与依赖信息以官网当前说明为准。scikit-learn 的接口、默认参数和可用算法会随版本变化,安装前建议确认当前版本的要求。
跑通这个最小示例之后,下一步可以学的内容包括:用真实数据集替换手工构造的数据、处理缺失值与类别特征、用交叉验证代替单次划分、以及理解更多算法之间的取舍。真正重要的不是记住某一行代码,而是建立起「数据 → 划分 → 训练 → 评估 → 预测」这条工作流的心理模型,之后换任何算法、任何数据集,骨架都是它。