J4:DenseNet与ResNet结合

🍨 本文为🔗365天深度学习训练营 中的学习记录博客
🍦 参考文章:[Pytorch实战 | 第J4周:ResNet与DenseNet结合探索
🍖 原作者:K同学啊|接辅导、项目定制

一、模型准备

ResNet50_V2


import torch
from torchvision import datasets, transforms
import torch.nn as nn
import time
import numpy as np
import matplotlib.pyplot as plt

import torchsummary as summary

import os
from collections import OrderedDict

class IdentityBlock_V2(nn.Module):

    def __init__(self, in_channel, kl_size, filters):
        super(IdentityBlock_V2, self).__init__()

        filter1, filter2, filter3 = filters
        self.bn0 = nn.BatchNorm2d(num_features=in_channel)

        self.cov1 = nn.Conv2d(in_channels=in_channel, out_channels=filter1, kernel_size=1, stride=1, padding=0)
        self.bn1 = nn.BatchNorm2d(num_features=filter1)
        self.relu = nn.ReLU(inplace=True)
        self.zeropadding2d1 = nn.ZeroPad2d(1)

        self.cov2 = nn.Conv2d(in_channels=filter1, out_channels=filter2, kernel_size=kl_size, stride=1, padding=0)
        self.bn2 = nn.BatchNorm2d(num_features=filter2)

        self.cov3 = nn.Conv2d(in_channels=filter2, out_channels=filter3, kernel_size=1, stride=1, padding=0)
        self.bn3 = nn.BatchNorm2d(num_features=filter3)

    def forward(self, x):

        identity = self.bn0(x)
        identity = self.relu(identity)

        identity = self.cov1(identity)
        identity = self.bn1(identity)
        identity = self.relu(identity)
        identity = self.zeropadding2d1(identity)

        identity = self.cov2(identity)
        identity = self.bn2(identity)
        identity = self.relu(identity)

        identity = self.cov3(identity)

        x = identity + x
        x = self.relu(x)

        return x

class ConvBlock_V2(nn.Module):

    def __init__(self, in_channel, kl_size, filters, stride_size=2, conv_shortcut=False):
        super(ConvBlock_V2, self).__init__()

        filter1, filter2, filter3 = filters

        self.bn0 = nn.BatchNorm2d(num_features=in_channel)

        self.cov1 = nn.Conv2d(in_channels=in_channel, out_channels=filter1, kernel_size=1, stride=stride_size, padding=0)
        self.bn1 = nn.BatchNorm2d(num_features=filter1)
        self.relu = nn.ReLU(inplace=True)
        self.zeropadding2d1 = nn.ZeroPad2d(1)

        self.cov2 = nn.Conv2d(in_channels=filter1, out_channels=filter2, kernel_size=kl_size, stride=1, padding=0)
        self.bn2 = nn.BatchNorm2d(num_features=filter2)

        self.cov3 = nn.Conv2d(in_channels=filter2, out_channels=filter3, kernel_size=1, stride=1, padding=0)
        self.bn3 = nn.BatchNorm2d(num_features=filter3)

        self.conv_shortcut = conv_shortcut
        if self.conv_shortcut:
            self.short_cut = nn.Conv2d(in_channels=in_channel, out_channels=filter3, kernel_size=1, stride=stride_size, padding=0)
        else:
            self.short_cut = nn.MaxPool2d(kernel_size=1, stride=stride_size, padding=0)

    def forward(self, x):

        identity = self.bn0(x)
        identity = self.relu(identity)
        short_cut = self.short_cut(identity)

        identity = self.cov1(identity)
        identity = self.bn1(identity)
        identity = self.relu(identity)
        identity = self.zeropadding2d1(identity)

        identity = self.cov2(identity)
        identity = self.bn2(identity)
        identity = self.relu(identity)

        identity = self.cov3(identity)
        x = identity + short_cut
        x = self.relu(x)

        return x

class Resnet50_Model_V2(nn.Module):
    def __init__(self, in_channel, N_classes):
        super(Resnet50_Model_V2, self).__init__()
        self.in_channels = in_channel
        # ============= 基础层
        # 方法1
        self.zeropadding2d_0 = nn.ZeroPad2d(3)
        self.cov0 = nn.Conv2d(self.in_channels, out_channels=64, kernel_size=7, stride=2)
        self.zeropadding2d_1 = nn.ZeroPad2d(1)
        self.maxpool0 = nn.MaxPool2d(kernel_size=3, stride=2, padding=0)

        self.layer1 = nn.Sequential(
            ConvBlock_V2(64, 3, [64, 64, 256], 1, 1),
            IdentityBlock_V2(256, 3, [64, 64, 256]),
            ConvBlock_V2(256, 3, [64, 64, 256], 2, 0),
            )

        self.layer2 = nn.Sequential(
            ConvBlock_V2(256, 3, [128, 128, 512], 1, 1),
            IdentityBlock_V2(512, 3, [128, 128, 512]),
            IdentityBlock_V2(512, 3, [128, 128, 512]),
            ConvBlock_V2(512, 3, [128, 128, 512], 2, 0),
            )

        self.layer3 = nn.Sequential(
            ConvBlock_V2(512, 3, [256, 256, 1024], 1, 1),
            IdentityBlock_V2(1024, 3, [256, 256, 1024]),
            IdentityBlock_V2(1024, 3, [256, 256, 1024]),
            IdentityBlock_V2(1024, 3, [256, 256, 1024]),
            IdentityBlock_V2(1024, 3, [256, 256, 1024]),
            ConvBlock_V2(1024, 3, [256, 256, 1024], 2, 0),
            )

        self.layer4 = nn.Sequential(
            ConvBlock_V2(1024, 3, [512, 512, 2048], 1, 1),
            IdentityBlock_V2(2048, 3, [512, 512, 2048]),
            IdentityBlock_V2(2048, 3, [512, 512, 2048]),
            )

        # 输出网络
        self.bn = nn.BatchNorm2d(num_features=2048)
        self.relu = nn.ReLU(inplace=True)
        self.avgpool = nn.AvgPool2d((7, 7))
        # classfication layer
        # 7*7均值后2048个参数
        self.fc = nn.Sequential(nn.Linear(2048, N_classes),
                                nn.Softmax(dim=1))

    def basic_layer1(self, x):
        '''
        input:  x = tensor(3, 224, 224).unsqueeze(0)
         Layer (type)               Output Shape         Param #
      ================================================================
            Conv2d-1         [-1, 64, 112, 112]           9,408
       BatchNorm2d-2         [-1, 64, 112, 112]             128
              ReLU-3         [-1, 64, 112, 112]               0
         MaxPool2d-4           [-1, 64, 56, 56]               0
      ================================================================
        '''
        x = self.zeropadding2d_0(x)
        x = self.cov0(x)
        x = self.zeropadding2d_1(x)
        x = self.maxpool0(x)

        return x

    def forward(self, x):

        x = self.forward1(x)

        return x

    def forward1(self, x):

        x = self.basic_layer1(x)

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

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

        return x

DenseNet

import torch
from torchvision import datasets, transforms
import torch.nn as nn
import time
import numpy as np
import matplotlib.pyplot as plt
import torch.nn.functional as F
import torchsummary as summary

import os
from collections import OrderedDict

class DenseLayer(nn.Sequential):
    def __init__(self, in_channel, growth_rate, bn_size, drop_rate):
       super(DenseLayer, self).__init__()

       self.out_channels1 = bn_size * growth_rate
       self.out_channels2 =  growth_rate

       self.add_module("norm1", nn.BatchNorm2d(num_features=in_channel))
       self.add_module("relu1", nn.ReLU(inplace=True))
       self.add_module("conv1",  nn.Conv2d(in_channels=in_channel, out_channels=self.out_channels1, kernel_size=1, stride=1, padding=0, bias=False))

       self.add_module("norm2", nn.BatchNorm2d(num_features=self.out_channels1))
       self.add_module("relu2", nn.ReLU(inplace=True))
       self.add_module("conv2", nn.Conv2d(in_channels=self.out_channels1, out_channels=self.out_channels2, kernel_size=3, stride=1, padding=1, bias=False))

       self.drop_rate = drop_rate

    def forward(self, x):

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

class DenseBlock(nn.Sequential):
     def __init__(self, num_layers, in_channel, growth_rate, bn_size, 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)

class Transition(nn.Sequential):

    def __init__(self, in_channel, out_channel):
       super(Transition, self).__init__()
       self.add_module("norm", nn.BatchNorm2d(num_features=in_channel))
       self.add_module("relu", nn.ReLU(inplace=True))
       self.add_module("conv",  nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=1, stride=1, padding=0, 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), num_init_features=64,
                 bn_size=4, compression_rate=0.5, drop_rate=0, num_classes=4):
        super(DenseNet, self).__init__()
        # basic_layer
        self.in_channels = 3
        self.basic_layer = nn.Sequential(
            nn.Conv2d(self.in_channels, out_channels=num_init_features, kernel_size=7, stride=2, padding=3, bias=False),
            nn.BatchNorm2d(num_features=num_init_features),
            nn.ReLU(inplace=False),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
            )

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

        # final_layer
        self.features.add_module('norm_f', nn.BatchNorm2d(num_features=self.num_features))
        self.add_module("relu_f", nn.ReLU(inplace=True))
        # classfication layer
        self.classifier =  nn.Sequential(nn.Linear(self.num_features, num_classes),
                                nn.Softmax(dim=1))


        # param initialization
        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):
        features = self.features(x)
        out = F.avg_pool2d(features, 7, stride=1).view(features.size(0), -1)
        out = self.classifier(out)
        return out

def densenet121(pretrained=False, **kwargs):

    model = DenseNet(growth_rate = 32, block_config=(6, 12, 24, 16), num_init_features=64)

    if pretrained:
        summary.summary(model, (3, 224, 224))

    return model

将两个模型结合

class DenseNet_Resnet(nn.Module):
    def __init__(self, growth_rate = 32, block_config=(6, 12, 24, 16), num_init_features=64,
                 bn_size=4, compression_rate=0.5, drop_rate=0, num_classes=4):
        super(DenseNet_Resnet, self).__init__()
        # basic_layer
        self.in_channels = 3
        self.basic_layer = nn.Sequential(
            nn.Conv2d(self.in_channels, out_channels=num_init_features, kernel_size=7, stride=2, padding=3, bias=False),
            nn.BatchNorm2d(num_features=num_init_features),
            nn.ReLU(inplace=False),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
            )

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

            # Resnet50_V2
            Resnet_ConvBlock_V2 = ConvBlock_V2(self.num_features, 3, [int(self.num_features/2), int(self.num_features/2), self.num_features * 2], 1, 1)
            self.features.add_module('ConvBlock_V2%d' %(i+1), Resnet_ConvBlock_V2)
            self.num_features = self.num_features * 2

        # final_layer
        self.features.add_module('norm_f', nn.BatchNorm2d(num_features=self.num_features))
        self.add_module("relu_f", nn.ReLU(inplace=True))
        # classfication layer
        self.classifier =  nn.Sequential(nn.Linear(self.num_features, num_classes),
                                nn.Softmax(dim=1))


        # param initialization
        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):
        features = self.features(x)
        out = F.avg_pool2d(features, 7, stride=1).view(features.size(0), -1)
        out = self.classifier(out)
        return out

模型可视化

model = DenseNet_Resnet()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)
summary.summary(model, (3, 224, 224))

在这里插入图片描述
很明显参数量增加不少

二、训练

导入本地数据

data_dir = './data/bird_photos'
# 归一化参数是怎么来的, tf模型中没有归一化这个过程会不会导致结果不一致?
raw_transforms = transforms.Compose(
        [
        transforms.Resize(224),#中心裁剪到224*224
        transforms.ToTensor(),#转化成张量)
])

def random_split_imagefolder(data_dir, transforms, random_split_rate=0.8):

    _total_data = datasets.ImageFolder(data_dir, transform=transforms)

    train_size = int(random_split_rate * len(_total_data))
    test_size = len(_total_data) - train_size

    _train_datasets, _test_datasets =  torch.utils.data.random_split(_total_data, [train_size, test_size])

    return _total_data, _train_datasets, _test_datasets

batch_size = 32
total_data, train_datasets, test_datasets = random_split_imagefolder(data_dir, raw_transforms, 0.8)
from torch.utils.data import DataLoader
from tqdm import tqdm
def train_and_test(model, loss_func, optimizer, epochs=25):

    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    model.to(device)
    summary.summary(model, (3, 224, 224))

    record = []
    best_acc = 0.0
    best_epoch = 0
    train_data  = DataLoader(train_datasets, batch_size=batch_size, shuffle=True)
    test_data = DataLoader(test_datasets, batch_size=batch_size, shuffle=True)
    train_data_size = len(train_data.dataset)
    test_data_size = len(test_data.dataset)

    for epoch in range(epochs):#训练epochs轮
            epoch_start = time.time()
            print("Epoch: {}/{}".format(epoch + 1, epochs))

            model.train()#训练

            train_loss = 0.0
            train_acc = 0.0
            valid_loss = 0.0
            valid_acc = 0.0

            for i, (inputs, labels) in enumerate(train_data):
                inputs = inputs.to(device)
                labels = labels.to(device)
                #print(labels)
                # 记得清零
                optimizer.zero_grad()

                outputs = model(inputs)

                loss = loss_func(outputs, labels)

                loss.backward()

                optimizer.step()

                train_loss += loss.item() * inputs.size(0)
                ret, predictions = torch.max(outputs.data, 1)
                correct_counts = predictions.eq(labels.data.view_as(predictions))

                acc = torch.mean(correct_counts.type(torch.FloatTensor))

                train_acc += acc.item() * inputs.size(0)

            with torch.no_grad():
                model.eval()#验证

                for j, (inputs, labels) in enumerate(test_data):
                    inputs = inputs.to(device)
                    labels = labels.to(device)

                    outputs = model(inputs)

                    loss = loss_func(outputs, labels)

                    valid_loss += loss.item() * inputs.size(0)
                    ret, predictions = torch.max(outputs.data, 1)
                    correct_counts = predictions.eq(labels.data.view_as(predictions))

                    acc = torch.mean(correct_counts.type(torch.FloatTensor))

                    valid_acc += acc.item() * inputs.size(0)

            avg_train_loss = train_loss / train_data_size
            avg_train_acc = train_acc / train_data_size

            avg_valid_loss = valid_loss / test_data_size
            avg_valid_acc = valid_acc / test_data_size


            record.append([avg_train_loss, avg_valid_loss, avg_train_acc, avg_valid_acc])

            if avg_valid_acc > best_acc  :#记录最高准确性的模型
                best_acc = avg_valid_acc
                best_epoch = epoch + 1

            epoch_end = time.time()

            print("Epoch: {:03d}, Training: Loss: {:.4f}, Accuracy: {:.4f}%, \n\t\tValidation: Loss: {:.4f}, Accuracy: {:.4f}%, Time: {:.4f}s".format(
                    epoch + 1, avg_valid_loss, avg_train_acc * 100, avg_valid_loss, avg_valid_acc * 100,
                    epoch_end - epoch_start))
            print("Best Accuracy for validation : {:.4f} at epoch {:03d}".format(best_acc, best_epoch))

    return model, record

正式训练

    epochs = 100
    model = DenseNet_Resnet()

    loss_func = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(),lr=0.001)
    model, record = train_and_test(model, loss_func, optimizer, epochs)


    record = np.array(record)
    plt.plot(record[:, 0:2])
    plt.legend(['Train Loss', 'Valid Loss'])
    plt.xlabel('Epoch Number')
    plt.ylabel('Loss')
    plt.ylim(0, 1.5)
    plt.savefig('Loss.png')
    plt.show()

    plt.plot(record[:, 2:4])
    plt.legend(['Train Accuracy', 'Valid Accuracy'])
    plt.xlabel('Epoch Number')
    plt.ylabel('Accuracy')
    plt.ylim(0, 1)
    plt.savefig('Accuracy.png')
    plt.show()

三、运行结果

在这里插入图片描述
在这里插入图片描述
结果比单独使用DenseNet或者RenNet要好一点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值