Post

机器学习

机器学习

传统编程 = 人写规则 → 程序处理数据 → 输出结果

机器学习 = 人提供数据+结果 → 程序自己发现规则

机器学习基础

监督学习

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\)

  • wb 是两个待定参数
  • 学习的过程 = 自动调参,让输出尽量准确

怎么衡量“准不准”

假设我们猜测: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

为什么要平方? 两个原因:

  1. 消除正负号(+50 和 -50 的误差一样大)
  2. 惩罚大误差(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)房龄(年)实际价格(万)
100380.55320
80232.015180
1504120.32580

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}} ]

This post is licensed under CC BY 4.0 by the author.

Trending Tags