機器學習:PCA(高維數據映射為低維數據 封裝&調用)


一、基礎理解

 1) PCA 降維的基本原理

  • 尋找另外一個坐標系,新坐標系中的坐標軸以此表示原來樣本的重要程度,也就是主成分;取出前 k 個主成分,將數據映射到這 k 個坐標軸上,獲得一個低維的數據集。

 

 2)主成分分析法的本質

  • 將數據集從一個坐標系轉換到另一個坐標系,原坐標系有 n 個維度(n 中特征),則轉換的新坐標系也有 n 個維度,每個主成分表示一個維度,只是對於轉換后的坐標系,只取前 k 個維度(也就是前 k 個主成分),此 k 個維度相對於數據集更加重要,形成矩陣 Wk

 

 

 3)將 n 維特征空間轉換為 k 維(此為降維的過程)

  • 原則:將 n 維的樣本數據轉換為 k 維的數據
  • 操作:將數據集 X 的一個 n 維樣本,與矩陣 Wk 相乘,得到一個 k 維數據;
  • 公式:X . WkT = Xk

 

 

 4)將降維后的 k 維數據 Xk 恢復到 n 維數據

  • 公式:Xk . Wk = Xm
  • :恢復后的數據集 X 已經不是原始的數據集了,因為在前期降維的過程回丟失原始數據集的信息,恢復數據集時,丟失的信息無法恢復;

 

 

二、代碼實現求取前 n 個主成分

 1)步驟

  • 初始化:__init__(self, n_components);
  • 求取主成分:fit(),使用梯度上升法求解
  1. demean(X):將原始數據集的每一列的均值歸零,即每一種特征的均值歸零;
  2. f(w, X):求當前變量 w 對應的目標函數值;
  3. df(w, X):求當前變量 w 對應的梯度值;
  4. direction(w):將每次初始化的變量 w 轉化為單位向量,也可以轉化其它向量;
  5. first_component():使用梯度上升法優化求取一個主成分;
  6. 其它代碼:循環,逐級求取前 n_components 個主成分;
  • 降維數據集:從 n 維降至 n_components 維數據,得到新的數據集
  • 恢復數據集:將降維后的 n_components 維數據集恢復到 n 維數據集,得到恢復后的數據集

 

 

 2)具體代碼

  • import numpy as np
    
    class PCA:
    
        def __init__(self, n_components):
            """初始化 PCA"""
            # n_components:要求取的主成分個數
            # n_components_:存放所主成分
            assert n_components >= 1, "n_components must ba valid"
            self.n_components = n_components
            self.components_ = None
    
        def fit(self, X, eta=0.01, n_iters=10**4):
            """獲得數據集 X 的前 n 個主成分"""
            assert self.n_components <= X.shape[1], \
                "n_components must not ba greater then the feature number of X"
    
            def demean(X):
                """均值歸零化"""
                return X - np.mean(X, axis=0)
    
            def f(w, X):
                """求目標函數"""
                return np.sum((X.dot(w) ** 2)) / len(X)
    
            def df(w, X):
                """求梯度"""
                return X.T.dot(X.dot(w)) * 2. / len(X)
    
            def direction(w):
                """將向量轉化為單位向量"""
                return w / np.linalg.norm(w)
    
            def first_component(X, initial_w, eta=0.01, n_iters=10**4, epsilon=10**-8):
                """梯度上升法求主成分"""
                w = direction(initial_w)
                cur_iter = 0
    
                while cur_iter < n_iters:
                    gradient = df(w, X)
                    last_w = w
                    w = w + eta * gradient
                    if (abs(f(w, X) - f(last_w, X)) < epsilon):
                        break
    
                    cur_iter += 1
    
                return w
    
            X_pca = demean(X)
            self.components_ = np.empty(shape=(self.n_components, X.shape[1]))
            for i in range(self.n_components):
                # 每次循環得到一個主成分,每次循環都初始化一次 w
                initial_w = np.random.random(X_pca.shape[1])
                w = first_component(X_pca, initial_w, eta, n_iters)
                self.components_[i,:] = w
    
                # 每次循環,都要在上次數據集的基礎上求取新的數據集,用來求取下一個主成分
                X_pca = X_pca - X_pca.dot(w).reshape(-1, 1) * w
    
            return self
    
        def transform(self, X):
            """將給定的 X,映射到各個主成分分量中,也就是獲取降維后的數據集"""
            assert X.shape[1] == self.components_.shape[1]
    
            return X.dot(self.components_.T)
    
        def inverse_transform(self, X):
            """將給定的 X,反向映射到原來的特征空間,也就是將低維數據升為高維數據"""
            assert X.shape[1] == self.components_.shape[0]
    
            return X.dot(self.components_)
    
        def __repr__(self):
            """實例化類 PCA 時所打印的內容:用__repr__() 函數顯示"""
            return "PCA(n_components=%d)" % self.n_components

     

 3)調用所寫的代碼

  • 模擬二維數據集
    import numpy as np
    import matplotlib.pyplot as plt
    
    X = np.empty((100, 2))
    X[:, 0] = np.random.uniform(0, 100, size=100)
    X[:, 1] = 0.75 * X[:,0] + 3. + np.random.normal(0, 10, size=100)

     

  • 調用得到降維后的數據集:X_reduction
    from playML.PCA import PCA
    
    pca = PCA(n_components=1)
    pca.fit(X)
    
    X_reduction = pca.transform(X)

     

  • 恢復數據集:X_restore
    X_restore = pca.inverse_transform(X_reduction)

     

  • 繪圖查看恢復的數據集和原始數據集的區別
    plt.scater(X[:,0], X[:,1], color='b', alpha=0.5)
    plt.scatter(X_restore[:,0], X_restore[:,1], color='r', alpha=0.5)
    plt.show()

    # 紅色為恢復數據集,藍色為原始數據集,恢復后的數據集丟失了部分信息,丟失的信息是在降維時發生的;

 

 

三、使用 scikit-learn 中的 PCA 算法

 1)機器學習問題,要先 train_test_split 原始數據集

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

# load_digits:手寫識別數字數據集
digits = datasets.load_digits()
X = digits.data
y = digits.target

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)

 

 

 2)使用 kNN 算法直接訓練數據集

%%time

from sklearn.neighbors import KNeighborsClassifier

knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train, y_train)
  • 准確度
    # 識別准確度
    knn_clf.score(X_train, y_train)
    
    # 輸出:0.991833704528582

     

 

 3)使用降維后的數據訓練

  • 對 X_train 和 X_test 數據集進行降維
    pca = PCA(n_components=2)
    
    pca.fit(X_train)
    X_train_reduction = pca.transform(X_train)
    X_test_reduction = pca.transform(X_test)
  1. 問題:為什么使用 X_train 數據集求解前 2 個主成分,讓 X_test 數據集也映射到這個新的特征空間上?
  2. 這樣做有問題嗎?
  3. 思考問題時要先假設老師是正確的,然后思考為什么正確,也就是這樣做的合理性
  4. 這種思路和 StandardScaler (數據標准化處理)一樣,要以 X_train 為標准處理 X_test

 

  • 使用 kNN 算法訓練降維后的數據集
    %%time
    
    knn_clf = KNeighborsClassifier()
    knn_clf.fit(X_train_reduction, y_train)

 

  • 准確度
    knn_clf.score(X_test_reduction, y_test)
    
    # 輸出:0.6066666666666667

     

  • 分析一:
  1. 問題(1):降低到 2 維,數據維度太低,雖然增加了運算效率,但精度太低;
  2. 問題(2):到底降到多少維合適?
  3. 方法:在 scikit-learn 中的 PCA 算法內,提供了一個指標,根據這個指標,可以方便找出針對一個數據集保持多少准確度合適;
  4. 指標(變量:pca.explained_variance_ratio_):返回的數據是各主成分解釋原始數據的方差的相應比例,也就是各個主成分所反映原始數據方差的比例;

 

  • pca.explained_variance_ratio_
    
    # 輸出:array([0.14566817, 0.13735469])
    # 只有兩個主成分

     

  • 分析二: array([0.14566817, 0.13735469])
  1. 也就是第一個主成分所解釋原始數據的主成分的比例為 0.14566817,第二個主成分所解釋的原始數據的方差的比例 0.13735469 ;
  2. 問題:兩個主成分一共可以解釋28%的原數據的方差,則丟失了72%的原數據的方差,丟失信息過多;
  • pca 的過程尋找的主成分,就是尋找使得原始數據方差為最大時的主成分,explained_variance_ratio_ 變量反應的是,該主成分對應的最大時的方差,占原始數據總方差的百分比;

 

 4)查看所有主成分對應的解釋的原始數據的方差的比例

  • pca = PCA(n_components=X_train.shape[1])
    pca.fit(X_train)
    pca.explained_variance_ratio_
    
    # 輸出: array([
    1.45668166e-01, 1.37354688e-01, 1.17777287e-01, 8.49968861e-02, 5.86018996e-02, 5.11542945e-02, 4.26605279e-02, 3.60119663e-02, 3.41105814e-02, 3.05407804e-02, 2.42337671e-02, 2.28700570e-02, 1.80304649e-02, 1.79346003e-02, 1.45798298e-02, 1.42044841e-02, 1.29961033e-02, 1.26617002e-02, 1.01728635e-02, 9.09314698e-03, 8.85220461e-03, 7.73828332e-03, 7.60516219e-03, 7.11864860e-03, 6.85977267e-03, 5.76411920e-03, 5.71688020e-03, 5.08255707e-03, 4.89020776e-03, 4.34888085e-03, 3.72917505e-03, 3.57755036e-03, 3.26989470e-03, 3.14917937e-03, 3.09269839e-03, 2.87619649e-03, 2.50362666e-03, 2.25417403e-03, 2.20030857e-03, 1.98028746e-03, 1.88195578e-03, 1.52769283e-03, 1.42823692e-03, 1.38003340e-03, 1.17572392e-03, 1.07377463e-03, 9.55152460e-04, 9.00017642e-04, 5.79162563e-04, 3.82793717e-04, 2.38328586e-04, 8.40132221e-05, 5.60545588e-05, 5.48538930e-05, 1.08077650e-05, 4.01354717e-06, 1.23186515e-06, 1.05783059e-06, 6.06659094e-07, 5.86686040e-07, 7.44075955e-34, 7.44075955e-34, 7.44075955e-34, 7.15189459e-34])
  1. 分析:從各個主成分對應的方差的比例,可以看出各個主成分的重要程度

 

  • 繪制各個主成分解釋的方差的比例
    plt.plot([i for i in range(X_train.shape[1])],
            [np.sum(pca.explained_variance_ratio_[:i+1]) for i in range(X_train.shape[1])])
    plt.show()

  1. 橫軸表示前 n 個主成分,縱軸表示,這前 n 個主成分所解釋的原始數據的方差的比例;

     

  2. 通過此曲線,可以判斷到底將原始數據降到多少維合適,也就是保留多少原始數據的信息比較合適;
  3. 此處,將各個主成分所解釋的方差占原始數據方差的比例,來表示新的樣本空間中的降維后的數據集包含的信息,占原始數據信息量的比例;

 

  • 在庫中 PCA 算法內:在不知道到底包含多少個主成分,也就是降多少維時,可以先設定保降維后的數據集,保留了原始數據集多少比例的信息

 

  • 要求降維后的數據集包含原始數據集 95% 的信息
    pca = PCA(0.95)
    pca.fit(X_train)
    
    
    # 查看主成分個數
    pca.n_components_
    
    # 輸出:28

     

  • 降維訓練數據集和測試數據集
    X_train_reduction = pca.transform(X_train)
    X_test_reduction = pca.transform(X_test)

     

  • 用 kNN 算法對降維后的數據集進行訓練
    %%time
    
    knn_clf = KNeighborsClassifier()
    knn_clf.fit(X_train_reduction, y_train)

     

  • 查看准確度
    knn_clf.score(X_test_reduction, y_test)
    
    # 輸出:0.98

     

  • 分析:
  1. 現象:此准確度比使用權樣本數據訓練得到的數據的准確度要低一點,但是在訓練的時間上,卻少很多;
  2. 優點:模型運算速度快,效率高;
  3. 缺點:丟失了部分數據信息,降低了模型准確度;
  4. 目的:用一部分准確度換取模型運算速度;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM