04 分类方法 (Classification)

核心问题:如何预测定性的类别标签?

在金融实务中,许多关键决策本质上是分类问题

  • 一家上市公司明年会不会被 ST 风险警示
  • 一笔贷款申请是否会 违约
  • 明天的股价会 还是

这些问题的响应变量 \(Y\)定性的(qualitative),而非连续的数值。

本章将系统介绍从逻辑回归到判别分析、朴素贝叶斯等主流分类方法。

分类问题的现实意义:从信贷审批到量化交易 {{#sec-classification-realworld}}

分类方法在金融行业中的应用远比想象中广泛:

应用场景 类别标签 商业价值
信贷审批 批准/拒绝 控制坏账率
ST 预警 ST/非ST 投资避雷
欺诈检测 正常/欺诈 风险防控
行业轮动 看好/看空 资产配置
客户分层 高/中/低价值 精准营销

核心挑战:与回归预测连续数值不同,分类的输出是离散的决策,错误分类的代价往往是不对称的(误判一笔坏账远比错失一笔好客户代价更大)。

在金融行业里,分类无处不在。银行每天要处理成千上万的贷款申请,每一笔都是一个分类决策。这些决策的错误代价是不对称的——如果把一个坏客户错判为好客户,银行可能损失整笔贷款本金;但如果把好客户错判为坏客户,只是损失了利息收入。这种不对称性是分类问题的重要特征。

分类 vs. 回归:响应变量的本质区别

特征 回归 (Regression) 分类 (Classification)
响应变量 \(Y\) 定量(连续) 定性(离散类别)
输出 预测值 \(\hat{Y}\) 类别标签 \(\hat{Y}\) 或概率 \(\hat{P}(Y=k\mid X)\)
常用方法 OLS、Ridge、Lasso 逻辑回归、LDA、QDA、朴素贝叶斯
损失函数 MSE 交叉熵、0-1 损失
金融应用 股票收益率预测 违约判别、ST 预警、涨跌方向

分类方法通常先估计条件概率 \(P(Y = k \mid X = x)\),再依据概率做出分类决策。

分类的两步策略:先估概率,再做决策 {{#sec-two-step-strategy}}

几乎所有分类方法都遵循相同的两步策略

第一步:估计条件概率

\[ \large{{ \hat{{P}}(Y = k \mid X = x) \quad \text{{for each class }} k }} \]

第二步:根据概率做出决策

\[ \large{{ \hat{{Y}} = \arg\max_k \hat{{P}}(Y = k \mid X = x) }} \]

为什么不直接输出类别标签?因为概率蕴含了更丰富的信息:

  • 概率 = 0.51概率 = 0.99 都会被分到同一类,但后者的把握大得多
  • 在金融风控中,概率本身就是风险度量——比如违约概率 PD
  • 调整分类阈值可以灵活权衡漏判率与误判率

分类的核心逻辑分为两步:先估概率、再做决策。这两步是分开的,我们可以独立调整。第一步用统计模型估计概率,第二步用阈值做决策。默认阈值是0.5,但在不平衡场景中往往需要调低。

为什么不能直接用线性回归做分类?

线性回归应用于分类会产生三个根本问题:

问题一:编码方式影响结果

若响应变量有三类(如”低风险=1、中风险=2、高风险=3”),线性回归隐含假设了”高风险与中风险的距离 = 中风险与低风险的距离”,但换成 1/2/3 → 1/3/5 编码会得到完全不同的结果。

问题二:预测值超出合理范围

线性回归的预测值 \(\hat{Y}\) 可以是任意实数,可能输出 \(-0.3\)\(1.5\)——这些值无法被解释为概率。

问题三:多分类困难

多于两类时,线性回归无法自然地建模多类之间的关系。

从线性回归到逻辑回归:关键的”跳板” {{#sec-bridge-to-logistic}}

线性回归做分类的根本问题在于:输出没有边界约束

我们需要一种函数,能将 \((-\infty, +\infty)\) 的线性预测值映射到 \((0, 1)\) 的概率空间。

解决方案:链接函数(Link Function)

方法 链接函数 输出范围 特征
线性回归 恒等函数 \(g(x) = x\) \((-\infty, +\infty)\) 无约束
逻辑回归 Logit 函数 \((0, 1)\) S 形曲线
Probit 模型 \(\Phi^{{-1}}\) \((0, 1)\) 正态CDF

逻辑回归选择了 Sigmoid 函数(即 Logistic 函数)作为链接函数的逆变换,这是最自然、最优雅的选择。

理解逻辑回归的关键在于”链接函数”的概念。线性回归用的是恒等链接——输入什么就输出什么。但分类需要概率,必须在0和1之间。逻辑回归用Sigmoid函数做了一个巧妙的变换,把整个实数轴”压缩”到了0和1之间。

逻辑回归:用 Sigmoid 函数约束概率

逻辑回归(Logistic Regression)使用 Sigmoid 函数 将线性预测值映射到 \((0,1)\)

\[ \large{ p(X) = \frac{e^{\beta_0 + \beta_1 X_1 + \cdots + \beta_p X_p}}{1 + e^{\beta_0 + \beta_1 X_1 + \cdots + \beta_p X_p}} } \]

关键性质:

  • \(\beta_0 + \beta^\top X \to +\infty\) 时,\(p(X) \to 1\)
  • \(\beta_0 + \beta^\top X \to -\infty\) 时,\(p(X) \to 0\)
  • 输出值始终在 \((0, 1)\) 范围内,天然可解释为概率

Sigmoid 函数的几何直觉 {{#sec-sigmoid-geometry}}

Sigmoid 函数 \(\sigma(z) = \frac{{1}}{{1+e^{{-z}}}}\) 具有以下关键几何性质:

形状特征:

  • \(z = 0\) 处通过 \((0, 0.5)\) —— 50/50 的不确定点
  • \(z > 0\) 时概率大于 0.5,\(z < 0\) 时概率小于 0.5
  • 曲线在 \(z = 0\) 处最陡,远离原点后逐渐平坦

导数的优美性质:

\[ \large{{ \sigma'(z) = \sigma(z) \cdot (1 - \sigma(z)) }} \]

这意味着:

  • 当预测概率接近 0.5 时,梯度最大 → 模型”最不确定”,学习最快
  • 当预测概率接近 0 或 1 时,梯度趋近于零 → 模型”很确定”,不再调整
  • 这与人类学习的直觉完全一致!

Sigmoid函数有一个非常优美的性质:它的导数等于自身乘以1减自身。这不仅简化了数学推导,还蕴含了深刻的直觉——模型在最不确定的地方学习最快,在最确定的地方保持稳定。这个性质也是深度学习中激活函数设计的灵感来源。

从概率到对数几率:Log-Odds 的线性结构

\(p(X)\) 做变换,可以得到对数几率(log-odds / logit):

\[ \large{ \log\frac{p(X)}{1-p(X)} = \beta_0 + \beta_1 X_1 + \cdots + \beta_p X_p } \]

  • 左边称为 logit 变换,将 \((0,1)\) 映射到 \((-\infty, +\infty)\)
  • 右边是标准的线性结构,系数 \(\beta_j\) 的含义:\(X_j\) 增加一单位,对数几率增加 \(\beta_j\)
  • \(\exp(\beta_j)\) 称为几率比(odds ratio),是金融风控中的核心可解释性指标

几率比的实际解读:金融风控中的核心指标 {{#sec-odds-ratio-interpretation}}

在金融风控报告中,系数的解读通常通过几率比(Odds Ratio)进行:

\[ \large{{ OR_j = e^{{\beta_j}} }} \]

解读规则:

  • \(OR_j > 1\)\(X_j\) 增加一单位,“事件发生”的几率增大
  • \(OR_j < 1\)\(X_j\) 增加一单位,“事件发生”的几率减小
  • \(OR_j = 1\)\(X_j\) 对结果无影响

风控应用示例:

特征 系数 \(\beta\) 几率比 \(OR\) 业务含义
资产负债率 +2.1 8.17 负债率每升1%,违约几率变为原来的8.17倍
ROA -3.5 0.03 ROA每升1%,违约几率降至原来的3%
对数总资产 -0.8 0.45 规模每扩大1个数量级,违约几率减半

几率比是银行风控报告中的核心指标。监管机构会要求银行说明:为什么这个客户被拒绝了?用几率比就能清楚解释——比如”该客户的资产负债率每增加1个百分点,违约的几率就变为原来的8倍多”。这种可解释性是逻辑回归在金融行业保持统治地位的根本原因。

最大似然估计:逻辑回归的参数学习

逻辑回归通过最大似然估计(MLE)求解参数,而非最小化残差平方和。

似然函数为:

\[ \large{ L(\beta) = \prod_{i=1}^{n} p(x_i)^{y_i} [1 - p(x_i)]^{1-y_i} } \]

取对数并求梯度:

\[ \large{ \nabla \ell(\beta) = \sum_{i=1}^{n} (y_i - p_i) x_i } \]

  • \(y_i = 1\)\(p_i\) 偏小时,梯度为正 → 增大 \(\beta\) 以提高 \(p_i\)
  • \(y_i = 0\)\(p_i\) 偏大时,梯度为负 → 减小 \(\beta\) 以降低 \(p_i\)
  • 这一梯度形式与 OLS 的 \((y_i - \hat{y}_i)x_i\) 在结构上完全对应

MLE vs. OLS:两种参数估计的哲学对比 {{#sec-mle-vs-ols}}

对比维度 OLS(线性回归) MLE(逻辑回归)
目标函数 最小化残差平方和 \(\sum(y_i - \hat{{y}}_i)^2\) 最大化似然函数 \(\prod p_i^{{y_i}}(1-p_i)^{{1-y_i}}\)
解析解 \(\hat{{\beta}} = (X^\top X)^{{-1}}X^\top y\) :需要迭代优化
优化算法 直接矩阵求逆 牛顿-拉夫森 / IRLS
收敛保证 一步到位 需要迭代,可能不收敛
不收敛原因 完全可分、多重共线性

补充说明:完全可分问题

如果存在一个超平面能完美分开两类数据,MLE 会将系数推向 \(\pm\infty\),无法收敛。此时需要添加正则化项(如 L1/L2 惩罚)。

这是一个重要的对比。线性回归有解析解,一步就能算出来。但逻辑回归没有解析解,必须用迭代算法。在实际使用中,你可能会遇到逻辑回归不收敛的情况——最常见的原因是”完全可分”,即数据太好分了,模型想把系数推到无穷大。解决办法是加正则化。

交叉熵损失:MLE 的另一副面孔 {{#sec-cross-entropy}}

对数似然函数取负号后,就是深度学习中著名的交叉熵损失(Cross-Entropy Loss):

\[ \large{{ \text{{CE}} = -\frac{{1}}{{n}} \sum_{{i=1}}^{{n}} \Big[ y_i \log p_i + (1-y_i) \log(1-p_i) \Big] }} \]

直觉理解:

  • \(y_i = 1\)\(p_i \to 0\) 时,\(-\log(p_i) \to +\infty\)惩罚极大
  • \(y_i = 1\)\(p_i \to 1\) 时,\(-\log(p_i) \to 0\)完美预测
  • 交叉熵衡量的是预测概率分布与真实标签分布之间的”距离”

因此,最大化对数似然 = 最小化交叉熵 = 最小化预测分布与真实分布的差异

这是统计学与深度学习的完美桥梁。

这里有一个非常重要的联系:逻辑回归中的最大似然估计和深度学习中的交叉熵损失本质上是同一件事。只不过一个是最大化、一个是最小化,差一个负号。理解了这一点,你就理解了从传统统计到深度学习的最关键桥梁。

A 股实证:逻辑回归预测股价涨跌方向

使用浦发银行(600000.XSHG)的日线行情数据,构造滞后收益率特征预测次日涨跌:

  • 特征:前三日收益率 \(\text{Lag}_1, \text{Lag}_2, \text{Lag}_3\)
  • 响应变量:次日涨跌方向(\(Y=1\) 表示上涨)
  • 模型statsmodels.Logit
Table 1: 逻辑回归系数估计(浦发银行日线涨跌预测)
import pandas as pd  # 导入pandas用于数据处理
import numpy as np  # 导入numpy用于数值计算
import os  # 导入os用于路径操作
import statsmodels.api as sm  # 导入statsmodels用于逻辑回归建模

# 根据操作系统自动选择数据路径
DATA_ROOT = 'C:/qiufei/data' if os.name == 'nt' else '/home/ubuntu/r2_data_mount/qiufei/data'  # 设置数据根目录
PRICE_PATH = os.path.join(DATA_ROOT, 'stock/stock_price_pre_adjusted.h5')  # 构建前复权股价文件路径
stock_price_df = pd.read_hdf(PRICE_PATH).reset_index()  # 读取前复权日线行情数据并将MultiIndex转为普通列

浦发银行逻辑回归实证(续)

# 筛选浦发银行的交易数据
pudong_bank_df = stock_price_df[stock_price_df['order_book_id'] == '600000.XSHG'].copy()  # 提取浦发银行数据
pudong_bank_df = pudong_bank_df.sort_values('date')  # 按日期排序确保时序正确
pudong_bank_df['daily_return'] = pudong_bank_df['close'].pct_change()  # 计算日收益率

# 构造滞后特征:前1/2/3日收益率
pudong_bank_df['lag_1'] = pudong_bank_df['daily_return'].shift(1)  # 前1日收益率
pudong_bank_df['lag_2'] = pudong_bank_df['daily_return'].shift(2)  # 前2日收益率
pudong_bank_df['lag_3'] = pudong_bank_df['daily_return'].shift(3)  # 前3日收益率

# 构造二分类响应变量:涨=1,跌=0
pudong_bank_df['is_up'] = (pudong_bank_df['daily_return'] > 0).astype(int)  # 当日收益率>0标记为上涨
pudong_bank_df = pudong_bank_df.dropna(subset=['lag_1', 'lag_2', 'lag_3', 'is_up'])  # 删除缺失值
# 构建特征矩阵并添加截距项
feature_matrix = sm.add_constant(pudong_bank_df[['lag_1', 'lag_2', 'lag_3']])  # 添加常数列作为截距
response_vector = pudong_bank_df['is_up']  # 提取响应变量

# 拟合逻辑回归模型
logit_model = sm.Logit(response_vector, feature_matrix).fit(disp=0)  # 使用MLE拟合逻辑回归
logit_model.summary2().tables[1]  # 展示系数估计表
Coef. Std.Err. z P>|z| [0.025 0.975]
const -0.091713 0.028113 -3.262326 0.001105 -0.146813 -0.036613
lag_1 -2.545384 1.345245 -1.892134 0.058473 -5.182016 0.091247
lag_2 -0.763111 1.341220 -0.568967 0.569378 -3.391854 1.865633
lag_3 0.553342 1.340937 0.412653 0.679861 -2.074847 3.181531

贝叶斯定理:分类的概率论基础

贝叶斯定理是生成式分类方法的理论基石:

\[ \large{ P(Y = k \mid X = x) = \frac{P(X = x \mid Y = k) \cdot P(Y = k)}{P(X = x)} } \]

名称 符号 含义
后验概率 \(P(Y=k\mid X)\) 观测到特征后的类别概率
似然 \(P(X\mid Y=k)\) 类别 \(k\) 下特征的分布
先验概率 \(P(Y=k)\) 各类别出现的频率
边际概率 \(P(X)\) 特征的整体分布(归一化常数)

判别式 vs. 生成式:逻辑回归直接建模后验 \(P(Y\mid X)\);而 LDA/QDA 通过建模似然 \(P(X\mid Y)\) 与先验 \(P(Y)\),再用贝叶斯定理间接得到后验。

判别式 vs. 生成式:两种建模哲学 {{#sec-discriminative-vs-generative}}

理解这两种范式是掌握分类方法的关键:

判别式方法(Discriminative)—— “直接画边界”

  • 直接学习决策边界 \(P(Y \mid X)\)
  • 不关心数据是怎么生成的
  • 代表:逻辑回归、SVM、神经网络

生成式方法(Generative)—— “理解数据的来源”

  • 先学习每个类别的数据生成过程 \(P(X \mid Y)\)
  • 再通过贝叶斯定理反推 \(P(Y \mid X)\)
  • 代表:LDA、QDA、朴素贝叶斯

类比理解:

  • 判别式像海关检查员:只关心护照和人是否匹配(边界判定)
  • 生成式像人口学家:先研究不同国家人群的特征分布,再判断这个人最可能来自哪国

判别式和生成式是机器学习中两种根本不同的世界观。判别式方法务实高效,直接画决策边界。生成式方法更有”野心”,它试图理解每个类别的数据是怎么产生的。这两种思路各有千秋——判别式通常分类精度更高,但生成式可以处理缺失数据、可以生成新样本、而且在小样本下更稳定。

LDA:线性判别分析

线性判别分析(LDA)假设每个类别的特征服从多元正态分布协方差矩阵相同

\[ \large{ X \mid Y = k \sim N(\mu_k, \Sigma) } \]

在此假设下,判别函数为线性形式:

\[ \large{ \delta_k(x) = x^\top \Sigma^{-1} \mu_k - \frac{1}{2} \mu_k^\top \Sigma^{-1} \mu_k + \log \pi_k } \]

  • \(\mu_k\):类别 \(k\) 的均值向量
  • \(\Sigma\):共享的协方差矩阵
  • \(\pi_k = P(Y=k)\):先验概率
  • 决策规则:将 \(x\) 分配到使 \(\delta_k(x)\) 最大的类别 \(k\)

LDA 的几何直觉:寻找最佳投影方向 {{#sec-lda-geometry}}

LDA 的核心思想可以用一句话概括:寻找一个方向,使得投影后两类数据分得最开

Fisher 准则:

\[ \large{{ J(w) = \frac{{|w^\top \mu_1 - w^\top \mu_2|^2}}{{w^\top \Sigma_w w}} = \frac{{\text{{类间散度}}}}{{\text{{类内散度}}}} }} \]

  • 分子(类间散度):两类均值的投影距离 → 越大越好
  • 分母(类内散度):每类内部的投影方差 → 越小越好
  • 最优投影方向:\(w^* = \Sigma_w^{{-1}}(\mu_1 - \mu_2)\)

直觉:好的投影就像好的”观察角度”——让不同群体的特征差异最大化,同时让同一群体内部的差异最小化。

LDA的几何含义非常直观。想象你在一个房间里看两群人——高个子一群、矮个子一群。你从不同角度看他们,有些角度两群人完全混在一起,有些角度则分得很清楚。LDA就是在寻找那个”最佳观察角度”,数学上就是使Fisher准则最大化的投影方向。

QDA:允许不同协方差的二次判别

二次判别分析(QDA)放松了 LDA 的等协方差假设,允许每个类别有自己的协方差矩阵:

\[ \large{ X \mid Y = k \sim N(\mu_k, \Sigma_k) } \]

判别函数变为二次形式

\[ \large{ \delta_k(x) = -\frac{1}{2} x^\top \Sigma_k^{-1} x + x^\top \Sigma_k^{-1} \mu_k + C_k } \]

对比 LDA QDA
协方差假设 \(\Sigma_1 = \Sigma_2 = \Sigma\) \(\Sigma_1 \neq \Sigma_2\)
决策边界 线性 二次曲线
参数数量 较少 较多
适用场景 小样本、类分布相似 大样本、类分布差异大

LDA vs. QDA:偏差-方差权衡的经典案例 {{#sec-lda-qda-tradeoff}}

LDA 和 QDA 的选择是偏差-方差权衡的教科书级案例:

LDA(高偏差,低方差)

  • 假设所有类共享同一协方差矩阵
  • 需要估计的参数:\(K\) 个均值 + \(1\) 个协方差 → \(O(Kp + p^2)\)
  • 当真实边界确实是线性时,LDA 最优
  • 小样本友好

QDA(低偏差,高方差)

  • 允许每个类有自己的协方差矩阵
  • 需要估计的参数:\(K\) 个均值 + \(K\) 个协方差 → \(O(Kp + Kp^2)\)
  • 当类别分布差异大时,QDA 更准确
  • 需要足够的样本量

经验法则:当每个类的样本量 \(n_k\) 远大于特征维度 \(p\) 时,选 QDA;否则选 LDA。

LDA和QDA的关系就是偏差-方差权衡的缩影。LDA限制更强,可能有偏,但估计更稳定。QDA更灵活,偏差更小,但参数多了K倍,方差也大了。在金融数据中,我们通常样本量有限而特征较多,所以LDA往往是更安全的选择。

朴素贝叶斯:高维分类的实用利器

朴素贝叶斯(Naive Bayes)做了一个大胆假设:给定类别后,特征之间相互独立

\[ \large{ P(X_1, \ldots, X_p \mid Y = k) = \prod_{j=1}^{p} P(X_j \mid Y = k) } \]

优势:

  • 参数量从 \(O(p^2)\) 降到 \(O(p)\),大幅降低过拟合风险
  • 计算速度极快,适合高维数据(如文本分类)
  • 在许多实际场景中,分类效果出奇地好

局限:

  • 独立性假设通常不严格成立
  • 概率估计的准确性较差(但排序性能好,AUC 可能不受影响)

朴素贝叶斯为何”朴素但有效”? {{#sec-nb-paradox}}

朴素贝叶斯的独立性假设几乎不成立,但它仍然有效。这个悖论的解释:

分类 ≠ 概率估计

  • 分类只需要 \(P(Y=1\mid X)\)\(P(Y=0\mid X)\)相对大小正确
  • 即使绝对概率值不准确,只要排序(ranking)不变,分类结果就是对的

维度灾难的天敌

  • \(p\) 维空间中,朴素贝叶斯只需估计 \(p\) 个一维分布
  • 而完整模型需要估计 \(p\) 维联合分布 → 参数量呈指数增长
  • \(p\) 很大时(如文本分类中成千上万的词),朴素贝叶斯的”错误假设”反而比”正确模型但估不准”要好得多

金融应用:上市公司研报情感分析(数千个词汇特征)、异常交易行为检测

朴素贝叶斯被称为”the most successful failure in machine learning”——最成功的失败模型。它的假设明显错误,但分类效果却出奇地好。关键在于:分类只需要概率的排序正确,而不需要概率的绝对值准确。在高维场景下,正确但复杂的模型反而因为参数太多而估不准,朴素贝叶斯的简单假设反而成了优势。

六种分类方法全景对比

方法 决策边界 概率校准 可解释性 小样本 高维
逻辑回归 线性 需正则化
LDA 线性
QDA 二次
朴素贝叶斯 非线性
KNN 非线性
SVM 非线性

金融实务指导

  • 风控报审 → 逻辑回归(监管要求可解释)
  • 小样本早期预警 → LDA
  • 高维文本情感 → 朴素贝叶斯

如何选择分类方法?一张决策流程图 {{#sec-method-selection}}

在实际项目中,选择分类方法的决策逻辑:

第一步:数据特征评估

  • 样本量 \(n\) vs. 特征维度 \(p\) 的比值
  • 类别是否平衡?
  • 是否需要概率输出?

第二步:根据场景选择

场景 首选方法 原因
监管要求可解释 逻辑回归 系数/几率比直接可解释
小样本 + 低维 LDA 参数少,稳定
大样本 + 非线性 QDA / SVM-RBF 灵活捕捉非线性
高维文本/稀疏数据 朴素贝叶斯 \(O(p)\) 参数量
探索性分析 KNN 无模型假设

第三步:交叉验证确认

不要仅凭理论选择——必须用交叉验证在你的数据上验证各方法的表现。

方法选择不是一成不变的。在中国的金融实务中,逻辑回归是绝对的主流,因为银保监会要求风控模型必须可解释。但在量化投资领域,SVM和集成方法更受欢迎,因为那里更看重预测精度。

A 股 ST 预警实证:八种模型性能比较

基于本地 A 股财务数据构建 ST(特别处理)预测模型:

  • 特征:ROA(资产收益率)、资产负债率、对数总资产
  • 标签:公司是否被 ST(0=正常,1=ST)
  • 方法:逻辑回归、LDA、QDA、朴素贝叶斯、KNN(K=5)、KNN(K=20)、SVM线性核、SVM-RBF核
import pandas as pd  # 导入pandas用于数据处理
import numpy as np  # 导入numpy用于数值计算
import os  # 导入os用于路径操作
import matplotlib.pyplot as plt  # 导入matplotlib用于绑图

# 根据操作系统自动选择数据路径
DATA_ROOT = 'C:/qiufei/data' if os.name == 'nt' else '/home/ubuntu/r2_data_mount/qiufei/data'  # 数据根目录
FINANCE_PATH = os.path.join(DATA_ROOT, 'stock/financial_statement.h5')  # 财务报表数据路径
BASIC_PATH = os.path.join(DATA_ROOT, 'stock/stock_basic_data.h5')  # 股票基本信息路径

financial_data_df = pd.read_hdf(FINANCE_PATH)  # 读取财务报表数据
basic_info_df = pd.read_hdf(BASIC_PATH)  # 读取股票基本信息

不平衡数据的挑战:准确率的”陷阱” {{#sec-imbalanced-data}}

在 ST 预警任务中,正常公司占比约 95%,ST 公司仅占 5%。

准确率悖论

一个什么都不做的模型——永远预测”非ST”——准确率就能达到 95%!

指标 “全预测非ST”模型 理想模型
准确率 95% 98%
召回率(ST) 0% 85%
商业价值 极高

因此,在不平衡场景下必须关注:

  • 混淆矩阵(Confusion Matrix):详细展示各类的分类情况
  • 召回率(Recall):真正的 ST 被识别出了多少?
  • 精确率(Precision):预测为 ST 的公司中有多少是真的?
  • AUC:不受阈值选择影响的综合指标

不平衡数据是金融分类中最常遇到的问题。在信贷违约预测中,坏账率通常不到5%。在ST预警中也是一样。如果你只看准确率,会被严重误导——一个永远预测”不违约”的模型准确率可能高达95%以上,但它毫无价值。必须深入看混淆矩阵和各类指标。

ST 预警实证:数据准备与特征工程

# 筛选2023年年报数据用于分析
financial_data_df = financial_data_df[financial_data_df['quarter'] == '2023q4'].copy()  # 选取2023年年报

# 合并财务数据与ST标记
join_key = 'order_book_id'  # 以股票代码作为合并键
merged_financial_data = pd.merge(  # 内连接合并两张表
    financial_data_df,
    basic_info_df[[join_key, 'special_type']],
    on=join_key, how='inner'
)
# 构造三个核心财务特征
merged_financial_data['roa'] = merged_financial_data['net_profit'] / merged_financial_data['total_assets']  # ROA:衡量资产创利能力
merged_financial_data['debt_to_assets'] = merged_financial_data['total_liabilities'] / merged_financial_data['total_assets']  # 资产负债率:衡量杠杆水平
merged_financial_data['log_size'] = np.log10(merged_financial_data['total_assets'] + 1)  # 对数总资产:控制规模效应

# 数据清洗:去除异常值和缺失值
merged_financial_data = merged_financial_data.replace([np.inf, -np.inf], np.nan).dropna(subset=['roa', 'debt_to_assets', 'log_size'])  # 替换无穷值并删除缺失
merged_financial_data = merged_financial_data[(merged_financial_data['debt_to_assets'] >= 0) & (merged_financial_data['debt_to_assets'] <= 2)]  # 资产负债率限制在合理范围
merged_financial_data = merged_financial_data[merged_financial_data['total_assets'] > 1e6]  # 排除总资产过小的异常记录

ST 预警实证:模型训练与交叉验证

from sklearn.model_selection import train_test_split, cross_val_score  # 导入数据切分与交叉验证工具
from sklearn.linear_model import LogisticRegression  # 导入逻辑回归
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis  # 导入LDA和QDA
from sklearn.naive_bayes import GaussianNB  # 导入朴素贝叶斯
from sklearn.neighbors import KNeighborsClassifier  # 导入K近邻
from sklearn.svm import SVC  # 导入支持向量机
from sklearn.preprocessing import StandardScaler  # 导入标准化工具

# 构造ST标签:special_type含'ST'标记为1,否则为0
merged_financial_data['is_st'] = merged_financial_data['special_type'].fillna('Normal').str.contains('ST').astype(int)  # 根据special_type列判断是否为ST
features_matrix = merged_financial_data[['roa', 'debt_to_assets', 'log_size']].values  # 提取特征矩阵
st_target_labels = merged_financial_data['is_st'].values  # 提取目标标签
# 分层抽样切分训练/测试集(70%/30%)
features_train, features_test, target_train, target_test = train_test_split(  # 按ST比例分层抽样
    features_matrix, st_target_labels, test_size=0.3, random_state=42, stratify=st_target_labels
)

# 特征标准化(对KNN和SVM至关重要)
scaler = StandardScaler()  # 初始化标准化器
features_train_scaled = scaler.fit_transform(features_train)  # 在训练集上拟合并转换
features_test_scaled = scaler.transform(features_test)  # 用训练集参数转换测试集(防止数据泄露)

ST 预警实证:八种模型评估结果

# 定义8种分类模型
models = {  # 构建模型字典
    '逻辑回归': LogisticRegression(max_iter=1000),  # 逻辑回归,增大迭代次数防止不收敛
    'LDA': LinearDiscriminantAnalysis(),  # 线性判别分析
    'QDA': QuadraticDiscriminantAnalysis(),  # 二次判别分析
    '朴素贝叶斯': GaussianNB(),  # 高斯朴素贝叶斯
    'KNN(K=5)': KNeighborsClassifier(n_neighbors=5),  # 5近邻
    'KNN(K=20)': KNeighborsClassifier(n_neighbors=20),  # 20近邻
    'SVM线性': SVC(kernel='linear', probability=True),  # 线性核SVM
    'SVM-RBF': SVC(kernel='rbf', probability=True),  # RBF核SVM
}

# 训练每个模型并记录结果
results = {}  # 初始化结果字典
for name, model in models.items():  # 遍历所有模型
    model.fit(features_train_scaled, target_train)  # 拟合模型
    test_score = model.score(features_test_scaled, target_test)  # 测试集准确率
    cv_scores = cross_val_score(model, features_train_scaled, target_train, cv=5)  # 5折交叉验证
    results[name] = {'测试准确率': test_score, 'CV均值': cv_scores.mean(), 'CV标准差': cv_scores.std()}  # 存储结果
pd.DataFrame(results).T.round(4)  # 转置并保留4位小数展示结果表
Table 2: 八种分类方法在 ST 预警任务上的性能对比
测试准确率 CV均值 CV标准差
逻辑回归 0.9654 0.9615 0.0026
LDA 0.9642 0.9636 0.0023
QDA 0.9623 0.9604 0.0038
朴素贝叶斯 0.9642 0.9606 0.0022
KNN(K=5) 0.9604 0.9628 0.0020
KNN(K=20) 0.9629 0.9642 0.0007
SVM线性 0.9642 0.9639 0.0005
SVM-RBF 0.9642 0.9636 0.0009

超越准确率:ROC 曲线与 AUC 的直觉 {{#sec-roc-auc}}

ROC 曲线(Receiver Operating Characteristic)展示了分类器在不同阈值下的表现:

  • 横轴:假阳性率(FPR)= 被错误标为 ST 的正常公司比例
  • 纵轴:真阳性率(TPR)= 被正确识别的 ST 公司比例
  • 对角线:随机猜测的基准线
  • 越靠左上角:模型越好

AUC 的直觉解读

\[ \large{{ \text{{AUC}} = P(\hat{{p}}_{{\text{{ST}}}} > \hat{{p}}_{{\text{{非ST}}}}) }} \]

即:随机抽取一个 ST 公司和一个非 ST 公司,模型给 ST 公司更高风险评分的概率。

  • AUC = 0.5:和抛硬币一样
  • AUC = 0.7-0.8:可接受
  • AUC = 0.8-0.9:良好
  • AUC > 0.9:优秀

ROC曲线和AUC是评估分类器最重要的工具,特别是在不平衡数据场景中。AUC有一个非常直观的概率解释:如果你随机从正类和负类各抽一个样本,AUC就是模型能正确区分它们的概率。在金融风控中,AUC通常是模型选择的首要指标。

ST 预警可视化:测试集 vs. 交叉验证准确率

Code
# 配置中文字体
plt.rcParams['font.sans-serif'] = ['Source Han Serif SC', 'SimHei', 'Arial Unicode MS']  # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

fig, ax = plt.subplots(figsize=(14, 7))  # 创建画布

methods = list(results.keys())  # 提取方法名列表
test_scores = [results[m]['测试准确率'] for m in methods]  # 各方法测试集准确率
cv_means = [results[m]['CV均值'] for m in methods]  # 各方法CV均值
cv_stds = [results[m]['CV标准差'] for m in methods]  # 各方法CV标准差

x = np.arange(len(methods))  # 分组柱状图的x轴位置
width = 0.35  # 柱子宽度
Figure 1: 不同分类方法的性能比较(ST 财务困境预测)
# 绘制分组柱状图
ax.bar(x - width/2, test_scores, width, label='测试集准确率', color='steelblue', alpha=0.8, edgecolor='black')  # 测试集准确率柱
ax.bar(x + width/2, cv_means, width, yerr=cv_stds, label='CV准确率(±1SD)', color='coral', alpha=0.8, edgecolor='black', capsize=5)  # CV准确率柱(含误差线)

ax.set_xlabel('分类方法', fontsize=13, fontweight='bold')  # x轴标签
ax.set_ylabel('准确率', fontsize=13, fontweight='bold')  # y轴标签
ax.set_title('分类方法性能比较(ST 财务困境预测)', fontsize=14, fontweight='bold')  # 图标题
ax.set_xticks(x)  # 设置x轴刻度位置
ax.set_xticklabels(methods, rotation=45, ha='right')  # 设置刻度标签并旋转
ax.legend(fontsize=11, loc='lower right')  # 添加图例
ax.set_ylim([0.7, 1.0])  # 聚焦有效区间
ax.grid(True, alpha=0.3, axis='y')  # 添加水平网格线
plt.tight_layout()  # 自动调整布局
plt.show()  # 显示图表
<Figure size 960x480 with 0 Axes>

分类方法的实践检查清单 {{#sec-classification-checklist}}

在实际项目中应用分类方法时,请逐项检查:

数据层面

  • 类别是否平衡?如果不平衡,是否使用了适当的评估指标(AUC/F1)?
  • 特征是否需要标准化?(KNN、SVM 必须,逻辑回归/LDA 推荐)
  • 是否有严重的多重共线性?(影响逻辑回归系数解释)

模型层面

  • 是否需要概率校准?(朴素贝叶斯的概率通常偏极端)
  • 阈值是否需要调整?(不平衡场景下 0.5 不一定最优)
  • 是否做了交叉验证?(避免选择偏差)

业务层面

  • 模型是否满足可解释性要求?(金融监管场景)
  • 误分类的代价是否对称?(通常不是 → 考虑成本敏感学习)
  • 模型是否在时间维度上稳定?(金融数据的分布会随时间漂移)

这张检查清单是你在实际项目中的”安全网”。很多学生在做课题时只关注模型的准确率,但在真实的金融场景中,数据质量、类别不平衡、可解释性、时间稳定性这些问题远比选用哪个算法更重要。

思考练习 {{#sec-exercises}}

练习一:逻辑回归的系数解读

某银行的违约预测模型中,“资产负债率”的系数 \(\beta = 1.5\)

  1. 计算资产负债率的几率比 \(OR\)
  2. 用一句话向非技术人员解释这个系数的含义
  3. 如果将资产负债率标准化后重新建模,系数会如何变化?

练习二:方法选择

你是一家券商量化部门的分析师,需要预测沪深 300 成分股中哪些在下一季度将被调出。

  1. 这是一个平衡还是不平衡的分类问题?为什么?
  2. 你会选择逻辑回归还是朴素贝叶斯?说明理由
  3. 你会用什么指标评估模型?为什么不能只看准确率?

这两道练习题考查了本章最核心的两个知识点:系数解读和方法选择。建议同学们先独立思考,再和同桌讨论。

本章知识体系总结

分类方法知识体系 展示判别式与生成式两大建模范式及其具体方法的层次关系图 分类方法知识体系 (Classification Methods) 分类问题 P(Y|X) 判别式 (Discriminative) 直接建模 P(Y|X) 逻辑回归 (Logistic) SVM KNN 生成式 (Generative) 建模 P(X|Y)·P(Y) LDA (线性) QDA (二次) 朴素贝叶斯 (NB) 核心要点 • 逻辑回归:可解释性强,风控首选 • LDA/QDA:基于正态假设,小样本稳定 • 朴素贝叶斯:高维高效,AUC 排序性能好 • 方法选择取决于:样本量、维度、可解释性 • 不平衡数据下准确率可能误导 • 实际落地需关注概率校准与阈值选择
Figure 2: 分类方法知识体系:判别式与生成式两大范式及其代表方法