【小白笔记】目标跟踪CFNet论文及源码笔记



这是CVPR17的一篇文章。 项目地址
该论文主要基于SiameseFC网络的改进,主要加入了CF层,并推导了前向和反向公式,也是端到端的网络,使得网络在浅层特征下也能有不错的性能,是整体网络轻量化,速度也更快。下面的分析将结合具体的代码,有不对的地方欢迎讨论~

1. 网络结构

基础及背景这里就不说了,直接来看模型。
在这里插入图片描述
为什么要加这个CF层:
结合传统的DCF进行对比,DCF就是线性核的KCF,DCF分为训练和检测两步,训练时用最小二乘法解一个岭回归问题把CF模板w求出来,检测时w和搜索域的patch卷积就得到了response,所以CFNet的原版SiameseFC没有训练直接把目标和搜索域做相关,比较naive,所以CFNet把DCF的一套都整合到了网络中,是一个端到端的模型,由于加入了最小二乘的思想所以浅层也能有不错的性能,但是这样做的问题是把CF的边界问题也带到网络中去了,所以作者又加了crop层仅保留中间的一部分,这就把边界效应降低了。
CF层怎么设计:
这里首先要会推导岭回归的最小二乘解w,然后把计算w的过程拆成能用网络表示的3步如下:
在这里插入图片描述
不要忘了DCF的重要性质样本x是循环样本,这样可以利用频域加速求解,卷积变成了点乘,如下:
在这里插入图片描述
这样就可以表示成一个网络流的形式如下图:
在这里插入图片描述

2. 构建基础特征网络

CFNet的两个支流的提特征部分是完全一样的,这也是Siamese的意思,训练也是只要训一支就好了,就是共享参数,这里使用了AlexNet的五层卷积结构,代码中解释如下:
以第一层为例,包括卷积,normalization和pooling操作

net = add_block(net, opts, '1', 11, 11, ...
                opts.num_in(i), opts.num_out(i), ...
                opts.conv_stride(i), 0, 1) ;
net = add_norm(net, opts, '1') ;
net.layers{end+1} = struct('type', 'pool', 'name', 'pool1', ...
                           'method', 'max', ...
                           'pool', [3 3], ...
                           'stride', opts.pool_stride(i), ...
                           'pad', 0) ;

(11,11)是滤波器大小,pooling的stride为2,且只有前两层有pooling,卷积层stride除了第一层是2,其余都是1;
卷积层输入输出通道数表示如下,至于输出为什么是输入的两倍这是因为AlexNet网络是用2台GPU跑的,计算量分到了两台GPU上。

层数12345
输入348256192192
输出96256384384256

3. 构建CF层

在构建CF层之前作者加了加窗层,目的和传统一样,抑制边界效应。

join.addLayer('cf_window', MulConst(), ...
                  {'in1'}, {'cf_example'}, {'window'});

这里的MulConst函数是作者设计的,MulConst中前传反传的核心代码在mul_const函数中如下:

function varargout = mul_const(x, h, der_y)
% x is [m1, m2, p, b]
% h is [m1, m2]
% der_y is same size as x

if nargin < 3
    der_y = [];
end

if isempty(der_y)
    y = bsxfun(@times, x, h);
    varargout = {y};
else

else
    der_x = bsxfun(@times, der_y, h);
    der_h = sum(sum(der_y .* x, 3), 4);
    varargout = {der_x, der_h};
end

end

反传的时候的误差是用前一层的误差和窗点乘,再按通道和batch求和。
然后加入CF层

join.addLayer('cf', ...
                      CorrFilter('lambda', join_opts.lambda, ...
                                 'bias', join_opts.bias), ...
                      {'cf_example'}, cf_outputs, {'cf_target'});

看看具体CF函数是如何构建的,代码在corr_filter.m中,按照流程图前向代码如下:

y_f = fft2(y);
x_f = fft2(x);
k_f = 1/n*sum(conj(x_f).*x_f, 3) + opts.lambda;
a_f = 1/n*bsxfun(@times, y_f, 1./k_f);
w_f = bsxfun(@times, conj(a_f), x_f);
w = real(ifft2(w_f));

y是target高斯label预先定义好的,所以前向过程仅输入一个变量x,输出w,过程完全符合流程图。
那么反向传播如何呢,反传增加一个输入der_w,即w的误差,前传的前面4行都一样除了最后两行,看看代码如何写的

der_w_f = fft2(der_w);
der_a_f = sum(x_f .* conj(der_w_f), 3);
der_x_f = bsxfun(@times, a_f, der_w_f);
der_y_f = 1/n*sum(der_a_f .* conj(1 ./ k_f), 4);
der_y = real(ifft2(der_y_f));
der_y = real(ifft2(der_y_f));
der_x_f = der_x_f + 2/n*bsxfun(@times, real(der_k_f), x_f);
der_x = real(ifft2(der_x_f));

最后得到了需要的输入误差der_x和der_y,式子论文中给出了推导结果
在这里插入图片描述
至于如何推导的看看论文的补充材料应该可以理解。

4. 构建Crop层

CF层后紧接了一个Crop层还是为了缓解边界效应。来看看前向如何实现

x = inputs{1};
sz = size_min_ndims(x, 4);
p = obj.margin;
y = x(1+p:end-p, 1+p:end-p, :, :);
outputs = {y};

目的很简单,仅仅取出中间目标的部分。反向如下:

x = inputs{1};
dldy = derOutputs{1};
dldx = zeros(size(x), class(x));
p = obj.margin;
dldx(1+p:end-p, 1+p:end-p, :, :) = dldy;
derInputs = {dldx};

也很简单,就是把中间目标的误差直接作为输入误差,其他部分置0。

5. 构建相关层

这里已经是最后一层了,将上面crop得到的模板特征和搜索域上提取的特征作相关,即滑窗点乘,最后得到响应图,对应最大值的位置就是目标的相对位置了。相关层在代码中代号“xcorr”,来看看具体代码,先是前传部分

y = vl_nnconv(x, z, []);

可以看到核心就是直接用卷积操作,代码做了一些额外的处理,如有无bias的处理,这里就不贴了。反传的时候输入y的误差der_y,

[der_x_, der_z] = vl_nnconv(x_, z, [], der_y);

可以看到核心仍然是卷积层代码,只是把卷积核换成了模板特征x。

6. 实验结果

最后看看CFNet的实验结果
在这里插入图片描述
可以看到速度相比baseline有了不错的提升,因为CF层的引入提升了特征的判别力所以浅层就可以获得不错的精度。欢迎与我讨论~

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值