当前位置: 首页 > article >正文

第J4周:ResNet与DenseNet结合探索

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊

方法 1:特征级融合

特征级融合通常是在 DenseNet 和 ResNet 的最后一层卷积之后,取各自的特征图,将它们拼接(concatenate)或相加,然后输入至一个全连接层进行最终分类。

方法 2:级联(Cascade)结构

在级联结构中,可以将一个模型的输出作为另一个模型的输入。例如,将 DenseNet 的特征传入 ResNet 的特征层继续处理,然后分类。这种方法要求两网络的输入和输出尺寸匹配。

方法 3:并联并投票决策

在这种方法中,DenseNet 和 ResNet 的分类输出独立进行预测,通过平均、加权平均或投票的方式得到最终分类结果。这种方法可行,但比较简单。

这里我们使用的方法1进行修改的

# @Date : 2024-10-30 18:13  # @Author : yjl
import torch
import torch.nn as nn
import torch.nn.functional as F

device = "cuda" if torch.cuda.is_available() else "cpu"
class Identity_block(nn.Module):
    def __init__(self, in_channels, filters, kernel_size, stage, block):
        super(Identity_block, self).__init__()
        filters1, filters2, filters3 = filters
        name_base = str(stage) + block + '_identity_block'
        self.conv1 = nn.Conv2d(in_channels, filters1, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn1 = nn.BatchNorm2d(filters1)

        self.conv2 = nn.Conv2d(filters1, filters2, kernel_size=kernel_size, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(filters2)

        self.conv3 = nn.Conv2d(filters2, filters3, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn3 = nn.BatchNorm2d(filters3)

        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        identity = x
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.relu(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))
        out += identity
        out = self.relu(out)
        return out


class Conv_block(nn.Module):
    def __init__(self, in_channels, filters, kernel_size, stage, block, strides=2):
        super(Conv_block, self).__init__()
        filters1, filters2, filters3 = filters
        res_name_base = str(stage) + block + '_conv_block_res_'
        name_base = str(stage) + block + '_conv_block_'

        self.conv1 = nn.Conv2d(in_channels, filters1, kernel_size=1, stride=strides, bias=False)
        self.bn1 = nn.BatchNorm2d(filters1)

        self.conv2 = nn.Conv2d(filters1, filters2, kernel_size=kernel_size, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(filters2)

        self.conv3 = nn.Conv2d(filters2, filters3, kernel_size=1, stride=1, bias=False)
        self.bn3 = nn.BatchNorm2d(filters3)

        self.shortcut = nn.Sequential(
            nn.Conv2d(in_channels, filters3, kernel_size=1, stride=strides, bias=False),
            nn.BatchNorm2d(filters3)
        )

        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        identity = self.shortcut(x)

        out = self.relu(self.bn1(self.conv1(x)))
        out = self.relu(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))
        out += identity
        out = self.relu(out)

        return out


class Resnet50(nn.Module):
    def __init__(self, input_shape=(3, 224, 224), classes=1000):
        super(Resnet50, self).__init__()
        self.in_channels = 64
        self.conv1 = nn.Conv2d(input_shape[0], self.in_channels, kernel_size=7, padding=3, stride=2, bias=False)
        self.bn1 = nn.BatchNorm2d(self.in_channels)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        self.layer1 = self._make_layer([64, 64, 256], blocks=3, stage=2, stride=1)
        self.layer2 = self._make_layer([128, 128, 512], blocks=4, stage=3, stride=2)
        self.layer3 = self._make_layer([256, 256, 1024], blocks=6, stage=4, stride=2)
        self.layer4 = self._make_layer([512, 512, 2048], blocks=3, stage=5, stride=2)

        self.avgpool = nn.AvgPool2d((7, 7))
        self.fc = nn.Linear(2048, classes)

    def _make_layer(self, filters, blocks, stage, stride):
        layers = []

        # first block is a ConvBlock with stride
        layers.append(Conv_block(self.in_channels, filters, kernel_size=3, stage=stage, block='a', strides=stride))
        self.in_channels = filters[2]

        # remaining is a IdentityBlocks
        for b in range(1, blocks):
            layers.append(Identity_block(self.in_channels, filters, kernel_size=3, stage=stage, block=chr(97 + b)))
        return nn.Sequential(*layers)  ##

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)

        return x




from collections import OrderedDict
import torch
import torch.nn as nn
import torch.nn.functional as F


class DenseLayer(nn.Sequential):
    def __init__(self, in_channel, growth_rate, bn_size, drop_rate):
        super(DenseLayer, self).__init__()
        self.add_module('norm1', nn.BatchNorm2d(in_channel))
        self.add_module('relu1', nn.ReLU(inplace=True))
        self.add_module('conv1', nn.Conv2d(in_channel, bn_size * growth_rate,
                                           kernel_size=1, stride=1, bias=False))
        self.add_module('norm2', nn.BatchNorm2d(bn_size * growth_rate))
        self.add_module('relu2', nn.ReLU(inplace=True))
        self.add_module('conv2', nn.Conv2d(bn_size * growth_rate, growth_rate,
                                           kernel_size=3, stride=1, padding=1, bias=False))
        self.drop_rate = drop_rate

    def forward(self, x):
        new_feature = super(DenseLayer, self).forward(x)
        if self.drop_rate > 0:
            new_feature = F.dropout(new_feature, p=self.drop_rate, training=self.training)
        return torch.cat([x, new_feature], 1)





class DenseBlock(nn.Sequential):
    def __init__(self, num_layers, in_channel, bn_size, growth_rate, drop_rate):
        super(DenseBlock, self).__init__()
        for i in range(num_layers):
            layer = DenseLayer(in_channel + i * growth_rate, growth_rate, bn_size, drop_rate)
            self.add_module('denselayer%d' % (i + 1,), layer)



''' Transition layer between two adjacent DenseBlock '''


class Transition(nn.Sequential):
    def __init__(self, in_channel, out_channel):
        super(Transition, self).__init__()
        self.add_module('norm', nn.BatchNorm2d(in_channel))
        self.add_module('relu', nn.ReLU(inplace=True))
        self.add_module('conv', nn.Conv2d(in_channel, out_channel,
                                          kernel_size=1, stride=1, bias=False))
        self.add_module('pool', nn.AvgPool2d(2, stride=2))





class DenseNet(nn.Module):
    def __init__(self, growth_rate=32, block_config=(6, 12, 24, 16), init_channel=64,
                 bn_size=4, compression_rate=0.5, drop_rate=0, num_classes=1000):

        super(DenseNet, self).__init__()
        # first Conv2d
        self.features = nn.Sequential(OrderedDict([
            ('conv0', nn.Conv2d(3, init_channel, kernel_size=7, stride=2, padding=3, bias=False)),
            ('norm0', nn.BatchNorm2d(init_channel)),
            ('relu0', nn.ReLU(inplace=True)),
            ('pool0', nn.MaxPool2d(3, stride=2, padding=1))
        ]))

        # DenseBlock
        num_features = init_channel
        for i, num_layers in enumerate(block_config):
            block = DenseBlock(num_layers, num_features, bn_size, growth_rate, drop_rate)
            self.features.add_module('denseblock%d' % (i + 1), block)
            num_features += num_layers * growth_rate
            if i != len(block_config) - 1:
                transition = Transition(num_features, int(num_features * compression_rate))
                self.features.add_module('transition%d' % (i + 1), transition)
                num_features = int(num_features * compression_rate)

        # final BN+ReLU
        self.features.add_module('norm5', nn.BatchNorm2d(num_features))
        self.features.add_module('relu5', nn.ReLU(inplace=True))
        # 分类层
        self.classifier = nn.Linear(num_features, num_classes)

        # 参数初始化
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.bias, 0)
                nn.init.constant_(m.weight, 1)
            elif isinstance(m, nn.Linear):
                nn.init.constant_(m.bias, 0)

    def forward(self, x):
        x = self.features(x)
        x = F.avg_pool2d(x, 7, stride=1).view(x.size(0), -1)
        x = self.classifier(x)
        return x





class DenseNetResNetCombined(nn.Module):
    def __init__(self, densenet, resnet, num_classes):
        super(DenseNetResNetCombined, self).__init__()

        # 分离DenseNet和ResNet的特征提取部分
        self.densenet_features = nn.Sequential(*list(densenet.features.children()))
        self.resnet_features = nn.Sequential(
            resnet.conv1,
            resnet.bn1,
            resnet.relu,
            resnet.maxpool,
            resnet.layer1,
            resnet.layer2,
            resnet.layer3,
            resnet.layer4
        )

        # 初次前向传播,以获取拼接的实际维度
        sample_input = torch.randn(1, 3, 224, 224)
        densenet_out = self.densenet_features(sample_input)
        resnet_out = self.resnet_features(sample_input)

        # 获取拼接后的实际特征维度
        self.combined_features_dim = densenet_out.shape[1] + resnet_out.shape[1]

        # 调整分类层的输入维度为拼接后的通道数
        self.fc = nn.Linear(self.combined_features_dim, num_classes)

    def forward(self, x):
        # 获取DenseNet和ResNet的特征
        densenet_out = self.densenet_features(x)
        densenet_out = F.avg_pool2d(densenet_out, kernel_size=7).view(densenet_out.size(0), -1)
        
        resnet_out = self.resnet_features(x)
        resnet_out = F.avg_pool2d(resnet_out, kernel_size=7).view(resnet_out.size(0), -1)

        # 拼接特征
        combined_features = torch.cat([densenet_out, resnet_out], dim=1)

        # 分类
        out = self.fc(combined_features)
        return out

# 初始化DenseNet和ResNet模型
densenet = DenseNet(init_channel=64, growth_rate=32, block_config=(6, 12, 24, 16), num_classes=1000)
resnet = Resnet50(classes=1000)

# 创建联合模型
model = DenseNetResNetCombined(densenet, resnet, num_classes=2)
model.to(device)
from torchsummary import summary
summary(model, (3, 224, 224))

在这里插入图片描述
并在J3周乳腺癌数据集上进行了测试,因为只更换了模型,训练的框架都是一样的,这里就没有重复放代码了。只在上方将模型的融合放在了上面。

结果对比

在这里插入图片描述
在这里插入图片描述

可以发现我们改进的模型相比较单独的densenet121模型,可以增加1个百分点。


http://www.kler.cn/a/372757.html

相关文章:

  • 问题记录01
  • 一文讲明白大模型分布式逻辑(从GPU通信原语到Megatron、Deepspeed)
  • Python小游戏17——飞机大战
  • 【JavaEE】【多线程】进阶知识
  • 2023 春季测试 题解
  • Vue3入门--[vue/compiler-sfc] Unexpected token, expected “,“ (18:0)
  • Docker常用命令汇总
  • win10 无法连接共享打印机 错误代码0x0000011b
  • YOLOv9模型重新参数化,将yolo.pt转为yolo-converted.pt
  • GetX在使用过程中一些问题
  • 计算机科学与技术-毕业设计选题推荐
  • Python OpenCV精讲系列 - 车牌识别的全方位指南(二十四)
  • 论文 | Ignore Previous Prompt: Attack Techniques For Language Models
  • 第二十三章 Vue组件通信之非父子组件通信
  • 【Linux】网络编程:初识协议,序列化与反序列化——基于json串实现,网络通信计算器中简单协议的实现、手写序列化与反序列化
  • 【Web前端】JavaScript 对象原型与继承机制
  • 「C/C++」C++ 三大特性 之 类和对象
  • 版本管理工具切换 | svn切换到gitlab | gitblit 迁移到 gitlab
  • STL——list的介绍和使用
  • 微信小程序-全局数据共享/页面间通信
  • unity :Error building Player: Incompatible color space with graphics API
  • k8s Ingress 七层负载
  • 迪杰斯特拉算法(Dijkstra‘s Algorithm
  • 路由参数与请求方式
  • 理解环境变量与Shell编程:Linux开发的基础
  • 将你的 Kibana Dev Console 请求导出到 Python 和 JavaScript 代码