在第1章中,您了解了数据科学家的角色如何类似于一位小提琴手,他使用他的管弦乐队和乐器合奏来创作一首优美的乐曲。同样,如果数据科学家想从他的数据和模型中挤出世界级的性能,他可以使用多种集成工具。在本章中,主要目标是学习混合训练数据以获得集成模型的不同方法。
以下是本章的目标。
建立对混合训练数据如何在集成学习中取得良好表现的直观理解
引入决策树
查看使用 scikit-learn 的决策树实现示例
引入随机森林作为决策树的集合
了解采样数据集和两种变体:使用代码示例进行无放回采样和有放回采样
通过使用代码示例了解装袋(引导聚合)
了解交叉验证技术:k折交叉验证和分层k折交叉验证
让我们首先了解为什么混合数据有用。查尔斯达尔文发现,如果一个物种没有足够的遗传多样性,它就更容易灭绝。为什么?如果一个物种的所有成员都具有相同的基因,那么一个物种就更容易受到意想不到的自然灾害和疾病的影响,因为每个人都同样容易受到意想不到的事件的影响。
一个物种如何自然地发展出足够的遗传多样性?当种群分裂并且必须在不同的环境条件下进化时,就会发生发展足够遗传多样性的自然方式之一。这确保了如果一个物种遇到不利和意外的环境,至少该物种的一个子集具有更强的适应力;所以总的来说,这个物种的生存是有保障的。将一个种群分成不同的群体并将它们暴露在不同的环境中会导致种群的进化略有不同,从而导致遗传多样性增加。
使用相同的知识并将其应用于机器学习,如果您使用整个训练数据训练单个模型,如果真实世界的测试数据具有与训练数据相似的分布,则机器学习模型可能表现良好。如果您使用的数据看起来不像训练数据的分布,您可能会遇到问题。为了解决这个问题,将训练数据分成不同的子集并在不同的数据子集上训练多个模型是一个好主意。由于具有不同的训练数据分布,这些模型中的每一个都具有略微不同的推理(真实世界性能)特征。然后我们可以通过集成它们来组合这些模型,以获得比单个模型更好的结果。这称为混合训练数据.
让我们通过一个称为决策树的机器学习模型示例来学习如何混合数据(见图2-1)。
图 2-1简单的决策树
决策树是一种自上而下的类似流程图的方法,您从顶部节点开始。每个节点代表需要根据一个或多个参数/变量做出的决定。您遍历这些节点,直到达到足够的深度,这取决于您要训练的参数数量。
让我们从一个示例数据集 开始,了解如何对其应用决策树。
鸢尾花数据集是机器学习社区中广泛使用的标准数据集。图2-2显示了鸢尾花数据集的决策树。我们的任务是将鸢尾花分为三种不同的花种。该数据集对三种鸢尾花种各有 50 个样本。以下参数在每个样本中可用:萼片长度、萼片宽度、花瓣长度、花瓣宽度。
通过考虑四个参数中的两个(萼片长度和萼片宽度)来创建示例决策树;所有叶节点都分配给一个花种类别。
图 2-2 鸢尾花数据集的决策树
通过从数据集中获取样本,在实践中应用决策树。树遍历 从顶部节点分区样本开始,并根据每个节点的条件进入桶中。每个节点都基于一个答案,然后取一个左节点或右节点。它通过不断应用条件测试从那里开始。你最终会到达底部的叶节点,在那里你得到了最终的分配。
要训练决策树,您可以使用scikit-learn Python 库。清单2-1展示了如何使用 Python 中的 scikit-learn 库来训练决策树。
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
train_X, test_X, train_Y, test_Y = train_test_split(X, y, test_size = 0.2, random_state = 123)
tree = DecisionTreeClassifier()
tree.fit(train_X, train_Y)
print(tree.score(test_X, test_Y))
# Output: 0.9333333333333333
清单 2-1 使用 scikit-learn训练 决策树
决策树的深度越大,训练数据集的准确性就越高。
但是使用决策树存在重大问题。为了获得足够的数据集准确性,您需要一棵树更大(深度更大)的树,但是随着树的深度增加,您开始遇到过度拟合,这会导致测试数据集的准确性降低。因此,您必须满足于精度较低、较浅的决策树或具有更多深度的过度拟合树。
造成此问题的原因之一是,在决策中使用的变量可能不足以从全球视角进行区分。
解决此问题的一种方法是使用多个决策树而不是一个。每个决策树都应该有一组不同的变量或训练数据的一个子集。然后,决策树的输出在随机森林中组合(见图2-3)。
图 2-3对于鸢尾花数据集,决策树数量为 4 的随机森林
顾名思义,随机森林 由决策树的集合组成,每棵树都在不同的训练数据集上进行训练。
清单2-2是Python scikit-learn 中随机森林的代码片段。
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
train_X, test_X, train_Y, test_Y = train_test_split(X, y, test_size = 0.1, random_state = 123)
forest = RandomForestClassifier(n_estimators=8)
forest = forest.fit(train_X, train_Y)
print(forest.score(test_X, test_Y))
# Output: 1.0
rf_output = forest.predict(test_X)
print(rf_output)
# Output: [1 2 2 1 0 2 1 0 0 1 2 0 1 2 2]
清单 2-2使用决策树数 = 4 的scikit-learn训练随机森林
来自一组决策树的随机森林提供了两全其美的方法:更浅的决策树具有更高的准确性,并且过度拟合的可能性更小。
随机森林是决策树集合的一个例子。我们采用单个机器学习模型(决策树)并使用不同训练数据和参数的组合对其进行训练以制作集成模型。
我们如何以不同的方式组合训练数据来制作组合集成方法?细节决定成败!
让我们从学习一些基础知识开始,如果您已经了解这些知识,则可以跳过或略读。
首先,我们谈谈抽样,抽样可以分为两种方式:无放回抽样(WOR)和有放回抽样。
抽样是划分数据集的行为。我们打个比方,一个渔夫在一个小池塘里钓鱼,鱼的数量有限。他想把鱼分成不同的组。他有两种方法可以做到这一点:不放回抽样和放回抽样。
假设渔夫有两个水桶。他从池塘里抓起鱼,然后把它们扔进两个桶中的一个。他的数据集分为两个不同的桶。使用这种方法,永远不会出现一条鱼同时属于两个桶的情况。
将数据集分成两个或多个不相交的集合的抽样方法称为无放回抽样(见图2-4)。
图 2-4 无放回抽样 (WOR)。原始数据集中没有共同样本
清单2-3显示了如何在 Python 的 scikit-learn 中获取样本而无需替换。
from sklearn.utils import resample
import numpy as np
# 固定随机种子,因此结果可以被 Reader 复制
np.random.seed(123)
# 要采样的数据
data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 需要的分割数
num_divisions = 2
list_of_data_divisions = []
for x in range(0, num_divisions):
sample = resample(data, replace=False, n_samples=5)
list_of_data_divisions.append(sample)
print('Samples', list_of_data_divisions)
# Output: Samples [[8, 1, 6, 7, 4], [4, 6, 5, 3, 8]]
清单 2-3 scikit-learn 中的无放回采样
让我们再次使用渔夫的类比。这一次,渔夫有两本日记。当他钓到每条鱼时,他都会在上面记上一个数字,并将这个数字输入到任何一本日记中。但有一个转折点:在他抓到鱼并在日记中对其进行编号后,他将鱼扔回池塘。他继续抓鱼。如果一条鱼已经分配了编号,他会在任何一本日记中输入相同的编号。他重复这个过程,直到池塘里的所有鱼都被分配了一个编号。在此过程中,可能会出现一条鱼条目可能同时出现在两个日志中的情况。这种抽样过程将数据集分成两组,但这些组不需要分离,称为有放回抽样。你可以看看清单2-4有关使用 scikit learn 实现的替换采样的示例代码。
图 2-5
放回抽样 (WR)。请注意,原始数据集中的一些样本在两个样本中都很常见(第 1、4、5 项)
from sklearn.utils import resample
import numpy as np
# 固定随机种子,因此结果可以被 Reader 复制
np.random.seed(123)
# 要采样的数据
data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 需要的分割数
num_divisions = 3
list_of_data_divisions = []
for x in range(0, num_divisions):
sample = resample(data, replace=True, n_samples=4)
list_of_data_divisions.append(sample)
print("Samples", list_of_data_divisions)
# Output: Samples [[3, 3, 7, 2], [4, 7, 2, 1], [2, 1, 1, 4]]
清单 2-4 scikit-learn 中的替换采样
您现在已经熟悉了使用或不使用替换对数据集进行采样。本节介绍最重要的集成技术之一:套袋。Bagging 是bootstrap aggregating的一种简短形式。它是一种集成技术,可将数据集分成n 个样本并进行替换。然后将划分的n 个样本中的每一个分别训练成n 个单独的机器学习模型。然后通过投票将所有独立模型的输出组合成一个输出(见图2-6)。
图 2-6 带放回采样的套袋
如您所见,装袋技术确保每个分类器获得随机样本,从而确保训练模型的多样性。这些不同的模型提供了比任何单个模型都好得多的性能模型。
Bagging 包括三个步骤:bootstrapping、training 和 aggregating。首先,自举 步骤将数据集分成n 个样本,每个样本都是总训练数据的一个子集。这些样本中的每一个都通过使用替换采样技术进行采样。正如我们之前讨论的,有放回抽样确保抽样是真正随机的;一个样本的组成不依赖于另一个样本 。
接下来是训练步骤,您可以在这些样本上分别训练各个模型。此步骤可确保您获得许多针对每个样本训练的相对较弱的机器学习模型。
第三步是聚合,其中使用投票等方法(您在第3章中学习)将所有弱分类器的结果组合起来。
清单2-5是使用所有三个装袋步骤的示例代码。
from sklearn.utils import resample
from sklearn import tree
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
import numpy as np
from sklearn.metrics import accuracy_score
# 要采样的数据
n_samples = 100
X,y = make_classification(n_samples=n_samples, n_features=4, n_informative=2, n_redundant=0, random_state=0, shuffle=False)
# 将数据分成训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.1, random_state = 123)
# 需要的分割数
num_divisions = 3
list_of_data_divisions = []
#将数据划分为分区
for x in range(0, num_divisions):
X_train_sample, y_train_sample = resample(X_train, y_train, replace=True, n_samples=7)
sample = [X_train_sample, y_train_sample]
list_of_data_divisions.append(sample)
#print(list_of_data_divisions)
# 为每个数据划分学习一个分类器
learners = []
for data_division in list_of_data_divisions:
data_x = data_division[0]
data_y = data_division[1]
decision_tree = tree.DecisionTreeClassifier()
decision_tree.fit(data_x, data_y)
learners.append(decision_tree)
# 使用投票合并所有分类器的输出
predictions = []
for i in range(len(y_test)):
counts = [0 for _ in range(num_divisions)]
for j , learner in enumerate(learners):
prediction = learner.predict([X_test[i]])
if prediction == 1:
counts[j] = counts[j] + 1
final_predictions = np.argmax(counts)
predictions.append(final_predictions)
accuracy = accuracy_score(y_test, predictions)
print("Accuracy:", accuracy)
# Output: Accuracy: 0.9
清单 2-5 从基元装袋
您可以直接从 scikit-learn 库中调用 Bagging分类器。清单2-6是来自 Bagging 分类器的 scikit-learn 实现的示例代码。
from sklearn.svm import SVC
from sklearn.ensemble import BaggingClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=100, n_features=4,
n_informative=2, n_redundant=0,
random_state=0, shuffle=False)
# 将数据分成训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 123)
clf = BaggingClassifier(base_estimator=SVC(),
n_estimators=10, random_state=0).fit(X_train, y_train)
print(clf.score(X_test, y_test))
# Output: 0.9
清单 2-6 装袋scikit-learn
进一步探索基于重采样的机器学习技术,最流行的技术之一是交叉验证,尤其是 K 折交叉验证。机器学习研究人员经常会遇到这样的情况,即他们在训练数据集中取得了很好的准确性,即使在测试数据集中也是如此;但是在现实世界或另一个隐藏的测试数据集上应用相同的模型时,它们由于精度不佳而失败。
对于在 Kaggle 等网站上进行竞争性机器学习的人来说,这尤其成问题。造成这种情况的主要原因之一是因为提供的验证集没有在现实世界中可能存在的所有不同分布。一个非常有效的解决方案是在训练和验证数据集本身的划分中应用抽样技术。这种方法称为交叉验证。
最流行的交叉验证技术之一是k折交叉验证(见图2-7)。在这种方法中,您通过使用无放回抽样迭代地划分验证和训练数据集。k表示应用于数据集的划分数。例如,当k = 10 时,我们将数据集分成 10 个不同的部分;9/10 的数据集用于训练,1/10 用于测试数据集的准确性。但分裂并不止于此。该过程重复 10 次,其中更改每个测试和训练划分并重新计算整体精度。模型的最终准确度是通过平均每个单独的准确度数字来计算的。
图 2-7
k 折交叉验证(来源:https ://scikit-learn.org/stable/auto_examples/model_selection/plot_cv_indices.html#sphx-glr-auto-examples-model-selection-plot-cv-indices-py )
清单2-7是使用 scikit-learn 应用此技术的示例代码。
import numpy as np
from sklearn.model_selection import KFold
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([1, 2, 3, 4])
kf = KFold(n_splits=2)
kf.get_n_splits(X)
print(kf)
# Output:
# KFold(n_splits=2, random_state=None, shuffle=False)
for train_index, test_index in kf.split(X):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# Output:
# TRAIN: [2 3] TEST: [0 1]
# TRAIN: [0 1] TEST: [2 3]
清单 2-7 k 折交叉验证
分层k折交叉验证是 k 折交叉验证的另一个子变体。与k折验证一样,您将数据集划分为不同的样本集,并轮流测试和训练集k次以进行统一交叉验证。但是初始采样有一个转折点。在分层k折交叉验证技术中,您确保初始k个样本中的每一个都具有数据分布,使得每个划分后的样本都具有与整个数据集相似的分布。
图2-8显示了 100 个随机生成的输入数据点。三个类在数据点之间不均匀地分布,10 个“组”在数据点之间均匀分布。换句话说,假设在您的整个数据集中有三个类:A 类、B 类和 C 类。每个类的样本比例分别为 30%、50% 和 20%。在分层k折交叉验证技术中,您必须确保如果将此数据集分成k = 5 折,那么这五折中的每一折都有相似的 30%、50% 和 20% 来自 A 类的样本,分别是B类和C类。
图 2-8
分层 k 折交叉验证(来源:https ://scikit-learn.org/stable/auto_examples/model_selection/plot_cv_indices.html#sphx-glr-auto-examples-model-selection-plot-cv-indices-py )
清单2-8是关于如何使用 scikit-learn 应用分层k折交叉验证技术的 Python 示例代码。
import numpy as np
from sklearn.model_selection import StratifiedKFold
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([0, 0, 1, 1])
skf = StratifiedKFold(n_splits=2)
skf.get_n_splits(X, y)
print(skf)
# Output:
# StratifiedKFold(n_splits=2, random_state=None, shuffle=False)
for train_index, test_index in skf.split(X, y):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# Output:
# TRAIN: [1 3] TEST: [0 2]
# TRAIN: [0 2] TEST: [1 3]
清单 2-8 分层 k 折交叉验证
让我们快速回顾一下您在本章中学到的内容。
组合混合数据以制作性能集成模型
使用代码示例的决策树和随机森林
采样数据
抽样的两种主要变体:无放回抽样和有放回抽样
装袋,或引导聚合技术,使用有放回的抽样
交叉验证技术即k折交叉验证和分层k折交叉验证
通过 scikit-learn 中的代码示例问题应用这些技术