1. 程式人生 > >【學習筆記4】Convolutional Pose Mashines在FashionAI中的應用【第二彈】——我的深度學習首秀(天池FashionAI關鍵點挑戰賽複賽篇)

【學習筆記4】Convolutional Pose Mashines在FashionAI中的應用【第二彈】——我的深度學習首秀(天池FashionAI關鍵點挑戰賽複賽篇)

又持續煉了一個月左右的丹,今天覆賽B階段結束了,老衲總算熬到頭了。和一群“仙人”競賽,既壓力山大,又動力滿滿。壓力山大是前排“大仙”令人驚歎的NE值以及Top20的誘人獎勵,畢竟自己曾經那麼接近Top20;動力滿滿是自己一直是抱著學習的態度把這次比賽當做一個實踐專案在做,能得到鍛鍊是我們一直做下去的動力,有差距,說明還有提升空間,這個差距激勵著我堅持著做了下去。最終我們以複賽B榜NE=4.45%的成績排名41/2322,較之初賽的NE=7.49%有了很大的提升,也算完成了初賽定下的保50的目標,排行榜上有名字還是好看一些的嘛(原諒我的虛榮心)。寫本文的目的旨在和大家分享一些複賽階段調CPM的一些細節,並開源我已證明有效的實驗程式碼。此外,也和大家分享一下自己的感受,自勉並共勉吧。

初賽階段其實就已經發現了CPM存在的一些問題,在我的上一篇博文中也已提及,之所以沒有放棄它一方面因為我們把初賽的多模型替換成單模型後效能有了質的提升,對CPM又重拾希望;另一方面是我們團隊大多抱著學習的態度去做這次比賽,CPM的單模型Baseline大約在NE=6%左右,我們就想挑戰一下baseline,就想看看在不使用檢測方法提供先驗constrain、不使用多checkpoint的ensemble、不與其他模型ensemble、只在原模型基礎上加入一些tricks的情況下,作為姿態估計經典模型的CPM究竟能達到什麼樣的效果。於是乎,我們便做了各種實驗,下文便說一些已經實現並證明有效的trick。

Trick 1——單模型和Weighted Loss

初賽階段我們想著已有的類別標籤是一個非常強的先驗條件,一個網路模型只預測單獨一個類別的衣服的關鍵點,網路不需要去考慮分類問題,任務更加簡單,因此訓了五個模型去分別預測五種衣服的關鍵點,最後初賽的NE=7.49%,效果不盡人意。複賽階段開始我們就決定拋棄所謂的類別標籤的先驗條件,配合weighted loss使用單模型去完成五種類別的衣服的關鍵點定位任務。全身所有點的加入使得網路可以學習到其他點的分佈、以及點與點之間的關聯性,對於CPM這類“腦補”能力極強的網路作用更為明顯,所以說全身所有點的加入給網路增加了更多的global資訊,使得網路不再關注區域性的某一種類別的衣服,從實驗結果來看,把褲腳預測成袖口等這類錯誤預測案例大大減少。

實現細節上就是feed進網路的ground truth的heatmap的channel數是24,也就是五種類別的衣服的所有關鍵點數,對於不存在的點,gt_heatmap所在的channel全為0。weighted loss即計算loss的時候,每個channel的loss乘上一個係數,存在點係數為1,不存在點為0,使得存在的點參與loss計算與梯度反傳,不存在的點loss為0,不產生梯度且不參與梯度反傳。實現程式碼如下:

# 計算每個stage的loss, weighted l2 loss
        for stage in range(self.stages):
            with tf.variable_scope('stage' + str(stage + 1) + '_loss'):
                self.stage_loss_batch = [0] * self.batch_size_np
                for batch in range(self.batch_size_np):
                    self.stage_loss_batch_hmindex = [0] * self.num_joints
                    for hmindex in range(self.num_joints):
                        self.stage_loss_batch_hmindex[hmindex] = tf.nn.l2_loss(self.stage_heatmap[stage][batch,:,:,hmindex] -
                                                      self.gt_heatmap[batch,:,:,hmindex]) * self.train_weights[batch][hmindex]
                    self.stage_loss_batch[batch] = tf.reduce_sum(self.stage_loss_batch_hmindex)
                self.stage_loss[stage] = tf.reduce_sum(self.stage_loss_batch) / self.batch_size

最終,通過train和warm_up_train兩個資料集訓練的單模型,在複賽A階段test_a上的NE=5.86%,較之多模型有了很大的提升,正是這個提升也讓我們重拾了對CPM的信心,也就有了後面的實驗。

Trick 2——FPN(特徵金字塔)

正如上一篇博文所說,CPM是沒有Hourglass那樣的多尺度特性的,初賽階段我們加入了尺度的增廣,通過資料預處理的方式使網路學習多尺度特性,但是這種做法治標不治本,我們需要極大的增加資料量,因此增加了訓練時間。還有一種主流的做法就是加入FPN。FPN的思想現在非常流行,很多領域都會用到,例如語義分割開山之作FCN、曠世今年的CVPR姿態估計大作CPN的RefineNet裡,都用到了多尺度特徵融合的思想。多尺度特徵融合就是將不同尺度上的特徵融合起來,形成更加全面的特徵。我們知道,網路的初始階段,圖片細節資訊豐富,網路關注細節資訊,像是衣服的花紋、衣服上的鈕釦等等;隨著網路pooling層的進行,feature map的scale在縮小,這也導致了圖片細節特徵的流失,這時候的網路關注global一些的資訊,例如衣服的輪廓等,而我們要做的是關鍵點定位,只有網路後端的衣服輪廓的特徵會因為細節特徵的流失影響精度,因此利用特徵金字塔思想,將不同pooling層後的不同scale的feature map疊加起來,為後端的feature map加入細節資訊,使得特徵圖同時具備細節和區域性的資訊,實現細節就是小scale的feature map上取樣和上一級大尺度的feature map相加,新的feature map再上取樣和更大尺度的feature map相加,以此類推。實現程式碼如下:

# FPN block
            C5 = sub_pool4  # channel = 256, size = 32
            C4 = sub_pool3  # channel = 256, size = 64
            C3 = sub_pool2  # channel = 128, size = 128

            C4_conv1 = C4

            # 1x1 conv change channels
            C3_conv1 = tf.layers.conv2d(inputs=C3,
                                        filters=256,
                                        kernel_size=[1, 1],
                                        strides=[1, 1],
                                        padding='same',
                                        kernel_initializer=tf.contrib.layers.xavier_initializer(),
                                        name='C3_conv1')    # channel = 256, size = 256
            P5 = C5     # channel = 256,size = 64

            # P5 2x
            W_P5 = self.create_variables(name="W_P5", shape=[3, 3, 256, 256])
            b_P5 = self.create_variables(name="b_P5", shape=[256])
            P5_2x = self.conv2d_transpose_strided(P5, W_P5, b_P5, tf.shape(C4_conv1))     # channel = 256, size = 64

            # P5 4x
            W_P5_4 = self.create_variables(name="W_P5_4", shape=[3, 3, 256, 256])
            b_P5_4 = self.create_variables(name="b_P5_4", shape=[256])
            P5_4x = self.conv2d_transpose_strided(P5_2x, W_P5_4, b_P5_4, tf.shape(C3_conv1))  # channel = 256, size = 128

            P4 = tf.add(C4_conv1, P5_2x, name="P4")      # channel = 256, size = 64
            # P4_2x
            W_P4 = self.create_variables(name="W_P4", shape=[3, 3, 256, 256])
            b_P4 = self.create_variables(name="b_P4", shape=[256])
            P4_2x = self.conv2d_transpose_strided(P4, W_P4, b_P4, tf.shape(C3_conv1))     # channel = 256, size = 128

            # P3
            P3 = tf.add(C3_conv1, P4_2x, name="P3")

            P_cat = tf.concat([P3, P4_2x, P5_4x], axis=3)       # channel = 256, size = 128

最後的P_cat便是多尺度特徵融合的特徵圖。實驗證明,通過train和warm_up_train兩個資料集訓練的加入FPN的單模型,在複賽A階段test_a上的NE=5.47%,效果還是很明顯的。

Trick 3——On-line Hard Key point Mining

線上難點挖掘這個trick也有很多網路都用到了,例如曠廠的CPN。確實對於一些案例有很多點很困難,比如說小姐姐在奇怪的場景中穿一樣奇怪的衣服再擺出奇怪的pose給預測增大了很多難度。判斷難點的依據無非是loss的大小。要知道我們在算loss的時候是所有channel的L2 loss相加,也就是所有點的loss都加到一起,這些點的loss有大有小,因此我們在算梯度反傳的時候是將這個加和的loss同等大小平均反傳給每一個點。換句話說,我們希望大的loss的點有大的梯度,小的loss的點有小的梯度,而加和平均這一操作使得所有點不管loss大小,梯度都是一樣的,因此大loss點的梯度不夠,不足以抵消偏差,所以loss大的點預測上依然存在問題。因此和CPN一樣,我們考慮加入線上難點挖掘。當初加這個trick的時候還沒有關注到曠廠的CPN,最後寫出來發現和CPN的實現幾乎一個意思,很是欣慰。

線上難點挖掘現在主流有hard和soft的兩種方法,hard方法就是對loss進行排序,取loss的前幾個相加取平均,這樣就拉大了loss的平均值,反傳的梯度也就加大了;soft方法就是利用softmax等對loss進行一個權值加權,大的loss權值大,小的loss權值小,這樣回傳的梯度也是大的loss梯度大,小的loss梯度小。我們和CPN一樣採用的是hard方法,對loss進行排序,取前一半計算loss參與梯度計算和回傳。實現程式碼如下:

# 計算每個stage的loss, weighted l2 loss, online hard keypoint mining
        for stage in range(self.stages):
            with tf.variable_scope('stage' + str(stage + 1) + '_loss'):
                self.stage_loss_batch = [0] * self.batch_size_np
                for batch in range(self.batch_size_np):
                    self.stage_loss_batch_hmindex = [0] * self.num_joints
                    for hmindex in range(self.num_joints):
                        self.stage_loss_batch_hmindex[hmindex] = tf.nn.l2_loss(self.stage_heatmap[stage][batch, :, :, hmindex] -
                                                      self.gt_heatmap[batch, :, :, hmindex]) * self.loss_weights[batch][hmindex]
                    '''
                        find index of top half loss
                    '''
                    # calculate the num of joints that is trained of each batch
                    num_visible_and_invisible = tf.reduce_sum(self.loss_weights[batch])
                    num_loss = num_invisible_and_visible
                    top_k = num_loss / 2
                    # consider top half of the joints are hard joints
                    max_loss_index_tensor = tf.nn.top_k(self.stage_loss_batch_hmindex, tf.cast(top_k, dtype=tf.int32))[1]
                    max_stage_loss_batch_hmindex = tf.gather(self.stage_loss_batch_hmindex, max_loss_index_tensor)
                    self.stage_loss_batch[batch] = tf.reduce_sum(max_stage_loss_batch_hmindex) / top_k * num_loss

                self.stage_loss[stage] = tf.reduce_sum(self.stage_loss_batch) / self.batch_size

實驗證明,通過train和warm_up_train兩個資料集訓練的單模型,在複賽A階段test_a上提升了0.1%,效果有,但是作用小,小到我都不敢肯定到底是難點挖掘帶來的提升還是fine tune帶來的提升。這一點,我也會繼續在別的模型上驗證,soft方法我也會繼續嘗試。

Trick 4——Test with augmentation

這一點其實也是慚愧,這個trick其實是就為了提升結果而採取的投機取巧的方法,就是在test的時候也給輸入圖片加上旋轉和尺度上的增廣,我是做了9次增廣的,加上原圖,相當於一張圖片在不同旋轉和尺度上預測10次,再把預測的heatmap相加,再提座標。說實在的,其實這就是一個只用一套模型引數的ensemble方法,有點違揹我前面提到的不用ensemble、只挑戰baseline的初衷,但這種ensemble又確實沒有和別的模型融合,也沒有多套引數融合,也不算很大地違背吧。在這裡提到這個trick也就是想說明這是一個很有效的比賽技巧,但是實際應用肯定不行,因為它降低了test的速度,是以時間為代價來換取精度的。比賽中也有很多其他隊伍用了這個小技巧,令我驚訝的是,他們的提升竟然比我小這麼多。他們採用這個方法提升大概在0.2%~0.3%左右,我的竟然提升了有0.6%。其實這也從另外一個角度反映了CPM並沒有很好地解決FashionAI這個資料集的問題,模型並沒有很好的擬合所有情景,一張圖片需要通過加入增廣來儘可能多地模擬所有情景再ensemble才能得到更高的精度。怎麼說呢,欲哭無淚吧。實現起來其實也就多一個數據增廣環節,程式碼如下:

# 讀關鍵點資訊
name = valid_set[valid_iter]
# 讀圖片
img = open_img(name)

# get dress type to determine the extract index
name_split = name.split('/')
dress_type = name_split[1]
if dress_type == 'blouse':
    index = Valid_FLAGS.blouse_index
elif dress_type == 'dress':
    index = Valid_FLAGS.dress_index
elif dress_type == 'outwear':
    index = Valid_FLAGS.outwear_index
elif dress_type == 'skirt':
    index = Valid_FLAGS.skirt_index
else:
    index = Valid_FLAGS.trousers_index

valid_img[0] = img
center_map = make_gaussian(height=512, width=512, sigma=150, center=None)
center_map = np.asarray(center_map)
center_map = center_map.reshape(512, 512, 1)
valid_centermap[0] = center_map

compress_ratio = np.ones((10), dtype=np.float32)
r_angle = np.zeros((10), dtype=np.float32)
cnt = 1
# test augmentation
while cnt < aug_num:
    # img2 = color_augment(img)
    img2, compress_ratio[cnt] = size_augment(img)
    img2, r_angle[cnt] = rotate_augment(img2)
    valid_img[cnt] = img2
    valid_centermap[cnt] = center_map
    cnt += 1

說到在test上加增廣,我還用了一個小技巧。我發現一些衣服在圖片中面積佔比特別小的話,很多點的預測就不是很準,而且heatmap上的高斯點全都疊加到一起去了,在提標籤的時候又會進一步造成誤差。為了解決這個問題,我根據預測的標籤結果計算衣服站圖片的比例,如果圖片比例小於7%,我就在test增廣的時候增大尺度放大區間,讓圖片有機會放大地更大參與預測。這一個trick又獲得了0.02%的微弱提升。

其它Tricks

  • 減小ground truth的heatmap上的高斯點的sigma的值,這個值越小表明,輻射坡度越陡,根據標籤座標打上的高斯點越小。適當減小高斯點的大小可以有效地解決前文提到的衣服佔比小時預測的高斯點疊加的問題,進一步提高提標籤的精度。

  • 在條件允許的情況下,圖片越大越好,大圖可以提供更多的資訊,但是也增大了計算量。在單卡的情況下用大圖可能只能用很小的batch size了。

  • 加入更多的資料集。資料集多了,提供的情景模式多了,當然會提高網路效能。最後我們加入初賽的測試集訓練,基本上是一個數據集0.15個點的提升。

  • 這是程式碼執行效率上的問題:少用for迴圈,能用內嵌函式的就用函式解決,我是吃了很多時間上的虧了,因為程式碼執行慢確實浪費了不少時間。

  • 其他理論有效但仍在實驗的tricks。

煉丹感悟

  • 第一條感悟就是沒卡真心不能活,比賽後期一個人用五張卡依然覺得不夠用。深度學習真是門吃硬體資源的學問。

  • 第二條感悟就是熬人,做深度學習真的是要有耐心,尤其越往後做越煎熬,基本上一個trick的實驗就算fine tune也得等個一天吧,也許等個一天效果還不一定好,而且效果不好肯定是佔大多數的。我不覺得這是徒勞,因為實驗的過程中我一方面鍛鍊了程式碼能力,另一方面也通過實驗感受trick在cpm中的作用。

  • 第三條感悟就是收穫頗豐。雖然最後沒有進入Top20拿到獎勵,但是通過這兩個月的學習,我得到了很大的鍛鍊。第一我也算了解了絕大部分姿態估計、關鍵點檢測中的主流做法,有很多關鍵trick都通過程式碼實現在cpm上感受過,理論功底得到了很大程度的提升;另外一方面程式碼能力也得到了很大的鍛鍊,比賽後期幾乎都是羅老師提方法,我們程式碼實現,我很享受這個鍛鍊過程,此外通過徹徹底底地調通一個模型對我今後實現別的模型都具有參考意義。自己科研的任務中寫程式碼也輕鬆了很多,和別人的科研交流中也開始可以為別人提出建設性意見。總之,很感謝這個過程給我帶來的鍛鍊。

  • 第四條感悟就是任重而道不遠。自己一直都想學更多東西,不僅是傳統機器學習,還有深度學習,深入學習以後才發現面是在太廣,全部掌握完全不可能。我自己又是一個對自己要求特別高的人,別人知道的理論我不知道,我就想去學,別人的程式碼寫的我看都看不懂,我就很難受。自己還有很多要提高的地方,明年這個時候估計就在實習了吧,所以說任重而道不遠,要學的還有很多,但是留給自己的時間只有一年了。

  • 最後就是我們確實受CPM模型限制了,確實也只能達到這樣的效果了,可能和前排大仙只差一個檢測模型的差距,這誰又知道呢~~~後面還有一些沒做完的非常fancy的實驗,我會接著繼續做下去,也會加入檢測,也會嘗試cpn,hg這樣更先進的模型繼續探索這一方向,只是不再這麼趕這麼熬人了。

就寫這麼多了,本文中存在的問題,歡迎大家指正。歡迎大家和我交流,歡迎這次比賽的大仙們看到了給我提提意見,你們是真的牛,我是服氣的。

-------------------------------------------

Youzhi Gu, master student

Foresight Control Center

College of Control Science & Engineering

Zhejiang University

Email: [email protected]

相關推薦

學習筆記4Convolutional Pose Mashines在FashionAI應用第二——深度學習天池FashionAI關鍵點挑戰賽複賽

又持續煉了一個月左右的丹,今天覆賽B階段結束了,老衲總算熬到頭了。和一群“仙人”競賽,既壓力山大,又動力滿滿。壓力山大是前排“大仙”令人驚歎的NE值以及Top20的誘人獎勵,畢竟自己曾經那麼接近Top20;動力滿滿是自己一直是抱著學習的態度把這次比賽當做一個實踐專案在做,能得

Docker學習筆記4: Docker-Compose—簡化複雜容器應用的利器

[[email protected] dockerfile_dir]# docker-compose up Building web Step 1 : FROM python:2.7 ---> 77cf0ea98df6 Step 2 : ADD . /code ---> 75cd86

Java學習筆記4. 在Linux Fedora 27安裝Eclipse並編譯第一個源程式

1. 在Linux Fedora中安裝Eclipse比較簡單,直接在bash裡輸入:# sudo dnf install -y eclipse2. 然後等待完成,完成後輸入eclipse啟動程式,現在的版本是Oxyen23. 啟動程式後首先選擇工作目錄,這裡預設不動,單擊 L

學習筆記3Convolutional Pose Mashines在FashionAI應用——深度學習

春學期開學至今這一個多月裡,除了有序推進自己的科研任務,非專業的我還和協會志同道合的小夥伴跟著羅老師以參加比賽的方式在深度學習計算機視覺領域裡進行實戰演練。我們參加的是天池FashionAI服飾關鍵點定位全球挑戰賽,今天(4月21日)是初賽截止的日子,憑藉有限的精力和資源我們

幹貨JavaScript DOM編程藝術學習筆記4-6

ext 屬性節點 另一個 機器 rep lin bsp 每次 增加 四、案例研究:JavaScript圖片庫 js: function showPic(whichpic){ //取得鏈接 var source=whichpic.getAttribute("hr

ArcGIS API for JavaScript3.x 學習筆記[4] 加載底圖Open Street Map開放街道地圖

asc 裏的 指定 訪問 utf-8 gis sca utf 同方 Open Street Map OpenStreetMap(簡稱OSM,中文是開放街道地圖)是一個網上地圖協作計劃,目標是創造一個內容自由且能讓所有人編輯的世界地圖。 OSM是一款由網絡大眾共同打造的免費開

Python學習筆記4-內置函數

打印數字 筆記 轉換 查看 判斷 lis clas 函數 ted 1、內置函數 1 print(all([1,2,3,0])) #判斷可叠代對象裏面是否都為真:非零即真非空即真原則 2 print(any([1,2,3,0])) #判斷可叠代對象裏面是否有一個為真:非

Python學習筆記4-time、md5、加密base64模塊

數據庫 sta 自己的 pri 時間 decode 不可 字符 ftime 1、time # 1、格式化好的時間 2018-1-14 16:42# 2、時間戳 是從unix元年到現在所有的秒數# 3、時間元組# 想時間戳和格式化好的時間互相轉換的話,都要先轉成時間元組,然後

Python學習筆記4-os、sys模塊

etc 創建 記錄 body spa platform pri eas usr 1、os操作系統模塊 1 import os 2 print(os.getcwd()) #獲取當前工作目錄 3 # print(os.chdir("..")) #更改當前目錄.當前目錄

無人機 學習筆記 4GPS與RTK技術

什麼是RTK技術    常規的GPS測量方法,如靜態、快速靜態、動態測量都需要事後進行解算才能獲得釐米級的精度,而RTK是能夠在野外實時得到釐米級定位精度的測量方法,它採用了載波相位動態實時差分(Real - time kinematic)方法,是GPS應用的重大里程碑,它的出現為工程放樣、

託業新託業TOEIC新題型真題學習筆記4-題庫一->P7

executive suite 高階套房;商務套房 reimbursement n. 報銷;償還 reimburse vt. 償還;賠償  retain vt. 保持;僱;記住 remain n. 遺蹟;剩餘物,殘骸 vi. 保持;依然;留下;剩餘;逗留;殘存 ensemble 重唱

DM8168學習筆記4ezsdk安裝過程記錄

[email protected]:~$ sudo '/usr/local/ezsdk/setup.sh'  [sudo] password for eagle:  -----------------------------------------------------------------

學習筆記pyQt5學習筆記(4——第一個影象識別demoV3.0B

 軟體更新日誌V3.0B版本,在2.0基礎上實現了呼叫攝像頭的實時識別功能。V3.0A版若不實時重新整理識別畫面,可以手動點一次識別一次…… 附上完整程式碼,也是做個程式碼備份。需要的小夥伴自取就ok~ 軟體目前存在的bug是1.在開始識別後會很卡,目前認為可能的原因是每次重新整理

機器學習-斯坦福學習筆記4 ——牛頓方法;指數分佈族; 廣義線性模型GLM

牛頓方法 本次課程大綱: 1、  牛頓方法:對Logistic模型進行擬合 2、 指數分佈族 3、  廣義線性模型(GLM):聯絡Logistic迴歸和最小二乘模型 複習: Logistic迴歸:分類演算法 假設給定x以為引數的y=1和y=0的概率:

Zynq學習筆記4問題彙總

問題1:debug發現硬體介面各種異常,例如uart無法傳送資料等。 解決辦法:Debug Configuration中沒有勾選“Run ps7_init”和“Run ps7_post_config” 問題2:cannot find -lxil 解決辦法:找不到lib ,更

Zynq-Linux移植學習筆記之27UIO機制響應外部中斷實現

轉自:https://blog.csdn.net/zhaoxinfan/article/details/80285150 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/jj12345jj198999/article/details/802851501、&n

[學習筆記]Segment Tree Beats!九老師線段樹 bzoj4695最假女選手

對於這樣一類問題: 區間取min,區間求和。 N<=100000 要求O(nlogn)級別的演算法   直觀體會一下,區間取min,還要維護區間和 增加的長度很不好求。。。。   然鵝, 從前有一個來自杭州天水幼兒園的julao叫九條可憐 他發明了一個線段樹的寫法

論文筆記4深入理解行人重識別網路的Loss

打完天池比賽後,可能由於長時間的持續輸出,精神上有些疲憊感,於是選擇去幹一些不是很費腦力的活兒,比如繼續充充電,看些論文補充一些理論知識。這兩天看了幾篇羅老師部落格裡總結的Person Re-Identification這塊的論文,包括羅老師自己發的兩篇論文。幾篇論文中都用到

Robot Operating System (ROS)學習筆記4---語音控制

sla 語音 出現 tput http 學習 process 輸入 ubun 搭建環境:XMWare Ubuntu14.04 ROS(indigo) 轉載自古月居 轉載連接:http://www.guyuehome.com/260 一、語音識別包 1、安裝

ESP8266學習筆記4:ESP8266的SmartConfig

rtc 訂閱號 new 例程 detail smart ted tracking 不能 今天花了將近一天的時間來研究ESP8266的SmartConfig功能,這個應該算是wifi雲產品的標配。這篇文章先把SmartConfig操作一遍,我還寫了還有一篇文章梳理了物理層