Keras Image Data Augmentation 各參數詳解

圖像深度學習任務中,面對小數據集,我們往往需要利用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:

圖1

1. 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,有修改意見的朋友歡迎留言。

圖4

4. rotation range

datagen = image.ImageDataGenerator(rotation_range=30)

rotation range的作用是用戶指定旋轉角度範圍,其參數只需指定一個整數即可,但並不是固定以這個角度進行旋轉,而是在 [0, 指定角度] 範圍內進行隨機角度旋轉。效果如圖5:

圖5

5. 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的情況,因此儘量不要設置太大的數值。

圖7

6. shear_range

datagen = image.ImageDataGenerator(shear_range=0.5)

shear_range就是錯切變換,效果就是讓所有點的x座標(或者y座標)保持不變,而對應的y座標(或者x座標)則按比例發生平移,且平移的大小和該點到x軸(或y軸)的垂直距離成正比。

如圖8所示,一個黑色矩形圖案變換爲藍色平行四邊形圖案。狗狗圖片變換效果如圖9所示。

圖8圖9

7. 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:

圖11

8. channel_shift_range

datagen = image.ImageDataGenerator(channel_shift_range=10)

channel_shift_range可以理解成改變圖片的顏色,通過對顏色通道的數值偏移,改變圖片的整體的顏色,這意味着是“整張圖”呈現某一種顏色,像是加了一塊有色玻璃在圖片前面一樣,因此它並不能單獨改變圖片某一元素的顏色,如黑色小狗不能變成白色小狗。當數值爲10時,效果如圖12;當數值爲100時,效果如圖13,可見當數值越大時,顏色變深的效果越強。

圖12圖13

9. horizontal_flip & vertical_flip

datagen = image.ImageDataGenerator(horizontal_flip=True)

horizontal_flip的作用是隨機對圖片執行水平翻轉操作,意味着不一定對所有圖片都會執行水平翻轉,每次生成均是隨機選取圖片進行翻轉。效果如圖14。

圖14
datagen = 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_centerfeaturewise_std_normalizationzca_whitening時需要此函數。

    • X:numpy array,樣本數據,秩應爲4.在黑白圖像的情況下channel軸的值爲1,在彩色圖像情況下值爲3

    • augment:布爾值,確定是否使用隨即提升過的數據

    • round:若設augment=True,確定要在數據上進行多少輪數據提升,默認值爲1

    • seed: 整數,隨機數種子

  • 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  # 否則生成器會退出循環


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章