【CV03】如何使用 Keras 歸一化、去均值、標準化圖像數據


1.Channels-First 和 Channels-Last 介紹

在Keras API中 數據有 format 參數,可以指定 Channels-FirstChannels-Last

彩色圖像具有高度,寬度和顏色通道尺寸。當表示爲三維數組時,默認情況下,圖像數據的通道維度爲最後一個維度,但出於性能調整(performance-tuning)的原因,可能會將其移動爲第一維度。

1.1 圖像轉化爲3維數組

圖像可以作爲三維數組存儲在內存中。通常,圖像格式的三個維度分別爲:高度、寬度、通道數。如果圖像是灰度圖,則通道尺寸不會明確顯示。彩色圖像通常具有三個通道,紅色,綠色和藍色分量的像素值。

深度學習神經網絡要求將圖像數據提供爲三維數組。有兩種方法可以將圖像數據表示爲三維數組。第一個將通道作爲數組的第三個維度,稱爲 Channels-Last,尺寸爲[rows,cols,channels]。第二個將通道作爲數組中的第一個維度,稱爲Channels-First,尺寸爲[channels,rows,cols]。

1.2 給灰度圖擴充通道維度

可以使用NumPy數組的 expand_dims() 函數實現。axis 參數允許指定將新尺寸添加到第一個尺寸的位置,例如,first首先用於通道,last用於通道最後。

from numpy import expand_dims
from numpy import asarray
from PIL import Image

img = Image.open('cat.jpg')

img = img.convert(mode='L')

data = asarray(img)
print(data.shape)

# add channels first
data_first = expand_dims(data, axis=0)
print(data_first.shape)

# add channels last
data_last = expand_dims(data, axis=2)
print(data_last.shape)

輸出:

(3264, 4928)
(1, 3264, 4928)
(3264, 4928, 1)

擴展數組維數的另一種流行的替代方法是使用NumPy的 reshape() 函數指定新形狀的元組。例如:

data = data.reshape((424, 640, 1))

1.3 更改圖像通道順序

將彩色圖像加載爲三維數組後,可以更改通道順序。

可以使用NumPy的 moveaxis() 函數實現。它需要指定原軸和目標軸的索引。

from numpy import moveaxis
from numpy import asarray
from PIL import Image

img = Image.open('cat.jpg')

data = asarray(img)
print(data.shape)

# change channels last to channels first format
data = moveaxis(data, 2, 0)
print(data.shape)

# change channels first to channels last format
data = moveaxis(data, 0, 2)
print(data.shape)

輸出:

(3264, 4928, 3)
(3, 3264, 4928)
(3264, 4928, 3)

1.4 強制修改通道順序

from keras import backend

# force channels-first ordering
backend.set_image_data_format('channels_first')
print(backend.image_data_format())

# force channels-last ordering
backend.set_image_data_format('channels_last')
print(backend.image_data_format())

查看通道順序:

from tensorflow.keras import backend
print(backend.image_data_format())

【注意】根據TensorFlow官方文檔提醒 fit_generator() 方法在未來版本中會棄用,下文僅做展示用。fit() 方法支持迭代器,通過指定 step_per_epoch 參數和 validation_steps 參數設置(step乘以batch size即爲每次訓練或驗證所用的樣本數),API詳細參數如下:

fit(
    x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None,
    validation_split=0.0, validation_data=None, shuffle=True, class_weight=None,
    sample_weight=None, initial_epoch=0, steps_per_epoch=None,
    validation_steps=None, validation_batch_size=None, validation_freq=1,
    max_queue_size=10, workers=1, use_multiprocessing=False
)

官方文檔:https://www.tensorflow.org/api_docs/python/tf/keras/Model#fit


2. 像素縮放ImageDataGenerator類

Keras中的ImageDataGenerator類提供了在建模之前縮放圖像數據集中的像素值的技術。該類將包裝圖像數據集,然後在請求時將在訓練,驗證或評估期間將圖像批量返回給算法,並及時應用縮放操作。當使用神經網絡建模時,這提供了一種有效且方便的方法來縮放圖像數據。

ImageDataGenerator類支持多種像素縮放方法以及多種數據增強技術。本文專注於像素縮放技術,數據增強方法以後介紹。

ImageDataGenerator類支持的三種主要類型的像素縮放技術如下:

  • 像素歸一化(Normalization):將像素值縮放到0-1範圍。
  • 像素居中(Centering):將像素值縮放爲均值爲0,即去均值。
  • 像素標準化(Standardization):將像素值縮放爲均值爲0和方差爲1。

通過在構造實例時爲ImageDataGenerator指定參數來選擇像素縮放比例的選擇;例如:

在兩個級別上支持像素標準化:每個圖像(稱爲樣本方式)或每個數據集(稱爲特徵方式)。具體而言,可以僅根據每個圖像(樣本方式)或整個訓練數據集(特徵方式)中的像素值來計算標準化像素值所需的均值和/或均值和標準差統計量。

還有其它像素縮放方法,例如ZCA,增亮(brightening)等,但本文介紹三種最常見的方法。(白化(whitening):假設訓練數據是圖像,由於圖像中相鄰像素之間具有很強的相關性,所以用於訓練時輸入是冗餘的。白化的目的就是降低輸入的冗餘性。

通過在構造實例時爲ImageDataGenerator指定參數來選擇像素縮放比例的選擇;例如:

# create and configure the data generator
datagen = ImageDataGenerator(...)

準備好之後,通過調用 flow() 函數檢索迭代器來返回數據批次並將其傳遞給 fit_generator() 函數,從而將數據生成器用於擬合神經網絡模型。

# get batch iterator
train_iterator = datagen.flow(trainX, trainy)
# fit model
model.fit_generator(train_iterator, ...)

如果需要驗證數據集,則可以從同一數據生成器創建一個單獨的批處理迭代器,該迭代器將執行相同的像素縮放操作,並使用在訓練數據集上計算的任何所需統計信息。

# get batch iterator for training
train_iterator = datagen.flow(trainX, trainy)
# get batch iterator for validation
val_iterator = datagen.flow(valX, valy)
# fit model
model.fit_generator(train_iterator, validation_data=val_iterator, ...)

擬合後,可以通過爲測試數據集創建批處理迭代器並在模型上調用 validate_generator() 函數來評估模型。

如果需要,將執行相同的像素縮放操作,並且將使用在訓練數據集上計算的統計信息。

# get batch iterator for testing
test_iterator = datagen.flow(testX, testy)
# evaluate model loss on test dataset
loss = model.evaluate_generator(test_iterator, ...)

3.使用ImageDataGenerator標準化圖像

ImageDataGenerator類可用於將像素值從0-255範圍重新縮放到神經網絡模型首選的0-1範圍。

3.1 代碼說明

將數據縮放到0-1的範圍通常稱爲歸一化(normalization)。

# create generator (1.0/255.0 = 0.003921568627451)
datagen = ImageDataGenerator(rescale=1.0/255.0)

接下來,可以使用生成器爲訓練和測試數據集創建迭代器。設置批處理大小爲64,即將圖像的所有訓練數據集和測試數據集分爲64個圖像組,然後在從迭代器返回時對其進行縮放。

通過打印每個迭代器的長度,可以查看一個epoch有多少個batch,例如,一次通過訓練數據集。

# prepare an iterators to scale images
train_iterator = datagen.flow(trainX, trainY, batch_size=64)
test_iterator = datagen.flow(testX, testY, batch_size=64)
print('Batches train=%d, test=%d' % (len(train_iterator), len(test_iterator)))

通過檢索第一批縮放圖像並檢查最小和最大像素值來確認是否已按預期執行了像素標準化。

# confirm the scaling works
batchX, batchy = train_iterator.next()
print('Batch shape=%s, min=%.3f, max=%.3f' % (batchX.shape, batchX.min(), batchX.max()))

使用數據生成器來擬合和評估模型。定義一個簡單的卷積神經網絡模型,訓練5個epoch,有60,000個樣本除以每批次64個樣本,每個epoch有938個批次。

# fit model with generator
model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5)

擬合後,在測試數據集上評估模型,將約10,000張圖像除以每批64個樣本,每個epoch約有157個batch。

_, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0)
print('Test Accuracy: %.3f' % (acc * 100))

3.2 完整示例

from keras.datasets import mnist
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Dense, Flatten
from keras.preprocessing.image import ImageDataGenerator

# 加載數據(灰度圖)
(trainX, trainY), (testX, testY) = mnist.load_data()

# 添加通道維度
width, height, channels = trainX.shape[1], trainX.shape[2], 1
trainX = trainX.reshape((trainX.shape[0], width, height, channels))
testX = testX.reshape((testX.shape[0], width, height, channels))

# one-hot編碼
trainY = to_categorical(trainY)
testY = to_categorical(testY)

# 確認是否縮放數據
print('Train min=%.3f, max=%.3f' % (trainX.min(), trainX.max()))
print('Test min=%.3f, max=%.3f' % (testX.min(), testX.max()))

# 創建生成器,用於縮放批次數據 (1.0/255.0 = 0.003921568627451)
datagen = ImageDataGenerator(rescale=1.0/255.0)

train_iterator = datagen.flow(trainX, trainY, batch_size=64)
test_iterator = datagen.flow(testX, testY, batch_size=64)
print('Batches train=%d, test=%d' % (len(train_iterator), len(test_iterator)))

# 確認是否縮放數據
batchX, batchy = train_iterator.next()
print('Batch shape=%s, min=%.3f, max=%.3f' % (batchX.shape, batchX.min(), batchX.max()))

# 定義神經網絡模型
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(width, height, channels)))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dense(10, activation='softmax'))

# 編譯模型
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# 以迭代器的方式加載數據並訓練模型
model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5)

# 評估模型
_, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0)
print('Test Accuracy: %.3f' % (acc * 100))

輸出:

Train min=0.000, max=255.000
Test min=0.000, max=255.000
Batches train=938, test=157
Batch shape=(64, 28, 28, 1), min=0.000, max=1.000
Epoch 1/5
938/938 [==============================] - 19s 20ms/step - loss: 0.1766 - accuracy: 0.9466
Epoch 2/5
938/938 [==============================] - 12s 13ms/step - loss: 0.0564 - accuracy: 0.9823
Epoch 3/5
938/938 [==============================] - 12s 13ms/step - loss: 0.0403 - accuracy: 0.9876
Epoch 4/5
938/938 [==============================] - 12s 13ms/step - loss: 0.0300 - accuracy: 0.9908
Epoch 5/5
938/938 [==============================] - 12s 13ms/step - loss: 0.0238 - accuracy: 0.9925
Test Accuracy: 98.920

運行示例打印訓練和測試集的最小和最大像素值,證實了原始數據的像素值在0-255範圍內。訓練數據集每個epoch有938個batch,測試數據集每個epoch有157個batch。從數據集中檢索第一批,並確認它包含64個圖像,其高度和寬度(行和列)分別爲28個像素和1個通道,並且新的最小和最大像素值分別爲0和1。這證實了歸一化已具有所需的效果。

4. 使用ImageDataGenerator將圖像居中

另一種流行的像素縮放方法是計算整個訓練數據集的平均像素值,然後從每個圖像中減去它。這稱爲居中(centering),也就是說,居中圖像的平均像素值將爲零

4.1 代碼說明

ImageDataGenerator類的居中使用在訓練數據集上計算出的平均值按特徵進行居中。它要求在縮放之前在訓練數據集上計算統計信息。

# create generator that centers pixel values
datagen = ImageDataGenerator(featurewise_center=True)
# calculate the mean on the training dataset
datagen.fit(trainX)

與計算每個圖像的平均像素值不同,Keras也支持按樣本居中,不需要在訓練數據集上計算任何統計量。

# create generator that centers pixel values
datagen = ImageDataGenerator(samplewise_center=True)

可以通過計算從批處理迭代器返回的一批圖像的均值來確認縮放過程已達到預期的效果。希望平均值是一個接近零的小值,但由於批次中的圖像數量少,所以不會爲零。

# get a batch
batchX, batchy = iterator.next()
# mean pixel value in the batch
print(batchX.shape, batchX.mean())

更好的檢查方法是將批次大小設置爲訓練數據集的大小(例如60,000個樣本),檢索一個批次,然後計算平均值。它應該是一個非常小的值,接近零。

# try to flow the entire training dataset
iterator = datagen.flow(trainX, trainy, batch_size=len(trainX), shuffle=False)
# get a batch
batchX, batchy = iterator.next()
# mean pixel value in the batch
print(batchX.shape, batchX.mean())

4.2 完整示例

from keras.datasets import mnist
from keras.preprocessing.image import ImageDataGenerator

# load dataset
(trainX, trainy), (testX, testy) = mnist.load_data()

# reshape dataset to have a single channel
width, height, channels = trainX.shape[1], trainX.shape[2], 1
trainX = trainX.reshape((trainX.shape[0], width, height, channels))
testX = testX.reshape((testX.shape[0], width, height, channels))

# report per-image mean
print('Means train=%.3f, test=%.3f' % (trainX.mean(), testX.mean()))

# create generator that centers pixel values
datagen = ImageDataGenerator(featurewise_center=True)

# calculate the mean on the training dataset
datagen.fit(trainX)
print('Data Generator Mean: %.3f' % datagen.mean)

# demonstrate effect on a single batch of samples
iterator = datagen.flow(trainX, trainy, batch_size=64)
# get a batch
batchX, batchy = iterator.next()
print(batchX.shape, batchX.mean())

# demonstrate effect on entire training dataset
iterator = datagen.flow(trainX, trainy, batch_size=len(trainX), shuffle=False)
batchX, batchy = iterator.next()
print(batchX.shape, batchX.mean())

輸出:

Means train=33.318, test=33.791
Data Generator Mean: 33.318
(64, 28, 28, 1) -0.3869071
(60000, 28, 28, 1) -1.9512918e-05

可以確認平均像素值是接近零的小值。使用整個訓練數據集作爲批次大小重複測試,在這種情況下,縮放數據集的平均像素值爲非常接近零的數字,從而確認居中具有預期的效果。


5.使用ImageDataGenerator標準化圖像

標準化是一種數據縮放技術,它假定數據的分佈是高斯分佈,並且將數據的分佈偏移爲平均值爲0,標準差爲1,具有這種分佈的數據稱爲標準高斯分佈。在訓練神經網絡時會很有用,因爲數據集的總和爲零,並且輸入是大約-3.0到3.0的大致範圍內的小值(例如,這些值的99.7%將落在平均值的三個標準差之內)。

通過減去平均像素值並將結果除以像素值的標準差,可以實現圖像的標準化

5.1 代碼說明

可以在訓練數據集上計算均值和標準差統計信息,如前一部分所述,Keras將其稱爲特徵(feature)。

# feature-wise generator
datagen = ImageDataGenerator(featurewise_center=True, featurewise_std_normalization=True)
# calculate mean and standard deviation on the training dataset
datagen.fit(trainX)

還可以計算統計信息,然後將其用於分別標準化每個圖像,Keras將此稱爲逐樣本標準化

# sample-wise standardization
datagen = ImageDataGenerator(samplewise_center=True, samplewise_std_normalization=True)

5.2 完整示例

from keras.datasets import mnist
from keras.preprocessing.image import ImageDataGenerator

# load dataset
(trainX, trainy), (testX, testy) = mnist.load_data()

# reshape dataset to have a single channel
width, height, channels = trainX.shape[1], trainX.shape[2], 1
trainX = trainX.reshape((trainX.shape[0], width, height, channels))
testX = testX.reshape((testX.shape[0], width, height, channels))

# report pixel means and standard deviations
print('Statistics train=%.3f (%.3f), test=%.3f (%.3f)' % (trainX.mean(), trainX.std(), testX.mean(), testX.std()))

# create generator that centers pixel values
datagen = ImageDataGenerator(featurewise_center=True, featurewise_std_normalization=True)
datagen.fit(trainX)
print('Data Generator mean=%.3f, std=%.3f' % (datagen.mean, datagen.std))

# demonstrate effect on a single batch of samples
iterator = datagen.flow(trainX, trainy, batch_size=64)
# get a batch
batchX, batchy = iterator.next()
# pixel stats in the batch
print(batchX.shape, batchX.mean(), batchX.std())

# demonstrate effect on entire training dataset
iterator = datagen.flow(trainX, trainy, batch_size=len(trainX), shuffle=False)
batchX, batchy = iterator.next()
print(batchX.shape, batchX.mean(), batchX.std())
Statistics train=33.318 (78.567), test=33.791 (79.172)
Data Generator mean=33.318, std=78.567
(64, 28, 28, 1) -0.030575937 0.9662089
(60000, 28, 28, 1) -3.4560264e-07 0.9999998

輸出:

Statistics train=33.318 (78.567), test=33.791 (79.172)
Data Generator mean=33.318, std=78.567
(64, 28, 28, 1) -0.030575937 0.9662089
(60000, 28, 28, 1) -3.4560264e-07 0.9999998

行示例將報告訓練和測試數據集中像素值的平均值和標準差。首先檢索一個batch的64張標準化圖像,可以確認其平均值和標準差接近預期的標準高斯分佈。然後在整個訓練數據集上重複測試,可以確認平均值確實是一個非常小的值,接近0.0,而標準差是一個非常接近的值1.0。


參考:
https://machinelearningmastery.com/how-to-normalize-center-and-standardize-images-with-the-imagedatagenerator-in-keras/
https://machinelearningmastery.com/a-gentle-introduction-to-channels-first-and-channels-last-image-formats-for-deep-learning/
https://www.cnblogs.com/demian/p/7627324.html

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