How to Normalize, Centre, and Standardize Image Pixels in Keras
The pixel values in imagery must be scaled before furnishing the images as input to a deep learning neural network model during the training or assessment of the model.
Conventionally, the imagery would have to be scaled before the development of the model and recorded in memory or on disk in the scaled format.
An alternative strategy is to scale the imagery leveraging a preferred scaling strategy just-in-time during the training or model assessment process. Keras assists this variant of data prep for image data through the ImageDataGenerator class and API.
In this guideline, you will find out how to leverage the ImageDataGenerator class to scale pixel data just-in-time when fitting and assessing deep learning neural network models.
After going through this guide, you will be aware of:
- How to setup and leverage the ImageDataGenerator class for train, validation, and evaluation datasets of images.
- How to leverage the ImageDataGenerator to normalize pixel values when fitting and assessing a convolutional neural network model.
- How to leverage the ImageDataGenerator to centre and standardize pixel values when fitting and assessing a convolutional neural network model.
Tutorial Summarization
This tutorial is subdivided into five portions, which are:
- MNIST Handwritten Image Classification Dataset
- ImageDataGenerator class for Pixel Scaling
- How to Normalize Images with ImageDataGenerator
- How to Centre Images with ImageDataGenerator
- How to Standardize image with ImageDataGenerator
MNIST Handwritten Image Classification Dataset
Prior to delving into the leveraging of the ImageDataGenerator class for prepping image data, we must choose an image dataset on which to evaluate the generator.
The MNIST problem, is an image classification problem made up of 70,000 images of handwritten digits.
The objective of the problem is to categorize a provided image of a handwritten digit as an integer from 0 to 9. As such, it is a multiclass image classification problem.
This dataset is furnished as part of the Keras library and can be automatically downloaded (if required) and loaded into memory by a call to the keras.datasets.mnist.load_data() function.
The function gives back two tuples: one for the training inputs and outputs and one for the evaluation inputs and outputs. For instance:
1 2 3 | # example of loading the MNIST dataset from keras.datasets import mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() |
We can load the MNIST dataset and summarize the dataset. The complete instance is detailed below.
1 2 3 4 5 6 7 8 9 10 | # load and summarize the MNIST dataset from keras.datasets import mnist # load dataset (train_images, train_labels), (test_images, test_labels) = mnist.load_data() # summarize dataset shape print(‘Train’, train_images.shape, train_labels.shape) print(‘Test’, (test_images.shape, test_labels.shape)) # summarize pixel values print(‘Train’, train_images.min(), train_images.max(), train_images.mean(), train_images.std()) print(‘Test’, test_images.min(), test_images.max(), test_images.mean(), test_images.std()) |
Running the instance first loads the dataset into memory. Then the shape of the train and evaluation datasets is reported.
We can observe that all imagery are 28×28 pixels with a singular channel for black-and-white images. There are 60,000 images for the training dataset and 10,000 for the evaluation dataset.
We can also observe that pixel values are integer values between 0 and 255 and that the mean and standard deviation of the pixel values are similar between the two datasets.
1 2 3 4 | Train (60000, 28, 28) (60000,) Test ((10000, 28, 28), (10000,)) Train 0 255 33.318421449829934 78.56748998339798 Test 0 255 33.791224489795916 79.17246322228644 |
We will leverage this dataset to explore differing pixel scaling strategies leveraging the ImageDataGenerator class in Keras.
ImageDataGenerator Class for Pixel Scaling
The ImageDataGenerator class in Keras furnishes an array of strategies for scaling pixel values in your image dataset before modelling.
The class will wrap your imagery dataset, then upon request, it will give back images in batches to the algorithm during the course of training, validation, or assessment and apply the scaling operations just-in-time. This furnishes an effective and convenient strategy to scaling imagery data when modelling with neural networks.
The leveraging of the ImageDataGenerator class is as follows:
- Load your dataset
- Configure the ImageDataGenerator (e.g. build an instance)
- Calculate image statistics. (e.g. call the fit() function)
- Leverage the generator to fit the model (e.g. pass the instance to the fit_generator() function).
- Leverage the generator to assess the model (e.g. pass the instance to the evaluate_generator() function).
The ImageDataGenerator class is compatible with an array of pixel scaling strategies, in addition to a plethora of data augmentation strategies. We will concentrate on the pixel scaling strategies and leave the data augmentation strategies to a later discussion.
The three primary variants of pixel scaling strategies compatible with the ImageDataGenerator class are as follows:
- Pixel normalization: scale pixel values to the range 0-1.
- Pixel centring: scale pixel values to have a zero mean.
- Pixel standardization: scale pixel values to have a zero mean and unit variance.
The pixel standardization is compatible at two levels, either pre-image (referred to as sample wise) or per-dataset (referred to as feature wise). Particularly, the mean and/or mean and standard deviation statistics needed to standardize pixel values can be calculated from the pixel values in every image only (sample-wise) or throughout the entire training dataset (feature-wise)
Other pixel scaling strategies are compatible, like ZCA, brightening and more, but we will concentrate on three most typical strategies.
The selection of pixel scaling is chosen by mentioning arguments to the ImageDataGenerator when an instance is developed, for instance:
# create and configure the data generator
datagen = ImageDataGenerator(…)
Then, if the next scaling strategy needs that statistics be calculated throughout the training dataset, then these statistics can be calculated and recorded by calling the fit() function.
When assessing and choosing a model, it is typical to calculate these statistics on the training dataset and then apply them to the validation and evaluation datasets.
# calculate scaling statistics on the training dataset
datagen.fit(trainX)
After prepped, the data generator can be leveraged to fit a neural network model by calling the flow() function to recover an iterator that returns batches of samples and passing it to the fit_generator() function.
1 2 3 4 | # get batch iterator train_iterator = datagen.flow(trainX, trainy) # fit model model.fit_generator(train_iterator, …) |
If a validation dataset is needed, a separate batch iterator can be developed from the same generator that will carry out the same pixel scaling operations and leverage any needed statistics calculated on the training dataset.
1 2 3 4 5 6 | # 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, …) |
After fitting, the model can be assessed by developing a batch iterator for the evaluation dataset and calling the evaluate_generator() function on the model.
Again, the same pixel scaling operations will be carried out and any stats calculated on the training dataset will be leveraged if required.
1 2 3 4 | # get batch iterator for testing test_iterator = datagen.flow(testX, testy) # evaluate model loss on test dataset loss = model.evaluate_generator(test_iterator, …) |
Now that we are acquainted with how to leverage the ImageDataGenerator class for scaling pixel values, let’s observe a few particular instances.
How to Normalize Images with ImageDataGenerator
The ImageDataGenerator class can be leveraged to rescale pixel values from the range of 0-255 to the range 0-1 preferred for neural network models.
Scaling data to the range of 0-1 is conventionally referenced to as normalization.
This can be accomplished by setting the rescale argument to a ratio by which every pixel can be multiplied to accomplish the desired range.
In this scenario, the ratio is 1/255 or about 0.0039. For instance:
1 2 | # create generator (1.0/255.0 = 0.003921568627451) datagen = ImageDataGenerator(rescale=1.0/255.0) |
The ImageDataGenerator does not require to be fit in this scenario as there are no global statistics that require to be calculated.
Then, iterators can be developed leveraging the generator for both the train and evaluation datasets. We will leverage a batch size of 64. This implies that each of the train and evaluation datasets of images are divided into groups of 64 images that will then be scaled when returned from the iterator.
We can observe how many batches there will be in a single epoch, example, one pass through the training dataset, through printing the length of every iterator.
1 2 3 4 | # 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))) |
We can then confirm that the pixel normalization has been carried out as expected by recovering the first batch of scaled imagery and inspecting the min and max pixel values.
1 2 3 | # confirm the scaling works batchX, batchy = train_iterator.next() print(‘Batch shape=%s, min=%.3f, max=%.3f’ % (batchX.shape, batchX.min(), batchX.ma |
Then, we can leverage the data generator to fit and assess a model. We will define a simplistic convolutional neural network model and fit on the train_iterator for five epochs with 60k samples divided by 64 samples each batch, or approximately 938 batches per epoch.
2 | # fit model with generator model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5) |
After fitting, we will assess the model on the test dataset, with approximately 10,000 images divided by 64 samples per batch, or approximately 157 steps in a singular epoch.
2 | _, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0) print(‘Test Accuracy: %.3f’ % (acc * 100)) |
We can connect all of this together, the complete instance is detailed below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | # example of using ImageDataGenerator to normalize images from keras.datasets import mnist from keras.utils import to_categorical from keras.models import Sequential from keras.layers import Conv2D from keras.layers import MaxPooling2D from keras.layers import Dense from keras.layers import Flatten 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)) # one hot encode target values trainY = to_categorical(trainY) testY = to_categorical(testY) # confirm scale of pixels print(‘Train min=%.3f, max=%.3f’ % (trainX.min(), trainX.max())) print(‘Test min=%.3f, max=%.3f’ % (testX.min(), testX.max())) # create generator (1.0/255.0 = 0.003921568627451) datagen = ImageDataGenerator(rescale=1.0/255.0) # 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())) # define model 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’)) # compile model model.compile(optimizer=’adam’, loss=’categorical_crossentropy’, metrics=[‘accuracy’]) # fit model with generator model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5) # evaluate model _, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0) print(‘Test Accuracy: %.3f’ % (acc * 100)) |
Running the instance first reports the min and max pixel values on the train and evaluation sets. This provides confirmation that indeed the raw data has pixel values in the range 0-255.
Then, the data generator is developed and the iterators are prepped. We can observe that we have 938 batches each epoch with the training dataset and 157 batches per epoch with the evaluation dataset.
We recover the starting batch from the dataset and provide confirmation that is consists of 64 images with the height and width (rows and columns) of 28 pixels and a single channel, and that the new minimum and maximum pixel values are nil and 1 respectively. This confirms that the normalization has had the desired impact.
1 2 3 4 | 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 |
The model is then fit on the normalized image data. Training does not take long on the CPU. Lastly, the model is assessed in the evaluation dataset, applying the same normalization.
1 2 3 4 5 6 7 8 9 10 11 | Epoch 1/5 938/938 [==============================] – 12s 13ms/step – loss: 0.1841 – acc: 0.9448 Epoch 2/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0573 – acc: 0.9826 Epoch 3/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0407 – acc: 0.9870 Epoch 4/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0299 – acc: 0.9904 Epoch 5/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0238 – acc: 0.9928 Test Accuracy: 99.050 |
Now that we are acquainted with how to leverage the ImageDataGenerator in general and particularly for image normalization, let’s observe instances of pixel centring and standardization.
How to Center Images with ImageDataGenerator
Another widespread pixel scaling strategy is to calculate the mean pixel value throughout the total training dataset as feature-wise centring. It needs that the statistic is calculated on the training dataset before scaling.
1 2 3 4 | # create generator that centers pixel values datagen = ImageDataGenerator(featurewise_center=True) # calculate the mean on the training dataset datagen.fit(trainX) |
It is different to calculating of the mean pixel value for every image, which Keras references to as sample-wise centring and does not need any statistics to be calculated on the training dataset.
# create generator that centers pixel values
datagen = ImageDataGenerator(samplewise_center=True)
We will illustrate feature-wise centring in this section. After the statistic has been calculated on the training dataset, we can provide confirmation of the value by accessing and printing it, for instance:
# print the mean calculated on the training dataset.
print(datagen.mean)
We can also provide confirmation that the scaling process has had the desired impact by calculating the mean of a batch of images returned from the batch iterator. We would expect/predict the mean to be a small value near to zero, but not nil due to the minimal number of imagery in the batch.
1 2 3 4 | # get a batch batchX, batchy = iterator.next() # mean pixel value in the batch print(batchX.shape, batchX.mean()) |
An improved check would be to set the batch size to the size of the training dataset (for example, 60,000 samples), recover one batch, then calculate the mean. It should be a really minimal value close to zero.
1 2 3 4 5 6 | # 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()) |
The complete instance is detailed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # example of centering a image dataset 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() # mean pixel value in the batch print(batchX.shape, batchX.mean()) # demonstrate effect on 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()) |
Running the instance first reports the mean pixel value for the train and test datasets.
The MNIST dataset just has a singular channel as the imagery is black and white (grayscale), but if the imagery was colour, the mean pixel values would be calculated throughout all channels in all imagery within the training dataset, that is, there would not be a separate mean value for every channel.
The ImageDataGenerator is fitted on the training dataset and we can confirm that the mean pixel value matches our proprietary manual calculation.
A singular batch of centred imagery is recovered and we can confirm that the mean pixel value is a small-ish value close to zero. The test is rinsed and repeated leveraging the entire training dataset as a the batch size, and in this scenario, the mean pixel value for the scaled dataset is a number really near to zero, confirming that the centring has the desired impact.
1 2 3 4 | Means train=33.318, test=33.791 Data Generator Mean: 33.318 (64, 28, 28, 1) 0.09971977 (60000, 28, 28, 1) -1.9512918e-05 |
We can illustrate centring with our convolutional neural network generated in the prior section.
The complete instance with feature-wise centring is detailed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | # example of using ImageDataGenerator to center images from keras.datasets import mnist from keras.utils import to_categorical from keras.models import Sequential from keras.layers import Conv2D from keras.layers import MaxPooling2D from keras.layers import Dense from keras.layers import Flatten 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)) # one hot encode target values trainY = to_categorical(trainY) testY = to_categorical(testY) # create generator to center images datagen = ImageDataGenerator(featurewise_center=True) # calculate mean on training dataset datagen.fit(trainX) # 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))) # define model 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’)) # compile model model.compile(optimizer=’adam’, loss=’categorical_crossentropy’, metrics=[‘accuracy’]) # fit model with generator model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5) # evaluate model _, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0) print(‘Test Accuracy: %.3f’ % (acc * 100)) |
Running the instance preps the ImageDataGenerator, centring images leveraging statistics calculated on the training dataset.
We can observe that the performance begins off poor but does enhance. The centred pixel values will possess a range of about -227 to 227, and neural networks typically train more effectively with small inputs. Normalizing followed by centring would be an improved strategy, practically.
Critically, the model is assessed on the evaluation dataset, where the images in the evaluation dataset were centred leveraging the mean value calculated on the training dataset. This is to prevent any data leakage.
1 2 3 4 5 6 7 8 9 10 11 12 | Batches train=938, test=157 Epoch 1/5 938/938 [==============================] – 12s 13ms/step – loss: 12.8824 – acc: 0.2001 Epoch 2/5 938/938 [==============================] – 12s 13ms/step – loss: 6.1425 – acc: 0.5958 Epoch 3/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0678 – acc: 0.9796 Epoch 4/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0464 – acc: 0.9857 Epoch 5/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0373 – acc: 0.9880 Test Accuracy: 98.540 |
How to Standardize Image with ImageDataGenerator
Standardization is a data scaling strategy that goes by the assumption that the distribution of the data is Gaussian and moves the distribution of the information to possess a mean of zero and a standard deviation of one.
Data with this distribution is referenced to as a standard/conventional Gaussian. It can be advantageous when training neural networks as the dataset sums to zero and the inputs are small values in the rough range of approximately -3.0 to 3.0. (for example, 99.7 of the values will fall within three standard deviations of the mean)
Standardization of imagery is accomplished by removing the mean pixel value and dividing the outcome by the traditional deviation of the pixel values.
The mean and standard deviation stats can be calculated on the training dataset, and as detailed in the prior section, Keras references to this as feature-wise.
1 2 3 4 | # feature-wise generator datagen = ImageDataGenerator(featurewise_center=True, featurewise_std_normalization=True) # calculate mean and standard deviation on the training dataset datagen.fit(trainX) |
The stats can also be calculated then leverage to standardize every image independently, and Keras references to this as sample-wise standardization.
1 2 | # sample-wise standardization datagen = ImageDataGenerator(samplewise_center=True, samplewise_std_normalization=True) |
We will illustrate the former of feature-wise strategy to image standardization in this portion. The impact will be batches of imagery with an approximate mean of zero and a standard deviation of one.
As with the prior section, we can provide confirmation of this with some simplistic experiments. The full example is detailed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # example of standardizing a image dataset 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) # calculate the mean on the training dataset 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) # get a batch batchX, batchy = iterator.next() # pixel stats in the batch print(batchX.shape, batchX.mean(), batchX.std()) |
Running the instance first reports the mean and standard deviation of pixel values in the train and test datasets.
The data generator is then setup for feature-wise standardization and the stats are calculated on the training dataset, matching what we would predict/expect when the stats are calculated manually.
A singular batch of 64 standardized images is then recovering and we can confirm that the mean and standard deviation of this small sample is near the expected conventional Gaussian.
The evaluation is then repeated on the total training dataset and we can confirm that the mean is indeed a very minimal value near to 0.0 and the standard deviation is value very near to 1.0
1 2 3 4 | Statistics train=33.318 (78.567), test=33.791 (79.172) Data Generator mean=33.318, std=78.567 (64, 28, 28, 1) 0.010656365 1.0107679 (60000, 28, 28, 1) -3.4560264e-07 0.9999998 |
Now that we have confirmed that the standardization of pixel values is being carried out as we expect/predict, we can apply the pixel scaling while fitting and assessing a convolutional neural network model.
The complete instance is detailed here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | # example of using ImageDataGenerator to standardize images from keras.datasets import mnist from keras.utils import to_categorical from keras.models import Sequential from keras.layers import Conv2D from keras.layers import MaxPooling2D from keras.layers import Dense from keras.layers import Flatten 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)) # one hot encode target values trainY = to_categorical(trainY) testY = to_categorical(testY) # create generator to standardize images datagen = ImageDataGenerator(featurewise_center=True, featurewise_std_normalization=True) # calculate mean on training dataset datagen.fit(trainX) # 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))) # define model 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’)) # compile model model.compile(optimizer=’adam’, loss=’categorical_crossentropy’, metrics=[‘accuracy’]) # fit model with generator model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5) # evaluate model _, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0) print(‘Test Accuracy: %.3f’ % (acc * 100)) |
Running the instance configures the ImageDataGenerator class to standardize imagery, calculates the needed statistics on the training set only, then preps the train and evaluation iterators for fitting and assessing the model respectively.
1 2 3 4 5 6 7 8 9 10 11 | Epoch 1/5 938/938 [==============================] – 12s 13ms/step – loss: 0.1342 – acc: 0.9592 Epoch 2/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0451 – acc: 0.9859 Epoch 3/5 938/938 [==============================] – 12s 13ms/step – loss: 0.0309 – acc: 0.9906 Epoch 4/5 938/938 [==============================] – 13s 13ms/step – loss: 0.0230 – acc: 0.9924 Epoch 5/5 938/938 [==============================] – 13s 14ms/step – loss: 0.0182 – acc: 0.9941 Test Accuracy: 99.120 |
Extensions
This section details some concepts and ideas for extension of the guide that you might desire to explore.
- Color: Update an instance to leverage an image dataset with colour imagery and confirm that scaling is carried out throughout the entire image instead of per-channel.
- Sample-wise: Illustrate an instance of sample-wise centring or standardization of pixel imagery.
- ZCA Whitening: Illustrate an instance of leveraging the ZCA approach to imagery data preparation.
Further Reading
This portion of the blog furnishes additional resources on the subject if you are seeking to delve deeper.
API
- MNIST database of handwritten digits, Keras API
- Image Preprocessing Keras API
- Sequential Model Keras API
Articles
- MNIST database, Wikipedia
- 68-95-99.7 rule, Wikipedia
Conclusion
In this guide, you found out how to leverage the ImageDataGenerator class to scale pixel data just-in-time when fitting and assessing deep learning neural network models.
Particularly, you learned about:
- How to setup and leverage the ImageDataGenerator class for train, validation, and test datasets of imagery
- How to leverage the ImageDataGenerator to normalize pixel values when fitting and assessing a convolutional neural network model.
- How to leverage the ImageDataGenerator to centre and standardize pixel values when fitting and assessing a convolutional neural network model.