一. 原理
經過上一節,我們有如下結論。
1. 目標:求w,使得f(X)最大。其中:
2. 梯度可表示爲:
即:
對其進行向量化處理:
也可寫成:
故將梯度向量化結果爲:
二. 實現
1. 生成數據集
(1)導入需要的模塊和包
import numpy as np
import matplotlib.pyplot as plt
(2)生成虛擬的測試用例。生成的X有兩個特徵,特徵1爲1~100之間隨機分佈的100個樣本,特徵2和相應的特徵1有基本的線性關係0.75 * 特徵1 + 3 + 噪音。(線性關係時降維效果比較明顯)
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)
查看圖像:
2. demean過程
(1)demean函數:
def demean(X):
# np.mean(X, axis=0):對X矩陣在行方向上求得均值,最終結果爲每一列的均值。
# 若X爲m * n,則操作後得到1 * n的向量
return X - np.mean(X, axis=0)
(2)測試一下函數:
# 對X矩陣數據均值歸零操作
X_demean = demean(X)
查看圖像,發現具體繪製數據情況和原先X大多一致。觀察座標軸,中心位置大概爲(0, 0)。
驗證:查看X和demean後的X在兩個維度上的平均值,發現demean後的平均值都接近於0。
np.mean(X_demean[:, 0]), np.mean(X_demean[:, 1])
3. 使用梯度上升法求解數據的主成分
① 相關函數的實現:
(1)根據目標函數:
計算目標函數的方法f(w, X):
# X爲歸零化的矩陣
def f(w, X):
return np.sum((X.dot(w) ** 2)) / len(X)
(2)根據目標函數對應梯度的計算公式:
計算目標函數對應的梯度的方法df_math(w, X):
def df_math(w, X):
return X.T.dot(X.dot(w)) * 2. / len(X)
(3)驗證梯度計算是否正確的函數df_debug(w, X, epsilon):
def df_debug(w, X, epsilon=0.0001):
res = np.empty(len(w))
for i in range(len(w)):
w_1 = w.copy()
w_1[i] += epsilon
w_2 = w.copy()
w_2[i] -= epsilon
res[i] = (f(w_1, X) - f(w_2, X)) / (2 * epsilon)
return res
(4)單位向量化函數direction(w)(公式爲:向量 / 模長):
# 計算單位向量:w / |w|
def direction(w):
return w / np.linalg.norm(w)
(5)梯度上升函數 gradient_ascent(注意先將w變成單位向量):
# 梯度上升法過程,代碼結構與梯度下降法完全一樣
# 注意:w爲單位向量
def gradient_ascent(df, X, inital_w, eta, n_iters = 1e4, epsilon=1e-8):
w = direction(inital_w)
cur_iter = 0
while cur_iter < n_iters:
gradient = df(w, X)
last_w = w
w = w + eta * gradient
# 注意:每次求單位向量
w = direction(w)
if (abs(f(w, X) - f(last_w, X)) < epsilon):
break
cur_iter += 1
return w
注意:對於PCA問題對樣本數據標準化歸一化過程不能使用StandardScaler!!!
爲什麼?由於PCA過程本身就是要求一個軸使得所有的樣本映射到那軸之後樣本的方差最大,但是一旦我們將我們樣本數據進行標準化,樣本的方差就變成1,方差的最大值就不存在了。這樣一來就求不出PCA真正想最大化的結果了。
實際上demean過程已經做了一半,將均值變爲0了,少做的一半就是不讓我們數據的標準差爲1。
② 調用梯度上升法:
(1)初始化w值:不能爲零向量。
# 調用梯度上升法
# 初始化w值。注意:不能爲零向量,否則帶入都爲0
init_w = np.random.random(X.shape[1]) # 個數爲樣本特徵數
init_w
# array([0.51062981, 0.24947237])
(2)分別使用測試和計算的方法,得出的w向量相同。
eta = 0.001
# 梯度上升法求得的原來的數據將它映射到此軸上方差最大
# 使用測試的方法
gradient_ascent(df_debug, X_demean, init_w, eta)
# array([0.76696199, 0.64169254])
# 使用計算的方法
w = gradient_ascent(df_math, X_demean, init_w, eta)
w
# array([0.76696199, 0.64169254])
(3)繪製出圖像對w進行觀察:
plt.scatter(X_demean[:, 0], X_demean[:, 1])
# 乘以30是以便觀察, 因爲方向向量非常小
plt.plot([0, w[0] * 30], [0, w[1] * 30], color='r')
plt.show()
繪製出的紅線即爲樣本要映射到的方向向量,使得樣本間方差最大。相應的軸即爲一個主成分,又稱爲第一主成分。