《机器学习:公式推导与代码实践》鲁伟著读书笔记。
线性判别分析(Linear Discriminant Analysis,LDA)是一种经典的线性分类方法,其基本思想是将数据投影到低维空间,使得同类数据尽可能接近,异类数据尽可能疏远,所以线性判别分析也是一种监督降维算法。
线性判别分析的基本思想是将数据集投影到一条直线上,使得同类样本的投影点尽可能接近,不同类样本的投影点尽可能疏远。按此原理训练完成之后,将新样本投影到该直线上,根据投影点的位置来确定新样本点的类别。以二维变量为例,“+”表示正例,“-”表示反例。LDA的优化目标就是使投影后的类内距离小,类间距离大。
LDA二维图片来源
下面对二分类LDA的基本原理和数学推导,给定数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x m , y m ) } D={\left \{(x_{1},y_{1}),(x_{2},y_{2}),...,(x_{m},y_{m})\right \}} D={(x1,y1),(x2,y2),...,(xm,ym)}其中, x i x_{i} xi为样本的n维特征向量, y ∈ ( 0 , 1 ) y\in (0,1) y∈(0,1)为样本的类别。令 N j N_{j} Nj(j为类别,0或1)为第j类样本的数量, X j X_{j} Xj为第j类样本的集合, μ j \mu_{j} μj为第j类样本的均值向量, Σ j \Sigma_{j} Σj为第j类样本的协方差矩阵。其计算公式为:
μ j \mu_{j} μj的表达式为: μ j = 1 N j Σ x ∈ X j x \mu_{j}=\frac{1}{N_{j}}\Sigma_{x\in X_{j}}x μj=Nj1Σx∈Xjx Σ j \Sigma_{j} Σj的表达式为: Σ j = Σ x ∈ X j ( x − μ j ) ( x − μ j ) T \Sigma_{j}=\Sigma_{x\in X_{j}}(x-\mu_{j})(x-\mu_{j})^{T} Σj=Σx∈Xj(x−μj)(x−μj)T由于是二分类模型,因此我们只需要将数据投影到一条直线上,假设投影直线为向量 ω \omega ω,对于任意一个样本 x x x,他在直线上的投影为 ω T x \omega^{T}x ωTx。则投影之后,每类样本的均值向量和协方差的计算如下:
投影之后每类样本的均值向量: 1 N j Σ x ∈ X j ω T x = ω T 1 N j Σ x ∈ X j x = ω T μ j \frac{1}{N_{j}}\Sigma_{x\in X_{j}}\omega^{T}x=\omega^{T}\frac{1}{N_{j}}\Sigma_{x\in X_{j}}x=\omega^{T}\mu_{j} Nj1Σx∈XjωTx=ωTNj1Σx∈Xjx=ωTμj投影之后每类样本的协方差矩阵: Σ x ∈ X j ( ω T x − ω T μ j ) ( ω T x − ω T μ j ) T = ω T Σ x ∈ X j ( x − μ j ) ( x − μ j ) T ω = ω T Σ j ω \Sigma_{x\in X_{j}}(\omega^{T}x-\omega^{T}\mu_{j})(\omega^{T}x-\omega^{T}\mu_{j})^{T}=\omega^{T}\Sigma_{x\in X_{j}}(x-\mu_{j})(x-\mu_{j})^{T}\omega=\omega^{T}\Sigma_{j} \omega Σx∈Xj(ωTx−ωTμj)(ωTx−ωTμj)T=ωTΣx∈Xj(x−μj)(x−μj)Tω=ωTΣjωLDA模型的优化目标是使同类样本的投影点尽可能接近,我们可以使同类样本的投影点的协方差尽可能小,即 ω T Σ 0 ω + ω T Σ 1 ω \omega^{T}\Sigma_{0} \omega+\omega^{T}\Sigma_{1} \omega ωTΣ0ω+ωTΣ1ω;异类样本的投影点尽可能疏远,可以使类中心点之间的距离尽可能远,即 ∣ ∣ ω T μ 0 − ω T μ 1 ∣ ∣ 2 2 ||\omega^{T}\mu_{0}-\omega^{T}\mu_{1}||^{2}_{2} ∣∣ωTμ0−ωTμ1∣∣22尽可能大。综合考虑两个优化目标的情况下,目标函数可以定义为: arg max J ( ω ) = ∣ ∣ ω T μ 0 − ω T μ 1 ∣ ∣ 2 2 ω T Σ 0 ω + ω T Σ 1 ω = ω T ( μ 0 − μ 1 ) ( μ 0 − μ 1 ) T ω ω T ( Σ 0 + Σ 1 ) ω \text {arg max }J(\omega)=\frac{||\omega^{T}\mu_{0}-\omega^{T}\mu_{1}||^{2}_{2}}{\omega^{T}\Sigma_{0} \omega+\omega^{T}\Sigma_{1} \omega}=\frac{\omega^{T}(\mu_{0}-\mu_{1})(\mu_{0}-\mu_{1})^{T}\omega}{\omega^{T}(\Sigma_{0}+\Sigma_{1} )\omega} arg max J(ω)=ωTΣ0ω+ωTΣ1ω∣∣ωTμ0−ωTμ1∣∣22=ωT(Σ0+Σ1)ωωT(μ0−μ1)(μ0−μ1)Tω定义类内散度矩阵为 S ω = Σ 0 + Σ 1 S_{\omega}=\Sigma_{0}+\Sigma_{1} Sω=Σ0+Σ1,类间散度矩阵为 S b = ( μ 0 − μ 1 ) ( μ 0 − μ 1 ) T S_{b}=(\mu_{0}-\mu_{1})(\mu_{0}-\mu_{1})^{T} Sb=(μ0−μ1)(μ0−μ1)T。则目标函数可以改写为: arg max J ( ω ) = ω T S b ω ω T S ω ω \text {arg max }J(\omega)=\frac{\omega^{T}S_{b}\omega}{\omega^{T}S_{\omega}\omega} arg max J(ω)=ωTSωωωTSbω为了对目标函数进行简化,我们令 ω T S ω ω = 1 \omega^{T}S_{\omega}\omega=1 ωTSωω=1,则可以将其视作目标函数的约束条件。具体如下: arg max F ( ω ) = ω T S b ω \text {arg max }F(\omega)=\omega^{T}S_{b}\omega arg max F(ω)=ωTSbω s.t. ω T S ω ω = 1 \text {s.t. }\omega^{T}S_{\omega}\omega=1 s.t. ωTSωω=1
定义(拉格朗日函数):对于优化问题:
min f ( u ) \text{min }f(u) min f(u) s.t. g i ( u ) ≤ 0 , i = 1 , 2 , 3 , . . . , m h i ( u ) = 0 , j = 1 , 2 , 3 , . . . , n \text{s.t. }g_{i}(u) \leq 0,i=1,2,3,...,m \\ h_{i}(u)=0,j=1,2,3,...,n s.t. gi(u)≤0,i=1,2,3,...,mhi(u)=0,j=1,2,3,...,n定义其拉格朗日函数便为: L ( u , α , β ) : = f ( u ) + ∑ i = 1 m α i g i ( u ) + ∑ j = 1 n β j h j ( u ) \mathcal{L}(\boldsymbol{u}, \boldsymbol{\alpha}, \boldsymbol{\beta}):=f(\boldsymbol{u})+\sum_{i=1}^{m} \alpha_{i} g_{i}(\boldsymbol{u})+\sum_{j=1}^{n} \beta_{j} h_{j}(\boldsymbol{u}) L(u,α,β):=f(u)+i=1∑mαigi(u)+j=1∑nβjhj(u)其中, α i > 0 \alpha_{i}>0 αi>0。
利用拉格朗日函数可得: L ( ω ) = ω T S b ω − λ ( ω T S ω ω − 1 ) \mathcal{L}(\omega)=\omega^{T}S_{b}\omega-\lambda(\omega^{T}S_{\omega}\omega-1) L(ω)=ωTSbω−λ(ωTSωω−1)取上式对 ω \omega ω求导可得: d L ( ω ) d ω = 2 S b ω − 2 λ S ω ω = 0 \frac{d\mathcal{L}(\omega)}{d\omega}=2S_{b}\omega-2\lambda S_{\omega}\omega=0 dωdL(ω)=2Sbω−2λSωω=0即: 2 S b ω = 2 λ S ω ω 2S_{b}\omega=2\lambda S_{\omega}\omega 2Sbω=2λSωω S b ω = λ S ω ω S_{b}\omega=\lambda S_{\omega}\omega Sbω=λSωω如果 S ω S_{\omega} Sω可逆,则: λ ω = S ω − 1 S b ω \lambda \omega=S_{\omega}^{-1}S_{b}\omega λω=Sω−1Sbω λ \lambda λ仅仅是一个参数,所以上式等于: ω = λ S ω − 1 S b ω \omega=\lambda S_{\omega}^{-1}S_{b}\omega ω=λSω−1Sbω考虑到 S ω S_{\omega} Sω矩阵数值解的稳定性,如果矩阵不可逆,则我们可以对矩阵 S ω S_{\omega} Sω进行奇异值分解,然后再对分解后的矩阵进行求逆操作,即可得到 S ω − 1 S_{\omega}^{-1} Sω−1。 S ω = U Σ V − 1 S_{\omega}=U\Sigma V^{-1} Sω=UΣV−1由于对于二分类模型, S b ω = ( μ 0 − μ 1 ) ( μ 0 − μ 1 ) T ω S_{b}\omega=(\mu_{0}-\mu_{1})(\mu_{0}-\mu_{1})^{T}\omega Sbω=(μ0−μ1)(μ0−μ1)Tω,可以看出 S b ω S_{b}\omega Sbω和 ( μ 0 − μ 1 ) (\mu_{0}-\mu_{1}) (μ0−μ1)是平行的,所以: S b ω = k ( μ 0 − μ 1 ) S_{b}\omega=k(\mu_{0}-\mu_{1}) Sbω=k(μ0−μ1)所以, ω \omega ω可以表示为: ω = λ S ω − 1 k ( μ 0 − μ 1 ) \omega=\lambda S_{\omega}^{-1}k(\mu_{0}-\mu_{1}) ω=λSω−1k(μ0−μ1)去除参数可得: ω = S ω − 1 ( μ 0 − μ 1 ) \omega=S_{\omega}^{-1}(\mu_{0}-\mu_{1}) ω=Sω−1(μ0−μ1)我们只需要求出原始数据集二分类样本的均值和方差就可以确定最佳的投影方向。
Numpy LDA实现
import numpy as np
class LDA():
def __init__(self):
# 初始化权重矩阵
self.w = None
# 计算协方差矩阵
def calc_cov(self, X, Y=None):
m = X.shape[0]
# 数据标准化
X = (X - np.mean(X, axis=0))/np.std(X, axis=0)
Y = X if Y == None else (Y - np.mean(Y, axis=0))/np.std(Y, axis=0)
return 1 / m * np.matmul(X.T, Y)
# 对数据进行投影
def project(self, X, y):
self.fit(X, y)
X_projection = X.dot(self.w)
return X_projection
# LDA拟合过程
def fit(self, X, y):
# 按类分组
X0 = X[y == 0]
X1 = X[y == 1]
# 分别计算两类数据自变量的协方差矩阵
sigma0 = self.calc_cov(X0)
sigma1 = self.calc_cov(X1)
# 计算类内散度矩阵
Sw = sigma0 + sigma1
# 分别计算两类数据自变量的均值和差
u0, u1 = np.mean(X0, axis=0), np.mean(X1, axis=0)
mean_diff = np.atleast_1d(u0 - u1)
# 对类内散度矩阵进行奇异值分解
U, S, V = np.linalg.svd(Sw)
# 计算类内散度矩阵的逆
Sw_ = np.dot(np.dot(V.T, np.linalg.pinv(np.diag(S))), U.T)
# 计算w
self.w = Sw_.dot(mean_diff)
# LDA分类预测
def predict(self, X):
y_pred = []
for sample in X:
h = sample.dot(self.w)
y = 1 * (h < 0)
y_pred.append(y)
return y_pred
LDA算法的数据测试:
from sklearn import datasets
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
#导入数据集
data = datasets.load_iris()
#数据与标签
X = data.data
y = data.target
#仅取标签为0,1的数据
X = X[y != 2]
y = y[y != 2]
#划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=41)
#创建模型
lda = LDA()
#LDA模型拟合
lda.fit(X_train, y_train)
#预测
y_pred = lda.predict(X_test)
#计算准确度
accuracy = accuracy_score(y_test, y_pred)
print(accuracy)
accuracy:0.85