過去幾年發表於各大 AI 頂會論文提出的 400 多種算法中,公開算法代碼的僅佔 6%,其中三分之一的論文做者分享了測試數據,約 54% 的分享包含「僞代碼」。這是今年 AAAI 會議上一個嚴峻的報告。 人工智能這個蓬勃發展的領域正面臨着實驗重現的危機,就像實驗重現問題過去十年來一直困擾着心理學、醫學以及其餘領域同樣。最根本的問題是研究人員一般不共享他們的源代碼。 算法
可驗證的知識是科學的基礎,它事關理解。隨着人工智能領域的發展,打破不可復現性將是必要的。爲此,PaperWeekly 聯手百度 PaddlePaddle 共同發起了本次論文有獎復現,咱們但願和來自學界、工業界的研究者一塊兒接力,爲 AI 行業帶來良性循環。json
做者丨Dicint網絡
學校丨北京信息科技大學app
研究方向丨分割、推薦dom
下載安裝命令 ## CPU版本安裝命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle ## GPU版本安裝命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu
Learning Feature Pyramids for Human Pose Estimation 是發表在 ICCV 2017 的工做,論文提出了一個新的特徵金字塔模塊,在卷積網絡中學習特徵金字塔,並修正了現有的網絡參數初始化方法,在人體姿態估計和圖像分類中都取得了很好的效果。ide
論文復現代碼: 函數
http://aistudio.baidu.com/?_=1540892031956#/projectdetail/29380學習
LSP數據集簡介
LSP && LSP_extended 測試
這個數據集是由 Flickr 上‘Volleyball’, ‘Badminton’, ‘Athletics’, ‘Baseball’, ‘Gymnastics’, ‘Parkour’, ‘Soccer’, ‘Tennis’(原數據集), ‘parkour’, ‘gymnastics’, and ‘athletics’ (擴展集)等標籤所構成。ui
每一個圖片都由 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 scipy.io as sioimport numpy as npdata = sio.loadmat(self.lsp_anno_path[count])joints = data['joints']joints = np.transpose(joints, (2, 1, 0))as sio 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 庫進行讀入。
import jsonanno = json.load(self.mpii_anno_pah) anno = json.load(self.mpii_anno_pah)
將每一個圖片打包成(圖片,標註,bounding box)的形式,bounding box 即圖片大小,其目的是將大小不一的圖片處理成 256 x 256 的大小。
from PIL import Imagefor 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}import Image 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}
數據加強
做者用到了幾種數據加強的手段:
縮放
讀入數據後,須要先把大小不一的標註圖片統一轉換成 256 x 256。
對於 LSP 測試集,做者使用的是圖像的中心做爲身體的位置,並直接以圖像大小來衡量身體大小。數據集裏的原圖片是大小不一的(原圖尺寸存在 bbox 裏),通常採起 crop 的方法有好幾種,好比直接進行 crop,而後放大,這樣作很明顯會有丟失關節點的可能性。也能夠先把圖片放在中間,而後將圖片縮放到目標尺寸範圍內原尺寸的可縮放的大小,而後四條邊還須要填充的距離,最後 resize 到應有大小。
這裏採用的是先擴展邊緣,而後放大圖片,再進行 crop,這樣作可以保證圖片中心處理後依然在中心位置,且沒有關節由於 crop 而丟失。注意在處理圖片的同時須要對標註也進行處理。
要注意 OpenCV 和 PIL 讀入的 RGB 順序是不同的,在使用不一樣庫進行處理時要轉換通道。
import cv2big_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] += addif '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)) 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 中可使用:
cv2.getRotationMatrix2D((center_x, center_y) , angle, 1.0)newimg = cv2.warpAffine(img, rotMat, (width, height)) newimg = cv2.warpAffine(img, rotMat, (width, height))
獲得轉換矩陣,並經過仿射變換獲得旋轉後的圖像。而標註點能夠直接經過旋轉矩陣得到對應點。
rot = rotMat[:, : 2]add = np.array([rotMat[0][2], rotMat[1][2]])coor = np.dot(rot, coor) + w add = np.array([rotMat[0][2], rotMat[1][2]]) coor = np.dot(rot, coor) + w
該部分代碼:
def rotate(self, img, cord, anno, center): 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) 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 水平垂直翻轉三種。
def flip(self, img, cod, anno_valid, symmetry): '''對圖片進行翻轉''' 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) '''對圖片進行翻轉''' 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% 高斯分佈的顏色點做爲噪聲。人爲地損失部分通道信息也能夠達到添加彩色噪聲的效果。
def add_color_noise(self, image, percentage=0.1): 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 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 的數據格式。
reader = paddle.batch(self.read_record(test_list, joint_dict, mode = 'train'), batch_size=1)fluid.recordio_writer.convert_reader_to_recordio_file("./work/test_" + str(i) + "_test.recordio", feeder=feeder, reader_creator=reader) '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 曲線波動的地方回溯到較爲平穩的點,並以平穩點的學習率爲起點,以更低的學習率接上上次學習。
轉載來源:PaperWeekly
下載安裝命令 ## CPU版本安裝命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle ## GPU版本安裝命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu
本文分享 CSDN - 飛槳PaddlePaddle。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。