圖像深度學習任務中,面對小數據集,我們往往需要利用Image Data Augmentation圖像增廣技術來擴充我們的數據集,而keras的內置ImageDataGenerator很好地幫我們實現圖像增廣。但是面對ImageDataGenerator中衆多的參數,每個參數所得到的效果分別是怎樣的呢?本文針對Keras中ImageDataGenerator的各項參數數值的效果進行了詳細解釋,爲各位深度學習研究者們提供一個參考。
我們先來看看ImageDataGenerator的官方說明:
keras.preprocessing.image.ImageDataGenerator(featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
zca_epsilon=1e-6,
rotation_range=0.,
width_shift_range=0.,
height_shift_range=0.,
shear_range=0.,
zoom_range=0.,
channel_shift_range=0.,
fill_mode='nearest',
cval=0.,
horizontal_flip=False,
vertical_flip=False,
rescale=None,
preprocessing_function=None,
data_format=K.image_data_format())
官方提供的參數解釋因爲太長就不貼出來了,大家可以直接點開上面的鏈接看英文原介紹,我們現在就從每一個參數開始看看它會帶來何種效果。
我們測試選用的是kaggle dogs vs cats redux 貓狗大戰的數據集,隨機選取了9張狗狗的照片,這9張均被resize成224×224的尺寸,如圖1:
圖11. featurewise
datagen = image.ImageDataGenerator(featurewise_center=True,
featurewise_std_normalization=True)
featurewise_center的官方解釋:"Set input mean to 0 over the dataset, feature-wise." 大意爲使數據集去中心化(使得其均值爲0),而samplewise_std_normalization的官方解釋是“ Divide inputs by std of the dataset, feature-wise.”,大意爲將輸入的每個樣本除以其自身的標準差。這兩個參數都是從數據集整體上對每張圖片進行標準化處理,我們看看效果如何:
圖2與圖1原圖相比,經過處理後的圖片在視覺上稍微“變暗”了一點。
2. samplewise
datagen = image.ImageDataGenerator(samplewise_center=True,
samplewise_std_normalization=True)
samplewise_center的官方解釋爲:“ Set each sample mean to 0.”,使輸入數據的每個樣本均值爲0;samplewise_std_normalization的官方解釋爲:“Divide each input by its std.”,將輸入的每個樣本除以其自身的標準差。這個月featurewise的處理不同,featurewise是從整個數據集的分佈去考慮的,而samplewise只是針對自身圖片,效果如圖3:
圖3看來針對自身數據分佈的處理在貓狗大戰數據集上沒有什麼意義,或許在mnist這類灰度圖上有用?讀者可以試試。
3. zca_whtening
datagen = image.ImageDataGenerator(zca_whitening=True)
zca白化的作用是針對圖片進行PCA降維操作,減少圖片的冗餘信息,保留最重要的特徵,細節可參看:Whitening transformation--維基百科,Whitening--斯坦福。
很抱歉的是,本人使用keras的官方演示代碼,並沒有復現出zca_whitening的效果,當我的圖片resize成224×224時,代碼報內存錯誤,應該是在計算SVD的過程中數值太大。後來resize成28×28,就沒有內存錯誤了,但是代碼運行了一晚上都不結束,因此使用貓狗大戰圖片無法復現效果,這裏轉發另外一個博客使用mnist復現出的結果,如下圖4。針對mnist的其它DataAugmentation結果可以看這個博客:Image Augmentation for Deep Learning With Keras,有修改意見的朋友歡迎留言。
圖44. rotation range
datagen = image.ImageDataGenerator(rotation_range=30)
rotation range的作用是用戶指定旋轉角度範圍,其參數只需指定一個整數即可,但並不是固定以這個角度進行旋轉,而是在 [0, 指定角度] 範圍內進行隨機角度旋轉。效果如圖5:
圖55. width_shift_range & height_shift_range
datagen = image.ImageDataGenerator(width_shift_range=0.5,height_shift_range=0.5)
width_shift_range & height_shift_range 分別是水平位置評議和上下位置平移,其參數可以是[0, 1]的浮點數,也可以大於1,其最大平移距離爲圖片長或寬的尺寸乘以參數,同樣平移距離並不固定爲最大平移距離,平移距離在 [0, 最大平移距離] 區間內。效果如圖6:
圖6平移圖片的時候一般會出現超出原圖範圍的區域,這部分區域會根據fill_mode的參數來補全,具體參數看下文。當參數設置過大時,會出現圖7的情況,因此儘量不要設置太大的數值。
圖76. shear_range
datagen = image.ImageDataGenerator(shear_range=0.5)
shear_range就是錯切變換,效果就是讓所有點的x座標(或者y座標)保持不變,而對應的y座標(或者x座標)則按比例發生平移,且平移的大小和該點到x軸(或y軸)的垂直距離成正比。
如圖8所示,一個黑色矩形圖案變換爲藍色平行四邊形圖案。狗狗圖片變換效果如圖9所示。
圖8圖97. zoom_range
datagen = image.ImageDataGenerator(zoom_range=0.5)
zoom_range參數可以讓圖片在長或寬的方向進行放大,可以理解爲某方向的resize,因此這個參數可以是一個數或者是一個list。當給出一個數時,圖片同時在長寬兩個方向進行同等程度的放縮操作;當給出一個list時,則代表[width_zoom_range, height_zoom_range],即分別對長寬進行不同程度的放縮。而參數大於0小於1時,執行的是放大操作,當參數大於1時,執行的是縮小操作。
參數大於0小於1時,效果如圖10:
圖10參數等於4時,效果如圖11:
圖118. channel_shift_range
datagen = image.ImageDataGenerator(channel_shift_range=10)
channel_shift_range可以理解成改變圖片的顏色,通過對顏色通道的數值偏移,改變圖片的整體的顏色,這意味着是“整張圖”呈現某一種顏色,像是加了一塊有色玻璃在圖片前面一樣,因此它並不能單獨改變圖片某一元素的顏色,如黑色小狗不能變成白色小狗。當數值爲10時,效果如圖12;當數值爲100時,效果如圖13,可見當數值越大時,顏色變深的效果越強。
圖12圖139. horizontal_flip & vertical_flip
datagen = image.ImageDataGenerator(horizontal_flip=True)
horizontal_flip的作用是隨機對圖片執行水平翻轉操作,意味着不一定對所有圖片都會執行水平翻轉,每次生成均是隨機選取圖片進行翻轉。效果如圖14。
圖14datagen = image.ImageDataGenerator(vertical_flip=True)
vertical_flip是作用是對圖片執行上下翻轉操作,和horizontal_flip一樣,每次生成均是隨機選取圖片進行翻轉,效果如圖15。
圖15當然了,在貓狗大戰數據集當中不適合使用vertical_flip,因爲一般沒有倒過來的動物。
10. rescale
datagen = image.ImageDataGenerator(rescale= 1/255, width_shift_range=0.1)
rescale的作用是對圖片的每個像素值均乘上這個放縮因子,這個操作在所有其它變換操作之前執行,在一些模型當中,直接輸入原圖的像素值可能會落入激活函數的“死亡區”,因此設置放縮因子爲1/255,把像素值放縮到0和1之間有利於模型的收斂,避免神經元“死亡”。
圖片經過rescale之後,保存到本地的圖片用肉眼看是沒有任何區別的,如果我們在內存中直接打印圖片的數值,可以看到以下結果:
圖16可以從圖16看到,圖片像素值都被縮小到0和1之間,但如果打開保存在本地的圖片,其數值依然不變,如圖17。
圖17應該是在保存到本地的時候,keras把圖像像素值恢復爲原來的尺度了,在內存中查看則不會。
11. fill_mode
datagen = image.ImageDataGenerator(fill_mode='wrap', zoom_range=[4, 4])
fill_mode爲填充模式,如前面提到,當對圖片進行平移、放縮、錯切等操作時,圖片中會出現一些缺失的地方,那這些缺失的地方該用什麼方式補全呢?就由fill_mode中的參數確定,包括:“constant”、“nearest”(默認)、“reflect”和“wrap”。這四種填充方式的效果對比如圖18所示,從左到右,從上到下分別爲:“reflect”、“wrap”、“nearest”、“constant”。
圖18當設置爲“constant”時,還有一個可選參數,cval,代表使用某個固定數值的顏色來進行填充。圖19爲cval=100時的效果,可以與圖18右下角的無cval參數的圖對比。
圖19自己動手來測試?
這裏給出一段小小的代碼,作爲進行這些參數調試時的代碼,你也可以使用jupyter notebook來試驗這些參數,把圖片結果打印到你的網頁上。
%matplotlib inline
import matplotlib.pyplot as plt
from PIL import Image
from keras.preprocessing import image
import glob
# 設置生成器參數
datagen = image.ImageDataGenerator(fill_mode='wrap', zoom_range=[4, 4])
gen_data = datagen.flow_from_directory(PATH,
batch_size=1,
shuffle=False,
save_to_dir=SAVE_PATH,
save_prefix='gen',
target_size=(224, 224))
# 生成9張圖
for i in range(9):
gen_data.next()
# 找到本地生成圖,把9張圖打印到同一張figure上
name_list = glob.glob(gen_path+'16/*')
fig = plt.figure()
for i in range(9):
img = Image.open(name_list[i])
sub_img = fig.add_subplot(331 + i)
sub_img.imshow(img)
plt.show()
結語
面對小數據集時,使用DataAugmentation擴充你的數據集就變得非常重要,但在使用DataAugmentation之前,先要了解你的數據集需不需要這類圖片,如貓狗大戰數據集不需要上下翻轉的圖片,以及思考一下變換的程度是不是合理的,例如把目標水平偏移到圖像外面就是不合理的。多試幾次效果,再最終確定使用哪些參數。上面所有內容已經公佈在我的github上面,附上了實驗時的jupyter notebook文件,大家可以玩一玩,have fun!
看看這個函數的官網介紹:
圖片預處理
圖片生成器ImageDataGenerator
keras.preprocessing.image.ImageDataGenerator(featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
zca_epsilon=1e-6,
rotation_range=0.,
width_shift_range=0.,
height_shift_range=0.,
shear_range=0.,
zoom_range=0.,
channel_shift_range=0.,
fill_mode='nearest',
cval=0.,
horizontal_flip=False,
vertical_flip=False,
rescale=None,
preprocessing_function=None,
data_format=K.image_data_format())
用以生成一個batch的圖像數據,支持實時數據提升。訓練時該函數會無限生成數據,直到達到規定的epoch次數爲止。
參數
featurewise_center:布爾值,使輸入數據集去中心化(均值爲0), 按feature執行
samplewise_center:布爾值,使輸入數據的每個樣本均值爲0
featurewise_std_normalization:布爾值,將輸入除以數據集的標準差以完成標準化, 按feature執行
samplewise_std_normalization:布爾值,將輸入的每個樣本除以其自身的標準差
zca_whitening:布爾值,對輸入數據施加ZCA白化
zca_epsilon: ZCA使用的eposilon,默認1e-6
rotation_range:整數,數據提升時圖片隨機轉動的角度
width_shift_range:浮點數,圖片寬度的某個比例,數據提升時圖片水平偏移的幅度
height_shift_range:浮點數,圖片高度的某個比例,數據提升時圖片豎直偏移的幅度
shear_range:浮點數,剪切強度(逆時針方向的剪切變換角度)
zoom_range:浮點數或形如
[lower,upper]
的列表,隨機縮放的幅度,若爲浮點數,則相當於[lower,upper] = [1 - zoom_range, 1+zoom_range]
channel_shift_range:浮點數,隨機通道偏移的幅度
fill_mode:;‘constant’,‘nearest’,‘reflect’或‘wrap’之一,當進行變換時超出邊界的點將根據本參數給定的方法進行處理
cval:浮點數或整數,當
fill_mode=constant
時,指定要向超出邊界的點填充的值horizontal_flip:布爾值,進行隨機水平翻轉
vertical_flip:布爾值,進行隨機豎直翻轉
rescale: 重放縮因子,默認爲None. 如果爲None或0則不進行放縮,否則會將該數值乘到數據上(在應用其他變換之前)
preprocessing_function: 將被應用於每個輸入的函數。該函數將在圖片縮放和數據提升之後運行。該函數接受一個參數,爲一張圖片(秩爲3的numpy array),並且輸出一個具有相同shape的numpy array
data_format:字符串,“channel_first”或“channel_last”之一,代表圖像的通道維的位置。該參數是Keras 1.x中的image_dim_ordering,“channel_last”對應原本的“tf”,“channel_first”對應原本的“th”。以128x128的RGB圖像爲例,“channel_first”應將數據組織爲(3,128,128),而“channel_last”應將數據組織爲(128,128,3)。該參數的默認值是
~/.keras/keras.json
中設置的值,若從未設置過,則爲“channel_last”
方法
fit(x, augment=False, rounds=1):計算依賴於數據的變換所需要的統計信息(均值方差等),只有使用
featurewise_center
,featurewise_std_normalization
或zca_whitening
時需要此函數。X:numpy array,樣本數據,秩應爲4.在黑白圖像的情況下channel軸的值爲1,在彩色圖像情況下值爲3
augment:布爾值,確定是否使用隨即提升過的數據
round:若設
augment=True
,確定要在數據上進行多少輪數據提升,默認值爲1seed: 整數,隨機數種子
flow(self, X, y, batch_size=32, shuffle=True, seed=None, save_to_dir=None, save_prefix='', save_format='png'):接收numpy數組和標籤爲參數,生成經過數據提升或標準化後的batch數據,並在一個無限循環中不斷的返回batch數據
x:樣本數據,秩應爲4.在黑白圖像的情況下channel軸的值爲1,在彩色圖像情況下值爲3
y:標籤
batch_size:整數,默認32
shuffle:布爾值,是否隨機打亂數據,默認爲True
save_to_dir:None或字符串,該參數能讓你將提升後的圖片保存起來,用以可視化
save_prefix:字符串,保存提升後圖片時使用的前綴, 僅當設置了
save_to_dir
時生效save_format:"png"或"jpeg"之一,指定保存圖片的數據格式,默認"jpeg"
yields:形如(x,y)的tuple,x是代表圖像數據的numpy數組.y是代表標籤的numpy數組.該迭代器無限循環.
seed: 整數,隨機數種子
flow_from_directory(directory): 以文件夾路徑爲參數,生成經過數據提升/歸一化後的數據,在一個無限循環中無限產生batch數據
- directory: 目標文件夾路徑,對於每一個類,該文件夾都要包含一個子文件夾.子文件夾中任何JPG、PNG、BNP、PPM的圖片都會被生成器使用.詳情請查看此腳本
- target_size: 整數tuple,默認爲(256, 256). 圖像將被resize成該尺寸
- color_mode: 顏色模式,爲"grayscale","rgb"之一,默認爲"rgb".代表這些圖片是否會被轉換爲單通道或三通道的圖片.
- classes: 可選參數,爲子文件夾的列表,如['dogs','cats']默認爲None. 若未提供,則該類別列表將從
directory
下的子文件夾名稱/結構自動推斷。每一個子文件夾都會被認爲是一個新的類。(類別的順序將按照字母表順序映射到標籤值)。通過屬性class_indices
可獲得文件夾名與類的序號的對應字典。 - class_mode: "categorical", "binary", "sparse"或None之一. 默認爲"categorical. 該參數決定了返回的標籤數組的形式, "categorical"會返回2D的one-hot編碼標籤,"binary"返回1D的二值標籤."sparse"返回1D的整數標籤,如果爲None則不返回任何標籤, 生成器將僅僅生成batch數據, 這種情況在使用
model.predict_generator()
和model.evaluate_generator()
等函數時會用到. - batch_size: batch數據的大小,默認32
- shuffle: 是否打亂數據,默認爲True
- seed: 可選參數,打亂數據和進行變換時的隨機數種子
- save_to_dir: None或字符串,該參數能讓你將提升後的圖片保存起來,用以可視化
- save_prefix:字符串,保存提升後圖片時使用的前綴, 僅當設置了
save_to_dir
時生效 - save_format:"png"或"jpeg"之一,指定保存圖片的數據格式,默認"jpeg"
- flollow_links: 是否訪問子文件夾中的軟鏈接
例子
使用.flow()
的例子
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
y_train = np_utils.to_categorical(y_train, num_classes)
y_test = np_utils.to_categorical(y_test, num_classes)
datagen = ImageDataGenerator(
featurewise_center=True,
featurewise_std_normalization=True,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True)
# compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied)
datagen.fit(x_train)
# fits the model on batches with real-time data augmentation:
model.fit_generator(datagen.flow(x_train, y_train, batch_size=32),
steps_per_epoch=len(x_train), epochs=epochs)
# here's a more "manual" example
for e in range(epochs):
print 'Epoch', e
batches = 0
for x_batch, y_batch in datagen.flow(x_train, y_train, batch_size=32):
loss = model.train(x_batch, y_batch)
batches += 1
if batches >= len(x_train) / 32:
# we need to break the loop by hand because
# the generator loops indefinitely
break
使用.flow_from_directory(directory)
的例子
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
'data/train',
target_size=(150, 150),
batch_size=32,
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
'data/validation',
target_size=(150, 150),
batch_size=32,
class_mode='binary')
model.fit_generator(
train_generator,
steps_per_epoch=2000,
epochs=50,
validation_data=validation_generator,
validation_steps=800)
同時變換圖像和mask
# we create two instances with the same arguments
data_gen_args = dict(featurewise_center=True,
featurewise_std_normalization=True,
rotation_range=90.,
width_shift_range=0.1,
height_shift_range=0.1,
zoom_range=0.2)
image_datagen = ImageDataGenerator(**data_gen_args)
mask_datagen = ImageDataGenerator(**data_gen_args)
# Provide the same seed and keyword arguments to the fit and flow methods
seed = 1
image_datagen.fit(images, augment=True, seed=seed)
mask_datagen.fit(masks, augment=True, seed=seed)
image_generator = image_datagen.flow_from_directory(
'data/images',
class_mode=None,
seed=seed)
mask_generator = mask_datagen.flow_from_directory(
'data/masks',
class_mode=None,
seed=seed)
# combine generators into one which yields image and masks
train_generator = zip(image_generator, mask_generator)
model.fit_generator(
train_generator,
steps_per_epoch=2000,
epochs=50)
看看別人用的例子:
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
img = load_img('data/cat.jpg') # 這是一個PIL圖像
x = img_to_array(img) # 把PIL圖像轉換成一個numpy數組,形狀爲(3, 150, 150)
x = x.reshape((1,) + x.shape) # 這是一個numpy數組,形狀爲 (1, 3, 150, 150)
# 下面是生產圖片的代碼
# 生產的所有圖片保存在 `preview/` 目錄下
i = 0
for batch in datagen.flow(x, batch_size=1,
save_to_dir='preview', save_prefix='cat', save_format='jpeg'):
i += 1
if i > 50:
break # 否則生成器會退出循環