python學習筆記-目錄索引html
第5章 展現了不少降維的技巧,從最知名的主成分分析出發,經由其核版本與隨機化版本,一直講到線性判別分析。python
本章會介紹一些下降數據維度的技術。將學習如下主題:
·建立三維散點圖,顯示主成分
·使用核PCA降維
·用主成分分析找到關鍵因素
·使用隨機PCA在數據中尋找主成分
·使用線性判別分析提取有用的維度
·用kNN分類模型給電話分類時使用多種降維技巧
5.1導論
現在數據的冗餘很惱人:數據集的增加不只體如今更多的觀測值上,還體如今更豐富的元數據上。
本章將展示一系列技巧,幫助你從數據中提取最重要的特徵,並用於建模。建模時使用主要成分替代原始特徵的缺點是,解釋清楚模型的係數——即理解預測或分類的驅動因素——幾乎是不可能的。
若是你的目標是作一個高準確度的預測者,或者項目的焦點不是理解驅動因素,那麼本章接下來要介紹的方法多是你感興趣的。web
5.2建立三維散點圖,顯示主成分
主成分不過是能夠用來轉換數據的多維向量。經過找到數據的主要維度,能夠發現數據的另外一番風景。
準備:須要帶MPL工具包的Matplotlib。若是你安裝的是Anaconda發行版Python,這些應該已經裝好了。
使用.plot_components(...)方法繪製三維數據(helper.py文件):算法
1 def plot_components(z, y, color_marker, **f_params): 2 ''' 3 Produce and save the chart presenting 3 principal 4 components 5 ''' 6 # import necessary modules 7 import matplotlib.pyplot as plt 8 from mpl_toolkits.mplot3d import Axes3D 9 10 # convert the dependent into a Numpy array 11 # this is done so z and y are in the same format 12 y_np = y 13 14 # do it only, however, if y is not NumPy array 15 if type(y_np) != np.array: 16 y_np = np.array(y_np) 17 18 # create a figure 19 fig = plt.figure() 20 ax = fig.add_subplot(111, projection='3d') 21 22 # plot the dots 23 for i, j in enumerate(np.unique(y_np)): 24 ax.scatter( 25 z[y_np == j, 0], 26 z[y_np == j, 1], 27 z[y_np == j, 2], 28 c=color_marker[i][0], 29 marker=color_marker[i][1]) 30 31 ax.set_xlabel('First component') 32 ax.set_ylabel('Second component') 33 ax.set_zlabel('Third component') 34 35 # save the figure 36 plt.savefig(**f_params)
原理:plot_components(...)方法接受多個參數。z參數是轉換到三維空間的數據集(即,只有最上面三個主成分)。y參數是類別的向量:咱們用的是和前兩章相同的數據集(限定了特徵數),因此這就是表明着銀行職員這通電話是否帶來了消費者的信用卡業務。color_marker是一系列「(顏色,標記)」形式構成的元組,每一個元素的第一個位置是顏色,第二個位置是標記,例如,('red','o');在這個例子中,用紅色畫小圈圈。
最後一個參數在本書中只是簡單涉及:**f_params。**f_params參數(或者更常見的**kwargs)能讓你向函數傳入可變數量的關鍵詞參數。例如,假設有個方法:
像下面這樣調用方法,會有不一樣的輸出:
也能夠經過*args參數傳入可變長度的非關鍵詞參數。
在plot_components(...)方法中,先導入須要的模塊:pyplot和mplot3d中的Axes3D;後者容許咱們生成三維圖形。而後,若是y向量不是NumPy數組,能夠將其轉變成NumPy數組。
接下來,開始繪圖。第一步,建立一個圖,並調用add_subplot(...)。111意味着你將圖線加到第一行(第一個1)和第一列(第二個1),而且它是圖表第一個也是惟一一個圖線(第三個1)。
傳入211會往第一行第一列加入一個子圖,而212會往一樣的圖表上加第二個子圖。換句話說,第一個數字是說圖表中有多少行,第二個數字是說有多少列,最後一個數字是放子圖的單元;單元是從左往右從上往下數的。
projection參數告訴add_subplot(...)咱們要傳三維的數據。
for循環遍歷y中的類,給圖線加上散點。對每一個類,選取三個主成分;你應該已經熟悉z[y_np==j,0]這種寫法,只選取z當中其一樣索引在y_np中對應的元素與類標籤j相等的元素,而且只選取第一列(數組中索引爲0)。
散點的c參數定義了用來畫這些點的顏色,marker決定了用什麼樣的標記。
查閱http://matplotlib.org/api/markers_api.html,看看你能用在圖表上的各類標記。
顧名思義,set_{x,y,z}label(...)方法會給圖表的座標軸加上描述。
最後,使用Matplotlib的.savefig(...)方法繪圖。至少要傳入文件名參數。因爲要保存爲PNG格式,所以也要指定dpi(Dots Per Inch)參數爲300,因此若是你想的話,能夠輸出這個圖表;若是用於Web展現,100 dpi就足夠了(有人用的是72 dpi)。
結果相似這樣:
5.3使用核PCA降維
PCA(Principal Components Analysis,主成分分析)將相關聯的一組變量轉換爲主成分:線性不相關的(正交的)變量。PCA能夠產生和變量同樣多的主成分,不過一般來說仍是會下降數據的維度。第一個主成分選用的是數據中對方差貢獻最大的特徵,接下來的主成分是以對方差貢獻降序排列的,而且保持主成分之間是正交的(無關的)。
準備:需安裝好pandas、NumPy和MLPY。繪圖時須要MPL工具包中的Matplotlib。
相似以前的技巧,咱們將構建模型的程序包了一層,這樣能夠用timeit裝飾器給它計時(reduce_pca.py文件):編程
1 def reduce_PCA(x): 2 ''' 3 Reduce the dimensions using Principal Component 4 Analysis 5 使用主成分分析降維 6 ''' 7 # create the PCA object建立PCA對象 8 pca = ml.PCA(whiten=True) 9 10 # learn the principal components from all the features 11 #從全部的特徵中學習主成分 12 pca.learn(x) 13 14 # return the object返回3個主成分 15 return pca
原理:首先,讀入數據。和其餘技巧同樣,使用pandas的DataFrame裝載數據:api
# the file name of the dataset 數據集結文件名 r_filename = '../../Data/Chapter05/bank_contacts.csv' # read the data csv_read = pd.read_csv(r_filename)
而後從csv_read對象中提取出自變量和因變量,在PCA中只使用自變量:數組
# split into independent and dependent features x = csv_read[csv_read.columns[:-1]] y = csv_read[csv_read.columns[-1]]
最後,調用reduce_PCA(...)方法。只須要一個參數x:app
# reduce the dimensionality z = reduce_PCA(x)
在這個方法中,首先建立了PCA(...)對象。whiten參數設爲True,使得PCA(...)對象縮放數據,使得每一個特徵的標準差都是1。
前一章介紹了漂洗。參考本書4.3節。
也能夠指定MLPY中實現的PCA(...)算法要使用那種方法:'svd'仍是'cov'。
若是你傾向數學上的解釋,好奇PCA(以及它與奇異值分解的關係),推薦你閱讀這篇論文:https://www.cs.princeton.edu/picasso/mats/PCA-Tutorial-Intuition_jp.pdf。
而後,讓pca對象從x數據中學習(.learn(...))主成分。在個人機器上,這真的很快,(針對這個數據集)一般花0.26s左右:
注意,數據集越大(更多的行,更多的列),這個過程越慢。若是真的太慢了,MLPY提供了另外一個尋找主成分的方法:FastPCA(...)。參考:http://mlpy.sourceforge.net/docs/3.5/dim_red.html#fast-principal-component-analysis-pcafast。本章後續會介紹隨機化PCA方法的。
最後,返回pca,把它保存在z對象中。
如今是畫出成分的時候了:dom
# plot and save the chart # to vary the colors and markers for the points #使用不一樣的顏色和標記繪圖並保存 color_marker = [('r','o'),('g','.')] file_save_params = { 'filename': '../../Data/Chapter05/charts/pca_3d.png', 'dpi': 300 }
/* The method reduce_PCA took 0.75 sec to run. Traceback (most recent call last): File "D:\Java2018\practicalDataAnalysis\Codes\Chapter05\reduce_pca.py", line 52, in <module> color_marker, **file_save_params) File "D:\Java2018\practicalDataAnalysis\helper.py", line 265, in plot_components plt.savefig(**f_params) File "D:\tools\Python37\lib\site-packages\matplotlib\pyplot.py", line 722, in savefig res = fig.savefig(*args, **kwargs) TypeError: savefig() missing 1 required positional argument: 'fname' */
Tips:注意將file_save_params中的'filename'改成'fname',便可。ide
color_marker列表指定了咱們用的標記:若是電話沒帶來信用卡的辦理,咱們用小的紅色圈圈表示;若是帶來了,就用綠色的點表示。file_save_params方法爲.savefig(...)方法保存了全部的關鍵詞參數,.savefig(...)方法會在.plot_components(...)方法中調用。
最後,調用.plot_components(...)方法。.transform(...)方法返回頭三個主成分的列表(k=3參數)。
Scikit也有找到數據主成分的方法:
1 import sklearn.decomposition as dc 2 3 /* @hlp.timeit 4 def reduce_PCA(x): 5 ''' 6 Reduce the dimensions using Principal Component 7 Analysis 8 ''' 9 # create the PCA object 10 pca = dc.PCA(n_components=3, whiten=True) 11 12 # learn the principal components from all the features 13 return pca.fit(x) 14 */
和一般同樣,建立pca對象,應用數據,即學習主成分。和MLPY不一樣,Scikit的.PCA(...)方法容許你指定想學習出的主成分個數;不指定這個參數就找出全部可能的主成分(最大值是數據集中特徵的個數)。
Scikit的.PCA(...)方法與MLPY的另外一點不一樣在於,它能夠知道每一個特定的主成分貢獻了多少方差。這樣展現出來:
# how much variance each component explains?每一個成分貢獻了多少方差? print(z.explained_variance_ratio_) # and total variance accounted for 整體方差 print(np.sum(z.explained_variance_ratio_))
找出來的成分與MLPY的.PCA(...)方法找出來的略有不一樣:
/* The method reduce_PCA took 0.29 sec to run. [0.12686333 0.07761521 0.07516711] 0.2796456484006652 */
參考:查看PCA的可視化:http://setosa.io/ev/principal-component-analysis/。
5.4用主成分分析找到關鍵因素
與前面介紹的PCA方法不一樣,核PCA使用用戶定義的核函數,將n維的數據映射到m維的特徵空間。PCA映射時用了線性函數,至關於一個使用了線性核的核PCA。
當數據不是線性可分時,核PCA就大有可爲了,它可使用多種非線性核將數據映射到更高的維度。
準備:需安裝好pandas和Scikit。
一樣,咱們給模型包了一層方法,以便追蹤模型到收斂時花了多久。使用核PCA,你應該內心有數,估算要更久(reduce_kernelPCA.py文件):
1 @hlp.timeit 2 def reduce_KernelPCA(x, **kwd_params): 3 ''' 4 Reduce the dimensions using Principal Component 5 Analysis with different kernels 6 ''' 7 # create the PCA object 8 pca = dc.KernelPCA(**kwd_params) 9 10 # learn the principal components from all the features 11 return pca.fit(x)
原理:和之前同樣,先讀入數據集,把它拆成自變量x和因變量y。使用關鍵詞**kwd_params參數,能夠輕鬆測試多種核模型;本例中,使用以前介紹過的RBF函數:
# reduce the dimensionality kwd_params = { 'kernel': 'rbf', 'gamma': 0.33, 'n_components': 3, 'max_iter': 1, 'tol': 0.9, 'eigen_solver': 'arpack' }
kernel參數容許咱們選用多種核:可使用linear、poly、rbf、sigmoid和cosine,或者預先計算你本身的核。n_components參數控制着找到多少個主成分。
KernelPCA(...)方法循環尋找數據中的主成分。使用arpack找到協方差矩陣的特徵向量。
要學習arpack的更多內容,參考http://www.caam.rice.edu/software/ARPACK或http://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html。
max_iter參數控制的是arpack中止估算前的最大循環次數。這是爲了不估算陷在局部極值點。
tol參數指定了容忍度;也控制着循環。若是循環之間的提高低於這個值,循環就會終止,認爲找到了特徵值。
gamma參數是poly核和rbf核的係數,無疑是最難選擇的參數。要具象化gamma參數對RBF核的影響,參考這個數據集(展現的是XOR函數):
在這個二維的例子中,要找到數據的最佳分割。咱們用的是RBF核PCA,這裏展現的是不一樣gamma參數下獲得的不一樣結果。在不一樣gamma參數的圖示後面,都跟着第一個主成分的圖,做爲視覺上的輔助,幫咱們檢驗數據是否分割:
從圖中能夠看出,gamma參數影響了數據集轉換後的形狀和密度。另外,也能夠看出XOR函數不易分割,即使使用了RBF函數也是這樣。最接近分割的gamma參數的值是5。
使用RBF核的reduce_KernelPCA(...)方法在做者的機器上通常花10min左右進行估算。
邀月機器內存16G過小,沒法計算!!!!!!,把原數據4萬行縮減爲2萬行,也須要多60倍的時間(18.38 sec VS 0.29秒 )
/* 如下方式試過無效: Run configuration: -Xms1024m -Xmx8192m -XX:MaxNewSize=10000m -XX:MaxPermSize=10000m -Xms512m -Xmx8192m -XX:PermSize=512M -XX:MaxPermSize=8192m -XX:ReservedCodeCacheSize=96m */
前面提過,可使用其餘函數做爲核。本技巧中,以XOR函數數據集爲例,展現如何用多種核函數估算核PCA,以避免等得像前一個例子那麼久(reduce_kernelPCA_alternative.py文件)。
首先,準備數據集:
1 def produce_XOR(sampleSize): 2 import sklearn.datasets as dt 3 4 # centers of the blobs 5 centers = [(0,0),(3,0),(3,3),(0,3)] 6 7 # create the sample 8 x, y = dt.make_blobs(n_samples=sampleSize, n_features=2, 9 cluster_std=0.8, centers=centers, shuffle=False 10 ) 11 12 # and make it XOR like 13 y[y == 2] = 0 14 y[y == 3] = 1 15 16 return x, y
1 def plot_components_2d(z, y, color_marker, **f_params): 2 ''' 3 Produce and save the chart presenting 3 principal 4 components 5 ''' 6 # import necessary modules 7 import matplotlib.pyplot as plt 8 from mpl_toolkits.mplot3d import Axes3D 9 10 # convert the dependent into a Numpy array 11 # this is done so z and y are in the same format 12 y_np = y 13 14 # do it only, however, if y is not NumPy array 15 if type(y_np) != np.array: 16 y_np = np.array(y_np) 17 18 # create a figure 19 fig = plt.figure() 20 ax = fig.add_subplot(111) 21 22 # plot the dots 23 for i in np.unique(y_np): 24 ax.scatter( 25 z[y_np == i, 0], 26 z[y_np == i, 1], 27 c=color_marker[i][0], 28 marker=color_marker[i][1]) 29 30 ax.set_xlabel('First component') 31 ax.set_ylabel('Second component') 32 33 # save the figure 34 plt.savefig(**f_params)
首先,導入一個輔助模塊sklearn.datasets。要在正方形的四角建立四個數據塊,因此在centers列表存了四個角的座標。使用.make_blobs(...)方法建立數據集:5000個數據點(n_samples參數),兩個特徵(咱們要的是二維的數據集,n_features)。數據塊密度是均勻的;這由cluster_std參數控制。用centers參數控制數據塊的中心點。最後一個參數shuffle控制着數據是否要打亂,這樣會帶來更高的變異性。
y對象保存數據的標籤列表。因爲傳入了四個中心,所以會獲得四個不一樣的標籤,XOR數據集應該只有兩個標籤:對角線上的數據塊,其標籤相同。所以,咱們會改變兩個數據塊的標籤。
而後咱們準備全部想測試的核的列表:
1 # reduce the dimensionality 2 kwd_params = [{ 'kernel': 'linear', 3 'n_components': 2,'max_iter': 3, 4 'tol': 1.0, 'eigen_solver': 'arpack' 5 }, { 'kernel': 'poly', 6 'degree': 2,'n_components': 2,'max_iter': 3, 7 'tol': 1.0, 'eigen_solver': 'arpack' 8 }, { 'kernel': 'sigmoid', 9 'n_components': 2,'max_iter': 3, 10 'tol': 1.0, 'eigen_solver': 'arpack' 11 }, { 'kernel': 'cosine', 12 'degree': 2,'n_components': 2,'max_iter': 3, 13 'tol': 1.0, 'eigen_solver': 'arpack'} 14 ]
遍歷全部列表,估算模型並保存圖表:
color_marker = [('r','^'),('g','o')] for params in kwd_params: z = reduce_KernelPCA(x, **params) # plot and save the chart # vary the colors and markers for the points file_save_params = { 'filename': '../../Data/Chapter05/charts/kernel_pca_3d_{0}.png'\ .format(params['kernel']), 'dpi': 300 } hlp.plot_components_2d(z.transform(x), y, color_marker, **file_save_params)
線性核等價於尋常的PCA模型。用在XOR數據集上,因爲它不是線性可分的,因此並不指望它的表現能有多好。多項式和s函數核分割的結果某種程度上要更好。雖然從餘弦函數的結果中能夠辨別出模式,但仍是會在綠點中包括紅點。在餘弦(及其餘方法)的例子中,沒法僅僅用一個維度區分數據。全部的圖表在Data/Chapter5/charts/文件夾下。
/* The method reduce_KernelPCA took 0.69 sec to run. The method reduce_KernelPCA took 0.74 sec to run. The method reduce_KernelPCA took 1.19 sec to run. The method reduce_KernelPCA took 0.67 sec to run. */
參考:
Sebastian Raschka給出了一些核PCA的好例子:http://sebastianraschka.com/Articles/2014_kernel_pca.html。
5.5使用隨機PCA在數據中尋找主成分
PCA(和核PCA)都用低階矩陣近似來估算主成分。低階矩陣近似最小化損失函數,這個損失函數表明矩陣及其近似之間的偏差。
這種作法對大規模數據集來講會很耗時。而隨機化輸入數據集中奇異值的分解,能夠顯著提高估算的速度。
準備:需安裝好NumPy、Scikit和Matplotlib。
和之前同樣,建立一個包裝方法來估算模型(reduce_randomizedPCA.py文件):
1 def reduce_randomizedPCA(x): 2 ''' 3 Reduce the dimensions using Randomized PCA algorithm 4 ''' 5 # create the CCA object 6 randomPCA = dc.RandomizedPCA(n_components=2, whiten=True, 7 copy=False) 8 9 # learn the principal components from all the features 10 #從全部特徵中學習主成分 11 return randomPCA.fit(x)
原理:這個方法的內部看上去幾乎和以前的模型包裝程序如出一轍。首先,咱們建立了模型對象,應用數據後返回。Scikit在其全部的分解方法中,保持了統一的API(Application Programming Interface,應用編程接口),因此咱們換模型不須要改太多代碼。
在本技巧中,咱們將聚焦於,當數據的特徵數和樣本容量都增加時,隨機PCA與PCA相比,速度上能有多大的提高。這裏,咱們先定義參數:
# prepare the sample sampleSizes = np.arange(1000, 50000, 3000) featureSpace = np.arange(100, 1000, 100)
NumPy的.arange(<start>,<end>,<step>)方法建立了一個範圍,自<start>始,以<end>(或左右)終,步長爲<step>。
在helper.py文件中,咱們加一個相似於.timeit裝飾器的方法:
def timeExecution(method, *args, **kwargs): ''' A method to measure time of execution of a method ''' start = time.time() result = method(*args, **kwargs) end = time.time() return result, end-start
timeExecution(...)方法的第一個參數是方法名,將傳入的這個方法的參數做爲後續參數(*args參數和變長參數**kwargs)。方法內部和timeit裝飾器的模式相似,可是不只返回方法的結果,也返回執行的時間。
咱們將使用timeExecution(...)方法衡量PCA和隨機PCA執行的時間,後面還會畫出圖;Z是記載執行時間的字典:
# object to hold the results Z = {'randomPCA': [], 'PCA': []}
腳本的主循環以下:
1 for features in featureSpace: 2 inner_z_randomPCA = [] 3 inner_z_PCA = [] 4 5 for sampleSize in sampleSizes: 6 # get the sample 7 x, y = hlp.produce_sample( 8 sampleSize=sampleSize, features=features) 9 10 print( 11 'Processing: sample size {0} and {1} features'\ 12 .format(sampleSize, features)) 13 14 # reduce the dimensionality 15 z_r, time_r = hlp.timeExecution( 16 reduce_randomizedPCA, x) 17 z_pca, time_pca = hlp.timeExecution( 18 reduce_PCA, x) 19 20 inner_z_randomPCA.append(time_r) 21 inner_z_PCA.append(time_pca) 22 23 Z['randomPCA'].append(inner_z_randomPCA) 24 Z['PCA'].append(inner_z_PCA)
咱們比較兩個維度的執行時間,要遍歷兩個循環。這樣作最終是爲了找出哪一個對執行時間的影響更大:是特徵數仍是樣本容量。inner_z_...列表記錄了執行時間。
在第二個循環的第一步,咱們使用produce_sample(...)方法。這是咱們要加到helper.py文件的另外一個方法:
def produce_sample(sampleSize, features): import sklearn.datasets as dt # create the sample x, y = dt.make_sparse_uncorrelated( n_samples=sampleSize, n_features=features) return x, y
這個方法生成一個樣本,容量sampleSize和特徵數由Scikit的make_sparse_uncorrelated方法預先指定了。
有了建立的這個樣本,咱們如今能夠爲reduce_randomizedPCA(...)和reduce_PCA(...)方法的執行計時了:後者是從本書5.3節的技巧中引入。
以後,咱們將時間附到記載執行時間的列表。循環以附加上列表的Z字典結束。
咱們如今畫出執行時間:
1 def saveSurfacePlot(X_in,Y_in,Z,**f_params): 2 from mpl_toolkits.mplot3d import Axes3D 3 import matplotlib.pyplot as plt 4 import matplotlib as mt 5 6 # adjust the font 調整字體 7 font = {'size': 8} 8 mt.rc('font', **font) 9 10 # create a mesh 建立風格 11 X, Y = np.meshgrid(X_in, Y_in) 12 13 # create figure and add axes 建立圖,加上座標 14 fig = plt.figure() 15 ax = fig.gca(projection='3d') 16 17 # plot the surface 繪製表面 18 surf = ax.plot_surface(X, Y, Z, 19 rstride=1, cstride=1, 20 cmap=mt.cm.seismic, 21 linewidth=0, antialiased=True) 22 23 # set the limits on the z-axis z軸設置界限 24 ax.set_zlim(0, 7) 25 26 # add labels to axes 給軸加上標籤 27 ax.set_xlabel('Sample size') 28 ax.set_ylabel('Feature space') 29 ax.set_zlabel('Time to estimate (s)') 30 31 # rotate the chart 旋轉圖表 32 ax.view_init(30, 130) 33 34 # and save the figure 35 fig.savefig(**f_params) 36 37 # filename params for the randomized PCA 38 f_params = { 39 'filename': 40 '../../Data/Chapter05/charts/time_r_pca_surf.png', 41 'dpi': 300 42 } 43 44 # prepare and save the plot 45 saveSurfacePlot(sampleSizes, featureSpace, 46 Z['randomPCA'], **f_params)
saveSurfacePlot(...)方法先引入須要的模塊。咱們減少字體,使圖表更可讀。
NumPy的meshgrid(...)方法建立n x m個度量X和Y,X_in的大小是m,Y_in的大小是n;X_in在X中重複n次(行),Y_in在Y中重複m次(列)。咱們遍歷每一個矩陣的全部m x n個元素時,就覆蓋了全部特徵和樣本容量的組合。
接下來,咱們建立一個圖,加上三維座標,畫出表面。X、Y和Z是點的座標。rstride和cstride參數說明,咱們要從x軸和y軸的每一個點,沿着表面分別繪製水平線和垂直線;這樣就在表面鋪好瓦磚。linewidth參數控制線的寬度,cmap參數定義了色彩圖(或溫度圖)——z的值越高,圖線越紅。
你能夠在這裏找到全部可用的色彩圖:http://matplotlib.org/examples/color/colormaps_reference.html。
antialiased參數在圖表上建立更平滑的線。
咱們在z軸上設了限制,以便在比較兩個不一樣方法的圖時,咱們能夠當即看出執行時間的差異。加標籤永遠是爲了幫助理解圖表的意義。
你也許須要調整.set_zlim(...)參數,由於你機器上的執行時間可能會不一樣。
最後,咱們旋轉圖表並保存。.view_init(...)方法將海拔設爲30度,並在x-y平面順時針旋轉130度:
PCA的圖看上去和前一個相似,不過隨機化版本的圖應該相似這樣:
Tips:
/* Processing: sample size 49000 and 900 features Traceback (most recent call last): File "D:\Java2018\practicalDataAnalysis\Codes\Chapter05\reduce_randomizedPCA.py", line 119, in <module> Z['PCA'], **f_params) File "D:\Java2018\practicalDataAnalysis\Codes\Chapter05\reduce_randomizedPCA.py", line 57, in saveSurfacePlot linewidth=0, antialiased=True) File "D:\tools\Python37\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1614, in plot_surface if Z.ndim != 2: AttributeError: 'list' object has no attribute 'ndim' */
解決方案:
跟蹤打印Z,注意到
AttributeError: 'list' object has no attribute 'shape'
/* #=============================================================================== # print('************************************') # print(Z['PCA']) # print(np.array(Z['PCA'])) #=============================================================================== #=============================================================================== # [[0.00498652458190918, 0.018980026245117188, 0.037740230560302734], [0.0076868534088134766, 0.02914142608642578, 0.05567455291748047]] # [[0.00498652 0.01898003 0.03774023] # [0.00768685 0.02914143 0.05567455]] #=============================================================================== */
/* #Z爲List,必須轉化爲Array,注意List對象沒有shape屬性 Z=np.array(Z) */
如你所見,當樣本容量和特徵空間增加時,PCA算法的執行時間增加得很是快;不過只有一個維度增加時不怎麼受影響。一樣的參數配置下,隨機化版本的PCA算法,執行時間快10倍。不過要注意,若是數據集很小(特徵數<100),隨機化PCA方法可能更慢。
5.6使用線性判別分析提取有用的維度
如今咱們理解了降維的機制(和取捨),咱們用它來分類吧。
本技巧介紹LDA(Linear Discriminant Analysis,線性判別分析)。與本章以前介紹的方法不一樣,LDA的目標是將因變量用不少其餘特徵的一個線性函數表示;在這個意義上,這和迴歸(下一章討論)很像。在數據的最佳方差的快照(解釋)中,對於如何爲線性關係建模這一點,LDA和ANAVA方差分析及邏輯迴歸都有類似處。
咱們使用一個線性SVM分類器,來測試相較於原始數據集,降維後的效果。再一次,咱們要使用銀行營銷電話數據集。
準備:需裝好pandas和MLPY。
使用下面的方法,經過LDA下降維度(reduce_LDA文件):
1 @hlp.timeit 2 def reduce_LDA(x, y): 3 ''' 4 Reduce the dimensions using Linear Discriminant 5 Analysis 6 ''' 7 # create the PCA object 8 lda = ml.LDA(method='fast') 9 10 # learn the principal components from all the features 11 lda.learn(x, y) 12 13 return lda
原理:和之前同樣,程序是以讀入數據集並拆成自變量集合x和因變量集合y開始的。因爲本技巧中,咱們要看降維對分類的影響,因此咱們還要將初始的數據集拆成訓練子集和測試子集:
1 # split the original data into training and testing 2 train_x_orig, train_y_orig, \ 3 test_x_orig, test_y_orig, \ 4 labels_orig = hlp.split_data( 5 csv_read, 6 y = 'credit_application' 7 )
完成了這步,如今咱們能夠下降數據集的維度:
# reduce the dimensionality csv_read['reduced'] = reduce_LDA(x, y).transform(x)
這一步作了不止一件事:咱們一個個分析。
首先,咱們調用reduce_LDA(...)方法。這個方法有兩個參數必傳,x是自變量集合,y是因變量集合。這看上去不太合乎正統,畢竟以前全部的方法只要自變量。但LDA要找的是自變量和因變量之間的(模型)線性關係,它得知道目標(還記得第4章開始導論部分討論的有監督訓練否)。
MLPY提供了.LDA(...)估算器。這裏指定了方法爲fast,由於咱們的數據集有4萬多記錄。接着,咱們讓模型學習(.learn(...)方法)學習數據中的聯繫,返回訓練後的對象。
.transform(x)方法將自變量編碼,而後存到原始數據集csv_read的reduced列。
如今是時候爲分類器另建立一個訓練集和測試集了:
1 # split the reduced data into training and testing 2 train_x_r, train_y_r, \ 3 test_x_r, test_y_r, \ 4 labels_r = hlp.split_data( 5 csv_read, 6 y = 'credit_application', 7 x = ['reduced'] 8 )
這裏,咱們只選取降維後的列做爲自變量。
咱們如今估算分類器:
1 # train the models 2 classifier_r = fitLinearSVM((train_x_r, train_y_r)) 3 classifier_orig = fitLinearSVM((train_x_orig, train_y_orig))
若是你還記得咱們在第3章中作過什麼,這裏就不會意外了。咱們重用了本書3.5節裏介紹的fitLinearSVM(...)方法。
1 @hlp.timeit 2 def fitLinearSVM(data): 3 ''' 4 Build the linear SVM classifier 5 ''' 6 # create the classifier object 7 svm = ml.LibSvm(svm_type='c_svc', 8 kernel_type='linear', C=20.0) 9 10 # fit the data 11 svm.learn(data[0],data[1]) 12 13 # return the classifier 14 return svm
測試下咱們的方法:
1 # classify the unseen data 預測數據 2 predicted_r = classifier_r.pred(test_x_r) 3 predicted_orig = classifier_orig.pred(test_x_orig) 4 5 # print out the results 打印結果 6 hlp.printModelSummary(test_y_r, predicted_r) 7 hlp.printModelSummary(test_y_orig, predicted_orig)
Tips:
/* ValueError: Buffer dtype mismatch, expected 'int_t' but got 'long long' */
解決方案:從非官方下載32位mlpy-3.5.0-cp37-cp37m-win32.whl文件,python官網下載32位安裝文件python-3.7.5.exe
首先,咱們用估算的分類器預測類別,而後評估模型。咱們看到:
/* The method reduce_LDA took 0.46 sec to run. The method fitLinearSVM took 2.23 sec to run. The method fitLinearSVM took 131.19 sec to run. Overall accuracy of the model is 90.70 percent Classification report: precision recall f1-score support 0.0 0.92 0.98 0.95 12167 1.0 0.69 0.33 0.44 1556 accuracy 0.91 13723 macro avg 0.80 0.65 0.70 13723 weighted avg 0.89 0.91 0.89 13723 Confusion matrix: [[11940 227] [ 1049 507]] ROC: 0.6535892262415742 Overall accuracy of the model is 90.52 percent Classification report: precision recall f1-score support 0.0 0.92 0.98 0.95 12145 1.0 0.65 0.32 0.43 1524 accuracy 0.91 13669 macro avg 0.79 0.65 0.69 13669 weighted avg 0.89 0.91 0.89 13669 Confusion matrix: [[11889 256] [ 1040 484]] ROC: 0.6482533343274454 */
LDA在咱們的數據上很快。
然而你能夠注意到執行時間的差異:使用完整(未降維)的數據集估算的模型,要比降維的模型慢接近50倍。這應該是情理之中的,畢竟降維數據集只有一個特徵,而完整的有59個特徵。
比較兩個模型的能力,降維空間的要優於完整數據集的!差別並不大,但用降維特徵估算的SVM在召回率、f-指標和ROC方面的表現更好。另外,總體的精確度更接近91%,而完整特徵的數據集更接近90%。
5.7用kNN分類模型給電話分類時使用多種降維技巧
既然咱們已經看到,維度的下降會帶給咱們表現更好的分類模型,那麼讓咱們試試更多的方法,介紹下另外一個分類算法:kNN(k-Nearest Neighbors,k鄰近)算法。
本技巧中,咱們會測試並比較三種降維模型:PCA(做爲基準)、快速ICA(Independent Component Analysis,獨立成分分析)以及截斷奇異值分解。
準備:需裝好pandas和Scikit。
本技巧中,咱們會用一個小例子展現,在Python中一切都是對象(方法也是),咱們能夠將其做爲參數傳入方法。這是咱們下降維度的方法(reduce_kNN.py文件):
1 import sklearn.decomposition as cd 2 import sklearn.neighbors as nb 3 4 @hlp.timeit 5 def reduceDimensions(method, data, **kwrd_params): 6 ''' 7 Reduce the dimensions 8 ''' 9 # split into independent and dependent features 10 x = data[data.columns[:-1]] 11 y = data[data.columns[-1]] 12 13 # create the reducer object 14 reducer = method(**kwrd_params) 15 16 # fit the data 17 reducer.fit(x) 18 19 # return the classifier 20 return reducer.transform(x)
原理:reduceDimensions(...)方法以reducer方法做爲第一個參數,以估算中要使用的數據做爲第二個參數,以reducer方法的全部關鍵詞參數做爲第三個參數。
爲了精簡代碼,咱們挪走了數據拆分的代碼。和以前模型相關的降維方法同樣,咱們先建立一個降維者,而後應用數據。不過這一次,咱們返回轉換後的數據。
咱們爲不一樣的降維者開發了模型相關的降維和分類的方法,用於本技巧。以使用PCA降維做爲一個例子: 1 @hlp.timeit
2 def fit_kNN_classifier(data): 3 ''' 4 Build the kNN classifier 5 ''' 6 # create the classifier object 7 knn = nb.KNeighborsClassifier() 8 9 # fit the data 10 knn.fit(data[0],data[1]) 11 12 #return the classifier 13 return knn
這個方法必需的參數只有數據。首先,咱們定義想使用的參數。而後,咱們調用傳入特性降維者的reduceDimensions(...)方法;在這個例子中,就是cd.PCA,以及全部其餘必需的參數data和**kwrd_params。
而後準備估算kNN分類器所必需的數據集。咱們使用prepare_data(...)方法:
1 def prepare_data(data, principal_components, n_components): 2 ''' 3 Prepare the data for the classification 4 ''' 5 # prepare the column names 6 cols = ['pc' + str(i) 7 for i in range(0, n_components)] 8 9 # concatenate the data 10 data = pd.concat( 11 [data, 12 pd.DataFrame(principal_components, 13 columns=cols)], 14 axis=1, join_axes=[data.index]) 15 16 # split the data into training and testing 17 train_x, train_y, \ 18 test_x, test_y, \ 19 labels = hlp.split_data( 20 data, 21 y = 'credit_application', 22 x = cols 23 ) 24 25 return (train_x, train_y, test_x, test_y)
這個方法以數據做爲第一個參數,包括一個編碼後的主成分列表(矩陣)以及指望的成分數目。本技巧全部的建模操做中,咱們讓降維模型返回五個主成分。
首先,咱們建立列名的列表,以便將咱們的原始數據集和降維後的矩陣鏈接。咱們使用pandas的.concat(...)方法鏈接。這個方法以要鏈接的DataFrame列表做爲第一個參數;這裏咱們傳遞原始數據,並用特定的列從principal_components建立一個DataFrame。.concat(...)的axis參數指定了鏈接的方向(行),join_axes指定了鏈接的鍵。
如今咱們往原始數據集加上了主成分的列,咱們能夠將數據集拆分紅訓練集和測試集。最終,咱們返回訓練和測試子集的元組。
準備了數據後,咱們如今估算kNN,並在fit_pca(...)方法中調用class_fit_predict_print(...)方法評估模型:
1 @hlp.timeit 2 def fit_kNN_classifier(data): 3 ''' 4 Build the kNN classifier 5 ''' 6 # create the classifier object 7 knn = nb.KNeighborsClassifier() 8 9 # fit the data 10 knn.fit(data[0],data[1]) 11 12 #return the classifier 13 return knn 14 15 def class_fit_predict_print(data): 16 ''' 17 Automating model estimation 18 ''' 19 # train the model 20 classifier = fit_kNN_classifier((data[0], data[1])) 21 22 # classify the unseen data 23 predicted = classifier.predict(data[2]) 24 25 # print out the results 26 hlp.printModelSummary(data[3], predicted)
傳入訓練數據和測試數據(形式是(train_x,train_y,test_x,test_y)元組)。
首先,咱們用fit_kNN_classifier(...)方法估算分類器。咱們僅傳入(train_x,train_y)元組做爲數據集用於估算。
如同其餘的估算方法,咱們建立分類器對象(.KNeighborsClassifier(...))。
.KNeighborsClassifier(...)可使用不少參數,但咱們以爲事情仍是簡單點。文檔能夠參考http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighbors Classifier.html。
而後,咱們應用模型,返回分類器。咱們使用.KNeighborsClassifier(...)暴露出的.predict(...)方法預測test_x數據集的分類。最後,咱們在真實的數據(test_y)上用.prin進咱們tModelSummary(...)方法打印結果,這個方法是咱們在本書3.2節中開發的。
本技巧的開始部分已經提過,咱們比較降維與否後kNN模型的效率:
1 # compare models 2 fit_clean(csv_read) 3 fit_pca(csv_read) 4 fit_fastICA(csv_read) 5 fit_truncatedSVD(csv_read)
Tips:
1 /* The method reduceDimensions took 0.22 sec to run. 2 D:\Java2018\practicalDataAnalysis\Codes\Chapter05\reduce_kNN.py:56: FutureWarning: The join_axes-keyword is deprecated.
3 Use .reindex or .reindex_like on the result to achieve the same functionality. 4 axis=1, join_axes=[data.index]) 5 */
解決方案:
/* # concatenate the data data = pd.concat( [data, pd.DataFrame(principal_components, columns=cols)], axis=1, join_axes=[data.index]) 修改成:********************************************* # concatenate the data data = pd.concat( [data, pd.DataFrame(principal_components, columns=cols)], axis=1).reindex() # axis=1, join_axes=[data.index]) */
結果以下:
/* --PCAClean The method fit_kNN_classifier took 0.79 sec to run. Overall accuracy of the model is 89.06 percent Classification report: precision recall f1-score support 0.0 0.91 0.97 0.94 12120 1.0 0.55 0.25 0.34 1568 accuracy 0.89 13688 macro avg 0.73 0.61 0.64 13688 weighted avg 0.87 0.89 0.87 13688 Confusion matrix: [[11805 315] [ 1182 386]] ROC: 0.6100916851889271 ----PCA The method reduceDimensions took 0.21 sec to run. The method fit_kNN_classifier took 0.04 sec to run. Overall accuracy of the model is 92.07 percent Classification report: precision recall f1-score support 0.0 0.93 0.98 0.96 12031 1.0 0.77 0.44 0.56 1558 accuracy 0.92 13589 macro avg 0.85 0.71 0.76 13589 weighted avg 0.91 0.92 0.91 13589 Confusion matrix: [[11826 205] [ 873 685]] ROC: 0.7113134618324997 --FastICA The method reduceDimensions took 0.53 sec to run. The method fit_kNN_classifier took 0.04 sec to run. Overall accuracy of the model is 91.67 percent Classification report: precision recall f1-score support 0.0 0.93 0.98 0.95 12113 1.0 0.72 0.43 0.54 1547 accuracy 0.92 13660 macro avg 0.83 0.70 0.75 13660 weighted avg 0.91 0.92 0.91 13660 Confusion matrix: [[11856 257] [ 881 666]] ROC: 0.7046468956861779 ---截斷奇異值 The method reduceDimensions took 0.16 sec to run. The method fit_kNN_classifier took 0.04 sec to run. Overall accuracy of the model is 92.71 percent Classification report: precision recall f1-score support 0.0 0.94 0.98 0.96 12059 1.0 0.77 0.49 0.60 1508 accuracy 0.93 13567 macro avg 0.85 0.74 0.78 13567 weighted avg 0.92 0.93 0.92 13567 Confusion matrix: [[11839 220] [ 769 739]] ROC: 0.7359047074694424 */
fit_clean(...)在一個特徵完備的數據集上應用kNN。比較之下,它表現最差,只有89%的總體精確度;其餘方法都接近92%,其中截斷奇異值分解幾乎達到了93%。截斷奇異值分解在準確率、召回率、f-指標和ROC幾個指標上的表現都優於其餘方法。
第5章完。