降維演算法 0基礎小白也能懂(附程式碼)

Mephostopheles發表於2024-09-13

降維演算法 0基礎小白也能懂(附程式碼)

原文連結

啥是降維演算法

在網際網路大資料場景下,我們經常需要面對高維資料,在對這些資料做分析和視覺化的時候,我們通常會面對「高維」這個障礙。在資料探勘和建模的過程中,高維資料也同樣帶來大的計算量,佔據更多的資源,而且許多變數之間可能存在相關性,從而增加了分析與建模的複雜性。

我們希望找到一種方法,在對資料完成降維「壓縮」的同時,儘量減少資訊損失。由於各變數之間存在一定的相關關係,因此可以考慮將關係緊密的變數變成儘可能少的新變數,使這些新變數是兩兩不相關的,那麼就可以用較少的綜合指標分別代表存在於各個變數中的各類資訊。機器學習中的降維演算法就是這樣的一類演算法。

主成分分析(Principal Components Analysis,簡稱PCA)是最重要的資料降維方法之一。在資料壓縮消除冗餘和資料噪音消除等領域都有廣泛的應用。本篇我們來展開講解一下這個演算法。

PCA與最大可分性

對於\(X= \left[ \begin{matrix} x_1 \\ x_2 \\ \vdots\\ x_n \\ \end{matrix} \right] ,X\in R^n\),我們希望\(X\)\(n\)維降到\(n^{'}\)維,同時希望資訊損失最少。比如,從\(n=2\)維降到\(n^{'}=1\)

上圖為一個典型的例子,假如我們要對一系列人的樣本進行資料降維(每個樣本包含「身高」「體重」兩個維度)。右圖我們既可以降維到第一主成分軸,也可以降維到第二主成分軸。

哪個主成分軸更優呢?從直觀感覺上,我們會認為「第一主成分軸」優於「第二主成分軸」,因為它比較大程度保留了資料之間的區分性(保留大部分資訊)。

對PCA演算法而言,我們希望找到小於原資料維度的若干個投影座標方向,把資料投影在這些方向,獲得壓縮的資訊表示。下面我們就一步一步來推導一下 PCA 演算法原理。

基變換

其實就是線性代數里面的矩陣相乘

方差

在本文的開始部分,我們提到了,降維的目的是希望壓縮資料但資訊損失最少,也就是說,我們希望投影后的資料儘可能分散開。在數學上,這種分散程度我們用「方差」來表達,方差越大,資料越分散。

設第一個特徵為\(a\),第二個特徵為\(b\),則某個樣本可以寫作\(x_i=\left[ \begin{matrix} a \\ b \\ \end{matrix} \right]\)

協方差

協方差(Covariance)在機率和統計學中用於衡量兩個變數的總體誤差。比如對於二維隨機變數 \(x_i=\left[ \begin{matrix} a \\ b \\ \end{matrix} \right]\),特徵a,b除了自身的數學期望和方差,還需要討論a,b之間互相關係的數學特徵。

協方差 \(Cov=\frac{1}{m}\sum_{i=1}^ma_ib_i\)

\(Cov=0\)時,變數a,b完全獨立,這也是我們希望達到的最佳化目標。方差是協方差的一種特殊情況,即當兩個變數是相同的情況 。

協方差矩陣

對於\(n\)維隨機變數,\(x_i=\left[ \begin{matrix} x_1 \\ x_2 \\ \vdots \\ x_n\\ \end{matrix} \right] ,C =\left[ \begin{matrix} Var(x_1) & Cov(x_1,x_2) & \cdots & Cov(x_1,x_n) \\ Cov(x_2,x_1) & Var(x_2) & \cdots & Cov(x_1,x_n) \\ \vdots & \vdots & \ddots & \vdots \\ Cov(x_n,x_1) & Cov(x_n,x_2) & \cdots & Var(x_n) \\ \end{matrix} \right] \)
我們可以看到,協方差矩陣是 n 行 n 列的對稱矩陣,主對角線上是方差,而協對角線上是協方差。

那如果有 m 個樣本的話,\(X=\left[ \begin{matrix} a_1 & a_2 &\cdots & a_m \\ b_1 & b_2 & \cdots & b_m \\ \end{matrix} \right]\)。對\(X\)做一些變換,用 \(X\) 乘以 \(X\) 的轉置,並乘上係數 \(1/m\)

\(\frac{1}{m}XX^T=\frac{1}{m} \left[ \begin{matrix} a_1 & a_2 &\cdots & a_m \\ b_1 & b_2 & \cdots & b_m \\ \end{matrix} \right]\left[ \begin{matrix} a_1 & b_1 \\ a_2 & b_2 \\ \vdots & \vdots \\ a_n & b_n \end{matrix} \right] ==\left[ \begin{matrix} \frac{1}{m}\sum_{i=1}^ma_i^2 & \frac{1}{m}\sum_{i=1}^ma_ib_i \\ \frac{1}{m}\sum_{i=1}^ma_ib_i & \frac{1}{m}\sum_{i=1}^mb_i^2 \\ \end{matrix} \right] \)
這正是協方差矩陣!

協方差矩陣對角化

再回到我們的場景和目標:

  • 現在我們有 m 個樣本資料,每個樣本有 n 個特徵,那麼設這些原始資料為 X,X 為 n 行 m 列的矩陣。

  • 想要找到一個基 P ,使 \(Y_{r\times m}=P_{r\times n}X_{n\times m}\),其中 \(r<n\),達到降維的目的

\(X\) 的協方差矩陣為 \(C\)\(Y\) 的協方差矩陣為 \(D\),且\(Y=PX\)

我們的目標變為:對原始資料X做PCA後,得到的 Y 的協方差矩陣 D 的各個方向方差最大(資料的方差表示了資料在該方向上的分散程度,也可以看作是資料中蘊含的資訊量。透過選擇方差最大的方向,我們能夠保留儘可能多的原始資料中的資訊。),協方差為 0。

那麼 C 與 D 是什麼關係呢?

\(D=\frac{1}{m}YY^T=\frac{1}{m}(PX)(PX^T)=\frac{1}{m}PXX^TP^T=PCP^T\)

到這裡就可以了,可以發現\(D\)\(C\)是透過\(P\)相聯絡的,同時呢,我們希望它是對角矩陣。這是因為對角矩陣意味著各個維度之間的協方差為 0,也就是說,新的主成分是相互獨立的。這正是 PCA 的目標:找到這樣一個變換,使得在新座標系下,各個方向上資料的方差最大且相互獨立。

之前我們說過,協方差矩陣\(C\)是一個是對稱矩陣,實對稱矩陣具有一些非常有用的性質:

正交特性:實對稱矩陣的不同特徵值對應的特徵向量必然正交。
特徵向量的線性無關性:對於具有相同特徵值的特徵向量,存在多個線性無關的特徵向量,並且這些特徵向量可以正交化。

由上面兩條可知,一個\(n\)\(n\)列的實對稱矩陣一定可以找到\(n\)個單位正交特徵向量,設這\(n\)個特徵向量為\(e_1,e_2,...,e_n\),我們將其按列組成矩陣:\(E=[e_1 e_2 ... e_n]\)

則對協方差矩陣\(C\)有如下結論:
\(E^TCE=\)\(\Lambda = \begin{pmatrix} \lambda_1 & 0 & 0 & \dots & 0 \\ 0 & \lambda_2 & 0 & \dots & 0 \\ 0 & 0 & \lambda_3 & \dots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \dots & \lambda_n \end{pmatrix}\),其對角元素為各特徵向量對應的特徵值(可能有重複)。

這裡也就是\(E^T=P\),這樣\(D\)就對角了。

PCA演算法思路整理

程式碼實現

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


# 定義PCA演算法
def PCA(X, num_components):
    # 資料中心化
    X_meaned = X - np.mean(X, axis=0)

    # 計算協方差矩陣
    covariance_matrix = np.cov(X_meaned, rowvar=False)

    # 計算協方差矩陣的特徵值和特徵向量
    eigen_values, eigen_vectors = np.linalg.eigh(covariance_matrix)

    # 按照特徵值從大到小排序
    sorted_index = np.argsort(eigen_values)[::-1]
    sorted_eigenvalue = eigen_values[sorted_index]
    sorted_eigenvectors = eigen_vectors[:, sorted_index]

    # 選擇前num_components個特徵向量
    eigenvector_subset = sorted_eigenvectors[:, 0:num_components]

    # 將資料投影到這些特徵向量上
    X_reduced = np.dot(X_meaned, eigenvector_subset)

    return X_reduced


# 生成幾個聚類的三維資料集
X, _ = make_blobs(n_samples=300, centers=3, n_features=3, cluster_std=1.0, random_state=42)

# 使用PCA將三維資料降維到二維
X_reduced = PCA(X, 2)

# 繪製三維原始資料和二維降維資料
fig = plt.figure(figsize=(12, 6))

# 三維原始資料
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(X[:, 0], X[:, 1], X[:, 2], color='blue', alpha=0.7)
ax1.set_title('Original 3D Clustered Data')
ax1.set_xlabel('X1')
ax1.set_ylabel('X2')
ax1.set_zlabel('X3')

# 二維降維資料
ax2 = fig.add_subplot(122)
ax2.scatter(X_reduced[:, 0], X_reduced[:, 1], color='red', alpha=0.7)
ax2.set_title('2D Clustered Data After PCA')
ax2.set_xlabel('PC1')
ax2.set_ylabel('PC2')

plt.tight_layout()
plt.show()

結果如下

相關文章