1. 程式人生 > >降維(PCA、核PCA、SVD、高斯隨機對映 和 NMF)

降維(PCA、核PCA、SVD、高斯隨機對映 和 NMF)

以下內容來自《Python資料科學指南》
降維方法比較:

  • PCA:計算代價高昂,特徵向量得存線上性相關。
  • 核PCA: 特徵向量是非線性相關也可以。
  • SVD:比PCA更能解釋資料,因為是直接作用於原資料集,不會像PCA一樣,將相關變數轉換為一系列不相干的變數。另外,PCA是單模因子分析方法,行列代表的是相同的實體,而SVD是雙模因子(即適用兩類實體矩陣),可以運用在文字挖掘中,行對應詞,列對應文件。
  • 高斯隨機對映:速度快,利用歐氏距離降維,但資料多會有記憶體問題,可以考慮稀疏隨機對映代替。
  • NMF:常見於推薦系統,輸入矩陣A = 降維矩陣(行)A_dash * 成本矩陣(列) F。
1. PCA: Principle Component Analysis, PCA 主成分分析,計算代價高昂,只適用於特徵向量間存線上性相關的環境下。
  • 將資料集中心化;
  • 找出資料集的相關矩陣和單位標準偏差值;
  • 將相關矩陣分解成它的特徵向量和值;
  • 基於降序的特徵值選擇Top-N特徵向量;
  • 投射輸入的特徵向量矩陣到一個新空間。
# -*- coding: utf-8 -*-
"""
Created on Fri Mar 30 17:47:41 2018

@author: Alvin AI
"""

from sklearn.datasets import load_iris
import numpy as np
import matplotlib.pyplot as plt
import scipy
from sklearn.preprocessing import scale

data = load_iris()
x = data['data']
y = data['target']

x_s = scale(x,with_mean=True,with_std=True,axis=0)#中心化
x_c = np.corrcoef(x_s.T)#計算過相關矩陣

eig_val,r_eig_vec = scipy.linalg.eig(x_c)
print 'Eigen values \n%s' % (eig_val)#相關矩陣中找到的特徵值
print '\n Eigen vectors \n%s' % (r_eig_vec)#相關矩陣中找的特徵向量
#可解釋變化的百分比=特徵值/原特徵變數的個數,這裡是4個變數
w = r_eig_vec[:,0:2]#選擇前兩個特徵向量,因為輸出結果Eigen values的前兩個特徵值較大

x_rd = x_s.dot(w)#叉乘,四維變二維y

plt.figure(1)
plt.scatter(x_rd[:,0],x_rd[:,1],c=y)
plt.xlabel('component 1')
plt.ylabel('component 2')

#如何選擇多少成分的方法
print "Component, Eigen Value, % of Variance, Cumulative %"
cum_per = 0
per_var = 0
for i,e_val in enumerate(eig_val):
    per_var = round((e_val/len(eig_val)),3)
    cum_per += per_var
    print ('%d, %0.2f, %0.2f, %0.2f')%(i+1, e_val, per_var*100, cum_per*100)

#輸出結果:
Eigen values #特徵值 
[2.91081808+0.j 0.92122093+0.j 0.14735328+0.j 0.02060771+0.j]

 Eigen vectors #特徵向量
[[ 0.52237162 -0.37231836 -0.72101681  0.26199559]
 [-0.26335492 -0.92555649  0.24203288 -0.12413481]
 [ 0.58125401 -0.02109478  0.14089226 -0.80115427]
 [ 0.56561105 -0.06541577  0.6338014   0.52354627]]

#可解釋變化的百分比=特徵值/原特徵變數的個數,這裡是4個變數
#2.91/4=72.80
#第一個成分可解釋72.80%,第二個成分可解釋23%,前兩份成分一起可以解釋95.8%
Component, Eigen Value, % of Variance, Cumulative %
1, 2.91, 72.80, 72.80
2, 0.92, 23.00, 95.80
3, 0.15, 3.70, 99.50
4, 0.02, 0.50, 100.00
2. 核PCA:針對非線性資料集進行降維。核類別有:線性、多項式、sigmoid、餘弦值、預先計算的、RBF。
from sklearn.datasets import make_circles
import matplotlib.pyplot as plt
import numpy as np
from sklearn.decomposition import PCA #PCA模組
from sklearn.decomposition import KernelPCA #核PCA模組

#生成一個變化非線性的資料集
np.random.seed(10)#定義一個隨機種子號
x,y = make_circles(n_samples=400, factor=.2, noise=0.02)#factor代表維度

plt.close('all')#關閉當前所有圖
plt.figure(1)
plt.title('original space')
plt.scatter(x[:,0],x[:,1],c=y)
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')

#使用PCA降維
pca = PCA(n_components=2)
pca.fit(x)
x_pca=pca.transform(x)

#繪製前兩個主成分的圖
plt.figure(2)
plt.title('pca')
plt.scatter(x_pca[:,0],x_pca[:,1],c=y)
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')

#將兩個成分單獨拎出來畫,發現結果均對映在一條直線上,無法實現區分
class_1_index = np.where(y==0)[0]
class_2_index = np.where(y==1)[0]

plt.figure(3)
plt.title('pca-one component')
plt.scatter(x_pca[class_1_index,0],np.zeros(len(class_1_index)),color='red')
plt.scatter(x_pca[class_2_index,0],np.zeros(len(class_2_index)),color='blue')

#使用kernal PCA
#這裡核PCA呼叫的核是徑向基函式(Radial Basis Function, RBF)
#gamma值為10,gamma是一個核(用於處理非線性)引數--核心係數
kpca = KernelPCA(kernel='rbf',gamma=10) 
x_kpca = kpca.fit_transform(x)

plt.figure(4)
plt.title('kernel pca')
plt.scatter(x_kpca[:,0],x_kpca[:,1],c=y)
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')
3. 奇異值分解:Singular Value Decomposition, SVD, 與PCA不同,直接作用於原始資料矩陣。SVD把m*n矩陣分解成三個矩陣的乘積:A = U*S*V^T 。
  • U:左奇異矩陣,m*k矩陣。
  • V:右奇異矩陣,n*k矩陣。
  • S:該矩陣的對角線值為奇異值,k*k矩陣。
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.preprocessing import scale
from scipy.linalg import svd

data = load_iris()
x = data['data']
y = data['target']

#只中心化,不需要把資料縮放到同一量綱
#因為現在的資料就是相同度量單位,不縮放還能捕捉到最大變化的基本單位
x_s = scale(x,with_mean=True,with_std=False,axis=0)

#用SVD分解矩陣
#沒必要縮放資料,full_matrices=False是一定要有的
U,S,V = svd(x_s,full_matrices=False)

#選擇最前兩個奇異值來近似原始的矩陣
x_t = U[:,:2]

#最後用降維的成分來繪製出資料集的圖形
plt.figure(1)
plt.scatter(x_t[:,0],x_t[:,1],c=y)
plt.xlabel("Component 1")
plt.ylabel("Component 2")
plt.show()
4. 高斯隨機對映:速度快,利用資料間的距離來降低維度。
# -*- coding: utf-8 -*-
"""
Created on Mon Apr 23 21:19:54 2018

@author: Alvin AI
"""

from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics import euclidean_distances
from sklearn.random_projection import GaussianRandomProjection
import matplotlib.pyplot as plt

#載入20個新聞租資料集
#我們只選用sci.crypt分類
#其他分類還包括“sci.med” "sci.space"等
cat = ['sci.crypt']
data = fetch_20newsgroups(categories=cat)

#從上面的資料集中建立一個詞-文件矩陣,詞頻作為值,不哦那個idf
vectorizer = TfidfVectorizer(use_idf=False)
vector = vectorizer.fit_transform(data.data)

#執行對映,我們把維度降為1000
gauss_proj = GaussianRandomProjection(n_components=1000)
vector_t = gauss_proj.fit_transform(vector)

#打印出轉換後的向量形態
print vector.shape
print vector_t.shape

#為了驗證轉換過程是否保持了距離,我們計算新的和舊的兩點間距離
org_dist = euclidean_distances(vector)
red_dist = euclidean_distances(vector_t)
diff_dist = abs(org_dist-red_dist)

#繪製差距熱圖(只有前100個文件)
plt.figure()
plt.pcolor(diff_dist[0:100,0:100])
plt.colorbar()
plt.show()
5. 非負矩陣分解:Non-negative Matrix Factorization, NMF 。常用於推薦系統,預測原本缺失的資料。
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 31 15:04:36 2018

@author: Alvin AI
"""

import numpy as np#
#from collections import dafaultdict
from sklearn.decomposition import NMF
import matplotlib.pyplot as plt

#生成電影評分資料集
ratings = [\
        [1,2,3,5,2,1],\
        [2,3,1,1,2,1],\
        [4,2,1,3,1,4],\
        [2,9,5,4,2,1],\
        [1,4,2,1,1,1]]

movie_dict = {1:'alvin story', 
              2:'star wars',
              3:'inception',
              4:'gunsa',
              5:'dream',
              6:'decomere'}

A = np.asmatrix(ratings,dtype=float)#向量化

max_components = 2
reconstruction_error = []
nmf = None
nmf = NMF(n_components = max_components, random_state=1) #降維到2
A_dash = nmf.fit_transform(A)#A_dash為降維矩陣,針對於行例項,即使用者

for i in range(A_dash.shape[0]):
    print 'User id = %d,  comp1 score = %0.2f, comp2 score = \
    %0.2f' % (i+1,A_dash[i][0],A_dash[i][1])

#輸入矩陣A=A_dash*F
    
#A_dash為降維矩陣,針對於行例項,即使用者
plt.figure(1)
plt.title('user concept mapping')
x = A_dash[:,0]
y = A_dash[:,1]
plt.scatter(x,y)
plt.xlabel('component1')
plt.ylabel('component2')

#F為成本矩陣,針對列例項,即電影
F =nmf.components_
plt.figure(2)
plt.title('movie concept mapping')
x = F[0,:]
y = F[1,:]
plt.scatter(x,y)
plt.xlabel('component1')
plt.ylabel('component2')

for i in range(F[0,:].shape[0]):
    plt.annotate(movie_dict[i+1],(F[0,:][i],F[1,:][i]))#在圖中給每個點加上電影名註釋
plt.show()    

#預測出電影評分,如果原輸入矩陣有些是0,而在下面結果輸出會預測出使用者評分
reconstructed_A = np.dot(A_dash,F)
np.set_printoptions(precision=2)#精確到小數點後2位
print reconstructed_A