數據和代碼鏈接附上:https://github.com/ciecus/homeworks/tree/master/pca_svd
歡迎fork我一起學習一起進步呀
1.1 數據集—musk
Musk數據集UCI機器學習實驗室於1949年9月建立的數據集,我們採用了版本一的數據集。該數據集共有476條數據,每條數據有168個屬性,沒有缺失值,適合二元分類任務。
數據集中包含了92個分子,其中47個被認定爲musk,剩下45個被認定爲non-musk,166個特徵描述了這些分子的形狀,構象,因爲分子鍵可以旋轉,所以一個分子可能有很多不同的形狀,爲了生成這個數據集,我們生成了分子的低能構象,並且過濾了高度相似的構象,保留了476個構象,musk分子構象如圖1所示。
圖1 musk分子構象
數據集的屬性描述表1所示:
表1:musk數據集屬性描述
列數 |
屬性名 |
內容 |
1 |
molecule_name |
每個分子的符號名稱。MUSK-188, NON-MUSK-jp13 |
2 |
conformation_name |
每個構象的符號名稱。MOL_ISO+CONF |
3-165 |
"distance features" along rays |
光線的“距離特徵”, 相對於沿每條光線放置的原點進行測量的。任何對數據的實驗都應該將這些特徵值視爲處於任意連續尺度上。 |
166 |
OXY-DIS |
分子中氧原子到3-space中指定點的距離。 |
167-168 |
displacement from the designated point |
從指定點位移 |
169 |
Class |
0:non-musk,1:musk |
1.2分析方法—pca+SVD
1.2.1 pca介紹
主成分分析(principal component analysis)是一種常見的數據降維方法,其目的是在“信息”損失較小的前提下,將高維的數據轉換到低維,從而減小計算量。
降維的必要性在於可以解決以下幾個問題:1.多重共線性--預測變量之間相互關聯。多重共線性會導致解空間的不穩定,從而可能導致結果的不連貫。2.高維空間本身具有稀疏性。一維正態分佈有68%的值落於正負標準差之間,而在十維空間上只有0.02%。3.過多的變量會妨礙查找規律的建立。4.僅在變量層面上分析可能會忽略變量之間的潛在聯繫。例如幾個預測變量可能落入僅反映數據某一方面特徵的一個組內。
PCA的本質就是找一些投影方向,使得數據在這些投影方向上的方差最大,而且這些投影方向是相互正交的。
具體實踐步驟可以分爲5步:
(1)特徵中心化
在PCA中,中心化後的數據有助於後續在求協方差的步驟中減少計算量,同時中心化後的數據才能比較好地“概括”原來的數據(如圖2)
圖2 特徵中心化作用
(2)求特徵的協方差矩陣/相關係數矩陣
特徵之間度量單位不同,或者度量單位相同但是數值上差異很大,這個時候用協方差矩陣的特徵值衡量貢獻率就容易受到方差大的特徵的影響,所以這個時候用相關係數計算比較合適。相關係數矩陣就相當於特徵歸一化之後的協方差矩陣。
(3) 計算協方差矩陣、相關係數矩陣的特徵值和特徵向量,將特徵向量和特徵值根據特徵值的大小從大到小排序
(4) 計算貢獻率,選取前k個特徵值對應的特徵向量組成的特徵向量矩陣
(5) 降維後的數據就等於原始數據乘以特徵向量矩陣。
def pca(x,d):
meanValue=np.mean(x,0)
x = x-meanValue
cov_mat = np.cov(x,rowvar = 0)
eigVal, eigVec = np.linalg.eig(mat(cov_mat))
#取最大的d個特徵值
eigValSorted_indices = np.argsort(eigVal)
eigVec_d = eigVec[:,eigValSorted_indices[:-d-1:-1]] #-d-1前加:才能向左切
eigVal_d = eigVal[eigValSorted_indices[:-d-1:-1]]
contributions = round(float(sum(eigVal_d)/sum(eigVal)),2)
#print("----------------------------eig vectors----------------\n",eigVec_d)
#print("----------------------------eig values----------------\n",eigVal_d)
#print("----------------------------contributions----------------\n",contributions)
return eigVec_d,eigVal_d,contributions
1.2.2 svd介紹
奇異值分解(Singular Value Decomposition,以下簡稱SVD)是在機器學習領域廣泛應用的算法,它不光可以用於降維算法中的特徵分解,還可以用於推薦系統,以及自然語言處理等領域。是很多機器學習算法的基石。
對於任意m × n的矩陣M都可以被分解爲M = UΣV*,如圖3所示,其中U是m × m的單位正交矩陣,Σ是m × n的對角元素上是奇異值的矩陣,V是一個n × n 的單位正交矩陣,Σ對角元素上的值σi被稱作奇異值。
圖3 奇異值分解效果圖
U、Σ、V的求解方法如下:
(1)左奇異值矩陣U:是MM*的特徵向量
(2)右奇異值矩陣V:是M*M的特徵向量
(3)奇異值,V上的對角元素:是M*M或者MM*非零特徵值的平方根
#svd pca
meanValue=np.mean(data1,0)
data1 = data1-meanValue
u, sigma, v = np.linalg.svd(data1[:, :])
#svd_pca_new_data = np.dot(u[:,:13],np.diag(sigma)[:13,:13])
svd_pca_new_data = np.dot(data1,u[:,:13])
print('-------------------------------------svd pca ---------------------------------')
print(sigma[:13])
print(svd_pca_new_data)
1.2.3 pca和svd的關係
用PCA降維,需要找到樣本協方差矩陣XTX的最大的d個特徵向量,然後用這最大的d個特徵向量張成的矩陣來做低維投影降維。可以看出,在這個過程中需要先求出協方差矩陣XTX,當樣本數多樣本特徵數也多的時候,這個計算量是很大的。
SVD也可以得到協方差矩陣XTX最大的d個特徵向量張成的矩陣,但是SVD有個好處,有一些SVD的實現算法可以不求先求出協方差矩陣XTX,也能求出右奇異矩陣V。也就是說,我們的PCA算法可以不用做特徵分解,而是做SVD來完成。這個方法在樣本量很大的時候很有效。實際上,scikit-learn的PCA算法的背後真正的實現就是用的SVD,而不是我們我們認爲的暴力特徵分解。
假設我們的樣本是m×n的矩陣X,如果我們通過SVD找到了矩陣XTX最大的d個特徵向量張成的m×d維矩陣U,則我們如果進行如下處理:
xd×n'=Ud×mTXm×n
可以得到一個d×n的矩陣X’,這個矩陣和我們原來的m×n維樣本矩陣X相比,行數從m減到了d,可見對行數進行了壓縮。也就是說,左奇異矩陣可以用於行數的壓縮。相對的,右奇異矩陣可以用於列數即特徵維度的壓縮,也就是我們的PCA降維。
1.3 實驗
1.3.1 三種pca方法比較
pca的實現我們使用了三種方法:一、對協方差矩陣特徵值分解,獲取特徵值最大的特徵向量二、使用svd獲得特徵向量 三、使用sklearn中的pca方法
from sklearn.decomposition import PCA
meanValue=np.mean(data1,0)
data1 = data1-meanValue
pca_sklearn=PCA(n_components=3).fit(data1)
reducedx = pca_sklearn.fit_transform(data1)
print('-------------------------------------skleran pca ---------------------------------')
print("explained_variance_ratio:",pca_sklearn.explained_variance_ratio_)
print("singular_values",pca_sklearn.singular_values_)
print(reducedx)
- 主成分個數的選擇
我們使用自己寫的pca方法對主成分的貢獻率以及貢獻增長率進行了分析,如圖4所示,其中紅色和黑色的線是使用協方差矩陣進行分析的結果,紅色爲總貢獻率,黑線爲貢獻增長率。橘色和藍色的線是使用相關係數矩陣進行分析的結果,藍線爲總貢獻率,橘線爲貢獻增長率。我們可以發現(1)使用協方差矩陣和相關係數矩陣的差異主要體現在貢獻率上,(2)在13個主成分之後增長率幾乎爲0。此時無論是使用協方差還是相關係數的貢獻率都大於等於80%,所以我們選定用13個主成分進行後面的比較和實驗。
def pca_contribution_plt(data,max_dim):
contributions_list = []
for n in range(max_dim):
#print("the eigenvalue number is %d\n"%n)
eigVec_d,eigVal_d,contributions = pca(data,n)
contributions_list.append(contributions)
contribution_growth = [round(contributions_list[i]-contributions_list[i-1],2) for i in range(1,max_dim)]
plt.figure()
plt.plot(range(max_dim),contributions_list)
plt.plot(range(1,max_dim),contribution_growth)
plt.show()
return contributions_list,contribution_growth
def plot(data,max_dim):
contributions_list,contribution_growth = pca_contribution_plt(data,max_dim)
data_scale = preprocessing.scale(data)
contributions_list_scale,contribution_growth_scale = pca_contribution_plt(data_scale,max_dim)
return contributions_list,contribution_growth,contributions_list_scale,contribution_growth_scale
圖4 主成分的貢獻率和貢獻增長率
- 方法比較
特徵值比較:
分解協方差的特徵值 |
分解相關係數的特徵值 |
Svd的奇異值 |
Sklearn的奇異值 |
453240 144754 77810.6 62335.7 49078.1 46721.8 30678.2 23608.9 18173.2 17244.4 14178.5 12883.3 12292.7 |
51.8813 23.159 12.6725 8.55541 8.18384 6.78398 5.40026 5.0614 3.31324 2.85645 2.57682 2.38175 2.21213 |
14672.7 8292.05 6079.48 5441.46 4828.26 4710.93 3817.35 3348.77 2938.08 2862.01 2595.15 2473.77 2416.41 |
14672.7 8292.05 6079.48 5441.46 4828.26 4710.93 3817.35 3348.77 2938.08 2862.01 2595.15 2473.77 2416.41 |
主成分比較(只比較第一個實例)
分解協方差的主成分 |
分解相關係數的主成分 |
Svd的主成分 |
Sklearn的主成分 |
-118.66637432 -750.09743105 -214.33802756 -643.55969459 169.49700686 54.1440832 96.74821257 -202.7629537 -42.09157408 154.61193861 -77.11861495 25.41700138 2.28442149 |
-0.78784999 8.66447962 -2.43317735 8.10925564 0.14098592 2.40527447 3.27031211 2.0719793 1.29801728 0.19001479 -0.91969795 0.19108686 0.44034676 |
-118.66637432 750.09743105 214.33802756 643.55969459 -169.49700686 54.1440832 96.74821257 -202.7629537 -42.09157408 -154.61193861 77.11861495 -25.41700138 2.28442149 |
118.66637432 750.09743105 214.33802756 -643.55969459 169.49700686 54.1440832 -96.74821257 -202.7629537 -42.09157408 154.61193861 -77.11861495 25.41700138 -2.28442149 |
可以發現雖然特徵值不同,但是最原始矩陣的作用效果相同,提取的主成分也相同。
1.4 降維數據可視化
#visualize 2d
def visualize_plot_2D(reduced_x,y):
red_x,red_y=[],[]
blue_x,blue_y=[],[]
for i in range(len(reduced_x)):
if y[i] ==0:
red_x.append(reduced_x[i][0])
red_y.append(reduced_x[i][1])
else:
blue_x.append(reduced_x[i][0])
blue_y.append(reduced_x[i][1])
plt.scatter(red_x,red_y,c='r',marker='x')
plt.scatter(blue_x,blue_y,c='b',marker='D')
plt.title('pca visualize 2d')
plt.show()
visualize_plot_2D(svd_pca_new_data,target1)
#visualize 3d
def visualize_plot_3D(reduced_x,y):
red_x,red_y,red_z=[],[],[]
blue_x,blue_y,blue_z=[],[],[]
for i in range(len(reduced_x)):
if y[i] ==0:
red_x.append(reduced_x[i][0])
red_y.append(reduced_x[i][1])
red_z.append(reduced_x[i][2])
else:
blue_x.append(reduced_x[i][0])
blue_y.append(reduced_x[i][1])
blue_z.append(reduced_x[i][2])
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111, projection='3d')
plt.rcParams['legend.fontsize'] = 10
ax.plot(red_x,red_y,red_z, 'o', markersize=8, color='blue', alpha=0.5, label='class1')
ax.plot(blue_x,blue_y,blue_z, '^', markersize=8, alpha=0.5, color='red', label='class2')
plt.title('pca visualize 3d')
ax.legend(loc='upper right')
plt.show()
visualize_plot_3D(svd_pca_new_data,target1)
圖5 降維之後的二維和三維可視化效果
由之前實驗可知,兩個主成分和三個主成分包含了不到50%的信息,所以不能很好的分類。
1.5 降維數據分類分析
使用logistic regression對降維後的數據進行分類預測,採用5折交叉驗證,評估指標爲精準度,召回率和準確率。
PRECISION= TPTP+FP
RECALL= TPTP+FN
ACCURACY= TP+TNTP+TN+FP+FN
|
|
1 |
2 |
3 |
4 |
5 |
平均 |
訓練集 |
Precison |
71.13% |
70% |
79.26% |
82.21% |
78.57% |
76.23% |
Recall |
62.16% |
56.25% |
78.01% |
82.61% |
79.71% |
71.75% |
|
Accuracy |
81.58% |
80.05% |
78.84% |
80.84% |
77.17% |
79.70% |
|
測試集 |
Precision |
100% |
100% |
36.36% |
0 |
0 |
47.27% |
Recall |
46.88% |
54.74% |
100% |
0 |
0 |
40.32% |
|
Accuary |
46.88% |
54.74% |
70.53% |
54.74% |
76.84% |
60.75% |
import tensorflow as tf
from sklearn.model_selection import KFold
import pandas as pd
def softmax_function(x_train,y_train,x_test,y_test):
loss_list = []
x = tf.placeholder(tf.float32,shape = (None,13))
y = tf.placeholder(tf.float32,shape = (None,2)) #predict
#用softmax 構建模型
w = tf.Variable(tf.zeros([13,2]))
b = tf.Variable(tf.zeros([2]))
pred = tf.nn.softmax(tf.matmul(x,w)+b)
#損失函數(交叉熵)
with tf.name_scope('loss'):
loss = tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred),1))
tf.summary.scalar('loss',loss)
#梯度下降
optimizer = tf.train.GradientDescentOptimizer(0.05).minimize(loss)
#準確率
accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)), tf.float32))
#加載session 圖
with tf.Session() as sess:
#初始化所有變量
init = tf.global_variables_initializer()
sess.run(init)
#開始訓練
for epoch in range(10000):
sess.run(optimizer,feed_dict={x:x_train,y:y_train})
loss_list.append(sess.run(loss,feed_dict={x:x_train,y:y_train}))
print('train accuracy:',sess.run(accuracy,feed_dict={x:x_train,y:y_train}))
print('test accuracy:',sess.run(accuracy,feed_dict={x:x_test,y:y_test}))
plt.figure()
plt.plot(range(10000),loss_list)
X = svd_pca_new_data
Y = y1
KF=KFold(n_splits=5)
i = 1
for train_index,test_index in KF.split(X):
print('for %d fold'%i)
i+=1
X_train,X_test=X[train_index],X[test_index]
Y_train,Y_test=Y[train_index],Y[test_index]
softmax_function(X_train,Y_train,X_test,Y_test)
1.6 總結
svd是一種矩陣分解的方法,在pca降維的時候使用svd可以起到更好的效果。