什么是机器学习
机器学习是教计算机从数据中发现模式,而不是手工编写规则
什么是机器学习
机器学习是教计算机从数据中发现模式,而不是手工编写规则。
类型: 学习 语言: Python 前置条件: Phase 1 (数学基础) 时间: ~45 分钟
学习目标
- 解释监督学习、无监督学习和强化学习的区别,并识别给定问题适用哪种类型
- 从零实现最近质心分类器,并对照随机基线进行评估
- 区分分类和回归任务,并为每种任务选择合适的损失函数
- 评估给定的业务问题是否适合使用 ML,还是用确定性规则更好
问题
你想构建一个垃圾邮件过滤器。传统方法:坐下来写几百条规则。“如果邮件包含 ‘FREE MONEY’,标记为垃圾邮件。如果超过3个感叹号,标记为垃圾邮件。“你花了几周写规则。然后垃圾邮件发送者改变了措辞。你的规则失效了。你写更多规则。这个循环永无止境。
机器学习颠覆了这一点。你不再编写规则,而是给计算机数千封标记好的邮件(“垃圾邮件”或”非垃圾邮件”),让它自己找出规则。计算机能发现你从未想到过的模式。当垃圾邮件发送者改变策略时,你只需在新数据上重新训练,而不是重写代码。
这种从”编写规则”到”从数据中学习”的转变,就是机器学习的核心。每个推荐引擎、语音助手、自动驾驶汽车和语言模型都是这样工作的。
概念
从数据中学习,而非规则
传统编程和机器学习以相反的方向解决问题。
传统编程:你编写规则。程序将规则应用于数据以产生输出。
机器学习:你提供数据和期望输出。算法发现规则。
训练产生的”模型”就是规则,以数字(权重、参数)编码。它从见过的示例中泛化,对从未见过的数据做出预测。
机器学习的三种类型
监督学习:你有输入-输出对。模型学习将输入映射到输出。
- “这里有10,000张标记为猫或狗的照片。学会区分它们。”
- “这里有房屋特征和价格。学会预测价格。”
无监督学习:你只有输入,没有标签。模型自行发现结构。
- “这里有10,000个客户购买历史。找出自然分组。”
- “这里有1000维数据点。在保持结构的同时降到2维。”
强化学习:智能体在环境中采取行动并获得奖励或惩罚。它学习一种策略(policy)来最大化总奖励。
- “玩这个游戏。赢+1,输-1。找出策略。”
- “控制这个机械臂。拿起物体+1,每浪费一秒-0.01。”
你在实践中构建的大部分内容都使用监督学习。无监督学习常用于预处理和探索。强化学习驱动游戏AI、机器人和语言模型的RLHF。
三大类型之外
上面三个类别很清晰,但现实世界的ML经常模糊界限。
半监督学习使用少量标记数据和大量未标记数据。你可能有100张标记的医学图像和100,000张未标记的。技术包括:
- **标签传播:**构建连接相似数据点的图。标签从标记节点通过图传播到未标记的邻居。
- **伪标签:**在标记数据上训练模型,用它预测未标记数据的标签,然后在所有数据上重新训练。模型自举其训练集。
- **一致性正则化:**模型对输入及其轻微扰动版本应给出相同的预测。这即使没有标签也有效。
自监督学习从数据本身创建监督。完全不需要人工标签。模型从数据结构中创建自己的预测任务。
- **掩码语言建模 (BERT):**隐藏句子中15%的词,训练模型预测缺失的词。“标签”来自原始文本。
- **对比学习 (SimCLR):**取一张图像,创建两个增强版本。训练模型识别它们来自同一图像,同时区分它们与其他图像的增强版本。
- **下一词预测 (GPT):**给定所有前面的词,预测下一个词。每个文本文档都成为训练样本。
这些不是三大类型的独立类别。它们是结合监督和无监督思想的策略。自监督学习在技术上是监督的(模型预测某物),但标签是自动生成的,不是人工标注的。
分类 vs 回归
这是两种主要的监督学习任务。
| 方面 | 分类 | 回归 |
|---|---|---|
| 输出 | 离散类别 | 连续数字 |
| 示例 | ”这封邮件是垃圾邮件吗?" | "房价会是多少?“ |
| 输出空间 | {猫, 狗, 鸟} | 任意实数 |
| 损失函数 | 交叉熵、准确率 | 均方误差、MAE |
| 决策 | 类别之间的边界 | 拟合数据的曲线 |
分类回答”哪个类别?“回归回答”多少?”
有些问题可以用两种方式表述。预测股票涨跌是分类。预测确切价格是回归。
ML工作流程
每个机器学习项目都遵循相同的流程,无论使用什么算法。
收集数据:收集原始数据。更多数据几乎总是更好的,但质量比数量更重要。
清洗与探索:处理缺失值、删除重复项、可视化分布、发现异常。这一步通常占总项目时间的60-80%。
特征工程:将原始数据转换为模型可以使用的特征。将日期转换为星期几。标准化数值列。编码分类变量。好的特征比花哨的算法更重要。
划分数据:分为训练集、验证集和测试集。模型在训练数据上训练,你在验证数据上调整超参数,在测试数据上报告最终性能。
训练模型:将训练数据输入算法。算法调整内部参数以最小化损失函数。
评估:在验证/测试数据上衡量性能。如果性能不可接受,返回尝试不同的特征、算法或超参数。
部署:将模型投入生产,对新数据进行预测。
监控:随时间跟踪性能。数据分布会变化(数据漂移),模型会退化。当性能下降时,重新训练。
训练集、验证集和测试集的划分
这是初学者最容易搞错的重要概念。你必须在训练期间从未见过的数据上评估模型。否则你测量的是记忆,而不是学习。
| 划分 | 目的 | 使用时机 | 典型大小 |
|---|---|---|---|
| 训练集 | 模型从中学习 | 训练期间 | 60-80% |
| 验证集 | 调整超参数,比较模型 | 每次训练运行后 | 10-20% |
| 测试集 | 最终无偏性能估计 | 最后,仅一次 | 10-20% |
测试集是神圣的。你只看它一次。如果你不断根据测试性能调整模型,你实际上是在测试集上训练,你报告的数字毫无意义。
对于小数据集,使用k折交叉验证:将数据分成k份,在k-1份上训练,在剩余一份上验证,轮换并平均结果。
过拟合 vs 欠拟合
欠拟合:模型太简单,无法捕捉数据中的模式。一条直线试图拟合弯曲的关系。训练误差高。测试误差高。
过拟合:模型太复杂,记住了训练数据,包括噪声。一条摆动的曲线穿过每个训练点但在新数据上失败。训练误差低。测试误差高。
良好拟合:模型捕捉真实模式而不记忆噪声。训练误差和测试误差都合理地低。
过拟合的迹象:
- 训练准确率远高于验证准确率
- 模型在训练数据上表现好但在新数据上表现差
- 添加更多训练数据能改善性能(模型在记忆,而非学习)
过拟合的修复方法:
- 获取更多训练数据
- 降低模型复杂度(更少参数、更简单架构)
- 正则化(对大权重添加惩罚)
- Dropout(训练时随机将神经元置零)
- 早停(当验证误差开始增加时停止训练)
欠拟合的修复方法:
- 使用更复杂的模型
- 添加更多特征
- 减少正则化
- 训练更长时间
偏差-方差权衡
这是过拟合和欠拟合背后的数学框架。
偏差:模型中错误假设导致的误差。当真实关系是非线性时,线性模型具有高偏差。高偏差导致欠拟合。
方差:对训练数据微小波动敏感导致的误差。高方差模型在不同数据子集上训练时给出非常不同的预测。高方差导致过拟合。
| 模型复杂度 | 偏差 | 方差 | 结果 |
|---|---|---|---|
| 太低(弯曲数据用线性模型) | 高 | 低 | 欠拟合 |
| 刚好 | 中 | 中 | 良好泛化 |
| 太高(10个点用20次多项式) | 低 | 高 | 过拟合 |
总误差 = 偏差^2 + 方差 + 不可约噪声
你无法减少不可约噪声(它是数据本身的随机性)。你想找到偏差^2 + 方差最小化的最佳点。
没有免费午餐定理
没有单一算法对所有问题都最好。在一类问题上表现良好的算法在另一类问题上会表现差。这就是为什么数据科学家尝试多种算法并比较结果。
在实践中,选择取决于:
- 你有多少数据
- 有多少特征
- 关系是线性还是非线性
- 是否需要可解释性
- 你能负担多少计算资源
何时不使用机器学习
ML很强大,但并不总是正确的工具。在使用模型之前,先问自己是否真的需要它。
不要使用ML的情况:
- **规则简单且明确。**税收计算、排序算法、单位转换。如果你能用几个if语句写出来,模型只会增加复杂性而没有收益。
- **你没有数据或数据很少。**ML需要样本来学习。10个数据点无法训练出任何有意义的东西。先收集数据。
- **错误的代价是灾难性的,你需要保证正确性。**医疗剂量计算、核反应堆控制、密码学验证。ML模型是概率性的。它们有时会出错。如果”有时出错”不可接受,使用确定性方法。
- **查找表或启发式规则就能解决问题。**如果简单的阈值或表格能覆盖99%的情况,添加ML只会增加维护成本而没有实质性改进。
- **你无法解释决策,而可解释性是必需的。**受监管行业(贷款、保险、刑事司法)有时要求每个决策都完全可解释。一些ML模型是可解释的(线性回归、小决策树)。大多数不是。
- **问题变化的速度快于你重新训练的速度。**如果规则每天变化而重新训练需要一周,模型总是过时的。
使用此决策流程图:
动手构建
code/ml_intro.py 中的代码从零实现了最近质心分类器,这是最简单的ML算法。它演示了核心思想:从数据中学习,然后对新数据进行预测。
步骤1:从零实现最近质心分类器
最近质心分类器计算训练数据中每个类别的中心(均值)。预测时,将每个新点分配给距离最近的类别中心。
class NearestCentroid:
def fit(self, X, y):
self.classes = np.unique(y)
self.centroids = np.array([
X[y == c].mean(axis=0) for c in self.classes
])
def predict(self, X):
distances = np.array([
np.sqrt(((X - c) ** 2).sum(axis=1))
for c in self.centroids
])
return self.classes[distances.argmin(axis=0)]
这就是整个算法。fit计算两个均值。predict计算距离。没有梯度下降,没有迭代,没有超参数。
步骤2:在合成数据上训练
我们生成一个2D分类数据集,两个类别略有重叠。质心分类器在类别中心之间绘制线性决策边界。
rng = np.random.RandomState(42)
X_class0 = rng.randn(100, 2) + np.array([1.0, 1.0])
X_class1 = rng.randn(100, 2) + np.array([-1.0, -1.0])
X = np.vstack([X_class0, X_class1])
y = np.array([0] * 100 + [1] * 100)
步骤3:对照基线比较
每个ML模型都应该与一个简单基线比较。这里,基线预测随机类别。如果你的ML模型不能超过随机猜测,说明有问题。
baseline_preds = rng.choice([0, 1], size=len(y_test))
baseline_acc = np.mean(baseline_preds == y_test)
质心分类器在这个干净的数据集上应该达到90%+的准确率。随机基线大约50%。
为什么这很重要
最近质心分类器极其简单。它没有超参数,没有迭代,没有梯度下降。但它捕捉了基本的ML模式:
- 学习训练数据的表示(质心)
- 预测新数据使用该表示(最近距离)
- 评估对照基线(随机猜测)
每个ML算法,从逻辑回归到transformer,都遵循相同的三步模式。表示变得更复杂,但工作流程保持不变。
步骤4:质心分类器不能做什么
最近质心分类器假设每个类别形成一个单一的团。它绘制线性决策边界。它在以下情况失败:
- 类别有多个簇(例如,数字”1”可以有多种写法)
- 决策边界是非线性的(例如,一个类别环绕另一个类别)
- 特征尺度差异很大(距离被最大尺度的特征主导)
这些局限性推动了你将学习的每个其他算法。K近邻处理多个簇。决策树处理非线性边界。特征缩放修复尺度问题。每节课都建立在前一节课的局限性之上。
实际使用
sklearn提供了 NearestCentroid 和合成数据生成器:
from sklearn.neighbors import NearestCentroid
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
X, y = make_classification(
n_samples=500, n_features=2, n_redundant=0,
n_clusters_per_class=1, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
clf = NearestCentroid()
clf.fit(X_train, y_train)
print(f"Accuracy: {clf.score(X_test, y_test):.3f}")
交付成果
本课程产出 outputs/prompt-ml-problem-framer.md — 一个将模糊的业务问题转化为具体ML任务的提示。给它一个问题描述(“我们想减少流失”或”预测下个季度的需求”),它会识别学习类型、定义预测目标、列出候选特征、选择成功指标、建立基线,并标记数据泄漏或类别不平衡等陷阱。在任何ML项目开始时使用它,避免构建错误的东西。
关键术语
| 术语 | 人们怎么说 | 实际含义 |
|---|---|---|
| 模型 | ”那个AI” | 一个具有可学习参数的数学函数,将输入映射到输出 |
| 训练 | ”教AI” | 运行优化算法调整模型参数,使预测匹配已知输出 |
| 特征 | ”输入列” | 模型用来做预测的数据的可测量属性 |
| 标签 | ”答案” | 训练样本的已知输出,用于计算误差信号 |
| 超参数 | ”你调整的设置” | 训练前设置的参数,控制学习过程(学习率、层数) |
| 损失函数 | ”模型有多错” | 衡量预测输出与实际输出差距的函数,训练试图最小化它 |
| 过拟合 | ”它记住了测试集” | 模型学习了训练特有的噪声而非通用模式,在新数据上失败 |
| 欠拟合 | ”它什么都没学到” | 模型太简单,无法捕捉数据中的真实模式 |
| 泛化 | ”在新数据上有效” | 模型对未训练数据做出准确预测的能力 |
| 交叉验证 | ”在不同块上测试” | 反复将数据分成训练/测试折并平均结果,给出更稳健的性能估计 |
| 正则化 | ”保持权重较小” | 在损失函数中添加惩罚项,阻止过于复杂的模型 |
| 数据漂移 | ”世界变了” | 输入数据的统计分布随时间变化,导致模型性能下降 |
练习
- 取任意数据集(如Iris、Titanic)。按70/15/15划分为训练/验证/测试集。解释为什么不应在测试集上调整超参数。
- 列出三个现实世界问题。对每个问题,识别它是分类、回归还是聚类,以及是监督还是无监督的。
- 一个模型在训练数据上达到99%准确率,但在测试数据上只有60%。诊断问题并列出三种你会尝试的修复方法。
延伸阅读
- An Introduction to Statistical Learning - 免费教材,涵盖所有经典ML方法及实践示例
- Google’s Machine Learning Crash Course - 简洁的ML概念可视化介绍
- Scikit-learn User Guide - Python实现ML的实用参考