我們在對數據進行降維時,比較常用的方法有LLE(局部線性嵌入),LE(拉普拉斯映射)還有t-SNE,前兩種主要考慮高維中距離相近的點在低維中還要保持距離相近,但是沒有考慮高維中距離較遠的點在低維是怎樣的情況,而t-SNE恰好解決了這個問題,t-SNE的目標是高維中距離相近的點低維中還要相近,高維中距離較遠的點,低維中距離要被拉大。
t-SNE將相似度轉換爲一種概率,左邊的高維的,右邊是低維的,力求兩個相似度接近。下邊的公式叫做相對熵或者KL距離,當兩個概率越來越近時,KL距離會無限接近於0。
我們繪製一個圖像,紅色線是高維的距離和相似度的圖像,使用指數函數,這是因爲指數函數下降比較快,當距離一拉開相似度就掉下來了,藍色線是低維的距離和相似度的圖像。我們發現上下兩個點,上邊的點在高維中距離較近相似度較高,所以低維中距離變化不大,下邊的點在高維中距離較遠相似度較低,所以一降維距離就被拉開了,因此達到目標效果。
目標效果:
代碼:
from time import time
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.manifold import TSNE
"""獲取數據"""
def get_data():
digits = datasets.load_digits(n_class=6) #取前六種數字圖片,0-5
data = digits.data #data.shape=[1083,64],表示1084張圖片,每個圖片8*8但是將圖片表示爲一個行向量
label = digits.target #表示取出的1083個圖片對應的數字
n_samples, n_features = data.shape #圖片數1083和每張圖片的維度64
return data, label, n_samples, n_features
"""顯示數據"""
def plot_embedding(result, label, title): #傳入1083個2維數據,1083個標籤,圖表標題
x_min, x_max = np.min(result, 0), np.max(result, 0) #分別求出每一列最小值和最大值
data = (result - x_min) / (x_max - x_min) #將數據進行正則化,分母爲數據的總長度,因此分子一定小於分母,生成的矩陣元素都是0-1區間內的
plt.figure() #創建一個畫布
for i in range(data.shape[0]): #遍歷所有的數據,共1083個
plt.text(data[i, 0], data[i, 1], str(label[i]),
color=plt.cm.Set1(label[i] / 10.),
fontdict={'weight': 'bold', 'size': 9}) #將該點的標籤設置顏色顯示到對應位置
plt.title(title) #設置標題
plt.show()
"""主函數"""
def main():
data, label, n_samples, n_features = get_data() #data種保存[1083,64]的向量
tsne = TSNE(n_components=2, init='pca', random_state=0) #n_components將64維降到該維度,默認2;init設置embedding初始化方式,可選pca和random,pca要穩定些
t0 = time() #記錄開始時間
result = tsne.fit_transform(data) #進行降維,[1083,64]-->[1083,2]
plot_embedding(result, label,'t-SNE embedding of the digits (time %.2fs)'% (time() - t0)) #顯示數據
if __name__ == '__main__':
main()
代碼中我覺得有一個理解起來比較繞的地方就是取最大值最小值那裏,已經在上一篇博客中做了解釋。
---end---