天天看點

深度解析 Keras 中的圖檔預處理:圖檔生成器 ImageDataGeneraor1. ImageDataGenerator 類2. ImageDataGenerator 類方法

  一般在使用 Keras 進行圖像方面的神經網絡的訓練的時候都會使用圖檔生成器 ImageDataGeneraor,它不僅僅使用友善,更支援實時資料增強。在使用 CPU 進行資料增強的同時,使用 GPU 訓練模型,大大加快了模型訓練的速度。

  Keras 的《官方文檔》 和早期網友翻譯的《文檔》都對它的使用進行了較為詳細的解釋,可能由于篇幅的限制,這些文檔對于一些較為細節的問題沒有過多的介紹,在這裡我在前人的基礎上進行一些補充。注意本文主要參照上面兩個文檔的内容而成,我自己單獨補充的部分進行了黑體加粗。

1. 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-06, 
                                             rotation_range=0, 
                                             width_shift_range=0.0, 
                                             height_shift_range=0.0, 
                                             brightness_range=None, 
                                             shear_range=0.0, 
                                             zoom_range=0.0, 
                                             channel_shift_range=0.0, 
                                             fill_mode='nearest', 
                                             cval=0.0, 
                                             horizontal_flip=False, 
                                             vertical_flip=False, 
                                             rescale=None, 
                                             preprocessing_function=None, 
                                             data_format=None, 
                                             validation_split=0.0, 
                                             dtype=None)
           

這個類用以生成一個batch的圖像資料,支援實時資料提升。訓練時該函數會無限生成資料,直到達到規定的 epoch 次數為止。

1.1 參數

  • featurewise_center: 布爾值。将輸入資料的均值設定為 0,逐特征進行。這裡所謂的逐特征進行,實際上就是逐通道進行,單通道圖像減去的均值的是 [[[average]]],三通道圖像減去的均值是 [[[R_average, G_average, B_average]]]
  • samplewise_center: 布爾值。将每個樣本的均值設定為 0。
  • featurewise_std_normalization: 布爾值。将輸入除以資料标準差,逐特征進行。
  • samplewise_std_normalization: 布爾值。将每個輸入除以其标準差。
  • zca_epsilon: ZCA 白化的 epsilon 值,預設為 1e-6。
  • zca_whitening: 布爾值。是否應用 ZCA 白化。
  • rotation_range: 整數。随機旋轉的度數範圍。
  • width_shift_range: 浮點數、一維數組或整數。随機水準移動的幅度範圍。

    float: 如果 <1,則是除以總寬度的值,或者如果 >=1,則為像素值。

    1-D 數組: 數組中的随機元素。

    int: 來自間隔 (-width_shift_range, +width_shift_range) 之間的整數個像素。

    width_shift_range=2 時,可能值是整數 [-1, 0, +1],與 width_shift_range=[-1, 0, +1] 相同;而 width_shift_range=1.0 時,可能值是 [-1.0, +1.0) 之間的浮點數。

  • height_shift_range: 浮點數、一維數組或整數。随機水準移動的幅度範圍。

    float: 如果 <1,則是除以總寬度的值,或者如果 >=1,則為像素值。

    1-D array-like: 數組中的随機元素。

    int: 來自間隔 (-height_shift_range, +height_shift_range) 之間的整數個像素。

    height_shift_range=2 時,可能值是整數 [-1, 0, +1],與 height_shift_range=[-1, 0, +1] 相同;而 height_shift_range=1.0 時,可能值是 [-1.0, +1.0) 之間的浮點數。

  • shear_range: 浮點數。剪切強度(以弧度逆時針方向剪切角度)。

    補充:什麼是剪切變換?

    剪切變換是仿射變換的一種原始變換,指的是類似四邊形不穩定性的那種性質,方形變四邊形,任意一邊均可被拉長的過程。

  • zoom_range: 浮點數 或 [lower, upper]。随機縮放範圍。如果是浮點數,[lower, upper] = [1-zoom_range, 1+zoom_range]。
  • channel_shift_range: 浮點數。随機通道轉換的範圍,或者可以了解為随機通道偏移的幅度。
  • fill_mode: {“constant”, “nearest”, “reflect” or “wrap”} 之一。常用于填充由于旋轉平移造成的圖像空白,預設為 ‘nearest’。輸入邊界以外的點根據給定的模式填充:

    ‘constant’: kkkkkkkk|abcd|kkkkkkkk (cval=k)

    ‘nearest’: aaaaaaaa|abcd|dddddddd

    ‘reflect’: abcddcba|abcd|dcbaabcd

    ‘wrap’: abcdabcd|abcd|abcdabcd

  • cval: 浮點數或整數。用于邊界之外的點的值,當 fill_mode = “constant” 時。
  • horizontal_flip: 布爾值。随機水準翻轉。
  • vertical_flip: 布爾值。随機垂直翻轉。
  • rescale: 重縮放因子。預設為 None。如果是 None 或 0,不進行縮放,否則将資料乘以所提供的值(在應用任何其他轉換之前)。常常用于将 [0, 255] 的輸入圖像縮放到 [0, 1]。
  • preprocessing_function: 應用于每個輸入的函數。這個函數會在任何其他改變之前運作。這個函數需要一個參數:一張圖像(秩為 3 的 Numpy 張量),并且應該輸出一個同尺寸的 Numpy 張量。

    也就是說這是在 Keras 進行實時資料增強之前的預處理。我們需要寫這樣的一個函數,它的輸入是本來進行實時資料增強的圖像,輸出的是與原來大小相同的圖像,相當于先執行一種 Keras 中沒有的圖像預處理方法。在下面會給出一個使用例子。

  • data_format: 圖像資料格式,{“channels_first”, “channels_last”} 之一。“channels_last” 模式表示圖像輸入尺寸應該為 (samples, height, width, channels),“channels_first” 模式表示輸入尺寸應該為 (samples, channels, height, width)。預設為 在 Keras 配置檔案 ~/.keras/keras.json 中的 image_data_format 值。如果你從未設定它,那它就是 “channels_last”。
  • validation_split: 浮點數。Float. 保留用于驗證的圖像的比例(嚴格在0和1之間)。
  • dtype: 生成數組使用的資料類型。

1.2 關于仿射變換進度的補充

  在上面的的各種增強方式中,旋轉,平移,剪切變換等各種仿射變換,均采用的是 1 階的插值方法,具體可以參考如下的代碼

cd /usr/local/lib/python2.7/dist-packages/keras_preprocessing
           

之後找到定義

apply_affine_transform

函數的位置,可以看到

if transform_matrix is not None:
        h, w = x.shape[row_axis], x.shape[col_axis]
        transform_matrix = transform_matrix_offset_center(
            transform_matrix, h, w)
        x = np.rollaxis(x, channel_axis, 0)
        final_affine_matrix = transform_matrix[:2, :2]
        final_offset = transform_matrix[:2, 2]

        channel_images = [scipy.ndimage.interpolation.affine_transform(
            x_channel,
            final_affine_matrix,
            final_offset,
            order=1,
            mode=fill_mode,
            cval=cval) for x_channel in x]
        x = np.stack(channel_images, axis=0)
        x = np.rollaxis(x, 0, channel_axis + 1)
           

其中

scipy.ndimage.interpolation.affine_transform

的參數

order=1

,其代表的含義是樣條插值的階,正常是使用

order=3

。而且從

scipy.ndimage.interpolation.affine_transform

的代碼可以看到,他實際上沒有進行任何插值操作,如下

def spline_filter(input, order=3, output=numpy.float64):
    """
    Multi-dimensional spline filter.

    For more details, see `spline_filter1d`.

    See Also
    --------
    spline_filter1d

    Notes
    -----
    The multi-dimensional filter is implemented as a sequence of
    one-dimensional spline filters. The intermediate arrays are stored
    in the same data type as the output. Therefore, for output types
    with a limited precision, the results may be imprecise because
    intermediate results may be stored with insufficient precision.

    """
    if order < 2 or order > 5:
        raise RuntimeError('spline order not supported')
    input = numpy.asarray(input)
    if numpy.iscomplexobj(input):
        raise TypeError('Complex type not supported')
    output = _ni_support._get_output(output, input)
    if order not in [0, 1] and input.ndim > 0:
        for axis in range(input.ndim):
            spline_filter1d(input, order, axis, output=output)
            input = output
    else:
        output[...] = input[...]
    return output
           

可以看到,輸入是什麼樣,輸出就是什麼樣。是以直接使用,會降低仿射變換後圖像的精度。

2. ImageDataGenerator 類方法

2.1 apply_transform

根據給定的參數将變換應用于圖像。

參數

  • x: 3D 張量,單張圖像。
  • transform_parameters: 字元串 - 參數 對表示的字典,用于描述轉換。目前,使用字典中的以下參數:

    ‘theta’: 浮點數。旋轉角度(度)。

    ‘tx’: 浮點數。在 x 方向上移動。

    ‘ty’: 浮點數。在 y 方向上移動。

    shear’: 浮點數。剪切角度(度)。

    ‘zx’: 浮點數。放大 x 方向。

    ‘zy’: 浮點數。放大 y 方向。

    ‘flip_horizontal’: 布爾 值。水準翻轉。

    ‘flip_vertical’: 布爾值。垂直翻轉。

    ‘channel_shift_intencity’: 浮點數。頻道轉換強度。

    ‘brightness’: 浮點數。亮度轉換強度。

傳回

  輸入的轉換後版本(相同尺寸)。

2.2 fit

  将資料生成器用于某些示例資料。它基于一組樣本資料,計算與資料轉換相關的内部資料統計。當且僅當

featurewise_center

featurewise_std_normalization

zca_whitening

設定為 True 時才需要。

參數

  • x: 樣本資料。秩應該為 4。對于灰階資料,通道軸的值應該為 1;對于 RGB 資料,值應該為 3。
  • augment: 布爾值(預設為 False)。是否使用随機樣本擴張。
  • rounds: 整數(預設為 1)。如果資料資料增強(augment=True),表明在資料上進行多少次增強。
  • seed: 整數(預設 None)。随機種子。

關于“去均值”的補充

  首先需要科普一個問題,為什麼使用圖像訓練神經網絡的時候需要進行去均值,以及應該從那個次元去除均值。去均值把輸入資料各個次元都中心化為0,避免資料過多偏差,影響訓練效果,同時可以加快模型的收斂速度。一般是從通道的次元去除均值,即分别求出 B, G, R 三個通道的均值,比如說[[[0.21568, 0.22314, 0.25657]]],然後每個圖像都減去它。

  有一點需要尤為注意,這裡減去的均值是 [0, 1] 區間的。這主要是因為在執行諸如

flow_from_directory

的指令時,其首先進行歸一化(recale = 1/255.0),然後在進行去均值化。進入定義 image.py 的檔案夾

cd /usr/local/lib/python2.7/dist-packages/keras_preprocessing
           

進入 image.py 檔案,找到定義

def standardize(self, x)

的函數,看到其具體實作如下

def standardize(self, x):
    """Applies the normalization configuration to a batch of inputs.

    # Arguments
        x: Batch of inputs to be normalized.

    # Returns
        The inputs, normalized.
    """
    if self.preprocessing_function:
        x = self.preprocessing_function(x)
    if self.rescale:
        x *= self.rescale
    if self.samplewise_center:
        x -= np.mean(x, keepdims=True)
    if self.samplewise_std_normalization:
        x /= (np.std(x, keepdims=True) + 1e-6)

    if self.featurewise_center:
        if self.mean is not None:
            x -= self.mean
        else:
            warnings.warn('This ImageDataGenerator specifies '
                          '`featurewise_center`, but it hasn\'t '
                          'been fit on any training data. Fit it '
                          'first by calling `.fit(numpy_data)`.')
    if self.featurewise_std_normalization:
        if self.std is not None:
            x /= (self.std + 1e-6)
        else:
            warnings.warn('This ImageDataGenerator specifies '
                          '`featurewise_std_normalization`, '
                          'but it hasn\'t '
                          'been fit on any training data. Fit it '
                          'first by calling `.fit(numpy_data)`.')
    if self.zca_whitening:
        if self.principal_components is not None:
            flatx = np.reshape(x, (-1, np.prod(x.shape[-3:])))
            whitex = np.dot(flatx, self.principal_components)
            x = np.reshape(whitex, x.shape)
        else:
            warnings.warn('This ImageDataGenerator specifies '
                          '`zca_whitening`, but it hasn\'t '
                          'been fit on any training data. Fit it '
                          'first by calling `.fit(numpy_data)`.')
    return x
           

可以看到它是先進行 rescale,然後再進行減均值,如果你做了 reascale = 1/255.0 ,那麼減的均值一定是位與 [0,1] 區間的。

  通過手工指定均值,标準差的方法,可以使得 train, validation, test 減去相同的均值,除以相同的标準差。但是樣本資料 x 需要将圖檔讀入到記憶體中,在計算均值等資料,當資料量過大的時候,沒有辦法一次性将資料讀入,這個時候可以考慮使用這樣的方法。首先通過手工計算的方式求出目前資料的均值,然後修改 Keras 底層源碼,将減去由 x 計算出的均值改為減去人為指定的均值。

  具體的修改方式是,進入定義 image.py 的檔案夾

cd /usr/local/lib/python2.7/dist-packages/keras_preprocessing
           

進入 image.py 檔案,找到定義類

ImageDataGenerator(object)

中進行初始化

def __init__

的位置,進行如下的修改,其中在注釋的下面

# self.mean = None
        self.mean = [[[55, 56, 57]]]
        self.std = None
        self.principal_components = None
           

其中被注釋掉的是原始的初始化的值,這裡改為給定一個數作為初始化的值。其中原理的在于,原始隻有經過 fit(x) 之後才可以給

self.mean

指派為計算出的均值;而我們這裡不進行 fir(x) 的計算,直接改變

self.mean

的初始值,這樣當調用

def standardize(self, x)

的時候,就可以使用我們手工指定的初值了。

  但是在計算均值的過程中有一點需要注意,使用 Opencv 預設讀入圖檔的顔色順序為BGR,而使用PIL預設讀入圖檔的顔色順序為 RGB。Keras 在使用圖檔生成器讀入資料的時候調用的是 PIL,是以如果是通過 Opencv 計算的均值需要将順序從 BGR 調整為 RGB 才可以使用。

2.3 flow

flow(x, y=None, batch_size=32, shuffle=True, sample_weight=None, seed=None, 
     save_to_dir=None, save_prefix='', save_format='png', subset=None)
           

  接收numpy數組和标簽為參數,生成經過資料提升或标準化後的batch資料,并在一個無限循環中不斷的傳回batch資料

參數

  • x: 輸入資料。秩為 4 的 Numpy 矩陣或元組。如果是元組,第一個元素應該包含圖像,第二個元素是另一個 Numpy 數組或一列 Numpy 數組,它們不經過任何修改就傳遞給輸出。可用于将模型雜項資料與圖像一起輸入。對于灰階資料,圖像數組的通道軸的值應該為 1,而對于 RGB 資料,其值應該為 3。
  • y: 标簽。
  • batch_size: 整數 (預設為 32)。
  • shuffle: 布爾值 (預設為 True)。
  • sample_weight: 樣本權重。
  • seed: 整數(預設為 None)。
  • save_to_dir: None 或 字元串(預設為 None)。這使您可以選擇指定要儲存的正在生成的增強圖檔的目錄(用于可視化您正在執行的操作)。
  • save_prefix: 字元串(預設 ‘’)。儲存圖檔的檔案名字首(僅當 save_to_dir 設定時可用)。
  • save_format: “png”, “jpeg” 之一(僅當 save_to_dir 設定時可用)。預設:“png”。
  • subset: 資料子集 (“training” 或 “validation”),如果 在 ImageDataGenerator 中設定了 validation_split。

傳回

  一個生成元組

(x, y)

Iterator

,其中

x

是圖像資料的 Numpy 數組(在單張圖像輸入時),或 Numpy 數組清單(在額外多個輸入時),

y

是對應的标簽的 Numpy 數組。如果 ‘sample_weight’ 不是 None,生成的元組形式為

(x, y, sample_weight)

。如果 y 是 None, 隻有 Numpy 數組 x 被傳回。

2.4 flow_from_directory

flow_from_directory(directory, target_size=(256, 256), color_mode='rgb', 
                    classes=None, class_mode='categorical', batch_size=32, 
                    shuffle=True, seed=None, save_to_dir=None, save_prefix='', 
                    save_format='png', follow_links=False, subset=None, 
                    interpolation='nearest')
           

參數

  • directory: 目标目錄的路徑。每個類應該包含一個子目錄。任何在子目錄樹下的 PNG, JPG, BMP, PPM 或 TIF 圖像,都将被包含在生成器中。
  • target_size: 整數元組 (height, width),預設:(256, 256)。所有的圖像将被調整到的尺寸。
  • color_mode: “grayscale”, “rbg” 之一。預設:“rgb”。圖像是否被轉換成 1 或 3 個顔色通道。
  • classes: 可選的類的子目錄清單(例如 [‘dogs’, ‘cats’])。預設:None。如果未提供,類的清單将自動從 directory 下的 子目錄名稱/結構 中推斷出來,其中每個子目錄都将被作為不同的類(類名将按字典序映射到标簽的索引)。包含從類名到類索引的映射的字典可以通過 class_indices 屬性獲得。
  • class_mode: “categorical”, “binary”, “sparse”, “input” 或 None 之一。預設:“categorical”。決定傳回的标簽數組的類型:

    “categorical” 将是 2D one-hot 編碼标簽,

    “binary” 将是 1D 二進制标簽,“sparse” 将是 1D 整數标簽,

    “input” 将是與輸入圖像相同的圖像(主要用于自動編碼器)。

    如果為 None,不傳回标簽(生成器将隻産生批量的圖像資料,對于 model.predict_generator(), model.evaluate_generator() 等很有用)。請注意,如果 class_mode 為 None,那麼資料仍然需要駐留在 directory 的子目錄中才能正常工作。

  • batch_size: 一批資料的大小(預設 32)。
  • shuffle: 是否混洗資料(預設 True)。
  • seed: 可選随機種子,用于混洗和轉換。
  • save_to_dir: None 或 字元串(預設 None)。這使你可以最佳地指定正在生成的增強圖檔要儲存的目錄(用于可視化你在做什麼)。
  • save_prefix: 字元串。 儲存圖檔的檔案名字首(僅當 save_to_dir 設定時可用)。
  • save_format: “png”, “jpeg” 之一(僅當 save_to_dir 設定時可用)。預設:“png”。
  • follow_links: 是否跟蹤類子目錄中的符号連結(預設為 False)。
  • subset: 資料子集 (“training” 或 “validation”),如果 在 ImageDataGenerator 中設定了 validation_split。
  • interpolation: 在目标大小與加載圖像的大小不同時,用于重新采樣圖像的插值方法。 支援的方法有 “nearest”, “bilinear”, and “bicubic”。 如果安裝了 1.1.3 以上版本的 PIL 的話,同樣支援 “lanczos”。 如果安裝了 3.4.0 以上版本的 PIL 的話,同樣支援 “box” 和 “hamming”。 預設情況下,使用 “nearest”。這個參數應該是在新版 keras (2.2.4) 中新添加的,在原來的 2.1.5 中是沒有的。

  從這裡可以看出

flow

flow_from_directory

的差別在于,

flow

的輸入是已經讀入的圖像,而

flow_from_directory

的輸入是存放圖像的檔案夾。

繼續閱讀