關于LARS的詳細介紹,可以參考Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani等人的那篇經典論文Least Angle Regression,文末附有論文位址。本文僅涉及LARS的Python實作。
實作代碼:
# 導入要用到的相關包和函數
%matplotlib inline
import numpy as np
import pandas as pd
from math import sqrt
import matplotlib.pyplot as plt
# 導入要用到的紅酒品質資料集
wine_data = pd.read_csv('wine.csv')
wine_data.head()
wine_data的表結構如下所示:
# 對wine_data中的資料做标準化處理:
wine_data = wine_data.apply(lambda x:(x - x.mean()) / x.std(),axis=0)
wine_data.head()
wine_data中的資料經标準化處理後,其表結構如下所示:
# 檢視經标準化處理後的wine_data的統計資訊
wine_data.describe()
wine_data描述性統計資訊如下所示(部分):
xNormalized = [] # 構造用于存放标準化處理後的屬性資料的清單
# 提取出wine_data中标準化處理後的标簽資料并放入清單中
labelNormalized = [float(label) for label in wine_data.iloc[:,-1].tolist()]
names = wine_data.columns.tolist() # 提取出wine_data中所有屬性的名稱并放入清單中
# 清單xNormalized中的每個元素對應着标準化處理後的wine_data資料中除去标簽列的每一行
for i in range(len(wine_data)):
xNormalized.append(wine_data.iloc[i,0:-1].tolist())
nrows = len(xNormalized) # 總的樣本數量
ncols = len(xNormalized[0]) # 屬性(特征)數量
# 初始化相關參數
beta = [0.0] * ncols # beta清單為參數向量
betaMat = [] # betaMat清單存放每次疊代後的beta參數清單
nSteps = 350 # 疊代次數
stepSize = 0.004 # 疊代步長
nzList = [] # 存放屬性對應的索引
residualsMat = [] # residualsMat清單存放每次疊代後的residuals清單
for i in range(nSteps):
# 計算xNormalized中的每個樣本對應的殘差
residuals = [0.0] * nrows
for j in range(nrows):
labelsHat = sum([xNormalized[j][k] * beta[k] for k in range(ncols)])
residuals[j] = labelNormalized[j] - labelsHat
residualsMat.append(list(residuals))
# 分别計算每個屬性與殘差的關聯值的平均值(兩個變量的關聯值等于标準化(減去均值後再除以标準差)值的乘積)
corr = [0.0] * ncols
for j in range(ncols):
corr[j] = sum([xNormalized[k][j] * residuals[k] for k in range(nrows)]) / nrows
iStar = 0
corrStar = corr[0]
# 找出絕對值最大的關聯值及其對應的索引
for j in range(1, (ncols)):
if abs(corrStar) < abs(corr[j]):
iStar = j
corrStar = corr[j]
# 利用屬性與殘差的最大關聯值(絕對值最大)更新最大關聯值對應的beta參數
beta[iStar] += stepSize * corrStar / abs(corrStar) # 關聯值為正,beta參數對應增加,關聯值為負,beta參數對應減小
betaMat.append(list(beta)) # 将隻有一個beta值被更新的beta參數清單存入清單betaMat中
# 找出beta參數清單中所有不等于零的beta參數對應的索引
nzBeta = [index for index in range(ncols) if beta[index] != 0.0]
# 将在nzBeta清單中但不在nzList清單中的索引放入nzList清單中
for q in nzBeta:
if (q in nzList) == False:
nzList.append(q)
# 清單nameList中的屬性已經按照屬性對預測目标的重要性排序
nameList = [names[nzList[i]] for i in range(len(nzList))]
print(nameList)
for i in range(ncols):
# 繪制各個屬性的系數與疊代次數的關系圖像
coefCurve = [betaMat[k][i] for k in range(nSteps)]
xaxis = range(nSteps)
plt.plot(xaxis, coefCurve)
plt.xlabel("Steps Taken")
plt.ylabel(("Coefficient Values"))
print(nameList)的輸出結果為:
['alcohol', 'volatile acidity', 'sulphates', 'total sulfur dioxide', 'chlorides',
'fixed acidity', 'pH','free sulfur dioxide', 'citric acid', 'residual sugar', 'density']
屬性值與疊代次數的關系圖像如下圖所示:
# 通過betaMat的值進一步檢視beta參數是如何更新的
betaMat[20:40]
利用交叉驗證來确定LARS生成結果中的最佳系數組合:
# 建立交叉驗證循環以确定最佳系數組合
# 初始化相關參數
nxval = 10 # nxval為交叉驗證的折數
nSteps = 350 # 疊代次數
stepSize = 0.004 #疊代步長
mseMat = [] # 用于存放MSE清單的清單
for ixval in range(nxval):
m = [] # m用于存放每次疊代後計算的的MSE
# 劃分測試和訓練索引集
idxTest = [a for a in range(nrows) if a%nxval == ixval]
idxTrain = [a for a in range(nrows) if a%nxval != ixval]
# 劃分測試與訓練用的屬性集和标簽集
xTrain = [xNormalized[r] for r in idxTrain]
xTest = [xNormalized[r] for r in idxTest]
labelTrain = [labelNormalized[r] for r in idxTrain]
labelTest = [labelNormalized[r] for r in idxTest]
# 在訓練資料集上訓練LARS回歸
nrowsTrain = len(idxTrain)
nrowsTest = len(idxTest)
# 初始化向量參數beta
beta = [0.0] * ncols
# 初始化用于存放每次疊代後的beta參數清單的betaMat清單
betaMat = []
betaMat.append(list(beta))
# 初始化用于存放誤差的清單
errors = []
for i in range(nSteps):
b = []
errors.append(b)
for iStep in range(nSteps):
# 計算xTrain中的每個樣本對應的殘差
residuals = [0.0] * nrowsTrain
for j in range(nrowsTrain):
labelsHat = sum([xTrain[j][k] * beta[k] for k in range(ncols)])
residuals[j] = labelTrain[j] - labelsHat
# 分别計算每個屬性與殘差的關聯值的平均值(兩個變量的關聯值等于标準化(減去均值後再除以标準差)值的乘積)
corr = [0.0] * ncols
for j in range(ncols):
corr[j] = sum([xTrain[k][j] * residuals[k] for k in range(nrowsTrain)]) / nrowsTrain
# 找出絕對值最大的關聯值及其對應的索引
iStar = 0
corrStar = corr[0]
for j in range(1, (ncols)):
if abs(corrStar) < abs(corr[j]):
iStar = j
corrStar = corr[j]
# 利用屬性與殘差的最大關聯值(絕對值最大)更新最大關聯值對應的beta參數
beta[iStar] += stepSize * corrStar / abs(corrStar) # 關聯值為正,beta參數對應增加,關聯值為負,beta參數對應減小
betaMat.append(list(beta)) # 将隻有一個beta值被更新的beta參數清單存入清單betaMat中
# 基于剛才計算的bate參數在測試資料集上計算預測值,并進一步計算樣本外誤差
for j in range(nrowsTest):
labelsHat = sum([xTest[j][k] * beta[k] for k in range(ncols)])
err = labelTest[j] - labelsHat
errors[iStep].append(err) # error中的元素是清單,清單中的每個元素存儲的是對應每次疊代更新後的所有預測值與對應真實值之間的誤差
# 計算每次疊代更新beta參數後的MSE
for errVect in errors:
mse = sum([x*x for x in errVect])/len(errVect)
m.append(mse) # 将每次疊代更新beta參數後計算的MSE存入m清單中
mseMat.append(m) # 将每次劃分訓練資料集和測試資料集後的m存入mseMa清單中
mseMat = np.array(mseMat) # 将mseMat轉化為array對象
avg_mse = np.mean(mseMat,axis=0) # 計算350次疊代産生的350個MSE在10次交叉驗證後的均值
minMse = min(avg_mse) # 最佳系數組合對應的最小MSE
minPt = [i for i in range(len(avg_mse)) if avg_mse[i] == minMse ][0] # 最小MSE對應的最佳系數組合的索引
print("Minimum Mean Square Error:", minMse)
print("Index of Minimum Mean Square Error:", minPt)
# 繪制MSE與疊代次數的關系圖像
xaxis = range(len(avg_mse))
plt.plot(xaxis, avg_mse)
plt.xlabel("Steps Taken")
plt.ylabel(("Mean Square Error"))
上述代碼中print("Minimum Mean Square Error", minMse)和print("Index of Minimum Mean Square Error", minPt)的輸出結果分别是:
Minimum Mean Square Error: 0.649156243914687和Index of Minimum Mean Square Error: 285
MSE與疊代次數的關系圖像如下圖所示:
由此可以确定最佳系數組合為betaMat[285](betaMat是以wine_data中的所有資料作為訓練資料得到的所有系數組合組成的清單):
betaMat[285]
其值為:
[0.008, -0.23200000000000018, -0.012, 0.008, -0.10400000000000006, 0.036000000000000004,
-0.11200000000000007, 0.0, -0.07600000000000004, 0.17600000000000013, 0.3800000000000003]
其他參考:
《Python機器學習——預測分析核心算法》Michael Bowles著
https://en.wikipedia.org/wiki/Correlation_and_dependence
https://blog.csdn.net/qq_41080850/article/details/86560645
論文Least Angle Regression及本文用到的紅酒資料集的位址:https://pan.baidu.com/s/1ONfd1pg5B3FH6vkn4hOLlQ