写在前面
最近需要在DPatch源码的基础上,做一些修改,改成自己的网络,生成自己的对抗样本。为了方便修改,需要花点时间将DPatch的源码啃明白。所以就有了这篇博客,记录源码学习的过程,方便后面查看修改。为了方便查看,我将自己对于源码的理解直接作为注释添加在源代码中。由于我主要是要修改darknet.py文件为其他网络结构,同时使用train.py文件进行训练,所以我主要关注这两个文件,同时关注一些输入和输出。
Darknet19的整个的网络结构
darknet.py文件
(1)def _make_layers(in_channels, net_cfg)
#这个文件用来定义网络中的每一层,其中的in_channels是每一层的输入通道数,out_channels是输出通道数
def _make_layers(in_channels, net_cfg):
layers = []
#如果net_cfg这个列表中不止一个元素,则对列表中每一个元素都进行一次_make_layers操作
if len(net_cfg) > 0 and isinstance(net_cfg[0], list):
for sub_cfg in net_cfg:
layer, in_channels = _make_layers(in_channels, sub_cfg)
#将每一层都保存到layers中
layers.append(layer)
else:
for item in net_cfg:
#net_cfg中的第一个元素如果为M,则表明要执行max pooling最大池化操作,这一层也要放到layers中
if item == 'M':
layers.append(nn.MaxPool2d(kernel_size=2, stride=2))
else:
out_channels, ksize = item
#此时应该是构建卷积层,通过调用net_utils中的Conv2d_BatchNorm函数,从而执行卷积,batch normalization这些操作
layers.append(net_utils.Conv2d_BatchNorm(in_channels,
out_channels,
ksize,
same_padding=True))
# layers.append(net_utils.Conv2d(in_channels, out_channels,
# ksize, same_padding=True))
in_channels = out_channels
#nn.sequential是一个容器,就是根据自己的需求,把不同的函数组合成一个模块使用,这里的in_channels就是最后一层的输出通道数目
return nn.Sequential(*layers), in_channels
(2)class Darknet19(nn.Module)
1)def __init__函数
def __init__(self, training=True):
super(Darknet19, self).__init__()
#这里就是Darknet19的网络结构,全部保存在net_cfgs这个列表中。其中M表示做最大池化,(64, 3)表示做一次卷积,输出通道数目64,3表示卷积核的size是3×3.
net_cfgs = [
# conv1s
[(32, 3)],
['M', (64, 3)],
['M', (128, 3), (64, 1), (128, 3)],
['M', (256, 3), (128, 1), (256, 3)],
['M', (512, 3), (256, 1), (512, 3), (256, 1), (512, 3)],
# conv2
['M', (1024, 3), (512, 1), (1024, 3), (512, 1), (1024, 3)],
# ------------
# conv3
[(1024, 3), (1024, 3)],
# conv4
[(1024, 3)]
]
# darknet
#每次调用_make_layers,其中3表示输入通道数目为3,c1是上一层的输出通道数目,同时也是下一层的输入通道数目
self.conv1s, c1 = _make_layers(3, net_cfgs[0:5])
self.conv2, c2 = _make_layers(c1, net_cfgs[5])
# ---
self.conv3, c3 = _make_layers(c2, net_cfgs[6])
stride = 2
# stride*stride times the channels of conv1s
#这里调用了作者自己写的函数ReorgLayer,下面对这个函数进行解析
self.reorg = ReorgLayer(stride=2)
# cat [conv1s, conv3]
#经过上面的reorg layer,此时的输入通道数目变成了c1*(stride*stride) + c3
self.conv4, c4 = _make_layers((c1*(stride*stride) + c3), net_cfgs[7])
# linear
out_channels = cfg.num_anchors * (cfg.num_classes + 5)
self.conv5 = net_utils.Conv2d(c4, out_channels, 1, 1, relu=False)
#这里的nn.AvgPool2d((1, 1))表示是做平均全局池化global average pool。具体解释见下面
self.global_average_pool = nn.AvgPool2d((1, 1))
# train
self.bbox_loss = None
self.iou_loss = None
self.cls_loss = None
self.pool = Pool(processes=10)
''' define a patch here '''
# training
if training:
#这里的意思是将一个固定不可训练的tensor转换成可以训练的类型parameter。
#所以经过类型转换这个self.v变成了模型的一部分,成为了模型中根据训练可以改动的参数了。
#使用这个函数的目的也是想让某些变量在学习的过程中不断的修改其值以达到最优化。
#(1,3)定义了输出张量的形状,cfg.img_w, cfg.img_h表示的是结果张量
self.patch = nn.Parameter(torch.rand(1,3, cfg.img_w, cfg.img_h),requires_grad=True)
# testing
else:
self.patch = cfg.patch.cuda()
2)forward函数:
def forward(self, im_data, gt_boxes=None, gt_classes=None, dontcare=None,
size_index=0, attack=None):
# add patch here
if attack is not None:
for k in range(im_data.size(