基于paddlepaddle复现crowdnet的人流密度检测

https://aistudio.baidu.com/aistudio/projectdetail/402118

前言(AIstudio学习过程):

不想看的直接跳下面正文!

实现这个网络的契机是我参加了aistudio的训练营。那时候才学习深度学习不久,之前一直在啃动手深度学习mxnet版本的原理知识,虽然提供了代码,可是自己这台笔记本实在胜任不了深度学习的任务,经常爆显存。纸上得来终觉浅,绝知此事要Code。所以像找点资源跑一下代码,其实之前就知道了aistudio,但是只能使用paddle这个框架,需要一定的学习成本,顺手网上一搜。。

主要原因是,

1.像tf,pytorch之类出现的比较早,使用群体比较广。paddle发展的晚,一开始必然会被大家拿来和各框架对比,我搜了一下早期的评价确实不高,容易劝退新手。

2.paddle社区活跃度不高,对于新手来说开始出现很多问题都不一定能通过搜索引擎找到问题(这个问题在我学paddle时有深刻体会,虽然有官方答疑qq群,但是及时性和准确性方面则比较低,毕竟程序员还是比较习惯用搜索引擎一些),所以一直不想去尝试。

作为新手各种百度知乎问哪里有免费资源,最后找到的要么FQ要么自己出钱。后来思来想去,天下哪有免费的午餐,虽然paddle现在比较小众暂时了解的人不多,但是总归依靠百度这个大平台,还是挺可靠的。毕竟资源免费啊,真香!学**的!花了将近1个星期看官方文档,都是看的静态图。开始都不知道有动静态图这个概念,通过看别人的项目和官方文档,最终自己动手封装了一个模型训练类,实现了Lenet,VGG,alexnet,NIN(这个不知道为啥一直不能收敛)。这个过程真的难受,代码一开始封装起来就出现很多问题,开始不知道怎么解决,百度又找不到问题,就去问qq群的大佬们,虽然还是很热心,但是答案基本37开的命中率吧,大部分都是自己一行一行的调试比对别人的项目,查api解决的。

某天晚上我给我导师写了学习汇报,提到了aistuido,然后我导师居然推荐我看这个aistudio的7天的训练营(真不是广告!),惊了!不过当时真的不想折腾去学这个东西。好吧,既然导师说了就去看一下,然后,卧槽,这代码和我的咋不一样,这是啥dygraph,我没学过,赶紧去看下原来叫动态图,咋这么复杂啊!比静态图还绕一些。后来想了想,反正也是学习,干脆直接就进这个训练营了,跟着课程做作业,作业都有baseline了需要自己实现关键网络,其实大部分都没啥问题,比如VGG这种,已经在静态图实现过了,就是paddle动态图一些使用不太会,得自己一个个去学习,最后算是勉强都通过了。 直到开始了人流密度检测的比赛,才真正让我体会到了深度学习的深度。。。以前我只知道vgg resnet googlenet啥的,最多每个领域就用那么几个经典模型,以为就这些网络用来用去,直到我开始比赛时,读baseline代码翻到:密度图?百度一下:人流密度密度图。真正打开了我的新世界,密度图->crowdnet->论文->原理->代码->CVPR->2016->2017->2018->2019->dilated convolution。。。。,这期间我都阅读了10-20篇博客+2篇论文(略读)+2-3个版本的github源码,纵观时间线,就这一个领域的研究都达到了很深的深度, 真的让我了解了深度学习是如何研究的,真正做出某个领域的成就需要非常大的知识储备,也让我学到了一个领域的前沿知识。

总结:这次课程真的让我学到了很多知识,深度学习代码编写,模型训练,网络复现,调参,paddlehub,参加比赛等等。作为一个初入深度学习的新手真的收获颇丰。学完之后,初步接触的感受是现在版本的paddle体验挺好的,而且paddle还在一直迭代维护,对于我这个新手来说经历完前期的小问题后使用paddle很顺畅了,后续继续使用,慢慢看后期表现如何吧!关键是这期间所有课程,资源,问答都是免费的!!感激之情,无法言表!

正文:

这篇文章只讲实现网络结构不讲具体原理,详情请看论文。新手第一次复现论文的网络,如有问题还请指正。

crowdnet:

https://arxiv.org/pdf/1608.06197.pdf

我复制出来代码的baseline: https://aistudio.baidu.com/aistudio/projectdetail/402118

这个baseline 是aistuido提供的,是私密的,进 aistudio => 深度学习7日入门-CV疫情特辑 => 课节=》课节06=>paddlehub体验=>06人流密度检测-基础版。里面还有一个训练营的比赛,也可以去试试。
https://aistudio.baidu.com/aistudio/education/group/info/1149

这个代码把数据集的配置读取和预处理都做好了,还包括关键的高斯密度图的生成代码!

一、CrowdNet
网络结构:


输入数据是对应尺寸的图像,输出和label都是密度图。

DeepNetwork:

原文说了借鉴了的VGG16的结构,说明一下上面的网络结构图 如:2 conv 3X3(64)表示:2层卷积,卷积的大小3*3,输出通道为64,另外卷积层的padding=1,stride=1,这样设置的效果是:不会改变输入图的大小,如:输入图的大小为X*Y, (X+2-3)/1+1计算得到还是X,尺寸不会变化。Pool层是 2*2 ,stride=2,padding=0,使用MAX最大化池化层,这样正好将图片缩小一半,为原来的1/2,(X+0-2)/2+1=X/2 。

这里和VGG不同的是,移除了第五层所有卷积和pool,修改了第四个pool:卷积大小为3*3,stride=1,padding=1,这样不会改变大小,最后得到的特征图大小为原图大小的1/8.

Shallow Network:

同理,这个网络层经过3个AVG池化层的pool缩小为原来的1/8,卷积层使用的是 24通道,卷积尺寸:5*5,仅1个卷积层,它的stride=1,那么padding=2; Pool层是 padding=0,stride=2,size=2*2。

CONCAT:

将这两个网络的输出,在通道维上连接起来,如:5*20*80*60 ---- 5*30*80*60=> 5*50*80*60

再经过一个 1x1size, padding=0,stride=1,输出通道也为1的卷积层,目的是减少通道数,这样最终合并成一个 比如:

变成1*80*60的特征层。

INTERP:

其实就是一个上采样,使用双线性插值将图片放大到label的密度图大小。如果label的大小和这里输出大小一样,这一步就没必要再使用了。

LOSS:损失函数可以看到使用的l2 loss均方误差计算。

查阅baseline的代码可以知道,对密度图求和就可以得到具体的人数。

论文后面还提到了数据增广:(这一步我没有做)

大意是将数据尺寸缩放为原图的0.5-1.2倍,形成特征 ‘金字塔结构’见下图,在这个金字塔图中裁剪出人流重叠50%以上的区域,每个区域的大小为225*225.

代码实现:
class ConvBNLayer(fluid.dygraph.Layer):
def __init__(self,
num_channels,
num_filters,
filter_size,
stride=1,
groups=1,
padding=0,
act=None):
"""
name_scope, 模块的名字
num_channels, 卷积层的输入通道数
num_filters, 卷积层的输出通道数
stride, 卷积层的步幅
groups, 分组卷积的组数,默认groups=1不使用分组卷积
act, 激活函数类型,默认act=None不使用激活函数
"""
super(ConvBNLayer, self).__init__()

# 创建卷积层
self._conv = fluid.dygraph.Conv2D(
num_channels=num_channels,
num_filters=num_filters,
filter_size=filter_size,
stride=stride,
padding=padding,
groups=groups,
act=None,
bias_attr=False)

# 创建BatchNorm层
self._batch_norm =fluid.dygraph.BatchNorm(num_filters, act=act)

def forward(self, inputs):
y = self._conv(inputs)
y = self._batch_norm(y)
return y

class crowdnet(fluid.dygraph.Layer):
'''
网络
crowdnet
'''
def __init__(self):
super(crowdnet, self).__init__()
#定义deepnetwork
self.conv01_1 = ConvBNLayer(num_channels=3, num_filters=64,filter_size=3,padding=1,act="relu")
self.conv01_2 = ConvBNLayer(num_channels=64, num_filters=64,filter_size=3,padding=1,act="relu")

self.pool01=fluid.dygraph.Pool2D(pool_size=2,pool_type='max',pool_stride=2,pool_padding=0)

self.conv02_1 = ConvBNLayer(num_channels=64, num_filters=128,filter_size=3, padding=1,act="relu")
self.conv02_2 = ConvBNLayer(num_channels=128, num_filters=128,filter_size=3, padding=1,act="relu")
self.pool02=fluid.dygraph.Pool2D(pool_size=2,pool_type='max',pool_stride=2,pool_padding=0)

self.conv03_1 = ConvBNLayer(num_channels=128, num_filters=256,filter_size=3, padding=1,act="relu")
self.conv03_2 = ConvBNLayer(num_channels=256, num_filters=256,filter_size=3, padding=1,act="relu")
self.conv03_3 = ConvBNLayer(num_channels=256, num_filters=256,filter_size=3, padding=1,act="relu")
self.pool03=fluid.dygraph.Pool2D(pool_size=2,pool_type='max',pool_stride=2,pool_padding=0)
#(x-2/2+1)
self.conv04_1 = ConvBNLayer(num_channels=256, num_filters=512,filter_size=3, padding=1,act="relu")
self.conv04_2 = ConvBNLayer(num_channels=512, num_filters=512,filter_size=3, padding=1,act="relu")
self.conv04_3 = ConvBNLayer(num_channels=512, num_filters=512,filter_size=3, padding=1,act="relu")
self.pool04=fluid.dygraph.Pool2D(pool_size=3,pool_type='max',pool_stride=1,pool_padding=1)
#(x-3+2)/1 + 1= x
self.conv05_1 = ConvBNLayer(num_channels=512, num_filters=512,filter_size=3,padding=1, act="relu")
self.conv05_2 = ConvBNLayer(num_channels=512, num_filters=512,filter_size=3,padding=1, act="relu")
self.conv05_3 = ConvBNLayer(num_channels=512, num_filters=512,filter_size=3,padding=1, act="relu")
#n*512*80*60

#shallow network
self.conv_s_1=ConvBNLayer(num_channels=3,num_filters=24,filter_size=5,padding=2,stride=1,act='relu')
self.pool_s_1=fluid.dygraph.Pool2D(pool_size=2,pool_type='avg',pool_stride=2,pool_padding=0)

self.conv_s_2=ConvBNLayer(num_channels=24,num_filters=24,filter_size=5,padding=2,stride=1,act='relu')
self.pool_s_2=fluid.dygraph.Pool2D(pool_size=2,pool_type='avg',pool_stride=2,pool_padding=0)

self.conv_s_3=ConvBNLayer(num_channels=24,num_filters=24,filter_size=5,padding=2,stride=1,act='relu')
self.pool_s_3=fluid.dygraph.Pool2D(pool_size=2,pool_type='avg',pool_stride=2,pool_padding=0)
#n*24*80*60

#通道维连接
#input:512+24 *80*60
self.connect_conv=ConvBNLayer(num_channels=512+24,num_filters=1,filter_size=1,padding=0,stride=1)
#output:1*80*60
#上采样,双线性插值生成label密度图的大小的图

#最后使用L2 loss计算损失
def forward(self, inputs, label=None):
"""前向计算"""
#deep net
out = self.conv01_1(inputs)
out = self.conv01_2(out)
out = self.pool01(out)

out = self.conv02_1(out)
out = self.conv02_2(out)
out = self.pool02(out)

out = self.conv03_1(out)
out = self.conv03_2(out)
out = self.conv03_3(out)
out = self.pool03(out)

out = self.conv04_1(out)
out = self.conv04_2(out)
out = self.conv04_3(out)
out = self.pool04(out)
out = self.conv05_1(out)
out = self.conv05_2(out)
out = self.conv05_3(out)

#shadow net
out1 = self.conv_s_1(inputs)
out1 = self.pool_s_1(out1)

out1 = self.conv_s_2(out1)
out1 = self.pool_s_2(out1)

out1 = self.conv_s_3(out1)
out1 = self.pool_s_3(out1)

#concatenate
#按通道维连接
conn=fluid.layers.concat(input=[out,out1],axis=1)
result=self.connect_conv(conn)
return result

参考博客:

https://www.jianshu.com/p/a1006c4b6fdc

https://blog.csdn.net/linolzhang/article/details/78838320?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2
————————————————
版权声明:本文为CSDN博主「5jerry」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shenkunchang1877/article/details/105360963

原文地址:https://www.cnblogs.com/shuimuqingyang/p/15245906.html