在上一節介紹了一種最常見的降維方法PCA,本節介紹另一種降維方法LLE,本來打算對於其他降維演算法一併進行一個簡介,不過既然看到這裡了,就對這些演算法做一個相對詳細的學習吧。
0.流形學習簡介
在前面PCA中說到,PCA是一種無法將資料進行拉直,當直接對於曲面進行降維後,導致資料的重疊,難以區分,如下圖所示:
這是因為在使用PCA降維時,PCA僅僅關注於保持降維後的方差最大,沒有考慮樣本的區域性特徵,如圖所示:
利用PCA在對點①進行降維後,沒有考慮點①與其他點②、③、④..的位置關係,也就是說對於點①來說,點⑤到點①的距離相較於點④到點①的距離更近,而在實際中並非如此,有句話說叫“舉頭見日,不見長安”。
此時就需要流形學習(Manifold Learning)來解決這個問題。所謂流形學習,就是一類基於流形的學習框架,所謂流形就是上面那樣一張“S”的曲面,也可以想象成一個將地毯捲起來的樣子,也就是一種不閉合的曲面,而流形學習就是對這個不閉合的去年進行降維,也就相當於把這上面那個“S”拉直,或者把地毯鋪開的一個過程。
不同於PCA演算法,流形學習在降維中關注保持樣本的區域性線性特徵,為了保持區域性特徵,在進行降維時,不能將歐氏距離作為樣本間的距離了,
此時就需要利用測地距離來保持樣本間的特徵,這個演算法就是ISOMAP等距對映演算法,該演算法考慮在降維後每一個樣本與其它樣本的測地距離。
但ISOMAP也有一個問題,就是它要考慮所有其他樣本之間的測地距離,當樣本的數量巨大時,演算法時間較長。這時就需要對演算法做進一步的改進。
1.LLE
1.1 LLE簡介及基本思想
LLE(Local Linear Embedding)就是一種流形學習的演算法,LLE在降維時不再考慮全部的樣本來尋找全域性最優解(這個在進行最終求解還是要考慮全域性的,只不過對於每個樣本而言,僅考慮區域性),
而是保留區域性的一些樣本點作為區域性特徵進行降維,這樣既保留了區域性特徵,又減少了計算量。
LLE的基本思想比較簡單,也就是隻考慮“最近幾個樣本點”進行降維,下面具體來說說LLE的思想:
樣本在原始空間中,分佈如圖所示:
假設現在我們對xi進行降維,將xi從原始空間降到低維空間,降維後暫且在這裡稱之為zi。
首先第一步,找出能夠“代表”xi的一些點,比如利用k-NN的方法,找到距離xi的最近的點xj.(注意:這裡xj並不是一個點,而是“一些”點);
然後,定義這些能夠“代表”xi的這些點與xj相連的權重為wij,那麼xi就可以用這些能夠“代表”它的點進行表示為:
但這些也僅僅代表而已,並不真正相等,但我們希望越能夠“代表”越好,也就是期望兩個值越接近越好。
這是對於一個樣本點i,而對於空間中所有的點都是如此,並且希望每一個樣本點與能夠“代表”它的那些點的越接近越好,也就是:
我們已經通過K-NN找出這些點xj了,那對於w還是未知的,因此,需要找出一組w,使得上面的那個式子的值越小越好。這不就是一個常規的NN演算法嗎?直接使用梯度下降進行求解就可以求出w了。
現在假設我們已經求出了一組w,然後用這組w進行降維,同樣,假設降維後的資料分佈如下圖:
這裡zi就是xi從原始空間降維後的結果,保持wij不變,找一組zi和zj,使得:
可以看出,這個式子與上面的式子完全一樣,不同的是,這裡我們所要尋找的是zi和zj,而上面的式子所要尋找的是權重wij,這裡同樣利用梯度下降就可以進行優化求解。
上面就是LLE的基本原理,其實LLE的原理從上面來看是比較簡單的,為保持區域性樣本的特點,將樣本用區域性其他樣本的線性表示然後進行降維。這種方法在李宏毅老師的課程裡用一句比較形象的話來概括:
1.2 LLE的數學推導
上面介紹了LLE的基本原理,說到了對於降維的過程直接用梯度下降的方法進行優化求解即可,然而同PCA一樣,LLE也有一套完整的推導流程。
前面對於PCA是基於數學推導流程,求出的解析解,然後說道PCA可以看做一個NN結構,利用梯度下降進行求解。
本打算這裡不再對LLE的推導進行闡述,但看到目前sklearn下對LLE用的就是解析解的方法,這裡就對LLE推導和求解過程進行一個簡單的學習和推導。
- 首先,對於第一步尋找xi的最近鄰樣本xj這裡就不再贅述,直接採用K-NN演算法找出就可以了。
- 假設原始空間中有m個樣本,每個樣本為n維,假設樣本xi的近鄰樣本xj有k個。
接下來,在高維空間中,需要找出xi與xj的空間線性關係,也就是找出權重係數wij。損失函式前面已經給出:
這裡一般會對權重係數做一個歸一化的處理,也就是:
為了便於推導,根據上面的歸一化對xi做一個變形:
則有:
作進一步的變換:
這裡,然後令,則有:
再對約束條件進一步變形:
這裡"1k"表示k*1全為1的向量。
根據損失函式L和約束條件,利用拉格朗日乘子求最小值:
對W進行求導,令其為0,則得到:
這裡就得到了在高維空間中的權重係數。
這裡Wi是一個k維的列向量,將所有樣本的Wi串起來後,最終W則是一個m*k維的矩陣。
- 接下來根據這些權重係數,在低維空間中找出降維後的資料,使得下面的損失函式越小越好:
這裡zi和zj是新的空間中的資料,也就是說原資料為m*n,到新空間中假設降到d維,則新空間資料變為m*d。
為了方便後邊的推導,這裡需要注意的是,這裡的j不再是1~k,而是1~m,也就是說,從m個資料中拿出對應的k個樣本,對於不屬於近鄰內的w則為0,也就是說,
W由原來的m*k維擴充到了m*m維,這裡同樣對於標準化資料有閒置條件:
對損失函式L(z)進一步整理得到:
令M=,則:
這裡就類似於PCA中降維的求解過程類似(在PCA中是使其最大),同樣的,對於上面的式子,使得L(z)最小的解為M的前d個最小的特徵值所組成的特徵向量所組成的Z。
這裡注意的是,M的最小特徵值為0,此時對應的特徵向量為全1,不能反映資料特徵,通常選擇第2到d+1小的特徵值對應的特徵向量得到最終降維後的資料Z。
1.3 LLE的演算法實現
根據上面的理論部分,按照其解析解的方式對LLE的演算法進行實現,加深演算法的理解。然後根據sklearn中的manifold中的LLE方法,實現LLE。
首先是LLE演算法的具體實現過程:
import numpy as np
def cal_pairwise_dist(x):
# 輸入矩陣x,返回兩兩之間的距離
"""
輸入矩陣x,返回兩兩之間的距離
(a-b)^2 = a^2 + b^2 - 2ab
"""
# a^2 + b^2
sum_x = np.sum(np.square(x), axis=1)
dist = np.add(np.add(-2 * np.dot(x, x.T), sum_x).T, sum_x)
return dist
def get_n_neighbors(data, n_neighbors=10):
dist = cal_pairwise_dist(data)
dist[dist < 0] = 0
dist = dist ** 0.5
n = dist.shape[0]
N = np.zeros((n, n_neighbors))
for i in range(n):
# 計算每一個樣本點,距離其最近的近鄰點的索引
index_ = np.argsort(dist[i])[1:n_neighbors+1]
# 距離每一個樣本最近的點的索引i
N[i] = N[i] + index_
return N.astype(np.int32)
def lle(data, n_dims=2, n_neighbors=10):
# 先獲取到樣本點的近鄰的樣本索引
N = get_n_neighbors(data, n_neighbors)
# 樣本數量n,維數為D
n, D = data.shape
# 當原空間維度小於近鄰點數量時,W不是滿秩的,要進行特殊處理
if n_neighbors > D:
tol = 1e-3
else:
tol = 0
# 初始化W,W應該是n * n——neighbors維度,即n個樣本有n個wi,每一個wi有n_neighbors, 這裡做了轉置
W = np.zeros((n_neighbors, n))
# 即1k,k維全為1的列向量
I = np.ones((n_neighbors, 1))
for i in range(n):
# 對於每一個樣本點xi
# 先將xi進行伸展,形狀同xj一致
Xi = np.tile(data[i], (n_neighbors, 1)).T
# xj所組成的矩陣
Ni = data[N[i]].T
# 求Yi
Yi = np.dot((Xi-Ni).T, (Xi - Ni))
# 這裡是對於樣本維度小於n_neighbors時做的特殊處理,MLLE演算法,保持區域性鄰域關係的增量Hessian LLE演算法
Yi = Yi + np.eye(n_neighbors) * tol * np.trace(Yi)
# 求解逆矩陣
Yi_inv = np.linalg.pinv(Yi)
# 求解每一個樣本的wi,並做歸一化處理
wi = (np.dot(Yi_inv, I))/(np.dot(np.dot(I.T, Yi_inv), I)[0, 0])
W[:, i] = wi[:, 0]
# 初始化W
W_y = np.zeros((n, n))
# 對上一步求的W做進一步擴充,之前是n*k維的,現在變成n*n維的,不是近鄰的位置補0
for i in range(n):
index = N[i]
for j in range(n_neighbors):
W_y[index[j], i] = W[j, i]
I_y = np.eye(n)
# 計算(I-W)(I-W).T
M = np.dot((I_y - W_y), (I_y - W_y).T)
# 求特徵值
eig_val, eig_vector = np.linalg.eig(M)
# 找出前n_dim個小的特徵值,忽略掉0,取第2到第n_dim+1個
index_ = np.argsort(np.abs(eig_val))[1: n_dims+1]
print("index_", index_)
# 特徵值對應的特徵向量就是最後降維後得到的樣本
Y = eig_vector[:, index_]
return Y
然後對上面的程式碼進行測試,首先匯入一些必要的畫圖的工具包和資料集,資料集採用sklearn自帶的“瑞士捲”資料集:
from sklearn.datasets import make_swiss_roll
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
X, color = make_swiss_roll(n_samples=5000, noise=0.1, random_state=42)
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=color, cmap=plt.cm.Spectral)
同時用sklearn自帶的manifold方法實現LLE,並同時用PCA對上面的資料集進行降維,對比三者得到的結果:
from sklearn import manifold
from sklearn.decomposition import PCA
# LLE
data_1 = lle(X, n_neighbors=30)
# sklearn LLE
data_2 = manifold.LocallyLinearEmbedding(n_components=2, n_neighbors=30).fit_transform(X)
# PCA
pca_data = PCA(n_components=2).fit_transform(X)
# 畫圖
plt.figure(figsize=(8, 4))
plt.subplot(131)
plt.title("LLE")
plt.scatter(data_1[:, 0], data_1[:, 1], c=color, cmap=plt.cm.Spectral)
plt.subplot(132)
plt.title("sklearn_LLE")
plt.scatter(data_2[:, 0], data_2[:, 1], c=color, cmap=plt.cm.Spectral)
plt.subplot(133)
plt.scatter(pca_data[:, 0], pca_data[:, 1], c=color, cmap=plt.cm.Spectral)
plt.title('PCA')
LLE演算法對於所選取的區域性樣本點的個數較為敏感,所選取區域性樣本數量不同,對結果影響比較大,如下一組實驗:
fig = plt.figure()
for index, k in enumerate((10, 20, 30, 40)):
plt.subplot(2, 2, index+1)
trans_data = manifold.LocallyLinearEmbedding(n_neighbors=k, n_components=2).fit_transform(X)
plt.scatter(trans_data[:, 0], trans_data[:, 1], c=color, cmap=plt.cm.Spectral)
可以看出,當n_neighbors不同取值,對於結果的影響較大,當過多或者過少時,結果則會完全壞掉。
1.4 LLE的優缺點
LLE的原理大致就是上面所介紹的,那麼在最後的實驗中也比較了不同的近鄰數目對結果的影響,在這裡總結一些LLE的優點和缺點進行比較:
優點:
-
- 方法簡單易於實現,相較於ISOMAP而言,計算複雜度低;
- LLE不同於PCA的線性降維,LLE是一種非線性的降維方法,能夠學習任意維的流形圖形;
- 因為其非線性的特點,LLE能夠表達區域性特徵,從而保留原資料特徵。
當然,LLE也自身存在一些缺點:
-
- 對於近鄰演算法選取的近鄰樣本點和樣本點的數量較為敏感;
- 僅能夠學習流形圖形,對於閉合的非流行圖形,演算法不適用;
- LLE只能夠用於稠密分佈均勻的樣本分佈,稀疏的樣本使用LLE演算法效果不佳(這可能也是因為LLE對於近鄰樣本敏感所造成的)。
2 TSNE演算法
前面介紹了兩種降維的方法PCA和LLE,這兩種降維的方法都有一個共同的特點:在進行降維時,都強調了降維後的相似的資料要儘可能地保持相似,但並沒有說對於那些不相似的資料,要有多不相似這個問題,
這就導致了在進行降維時,可能導致資料的重疊問題,導致在低維空間中一樣很難進行區分。
這時就需要另一種降維的方法——T-SNE。
所謂T-SNE,其就是SNE(stochastic neighbor embedding)的升級版,SNE同前面的演算法一樣,希望樣本在高維空間中相似的資料點,到低維空間也相似,而T-SNE則是在SNE的基礎上,要使得對於高維空間不相似的樣本,在低維空間中也儘可能不相似。
SNE則把這種距離的關係轉換為一種概率。下面先介紹二者共同的部分,對於區別和改進,在後邊進行對比。這裡僅做簡要原理介紹,不再做過多推導。
首先,定義在高維空間中兩個樣本xi和xj之間的相似度S(xi,xj),則:
這個概率表示,xi作為中心點,xj是其近鄰點的概率,當兩個點越近,則概率越大,距離較遠時,則概率較小。
同樣的,樣本對映到低維空間後,分別為zi和zj,在低位空間中,二者的相似度定義為S'(zi,zj),則:
然後根據這兩個分佈,在高維空間和低維空間中,我們希望這兩個分佈越相似越好,而用來衡量樣本相似度的指標為KL散度,因此定義損失如下:
具體的KL散度的公式如下:
然後根據這個損失函式,找一組zi和zj,使得損失越小越好。然後就是梯度下降進行求解。
上面就是SNE與TSNE所共同的過程,在高維空間中,二者都採用高斯分佈來度量任意兩個點相似性,即:
而在低維空間中,SNE同樣採用高斯分佈作為相似性度量的方式,即:
而對於TSNE來說,則採用了更一般的T分佈來替代高斯分佈:
這樣可以不但拉近相似的樣本的之間距離,同時,當樣本不相似時,則可以使距離拉的更遠。如下圖所示:
對於兩條曲線而言,當i與j相距較近時,二者在相似度上差不多,而當i與j相距較遠時,T分佈則可以進一步拉遠二者之間的距離。
上面就是TSNE的基本思想,具體原理和演算法這裡不再進行推導,詳細內容可以參考部落格:TSNE-原理與實現。
關於TSNE的具體實現方法,git地址https://github.com/heucoder/dimensionality_reduction_alo_codes/tree/master/codes/T-SNE。這裡就利用sklearn中的tsne方法對其進行實現和比較:
from sklearn.manifold import TSNE
plt.figure()
for index, k in enumerate((5, 15, 25, 30, 40, 100)):
plt.subplot(230+index+1)
data_tsne = TSNE(n_components=2, perplexity=k).fit_transform(X)
plt.scatter(data_tsne[:, 0], data_tsne[:, 1], c=color, cmap=plt.cm.Spectral)
對於在手寫數字識別上使用TSNE得到的結果,能夠更加明顯地看出TSNE的優點:
可以看出,TSNE解決了資料在降維之後的擁擠問題。
以上就是關於LLE和TSNE的內容,這裡暫時沒有對TSNE進行推導,後面自己會對TSNE的原始碼進行熟悉和了解加深理解,本節有關降維的方法就先到這裡了。
本文參考資料:
李宏毅《機器學習》
降維的方法其實有很多,也有同一種的不同變形,當用到時後面會再進行補充。下一更準備對比較重要的演算法AutoEnder做一個複習。