Lasso Regression
標籤(空格分隔): 監督學習
在數據挖掘和機器學習算法的模型建立之初,爲了儘量的減少因缺少重要變量而出現的模型偏差問題,我們通常會儘可能的多的選擇自變量。但是在實際建模的過程中,通常又需要尋找 對響應變量具有解釋能力的自變量子集,以提高模型的解釋能力與預測精度,這個過程稱爲特徵選擇。
還是考慮《線性迴歸》中的一般線性迴歸模型
最小二乘估計雖然有不錯的解析性,但是其在大多數情況下的數據分析能力是不夠的,主要有兩個原因:
-
預測精度問題:最小二乘法雖然是無偏估計,但是他的方差在自變量存在多重共線性(變量間線性相關)時會非常大,這個可以通過將某些係數壓縮到0來改進預測精度,但這個是以一定的有偏爲代價來降低預測值的方差。
-
模型的可解釋性:自變量個數很多的時候,我們總是希望能夠確定一個較小的變量模型來表現較好的結果
對於以上的問題,就有兩種方法可以對最小二乘估計進行改進:子集選擇和脊歸回。子集選擇過程中,對變量要麼保留,要麼剔除,這很可能使得觀測數據的一個微小變動就導致要選擇一個新的模型,使得模型變得不穩定,但由於模型的變量少了,使得模型的解釋性得到了提高;脊迴歸是一個連續的方法,它在不拋棄任何一個變量的情況下,縮小了迴歸係數,使得模型相對而言比較的穩定,但這會使得模型的變量特別多,模型解釋性差。
基於以上的問題,纔有了現在要說的一種新的變量選擇技術:Lasso(Least Absolute Shrinkage and Selection Operator)。這種方法使用模型係數的
Lasso迴歸模型,是一個用於估計稀疏參數的線性模型,特別適用於參數數目縮減。基於這個原因,Lasso迴歸模型在壓縮感知(compressed sensing)中應用的十分廣泛。從數學上來說,Lasso是在線性模型上加上了一個
也可以表示爲:
Lasso迴歸解法
Lasso 迴歸主要的解法有兩種:座標軸下降法(coordinate descent)和最小角迴歸法( Least Angle Regression)。
座標軸下降法
- 座標下降優化方法是一種非梯度優化算法。爲了找到一個函數的局部極小值,在每次迭代中可以在當前點處沿一個座標方向進行一維搜索。在整個過程中循環使用不同的座標方向。一個週期的一維搜索迭代過程相當於一個梯度迭代。 其實,gradient descent 方法是利用目標函數的導數(梯度)來確定搜索方向的,而該梯度方向可能不與任何座標軸平行。而coordinate descent方法是利用當前座標系統進行搜索,不需要求目標函數的導數,只按照某一座標方向進行搜索最小值。座標下降法在稀疏矩陣上的計算速度非常快,同時也是Lasso迴歸最快的解法。
下面這份代碼是在稀疏係數上使用Lasso迴歸,這裏Lasso內置的是座標下降法:
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
author : [email protected]
time : 2016-06-06_15-41
Lasso 迴歸應用於稀疏信號
"""
print(__doc__)
import numpy as np
import matplotlib.pyplot as plt
import time
from sklearn.linear_model import Lasso
from sklearn.metrics import r2_score
# 用於產生稀疏數據
np.random.seed(int(time.time()))
# 生成係數數據,樣本爲50個,參數爲200維
n_samples, n_features = 50, 200
# 基於高斯函數生成數據
X = np.random.randn(n_samples, n_features)
# 每個變量對應的係數
coef = 3 * np.random.randn(n_features)
# 變量的下標
inds = np.arange(n_features)
# 變量下標隨機排列
np.random.shuffle(inds)
# 僅僅保留10個變量的係數,其他係數全部設置爲0
# 生成稀疏參數
coef[inds[10:]] = 0
# 得到目標值,y
y = np.dot(X, coef)
# 爲y添加噪聲
y += 0.01 * np.random.normal((n_samples,))
# 將數據分爲訓練集和測試集
n_samples = X.shape[0]
X_train, y_train = X[:n_samples / 2], y[:n_samples / 2]
X_test, y_test = X[n_samples / 2:], y[n_samples / 2:]
# Lasso 迴歸的參數
alpha = 0.1
lasso = Lasso(max_iter=10000, alpha=alpha)
# 基於訓練數據,得到的模型的測試結果
# 這裏使用的是座標軸下降算法(coordinate descent)
y_pred_lasso = lasso.fit(X_train, y_train).predict(X_test)
# 這裏是R2可決係數(coefficient of determination)
# 迴歸平方和(RSS)在總變差(TSS)中所佔的比重稱爲可決係數
# 可決係數可以作爲綜合度量回歸模型對樣本觀測值擬合優度的度量指標。
# 可決係數越大,說明在總變差中由模型作出瞭解釋的部分佔的比重越大,模型擬合優度越好。
# 反之可決係數小,說明模型對樣本觀測值的擬合程度越差。
# R2可決係數最好的效果是1。
r2_score_lasso = r2_score(y_test, y_pred_lasso)
print("測試集上的R2可決係數 : %f" % r2_score_lasso)
plt.plot(lasso.coef_, label='Lasso coefficients')
plt.plot(coef, '--', label='original coefficients')
plt.legend(loc='best')
plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
最小角迴歸法
在闡述最小角迴歸( Least Angle Regression)算法之前,這裏需要對兩種更爲簡單直觀的前向(Forward)算法做一些說明,最小回歸算法是以這兩種前向算法爲基礎的:
前向選擇算法
前向選擇(Forward Selection)算法,是一種典型的貪心算法。它在自變量
即:
此算法對每個變量只需要執行一次操作,效率高,速度快。但也容易看出,當自變量不是正交的時候,由於每次都是在做投影,所有算法只能給出最優解的一個近似解。
前向梯度算法
前向梯度(Forward Stagewise)和前向選擇算法類似,也是每次取相關性最大的一個特徵
然後和前向選擇算法一樣,使用
這個算法在
最小角迴歸
計算機領域的很多算法的提出都是這樣:先給出兩種算法,一種是速度快的,精度低;另一種是精度高的,太複雜。於是(這就像動漫裏面一定有兩個男主和一個女主,一個能力特別強,但是特別高冷;一個稍弱能力弱,但是2B搞笑,最後兩人同時追女主一樣。說的大一點,這也算是一個種人生的哲學)計算機領域中,就會出現一個結合前兩個算法的第三個算法,是前兩種算法的折中,其速度不算特別慢,精度也還不錯。在本文,下面就要提出最小角迴歸(Least Angle Regression)。
LARS算法是結合前兩種前向算法的所得到的。
首先,還是找到與因變量
直到出現一個
當出現第三個特徵
LARS算法是一個適用於高維數據的迴歸算法,其主要的優點如下:
-
對於特徵維度
n 遠高於樣本點數m 的情況(n≥m ),該算法有極高的數值計算效率 -
該算法的最壞計算複雜度和最小二乘法(OLS)類似,但是其計算速度幾乎和前向選擇算法一樣
-
它可以產生分段線性結果的完整路徑,這在模型的交叉驗證中極爲有用
其主要的確點爲:
- 由於LARS的迭代方向是根據目標的殘差
yres 定的,所以該算法對樣本的噪聲是極爲敏感的
下面這份代碼是LARS算法的展示:
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
author : [email protected]
time : 2016-06-07-10-24
LARS測試代碼
這裏計算了LARS算法在diabetes數據集上,其正則化參數的路徑
最終結果圖中的每一個顏色代表參數向量中不同的特徵
"""
print(__doc__)
import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model
from sklearn import datasets
# 導入數據集
# 這個數據集,總的樣本個數爲442個,特徵維度爲10
diabetes = datasets.load_diabetes()
X = diabetes.data
y = diabetes.target
print X.shape
# 所謂參數正則化路徑
# 其實就是LARS算法每次迭代的時候,每個參數的數值所組成的曲線
# 其橫軸對應着迭代的程度,縱軸是每個特徵參數對應的數值
# 這裏一共有10個特徵,所以有10條特徵正則化曲線
print("基於LARS算法計算正則化路徑:")
alphas, _, coefs = linear_model.lars_path(X, y, method='lasso', verbose=True)
# 這裏講迭代程度歸一化到[0,1]直間
xx = np.sum(np.abs(coefs.T), axis=1)
xx /= xx[-1]
plt.plot(xx, coefs.T)
ymin, ymax = plt.ylim()
plt.vlines(xx, ymin, ymax, linestyle='dashed')
plt.xlabel('|coef| / max|coef|')
plt.ylabel('Coefficients')
plt.title('LASSO Path')
plt.axis('tight')
plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
LARS會計算參數正則化路徑上的每一個拐點,所以,如果設計矩陣(數據矩陣)的尺寸比較的小,即樣本和特徵的數目比較的少,那麼LARS將會有很高的計算效
當然,即便在沒有元參數的情況下,LARS也可以計算出完整的參數路徑
座標軸下降法是在一個事先確定的座標軸順序上計算參數路徑的
所以,如果座標軸數目比路徑上拐點的數目要少的話,那麼座標軸下降法效率會更高
基於Lasso的特徵選擇
使用
下面這個代碼對基於Lasso的特徵選擇做了說明
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
author : [email protected]
time : 2016-06-06_16-39
基於lasso的特徵選擇
這個功能一般和其他的分類器一起使用
或直接內置於其他分類器算中
"""
import numpy as np
import time
from sklearn.svm import LinearSVC
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
from sklearn.metrics import r2_score
np.random.seed(int(time.time()))
# 導入iris數據集
# 這個數據集一共有150個樣本,特徵維數爲4維
iris = load_iris()
X, y = iris.data, iris.target
print '生成矩陣的尺寸:150, 4'
print X.shape
# 對原始樣本重排列
inds = np.arange(X.shape[0])
np.random.shuffle(inds)
# 提取訓練數據集和測試數據集
X_train = X[inds[:100]]
y_train = y[inds[:100]]
X_test = X[inds[100:]]
y_test = y[inds[100:]]
print '原始特徵的維度:', X_train.shape[1]
# 線性核的支持向量機分類器(Linear kernel Support Vector Machine classifier)
# 支持向量機的參數C爲0.01,使用l1正則化項
lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X_train, y_train)
print '原始特徵,在測試集上的準確率:', lsvc.score(X_test, y_test)
print '原始特徵,在測試集上的R2可決係數:', r2_score(lsvc.predict(X_test), y_test)
# 基於l1正則化的特徵選擇
model = SelectFromModel(lsvc, prefit=True)
# 將原始特徵,轉換爲新的特徵
X_train_new = model.transform(X_train)
X_test_new = model.transform(X_test)
print
print '新特徵的維度:', X_train_new.shape[1]
# 用新的特徵重新訓練模型
lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X_train_new, y_train)
print '新特徵,在測試集上的準確率:', lsvc.score(X_test_new, y_test)
print '新始特徵,在測試集上的R2可決係數:', r2_score(lsvc.predict(X_test_new), y_test)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
其運行結果爲:
生成矩陣的尺寸:150, 4
(150L, 4L)
原始特徵的維度: 4
原始特徵,在測試集上的準確率: 0.62
原始特徵,在測試集上的R2可決係數: 0.506237006237
新特徵的維度: 3
新特徵,在測試集上的準確率: 0.62
新始特徵,在測試集上的R2可決係數: 0.506237006237
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
由運行結果可以看出,基於Lasso的特徵選擇,在不改變模型測試集準去率的情況下,減小了特徵的維度。
在實際測試中,還存在特徵維度被較小到了2的情況,但是出現的次數並不多。
- 關於
l1 特徵選擇的應用中,最具有現實意義的,莫過於文本分類,文本的原始特徵維度幾乎是整個字典,而一個文本的單詞量基本上在100~1000之間,所以文本分類中的設計矩陣是一個極其稀疏的舉證,在我實際的測試中,其稀疏度可以達到1%以下。對於這種問題,l1 懲罰會非常的有用,相關的原理和代碼示例,會在文本分類的章節中說明。
參數α 的選擇
對於Lasso迴歸的損失函數而言:
其中
交叉驗證(Cross-validation)
將交叉驗證和座標軸下降法結合,可以有LassoCV算法,和LARS算法結合,可以有LassolarsCV算法。
- 對於變量間存在相關性的高維數據而言,LassoCV往往有更好的效果。但是相對於LassoCV而言,LassolarsCV可以得到更多的
α 相關的參數數值,所以在設計矩陣的維度遠大於樣本個數的情況下,LassolarsCV比LassoCV有更高的計算效率。
基於信息準則的模型選擇
再一個常用的模型選擇的方式,就是信息準則,一般有兩個信息準則可以使用:
-
Akaike information criterion (AIC),是衡量統計模型擬合優良性的一種標準,是由日本統計學家赤池弘次創立和發展的。赤池信息量準則建立在熵的概念基礎上,可以權衡所估計模型的複雜度和此模型擬合數據的優良性,優先考慮的模型應是AIC值最小的那一個。
AIC=2k−2ln(L) 這裏,
L 是模型極大似然函數的最大值,而k 是模型參數的個數。 -
Bayesian Information Criterions(BIC),貝葉斯信息準則(BIC)是在赤池信息量準則(AIC)的基礎上建立起來的,它們兩非常的相似。相對於AIC而言,BIC對模型的複雜度的懲罰更爲重一些。和AIC一樣,優先考慮的模型應是BIC值最小的那一個。
下面這份代碼展示了Lasso中參數
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
author : [email protected]
time : 2016-06-07_13-47
基於CV/AIC/BIC的 Lasso模型選擇
CV(cross-validation)交叉驗證
AIC(Akaike information criterion)赤池信息準則
BIC(Bayes Information criterion)貝葉斯信息準則
這裏AIC和BIC信息準則使用的是LassoLarsIC實現的,使用的是LARS算法
基於信息準則的模型選擇的速度是非常的快的
但是,其依賴於模型是正確的基本假設,且對模型自由度需要有恰當的估計,
這樣才能在大量的樣本上得到一個漸進的結果。
當特徵的數量遠大於樣本的數量的時候,信息準則的模型選擇效果並不理想
對於交叉驗證而言,基於座標軸下降算法的交叉驗證可以使用LassoCV
基於LARS算法的交叉驗證可以使用LassoLarsCV
在實際使用中,這兩種算法僅僅是在速度上存在一定的差異,其結果幾乎差不多
由於參數的選擇對未知的數據可能不是最優的
所以在評價一個使用交叉驗證得到的參數的方法的時候
嵌套交叉驗證是有必要的
"""
print(__doc__)
import time
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LassoCV, LassoLarsCV, LassoLarsIC
from sklearn import datasets
# 加載數據集
# 該數據集有442個樣本,特徵的維度爲10
diabetes = datasets.load_diabetes()
X = diabetes.data
y = diabetes.target
print X.shape
# 選擇隨機種子
rng = np.random.RandomState(int(time.time())%100)
# 添加14個噪聲的特徵
X = np.c_[X, rng.randn(X.shape[0], 14)]
# 對每個特徵做數據歸一化,這個過程是LARS算法需要的
X /= np.sqrt(np.sum(X ** 2, axis=0))
# LassoLarsIC: 使用LARS算法做BIC/AIC信息準則
model_bic = LassoLarsIC(criterion='bic')
t1 = time.time()
model_bic.fit(X, y)
t_bic = time.time() - t1
alpha_bic_ = model_bic.alpha_
model_aic = LassoLarsIC(criterion='aic')
model_aic.fit(X, y)
alpha_aic_ = model_aic.alpha_
# 這裏alpha_是最終選擇的參數
# alphas_是所有的alpha選擇
# alpha_就是alphas_中,對於的信息準則最小的那個值
# criterion_是和alphas_對應的信息準則的結果
def plot_ic_criterion(model, name, color):
alpha_ = model.alpha_
alphas_ = model.alphas_
criterion_ = model.criterion_
plt.plot(-np.log10(alphas_), criterion_, '--', color=color,
linewidth=3, label='%s criterion' % name)
plt.axvline(-np.log10(alpha_), color=color, linewidth=3,
label='alpha: %s estimate' % name)
plt.xlabel('-log(alpha)')
plt.ylabel('criterion')
plt.figure()
plot_ic_criterion(model_aic, 'AIC', 'b')
plot_ic_criterion(model_bic, 'BIC', 'r')
plt.legend()
plt.title('Information-criterion for model selection (training time %.3fs)'
% t_bic)
# LassoCV: 基於座標軸下降法的Lasso交叉驗證
print("使用座標軸下降法計算參數正則化路徑:")
t1 = time.time()
# 這裏是用20折的交叉驗證
model = LassoCV(cv=20).fit(X, y)
t_lasso_cv = time.time() - t1
# 最終alpha的結果,因爲有的alpha實在是太小了
# 所以使用負對數形式表示
m_log_alphas = -np.log10(model.alphas_)
# 由於這裏使用的是20折交叉驗證
# 所以model.mse_path_有20列
# model.mse_path_中每一列,是對應交叉驗證,在alpha選擇不同值的時候
# 其對應的均方誤差(mean square error)
# 模型最終選擇的alpha是所有交叉驗證結果的平均值中
# 最小的那個平均的均方誤差對應的alpha
plt.figure()
ymin, ymax = 2300, 3800
plt.plot(m_log_alphas, model.mse_path_, ':')
plt.plot(m_log_alphas, model.mse_path_.mean(axis=-1), 'k',
label='Average across the folds', linewidth=2)
plt.axvline(-np.log10(model.alpha_), linestyle='--', color='k',
label='alpha: CV estimate')
plt.legend()
plt.xlabel('-log(alpha)')
plt.ylabel('Mean square error')
plt.title('Mean square error on each fold: coordinate descent '
'(train time: %.2fs)' % t_lasso_cv)
plt.axis('tight')
plt.ylim(ymin, ymax)
# LassoLarsCV: 基於LARS算法的交叉驗證
print("使用LARS算法計算參數正則化路徑:")
t1 = time.time()
model = LassoLarsCV(cv=20).fit(X, y)
t_lasso_lars_cv = time.time() - t1
# 最終alpha的結果,因爲有的alpha實在是太小了
# 所以使用負對數形式表示
m_log_alphas = -np.log10(model.cv_alphas_)
# 參數說明和上面是一樣的
plt.figure()
plt.plot(m_log_alphas, model.cv_mse_path_, ':')
plt.plot(m_log_alphas, model.cv_mse_path_.mean(axis=-1), 'k',
label='Average across the folds', linewidth=2)
plt.axvline(-np.log10(model.alpha_), linestyle='--', color='k',
label='alpha CV')
plt.legend()
plt.xlabel('-log(alpha)')
plt.ylabel('Mean square error')
plt.title('Mean square error on each fold: Lars (train time: %.2fs)'
% t_lasso_lars_cv)
plt.axis('tight')
plt.ylim(ymin, ymax)
plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152