重要意义:引入shortcut connection,让网络信息有效传播,梯度反传顺畅,使得数千层卷积神经网络都可以收敛
残差结构:
Identity与F(x)结合形式探讨:
.A-全零填充:维度增加的部分采用零来填充
B-网络层映射:当维度发生变化时,通过网络层映射(例如:1+1卷积)特征图至相同维度
C-所有Shortcut均通过网络层映射(例如:1*1卷积)
有点:1有利于梯度传播 2.提供了恒等映射的可能
里面模块的堆叠方式
Basic:两个3X3卷积堆叠
Bottleneck:利用1X1卷积减少计算量
. 恒等映射形式的shortcut connection是从网络退化问题中思考而来
网络退化:指的是模型复杂,难以训练,而不是模型复杂,准确度下降
代码构建部分:
通过建立makelayer建立模块堆叠
class ResNet(nn.Module):
def __init__(self, block, num_blocks, num_classes=10):
super(ResNet, self).__init__()
self.in_planes = 16
self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(16)
self.layer1 = self._make_layer(block, 16, num_blocks[0], stride=1)#numblock这个指的是堆叠数量
self.layer2 = self._make_layer(block, 32, num_blocks[1], stride=2)
self.layer3 = self._make_layer(block, 64, num_blocks[2], stride=2)
self.linear = nn.Linear(64, num_classes)
self.apply(_weights_init)
def _make_layer(self, block, planes, num_blocks, stride):
strides = [stride] + [1]*(num_blocks-1)
layers = []
for stride in strides:#进行堆叠
layers.append(block(self.in_planes, planes, stride))
self.in_planes = planes * block.expansion#输入通道数和上一层输出通道数要对应
return nn.Sequential(*layers)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = F.avg_pool2d(out, out.size()[3])
out = out.view(out.size(0), -1)
out = self.linear(out)
return out
构建Basicblock导入堆叠
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_planes, planes, stride=1, option='A'):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != planes:#判断是不是维度变化(因为步长加长,特征图变小,需要处理维度问题)
if option == 'A':
"""
For CIFAR10 ResNet paper uses option A.
"""
self.shortcut = LambdaLayer(lambda x:
F.pad(x[:, :, ::2, ::2], (0, 0, 0, 0, planes//4, planes//4), "constant", 0))#如果维度增加那部分用0填充
elif option == 'B':
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False),#用1x1卷积生维度
nn.BatchNorm2d(self.expansion * planes)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out += self.shortcut(x)#加多一条Identity的线,就是那个恒等映射那里
out = F.relu(out)
return out
PS:
建立混淆矩阵直观看到训练和测试效果
混淆矩阵(Confusion Matrix)常用来观察分类结果,其是一个N*N的方阵,N表示类别数。
混淆矩阵的行表示真实类别,列表示预测类别。例如,猫狗的二分类问题,有猫的图像10张,狗的图像30张,模型对这40张图片进行预测,得到的混淆矩阵为
类别 阿猫 阿狗
阿猫 7 3
阿狗 10 20
从第一行中可知道,10张猫的图像中,7张预测为猫,3张预测为狗,猫的召回率(Recall)为7/10 = 70%,
从第二行中可知道,30张狗的图像中,8张预测为猫,22张预测为狗,狗的召回率为20/30 = 66.7%,
从第一列中可知道,预测为猫的17张图像中,有7张是真正的猫,猫的精确度(Precision)为7 / 17 = 41.17%
从第二列中可知道,预测为狗的23张图像中,有20张是真正的狗,狗的精确度(Precision)为20 / 23 = 86.96%
模型的准确率(Accuracy)为 (7+20) / 40 = 67.5%
可以发现通过混淆矩阵可以清晰的看出网络模型的分类情况,若再结合上颜色可视化,可方便的看出模型的分类偏好。
代码部分:
conf_mat = np.zeros((10, 10))
loss_sigma = []
for i, data in enumerate(data_loader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
optimizer.zero_grad()
loss = loss_f(outputs, labels)
loss.backward()
optimizer.step()
# 统计预测信息
_, predicted = torch.max(outputs.data, 1)
# 统计混淆矩阵,这个不错,值得看
for j in range(len(labels)):
cate_i = labels[j].cpu().numpy()
pre_i = predicted[j].cpu().numpy()
conf_mat[cate_i, pre_i] += 1.#这里就是构建
# 统计loss
loss_sigma.append(loss.item())
acc_avg = conf_mat.trace() / conf_mat.sum()
导入进行可视化
def show_confMat(confusion_mat, classes, set_name, out_dir, verbose=False):
"""
混淆矩阵绘制
:param confusion_mat:
:param classes: 类别名
:param set_name: trian/valid
:param out_dir:
:return:
"""
cls_num = len(classes)
# 归一化
confusion_mat_N = confusion_mat.copy()
for i in range(len(classes)):
confusion_mat_N[i, :] = confusion_mat[i, :] / confusion_mat[i, :].sum()
# 获取颜色
cmap = plt.cm.get_cmap('Greys') # 更多颜色: http://matplotlib.org/examples/color/colormaps_reference.html
plt.imshow(confusion_mat_N, cmap=cmap)
plt.colorbar()
# 设置文字
xlocations = np.array(range(len(classes)))
plt.xticks(xlocations, list(classes), rotation=60)
plt.yticks(xlocations, list(classes))
plt.xlabel('Predict label')
plt.ylabel('True label')
plt.title('Confusion_Matrix_' + set_name)
# 打印数字
for i in range(confusion_mat_N.shape[0]):
for j in range(confusion_mat_N.shape[1]):
plt.text(x=j, y=i, s=int(confusion_mat[i, j]), va='center', ha='center', color='red', fontsize=10)
# 显示
plt.savefig(os.path.join(out_dir, 'Confusion_Matrix' + set_name + '.png'))
# plt.show()
plt.close()
if verbose:
for i in range(cls_num):
print('class:{:<10}, total num:{:<6}, correct num:{:<5} Recall: {:.2%} Precision: {:.2%}'.format(
classes[i], np.sum(confusion_mat[i, :]), confusion_mat[i, i],
confusion_mat[i, i] / (.1 + np.sum(confusion_mat[i, :])),
confusion_mat[i, i] / (.1 + np.sum(confusion_mat[:, i]))))