数据分析——“红酒口感”数据集最佳模型选择

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个模型从训练集部署到整个数据集上。

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