返回章节列表

最小地基 / Chapter 01

01. 机器学习到底在学什么

用一个表格二分类实验跑通数据、模型、损失、优化和评估的第一条训练闭环。

本章实践入口代码和实验从这里开始

从真实表格分类数据开始,先跑通 data -> model -> loss -> optimizer -> metrics。

运行命令
uv run python -m llm_tutor.experiments.train_linear_classifier --epochs 20

训练型脚本通常支持 --output-dir,可以把配置、指标、日志和模型产物保存到 runs/ 下,便于复盘。

本章学习契约

  • 新增概念:监督学习、输入特征、标签、线性分类器、训练/验证/测试拆分。
  • 实验要验证:一个很简单的模型也能通过 loss 和优化器,从表格数据里学到可复用规律。
  • 实验不验证:它不是医学模型评测,也不能说明高测试分数就能直接用于真实诊断。
  • 跑完重点看:train_loss 是否下降,val_acc/val_f1 是否高于随机猜测,最后的 malignant precision/recall/F1 是否暴露不同错误类型。

机器学习最朴素的说法是:我们给模型一些样本,让它从样本里找到一个可以复用的规律。

在监督学习里,一个样本通常长这样:

text
输入特征 x -> 真实标签 y

模型要学习的是一个函数:

text
model(x) ≈ y

这里的“学习”不是把训练集答案背下来,而是不断调整模型内部的参数,让模型在训练样本上的错误变小,同时希望它在没见过的新样本上也表现不错。

一个表格分类例子

第一章使用 Breast Cancer Wisconsin 数据集。它包含一组肿瘤细胞测量指标,任务是预测样本属于 malignant 还是 benign。

这不是因为医学分类是本课程重点,而是因为它非常适合做第一个实验:

  • 数据量小,CPU 可以快速训练。
  • 特征是普通数字,方便理解“输入指标 -> 输出类别”。
  • 不需要下载大型文件,适合作为稳定 smoke test。

线性分类器

我们先训练一个最小模型:

text
logits = x @ W + b

其中:

  • x 是输入特征。
  • Wb 是模型要学习的参数。
  • logits 是模型对每个类别给出的原始分数。

分类时,我们取分数最高的类别作为预测结果。

logits 不是概率,而是模型还没有归一化的原始分数。例如:

text
logits = [2.0, 0.5]

它只表示第一个类别的分数比第二个类别高。经过 softmax 之后,才会变成两个和为 1 的概率。一个小但重要的结论是:argmax(logits)argmax(softmax(logits)) 会得到同一个类别,所以训练和预测代码通常直接使用 logits。下一章会解释为什么 PyTorch 的 CrossEntropyLoss 也直接接收 logits。

这一章暂时不细拆分类 loss。你可以先把它理解成“模型预测和真实标签之间的差距”。下一章会把 MSE、softmax、交叉熵和 PyTorch 的 CrossEntropyLoss 拆开讲清楚。

为什么要分训练集、验证集、测试集

如果只看训练集表现,模型可能只是记住了训练样本。为了知道它能不能泛化,我们把数据拆成三份:

  • 训练集:用来更新参数。
  • 验证集:用来观察训练过程、调超参数。
  • 测试集:最后才看,用来估计最终效果。

运行实验

bash
uv run python -m llm_tutor.experiments.train_linear_classifier --epochs 20

你会看到每个 epoch 的训练 loss、验证 loss、验证 accuracy 和 F1。最后会输出测试集指标。输出里的 malignant_precisionmalignant_recallmalignant_f1 表示我们把 malignant 当作正类来计算指标。

样例输出大致会长这样:

text
epoch=01 train_loss=... val_loss=... val_acc=... val_f1=...
...
test loss=... acc=... malignant_precision=... malignant_recall=... malignant_f1=...

不同机器、不同随机种子下数值会略有波动。你更应该观察趋势:loss 是否下降,验证集指标是否比随机猜测好。

有时你会看到 loss 下降,但 accuracy 或 F1 暂时没动,甚至小幅波动。这并不矛盾。loss 关注的是模型给真实类别的概率有多高,所以即使最终预测类别还没变,置信度变得更合理也会让 loss 下降。accuracy/F1 只看最后分类结果,对“预测对了但不够自信”或“预测错了但接近改对”不敏感。

二分类指标最好放到混淆矩阵里理解。这里把 malignant 当作正类:

真实/预测预测 malignant预测 benign
真实 malignanttrue positivefalse negative
真实 benignfalse positivetrue negative

malignant_recall 关心真实 malignant 里有多少被找出来;malignant_precision 关心预测为 malignant 的样本里有多少是真的。真实医学场景里哪个错误更严重,必须由业务和伦理约束决定,不能只交给一个指标。

这个实验能说明什么

它能说明:

  • 一个简单线性模型也可以通过数据学习有用规律。
  • 训练过程可以用 loss 曲线和验证指标观察。
  • accuracy 不是唯一指标,precision、recall、F1 能帮助观察不同错误类型的比例和取舍。

它不能说明:

  • 线性模型足够解决所有问题。
  • 测试集高分就代表真实业务场景一定可靠。
  • 模型真的理解了医学概念。
  • 指标天然知道哪个错误更严重。真实医学任务必须先明确更关心 malignant 还是 benign。

这章最重要的是建立第一个闭环:数据进入模型,模型输出预测,损失函数衡量错误,优化器更新参数。

常见失败

  • 如果命令找不到 llm_tutor,先运行 uv sync --python 3.12
  • 如果 loss 完全不下降,先检查学习率、数据标准化和标签 shape。
  • 如果只看 accuracy,可能会忽略某一类被系统性预测错的问题。

下一章:02. 损失函数、梯度下降和优化器