模型的评估与选择
在机器学习中,模型评估与选择是一个至关重要的步骤。通过模型评估和选择,我们可以了解模型的性能、找到最佳模型和提高预测精度。在本篇博客中,我们将深入研究机器学习中的模型评估与选择的重要性、常用的评估方法和选择最佳模型的技术,帮助读者更好地了解机器学习中的模型评估和选择。
1、导言
我们把学习器的实际预测输出与样本的真实输出之间的差异称为“误差”,学习器在训练集上的误差称为“训练误差”,在新样本上的误差称为“泛化误差”,我们实际希望的,是学习器在新样本上表现的更好,为了达到这个目的,应该从训练样本中学出适合于所有潜在样本的普遍规律,这样才能在遇到新样本时做出正确的判别。然而,当学习器把训练样本学习得”太好了“的时候,很可能就会把训练集样本自身特有的规律当作所有潜在样本的普遍规律,这种现象我们称之为”过拟合“(overfitting),相对的,如果我们在训练集中连普遍规律都没学好的话,这种现象称之为”欠拟合“(underfitting)。
2、评估方法
通常,我们通过实验测试来对学习的泛化误差进行评估而做出选择,为此我们需要将输入数据分为训练集(training set)和测试集(testing set),将测试集中的测试误差作为泛化误差的近似,我们假设测试样本是从样本真实分布中独立同分布采样得来的。需要注意的是,测试集应该尽可能和训练集互斥,即测试样本尽量不在训练集中出现。
那么,我们应该怎样从输数据入中获得训练集和测试集呢?下面介绍几种常见的做法。
2.1、留出法
留出法(hold-out)直接将数据集D划分为两个互斥的集合,其中一个集合作为训练集S,另一个作为测试集T,即 D = S ⋃ T , S ⋂ T = ∅ D=S \bigcup T,S\bigcap T=\varnothing D=S⋃T,S⋂T=∅。在S上训练出模型后,用T来评估其测试误差,作为泛化误差的估计。留出法通常用于样本量很大的情况。
让我们看看留出法在Python中用代码是怎么实现的
# 导入数据集
# Iris 鸢尾花数据集是一个经典数据集,在统计学习和机器学习领域都经常被用作示例。数据集内包含 3 类共 150 条记录,每类各 50 个数据,每条记录都有 4 项特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度,可以通过这4个特征预测鸢尾花卉属于(iris-setosa, iris-versicolour, iris-virginica)中的哪一品种。
from sklearn.datasets import load_iris
iris = load_iris()
X_data,y = iris.data,iris.target
print("Dataset labels{}".format(iris.target))
#用留出法划分数据集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X_data,y,train_size=0.6,random_state=2)
print("Train labels\n{}".format(y_train))
print("Test labels\n{}".format(y_test))
输出结果为
Dataset labels
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 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 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]
Train labels
[0 2 1 1 2 2 1 0 1 0 2 1 1 0 1 1 1 2 0 1 0 1 2 0 1 0 0 0 2 2 0 0 2 2 1 2 1
1 2 0 2 2 2 0 2 0 0 1 2 1 2 1 1 2 1 1 1 2 1 2 1 0 1 1 1 1 2 1 0 0 2 1 2 0
2 0 2 2 0 1 0 2 1 0 2 1 0 0 1 0]
Test labels
[0 0 2 0 0 2 0 2 2 0 0 0 0 0 1 1 0 1 2 1 1 1 2 1 1 0 0 2 0 2 2 0 1 2 1 0 2
1 1 2 1 1 2 1 0 2 0 1 0 0 0 2 2 2 0 2 2 2 2 0]
当数据集中包含多个类别,并且每个类别的样本数不同,尤其是某些类别的样本数较少时,可以使用分层采样来确保每个类别的样本在训练集和测试集中都有相应的比例。分层采样划分数据主要目的是避免训练集或测试集中缺少某些类别的样本,从而导致模型训练不充分或测试结果不准确的问题。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import numpy as np
iris = load_iris()
X_data,y = iris.data,iris.target
# 在python中实现分层采样非常简单,只需要在调用train_test_split()的时候设置stratify就行了
# stratify参数需要传入一个与原始数据集相同长度的一维数组,该数组用于指定目标变量的类别信息。在这种情况下,我们传递的是原始数据集中的目标变量 y。
X_train,X_test,y_train,y_test = train_test_split(X_data,y,train_size=0.6,random_state=2,stratify=y)
print("训练集中每一个类别的个数为:\n{}".format(np.bincount(y_train)))
print("测试集中每一个类别的个数为:\n{}".format(np.bincount(y_test)))
输出结果为
训练集中每一个类别的个数为:
[30 30 30]
测试集中每一个类别的个数为:
[20 20 20]
2.2、交叉验证法
“交叉验证法”(cross validation)先将数据集划分为K个大小相似的互斥子集,即 D = D 1 ⋃ D 2 . . . ⋃ D k , D i ⋂ D j = ∅ ( i ≠ j ) D = D{_1}\bigcup D{_2}...\bigcup D{_k}, D{_i}\bigcap D{_j} = \varnothing(i \neq j) D=D1⋃D2...⋃Dk,Di⋂Dj=∅(i=j),
每一个自己 D i D{_i} Di都尽可能保持数据分布的一致性,即从 D D D中通过分层采样得到。然后每次用 k − 1 k-1 k−1个子集的并集作为训练集;这样就可以得到 k k k组训练 / / /测试集,从而可以进行 k k k次训练,因此交叉验证法也称为“ k k k折交叉验证法” k − k- k−fold cross validation。 k k k常用的取值为10,此时称为10折交叉验证法。下面是10折交叉验证法的示意图.
我们来看看在python中是怎样实现交叉验证法的,输出结果就不在这里展示了,大家复制一下自己到pycharm去实现。同样,我们在用交叉验证法的时候也会遇到某种类别的样本数特别少的情况,遇到这种情况的时候我们可以采用分层 k k k折交叉验证数据划分法,在使用时只需要将下面代码中的KFold改为使用StratifiedKFold就行了,其他代码和普通的交叉验证一样,这里就不重复书写了。交叉验证法通常用于样本量不充分的情况下。
from sklearn.model_selection import KFold
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn import metrics
data = load_iris() # 获取莺尾花数据集
x = data.data
y = data.target
kf = KFold(n_splits=10,shuffle=True,random_state=1) #10折交叉验证
for train_index,test_index in kf.split(x,y):
X_train,X_test = x[train_index],x[test_index]
Y_train,Y_test = y[train_index],x[test_index]
print("Train labels:\n{}".format(Y_train))
print("Test labels:\n{}".format(Y_test))
2.3、自助法
“自助法”(bootstrapping)也称之为有放回采样,给定 m m m个样本的数据集 D D D,我们对他进行采样产生数据集 D ′ D{'} D′:每次随机从 D D D中挑选一个样本,将其拷贝放入 D ′ D{'} D′,然后再将这个样本放入初始数据集 D D D中,使得该样本在下次采样时仍然有可能被采到,这个过程重复执行 m m m次后,我们就得到了包含 m m m个样本的数据集 D ′ D{'} D′,于是我们可将 D ′ D{'} D′作为训练集, D D ′ DD{'} DD′作为测试集,这就是自助法。自助法通常用于样本量特别少的情况下。下面我们来看看自助法的代码是怎样实现的。
import numpy as np
# 产生样本
x = np.random.randint(-10,10,10)
y = (x>0).astype(int)
# bootstrapping中存储的是随机选择的样本的下标
bootstrapping = []
for i in range(len(x)):
bootstrapping.append(np.floor(np.random.random()*len(x))) # np.floor()返回向下取整的浮点数
X_1 = []
Y_1 = []
for i in range(len(x)):
X_1.append(x[int(bootstrapping[i])])
Y_1.append(y[int(bootstrapping[i])])
print(X_1)
print(Y_1)
# 输出结果
[7, 4, 7, -3, 3, 4, -3, 5, 4, 5]
[1, 1, 1, 0, 1, 1, 0, 1, 1, 1]
3、性能度量
对机器学习的泛化性能进行评估,不仅需要有效可行的实验估计方法还需要有衡量模型泛化能力的评价标准,这就是性能度量(performance measure),性能度量反映了任务需求,在对比不同的模型时,使用不同的性能度量往往会导致不同的评判结果;这意味着模型的好坏是相对的,不仅取决于算法和数据,还取决于任务需求。
在评估学习器 f f f的性能通常是把预测结果 f ( x ) f(x) f(x) 和数据的真实标签进行比较。(下文中把预测值 f ( x ) f(x) f(x)记为 y ^ \hat{y} y^),下面介绍几种常见的性能度量。
3.1、回归问题的性能度量
3.1.1、平均绝对误差(MAE)
M A E ( y , y ^ ) = 1 n ∑ i = 1 n ∣ y i − y ^ ∣ MAE(y,\hat{y})=\dfrac{1}{n}\sum\limits_{i=1}^n\vert {y{_i-\hat{y}}}\vert MAE(y,y^)=