一篇简明的python机器学习模型评估与超参数调试基础总结(交叉验证,学习曲线,网格搜索等)

文章目录

  • 交叉验证
    • 保持法(holdout method)
    • 交叉验证原理
    • 选择K
    • python实现分层交叉验证
  • 模型评估
    • 学习曲线(learning curve)
      • python 实现
    • 验证曲线(validation curve)
      • python实现
  • 超参数调试
    • 网格搜索(grid search)
      • python实现
    • 嵌套交叉验证(nested cross-validation)
      • 双层嵌套(5-2法)
      • python实现

source:python machine learning 3rd

高方差和高偏置是机器学习碰到的常见问题,而在高方差及高偏置之间寻找一个平衡点从而对模型进行准确的预测涉及到了我们对模型的评估(找到问题)和对超参数的调整(解决问题),其中最典型的超参数就是正则系数 λ \lambda λ,它的大小直接影响了模型在高方差和高偏置之间的表现。

此外,模型评估和超参数的调整对于解决机器学习的其它问题都是必要的,本文重点介绍几个经典方法来解决这一问题

如果你想尝试这些代码,你可以使用wine数据集

交叉验证

交叉训练集同样通过数字评价来评估模型,使用交叉训练集和使用测试集都能够让我们评估模型预测新数据的能力。但是交叉训练集专门用于模型选择,即帮助我们评价和调整超参数的表现,而测试集则为我们的模型进行总评

实践证明使用测试集完成交叉验证集的工作是错误的方法

保持法(holdout method)

一篇简明的python机器学习模型评估与超参数调试基础总结(交叉验证,学习曲线,网格搜索等)_第1张图片
保持法的核心原则是在得到完整的训练模型和各项参数之前,保持测试集不参与模型构建

为了维护这个原则,保持法从训练集中分离出了验证集,来检测不同超参数产生的预测结果

交叉验证原理

一篇简明的python机器学习模型评估与超参数调试基础总结(交叉验证,学习曲线,网格搜索等)_第2张图片

  1. 从原始数据集中分割出交叉训练集
  2. 将交叉训练集分割为k个数据集,比如k=10
  3. 放回地选择一个不重复的数据集,使用从训练集获得的模型参数对其它集进行训练,然后使用选择的数据集进行评分,重复知道获得了k组分数
  4. 对k组分数求平均值,结果为模型评分

拥有了模型评分,我们就拥有了评价和调整模型超参数的依据

选择K

  • k = 10一般都能达到最好的评估效果
  • 面对小数据集,适当增加k,模型评估效果更好
  • 面对大数据集,适当减小k,可以在减少计算量的同时保证模型评估的准确性

python实现分层交叉验证

分层交叉验证相比于交叉验证,能够通过确保验证集中各类比例与原始数据相同来获得更好的评估效果:

import numpy as np
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import cross_val_score
# 获得交叉验证集
...
# 获得分割后的k组数据集
kfold = StratifiedKFold(n_splits=10).split(X_train, y_train)
# 完成k次迭代,获得评估模型
for k, (train, test) in enumerate(kfold):
	pipe_lr.fit(X_train[train], y_train[train])
# 计算分数
scores = cross_val_score(estimator=pipe_lr, X=X_train, y=y_train, cv=10, n_jobs=1)
# 获得最总评估结果
final_score = np.mean(scores)

模型评估

学习曲线(learning curve)

交叉验证集获得的分数可以作为我们评估模型参数表现的有效依据,但是我们选择的参数到底是对模型产生了高偏置还是高方差的影响呢?这个时候我们需要学习曲线:
一篇简明的python机器学习模型评估与超参数调试基础总结(交叉验证,学习曲线,网格搜索等)_第3张图片
简单而言,学习曲线就是训练集和验证集的测试得分随着训练样本(验证集的样本数量没有增多)的增加所得到的曲线

从上图的学习曲线我们可以直观地判断高方差和高偏置:

  • 高方差:验证集和训练集的准确度都很低
  • 高偏置:训练集准确度高,验证集准确度低

python 实现

# 假设我们已经获得了x_train和y_train
...
import matplotlib.pyplot as plt

# 导入学习曲线
from sklearn.model_selection import learning_curve


pipe_lr = make_pipeline(StandardScaler(), # 需导入
                        LogisticRegression(penalty='l2', random_state=1,
                                           solver='lbfgs', max_iter=10000))

train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_lr,
							                             X=X_train,
							                              y=y_train,
							                              # 关键的一部设置,以10为单位递增训练样本的数量,从而获得学习曲线
							                              train_sizes=np.linspace(0.1, 1.0, 10),
							                              cv=10,
							                              # n_jobs控制参与计算的cpu核数
							                              n_jobs=1)

可视化:

# train_std考虑了准确度得分的偏差,这个值越小,曲线置信度越高
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')

plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')

plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')

plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')

plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.03])

plt.tight_layout()
plt.show()

一篇简明的python机器学习模型评估与超参数调试基础总结(交叉验证,学习曲线,网格搜索等)_第4张图片
可见这是一个平衡了高方差和高偏置的良好模型

验证曲线(validation curve)

即使我们通过学习曲线判断出了模型出现的问题,那么到底是什么原因造成了高偏置/高方差的出现呢?具体而言,我们应该调整哪些参数,如何调整?这个时候,就该验证曲线出场了!

python实现

验证曲线的实现和学习曲线十分接近,主要在于参数的设置不同,我们直接从代码来看:

...
# 引入验证曲线
from sklearn.model_selection import validation_curve


param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]

train_scores, test_scores = validation_curve(
                estimator=pipe_lr, 
                X=X_train, 
                y=y_train, 
                # 设置选择分析的参数,这里以C(1\lambda)为例,参数名称是sklearn固定的
                param_name='logisticregression__C', 
                # 设置参数的分析范围
                param_range=param_range,
                cv=10)

可视化:

train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

plt.plot(param_range, train_mean, 
         color='blue', marker='o', 
         markersize=5, label='Training accuracy')

plt.fill_between(param_range, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')

plt.plot(param_range, test_mean, 
         color='green', linestyle='--', 
         marker='s', markersize=5, 
         label='Validation accuracy')

plt.fill_between(param_range, 
                 test_mean + test_std,
                 test_mean - test_std, 
                 alpha=0.15, color='green')

plt.grid()
plt.xscale('log')
plt.legend(loc='lower right')
plt.xlabel('Parameter C')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.0])
plt.tight_layout()
plt.show()

一篇简明的python机器学习模型评估与超参数调试基础总结(交叉验证,学习曲线,网格搜索等)_第5张图片
图都画出来了,很快我们就会发现,C取值应设置在1附近,如果C过大,就会产生高方差,C过小就会产生高偏置,由此,我们可以很方便检验我们参数的选择

超参数调试

网格搜索(grid search)

单个单个查参数未免太繁琐了,即使都获得了单独表现的最佳参数组,怎么组合也是个问题,我们搞机器学习肯定是希望能够同时获得一组最优参数,这个时候不得不上网格搜索了~

网格搜索的原理很简单,即暴力演算:

  • 不同的参数组合
  • 不同的参数取值(比如逻辑回归中的参数C的取值)
  • 不同的参数种类(比如’l1’, ‘l2’…)

谁得到的分数高,谁就选谁

scikit learn中的网格搜索可以给我们提供三个最佳:

  • 最佳参数
  • 最佳模型(estimator)
  • 最佳模型预测得分
  • 。。。等等

python实现

from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC

pipe_svc = make_pipeline(StandardScaler(),
                         SVC(random_state=1))

# 设置好不同的参数范围
param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0]

# 设置好不同的参数组合
param_grid = [{'svc__C': param_range, 
               'svc__kernel': ['linear']},
              {'svc__C': param_range, 
               'svc__gamma': param_range, 
               'svc__kernel': ['rbf']}]

# 开始进行网格搜索
gs = GridSearchCV(estimator=pipe_svc, 
                  param_grid=param_grid, 
                  # 选择评分方法
                  scoring='accuracy', 
                  # refit会自动训练选择出来的租价模型
                  refit=True,
                  cv=10,
                  n_jobs=-1)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)

clf = gs.best_estimator_

# clf.fit(X_train, y_train) 
# note that we do not need to refit the classifier
# because this is done automatically via refit=True.

print('Test accuracy: %.3f' % clf.score(X_test, y_test))

结果:

-> 0.9846859903381642 最佳模型评分
-> {'svc__C': 100.0, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'} 最佳参数组合
-> Test accuracy: 0.974 使用最佳模型预测新数据时的得分

实践证明,每个表现最佳的参数组合到一起不一定就最佳的参数组合,你可能需要使用网格搜索不停地进行尝试

嵌套交叉验证(nested cross-validation)

使用交叉验证时,你可能仍然会怀疑:交叉验证集始终是同一部分数据,会不会使得模型评估仍然会因为这一部分数据而产生适应性导致偏差产生呢?

答案是是的,为此,我们引入嵌套交叉验证来完完全全解决这个麻烦

双层嵌套(5-2法)

嵌套验证仍然使用十套数据集,但是分为两层嵌套使用,其中:

  • 外层分为五组
  • 内层分为两组

故又称为5-2法,一般使用嵌套验证都使用这种嵌套方式
一篇简明的python机器学习模型评估与超参数调试基础总结(交叉验证,学习曲线,网格搜索等)_第6张图片
从图中我们可以看出,外层通过选择不同的测试集,实现了外层的五次循环;而内层实际上等分了训练集为训练集+交叉验证集

python实现

sklearn可以通过十分简洁巧妙地方法帮助我们实现美妙的嵌套循环,其中需要用到网格搜索,这也是为什么嵌套循环排在最后的原因:

...
from sklearn.tree import DecisionTreeClassifier

# 使用网格搜索确定内层,设置cv=2,即内层两次循环
# 此时你仍然可以使用网格搜索顺便选几个最佳参数,获得的评分默认为最佳模型获得的分数,多么美妙~
gs = GridSearchCV(estimator=DecisionTreeClassifier(random_state=0),
                  param_grid=[{'max_depth': [1, 2, 3, 4, 5, 6, 7, None]}],
                  scoring='accuracy',
                  cv=2)

# 使用交叉验证将原始数据区分为五组,分为训练级和测试集
# 这里的训练集,验证集,测试集都只是分割数据的一种方式,可以对分类得到的数据集自定义是哪一种
scores = cross_val_score(gs, X_train, y_train, 
                         scoring='accuracy', cv=5)
print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), 
                                      np.std(scores)))

感谢你的阅读,希望这边文章能够值得你花费的时间,欢迎提出建议和补充

你可能感兴趣的:(python机器学习,-)