- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊 | 接辅导、项目定制
- 🚀 文章来源:K同学的学习圈子
📌 本周任务:
●阅读ResNeXt论文,了解作者的构建思路
●对比我们之前介绍的ResNet50V2、DenseNet算法
●使用ResNeXt-50算法完成猴痘病识别
前言
ResNeXt是由何凯明团队在2017年CVPR会议上提出来的新型图像分类网络。在论文《Aggregated Residual Transformations for Deep Neural Networks》作者提出了当时普遍存在的问题:如何提高模型的准确率?
常用的方法是提高网络的深度或宽度,但单纯的提高网络的深度或宽度,加大了设计的难度,也加大了计算的开销。由此何团队设计了cardinality的概念。将卷积通道分组,再对分组进行卷积。
分组卷积
分组卷机简单来说就是将特征图分为不同的组,再对每组特征图分别进行卷积,这个操作可以有效的降低计算量。在分组卷积中,每个卷积核只处理部分通道,每个卷积核生成一张特征图。
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, in_channel, out_channel, stride=1, downsample=None, groups=1, base_width=64): # 初始化方法
super(Bottleneck, self).__init__()
width = int(in_channel * (base_width / 64.0)) * groups
self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=width, kernel_size=1, stride=1,
bias=False)
self.bn1 = nn.BatchNorm2d(num_features=width)
self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, groups=groups, kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(num_features=width)
self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel * self.expansion, kernel_size=1, stride=1,
bias=False)
self.bn3 = nn.BatchNorm2d(num_features=out_channel * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
def forward(self, x):
identity = x
if self.downsample:
identity = self.downsample(x)
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.conv2(x)
x = self.bn2(x)
x = self.relu(x)
x = self.conv3(x)
x = self.bn3(x)
x += identity
x = self.relu(x)
return x
ResNeXt50_Model模型
class ResNeXt50_Model(nn.Module):
def __init__(self, in_channel=3, N_classes=1000):
super(ResNeXt50_Model, self).__init__()
self.in_channels = in_channel
self.layers = [2, 3, 5, 2]
self.zeropadding2d = nn.ZeroPad2d(3)
self.cov0 = nn.Conv2d(self.in_channels, out_channels=64, kernel_size=7, stride=2, padding=3)
self.bn0 = nn.BatchNorm2d(num_features=64)
self.relu0 = nn.ReLU(inplace=False)
self.maxpool0 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.channel = 64
self.groups = 1
self.base_width = 64
self.layer1 = self._make_layer(Bottleneck, 64, self.layers[0])
self.layer2 = self._make_layer(Bottleneck, 128, self.layers[1], stride=2)
self.layer3 = self._make_layer(Bottleneck, 256, self.layers[2], stride=2)
self.layer4 = self._make_layer(Bottleneck, 512, self.layers[3], stride=2)
self.avgpool = nn.AvgPool2d((7, 7))
self.fc = nn.Sequential(nn.Linear(2048, N_classes),
nn.Softmax(dim=1))
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
def basic_layer1(self, x):
x = self.zeropadding2d(x)
x = self.cov0(x)
x = self.bn0(x)
x = self.relu0(x)
x = self.maxpool0(x)
return x
def _make_layer(self, block, channel, blocks, stride=1):
downsample = None
if stride != 1 or self.channel != channel * block.expansion:
downsample = nn.Sequential(
nn.Conv2d(in_channels=self.channel, out_channels=channel * block.expansion, kernel_size=1,
stride=stride, bias=False),
nn.BatchNorm2d(num_features=channel * block.expansion)
)
layers = []
layers.append(block(self.channel, channel, downsample=downsample, stride=stride, groups=self.groups,
base_width=self.base_width))
self.channel = channel * block.expansion
for _ in range(1, blocks):
layers.append(block(self.channel, channel, groups=self.groups, base_width=self.base_width))
return nn.Sequential(*layers)
def forward(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.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
测试模型
def train_and_test(model, loss_fn, optimizer,Epoch):
train_loss = []
train_acc = []
test_loss = []
test_acc = []
best_acc = 0.0
best_epoch = 0
for epoch in range(1,Epoch+1):
time_start = time.time()
model.train()
running_loss = 0.0
running_corrects = 0
for inputs, labels in train_loader:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
loss = loss_fn(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
_, preds = torch.max(outputs, 1)
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / train_data_size
epoch_acc = running_corrects.double() / train_data_size
train_loss.append(epoch_loss)
train_acc.append(epoch_acc)
time_train = time.time() - time_start
print('Epoch: {} Train Loss: {:.4f} Train Acc: {:.4f} Training Time : {:.4f}'.format(epoch, epoch_loss, epoch_acc,time_train))
model.eval()
running_loss = 0.0
running_corrects = 0
with torch.no_grad():
time_test = time.time()
for inputs, labels in test_loader:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
loss = loss_fn(outputs, labels)
_, preds = torch.max(outputs, 1)
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / test_data_size
epoch_acc = running_corrects.double() / test_data_size
test_loss.append(epoch_loss)
test_acc.append(epoch_acc)
time_test = time.time() - time_test
print('Epoch: {} Test Loss: {:.4f} Test Acc: {:.4f} Testing Time : {:.4f}'.format(epoch, epoch_loss, epoch_acc,time_test))
if epoch_acc > best_acc:
best_acc = epoch_acc
best_epoch = epoch
return model , train_loss, train_acc, test_loss, test_acc
最佳Accuracy为89.28%