机器学习基础:线性判别分析
引言:
线性判别分析(Linear Discriminant Analysis)是一种非常经典和实用的监督学习算法,主要应用于分类和降维,核心思想是将高维数据投影到低维空间,相同种类的样本点靠的尽可能近,不同种类的样本点靠的尽可能远。本篇文章我将采用循序渐进的思想,构建一个二维模式的样本特征点进行研究,并简单介绍一下数学原理在线性判别分析中的运用和如何构建一个线性判别分析。
首先引入一个案列进行分析:
在上图中,数据样本分为class 0和class 1两类,分布在二维平面内。采用线性判别分析(LDA)方法可实现有效分类。LDA通过寻找最优投影方向,把所有点投影在一条直线上,在降维的同时最大化类间差异并最小化类内差异,从而实现最佳分类效果。
数学原理:
均值向量(μ\muμ):因为我们研究的尺度是二维尺度,所以a类样本均值向量的形式就是:
μˉa=[1m∑i=1mxi(0),1m∑i=1mxi(1)] \bar{\mu}_a = [\frac{1}{m}\sum_{i=1}^{m}x_i^{(0)},\frac{1}{m}\sum_{i =1}^{m}x_i^{(1)}]μˉa=[m1i=1∑mxi(0),m1i=1∑mxi(1)]
投影到直线后的均值:
μ0=wTμˉa,μ1=wTμˉb\mu_0 = w^T \bar{\mu}_a,\mu_1 =w^T \bar{\mu}_bμ0=wTμˉa,μ1=wTμˉb
其中wTw^TwT表示投影的方向,μˉa\bar{\mu}_aμˉa和μˉb\bar{\mu}_bμˉb分别表示a类样本和b类样本的均值向量。
类内散度矩阵:用于描述一个类别各点的分散程度,计算方法为(把所有类的散度矩阵加起来):
Sw=S0+S1S_w = S_0+S_1Sw=S0+S1
a类样本和b类样本的散度矩阵分别为:
S0=∑i=1m(xi−μˉa)(xi−μˉa)T,S1=∑i=1m(xi−μˉb)(xi−μˉb)T S_0 = \sum_{i = 1}^{m}(x_i-\bar{\mu}_a)(x_i-\bar{\mu}_a)^T,S_1 = \sum_{i = 1}^{m}(x_i-\bar{\mu}_b)(x_i-\bar{\mu}_b)^TS0=i=1∑m(xi−μˉa)(xi−μˉa)T,S1=i=1∑m(xi−μˉb)(xi−μˉb)T
类间散度矩阵:用于描述不同类别之间的分散程度:
Sb=(μˉa−μˉb)(μˉa−μˉb)TS_b = (\bar{\mu}_a-\bar{\mu}_b)(\bar{\mu}_a-\bar{\mu}_b)^TSb=(μˉa−μˉb)(μˉa−μˉb)T
Fisher准则:
J(w)=wTSbwwTSwwJ(w) = \frac{w^TS_bw}{w^TS_ww}J(w)=wTSwwwTSbw
LDA的核心目标是最大化目标函数J(w)J(w)J(w)。其中,分子反映类间样本距离,其值越大表明不同类别的样本分布越分散;分母代表类内样本距离,其值越小则表示相同类别的样本聚集越紧密。
我们对J(w)J(w)J(w)求偏导:
∂J(w)∂w=∂∂w(wTSbwwTSww)\frac{\partial J(w)}{\partial w} = \frac{\partial}{\partial w}(\frac{w^TS_bw}{w^TS_ww})∂w∂J(w)=∂w∂(wTSwwwTSbw)
并令偏导为0,可以得到:
(wTSww)Sbw=(wTSbw)Sww(w^T S_w w) S_b w = (w^T S_b w) S_w w(wTSww)Sbw=(wTSbw)Sww
由于 wTSwww^T S_w wwTSww 和 wTSbww^T S_b wwTSbw 都是标量,设:
λ=wTSbwwTSww\lambda = \frac{w^T S_b w}{w^T S_w w}λ=wTSwwwTSbw
所以就有:Sbw=λSwwS_b w = \lambda S_w wSbw=λSww
所以方向w∗w^*w∗就可以这样表示,当特征值最大时,对应特征向量就为w∗w^*w∗:
w∗=argmaxλeigenvector(Sw−1Sb)w^* = \arg\max_{\lambda} \text{eigenvector}(S_w^{-1} S_b)w∗=argλmaxeigenvector(Sw−1Sb)
代码实现:
接下来我们就使用scikit-learn构建一个简单的线性判别分析,并对其进行可视化:
import matplotlib.pyplot as plt
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
设置随机种子:
np.random.seed(42)
根据均值向量和协方差矩阵生成两类数据:
# 生成类别0的数据
mean0 = [2, 2]
cov0 = [[2, 0.8], [0.8, 2]]
X0 = np.random.multivariate_normal(mean0, cov0, 100)
# 生成类别1的数据
mean1 = [5, 5]
cov1 = [[2, -0.5], [-0.5, 2]]
X1 = np.random.multivariate_normal(mean1, cov1, 100)
将两类数据合并在一起便于训练:
# 合并数据和标签
X = np.vstack([X0, X1])
y = np.hstack([np.zeros(100), np.ones(100)])
LDA降维并训练模型:
# LDA降维
lda = LinearDiscriminantAnalysis(n_components=1)
lda.fit(X, y)
获取投影方向:
projection_direction = lda.scalings_[:, 0]
data_center = np.mean(X, axis=0)
将散点图和预测直线绘制出来:
# 创建图形
plt.figure(figsize=(10, 6))
# 绘制散点图
plt.scatter(X0[:, 0], X0[:, 1], c='blue', label='Class 0', alpha=0.6, s=50)
plt.scatter(X1[:, 0], X1[:, 1], c='red', label='Class 1', alpha=0.6, s=50)
plt.xlabel('Feature 1', fontsize=12)
plt.ylabel('Feature 2', fontsize=12)
plt.title('Two-Class Scatter Plot', fontsize=14)
plt.legend(fontsize=10)
# 根据投影方向线必过样本中心点的特性绘制
t = np.linspace(-10, 10, 100)
line_x = data_center[0] + t * projection_direction[0]
line_y = data_center[1] + t * projection_direction[1]
plt.plot(line_x, line_y, 'g-', linewidth=2, label='Projection Direction', alpha=0.8)
plt.plot(data_center[0], data_center[1], 'k*', markersize=15, label='Data Center')
plt.grid(True, alpha=0.3)
plt.show()
最终我们绘制出来的结果跟最上面的结果是一摸一样的,然后我们可以再加一个点进行预测,所以稍微改一下绘制代码:
# 创建图形
plt.figure(figsize=(10, 6))
# 创建两个样本点进行预测
sample1 = np.array([1,5])
sample2 = np.array([4,3])
pred1 = lda.predict(sample1.reshape(1, -1))
pred2 = lda.predict(sample2.reshape(1, -1))
print(f"Sample 1 prediction: {pred1[0]}")
print(f"Sample 2 prediction: {pred2[0]}")
# 绘制散点图
plt.scatter(X0[:, 0], X0[:, 1], c='blue', label='Class 0', alpha=0.6, s=50)
plt.scatter(X1[:, 0], X1[:, 1], c='red', label='Class 1', alpha=0.6, s=50)
plt.scatter(sample1[0], sample1[1], c='green', label='Sample 1', alpha=0.6, s=50)
plt.scatter(sample2[0], sample2[1], c='green', label='Sample 2', alpha=0.6, s=50)
plt.xlabel('Feature 1', fontsize=12)
plt.ylabel('Feature 2', fontsize=12)
plt.title('Two-Class Scatter Plot', fontsize=14)
plt.legend(fontsize=10)
# 绘制投影方向线
t = np.linspace(-10, 10, 100)
line_x = data_center[0] + t * projection_direction[0]
line_y = data_center[1] + t * projection_direction[1]
plt.plot(line_x, line_y, 'g-', linewidth=2, label='Projection Direction', alpha=0.8)
plt.plot(data_center[0], data_center[1], 'k*', markersize=15, label='Data Center')
plt.grid(True, alpha=0.3)
plt.show()
就可以发现:
可以看出我们添加了两个绿色的小点,它们被预测的类别都是class 0,与肉眼直观的并无太大差别。
对这个模型进行评估:

可以发现预测精确度特别高,所以算一个比较好的模型了。
下面贴上全部代码:
# 导入必要的库
import matplotlib.pyplot as plt
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# 设置随机种子
np.random.seed(42)
# 生成类别0的数据
mean0 = [2, 2]
cov0 = [[2, 0.8], [0.8, 2]]
X0 = np.random.multivariate_normal(mean0, cov0, 100)
# 生成类别1的数据
mean1 = [5, 5]
cov1 = [[2, -0.5], [-0.5, 2]]
X1 = np.random.multivariate_normal(mean1, cov1, 100)
# 合并数据和标签
X = np.vstack([X0, X1])
y = np.hstack([np.zeros(100), np.ones(100)])
# 划分训练集和测试集(80%训练,20%测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# LDA降维
lda = LinearDiscriminantAnalysis(n_components=1)
lda.fit(X_train, y_train)
# 获取投影方向
projection_direction = lda.scalings_[:, 0]
data_center = np.mean(X, axis=0)
# 创建图形
plt.figure(figsize=(10, 6))
# 创建两个样本点进行预测
sample1 = np.array([1,5])
sample2 = np.array([4,3])
pred1 = lda.predict(sample1.reshape(1, -1))
pred2 = lda.predict(sample2.reshape(1, -1))
print(f"Sample 1 prediction: {pred1[0]}")
print(f"Sample 2 prediction: {pred2[0]}")
# 绘制散点图
plt.scatter(X0[:, 0], X0[:, 1], c='blue', label='Class 0', alpha=0.6, s=50)
plt.scatter(X1[:, 0], X1[:, 1], c='red', label='Class 1', alpha=0.6, s=50)
plt.scatter(sample1[0], sample1[1], c='green', label='Sample 1', alpha=0.6, s=50)
plt.scatter(sample2[0], sample2[1], c='green', label='Sample 2', alpha=0.6, s=50)
plt.xlabel('Feature 1', fontsize=12)
plt.ylabel('Feature 2', fontsize=12)
plt.title('Two-Class Scatter Plot', fontsize=14)
plt.legend(fontsize=10)
# 绘制投影方向线
t = np.linspace(-10, 10, 100)
line_x = data_center[0] + t * projection_direction[0]
line_y = data_center[1] + t * projection_direction[1]
plt.plot(line_x, line_y, 'g-', linewidth=2, label='Projection Direction', alpha=0.8)
plt.plot(data_center[0], data_center[1], 'k*', markersize=15, label='Data Center')
plt.grid(True, alpha=0.3)
plt.show()
更多推荐
所有评论(0)