LPP(局部保持投影)以及LE(拉普拉斯映射)的區別以及代碼python解讀

關於LPP與LE在降維上的區別在這篇文章上已經描述的十分清楚了:LE與LPP的簡介(強烈建議看完這篇文章在看本文的代碼解析)

假設已有數據集樣本集合X={{\bold x_1}, {\bold x_2},..., {\bold x_n}},且每個樣本的維度爲m。

(一)求解LPP映射矩陣的算法步驟如下:

1.對整個數據集構建鄰接矩陣W,即查找每個樣本x_i在數據集中距離最近的k個樣本,如樣本{\bold x_j}爲樣本{\bold x_i}k個最近鄰接點之一,則在鄰接矩陣W中的第i行j列元素{\bold W}_{ij}以及第i列j行元素{\bold W}_{ji}可通過以下公式計算得出:

                                                        \LARGE {\bold W}_{ij}={\bold W}_{ji}=e^{-\frac{||{\bold x_i}-{\bold x_j}||^2}{t}}                                  (1)

其中t爲熱核參數(設置多少自己看模型表現而定,一般可嘗試1-10之間)若樣本\large {\bold x_i}不爲樣本\large {\bold x_j}k鄰接點之一,或樣本\large {\bold x_j}不爲樣本\large {\bold x_i}k鄰接點之一,則:

                                                                  \LARGE {\bold W}_{ij}={\bold W}_{ji}=0                                             (2)

2.求解下面問題的特徵值\large \lambda以及特徵向量\large {\bold v}

                                                           \LARGE {\bold X}{\bold L}{\bold X}^T{\bold v} = \lambda {\bold X}{\bold D}{\bold X}^T {\bold v}                                    (3)

其中的D是對角矩陣(diagonal matrix),其中的對角元素{\bold D_{ii}}的值爲第i行或者第i列的所有元素的值之和。其中的L爲拉普拉斯矩陣,也是對稱的半正定矩陣,\large {\bold L}={\bold D}-{\bold W}

3.通過求解以上公式(3)中的特徵問題,可以得到一系列的特徵值以及該特徵值對應的特徵向量。然後我們根據這一系列的特徵值進行升序排列,同時將對應的特徵向量按列放入映射矩陣{\bold V}中。若我們需要將原始數據從m維降至d維,則我們需要取映射矩陣的前d列。映射公式如下:(映射矩陣{\bold V}爲m*d的矩陣,樣本\large {\bold x_i}爲m×1的向量,\LARGE {\bold s_i}爲從\large {\bold x_i}降維得到的d*1的向量)

                                                                   \LARGE {\bold s_i}={\bold V}^T{\bold x_i}                                                           (4)

4.如果你沒看文字開頭鏈接的文章,你可能會有疑問,爲什麼求公式(3)的特徵問題來得到映 射矩陣。

         公式(3)是由LPP的最小化問題轉化而來,該最小化問題如下:

                                                                  \LARGE \underset{{\bold v}}{argmin} {\bold v}^T{\bold X}{\bold L}{\bold X}^T{\bold v}  

                                                                  \large s.t. {\bold v}^T {\bold X}{\bold D}{\bold X}^T{\bold v}=1

         XL已經確定的情況下,我們通過求解較小的特徵值以及對應的特徵向量來使該問題最小化。


以下是來自哈工大大佬的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個鄰接點建立權重關係:

                                                        \LARGE {\bold W}_{ij}={\bold W}_{ji}=e^{-\frac{||{\bold x_i}-{\bold x_j}||^2}{t}}                               (5)

2.求解下面問題的特徵值\large \lambda以及特徵向量\large {\bold y}

                                                              \LARGE {\bold L}{\bold y}=\lambda {\bold D}{\bold y}                                                            (6)

其中,降維後的矩陣的列向量將會來自公式(6)求出的特徵矩陣\large {\bold Y}=\{{{\bold y}_1, {\bold y}_2,..., {\bold y}_n}\}

3.通過求解公式(6),我們將得到的特徵值按升序排列,並把特徵向量按照對應特徵值的大小依次作爲列向量放入特徵矩陣Y中。若我們需要將數據從m維降至d維,則只需要取特徵矩陣的前d列。形成的n*d的矩陣即爲降維後的矩陣,其中每一行數據\large {\bold s}_i爲樣本\large {\bold x}_i降維表示。

4.公式(6)由LE的最小化問題轉化而來,該最小化問題表示如下:

                                                              \LARGE \underset{{\bold y}}{argmin} {\bold y}^T{\bold L}{\bold y}                                                    (7)

                                                              \large s.t. {\bold y}^T {\bold D}{\bold y}=1

LD是確定的情況下,我們通過求解公式(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()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章