來源:Ustinian - kesci.com
原文鏈接:數(shù)據(jù)增強(qiáng)方法綜述
點(diǎn)擊以上鏈接?? 不用配置環(huán)境高职,直接在線運(yùn)行
導(dǎo)語
在深度學(xué)習(xí)時(shí)代钩乍,數(shù)據(jù)的規(guī)模越大、質(zhì)量越高怔锌,模型就能夠擁有更好的泛化能力寥粹,數(shù)據(jù)直接決定了模型學(xué)習(xí)的上限。然而在實(shí)際工程中埃元,采集的數(shù)據(jù)很難覆蓋全部的場景涝涤,比如圖像的光照條件,同一場景拍攝的圖片可能由于光線不同就會(huì)有很大的差異性岛杀,那么在訓(xùn)練模型的時(shí)候就需要加入光照方面的數(shù)據(jù)增強(qiáng)阔拳。另一方面,即使擁有大量的數(shù)據(jù)类嗤,也應(yīng)該進(jìn)行數(shù)據(jù)增強(qiáng)糊肠,這樣有助于添加相關(guān)數(shù)據(jù)數(shù)據(jù)集中數(shù)據(jù)的數(shù)量辨宠,防止模型學(xué)習(xí)到不想要的模型,避免出現(xiàn)過擬合現(xiàn)象货裹。
1.數(shù)據(jù)增強(qiáng)的方法和種類
數(shù)據(jù)增強(qiáng)的具體使用方法有兩種嗤形,一種是事先執(zhí)行所有的轉(zhuǎn)換,實(shí)質(zhì)是增強(qiáng)數(shù)據(jù)集的大小弧圆,這種方法稱為線下增強(qiáng)赋兵。它比較適用于較小的數(shù)據(jù)集,最終將增加一定倍數(shù)的數(shù)據(jù)量搔预,這個(gè)倍數(shù)取決于轉(zhuǎn)換的圖片個(gè)數(shù)霹期,比如我需要對所有的圖片進(jìn)行旋轉(zhuǎn),則數(shù)據(jù)量增加一倍斯撮,本文中討論的就是該方法经伙。另一種是在將數(shù)據(jù)送入到機(jī)器學(xué)習(xí)模型的時(shí)候小批量(mini-batch)的轉(zhuǎn)換,這種方法被稱為線上增強(qiáng)或者飛行增強(qiáng)。這種方法比較適用于大數(shù)據(jù)集合勿锅,pytorch 中的** transforms** 函數(shù)就是基于該方法帕膜,在訓(xùn)練中每次對原始圖像進(jìn)行擾動(dòng)處理,經(jīng)過多次幾輪(epoch)訓(xùn)練之后溢十,就等效于數(shù)據(jù)增加垮刹。
常用的數(shù)據(jù)增強(qiáng)有兩種,有監(jiān)督和無監(jiān)督兩種张弛。本文只探討有監(jiān)督數(shù)據(jù)增強(qiáng)荒典。有監(jiān)督數(shù)據(jù)增強(qiáng)是基于現(xiàn)有的數(shù)據(jù)集,通過分析數(shù)據(jù)的完備性吞鸭,采用一定的規(guī)則對現(xiàn)有數(shù)據(jù)進(jìn)行擴(kuò)充寺董。有監(jiān)督數(shù)據(jù)增強(qiáng)可以細(xì)分為單樣本數(shù)據(jù)增強(qiáng)和多樣本數(shù)據(jù)增強(qiáng),在實(shí)際工程應(yīng)用中刻剥,單樣本數(shù)據(jù)增強(qiáng)使用更多遮咖,在 git 上有一些性能較好開源數(shù)據(jù)增強(qiáng)項(xiàng)目,他們功能較全并且處理速度也很快造虏,開發(fā)者可以直接調(diào)用御吞,如 imgaug 和 albumentations。在 pytorch 中漓藕,可以通過 torchvision 的 transforms 模塊來實(shí)現(xiàn)集成了很多數(shù)據(jù)增強(qiáng)函數(shù)包陶珠。本文主要介紹單樣本數(shù)據(jù)增強(qiáng)的一些常用方法。
2.裁剪
做裁剪操作主要是考慮原始圖像的寬高擾動(dòng)享钞,在大多數(shù)圖像分類網(wǎng)絡(luò)中揍诽,樣本在輸入網(wǎng)絡(luò)前必須要統(tǒng)一大小,所以通過調(diào)整圖像的尺寸可以大量的擴(kuò)展數(shù)據(jù)。通過裁剪有兩種擴(kuò)種方式暑脆,一種是對大尺寸的圖像直接按照需要送入網(wǎng)絡(luò)的尺寸進(jìn)行裁剪交排,比如原始圖像的分辨率大小是256x256,現(xiàn)在網(wǎng)絡(luò)需要輸入的圖像像素尺寸是224x224饵筑,這樣可以直接在原始圖像上進(jìn)行隨機(jī)裁剪224x224 像素大小的圖像即可埃篓,這樣一張圖可以擴(kuò)充32x32張圖片;另外一種是將隨機(jī)裁剪固定尺寸大小的圖片根资,然后再將圖像通過插值算法調(diào)整到網(wǎng)絡(luò)需要的尺寸大小架专。由于數(shù)據(jù)集中通常數(shù)據(jù)大小不一,后者通常使用的較多玄帕。
使用opencv進(jìn)行圖像裁剪部脚,利用隨機(jī)數(shù)確定圖像的裁剪范圍,代碼如下:
img_path = '../../img/ch3_img1.jpg'
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w, _ = img.shape
new_h1, new_h2 = np.random.randint(0, h-512, 2)
new_w1, new_w2 = np.random.randint(0, w-512, 2)
img_crop1 = img[new_h1:new_h1+512, new_w1:new_w1+512, :]
img_crop2 = img[new_h2:new_h2+512, new_w2:new_w2+512, :]
# 顯示
plt.figure(figsize=(15, 10))
plt.subplot(1,3,1), plt.imshow(img)
plt.axis('off'); plt.title('原圖')
plt.subplot(1,3,2), plt.imshow(img_crop1)
plt.axis('off'); plt.title('水平鏡像')
plt.subplot(1,3,3), plt.imshow(img_crop2)
plt.axis('off'); plt.title('垂直鏡像')
plt.show()
3.翻轉(zhuǎn)和旋轉(zhuǎn)
翻轉(zhuǎn)和旋轉(zhuǎn)都是將原始的圖像像素在位置空間上做變換裤纹,圖像的翻轉(zhuǎn)是將原始的圖像進(jìn)行鏡像操作委刘,鏡像操作在數(shù)據(jù)增強(qiáng)中會(huì)經(jīng)常被使用,并且起了非常重要的作用鹰椒,它主要包括水平鏡像翻轉(zhuǎn)锡移,垂直鏡像翻轉(zhuǎn)和原點(diǎn)鏡像翻轉(zhuǎn),具體在使用中漆际,需要結(jié)合數(shù)據(jù)形式選擇相應(yīng)翻轉(zhuǎn)操作淆珊,比如數(shù)據(jù)集是汽車圖像數(shù)據(jù),訓(xùn)練集合測試集都是正常拍攝的圖片奸汇,此時(shí)只使用水平鏡像操作施符,如果加入垂直或者原點(diǎn)鏡像翻轉(zhuǎn),會(huì)對原始圖像產(chǎn)生干擾擂找。
角度旋轉(zhuǎn)操作和圖像鏡像相對戳吝,它主要是沿著畫面的中心進(jìn)行任意角度的變換,該變換是通過將原圖像和仿射變換矩陣相乘實(shí)現(xiàn)的贯涎。為了實(shí)現(xiàn)圖像的中心旋轉(zhuǎn)听哭,除了要知道旋轉(zhuǎn)角度,還要計(jì)算平移的量才能能讓仿射變換的效果等效于旋轉(zhuǎn)軸的畫面中心柬采。仿射變換矩陣是一個(gè)余弦矩陣欢唾,在OpenCV中有實(shí)現(xiàn)的庫cv2.getRotationMatrix2D可以使用且警,該函數(shù)的第1個(gè)參數(shù)是旋轉(zhuǎn)中心粉捻,第2個(gè)參數(shù)是逆時(shí)針旋轉(zhuǎn)角度,第3個(gè)參數(shù)是縮放倍數(shù)斑芜,對于只是旋轉(zhuǎn)的情況參數(shù)值是1肩刃,返回的值就是做仿射變換的矩陣。然后通過cv2.warpAffine()將原圖像矩陣乘以旋轉(zhuǎn)矩陣得到最終的結(jié)果。
通過上述的操作旋轉(zhuǎn)的圖像會(huì)有存在黑邊盈包,如果想去除掉圖片的黑邊沸呐,需要將原始的圖像做出一些犧牲,對旋轉(zhuǎn)后的圖像取最大內(nèi)接矩陣呢燥,該矩陣的長寬比和原始圖像相同崭添,如下圖所示。要計(jì)算內(nèi)切矩陣的坐標(biāo)Q,需要通過旋轉(zhuǎn)角度和原始圖像矩陣的邊長OP得到叛氨。
利用opencv實(shí)現(xiàn)的代碼如下:
# 去除黑邊的操作
crop_image = lambda img, x0, y0, w, h: img[y0:y0+h, x0:x0+w] # 定義裁切函數(shù)呼渣,后續(xù)裁切黑邊使用
def rotate_image(img, angle, crop):
"""
angle: 旋轉(zhuǎn)的角度
crop: 是否需要進(jìn)行裁剪,布爾向量
"""
w, h = img.shape[:2]
# 旋轉(zhuǎn)角度的周期是360°
angle %= 360
# 計(jì)算仿射變換矩陣
M_rotation = cv2.getRotationMatrix2D((w / 2, h / 2), angle, 1)
# 得到旋轉(zhuǎn)后的圖像
img_rotated = cv2.warpAffine(img, M_rotation, (w, h))
# 如果需要去除黑邊
if crop:
# 裁剪角度的等效周期是180°
angle_crop = angle % 180
if angle > 90:
angle_crop = 180 - angle_crop
# 轉(zhuǎn)化角度為弧度
theta = angle_crop * np.pi / 180
# 計(jì)算高寬比
hw_ratio = float(h) / float(w)
# 計(jì)算裁剪邊長系數(shù)的分子項(xiàng)
tan_theta = np.tan(theta)
numerator = np.cos(theta) + np.sin(theta) * np.tan(theta)
# 計(jì)算分母中和高寬比相關(guān)的項(xiàng)
r = hw_ratio if h > w else 1 / hw_ratio
# 計(jì)算分母項(xiàng)
denominator = r * tan_theta + 1
# 最終的邊長系數(shù)
crop_mult = numerator / denominator
# 得到裁剪區(qū)域
w_crop = int(crop_mult * w)
h_crop = int(crop_mult * h)
x0 = int((w - w_crop) / 2)
y0 = int((h - h_crop) / 2)
img_rotated = crop_image(img_rotated, x0, y0, w_crop, h_crop)
return img_rotated
#水平鏡像
h_flip = cv2.flip(img,1)
#垂直鏡像
v_flip = cv2.flip(img,0)
#水平垂直鏡像
hv_flip = cv2.flip(img,-1)
#90度旋轉(zhuǎn)
rows, cols, _ = img.shape
M = cv2.getRotationMatrix2D((cols/2, rows/2), 45, 1)
rotation_45 = cv2.warpAffine(img, M, (cols, rows))
#45度旋轉(zhuǎn)
M = cv2.getRotationMatrix2D((cols/2, rows/2), 135, 2)
rotation_135 = cv2.warpAffine(img, M,(cols, rows))
#去黑邊旋轉(zhuǎn)45度
image_rotated = rotate_image(img, 45, True)
#顯示
plt.figure(figsize=(15, 10))
plt.subplot(2,3,1), plt.imshow(img)
plt.axis('off'); plt.title('原圖')
plt.subplot(2,3,2), plt.imshow(h_flip)
plt.axis('off'); plt.title('水平鏡像')
plt.subplot(2,3,3), plt.imshow(v_flip)
plt.axis('off'); plt.title('垂直鏡像')
plt.subplot(2,3,4), plt.imshow(hv_flip)
plt.axis('off'); plt.title('水平垂直鏡像')
plt.subplot(2,3,5), plt.imshow(rotation_45)
plt.axis('off'); plt.title('旋轉(zhuǎn)45度')
plt.subplot(2,3,6), plt.imshow(image_rotated)
plt.axis('off'); plt.title('去黑邊旋轉(zhuǎn)45度')
plt.show()
上述代碼通過opencv自帶的flip函數(shù)實(shí)現(xiàn)了翻轉(zhuǎn)操作寞埠,該函數(shù)的第二個(gè)參數(shù)是控制翻轉(zhuǎn)的方向屁置。通過內(nèi)切矩陣計(jì)算公式可得無黑邊剪切結(jié)果。上述代碼的結(jié)果圖如下圖所示仁连。通過結(jié)果可以看出蓝角,旋轉(zhuǎn)
4.縮放
圖像可以向外或向內(nèi)縮放。向外縮放時(shí)饭冬,最終圖像尺寸將大于原始圖像尺寸使鹅,為了保持原始圖像的大小,通常需要結(jié)合裁剪昌抠,從縮放后的圖像中裁剪出和原始圖像大小一樣的圖像并徘。另一種方法是向內(nèi)縮放,它會(huì)縮小圖像大小扰魂,縮小到預(yù)設(shè)的大小麦乞。縮放也會(huì)帶了一些問題劝评,如縮放后的圖像尺寸和原始圖像尺寸的長寬比差異較大姐直,會(huì)出現(xiàn)圖像失幀的現(xiàn)象,如果在實(shí)驗(yàn)中對最終的結(jié)果有一定的影響蒋畜,需要做等比例縮放声畏,對不足的地方進(jìn)行邊緣填充。以下是縮放的代碼和結(jié)果圖像姻成。
img_2 = cv2.resize(img, (int(h * 1.5), int(w * 1.5)))
img_2 = img_2[int((h - 512) / 2) : int((h + 512) / 2), int((w - 512) / 2) : int((w + 512) /2), :]
img_3 = cv2.resize(img, (512, 512))
## 顯示
plt.figure(figsize=(15, 10))
plt.subplot(1,3,1), plt.imshow(img)
plt.axis('off'); plt.title('原圖')
plt.subplot(1,3,2), plt.imshow(img_2)
plt.axis('off'); plt.title('向外縮放')
plt.subplot(1,3,3), plt.imshow(img_3)
plt.axis('off'); plt.title('向內(nèi)縮放')
plt.show()
5.移位
移位只涉及沿X或Y方向(或兩者)移動(dòng)圖像插龄,如果圖像的背景是單色被背景或者是純的黑色背景,使用該方法可以很有效的增強(qiáng)數(shù)據(jù)數(shù)量科展,可以通過cv2.warpAffine實(shí)現(xiàn)該代碼:
mat_shift = np.float32([[1,0,100], [0,1,200]])
img_1 = cv2.warpAffine(img, mat_shift, (h, w))
mat_shift = np.float32([[1, 0, -150], [0, 1, -150]])
img_2 = cv2.warpAffine(img, mat_shift, (h, w))
## 顯示
plt.figure(figsize=(15, 10))
plt.subplot(1,3,1), plt.imshow(img)
plt.axis('off'); plt.title('原圖')
plt.subplot(1,3,2), plt.imshow(img_1)
plt.axis('off'); plt.title('向右下移動(dòng)')
plt.subplot(1,3,3), plt.imshow(img_2)
plt.axis('off'); plt.title('左上移動(dòng)')
plt.show()
6.高斯噪聲
當(dāng)神經(jīng)網(wǎng)絡(luò)試圖學(xué)習(xí)可能無用的高頻特征(即圖像中大量出現(xiàn)的模式)時(shí)均牢,通常會(huì)發(fā)生過度擬合。具有零均值的高斯噪聲基本上在所有頻率中具有數(shù)據(jù)點(diǎn),從而有效地扭曲高頻特征。這也意味著較低頻率的組件也會(huì)失真火架,但你的神經(jīng)網(wǎng)絡(luò)可以學(xué)會(huì)超越它强戴,添加適量的噪音可以增強(qiáng)學(xué)習(xí)能力歉摧。
基于噪聲的數(shù)據(jù)增強(qiáng)就是在原來的圖片的基礎(chǔ)上燃异,隨機(jī)疊加一些噪聲礼饱,最常見的做法就是高斯噪聲作郭。更復(fù)雜一點(diǎn)的就是在面積大小可選定哨查、位置隨機(jī)的矩形區(qū)域上丟棄像素產(chǎn)生黑色矩形塊逗抑,從而產(chǎn)生一些彩色噪聲,以Coarse Dropout方法為代表寒亥,甚至還可以對圖片上隨機(jī)選取一塊區(qū)域并擦除圖像信息锋八。以下是代碼和圖像:
img_s1 = gasuss_noise(img, 0, 0.005)
img_s2 = gasuss_noise(img, 0, 0.05)
plt.figure(figsize=(15, 10))
plt.subplot(1,3,1), plt.imshow(img)
plt.axis('off'); plt.title('原圖')
plt.subplot(1,3,2), plt.imshow(img_s1)
plt.axis('off'); plt.title('方差為0.005')
plt.subplot(1,3,3), plt.imshow(img_s2)
plt.axis('off'); plt.title('方差為0.05')
plt.show()
7.色彩抖動(dòng)
上面提到的圖像中有一個(gè)比較大的難點(diǎn)是背景干擾,在實(shí)際工程中為了消除圖像在不同背景中存在的差異性护盈,通常會(huì)做一些色彩抖動(dòng)操作挟纱,擴(kuò)充數(shù)據(jù)集合。色彩抖動(dòng)主要是在圖像的顏色方面做增強(qiáng)腐宋,主要調(diào)整的是圖像的亮度紊服,飽和度和對比度。工程中不是任何數(shù)據(jù)集都適用胸竞,通常如果不同背景的圖像較多欺嗤,加入色彩抖動(dòng)操作會(huì)有很好的提升。
def randomColor(image, saturation=0, brightness=0, contrast=0, sharpness=0):
if random.random() < saturation:
random_factor = np.random.randint(0, 31) / 10. # 隨機(jī)因子
image = ImageEnhance.Color(image).enhance(random_factor) # 調(diào)整圖像的飽和度
if random.random() < brightness:
random_factor = np.random.randint(10, 21) / 10. # 隨機(jī)因子
image = ImageEnhance.Brightness(image).enhance(random_factor) # 調(diào)整圖像的亮度
if random.random() < contrast:
random_factor = np.random.randint(10, 21) / 10. # 隨機(jī)因1子
image = ImageEnhance.Contrast(image).enhance(random_factor) # 調(diào)整圖像對比度
if random.random() < sharpness:
random_factor = np.random.randint(0, 31) / 10. # 隨機(jī)因子
ImageEnhance.Sharpness(image).enhance(random_factor) # 調(diào)整圖像銳度
return image
cj_img = Image.fromarray(img)
sa_img = np.asarray(randomColor(cj_img, saturation=1))
br_img = np.asarray(randomColor(cj_img, brightness=1))
co_img = np.asarray(randomColor(cj_img, contrast=1))
sh_img = np.asarray(randomColor(cj_img, sharpness=1))
rc_img = np.asarray(randomColor(cj_img, saturation=1, \
brightness=1, contrast=1, sharpness=1))
plt.figure(figsize=(15, 10))
plt.subplot(2,3,1), plt.imshow(img)
plt.axis('off'); plt.title('原圖')
plt.subplot(2,3,2), plt.imshow(sa_img)
plt.axis('off'); plt.title('調(diào)整飽和度')
plt.subplot(2,3,3), plt.imshow(br_img)
plt.axis('off'); plt.title('調(diào)整亮度')
plt.subplot(2,3,4), plt.imshow(co_img)
plt.axis('off'); plt.title('調(diào)整對比度')
plt.subplot(2,3,5), plt.imshow(sh_img)
plt.axis('off'); plt.title('調(diào)整銳度')
plt.subplot(2,3,6), plt.imshow(rc_img)
plt.axis('off'); plt.title('調(diào)整所有項(xiàng)')
plt.show()
參考文獻(xiàn):
http://docs.opencv.org/master/d4/d13/tutorial_py_filtering.html
葉韻.深度學(xué)習(xí)與計(jì)算機(jī)視覺:算法原理卫枝、框架應(yīng)用與代碼實(shí)現(xiàn)[M].北京:機(jī)械工業(yè)出版社煎饼,2018:194-195.
Zhang H, Cisse M, Dauphin Y N, et al. mixup: Beyond Empirical Risk Minimization[J]. 2017.