對于機器學(xué)習(xí)來說运悲,數(shù)據(jù)的質(zhì)量很大程度上決定了模型的質(zhì)量,因此對于幾乎所有的情況下都需要對于數(shù)據(jù)進行預(yù)處理项钮。其中較為常用的數(shù)據(jù)處理方式是數(shù)據(jù)的標(biāo)準(zhǔn)化班眯,進一步還可以通過主成分分析 Principal components analysis, PCA,隨機投影 Random Projection 和獨立成分分析 Independent Conponent Analysis 來對數(shù)據(jù)進行降維 dimensionality reduction烁巫。
數(shù)據(jù)的標(biāo)準(zhǔn)化
數(shù)據(jù)標(biāo)準(zhǔn)化最基本目的是對于將要進行運算的數(shù)據(jù)的取值進行縮放署隘,使得數(shù)據(jù)在取值范圍上處于同一個數(shù)量級,避免在運算過程中取值較小的特征被取值較大的特征“吃掉”亚隙。但將不同特征縮放到同一數(shù)量級的前提是各自特征對于最終分類和判斷的重要性是等同的磁餐,否則就不應(yīng)該做這一處理。
常用的標(biāo)準(zhǔn)化方式:
采用最大值最小值縮放 Min-Max Scaler 的方式:x' = [ x - min(x) ] / [ max(x) - min(x) ]
采用正態(tài)分布的標(biāo)準(zhǔn)值 standard value 的方式:z = (x - x?) / σ
-采用整體縮放的方式:在圖像處理中常常將圖片數(shù)組整體除以 255 以將取值縮放在 [0, 1] 之間
這里 x 和 x'阿弃, z 代表向量诊霹,采用最大值最小縮放標(biāo)準(zhǔn)化后的取值范圍是 [0, 1]羞延,而標(biāo)準(zhǔn)值方法標(biāo)準(zhǔn)化的結(jié)果是將原本服從正態(tài)分布的元素進一步標(biāo)準(zhǔn)化成服從均值為 0, 方差為 1 的標(biāo)準(zhǔn)正態(tài)分布畅哑,且這兩種方法在使用過程中必須要注意 排除異常值 Outlier 的影響肴楷。
數(shù)據(jù)的標(biāo)準(zhǔn)化可以通過借助 skitlearn 的預(yù)處理模塊很容易的完成:
In[3]:
import numpy as np
from sklearn.preprocessing import MinMaxScaler
# sklearn prefers your inputs as float
# otherwise will pop out a warning BUT still do the calculation
weights = np.array([[115], [140], [175]])
scaler = MinMaxScaler()
rescaled_weight = scaler.fit_transform(weights)
# rescaled_weight = MinMaxScaler().fit_transform(weights)
rescaled_weight
Out[3]:
array([[ 0. ],
[ 0.41666667],
[ 1. ]])
In [4]:
from sklearn.preprocessing import StandardScaler
weights = np.array([[115], [140], [175]])
scaler = StandardScaler()
rescaled_weight = scaler.fit_transform(weights)
# rescaled_weight = StandardScaler().fit_transform(weights)
rescaled_weight
Out[4]:
array([[-1.15138528],
[-0.13545709],
[ 1.28684238]])
矩陣的數(shù)據(jù)處理
在實際的工作中,尤其是計算機視覺方面的應(yīng)用荠呐, 更多的數(shù)據(jù)是以矩陣的形式存儲的。對于一個形如 [N, D] 的矩陣 X砂客,其中 N 為樣本的數(shù)量泥张,D 為特征的數(shù)量。
特征的去均值化 Mean subtraction
對于矩陣中的每一列特征都減去相應(yīng)特征的均值鞠值,這種處理使得數(shù)據(jù)在各個特征維度上都更加趨近于中心位置媚创,使得數(shù)據(jù)更加密集。
相應(yīng)的在 Numpy 中的處理方式為:X = X - np.mean(X, axis=0)
而對于圖像矩陣來說最常做的一個數(shù)據(jù)處理是在圖像矩陣的各個通道上減去相應(yīng)通道上全部訓(xùn)練樣本的像素值的均值彤恶,例如在 VGG16 中的 Imagenet 數(shù)據(jù)前處理部分就是將輸入圖像的三個 RGB 通道上分別減去 103.939钞钙,116.779,123.68声离,后面這三個數(shù)是所有 Imagenet 中的圖片在三個通道上分別計算得到的均值芒炼。
相應(yīng)的在 Numpy 中的處理方式為:X = X - np.mean(X)
在去均值化的基礎(chǔ)上,如果有必要還可以進一步除以相應(yīng)特征的標(biāo)準(zhǔn)差使得數(shù)據(jù)進一步標(biāo)準(zhǔn)化:X /= np.std(X, axis=0)
在這里需要注意的是术徊,這里的均值和方差都是針對訓(xùn)練數(shù)據(jù)集而言的本刽,也即應(yīng)該在劃分訓(xùn)練數(shù)據(jù)集、驗證數(shù)據(jù)集和測試數(shù)據(jù)集后在訓(xùn)練數(shù)據(jù)集上進行計算赠涮,再用訓(xùn)練數(shù)據(jù)集中的均值和方差來處理驗證數(shù)據(jù)集和測試數(shù)據(jù)集子寓。
主成分分析 Principal Components Analysis
對于本部分需要的數(shù)學(xué)知識,如 基的變換笋除、本征值分解和奇異值分解斜友,協(xié)方差 請見鏈接中的筆記內(nèi)容,這里直接進入主題垃它。
之所以要做主成分分析鲜屏,是因為大多數(shù)實踐中的數(shù)據(jù)都是默認(rèn)基于自然基 naive basis 進行記錄和表示的,并且這些數(shù)據(jù)當(dāng)中通常有大量的干擾噪聲 noise 和冗余特征 redundancy嗤瞎,而尋找主成分的過程就是希望通過對于基的線性變換來找到對于被觀察的數(shù)據(jù)更簡潔的表示形式墙歪,從中提取最為重要的特征,即主成分贝奇,而忽略次要特征虹菲,這對簡化模型復(fù)雜度和提高模型的穩(wěn)健性具有重要的意義。
如果我們通過對自然基下的特征數(shù)據(jù)做線性變換后發(fā)現(xiàn)其在某一個方向上變動的離散程度很大掉瞳,也即方差最大毕源,則這個方向就可以認(rèn)為是特征取值變動的方向浪漠,通常也就是我們感興趣的方向,或者稱信號的方向霎褐,而與之垂直的方向則可以理解為噪聲的方向址愿。評估數(shù)據(jù)質(zhì)量的一個重要指標(biāo)是信噪比 Signal-to-noise-ratio, SNR,其數(shù)學(xué)定義為:
- SNR = σ2signal / σ2noise 冻璃,這個公式也隱含(一般情況下)高信號方差對應(yīng)高信噪比
除噪聲外响谓,多個特征之間很可能存在直接的相關(guān)性,也即我們只需要包含其中的部分特征就可以推導(dǎo)出其他的全部特征省艳,因此可以在數(shù)據(jù)處理的過程中去除冗余特征娘纷,借此降低特征矩陣的維數(shù),以減小模型需要處理的數(shù)據(jù)量跋炕。
在機器學(xué)習(xí)中對于被研究對象的多個特征的多次觀測的結(jié)果通常會以一個矩陣的形式表示赖晶,稱為特征矩陣,因此為了便于區(qū)分辐烂,后續(xù)涉及到利用本征值對矩陣進行的分解我都稱之為本征值分解遏插,而不是國內(nèi)很多教材上的特征分解。借由統(tǒng)計相關(guān)知識纠修,兩個特征的相關(guān)性可以通過協(xié)方差 Covariance 來衡量胳嘲,對于多個特征來說,則可以基于原有的特征矩陣構(gòu)建協(xié)方差矩陣 Covariance matrix分瘾。在協(xié)方差矩陣中胎围,對角線元素為同一個特征的方差,較大的方差值則意味著其可能是我們需要主要關(guān)注的重要變動元素德召。而非對角線元素則對應(yīng)不同的兩個特征之間的協(xié)方差白魂,較大的協(xié)方差數(shù)值意味著兩個特征之間具有較大的線性相關(guān)性,也即存在較大可能的冗余上岗。
如果期望可以最大程度的降低冗余福荸,則希望這個協(xié)方差矩陣可以通過線性變換變成一個對角矩陣。從協(xié)方差矩陣的構(gòu)建過程可以看到它是一個實對稱矩陣肴掷,而對于任意實對稱矩陣來說都可以進行本征值分解敬锐,其結(jié)果為 C = QΛQT = QΛQ-1,其中 Q 為本征向量構(gòu)成的正交矩陣 Quadrature matrix呆瞻,Λ 為本征值構(gòu)成的對角矩陣台夺。對于本征向量來說,如果一個向量是矩陣的本征向量痴脾,則其任意非零 k 倍也是本征向量颤介,這也可以理解為在本征向量的方向上可以有最多的信號聚集,在機器學(xué)習(xí)和深度學(xué)習(xí)的語境中,協(xié)方差矩陣的本征向量構(gòu)成的正交矩陣就是特征數(shù)據(jù)集的主成分 Principal components滚朵。
同時冤灾,為突出具有較大方差的特征的重要性,可以將 Λ 對角線上的元素按照從大到小的順序進行布置辕近。對本征值從大到小的排列后韵吨,為了滿足本征分解的運算條件,本征值對應(yīng)的本征向量也要在正交矩陣中保持相同的順序移宅,這也使得我們可以容易的識別出哪些本征向量的方向最為重要归粉,進而舍棄掉不重要的特征實現(xiàn)維度縮減。
對于已有的輸入特征矩陣 X吞杭,在通過本征值分解進行主元素分析時盏浇,需要采用以下幾個步驟:
對特征矩陣的每一列 x 進行去均值化得到標(biāo)準(zhǔn)化后的矩陣 X:
X -= np.mean(X, axis = 0)
-
通過計算每一列特征 x 與其他特征的協(xié)方差來構(gòu)造協(xié)方差矩陣
兩個特征向量的協(xié)方差計算公式為:Cov(x, y) = sx,y = (x - x?) ? (y - y?) / n - 1,其中 n 為每一個特征的樣本數(shù)量芽狗,n - 1 是為了實現(xiàn)誤差校正,即減少因樣本方差少于總體方差帶來的估計誤差痒蓬,并且采用代碼實現(xiàn)時分母采用向量內(nèi)積 np.dot(x - x?, y - y?)
covariance_matrix = np.dot(X.T, X) / (X.shape[0] - 1)
-
在 Numpy 中實施本征分解的方法為:
eigen_values, eigen_vectors = np.linalg.eig(convariance_matrix)
-
在本征分解后童擎,將本征值和本征向量配對,并將本征值按照從大到小的方式排列(Numpy 默認(rèn)不是按照本征值大小進行排列的):
eigen_pairs = [(np.abs(eigen_values[i]), eigen_vectors[:, i]) for i in range(len(eigen_values))]
eigen_paris.sort(reverse=True) # sort the pairs with eigen_values
此時在 Numpy 中本征向量會以行向量的方式進行存儲攻晒,構(gòu)造本征向量構(gòu)成的投影矩陣 P
用之前的特征矩陣乘以這個投影矩陣得到 Y = XPT 即為基變換后的矩陣顾复,如果只選取前 n' 行,n' ≤ n 即可以實現(xiàn)降維
Y = np.dot(X, P.T)
在 Numpy 中的 PCA 具體實現(xiàn)舉例如下:
import numpy as np
In [42]:
X = np.array([[1, 2, 3], [4, 6, 1], [6, 2, 0], [7, 3, 1]], dtype='float64')
X
Out[42]:
array([[ 1., 2., 3.],
[ 4., 6., 1.],
[ 6., 2., 0.],
[ 7., 3., 1.]])
In [44]:
X -= np.mean(X, axis=0)
X
Out[44]:
array([[-3.5 , -1.25, 1.75],
[-0.5 , 2.75, -0.25],
[ 1.5 , -1.25, -1.25],
[ 2.5 , -0.25, -0.25]])
In [45]:
cov = np.dot(X.T, X)
cov
Out[45]:
array([[ 21. , 0.5 , -8.5 ],
[ 0.5 , 10.75, -1.25],
[ -8.5 , -1.25, 4.75]])
In [46]:
eigen_values, eigen_vectors = np.linalg.eig(cov)
In [47]:
eigen_values
Out[47]:
array([ 24.6986712 , 1.02265454, 10.77867426])
In [48]:
eigen_vectors
Out[48]:
array([[ 0.91627689, 0.38756163, -0.10115656],
[ 0.06821481, 0.0978696 , 0.99285864],
[-0.39469406, 0.9166338 , -0.06323821]])
In [49]:
eigen_pairs = [(np.abs(eigen_values[i]), eigen_vectors[:, i]) for i in range(len(eigen_values))]
eigen_pairs.sort(reverse=True)
In [50]:
eigen_pairs
Out[50]:
[(24.698671197292434, array([ 0.91627689, 0.06821481, -0.39469406])),
(10.778674258520375, array([-0.10115656, 0.99285864, -0.06323821])),
(1.0226545441871755, array([ 0.38756163, 0.0978696 , 0.9166338 ]))]
In [51]:
projection = np.array([element[1] for element in eigen_pairs[:2]])
projection
Out[51]:
array([[ 0.91627689, 0.06821481, -0.39469406],
[-0.10115656, 0.99285864, -0.06323821]])
In [52]:
Y = np.dot(X, projection.T)
Y
Out[52]:
array([[-3.98295224, -0.99769222],
[-0.17187419, 2.79674909],
[ 1.78251439, -1.31376037],
[ 2.37231203, -0.4852965 ]])
由于 Numpy 中 SVD 分解后會默認(rèn)的將奇異值按照從大到小的方式進行排列鲁捏,因此上述 PCA 過程還可以利用 Numpy 的 SVD 分解來進行:
In [53]:
cov
Out[53]:
array([[ 21. , 0.5 , -8.5 ],
[ 0.5 , 10.75, -1.25],
[ -8.5 , -1.25, 4.75]])
In [54]:
U, S, V = np.linalg.svd(cov)
U
Out[54]:
array([[-0.91627689, 0.10115656, 0.38756163],
[-0.06821481, -0.99285864, 0.0978696 ],
[ 0.39469406, 0.06323821, 0.9166338 ]])
In [55]:
S
Out[55]:
array([ 24.6986712 , 10.77867426, 1.02265454])
In [56]:
V
Out[56]:
array([[-0.91627689, -0.06821481, 0.39469406],
[ 0.10115656, -0.99285864, 0.06323821],
[ 0.38756163, 0.0978696 , 0.9166338 ]])
In [57]:
Z = np.dot(X, U[:, :2])
Z
Out[57]:
array([[ 3.98295224, 0.99769222],
[ 0.17187419, -2.79674909],
[-1.78251439, 1.31376037],
[-2.37231203, 0.4852965 ]])
上述兩種方法計算得到的特征向量有一個互為相反數(shù)芯砸,是因為特征向量不唯一導(dǎo)致的。
進一步地给梅,如果輸入特征本身不可以通過自然基線性表示假丧、不服從正態(tài)分布,或者特征之間不能正交分離动羽,那么則無法有效的通過上述方法進行降維包帚。
隨機投影 Random Projection
當(dāng)發(fā)現(xiàn)數(shù)據(jù)集中的特征維數(shù)過高時,此時如果通過 PCA 來實現(xiàn)降維可能所需的計算量非常大运吓,此時可以考慮通過矩陣乘積的形式對原始數(shù)據(jù)進行隨機投影渴邦,這一投影降維的過程可以理解為一種 Embedding 實現(xiàn)。
在 Scikit-Learn 中實現(xiàn)隨機投影的代碼如下:
from sklearn import random_projection
rp = random_projection.SparseRandomProjection()
projected = rp.fit_transform(X)
獨立成分分析 ICA
PCA 通過分離出輸入數(shù)據(jù)中方差變化較大的項而實現(xiàn)降維拘哨,ICA 則從另一個角度谋梭,其認(rèn)為輸入的高維數(shù)據(jù)是由多個不同的獨立成分混合而成的,因此這一算法試圖分離出這些獨立的成分以實現(xiàn)降維倦青。
在 Scikit-Learn 中實現(xiàn) ICA 的代碼如下:
from sklearn.decomposition import FastICA
ica = FastICA(n_components=3)
components = ica.fit(X)