在寫《特徵學習:學習之海中的遺珠》過程中,發現內容太多,已經超過單篇博客的承載能力,另外裏面幾個內容都有各自己額外的主題。因此,本人將《特徵學習:學習之海中的遺珠》第二節(介紹幾個經典的特徵學習方法)中能夠獨立成篇的3個小節單獨提出形成三篇對應的博客。本篇對應第1小節:從主成分分析到發育網絡的核心算法。
文章目錄
1.寫在前面
PCA可以用來解決的問題【Andrew Ng曾在講PCA時提到過】:
1)減少數據因爲存儲而造成的內存和硬盤的佔用;
2)加速訓練過程;
3)高維數據可視化。
值得注意的是: PCA
並沒有把握
一定能提高後續機器學習任務的效果,也沒有把握
能夠防止過擬合問題。
主成分分析(Principal Component Analysis,簡稱PCA) 提供了一種用低維數據來表示高維複雜數據最主要特徵的途徑。PCA的思想是將維特徵映射到維空間上,這維特徵是全新的正交特徵,最新構造出來的維特徵,而不是簡單地從維特徵中去除其餘維特徵。博客主成分分析|PCA算法大全羅列了大部分現有的PCA類算法,包括:針對 非線性數據的KPCA,針對二維圖片數據的2DPCA/2D2DPCA以及BDPCA等。此處只介紹原始PCA算法以及它的一個增量式版本——CCIPCA,然後介紹翁巨揚教授提出的發育網絡(Developmental Network,簡稱DN) 中的核心算法增量式葉成分分析(CCILCA)。各部分都可展開成原理、算法推導、示例代碼、結果分析四個部分。
本文並非單純爲了羅列方法,驅動我總結這篇博客的原因是主成分分析與發育網絡的關係。CCILCA作爲發育網絡的核心算法,根據新到來的數據對網絡的節點進行更新,其中每個節點的更新算法就是一個CCIPCA。也就是說發育網絡的核心原理之一竟然是主成分分析(另外一個是赫布學習理論)。CCIPCA與CCILCA的關係會在第四部分進行時詳細介紹。
2. 主成分分析(PCA)算法與應用
2.1 原理
PCA可以被定義爲數據在低維線性空間上的正交投影,這個線性空間被稱爲主子空間(principal subspace),使得投影數據的方差被最大化(Hotelling, 1933),即最大方差理論。等價地,它也可以被定義爲使得平均投影代價最小的線性投影,即最小誤差理論。平均投影代價是指數據點和它們的投影之間的平均平方距離(Pearson, 1901)。從最大方差理論與最小誤差理論來推導PCA,最終的結果是一樣的。對於數據集,我們以最大方差理論爲例來推導PCA:
在信號處理中,認爲信號具有較大的方差,噪聲有較小的方差,信噪比就是信號與噪聲的方差比,越大越好。因此我們認爲,最好的k維特徵是將m維樣本點變換爲k後,每一維上的樣本方差都儘可能的大。
2.2 算法推導
首先,考慮在一維空間上的投影。我們可以使用維向量定義這個空間的方向。爲了方便並不失一般性,我們假定選擇一個單位向量,從而。
(假設數據是零均值化後的)
如上圖所示,紅色點表示原樣本點,是藍色直線的斜率也是直線的方向向量,而且是單位向量,直線上藍色點表示原樣本點在上的投影。容易知道投影點離原點的距離是,由於這些原始樣本點的每一維特徵均值都爲0,因此投影到上的樣本點的均值仍然是0。
假設原始數據集爲,我們的目標是找到最佳的投影空間,其中是單位向量且是單位向量且與正交,何爲最佳的?就是原始樣本點投影到上之後,使得投影后的樣本點方差最大。
我們先將原始數據集零均值化爲,其中,。
由於投影后均值爲,因此數據集沿某一單位向量投影后的總方差爲:
其中就是原始數據集的協方差矩陣(因爲無偏估計的原因,一般協方差矩陣除以,這是用)。
其中,。
上式兩邊同時左乘,注意到(單位向量),則有
所以是矩陣的特徵值所對應的特徵向量。
欲使投影后的總方差最大,即最大, 可知最佳的投影向量是特徵值最大時對應的特徵向量,因此,當我們將設置爲與具有最大的特徵值向量相等時,方差會達到最大值。這個特徵向量被稱爲第一主成分。
PCA算法如下圖所示:
2.3 示例程序
from sklearn.decomposition import PCA
from sklearn.datasets import load_digits #8x8的手寫數字數據
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
import time
X, y = load_digits(return_X_y=True)
print('X shape:', X.shape) # 此處會看到X是64維的數據
X_train, x_test, y_train, y_test = train_test_split(X, y)
tic = time.time()
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train, y_train)
print (knn_clf.score(x_test, y_test))
toc = time.time()
print ('Time of method[exec_without_pca] costs:'+str(toc-tic))
print ('----' * 10)
tic = time.time()
knn_clf = KNeighborsClassifier()
pca = PCA(n_components=0.95) # 重構閾值爲95%
pca.fit(X_train, y_train)
X_train_dunction = pca.transform(X_train)
X_test_dunction = pca.transform(x_test)
knn_clf.fit(X_train_dunction, y_train)
print (knn_clf.score(X_test_dunction, y_test))
toc = time.time()
print ('Time of method[exec_with_pca] costs:'+str(toc-tic))
import matplotlib.pyplot as plt
%matplotlib inline
def draw_graph():
pca = PCA(n_components=2)
pca.fit(X)
X_reduction = pca.transform(X)
for i in range(10):
plt.scatter(X_reduction[y==i,0], X_reduction[y==i,1], alpha=0.8, label='%s' % i)
plt.legend()
plt.show()
輸出:
降維數據可視化結果:’
其它數據顯示:
經過PCA降維後,發現降到28維(原始維數爲64維)後,就能達到95%數據重構精度。
>>print(len(pca.components_))
28
>>print(pca.explained_variance_ratio_)
[0.14797442 0.13659413 0.11802823 0.08412275 0.05932165 0.05048908
0.04237307 0.0355668 0.0334889 0.03029803 0.02443289 0.02229051
0.01804256 0.01782603 0.01516046 0.01396237 0.013309 0.01217414
0.01043489 0.00897301 0.00880779 0.00771099 0.00760128 0.00726781
0.00689468 0.00609627 0.00579926 0.00505733]
將這前28特徵向量打印出來:
A = pca.components_
print(pca.explained_variance_ratio_)
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure(figsize=(12, 8))
for i in range(len(A)):
fig.add_subplot(4, 7, i+1)
plt.title(i+1)
plt.imshow(A[i].reshape((8,8)))
plt.xticks([])
plt.yticks([])
plt.axis("off")
plt.show()
2.4 結果分析
以上示例與結果表明,數據經PCA降維後的準確率沒有明顯提高。但是經過PCA降維後,用於訓練的數據維度變少了,因此計算成本明顯降低了。並且,在原始高維空間中揉雜在一起,不可分的數據,利用PCA降維的低維空間中,數據也還是揉雜在一起的。
3 增量式PCA(CCIPCA)算法與應用
candid covariance-free incremental principle component analysis直接協方差無關的增量式主成分分析,簡稱CCIPCA
考慮到很多實際應用無法一次性收集足夠的訓練數據,更普遍的情況是:數據是隨着時間不斷產生的,按序列依次輸入到學習系統中。要應對這種情況,算法必需能夠具備僅僅依靠已經學到的知識與新接受到的數據進行增量式的學習。明顯地,原始PCA算法無法應對這種情況,本節介紹的CCIPCA是對原始PCA的改進,它能夠只依賴當前的新數據進行學習降維模型。
3.1 算法推導
上一節根據最大方差理論推導PCA過程中,可得數據集沿某一單位向量投影后的總方差爲:
其中就是原始數據集的協方差矩陣(因爲的均值爲,因爲無偏估計的原因,一般協方差矩陣除以,這是用n)。
第個特徵值和特徵向量的計算公式爲,其中爲第輸入時待求的第個特徵向量,爲對應的特徵值 。CCIPCA算法爲了加快迭代的速度,整個迭代是對特徵值和特徵向量的乘積進行的,設第個輸入時有
把(1)式代入(2)式,可得
若通過迭代獲得特徵值和特徵向量的乘積,因特徵向量是歸一的,只要對(2)式求模(內積,開根),可求得。
迭代採用(3)式,把近似爲代入(3)式,經變換可得CCIPCA的基本迭代公式:
其中,爲上一步的迭代值 的權值,第2項的相當於迭代的調整步長。
作爲第個新輸入數據對迭代向量的調整,在迭代中逐步收斂到所求的第個特徵向量。
對不同序號的向量,都可以用(4)式迭代,只是輸入的向量不同。求最大的特徵值對應的特徵向量時,爲機器人採到的第個均值化的數據。在求第2,第3乃至更後面的特徵向量時,須作以下處理:
已經通過迭代得到第1個特徵向量,先設,並把投影到上一個已經求到的特徵向量上(現爲第1個特徵向量),求出殘差數據,如下式表示:
作爲求第2個特徵向量的輸入,類似的可求出第3,第4,…個特徵向量。因殘差數據和上1個特徵向量所恢復的數據正交,從而可求出所有相互正交的特徵向量。另外,每輸入1個新的數據時,均值也要更新,對(1)式,輸入第個數據時的均值 採用如下迭代式,
注意:最終的特徵向量.
CCIPCA算法如下圖所示:
3.2 示例程序
# CCIPCA
import numpy as np
import time
from sklearn.datasets import load_digits
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
X, y = load_digits(return_X_y=True)
k = 28
V = np.zeros((k, 64))
n = 1
mt = 0.1*np.random.randn(64)
tic = time.time()
for j in range(len(X)):
u = X[j]
if n == 1: mt = mt + u
else: mt = float(n-1)*mt/n + u/float(n)
u1 = u-mt
for i in range(min(k, n)):
if (i+1) == n: V[i,:] = u1
else:
V[i,:] = (n-1)*V[i,:]/float(n)+(np.linalg.norm(u1)**2)*V[i,:]/(n*np.linalg.norm(V[i,:]))
u1 = u1 - np.sum(u1*V[i,:].reshape(-1))*V[i,:]/(np.linalg.norm(V[i,:])**2)
n = n+1
toc = time.time()
W = V/np.linalg.norm(V, axis=1, keepdims=True)
X = X-mt
X_train, X_test, y_train, y_test = train_test_split(X, y)
knn_clf = KNeighborsClassifier()
X_train_dunction = np.dot(V, X_train.T).T
X_test_dunction = np.dot(V, X_test.T).T
knn_clf.fit(X_train_dunction, y_train)
print (knn_clf.score(X_test_dunction, y_test))
print("Time of method CCIPCA costs : "+str(toc-tic))
print ('----' * 10)
tic = time.time()
knn_clf = KNeighborsClassifier()
pca = PCA(n_components=0.95)
pca.fit(X_train, y_train)
X_train_dunction = pca.transform(X_train)
X_test_dunction = pca.transform(X_test)
knn_clf.fit(X_train_dunction, y_train)
print (knn_clf.score(X_test_dunction, y_test))
toc = time.time()
print ('Time of method PCA costs:'+str(toc-tic))
特徵向量可視化:
經過CCIPCA降維後,28個特徵向量如下所示。
3.3 結果分析
以上示例與結果表明,增量式PCA(CCIPCA)算法的恢復精度略低於原始的PCA算法。對於同樣數量的樣本,CCIPCA的總計算時間成本明顯高於原始PCA。但是,PCA的時間成本約爲CCIPCA的每一步更新時間成本的50倍。另一方面,CCIPCA解決的一個最重要的問題,就是使學習系統能夠僅依賴依次到來的新數據,學習特徵向量。CCIPCA隨着數據的不斷加入,降維模型也不斷更新。
CCIPCA還有一個好處【也是增量式學習的好處】就是:它的降維模型的訓練與利用是可以並存的,這使得CCIPCA可以作爲學習系統的終身學習算法。
(原始PCA需要在訓練結束後,才能使用學習到的降維模型。)
4. 發育網絡的核心算法——CCILCA
Candid Covariance-Free Incremental Principal Component Analysis 直接協方差無關的增量式,簡稱CCILCA
4.1 葉成分
在給定神經元個數,葉成分分析(LCA)將樣本空間劃分成個不重疊的區域,我們稱其爲葉子區域:
其中,如果,則有,具體葉成分可參照下圖。
每一個葉區域可以由一個單獨的特徵向量表示,被稱爲葉成分。這些葉成分不需要正交,也不需要線性獨立。它們撐起一個葉特徵子空間
通常情況下,子空間的維數可以比原始輸入空間的維數要低。
4.2 算法推導
給定狀態空間的數據集,我們可以通過
將劃分到各葉區域中,得到與各葉區域相關的子數據集。
那麼在已知,我們怎麼求的最優特徵向量呢?
該問題可轉變成最大化期望
其中,爲子數據集的總協方差,爲中元素總個數。
根據PCA的的基本理論,可知上式中取與最大特徵值對應的特徵相向量爲最優特徵向量。
因此,針對每個葉區域,特徵向量的核心迭代更式與CCIPCA相同,如下圖所示。
由於每個葉區域只需要維護一個最優的,可以看作是CCIPCA(維護個特徵向量)的特殊形式。【此處結論:CCILCA算法的基礎元件是CCIPCA算法。
】
其中,爲第個輸入對第個葉區域的響應值,該值度量當前狀態與該葉區域的相似性,相似性越高表明屬於的可能性越大。
其於以上分析可總結CCILCA的算法如下:
CCILCA算法
4.3 例子程序
# CCILCA
import numpy as np
from sklearn.datasets import load_digits
X, _ = load_digits(return_X_y=True)
c = 10 #feature nodes numbers
k = 1 # topk
V = np.zeros((c, 64))
n = np.ones(c)
for i in range(c):
index = np.random.randint(len(X))
V[i] = X[index]
def topk(array, k):
k_index = np.argpartition(-array, k-1)[0:k+1]
k_array = array[k_index]
ynorm = [(k_array[i]-k_array[-1])/(k_array[0]-k_array[-1]) for i in range(k)]
return k_array, k_index, ynorm
for j in range(1, len(X)):
index = np.random.randint(len(X))
xt = X[index]
Vnorm = np.linalg.norm(V, axis=1)
Y = np.array([np.sum(xt*V[a])/Vnorm[a] for a in range(c)])
k_array, k_index, ynorm = topk(Y, k)
for i in range(k):
n[k_index[i]] += ynorm[i]
V[k_index[i],:] = (n[k_index[i]]-1)*V[k_index[i],:]/float(n[k_index[i]])+k_array[i]*xt/float(n[k_index[i]])
特徵向量可視化:
經過CCILCA學習後,10個特徵向量如下所示(題圖標號只是特徵向量的序號而已,並不是類標)。
將學習到的特徵向量應用於分類問題
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
knn_clf = KNeighborsClassifier()
emdX = np.dot(X, V.T) # 利用特徵向量組成的轉換矩陣將數據轉換到特徵空間
X_train, X_test, y_train, y_test = train_test_split(emdX, y)
knn_clf.fit(X_train, y_train)
print (knn_clf.score(X_test, y_test))
4.4 結果分析
以上示例與結果表明,增量式葉成分分析(CCILCA)算法可以學習得到一個聚類模型,每個聚類中心可用相應的特徵向量表示。CCILCA也可以作爲降維算法使用,上面我們利用學習到的特徵向量將數據轉換到特徵空間後,再學習分類任務。在測試數據集上的準確率爲88.6%,不是很高(畢竟從64維降到了10維)。
另外,CCILCA作爲發育網絡的核心算法是用於整個網絡中的節點更新的全局算法,而CCIPCA則是單個節點的更新算法。這一節主要是介紹了發育網絡的核心算法CCILCA,本節可以作爲【博客】發育網絡(DN): 一個涌現的圖靈機的補充內容。
總結
主成分分析作爲最經典的特徵學習算法之一,已經被廣泛應用於機器學習的數據預處理中。但是,主成分分析僅利用了輸入狀態空間的信息對特徵進行學習,它只能保證利用剩餘的k維特徵還原原始狀態數據的準確率。 由於PCA完全忽略標籤、有監督信號的信息,因此:PCA並沒有把握
一定能提高後續機器學習任務的效果,也沒有把握
能夠防止過擬合問題。
實際數據中,各維度可能具有物理意義,也可能沒有物理意義,這些維度上的數據尺度有可能相差非常大,加上每個維度對最終的學習結論(分類結果、迴歸值)的重要性也不是相等的。有些維度的數值比其他維度的數值小太多,但是該維度可能與結論有很大的關聯。這種數值幅值比其他維度數值小太多的維度往往會被PCA當作噪聲捨棄。因此,PCA可能一不小心就幫我們敗掉了數據90%的信息。
對於聚類任務,或者只有狀態數據,而無標籤信息可用的任務時,預處理(只能)上各種PCA就行了。對於分類或迴歸任務,如果輸入狀態空間的各維度是經過專業設計考量的,那麼(懶的話)PCA也能用。不過,我們放棄的那些信息,肯定會影響最終的學習效果。
對於輸入狀態空間各維度的設計不是那麼講究(又或者說,很難通過主觀經驗設計好)的分類與迴歸任務,我們怎麼辦呢?我們怎麼將標籤信息加入到特徵學習中?特徵學習可以學出狀態各維度的重要性嗎?答案是肯定的。後續我將繼續總結特徵學習相關的研究。
參考文獻
[1] Weng J , Zhang Y , Hwang W S . Candid covariance-free incremental principal component analysis[J]. IEEE Transactions on Pattern Analysis and Machine Intelligence, 2003, 25(8):0-1040.
[2] Weng J , Luciw M . Dually Optimal Neuronal Layers: Lobe Component Analysis[J]. IEEE Transactions on Autonomous Mental Development, 2009, 1(1):68-85.