如何用TensorLayer做目標檢測的數據增強

數據增強在機器學習中的作用不言而喻。和圖片分類的數據增強不同,訓練目標檢測模型的數據增強在對圖像做處理時,還需要對圖片中每個目標的坐標做相應的處理。此外,位移、裁剪等操作還有可能使得一些目標在處理後只有一小部分區域保留在原圖中,這需要額外的機制來判斷是否需要去掉該目標來訓練模型。為此TensorLayer 1.7.0發布中,提供了大量關於目標檢測任務的數據集下載、目標坐標處理、數據增強的API。最近的幾次發布主要面向新的卷積方式(Deformable Convolution, Depthwise ...),優化Subpixel Convolution以及提供新的遞歸方式(ConvLSTM)等。

首先,我們下載VOC2012數據集並對類別和坐標做預處理。tl.files.load_voc_dataset函數自動下載數據集,其返回的坐標格式和Darknet一樣,則[x_c, y_c, w,h],其中x_c和y_c代表一個目標的中心在圖片上的位置,w和h代表該目標的寬度和高度,這4個值是其和原圖高度和寬度的比例,所以這4個值的範圍在0~1之間。

import tensorlayer as tl## 下載 VOC 2012 數據集imgs_file_list, _, _, _, classes, _, _, _, objs_info_list, _ = tl.files.load_voc_dataset(dataset="2012")## 圖片標記預處理為列表形式ann_list = []for info in objs_info_list: ann = tl.prepro.parse_darknet_ann_str_to_list(info) c, b = tl.prepro.parse_darknet_ann_list_to_cls_box(ann) ann_list.append([c, b])

單張圖片處理

我們先對一張圖片做處理,以觀察tl.prepro工具箱中各個API的效果。這裡我們保存2號圖片的原圖,以供後面做比較。

# 讀取一張圖片,並保存idx = 2 # 可自行選擇圖片image = tl.vis.read_image(imgs_file_list[idx])tl.vis.draw_boxes_and_labels_to_image(image, ann_list[idx][0], ann_list[idx][1], [], classes, True, save_name="_im_original.png")

原圖 Original Image

# 左右翻轉im_flip, coords = tl.prepro.obj_box_left_right_flip(image, ann_list[idx][1], is_rescale=True, is_center=True, is_random=False)tl.vis.draw_boxes_and_labels_to_image(im_flip, ann_list[idx][0], coords, [], classes, True, save_name="_im_flip.png")

左右翻轉 Flip

# 位移im_shfit, clas, coords = tl.prepro.obj_box_shift(image, ann_list[idx][0], ann_list[idx][1], wrg=0.1, hrg=0.1, is_rescale=True, is_center=True, is_random=False)tl.vis.draw_boxes_and_labels_to_image(im_shfit, clas, coords, [], classes, True, save_name="_im_shift.png")

位移 Shift

# 高寬縮放im_zoom, clas, coords = tl.prepro.obj_box_zoom(image, ann_list[idx][0], ann_list[idx][1], zoom_range=(1.3, 0.7), is_rescale=True, is_center=True, is_random=False)tl.vis.draw_boxes_and_labels_to_image(im_zoom, clas, coords, [], classes, True, save_name="_im_zoom.png")

高寬縮放Zoom

從縮放的圖片中,我們可以看到一架飛機由於大部分區域被移到圖像之外了,只剩下機頭的一小部分,所以這個目標被去除了。tl.prepro工具箱中關於目標檢測的API往往有thresh_wh和thresh_wh2兩個閥值,thresh_wh表示在處理圖像之後,若一個目標的寬或高和圖片本身寬高的比例小於這個值,則去除該目標;thresh_wh2表示在處理圖像之後,若一個目標的寬高或高寬比例小於這個值,則去除該目標。大家可以根據特定開發任務來設置這兩個值,作者建議在常規情況下使用默認值。

# 調整圖片大小 im_resize, coords = tl.prepro.obj_box_imresize(image, coords=ann_list[idx][1], size=[300, 200], is_rescale=True)tl.vis.draw_boxes_and_labels_to_image(im_resize, ann_list[idx][0], coords, [], classes, True, save_name="_im_resize.png")

調整大小

# 裁剪im_crop, clas, coords = tl.prepro.obj_box_crop(image, ann_list[idx][0], ann_list[idx][1], wrg=200, hrg=200, is_rescale=True, is_center=True, is_random=False)tl.vis.draw_boxes_and_labels_to_image(im_crop, clas, coords, [], classes, True, save_name="_im_crop.png")

裁剪 Crop

多線程處理

實際訓練模型時,我們可能會使用多線程方法來對一個batch的圖片做隨機的數據增強。這時,tl.prepro工具箱的API中is_random全部設為True。

import tensorlayer as tlimport randombatch_size = 64im_size = [416, 416] # 輸出圖的大小n_data = len(imgs_file_list)jitter = 0.2def _data_pre_aug_fn(data): im, ann = data clas, coords = ann ## 隨機改變圖片亮度、對比度和飽和度 im = tl.prepro.illumination(im, gamma=(0.5, 1.5), contrast=(0.5, 1.5), saturation=(0.5, 1.5), is_random=True) ## 隨機左右翻轉 im, coords = tl.prepro.obj_box_left_right_flip(im, coords, is_rescale=True, is_center=True, is_random=True) ## 隨機調整大小並裁剪出指定大小的圖片,這同時達到了隨機縮放的效果 tmp0 = random.randint(1, int(im_size[0]*jitter)) tmp1 = random.randint(1, int(im_size[1]*jitter)) im, coords = tl.prepro.obj_box_imresize(im, coords, [im_size[0]+tmp0, im_size[1]+tmp1], is_rescale=True, interp="bicubic") im, clas, coords = tl.prepro.obj_box_crop(im, clas, coords, wrg=im_size[1], hrg=im_size[0], is_rescale=True, is_center=True, is_random=True) ## 把數值範圍從 [0, 255] 轉到 [-1, 1] (可選) im = im / 127.5 - 1 return im, [clas, coords]# 隨機讀取一個batch的圖片及其標記idexs = tl.utils.get_random_int(min=0, max=n_data-1, number=batch_size)b_im_path = [imgs_file_list[i] for i in idexs]b_images = tl.prepro.threading_data(b_im_path, fn=tl.vis.read_image)b_ann = [ann_list[i] for i in idexs]# 多線程處理data = tl.prepro.threading_data([_ for _ in zip(b_images, b_ann)], _data_pre_aug_fn)b_images2 = [d[0] for d in data]b_ann = [d[1] for d in data]# 保存每一組圖片以供體會for i in range(len(b_images)): tl.vis.draw_boxes_and_labels_to_image(b_images[i], ann_list[idexs[i]][0], ann_list[idexs[i]][1], [], classes, True, save_name="_bbox_vis_%d_original.png" % i) tl.vis.draw_boxes_and_labels_to_image((b_images2[i]+1)*127.5, b_ann[i][0], b_ann[i][1], [], classes, True, save_name="_bbox_vis_%d.png" % i)

最後,我們得到64組處理前和處理後的圖片,下面列出2組圖片以供參考。

原圖

隨機處理後

原圖

隨機處理後

處理邏輯

這就完了嗎?大家認真思考一下上面的 _data_pre_aug_fn 函數做數據增強有什麼潛在缺點?假設我們的訓練圖像高寬非常不確定的話,比如有的圖是300x1000而有的圖是1000x300,上面的函數一上來就把圖片resize到一個正方形,會導致很多形狀高寬信息丟失!

當我們的數據集存在高寬比例多樣性很大時,我們需要另外的機制來解決這個問題。下面的函數中,我們的resize會根據原圖高寬來決定,我們把原圖最小的那個邊resize成最終尺寸對應需要的大小,同時另外一個邊以同比例resize(比如,如果原圖高比寬小,則把高resize成最終需要的高,同時寬以相同比例resize)。做完這一步之後,我們再對其進行隨機左右翻轉,縮放等操作,最終裁剪出我們需要的尺寸的圖。

def _data_aug_fn(self, data, jitter): im, ann = data clas, coords = ann ## resize到高寬合適的大小 scale = np.max((self.im_size[1] / float(im.shape[1]), self.im_size[0] / float(im.shape[0]))) im, coords = tl.prepro.obj_box_imresize(im, coords, [int(im.shape[0]*scale)+2, int(im.shape[1]*scale)+2], is_rescale=True, interp="bicubic") ## 幾何增強 geometric transformation im, coords = tl.prepro.obj_box_left_right_flip(im, coords, is_rescale=True, is_center=True, is_random=True) im, clas, coords = tl.prepro.obj_box_shift(im, clas, coords, wrg=0.1, hrg=0.1, is_rescale=True, is_center=True, is_random=True) im, clas, coords = tl.prepro.obj_box_zoom(im, clas, coords, zoom_range=(1-jitter, 1+jitter), is_rescale=True, is_center=True, is_random=True) im, clas, coords = tl.prepro.obj_box_crop(im, clas, coords, wrg=self.im_size[1], hrg=self.im_size[0], is_rescale=True, is_center=True, is_random=True) ## 光度增強 photometric transformation im = tl.prepro.illumination(im, gamma=(0.5, 1.5), contrast=(0.5, 1.5), saturation=(0.5, 1.5), is_random=True) im = tl.prepro.adjust_hue(im, hout=0.1, is_offset=True, is_clip=True, is_random=True) im = tl.prepro.pixel_value_scale(im, 0.1, [0, 255], is_random=True) ## 把數值範圍從 [0, 255] 轉到 [-1, 1] (可選) im = im / 127.5 - 1. im = np.clip(im, -1., 1.) return im, [clas, coords]

原圖

隨機處理後

原圖

隨機處理後

原圖

隨機處理後

處理前

隨機處理後

結束語

對於產業界的朋友來說,數據增強的邏輯和業務本身是非常相關的,我們需要對不同的數據集寫不同的增強代碼,合理的增強邏輯往往會在相同的演算法上大大提高準確性。各位還可以仔細思考一下crop和shift, zoom之間的先後問題會對圖片有什麼影響。TensorLayer把每一種增強行為都獨立開來,以便大家完全可控地實現自己的增強演算法邏輯。

完畢,歡迎轉載。


推薦閱讀:

Coursera吳恩達《卷積神經網路》課程筆記(3)-- 目標檢測
分分鐘帶你殺入Kaggle Top 1%
[原創]#Deep Learning回顧#之2006年的Science Paper
【量化策略】當Trading遇上Reinforcement Learning

TAG:TensorLayer深度学习库 | 深度学习DeepLearning | 机器学习 |