机器学习
传统编程 = 人写规则 → 程序处理数据 → 输出结果
机器学习 = 人提供数据+结果 → 程序自己发现规则
机器学习基础
监督学习
intro:想象你是一个房产中介,脑子里积累了大量经验:
- 100㎡的房子,卖了200万
- 150㎡的房子,卖了280万
- 80㎡的房子,卖了160万
现在来了一套120㎡的房子,你凭经验估价。
这个”凭经验估价”的过程,就是机器学习在做的事。
只不过机器没有”经验”,它只有数据。所以问题变成:
给定历史数据,如何找到一个数学规律,用来预测新数据?
这就是监督学习的本质。
定义
“监督”这个词来自:每条训练数据都有正确答案(标签)
1
2
3
4
5
6
7
输入 X → 模型 → 预测 ŷ
(房屋面积120㎡) (预测价格?)
↕ 对比
真实答案 y
(实际成交价230万)
三个核心要素:
| 要素 | 含义 | 房价例子 |
|---|---|---|
| 特征 X | 输入数据 | 面积、地段、房间数 |
| 标签 y | 正确答案 | 实际售价 |
| 模型 f | 找到的规律 | f(X) ≈ y |
目标:找到一个函数 f,使得 f(X) 尽可能接近真实的 y。
线性回归(Linear Regression)
线性回归假设, X 和 y 之间的关系是一条直线 \(ŷ = w · x + b\)
w和b是两个待定参数- 学习的过程 = 自动调参,让输出尽量准确
怎么衡量“准不准”
假设我们猜测:w=1.5, b=0:
1
2
3
4
5
6
7
8
9
真实数据:
x=100㎡ → y=200万
x=150㎡ → y=280万
x=80㎡ → y=160万
模型预测(w=1.5, b=0):
x=100 → ŷ=150 误差:200-150 = 50万 ❌
x=150 → ŷ=225 误差:280-225 = 55万 ❌
x=80 → ŷ=120 误差:160-120 = 40万 ❌
我们需要一个单一数字来描述”这组参数有多差劲”。
这就是损失函数(Loss Function):
1
2
3
4
MSE = (1/n) × Σ (y - ŷ)²
= (50² + 55² + 40²) / 3
= (2500 + 3025 + 1600) / 3
= 2375
为什么要平方? 两个原因:
- 消除正负号(+50 和 -50 的误差一样大)
- 惩罚大误差(50²=2500 远大于 10²=100,迫使模型优先修正大错误)
损失函数越小 = 模型越准。 学习的目标 = 找到让损失函数最小的 w 和 b。
那么该如何找到最好的w和b呢?
1
2
3
4
5
6
7
8
9
损失值
│ *
│ * *
│ * *
│ * *
│*_________*___
w值
↑
最低点 = 最优的w
- 横轴是
w的取值 - 纵轴是对应的损失
- 我们要找山谷的最低点
方法叫梯度下降(Gradient Descent)。
现在我们知道,机器通过反复调整 w 和 b,沿着”下坡方向”走,最终到达最低点。
1
2
3
4
5
6
7
8
9
10
11
12
13
① 准备数据 (x, y)
↓
② 初始化参数 w, b(随便猜一个)
↓
③ 用当前 w, b 计算预测值 ŷ = wx + b
↓
④ 计算损失 Loss = MSE(y, ŷ)
↓
⑤ 调整 w, b(让Loss变小)
↓
⑥ 重复③④⑤,直到Loss足够小
↓
⑦ 得到最终的 w, b → 模型训练完成
但是我们也看到了,线性回归必须依赖于“X和y是线性关系”这一前提
现实中:
- 房价不只取决于面积(地段、楼层、学区…)→ 多特征问题
- 关系不是直线(豪宅价格飞涨)→ 非线性问题
- 预测的不是数值,而是”是/否” → 分类问题
梯度下降 Gradient Descent:模型怎么学习
问题:怎么找最小的k和b
最naive的想法:把所有可能的 w 和 b 都试一遍,找最小的。
为什么不行?
w是连续实数,有无穷多个取值- 多个参数组合爆炸:100个参数 → 无穷∞次尝试
- 暴力搜索在数学上不可行
导数回答一个问题:
w 增加一点点,Loss 会怎么变?
1
2
3
导数 > 0 → w增大,Loss也增大 → 应该把w减小
导数 < 0 → w增大,Loss减小 → 应该把w增大
导数 = 0 → 到达最低点! → 停止
用图来看:
1
2
3
4
5
6
7
8
9
10
Loss
│ *
│ * *
│ * *
│ * *
│*_______________*___ w
左侧斜坡:导数<0(向右下坡)
右侧斜坡:导数>0(向左下坡)
最低点: 导数=0(停!)
对我们的损失函数求导
1
2
Loss = (1/n) × Σ(y - ŷ)²
ŷ = w·x + b
对 w 求导结果是:
1
2
∂Loss/∂w = (2/n) × Σ(ŷ - y) · x
∂Loss/∂b = (2/n) × Σ(ŷ - y)
“把每个样本的预测误差
(ŷ-y)加权平均一下,就是坡度。”
那我们怎么“走一步”呢?
1
2
w_new = w_old - α × ∂Loss/∂w
b_new = b_old - α × ∂Loss/∂b
减号:因为要往下坡走,坡度是正的就减小w,坡度是负的就增大w。
α 叫学习率(Learning Rate),控制每步走多远。
学习率
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),学习率 α,迭代次数 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 - α · grad_w
b = b - α · grad_b
输出:最终的 w, b
问题:局部最优
线性回归:没有这个问题,损失函数是碗形,只有一个最低点
神经网络:有这个问题,但实践中比理论担心的要好处理
多特征线性回归
intro:我们用的模型:
1 价格 = w × 面积 + b现实中的房价数据长这样:
面积(㎡) 房间数 楼层 距地铁(km) 房龄(年) 实际价格(万) 100 3 8 0.5 5 320 80 2 3 2.0 15 180 150 4 12 0.3 2 580 5个特征,每个都影响价格。
用之前的方法,要写:
1 价格 = w₁×面积 + w₂×房间数 + w₃×楼层 + w₄×距地铁 + w₅×房龄 + b现在问题来了:
- 特征从1个变成100个怎么办?写100项?
- 计算梯度要对每个w分别求导?
答案是:用向量和矩阵来统一表达。
向量:把多个特征打包
向量让”对所有特征做同一件事”变成一个操作。
1
2
3
4
5
6
7
8
把房子的特征打包成向量:x = [100, 3, 8, 0.5, 5]
面积 房间 楼层 地铁 房龄
打包权重:w = [w₁, w₂, w₃, w₄, w₅]
然后预测值变成:
ŷ = w₁×100 + w₂×3 + w₃×8 + w₄×0.5 + w₅×5 + b
= w · x + b ← 点积,一个操作搞定
点积的本质:两个向量对应位置相乘,然后求和。
当我们想预测所有样本:
1
2
3
4
ŷ = X · w + b
X是(3×5)矩阵,w是(5×1)向量
结果ŷ是(3×1)向量 = 3个样本的预测值
一行代码,同时预测所有样本。
这就是向量化的价值:不是循环遍历每个样本,而是用矩阵运算一次处理所有数据。GPU就是专门做这种大规模矩阵运算的硬件。
特征归一化
我们的特征:
1
2
3
面积:80 ~ 200 (量级:百)
房间数:1 ~ 5 (量级:个位)
距地铁:0.1~5.0 (量级:小数)
现在梯度下降更新参数:
1
2
w₁(面积) 的梯度 ≈ 很大(因为x₁数值大)
w₄(距地铁) 的梯度 ≈ 很小(因为x₄数值小)
结果:Loss的形状变成了一个狭长的椭圆,而不是圆形:
1
2
3
4
5
6
7
8
9
没有归一化: 归一化之后:
w₂ w₂
│ 椭圆形 │ 圆形
│ ┌──────┐ │ ┌──┐
│ │ * │ │ │* │
│ └──────┘ │ └──┘
└─────────── w₁ └─────── w₁
梯度下降路径:曲折低效 梯度下降路径:直接高效
解决方案:标准化(Z-score Normalization)
1
2
3
4
5
6
7
8
9
10
x_scaled = (x - μ) / σ
μ = 特征的均值
σ = 特征的标准差
# 面积原始值:[80, 100, 120, 150, 180]
# 均值μ=126, 标准差σ=36.7
# 归一化后:[-1.25, -0.71, -0.16, 0.65, 1.47]
# 全部压缩到同一量级
操作后每个特征都变成:均值为0,标准差为1。
常见误区:归一化参数(μ和σ)只能从训练数据上计算,然后用同样的参数处理测试数据。不能在测试数据上重新计算,否则就是”数据泄露”。
归一化之后,权重的大小直接反映特征的重要性:
| 特征 | 权重 | 含义 |
|---|---|---|
| 面积 | +1.8 | 正向影响,较重要 |
| 房间数 | +15.2 | 正向影响,最重要 |
| 楼层 | +0.3 | 正向影响,不重要 |
| 距地铁 | -22.5 | 负向影响,越远越便宜,最关键特征 |
| 房龄 | -2.1 | 负向影响,越老越便宜 |
权重不只是计算工具,它是模型对”什么因素影响结果”的自动发现。这是ML和传统编程最大的不同——规律是从数据中涌现出来的,不是人写进去的。
1
2
3
4
5
6
7
8
9
10
11
12
13
数据 X (矩阵)
↓
特征归一化
↓
ŷ = X @ w + b ← 前向传播
↓
Loss = MSE(y, ŷ) ← 衡量误差
↓
梯度 = X.T @ error ← 反向计算
↓
w -= α × 梯度 ← 参数更新
↓
重复直到收敛
这个框架,本质上和神经网络的训练框架完全相同。 神经网络只是在”前向传播”那一步变得更复杂了。
逻辑回归——从预测数值到预测类别
intro:为什么不能直接用线性回归做分类。
假设数据:判断肿瘤是否恶性(0=良性,1=恶性)
1 2 肿瘤大小(cm): 1 2 3 4 5 6 是否恶性: 0 0 0 1 1 1用线性回归强行拟合:
1 2 3 4 5 6 7 8 9 10 11 12输出值 1.2│ * * * │ / 0.5│ / │ / -0.2│ * * * └─────────────────── 肿瘤大小 1 2 3 4 5 6 问题1:输出值可以是1.2、-0.2,根本不是概率 问题2:如果来了个10cm的肿瘤,预测值变成2.5,毫无意义 问题3:决策边界会被极端值拖偏我们真正需要的输出:一个介于 0 和 1 之间的数,表示”是恶性的概率”。
Sigmoid函数
需求:我们需要一个函数f,满足:
1
2
3
4
① 输入:任意实数(-∞ 到 +∞)
② 输出:严格在 (0, 1) 之间
③ 单调递增(输入越大,概率越高)
④ 平滑可导(梯度下降需要)
所以我们就找到了Sigmoid函数: \(\sigma(z) = \frac{1}{1 + e^{-z}}\)
[ \sigma(z) = \frac{1}{1 + e^{-z}} ]