前言
天气软件估计明天的气温,邮箱识别垃圾邮件,购物网站推荐可能感兴趣的商品,这些功能背后都可能用到机器学习。不过,机器学习并不是让计算机像人一样“凭空思考”,它更像一种从已有数据中总结规律、再利用规律处理新数据的方法。
这篇文章先建立几个最常用的概念,再用 Python 和 scikit-learn 完成一个小型回归任务:根据学习时长预测考试分数。示例数据由代码生成,不代表真实教学研究;它的用途是让我们看清一次机器学习实验的完整路径。
什么是机器学习
机器学习(Machine Learning)是让计算机从数据中学习规律,并利用这些规律做出预测或决策的一类方法。这里的“学习”不是记住标准答案,而是调整模型内部的参数,使模型给出的结果尽量接近已知结果。
例如,我们收集一些学生的学习时长和考试分数。多数情况下,学习时间增加,分数也会发生相应变化。机器学习模型会尝试从这些成对数据中找出关系。之后给它一个没有见过的学习时长,它就能给出预测分数。
机器学习适合规律存在、但很难手写成大量精确规则的问题。它也有边界:训练数据有偏差,模型通常会继承这种偏差;数据太少或输入与目标没有稳定关系,预测也不会可靠。
传统编程与机器学习的区别
传统编程通常是“数据 + 人写的规则 → 结果”。例如,规定成绩大于等于 60 分就是及格,程序只需忠实执行这条规则。
监督式机器学习更接近“数据 + 已知答案 → 模型”。我们不直接告诉程序学习 1 小时应得多少分,而是提供多组学习时长和对应分数,让算法自己估计两者的关系。训练完成后,再使用“新数据 + 模型 → 预测结果”。
两者并不是互相替代。规则明确、稳定并且必须严格执行时,传统程序往往更合适;规则难以逐条描述,却有足够的代表性数据时,可以考虑机器学习。实际系统也经常把两者组合起来。
监督学习、无监督学习和强化学习
按照获得反馈的方式,机器学习常被分为三类:
三者解决的问题不同。初学时不必急着记住大量算法,先判断“有没有答案”“希望预测什么”更重要。
特征、标签、样本和模型
这四个词会贯穿后续文章:
- 样本(Sample):一条观察记录。本例中,一名学生的一组“学习时长与分数”就是一个样本。
- 特征(Feature):模型用来进行预测的输入信息。本例只有“学习时长”一个特征;真实任务还可能加入出勤率等特征。
- 标签(Label):监督学习中希望模型预测的已知结果。本例的标签是考试分数。
- 模型(Model):从训练数据中学到的数学关系。本文使用线性回归(Linear Regression),它尝试用一条直线描述学习时长与分数的关系。
在 scikit-learn 中,特征通常记为 X,标签通常记为 y。即使只有一个特征,X 也通常写成“样本数 × 特征数”的二维数组。
训练集与测试集
如果把所有数据都交给模型训练,再用同一批数据考试,得到的分数容易过于乐观。因此,我们通常先拆分数据:
- 训练集(Training Set)用于让模型学习参数;
- 测试集(Test Set)只在训练完成后用于检查模型对未见样本的表现。
- 验证集(Val Set) 模型确定后参加最终考试,用来报告真实准确率。
本文把 75% 的样本用于训练,25% 留作测试,并给 train_test_split 设置 random_state=42。固定随机种子意味着每次运行都会得到相同的拆分,便于复现和核对。测试集不参与 fit,否则就失去了模拟“新数据”的意义。
第一个机器学习案例
我们用代码构造 20 条模拟数据:学习时长从 1.0 小时增加到 10.5 小时,分数总体随时长上升,同时加入少量随机波动。随机数生成器也固定为 42,所以数据本身每次都一致。
任务流程是:生成数据 → 拆分训练集和测试集 → 训练线性回归模型 → 预测测试集 → 计算评估指标 → 预测一个新的 6.5 小时样本。
完整可运行代码
将下面代码保存为 01_demo.py,在装有 NumPy 和 scikit-learn 的 Python 3 环境中运行:
"""根据学习时长预测考试分数。"""
import sys
def check_environment():
"""检查 Python 版本和示例依赖,并给出可读的错误信息。"""
if sys.version_info < (3, 8):
raise RuntimeError("本示例需要 Python 3.8 或更高版本。")
try:
import numpy as np
import sklearn
except ImportError as exc:
raise RuntimeError(
"缺少 NumPy 或 scikit-learn,请先在当前环境安装 requirements.txt 中的依赖。"
) from exc
return np, sklearn
def main():
np, sklearn = check_environment()
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score
from sklearn.model_selection import train_test_split
# 构造一组教学用的模拟数据。固定种子使每次生成的数据完全一致。
random_seed = 42
rng = np.random.default_rng(random_seed)
study_hours = np.arange(1.0, 11.0, 0.5).reshape(-1, 1)
scores = 30.0 + 6.2 * study_hours.ravel() + rng.normal(
loc=0.0, scale=2.0, size=study_hours.shape[0]
)
if study_hours.shape[0] != scores.shape[0] or not np.isfinite(scores).all():
raise ValueError("示例数据无效:特征和标签数量不一致,或标签包含非有限值。")
# 留出 25% 的样本作为测试集,模型训练时不会看到它们。
x_train, x_test, y_train, y_test = train_test_split(
study_hours,
scores,
test_size=0.25,
random_state=random_seed,
)
model = LinearRegression()
model.fit(x_train, y_train)
y_pred = model.predict(x_test)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
new_prediction = model.predict(np.array([[6.5]]))[0]
print(f"Python: {sys.version.split()[0]}")
print(f"NumPy: {np.__version__}")
print(f"scikit-learn: {sklearn.__version__}")
print(f"训练集样本数: {len(x_train)}")
print(f"测试集样本数: {len(x_test)}")
print("\\n测试集预测结果:")
for hours, actual, predicted in zip(x_test.ravel(), y_test, y_pred):
print(
f"学习 {hours:>4.1f} 小时 | 实际分数 {actual:>5.2f} | "
f"预测分数 {predicted:>5.2f}"
)
print(f"\\n平均绝对误差(MAE): {mae:.2f}")
print(f"决定系数(R2): {r2:.3f}")
print(f"学习 6.5 小时的预测分数: {new_prediction:.2f}")
if __name__ == "__main__":
try:
main()
except (RuntimeError, ValueError) as exc:
print(f"程序无法继续:{exc}", file=sys.stderr)
sys.exit(1)
逐段代码解释
check_environment 先确认 Python 版本,再尝试导入 NumPy 和 scikit-learn。如果依赖缺失,程序会给出明确提示并正常进入错误处理,而不是在后续步骤产生难懂的异常。
np.arange(…).reshape(-1, 1) 生成学习时长,并把一维数据改为 scikit-learn 需要的二维特征矩阵。rng.normal 生成均值为 0 的小幅随机波动。这里明确写出它是模拟数据,不能把结果解释成真实学生群体的规律。
train_test_split 完成随机拆分。model.fit(x_train, y_train) 是训练步骤,线性回归会根据 15 个训练样本估计直线的斜率和截距。model.predict(x_test) 则把 5 个未参与训练的学习时长放入模型,得到预测分数。
最后,代码计算测试集上的 MAE 和 R²,并预测学习 6.5 小时的分数。传入 [[6.5]] 而不是 6.5,是因为模型期待的输入形状仍然是“1 个样本、1 个特征”。
这份代码已在 Conda 的 base 环境中实际运行,版本为 Python 3.11.4、NumPy 1.24.3 和 scikit-learn 1.3.0。程序正常结束,本次固定数据与拆分得到:训练集 15 条、测试集 5 条,MAE 为 1.66,R² 为 0.992,学习 6.5 小时的预测分数为 70.67。因为随机种子和库版本都固定,在相同环境中应得到相同结果;不同库版本可能出现很小的浮点差异。
如何判断模型效果
只看某一条预测并不够,因此需要评估指标(Evaluation Metric),也就是把一组预测质量压缩成数值的方法。
平均绝对误差(Mean Absolute Error,MAE)会计算每条预测与真实值之差的绝对值,再取平均。这里 MAE 为 1.66,可以直观理解为:在这 5 条测试样本上,预测分数平均相差约 1.66 分。MAE 越接近 0 越好。
决定系数(R-squared,R²)衡量模型相对于“总是预测平均值”的方法解释了多少变化。它通常越接近 1 越好,0 表示没有优于简单的平均值基线,也可能出现负数。本例的 0.992 接近1,是因为模拟数据本来就接近直线;这不能证明模型在真实考试数据上也会同样准确。
测试集只有 5 条,所以这个数值主要用于演示流程,不能据此作现实决策。真实项目还要使用更多有代表性的数据,并检查异常值、偏差和重复实验结果。
初学者常见问题
1. 机器学习是不是必须使用大量数学公式?
入门阶段可以先理解数据、训练和评估的流程。随着任务深入,线性代数、概率统计和微积分会帮助你理解算法为什么有效,但不必等到学完所有数学才开始实践。
2. 为什么不能用全部数据训练?
模型需要在没有见过的数据上接受检查。如果没有测试集,我们很难分辨模型是真正学到了规律,还是只记住了训练数据。
3. R² 很高是否说明模型一定很好?
不一定。指标还会受到数据规模、拆分方式、数据泄漏和数据代表性的影响。本例是刻意设计的简单模拟数据,因此结果较高。现实任务必须结合业务目标和数据来源判断。
4. 为什么固定随机种子?
随机拆分和随机生成数据可能让每次结果不同。固定种子可以让实验更容易复现和排查。固定种子不会自动保证模型正确,它只是固定了随机过程。
5. 预测结果可以当作真实成绩吗?
不能。本例只使用学习时长这一项模拟特征,也没有真实学生数据。它是编程教学案例,不适合评价个人、指导教学或作出实际决定。
6. 运行时提示缺少 sklearn 怎么办?
先确认终端使用的是预期的 Conda 环境,再在该环境中安装与项目匹配的 scikit-learn。包的安装名是 scikit-learn,代码中的导入名是 sklearn。不要为了一个示例盲目升级整个环境。
本文总结
机器学习的核心不是某个神秘函数,而是一套从数据中学习并用新数据检验的过程。本文区分了传统编程与机器学习,认识了三类常见学习方式,也明确了样本、特征、标签、模型、训练集和测试集。随后,我们完成了一个可复现的线性回归案例,并用 MAE 与 R² 检查预测效果。
记住一个基本顺序即可:明确问题,准备数据,拆分数据,训练模型,评估模型,再谨慎使用预测结果。
下一篇预告
下一篇将介绍 Anaconda、Python 与机器学习开发环境,包括如何理解 Conda 环境、选择解释器、安装依赖,以及避开“包装好了却运行不了”的常见问题。
网硕互联帮助中心


评论前必须登录!
注册