线性判别分析 (LDA) 是一种非常常见的用于监督分类问题的技术,让我们一起了解一下什么是 LDA 以及它是如何工作的。
什么是线性判别分析
LDA是一种降维技术,常用于机器学习和模式分类应用中的预处理步骤。
降维技术的主要目标是通过将特征从高维空间转换到低维空间,移除冗余和相关特征,从而减少数据的维度。
如上图,LDA是一种监督分类技术,它在降维过程中考虑了标签,这类降维技术广泛应用于生物识别、生物信息学和化学等领域,而PCA(主成分分析)是一种非监督的降维方法。
LDA的工作原理
LDA的目标是将高维空间中的特征投影到低维空间。
这可以通过三个步骤来实现:
第一步是计算不同类别之间的可分性(即不同类别均值之间的距离),也称为类间方差。
其中,,
第二步是计算每个类别中均值与样本之间的距离,这被称为类内方差。
其中,。
第三步是构建一个低维空间,使得类间方差最大化,同时类内方差最小化。记 为低维空间的投影向量,这个过程被称为 Fisher 判别准则 (Fisher’s criterion)。目标函数可以写为:
简单来说,目标函数使不同类投影后的类间方差最大,相同类投影后的类内方差最小,即不同类越远越好,相同类越聚集越好。
如下图,右边的投影向量要好于左边的投影向量。
第四步是求解目标函数最大值对应的投影矩阵P,根据广义瑞利商性质(这里不展开),当投影向量满足下面等式时,目标函数达到最大。
根据的公式,我们知道在数据集给定的情况下,是已知的。因此上述方程是已知矩阵的特征向量和特征值求解,比较简单,不懂的可以看看线性代数的特征向量和特征值的求解方法。
LDA代码实战
代码的数据集是银行票据鉴定数据集,该数据集公开可在 UCI 机器学习库中获取(https://archive.ics.uci.edu/ml/datasets/banknote+authentication#)。
数据集中包含的属性有:小波变换图像的方差(连续型)、小波变换图像的偏度(连续型)、小波变换图像的峰度(连续型)、图像的熵(连续型),以及类别(整数型,0-非真实,1-真实)。
在开始LDA之前,首先导入所有必要的依赖项。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
现在使用 Pandas 的 read_csv
函数将数据集加载到 DataFrame 中。
columns = ["var","skewness","curtosis","entropy","class"]
df = pd.read_csv("http://archive.ics.uci.edu/ml/machine-learning-databases/00267/\
data_banknote_authentication.txt",index_col=False, names = columns)
数据集包含总共 1372 个实例,其中 762 个是非真实票据,610 个是真实票据。属性的数据分布可以通过单变量和多变量图来查看:
f, ax = plt.subplots(1, 4, figsize=(10,3))
vis1 = sns.distplot(df["var"],bins=10, ax= ax[0])
vis2 = sns.distplot(df["skewness"],bins=10, ax=ax[1])
vis3 = sns.distplot(df["curtosis"],bins=10, ax= ax[2])
vis4 = sns.distplot(df["entropy"],bins=10, ax=ax[3])
f.savefig('subplot.png')
sns.pairplot(df, hue="class")
现在,我们将计算两个类别的四维均值向量(4 = 特征数量)。与 PCA 不同,在 LDA 中不需要对数据进行标准化,因为标准化不会影响结果。LDA 的主要结果不受标准化影响的原因是,LDA 处理的是类间协方差与类内协方差的比率,而不是协方差的绝对值(如 PCA 所做的那样)。
mean_vec = []
for i in df["class"].unique():
mean_vec.append( np.array((df[df["class"]==i].mean()[:4]) ))
print(mean_vec)
下一步是计算类间矩阵()和类内矩阵():
SW = np.zeros((4,4))
for i in range(1,4): #2 is number of classes
per_class_sc_mat = np.zeros((4,4))
for j in range(df[df["class"]==i].shape[0]):
row, mv = df.loc[j][:4].reshape(4,1), mean_vec[i].reshape(4,1)
per_class_sc_mat += (row-mv).dot((row-mv).T)
SW += per_class_sc_mat
print('within-class Scatter Matrix:\n', SW)
overall_mean = np.array(df.drop("class", axis=1).mean())
SB = np.zeros((4,4))
for i in range(2): #2 is number of classes
n = df[df["class"]==i].shape[0]
mv = mean_vec[i].reshape(4,1)
overall_mean = overall_mean.reshape(4,1) # make column vector
SB += n * (mv - overall_mean).dot((mv - overall_mean).T)
print('between-class Scatter Matrix:\n', SB)
然后求的特征值和特征向量:
e_vals, e_vecs = np.linalg.eig(np.linalg.inv(SW).dot(SB))
print('Eigenvectors \n%s' %e_vecs)
print('\nEigenvalues \n%s' %e_vals)
现在,我们需要选择与前-k 个特征值对应的前-k 个特征向量。选择 k 的标准取决于每个方向上可能保留的方差量。对于数据压缩,我们通常选择保留 99% 方差的维度,而对于可视化,我们将维度设置为 2 或 3。在这里,我们将选择前两个特征值对应的特征向量用于可视化。由于最大的特征值对应的特征向量几乎保留了 100% 的方差,因此可以忽略其他三个特征值。变换矩阵 将是:
W = np.hstack((e_vecs[0][1].reshape(4,1), e_vecs[1][1].reshape(4,1)))
print('Matrix W:\n', W.real)
现在,我们需要将 4 维特征空间 转换为 2 维特征子空间 。
X_lda = X.dot(W)
df["PC1"] = X_lda[:,0]
df["PC2"] = X_lda[:,1]
可视化lda降维后的数据分布:
vis = sns.lmplot(data = df[["PC1","PC2","class"]], x = "PC1", y = "PC2",fit_reg=False, hue = "class",\
size = 6, aspect=1.5, scatter_kws = {'s':50}, )
vis.savefig("lda.png")
显示变量和类别的数据分布:
sns.pairplot(df[["PC1","PC2","class"]], hue="class")
从上述图中可以看出,只用 PC1 就足以有效地区分两个类别
也可以使用scikit-learn库实现LDA:
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
model = LDA(n_components=3)
X_lda = model.fit_transform(X, y)
df["PC1"] = X_lda[:,0]
sns.regplot(data = df[["PC1","class"]], x = "PC1",y = "class", fit_reg=False,scatter_kws = {'s':50}, )
在这里我们可以看到,LDA降维后的第一个特征就能够很好的区分类别。
结论
LDA算法既可以用来降维,又可以用来分类,但是目前来说,主要还是用于降维。在我们进行图像识别相关的数据分析时,LDA是一个有力的工具。
欢迎扫码关注: