Fork me on GitHub

葡萄酒品质评价

前言

        嘻嘻,这是本人练习的第二个机器学习的小项目,对葡萄酒的品质进行鉴定。其实,参加数模的时候做过葡萄酒品质鉴定的练习题现在回想起来还真是2333呀。废话不多说,咱们走起来吧!

导入库和模块

        首先,我们要导入构建算法模型需要用到的块。导入numpy,它为更有效的数值计算提供支持;接下来,我们将导入Pandas,一个支持数据矩导入的方便库。虽然Scikit-Learn可以直接处理数值矩阵,但它会make our liveseasier;接下来是导入机器学习功能了。第一个是model_selection模块中的train_test_split()函数。顾名思义,该模块包含许多实用程序,可帮助我们在模型之间进行选择。导入整个预处理模块。用于标准化,转换和处理数据的实用程序。导入块族及帮助我们执行交叉验证(cross-validation)的块。我们还将导入一种方法来保存模型以供将来使用。代码如下:

import numpy as np 
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn import preprocessing
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.externals import joblib

导入红酒数据

        现在我们导入我们的数据集。我们导入的Pandas库拥有一整套有用的导入or输出工具,可以实现从CSV,Excel,SQL,SAS和许多其他数据格式中读取数据。

1
2
dataset_url = 'http://mlr.cs.umass.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv'
data = pd.read_csv(dataset_url)

现在让我们看看前5行数据:

print data.head()

执行结果如下:

fixed acidity;"volatile acidity";"citric acid"...
0   7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56...
1   7.8;0.88;0;2.6;0.098;25;67;0.9968;3.2;0.68...
2   7.8;0.76;0.04;2.3;0.092;15;54;0.997;3.26;0...
3   11.2;0.28;0.56;1.9;0.075;17;60;0.998;3.16;...
4   7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56...

…看起来很乱。因为CSV文件实际上使用分号来分隔数据。这很烦人,但很容易解决:

data = pd.read_csv(dataset_url, sep=';')
print (data.head(5))

结果如下:

#  fixed acidity  volatile acidity   ...     alcohol  quality
0            7.4              0.70   ...         9.4        5
1            7.8              0.88   ...         9.8        5
2            7.8              0.76   ...         9.8        5
3           11.2              0.28   ...         9.8        6
4            7.4              0.70   ...         9.4        5
5            7.4              0.66   ...         9.4        5

了解一下数据的概况:

print data.shape
# (1599, 12)

可以看出有1,599个样本和12个特征,下面看一下一些样本的主要统计值:

print data.describe()
#        fixed acidity  volatile acidity  citric acid...
# count    1599.000000       1599.000000  1599.000000...
# mean        8.319637          0.527821     0.270976...
# std         1.741096          0.179060     0.194801...
# min         4.600000          0.120000     0.000000...
# 25%         7.100000          0.390000     0.090000...
# 50%         7.900000          0.520000     0.260000...
# 75%         9.200000          0.640000     0.420000...
# max        15.900000          1.580000     1.000000...

下面是12个特征的情况

  • fixed acidity
  • volatile acidity
  • citric acid
  • residual sugar
  • chlorides
  • free sulfur dioxide
  • total sulfur dioxide
  • density
  • pH
  • sulphates
  • alcohol

分离数据

        在建模工作流程开始时将数据拆分为训练和测试集对于获得模型性能的实际估计至关重要
首先,让我们将目标(y)特征与输入(X)特征分开:

y = data.quality
X = data.drop('quality', axis=1)

这使我们可以利用Scikit-Learn的非常有用的rain_test_split功能:

X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size=0.2, 
                                                    random_state=123, 
                                                    stratify=y)

从参数可以看出,我们留出20%的数据作为评估模型的测试集。我们还设置了一个任意的“随机状态”(即seed),以便重现我们的结果。

声明数据预处理步骤

所有特征都是数字,可它们的尺度不同,为了保证模型的收敛,我们要进行数据的标准化。

标准化是什么鬼

标准化是从每个特征中减去平均值然后除以特征标准偏差的过程。标准化是机器学习任务的常见要求。许多算法假设所有特征都以零为中心并具有大致相同的方差。

        使用Scikit-Learn中称为Transformer API的功能,而不是直接调用scale函数。 Transformer API使训练数据“拟合”预处理步骤,并且使以后的数据集按同样的步骤处理。
以上的处理能使对模型性能的最终估计更加真实,并且允许将预处理步骤插入到交叉验证的过程中。

scaler = preprocessing.StandardScaler().fit(X_train)

让我们确认一下是否有效:

1
2
3
4
5
6
7
X_train_scaled = scaler.transform(X_train)

print X_train_scaled.mean(axis=0)
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

print X_train_scaled.std(axis=0)
# [ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

        现在我们已经把训练集中每一个特征的均值和标准偏差记录并保存下来,用于以后的数据集标准化的处理。我们通过下面代码验证确实如我们所阐述那样:

1
2
3
4
5
6
7
X_train_scaled = scaler.transform(X_train)

print X_train_scaled.mean(axis=0)
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

print X_train_scaled.std(axis=0)
# [ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

注意我们使用缩放器对象来转换训练集。之后,我们使用相同的平均值和标准偏差来处理测试集,使其标准化:

X_test_scaled = scaler.transform(X_test)

print X_test_scaled.mean(axis=0)
# [ 0.02776704  0.02592492 -0.03078587 -0.03137977 -0.00471876 -0.04413827
#  -0.02414174 -0.00293273 -0.00467444 -0.10894663  0.01043391]

print X_test_scaled.std(axis=0)
# [ 1.02160495  1.00135689  0.97456598  0.91099054  0.86716698  0.94193125
#  1.03673213  1.03145119  0.95734849  0.83829505  1.0286218 ]

        我们可以看到测试集中的缩放特征如何在单位方差下不能完全等于零!这正是我们所期望的,因为我们使用训练集中数据的均值和偏差标准而不是测试集本身的均值和标准偏差来处理测试集。
实际上,当我们设置cross-validation pipeline时,我们甚至不需要手动调整Transformer API。相反,我们将简单地声明类对象,如下所示:

pipeline = make_pipeline(preprocessing.StandardScaler(), 
                         RandomForestRegressor(n_estimators=100))

        正如上述代码所表示一个modeling pipeline,首先使用StandardScaler()处理数据,然后使用随机森林回归器拟合模型。现在,让我们通过交叉验证来声明我们想要调整的超参数:

hyperparameters = { 'randomforestregressor__max_features' : ['auto', 'sqrt', 'log2'],
                  'randomforestregressor__max_depth': [None, 5, 3, 1]}

如上,格式应该是Python字典(键值对的数据结构),其中键是超参数名称,值是要尝试的设置列表。可以在文档页面上找到参数值的选项。
利用Scikit-Learn进行CV pipeline的预处理:

clf = GridSearchCV(pipeline, hyperparameters, cv=10)

# Fit and tune model
clf.fit(X_train, y_train)

GridSearchCV实质上在超参数的整个“网格”(所有可能的排列)上执行交叉验证。现在,可以看到使用CV找到的最佳参数集:

print clf.best_params_
# {'randomforestregressor__max_depth': None, 'randomforestregressor__max_features': 'auto'}

重新拟合训练集

利用sklearn的GridSearchCV可以使整个训练集自动使用最佳超参数集重新建模。接着用clf应用于其他数据集。

# Predict a new set of dataPython
y_pred = clf.predict(X_test)

使用之前导入的指标来评估我们的模型性能:

print r2_score(y_test, y_pred)
# 0.45044082571584243

print mean_squared_error(y_test, y_pred)
# 0.35461593750000003

很好,所以现在的问题是emmmmm这样的性能是否足够好,是否满足正常使用,解决办法很简单——在实践中检验它!

总结

-------------本文结束感谢您的阅读-------------