Pytorch基础-06-多模型融合(VGG16+ResNet50之猫狗分类)

1.多模型融合中的问题:

首先,在使用融合神经网络模型的过程中遇到的第1个问题就是训练复杂神经网络非常耗时,因为优秀的模型一般都是深度神经网络模型,这些网络模型的特点是层次较深、参数较多,所以对融合了多个深 度神经网络的模型进行参数训练,会比我们使用单一的深度神经网络模 型进行参数训练所耗费的时间要多上好几倍。对于这种情况,我们一般 使用两种方法进行解决:挑选一些结构比较简单、网络层次较少的神经 网络参与到多模型融合中;如果还想继续使用深度神经网络模型进行多 模型融合,就需要使用迁移学习方法来辅助模型的融合,以减少训练耗时。

其次,在对各个模型进行融合时,在融合方法的类型选择上也很让 人头痛,因为在选择不同的模型融合方法解决某些问题时其结果的表现 不同,而且可以选择是针对模型的过程进行融合,还是仅针对各个模型 输出的结果进行融合,这都是值得我们思考的。

结果融合法是针对各个模型的输出结果进行的融合,主要包括结果多数表决结果直接平均结果加权平均这三种主要的类型。在结果融合法中有一个比较通用的理论,就是若我们想通过多模型融合来提高输出结果的预测准确率,则各个模型的相关度越低,融合的效果会更好, 也就是说各个模型的输出结果的差异性越高,多模型融合的效果就会越好。

1.1 结果多数表决

以下表为例说明:
在这里插入图片描述

可以看出,这三个模型的预测结果的准确率分别是80%、80%和60%,现在我们使用结果多数表决的方法对统计得到的结果进行融合。以三个模型中的第1个新数据的预测结果为例,模型一对新数据的预测结果为True,模型二对新数据集的预测结果为False,模型三对新数据集的预测结果为True,通过多数表决得到的融合模型对新数据的预测结果为True,其他新数据的预测结果以此类推,最后得到多模型的预测结果如下:
在这里插入图片描述

通过统计结果的准确率可以发现,使用多模型融合后的预测准确率 也是80%,虽然在准确率的表现上比最差的模型三要好,但是和模型一 和模型二的准确率处于同一水平,没有体现出模型融合的优势,所以这 也是我们需要注意的。进行多模型融合并不一定能取得理想的效果,需 要使用不同的方法不断地尝试。

下面对之前的实例稍微进行改变,如下:
在这里插入图片描述

在进行调整之后,三个模型的预测结果的准确率依然分别是80%、 80%和60%,我们再计算一遍多模型融合的预测结果,得到如下表:
在这里插入图片描述

这时多模型融合的预测结果对10个新数据的预测准确率已经提升到 了90%,在预测结果的准确率上超过了被融合的三个模型中的任意一 个。为什么预测结果最后会发生这样的改变?这是因为我们扩大了模型三在预测结果上和模型一及模型二的差异性,这也印证了我们之前提到 过的通用理论,参与融合的各个模型在输出结果的表现上差异性越高, 则最终的融合模型的预测结果越好。

1.2 结果直接平均

结果直接平均追求的是融合各个模型的平均预测水平,以提升模型整体的预测能力,但是与结果多数表决相比,结果直接平均不强调个别模型的突出优势,却可以弥补个别模型的明显劣势,比如在参与融合的模型中有一个模型已经发生了过拟合的问题,另一个模型却发生了欠拟合的问题,但是通过结果直接平均的方法能够很好地综合这两个模型的劣势,最后可预防融合模型过拟合和欠拟合的发生,如下:
在这里插入图片描述

假设在上图左中两个模型处理的是同一个分类问题,圆圈和叉号代表不同的类别,则两个模型在泛化能力上的表现都不尽如人意,一个模型出现了严重的过拟合现象,另一个模型出现了严重的欠拟合现象;再看上图右中通过结果直接平均的融合模型,它在泛化能力上表现不错,受噪声值的影响不大。所以,如果我们对两个模型进行融合并且使用结果直接平均的方法,那么最后得到的结果在一定程度上弥补了各个模型的不足,不仅如此,融合的模型还有可能取得比两个模型更好的泛化能力。

虽然结果直接平均的方法追求的是“平均水平”,但是使用结果直接平均的多模型融合在处理很多问题时取得了优于平均水平的成绩。

下面我们来看一个使用结果直接平均进行多模型融合的实例。依旧 假设我们现在已经拥有三个优化好的模型,而且它们能够独立完成对新 输入数据的预测,现在我们向这三个模型分别输入10个新数据,然后 统计模型的预测结果。不过,我们对结果的记录使用的不 是“True”和“False”,而是直接记录每个模型对新数据预测的可能性值, 如果预测正确的可能性值大于50%,那么在计算准确率时就把这个预测 结果看作正确的,三个模型的预测结果如下表所示。
在这里插入图片描述

可以看出,这三个模型的预测结果的准确率分别是80%、80%和 60%,现在使用多模型融合的方法对这三个模型的输出结果进行直接平 均。以三个模型中的第1个预测结果为例,模型一对新数据的预测结果 为80%,模型二对新数据的预测结果为30%,模型三对新数据的预测结 果为70%,通过直接平均得到的融合模型对新数据的预测结果为60%, 其他新数据的预测结果以此类推,最后得到多模型的预测结果如下表所示。
在这里插入图片描述

通过对结果进行简单计算,我们便可以知道使用直接平均方法得到 的融合模型的最终预测准确率是90%,在融合模型得到的对新数据的所 有预测结果中,预测可能性值低于50%的只有一个,所以结果直接平均 在总体的准确率上都好于被融合的三个模型,不过我们在经仔细观察后 发现,融合的模型在单个数据的预测能力上并没有完胜其他三个模型, 所以这也是结果直接平均的最大不足。

1.3 结果加权平均

我们可以将结果加权平均看作结果直接平均的改进版本,在结果加权平均的方法中会新增一个权重参数,这个权重参数用于控制各个模型对融合模型结果的影响程度。简单来说,我们之前使用结果直接平均融合的模型,其实可以被看作由三个使用了一样的权重参数的模型按照结果加权平均融合而成的。所以结果加权平均的关键是对权重参数的控制,通过对不同模型的权重参数的控制,可以得到不同的模型融合方
法,最后影响融合模型的预测结果。

下面再来看一个使用结果加权平均进行多模型融合的实例。假设我们现在已经拥有了两个优化好的模型,不是之前的三个,而且它们能够独立预测新的输入数据,现在,我们向这两个模型分别输入10个同样的新数据,然后统计模型的预测结果,并直接记录每个模型对新数据预测的可能性值,同样,如果预测正确的可能性值大于50%,那么在计算准确率时把这个预测结果看作正确的,两个模型的预测结果如下表所示。
在这里插入图片描述

可以看出,这两个模型的预测结果的准确率均是80%,如果要对两个模型进行结果加权平均,那么首先需要设定各个模型的权重参数,假设模型一的权重值是0.8,模型二的权重值是0.2,则接下来看看如何使用结果加权平均对输出的结果进行融合。我们在计算过程中首先看到模型一对第1个新数据的预测结果为80%,模型二对第1个新数据的预测结果为30%,通过结果加权平均得到的融合模型对新数据的预测结果为70%,计算方法如下:
70% = 0.8x80% + 0.2x30%

其他新数据的预测结果以此类推,最后得到多模型的预测结果如下表:
在这里插入图片描述

通过简单计算,我们可以知道使用结果加权平均的方法融合的模型 的预测准确率是90%,在我们得到的对新数据的所有预测结果中,预测 可能性值低于50%的预测结果只有一个,融合的模型在预测结果准确率 的表现上优于被融合的两个模型,而且融合模型对新数据的单个预测值 也不低。下面再做一个实验,把模型一的权重值和模型二的权重值进行 对调,即模型一的权重值变成了0.2,模型二的权重值变成了0.8,那么 我们融合的模型的预测结果如下表所示。
在这里插入图片描述

这次结果的准确率降低到了80%,而且融合模型对新数据的单个预 测值明显下降,可见调节各个模型的权重参数对最后的融合模型的结果 影响较大。所以在使用权重平均的过程中,我们需要不断尝试使用不同的权重值组合,以达到多模型融合的最优解决方案。

2 PyTorch多模型融合实战

使用VGG16架构和ResNet50架构的卷积神经网络模型参
与本次模型的融合,然后按照结果加权平均的方法分别对这两个模型提前拟定好会使用到的权重值,对VGG16模型预测结果给予权重值0.6,对ResNet50的预测结果给予权重0.4。

实现多模型融合的完整代码如下:

import torch
import torchvision
from torchvision import datasets, models, transforms
import os
from torch.autograd import Variable
import matplotlib.pyplot as plt
import time

data_dir = 'DvC'
data_transform = {
    x: transforms.Compose([transforms.Scale([64, 64]), transforms.ToTensor()]) for x in ['train', 'valid']
}

image_datasets = {
    x: datasets.ImageFolder(root=os.path.join(data_dir, x), transform=data_transform[x]) for x in ['train', 'valid']
}

dataloader = {
    x: torch.utils.data.DataLoader(dataset=image_datasets[x], batch_size=16, shuffle=True) for x in ['train', 'valid']
}
x_example, y_example = next(iter(dataloader['train']))
example_classes = image_datasets['train'].classes
index_classes = image_datasets['train'].class_to_idx

model_1 = models.vgg16(pretrained=False)
model_2 = models.resnet50(pretrained=False)

use_gpu = torch.cuda.is_available()

for parma in model_1.parameters():
    parma.requires_grad = False

model_1.classifier = torch.nn.Sequential(torch.nn.Linear(25088, 4096),
                                         torch.nn.ReLU(),
                                         torch.nn.Dropout(p=0.5),
                                         torch.nn.Linear(4096, 4096),
                                         torch.nn.ReLU(),
                                         torch.nn.Dropout(p=0.5),
                                         torch.nn.Linear(4096, 2))

for parma in model_2.parameters():
    parma.requires_grad = False

model_2.fc = torch.nn.Linear(2048, 2)

if use_gpu:
    model_1 = model_1.cuda()
    model_2 = model_2.cuda()

loss_f_1 = torch.nn.CrossEntropyLoss()
loss_f_2 = torch.nn.CrossEntropyLoss()
optimizer_1 = torch.optim.Adam(model_1.classifier.parameters(), lr=0.00001)
optimizer_2 = torch.optim.Adam(model_2.fc.parameters(), lr=0.00001)
weight_1 = 0.6
weight_2 = 0.4

epoch_n = 5
time_open = time.time()

for epoch in range(epoch_n):
    print('Epoch {}/{}'.format(epoch, epoch_n - 1))
    print('---' * 10)
    for phase in ['train', 'valid']:
        if phase == 'train':
            print('Training...')
            model_1.train(True)
            model_2.train(True)
        else:
            print('Validing...')
            model_1.train(False)
            model_2.train(False)

        running_loss_1 = 0.0
        running_corrects_1 = 0
        running_loss_2 = 0.0
        running_corrects_2 = 0
        blending_running_corrects = 0

        for batch, data in enumerate(dataloader[phase], 1):
            X, y = data
            if use_gpu:
                X, y = Variable(X.cuda()), Variable(y.cuda())
            else:
                X, y = Variable(X), Variable(y)

            y_pred_1 = model_1(X)
            y_pred_2 = model_2(X)
            blending_y_pred = y_pred_1 * weight_1 + y_pred_2 * weight_2

            _, pred_1 = torch.max(y_pred_1.data, 1)
            _, pred_2 = torch.max(y_pred_2.data, 1)
            _, blending_pred = torch.max(blending_y_pred.data, 1)

            optimizer_1.zero_grad()
            optimizer_2.zero_grad()

            loss_1 = loss_f_1(y_pred_1, y)
            loss_2 = loss_f_2(y_pred_2, y)

            if phase == 'train':
                loss_1.backward()
                loss_2.backward()
                optimizer_1.step()
                optimizer_2.step()

            running_loss_1 += loss_1.data
            running_corrects_1 += torch.sum(pred_1 == y.data)
            running_loss_2 += loss_2.data
            running_corrects_2 += torch.sum(pred_2 == y.data)
            blending_running_corrects += torch.sum(blending_pred == y.data)

            if batch % 500 == 0 and phase == 'train':
                print('Batch {},Model_1 Train Loss:{},Model_1 Train ACC:{},Model_2 Train Loss:{},Model_2 Train ACC:{},\
                Blending_Model ACC:{}'.format(batch, running_loss_1 / batch, 100 * running_corrects_1 / (16 * batch),
                                              running_loss_2 / batch, 100 * running_corrects_2 / (16 * batch),
                                              100 * blending_running_corrects / (16 * batch)))
                
        epoch_loss_1 = running_loss_1 * 16 / len(image_datasets[phase])
        epoch_acc_1 = 100 * running_corrects_1 / len(image_datasets[phase])
        epoch_loss_2 = running_loss_2 * 16 / len(image_datasets[phase])
        epoch_acc_2 = 100 * running_corrects_2 / len(image_datasets[phase])
        epoch_blending_acc = 100 * blending_running_corrects / len(image_datasets[phase])
        print('Epoch,Model_1 Loss:{},Model_1 ACC:{},Model_2  Loss:{},Model_2  ACC:{}, Blending_Model ACC:{}'.format(
            epoch_loss_1, epoch_acc_1, epoch_loss_2, epoch_acc_2, epoch_blending_acc))
time_end = time.time() - time_open
print(time_end)
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值