深度学习-第J3周/J3-1周:Densenet算法对比及多数据集对比(乳腺癌识别)

本周任务:

前言

在计算机视觉领域,卷积神经网络模型已成为主流模型,何恺明在2015年提出的深度残差网络ResNet把短路连接的概念引入CNN网络,进而训练更深的CNN模型,ResNet模型可以参数上两周的文章,我对比了ResNet与ResNet_V2的模型,发现ResNet_V2模型相对于ResNet模型收敛更快,精度达到的更高

上周文章:深度学习-第J2周:ResNet-50-V2算法_quant_day的博客-CSDN博客

本周我将介绍Densenet模型,这篇模型是打败了何恺明的ResNet,成为CVPR2017年的Best Paper,虽然有人觉得不值,他的主要思路是将前面的层与后面的层进行密集连接,并使得特征在channel上连接实现了特征重用的过程,使得比ResNet参数更少的情况下性能更优

一、Densenet与ResNet模型对比

ResNet模型短路机制如下图

 ResNet模型的shortcut机制更多的是上一层跟下一层按顺序连接,跨层相加

而对于Densenet,是把前面所有层作为一个输入cat,最后进行输出

二、Densenet实现

Densenet的框架图

 不难看出Densenet主要分为两个block, 红色的DenseBlock跟灰色的Transition

下面分别实现这两个block, DenseBlock

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)

Transition

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))        

在上两周的基础上,这种写法没什么难度

最后实现DenseNet算法

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=1000):
        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, N_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

习惯性的我classifier使用的是Softmax输出,使得输出是一个概率,代码中

   # 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)

通过不断给features添加网络结构add_module把DenseBlock与Transition添加进去,这个方法很好用在后面对DenseNet与ResNet结合中会提到

三、GPU工具箱介绍

在实际运行模型之前我介绍一个好用的GPU监控插件nvitop

安装方法:

pip3 install nvitop

参考文章:nvitop: 史上最强GPU性能实时监测工具 - 知乎

运行脚本

nvitop -m full --user

运行结果如图

四、DenseNet运行模型结果并与ResNet模型对比

数据读取过程与ResNet模型一致,不再介绍

if __name__=='__main__':
 
    epochs = 100
    model = DenseNet()
    
    loss_func = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(),lr=0.0001)
    model, record = train_and_test(model, loss_func, optimizer, epochs)

    torch.save(model, './Best_DenseNet.pth')

    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算法,在20层Epoch以前已到达比较好的结果

对比上周的结果:

 上周文章:深度学习-第J2周:ResNet-50-V2算法_quant_day的博客-CSDN博客

 ResNet50模型,在40层Epoch以后到达比较好的结果

 ResNet50_V2模型,在30层Epoch到达比较好的结果

而且DenseNet算法,第一层val的精准度就在80%左右,看上去模型很优良

对比下参数

ResNet50参数量:Total params: 23,544,708

 ResNet50_V2参数量:Total params: 23,535,108

DenseNet参数量:Total params: 6,957,956

 DenseNet只用了1/4的参数量就达到了ResNet50_V2的效果

五、不同数据集下的表现

将乳腺癌识别图像放到./J3-data中,读取数据

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
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
from collections import OrderedDict

data_dir = './J3-data'

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

N_classes=2
batch_size = 32
mean = [0.4958, 0.4984, 0.4068]
std = [0.2093, 0.2026, 0.2170]
# 真实均值-标准差重新读取数据
real_transforms = transforms.Compose(
        [
        transforms.Resize([224, 224]),#中心裁剪到224*224
        transforms.ToTensor(),#转化成张量
        transforms.Normalize(mean, std)
])
total_data, train_datasets, test_datasets = random_split_imagefolder(data_dir, real_transforms, 0.8)

# 批读取文件
train_data = torch.utils.data.DataLoader(train_datasets, batch_size=batch_size, shuffle=True, num_workers=8)
test_data = torch.utils.data.DataLoader(test_datasets, batch_size=batch_size, shuffle=True, num_workers=8)

train_data_size = len(train_datasets)
test_data_size = len(test_datasets)

  transforms.Resize([224, 224]),这点很重要,因为原图的大小并不一致,类型为N_classes=2

运行结果如下:

ResNet50模型

 ResNet50_V2模型

 DenseNet模型

结论:

 DenseNet在不同数据集上面的收敛速度都要快于ResNet50、ResNet50_V2模型

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值