4. 模型验证(ModelValidation)

模型验证就是当选择了模型和超参数后,通过对训练数据进行学习,对比模型对已知数据的预测值和实际值的差距。

然后根据不同的差距,进行调整,选择。

4.1. 错误的模型验证

模型验证需要对数据集进行选择分类,如果使用训练集进行验证或者错误的数据集分类,则可能导致错误结论。

上面的鸢尾花例子就说典型的错误结论,一般很少出现验证结果完全正确的情况,事出反常必有妖,小心!!!

通过检查代码我们发现,上面鸢尾花出现百分百正确的情况主要问题是,我们训练模型用的数据和测试模型用的数据 是同一批数据,练习题居然和考试题是一套,怪不得人人都打满分,这样是不行的。

4.2. 模型验证的正确方法:留出集(HoldOut)

为了解决上面考试题和练习题同一套的问题,即为了更客观的评估学生的成绩,最简单的办法就是考试题不能使平时的训练题,我们在 评估结果的时候,使用的数据集不能是训练的数据集,这样就需要在寻来开始对总数据集进行分类,一部分用来训练, 一部分用来验证。

用来考试的题需要从练习题中预先留出来,我们把这类数据集叫做留出集。

数据集可以手动分出,但手动分存在着许多问题,常见的比如分出的数据不科学不客观,我们一般使用sklearn给我们提供的分类工具。

Sklear也为我们提供了相应的工具,用来分类数据集: train_test_split

# 导入数据集分类工具
# sklearn 0.2 y以上版本移入model_selection包中
# from sklearn.cross_validation import train_test_split
from sklearn.model_selection import train_test_split

#训练集和测试集各占50%
X1, X2, y1, y2 = train_test_split(X, y, random_state=0, train_size=0.5)


#使用一部分用来训练
model.fit(X1, y1)

# 使用另一部分测试
y2_model = model.predict(X2)

#对留出集的预测后的结果和原来真实值进行对比
rst = accuracy_score(y2, y2_model)

print("模型准确率:{}%".format(rst * 100))
模型准确率:90.66666666666666%

4.3. 交叉验证

如果使用留出集测试,得出的结果是值得信赖的,但也有一个缺点,模型失去了一部分训练的计划, 即我们把原来用来训练的数据拿来测试了,这可能导致结果并不是最优结果,特别是对训练集规模比较小的时候, 甚至可能会导致错误结果。

解决这一问题的方法是交叉验证,也就是一组拟合,让数据的每个子集即是训练集又是验证集合,方法如下:

  • 计算总共分为两轮
  • 第一轮X1为训练集,X2是测试集
  • 第二轮X2为训练集,X1是测试集

这样虽然多了一轮工作,但使模型得到了充分训练。

#这里用两轮验证,轮流用一般数据作为留出集

y2_model = model.fit(X1, y1).predict(X2)
y1_model = model.fit(X2, y2).predict(X1)

rst1 = accuracy_score(y1, y1_model)
rst2 = accuracy_score(y2, y2_model)

print("模型1准确率:{}%".format(rst1 * 100))
print("模型2准确率:{}%".format(rst2 * 100))
模型1准确率:96.0%
模型2准确率:90.66666666666666%

以上交叉验证共进行了两轮,最终结果可以把两次结合计算,比如求平均值,称为两轮交叉验证。

对两轮交叉验证的概念可以扩展,比如五轮交叉验证,即每次选取 20% 作为留出集,这样可以进行五轮。 或者可以理解成分成五等份,每次一份(20%)用来作为留出集,其余四份作为训练集。

# sklearn 0.2 以后版本移入model_selection包
# from sklearn.cross_validation import cross_val_score
from sklearn.model_selection import cross_val_score


# sklearn.model_selection.cross_val_score(estimator, X, y=None, 
#                                          scoring=None, cv=None, 
#                                          n_jobs=1, verbose=0, 
#                                          fit_params=None, 
#                                          pre_dispatch=‘2*n_jobs’)
# estimator:数据对象
# X:数据
# y:预测数据
# soring:调用的方法
# cv:交叉验证生成器或可迭代的次数
# n_jobs:同时工作的cpu个数(-1代表全部)
# verbose:详细程度
# fit_params:传递给估计器的拟合方法的参数
# pre_dispatch:控制并行执行期间调度的作业数量。减少这个数量对于避免在CPU发送更多作业时CPU内存消耗的扩大是有用的。该参数可以是:
#     - 没有,在这种情况下,所有的工作立即创建并产生。将其用于轻量级和快速运行的作业,以避免由于按需产生作业而导致延迟
#     - 一个int,给出所产生的总工作的确切数量
#     - 一个字符串,给出一个表达式作为n_jobs的函数,如'2 * n_jobs'


rst = cross_val_score(model, X, y, cv=5)
print("模型准确率:{}".format(rst * 100))
模型准确率:[ 96.66666667  96.66666667  93.33333333  93.33333333 100.        ]

这种把数据分成N份,一份作为留出集,其他用来训练的交叉验证类型叫LOO(LeaveOneOut)交叉验证。

极端情况下我们可以把整个数据集每个数据作为一个集合,每次拿出一个数据作为留出集。

from sklearn.model_selection import LeaveOneOut, cross_val_score
 
scores = cross_val_score(model, X, y, cv=LeaveOneOut())

print("验证分数总共:{}".format(scores))

#每次结果求均值作为最终结果
print("\n最终验证结果:{}".format(scores.mean()))
验证分数总共:[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1.
 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1.]

最终验证结果:0.96