Question: I wrote a simple CNN using tensorflow (v2.4) + keras in python (v3.8.3). I am trying to optimize the network, and I want more info on what it is failing to predict. I am trying to add a confusion matrix, and I need to feed tensorflow.math.confusion_matrix() the test labels. My problem is that I cannot figure out how to access the labels from the dataset object created by tf.keras.preprocessing.image_dataset_from_directory() My images are organized in directories having the label as the name. The documentation says the function returns a tf.data.Dataset object.  If label_mode is None, it yields float32 tensors of shape (batch_size, image_size[0], image_size[1], num_channels), encoding  images (see below for rules regarding num_channels). Otherwise, it yields a tuple (images, labels), where images has shape (batch_size, image_size[0], image_size[1], num_channels), and labels follows the format described below.  Here is the code: import tensorflow as tf from tensorflow.keras import layers #import matplotlib.pyplot as plt import numpy as np import random import PIL import PIL.Image import os import pathlib #load the IMAGES dataDirectory = '/p/home/username/tensorflow/newBirds' dataDirectory = pathlib.Path(dataDirectory) imageCount = len(list(dataDirectory.glob('*/*.jpg'))) print('Image count: {0}\n'.format(imageCount)) #test display an image # osprey = list(dataDirectory.glob('OSPREY/*')) # ospreyImage = PIL.Image.open(str(osprey[random.randint(1,100)])) # ospreyImage.show() # nFlicker = list(dataDirectory.glob('NORTHERN FLICKER/*')) # nFlickerImage = PIL.Image.open(str(nFlicker[random.randint(1,100)])) # nFlickerImage.show() #set parameters batchSize = 32 height=224 width=224 (trainData, trainLabels) = tf.keras.preprocessing.image_dataset_from_directory( dataDirectory, labels='inferred', label_mode='categorical', validation_split=0.2, subset='training', seed=324893, image_size=(height,width), batch_size=batchSize) testData = tf.keras.preprocessing.image_dataset_from_directory( dataDirectory, labels='inferred', label_mode='categorical', validation_split=0.2, subset='validation', seed=324893, image_size=(height,width), batch_size=batchSize) #class names and sampling a few images classes = trainData.class_names testClasses = testData.class_names #plt.figure(figsize=(10,10)) # for images, labels in trainData.take(1): # for i in range(9): # ax = plt.subplot(3, 3, i+1) # plt.imshow(images[i].numpy().astype("uint8")) # plt.title(classes[labels[i]]) # plt.axis("off") # plt.show() #buffer to hold the data in memory for faster performance autotune = tf.data.experimental.AUTOTUNE trainData = trainData.cache().shuffle(1000).prefetch(buffer_size=autotune) testData = testData.cache().prefetch(buffer_size=autotune) #augment the dataset with zoomed and rotated images #use convolutional layers to maintain spatial information about the images #use max pool layers to reduce #flatten and then apply a dense layer to predict classes model = tf.keras.Sequential([ #layers.experimental.preprocessing.RandomFlip('horizontal', input_shape=(height, width, 3)), #layers.experimental.preprocessing.RandomRotation(0.1), #layers.experimental.preprocessing.RandomZoom(0.1), layers.experimental.preprocessing.Rescaling(1./255, input_shape=(height, width, 3)), layers.Conv2D(16, 3, padding='same', activation='relu'), layers.MaxPooling2D(), layers.Conv2D(32, 3, padding='same', activation='relu'), layers.MaxPooling2D(), layers.Conv2D(64, 3, padding='same', activation='relu'), layers.MaxPooling2D(), layers.Conv2D(128, 3, padding='same', activation='relu'), layers.MaxPooling2D(), layers.Conv2D(256, 3, padding='same', activation='relu'), layers.MaxPooling2D(), # layers.Conv2D(512, 3, padding='same', activation='relu'), # layers.MaxPooling2D(), #layers.Conv2D(1024, 3, padding='same', activation='relu'), #layers.MaxPooling2D(), #dropout prevents overtraining by not allowing each node to see each datapoint #layers.Dropout(0.5), layers.Flatten(), layers.Dense(512, activation='relu'), layers.Dense(len(classes)) ]) model.compile(optimizer='adam', loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True), metrics=['accuracy']) model.summary() epochs=2 history = model.fit( trainData, validation_data=testData, epochs=epochs ) #create confusion matrix predictions = model.predict_classes(testData) confusionMatrix = tf.math.confusion_matrix(labels=testClasses, predictions=predictions).numpy()  I have tried using (foo, foo1) = tf.keras.preprocessing.image_dataset_from_directory(dataDirectory, etc), but I get (trainData, trainLabels) = tf.keras.preprocessing.image_dataset_from_directory( ValueError: too many values to unpack (expected 2) And if I try to return as one variable and then split it as so: train = tf.keras.preprocessing.image_dataset_from_directory( dataDirectory, labels='inferred', label_mode='categorical', validation_split=0.2, subset='training', seed=324893, image_size=(height,width), batch_size=batchSize) trainData = train[0] trainLabels = train[1]  I get TypeError: 'BatchDataset' object is not subscriptable I can access the labels via testClasses = testData.class_names, but I get:  2020-11-03 14:15:14.643300: W tensorflow/core/framework/op_kernel.cc:1740] OP_REQUIRES failed at cast_op.cc:121 : Unimplemented: Cast string to int64 is not supported Traceback (most recent call last): File "birdFake.py", line 115, in confusionMatrix = tf.math.confusion_matrix(labels=testClasses, predictions=predictions).numpy() File "/p/home/username/miniconda3/lib/python3.8/site-packages/tensorflow/python/util/dispatch.py", line 201, in wrapper return target(*args, **kwargs) File "/p/home/username/miniconda3/lib/python3.8/site-packages/tensorflow/python/ops/confusion_matrix.py", line 159, in confusion_matrix labels = math_ops.cast(labels, dtypes.int64) File "/p/home/username/miniconda3/lib/python3.8/site-packages/tensorflow/python/util/dispatch.py", line 201, in wrapper return target(*args, **kwargs) File "/p/home/username/miniconda3/lib/python3.8/site-packages/tensorflow/python/ops/math_ops.py", line 966, in cast x = gen_math_ops.cast(x, base_type, name=name) File "/p/home/username/miniconda3/lib/python3.8/site-packages/tensorflow/python/ops/gen_math_ops.py", line 1827, in cast _ops.raise_from_not_ok_status(e, name) File "/p/home/username/miniconda3/lib/python3.8/site-packages/tensorflow/python/framework/ops.py", line 6862, in raise_from_not_ok_status six.raise_from(core._status_to_exception(e.code, message), None) File "", line 3, in raise_from tensorflow.python.framework.errors_impl.UnimplementedError: Cast string to int64 is not supported [Op:Cast]  I am open to any method to get those labels into the confusion matrix. Any ideas as to why what I am doing is not working would also be appreciated. UPDATE: I tried the method proposed by Alexandre Catalano, and I get the following error  Traceback (most recent call last): File "./birdFake.py", line 118, in labels = np.concatenate([labels, np.argmax(y.numpy(), axis=-1)]) File "<array_function internals>", line 5, in concatenate ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 0 dimension(s)  I printed the first element of the labels array, and it is zero

Answer: If I were you, I'll iterate over the entire testData, I'll save the predictions and labels along the way and I'll build the confusion matrix at the end. testData = tf.keras.preprocessing.image_dataset_from_directory( dataDirectory, labels='inferred', label_mode='categorical', seed=324893, image_size=(height,width), batch_size=32) predictions = np.array([]) labels = np.array([]) for x, y in testData: predictions = np.concatenate([predictions, model.predict_classes(x)]) labels = np.concatenate([labels, np.argmax(y.numpy(), axis=-1)]) tf.math.confusion_matrix(labels=labels, predictions=predictions).numpy()  and the result is Found 4 files belonging to 2 classes. array([[2, 0], [2, 0]], dtype=int32)