机器学习入门系列(2)–如何构建一个完整的机器学习项目,第十一篇!
该系列的前 10 篇文章:
- 机器学习入门系列(2)–如何构建一个完整的机器学习项目(一)
- 机器学习数据集的获取和测试集的构建方法
- 特征工程之数据预处理(上)
- 特征工程之数据预处理(下)
- 特征工程之特征缩放&特征编码
- 特征工程(完)
- 常用机器学习算法汇总比较(上)
- 常用机器学习算法汇总比较(中)
- 常用机器学习算法汇总比较(完)
- 简单聊聊模型的性能评估标准
上一篇文章介绍了性能评估标准,但如何进行模型评估呢,如何对数据集进行划分出训练集、验证集和测试集呢?如何应对可能的过拟合和欠拟合问题,还有超参数的调优,如何更好更快找到最优的参数呢?
本文会一一介绍上述的问题和解决方法。
2. 模型评估的方法
2.1 泛化能力
- 泛化能力:指模型对未知的、新鲜的数据的预测能力,通常是根据测试误差来衡量模型的泛化能力,测试误差越小,模型能力越强;
- 统计理论表明:如果训练集和测试集中的样本都是独立同分布产生的,则有 模型的训练误差的期望等于模型的测试误差的期望 。
- 机器学习的“没有免费的午餐定理”表明:在所有可能的数据生成分布上,没有一个机器学习算法总是比其他的要好。
- 该结论仅在考虑所有可能的数据分布时才成立。
- 现实中特定任务的数据分布往往满足某类假设,从而可以设计在这类分布上效果更好的学习算法。
- 这意味着机器学习并不需要寻找一个通用的学习算法,而是寻找一个在关心的数据分布上效果最好的算法。
- 正则化是对学习算法做的一个修改,这种修改趋向于降低泛化误差(而不是降低训练误差)。
- 正则化是机器学习领域的中心问题之一。
- 没有免费的午餐定理说明了没有最优的学习算法,因此也没有最优的正则化形式。
2.2 泛化能力的评估
常用的对模型泛化能力的评估方法有以下几种,主要区别就是如何划分测试集。
- 留出法(Holdout)
k-fold
交叉验证(Cross Validation)- 留一法(Leave One Out, LOO)
- 自助法(bootstrapping)
2.2.1 留出法(Holdout)
留出法是最简单也是最直接的验证方法,它就是将数据集随机划分为两个互斥的集合,即训练集和测试集,比如按照 7:3 的比例划分,70% 的数据作为训练集,30% 的数据作为测试集。也可以划分为三个互斥的集合,此时就增加一个验证集,用于调试参数和选择模型。
直接采用 sklearn
库的 train_test_split
函数即可实现,一个简单的示例代码如下,这里简单调用 knn
算法,采用 Iris
数据集。
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
# 加载 Iris 数据集
dataset = load_iris()
# 划分训练集和测试集
(trainX, testX, trainY, testY) = train_test_split(dataset.data, dataset.target, random_state=3, test_size=0.3)
# 建立模型
knn = KNeighborsClassifier()
# 训练模型
knn.fit(trainX, trainY)
# 将准确率打印
print('hold_out, score:', knn.score(testX, testY))
留出法的使用需要注意:
-
数据集的划分要尽可能保持数据分布的一致性,避免因为数据划分过程引入额外的偏差而对最终结果产生影响。比如训练、验证和测试集的类别比例差别很大,则误差估计将由于三个集合数据分布的差异而产生偏差。
因此,分类任务中必须保持每个集合中的类别比例相似。从采样的角度看数据集的划分过程,这种保留类别比例的采样方式称为“分层采样”。
-
即便确定了训练、验证、测试集的比例,还是有多种划分方式,比如排序后划分、随机划分等等,这些不同的划分方式导致单次留出法得到的估计结果往往不够稳定可靠。因此,使用留出法的时候,往往采用若干次随机划分、重复进行实验后,取平均值作为最终评估结果。
分层采样的简单代码实现如下所示,主要是调用了 sklearn.model_selection
中的 StratifiedKFold
from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone
def StratifiedKFold_method(n_splits=3):
'''
分层采样
:return:
'''
# 加载 Iris 数据集
dataset = load_iris()
data = dataset.data
label = dataset.target
# 建立模型
knn = KNeighborsClassifier()
print('use StratifiedKFold')
skfolds = StratifiedKFold