机器学习——数据降维
文章目錄
- 數據降維
- 1 維度災難與降維
- 2 主成分分析
- 2.1 PCA原理
- 2.2 PCA算法
- 2.3 PCA算法實驗
- 3 SVD降維
- 4 核主成分分析降維
- 總結
數據降維
數據降維又稱維數約簡,就是降低數據的維度。其方法有很多種,從不同角度入手可以有不同的分類,主要分類方法有:根據數據的特性可以劃分為線性降維和非線性降維,根據是否考慮和利用數據的監督信息可以劃分為無監督降維、有監督降維和半監督降維,根據保持數據的結構可以分為全局保持降維、局部保持降維和全局與局部保持一致降維等。需要根據特定的問題選擇合適的數據降維方法。
數據降維一方面可以解決“維數災難”,緩解信息豐富、知識貧乏的現狀,降低復雜度,另一方面可以更好地認識和理解數據。
1 維度災難與降維
維度災難用來描述當空間維度增加時,分析和組織高維空間,因體積指數增加而遇到各種問題場景。例如在高維情況下,數據樣本稀疏的問題。例如k近鄰算法中,任意樣本附近任意小的距離內總能找到一個訓練樣本,即訓練樣本的采樣密度足夠大才能保證分類性能,當特征維度很大時,滿足密采樣的樣本數量會呈現指數級增大,大到幾乎無法達到。所以在高維度情況下,涉及距離、內積的計算變得困難。
緩解維度災難的一個重要途徑就是降維。一般來說,想獲得低維子空間,最簡單的方法就是對原始高維度空間進行線性變換:給定d維空間中的樣本X=(x1,x2,···,xm)變換之后得到的d’<=d維空間中的樣本Z=W^TX。
變換矩陣W可視為d’個d維基向量,新空間中的屬性是原空間屬性的線性組合,基于線性變換來進行降維的方法都成線性維方法,主要區別于對低維子空間的性質有所不同,相當于對W施加了不同的約束。
2 主成分分析
主成分分析是一種最常用的無監督降維方法,通過降維技術吧多個變量化為少數幾個主成分的統計分析方法。這些主成分能夠反映原始變量的絕大部分信息,它們通常表示為原始變量的某種線性組合。
2.1 PCA原理
為了便于維度變換有如下假設:
- 假設樣本數據是n維的。
- 假設原始坐標系為由標準正交基向量{i1,i2,···,in}張成的空間。
- 假設經過線性變換后的新坐標系為由標準正交基向量{j1,j2,···,jn}張成的空間。
根據定義有:
記ws=(js·i1,js·i2,···,js·in),其各個分量就是基向量js在原始坐標系中的投影。
根據定義,有:
令坐標變換矩陣W為:W=(w1,w2,···,wn),則有:
這樣W的第s列就是js在原始坐標系中的投影。
假設樣本點xi在原始坐標系中的表示為:
令其中:
假設樣本點xi在新坐標系中的表示為:
令其中:
于是可以推導出:
丟棄其中的部分坐標,將維度降低到d<n,則樣本點xi在低維坐標系中的坐標:
那么,現在出現一個問題,即丟棄哪些坐標?思想是:基于降低之后的坐標重構樣本時,盡量要與原始樣本接近。
2.2 PCA算法
算法的輸入為樣本集D與低維空間維數d,輸出為所求的投影矩陣W。
算法的步驟表現在:
- 對所有樣本進行中心化操作:
- 計算樣本的協方差矩陣XX^T。
- 對協方差矩陣作特征值分解。
- 取最大的d個特征值對應的特征向量構造投影矩陣W。
通常低維空間維數d的選取有兩種方法:
- 通過交叉驗證法選取較好的d。
- 從算法原理角度設置一個閾值,例如t=95%,然后選取使得下式成立的最小的d的值:
PCA降維有兩個準則:
- 最近重構性:樣本集中所有點,重構后的點距離原來點的誤差之和最小。
- 最大可分性:樣本點在低維空間的投影盡可能分開。
2.3 PCA算法實驗
加載PCA算法包,并加載PCA算法設置降維后的主成分數目為2。
from sklearn.decomposition import PCApca=PCA(n_components=2) reduced_x=pca.fit_transform(x)樣本的數據結構如下圖所示:
數據集包含150個數據集,分為三類,每類50個數據,每個數據包含4個特征。可以通過花萼長度,花萼寬度,花瓣長度,花瓣寬度4個特征預測鳶尾花卉屬于(Setosa,Versicolour,Virginica)三個種類中的哪一類。
數據的結構化表示為:
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from sklearn import datasets from sklearn.decomposition import PCA import pandas as pd import seaborndata = datasets.load_iris() X =data['data'] y =data['target'] a = pd.DataFrame(X, columns=data.feature_names)#seaborn.boxplot(data= a) plt.plot(a) plt.legend(data.feature_names) plt.show()首先選取三個特征查看數據的分布情況:
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from sklearn import datasetsdata = datasets.load_iris() X =data['data'] y =data['target'] ax = Axes3D(plt.figure()) for c, i, target_name in zip('rgb', [0, 1, 2], data.target_names):ax.scatter(X[y==i, 0], X[y==i, 2], c=c, label=target_name)ax.set_xlabel(data.feature_names[0]) ax.set_xlabel(data.feature_names[1]) ax.set_xlabel(data.feature_names[2]) ax.set_title('Iris') plt.legend() plt.show()可以發現紅色數據點,也就是Setosa距離其他數據較遠。
然后選擇兩個特征子集查看數據分布情況。
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from sklearn import datasetsdata = datasets.load_iris() X =data['data'] y =data['target'] ax = Axes3D(plt.figure()) for c, i, target_name in zip('rgb', [0, 1, 2], data.target_names):ax.scatter(X[y==i, 0], X[y==i, 1], c=c, label=target_name)ax.set_xlabel(data.feature_names[0]) ax.set_xlabel(data.feature_names[1]) ax.set_title('Iris') plt.legend() plt.show()可以發現紅色的Setosa仍然是線性可分的。
利用PCA將數據降到二維:
import matplotlib.pyplot as plt from sklearn.decomposition import PCA from sklearn.datasets import load_irisdata=load_iris() y=data.target x=data.data pca=PCA(n_components=2) reduced_x=pca.fit_transform(x)red_x,red_y=[],[] blue_x,blue_y=[],[] green_x,green_y=[],[]for i in range(len(reduced_x)):if y[i] ==0:red_x.append(reduced_x[i][0])red_y.append(reduced_x[i][1])elif y[i]==1:blue_x.append(reduced_x[i][0])blue_y.append(reduced_x[i][1])else:green_x.append(reduced_x[i][0])green_y.append(reduced_x[i][1])plt.scatter(red_x,red_y,c='r',marker='x') plt.scatter(blue_x,blue_y,c='b',marker='D') plt.scatter(green_x,green_y,c='g',marker='.') plt.show()最終得到的主成分降維效果如下圖所示:
可以看到用sklearn庫實現降維后,將原本的四維數據變成二維,以實現在平面中畫出樣本點的分布。
3 SVD降維
奇異值分解(SVD):設X為n*N階矩陣,且rank(X)=r,則n階正交矩陣V和N階正交矩陣U,使得:
其中,
根據正交矩陣的性質,有:
若令M為:
則可以推導出:
那么,λi(i=1,2,···,r)就是XX^ T的特征值,其對應的特征向量組成正交矩陣V。因此SVD奇異值分解等價于PCA主成分分析,核心都是求解XX^ T的特征值以及對應的特征向量。
下面的實驗利用SVD對給定的數據進行降維處理。
用戶數據的SVD奇異值分解:
def _svd(self):self.U, self.S, self.VT = np.linalg.svd(self.data)return self.U, self.S, self.VT前k個奇異值的平方和占比要大于等于percentage,求出滿足此條件的最小的k值。
def _calc_k(self, percentge):self.k = 0total = sum(np.square(self.S))svss = 0 for i in range(np.shape(self.S)[0]):svss += np.square(self.S[i])if (svss/total) >= percentge:self.k = i+1breakreturn self.k構建由奇異值組成的對角矩陣。
def _buildSD(self, k):self.SD = np.eye(self.k) * self.S[:self.k] e = np.eye(self.k)for i in range(self.k):e[i,i] = self.S[i] return self.SDSVD降維。
def DimReduce(self, percentage):self._svd()self._calc_k(percentage)print('\n按照奇異值開方和占比閾值percentage=%d, 求得降維的k=%d'%(percentage, self.k))self._buildSD(self.k)k,U,SD,VT = self.k,self.U, self.SD, self.VTa = U[:len(U), :k]b = np.dot(SD, VT[:k, :len(VT)])newData = np.dot(a,b)return newData訓練數據集,用戶對商品的評分矩陣,行為多個用戶對單個商品的評分,列為用戶對每個商品的評分。
def CSVD_manual():data = np.array([[5, 5, 0, 5],[5, 0, 3, 4],[3, 4, 0, 3],[0, 0, 5, 3],[5, 4, 4, 5],[5, 4, 5, 5]])percentage = 0.9svdor = CSVD(data)ret = svdor.DimReduce(percentage)print('====================================================')print('原始用戶數據矩陣:\n', data)print('降維后的數據矩陣:\n', ret)print('====================================================')最后運行得到的結果如下圖所示:
4 核主成分分析降維
PCA方法假設從高維空間到低維空間的函數映射是線性的,但是在很多現實任務中,可能需要非線性的映射才能找到合適的降維空間來降維。
非線性降維的一種常用方法是基于核技巧對線性降維方法進行核化。
考慮一個線性不可分的數據集,如果我們用一個映射 ,將其映射到三維空間,比如可取:
可以看到,映射到高維空間后數據變得線性可分。這意味著,非線性映射使得我們能夠處理非線性的數據。
假設要將高維特征空間中的數據投影到低維空間中,投影矩陣W根據PCA求解的結果需要求解方程:
通常并不清楚Φ的解析表達式,于是引入核函數:
定義核矩陣:
定義:
可以推導出:
最終通過矩陣的化簡可以得到KA=λA。同樣該問題也是一個特征值分解問題,取K的最大的d個特征對應的特征向量組成W即可。
對于新樣本x,其投影后的坐標為:
可以看到,為了獲取投影后的坐標,KPCA需要對所有樣本求和,因此它的計算開銷更大。
下面實現對非線性映射的降維。
def rbf_kernel_pca(X,gama,n_components):sq_dists = pdist (X, 'sqeuclidean') mat_sq_dists=squareform(sq_dists) K=exp(-gama * mat_sq_dists) N=K.shape[0]one_n = np.ones((N,N))/N K=K - one_n.dot(K) - K.dot(one_n) + one_n.dot(K).dot(one_n) eigvals,eigvecs = eigh(K)X_pc = np.column_stack((eigvecs[:,-i] for i in range(1,n_components+1))) return X_pc首先計算樣本對歐幾里得距離,并生成核矩陣k(x,y)=exp(-gama * ||x-y||^ 2),x和y表示樣本,構建一個NXN的核矩陣,矩陣值是樣本間的歐氏距離值。然后,聚集核矩陣K’=K-LK-KL + LKL,其中L是一個nXn的矩陣(和核矩陣K的維數相同,所有的值都是1/n。
聚集核矩陣的必要性是:樣本經過標準化處理后,當在生成協方差矩陣并以非線性特征的組合替代點積時,所有特征的均值為0;但用低維點積計算時并沒有精確計算新的高維特征空間,也無法確定新特征空間的中心在零點。
隨后對聚集后的核矩陣求取特征值和特征向量 ,選擇前k個特征值所對應的特征向量,和PCA不同,KPCA得到的K個特征,不是主成分軸,而是高維映射到低維后的低維特征數量核化過程是低維映射到高維,PCA是降維,經過核化后的維度已經不是原來的特征空間。核化是低維映射到高維,但并不是在高維空間計算(非線性特征組合)而是在低維空間計算(點積),做到這點關鍵是核函數,核函數通過兩個向量點積來度量向量間相似度,能在低維空間內近似計算出高維空間的非線性特征空間。
X,y=make_moons(n_samples=100,random_state=123) plt.scatter(X[y==0,0],X[y==0,1],color='red',marker='^',alpha=0.5) plt.scatter(X[y==1,0],X[y==1,1],color='blue',marker='o',alpha=0.5) plt.show()分離半月形數據,生成二維線性不可分數據。
sk_pca = PCA(n_components=2) X_spca=sk_pca.fit_transform(X) fig,ax = plt.subplots(nrows=1,ncols=2,figsize=(7,3)) ax[0].scatter(X_spca[y==0,0],X_spca[y==0,1],color='red',marker='^',alpha=0.5) ax[0].scatter(X_spca[y==1,0],X_spca[y==1,1],color='blue',marker='o',alpha=0.5) ax[1].scatter(X_spca[y==0,0],np.zeros((50,1))+0.02,color='red',marker='^',alpha=0.5) ax[1].scatter(X_spca[y==1,0],np.zeros((50,1))-0.02,color='blue',marker='^',alpha=0.5) ax[0].set_xlabel('PC1') ax[0].set_ylabel('PC2') ax[1].set_ylim([-1,1]) ax[1].set_yticks([]) ax[1].set_xlabel('PC1') plt.show()
顯然,這兩個半月形不是線性可分的,而我們的目標是通過核 PCA 將這兩個半月形數據展開,使得數據集成為適用于某一線性分類器的輸入數據。 PCA降維,映射到主成分,但仍不能很好地進行線性分類。
利用基于RBF核的KPCA來實現線性可分。可以看到,兩個類別(圓形和三角形)此時是線性可分的,這使得轉換后的數據適合作為線性分類器的訓練數據集。
X,y=make_circles(n_samples=1000,random_state=123,noise=0.1,factor=0.2) plt.scatter(X[y==0,0],X[y==0,1],color='red',marker='^',alpha=0.5) plt.scatter(X[y==1,0],X[y==1,1],color='blue',marker='o',alpha=0.5) plt.show()分離同心圓,生成同心圓數據。再進行標準的PCA映射,最后得到KPCA的降維效果圖。
sk_pca = PCA(n_components=2) X_spca=sk_pca.fit_transform(X) fig,ax = plt.subplots(nrows=1,ncols=2,figsize=(7,3)) ax[0].scatter(X_spca[y==0,0],X_spca[y==0,1],color='red',marker='^',alpha=0.5) ax[0].scatter(X_spca[y==1,0],X_spca[y==1,1],color='blue',marker='o',alpha=0.5) ax[1].scatter(X_spca[y==0,0],np.zeros((500,1))+0.02,color='red',marker='^',alpha=0.5) ax[1].scatter(X_spca[y==1,0],np.zeros((500,1))-0.02,color='blue',marker='^',alpha=0.5) ax[0].set_xlabel('PC1') ax[0].set_ylabel('PC2') ax[1].set_ylim([-1,1]) ax[1].set_yticks([]) ax[1].set_xlabel('PC1') plt.show()X_kpca=rbf_kernel_pca(X, gama=15, n_components=2) fig,ax = plt.subplots(nrows=1,ncols=2,figsize=(7,3)) ax[0].scatter(X_kpca[y==0,0],X_kpca[y==0,1],color='red',marker='^',alpha=0.5) ax[0].scatter(X_kpca[y==1,0],X_kpca[y==1,1],color='blue',marker='o',alpha=0.5) ax[1].scatter(X_kpca[y==0,0],np.zeros((500,1))+0.02,color='red',marker='^',alpha=0.5) ax[1].scatter(X_kpca[y==1,0],np.zeros((500,1))-0.02,color='blue',marker='^',alpha=0.5) ax[0].set_xlabel('PC1') ax[0].set_ylabel('PC2') ax[1].set_ylim([-1,1]) ax[1].set_yticks([]) ax[1].set_xlabel('PC1') ax[0].xaxis.set_major_formatter(FormatStrFormatter('%0.1f')) ax[1].xaxis.set_major_formatter(FormatStrFormatter('%0.1f')) plt.show()
通過實驗可以發現,仔細觀察PCA其實只對原始數據進行了旋轉操作,這是由于其尋找的是數據的“主要分布方向”。從圖中看出,在重構后的數據清晰的分為兩類,且在主成分方向上分割明顯,便于使用線性分類方法進行處理。KPCA可以將原始數據投影至線性可分情況,打破了數據的原有結構。但是KPCA不能將數據投影至完全線性可分的程度,這說明KPCA只是個無監督的降維算法,它不管樣本的類別屬性,只是降維而已。
總結
主成分分析是一種降維方法。在PCA中,數據從原來的坐標系轉換到了新的坐標系,新坐標系由數據本身決定。在新坐標系中,第一個坐標軸選擇的是原始數據中方差最大的方向,第二個坐標軸選擇的是和第一個坐標軸正交且具有最大方差的方向。該過程一直重復,重復次數為原始數據中特征的數目。我們會發現,大部分方差都包含在最前面的幾個新坐標軸中。因此,我們可以忽略余下的坐標軸,即對數據進行了降維處理。其優點是降低數據的復雜性,識別最重要的特征,但其缺點在于不一定需要,且可能損失有用信息。
奇異值分解,是一種矩陣因式分解。通過計算奇異值個數和奇異向量,生成一個可以代替原矩陣的近似矩陣,將數據集的奇異值表征按重要性排列,舍棄不重要的特征向量。可用來達到降維的目的,從而找出數據中的主成分。其優點在于并行化,缺點是計算量大,分解后的矩陣解釋性較弱。我們說到PCA降維時,需要找到樣本協方差矩陣X^ TX最大的d個特征向量,然后用著最大的d個特征向量組成的矩陣來做低維投影降維。可以看出,在這個過程中需要先求出協方差矩陣X^ TX,當樣本數多、樣本特征數也多的時候,比如10000*10000的矩陣,這個計算量是很大的。注意到SVD也可以求出協方差矩陣X^ TX最大的d個特征向量組成的矩陣,但是SVD有個好處,就是可以不求出協方差矩陣X^TX,也能通過某些算法求出右奇異矩陣V。也就是說,PCA算法可以不用做特征分解,而是用SVD來進行完成。
核主成分分析降維,為了更好地處理非線性數據,引入非線性映射函數,將原空間中的數據映射到高維空間,注意,這個是隱性的,利用核函數進行處理,無需知道映射函數的具體形式,它讓降維變得更有意義
總結
以上是生活随笔為你收集整理的机器学习——数据降维的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 机器学习——聚类算法
- 下一篇: (FCN)-Fully Convolut