90 分钟学习安排:何时需要非线性?
- 目标:
- 课堂安排:
- 先修先答: 两类均值相同但协方差不同,LDA 与 QDA 中谁能利用这一差异?
- 反馈: QDA;它为每类估计 \(\Sigma_k\),代价是参数更多、方差更大。
本章核心问题
当现实世界不是线性时,我们该怎么办?
我们之前的学习大多集中在线性模型,它们是计量经济学和机器学习的基石。
但是,现实世界中的经济和金融关系往往是复杂的、非线性的。
线性模型的局限性:一个直观例子
假设我们用线性回归拟合一组明显呈曲线关系的数据。
线性边界 ✕ 同心圆数据 → 需要曲线边界
观察
模型系统性地低估了两端,高估了中间。均方误差(MSE)很高。
现实中的非线性关系
- 信贷违约风险:
- 资产收益率: 资产收益与市场因子的关系(Beta)可能随市场波动性变化而变化,呈现非线性。
- 消费者行为: 价格折扣对销量的提升效应会逐渐减弱,呈现饱和曲线,而非直线。
我们的探索之旅:三条非线性路径
本章我们将系统地学习三类强大的非线性模型,以应对线性模型的局限性。
分段线性判别:最直观的“蛮力”方法
核心思想
如果一条直线不行,那就用多条直线来拼接和逼近。
. . .
这个策略通常分为两步:
- 子类划分 (Subclass Partitioning): 将原始的某一类(例如蓝色点)进一步细分为多个子类,使得每个子类都能够与其他类别线性可分。
- 同类合并 (Class Merging): 在做出最终决策时,将所有属于同一原始大类的子类的预测结果合并。
分段线性判别的几何直观 (1/3)
首先,我们有一个无法被单一直线分开的数据集。
分段线性判别的几何直观 (2/3)
我们将“信用良好”(蓝色)的客户群体,想象成两个潜在的子群体。然后我们用两条不同的直线来分割。
- 直线1: 分开了紫色群体和蓝色“左下”子群体。
- 直线2: 分开了紫色群体和蓝色“右上”子群体。
分段线性判别的几何直观 (3/3)
最终的决策边界,是由这两条直线的一部分组合而成的“V”形。
关联思想:决策树
决策树模型本质上也是在进行一种分段划分。它通过一系列与坐标轴平行的“分割”,将特征空间切分成多个矩形区域。
分段线性判别的优势与局限
| 思想简单,易于理解 |
如何划分子类是关键难题 |
| 可以利用现有的线性模型算法 |
子类数量需要预先设定 (超参数) |
| 只要分段足够多,可以拟合任意复杂的形状 |
容易产生过拟合 (Overfitting) |
| 与决策树等模型思想相通 |
模型的“整体性”和“优雅性”不足 |
结论
分段线性判别是一个有效的“启发式”方法,但在实践中,我们通常寻求更系统、更优雅的解决方案。
QDA:不再满足于逼近,直接拟合曲线
二次判别分析 (Quadratic Discriminant Analysis, QDA) 不再用多条直线拼接,而是直接构建一个二次型的决策边界。
对于我们的月亮型数据,一条优美的抛物线就可以很好地完成分类任务。
QDA判别函数的形式
QDA 的判别函数 \(f(\mathbf{x}_n)\) 是一个关于输入向量 \(\mathbf{x}_n\) 的二次函数:
\[
\large{ f(\mathbf{x}_n, \mathbf{W}, \mathbf{w}, b) = \mathbf{x}_n^T \mathbf{W} \mathbf{x}_n + \mathbf{w}^T \mathbf{x}_n + b }
\]
- \(\mathbf{x}_n \in \mathbb{R}^d\): d维的特征向量。
- \(\mathbf{W} \in \mathbb{R}^{d \times d}\): 二次项系数矩阵,它“掰弯”了决策边界。
- \(\mathbf{w} \in \mathbb{R}^d\): 线性项系数向量,它“平移”决策边界。
- \(b \in \mathbb{R}\): 偏置项。
QDA的核心:二次项矩阵 \(\mathbf{W}\)
在判别函数中,真正让 QDA 具有非线性能力的是二次项 \(\mathbf{x}_n^T \mathbf{W} \mathbf{x}_n\)。
- 如果 \(\mathbf{W} = \mathbf{0}\),那么 QDA 就退化为了我们熟悉的线性判别分析 (LDA)。决策边界是线性的。
- 如果 \(\mathbf{W} \neq \mathbf{0}\),决策边界 \(f(\mathbf{x}_n) = 0\) 就是一个二次曲面 (在二维空间中是圆锥曲线,如椭圆、抛物线、双曲线)。
\(\mathbf{W}\) 的几何意义:决定边界形状
不同的 \(\mathbf{W}\) 矩阵可以生成各种形状的二次曲面边界。
QDA的“代价”:参数数量的爆炸
这种灵活性是有代价的。我们来数一下模型的参数个数:
- 矩阵 \(\mathbf{W}\): 由于 \(\mathbf{W}\) 可以被假定为对称矩阵,它有 \(d(d+1)/2\) 个独立参数。
- 向量 \(\mathbf{w}\): 有 \(d\) 个参数。
- 标量 \(b\): 有 \(1\) 个参数。
总参数数量为 \(\frac{d(d+1)}{2} + d + 1\),大约为 \(O(d^2)\)。
参数数量爆炸的后果
| 2 |
6 |
| 10 |
66 |
| 50 |
1,326 |
| 100 |
5,151 |
| 1000 |
~500,000 |
这会带来两个严峻的实际问题:
- 计算量巨大: 求解一个包含 \(d^2\) 级别参数的模型非常耗时。
- 样本需求量大: 要稳健地估计这么多参数,需要非常大的样本量,否则极易过拟合。
从概率视角理解QDA:更实用的路径
实践中,我们不直接去拟合那个庞大的 \(\mathbf{W}\) 矩阵,而是通过概率生成模型的思路来推导 QDA。
核心假设: 我们假设每个类别 \(k\) 的数据都服从一个多元高斯分布 (Multivariate Gaussian Distribution)。
\[
\large{ p(\mathbf{x} | y=k) = \mathcal{N}(\mathbf{x} | \boldsymbol{\mu}_k, \boldsymbol{\Sigma}_k) }
\]
- \(\boldsymbol{\mu}_k\): 第 \(k\) 类的数据均值向量(分布的中心)。
- \(\boldsymbol{\Sigma}_k\): 第 \(k\) 类的数据协方差矩阵(分布的形状和方向)。
QDA与LDA的关键区别:协方差矩阵
QDA 和 LDA 都假设数据服从高斯分布,但它们对协方差矩阵的假设不同。
LDA (线性判别分析): 假设所有类别的协方差矩阵是相同的。 \(\boldsymbol{\Sigma}_1 = \boldsymbol{\Sigma}_2 = \dots = \boldsymbol{\Sigma}_K = \boldsymbol{\Sigma}\) 这个强假设导致了线性的决策边界。
QDA (二次判别分析): 假设每个类别的协方差矩阵可以是不同的。 \(\boldsymbol{\Sigma}_i \neq \boldsymbol{\Sigma}_j\) for \(i \neq j\) 这个更宽松的假设,恰恰是产生二次决策边界的根源。
可视化对比:LDA vs. QDA 的假设
QDA判别函数的推导
\[
\large{ \delta_k(\mathbf{x}) = -\frac{1}{2} \log |\boldsymbol{\Sigma}_k| - \frac{1}{2} (\mathbf{x} - \boldsymbol{\mu}_k)^T \boldsymbol{\Sigma}_k^{-1} (\mathbf{x} - \boldsymbol{\mu}_k) + \log \pi_k }
\]
这是一个关于 \(\mathbf{x}\) 的二次函数!其中二次项来自于 \(\mathbf{x}^T \boldsymbol{\Sigma}_k^{-1} \mathbf{x}\)。
什么是马氏距离 (Mahalanobis Distance)?
QDA 判别函数的核心是马氏距离的平方:
\[
\large{ D_M^2(\mathbf{x}, \boldsymbol{\mu}_k) = (\mathbf{x} - \boldsymbol{\mu}_k)^T \boldsymbol{\Sigma}_k^{-1} (\mathbf{x} - \boldsymbol{\mu}_k) }
\]
- 直观理解: 它衡量了一个点 \(\mathbf{x}\) 到一个分布中心 \(\boldsymbol{\mu}_k\) 的“统计距离”。
- 它考虑了数据本身的协方差结构 \(\boldsymbol{\Sigma}_k\)。如果数据在某个方向上变化很大(方差大),那么在这个方向上的距离就会被“缩小”。
- QDA 不能只比较马氏距离;还必须比较每类的 \(-\tfrac12\log|\Sigma_k|\) 体积惩罚与 \(\log\pi_k\) 先验项。
可视化:欧氏距离 vs. 马氏距离
结论: 点A在数据分布的“长轴”方向上,虽然欧氏距离远,但统计上更“近”。
QDA决策规则
对于一个二分类问题 (类别 \(\omega_1, \omega_2\)),我们可以定义一个判别函数 \(f_i(\mathbf{x})\):
\[
\large{ f_i(\mathbf{x}) = \log\pi_i - \frac{1}{2}\log|\boldsymbol{\Sigma}_i| - \frac{1}{2}(\mathbf{x} - \boldsymbol{\mu}_i)^T \boldsymbol{\Sigma}_i^{-1} (\mathbf{x} - \boldsymbol{\mu}_i) }
\]
决策规则:
- 如果 \(f_1(\mathbf{x}) \geq f_2(\mathbf{x})\),则预测为类别 \(\omega_1\)。
- 否则,预测为类别 \(\omega_2\)。
由于每个类的 \(f_i(\mathbf{x})\) 都有自己的协方差矩阵 \(\boldsymbol{\Sigma}_i\),决策边界 \(f_1(\mathbf{x}) = f_2(\mathbf{x})\) 是一个二次函数。
QDA总结:何时应该使用它?
- 适用场景:
- 优点: 模型形式明确,比许多非线性方法(如核SVM)的计算成本更低。
- 缺点: 对高斯分布的假设较强。当特征维度 \(d\) 很高时,参数数量会变得非常大,需要大量数据来避免过拟合,且计算成本高。
第三部分:核方法 (The Kernel Trick)
核方法:一个“改变游戏规则”的思想
QDA虽然有效,但它只局限于二次函数。如果我们想拟合更复杂的边界怎么办?
核方法 (Kernel Method) 提供了一个极其优雅和强大的思路:
我们不直接在原始的低维空间中学习一个复杂的非线性模型,而是将数据通过一个非线性映射 \(\phi(\mathbf{x})\) 投射到一个更高维度的“特征空间”,然后在这个高维空间中学习一个简单的线性模型。
这个思想,通常被称为“核技巧 (Kernel Trick)”,是现代机器学习的基石之一。
核方法的核心直观:升维打击 (1/3)
想象一下一维空间中的一些数据点,它们无法被一个“点”(0维超平面)分开。
核方法的核心直观:升维打击 (2/3)
我们定义一个简单的非线性映射 \(\phi(x) = (x, x^2)\),将一维数据点 \(x\) 映射到二维空间。
核方法的核心直观:升维打击 (3/3)
奇迹发生了! 在这个新的二维空间里,数据点变得可以用一条直线分开了。
核方法的完整流程图
映射后的新家:希尔伯特空间
对于我们经济系的学生,你不需要深入了解它的严格数学定义。可以这样直观理解:
一个希尔伯特空间,就是一个行为表现良好的、可能无穷维的广义欧几里得空间。
在这里,我们可以像在普通空间一样,放心地计算长度、距离和角度。
希尔伯特空间的关键性质:内积
希尔伯特空间最重要的性质是它定义了内积 (Inner Product),我们记为 \(\langle \cdot, \cdot \rangle_{\mathcal{H}}\)。
内积是我们熟悉的点积 (dot product) 的推广:
\[
\begin{aligned}
\langle \mathbf{x}_1, \mathbf{x}_2 \rangle
&= \mathbf{x}_1^T \mathbf{x}_2
= \sum_{i=1}^d x_{1,i}x_{2,i}, \\
\langle \mathbf{x}_1, \mathbf{x}_2 \rangle
&= \lVert\mathbf{x}_1\rVert\,\lVert\mathbf{x}_2\rVert\cos\theta.
\end{aligned}
\]
有了内积,我们就可以定义范数 (Norm, 即长度) 和 距离 (Distance),就像在欧氏空间中一样。内积还包含了向量间夹角的信息,因此可以衡量相似度。
核技巧的“魔法”所在
我们回顾一下,很多线性算法(如SVM, PCA, 线性回归)的最终计算都可以只用样本点的内积 \(\langle \mathbf{x}_i, \mathbf{x}_j \rangle\) 来表示。
那么,如果我们将数据映射到高维空间 \(\phi(\mathbf{x})\),算法就需要计算映射后的内积 \(\langle \phi(\mathbf{x}_i), \phi(\mathbf{x}_j) \rangle_{\mathcal{H}}\)。
问题:
- 映射函数 \(\phi(\mathbf{x})\) 可能非常复杂,甚至我们都不知道它的显式形式。
- 高维空间的维度可能非常高,甚至是无穷维,直接计算内积是不可能的。
核函数:通往高维空间的捷径
核函数 (Kernel Function) \(K(\mathbf{x}_i, \mathbf{x}_j)\) 的定义就是为了解决这个问题:
一个函数 \(K(\mathbf{x}_i, \mathbf{x}_j)\) 如果能被写成某个映射 \(\phi\) 在高维空间中的内积形式,即 \(K(\mathbf{x}_i, \mathbf{x}_j) = \langle \phi(\mathbf{x}_i), \phi(\mathbf{x}_j) \rangle_{\mathcal{H}}\),那么这个函数 \(K\) 就是一个核函数。
核技巧的精髓
我们可以直接计算 \(K(\mathbf{x}_i, \mathbf{x}_j)\),而不显式构造 \(\phi(\mathbf{x})\) 的高维坐标。
这节省的是特征展开的计算与存储;它并不消除高维模型的统计困难、样本复杂度或过拟合风险。
例子:一个简单的二次核函数
假设我们的原始数据是二维的 \(\mathbf{x} = (x_1, x_2)\)。 考虑一个简单的核函数: \(K(\mathbf{x}_i, \mathbf{x}_j) = (\mathbf{x}_i^T \mathbf{x}_j)^2\)。
让我们展开它:
\[
\begin{aligned}
\large{ K(\mathbf{x}_i, \mathbf{x}_j) } &= \large{ (x_{i1}x_{j1} + x_{i2}x_{j2})^2 } \\
&= \large{ x_{i1}^2x_{j1}^2 + x_{i2}^2x_{j2}^2 + 2x_{i1}x_{j1}x_{i2}x_{j2} } \\
&= \large{ \langle (x_{i1}^2, x_{i2}^2, \sqrt{2}x_{i1}x_{i2}), (x_{j1}^2, x_{j2}^2, \sqrt{2}x_{j1}x_{j2}) \rangle }
\end{aligned}
\]
这说明,这个简单的核函数背后隐含了一个从二维到三维的映射: \(\phi(\mathbf{x}) = (x_1^2, x_2^2, \sqrt{2}x_1x_2)\)。
如何判断一个函数是合法的核函数?
一个函数 \(K(\mathbf{x}_i, \mathbf{x}_j)\) 必须满足什么条件,才能保证它背后一定存在一个希尔伯特空间和一个映射 \(\phi\) 呢?
Mercer 定理给出了答案:
对于任意有限的数据点集合 \(\{\mathbf{x}_1, \dots, \mathbf{x}_N\}\),如果由核函数计算出的 Gram 矩阵 \(\mathbf{K}\) 都是半正定的,那么 \(K\) 就是一个合法的核函数。
什么是Gram矩阵?
Gram 矩阵 \(\mathbf{K}\) 是一个 \(N \times N\) 的对称矩阵,其中每个元素 \(K_{ij}\) 都是由第 \(i\) 个和第 \(j\) 个数据点通过核函数计算得到的值:
\[
\large{
\mathbf{K} =
\begin{pmatrix}
K(\mathbf{x}_1, \mathbf{x}_1) & K(\mathbf{x}_1, \mathbf{x}_2) & \dots & K(\mathbf{x}_1, \mathbf{x}_N) \\
K(\mathbf{x}_2, \mathbf{x}_1) & K(\mathbf{x}_2, \mathbf{x}_2) & \dots & K(\mathbf{x}_2, \mathbf{x}_N) \\
\vdots & \vdots & \ddots & \vdots \\
K(\mathbf{x}_N, \mathbf{x}_1) & K(\mathbf{x}_N, \mathbf{x}_2) & \dots & K(\mathbf{x}_N, \mathbf{x}_N)
\end{pmatrix}
}
\]
直观理解
Gram 矩阵是一个成对相似度矩阵,描述了数据集中所有样本点之间的关系。半正定性保证了这种“相似度”是几何上一致的(没有自相矛盾)。
常用核函数:你的“弹药库”
幸运的是,我们不需要每次都自己去验证 Mercer 定理。有很多现成的、被证明是合法的核函数供我们使用。
选择核函数的基本原则
先保证对称 Gram 矩阵半正定,再依据任务含义、缩放方式和无泄漏验证选择核与超参数。
只有在特定核和已定义的相似度下,核值才可作单调相似度解释;
例如 RBF 核随标准化后的欧氏距离单调下降,而未归一化线性核并不满足这一普遍规则。
接下来我们介绍几种最常用的核函数。
常用核函数1:线性核 (Linear Kernel)
\[
\large{ K(\mathbf{x}_i, \mathbf{x}_j) = \mathbf{x}_i^T \mathbf{x}_j }
\]
- 本质: 就是原始空间中的点积。
- 对应映射: \(\phi(\mathbf{x}) = \mathbf{x}\),即没有进行映射。
- 应用: 当数据本身就是线性可分时,使用线性核的SVM就是标准的线性SVM。这是最简单、最快的基准。
常用核函数2:多项式核
\[
\large{ K(\mathbf{x}_i, \mathbf{x}_j) = (\gamma \mathbf{x}_i^T \mathbf{x}_j + r)^d }
\]
- 参数:
- \(d\): 多项式的次数 (degree)。
- \(\gamma\): 缩放系数。
- \(r\): 非负常数项;通常取 \(d\in\mathbb N\)、\(\gamma>0\)、\(r\ge0\) 以得到标准合法核。
- 本质: \(r=0\) 时是只含总次数 \(d\) 的齐次多项式核;\(r>0\) 时才通过展开包含 0 到 \(d\) 次项。
- 应用:
常用核函数3:高斯核 (RBF Kernel)
径向基函数核 (Radial Basis Function Kernel),通常也叫高斯核,是常用且具有通用逼近能力的候选核,但不是脱离任务与验证方法的“最强”选择。
\[
\large{ K(\mathbf{x}_i, \mathbf{x}_j) = \exp \left( -\frac{||\mathbf{x}_i - \mathbf{x}_j||^2}{2\sigma^2} \right) = \exp(-\gamma ||\mathbf{x}_i - \mathbf{x}_j||^2) }
\]
- 参数:
- \(\sigma\) (或 \(\gamma = 1/(2\sigma^2)\)): 控制核的“宽度”。
- 本质: 相似度完全由两点间的欧氏距离决定。两点距离越近,核函数值越接近1;距离越远,核函数值越接近0。
- 对应映射: 这是一个非常强大的核,因为它对应于一个无穷维的特征空间。
RBF核的直观理解
你可以将每个数据点想象成在空间中放置一个高斯“小山丘”。RBF核的决策边界是由这些“小山丘”的等高线组合而成的。
如何阅读 RBF 的 γ 图示
- 小 \(\gamma\) (大 \(\sigma\)): 核的半径很大,影响力范围广,决策边界非常平滑。
- 大 \(\gamma\) (小 \(\sigma\)): 核的半径很小,每个数据点的影响范围有限,决策边界会变得非常曲折,容易过拟合。
结论
RBF 是候选而非默认赢家;以无泄漏验证和简单基准决定是否采用。
常用核函数4:Sigmoid核 (Sigmoid Kernel)
\[
\large{ K(\mathbf{x}_i, \mathbf{x}_j) = \tanh(\alpha \mathbf{x}_i^T \mathbf{x}_j + \beta) }
\]
- 参数: \(\alpha\) 和 \(\beta\)。
- 来源: 它的形式来源于神经网络中的激活函数。
- 注意: Sigmoid核只有在特定参数下才满足Mercer条件,因此在实践中不如RBF核和多项式核常用。
核心内容提示:KPCA/KSVM 应用为拓展内容;核心内容在核技巧与 Gram 检查后直接进入福耀玻璃主要案例。
核方法的应用:算法的“核化” (Kernelization)
\[
\large{ \langle \mathbf{x}_i, \mathbf{x}_j \rangle \quad \xrightarrow{\text{Kernelize}} \quad K(\mathbf{x}_i, \mathbf{x}_j) = \langle \phi(\mathbf{x}_i), \phi(\mathbf{x}_j) \rangle_{\mathcal{H}} }
\]
这使得我们能够将线性算法无缝升级,赋予其强大的非线性处理能力。
经典应用
我们将介绍两个经典应用:
- 核主成分分析 (Kernel PCA)
- 核支持向量机 (Kernel SVM)
应用1:核主成分分析 (KPCA)
拓展内容提示:核心内容在核技巧与 Gram 矩阵检查后直接进入福耀玻璃主要案例;KPCA 与 KSVM 推导在完成核心内容后选讲。
PCA 的局限性
回顾PCA:
- 计算数据的协方差矩阵 \(\mathbf{\Sigma} = \frac{1}{N} \sum_{n=1}^N \mathbf{x}_n \mathbf{x}_n^T\) (假设数据已中心化)。
- 对 \(\mathbf{\Sigma}\) 进行特征值分解,找到主要的特征向量(主成分)。
问题
PCA只能发现数据中的线性结构。对于非线性结构,PCA会失效。
KPCA 的思路
KPCA的思路:
- 将数据映射后中心化:\(\tilde\phi(\mathbf{x}_n)=\phi(\mathbf{x}_n)-N^{-1}\sum_m\phi(\mathbf{x}_m)\)。
- 在特征空间计算协方差:\(\mathbf{\Sigma}_\phi = N^{-1} \sum_n \tilde\phi(\mathbf{x}_n)\tilde\phi(\mathbf{x}_n)^T\)。
- 对 \(\mathbf{\Sigma}_\phi\) 进行特征值分解。
KPCA的计算技巧
\[
\mathbf{v} = \sum_{n=1}^N \alpha_n \phi(\mathbf{x}_n)
\]
令 \(H=I-N^{-1}\mathbf 1\mathbf 1^T\),先把原始 Gram 矩阵双中心化为 \(K_c=HKH\)。特征值问题为:
\[
\large{ K_c\boldsymbol{\alpha}=N\lambda\boldsymbol{\alpha},\qquad
\boldsymbol{\alpha}^{T}K_c\boldsymbol{\alpha}=1 }
\]
这里 \(K_{ij}=K(\mathbf{x}_i,\mathbf{x}_j)\);\(K_c\) 对应中心化后的特征。整个计算避免显式构造 \(\phi\),但不能省略中心化。
回顾线性SVM
- 目标: 找到一个能将两类数据分开,并且间隔 (margin) 最大的超平面。
- 数学上, 这等价于求解一个带约束的二次优化问题。
对偶问题 (Dual Problem)
- 通过拉格朗日对偶,可以把SVM的优化问题转化为“对偶形式”。
- 神奇的是,在对偶形式中,优化目标和决策函数都只依赖于数据点的内积 \(\mathbf{x}_i^T \mathbf{x}_j\)。
软间隔 SVM 对偶:目标与可行域
对带惩罚参数 \(C\) 的软间隔线性 SVM,对偶问题必须同时写出目标与可行域:
\[
\begin{aligned}
\max_{\boldsymbol{\lambda}}\quad
&\sum_{n=1}^{N}\lambda_n-
\frac12\sum_{n=1}^{N}\sum_{m=1}^{N}
\lambda_n\lambda_m y_ny_m\,\mathbf{x}_n^{\mathsf T}\mathbf{x}_m,\\
\text{s.t.}\quad
&0\le \lambda_n\le C\quad(n=1,\ldots,N),\\
&\sum_{n=1}^{N}\lambda_n y_n=0.
\end{aligned}
\]
其中 \(\lambda_n\) 是拉格朗日乘子,\(y_n\in\{-1,+1\}\)。硬间隔情形移除上界 \(C\),保留 \(\lambda_n\ge0\) 与等式约束。
从 SVM 到 KSVM:只替换内积
核化只替换目标函数与决策函数中的内积,不改变可行域:
\[
\large{ \mathbf{x}_m^T \mathbf{x}_n \quad \longrightarrow \quad K(\mathbf{x}_m, \mathbf{x}_n) }
\]
核 SVM 的完整对偶为:
\[
\begin{aligned}
\max_{\boldsymbol{\lambda}}\quad
&\sum_{n=1}^{N}\lambda_n-
\frac12\sum_{n=1}^{N}\sum_{m=1}^{N}
\lambda_n\lambda_m y_ny_m K(\mathbf{x}_n,\mathbf{x}_m),\\
\text{s.t.}\quad
&0\le \lambda_n\le C,\qquad
\sum_{n=1}^{N}\lambda_n y_n=0.
\end{aligned}
\]
进入主要案例前的学习目标回顾
- 答案: QDA。LDA 强制共享协方差,QDA 为每类估计 \(\Sigma_k\),因而边界含二次项;代价是参数更多、估计更不稳定。
形成性检查 1:完整 QDA 判别值
两类到样本的马氏距离相同,但 \(|\Sigma_1|=1\)、\(|\Sigma_2|=4\) 且先验相同。哪类判别值更大?
答案
类 1。距离项相消后,\(-\tfrac12\log|\Sigma_1|=0\),类 2 为 \(-\tfrac12\log4<0\)。所以“选最近马氏距离”不是完整 QDA。
主要案例:福耀玻璃 QDA 下跌分类
Code
from pathlib import Path # 使用统一路径对象定位数据文件
import pandas as pd # 导入表格工具以读取本地行情
from sklearn.pipeline import make_pipeline # 在每个训练期内同时完成缩放和 QDA 拟合
from sklearn.preprocessing import StandardScaler # 统一收益与波动率的数值尺度
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis # 在见到测试标签前同时确定线性与二次对照
from sklearn.metrics import balanced_accuracy_score, roc_auc_score # 评价类别判断与概率排序
# 公网下载:https://assets.qiufei.site/data/stock/stock_price_pre_adjusted.h5
# 下载后把下一行改为本机文件位置;按课程结构存放时可用 Path("data/stock/stock_price_pre_adjusted.h5")。
# Windows:Path(r"C:\qiufei\data\stock\stock_price_pre_adjusted.h5")
# macOS:Path("/Users/你的用户名/data/stock/stock_price_pre_adjusted.h5")
# Linux:Path("/home/你的用户名/data/stock/stock_price_pre_adjusted.h5")
price_path = Path("/home/ubuntu/r2_data_mount/data/stock/stock_price_pre_adjusted.h5")
price_rows = pd.read_hdf(price_path, key='data', where=['order_book_id=="600660.XSHG"', 'date>=Timestamp("2018-01-01")', 'date<=Timestamp("2024-12-31")'], columns=['close']) # 仅载入目标公司与时期
qda_frame = price_rows.reset_index().sort_values('date') # 按交易日建立预测顺序
qda_frame['return_t'] = qda_frame['close'].pct_change() # 构造单日收益率
qda_frame['return_5d_t'] = qda_frame['close'].pct_change(5) # 构造五日累计收益
qda_frame['volatility_5d_t'] = qda_frame['return_t'].rolling(5).std() # 构造短期历史波动率
qda_frame['volatility_20d_t'] = qda_frame['return_t'].rolling(20).std() # 构造月度历史波动率
qda_frame['future_return_t1'] = qda_frame['return_t'].shift(-1) # 先保留连续未来收益以识别末端未知标签
qda_frame['target_date_t1'] = qda_frame['date'].shift(-1) # 保存标签实现日以清除跨窗口样本
qda_frame = qda_frame.dropna() # 在整数化之前删除滚动窗口与未来收益缺失行
Code
qda_frame['down_t1'] = (qda_frame['future_return_t1'] < 0).astype(int) # 仅把真实可观测的未来收益转换为类别
assert qda_frame['future_return_t1'].notna().all() and qda_frame['date'].max() < price_rows.reset_index()['date'].max() # 确认最后特征日早于最后标签实现日
split_row = int(len(qda_frame) * 0.8) # 固定前百分之八十为训练期
test_start_date = qda_frame.iloc[split_row]['date'] # 从完整序列确定测试起点
train_rows = qda_frame[(qda_frame['date'] < test_start_date) & (qda_frame['target_date_t1'] < test_start_date)] # 清除到测试期才实现的训练标签
test_rows = qda_frame[qda_frame['date'] >= test_start_date] # 确定测试特征窗口
assert train_rows['target_date_t1'].max() < test_rows['date'].min() # 确认标签实现边界
feature_names = ['return_t', 'return_5d_t', 'volatility_5d_t', 'volatility_20d_t'] # 声明模型实际字段
lda_model = make_pipeline(StandardScaler(), LinearDiscriminantAnalysis()).fit(train_rows[feature_names], train_rows['down_t1']) # 拟合事先说明的线性对照
qda_model = make_pipeline(StandardScaler(), QuadraticDiscriminantAnalysis(reg_param=0.1)).fit(train_rows[feature_names], train_rows['down_t1']) # 拟合事先说明的轻度协方差收缩 QDA
lda_probability = lda_model.predict_proba(test_rows[feature_names])[:, 1] # 在同一测试期计算 LDA 概率
down_probability = qda_model.predict_proba(test_rows[feature_names])[:, 1] # 在同一测试期计算 QDA 概率
descriptive_comparison = pd.DataFrame([{'model': 'LDA', 'roc_auc': roc_auc_score(test_rows['down_t1'], lda_probability), 'balanced_accuracy': balanced_accuracy_score(test_rows['down_t1'], lda_probability >= 0.5)}, {'model': 'QDA', 'roc_auc': roc_auc_score(test_rows['down_t1'], down_probability), 'balanced_accuracy': balanced_accuracy_score(test_rows['down_t1'], down_probability >= 0.5)}]) # 一次性保存事先确定的描述性对照
display(descriptive_comparison) # 展示共同测试检查,不用结果选模型
| 0 |
LDA |
0.539681 |
0.513192 |
| 1 |
QDA |
0.458896 |
0.495022 |
形成性检查 2:核函数是否合法?
对任意样本,候选核产生的 Gram 矩阵若有明显负特征值,它能直接用于标准核 SVM 吗?
答案
通常不能。Mercer 条件要求 Gram 矩阵半正定;明显负特征值说明它未对应有效内积,优化问题可能失去标准凸性保证。
分步练习:解读 LDA/QDA 比较结果
- 任务: 解读同一次测试期中的 LDA/QDA AUC、平衡准确率与校准;明确这些事后描述不能选胜者或据此增加模型复杂度。
Code
comparison_rows = descriptive_comparison.copy() # 复用已完成的一次测试结果,不再次使用测试集选模型
display(comparison_rows) # 展示描述性对照并保留不选模型的边界
分步提示完整解答:五组校准表
Code
calibration_parts = [] # 收集 LDA 与 QDA 的五组校准结果
for model_name, model_probability in [('LDA', lda_probability), ('QDA', down_probability)]: # 对同一测试标签评价两组概率
model_calibration = pd.DataFrame({'observed': test_rows['down_t1'].to_numpy(), 'probability': model_probability}) # 对齐预测概率与真实类别
model_calibration['probability_bin'] = pd.cut(model_calibration['probability'], bins=[0, .2, .4, .6, .8, 1], include_lowest=True) # 使用预先声明的五个概率区间
model_table = model_calibration.groupby('probability_bin', observed=False).agg(n=('observed', 'size'), mean_probability=('probability', 'mean'), observed_rate=('observed', 'mean')).reset_index() # 计算每组预测均值与真实频率
model_table.insert(0, 'model', model_name) # 标记校准表所属模型
calibration_parts.append(model_table) # 记录当前模型的五组结果
pd.concat(calibration_parts, ignore_index=True).query("model == 'LDA'") # 先输出 LDA 的五组校准证据
分步提示完整答案:QDA 校准
Code
pd.concat(calibration_parts, ignore_index=True).query("model == 'QDA'") # 再输出 QDA 的五组校准证据
空组或极小组本身就是不稳定证据;比较预测均值与真实频率时必须同时报告 n。
综合练习
换用恒瑞医药 600276.XSHG,比较 QDA 概率、RBF-SVM 决策分数和训练期先验概率基准;仅在训练期扩展窗口选择 reg_param、\(C\) 与 \(\gamma\)。
综合练习完整答案:选参与确定测试
Code
transfer_cv = TimeSeriesSplit(5, gap=1) # 一日预测 horizon 在每折训练与验证之间清除一行
assert all(transfer_train.iloc[fit_idx]['target_date_t1'].max() < transfer_train.iloc[val_idx]['date'].min() for fit_idx, val_idx in transfer_cv.split(transfer_train)) # 确认全部训练折标签先实现
qda_search = GridSearchCV(make_pipeline(StandardScaler(), QuadraticDiscriminantAnalysis()), {'quadraticdiscriminantanalysis__reg_param': [0, .1, .5]}, cv=transfer_cv, scoring='roc_auc').fit(transfer_train[transfer_features], transfer_train['down_t1']) # 仅在训练期选择 QDA 收缩
svm_search = GridSearchCV(make_pipeline(StandardScaler(), SVC(kernel='rbf')), {'svc__C': [.1, 1, 10], 'svc__gamma': ['scale', .1, 1]}, cv=transfer_cv, scoring='roc_auc').fit(transfer_train[transfer_features], transfer_train['down_t1']) # 仅在训练期选核参数且不触发随机内部概率拟合
transfer_probabilities = {'QDA': qda_search.predict_proba(transfer_test[transfer_features])[:, 1], 'prior baseline': pd.Series(transfer_train['down_t1'].mean(), index=transfer_test.index).to_numpy()} # 只保留具有明确概率语义的输出
svm_decision_score = svm_search.decision_function(transfer_test[transfer_features]) # 为 SVM 生成未校准的排序分数
probability_metrics = [{'model': model_name, 'roc_auc': roc_auc_score(transfer_test['down_t1'], probability), 'balanced_accuracy': balanced_accuracy_score(transfer_test['down_t1'], probability >= .5)} for model_name, probability in transfer_probabilities.items()] # 只对概率输出应用 0.5 阈值
transfer_metrics = pd.DataFrame(probability_metrics + [{'model': 'RBF-SVM score', 'roc_auc': roc_auc_score(transfer_test['down_t1'], svm_decision_score), 'balanced_accuracy': float('nan')}]) # SVM 只报告阈值无关排序证据
transfer_calibration = [] # 收集两个概率模型的五组校准表
for model_name, probability in transfer_probabilities.items(): # 对每组概率使用同一分箱规则
calibration_frame = pd.DataFrame({'observed': transfer_test['down_t1'].to_numpy(), 'probability': probability}) # 对齐概率与真实标签
calibration_frame['bin'] = pd.cut(calibration_frame['probability'], [0, .2, .4, .6, .8, 1], include_lowest=True) # 划分五个固定概率组
model_calibration = calibration_frame.groupby('bin', observed=False).agg(n=('observed', 'size'), mean_probability=('probability', 'mean'), observed_rate=('observed', 'mean')).reset_index() # 计算校准证据
model_calibration.insert(0, 'model', model_name) # 标明每组来源模型
transfer_calibration.append(model_calibration) # 记录当前模型结果
display(pd.Series({'QDA_best': qda_search.best_params_, 'SVM_best': svm_search.best_params_})) # 输出训练期确定的超参数
display(transfer_metrics) # 输出共同测试 AUC 与平衡准确率
真实结果:QDA 选择 reg_param=0.5,AUC/平衡准确率为 0.508321/0.503456;RBF-SVM 选择 \(C=1,\gamma=0.1\),只检查未校准决策分数的测试 AUC;先验概率基准为 0.500000/0.500000。
SVM 不使用 probability=True,因为其内部随机五折概率拟合不满足本题的时间边界;
没有单独的时间有效校准集,就不报告 SVM 的 0.5 概率阈值或校准表。
综合练习完整答案:确定测试校准
Code
pd.concat(transfer_calibration, ignore_index=True).query("model == 'QDA'") # 输出 QDA 的五组校准表
RBF-SVM:只检查未校准决策分数
Code
pd.Series({'score_min': svm_decision_score.min(), 'score_median': pd.Series(svm_decision_score).median(), 'score_max': svm_decision_score.max(), 'test_roc_auc': roc_auc_score(transfer_test['down_t1'], svm_decision_score)}) # 输出分数范围与阈值无关 AUC
确定测试校准:先验概率基准
Code
pd.concat(transfer_calibration, ignore_index=True).query("model == 'prior baseline'") # 输出先验概率基准的五组校准表
形成性检查 3:何时不用非线性?
非线性模型训练分数更高,但所有时间验证窗口均未优于线性基准。应选哪个?
答案
优先线性基准。训练拟合不是泛化证据;只有稳定、样本外且足以补偿复杂度成本的提升才支持升级模型。
来源与延伸阅读
- Hastie, Tibshirani & Friedman, The Elements of Statistical Learning, discriminant analysis and kernels。
- Schölkopf & Smola, Learning with Kernels。
- Ledoit & Wolf (2004), covariance shrinkage。
- 数据:本地 A 股前复权行情数据;文件、key、字段、时期和切分见 前面的案例。
本章总结
课后选学
完成核心内容后,可以继续学习KPCA 与 KSVM 推导,随后进入最终总结。
三种非线性建模思想对比
| 核心思想 |
用多段直线逼近 |
直接拟合二次曲线 |
升维打击,化曲为直 |
| 灵活性 |
高(依赖分段设计) |
中等(仅限二次型) |
高(取决于核、超参数与表示维数) |
| 计算成本 |
取决于分段数量 |
中等 (\(O(d^2)\)) |
高 (\(O(N^2)\) 至 \(O(N^3)\)) |
| 可解释性 |
中等(类似决策树) |
较高(二次函数) |
低(高维空间无法直观理解) |
| 主要假设 |
无特定分布假设 |
数据服从高斯分布 |
无特定分布假设 |
| 关键参数 |
子类/分段的数量 |
类别先验、协方差估计与正则化强度 |
核函数类型及其参数 (如 gamma, d) |
| 适用场景 |
直观建模,类似决策树的场景 |
类别协方差不同,特征维度不高 |
经样本外结果验证后确有增益的非线性问题 |
关键要点回顾
- 直面非线性: 现实世界是复杂的,线性模型有其局限性,学习非线性模型是必要的。
- QDA: 通过为每个类假定不同的协方差矩阵来生成二次边界,是LDA的自然推广,在LDA和更复杂模型间取得良好平衡。
- 核方法 (Kernel Trick):
- 核心思想: 升维 + 线性模型。
- 通过核函数 \(K(\mathbf{x}_i, \mathbf{x}_j) = \langle \phi(\mathbf{x}_i), \phi(\mathbf{x}_j) \rangle\) 避免显式构造高维坐标,但仍须面对样本复杂度、正则化和过拟合。
- 可“核化”任何依赖于内积的算法 (如PCA, SVM),赋予其强大的非线性能力。
- RBF 核是常用候选,但表现依赖核宽度、保留维数、下游模型与样本外评价结果;本讲的一维 KPCA 反例说明它不保证可分。