如何从头开始使用Python实现堆栈泛化(Stacking)

      【翻译自 :  How to Implement Stacked Generalization (Stacking) From Scratch With Python

      【说明:Jason Brownlee PhD大神的文章个人很喜欢,所以闲暇时间里会做一点翻译和学习实践的工作,这里是相应工作的实践记录,希望能帮到有需要的人!】

      集成方法是提高机器学习问题的预测性能的绝佳方法。堆叠概括或堆叠是一种集成技术,它使用新模型来学习如何最佳地组合来自数据集上训练的两个或多个模型的预测。

      在本教程中,您将发现如何在Python中从头开始实现堆栈。完成本教程后,您将知道:

如何学习在数据集上组合来自多个模型的预测。
如何将堆叠泛化应用于现实世界的预测建模问题。

描述
     

       本节简要概述了本教程中使用的Stacked Generalization算法和Sonar数据集。

堆叠泛化算法

      堆叠泛化或堆叠是一种集成算法,其中训练了一个新模型,以结合来自两个或多个已经训练的模型或您的数据集中的预测。来自现有模型或子模型的预测使用新模型进行组合,因此,由于来自子模型的预测被混合在一起,因此通常将其称为混合。通常,使用简单的线性方法将子模型的预测(例如简单平均或投票)与使用线性回归或逻辑回归的加权总和相结合。结合了预测的模型必须具有解决问题的技能,但不必成为最佳模型。这意味着您无需专心调整子模型,只要模型相对于基线预测显示出某些优势即可。重要的是,子模型会产生不同的预测,即所谓的不相关预测。当合并的预测都是熟练的,但以不同的方式熟练时,堆叠的效果最佳。这可以通过使用使用非常不同的内部表示(与实例相比,树)和/或在训练数据的不同表示或投影上训练的模型的算法来实现。在本教程中,我们将研究两个非常不同且未调整的子模型,并将它们的预测与简单的逻辑回归算法结合起来。

声纳数据集

      我们将在本教程中使用的数据集是Sonar数据集。这是一个描述声纳chi回波从不同表面弹起的数据集。 60个输入变量是不同角度的收益强度。这是一个二元分类问题,需要一个模型来区分岩石和金属圆柱体。有208个观察结果。

这是一个易于理解的数据集。所有变量都是连续的,通常在0到1的范围内。输出变量是字符串“ M”(对于矿井)和“ R”(对于岩石),需要将其转换为整数1和0。通过用数据集中(M或地雷)中观察次数最多的类别进行预测,零规则算法可以实现约53%的准确性。您可以在UCI机器学习存储库中了解有关此数据集的更多信息。免费下载数据集,并将其放置在文件名为sonar.all-data.csv的工作目录中。

教程

    本教程分为3个步骤:

子模型和聚合器。
结合预测。
声纳数据集案例研究。

     这些步骤提供了您需要了解和实现自己的预测建模问题的基础。

1.子模型和聚合器

     我们将使用两个模型作为用于堆叠的子模型,并使用线性模型作为聚合器模型。

这部分分为3个部分:

子模型1:k个最近邻居。
子模型2:感知器。
聚集器模型:逻辑回归。

       将根据用于训练模型的功能和用于进行预测的功能来描述每个模型。

1.1子模型1:k个近邻

      k最近邻居算法或kNN使用整个训练数据集作为模型。因此,训练模型涉及保留训练数据集。 下面是一个名为knn_model()的函数。

# Prepare the kNN model
def knn_model(train):
	return train

      进行预测涉及在训练数据集中找到k个最相似的记录,并选择最常见的班级值。 欧几里得距离函数用于计算新数据行与训练数据集中的行之间的相似度。 以下是涉及对kNN模型进行预测的这些辅助函数。 函数euclidean_distance()计算两行数据之间的距离,get_neighbors()在训练数据集中找到新数据行的所有邻居,而knn_predict()从邻居对新数据行进行预测。

# Calculate the Euclidean distance between two vectors
def euclidean_distance(row1, row2):
	distance = 0.0
	for i in range(len(row1)-1):
		distance += (row1[i] - row2[i])**2
	return sqrt(distance)

# Locate neighbors for a new row
def get_neighbors(train, test_row, num_neighbors):
	distances = list()
	for train_row in train:
		dist = euclidean_distance(test_row, train_row)
		distances.append((train_row, dist))
	distances.sort(key=lambda tup: tup[1])
	neighbors = list()
	for i in range(num_neighbors):
		neighbors.append(distances[i][0])
	return neighbors

# Make a prediction with kNN
def knn_predict(model, test_row, num_neighbors=2):
	neighbors = get_neighbors(model, test_row, num_neighbors)
	output_values = [row[-1] for row in neighbors]
	prediction = max(set(output_values), key=output_values.count)
	return prediction

        您可以看到,邻居数(k)设置为2作为knn_predict()函数的默认参数。 选择此编号时需要经过反复尝试,并且尚未调整。 现在我们已经有了kNN模型的基础,让我们看一下Perceptron算法。

1.2子模型2:感知器

      Perceptron算法的模型是从训练数据中学到的一组权重。为了训练权重,需要对训练数据进行许多预测以计算误差值。 因此,模型训练和预测都需要具有预测功能。以下是用于实现Perceptron算法的帮助函数。 perceptron_model()函数在训练数据集上训练Perceptron模型,perceptron_predict()用于对一行数据进行预测。

# Make a prediction with weights
def perceptron_predict(model, row):
	activation = model[0]
	for i in range(len(row)-1):
		activation += model[i + 1] * row[i]
	return 1.0 if activation >= 0.0 else 0.0

# Estimate Perceptron weights using stochastic gradient descent
def perceptron_model(train, l_rate=0.01, n_epoch=5000):
	weights = [0.0 for i in range(len(train[0]))]
	for epoch in range(n_epoch):
		for row in train:
			prediction = perceptron_predict(weights, row)
			error = row[-1] - prediction
			weights[0] = weights[0] + l_rate * error
			for i in range(len(row)-1):
				weights[i + 1] = weights[i + 1] + l_rate * error * row[i]
	return weights

       perceptron_model()模型将学习率和训练时期数都指定为默认参数。 同样,这些参数的选择是经过反复试验的,但并未在数据集中进行调整。 现在,我们有两个子模型的实现,让我们看一下实现聚合器模型的过程。

1.3聚集器模型:逻辑回归

      像Perceptron算法一样,逻辑回归使用一组权重(称为系数)来表示模型。就像Perceptron算法一样,系数是通过迭代对训练数据进行预测并对其进行更新来学习的。以下是用于实现逻辑回归算法的辅助函数。 logistic_regression_model()函数用于在训练数据集上训练系数,而logistic_regression_predict()用于对一行数据进行预测。

# Make a prediction with coefficients
def logistic_regression_predict(model, row):
	yhat = model[0]
	for i in range(len(row)-1):
		yhat += model[i + 1] * row[i]
	return 1.0 / (1.0 + exp(-yhat))

# Estimate logistic regression coefficients using stochastic gradient descent
def logistic_regression_model(train, l_rate=0.01, n_epoch=5000):
	coef = [0.0 for i in range(len(train[0]))]
	for epoch in range(n_epoch):
		for row in train:
			yhat = logistic_regression_predict(coef, row)
			error = row[-1] - yhat
			coef[0] = coef[0] + l_rate * error * yhat * (1.0 - yhat)
			for i in range(len(row)-1):
				coef[i + 1] = coef[i + 1] + l_rate * error * yhat * (1.0 - yhat) * row[i]
	return coef

        logistic_regression_model()将学习率和时期数定义为默认参数,并且与其他算法一样,这些参数经过反复试验后仍发现并且未优化。 现在,我们已经有了子模型和聚合器模型的实现,让我们看看如何将多个模型的预测结合起来。

2.结合预测

      对于机器学习算法,学习如何组合预测与从训练数据集中学习非常相似。可以根据子模型的预测来构建新的训练数据集,如下所示:

每行代表训练数据集中的一行。
第一列包含由第一子模型(例如k最近邻居)在训练数据集中每一行的预测。
第二列包含第二个子模型(例如Perceptron算法)对训练数据集中的每一行的预测。
第三列包含训练数据集中该行的预期输出值。

     下面是一个构造的堆叠数据集的人为示例:

kNN,	Per,	Y
0,	0	0
1,	0	1
0,	1	0
1,	1	1
0,	1	0

       然后可以在此新数据集上训练机器学习算法,例如逻辑回归。 从本质上讲,这个新的元算法学习如何最佳地组合来自多个子模型的预测。

       下面是一个名为to_stacked_row()的函数,该函数实现此过程以为此堆栈数据集创建新行。

      该函数将模型列表作为输入,这些模型用于进行预测。 该函数还将函数列表作为输入,一个函数用于对每个模型进行预测。 最后,包括训练数据集中的一行。

     一次将新行构造为一列。 使用每种模型和训练数据行来计算预测。 然后,将训练数据集行的预期输出值添加为该行的最后一列。

# Make predictions with sub-models and construct a new stacked row
def to_stacked_row(models, predict_list, row):
	stacked_row = list()
	for i in range(len(models)):
		prediction = predict_list[i](models[i], row)
		stacked_row.append(prediction)
	stacked_row.append(row[-1])
	return stacked_row

      在某些预测性建模问题上,可以通过在训练行和子模型进行的预测两者上训练聚合模型来获得更大的提升。

     此改进使聚合器模型同时具有训练行中所有数据的上下文,以帮助确定如何以及何时最佳地组合子模型的预测。

     我们可以通过汇总训练行(减去最后一列)和上面创建的堆叠行来更新to_stacked_row()函数以包括此函数。

     以下是实现此改进的to_stacked_row()函数的更新版本。

# Make predictions with sub-models and construct a new stacked row
def to_stacked_row(models, predict_list, row):
	stacked_row = list()
	for i in range(len(models)):
		prediction = predict_list[i](models[i], row)
		stacked_row.append(prediction)
	stacked_row.append(row[-1])
	return row[0:len(row)-1] + stacked_row

        尝试对问题使用两种方法以查看哪种方法最好是一个好主意。 既然我们拥有用于堆叠概括的所有要素,我们就可以将其应用于实际问题。

3.声纳数据集案例研究

       在本节中,我们将把Stacking算法应用于Sonar数据集。该示例假定数据集的CSV副本位于当前工作目录中,文件名为sonar.all-data.csv。首先加载数据集,将字符串值转换为数字,然后将输出列从字符串转换为0到1的整数。这可以通过辅助函数load_csv(),str_column_to_float()和str_column_to_int()来实现数据集。我们将使用k倍交叉验证来评估学习模型在看不见的数据上的性能。这意味着我们将构建和评估k个模型,并将性能评估为平均模型误差。分类准确性将用于评估模型。这些行为在cross_validation_split(),precision_metric()和valuate_algorithm()帮助函数中提供。

     我们将使用上面实现的k最近邻,感知器和逻辑回归算法。我们还将使用我们的技术来创建上一步中定义的新的堆叠数据集。开发了一个新的函数名称stacking()。此功能执行4件事:

它首先训练模型列表(kNN和Perceptron)。
然后使用模型进行预测并创建一个新的堆叠数据集。
然后,在堆叠的数据集上训练聚合模型(逻辑回归)。
然后,它使用子模型和聚合器模型对测试数据集进行预测。

       下面列出了完整的示例。

# Test stacking on the sonar dataset
from random import seed
from random import randrange
from csv import reader
from math import sqrt
from math import exp

# Load a CSV file
def load_csv(filename):
	dataset = list()
	with open(filename, 'r') as file:
		csv_reader = reader(file)
		for row in csv_reader:
			if not row:
				continue
			dataset.append(row)
	return dataset

# Convert string column to float
def str_column_to_float(dataset, column):
	for row in dataset:
		row[column] = float(row[column].strip())

# Convert string column to integer
def str_column_to_int(dataset, column):
	class_values = [row[column] for row in dataset]
	unique = set(class_values)
	lookup = dict()
	for i, value in enumerate(unique):
		lookup[value] = i
	for row in dataset:
		row[column] = lookup[row[column]]
	return lookup

# Split a dataset into k folds
def cross_validation_split(dataset, n_folds):
	dataset_split = list()
	dataset_copy = list(dataset)
	fold_size = int(len(dataset) / n_folds)
	for i in range(n_folds):
		fold = list()
		while len(fold) < fold_size:
			index = randrange(len(dataset_copy))
			fold.append(dataset_copy.pop(index))
		dataset_split.append(fold)
	return dataset_split

# Calculate accuracy percentage
def accuracy_metric(actual, predicted):
	correct = 0
	for i in range(len(actual)):
		if actual[i] == predicted[i]:
			correct += 1
	return correct / float(len(actual)) * 100.0

# Evaluate an algorithm using a cross validation split
def evaluate_algorithm(dataset, algorithm, n_folds, *args):
	folds = cross_validation_split(dataset, n_folds)
	scores = list()
	for fold in folds:
		train_set = list(folds)
		train_set.remove(fold)
		train_set = sum(train_set, [])
		test_set = list()
		for row in fold:
			row_copy = list(row)
			test_set.append(row_copy)
			row_copy[-1] = None
		predicted = algorithm(train_set, test_set, *args)
		actual = [row[-1] for row in fold]
		accuracy = accuracy_metric(actual, predicted)
		scores.append(accuracy)
	return scores

# Calculate the Euclidean distance between two vectors
def euclidean_distance(row1, row2):
	distance = 0.0
	for i in range(len(row1)-1):
		distance += (row1[i] - row2[i])**2
	return sqrt(distance)

# Locate neighbors for a new row
def get_neighbors(train, test_row, num_neighbors):
	distances = list()
	for train_row in train:
		dist = euclidean_distance(test_row, train_row)
		distances.append((train_row, dist))
	distances.sort(key=lambda tup: tup[1])
	neighbors = list()
	for i in range(num_neighbors):
		neighbors.append(distances[i][0])
	return neighbors

# Make a prediction with kNN
def knn_predict(model, test_row, num_neighbors=2):
	neighbors = get_neighbors(model, test_row, num_neighbors)
	output_values = [row[-1] for row in neighbors]
	prediction = max(set(output_values), key=output_values.count)
	return prediction

# Prepare the kNN model
def knn_model(train):
	return train

# Make a prediction with weights
def perceptron_predict(model, row):
	activation = model[0]
	for i in range(len(row)-1):
		activation += model[i + 1] * row[i]
	return 1.0 if activation >= 0.0 else 0.0

# Estimate Perceptron weights using stochastic gradient descent
def perceptron_model(train, l_rate=0.01, n_epoch=5000):
	weights = [0.0 for i in range(len(train[0]))]
	for epoch in range(n_epoch):
		for row in train:
			prediction = perceptron_predict(weights, row)
			error = row[-1] - prediction
			weights[0] = weights[0] + l_rate * error
			for i in range(len(row)-1):
				weights[i + 1] = weights[i + 1] + l_rate * error * row[i]
	return weights

# Make a prediction with coefficients
def logistic_regression_predict(model, row):
	yhat = model[0]
	for i in range(len(row)-1):
		yhat += model[i + 1] * row[i]
	return 1.0 / (1.0 + exp(-yhat))

# Estimate logistic regression coefficients using stochastic gradient descent
def logistic_regression_model(train, l_rate=0.01, n_epoch=5000):
	coef = [0.0 for i in range(len(train[0]))]
	for epoch in range(n_epoch):
		for row in train:
			yhat = logistic_regression_predict(coef, row)
			error = row[-1] - yhat
			coef[0] = coef[0] + l_rate * error * yhat * (1.0 - yhat)
			for i in range(len(row)-1):
				coef[i + 1] = coef[i + 1] + l_rate * error * yhat * (1.0 - yhat) * row[i]
	return coef

# Make predictions with sub-models and construct a new stacked row
def to_stacked_row(models, predict_list, row):
	stacked_row = list()
	for i in range(len(models)):
		prediction = predict_list[i](models[i], row)
		stacked_row.append(prediction)
	stacked_row.append(row[-1])
	return row[0:len(row)-1] + stacked_row

# Stacked Generalization Algorithm
def stacking(train, test):
	model_list = [knn_model, perceptron_model]
	predict_list = [knn_predict, perceptron_predict]
	models = list()
	for i in range(len(model_list)):
		model = model_list[i](train)
		models.append(model)
	stacked_dataset = list()
	for row in train:
		stacked_row = to_stacked_row(models, predict_list, row)
		stacked_dataset.append(stacked_row)
	stacked_model = logistic_regression_model(stacked_dataset)
	predictions = list()
	for row in test:
		stacked_row = to_stacked_row(models, predict_list, row)
		stacked_dataset.append(stacked_row)
		prediction = logistic_regression_predict(stacked_model, stacked_row)
		prediction = round(prediction)
		predictions.append(prediction)
	return predictions

# Test stacking on the sonar dataset
seed(1)
# load and prepare data
filename = 'sonar.all-data.csv'
dataset = load_csv(filename)
# convert string attributes to integers
for i in range(len(dataset[0])-1):
	str_column_to_float(dataset, i)
# convert class column to integers
str_column_to_int(dataset, len(dataset[0])-1)
n_folds = 3
scores = evaluate_algorithm(dataset, stacking, n_folds)
print('Scores: %s' % scores)
print('Mean Accuracy: %.3f%%' % (sum(scores)/float(len(scores))))

       使用k值为3进行交叉验证,得出每个折叠208/3 = 69.3或略低于70条记录,以便在每次迭代时进行评估。 运行示例将打印最终配置的分数和分数平均值。

Scores: [78.26086956521739, 76.81159420289855, 69.56521739130434]
Mean Accuracy: 74.879%

 

 

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 护眼 设计师:闪电赇 返回首页
实付 19.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值