03.18 通過線性判別分析(LDA)實現特徵抽取

一、什麼是線性判別分析?

線性判別分析(Linear discriminant Analysis,LDA)與PCA類似也是一種特徵抽取的算法,它能夠提高數據分析過程中的計算效率。PCA是尋找數據集中方差最大的方向作為主成分分量的軸,而LDA是最優化分類的特徵子空間。LDA和PCA都是用來降低數據維度的線性轉換技巧。PCA屬於無監督算法,LDA屬於監督算法。相對於PCA算法而言,LDA更適合對於分類特徵的提取。

通過線性判別分析(LDA)實現特徵抽取

上圖中,叉叉表示類別1,實心圓表示類別2,LD1表示x軸,LD2表示y軸。其中,類別1和類別2都滿足正態分佈,我們對類別1和類別2分佈在x軸和y軸上進行投影,在x軸方向上,通過線性判定,我們可以將類別1和類別2區分開,所以它是一個好的線性判定。而y軸方向的線性判定保持了數據集的較大方差,而無法將類別1和類別2進行區分,所以它不是一個好的線性判定。

二、如何來做線性判別分析?

在使用線性判別分析之前,還需要滿足幾個假設條件。第一個假設是數據需要滿足正態分佈,第二個就是各個類別數據具有相同的協方差矩陣,且樣本的特徵是相互獨立的。即使沒有滿足這些條件,LDA還是可以很好的工作,LDA一共包含了6個步驟:

1、標準化處理

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
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)

2、計算每一類別特徵的均值向量

#用來存放不同類別特徵的平均值向量
mean_vecs = []
#葡萄酒數據集一共有3個類別1,2,3
for label in range(1,4):
mean_vecs.append(np.mean(train_x_std[label==train_y],axis=0))

通過線性判別分析(LDA)實現特徵抽取

3、計算類間散佈矩陣S(B)和類內散佈矩陣S(W)

通過線性判別分析(LDA)實現特徵抽取

a、計算類內散佈矩陣S(W)

#定義每行數據的大小,每條數據有13個特徵
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
# print(np.bincount(train_y)[1:])

在使用累加的方式來計算散佈矩陣的時候,需要滿足訓練集的不同類別的類標分佈式均勻的,下面我們查看不同類別的類標分佈情況

print(np.bincount(train_y)[1:])#[40 49 35]

所以,我們需要對不同類別的散佈矩陣S(i)做縮放處理,對各個類別的散佈矩陣除以該類別內樣本數量N(i),發現計算散佈矩陣的方式與計算協方差矩陣的方式是一樣的,協方差矩陣可以看作是歸一化的散佈矩陣

通過線性判別分析(LDA)實現特徵抽取

#計算類內的散佈矩陣
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

b、計算類間散佈矩陣S(B)

通過線性判別分析(LDA)實現特徵抽取

#計算全局均值m
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)

4、計算矩陣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])

通過線性判別分析(LDA)實現特徵抽取

5、選取前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()

通過線性判別分析(LDA)實現特徵抽取

通過上面的特徵值和圖可以發現,只存在兩個特徵值不為0(其餘的特徵值接近於0),前兩個特徵值累計已經接近於100%,所以我們選取前兩個特徵向量來構造轉換矩陣W。

W = np.hstack((eigen_pairs[0][1][:,np.newaxis].real,
eigen_pairs[1][1][:,np.newaxis].real))
print(W)

通過線性判別分析(LDA)實現特徵抽取

6、將訓練樣本通過轉換矩陣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()

通過線性判別分析(LDA)實現特徵抽取

三、使用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
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的維度
lda = LinearDiscriminantAnalysis(n_components=2)
#將X通過LDA進行轉換
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


分享到:


相關文章: