py-MDNet详解附代码(一):train


都0202年了,为啥还要看MDNet(CVPR2016)这么早的算法呢?原因如下:

  • 这篇论文里面有比较经典的在线更新的实现,可以作为以后理解此类手法的参考
  • 作为一个本质上是分类的跟踪器,取得了VOT2015的winner,之后的VOT2018-LT的winner–MBMD也是借鉴的MDNet

论文链接:Learning Multi-Domain Convolutional Neural Networks for Visual Tracking国内镜像
代码:https://github.com/hyeonseobnam/py-MDNet
这篇先来介绍整篇里面较容易的部分:train

Architecture

MDNet的整体架构
论文中的图已经很清楚展示了MDNet整体的架构:

  • 网络分为shared layers和domain-specific layers两部分,前者采用VGG-M的模型和预训练参数,后者有K个分支,对应K个训练序列,也称为K个domain(这里只加载conv部分的预训练模型,然后离线学习阶段conv和fc全都训练)
  • 训练过程中,每次迭代从一个序列中提取32个positive samples和96个negative samples来构成一个mini-batch,训练对应的 f c 6 k fc6^{k} fc6k分支(也就是说每个epoch下,每个fc6分支都依次被训一次)
  • fc6每个分支输出二维特征,分别表示target和background的pos_score和neg_score,对于正样本的标签就是[0, 1],负样本的标签就是[1, 0],损失采用二分类交叉熵:binary softmax cross-entropy loss

现在就从代码角度看看这几个关键的部分是怎么实现的,其中最关键的还是怎么产生正负样本:

Regions

因为MDNet就是利用分类来判断目标,所以需要构造正负样本来训练分类器,主要通过以下两句实现:

dataset[k] = RegionDataset(seq['images'], seq['gt'], opts)
# take vot2013/cup sequence for example
# seq['images'] -> 'datasets/VOT/vot2013/cup/00000001.jpg',...
# seq['gt'] -> (303, 4) np.ndarray
# training
pos_regions, neg_regions = dataset[k].next()

而类RegionDataset下有魔法方法__next__(),生成32个pos_regions和96个neg_regions,这里主要通过一个序列中选8帧,每帧生成4个pos_regions和12个neg_regions来构成,其中pos_regions就是与GT bbox的IoU介于[0.7, 1]之间的samples;neg_regions就是与GT bbox的IoU介于[0, 0.5]之间的samples,如论文中所说:For offline multi-domain learning, we collect 50 positive and 200 negative samples from every frame, where positive and negative examples have ≥ 0.7 and ≤ 0.5 IoU overlap ratios with ground-truth bounding boxes, respectively.

def __next__(self):
    next_pointer = min(self.pointer + self.batch_frames, len(self.img_list)) # 8
    idx = self.index[self.pointer:next_pointer]
    if len(idx) < self.batch_frames:
        self.index = np.random.permutation(len(self.img_list))
        next_pointer = self.batch_frames - len(idx)
        idx = np.concatenate((idx, self.index[:next_pointer]))
    self.pointer = next_pointer

    pos_regions = np.empty((0, 3, self.crop_size, self.crop_size), dtype='float32')
    neg_regions = np.empty((0, 3, self.crop_size, self.crop_size), dtype='float32')
    for i, (img_path, bbox) in enumerate(zip(self.img_list[idx], self.gt[idx])):
        image = Image.open(img_path).convert('RGB')
        image = np.asarray(image)

        n_pos = (self.batch_pos - len(pos_regions)) // (self.batch_frames - i)  # 4 * 8
        n_neg = (self.batch_neg - len(neg_regions)) // (self.batch_frames - i)  # 12 * 8
        pos_examples = self.pos_generator(bbox, n_pos, overlap_range=self.overlap_pos) # [0.7, 1]
        neg_examples = self.neg_generator(bbox, n_neg, overlap_range=self.overlap_neg) # [0, 0.5]

        pos_regions = np.concatenate((pos_regions, self.extract_regions(image, pos_examples)), axis=0)
        neg_regions = np.concatenate((neg_regions, self.extract_regions(image, neg_examples)), axis=0)

    pos_regions = torch.from_numpy(pos_regions)
    neg_regions = torch.from_numpy(neg_regions)
    return pos_regions, neg_regions

有训练数据之后,就是构造损失函数criterion和评价器evaluator来评估训练是否收敛,以及优化器optimizer:

Loss function

这里的损失函数非常简单,就是直接是二分类交叉熵:

class BCELoss(nn.Module):
    def forward(self, pos_score, neg_score, average=True):
        # pos_score:(32, 2) | neg_score:(96, 2)
        pos_loss = -F.log_softmax(pos_score, dim=1)[:, 1]
        neg_loss = -F.log_softmax(neg_score, dim=1)[:, 0]

        loss = pos_loss.sum() + neg_loss.sum()
        if average:
            loss /= (pos_loss.size(0) + neg_loss.size(0))
        return loss

本来F.log_softmax还得配合F.nll_loss来使用的,这里乘1就直接省略了。从这里就能看出,对于正样本的标签就是[0, 1],负样本的标签就是[1, 0]。

evaluator

这里看训练是否有效进行的指标是Precision,它是这样定义的:

class Precision():
    def __call__(self, pos_score, neg_score):
        scores = torch.cat((pos_score[:, 1], neg_score[:, 1]), 0)
        topk = torch.topk(scores, pos_score.size(0))[1]
        # torch.topk -> (values, indexes)
        prec = (topk < pos_score.size(0)).float().sum() / (pos_score.size(0) + 1e-8)
        return prec.item()

这里是pos_regions中最小的目标得分都要比neg_regions中最大的目标得分大,那就Precision = 1;有这样的趋势:越多的pos_regions的目标得分比neg_regions的目标得分大,Precision就越高。

Hyper-parameters

这里罗列一下论文中和代码中用到的几个超参数:

paremspapercode
offline learning iterations100K50K
lr for conv0.00010.0001
lr for fc0.0010.001
IoU range for pos samples[0.7, 1][0.7, 1]
IoU range for neg samples[0, 0.5][0, 0.5]

Figures

下面这张图可视化了训练阶段的正负样本(我这里只是各选取了16个),红色为pos_examples,蓝色为neg_examples,绿色为GT bbox。
原图上的正负样本可视化

下面这些是从上面各拿了2个正负样本进行crop后的pos_regions,neg_regions的可视化,也就是网络的输入,这里可以进行一些patch的数据增强
送入网络学习的正负regions
下面是训练过程截图,不过代码里面设置的并不是最大precision保存模型,而是每个cycle覆盖保存。
训练过程截图

Discuss

MDNet关键还不是在训练阶段,而是在online tracking阶段有更多的花样,但是从训练部分可以有几个讨论的点:

  • 训练非常类似R-CNN,因为原图上crop下来一起送入shared layers,计算冗余,效率低下
  • 这样一个分类的方式,用一个序列训练一个domain,如果没有在线更新部分,个人感觉只是对见过的物体跟踪比较准确,当然有了在线更新的部分会好很多,无疑速度也变慢了,更多细节见下篇
  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

laizi_laizi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值