<p>For the benefit of community here i am explaining, how to use <code>image_generator</code> in Tensorflow with input_shape <code>(100, 100, 3)</code> using <code>dogs vs cats</code> dataset </p>

<p>If we haven't choose right batch size there is a chance of model struck right after first epoch, hence i am starting my explanation with <code>how to choose batch_size ?</code>  </p>

<p>We generally observe that <code>batch size</code> to be the <code>power of 2</code>, this is because of the effective work of optimized matrix operation libraries. This is further elaborated in <a href="https://arxiv.org/abs/1303.2314" rel="nofollow noreferrer">this</a> research paper. </p>

<p>Check out <a href="https://mydeeplearningnb.wordpress.com/2019/02/23/convnet-for-classification-of-cifar-10/" rel="nofollow noreferrer">this</a> blog which describes how to choose the right <code>batch size</code> while comparing the effects of different batch sizes on the <code>accuracy</code> of CIFAR-10 dataset.</p>

<p>Here is the end to end working code with outputs</p>

<pre><code>import os
import numpy as np
from keras import layers
import pandas as pd
from tensorflow.keras.layers import Input, Dense, Activation, ZeroPadding2D, BatchNormalization, Flatten, Conv2D
from tensorflow.keras.layers import AveragePooling2D, MaxPooling2D, Dropout, GlobalMaxPooling2D, GlobalAveragePooling2D
from tensorflow.keras.models import Sequential
from tensorflow.keras import regularizers, optimizers
from tensorflow.keras.preprocessing import image
from tensorflow.keras.preprocessing.image import ImageDataGenerator

import keras.backend as K

K.set_image_data_format('channels_last')

train_dir  = '/content/drive/My Drive/Dogs_Vs_Cats/train'
test_dir = '/content/drive/My Drive/Dogs_Vs_Cats/test'

img_width, img_height = 100, 100
input_shape = img_width, img_height, 3

train_samples = 2000
test_samples = 1000
epochs = 30
batch_size = 32

train_datagen = ImageDataGenerator(
    rescale = 1. /255,
    shear_range = 0.2,
    zoom_range = 0.2,
    horizontal_flip = True)

test_datagen = ImageDataGenerator(
    rescale = 1. /255)

train_data = train_datagen.flow_from_directory(
    train_dir,
    target_size = (img_width, img_height),
    batch_size = batch_size,
    class_mode = 'binary')

test_data = test_datagen.flow_from_directory(
    test_dir,
    target_size = (img_width, img_height),
    batch_size = batch_size,
    class_mode = 'binary')


model = Sequential()

model.add(Conv2D(32, (7, 7), strides = (1, 1), input_shape = input_shape))
model.add(BatchNormalization(axis = 3))
model.add(Activation('relu'))
model.add(MaxPooling2D((2, 2)))

model.add(Conv2D(64, (7, 7), strides = (1, 1)))
model.add(BatchNormalization(axis = 3))
model.add(Activation('relu'))
model.add(MaxPooling2D((2, 2)))

model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))
model.summary() 


model.compile(loss = 'binary_crossentropy',
            optimizer = 'rmsprop',
            metrics = ['accuracy'])

model.fit_generator(
        train_data,
        steps_per_epoch = train_samples//batch_size,
        epochs = epochs,
        validation_data = test_data,
        verbose = 1,
        validation_steps = test_samples//batch_size)
</code></pre>

<p>Output:</p>

<pre><code>Found 2000 images belonging to 2 classes.
Found 1000 images belonging to 2 classes.
Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_8 (Conv2D)            (None, 94, 94, 32)        4736      
_________________________________________________________________
batch_normalization_8 (Batch (None, 94, 94, 32)        128       
_________________________________________________________________
activation_8 (Activation)    (None, 94, 94, 32)        0         
_________________________________________________________________
max_pooling2d_8 (MaxPooling2 (None, 47, 47, 32)        0         
_________________________________________________________________
conv2d_9 (Conv2D)            (None, 41, 41, 64)        100416    
_________________________________________________________________
batch_normalization_9 (Batch (None, 41, 41, 64)        256       
_________________________________________________________________
activation_9 (Activation)    (None, 41, 41, 64)        0         
_________________________________________________________________
max_pooling2d_9 (MaxPooling2 (None, 20, 20, 64)        0         
_________________________________________________________________
flatten_4 (Flatten)          (None, 25600)             0         
_________________________________________________________________
dense_11 (Dense)             (None, 64)                1638464   
_________________________________________________________________
dropout_4 (Dropout)          (None, 64)                0         
_________________________________________________________________
dense_12 (Dense)             (None, 1)                 65        
=================================================================
Total params: 1,744,065
Trainable params: 1,743,873
Non-trainable params: 192
_________________________________________________________________
Epoch 1/30
62/62 [==============================] - 14s 225ms/step - loss: 1.8307 - accuracy: 0.4853 - val_loss: 0.6931 - val_accuracy: 0.5000
Epoch 2/30
62/62 [==============================] - 14s 226ms/step - loss: 0.7085 - accuracy: 0.4832 - val_loss: 0.6931 - val_accuracy: 0.5010
Epoch 3/30
62/62 [==============================] - 14s 218ms/step - loss: 0.6955 - accuracy: 0.5300 - val_loss: 0.6894 - val_accuracy: 0.5292
Epoch 4/30
62/62 [==============================] - 14s 221ms/step - loss: 0.6938 - accuracy: 0.5407 - val_loss: 0.7309 - val_accuracy: 0.5262
Epoch 5/30
62/62 [==============================] - 14s 218ms/step - loss: 0.6860 - accuracy: 0.5498 - val_loss: 0.6776 - val_accuracy: 0.5665
Epoch 6/30
62/62 [==============================] - 13s 216ms/step - loss: 0.7027 - accuracy: 0.5407 - val_loss: 0.6895 - val_accuracy: 0.5101
Epoch 7/30
62/62 [==============================] - 13s 216ms/step - loss: 0.6852 - accuracy: 0.5528 - val_loss: 0.6567 - val_accuracy: 0.5887
Epoch 8/30
62/62 [==============================] - 13s 217ms/step - loss: 0.6772 - accuracy: 0.5427 - val_loss: 0.6643 - val_accuracy: 0.5847
Epoch 9/30
62/62 [==============================] - 13s 217ms/step - loss: 0.6709 - accuracy: 0.5534 - val_loss: 0.6623 - val_accuracy: 0.5887
Epoch 10/30
62/62 [==============================] - 14s 219ms/step - loss: 0.6579 - accuracy: 0.5711 - val_loss: 0.6614 - val_accuracy: 0.6058
Epoch 11/30
62/62 [==============================] - 13s 218ms/step - loss: 0.6591 - accuracy: 0.5625 - val_loss: 0.6594 - val_accuracy: 0.5454
Epoch 12/30
62/62 [==============================] - 13s 216ms/step - loss: 0.6419 - accuracy: 0.5767 - val_loss: 1.1041 - val_accuracy: 0.5161
Epoch 13/30
62/62 [==============================] - 13s 215ms/step - loss: 0.6479 - accuracy: 0.5783 - val_loss: 0.6441 - val_accuracy: 0.5837
Epoch 14/30
62/62 [==============================] - 13s 216ms/step - loss: 0.6373 - accuracy: 0.5899 - val_loss: 0.6427 - val_accuracy: 0.6310
Epoch 15/30
62/62 [==============================] - 13s 215ms/step - loss: 0.6203 - accuracy: 0.6133 - val_loss: 0.7390 - val_accuracy: 0.6220
Epoch 16/30
62/62 [==============================] - 13s 217ms/step - loss: 0.6277 - accuracy: 0.6362 - val_loss: 0.6649 - val_accuracy: 0.5786
Epoch 17/30
62/62 [==============================] - 13s 215ms/step - loss: 0.6155 - accuracy: 0.6316 - val_loss: 0.9823 - val_accuracy: 0.5484
Epoch 18/30
62/62 [==============================] - 14s 222ms/step - loss: 0.6056 - accuracy: 0.6408 - val_loss: 0.6333 - val_accuracy: 0.6048
Epoch 19/30
62/62 [==============================] - 14s 218ms/step - loss: 0.6025 - accuracy: 0.6529 - val_loss: 0.6514 - val_accuracy: 0.6442
Epoch 20/30
62/62 [==============================] - 13s 215ms/step - loss: 0.6149 - accuracy: 0.6423 - val_loss: 0.6373 - val_accuracy: 0.6048
Epoch 21/30
62/62 [==============================] - 13s 215ms/step - loss: 0.6030 - accuracy: 0.6519 - val_loss: 0.6086 - val_accuracy: 0.6573
Epoch 22/30
62/62 [==============================] - 13s 217ms/step - loss: 0.5936 - accuracy: 0.6865 - val_loss: 1.0677 - val_accuracy: 0.5605
Epoch 23/30
62/62 [==============================] - 13s 214ms/step - loss: 0.5964 - accuracy: 0.6728 - val_loss: 0.7927 - val_accuracy: 0.5877
Epoch 24/30
62/62 [==============================] - 13s 215ms/step - loss: 0.5866 - accuracy: 0.6707 - val_loss: 0.6116 - val_accuracy: 0.6421
Epoch 25/30
62/62 [==============================] - 13s 214ms/step - loss: 0.5933 - accuracy: 0.6662 - val_loss: 0.8282 - val_accuracy: 0.6048
Epoch 26/30
62/62 [==============================] - 13s 214ms/step - loss: 0.5705 - accuracy: 0.6885 - val_loss: 0.5806 - val_accuracy: 0.6966
Epoch 27/30
62/62 [==============================] - 14s 218ms/step - loss: 0.5709 - accuracy: 0.7017 - val_loss: 1.2404 - val_accuracy: 0.5333
Epoch 28/30
62/62 [==============================] - 13s 216ms/step - loss: 0.5691 - accuracy: 0.7104 - val_loss: 0.6136 - val_accuracy: 0.6442
Epoch 29/30
62/62 [==============================] - 13s 215ms/step - loss: 0.5627 - accuracy: 0.7048 - val_loss: 0.6936 - val_accuracy: 0.6613
Epoch 30/30
62/62 [==============================] - 13s 214ms/step - loss: 0.5714 - accuracy: 0.6941 - val_loss: 0.5872 - val_accuracy: 0.6825
</code></pre>
input_shape with image_generator in Tensorflow
<p>I'm trying to use this approach in Tensorflow 2.X to load large dataset that does not fit in memory.</p>

<p>I have a folder with X sub-folders that contains images. Each sub-folder is a class.</p>

<pre><code>\dataset
    -\class1
        -img1_1.jpg
        -img1_2.jpg
        -...
    -\classe2
        -img2_1.jpg
        -img2_2.jpg
        -...
</code></pre>

<p>I create my data generator from my folder like this:</p>

<pre><code>train_data_gen = image_generator.flow_from_directory(directory="path\\to\\dataset",
                                                     batch_size=100,
                                                     shuffle=True,
                                                     target_size=(100, 100), # Image H x W
                                                     classes=list(CLASS_NAMES)) # list of folder/class names ["class1", "class2", ...., "classX"]
</code></pre>

<blockquote>
  <p>Found 629 images belonging to 2 classes.</p>
</blockquote>

<p>I've did a smaller dataset to test the pipeline. Only 629 images in 2 classes.
Now I can create a dummy model like this:</p>

<pre><code>model = tf.keras.Sequential()
model.add(Dense(1, activation=activation, input_shape=(100, 100, 3))) # only 1 layer of 1 neuron
model.add(Dense(2)) # 2classes
model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=['categorical_accuracy'])
</code></pre>

<p>Once compile I try to fit this dummy model:</p>

<pre><code>STEPS_PER_EPOCH = np.ceil(image_count / batch_size) # 629 / 100
model.fit_generator(generator=train_data_gen , steps_per_epoch=STEPS_PER_EPOCH, epochs=2, verbose=1)

1/7 [===&gt;..........................] - ETA: 2s - loss: 1.1921e-07 - categorical_accuracy: 0.9948
2/7 [=======&gt;......................] - ETA: 1s - loss: 1.1921e-07 - categorical_accuracy: 0.5124
3/7 [===========&gt;..................] - ETA: 0s - loss: 1.1921e-07 - categorical_accuracy: 0.3449
4/7 [================&gt;.............] - ETA: 0s - loss: 1.1921e-07 - categorical_accuracy: 0.2662
5/7 [====================&gt;.........] - ETA: 0s - loss: 1.1921e-07 - categorical_accuracy: 0.2130
6/7 [========================&gt;.....] - ETA: 0s - loss: 1.1921e-07 - categorical_accuracy: 0.1808
</code></pre>

<blockquote>
  <p>2020-04-14 20:39:48.629203: W tensorflow/core/framework/op_kernel.cc:1610] Invalid argument: ValueError: <code>generator</code> yielded an element of shape (29, 100, 100, 3) where an element of shape (100, 100, 100, 3) was expected.</p>
</blockquote>

<p>From what i understand, the last batch doesn't has the same shape has the previous batches. So it crashes. I've tried to specify a <code>batch_input_shape</code>.</p>

<pre><code>model.add(Dense(1, activation=activation, batch_input_shape=(None, 100, 100, 3)))
</code></pre>

<p>I've found <a href="https://stackoverflow.com/a/52126464/5462743">here</a> that I should put <code>None</code> to not specify the number of elements in the batch so it can be dynamic. But no success.</p>

<p>Edit: From the comment I had 2 mistakes:</p>

<ul>
<li>The output shape was bad. I missed the flatten layer in the model.</li>
<li>The previous link does work with the correction of the flatten layer</li>
<li>Missing some code, I actually feed the <code>fit_generator</code> with a <code>tf.data.Dataset.from_generator</code> but I gave here a <code>image_generator.flow_from_directory</code>.</li>
</ul>

<p>Here is the final code:</p>

<pre><code>train_data_gen = image_generator.flow_from_directory(directory="path\\to\\dataset",
    batch_size=1000,
    shuffle=True,
    target_size=(100, 100),
    classes=list(CLASS_NAMES))

train_dataset = tf.data.Dataset.from_generator(
    lambda: train_data_gen,
    output_types=(tf.float32, tf.float32),
    output_shapes=([None, x, y, 3],
    [None, len(CLASS_NAMES)]))

model = tf.keras.Sequential()
model.add(Flatten(batch_input_shape=(None, 100, 100, 3)))
model.add(Dense(1, activation=activation))
model.add(Dense(2))
model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=['categorical_accuracy'])

STEPS_PER_EPOCH = np.ceil(image_count / batch_size) # 629 / 100
model.fit_generator(generator=train_data_gen , steps_per_epoch=STEPS_PER_EPOCH, epochs=2, verbose=1)
</code></pre>