跳转至

机器学习完整指南

适用人群:希望建立完整机器学习流程认知并能够训练基础模型的学习者 前置要求:掌握 Python 基础,具备函数、向量、均值与概率的基本直觉 最后更新:2026-07-20


1. 机器学习是什么:一种新的编程范式

1.1 传统编程与机器学习的根本区别

要真正理解机器学习,最好先想清楚它与传统编程的区别。传统的软件开发是这样工作的:程序员把问题的解法用代码表达出来,机器按照这些明确的规则执行。如果你想写一个判断邮件是否是垃圾邮件的程序,你可能会写很多条规则:包含"中奖"两个字的是垃圾邮件,来自某些域名的是垃圾邮件,包含超过三个感叹号的可疑……这条路走下去很快就会遇到困境——规则越写越多,边界情况越来越难以穷举,而且垃圾邮件发送者会不断改变策略,你的规则永远追不上现实。

机器学习提供了一种根本不同的思路。它不是让程序员来写规则,而是让机器从数据中自己学习规律。你给它几万封已经标注好"垃圾邮件"或"正常邮件"的样本,让算法从这些数据里找出垃圾邮件的模式——也许是词汇的组合方式,也许是发件人的行为特征,也许是邮件结构的某种统计规律。这些规律往往非常微妙,复杂到人工无法逐条枚举,但统计方法能够有效捕捉。

可以用一个等式来描述这两种范式的区别:

传统编程:规则 + 数据 → 答案
机器学习:数据 + 答案 → 规则(模型)

这个转变听起来简单,却彻底改变了我们能解决的问题边界。很多问题,人类自己都说不清楚解法的规则,比如"如何识别一张图片里有没有猫"——你知道猫长什么样,但你真的能把这个判断过程表达成一组规则吗?几乎不可能。但如果你有足够多的猫和非猫的图片,机器学习算法可以学会这件事,而且学得相当好。

1.2 机器学习、深度学习与人工智能的关系

这三个词经常被混用,但它们是有层级关系的概念。人工智能(AI) 是最宽泛的概念,泛指让机器表现出智能行为的所有技术,包括专家系统、搜索算法、游戏策略等。机器学习(ML) 是人工智能的一个子领域,专指从数据中学习规律的方法。深度学习(DL) 则是机器学习的一个特定分支,指使用多层神经网络进行学习。

过去十年里深度学习取得了惊人的突破,以至于很多人以为机器学习就等于深度学习。但这是一种误解。对于很多实际业务问题——比如预测用户流失、评估信贷风险、分析销售趋势——传统的机器学习算法(如随机森林、梯度提升树)不仅够用,而且往往更实用:训练快、需要的数据少、结果更容易解释。深度学习在图像、语音、自然语言这类非结构化数据上优势明显,但不是所有场景的银弹。

1.3 机器学习能做什么,不能做什么

机器学习能做的事情可以大致分为几类:根据已有数据预测未知数值(回归),根据特征判断类别归属(分类),在无标签数据中找出自然分组(聚类),给用户推荐可能感兴趣的内容(推荐系统),以及各种感知任务(图像识别、语音识别、文本理解)。

但机器学习也有很明确的局限性。它本质上是对历史数据中统计规律的提取,如果训练数据本身存在偏差,模型就会学到这些偏差。它不能凭空推理出从未见过的场景,也不能保证在分布发生变化的新数据上仍然表现良好。它需要相当数量的数据才能工作,对于极端小样本问题效果有限。最重要的是,它发现的是相关性,而不是因果关系——冰淇淋销量和溺水人数正相关,但你不能据此禁止卖冰淇淋。


2. 三种学习方式:监督、无监督与强化学习

机器学习算法按照学习方式可以分为三大类,理解这三种范式是系统学习 ML 的起点。

2.1 监督学习:从标注数据中学习

监督学习是目前应用最广泛的机器学习范式。它的核心特征是:训练数据中每个样本都有对应的标签(正确答案),算法的任务是学习从输入特征到标签的映射关系。

"监督"这个词的含义来源于此——就好像一个老师监督学生做练习题,每道题都有标准答案,学生通过对比自己的答案和标准答案来改进。模型训练的过程,本质上也是这样一个不断犯错、不断纠正的循环。

监督学习的典型任务分为两类。第一类是分类,输出的是离散类别,比如判断一封邮件是否是垃圾邮件(二分类),或者把一张图片分到"猫、狗、鸟、鱼"里的某一个(多分类)。第二类是回归,输出的是连续数值,比如根据房屋面积、位置、楼龄预测房价,或者根据历史销量预测下个月的需求量。

监督学习的质量高度依赖标注数据的质量和数量。大规模高质量的标注数据往往需要大量人工投入,这也是为什么很多公司把数据标注当作一项核心业务来对待。

2.2 无监督学习:在没有答案的数据中找规律

无监督学习面对的是没有标签的原始数据,算法需要自己发现数据内部的结构和规律。这更接近人类在陌生环境中自发学习的方式——没有老师,没有答案,只有观察和总结。

最常见的无监督学习任务是聚类,把相似的数据点归到同一个组里,不同组之间差异尽量大。比如把电商平台的用户按购买行为分成几种典型群体,每个群体对应一类消费偏好,这些群体事先并不知道是什么,算法要自己找出来。另一大类是降维,把高维数据压缩到低维空间,保留主要的信息结构,同时去掉冗余和噪声。降维既可以用于可视化(把几百维的数据压到二维,画出来看看),也可以用于特征压缩,减少后续模型的输入复杂度。

还有一类叫做异常检测,在大量正常数据中找出与众不同的异常点。工厂设备的传感器数据中,大多数时间都是正常运转的曲线,偶尔会出现设备故障前的异常信号,异常检测模型能够捕捉这些细微的偏离,实现预测性维护。

2.3 强化学习:在交互中学习策略

强化学习的学习机制与前两种完全不同。它不依赖预先准备好的数据集,而是通过在环境中不断试错、获取奖励信号来学习最优策略。一个强化学习智能体在做动作,环境给出奖励或惩罚,智能体根据这些反馈调整自己的行为策略,逐渐找到累积奖励最大的行动方式。

下围棋的 AlphaGo、玩 Atari 游戏的 DQN、训练机器人行走的各种策略网络,都是强化学习的经典应用。大语言模型训练过程中的 RLHF(基于人类反馈的强化学习)也是强化学习的一种变体,用来让模型的输出与人类偏好对齐。

强化学习概念上很优雅,但工程实现通常比监督学习复杂得多:需要设计合理的奖励函数(这本身就是个难题),需要大量的探索时间,训练过程不稳定,超参数敏感。除非你的问题天然适合用序列决策来建模,否则一般不是首选。


3. 核心流程:从数据到模型的完整路径

无论用什么算法,机器学习项目的整体流程都遵循一条相似的路径。理解这条路径,比理解任何单个算法都重要,因为它决定了你在实际项目中的工作方式和决策顺序。

问题定义是一切的起点。你需要把业务问题转化为一个机器学习可以解决的形式——预测值是什么,输入特征是什么,用什么指标衡量成功。很多 ML 项目失败,不是因为算法不够好,而是因为一开始就把问题定义错了。

数据收集与探索(EDA,Exploratory Data Analysis)是紧接着的步骤,也是往往被低估的步骤。你需要理解数据的分布、缺失情况、异常值、各特征与目标变量的关系。一个好的 ML 从业者在动手建模之前,会花大量时间"和数据交朋友"——画各种图、统计各种分布、查各种相关性,直到对数据有直觉性的理解。

数据预处理与特征工程通常是整个流程中最耗时、也最有价值的部分。原始数据几乎从不是"干净"的,总有缺失值、格式不一致、量纲差异巨大、类别变量需要编码等各种问题。特征工程则是在原始数据之上,通过领域知识和创造性构造,生成对预测任务更有用的输入特征。

模型选择与训练相对来说是流程中最"机械"的部分,有了好数据和好特征,试验不同算法并找到合适的通常不太难。通常做法是先用简单模型建立基准(baseline),再逐步尝试更复杂的模型,看是否有明显提升。

模型评估与调优需要在训练集之外的数据上验证模型表现,调整超参数,防止过拟合。最后是部署与监控,把训练好的模型集成到业务系统中,并持续监控线上表现,因为真实世界的数据分布会随时间变化。


4. 线性回归:从直线开始理解学习

4.1 直觉:用一条直线拟合数据

线性回归是机器学习的"Hello World",简单到可以在几行代码里实现,但它包含了理解整个机器学习框架所需要的几乎所有核心思想。

假设你有一批房屋数据,每条记录包含房屋面积和成交价格。直觉告诉你,面积越大的房子通常越贵——这两者之间存在某种规律性的关系。线性回归要做的,就是找到一条最能代表这种关系的直线:给定任意面积,这条直线能给出一个合理的价格预测。

问题转化成了:如何定义"最好的直线"?直觉上,最好的直线应该让所有数据点到直线的距离之和最小。线性回归用的是均方误差(MSE)来衡量这个"距离"——计算每个数据点的预测值与真实值之差的平方,取平均。这个误差也叫做损失函数(Loss Function),它是模型好坏的度量标准。

训练的过程,就是调整直线的斜率和截距,让损失函数的值越来越小。对于线性回归,有精确的数学解法可以一步算出最优参数,但在更复杂的模型里,通常需要用梯度下降这种迭代优化方法逐步逼近。

4.2 从一维到多维:多元线性回归

现实中的价格预测当然不会只看面积一个因素,还要考虑楼层、朝向、地段、装修情况、建筑年代等等。把更多特征加进来,就变成了多元线性回归——输入从一个数变成了一个向量,但本质思想完全相同:找到一组权重参数,让每个特征与对应权重相乘再加总,得到的预测值和真实值的误差最小。

这里引出了一个重要概念:参数(或权重)是模型在训练过程中学习到的值,而不是人工设定的。训练前它们是随机的(或者零),训练后它们代表了数据中学到的规律。每个权重的大小,某种程度上反映了对应特征对预测目标的影响力。

线性回归的假设是特征与目标之间存在线性关系。如果真实关系是弯曲的(比如收益随时间先增后减),线性回归就不够用了。但可以通过对原始特征做变换(比如取对数、平方,或者组合多个特征)来扩大线性回归的适用范围,这就是特征工程的一个典型应用。

from sklearn.linear_model import LinearRegression
import numpy as np

# 假设我们有房屋面积(平方米)和价格(万元)的数据
X = np.array([[50], [80], [100], [120], [150]])  # 特征:面积
y = np.array([150, 240, 300, 360, 450])           # 目标:价格

model = LinearRegression()
model.fit(X, y)

# 预测 90 平方米房屋的价格
predicted = model.predict([[90]])
print(f"预测价格:{predicted[0]:.1f} 万元")

# 查看学到的参数
print(f"斜率(面积权重):{model.coef_[0]:.2f}")
print(f"截距:{model.intercept_:.2f}")

5. 逻辑回归与分类:判断而非预测数值

5.1 为什么线性回归不能直接用于分类

名字里带"回归",但逻辑回归实际上是一个分类算法——这个命名是历史遗留问题。分类问题的输出是类别标签(比如 0 或 1,垃圾邮件或正常邮件),而不是连续数值。

如果直接用线性回归做分类,会有个根本性的问题:线性回归的输出可以是任意实数,但概率必须在 0 到 1 之间。逻辑回归的解法是在线性回归的输出上套一个 Sigmoid 函数,把任意实数"压缩"到 0 到 1 这个区间,输出可以解释为属于正类的概率。最后再设一个阈值(通常是 0.5),概率大于阈值就判为正类,反之为负类。

5.2 从二分类到多分类

逻辑回归天生是二分类算法,但实际问题经常需要分多个类别,比如手写数字识别(0-9 共十个类别)或者图片内容分类(成千上万个类别)。解决多分类有几种方式。

最直观的是 One vs Rest(OvR):对于每个类别,训练一个"这个类别 vs 其他所有类别"的二分类器,预测时看哪个分类器的置信度最高。另一种方式是 Softmax 回归,它把逻辑回归的 Sigmoid 函数推广到多分类版本,直接输出每个类别的概率,所有类别的概率之和为 1。Softmax 在深度学习的分类网络最后一层几乎是标配。


6. 决策树与随机森林:像人一样做决策

6.1 决策树的直觉:一系列 if-else 的自动学习

决策树是所有机器学习算法中最直觉化的一种。它的结构就是一棵倒过来的树:从根节点开始,每个内部节点是一个条件判断(比如"年龄是否大于 30?""收入是否超过 5 万?"),每条边对应一个判断结果,沿着边往下走,最终到达叶节点,叶节点给出预测结果。

决策树的构建过程是一个递归的分裂过程。算法在每个节点上,遍历所有特征和所有可能的切分点,找到能让分裂后两个子集"纯度"最高的那个切分——这里的"纯度"通常用基尼不纯度或信息熵来衡量,越纯意味着同一个子集里的样本越多是同一类别。不断重复这个过程,直到满足停止条件(树达到最大深度、节点里的样本数少于阈值、进一步分裂没有增益等)。

决策树最大的优点是可解释性。你可以把一棵训练好的决策树打印出来,一眼看懂模型是怎么做判断的,这在金融、医疗等对可解释性要求高的场景非常有价值。缺点也很明显:决策树容易过拟合,一棵不加约束的树可以把训练集上的每个样本都完美"记住",但泛化能力很差。

6.2 随机森林:用集体智慧克服单棵树的弱点

随机森林是决策树的直接升级版,核心思想是集成学习(Ensemble Learning):训练很多棵各不相同的决策树,预测时让所有树"投票"(分类)或取平均(回归),用集体决策代替单棵树的独断专行。这个思路在生活中也很常见——专家委员会的集体决策通常比单个专家更可靠。

随机性体现在两个地方:一是每棵树用自助采样(Bootstrap)从原始数据集中有放回地抽取子集来训练,不同树看到的训练数据不完全相同;二是每次节点分裂时,不是从所有特征中选最优,而是从随机子集的特征中选,这让不同的树关注不同的特征组合,增加了多样性。

正是这两种随机化,使得各棵树之间的相关性降低,集成后的误差得以显著减小。随机森林在很多实际问题上表现相当好——不需要太多数据预处理,对异常值不敏感,能自然处理高维数据,而且通过特征重要性评分可以了解哪些特征对预测最有用。

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42
)

model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

accuracy = model.score(X_test, y_test)
print(f"测试集准确率:{accuracy:.3f}")

# 查看特征重要性
for name, importance in zip(iris.feature_names, model.feature_importances_):
    print(f"{name}: {importance:.3f}")

梯度提升树(Gradient Boosting) 是另一种集成方法,与随机森林平行构建不同,梯度提升是串行的:每棵新树专门学习上一棵树犯的错误,逐步修正残差。XGBoost、LightGBM、CatBoost 是梯度提升树的工程实现,在表格数据上常常是竞赛和工业界的首选。


7. 支持向量机:寻找最优边界

支持向量机(SVM)的核心思想简洁而优雅。在二分类问题中,数据被分为两类,SVM 的目标是找到一条决策边界(在高维空间中是一个超平面),把两类数据分开。

与逻辑回归找"总体误差最小的边界"不同,SVM 寻找的是间隔最大化的边界——让决策边界与两侧最近的数据点(这些点被称为"支持向量")之间的距离尽可能大。这个"最大间隔"的设计使得 SVM 对数据的扰动更加鲁棒,泛化能力更强。

但现实数据往往不是线性可分的,SVM 的一个聪明设计是核函数(Kernel):通过一个数学变换,把数据映射到更高维的空间,在那个空间里原本线性不可分的数据可能就变得可分了。常用的核函数有多项式核、RBF(径向基函数)核等。核函数的神奇之处在于,你不需要真正去做高维映射(那样计算量会爆炸),只需要计算两个数据点在高维空间中的内积,而这个内积可以通过原始空间中的核函数直接算出来——这个技巧被称为"核技巧"。

SVM 在小数据集上表现很好,对高维数据(如文本分类)也有出色的效果,但训练和预测速度在大数据集上不如树模型,调参(选择核函数和正则化参数)也需要一定经验。


8. 聚类算法:在无标签数据中发现结构

8.1 K-Means:把数据分成 K 个组

K-Means 是最简单也最常用的聚类算法。你告诉它"把数据分成 K 个组",它就自动找出 K 个质心(每个组的中心点),使得每个数据点到它所属组质心的距离之和最小。

算法的过程相当直观:先随机初始化 K 个质心,然后进入循环——把每个数据点分配给距离最近的质心,再把每个组的质心更新为该组所有点的均值,重复直到质心不再移动。这个迭代收敛过程通常很快。

K-Means 的主要痛点是你需要提前指定 K 的值。实际使用时,可以用"肘部法则"(Elbow Method):对不同的 K 值绘制组内误差,找到误差下降速度明显变慢的"肘部",那个 K 值通常是合适的选择。

8.2 DBSCAN:不需要指定簇数的密度聚类

DBSCAN(基于密度的带噪声空间聚类应用)是另一种思路完全不同的聚类算法。它不要求你预先指定簇的数量,而是从数据的密度分布出发,把高密度区域的点连成一片,视为一个簇,而稀疏区域的点被标记为噪声点。

DBSCAN 有两个关键参数:邻域半径(多大范围内算"邻居")和最小样本数(多少个邻居才算"核心点")。它能自然发现任意形状的簇,对噪声和异常点有天然的鲁棒性,这是 K-Means 做不到的——K-Means 总会把所有点分配到某个簇,不管那个点有多"格格不入"。

from sklearn.cluster import KMeans, DBSCAN
from sklearn.preprocessing import StandardScaler
import numpy as np

# K-Means 示例
X = np.random.randn(300, 2)
kmeans = KMeans(n_clusters=3, random_state=42)
labels = kmeans.fit_predict(X)

# DBSCAN 示例(注意:DBSCAN 对数据尺度敏感,需要标准化)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
dbscan = DBSCAN(eps=0.5, min_samples=10)
labels_db = dbscan.fit_predict(X_scaled)
# 标签为 -1 的点是噪声点
n_noise = (labels_db == -1).sum()
print(f"噪声点数量:{n_noise}")

9. 环境搭建与常用工具库

机器学习的 Python 生态相当成熟,有一套几乎成为行业标准的工具组合。

# 推荐用 Conda 创建独立的 ML 环境(避免依赖冲突)
conda create -n mlenv python=3.11
conda activate mlenv

# 核心数据科学库
conda install -c conda-forge numpy pandas matplotlib seaborn scikit-learn

# 深度学习框架(PyTorch,CPU 版本)
conda install -c pytorch pytorch torchvision torchaudio cpuonly

# 深度学习框架(PyTorch,GPU CUDA 12.1 版本)
conda install -c pytorch pytorch torchvision torchaudio pytorch-cuda=12.1 -c nvidia

# Jupyter Notebook / JupyterLab(交互式开发必备)
conda install -c conda-forge jupyterlab

# 梯度提升树(表格数据首选)
pip install xgboost lightgbm catboost

# 数据可视化进阶
pip install plotly

# 模型解释性工具
pip install shap

# 启动 JupyterLab
jupyter lab

核心库简介

  • NumPy:Python 科学计算的基石,提供高效的多维数组(ndarray)和数学运算
  • Pandas:数据分析利器,提供 DataFrame 结构,让处理表格数据就像操作 Excel 一样直观
  • Matplotlib / Seaborn:数据可视化,Matplotlib 是底层绘图库,Seaborn 在其上封装了更美观的统计图表
  • scikit-learn:传统 ML 算法的瑞士军刀,几乎所有常用算法都有统一接口的实现,还有完善的预处理、评估和流水线工具
  • PyTorch:深度学习框架,以动态计算图和 Python 风格 API 见长,学术界主流,工业界也广泛使用
  • TensorFlow / Keras:Google 开源的深度学习框架,Keras 是其高层 API,生产部署生态成熟

10. 特征工程:数据质量决定模型上限

业界有一句话广为流传:"数据和特征决定了机器学习的上限,而模型和算法只是在逼近这个上限。" 这句话可能有些夸张,但指向了一个真实的现象:在大多数实际项目中,花在数据处理和特征工程上的时间,远多于花在选择模型上的时间,而且前者带来的收益往往也更大。

10.1 数据清洗

真实数据几乎总是不干净的。缺失值是最常见的问题,处理方式取决于缺失的原因和比例:少量随机缺失可以用均值、中位数或众数填充;大量缺失则可能需要删除该特征或用更复杂的插值方法;有时候"缺失"本身就是一个有意义的信号(比如用户没有填写某项意味着什么),可以创建一个"是否缺失"的二值特征。

异常值(Outlier)有时候是真实的极端情况(真的有人在网上一次买了一万件商品),有时候是数据录入错误。处理前需要结合业务理解判断,不能无脑删除。常见处理方式有截断(把超过某个阈值的值截断到阈值)、对数变换(对高度偏斜的分布取对数,压缩极端值的影响)等。

10.2 特征变换

不同特征的数值范围可能差异悬殊,比如年龄在 0-100 之间,收入可能在 0-1,000,000 之间。对于依赖距离或梯度的算法(线性回归、SVM、神经网络、KNN 等),这种量纲差异会导致数值大的特征主导训练,产生不合理的偏差。标准化(Standardization) 把特征变换为均值 0、标准差 1 的分布;归一化(Min-Max Scaling) 把特征压缩到 [0, 1] 区间。树模型不需要这步操作,因为它们只关心特征的大小排序,不关心具体数值。

对于类别特征(如城市名、职业、颜色),必须转换为数值才能输入模型。二值特征用 0/1 表示;少类别的名义型特征(类别之间没有顺序关系)用 One-Hot 编码(每个类别独立一列,取值 0 或 1);高基数类别特征(比如用户 ID、商品 ID 有几百万个取值)直接 One-Hot 会产生极高维度,通常用目标编码或嵌入向量处理。

import pandas as pd
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer

# 示例:构建预处理流水线
numeric_features = ["age", "income", "years_experience"]
categorical_features = ["city", "education_level"]

# 数值特征:填充缺失值 + 标准化
numeric_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])

# 类别特征:填充缺失值 + One-Hot 编码
categorical_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore")),
])

preprocessor = ColumnTransformer(transformers=[
    ("num", numeric_transformer, numeric_features),
    ("cat", categorical_transformer, categorical_features),
])

11. 模型评估与调优

11.1 正确地评估模型

新手最常犯的错误之一是用训练数据评估模型——模型在自己见过的数据上表现好毫无意义,就像开卷考试不能评估学生真实能力。正确的做法是把数据集分成训练集(用于训练模型)和测试集(用于最终评估),测试集在整个开发过程中只使用一次,相当于"最终考试"。

更严谨的做法是用交叉验证(Cross-Validation),尤其是在数据量有限的情况下。K 折交叉验证把数据随机分成 K 份,依次把每份作为验证集,其余 K-1 份作为训练集,训练 K 个模型,把 K 次验证结果取平均,作为模型性能的无偏估计。

11.2 评估指标的选择

不同任务、不同业务目标,需要选择不同的评估指标。对于分类任务,准确率(Accuracy) 最直观,但在类别极度不平衡时会产生误导——如果 99% 的样本是负类,一个永远预测负类的"模型"准确率高达 99%,但毫无用处。

精确率(Precision) 表示模型预测为正的样本中实际是正的比例,召回率(Recall) 表示所有实际正样本中被模型找出来的比例。这两个指标往往是此消彼长的关系:放宽判定标准会提高召回率但降低精确率,提高判定门槛则相反。F1 分数 是精确率和召回率的调和平均,在两者都需要兼顾时是常用指标。AUC-ROC 衡量模型区分正负样本的整体能力,不受阈值选择影响,适合类别不平衡场景。

11.3 超参数调优

模型有两类参数:参数(从数据中学习,如权重)和超参数(训练前设定,如树的深度、学习率、正则化强度)。超参数的选择对模型性能影响很大,但没有通用的最优值,需要针对具体数据和问题来调优。

网格搜索(Grid Search) 是最直接的方式:列出每个超参数的候选值,遍历所有组合,用交叉验证评估每个组合的性能。简单可靠,但在超参数数量多时计算量爆炸。随机搜索(Random Search) 在参数空间中随机采样,在超参数多的情况下效率远优于网格搜索。更先进的方法是贝叶斯优化,用历史评估结果指导下一次搜索方向,以最少的试验次数找到最优参数组合。

from sklearn.model_selection import GridSearchCV, cross_val_score
from sklearn.ensemble import RandomForestClassifier

# 网格搜索示例
param_grid = {
    "n_estimators": [50, 100, 200],
    "max_depth": [None, 5, 10],
    "min_samples_split": [2, 5, 10],
}

grid_search = GridSearchCV(
    RandomForestClassifier(random_state=42),
    param_grid,
    cv=5,                        # 5 折交叉验证
    scoring="f1",                # 用 F1 分数评估
    n_jobs=-1,                   # 并行使用所有 CPU 核心
)
grid_search.fit(X_train, y_train)

print(f"最佳参数:{grid_search.best_params_}")
print(f"最佳 F1:{grid_search.best_score_:.3f}")

12. 过拟合与欠拟合:模型的两种病

过拟合和欠拟合是机器学习中两个最重要的对立概念,理解它们对于诊断和改进模型至关重要。

欠拟合(Underfitting) 意味着模型过于简单,连训练数据上的规律都没有学好。表现是训练集和测试集上的误差都很高。原因通常是模型容量不足(比如用线性模型去拟合明显的曲线关系)、特征不够充分或者训练不充分。解决方向是增加模型复杂度、增加特征、减小正则化强度或者训练更长时间。

过拟合(Overfitting) 则是模型过于复杂,把训练数据中的噪声和特殊情况也"学"进去了,在训练集上表现极好,但一遇到新数据就失灵。就像一个学生把所有练习题的答案都背下来,考试时换个问法就不会做。表现是训练集误差很低,而测试集误差明显更高。

解决过拟合的方法有几个方向。正则化是在损失函数中加入惩罚项,限制参数值过大,L1 正则化(Lasso)会倾向于产生稀疏参数(很多参数变成零,相当于自动特征选择),L2 正则化(Ridge)会让参数值整体偏小但不为零。Dropout 是深度学习中特有的正则化技术,在训练时随机"关掉"一部分神经元,强迫网络不能依赖特定神经元的组合,学习更鲁棒的特征表示。增加数据是解决过拟合最根本的方式——数据越多,模型越难把噪声当规律来学。数据增强(Data Augmentation) 在图像任务中很常用,通过对已有图像做翻转、旋转、裁剪、颜色扰动等变换,人工扩大训练集规模。早停(Early Stopping) 是在验证集误差开始上升时就停止训练,防止在训练集上继续过拟合。


13. 完整实战示例:用 scikit-learn 构建分类器

下面是一个完整的机器学习项目流程示例,使用经典的泰坦尼克号数据集,预测乘客是否存活:

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import classification_report, roc_auc_score
import warnings
warnings.filterwarnings("ignore")

# ── 1. 数据加载与探索 ──────────────────────────────────────────
# 实际中从文件或数据库加载,这里用 seaborn 内置数据集示意
import seaborn as sns
df = sns.load_dataset("titanic")

# 基础探索
print("数据形状:", df.shape)
print("\n各列缺失值:")
print(df.isnull().sum())
print("\n目标变量分布:")
print(df["survived"].value_counts(normalize=True))

# ── 2. 特征选择与数据准备 ──────────────────────────────────────
# 选择有意义的特征
features = ["pclass", "sex", "age", "sibsp", "parch", "fare", "embarked"]
target   = "survived"

X = df[features].copy()
y = df[target].copy()

# 划分训练集和测试集(保留 20% 作为最终测试)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# ── 3. 构建预处理流水线 ────────────────────────────────────────
numeric_features     = ["age", "fare", "sibsp", "parch"]
categorical_features = ["pclass", "sex", "embarked"]

numeric_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])

categorical_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])

preprocessor = ColumnTransformer([
    ("num", numeric_transformer, numeric_features),
    ("cat", categorical_transformer, categorical_features),
])

# ── 4. 训练与评估多个模型 ─────────────────────────────────────
models = {
    "随机森林": RandomForestClassifier(n_estimators=100, random_state=42),
    "梯度提升": GradientBoostingClassifier(n_estimators=100, random_state=42),
}

for name, clf in models.items():
    pipeline = Pipeline([
        ("preprocessor", preprocessor),
        ("classifier", clf),
    ])

    # 5 折交叉验证
    cv_scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring="roc_auc")
    print(f"\n{name} - 交叉验证 AUC:{cv_scores.mean():.3f} ± {cv_scores.std():.3f}")

    # 在测试集上最终评估
    pipeline.fit(X_train, y_train)
    y_pred    = pipeline.predict(X_test)
    y_prob    = pipeline.predict_proba(X_test)[:, 1]

    print(f"{name} - 测试集 AUC:{roc_auc_score(y_test, y_prob):.3f}")
    print(classification_report(y_test, y_pred, target_names=["遇难", "存活"]))

14. 核心公式与指标速查

公式的作用不是替代直觉,而是明确“模型到底优化什么”。设样本数为 \(n\),特征向量为 \(\mathbf{x}\),参数为 \(\mathbf{w}\),真实值为 \(y\),预测值为 \(\hat y\)

14.1 线性回归与均方误差

\[ \hat y=\mathbf{w}^{\mathsf T}\mathbf{x}+b \]
\[ MSE=\frac{1}{n}\sum_{i=1}^{n}(y_i-\hat y_i)^2 \]

MSE 会平方放大较大的误差,因此对异常值敏感。若业务更关心平均偏差而不希望少数异常值主导结果,可使用 MAE。

14.2 逻辑回归与交叉熵

Sigmoid 把任意实数映射到 0 到 1:

\[ \sigma(z)=\frac{1}{1+e^{-z}},\qquad P(y=1\mid\mathbf{x})=\sigma(\mathbf{w}^{\mathsf T}\mathbf{x}+b) \]

二分类交叉熵为:

\[ \mathcal{L}=-\frac{1}{n}\sum_{i=1}^{n}\left[y_i\log p_i+(1-y_i)\log(1-p_i)\right] \]

当模型对错误答案非常自信,损失会迅速增大。这使概率输出不仅要“分类正确”,还要具有合理置信度。

14.3 决策树分裂指标

类别概率为 \(p_k\) 时,信息熵和基尼不纯度分别为:

\[ H=-\sum_{k=1}^{K}p_k\log p_k \]
\[ Gini=1-\sum_{k=1}^{K}p_k^2 \]

节点中的样本越属于同一类别,两者越接近 0。树模型选择能最大程度降低不纯度的特征和切分点。

14.4 标准化与评价指标

标准化把特征调整为均值约 0、标准差约 1:

\[ z=\frac{x-\mu}{\sigma} \]

它对基于距离或梯度的模型很重要,但对普通决策树通常不是必需。分类指标为:

\[ Precision=\frac{TP}{TP+FP},\qquad Recall=\frac{TP}{TP+FN} \]
\[ F_1=2\cdot\frac{Precision\cdot Recall}{Precision+Recall} \]

回归中的决定系数为:

\[ R^2=1-\frac{\sum_i(y_i-\hat y_i)^2}{\sum_i(y_i-\bar y)^2} \]

\(R^2=1\) 表示完美拟合,\(R^2=0\) 表示不优于直接预测均值,测试集上的 \(R^2\) 也可能为负。


15. 延伸阅读

系统课程

书籍推荐

工具文档

进阶资源

  • Papers With Code:把最新 ML 论文与对应代码实现对应起来,跟踪前沿研究的好去处
  • Kaggle:数据科学竞赛平台,有海量公开数据集、他人分享的 Notebook 和实战比赛,是积累工程经验的最好场所之一
  • Distill.pub:高质量的机器学习可视化解释文章,把复杂概念讲得极其直观

本文档持续维护更新。如有错误或建议,欢迎提交 Issue 或 PR。