基於paddlepaddle復現crowdnet的人流密度檢測

前言(AIstudio學習過程):

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

實現這個網絡的契機是我參加了aistudio的訓練營。那時候才學習深度學習不久,之前一直在啃動手深度學習mxnet版本的原理知識,雖然提供了代碼,可是自己這檯筆記本實在勝任不了深度學習的任務,經常爆顯存。紙上得來終覺淺,絕知此事要Code。所以像找點資源跑一下代碼,其實之前就知道了aistudio,但是隻能使用paddle這個框架,需要一定的學習成本,順手網上一搜。。

主要原因是,

1.像tf,pytorch之類出現的比較早,使用羣體比較廣。paddle發展的晚,一開始必然會被大家拿來和各框架對比,我搜了一下早期的評價確實不高,容易勸退新手。

2.paddle社區活躍度不高,對於新手來說開始出現很多問題都不一定能通過搜索引擎找到問題(這個問題在我學paddle時有深刻體會,雖然有官方答疑qq羣,但是及時性和準確性方面則比較低,畢竟程序員還是比較習慣用搜索引擎一些),所以一直不想去嘗試。

作爲新手各種百度知乎問哪裏有免費資源,最後找到的要麼翻牆要麼自己出錢。後來思來想去,天下哪有免費的午餐,雖然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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章