机器学习—模型选择与优化7-1(k-fold交叉验证法)
使用k-fold交叉验证法选择最优钻石价格模型
主要步骤流程:
- 1. 导入包
- 2. 导入数据集
- 3. 数据预处理
- 3.1 处理缺失数据
- 3.2 处理类别型字段
- 3.2.1 统计类别型字段
- 3.2.2 字符编码和独热编码
- 3.3 生成自变量和因变量
- 3.4 拆分数据集
- 3.5 特征缩放
- 4. 构建不同的模型并调用k折交叉验证
- 4.1 构建随机森林模型并调用k折交叉验证
- 4.2 构建多元线性回归模型并调用k折交叉验证
- 4.3 构建SVM模型并调用k折交叉验证
- 5. 比较3个模型的性能
1. 导入包
In [1]:# 导入包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
2. 导入数据集
In [2]:# 导入数据集
dataset = pd.read_csv('diamonds.csv')
dataset
Out[2]:
| carat | cut | color | clarity | depth | table | x | y | z | price | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.23 | Ideal | E | SI2 | 61.5 | 55.0 | 3.95 | 3.98 | 2.43 | 326 |
| 1 | 0.21 | Premium | E | SI1 | 59.8 | 61.0 | 3.89 | 3.84 | 2.31 | 326 |
| 2 | 0.23 | Good | E | VS1 | 56.9 | 65.0 | 4.05 | 4.07 | 2.31 | 327 |
| 3 | 0.29 | Premium | I | VS2 | 62.4 | 58.0 | 4.20 | 4.23 | 2.63 | 334 |
| 4 | 0.31 | Good | J | SI2 | 63.3 | 58.0 | 4.34 | 4.35 | 2.75 | 335 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 53935 | 0.72 | Ideal | D | SI1 | 60.8 | 57.0 | 5.75 | 5.76 | 3.50 | 2757 |
| 53936 | 0.72 | Good | D | SI1 | 63.1 | 55.0 | 5.69 | 5.75 | 3.61 | 2757 |
| 53937 | 0.70 | Very Good | D | SI1 | 62.8 | 60.0 | 5.66 | 5.68 | 3.56 | 2757 |
| 53938 | 0.86 | Premium | H | SI2 | 61.0 | 58.0 | 6.15 | 6.12 | 3.74 | 2757 |
| 53939 | 0.75 | Ideal | D | SI2 | 62.2 | 55.0 | 5.83 | 5.87 | 3.64 | 2757 |
53940 rows × 10 columns
3. 数据预处理
3.1 处理缺失数据
In [3]:# 统计缺失数据
dataset.isnull().sum()
Out[3]:
carat 0
cut 0
color 0
clarity 0
depth 0
table 0
x 0
y 0
z 0
price 0
dtype: int64
没有任何一个字段有缺失值
In [4]:# 找出业务不合理的数据
xyz_zero_df = dataset.query('x < 0.01 or y < 0.01 or z < 0.01')
xyz_zero_df
Out[4]:
| carat | cut | color | clarity | depth | table | x | y | z | price | |
|---|---|---|---|---|---|---|---|---|---|---|
| 2207 | 1.00 | Premium | G | SI2 | 59.1 | 59.0 | 6.55 | 6.48 | 0.0 | 3142 |
| 2314 | 1.01 | Premium | H | I1 | 58.1 | 59.0 | 6.66 | 6.60 | 0.0 | 3167 |
| 4791 | 1.10 | Premium | G | SI2 | 63.0 | 59.0 | 6.50 | 6.47 | 0.0 | 3696 |
| 5471 | 1.01 | Premium | F | SI2 | 59.2 | 58.0 | 6.50 | 6.47 | 0.0 | 3837 |
| 10167 | 1.50 | Good | G | I1 | 64.0 | 61.0 | 7.15 | 7.04 | 0.0 | 4731 |
| 11182 | 1.07 | Ideal | F | SI2 | 61.6 | 56.0 | 0.00 | 6.62 | 0.0 | 4954 |
| 11963 | 1.00 | Very Good | H | VS2 | 63.3 | 53.0 | 0.00 | 0.00 | 0.0 | 5139 |
| 13601 | 1.15 | Ideal | G | VS2 | 59.2 | 56.0 | 6.88 | 6.83 | 0.0 | 5564 |
| 15951 | 1.14 | Fair | G | VS1 | 57.5 | 67.0 | 0.00 | 0.00 | 0.0 | 6381 |
| 24394 | 2.18 | Premium | H | SI2 | 59.4 | 61.0 | 8.49 | 8.45 | 0.0 | 12631 |
| 24520 | 1.56 | Ideal | G | VS2 | 62.2 | 54.0 | 0.00 | 0.00 | 0.0 | 12800 |
| 26123 | 2.25 | Premium | I | SI1 | 61.3 | 58.0 | 8.52 | 8.42 | 0.0 | 15397 |
| 26243 | 1.20 | Premium | D | VVS1 | 62.1 | 59.0 | 0.00 | 0.00 | 0.0 | 15686 |
| 27112 | 2.20 | Premium | H | SI1 | 61.2 | 59.0 | 8.42 | 8.37 | 0.0 | 17265 |
| 27429 | 2.25 | Premium | H | SI2 | 62.8 | 59.0 | 0.00 | 0.00 | 0.0 | 18034 |
| 27503 | 2.02 | Premium | H | VS2 | 62.7 | 53.0 | 8.02 | 7.95 | 0.0 | 18207 |
| 27739 | 2.80 | Good | G | SI2 | 63.8 | 58.0 | 8.90 | 8.85 | 0.0 | 18788 |
| 49556 | 0.71 | Good | F | SI2 | 64.1 | 60.0 | 0.00 | 0.00 | 0.0 | 2130 |
| 49557 | 0.71 | Good | F | SI2 | 64.1 | 60.0 | 0.00 | 0.00 | 0.0 | 2130 |
| 51506 | 1.12 | Premium | G | I1 | 60.4 | 59.0 | 6.71 | 6.67 | 0.0 | 2383 |
x < 0.01 或者 y < 0.01 或者 z < 0.01,这些数据不符合业务场景,且数据量比较少,故直接删除。
In [5]:dataset = dataset.drop(xyz_zero_df.index)
xyz_zero_df = dataset.query('x < 0.01 or y < 0.01 or z < 0.01')
xyz_zero_df
Out[5]:
| carat | cut | color | clarity | depth | table | x | y | z | price |
|---|
3.2 处理类别型字段
3.2.1 统计类别型字段
In [6]:# take care of categorical data
cols = dataset.columns
num_cols = dataset._get_numeric_data().columns
cat_cols = set(cols) - set(num_cols)
print('类别型字段是:' + str(cat_cols))
类别型字段是:{'color', 'clarity', 'cut'}
由数据集字段说明可知,cut、clarity、color这3个字段都是类别型字段。对他们做字符编码和独热编码。
3.2.2 字符编码和独热编码
In [7]:dataset = pd.get_dummies(dataset, drop_first=True)
dataset
Out[7]:
| carat | depth | table | x | y | z | price | cut_Good | cut_Ideal | cut_Premium | ... | color_H | color_I | color_J | clarity_IF | clarity_SI1 | clarity_SI2 | clarity_VS1 | clarity_VS2 | clarity_VVS1 | clarity_VVS2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.23 | 61.5 | 55.0 | 3.95 | 3.98 | 2.43 | 326 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 1 | 0.21 | 59.8 | 61.0 | 3.89 | 3.84 | 2.31 | 326 | 0 | 0 | 1 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 2 | 0.23 | 56.9 | 65.0 | 4.05 | 4.07 | 2.31 | 327 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| 3 | 0.29 | 62.4 | 58.0 | 4.20 | 4.23 | 2.63 | 334 | 0 | 0 | 1 | ... | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 4 | 0.31 | 63.3 | 58.0 | 4.34 | 4.35 | 2.75 | 335 | 1 | 0 | 0 | ... | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 53935 | 0.72 | 60.8 | 57.0 | 5.75 | 5.76 | 3.50 | 2757 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 53936 | 0.72 | 63.1 | 55.0 | 5.69 | 5.75 | 3.61 | 2757 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 53937 | 0.70 | 62.8 | 60.0 | 5.66 | 5.68 | 3.56 | 2757 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 53938 | 0.86 | 61.0 | 58.0 | 6.15 | 6.12 | 3.74 | 2757 | 0 | 0 | 1 | ... | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 53939 | 0.75 | 62.2 | 55.0 | 5.83 | 5.87 | 3.64 | 2757 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
53920 rows × 24 columns
数据集由10列变为24列。
In [8]:dataset.head()
Out[8]:
| carat | depth | table | x | y | z | price | cut_Good | cut_Ideal | cut_Premium | ... | color_H | color_I | color_J | clarity_IF | clarity_SI1 | clarity_SI2 | clarity_VS1 | clarity_VS2 | clarity_VVS1 | clarity_VVS2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.23 | 61.5 | 55.0 | 3.95 | 3.98 | 2.43 | 326 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 1 | 0.21 | 59.8 | 61.0 | 3.89 | 3.84 | 2.31 | 326 | 0 | 0 | 1 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 2 | 0.23 | 56.9 | 65.0 | 4.05 | 4.07 | 2.31 | 327 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| 3 | 0.29 | 62.4 | 58.0 | 4.20 | 4.23 | 2.63 | 334 | 0 | 0 | 1 | ... | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 4 | 0.31 | 63.3 | 58.0 | 4.34 | 4.35 | 2.75 | 335 | 1 | 0 | 0 | ... | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
5 rows × 24 columns
3.3 生成自变量和因变量
In [9]:# 生成自变量和因变量
y = dataset['price'].values
dataset = dataset.drop(['price'], axis = 1)
X = dataset.values
In [10]:
X[:5, :]
Out[10]:
array([[ 0.23, 61.5 , 55. , 3.95, 3.98, 2.43, 0. , 1. , 0. ,
0. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ,
1. , 0. , 0. , 0. , 0. ],
[ 0.21, 59.8 , 61. , 3.89, 3.84, 2.31, 0. , 0. , 1. ,
0. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 1. ,
0. , 0. , 0. , 0. , 0. ],
[ 0.23, 56.9 , 65. , 4.05, 4.07, 2.31, 1. , 0. , 0. ,
0. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ,
0. , 1. , 0. , 0. , 0. ],
[ 0.29, 62.4 , 58. , 4.2 , 4.23, 2.63, 0. , 0. , 1. ,
0. , 0. , 0. , 0. , 0. , 1. , 0. , 0. , 0. ,
0. , 0. , 1. , 0. , 0. ],
[ 0.31, 63.3 , 58. , 4.34, 4.35, 2.75, 1. , 0. , 0. ,
0. , 0. , 0. , 0. , 0. , 0. , 1. , 0. , 0. ,
1. , 0. , 0. , 0. , 0. ]])
In [11]:
y[:5]
Out[11]:
array([326, 326, 327, 334, 335], dtype=int64)
3.4 拆分数据集
In [12]:# 拆分数据集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(43136, 23)
(10784, 23)
(43136,)
(10784,)
3.5 特征缩放
In [13]:# 特征缩放
from sklearn.preprocessing import StandardScaler
sc_X = StandardScaler()
X_train = sc_X.fit_transform(X_train)
X_test = sc_X.transform(X_test)
In [14]:
sc_y = StandardScaler()
y_train = np.ravel(sc_y.fit_transform(y_train.reshape(-1, 1)))
y_test = np.ravel(sc_y.transform(y_test.reshape(-1, 1)))
4. 构建不同的模型并调用k折交叉验证
4.1 构建随机森林模型并调用k折交叉验证
In [15]:# 随机森林回归模型
from sklearn.ensemble import RandomForestRegressor
rf_regressor = RandomForestRegressor(n_estimators = 500, random_state = 0, verbose = 1)
下面的代码,在4核8GB机器上,大约训练 2~3 分钟。
In [16]:# 应用K折交叉验证(K=5)
from sklearn.model_selection import cross_val_score
rf_mse = cross_val_score(estimator = rf_regressor, X = X_train, y = y_train, scoring = 'r2', cv = 5, verbose = 1, n_jobs=6)
[Parallel(n_jobs=6)]: Using backend LokyBackend with 6 concurrent workers.
[Parallel(n_jobs=6)]: Done 5 out of 5 | elapsed: 3.1min finished
4.2 构建多元线性回归模型并调用k折交叉验证
In [17]:# 多元线性回归模型
from sklearn.linear_model import LinearRegression
ml_regressor = LinearRegression()
下面的代码,在4核8GB机器上,大约训练 1 秒钟。
In [18]:# 应用K折交叉验证(K=5)
from sklearn.model_selection import cross_val_score
ml_mse = cross_val_score(estimator = ml_regressor, X = X_train, y = y_train, scoring = 'r2', cv = 5, verbose = 1, n_jobs=6)
[Parallel(n_jobs=6)]: Using backend LokyBackend with 6 concurrent workers.
[Parallel(n_jobs=6)]: Done 5 out of 5 | elapsed: 0.6s finished
4.3 构建SVM模型并调用k折交叉验证
In [19]:# 支持向量机模型
from sklearn.svm import SVR
svr_regressor = SVR(kernel = 'rbf', verbose = 1)
下面的代码,在4核8GB机器上,大约训练 1 分钟。
In [20]:# 应用K折交叉验证(K=5)
from sklearn.model_selection import cross_val_score
svr_mse = cross_val_score(estimator = svr_regressor, X = X_train, y = y_train, scoring = 'r2', cv = 5, verbose = 1, n_jobs=6)
[Parallel(n_jobs=6)]: Using backend LokyBackend with 6 concurrent workers.
[Parallel(n_jobs=6)]: Done 5 out of 5 | elapsed: 1.5min finished
5. 比较3个模型的性能
In [21]:# 比较3个模型性能
print('随机森林模型中,R2的平均数是 %.4f,标准差是 %.4f' %(rf_mse.mean(), rf_mse.std()))
随机森林模型中,R2的平均数是 0.9745,标准差是 0.0004
In [22]:
print('多元线性回归模型中,R2的平均数是 %.4f,标准差是 %.4f' %(ml_mse.mean(), ml_mse.std()))
多元线性回归模型中,R2的平均数是 0.9177,标准差是 0.0047
In [23]:
print('支持向量机模型中,R2的平均数是 %.4f,标准差是 %.4f' %(svr_mse.mean(), svr_mse.std()))
支持向量机模型中,R2的平均数是 0.9755,标准差是 0.0004
结论:
- 支持向量机模型R2的平均数最高(低偏差),标准差最小(低方差),说明 支持向量机 是最优模型。