详解 LDA
详解 LDA基本概念什么是LDALDA 核心思想LDA 简单二分类实例
实现步骤(python)第一步 标准化处理第二步 计算每一类别特征的均值向量第三步 计算类间散布矩阵S(B)和类内散布矩阵S(W)第四步 计算矩阵S(W)^(-1)S(B)的特征值和对应的特征向量第五步 选取前k个特征和对应的特征向量,构造一个d×k维的转换矩阵W,其中特征向量以列的形式排列第六步 将训练样本通过转换矩阵W映射到新的特征空间
使用scikit-learn实现LDA分析
详解 LDA
基本概念
什么是LDA
线性判别分析(LDA)是一种有监督算法,同时经常来数据进行降维,
相比于 PCA , LDA 可以作为一种有监督的降维算法 。在 PCA 中 ,算法没有考虑数据的标签(类别) , 只是把原数据映射到一些方差比较大的方向上而已。
看一个实例: 可以看出,如果是PCA的话,为了方差最大化,会选择投影到左边,而LDA则会选择投影到下面。
LDA 核心思想
LDA的思想可以用一句话概括,就是“投影后类内方差最小,类间方差最大”。也就是说,要将数据在低维度上进行投影,投影后希望每一种类别数据的投影点尽可能的接近,而不同类别的数据的类别中心之间的距离尽可能的大:
LDA 简单二分类实例
假设有两类数据,分别为红色和蓝色,如下图所示,这些数据特征是二维的,希望将这些数据投影到一维的一条直线,让每一种类别数据的投影点尽可能的接近,而红色和蓝色数据中心之间的距离尽可能的大。 上图中提供了两种投影方式,哪一种能更好的满足我们的标准呢?
从直观上可以看出,右图要比左图的投影效果好,因为右图的黑色数据和蓝色数据各个较为集中,且类别之间的距离明显。左图则在边界处数据混杂。
以上就是LDA的主要思想了,当然在实际应用中,数据是多个类别的,我们的原始数据一般也是超过二维的,投影后的也一般不是直线,而是一个低维的超平面。
实现步骤(python)
第一步 标准化处理
import pandas
as pd
from sklearn
.preprocessing
import StandardScaler
from sklearn
.model_selection
import train_test_split
if __name__
== "__main__":
data
= pd
.read_csv
("G:/dataset/wine.csv")
x
,y
= data
.ix
[:,1:],data
.ix
[:,0]
train_x
,test_x
,train_y
,test_y
= train_test_split
(x
,y
,test_size
=0.3,random_state
=1)
std
= StandardScaler
()
train_x_std
= std
.fit_transform
(train_x
)
test_x_std
= std
.fit_transform
(test_x
)
第二步 计算每一类别特征的均值向量
mean_vecs
= []
for label
in range(1,4):
mean_vecs
.append
(np
.mean
(train_x_std
[label
==train_y
],axis
=0))
第三步 计算类间散布矩阵S(B)和类内散布矩阵S(W)
计算类内散布矩阵S(W)
d
= 13
S_W
= np
.zeros
((d
,d
))
for label
, mv
in zip(range(1, 4), mean_vecs
):
class_scatter
= np
.zeros
((d
, d
))
for row
in train_x_std
[train_y
== label
]:
row
, mv
= row
.reshape
(d
, 1), mv
.reshape
(d
, 1)
class_scatter
+= (row
- mv
).dot
((row
- mv
).T
)
S_W
+= class_scatter
我们需要对不同类别的散布矩阵S(i)做缩放处理,对各个类别的散布矩阵除以该类别内样本数量N(i),发现计算散布矩阵的方式与计算协方差矩阵的方式是一样的,协方差矩阵可以看作是归一化的散布矩阵
for label
,mv
in zip(range(1,4),mean_vecs
):
class_scatter
= np
.cov
(train_x_std
[train_y
== label
].T
)
S_W
+= class_scatter
计算类间散布矩阵S(B)
mean_overall
= np
.mean
(train_x_std
,axis
=0)
S_B
= np
.zeros
((d
,d
))
for i
,mean_vec
in enumerate(mean_vecs
):
N
= train_x_std
[train_y
== i
+1,:].shape
[0]
mean_vec
= mean_vec
.reshape
(d
,1)
mean_overall
= mean_overall
.reshape
(d
,1)
S_B
+= N
* (mean_vec
- mean_overall
).dot
((mean_vec
- mean_overall
).T
)
第四步 计算矩阵S(W)^(-1)S(B)的特征值和对应的特征向量
eigen_vals
,eigen_vecs
= np
.linalg
.eig
(np
.linalg
.inv
(S_W
).dot
(S_B
))
eigen_pairs
= [(np
.abs(eigen_vals
[i
]),eigen_vecs
[:,i
]) for i
in range(len(eigen_vals
))]
eigen_pairs
= sorted(eigen_pairs
,key
=lambda k
: k
[0],reverse
=True)
for eigen
in eigen_pairs
:
print(eigen
[0])
第五步 选取前k个特征和对应的特征向量,构造一个d×k维的转换矩阵W,其中特征向量以列的形式排列
下面我们通过图像来判断,特征向量的个数对于不同类别的区分能力
tot
= sum(eigen_vals
.real
)
discr
= [(i
/ tot
) for i
in sorted(eigen_vals
.real
,reverse
=True)]
cum_discr
= np
.cumsum
(discr
)
plt
.bar
(range(1,14),discr
,alpha
=0.5,align
="center",label
="单个特征区分")
plt
.step
(range(1,14),cum_discr
,where
="mid",label
="累计区分")
plt
.xlabel
("线性判别(LDA)")
plt
.ylabel
("区分率")
plt
.ylim
([-0.1,1.1])
plt
.legend
(loc
="best")
plt
.show
()
通过上面的特征值和图可以发现,只存在两个特征值不为0(其余的特征值接近于0),前两个特征值累计已经接近于100%,所以我们选取前两个特征向量来构造转换矩阵W。
第六步 将训练样本通过转换矩阵W映射到新的特征空间
train_x_std_lda
= train_x_std
.dot
(W
)
colors
= ["r","b","g"]
markers
= ["s","x","o"]
for l
,c
,m
in zip(np
.unique
(train_y
),colors
,markers
):
plt
.scatter
(train_x_std_lda
[train_y
== l
,0],train_x_std_lda
[train_y
== l
,1],
c
=c
, label
=l
,marker
=m
)
plt
.xlabel
("LD1")
plt
.ylabel
("LD2")
plt
.legend
(loc
="upper right")
plt
.show
()
使用scikit-learn实现LDA分析
import pandas
as pd
import numpy
as np
from sklearn
.preprocessing
import StandardScaler
from sklearn
.model_selection
import train_test_split
import matplotlib
.pyplot
as plt
from sklearn
.discriminant_analysis
import LinearDiscriminantAnalysis
from sklearn
.linear_model
import LogisticRegression
if __name__
== "__main__":
data
= pd
.read_csv
("G:/dataset/wine.csv")
x
, y
= data
.ix
[:, 1:], data
.ix
[:, 0]
train_x
, test_x
, train_y
, test_y
= train_test_split
(x
, y
, test_size
=0.3, random_state
=0)
std
= StandardScaler
()
train_x_std
= std
.fit_transform
(train_x
)
test_x_std
= std
.fit_transform
(test_x
)
lda
= LinearDiscriminantAnalysis
(n_components
=2)
train_x_std_lda
= lda
.fit_transform
(train_x_std
,train_y
)
test_x_std_lda
= lda
.fit_transform
(test_x_std
,test_y
)
logistic
= LogisticRegression
()
logistic
.fit
(train_x_std_lda
,train_y
)
print("训练集上的准确率:",logistic
.score
(train_x_std_lda
,train_y
))
print("测试集上的准确率:",logistic
.score
(test_x_std_lda
,test_y
))
训练集上的准确率:
0.991935483871
测试集上的准确率:
1.0