手把手带你复现ICCV 2017经典论文—PyraNet
過去幾年發表于各大 AI 頂會論文提出的 400 多種算法中,公開算法代碼的僅占 6%,其中三分之一的論文作者分享了測試數據,約 54% 的分享包含“偽代碼”。這是今年 AAAI 會議上一個嚴峻的報告。?人工智能這個蓬勃發展的領域正面臨著實驗重現的危機,就像實驗重現問題過去十年來一直困擾著心理學、醫學以及其他領域一樣。最根本的問題是研究人員通常不共享他們的源代碼。?
可驗證的知識是科學的基礎,它事關理解。隨著人工智能領域的發展,打破不可復現性將是必要的。為此,PaperWeekly 聯手百度 PaddlePaddle 共同發起了本次論文有獎復現,我們希望和來自學界、工業界的研究者一起接力,為 AI 行業帶來良性循環。
作者丨Dicint
學校丨北京信息科技大學
研究方向丨分割、推薦
Learning Feature Pyramids for Human Pose Estimation 是發表在 ICCV 2017 的工作,論文提出了一個新的特征金字塔模塊,在卷積網絡中學習特征金字塔,并修正了現有的網絡參數初始化方法,在人體姿態估計和圖像分類中都取得了很好的效果。
論文復現代碼:?
http://aistudio.baidu.com/?_=1540892031956#/projectdetail/29380
LSP數據集簡介
LSP && LSP_extended?
這個數據集是由 Flickr 上‘Volleyball’, ‘Badminton’, ‘Athletics’, ‘Baseball’, ‘Gymnastics’, ‘Parkour’, ‘Soccer’, ‘Tennis’(原數據集), ‘parkour’, ‘gymnastics’, and ‘athletics’ (擴展集)等標簽所構成。
每個圖片都由 Amazon Mechanical Turk 和類似的途徑標注而來,并不高度準確。這些圖片被縮放至每個人大約 150 px 長度進行標注,包含了 14 個節點。?
LSP 地址:
http://sam.johnson.io/research/lsp_dataset.zip?
LSP 樣本數:2000 個(全身,單人)?
LSP_extended 地址:
http://sam.johnson.io/research/lspet_dataset.zip?
LSP_extended 樣本數:10000 個(全身,單人)?
LSP && LSP_extended 共 12000 個標注,節點是以下 14 個:?
{1. Right ankle 2. Right knee 3. Right hip 4. Left hip 5. Left knee 6.Left ankle 7.Right wrist 8. Right elbow 9. Right shoulder 10. Left shoulder 11. Left elbow 12. Left wrist 13. Neck 14. Head top}?
由于是單人數據集,該數據集的訓練難度比多人數據集更簡單。
MPII數據集簡介
MPII 人體姿勢數據集是人體姿勢預估的一個 benchmark,數據集包括了超過 40k 人的 25000 張帶標注圖片,這些圖片是從 YouTube video 中抽取出來的。在測試集中還收錄了身體部位遮擋、3D 軀干、頭部方向的標注。?
MPII 地址:
http://human-pose.mpi-inf.mpg.de/#overview?
MPII 樣本數:25000 個(單人、多人)?
包括以下 16 類標注:?
{Head – 0, Neck – 1, Right Shoulder – 2, Right Elbow – 3, Right Wrist – 4, Left Shoulder – 5, Left Elbow – 6, Left Wrist – 7, Right Hip – 8, Right Knee – 9, Right Ankle – 10, Left Hip – 11, Left Knee – 12, Left Ankle – 13, Chest – 14, Background – 15}
數據集處理
MATLAB格式讀入
文件 joints.mat 是 MATLAB 數據格式,包含了一個以 x 坐標、y 坐標和一個表示關節可見性的二進制數字所構成的 3 x 14 x 10000 的矩陣。使用模塊 scipy.io 的函數 loadmat 和 savemat 可以實現對 mat 數據的讀寫。讀入后對原始標注進行轉置,轉置目的是分離每個圖片的標注。
import?numpy?as?np
data?=?sio.loadmat(self.lsp_anno_path[count])
joints?=?data['joints']
joints?=?np.transpose(joints,?(2,?1,?0))
JSON格式讀入?
MPII 數據集是以 JSON 格式進行的標注,可以通過 JSON 庫進行讀入。
anno?=?json.load(self.mpii_anno_pah)
將每個圖片打包成(圖片,標注,bounding box)的形式,bounding box 即圖片大小,其目的是將大小不一的圖片處理成 256 x 256 的大小。
for?idd,?joint_idd?in?enumerate(joints):
????image_name?=?"im%s.jpg"?%?str(idd?+?1).zfill(5)?if?count?else?"im%s.jpg"?%?str(idd?+?1).zfill(4)
????joint_id?=?idd?+?len(joints)?if?count?else?idd
????im_path?=?os.path.join(self.lsp_data_path[count],?image_name)
????im?=?Image.open(im_path)
????im?=?np.asarray(im)
????shape?=?im.shape
????bbox?=?[0,?0,?shape[1],?shape[0]]
????joint_dict[joint_id]?=?{'imgpath':?im_path,?'joints':?joint_idd,?'bbox':?bbox}
數據增強?
作者用到了幾種數據增強的手段:
縮放 scale?
旋轉 rotate?
翻轉 flip?
添加顏色噪聲 add color noise
縮放
讀入數據后,需要先把大小不一的標注圖片統一轉換成 256 x 256。
對于 LSP 測試集,作者使用的是圖像的中心作為身體的位置,并直接以圖像大小來衡量身體大小。數據集里的原圖片是大小不一的(原圖尺寸存在 bbox 里),一般采取 crop 的方法有好幾種,比如直接進行 crop,然后放大,這樣做很明顯會有丟失關節點的可能性。也可以先把圖片放在中間,然后將圖片縮放到目標尺寸范圍內原尺寸的可縮放的大小,然后四條邊還需要填充的距離,最后 resize 到應有大小。?
這里采用的是先擴展邊緣,然后放大圖片,再進行 crop,這樣做能夠保證圖片中心處理后依然在中心位置,且沒有關節因為 crop 而丟失。注意在處理圖片的同時需要對標注也進行處理。?
要注意 OpenCV 和 PIL 讀入的 RGB 順序是不一樣的,在使用不同庫進行處理時要轉換通道。
big_img?=?cv2.copyMakeBorder(img,?add,?add,?add,?add,?borderType?=?cv2.BORDER_CONSTANT,?value=self.pixel_means.reshape(-1))
#self.show(bimg)??
bbox?=?np.array(dic['bbox']).reshape(4,?).astype(np.float32)
bbox[:2]?+=?add
if?'joints'?in?dic:
????process(joints_anno)
objcenter?=?np.array([bbox[0]?+?bbox[2]?/?2.,?bbox[1]?+?bbox[3]?/?2.])
minx,?miny,?maxx,?maxy?=?compute(extend_border,?objcenter,?in_size,?out_size)
img?=?cv2.resize(big_img[min_y:?max_y,?min_x:?max_x,:],?(width,?height))
示例圖:?
▲?左:原圖,右:縮放后
示例圖的十四個標注點:?
(88.995834, 187.24898);(107.715065, 160.57408);(119.648575, 124.30561) (135.3259, 124.53958);(145.38748, 155.4263);(133.68799, 165.95587) (118.47862, 109.330215);(108.41703, 104.65042);(120.81852, 84.05927) (151.70525, 86.63316);(162.93677, 101.14057);(161.29883, 124.773575) (136.0279, 85.93119);(138.13379, 66.509995)
旋轉?
旋轉后點的坐標需要通過一個旋轉矩陣來確定,在網上的開源代碼中,作者使用了以下矩陣的變換矩陣圍繞著 (x,y) 進行任意角度的變換。
在 OpenCV 中可以使用:
newimg?=?cv2.warpAffine(img,?rotMat,?(width,?height))
得到轉換矩陣,并通過仿射變換得到旋轉后的圖像。而標注點可以直接通過旋轉矩陣獲得對應點。
add?=?np.array([rotMat[0][2],?rotMat[1][2]])
coor?=?np.dot(rot,?coor)?+?w
該部分代碼:
????angle?=?random.uniform(45,?135)
????rotMat?=?cv2.getRotationMatrix2D((center[0],?center[1])?,?angle,?1.0)
????newimg?=?cv2.warpAffine(img,?rotMat,?(width,?height))
????for?i?in?range(n):
????????x,?y?=?anno[i][0],?anno[i][1]
????????coor?=?np.array([x,?y])
????????rot?=?rotMat[:,?:?2]
????????add?=?np.array([rotMat[0][2],?rotMat[1][2]])
????????coor?=?np.dot(rot,?coor)?+?add
????????label.append((coor[0],?coor[1]))
????newimg?=?newimg.transpose(2,?0,?1)
????train_data[cnt++]?=?newimg
????train_label[cnt++]?=?np.array(label)
翻轉
使用 OpenCV 中的 flip 進行翻轉,并對標注點進行處理。在 OpenCV 中 flip 函數的參數有 1 水平翻轉、0 垂直翻轉、-1 水平垂直翻轉三種。
????'''對圖片進行翻轉'''
????newimg?=?cv2.flip(img,?1)
????train_data[counter]?=?newimg.transpose(2,?0,?1)
????'''處理標注點,symmetry是flip后所對應的標注,具體需要自己根據實際情況確定'''
????for?(l,?r)?in?symmetry:
????????cod[l],?cod[r]?=?cod[l],?cod[r]
????for?i?in?range(n):
????????label.append((cod[i][0],cod[i][1]))
????train_label[cnt++]?=?np.array(label)
添加顏色噪聲
我所采用的方法是直接添加 10% 高斯分布的顏色點作為噪聲。人為地損失部分通道信息也可以達到添加彩色噪聲的效果。
????noise_img?=?image?
????'''產生圖像大小10%的隨機點'''
????num?=?int(percentage*image.shape[0]*image.shape[1])
????'''添加噪聲'''
????for?i?in?range(num):?
????????x?=?np.random.randint(0,image.shape[0])?
????????y?=?np.random.randint(0,image.shape[1])?
????????for?j?in?range(3):
????????????noise_img[x,?y,?i]?=?noise_img[x,?y,?i]?+?random.gauss(2,4)
????????????noise_img[x,?y,?i]?=?255?if?noise_img[x,?y,?ch]?>?255?else?0
????return?noise_img
除此之外,以下數據增強的方法也很常見:
1. 從顏色上考慮,還可以做圖像亮度、飽和度、對比度變化、PCA Jittering(按照 RGB 三個顏色通道計算均值和標準差后在整個訓練集上計算協方差矩陣,進行特征分解,得到特征向量和特征值);?
2. 從圖像空間性質上考慮,還可以使用隨機裁剪、平移;
3. 從噪聲角度,高斯噪聲、椒鹽噪聲、模糊處理;?
4. 從類別分布的角度,可以采用 label shuffle、Supervised Data Augmentation(海康威視 ILSVRC 2016 的 report)。?
在這個具體例子中,進行數據增強的時候要考慮的是:1)形變會不會影響結果;2)會不會丟掉部分節點。
制作Paddle數據
使用 paddle.batch 批量讀入數據,并制作成 Paddle 的數據格式。
????????????????mode?=?'train'),?batch_size=1)
fluid.recordio_writer.convert_reader_to_recordio_file("./work/test_"??+?str(i)?+?"_test.recordio",?
????????????????feeder=feeder,?reader_creator=reader)?
其他數據相關內容
論文的評價標準
PCK:檢測的關鍵點與其對應的 groundtruth 之間的歸一化距離小于設定閾值的比例。在本篇論文中,作者將圖片中心作為身體的位置,并以圖片大小作為衡量身體尺寸的標準。?
PCK@0.2 on LSP && LSP-extended:以驅干直徑為歸一化標準。
PCKh@0.5 on MPII:以頭部為歸一化標準。
關于訓練的過擬合搶救
對于容易過擬合的數據,數據增強是比較重要的,訓練的時候學習率需要不能太大,當一次訓練過擬合后,可以從 loss 曲線波動的地方回溯到較為平穩的點,并以平穩點的學習率為起點,以更低的學習率接上上次學習。
關于PaddlePaddle
查了一下資料,PaddlePaddle 最早在 16 年就已經對外開放,然而可能因為本人入門做機器學習時間較晚有關,在復現活動之前,我只是聽過有一個開源深度學習平臺而不知道其名字。
從官方開源的一些 demo 項目來講,對推薦和文本處理方面的應用比較友好,搜索相關關鍵字也能獲得很多入門的博客、在不同環境的安裝指南,官方甚至還做了教學視頻。
據說當前版本的 Fluid 在編寫邏輯上和過去的版本已經有了很大的區別,在使用上直觀的感受是和 TensorFlow 有一定的相似性。
但由于不熟悉這個框架,也會遇到一些問題:一開始在 AI 開放平臺上找了半天沒找到文檔入口,在搜索引擎上才發現有另一個 paddlepaddle.org 的入口;當一些算子的名字和其他框架不太一樣的時候,不太容易從文檔里找到;不清楚不同版本之間的區別(能跑就行?);官網介紹對大規模計算友好、對可視化的支持均沒有體驗;Notebook 非常容易崩等問題等等……
盡管如此,在使用一定時間后,我覺得還是覺得挺方便的。這個框架的使用群體目前并不多,對大公司來講大家都有內部各自對 TensorFlow 的封裝性優化平臺,對入門者而言往往又不是那么首選.
從個人學習路徑來講,我覺得就 TensorFlow 和現在流行的 PyTorch 而言,前者是業界工程依賴程度高,后者是研究者使用方便,PaddlePaddle 需要有一個清晰的受眾定位和有效的推廣機制。
點擊標題查看更多論文解讀:?
經典論文復現 | 基于深度學習的圖像超分辨率重建
經典論文復現 | LSGAN:最小二乘生成對抗網絡
PyraNet:基于特征金字塔網絡的人體姿態估計
經典論文復現 | InfoGAN:一種無監督生成方法
經典論文復現 | ICML 2017大熱論文:Wasserstein GAN
#投 稿 通 道#
?讓你的論文被更多人看到?
如何才能讓更多的優質內容以更短路徑到達讀者群體,縮短讀者尋找優質內容的成本呢??答案就是:你不認識的人。
總有一些你不認識的人,知道你想知道的東西。PaperWeekly 或許可以成為一座橋梁,促使不同背景、不同方向的學者和學術靈感相互碰撞,迸發出更多的可能性。?
PaperWeekly 鼓勵高校實驗室或個人,在我們的平臺上分享各類優質內容,可以是最新論文解讀,也可以是學習心得或技術干貨。我們的目的只有一個,讓知識真正流動起來。
??來稿標準:
? 稿件確系個人原創作品,來稿需注明作者個人信息(姓名+學校/工作單位+學歷/職位+研究方向)?
? 如果文章并非首發,請在投稿時提醒并附上所有已發布鏈接?
? PaperWeekly 默認每篇文章都是首發,均會添加“原創”標志
? 投稿郵箱:
? 投稿郵箱:hr@paperweekly.site?
? 所有文章配圖,請單獨在附件中發送?
? 請留下即時聯系方式(微信或手機),以便我們在編輯發布時和作者溝通
?
現在,在「知乎」也能找到我們了
進入知乎首頁搜索「PaperWeekly」
點擊「關注」訂閱我們的專欄吧
關于PaperWeekly
PaperWeekly 是一個推薦、解讀、討論、報道人工智能前沿論文成果的學術平臺。如果你研究或從事 AI 領域,歡迎在公眾號后臺點擊「交流群」,小助手將把你帶入 PaperWeekly 的交流群里。
▽ 點擊 |?閱讀原文?| 收藏復現代碼
總結
以上是生活随笔為你收集整理的手把手带你复现ICCV 2017经典论文—PyraNet的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线下沙龙 × 报名 | “大规模数据存储
- 下一篇: UC Berkeley提出变分判别器瓶颈