關於LPP與LE在降維上的區別在這篇文章上已經描述的十分清楚了:LE與LPP的簡介(強烈建議看完這篇文章在看本文的代碼解析)
假設已有數據集樣本集合X={, ,..., },且每個樣本的維度爲m。
(一)求解LPP映射矩陣的算法步驟如下:
1.對整個數據集構建鄰接矩陣W,即查找每個樣本在數據集中距離最近的k個樣本,如樣本爲樣本的k個最近鄰接點之一,則在鄰接矩陣W中的第i行j列元素以及第i列j行元素可通過以下公式計算得出:
(1)
其中t爲熱核參數(設置多少自己看模型表現而定,一般可嘗試1-10之間)若樣本不爲樣本的k鄰接點之一,或樣本不爲樣本的k鄰接點之一,則:
(2)
2.求解下面問題的特徵值以及特徵向量:
(3)
其中的D是對角矩陣(diagonal matrix),其中的對角元素的值爲第i行或者第i列的所有元素的值之和。其中的L爲拉普拉斯矩陣,也是對稱的半正定矩陣,。
3.通過求解以上公式(3)中的特徵問題,可以得到一系列的特徵值以及該特徵值對應的特徵向量。然後我們根據這一系列的特徵值進行升序排列,同時將對應的特徵向量按列放入映射矩陣中。若我們需要將原始數據從m維降至d維,則我們需要取映射矩陣的前d列。映射公式如下:(映射矩陣爲m*d的矩陣,樣本爲m×1的向量,爲從降維得到的d*1的向量)
(4)
4.如果你沒看文字開頭鏈接的文章,你可能會有疑問,爲什麼求公式(3)的特徵問題來得到映 射矩陣。
公式(3)是由LPP的最小化問題轉化而來,該最小化問題如下:
X和L已經確定的情況下,我們通過求解較小的特徵值以及對應的特徵向量來使該問題最小化。
以下是來自哈工大大佬的python代碼塊,github地址:https://github.com/heucoder/dimensionality_reduction_alo_codes/tree/master/codes
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.datasets import load_digits, load_iris
def rbf(dist, t = 1.0):
'''
rbf kernel function
'''
return np.exp(-(dist/t))
def cal_pairwise_dist(x):
'''計算pairwise 距離, x是matrix
(a-b)^2 = a^2 + b^2 - 2*a*b
'''
sum_x = np.sum(np.square(x), 1)
dist = np.add(np.add(-2 * np.dot(x, x.T), sum_x).T, sum_x)
#返回任意兩個點之間距離的平方
return dist
def cal_rbf_dist(data, n_neighbors = 10, t = 1):
dist = cal_pairwise_dist(data)
n = dist.shape[0]
rbf_dist = rbf(dist, t)
W = np.zeros((n, n))
for i in range(n):
index_ = np.argsort(dist[i])[1:1 + n_neighbors]
W[i, index_] = rbf_dist[i, index_]
W[index_, i] = rbf_dist[index_, i]
return W
def lpp(data,
n_dims = 2,
n_neighbors = 30, t = 1.0):
'''
:param data: (n_samples, n_features)
:param n_dims: target dim
:param n_neighbors: k nearest neighbors
:param t: a param for rbf
:return:
'''
N = data.shape[0]
W = cal_rbf_dist(data, n_neighbors, t) #建立鄰接矩陣W,參數有最近k個鄰接點,以及熱核參數t
D = np.zeros_like(W)
for i in range(N):
D[i,i] = np.sum(W[i]) #求和每一行的元素的值,作爲對角矩陣D的對角元素
L = D - W #L矩陣
XDXT = np.dot(np.dot(data.T, D), data)
XLXT = np.dot(np.dot(data.T, L), data)
'''
np.linalg.eig用作計算方陣的特徵值以及右特徵向量
np.linalg.pinv對矩陣進行求逆
本人理解:在求解最小特徵問題(公式3)時,左乘XDX^T的逆,而後求其特徵值以及對應特徵向量
輸出的eig_val,eig_vec爲特徵值集合以及特徵向量集合(此時是無序的)
'''
eig_val, eig_vec = np.linalg.eig(np.dot(np.linalg.pinv(XDXT), XLXT))
'''
argsort返回的是特徵值在數據eig_val排序後的序號。
如數組a=[2,1,7,4],則np.argsort(a)返回的是[1,0,3,2]
'''
sort_index_ = np.argsort(np.abs(eig_val))
eig_val = eig_val[sort_index_]#對特徵值進行排序
print("eig_val[:10]", eig_val[:10])
#此時eig_val已經是升序排列了,需要排除前幾個特徵值接近0的數,至於爲啥請看論文 "Locality Preserving Projections"
j = 0
while eig_val[j] < 1e-6:
j+=1
print("j: ", j)
#返回需要提取的前n個特徵值所對應的n個特徵向量的序列號
sort_index_ = sort_index_[j:j+n_dims]
# print(sort_index_)
eig_val_picked = eig_val[j:j+n_dims]
print(eig_val_picked)
eig_vec_picked = eig_vec[:, sort_index_] #獲取該n個特徵向量組成的映射矩陣
data_ndim = np.dot(data, eig_vec_picked) #公式(4)
return data_ndim
if __name__ == '__main__':
X = load_digits().data
y = load_digits().target
dist = cal_pairwise_dist(X)
max_dist = np.max(dist)
print("max_dist", max_dist)
data_2d = lpp(X, n_neighbors = 5, t = 0.01*max_dist)
plt.figure(figsize=(12,6))
plt.subplot(121)
plt.title("LPP")
plt.scatter(data_2d[:, 0], data_2d[:, 1], c = y)
(二)接下來是求解LE降維的步驟:
1.第一步構造鄰接矩陣W,構造的思路與LPP一致,每個樣本均與最近的k個鄰接點建立權重關係:
(5)
2.求解下面問題的特徵值以及特徵向量:
(6)
其中,降維後的矩陣的列向量將會來自公式(6)求出的特徵矩陣,
3.通過求解公式(6),我們將得到的特徵值按升序排列,並把特徵向量按照對應特徵值的大小依次作爲列向量放入特徵矩陣Y中。若我們需要將數據從m維降至d維,則只需要取特徵矩陣的前d列。形成的n*d的矩陣即爲降維後的矩陣,其中每一行數據爲樣本降維表示。
4.公式(6)由LE的最小化問題轉化而來,該最小化問題表示如下:
(7)
在L和D是確定的情況下,我們通過求解公式(6)較小的特徵值以及對應的特徵向量來是問題(7)最小化。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from mpl_toolkits.mplot3d import Axes3D
def rbf(dist, t = 1.0):
'''
rbf kernel function
'''
return np.exp(-(dist/t))
def cal_pairwise_dist(x):
'''計算pairwise 距離, x是matrix
(a-b)^2 = a^2 + b^2 - 2*a*b
'''
sum_x = np.sum(np.square(x), 1)
dist = np.add(np.add(-2 * np.dot(x, x.T), sum_x).T, sum_x)
#返回任意兩個點之間距離的平方
return dist
def cal_rbf_dist(data, n_neighbors = 10, t = 1):
dist = cal_pairwise_dist(data)
n = dist.shape[0]
rbf_dist = rbf(dist, t)
W = np.zeros((n, n))
for i in range(n):
index_ = np.argsort(dist[i])[1:1+n_neighbors]
W[i, index_] = rbf_dist[i, index_]
W[index_, i] = rbf_dist[index_, i]
return W
def le(data,
n_dims = 2,
n_neighbors = 5, t = 1.0):
'''
:param data: (n_samples, n_features)
:param n_dims: 目標維度
:param n_neighbors: k個最近鄰接點
:param t: 熱核參數t
:return:
'''
N = data.shape[0]
W = cal_rbf_dist(data, n_neighbors, t) #求鄰接矩陣W
D = np.zeros_like(W)
for i in range(N):
D[i,i] = np.sum(W[i])#建立對角矩陣D,對角元素均爲鄰接矩陣第i行或者第i列元素值之和
'''
本人理解:在求解最小特徵問題(公式6)時,左乘矩陣D的逆,而後求其特徵值以及對應特徵向量
輸出的eig_val,eig_vec爲特徵值集合以及特徵向量集合(此時是無序的)
'''
D_inv = np.linalg.inv(D)
L = D - W
eig_val, eig_vec = np.linalg.eig(np.dot(D_inv, L))
sort_index_ = np.argsort(eig_val)
eig_val = eig_val[sort_index_]
print("eig_val[:10]: ", eig_val[:10])
j = 0
while eig_val[j] < 1e-6: #排除特徵值接近於0,以及該特徵值對應的特徵向量
j+=1
print("j: ", j)
sort_index_ = sort_index_[j:j+n_dims] #提取特徵值大於1e-6開始的序號
eig_val_picked = eig_val[j:j+n_dims]
print(eig_val_picked)
eig_vec_picked = eig_vec[:, sort_index_]#根據序號從特徵向量集合中得到映射後的降維矩陣
# print("L: ")
# print(np.dot(np.dot(eig_vec_picked.T, L), eig_vec_picked))
# print("D: ")
# D not equal I ???
print(np.dot(np.dot(eig_vec_picked.T, D), eig_vec_picked))
#LE不同於LPP,LE不存在Si=YXi的映射關係,特徵矩陣中[j:j+n_dim]的列向量便是原始數據的降維表示。
X_ndim = eig_vec_picked
return X_ndim
if __name__ == '__main__':
X = load_digits().data
y = load_digits().target
dist = cal_pairwise_dist(X)
max_dist = np.max(dist)
print("max_dist", max_dist)
X_ndim = le(X, n_neighbors = 20, t = max_dist*0.1)
plt.scatter(X_ndim[:, 0], X_ndim[:, 1], c = y)
plt.show()