為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)

為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)

翻譯:張恬鈺

校對:吳金笛

本文5000字,建議閱讀12分鐘。

本文討論了幾種子集和收縮方法:最佳子集迴歸, 嶺迴歸, LASSO, 彈性網, 最小角度迴歸, 主成分迴歸和偏最小二乘。

本文討論了七種流行的收縮和選擇方法的數學屬性和實際的Python應用。

在本文中,我們將介紹七種流行的子集選擇和線性迴歸收縮方法。在介紹了證明需要這些方法的主題之後,我們將逐一研究每種方法,包括數學屬性和Python應用程序。

為什麼收縮或子集,這是什麼意思?

在線性迴歸上下文中,子集意味著從可用變量中選擇要包含在模型中的子集,從而減少其維數。另一方面,收縮意味著減小系數估計的大小(將它們縮小到零)。請注意,如果係數縮小到恰好為零,則相應的變量將退出模型。因此,這種情況也可以看作是一種子集。

收縮和選擇旨在改進簡單的線性迴歸。關於為什麼需要改進,這裡有兩個原因:

  • 預測準確性:線性迴歸估計傾向於具有低偏差和高方差。降低模型複雜性(需要估計的參數數量)導致減少差異,但代價是引入更多偏差。如果我們能夠找到總誤差的最佳位置,那麼偏差導致的誤差加上方差的誤差被最小化,這樣我們就可以改進模型的預測。
  • 模型的可解釋性:由於預測變量太多,人類很難掌握變量之間的所有關係。在某些情況下,我們願意確定影響最大的一小部分變量,為全局著想而犧牲一些細節。

設置和數據加載

在直接跳到方法本身之前,讓我們先看看我們將要分析的數據集。它來自Stamey等人的一項研究(1989)。Stamey研究了不同臨床測量對前列腺特異性抗原(PSA)水平的影響。任務是根據一組臨床和人口統計學變量確定前列腺癌的風險因素。數據以及變量的一些描述可以在Hastie等人的網站以及 “統計學習的要素”教科書的數據部分找到。

Hastie等人的網站

http://web.stanford.edu/~hastie/ElemStatLearn/

我們將首先導入本文中使用的模塊,加載數據並將其拆分為訓練和測試集,分別保留目標和特徵。然後,我們將討論每種收縮和選擇方法,使其適合訓練數據,並使用測試集檢查它預測新數據的PSA水平的效果如何。

# Import necessary modules and set options

import pandas as pd

import numpy as np

import itertools

from sklearn.linear_model import LinearRegression, RidgeCV, LassoCV, ElasticNetCV, LarsCV

from sklearn.cross_decomposition import PLSRegression

from sklearn.decomposition import PCA

from sklearn.pipeline import Pipeline

from sklearn.model_selection import GridSearchCV

import warnings

warnings.filterwarnings("ignore")

# Load data

data = pd.read_csv("prostate_data", sep = "\t")

print(data.head())

# Train-test split

y_train = np.array(data[data.train == "T"]['lpsa'])

y_test = np.array(data[data.train == "F"]['lpsa'])

X_train = np.array(data[data.train == "T"].drop(['lpsa', 'train'], axis=1))

X_test = np.array(data[data.train == "F"].drop(['lpsa', 'train'], axis=1))

線性迴歸

讓我們從簡單的線性迴歸開始,這將構成我們的基準。它將目標變量y建模為p個預測變量或特徵X的線性組合:


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


該模型具有必須從訓練數據估計的p + 2個參數:

  • p個特徵係數β,每個變量一個,表示它們對目標的影響;
  • 一個截距參數,表示為上面的β0,它是在所有X都為零的情況下的預測。不一定要將它包含在模型中,實際上在某些情況下應該將其刪除(例如,如果想要包含表示分類變量級別的全套虛擬對象),但通常它會給模型更大的靈活性,正如你將在下一段中看到的;
  • 高斯誤差項的一個方差參數。

通常使用普通最小二乘法(OLS)估計這些參數。 OLS最小化殘差平方和,由下式給出


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


以圖形方式考慮這種最小化標準是有幫助的。只有一個預測變量X時,我們處於由預測變量和目標形成的2D空間中。在此設置中,模型在最接近所有數據點的X-Y空間中擬合這樣的線:接近度測量為所有數據點的垂直距離的平方和 - 請參見下面的左圖。如果有兩個預測變量X1和X2,則空間增長到3D,現在模型擬合最接近3D空間中所有點的平面 - 請參見下面的右圖。有了兩個以上的特徵,平面變成了有點抽象的超平面,但這個思想仍然是一樣的。這些可視化也有助於瞭解截距如何為模型提供更大的靈活性:如果包含它,它允許線或平面不跨越空間的原點。


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


上述最小化問題證明具有解析解,並且β參數可以被計算為


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


在X矩陣中包括一列1可以表達上述公式中的β帽矢量的截距部分。 “β”上方的“帽子”表示它是基於訓練數據的估計值。

偏差-方差權衡

在統計學中,要考慮估計量的兩個關鍵特徵:偏差和方差。偏差是真實總體參數和預期估計量之間的差異。它衡量估計數的不準確性。另一方面,方差衡量它們之間的差異。


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


顯然,如果模型太大,偏差和方差都會損害模型的預測性能。然而,線性迴歸更受到方差的影響,同時具有低偏差。如果模型中存在許多預測特徵或者它們彼此高度相關,則尤其如此。這就是用到子集化和正則化來修正的地方。它們允許以引入一些偏差為代價來減少方差,最終減少模型的總誤差。

在詳細討論這些方法之前,讓我們將線性迴歸擬合到前列腺數據中並檢查其樣本外的平均預測誤差(MAE)。

linreg_model = LinearRegression(normalize=True).fit(X_train, y_train)

linreg_prediction = linreg_model.predict(X_test)

linreg_mae = np.mean(np.abs(y_test - linreg_prediction))

linreg_coefs = dict(

zip(['Intercept'] + data.columns.tolist()[:-1],

np.round(np.concatenate((linreg_model.intercept_, linreg_model.coef_),

axis=None), 3))

)

print('Linear Regression MAE: {}'.format(np.round(linreg_mae, 3)))

print('Linear Regression coefficients:')

linreg_coefs

最佳子集迴歸

選擇線性迴歸變量子集的直接方法是嘗試所有可能的組合,並選擇一個最小化某些標準的組合。這就是最佳子集迴歸的目標。對於每個k∈{1,2,...,p},其中p是可用特徵的總數,它選擇大小為k的子集,其給出最小的殘差平方和。然而,平方和不能用作確定k本身的標準,因為它必然隨k減小:模型中包含的變量越多,其殘差越小。但這並不能保證更好的預測性能。這就是為什麼應該使用另一個標準來選擇最終模型的原因。對於專注於預測的模型,測試數據上的(可能是交叉驗證的)錯誤是常見的選擇。

由於最佳子集迴歸沒有在任何Python包中實現,我們必須手動循環k和k大小的所有子集。以下代碼塊完成了這項工作。

results = pd.DataFrame(columns=['num_features', 'features', 'MAE'])

# Loop over all possible numbers of features to be included

for k in range(1, X_train.shape[1] + 1):

# Loop over all possible subsets of size k

for subset in itertools.combinations(range(X_train.shape[1]), k):

subset = list(subset)

linreg_model = LinearRegression(normalize=True).fit(X_train[:, subset], y_train)

linreg_prediction = linreg_model.predict(X_test[:, subset])

linreg_mae = np.mean(np.abs(y_test - linreg_prediction))

results = results.append(pd.DataFrame([{'num_features': k,

'features': subset,

'MAE': linreg_mae}]))

# Inspect best combinations

results = results.sort_values('MAE').reset_index()

print(results.head())

# Fit best model

best_subset_model = LinearRegression(normalize=True).fit(X_train[:, results['features'][0]], y_train)

best_subset_coefs = dict(

zip(['Intercept'] + data.columns.tolist()[:-1],

np.round(np.concatenate((best_subset_model.intercept_, best_subset_model.coef_), axis=None), 3))

)

print('Best Subset Regression MAE: {}'.format(np.round(results['MAE'][0], 3)))

print('Best Subset Regression coefficients:')

best_subset_coefs

嶺迴歸

最佳子集迴歸的一個缺點是它沒有告訴我們關於從模型中排除的變量對響應變量的影響。嶺迴歸提供了這種難以選擇的變量的替代方案,這些變量將它們分解為模型中包括的和不包括的。相反,它懲罰係數以將它們縮小到零。不完全為零,因為這意味著從模型中移除,但是在零方向上,這可以被視為以連續方式降低模型的複雜性,同時將所有變量保持在模型中。

在嶺迴歸中,線性迴歸損失函數以這樣的方式增強,不僅可以最小化殘差平方和,還可以懲罰參數估計的大小:


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


解決這個最小化問題可得到βs的分析公式:


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


其中I表示單位矩陣。懲罰項λ是要選擇的超參數:其值越大,係數越向零收縮。從上面的公式可以看出,當λ變為零時,加性罰分消失,β-ridge與線性迴歸中的β-OLS相同。另一方面,隨著λ增長到無窮大,β-ridge接近零:在足夠高的懲罰下,係數可以任意地收縮到接近零。

但這種收縮是否真的會導致減少模型的方差並以承諾的方式引入一些偏差呢?是的,確實如此,從嶺迴歸估計的偏差和方差的公式中可以清楚地看出:隨著λ的增加,偏差也隨之增加,而方差則下降!


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


現在,如何選擇λ的最佳值?進行交叉驗證嘗試一組不同的值,並選擇一個最小化測試數據上交叉驗證錯誤的值。幸運的是,Python的scikit-learn可以為我們做到這一點。

ridge_cv = RidgeCV(normalize=True, alphas=np.logspace(-10, 1, 400))

ridge_model = ridge_cv.fit(X_train, y_train)

ridge_prediction = ridge_model.predict(X_test)

ridge_mae = np.mean(np.abs(y_test - ridge_prediction))

ridge_coefs = dict(

zip(['Intercept'] + data.columns.tolist()[:-1],

np.round(np.concatenate((ridge_model.intercept_, ridge_model.coef_),

axis=None), 3))

)

print('Ridge Regression MAE: {}'.format(np.round(ridge_mae, 3)))

print('Ridge Regression coefficients:')

ridge_coefs

LASSO

Lasso,或最小絕對收縮和選擇算子,在本質上與嶺迴歸非常相似。它也為損失函數的非零係數增加了一個懲罰,但與懲罰平方係數之和(所謂的L2懲罰)的嶺迴歸不同,LASSO懲罰它們的絕對值之和(L1懲罰)。因此,對於λ的高值,許多係數在LASSO下完全歸零,在嶺迴歸中從未如此。

它們之間的另一個重要區別是它們如何解決這些特徵之間的多重共線性問題。在嶺迴歸中,相關變量的係數趨於相似,而在LASSO中,其中一個通常為零,另一個則賦值為整個影響。因此,如果存在相同值的許多大參數,即當大多數預測器真正影響響應時,預期嶺迴歸將有更好的效果。另一方面,當存在少量重要參數且其他參數接近零時,即當只有少數預測因子實際影響響應時,LASSO將佔據首位。

然而,在實踐中,人們不知道參數的真實值。因此,嶺迴歸和LASSO之間的選擇可以基於樣本外預測誤差。另一種選擇是將這兩種方法合二為一 - 見下一節!

LASSO的損失函數如下:


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


與嶺迴歸不同,這種最小化問題無法通過分析解決。幸運的是,有數值算法可以處理它。

lasso_cv = LassoCV(normalize=True, alphas=np.logspace(-10, 1, 400))

lasso_model = lasso_cv.fit(X_train, y_train)

lasso_prediction = lasso_model.predict(X_test)

lasso_mae = np.mean(np.abs(y_test - lasso_prediction))

lasso_coefs = dict(

zip(['Intercept'] + data.columns.tolist()[:-1],

np.round(np.concatenate((lasso_model.intercept_, lasso_model.coef_), axis=None), 3))

)

print('LASSO MAE: {}'.format(np.round(lasso_mae, 3)))

print('LASSO coefficients:')

lasso_coefs

彈性網

彈性網首先是對LASSO的批評而產生的,LASSO的變量選擇過於依賴數據,因而不穩定。它的解決方案是將嶺迴歸和LASSO的懲罰結合起來,以獲得兩全其美的效果。彈性網旨在最大限度地減少包括L1和L2懲罰的損失函數:


為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


其中α是嶺迴歸(當它為零時)和LASSO(當它為1時)之間的混合參數。可以使用基於scikit-learn的基於交叉驗證的超左側調整來選擇最佳α。

elastic_net_cv = ElasticNetCV(normalize=True, alphas=np.logspace(-10, 1, 400),

l1_ratio=np.linspace(0, 1, 100))

elastic_net_model = elastic_net_cv.fit(X_train, y_train)

elastic_net_prediction = elastic_net_model.predict(X_test)

elastic_net_mae = np.mean(np.abs(y_test - elastic_net_prediction))

elastic_net_coefs = dict(

zip(['Intercept'] + data.columns.tolist()[:-1],

np.round(np.concatenate((elastic_net_model.intercept_,

elastic_net_model.coef_), axis=None), 3))

)

print('Elastic Net MAE: {}'.format(np.round(elastic_net_mae, 3)))

print('Elastic Net coefficients:')

elastic_net_coefs

最小角度迴歸

到目前為止,我們已經討論了一種子集化方法,最佳子集迴歸和三種收縮方法:嶺迴歸,LASSO及其組合,彈性網絡。本節專門介紹位於子集和收縮之間的方法:最小角度迴歸(LAR)。該算法以空模型開始,所有係數等於零,然後迭代地工作,在每個步驟將一個變量的係數移向其最小二乘值。

更具體地說,LAR從識別與響應最相關的變量開始。然後,它將該變量的係數連續地移向其最小平方值,從而降低其與演化殘差的相關性。一旦另一個變量在與殘差的相關性方面“趕上”,該過程就會暫停。然後,第二個變量加入有效集,即具有非零係數的變量集,並且它們的係數以保持它們的相關性連接和減少的方式一起移動。繼續該過程直到所有變量都在模型中,並以完全最小二乘擬合結束。名稱“最小角度迴歸”來自算法的幾何解釋,其中給定步驟處的新擬合方向與已經具有非零係數的每個特徵形成最小角度。

下面的代碼塊將LAR應用於前列腺數據。

LAR_cv = LarsCV(normalize=True)

LAR_model = LAR_cv.fit(X_train, y_train)

LAR_prediction = LAR_model.predict(X_test)

LAR_mae = np.mean(np.abs(y_test - LAR_prediction))

LAR_coefs = dict(

zip(['Intercept'] + data.columns.tolist()[:-1],

np.round(np.concatenate((LAR_model.intercept_, LAR_model.coef_), axis=None), 3))

)

print('Least Angle Regression MAE: {}'.format(np.round(LAR_mae, 3)))

print('Least Angle Regression coefficients:')

LAR_coefs

主成分迴歸

我們已經討論了選擇變量(子集)和降低係數(收縮率)的方法。本文中介紹的最後兩種方法採用了稍微不同的方法:它們將原始要素的輸入空間擠壓到較低維度的空間中。主要是,他們使用X創建一小組新特徵Z,它們是X的線性組合,然後在迴歸模型中使用它們。

這兩種方法中的第一種是主成分迴歸。它應用主成分分析,這種方法允許獲得一組新特徵,彼此不相關且具有高方差(以便它們可以解釋目標的方差),然後將它們用作簡單線性迴歸中的特徵。這使得它類似於嶺迴歸,因為它們都在原始特徵的主成分空間上運行(對於基於PCA的嶺迴歸推導,參見本文底部的Sources中的[1])。不同之處在於PCR丟棄具有最少信息功能的組件,而嶺迴歸只是將它們收縮得更強。

要重新獲得的組件數量可以視為超參數,並通過交叉驗證進行調整,如下面的代碼塊中的情況。

regression_model = LinearRegression(normalize=True)

pca_model = PCA()

pipe = Pipeline(steps=[('pca', pca_model), ('least_squares', regression_model)])

param_grid = {'pca__n_components': range(1, 9)}

search = GridSearchCV(pipe, param_grid)

pcareg_model = search.fit(X_train, y_train)

pcareg_prediction = pcareg_model.predict(X_test)

pcareg_mae = np.mean(np.abs(y_test - pcareg_prediction))

n_comp = list(pcareg_model.best_params_.values())[0]

pcareg_coefs = dict(

zip(['Intercept'] + ['PCA_comp_' + str(x) for x in range(1, n_comp + 1)],

np.round(np.concatenate((pcareg_model.best_estimator_.steps[1][1].intercept_,

pcareg_model.best_estimator_.steps[1][1].coef_), axis=None), 3))

)

print('Principal Components Regression MAE: {}'.format(np.round(pcareg_mae, 3)))

print('Principal Components Regression coefficients:')

pcareg_coefs

偏最小二乘法

本文討論的最終方法是偏最小二乘法(PLS)。與主成分迴歸類似,它也使用原始要素的一小組線性組合。不同之處在於如何構建這些組合。雖然主成分迴歸僅使用X自身來創建派生特徵Z,但偏最小二乘另外使用目標y。因此,在構建Z時,PLS尋找具有高方差的方向(因為這些可以解釋目標中的方差)以及與目標的高相關性。與主成分分析形成對比,主成分分析僅關注高差異。

在算法的驅動下,第一個新特徵z1被創建為所有特徵X的線性組合,其中每個X由其內積與目標y加權。然後,y在z1上回歸,給出PLSβ係數。最後,所有X都相對於z1正交化。然後,該過程重新開始z2並繼續,直到獲得Z中所需的組件數量。像往常一樣,這個數字可以通過交叉驗證來選擇。

可以證明,儘管PLS根據需要縮小了Z中的低方差分量,但它有時會使高方差分量膨脹,這可能導致在某些情況下更高的預測誤差。這似乎是我們的前列腺數據的情況:PLS在所有討論的方法中表現最差。

pls_model_setup = PLSRegression(scale=True)

param_grid = {'n_components': range(1, 9)}

search = GridSearchCV(pls_model_setup, param_grid)

pls_model = search.fit(X_train, y_train)

pls_prediction = pls_model.predict(X_test)

pls_mae = np.mean(np.abs(y_test - pls_prediction))

pls_coefs = dict(

zip(data.columns.tolist()[:-1],

np.round(np.concatenate((pls_model.best_estimator_.coef_), axis=None), 3))

)

print('Partial Least Squares Regression MAE: {}'.format(np.round(pls_mae, 3)))

print('Partial Least Squares Regression coefficients:')

pls_coefs

回顧與結論

由於模型參數存在很大的方差,線性模型具有許多可能相關的特徵,導致預測精度和模型的可解釋性方面較差。這可以通過減少方差來緩解,這種方差只會以引入一些偏差為代價。然而,找到最佳的偏差 - 方差權衡可以優化模型的性能。

允許實現此目的的兩大類方法是子集和收縮。前者選擇變量的子集,而後者將模型的係數縮小為零。這兩種方法都會降低模型的複雜性,從而導致參數方差的減少。

本文討論了幾種子集和收縮方法:

  • 最佳子集迴歸迭代所有可能的特徵組合以選擇最佳特徵組合;
  • 嶺迴歸懲罰平方係數值(L2懲罰),強制它們很小;
  • LASSO懲罰係數的絕對值(L1懲罰),這可以迫使它們中的一些精確為零;
  • 彈性網結合了L1和L2的懲罰,享受了Ridge和Lasso的精華;
  • 最小角度迴歸適用於子集和收縮之間:它迭代地工作,在每個步驟中添加其中一個特徵的“某個部分”;
  • 主成分迴歸執行PCA將原始特徵壓縮為一小部分新特徵,然後將其用作預測變量;
  • 偏最小二乘也將原始特徵概括為較小的新特徵子集,但與PCR不同,它也利用目標構建它們。


正如您在上面運行代碼塊時從應用程序看到的前列腺數據,大多數這些方法在預測準確性方面表現相似。前5種方法的誤差範圍在0.467和0.517之間,擊敗最小二乘誤差為0.523。最後兩個,PCR和PLS,表現更差,可能是由於數據中沒有那麼多特徵,因此降維的收益是有限的。

謝謝閱讀!我希望你學到了新東西!

來源

  • Hastie, T., Tibshirani, R., & Friedman, J. H. (2009). The elements of statistical learning: data mining, inference, and prediction. 2nd ed. New York: Springer.
  • https://www.datacamp.com/community/tutorials/tutorial-ridge-lasso-elastic-net


原文標題:

A Comparison of Shrinkage and Selection Methods for Linear Regression

原文鏈接:

https://towardsdatascience.com/a-comparison-of-shrinkage-and-selection-methods-for-linear-regression-ee4dd3a71f16

校對:洪舒越

譯者簡介

為你介紹7種流行的線性迴歸收縮與選擇方法(附代碼)


張恬鈺,上海交通大學本科物理專業,Emory University生物統計碩士在讀。以後想繼續在生物統計方向深造。希望能留在美國學習和工作。希望能和廣大的數據愛好者做朋友!

— 完 —

關注清華-青島數據科學研究院官方微信公眾平臺“THU數據派”及姊妹號“數據派THU”獲取更多講座福利及優質內容。


分享到:


相關文章: