08-DenseNet 图像分类
DenseNet的结构有如下两个特性:
·神经网络一般需要使用池化等操作缩小特征图尺寸来提取语义特征, 而Dense Block需要保持每一个Block内的特征图尺寸一致来直接进行Concatnate操作, 因此DenseNet被分成了多个Block。 Block的数量一般为4。
·两个相邻的Dense Block之间的部分被称为Transition层, 具体包括BN、 ReLU、 1×1卷积、 2×2平均池化操作。 1×1卷积的作用是降维, 起到压缩模型的作用, 而平均池化则是降低特征图的尺寸,
具体的Block实现细节如图3.20所示, 每一个Block由若干个Bottleneck的卷积层组成, 对应图3.19中的黑点。 Bottleneck由BNReLU、 1×1卷积、 BN、 ReLU、 3×3卷积的顺序构成。
关于Block, 有以下4个细节需要注意:
·每一个Bottleneck输出的特征通道数是相同的, 例如这里的32。 同时可以看到, 经过Concatnate操作后的通道数是按32的增长量增加的,因此这个32也被称为GrowthRate。
·这里1×1卷积的作用是固定输出通道数, 达到降维的作用。 当几十个Bottleneck相连接时, Concatnate后的通道数会增加到上千, 如果不增加1×1的卷积来降维, 后续3×3卷积所需的参数量会急剧增加。 1×1卷积的通道数通常是GrowthRate的4倍。
·图3.20中的特征传递方式是直接将前面所有层的特征Concatnate后传到下一层, 这种方式与具体代码实现的方式是一致的, 而不像图Figue2 中, 前面层都要有一个箭头指向后面的所有层。
·Block采用了激活函数在前、 卷积层在后的顺序, 这与一般的网络上是不同的。
DenseNet代码实现(pytorch):
1 import torch 2 import torch.nn as nn 3 import torchvision 4 5 print("PyTorch Version: ",torch.__version__) 6 print("Torchvision Version: ",torchvision.__version__) 7 8 __all__ = ['DenseNet121', 'DenseNet169','DenseNet201','DenseNet264'] 9 10 def Conv1(in_planes, places, stride=2): 11 return nn.Sequential( 12 nn.Conv2d(in_channels=in_planes,out_channels=places,kernel_size=7,stride=stride,padding=3, bias=False), 13 nn.BatchNorm2d(places), 14 nn.ReLU(inplace=True), 15 nn.MaxPool2d(kernel_size=3, stride=2, padding=1) 16 ) 17 18 class _TransitionLayer(nn.Module): 19 def __init__(self, inplace, plance): 20 super(_TransitionLayer, self).__init__() 21 self.transition_layer = nn.Sequential( 22 nn.BatchNorm2d(inplace), 23 nn.ReLU(inplace=True), 24 nn.Conv2d(in_channels=inplace,out_channels=plance,kernel_size=1,stride=1,padding=0,bias=False), 25 nn.AvgPool2d(kernel_size=2,stride=2), 26 ) 27 28 def forward(self, x): 29 return self.transition_layer(x) 30 31 32 class _DenseLayer(nn.Module): 33 def __init__(self, inplace, growth_rate, bn_size, drop_rate=0): 34 super(_DenseLayer, self).__init__() 35 self.drop_rate = drop_rate 36 self.dense_layer = nn.Sequential( 37 nn.BatchNorm2d(inplace), 38 nn.ReLU(inplace=True), 39 nn.Conv2d(in_channels=inplace, out_channels=bn_size * growth_rate, kernel_size=1, stride=1, padding=0, bias=False), 40 nn.BatchNorm2d(bn_size * growth_rate), 41 nn.ReLU(inplace=True), 42 nn.Conv2d(in_channels=bn_size * growth_rate, out_channels=growth_rate, kernel_size=3, stride=1, padding=1, bias=False), 43 ) 44 self.dropout = nn.Dropout(p=self.drop_rate) 45 46 def forward(self, x): 47 y = self.dense_layer(x) 48 if self.drop_rate > 0: 49 y = self.dropout(y) 50 return torch.cat([x, y], 1) 51 52 53 class DenseBlock(nn.Module): 54 def __init__(self, num_layers, inplances, growth_rate, bn_size , drop_rate=0): 55 super(DenseBlock, self).__init__() 56 layers = [] 57 for i in range(num_layers): 58 layers.append(_DenseLayer(inplances + i * growth_rate, growth_rate, bn_size, drop_rate)) 59 self.layers = nn.Sequential(*layers) 60 61 def forward(self, x): 62 return self.layers(x) 63 64 65 class DenseNet(nn.Module): 66 def __init__(self, init_channels=64, growth_rate=32, blocks=[6, 12, 24, 16],num_classes=1000): 67 super(DenseNet, self).__init__() 68 bn_size = 4 69 drop_rate = 0 70 self.conv1 = Conv1(in_planes=3, places=init_channels) 71 72 num_features = init_channels 73 self.layer1 = DenseBlock(num_layers=blocks[0], inplances=num_features, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate) 74 num_features = num_features + blocks[0] * growth_rate 75 self.transition1 = _TransitionLayer(inplace=num_features, plance=num_features // 2) 76 num_features = num_features // 2 77 self.layer2 = DenseBlock(num_layers=blocks[1], inplances=num_features, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate) 78 num_features = num_features + blocks[1] * growth_rate 79 self.transition2 = _TransitionLayer(inplace=num_features, plance=num_features // 2) 80 num_features = num_features // 2 81 self.layer3 = DenseBlock(num_layers=blocks[2], inplances=num_features, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate) 82 num_features = num_features + blocks[2] * growth_rate 83 self.transition3 = _TransitionLayer(inplace=num_features, plance=num_features // 2) 84 num_features = num_features // 2 85 self.layer4 = DenseBlock(num_layers=blocks[3], inplances=num_features, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate) 86 num_features = num_features + blocks[3] * growth_rate 87 88 self.avgpool = nn.AvgPool2d(7, stride=1) 89 self.fc = nn.Linear(num_features, num_classes) 90 91 def forward(self, x): 92 x = self.conv1(x) 93 94 x = self.layer1(x) 95 x = self.transition1(x) 96 x = self.layer2(x) 97 x = self.transition2(x) 98 x = self.layer3(x) 99 x = self.transition3(x) 100 x = self.layer4(x) 101 102 x = self.avgpool(x) 103 x = x.view(x.size(0), -1) 104 x = self.fc(x) 105 return x 106 107 def DenseNet121(): 108 return DenseNet(init_channels=64, growth_rate=32, blocks=[6, 12, 24, 16], num_classes=10) 109 110 def DenseNet169(): 111 return DenseNet(init_channels=64, growth_rate=32, blocks=[6, 12, 32, 32],num_classes=10) 112 113 def DenseNet201(): 114 return DenseNet(init_channels=64, growth_rate=32, blocks=[6, 12, 48, 32],num_classes=10) 115 116 def DenseNet264(): 117 return DenseNet(init_channels=64, growth_rate=32, blocks=[6, 12, 64, 48],num_classes=10) 118 119 # if __name__=='__main__': 120 # # model = torchvision.models.densenet121() 121 # model = DenseNet121() 122 # print(model) 123 124 # input = torch.randn(1, 3, 224, 224) 125 # out = model(input) 126 # print(out.shape)
ClassfyNet_main.py
1 import torch 2 from torch.utils.data import DataLoader 3 from torch import nn, optim 4 from torchvision import datasets, transforms 5 from torchvision.transforms.functional import InterpolationMode 6 7 from matplotlib import pyplot as plt 8 9 10 import time 11 12 from Lenet5 import Lenet5_new 13 from Resnet18 import ResNet18,ResNet18_new 14 from AlexNet import AlexNet 15 from Vgg16 import VGGNet16 16 from Densenet import DenseNet121, DenseNet169, DenseNet201, DenseNet264 17 18 def main(): 19 20 print("Load datasets...") 21 22 # transforms.RandomHorizontalFlip(p=0.5)---以0.5的概率对图片做水平横向翻转 23 # transforms.ToTensor()---shape从(H,W,C)->(C,H,W), 每个像素点从(0-255)映射到(0-1):直接除以255 24 # transforms.Normalize---先将输入归一化到(0,1),像素点通过"(x-mean)/std",将每个元素分布到(-1,1) 25 transform_train = transforms.Compose([ 26 transforms.Resize((224, 224), interpolation=InterpolationMode.BICUBIC), 27 # transforms.RandomCrop(32, padding=4), # 先四周填充0,在吧图像随机裁剪成32*32 28 transforms.RandomHorizontalFlip(p=0.5), 29 transforms.ToTensor(), 30 transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) 31 ]) 32 33 transform_test = transforms.Compose([ 34 transforms.Resize((224, 224), interpolation=InterpolationMode.BICUBIC), 35 # transforms.RandomCrop(32, padding=4), # 先四周填充0,在吧图像随机裁剪成32*32 36 transforms.ToTensor(), 37 transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) 38 ]) 39 40 # 内置函数下载数据集 41 train_dataset = datasets.CIFAR10(root="./data/Cifar10/", train=True, 42 transform = transform_train, 43 download=True) 44 test_dataset = datasets.CIFAR10(root = "./data/Cifar10/", 45 train = False, 46 transform = transform_test, 47 download=True) 48 49 print(len(train_dataset), len(test_dataset)) 50 51 Batch_size = 64 52 train_loader = DataLoader(train_dataset, batch_size=Batch_size, shuffle = True, num_workers=4) 53 test_loader = DataLoader(test_dataset, batch_size = Batch_size, shuffle = False, num_workers=4) 54 55 # 设置CUDA 56 device = torch.device("cuda:2" if torch.cuda.is_available() else "cpu") 57 58 # 初始化模型 59 # 直接更换模型就行,其他无需操作 60 # model = Lenet5_new().to(device) 61 # model = ResNet18().to(device) 62 # model = ResNet18_new().to(device) 63 # model = VGGNet16().to(device) 64 65 model = DenseNet121().to(device) 66 # model = DenseNet169().to(device) 67 68 # model = AlexNet(num_classes=10, init_weights=True).to(device) 69 print("DenseNet121 train...") 70 71 # 构造损失函数和优化器 72 criterion = nn.CrossEntropyLoss() # 多分类softmax构造损失 73 # opt = optim.SGD(model.parameters(), lr=0.01, momentum=0.8, weight_decay=0.001) 74 opt = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=0.0005) 75 76 # 动态更新学习率 ------每隔step_size : lr = lr * gamma 77 schedule = optim.lr_scheduler.StepLR(opt, step_size=10, gamma=0.6, last_epoch=-1) 78 79 # 开始训练 80 print("Start Train...") 81 82 epochs = 100 83 84 loss_list = [] 85 train_acc_list =[] 86 test_acc_list = [] 87 epochs_list = [] 88 89 for epoch in range(0, epochs): 90 91 start = time.time() 92 93 model.train() 94 95 running_loss = 0.0 96 batch_num = 0 97 98 for i, (inputs, labels) in enumerate(train_loader): 99 100 inputs, labels = inputs.to(device), labels.to(device) 101 102 # 将数据送入模型训练 103 outputs = model(inputs) 104 # 计算损失 105 loss = criterion(outputs, labels).to(device) 106 107 # 重置梯度 108 opt.zero_grad() 109 # 计算梯度,反向传播 110 loss.backward() 111 # 根据反向传播的梯度值优化更新参数 112 opt.step() 113 114 # 100个batch的 loss 之和 115 running_loss += loss.item() 116 # loss_list.append(loss.item()) 117 batch_num+=1 118 119 120 epochs_list.append(epoch) 121 122 # 每一轮结束输出一下当前的学习率 lr 123 lr_1 = opt.param_groups[0]['lr'] 124 print("learn_rate:%.15f" % lr_1) 125 schedule.step() 126 127 end = time.time() 128 print('epoch = %d/100, batch_num = %d, loss = %.6f, time = %.3f' % (epoch+1, batch_num, running_loss/batch_num, end-start)) 129 running_loss=0.0 130 131 # 每个epoch训练结束,都进行一次测试验证 132 model.eval() 133 train_correct = 0.0 134 train_total = 0 135 136 test_correct = 0.0 137 test_total = 0 138 139 # 训练模式不需要反向传播更新梯度 140 with torch.no_grad(): 141 142 # print("=======================train=======================") 143 for inputs, labels in train_loader: 144 inputs, labels = inputs.to(device), labels.to(device) 145 outputs = model(inputs) 146 147 pred = outputs.argmax(dim=1) # 返回每一行中最大值元素索引 148 train_total += inputs.size(0) 149 train_correct += torch.eq(pred, labels).sum().item() 150 151 152 # print("=======================test=======================") 153 for inputs, labels in test_loader: 154 inputs, labels = inputs.to(device), labels.to(device) 155 outputs = model(inputs) 156 157 pred = outputs.argmax(dim=1) # 返回每一行中最大值元素索引 158 test_total += inputs.size(0) 159 test_correct += torch.eq(pred, labels).sum().item() 160 161 print("train_total = %d, Accuracy = %.5f %%, test_total= %d, Accuracy = %.5f %%" %(train_total, 100 * train_correct / train_total, test_total, 100 * test_correct / test_total)) 162 163 train_acc_list.append(100 * train_correct / train_total) 164 test_acc_list.append(100 * test_correct / test_total) 165 166 # print("Accuracy of the network on the 10000 test images:%.5f %%" % (100 * test_correct / test_total)) 167 # print("===============================================") 168 169 fig = plt.figure(figsize=(4, 4)) 170 171 plt.plot(epochs_list, train_acc_list, label='train_acc_list') 172 plt.plot(epochs_list, test_acc_list, label='test_acc_list') 173 plt.legend() 174 plt.title("train_test_acc") 175 plt.savefig('DenseNet121_acc_epoch_{:04d}.png'.format(epochs)) 176 plt.close() 177 178 if __name__ == "__main__": 179 180 main()
图4 DenseNet121_acc_epoch_0100