1. 程式人生 > >12種降維方法終極指南

12種降維方法終極指南

來源:Analytics Vidhya
編譯:Bot

授權自 論智

你遇到過特徵超過1000個的資料集嗎?超過5萬個的呢?我遇到過。降維是一個非常具有挑戰性的任務,尤其是當你不知道該從哪裡開始的時候。擁有這麼多變數既是一個恩惠——資料量越大,分析結果越可信;也是一種詛咒——你真的會感到一片茫然,無從下手。

面對這麼多特徵,在微觀層面分析每個變數顯然不可行,因為這至少要幾天甚至幾個月,而這背後的時間成本是難以估計的。為此,我們需要一種更好的方法來處理高維資料,比如本文介紹的降維:一種能在減少資料集中特徵數量的同時,避免丟失太多資訊並保持/改進模型效能的方法。

什麼是降維?

每天,我們都會生成大量資料,而事實上,現在世界上約90%的資料都是在過去3到4年中產生的,這是個令人難以置信的現實。如果你不信,下面是收集資料的幾個示例:


  • Facebook會收集你喜歡、分享、釋出、訪問的內容等資料,比如你喜歡哪家餐廳。

  • 智慧手機中的各類應用會收集大量關於你的個人資訊,比如你所在的地點。

  • 淘寶會收集你在其網站上購買、檢視、點選的內容等資料。

  • 賭場會跟蹤每位客戶的每一步行動。

隨著資料的生成和資料收集量的不斷增加,視覺化和繪製推理圖變得越來越困難。一般情況下,我們經常會通過繪製圖表來視覺化資料,比如假設我們手頭有兩個變數,一個年齡,一個身高。我們就可以繪製散點圖或折線圖,輕鬆反映它們之間的關係。

下圖是一個簡單的例子:

其中橫座標X1的單位為“千克”,縱座標X2的單位為“磅”。可以發現,雖然是兩個變數,但它們傳達的資訊是一致的,即物體的重量。所以我們只需選用其中的一個就能保留原始意義,把2維資料壓縮到1維(Y1)後,上圖就變成:

類似地,我們可以把資料從原本的p維轉變為一系列k維的子集(k<<n),這就是降維。

為什麼要降維?

以下是在資料集中應用降維的用處:

  • 隨著資料維度不斷降低,資料儲存所需的空間也會隨之減少。

  • 低維資料有助於減少計算/訓練用時。

  • 一些演算法在高維度資料上容易表現不佳,降維可提高演算法可用性。

  • 降維可以用刪除冗餘特徵解決多重共線性問題。比如我們有兩個變數:“一段時間內在跑步機上的耗時”和“卡路里消耗量”。這兩個變數高度相關,在跑步機上花的時間越長,燃燒的卡路里自然就越多。因此,同時儲存這兩個資料意義不大,只需一個就夠了。

  • 降維有助於資料視覺化。如前所述,如果資料維度很高,視覺化會變得相當困難,而繪製二維三維資料的圖表非常簡單。

資料集1:Big Mart Sales III

降維技術一覽

資料維度的降低方法主要有兩種:

  • 僅保留原始資料集中最相關的變數(特徵選擇)。

  • 尋找一組較小的新變數,其中每個變數都是輸入變數的組合,包含與輸入變數基本相同的資訊(降維)。

1. 缺失值比率(Missing Value Ratio)

假設你有一個數據集,你第一步會做什麼?在構建模型前,對資料進行探索性分析必不可少。但在瀏覽資料的過程中,有時候我們會發現其中包含不少缺失值。如果缺失值少,我們可以填補缺失值或直接刪除這個變數;如果缺失值過多,你會怎麼辦呢?

當缺失值在資料集中的佔比過高時,一般我會選擇直接刪除這個變數,因為它包含的資訊太少了。但具體刪不刪、怎麼刪需要視情況而定,我們可以設定一個閾值,如果缺失值佔比高於閾值,刪除它所在的列。閾值越高,降維方法越積極。

下面是具體程式碼:

  1. # 匯入需要的庫

  2. import pandas as pd

  3. import numpy as np

  4. import matplotlib.pyplot as plt

載入資料:

  1. # 讀取資料

  2. train=pd.read_csv("Train_UWu5bXk.csv")

[注]:應在讀取資料時新增檔案的路徑。

.isnull().sum()檢查每個變數中缺失值的佔比:

  1. train.isnull().sum()/len(train)*100

如上表所示,缺失值很少。我們設閾值為20%:

  1. # 儲存變數中的缺失值

  2. a = train.isnull().sum()/len(train)*100

  3. # 儲存列名

  4. variables = train.columns

  5. variable = [ ]

  6. for i in range(0,12):

  7. if a[i]<=20:   #setting the threshold as 20%

  8.        variable.append(variables[i])

2. 低方差濾波(Low Variance Filter)

如果我們有一個數據集,其中某列的數值基本一致,也就是它的方差非常低,那麼這個變數還有價值嗎?和上一種方法的思路一致,我們通常認為低方差變數攜帶的資訊量也很少,所以可以把它直接刪除。

放到實踐中,就是先計算所有變數的方差大小,然後刪去其中最小的幾個。需要注意的一點是:方差與資料範圍相關的,因此在採用該方法前需要對資料做歸一化處理。

放在示例中,我們先估算缺失值:

  1. train['Item_Weight'].fillna(train['Item_Weight'].median, inplace=True)

  2. train['Outlet_Size'].fillna(train['Outlet_Size'].mode()[0], inplace=True)

檢查缺失值是否已經被填充:

  1. train.isnull().sum()/len(train)*100

再計算所有數值變數的方差:

  1. train.var()

如上圖所示,和其他變數相比,Item_Visibility的方差非常小,因此可以把它直接刪除。

  1. umeric = train[['Item_Weight', 'Item_Visibility', 'Item_MRP', 'Outlet_Establishment_Year']]

  2. var = numeric.var()

  3. numeric = numeric.columns

  4. variable = [ ]

  5. for i in range(0,len(var)):

  6. ifvar[i]>=10:   # 將閾值設定為10%

  7.       variable.append(numeric[i+1])

以上程式碼幫我們列出了方差大於10的所有變數。

3. 高相關濾波(High Correlation filter)

如果兩個變數之間是高度相關的,這意味著它們具有相似的趨勢並且可能攜帶類似的資訊。同理,這類變數的存在會降低某些模型的效能(例如線性和邏輯迴歸模型)。為了解決這個問題,我們可以計算獨立數值變數之間的相關性。如果相關係數超過某個閾值,就刪除其中一個變數。

作為一般準則,我們應該保留那些與目標變數顯示相當或高相關性的變數。

首先,刪除因變數(ItemOutletSales),並將剩餘的變數儲存在新的資料列(df)中。

  1. df=train.drop('Item_Outlet_Sales', 1)

  2. df.corr()

如上表所示,示例資料集中不存在高相關變數,但通常情況下,如果一對變數之間的相關性大於0.5-0.6,那就應該考慮是否要刪除一列了。

4. 隨機森林(Random Forest)

隨機森林是一種廣泛使用的特徵選擇演算法,它會自動計算各個特徵的重要性,所以無需單獨程式設計。這有助於我們選擇較小的特徵子集。

在開始降維前,我們先把資料轉換成數字格式,因為隨機森林只接受數字輸入。同時,ID這個變數雖然是數字,但它目前並不重要,所以可以刪去。

  1. from sklearn.ensemble importRandomForestRegressor

  2. df=df.drop(['Item_Identifier', 'Outlet_Identifier'], axis=1)

  3. model = RandomForestRegressor(random_state=1, max_depth=10)

  4. df=pd.get_dummies(df)

  5. model.fit(df,train.Item_Outlet_Sales)

擬合模型後,根據特徵的重要性繪製成圖:

  1. features = df.columns

  2. importances = model.feature_importances_

  3. indices = np.argsort(importances[0:9])  # top 10 features

  4. plt.title('Feature Importances')

  5. plt.barh(range(len(indices)), importances[indices], color='b', align='center')

  6. plt.yticks(range(len(indices)), [features[i] for i in indices])

  7. plt.xlabel('Relative Importance')

  8. plt.show()

基於上圖,我們可以手動選擇最頂層的特徵來減少資料集中的維度。如果你用的是sklearn,可以直接使用SelectFromModel,它根據權重的重要性選擇特徵。

  1. from sklearn.feature_selection importSelectFromModel

  2. feature = SelectFromModel(model)

  3. Fit = feature.fit_transform(df, train.Item_Outlet_Sales)

5. 反向特徵消除(Backward Feature Elimination)

以下是反向特徵消除的主要步驟:

  • 先獲取資料集中的全部n個變數,然後用它們訓練一個模型。

  • 計算模型的效能。

  • 在刪除每個變數(n次)後計算模型的效能,即我們每次都去掉一個變數,用剩餘的n-1個變數訓練模型。

  • 確定對模型效能影響最小的變數,把它刪除。

  • 重複此過程,直到不再能刪除任何變數。

在構建線性迴歸或Logistic迴歸模型時,可以使用這種方法。

  1. from sklearn.linear_model importLinearRegression

  2. from sklearn.feature_selection import RFE

  3. from sklearn import datasets

  4. lreg = LinearRegression()

  5. rfe = RFE(lreg, 10)

  6. rfe = rfe.fit_transform(df, train.Item_Outlet_Sales)

我們需要指定演算法和要選擇的特徵數量,然後返回反向特徵消除輸出的變數列表。此外,rfe.ranking_可以用來檢查變數排名。

6. 前向特徵選擇(Forward Feature Selection)

前向特徵選擇其實就是反向特徵消除的相反過程,即找到能改善模型效能的最佳特徵,而不是刪除弱影響特徵。它背後的思路如下所述:

  • 選擇一個特徵,用每個特徵訓練模型n次,得到n個模型。

  • 選擇模型效能最佳的變數作為初始變數。

  • 每次新增一個變數繼續訓練,重複上一過程,最後保留效能提升最大的變數。

  • 一直新增,一直篩選,直到模型效能不再有明顯提高。

  1. from sklearn.feature_selection import f_regression

  2. ffs = f_regression(df,train.Item_Outlet_Sales )

上述程式碼會返回一個數組,其中包括變數F值和每個F對應的p值。在這裡,我們選擇F值大於10的變數:

  1. variable = [ ]

  2. for i in range(0,len(df.columns)-1):

  3. if ffs[0][i] >=10:

  4.       variable.append(df.columns[i])

[注]:前向特徵選擇和反向特徵消除耗時較久,計算成本也都很高,所以只適用於輸入變數較少的資料集。

到目前為止,我們介紹的6種方法都能很好地解決示例的商場銷售預測問題,因為這個資料集本身輸入變數不多。在下文中,為了展示多變數資料集的降維方法,我們將把資料集改成Fashion MNIST,它共有70,000張影象,其中訓練集60,000張,測試集10,000張。我們的目標是訓練一個能分類各類服裝配飾的模型。

資料集2:Fashion MNIST

7. 因子分析(Factor Analysis)

因子分析是一種常見的統計方法,它能從多個變數中提取共性因子,並得到最優解。假設我們有兩個變數:收入和教育。它們可能是高度相關的,因為總體來看,學歷高的人一般收入也更高,反之亦然。所以它們可能存在一個潛在的共性因子,比如“能力”。

在因子分析中,我們將變數按其相關性分組,即特定組內所有變數的相關性較高,組間變數的相關性較低。我們把每個組稱為一個因子,它是多個變數的組合。和原始資料集的變數相比,這些因子在數量上更少,但攜帶的資訊基本一致。

  1. import pandas as pd

  2. import numpy as np

  3. from glob import glob

  4. import cv2

  5. images = [cv2.imread(file) for file in glob('train/*.png')]

[注]:你必須使用train資料夾的路徑替換glob函式內的路徑。

現在我們先把這些影象轉換為numpy陣列格式,以便執行數學運算並繪製圖像。

  1. images = np.array(images)

  2. images.shape

返回:(60000, 28, 28, 3)

如上所示,這是一個三維陣列,但我們的目標是把它轉成一維,因為後續只接受一維輸入。所以我們還得展平影象:

  1. image = []

  2. for i in range(0,60000):

  3.    img = images[i].flatten()

  4.    image.append(img)

  5. image = np.array(image)

建立一個數據框,其中包含每個畫素的畫素值,以及它們對應的標籤:

  1. train = pd.read_csv("train.csv")     # Give the complete path of your train.csv file

  2. feat_cols = [ 'pixel'+str(i) for i in range(image.shape[1]) ]

  3. df = pd.DataFrame(image,columns=feat_cols)

  4. df['label'] = train['label']

用因子分析分解資料集:

  1. from sklearn.decomposition importFactorAnalysis

  2. FA = FactorAnalysis(n_components = 3).fit_transform(df[feat_cols].values)

這裡,n_components將決定轉換資料中的因子數量。轉換完成後,視覺化結果:

  1. %matplotlib inline

  2. import matplotlib.pyplot as plt

  3. plt.figure(figsize=(12,8))

  4. plt.title('Factor Analysis Components')

  5. plt.scatter(FA[:,0], FA[:,1])

  6. plt.scatter(FA[:,1], FA[:,2])

  7. plt.scatter(FA[:,2],FA[:,0])

在上圖中,x軸和y軸表示分解因子的值,雖然共性因子是潛在的,很難被觀察到,但我們已經成功降維。

8. 主成分分析(PCA)

如果說因子分析是假設存在一系列潛在因子,能反映變數攜帶的資訊,那PCA就是通過正交變換將原始的n維資料集變換到一個新的被稱做主成分的資料集中,即從現有的大量變數中提取一組新的變數。下面是關於PCA的一些要點:

  • 主成分是原始變數的線性組合。

  • 第一個主成分具有最大的方差值。

  • 第二主成分試圖解釋資料集中的剩餘方差,並且與第一主成分不相關(正交)。

  • 第三主成分試圖解釋前兩個主成分等沒有解釋的方差。

再進一步降維前,我們先隨機繪製資料集中的某些圖:

  1. rndperm = np.random.permutation(df.shape[0])

  2. plt.gray()

  3. fig = plt.figure(figsize=(20,10))

  4. for i in range(0,15):

  5.    ax = fig.add_subplot(3,5,i+1)

  6.    ax.matshow(df.loc[rndperm[i],feat_cols].values.reshape((28,28*3)).astype(float))

實現PCA:

  1. from sklearn.decomposition import PCA

  2. pca = PCA(n_components=4)

  3. pca_result = pca.fit_transform(df[feat_cols].values)

其中n_components將決定轉換資料中的主成分。接下來,我們看一下這四個主成分解釋了多少方差:

  1. plt.plot(range(4), pca.explained_variance_ratio_)

  2. plt.plot(range(4), np.cumsum(pca.explained_variance_ratio_))

  3. plt.title("Component-wise and Cumulative Explained Variance")

在上圖中,藍線表示分量解釋的方差,而橙線表示累積解釋的方差。我們只用四個成分就解釋了資料集中約60%的方差。

9. 獨立分量分析(ICA)

獨立分量分析(ICA)基於資訊理論,是最廣泛使用的降維技術之一。PCA和ICA之間的主要區別在於,PCA尋找不相關的因素,而ICA尋找獨立因素。

如果兩個變數不相關,它們之間就沒有線性關係。如果它們是獨立的,它們就不依賴於其他變數。例如,一個人的年齡和他吃了什麼/看了什麼電視無關。

該演算法假設給定變數是一些未知潛在變數的線性混合。它還假設這些潛在變數是相互獨立的,即它們不依賴於其他變數,因此它們被稱為觀察資料的獨立分量。

下圖是ICA和PCA的一個直觀比較:

(a)PCA,(b)ICA

PCA的等式是x = Wχ。

這裡,

  • x是觀察結果

  • W是混合矩陣

  • χ是來源或獨立成分

現在我們必須找到一個非混合矩陣,使成分儘可能獨立。而測試成分獨立性最常用的方法是非高斯性:

  • 根據中心極限定理(Central Limit Theorem),多個獨立隨機變數混合之後會趨向於正態分佈(高斯分佈)。

  • 因此,我們可以尋找所有獨立分量中能最大化峰度的分量。

  • 一旦峰度被最大化,整個分佈會呈現非高斯分佈,我們也能得到獨立分量。

在Python中實現ICA:

  1. from sklearn.decomposition importFastICA

  2. ICA = FastICA(n_components=3, random_state=12)

  3. X=ICA.fit_transform(df[feat_cols].values)

10. IOSMAP

程式碼:

  1. from sklearn import manifold

  2. trans_data = manifold.Isomap(n_neighbors=5, n_components=3, n_jobs=-1).fit_transform(df[feat_cols][:6000].values)

使用的引數:

  • n_neighbors:決定每個點的相鄰點數

  • n_components:決定流形的座標數

  • n_jobs = -1:使用所有可用的CPU核心

視覺化:

  1. plt.figure(figsize=(12,8))

  2. plt.title('Decomposition using ISOMAP')

  3. plt.scatter(trans_data[:,0], trans_data[:,1])

  4. plt.scatter(trans_data[:,1], trans_data[:,2])

  5. plt.scatter(trans_data[:,2], trans_data[:,0])

11. t-SNE

程式碼:

  1. from sklearn.manifold import TSNE

  2. tsne = TSNE(n_components=3, n_iter=300).fit_transform(df[feat_cols][:6000].values)

視覺化:

  1. plt.figure(figsize=(12,8))

  2. plt.title('t-SNE components')

  3. plt.scatter(tsne[:,0], tsne[:,1])

  4. plt.scatter(tsne[:,1], tsne[:,2])

  5. plt.scatter(tsne[:,2], tsne[:,0])

12. UMAP

程式碼:

  1. import umap

  2. umap_data = umap.UMAP(n_neighbors=5, min_dist=0.3, n_components=3).fit_transform(df[feat_cols][:6000].values)

這裡,

  • n_neighbors:確定相鄰點的數量。

  • min_dist:控制允許嵌入的緊密程度,較大的值可確保嵌入點的分佈更均勻。

視覺化:

  1. plt.figure(figsize=(12,8))

  2. plt.title('Decomposition using UMAP')

  3. plt.scatter(umap_data[:,0], umap_data[:,1])

  4. plt.scatter(umap_data[:,1], umap_data[:,2])

  5. plt.scatter(umap_data[:,2], umap_data[:,0])

總結

到目前為止,我們已經介紹了12種降維方法,考慮到篇幅,我們沒有仔細介紹後三種方法的原理,感興趣的讀者可以找資料查閱,因為它們中的任何一個都足夠寫一篇專門介紹的長文。本節會對這12種方法做一個總結,簡要介紹它們的優點和缺點。

  • 缺失值比率:如果資料集的缺失值太多,我們可以用這種方法減少變數數。

  • 低方差濾波:這個方法可以從資料集中識別和刪除常量變數,方差小的變數對目標變數影響不大,所以可以放心刪去。

  • 高相關濾波:具有高相關性的一對變數會增加資料集中的多重共線性,所以用這種方法刪去其中一個是有必要的。

  • 隨機森林:這是最常用的降維方法之一,它會明確算出資料集中每個特徵的重要性。

  • 前向特徵選擇反向特徵消除:這兩種方法耗時較久,計算成本也都很高,所以只適用於輸入變數較少的資料集。

  • 因子分析:這種方法適合資料集中存在高度相關的變數集的情況。

  • PCA:這是處理線性資料最廣泛使用的技術之一。

  • ICA:我們可以用ICA將資料轉換為獨立的分量,使用更少的分量來描述資料。

  • ISOMAP:適合非線性資料處理。

  • t-SNE:也適合非線性資料處理,相較上一種方法,這種方法的視覺化更直接。

  • UMAP:適用於高維資料,與t-SNE相比,這種方法速度更快。

原文地址:www.analyticsvidhya.com/blog/2018/08/dimensionality-reduction-techniques-python/

推薦閱讀: