Python數(shù)據(jù)科學(xué):正則化方法
本文主要介紹,Python數(shù)據(jù)科學(xué):正則化方法。正則化方法的出現(xiàn),通過(guò)收縮方法(正則化方法)進(jìn)行回歸。
正則化方法主要包括嶺回歸與LASSO回歸。
一、嶺回歸
嶺回歸通過(guò)人為加入的懲罰項(xiàng)(約束項(xiàng)),對(duì)回歸系數(shù)進(jìn)行估計(jì),為有偏估計(jì)。
有偏估計(jì),允許估計(jì)有不大的偏度,以換取估計(jì)的誤差顯著減小,并在其殘差平方和為最小的原則下估計(jì)回歸系數(shù)。
通常嶺回歸方程中的R²會(huì)稍低于線性回歸分析,但回歸系數(shù)的顯著性往往明顯高于普通線性回歸。
這里不對(duì)相應(yīng)的理論知識(shí)進(jìn)行細(xì)說(shuō),說(shuō)實(shí)話小F也是暈乎乎...
所以選擇先調(diào)包,看看效果是啥樣的。
使用機(jī)器學(xué)習(xí)框架scikit-learn進(jìn)行嶺回歸參數(shù)的選擇(正則化系數(shù))。
數(shù)據(jù)是書(shū)中的數(shù)據(jù),已上傳網(wǎng)盤(pán),公眾號(hào)回復(fù)「正則化」,即可獲取。
scikit-learn當(dāng)中的模型不會(huì)默認(rèn)對(duì)數(shù)據(jù)標(biāo)準(zhǔn)化,必須手動(dòng)執(zhí)行。
標(biāo)準(zhǔn)化后的數(shù)據(jù)可以消除量綱,讓每個(gè)變量的系數(shù)在一定意義下進(jìn)行直接比較。
- import numpy as np
- import pandas as pd
- import matplotlib.pyplot as plt
- from sklearn.linear_model import Ridge
- from sklearn.linear_model import RidgeCV
- from sklearn.preprocessing import StandardScaler
- # 消除pandas輸出省略號(hào)情況及換行情況
- pd.set_option('display.max_columns', 500)
- pd.set_option('display.width', 1000)
- # 讀取數(shù)據(jù),skipinitialspace:忽略分隔符后的空白
- df = pd.read_csv('creditcard_exp.csv', skipinitialspace=True)
- # 獲取信用卡有支出的行數(shù)據(jù)
- exp = df[df['avg_exp'].notnull()].copy().iloc[:, 2:].drop('age2', axis=1)
- # 獲取信用卡無(wú)支出的行數(shù)據(jù),NaN
- exp_new = df[df['avg_exp'].isnull()].copy().iloc[:, 2:].drop('age2', axis=1)
- # 選擇4個(gè)連續(xù)變量,分別是年齡 收入 當(dāng)?shù)匦^(qū)價(jià)格 當(dāng)?shù)厝司杖?nbsp;
- continuous_xcols = ['Age', 'Income', 'dist_home_val', 'dist_avg_income']
- # 標(biāo)準(zhǔn)化
- scaler = StandardScaler()
- # 解釋變量,二維數(shù)組
- X = scaler.fit_transform(exp[continuous_xcols])
- # 被解釋變量,一維數(shù)組
- y = exp['avg_exp_ln']
- # 生成正則化系數(shù)
- alphas = np.logspace(-2, 3, 100, base=10)
- # 使用不同的正則化系數(shù)對(duì)模型進(jìn)行交叉驗(yàn)證
- rcv = RidgeCV(alphas=alphas, store_cv_values=True)
- # 使用數(shù)據(jù)集訓(xùn)練(fit)
- rcv.fit(X, y)
- # 輸出最優(yōu)參數(shù),正則化系數(shù)及相應(yīng)模型R²
- print('The best alpha is {}'.format(rcv.alpha_))
- print('The r-square is {}'.format(rcv.score(X, y)))
- # 訓(xùn)練好后使用transform進(jìn)行數(shù)據(jù)轉(zhuǎn)換
- X_new = scaler.transform(exp_new[continuous_xcols])
- # 使用模型對(duì)數(shù)據(jù)做預(yù)測(cè)
- print(np.exp(rcv.predict(X_new)[:5]))
輸出結(jié)果如下。
最優(yōu)正則化系數(shù)為0.29,模型R²為0.475。
并使用最優(yōu)正則化系數(shù)下的嶺回歸模型預(yù)測(cè)數(shù)據(jù)。
對(duì)不同正則化系數(shù)下模型的均方誤差進(jìn)行可視化。
- # 正則化系數(shù)搜索空間當(dāng)中每輪交叉驗(yàn)證的結(jié)果,模型的均方誤差
- cv_values = rcv.cv_values_
- n_fold, n_alphas = cv_values.shape
- # 模型均方誤差上下波動(dòng)值
- cv_mean = cv_values.mean(axis=0)
- cv_std = cv_values.std(axis=0)
- ub = cv_mean + cv_std / np.sqrt(n_fold)
- lb = cv_mean - cv_std / np.sqrt(n_fold)
- # 繪制折線圖,x軸是指數(shù)型形式
- plt.semilogx(alphas, cv_mean, label='mean_score')
- # y1(lb)和y2(ub)之間進(jìn)行填充
- plt.fill_between(alphas, lb, ub, alpha=0.2)
- plt.xlabel('$\\alpha$')
- plt.ylabel('mean squared errors')
- plt.legend(loc='best')
- plt.show()
輸出結(jié)果如下。
發(fā)現(xiàn)正則化系數(shù)在40或50以下時(shí),模型的均方誤差相差不大。
當(dāng)系數(shù)超過(guò)該閾值時(shí),均方誤差則快速上升。
所以正則化系數(shù)只要小于40或50,模型的擬合效果應(yīng)該都不錯(cuò)。
- 正則化系數(shù)越小則模型擬合越好,但過(guò)擬合情況也越容易發(fā)生。
- 正則化系數(shù)越大,則越不容易過(guò)擬合,但模型的偏差越大。
RidgeCV通過(guò)交叉驗(yàn)證,可以快速返回“最優(yōu)”的正則化系數(shù)。
當(dāng)這只是基于數(shù)值計(jì)算的,可能最終結(jié)果并不符合業(yè)務(wù)邏輯。
比如本次模型的變量系數(shù)。
- # 輸出模型的變量系數(shù)
- print(rcv.coef_)
- # 輸出結(jié)果
- [ 0.03321449 -0.30956185 0.05551208 0.59067449]
發(fā)現(xiàn)收入的系數(shù)為負(fù)值,這肯定是不合理的。
下面通過(guò)嶺跡圖進(jìn)行進(jìn)一步分析。
嶺跡圖是在不同正則化系數(shù)下變量系數(shù)的軌跡。
- ridge = Ridge()
- coefs = []
- # 不同正則化系數(shù)下的變量系數(shù)
- for alpha in alphas:
- ridge.set_params(alpha=alpha)
- ridge.fit(X, y)
- coefs.append(ridge.coef_)
- # 繪制變量系數(shù)隨正則化系數(shù)變化的軌跡
- ax = plt.gca()
- ax.plot(alphas, coefs)
- ax.set_xscale('log')
- plt.xlabel('alpha')
- plt.ylabel('weights')
- plt.title('Ridge coefficients as a function of the regularization')
- plt.axis('tight')
- plt.show()
輸出結(jié)果。
- ①有兩個(gè)變量的系數(shù)在不同的正則化系數(shù)下都很接近于0,那么可以選擇刪除。
- ②正則化系數(shù)越大,對(duì)變量系數(shù)的懲罰越大,所有變量的系數(shù)都趨近于0。
- ③有一個(gè)變量的系數(shù)變化非常大(有正有負(fù)),說(shuō)明該系數(shù)的方差大,存在共線性的情況。
綜合模型均方誤差和嶺跡圖的情況,選取正則化系數(shù)為40。
- 如果大于40,則模型均方誤差增大,模型擬合效果變差。
- 如果小于40,則變量系數(shù)不穩(wěn)定,共線性沒(méi)有得到抑制。
那么就來(lái)看看,當(dāng)正則化系數(shù)為40時(shí),模型變量系數(shù)的情況。
- ridge.set_params(alpha=40)
- ridge.fit(X, y)
- # 輸出變量系數(shù)
- print(ridge.coef_)
- # 輸出模型R²
- print(ridge.score(X, y))
- # 預(yù)測(cè)數(shù)據(jù)
- print(np.exp(ridge.predict(X_new)[:5]))
- # 輸出結(jié)果
- [0.03293109 0.09907747 0.04976305 0.12101456]
- 0.4255673043353688
- [934.79025945 727.11042209 703.88143602 759.04342764 709.54172995]
發(fā)現(xiàn)變量系數(shù)都為正值,符合業(yè)務(wù)直覺(jué)。
收入和當(dāng)?shù)厝司杖脒@兩個(gè)變量可以保留,另外兩個(gè)刪除。
二、LASSO回歸
LASSO回歸,在令回歸系數(shù)的絕對(duì)值之和小于一個(gè)常數(shù)的約束條件下,使殘差平方和最小化。
從而能夠產(chǎn)生某些嚴(yán)格等于0的回歸系數(shù),得到解釋力較強(qiáng)的模型。
相比嶺回歸,LASSO回歸還可以進(jìn)行變量篩選。
使用LassoCV交叉驗(yàn)證確定最優(yōu)的正則化系數(shù)。
- # 生成正則化系數(shù)
- lasso_alphas = np.logspace(-3, 0, 100, base=10)
- # 使用不同的正則化系數(shù)對(duì)模型進(jìn)行交叉驗(yàn)證
- lcv = LassoCV(alphas=lasso_alphas, cv=10)
- # 使用數(shù)據(jù)集訓(xùn)練(fit)
- lcv.fit(X, y)
- # 輸出最優(yōu)參數(shù),正則化系數(shù)及相應(yīng)模型R²
- print('The best alpha is {}'.format(lcv.alpha_))
- print('The r-square is {}'.format(lcv.score(X, y)))
- # 輸出結(jié)果
- The best alpha is 0.04037017258596556
- The r-square is 0.4426451069862233
發(fā)現(xiàn)最優(yōu)的正則化系數(shù)為0.04,模型R²為0.443。
接下來(lái)獲取不同正則化系數(shù)下的變量系數(shù)軌跡。
- lasso = Lasso()
- lasso_coefs = []
- # 不同正則化系數(shù)下的變量系數(shù)
- for alpha in lasso_alphas:
- lasso.set_params(alpha=alpha)
- lasso.fit(X, y)
- lasso_coefs.append(lasso.coef_)
- # 繪制變量系數(shù)隨正則化系數(shù)變化的軌跡
- ax = plt.gca()
- ax.plot(lasso_alphas, lasso_coefs)
- ax.set_xscale('log')
- plt.xlabel('alpha')
- plt.ylabel('weights')
- plt.title('Lasso coefficients as a function of the regularization')
- plt.axis('tight')
- plt.show()
輸出結(jié)果。
發(fā)現(xiàn)隨著正則化系數(shù)的增大,所有變量的系數(shù)會(huì)在某一閾值突降為0。
其中緣由與LASSO回歸方程有關(guān),不細(xì)說(shuō)。
輸出LASSO回歸的變量系數(shù)。
- print(lcv.coef_)
- # 輸出結(jié)果
- [0. 0. 0.02789489 0.26549855]
發(fā)現(xiàn)前兩個(gè)變量被篩選掉了,即年齡和收入。
為啥和嶺回歸的結(jié)果不一樣呢???
三、總結(jié)
坑留的有點(diǎn)多,待小F慢慢填...