对模型评估的基本步骤如下:
在机器学习问题中,从理论上我们需要对数据集划分为训练集、验证集、测试集。
如果在一个数据集A上进行训练,同样再用A作为评估数据集,无法评估在新数据集上的表现,为了避免这种情况,通常会取出一部分未被训练的数据作为测试集。
假如我们用训练集训练了一个随机森林模型,用测试集去评估模型,评估的过程中,会手动调整树颗数或者树深度这种超参数,然后再去测试集上看看效果如何。其实这种操作是错误的,这样做是在迎合测试集,造成测试集数据泄露,如果下一次再随机划分测试集,效果未必好,所以这是一个高方差的估计。
任何方式使用来自测试集的信息这种行为,都是 “窥视”。
习惯上我们称评估最终模型性能的数据集为 “测试集”,最终模型的评估必须在一个没有在训练模型或是调整参数时被使用过的数据集上进行。
问:若“封存”了测试集,那么通过什么来评估模型对不可见数据的性能呢?通过什么标准调参(超参数)呢?
答案:验证集
验证集用于初步评估模型能力,当验证集上的评估实验比较成功时,在测试集上进行最后的评估。
但是仅仅有单一的验证集还是有局限性的:
所以用K折交叉验证的方式来评估模型,能够减小偏差,并且评估性能较稳定。
如何解决上述问题?
答案:K折交叉验证
首先简述下K折交叉验证思路:
验证数据其实是训练数据的一部分,假设将训练数据分成4折:
最终分数是S1、S2、S3、S4的平均。
K-cv解决的问题:
训练集中的每个数据都可以参与训练,解决了训练数据不足的问题。
K折就是K个验证集,验证了K次,减小了验证集的不确定性。
把数据集分成K份,每个子集互不相交且大小相同,依次从K份中选出1份作为验证集,其余K-1份作为训练集,这样进行K次单独的模型训练和验证,最后将K次验证结果取平均值,作为此模型的验证误差。当K=m时,就变为留一法。可见留一法是K折交叉验证的特例。
根据经验,K一般取10。(在各种真实数据集上进行实验发现,10折交叉验证在偏差和方差之间取得了最佳的平衡。)
# 导入相关库
from sklearn.model_selection import train_test_split
from sklearn import datasets
from sklearn import metrics
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import pandas as pd
# 导入数据
df = pd.read_csv(r'D:\Program Files (x86)\Python\Lib\site-packages\tensorflow\contrib\learn\python\learn\datasets\data\iris.csv')
# 划分数据集和测试集
print(df.shape)
train_set, test_set = train_test_split(df, test_size=0.3,
random_state=12345)
print(train_set.shape,test_set.shape)
评估模型时,我们最常用的方法之一就是交叉验证
例如10折交叉验证(10-fold cross validation),将数据集分成十份,轮流将其中9份做训练1份做验证,10次的结果的均值作为对算法精度的估计,一般还需要进行多次10折交叉验证求均值,例如:10次10折交叉验证,以求更精确一点。
# 加载数据
digits = datasets.load_digits()
# 创建特征矩阵
features = digits.data
target = digits.target
# 进行标准化
stand = StandardScaler()
# 创建logistic回归器
logistic = LogisticRegression()
# 创建一个包含数据标准化和逻辑回归的流水线
pipline = make_pipeline(stand, logistic)# 先对数据进行标准化,再用logistic回归拟合
# 创建k折交叉验证对象
kf = KFold(n_splits=10, shuffle=True, random_state=1)
# 进行k折交叉验证
cv_results = cross_val_score(pipline,
features,
target,
cv=kf,
scoring='accuracy',#评估的指标
n_jobs=-1)#调用所有的cpu
print(cv_results.mean())
使用pipeline方法可以使得我们这个过程很方便,上述我们是直接对数据集进行了交叉验证,在实际应用中,建议先对数据集进行划分,再对训练集使用交叉验证。
# 导入相关库
from sklearn.model_selection import train_test_split
from sklearn import datasets
from sklearn import metrics
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import pandas as pd
# 导入数据
df = pd.read_csv(r'D:\Program Files (x86)\Python\Lib\site-packages\tensorflow\contrib\learn\python\learn\datasets\data\iris.csv')
# 划分数据集和测试集
print(df.shape)
train_set, test_set = train_test_split(df, test_size=0.3,
random_state=12345)
print(train_set.shape,test_set.shape)
# 加载数据
digits = datasets.load_digits()
# 创建特征矩阵
features = digits.data
target = digits.target
# 划分数据集
features_train, features_test, target_train, target_test = train_test_split(features,
target,
test_size=0.1,random_state=1)
# 进行标准化
stand = StandardScaler()
# 使用训练集来计算标准化参数
stand.fit(features_train)
# 然后在训练集和测试集上运用
features_train_std = stand.transform(features_train)
features_test_std = stand.transform(features_test)
# 创建logistic回归器
logistic = LogisticRegression()
pipeline = make_pipeline(stand, logistic)
# 创建k折交叉验证对象
kf = KFold(n_splits=10, shuffle=True, random_state=1)
cv_results = cross_val_score(pipeline,
features_train_std,
target_train,
cv=kf,
scoring='accuracy',
n_jobs=-1)
print(cv_results.mean())
这里之所以这样处理是因为我们的测试集是未知数据,如果使用测试集和训练集一起训练预处理器的话,测试集的信息有一部分就会泄露,因此是不科学的。在这里我认为更general的做法是先将训练集训练模型,用验证集评估选择模型,最后再用训练集和验证集一起来训练选择好的模型,再来在测试集上进行测试。