文章目錄
1.Python代碼
#!/usr/bin/env python3
# encoding: utf-8
'''
@file: larsWineCV.py
@time: 2020/6/13 0013 10:22
@author: Jack
@contact: [email protected]
'''
import urllib.request
import numpy as np
from sklearn import datasets, linear_model
from math import sqrt
import matplotlib.pyplot as plt
## 讀取數據集
target_url = ("http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv")
data = urllib.request.urlopen(target_url)
xList = []
labels = []
names = []
firstLine = True
for line in data:
if firstLine:
names = str(line, encoding='utf-8').strip().split(";")
firstLine = False
else:
row = str(line, encoding='utf-8').strip().split(";")
labels.append(float(row[-1]))
row.pop()
floatRow = [float(num) for num in row]
xList.append(floatRow)
nrows = len(xList)
ncols = len(xList[0])
## 計算均值方差
xMean = []
xSD = []
for i in range(ncols):
col = [xList[j][i] for j in range(nrows)]
mean = sum(col) / nrows
xMean.append(mean)
colDiff = [(xList[j][i] - mean) for j in range(nrows)]
sumSq = sum([colDiff[i] * colDiff[i] for i in range(nrows)])
stdDev = sqrt(sumSq / nrows)
xSD.append(stdDev)
## 歸一化屬性和標籤
xNormalized = []
for i in range(nrows):
rowNormalized = [(xList[i][j] - xMean[j]) / xSD[j] for j in range(ncols)]
xNormalized.append(rowNormalized)
meanLabel = sum(labels) / nrows
sdLabel = sqrt(sum([(labels[i] - meanLabel) * (labels[i] - meanLabel) for i in range(nrows)]) / nrows)
labelNormalized = [(labels[i] - meanLabel) / sdLabel for i in range(nrows)]
## 通過交叉驗證確定最佳係數
nxval = 10 ## 交叉驗證折數
nSteps = 350 ## 迭代次數
stepSize = 0.004 ## 步長
## 初始化error列表
errors = []
for i in range(nSteps):
b = []
errors.append(b)
for ixval in range(nxval):
idxTest = [a for a in range(nrows) if a % nxval == ixval * nxval]
idxTrain = [a for a in range(nrows) if a % nxval != ixval * nxval]
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]
nrowsTrain = len(idxTrain)
nrowsTest = len(idxTest)
beta = [0.0] * ncols
betaMat = []
betaMat.append(list(beta))
for iStep in range(nSteps):
residuals = [0.0] * nrows
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[iStar] += stepSize * corrStar / abs(corrStar)
betaMat.append(list(beta))
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)
cvCurve = []
for errVect in errors:
mse = sum([x * x for x in errVect]) / len(errVect)
cvCurve.append(mse)
minMse = min(cvCurve)
minPt = [i for i in range(len(cvCurve)) if cvCurve[i] == minMse][0]
print("Minimum Mean Square Error", minMse)
print("Index of Minimum Mean Square Error", minPt)
xaxis = range(len(cvCurve))
plt.plot(xaxis, cvCurve)
plt.xlabel("Steps Taken")
plt.ylabel(("Mean Square Error"))
plt.show()
Minimum Mean Square Error 0.5873018933136459
Index of Minimum Mean Square Error 311
2.結果分析
目前關於利用紅酒的化學屬性上來預測紅酒口感得分,已經有 350 個可能的解。如何選擇最佳的解?爲了選擇使用的曲線,需要了解350個選擇中每種選擇的優劣。本例中通過10折交叉驗證的方法來決定部署使用的最佳係數集合。10折交叉驗證是將輸入數據切分爲10份幾乎均等的數據,將其中一份數據移除,使用剩下數據進行訓練,然後再在移除的數據上進行測試。通過遍歷10份數據,每次移除一份數據用於測試,從而可以對錯誤進行估計從而估計預測的變化情況。關於交叉驗證份數的選擇,如果份數較少,那麼每次訓練使用的數據也較少。如果設爲 5 折,那麼每次訓練時會預留 20% 的數據。如果使用 10 折,只會留出 10% 的數據。在較少的數據上訓練會降低算法的準確性。然而,切分份數過多也意味着在訓練過程中需要多次遍歷,這會顯著增加訓練時間。
在進行交叉驗證循環前,首先初始化一個錯誤列表。該錯誤列表包含算法中每一步迭代的錯誤。算法會對所有 10 折交叉驗證的每折錯誤進行累加。這裏使用一個取模函數來定義這些集合。在有些情況下,需要使用分層抽樣。例如要在一個非平衡數據集上構建分類器,其中某一類的樣本點很少。如果希望訓練集能夠代表整個數據集,那麼需要按照類來抽樣數據,從而確保樣本內和樣本外的類大小比例一致。
結果圖中曲線展示了一個非常普遍的模式。基本上 MSE 隨着迭代步數的增加而單調遞減。嚴格來講,從程序的輸出來看,最小值在第 311 步左右。但是該圖顯示最小值比較穩定,相對周圍沒有明顯突出。在某些情況下,該曲線會在某個點上到達一個明顯的最小值,往左或者往右都會顯著增加。交叉驗證在訓練階段的第311步取到 MSE 的最小值 0.59。圖中係數曲線是在整個數據集上訓練得到的。因爲並不瞭解 350 個係數集合的哪一個應該被部署使用,所以使用交叉驗證確定。交叉驗證會生成對 MSE 的合理估計,告訴我們將第311個模型從訓練集部署到整個數據集上。