集成神经网络模型
介绍
模型集成是机器学习中一种受欢迎的方法,可以帮助提高模型的表现,特别是在新的、未见过的数据上。其核心是结合多种模型的优势,以获得比任何单一模型自身更好的结果。这种技术提高了准确性,增强了鲁棒性,并增强了模型的泛化能力。
基于树的算法,例如,通常在某些任务中表现出色,因为它们通过组合多个决策树的输出来自然地创建一个更强的整体预测。
在集成中,有多种方法可以结合模型,从简单的方法如平均它们的预测,到更复杂的技术如自助法、提升法或堆叠法。每种方法提供了自己融合模型的方式,以使最终决策更加可靠。
主要要点
集成学习 结合多个神经网络,以提高预测准确性、稳健性和泛化能力。
-
工作流程通常包括准备数据集、训练多个模型,以及使用集成策略整合它们的输出。
常见的集成方法包括:
- 连接集成: 融合多个模型的特征以获得更丰富的表征。
- 平均集成: 取所有模型预测的平均值,简单而有效。
- 加权平均集成:根据模型的个体表现为其分配不同的重要性(权重)。
单一模型与集成模型的比较显示,在复杂任务中,集成模型通常比单一模型表现更好,但可能会增加更多的计算开销。
集成方法在个体模型出现多样化错误且可以集体纠正时特别有益。
选择合适的集成技术取决于你的数据类型、模型架构和评估目标。
与经典算法相比,将多个神经网络模型组合用于单一任务并不常见。这主要是因为单个模型通常足以映射特征与目标之间的关系,并且将多个神经网络组合在一起可能会使模型在较小的任务中变得复杂,并导致过拟合。实践者通常倾向于增加单个神经网络模型的规模,以更好地拟合数据,而不是将多个神经网络模型组合成一个。
然而,对于较大的任务,集成多个神经网络,特别是针对类似问题训练的神经网络,可能非常有用,能够提高最终模型的性能以及其适应更广泛用例的能力。
通常情况下,需要尝试多种具有不同配置的模型,以选择适合某一问题的最佳表现模型。神经网络集成提供了利用不同模型信息的选项,以开发更平衡和高效的模型。
例如,将多个在特定数据集(如Imagenet数据集)上训练的网络模型结合起来,可以有效提高在具有相似类别的数据集上训练的模型的性能。不同模型所学习的信息可以通过模型集成结合起来,以提高整体模型的性能和聚合模型的稳健性。在对这些模型进行集成之前,重要的是要训练和微调集成成员,以确保每个模型对最终模型提供相关信息。
数据集
在本教程中,我们将使用自然图像的图像分类。我们将使用自然图像数据集,该数据集包含6899张代表8个类别的自然图像。数据集中代表的类别包括飞机、汽车、猫、狗、水果、摩托车和人。
我们的目标是开发一个神经网络模型,能够识别属于不同类别的图像,并准确分类每个类别的新图像。
工作流程
我们将为我们的任务开发三种不同的模型,然后使用不同的方法进行集成。配置我们的模型以为最终模型提供相关信息是很重要的。
设置种子以确保可重复性并预览每个类别的示例图像
Seed environment
seed_value = 1 # seed value
Set`PYTHONHASHSEED` environment variable at a fixed value
import os os.environ['PYTHONHASHSEED']=str(seed_value)
rti
Set the `python` built-in pseudo-random generator at a fixed value
import random
random.seed = seed_value
Set the `numpy` pseudo-random generator at a fixed value
import numpy as np
np.random.seed = seed_value
Set the `tensorflow` pseudo-random generator at a fixed value import tensorflow as tf tf.seed = seed_value
设置数据配置并获取类别
set configs
base_path = './natural_images'
target_size = (224,224,3)
define shape for all images # get classes classes = os.listdir(base_path) print(classes)
绘制数据集的样本图像
plot sample images
import matplotlib.pyplot as plt
import cv2
f, axes = plt.subplots(2, 4, sharex=True, sharey=True, figsize = (16,7))
for ax, label in zip(axes.ravel(), classes):
img = np.random.choice(os.listdir(os.path.join(base_path, label)))
img = cv2.imread(os.path.join(base_path, label, img))
img = cv2.resize(img, target_size[:2])
ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGRA2RGB))
ax.set_title(label)
ax.axis(False)
加载图像
我们使用 Keras 图像生成器按批次加载图像,并指定一个验证划分,以将数据集拆分为训练和验证划分。
我们还应用随机图像增强技术来扩展训练数据集,提高模型的性能和泛化能力。
from keras.preprocessing.image import ImageDataGenerator
batch_size = 32
datagen = ImageDataGenerator(rescale=1./255,
rotation_range=20,
shear_range=0.2,
zoom_range=0.2,
width_shift_range = 0.2,
height_shift_range = 0.2,
vertical_flip = True,
validation_split=0.25)
train_gen = datagen.flow_from_directory(base_path,
target_size=target_size[:2],
batch_size=batch_size,
class_mode='categorical',
subset='training')
val_gen = datagen.flow_from_directory(base_path,
target_size=target_size[:2],
batch_size=batch_size,
class_mode='categorical',
subset='validation',
shuffle=False)
1. 构建和训练一个自定义模型
对于我们的第一个模型,我们使用Tensorflow的函数式方法构建了一个自定义模型。
Build model
input = Input(shape= target_size)
x = Conv2D(filters=32, kernel_size=(3,3), activation='relu')(input)
x = MaxPool2D(2,2)(x)
x = Conv2D(filters=64, kernel_size=(3,3), activation='relu')(x)
x = MaxPool2D(2,2)(x)
x = Conv2D(filters=128, kernel_size=(3,3), activation='relu')(x)
x = MaxPool2D(2,2)(x)
x = Conv2D(filters=256, kernel_size=(3,3), activation='relu')(x)
x = MaxPool2D(2,2)(x)
x = Dropout(0.25)(x)
x = Flatten()(x)
x = Dense(units=128, activation='relu')(x)
x = Dense(units=64, activation='relu')(x)
output = Dense(units=8, activation='softmax')(x)
custom_model = Model(input, output, name= 'Custom_Model')
接下来,我们编译自定义模型并设置回调以管理训练。我们使用在平稳期减少学习率、早期停止来在没有进一步改进时终止训练,以及在每个周期保存最佳模型的模型检查点。这些都是有效深度学习训练的标准做法。
compile model
custom_model.compile(loss= 'categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
initialize callbacks
reduceLR = ReduceLROnPlateau(monitor='val_loss', patience= 3, verbose= 1, mode='min', factor= 0.2, min_lr = 1e-6)
early_stopping = EarlyStopping(monitor='val_loss', patience = 5 , verbose=1, mode='min', restore_best_weights= True)
checkpoint = ModelCheckpoint('CustomModel.weights.hdf5', monitor='val_loss', verbose=1,save_best_only=True, mode= 'min')
callbacks= [reduceLR, early_stopping,checkpoint]
现在我们可以定义我们的训练配置并训练我们的自定义模型。
define training config
TRAIN_STEPS = 5177 // batch_size
VAL_STEPS = 1722 //batch_size
epochs = 80
train model
custom_model.fit(train_gen, steps_per_epoch= TRAIN_STEPS, validation_data=val_gen, validation_steps=VAL_STEPS, epochs= epochs, callbacks= callbacks)
在训练我们的自定义模型之后,我们可以评估它的性能。我将对验证集进行模型评估。实际上,最好为模型评估单独留出一个测试集。
Evaluate the model
custom_model.evaluate(val_gen)
验证损失和准确性
我们还可以获取预测和实际验证标签,以便检查自定义模型的分类报告和混淆矩阵评估。
get validation labels
val_labels = []
for i in range(VAL_STEPS + 1):
val_labels.extend(val_gen[i][1])
val_labels = np.argmax(val_labels, axis=1)
show classification report
from sklearn.metrics import classification_report
print(classification_report(val_labels, predicted_labels, target_names=classes))
分类报告
function to plot confusion matrix
import itertools
def plot_confusion_matrix(actual, predicted):
cm = confusion_matrix(actual, predicted)
cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
plt.figure(figsize=(7,7))
cmap=plt.cm.Blues
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title('Confusion matrix', fontsize=25)
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=90, fontsize=15)
plt.yticks(tick_marks, classes, fontsize=15)
thresh = cm.max() / 2.
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, format(cm[i, j], '.2f'),
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black", fontsize = 14)
plt.ylabel('True label', fontsize=20)
plt.xlabel('Predicted label', fontsize=20)
plt.show()
plot confusion matrix
plot_confusion_matrix(val_labels, predicted_labels)
使用自定义模型的混淆矩阵
从模型评估中,我们注意到自定义模型无法正确识别猫和花的图像。它在识别飞机时也给出了次优的精确度。
接下来,我们将尝试另一种模型构建技术:利用在Imagenet数据集上预训练的网络学习到的权重来提升我们模型在准确率较低的类别上的表现。
2. 使用预训练的VGG16模型。
对于我们的第二个模型,我们将使用在Imagenet数据集上预训练的VGG16模型来训练一个适用于我们用例的模型。这使我们能够继承在相似数据集上训练所学到的权重,从而提升我们模型的性能。
初始化和微调VGG16模型
Import the VGG16 pretrained model
from tensorflow.keras.applications import VGG16
initialize the model vgg16 = VGG16(input_shape=(224,224,3), weights='imagenet', include_top=False)
Freeze all but the last 3 layers for layer in vgg16.layers[:-3]: layer.trainable = False
build model
input = vgg16.layers[-1].output # input is the last output from vgg16
x = Dropout(0.25)(input)
x = Flatten()(x)
output = Dense(8, activation='softmax')(x)
create the model
vgg16_model = Model(vgg16.input, output, name='VGG16_Model')
我们初始化预训练模型,并冻结最后三个层之前的所有层,以便利用模型的权重,并在最后三个层中学习特定于我们用例的新信息。我们还添加了一个 dropout 层,以在添加最终输出层之前对模型进行正则化,以防止其在我们的数据集上过拟合。
然后我们可以使用与我们的自定义模型类似的训练配置来训练我们的VGG16微调模型。
compile the model
vgg16_model.compile(optimizer= SGD(learning_rate=1e-3), loss= 'categorical_crossentropy', metrics= ['accuracy'])
reinitialize callbacks
checkpoint = ModelCheckpoint('VggModel.weights.hdf5', monitor='val_loss', verbose=1,save_best_only=True, mode= 'min')
callbacks= [reduceLR, early_stopping,checkpoint]
Train model
vgg16_model.fit(train_gen, steps_per_epoch= TRAIN_STEPS, validation_data=val_gen, validation_steps=VAL_STEPS, epochs= epochs, callbacks= callbacks)
训练后,我们可以使用上一次模型评估的类似脚本来评估模型的性能。
Evaluate the model
vgg16_model.evaluate(val_gen)
get the model predictions
predicted_labels = np.argmax(vgg16_model.predict(val_gen), axis=1)
show classification report
print(classification_report(val_labels, predicted_labels, target_names=classes))
plot confusion matrix
plot_confusion_matrix(val_labels, predicted_labels)
使用预训练VGG模型的分类报告
使用预训练VGG模型的混淆矩阵报告
使用预训练的VGG16模型能够产生更好的性能,特别是在得分较低的类别上。
最后,在集成之前,我们将使用另一个参数更少的预训练模型,而不是VGG16。目的是利用不同的架构,以便我们能够从它们不同的特性中受益,以提高最终模型的质量。我们将使用在相同的Imagenet数据集上预训练的Mobilenet模型。
3. 使用预训练的Mobilenet模型。
对于我们的最终模型,我们将对MobileNet模型进行微调。
initializing the mobilenet model
mobilenet = MobileNet(input_shape=(224,224,3), weights='imagenet', include_top=False)
freezing all but the last 5 layers
for layer in mobilenet.layers[:-5]:
layer.trainable = False
add few mor layers
x = mobilenet.layers[-1].output
x = Dropout(0.5)(x)
x = Flatten()(x)
x = Dense(32, activation='relu')(x)
x = Dropout(0.5)(x)
x = Dense(16, activation='relu')(x)
output = Dense(8, activation='softmax')(x)
Create the model
mobilenet_model = Model(mobilenet.input, output, name= "Mobilenet_Model")
为了训练模型,我们保留了一些之前训练的配置。
compile the model
mobilenet_model.compile(optimizer= SGD(learning_rate=1e-3), loss= 'categorical_crossentropy', metrics= ['accuracy'])
reinitialize callbacks
checkpoint = ModelCheckpoint('MobilenetModel.weights.hdf5', monitor='val_loss', verbose=1,save_best_only=True, mode= 'min')
callbacks= [reduceLR, early_stopping,checkpoint]
model training
mobilenet_model.fit(train_gen, steps_per_epoch= TRAIN_STEPS, validation_data=val_gen, validation_steps=VAL_STEPS, epochs=epochs, callbacks= callbacks)
在训练我们的精细调优Mobilenet模型后,我们可以评估其性能。
Evaluate the model
mobilenet_model.evaluate(val_gen)
get the model's predictions
predicted_labels = np.argmax(mobilenet_model.predict(val_gen), axis=1)
show the classification report
print(classification_report(val_labels, predicted_labels, target_names=classes))
plot the confusion matrix
plot_confusion_matrix(val_labels, predicted_labels)
经过微调的Mobilenet模型比之前的模型表现更好,显著提高了对狗和花的识别能力。
虽然使用训练好的Mobilenet模型或我们的任何模型作为最终选择是可以的,但将这三种模型的结构和权重结合起来,可能会在开发一个更具通用性的单一模型时表现更好,以便投入生产。
集成模型
如前所述,我们将使用三种不同的集成方法来集成模型:拼接、平均和加权平均。
这些方法各有优缺点,通常被考虑用于确定特定用例的选择。
串联集成
这涉及将多个模型的参数合并为一个模型。这在Tensorflow中通过拼接层实现,该层接收具有相同形状(除了拼接轴外)的张量输入,并将它们合并为一个。因此,这一层可以用于将不同模型在单个轴上并排学习到的所有信息合并为一个模型。
要合并我们的模型,我们只需将我们的模型输入到连接层。
concatenate the models
import concatenate layer
from tensorflow.keras.layers import Concatenate
get list of models
models = [custom_model, vgg16_model, mobilenet_model]
input = Input(shape=(224, 224, 3), name='input') # input layer
get output for each model input
outputs = [model(input) for model in models]
contenate the ouputs
x = Concatenate()(outputs)
add further layers
x = Dropout(0.5)(x)
output = Dense(8, activation='softmax', name='output')(x) # output layer
create concatenated model
conc_model = Model(input, output, name= 'Concatenated_Model')
在连接后,我们根据任务需要添加了更多层。在这个例子中,我添加了一个 dropout 层和一个最后的输出层,具有我们用例的适当输出维度。
让我们检查一下我们连接模型的结构。
show model structure
from tensorflow.keras.utils import plot_model
plot_model(conc_model)
拼接模型的结构
我们可以看到我们构建的三种不同功能模型是如何通过进一步的 dropout 和输出层合并为一体的。
在某些情况下,这可能会 prove 有用,因为我们正在利用不同模型所获得的所有信息。这个方法的问题在于我们最终模型的维度是所有集成成员维度的连接,这可能导致维度爆炸、包含无关信息,从而导致过拟合。
平均集成
与其通过连接多个模型来冒险扩大模型的维度和在更简单的任务上过拟合,不如直接将我们的最终模型设为所有模型的平均值。这样,我们能够在利用所有模型的信息的同时,保持我们的最终模型维度的合理平均值。
这一次我们将使用 Tensorflow 的 平均 层,它接收输入并输出它们的平均值。因此,我们的最终模型是输入模型参数的平均值。
为了通过平均来集成我们的模型,我们只需在平均层将我们的模型组合在一起。
average ensemble model
import Average layer
from tensorflow.keras.layers import Average
input = Input(shape=(224, 224, 3), name='input') # input layer
get output for each input model
outputs = [model(input) for model in models]
take average of the outputs
x = Average()(outputs)
x = Dense(16, activation='relu')(x)
x = Dropout(0.3)(x)
output = Dense(8, activation='softmax', name='output')(x) # output layer
create average ensembled model
avg_model = Model(input, output)
如前所述,可以根据需要向网络添加进一步的层。我们还可以看到平均集成模型的结构。
show model structure
plot_model(avg_model)
这种集成方法特别有用,因为通过取平均值,针对某些类别表现较差的模型可以被表现较好的模型所提升,从而改善模型的整体性能和泛化能力。
这种方法的缺点是,通过取平均值,某些模型的信息将不包含在最终模型中。另一点是,由于我们采用简单平均,某些模型的优势和相关性也会丧失。在我们的用例中,Mobilenet模型相对于其他模型的性能优势也因此而丧失。
通过对模型进行加权平均来解决这个问题,从而反映每个模型在最终决策中的重要性。
加权平均集成
在加权平均集成中,模型的输出乘以分配的权重,然后取平均值以得到最终模型。通过在这种集成方式中为模型分配权重,我们保留了它们在最终决策中的重要性和贡献。
虽然 TensorFlow 没有内置的加权平均集成层,但我们可以利用内置的 Layer 类的属性来实现一个自定义的加权平均集成层。对于加权平均集成,权重的和必须为一,我们可以通过在集成之前对权重应用 softmax 函数来确保这一点。有关在 TensorFlow 中创建自定义层的更多信息,请查看这里。
实现一个自定义加权平均层
首先,我们定义一个自定义函数来设置我们的权重。
function for setting weights
import numpy as np
def weight_init(shape =(1,1,3), weights=[1,2,3], dtype=tf.float32):
return tf.constant(np.array(weights).reshape(shape), dtype=dtype)
在这里,我们将模型的权重设置为1、2和3。然后,我们可以构建自定义加权平均层。
implement custom weighted average layer
import tensorflow as tf
from tensorflow.keras.layers import Layer, Concatenate
class WeightedAverage(Layer):
def __init__(self):
super(WeightedAverage, self).__init__()
def build(self, input_shape):
self.W = self.add_weight(
shape=(1,1,len(input_shape)),
initializer=weighted_init,
dtype=tf.float32,
trainable=True)
def call(self, inputs):
inputs = [tf.expand_dims(i, -1) for i in inputs]
inputs = Concatenate(axis=-1)(inputs)
weights = tf.nn.softmax(self.W, axis=-1)
return tf.reduce_mean(weights*inputs, axis=-1)
为了构建我们自定义的加权平均层,我们继承了内置 Layer 类的属性,并使用我们的权重初始化函数添加了权重。我们对初始化的权重应用 softmax,以确保它们的总和为一,然后再将它们乘以模型。最后,我们对加权输入进行均值减少,以获得我们的最终模型。
然后我们可以使用自定义加权平均层来集成我们的模型。
input = Input(shape=(224, 224, 3), name='input') # input layer
get output for each input model
outputs = [model(input) for model in models]
get weighted average of outputs
x = WeightedAverage()(outputs)
output = Dense(8, activation='softmax')(x) # output layer
weighted_avg_model = Model(input, output, name= 'Weighted_AVerage_Model')
让我们检查一下我们加权平均模型的结构。
plot model
plot_model(weighted_avg_model)
加权平均集成模型的结构 如之前的方法,我们的模型是集成的,但这一次是三种模型的加权平均。集成模型的输出维度也与我们任务所需的输出维度兼容,而无需添加任何进一步的层。
此外,我们可以利用Tensorflow内置的权重初始化器来初始化在训练过程中优化的权重,而不是使用自定义权重初始化函数手动设置权重。请查看这里Tensorflow中可用的内置权重初始化器。
单一模型与集成模型
| 公制 | 单一模型 | 集成(堆叠) |
|---|---|---|
| 准确性 | ~85% | ~89–91% |
| 训练时间 | 低 | 高(多个模型) |
| 推理时间 | 快 | 慢一点(多次通过) |
| 概括 | 适度 | 高 |
| 鲁棒性 | 低 | 更高的 |
集成提高了准确性和稳健性,但增加了训练/推理时间和内存使用。当性能比延迟更重要时,集成是有用的。
常见问题
1. 什么是神经网络集成?
神经网络集成是指结合多个神经模型的预测,以提高在未见数据上的准确性、鲁棒性和泛化能力。
2. 你可以组合不同类型的神经网络吗?
是的,你可以将像 CNN、LSTM 和 MLP 这样的模型组合在一起。这在每个模型捕捉到数据的不同方面时尤其有用。
3. 我应该在集成中使用多少个模型?
没有固定的数量——这取决于你的数据集、计算能力和性能目标。即使是2–3个精心挑选的模型也能带来显著的改善。
4. 装袋和堆叠有什么区别?
装袋在不同的数据子集上独立地训练模型并平均预测,而堆叠则结合多样的模型,使用一个在它们的输出上训练的元学习器。
5. 在大语言模型中如何使用集成方法?
在大语言模型(LLMs)中,集成方法用于结合来自多个模型检查点或版本的输出,以提高准确性并减少偏见或幻觉。
摘要
模型集成通过将多个模型结合成一个统一架构,增强了机器学习模型的性能和泛化能力。在神经网络中,TensorFlow允许多种集成策略,如连接、平均和自定义加权平均。虽然连接保留了所有特征(代价是增加维度),但平均简化了模型,可能忽视模型的重要性。自定义加权平均通过在训练期间学习最佳权重来解决这个问题。这些技术还可以通过更高级或特定任务的操作进行扩展,使集成成为深度学习中灵活而强大的工具。
相关资源
- 深度学习是什么
- https://linux-console.net/community/tutorials/constructing-neural-networks-from-scratch
- https://linux-console.net/community/tutorials/global-pooling-in-convolutional-neural-networks
- 在机器学习中使用随机森林
- 决策树:机器学习中的解释















