數據分析——“紅酒口感”數據集最佳模型選擇

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個模型從訓練集部署到整個數據集上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章