查看全课大纲(第 74 / 82 节)
- 1.函数与数据泛化
- 2.向量:从几何直觉到高维空间
- 3.矩阵与张量
- 4.特征值与特征向量:矩阵变换的不动方向
- 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
- 6.概率与统计:机器学习的数学基础
- 7.独立性与随机变量:贝叶斯公式与二项分布
- 8.期望、方差与协方差:随机变量的数字画像
- 9.联合概率:当一条轴不够用的时候
- 10.概率分布(一):从伯努利到伽马函数
- 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
- 12.机器学习的模型:找输入与输出的关系
- 13.机器学习的应用
- 14.过拟合
- 15.机器学习的分类
- 16.有监督学习:从房价预测说起
- 17.损失函数
- 18.梯度下降(上):更新规则与学习速率
- 19.梯度下降(2)
- 20.牛顿方法
- 21.KNN简介
- 22.距离测算
- 23.朴素贝叶斯
- 24.朴素贝叶斯的实际应用
- 25.信息熵:决策树构建的度量基础
- 26.信息增益与决策树的构建
- 27.ID3算法
- 28.决策树节点的划分
- 29.CART树
- 30.Bagging
- 31.Boosting:自适应提升算法
- 32.Gradient Boosting
- 33.线性模型:回归与分类的同一副骨架
- 34.最小二乘:把最优参数直接解出来
- 35.最小二乘法的几何与概率视角
- 36.极大似然:给最小二乘一个概率出身
- 37.逻辑回归(1):把线性模型接到分类上
- 38.逻辑回归(2):损失函数与多分类
- 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
- 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
- 41.优化方法(1):坐标下降与前向选择
- 42.优化方法(2):前向策略与最小角回归
- 43.拉格朗日乘子法(1):从无约束到等式约束
- 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
- 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
- 46.最大间隔:把分类问题变成优化问题
- 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
- 48.支持向量:边界上的少数派
- 49.SVM 计算过程:一笔到底算完,再走进核方法
- 50.贝叶斯公式:概率即信念
- 51.隐含变量和概率模型:给看不见的原因建模
- 52.EM 算法:期望与最大化的迭代
- 53.琴生不等式:EM 的数学钥匙
- 54.三个硬币问题:EM 的复盘与衔接
- 55.高斯混合模型:用隐变量解释双峰数据
- 56.隐马尔可夫模型
- 57.隐马尔可夫模型(2):前向算法与动态混合模型
- 58.隐马尔可夫模型在 NLP 中的运用
- 59.EM 算法:把「为什么收敛」推到底
- 60.HMM 在 NLP 中的运用:从分词到机器翻译
- 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
- 62.高维下的高斯模型
- 63.主题模型:从词袋到隐含主题
- 64.Dirichlet 分布(1)
- 65.Dirichlet 分布(2)
- 66.统计学习内容回顾
- 67.K-means 算法
- 68.PCA 降维
- 69.SVD 降维
- 70.从聚类到流形
- 71.局部线性嵌入
- 72.随机邻域嵌入:SNE 与 t-SNE
- 73.无参模型:让数据自己说话
- 74.高斯过程:函数空间里的正态分布
- 75.高斯过程(2):噪声、正则化与核的线性组合
- 76.神经网络简介:从感知器到自编码器
- 77.反向传播:把误差一层层传回去
- 78.卷积神经网络(CNN):让特征自己学出来
- 79.循环神经网络(RNN):给网络装上记忆
- 80.深度学习回顾:静态一类,动态一类
- 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
- 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传
高斯过程:函数空间里的正态分布
约 23 分钟
小象实战讲义 · 机器学习算法精讲
上一节我们讲了无参模型的开场:KNN 推广到核加权、KDE 用小高斯叠加出密度。这一节进入一个特别重要的非参数模型——高斯过程(Gaussian process,GP)。讲师说,讲这个过程时想换一种讲法、看能不能让大家理解得更好一点——这也正是把 GP 和 KDE 放在一起讲的原因:我们研究的问题是类似的,都是给定数据之后的估计问题。但 GP 会给出一个比 KDE 更进一步的答案:它假设函数值本身服从高维正态分布,于是对任何新点的估计,给出的不是一个点,而是一个完整的分布。
💡 核心导读
- process = 随机过程:Gaussian process(高斯过程)是随机过程(stochastic process)的一个特例——与高斯相关、定义在函数空间里。
- 核心假设:给定 ,它们对应的函数值 这个三维向量,服从三维空间里的正态分布——均值向量 + 协方差矩阵。
- 核函数填协方差:——自己跟自己对角线为 1,距离无穷大时趋 0;λ(兰姆达)是可调的宽窄旋钮。
- 加一维就能预测:把 加进联合分布变四维正态,用边缘概率取出 ——给的不是点的估计,是一个分布。
- 扫出一条带带子的线: 从 移到 ,学出中心线;有数据的点方差小,没有数据的地方方差大。
从三点数据到一个假设
还是上一节类似的场景,给定三个数据:、、。注意一个记号细节——它们对应的值,我们不用 表示,用 (讲师原话「一会告诉大家为什么我管它叫 f」):因为接下来要建模的对象,正是 所对应的函数关系 本身。现在来了新的问题:还有一个新的 ,想估计它所对应的 应该等于多少——这就是我们想估计的问题。
既然要做高斯过程,就需要一个假设。这个假设是: 所对应的函数关系 —— 映射到 这个函数上——在高维空间里服从一个正态分布。也就是说,给定的 是一个三维的向量,它服从三维空间上的高斯分布。
维度升级时,正态分布的「装备」也要升级:一维空间的正态分布是均值 和方差 ;到了不同维度,就是均值向量和协方差矩阵——所以这里的 变成了一个协方差矩阵,比如三维的情形:
所以「给定 ,它们对应的函数值在高维空间服从正态分布」——这就是对高斯过程的假设。(当然也可以假设成其他的分布,那就是不同的 process;我们先考虑高斯这一种。)
协方差矩阵 K:kernel 的用武之地
假设有了,默认均值 ,现在的问题是:协方差矩阵 K 应该怎么求?跟什么相关?
协方差代表的是这三个维度之间的关联和关系。每一个 都是一个随机变量—— 代表 跟 之间的关系:自己跟自己,相关性完全百分之百,所以 应该等于 1(、 同理)。
那 和 ,大家觉得哪个应该更大?它们描述的是 1 和 2 之间、1 和 3 之间的关系——而 1 到 2 的距离和 1 到 3 的距离不一样:离得近的一对,对应的值应该更高(讲师板书举例:比如近的那个是 0.7,远的那个是 0.2)。(讲师口播时把这句话说反了——「距离更大的话应该是更高的值」;按核函数的性质,距离小、相关性高、K 值大,正过来讲。)
那这些具体的 0.7、0.2 该怎么算?用什么办法?——我们刚才 18.1 节学了那么久,答案呼之欲出:用 kernel function。用核函数来构造协方差矩阵: 与 之间的相对距离,化成一个权重的关系,这个权重关系就是高维空间的协方差矩阵。定义:
验证它符合我们对 kernel 的定义:当 时,,——对角线永远是 1 ✓;当 与 的距离趋于无穷大时,权重趋于 0 ✓。而 (兰姆达)这个系数可以不断调整,从而构造出不同宽窄的核——λ 越大核越窄、λ 越小核越宽(注意它跟上一节 δ 的旋钮方向正好相反,大致是 的关系)。
加一维:四维联合分布
有了协方差矩阵,怎么用它估计 的 ?思路漂亮得出奇:如果 服从三维空间的正态分布,那我们再给它加一维—— 四个一起,就服从四维空间的正态分布。这就是基本假设。而且 可以不断加多:原来的 3 个数据再加 4 个新点,就是 7 维空间的分布;加 10 个,就是 13 维空间的分布——加得越多,就越高维的正态分布,这就是我们构造的高斯过程。
加了 之后,协方差矩阵跟着扩成 :原来的 全部不变,多出一列 (新点与三个旧点)、一行 ,以及右下角的 。写成分块形式:
其中 服从三维的 (以前那个空间); 单独属于一维。当然也可以一次加好多个数据,让 同时估计一批点——咱们先看最简单的一维情形。
那 的分布怎么从四维联合分布里「取」出来?这里用到正态分布的一个好性质:高维正态在任何方向上投影,边缘概率仍然是正态分布。想象一个二维正态——一口倒扣的碗,映射下来是一圈圈 contour 等高线——无论沿着 方向投影、还是沿任何斜的方向投影,得到的边缘分布仍然是正态分布。所以在四维正态里, 服从三维正态,而 这一维可以通过求边缘概率得到——它自己服从一个一维的正态分布,均值 和方差 就是关键。
(讲师在这里明确说:「我就不给大家具体去演算里边的最终每一步怎么算出来的」——因为到今天来讲,Gaussian process 有很多完整的代码,我们需要讲清楚的是背后的道理。下面直接给直观结果。)
μ* 与 σ*:给的不是点,是一个分布
通过贝叶斯公式,最终求出的结果是:
先做一遍维度走查: 是 (那个三维向量的转置),乘上 是 ,再乘 是 ——,最后得到的是一个标量,一个数,这个数就是 ; 算出来也是一个数(因为 是一维空间里的分布,就像它的均值和方差)。而 —— 自己跟自己——对角线永远是 1。
于是 (均值)和 (方差)就构成了对 的最好估计。注意最关键的一点:我们给出的估计,并不是一个点的估计,仍然是一个分布—— 以 为中心、以 为方差。这跟刚才的 kernel density estimation 是类似的情况:给定 的时候,不是只给这个点的最好估计,实际上给的是这个点和它所对应的方差——给了一个区域。
用代码把这套公式跑起来(三个观测点是讲义设定,核取 ):
import math
def kern(a, b, lam):
"""GP 核: k(x, x') = exp(-lam * (x - x')^2)"""
return math.exp(-lam * (a - b) ** 2)
def inv3(m):
"""3x3 矩阵求逆(伴随矩阵法)"""
a, b, c = m[0]
d, e, f = m[1]
g, h, i = m[2]
det = a*(e*i - f*h) - b*(d*i - f*g) + c*(d*h - e*g)
r = 1.0 / det
return [[(e*i - f*h)*r, (c*h - b*i)*r, (b*f - c*e)*r],
[(f*g - d*i)*r, (a*i - c*g)*r, (c*d - a*f)*r],
[(d*h - e*g)*r, (b*g - a*h)*r, (a*e - b*d)*r]]
def gp_predict(X, f, xq, lam):
"""GP 单点预测: 返回 K、k*、mu*、sigma*^2"""
n = len(X)
K = [[kern(X[i], X[j], lam) for j in range(n)] for i in range(n)]
Ki = inv3(K)
ks = [kern(x, xq, lam) for x in X]
Ki_f = [sum(Ki[i][j] * f[j] for j in range(n)) for i in range(n)]
mu = sum(ks[i] * Ki_f[i] for i in range(n))
Ki_ks = [sum(Ki[i][j] * ks[j] for j in range(n)) for i in range(n)]
s2 = kern(xq, xq, lam) - sum(ks[i] * Ki_ks[i] for i in range(n))
return K, ks, mu, s2
X = [1.0, 2.0, 4.0] # 三个观测点(讲义设定)
f = [1.2, 1.8, 2.1]
lam = 0.5
xq = 3.0
K, ks, mu, s2 = gp_predict(X, f, xq, lam)
print("协方差矩阵 K (lambda=0.5):")
for row in K:
print(" [" + " ".join(f"{v:.4f}" for v in row) + "]")
print(f"查询点 x* = {xq}")
print(f"k* = [{', '.join(f'{v:.4f}' for v in ks)}]")
print(f"mu* = {mu:.4f}, sigma*^2 = {s2:.4f}, sigma* = {math.sqrt(s2):.4f}")
print(f"90% 区间: [{mu - 1.645 * math.sqrt(s2):.4f}, {mu + 1.645 * math.sqrt(s2):.4f}]")
_, _, mu2, s22 = gp_predict(X, f, xq, 1.5)
print(f"核调窄 lambda=1.5: mu* = {mu2:.4f}, sigma*^2 = {s22:.4f}")
_, _, mu3, s23 = gp_predict(X, f, 10.0, 0.5)
print(f"x* = 10 (远离所有数据): mu* = {mu3:.4f}, sigma*^2 = {s23:.4f}")协方差矩阵 K (lambda=0.5):
[1.0000 0.6065 0.0111]
[0.6065 1.0000 0.1353]
[0.0111 0.1353 1.0000]
查询点 x* = 3.0
k* = [0.1353, 0.6065, 0.6065]
mu* = 2.0082, sigma*^2 = 0.2915, sigma* = 0.5399
90% 区间: [1.1200, 2.8964]
核调窄 lambda=1.5: mu* = 0.8284, sigma*^2 = 0.8983
x* = 10 (远离所有数据): mu* = 0.0000, sigma*^2 = 1.0000读这组输出:K 矩阵对角线全是 1、近的点对( 距离 1)协方差 0.6065、远的点对( 距离 3)掉到 0.0111——「距离近值高」的规则逐格兑现。 夹在 和 中间,,恰好落在两个观测值 1.8 和 2.1 之间——核加权插值;同时给出 、90% 区间 ——估计自带「打包票的幅度」。两个对照也很说明问题:把核调窄(), 与数据的关联被掐细,方差反而涨到 0.8983、均值被拉向先验 0;把 挪到 10——远离所有数据—— 全部归零,、:完全回到先验(均值 0、方差 1 的正态),模型诚实地承认「这里我一无所知」。
扫出一条线,带一条带子
把上一节 KDE 的收尾动作照搬过来:把 沿着最小值和最大值不断地移动,每移到一个位置就算一组 ——我们仍然可以学出一条线。而这条线自带一条「带子」:在已经有值的点上,方差小;在其他地方,方差大。给定三个点,中心线是得到的最好值的连线,外围的区域里有很大的概率(比如 90% 的概率都在这个区域里),三个观测点之间 variance 比较小。对任何给定的 ,都可以估计它落在这个区域里的概率有多大。
import math
# 数据与核同上一块(讲义设定), 此块自包含重写
def kern(a, b, lam):
return math.exp(-lam * (a - b) ** 2)
def inv3(m):
a, b, c = m[0]
d, e, f = m[1]
g, h, i = m[2]
det = a*(e*i - f*h) - b*(d*i - f*g) + c*(d*h - e*g)
r = 1.0 / det
return [[(e*i - f*h)*r, (c*h - b*i)*r, (b*f - c*e)*r],
[(f*g - d*i)*r, (a*i - c*g)*r, (c*d - a*f)*r],
[(d*h - e*g)*r, (b*g - a*h)*r, (a*e - b*d)*r]]
X = [1.0, 2.0, 4.0]
f = [1.2, 1.8, 2.1]
lam = 0.5
def gp_mu_sigma(xq):
n = len(X)
K = [[kern(X[i], X[j], lam) for j in range(n)] for i in range(n)]
Ki = inv3(K)
ks = [kern(x, xq, lam) for x in X]
Ki_f = [sum(Ki[i][j] * f[j] for j in range(n)) for i in range(n)]
mu = sum(ks[i] * Ki_f[i] for i in range(n))
Ki_ks = [sum(Ki[i][j] * ks[j] for j in range(n)) for i in range(n)]
s2 = kern(xq, xq, lam) - sum(ks[i] * Ki_ks[i] for i in range(n))
return mu, math.sqrt(max(s2, 0.0))
print("x* 扫描 (lambda=0.5):")
for i in range(11):
xq = i * 0.5
mu, sg = gp_mu_sigma(xq)
print(f"x*={xq:4.1f} mu*={mu:8.4f} sigma*={sg:8.4f}")x* 扫描 (lambda=0.5):
x*= 0.0 mu*= 0.4119 sigma*= 0.7381
x*= 0.5 mu*= 0.7694 sigma*= 0.3870
x*= 1.0 mu*= 1.2000 sigma*= 0.0000
x*= 1.5 mu*= 1.5779 sigma*= 0.1706
x*= 2.0 mu*= 1.8000 sigma*= 0.0000
x*= 2.5 mu*= 1.9009 sigma*= 0.3423
x*= 3.0 mu*= 2.0082 sigma*= 0.5399
x*= 3.5 mu*= 2.1344 sigma*= 0.4058
x*= 4.0 mu*= 2.1000 sigma*= 0.0000
x*= 4.5 mu*= 1.7517 sigma*= 0.4615
x*= 5.0 mu*= 1.1786 sigma*= 0.7904这张表就是那条「线 + 带子」的数值形态。看 那一列的节律:恰好落在观测点上()时 ——有数据的地方,模型分毫不让(这是无噪声 GP 的精确插值性质);离开观测点半步( 只有 0.17)方差开始抬头;两点之间的空档( 夹在 2 与 4 之间较远的距离)涨到 0.54;两端越出数据范围( 和 )方差冲到 0.74~0.79,直奔先验的 1。而 那一列穿过三个观测值,在数据覆盖的区间里平滑爬升,出了区间就被拉回先验均值 0 的方向——中心线由数据说话,带子的宽窄由「离数据多远」说话。
GP 与 KDE:区别与相似
最后把两节课放在一起对齐。大家可以看到,GP 跟刚才描述的 KDE 有区别,但关键的部分有相似: 和 的构造,都是关于 和 的——而所有的 K 都是可求的: 根据 彼此之间的距离,带入 kernel 算出值的大小; 也一样可算:给定了新的 ,算它对 各种不同的距离,带入 kernel 就求出了 。
所以关键的问题还是看:给定一个对 的估计,就看 相对于每一个 之间的距离,和它们 之间互相的距离,决定出 kernel 构成的协方差矩阵,最后决定估计。这跟 KDE 只是形式不同——不同的地方在最开始的假设:GP 认为得到的函数所对应的函数关系,在函数空间、在高维空间里,服从一个高维的正态分布——这是高斯过程跟别的模型很不同的部分。
📝 动手练一练
- 概念辨析:GP 和 KDE 都用核函数、都从数据出发,它们研究的分别是什么问题?GP 对新点 给出的「估计」和 KDE 给出的有什么本质区别?
👉 点击查看参考答案
相似:都是非参数方法;核心都靠 kernel 把「距离」化成「权重/相关性」——KDE 里核是密度的小山峰,GP 里核是协方差矩阵的元素。
区别:KDE 研究的是数据本身的分布——给定 ,估计密度 ,回答「数据在这里出现的可能性多大」;GP 研究的是函数关系——假设函数值 的联合分布是高维正态,给定 回答「 最可能是什么、我有多确信」。GP 给出的估计不是一个点,而是一个分布 ——均值给预测、方差给置信。
- 单点 GP 手算:只有一个观测点 ,核 。预测点 :手算 、、 和 。
👉 点击查看参考答案
单点情形:,所以 ;;。
预测 :只有一个观测点、距离又是 1,所以不确定性相当大。若把 挪远, 变小, 趋向先验均值 0、 趋向先验方差 1——跟正文中 的机器验证一致。
本章小结
这一节从「估计问题」出发构造了高斯过程。第一,核心假设: 对应的函数关系 ,其函数值 作为三维向量服从三维正态——均值向量(默认 0)+ 协方差矩阵;也可以假设别的分布,那就是别的 process。第二,协方差矩阵由核函数填:——对角线恒 1、无穷远趋 0、λ 调宽窄。第三,预测 = 加一维: 服从四维正态(加 4 个点就是 7 维、加 10 个就是 13 维),分块成 ;利用「高维正态任何方向的边缘仍是正态」的性质,取出 ,其中 、。第四,给的不是点的估计而是分布—— 沿 min 到 max 扫描,学出中心线加置信带:观测点上方差为 0、点间渐宽、数据之外回到先验。第五,与 KDE 的对照:一切都绕着 和 转,而它们全部由距离经 kernel 可求——GP 独特之处在于「函数值在高维空间服从正态分布」这个假设。下一节把 GP 重新正式定义、泛化到一般形式(均值函数 与协方差核 )。
📋 行动清单
- 口述假设:不借助资料,说清「为什么用 不用 」和高斯过程的核心假设(函数值联合正态)。
- 手算走查:独立完成练一练第 2 题的单点 GP,再用正文代码的思路验证。
- 跑改代码:把扫描块的观测点改成 、函数值改成自己编的三个数,观察 的行怎么移动。
- 对照两节课:各用一句话说清 KDE 与 GP 里 kernel 的角色(密度的小山峰 vs 协方差的元素),以及两者「给估计」的方式差在哪。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载第18章笔记:核方法(Kernel)