机器学习基础
Machine Learning : “Field of study that gives computers the ability to learn without being explicitly programmed.” (Samuel 1959)
传统编程 = 人写规则 → 程序处理数据 → 输出结果
机器学习 = 人提供数据+结果 → 程序自己发现规则
本质上,就是让机器具备寻找函数的能力
主流机器学习分为监督学习(supervised learning)和无监督学习(unsupervised learning)
【补:推荐系统(recommender systems)和强化学习(reinforcement learning)】
机器怎么寻找一个函数
Step 1:Function with Unknown
选一个有参数的函数族(hypothesis class),例如线性模型:
\[\hat{y} = wx + b\]- 已知的$x$叫feature,$w, b$ 是 unknown的(weight, bias),是我们要找的
- 函数族的选择本身就是一个设计决策(bias-variance tradeoff 的起点)
Step 2:Define Loss from Training Data
为什么需要 Loss? 我们不能直接比较两个函数的好坏,但可以比较它们在已知数据上的”错误程度”。Loss 就是把”函数好不好”量化成一个标量,让优化成为可能。
常见选择:
| Loss 函数 | 适用场景 | 直觉 |
|---|---|---|
| MSE: $\frac{1}{N}\sum(\hat{y}-y)^2$ | 回归 | 惩罚大误差更重 |
| MAE: \(\frac{1}{N}\sum\lvert\hat{y}-y\rvert\) | 回归(鲁棒) | 对离群点不敏感 |
| Cross-Entropy | 分类 | 基于概率的惩罚 |
关键洞察:Loss 是定义在训练集上的。这是泛化问题的根源——我们优化的目标和真正关心的目标(在未见数据上表现好)不完全一致。
Step 3:Optimization — Gradient Descent
\[w^*, b^* = \arg\min_{w,b} L\]问题:$L$ 对 $w, b$ 是一个高维曲面,如何找最低点? — 用局部信息(梯度)指导方向。
Gradient Descent 机制:模型怎么学习
问题:怎么找最小的w和b
最naive的想法:把所有可能的 w 和 b 都试一遍,找最小的。
为什么不行?
w是连续实数,有无穷多个取值- 多个参数组合爆炸:100个参数 → 无穷∞次尝试
- 暴力搜索在数学上不可行
先看单参数情形(固定 $b$,只优化 $w$):
Update rule:
\[w_{t+1} = w_t - \eta \cdot \frac{\partial L}{\partial w}\bigg|_{w_t}\]逐项拆解:
| 符号 | 含义 | 角色 |
|---|---|---|
| $\eta$ | Learning rate | 超参数,自己设定 |
| $\frac{\partial L}{\partial w}$ | 斜率(梯度) | 告诉方向和陡峭程度 |
| 负号 | 往下走 | 梯度指向上升最快,取负则下降 |
为什么步长 = $\eta \times$ 斜率,而不只是固定步长?
- 斜率大 → 离最优还远,可以走大步
- 斜率小 → 接近最优,自动小步,防止越过
⚠️ Learning rate 不等于步长。步长 = \(\eta \times \lvert\frac{\partial L}{\partial w}\rvert\),两者共同决定。
扩展到两个参数
固定 $b$ 只是为了讲清楚直觉。实际中 $w$ 和 $b$ 同时更新:
\[w \leftarrow w - \eta \cdot \frac{\partial L}{\partial w}\] \[b \leftarrow b - \eta \cdot \frac{\partial L}{\partial b}\]两个偏导数各自负责自己参数的方向——这就是偏微分!
减号:因为要往下坡走,坡度是正的就减小w,坡度是负的就增大w。
$\eta$ 叫学习率(Learning Rate),控制每步走多远。
Learning Rate 的工程意义
| $\eta$ 取值 | 结果 |
|---|---|
| 太小 | 收敛极慢,训练成本高 |
| 太大 | 在最优附近震荡甚至发散 |
| 动态调整 | Learning Rate Schedule(如 warmup + decay)——实际系统中的标准操作 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20α 太小: Loss │* │ * │ * │ * │ * ← 收敛极慢,要走几万步 └──────── 迭代次数 α 太大: Loss │* * │ * * │ * ← 在山谷两侧反复横跳,永远到不了底部 └──────── 迭代次数 α 合适: Loss │* │ * │ ** │ *** ← 稳定下降,合理收敛 └──────── 迭代次数学习率不是模型从数据中学到的,是手动设置的。 这类参数叫超参数(Hyperparameter)
1
2
3
4
5
6
7
8
9
10
11
12
13
输入:训练数据 (x, y),学习率 eta,迭代次数 N
初始化:w = 0, b = 0(或随机值)
重复 N 次:
1. 计算预测值: ŷ = w·x + b
2. 计算误差: error = ŷ - y
3. 计算梯度: grad_w = mean(error · x)
grad_b = mean(error)
4. 更新参数: w = w - eta · grad_w
b = b - eta · grad_b
输出:最终的 w, b
局限性
局部最优:GD 只保证收敛到局部最小值。深度学习的 loss 曲面极其复杂,但实践发现许多局部最优”足够好”。
线性回归:没有这个问题,损失函数是碗形,只有一个最低点
神经网络:有这个问题,但实践中比理论担心的要好处理
鞍点问题:梯度为 0 但不是最优——SGD 的噪声反而能帮助逃离。
- 计算代价:每次更新都算全部训练数据的梯度(Batch GD)——太慢,引出 Stochastic GD / Mini-batch GD。
1
2
3
4
5
6
Gradient Descent
├── SGD(随机梯度下降)— 用单个样本估计梯度
├── Mini-batch GD — 实际最常用
├── Momentum — 引入"惯性"加速收敛
├── Adam — 自适应 learning rate(工程首选)
└── 二阶方法(Newton)— 用曲率信息,理论更优但计算贵
在 PyTorch/TensorFlow 里调用
optimizer.step(),本质上就是执行上面的 update rule——只是框架帮你自动计算了 $\frac{\partial L}{\partial w}$
为什么引入sigmoid函数?
线性模型 $ y = b + wx_1$ 的本质是: 在所有可能的函数里,我们只在“直线”这个极小的子集里搜索。
如果真实的输入输出关系是非线性的,无论怎么调 $w, b$,直线永远拟合不了。这就是 Model Bias(模型偏差):函数族本身太受限,最优解根本不在搜索空间里。
朴素想法:用折线逼近任意曲线
先看这个核心思想:任意一条连续曲线,都可以用足够多的“阶梯函数”之和来无限逼近。Sigmoid 的引入,本质上就是把这个思路变得可微分、可优化。
Sigmoid 解决了两个问题
问题 1:非线性(Model Bias) 线性模型只能表达直线。引入非线性激活函数,模型才能弯曲,才能拟合复杂的输入输出关系。
问题 2:可微分性(让 GD 能工作) 最直觉的非线性函数是”硬阶梯”(Heaviside step function)——值要么 0 要么 1,跳跃处梯度不存在。Sigmoid 是它的光滑近似,处处可微,梯度下降才能工作。
一句话:Sigmoid = “软化版的阶梯函数”,既带来非线性,又保持可微性。
如:$ y = b + wx_1$可以变成:
\[y = b + \sum_i c_i \, \text{sigmoid}(b_i + w_i x_1)\]在 $c_i \cdot \text{sigmoid}(b_i + w_i x) $ 中:
通过叠加足够多的 sigmoid,可以组合出任意形状的折线——这就是Universal Approximation Theorem 的直觉:只要隐层神经元足够多,单隐层网络可以逼近任意连续函数
拓展到多个输入,$y = b+\sum_j\ w_j x_j$可以变成:
\[y = b + \sum_i c_i \, \text{sigmoid}(b_i + \sum_jw_{ij} x_j)\]内层的 $\sum_j w_{ij} x_j $ 是对所有输入特征的加权求和——本质是把多维输入先做线性投影,再过 sigmoid。每个 $i$ 对应一个“神经元”,负责检测输入空间里某个特定的线性组合是否超过阈值。【$i$是自己决定的,也就是说sigmoid函数的个数是自己决定的】
这正是全连接层(fully connected layer)+ 激活函数的完整结构。
我们假设i、j的取值都是1,2,3,那么就有:
\[r_1 = b_1 + w_{11}x_1 + w_{12}x_2 + w_{13}x_3\] \[r_2 = b_2 + w_{21}x_1 + w_{22}x_2 + w_{23}x_3\] \[r_3 = b_3 + w_{31}x_1 + w_{32}x_2 + w_{33}x_3\] \[\begin{bmatrix} r_1 \\ r_2 \\ r_3 \end{bmatrix} = \begin{bmatrix} b_1 \\ b_2 \\ b_3 \end{bmatrix} + \begin{bmatrix} W_{11} & W_{12} & W_{13} \\ W_{21} & W_{22} & W_{23} \\ W_{31} & W_{32} & W_{33} \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \\ x_3 \end{bmatrix}\]对于$i = 1,2,3$ ,都有:
\[a_i = \text{sigmoid}(r_i) = \frac{1}{1 + e^{-r_i}}\]这样,就获得了一个sigmoid函数,接着:
\[y = b + c^T a\]而这和我们上面的式子一模一样
当我们有多个输入的时候,Loss和Optimization没有任何区别:
Loss
\[Loss = \frac{1}{N} \sum_n e_n\]Optimization
\[\space\space\theta^* = \arg \min_{\theta} L\]其中$\theta$是包含所有未知数的向量
梯度计算公式:
\[gradient=\begin{bmatrix} \frac{\partial L}{\partial \theta_1} \bigg|_{\theta = \theta^0} \\ \frac{\partial L}{\partial \theta_2} \bigg|_{\theta = \theta^0} \\ \vdots \end{bmatrix}\]简写为: \(g = \nabla L(\theta^0)\)
Gradient Descent:
\[\begin{bmatrix} \theta_1^{1} \\ \theta_2^{1} \\ \vdots \end{bmatrix} \leftarrow \begin{bmatrix} \theta_1^{0} \\ \theta_2^{0} \\ \vdots \end{bmatrix} - \begin{bmatrix} \eta \frac{\partial L}{\partial \theta_1} \bigg|_{\theta = \theta^0} \\ \eta \frac{\partial L}{\partial \theta_2} \bigg|_{\theta = \theta^0} \\ \vdots \end{bmatrix}\]简写为: \(\theta^1 \leftarrow \theta^0 - \eta g\)
在实际操作中,可以把N个资料随机分成一组一组的batch,假设每个batch有B个资料,现在我们只拿出B个data拿出来算loss(假设叫$L_1$),根据$L_1$算出Gradinent,用这个Gradient来更新参数;接着再选一个batch算出$L_2$,再更新参数……当我们把所有的batch看过一次,这就叫一个Epoch,每次更新参数就叫一次Update
局限
Sigmoid 并不是现在深度学习的首选,原因是:
- 梯度消失(Vanishing Gradient):sigmoid 在输入很大或很小时,导数趋近于 0。深层网络里,多层梯度连乘后几乎为 0,前面的层几乎学不到东西。
- 输出不以 0 为中心(范围是 0~1),会影响训练稳定性。
这就是为什么现代网络主要用 ReLU($\max(0, x) $)及其变体——同样是非线性,但梯度消失问题轻得多。Sigmoid 更多出现在输出层(二分类概率输出)。
ReLU
梯度消失不是“梯度变小”,而是指数级衰减。Sigmoid 导数最大值只有 0.25,链式法则连乘之后,10 层网络前面的层几乎收不到任何信号。这是促使 ReLU 诞生的直接动机。
定义
\[\text{ReLU}(x) = \max(0, x) = \begin{cases} x & x > 0 \\ 0 & x \leq 0 \end{cases}\]ReLU 的本质:一个二选一的”开关”:
x > 0 → 激活,梯度 = 1,信号完整通过,不衰减;
x ≤ 0 → 沉默,梯度 = 0,该神经元本次不参与学习
把一个sigmoid改写成ReLU(两个ReLU才能合成一个hard sigmoid):
\[y = b + \sum_{2i} c_i \, \text{max} \left( 0, b_i + \sum_j w_{ij} x_j \right)\]ReLU 解决了什么,引入了什么
解决的问题:
- 梯度消失:正区间导数恒为 1,链式法则连乘不衰减,深层网络终于可以训练
- 计算极廉价:只是一个
max操作,比 sigmoid 的exp快得多 - 稀疏激活:网络里通常约 50% 的神经元输出为 0,形成稀疏表示,有正则化效果
引入的新问题——Dying ReLU:
当某个神经元的输入在训练过程中始终 $\leq 0 $(比如被一个大的负梯度“打死”),该神经元输出恒为 0,梯度也恒为 0,永远无法再更新。这个神经元就“死了”。
一旦死亡,无法自愈——因为梯度为 0,参数不再移动。
为解决负区间难以处理的问题,产生了许多变体:
ReLU 还是非线性的吗?
ReLU 在正区间斜率是 1,是线性的。那它为什么还有用?
因为线性性是分段的。对于不同的输入,不同神经元的激活状态不同,网络有效地选择了不同的线性区域组合。整体看,网络是一个分段线性函数(piecewise linear function),拐点由激活边界决定。
本质上:ReLU 网络 = 对输入空间做分区,每个区域内是线性函数,区域之间非线性拼接。
这也是 ReLU 能保持强表达能力的原因——$n $ 个 ReLU 神经元最多能创造 $O(2^n) $ 个不同的线性区域。
| Sigmoid | ReLU | |
|---|---|---|
| 导数范围 | (0, 0.25] | {0, 1} |
| 梯度消失 | 严重 | 正区间无 |
| 计算速度 | 慢(含 exp) | 极快(max) |
| 死亡风险 | 无 | 有(负区间永久沉默) |
| 输出范围 | (0, 1) | [0, +∞) |
| 常用场景 | 输出层(概率) | 隐藏层(默认) |
注意:我们可以令 $a^t$表示第 t 轮的输出,则第 t+1 轮的输入为$a^t$,并计算$a^{t+1}=σ(a^t)$,进行多轮训练,如图:
这些sigmoid或者ReLU就叫Neuron,整合起来就叫Neural Network;每一层叫一个hidden layer,许多hidden layer组合起来,就叫deep learning
🫠 但是想一想,只需要一层sigmoid或者ReLU,是不是也可以逼近任意的function,那deep learning的意义在哪呢?为什么不用“fat learning”呢?——暂且按下不表!
Overfitting:在训练过的资料上变好,但是在没看过的资料上没有变好
机器学习任务攻略
\[\text{Training data: } \{(x^1, \hat{y}^1), (x^2, \hat{y}^2), \dots, (x^N, \hat{y}^N)\}\] \[\text{Testing data: } \{x^{N+1}, x^{N+2}, \dots, x^{N+M}\}\]Pipeline
loss on training data:large
在训练资料上没有学好 — 为什么
原因1:model bias
模型过于简单,在所有可能的function set里会存在一个让loss相对最小的,但是loss小只是相对的,更好的function根本不在set里!
“函数族本身太受限,最优解根本不在搜索空间里”
解决方案:重新设计model,给model更大的弹性,如给model更多的feature
原因2:optimization issue
由于Gradient Descent找到的只是局部最优解,可能更好的function确实存在于这个set里,但是由于GD本身的原因没找出来
怎么判断是哪个原因呢? — 可以通过比较不同的模型来看现在的模型够不够大
ref:Deep Residual Learning for Image Recognition
根据图1,就可以判断是optimization的问题,因为56层的network弹性更大,一定可以做到20层network可以做到的事,但结果并非如此,这不是overfitting,也不是model bias!
可以先跑一些比较浅的network,甚至一些不是deep learning的方法,如linear model、support vector machine,这些model一般不会有失败的问题,先有个概念,看看这些model可以得到什么样的loss;再换更深的model,如果深的model明明弹性比较大,但是弹性不能压得更低,那就说明optimization有问题 — 怎么办呢?我们暂且按下不表
loss on training data:small
Test Loss:Large — overfitting
考虑一个极端的例子
\[\text{Training data: } \{(x^1, \hat{y}^1), (x^2, \hat{y}^2), \dots, (x^N, \hat{y}^N)\}\] \[f(x) = \begin{cases} \hat{y}^i & \exists x^i=x \\ \text{random}& \text{otherwise} \end{cases}\]这个一无是处的function的loss可是0啊!
为什么更有弹性的model更容易overfitting?
solution:
- 增加训练资料,如Data augmentation(数据增强) = 人工“造”更多训练数据,但不改变标签语义:如在图片识别里,将图片左右翻转、局部放大来获得更多的training data
- 不要让模型有这么大的弹性,去给模型一些限制 — 需要对研究的问题有较好的理解,一般有如下这些方法:
- 给比较少的参数/神经元数目
- 让model共用参数 — 详见CNN(fully-connected,弹性差)讲解部分
- 用比较少的feature
- early stopping
- regularization
- dropout
怎么选出一个好模型呢?
N-fold Cross Validation(N折交叉验证): 把数据分成 N 份,每次拿 1 份做测试(validation),其余做训练(train),重复 N 次,最后取平均
监督学习
\[\text{input} \xrightarrow{\text{mapping}} \text{output label}\]intro:想象你是一个房产中介,脑子里积累了大量经验:
- 100㎡的房子,卖了200万
- 150㎡的房子,卖了280万
- 80㎡的房子,卖了160万
现在来了一套120㎡的房子,你凭经验估价。
这个”凭经验估价”的过程,就是机器学习在做的事。
只不过机器没有”经验”,它只有数据。所以问题变成:
给定历史数据,如何找到一个数学规律,用来预测新数据?
这就是监督学习的本质。
定义
“监督”这个词来自:每条训练数据都有正确答案(标签);监督学习正是通过看到正确的输入X和期望的输出标签Y对来进行学习,算法最终期盼在仅有输入的情况下就输出准确的预测
1
2
3
4
5
6
7
输入 X → 模型 → 预测 ŷ
(房屋面积120㎡) (预测价格?)
↕ 对比
真实答案 y
(实际成交价230万)
三个核心要素:
| 要素 | 含义 | 房价例子 |
|---|---|---|
| 特征 X | 输入数据 | 面积、地段、房间数 |
| 标签 y | 正确答案 | 实际售价 |
| 模型 f | 找到的规律 | f(X) ≈ y |
目标:找到一个函数 f,使得 f(X) 尽可能接近真实的 y。
分类
回归(regression):从无限多个可能的数字中预测一个数字
分类(classification):只有有限个、离散的输出(categories) — 继而可以尝试寻找边界
无监督学习
定义
无监督学习使用的训练数据没有标签,目标是让算法自己从数据中发现隐藏的结构、模式或规律,而不是学习从输入到输出的映射
如:无监督学习可能把结果分配到不同的组或者簇中 — clustering
- Anomaly detection : find unusual data points
- Dimensionalty reduction : compress data using fewer numbers









