Am I mislabeling my data in my neural network? - python

I'm working on an implementation of EfficientNet in Tensorflow. My model is overfitting and predicting all three classes as just a single class. My training and validation accuracy is in the 99% after a few epochs and my loss is <0.5. I have 32,000 images between the three classes (12, 8, 12).
My hypothesis is that it has to do with the way I input the data and one hot coded the labels. Perhaps it is due to everything being labeled the same accidentally, but I can't figure out where.
# Load Data
train_ds = tf.keras.utils.image_dataset_from_directory(
train_dir,
labels='inferred',
seed=42,
image_size=(height, width),
batch_size=batch_size
)
val_ds = tf.keras.utils.image_dataset_from_directory(
val_dir,
labels='inferred',
seed=42,
image_size=(height, width),
batch_size=batch_size
)
class_names = train_ds.class_names
num_classes = len(class_names)
print('There are ' + str(num_classes) + ' classes:\n' + str(class_names))
# Resize images
train_ds = train_ds.map(lambda image, label: (
tf.image.resize(image, (height, width)), label))
val_ds = val_ds.map(lambda image, label: (
tf.image.resize(image, (height, width)), label))
This provides a sample of the correct images and class labels:
# # Visualization of samples
# plt.figure(figsize=(10, 10))
# for images, labels in train_ds.take(1):
# for i in range(9):
# ax = plt.subplot(3, 3, i + 1)
# plt.imshow(images[i].numpy().astype("uint8"))
# plt.title(class_names[labels[i]])
# plt.axis("off")
Could this be causing an issue with labels?
# Prepare inputs
# One-hot / categorical encoding
def input_preprocess(image, label):
label = tf.one_hot(label, num_classes)
return image, label
train_ds = train_ds.map(input_preprocess,
num_parallel_calls=tf.data.AUTOTUNE)
train_ds = train_ds.prefetch(tf.data.AUTOTUNE)
val_ds = val_ds.map(input_preprocess)
My network:
def build_model(num_classes):
inputs = Input(shape=(height, width, 3))
x = img_augmentation(inputs)
model = EfficientNetB0(
include_top=False, input_tensor=x, weights="imagenet")
# Freeze the pretrained weights
model.trainable = False
# Rebuild top
x = layers.GlobalAveragePooling2D(name="avg_pool")(model.output)
x = layers.BatchNormalization()(x)
top_dropout_rate = 0.4
x = layers.Dropout(top_dropout_rate, name="top_dropout")(x)
outputs = layers.Dense(num_classes, activation="softmax", name="pred")(x)
# Compile
model = tf.keras.Model(inputs, outputs, name="EfficientNet")
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
model.compile(
optimizer=optimizer, loss="categorical_crossentropy", metrics=["accuracy"]
)
return model
with strategy.scope():
model = build_model(num_classes=num_classes)
epochs = 40
hist = model.fit(train_ds, epochs=epochs, validation_data=val_ds,
workers=6, verbose=1, callbacks=callback)
plot_hist(hist)

Well first off you are writing more code than you need to. In train_ds and val_ds you did not specify the parameter label_mode. By default it is set to 'int'. Which means your labels will be integers. This is fine if your compile your model using loss=tf.keras.losses.SparseCategoricalCrossentropy. If you had set
label_mode= 'categorical' then you can use loss=tf.keras.losses.CategoricalCrossentropy
You did convert you labels to one-hot-encoded and that appears to have been done correctly. But you could have avoided having to do that by setting the label mode to categorical as mentioned. You also wrote code to resize the images. This is not necessary since tf.keras.utils.image_dataset_from_directory resized the images for you. I had trouble getting your model to run probably because I don't have the code for x = img_augmentation(inputs). you have the code
model = EfficientNetB0(
include_top=False, input_tensor=x, weights="imagenet")
Since you are using the model API I think this should be
model = EfficientNetB0( include_top=False, weights="imagenet", pooling='max')(x)
NOTE I included pooliing='max' so efficientnet produces a one dimensional tensor output and thus you do not need the layer
x = layers.GlobalAveragePooling2D(name="avg_pool")(model.output)
I also modified your code to produce a test_ds so I could test the accuracy of the model. Of course I used a different dataset but the results were fine.
My complete code is shown below
train_dir=r'../input/beauty-detection-data-set/train'
val_dir=r'../input/beauty-detection-data-set/valid'
batch_size=32
height=224
width=224
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
train_dir,
labels='inferred',
validation_split=0.1,
subset="training",
label_mode='categorical',
seed=42,
image_size=(height, width),
batch_size=batch_size
)
test_ds = tf.keras.preprocessing.image_dataset_from_directory(
train_dir,
labels='inferred',
validation_split=0.1,
subset="validation",
label_mode='categorical',
seed=42,
image_size=(height, width),
batch_size=batch_size)
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
val_dir,
labels='inferred',
seed=42,
label_mode='categorical',
image_size=(height, width),
batch_size=batch_size
)
class_names = train_ds.class_names
num_classes = len(class_names)
print('There are ' + str(num_classes) + ' classes:\n' + str(class_names))
img_shape=(224,224,3)
base_model=tf.keras.applications.EfficientNetB3(include_top=False, weights="imagenet",input_shape=img_shape, pooling='max')
x=base_model.output
x=keras.layers.BatchNormalization(axis=-1, momentum=0.99, epsilon=0.001 )(x)
x = Dense(256, kernel_regularizer = regularizers.l2(l = 0.016),activity_regularizer=regularizers.l1(0.006),
bias_regularizer=regularizers.l1(0.006) ,activation='relu')(x)
x=Dropout(rate=.45, seed=123)(x)
output=Dense(num_classes, activation='softmax')(x)
model=Model(inputs=base_model.input, outputs=output)
model.compile(Adamax(lr=.001), loss='categorical_crossentropy', metrics=['accuracy'])
epochs =5
hist = model.fit(train_ds, epochs=epochs, validation_data=val_ds,
verbose=1)
accuracy =model.evaluate(test_ds, verbose=1)[1]
print (accuracy)
```

If you use labels='inferred', you should also specify class_names which will be the name of the three folders you're getting images from.

Related

UnimplementedError: Graph execution error: Fused conv implementation does not support grouped convolutions for now

I have checked to make sure that the number of channels in my model and imagedatagenerator match, and have rgb images that are passed into the model. I know this question is similar to another question that was posted but I tried those solutions and haven't been able to get past this error.
# Build model
num_channels = 1
image_size = 720
num_labels = 49
model1 = Sequential()
model1.add(Conv2D(32, (3,3), input_shape = (image_size, image_size, num_channels)))
model1.add(Activation('relu'))
model1.add(Conv2D(32, (3,3)))
model1.add(Activation('relu'))
model1.add(MaxPooling2D(pool_size=(2,2)))
model1.add(Conv2D(64, (3,3)))
model1.add(Activation('relu'))
model1.add(Conv2D(64, (3,3)))
model1.add(Activation('relu'))
model1.add(MaxPooling2D(pool_size=(2,2)))
model1.add(Flatten())
model1.add(Dense(200))
model1.add(Activation('relu'))
model1.add(Dense(200))
model1.add(Activation('relu'))
model1.add(Dense(num_labels))
model1.save_weights("ckpt")
model1.load_weights("ckpt")
model1.summary()
# load data into ImageDataGen for on the fly augmented imgs and fit into model[enter image description here][1]
CWD = os.getcwd()
# print(train_dir_file_list[0])
TRAINING_DATA_PATH = os.path.join(CWD, 'campaign_data/data/')
print(os.path.join(CWD, 'campaign_data/data/'))
IMAGE_SIZE = 720
IMG_SHAPE = (IMAGE_SIZE, IMAGE_SIZE, 1)
TRAIN_BATCH_SIZE = 32
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
# rescale=1.0/127.5, # from various posts, if resnet50.preprocess_input is used, do not rescale
rotation_range=90.,
shear_range=0.2,
# for image data rescale as such
# rescale= 1.0/255,
zoom_range=[0.8,1.2],
horizontal_flip=True,
validation_split=0.2,
preprocessing_function=tf.keras.applications.resnet50.preprocess_input)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# test_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
# preprocessing_function=tf.keras.applications.resnet50.preprocess_input)
# Next we're going to take the images from our directory in batches and categorical classes:
#flow_from_directory
train_generator = train_datagen.flow_from_directory(TRAINING_DATA_PATH,
target_size=(IMAGE_SIZE, IMAGE_SIZE),
color_mode='rgb',
batch_size=TRAIN_BATCH_SIZE,
class_mode='categorical',
shuffle=True,
subset='training',
seed=42)
validation_generator = train_datagen.flow_from_directory(TRAINING_DATA_PATH,
target_size=(IMAGE_SIZE, IMAGE_SIZE),
color_mode='rgb',
batch_size=TRAIN_BATCH_SIZE,
class_mode='categorical',
shuffle=True,
subset='validation',
seed=42)
# confirm the scaling works
batchX, batchY = train_generator.next()
print('Batch shape=%s, min=%.3f, max=%.3f' % (batchX.shape, batchX.min(), batchX.max()))
labels = train_generator.class_indices
print('\nclass_indices = ', labels)
labels_dict = dict((v,k) for k, v in labels.items())
print('\nlabels_dict = ', labels_dict)
print(train_generator.filenames[0:5])
with open('labels_dict.json', 'w') as f:
json.dump(labels_dict, f)
METRICS = [
tf.keras.metrics.TruePositives(name='tp'),
tf.keras.metrics.FalsePositives(name='fp'),
tf.keras.metrics.TrueNegatives(name='tn'),
tf.keras.metrics.FalseNegatives(name='fn'),
tf.keras.metrics.BinaryAccuracy(name='accuracy'),
tf.keras.metrics.Precision(name='precision'),
tf.keras.metrics.Recall(name='recall'),
tf.keras.metrics.AUC(name='auc'),
tf.keras.metrics.AUC(name='prc', curve='PR') # precision-recall curve
]
model1.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
loss='categorical_crossentropy',
metrics=METRICS)
callbacks = [tf.keras.callbacks.TensorBoard(log_dir='./log/transer_learning_model', update_freq='batch'),
tf.keras.callbacks.EarlyStopping(patience=4)]
print('Training model...')
# print(train_generator)
history = model1.fit(train_generator,
steps_per_epoch=train_generator.samples//TRAIN_BATCH_SIZE,
epochs=25,
validation_data=validation_generator,
validation_steps=validation_generator.samples//TRAIN_BATCH_SIZE,
callbacks=callbacks)
Here is the exact error [1]: https://i.stack.imgur.com/cF2Sa.png
And at the bottom of the error is this text
Node: 'sequential_8/conv2d_34/Relu'
Fused conv implementation does not support grouped convolutions for now.
[[{{node sequential_8/conv2d_34/Relu}}]] [Op:__inference_train_function_70418]
I think you need to change the num_channels to 3 because the image has size 720x720x3.
Also you were able to train without the first layer since the input_shape does not need to be specified. It will just create {input_channel_size} * {filer_size} number of kernels.

Keras: one-hot for labels in `image_dataset_from_directory`

I am trying to do a binary image classification using efficientNet. The following is my code.
import matplotlib.pyplot as plt
from tensorflow.keras.applications import EfficientNetB0
from tensorflow.keras.preprocessing import image_dataset_from_directory
from tensorflow.keras.models import Sequential
from tensorflow.keras import layers
from tensorflow.data import AUTOTUNE
DATA_DIR = "img/"
IMG_SIZE = 224
NUM_CLASSES = 2
EPOCH = 50
def train_val_split(DATA_DIR, IMG_SIZE):
val_data = image_dataset_from_directory(
DATA_DIR,
labels="inferred",
label_mode="binary",
color_mode="rgb",
batch_size=32,
image_size=(IMG_SIZE, IMG_SIZE),
validation_split=0.2,
subset="training",
seed=1
)
train_data = image_dataset_from_directory(
DATA_DIR,
labels="inferred",
label_mode="binary",
color_mode="rgb",
batch_size=32,
image_size=(IMG_SIZE, IMG_SIZE),
validation_split=0.2,
subset="validation",
seed=1
)
train_data = train_data.cache().prefetch(buffer_size=AUTOTUNE)
val_data = val_data.cache().prefetch(buffer_size=AUTOTUNE)
return train_data, val_data
def model_arch(NUM_CLASSES, IMG_SIZE):
"""efficientnet transfer learning"""
inputs = layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3))
img_augmentation = Sequential(
[
layers.RandomRotation(factor=0.15),
layers.RandomTranslation(height_factor=0.1, width_factor=0.1),
layers.RandomFlip(),
layers.RandomContrast(factor=0.1),
],
name="img_augmentation",
)
x = img_augmentation(inputs)
# model = EfficientNetB0(include_top=False, input_tensor=x, weights="imagenet")
model = EfficientNetB0(include_top=False, input_tensor=x, weights='model/efficientnetb0_notop.h5')
# Freeze the pretrained weights
model.trainable = False
# Rebuild top
x = layers.GlobalAveragePooling2D(name="avg_pool")(model.output)
x = layers.BatchNormalization()(x)
top_dropout_rate = 0.2
x = layers.Dropout(top_dropout_rate, name="top_dropout")(x)
outputs = layers.Dense(NUM_CLASSES, activation="softmax", name="pred")(x)
# Compile
model = tf.keras.Model(inputs, outputs, name="EfficientNet")
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-2)
model.compile(
optimizer=optimizer,
loss="binary_crossentropy",
metrics=["accuracy"]
)
return model
if __name__ == "__main__":
train_data, val_data = train_val_split(DATA_DIR, IMG_SIZE)
model = model_arch(NUM_CLASSES, IMG_SIZE)
hist = model.fit(train_data,
epochs=EPOCH,
validation_data=val_data,
verbose=1)
However, I encountered the following error.
ValueError: logits and labels must have the same shape ((None, 2) vs (None, 1))
I found out that this is because the labels loaded in image_dataset_from_directory is not one-hot encoded.
print(train_data)
<PrefetchDataset shapes: ((None, 224, 224, 3), (None, 1)), types: (tf.float32, tf.float32)>
How can I tweak the code for train_data and val_data so that it can fit into the model without issues?
Thanks.
Managed to figure out the answer!
import tensorflow as tf
# one-hot encoding
train_data = train_data.map(lambda x, y: (x, tf.one_hot(y, depth=NUM_CLASSES)))
val_data = val_data.map(lambda x, y: (x, tf.one_hot(y, depth=NUM_CLASSES)))
You need to change label_mode to a better option.
In your case, I think you want label_mode='categorical',.
As #Dr. Snoopy said the information is here: https://www.tensorflow.org/api_docs/python/tf/keras/utils/image_dataset_from_directory

Issue with shape on CNN with grayscale images

I made a CNN for classification with 10 classes.
input = 49000 grayscale pictures, all mixed (I mean training set and validation set)
train.csv contain the list of the images and their labels like this for example :
id,label
0.png,4
1.png,9
2.png,1
3.png,7
4.png,3
5.png,9
Here a part of the CNN:
traindf = pd.read_csv('train.csv', dtype=str)
datagen = ImageDataGenerator(rescale=1./255, validation_split=0.25, shear_range = 0.2, zoom_range = 0.2, horizontal_flip = True)
train_generator = datagen.flow_from_dataframe(dataframe = traindf,
directory='images',
target_size = (28, 28),
color_mode='grayscale',
x_col='id',
y_col='label',
subset='training',
batch_size = 32,
shuffle=True,
class_mode = 'categorical')
valid_generator = datagen.flow_from_dataframe(dataframe = traindf,
directory="images",
target_size=(28,28),
color_mode='grayscale',
x_col="id",
y_col="label",
subset="validation",
batch_size=32,
shuffle=True,
class_mode="categorical")
STEP_SIZE_TRAIN = train_generator.n//train_generator.batch_size
STEP_SIZE_VALID = valid_generator.n//valid_generator.batch_size
and the fit method:
classifier.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])
classifier.fit_generator(train_generator,
steps_per_epoch = STEP_SIZE_TRAIN,
epochs = 10,
validation_data = valid_generator,
validation_steps = STEP_SIZE_VALID)
for the sake of clarity I do not display the creation of the neural networks, but note that input_shape = (28,28,1)
Then I save the model:
filepath = './NumChar_model_01'
save_model(classifier, filepath)
In another program, I want to predict an image:
loaded_model = load_model(filepath,custom_objects=None, compile=True)
img = img.resize((28, 28))
img = image.img_to_array(img)
img = np.expand_dims(img, axis = 0)
result = loaded_model.predict(img)
And I got this error:
ValueError: Input 0 of layer sequential is incompatible with the layer: expected axis -1 of input shape to have value 1 but received input with shape [None, 28, 28, 3]
I know it's an issue with shape, but believe me, I read all the world topic about that...
I tried reshape on train_generator and valid_generator but it give me an error too ('DataFrameIterator' object has no attribute 'reshape')
SOLUTION:
img was gotten by
img = ImageGrab.grab((cx, cy, cx + cw, cy + ch))
It returns an RGB image, which throw the error. So I convert it into a grayscale image with:
img = img.convert('L')
and it works!

how to fix overfitting or where is my fault in my code

I'm using a pre-trained model Vgg16 to do 100 classification problem. The dataset is tiny-imagenet, each class has 500 images, and I random choose 100 class from tiny-imagenet for my training(400) and validation(100) data. So I change input_shape of vgg16 for 32*32 size.
The results always look like overfitting. Training acc is high, but val_acc always stuck at almost 40%.
I used dropout, regularization L2, data augmentation ... , but val_acc is also stuck at almost 40%.
How could I do for overfitting or correct my code.
Thanks
img_width, img_height = 32, 32
epochs = 50
learning_rate = 1e-4
steps_per_epoch = 2500
train_path='./training_set_100A/'
valid_path='./testing_set_100A/'
test_path='./testing_set_100A/'
class_num = 100
train_batches = ImageDataGenerator(rescale=1. / 255
,rotation_range=20, zoom_range=0.15,
width_shift_range=0.2, height_shift_range=0.2,
shear_range=0.15,
horizontal_flip=True, fill_mode="nearest"
).flow_from_directory(
train_path, target_size=(img_width,img_height),
batch_size=32, shuffle=True)
valid_batches = ImageDataGenerator(rescale=1. / 255).flow_from_directory(
valid_path, target_size=(img_width,img_height),
batch_size=10, shuffle=False)
test_batches = ImageDataGenerator(rescale=1. / 255).flow_from_directory(
test_path, target_size=
(img_width,img_height),batch_size=10,shuffle=False)
seqmodel = Sequential()
VGG16Model = VGG16(weights='imagenet', include_top=False)
input = Input(shape=(img_width, img_height, 3), name='image_intput')
output_vgg16_conv = VGG16Model(input)
x = Flatten()(output_vgg16_conv)
x = Dense(4096, activation='relu')(x)
x = Dropout(0.5)(x)
x = Dense(4096, activation='relu')(x)
x = Dropout(0.5)(x)
x = Dense(class_num, activation='softmax')(x)
funcmodel = Model([input], [x])
funcmodel.summary()
funcmodel.compile(optimizer=SGD(lr=learning_rate, momentum=0.9),
loss='categorical_crossentropy', metrics=['accuracy'])
train_history = funcmodel.fit_generator(train_batches,
steps_per_epoch=steps_per_epoch, validation_data=valid_batches,
validation_steps=1000, epochs=epochs, verbose=1)
`
It seems you followed examples of implementing this from other sites, but you're training samples are very small to train the 2 new Dense layers of 4096 size each.
you have to either lower the size of you layers or add a lot more samples 20,000 instead of 500.
1) 50epoch is too much. Try running smaller epoch?
2) Check your validation accuracy for every epoch?
3) VGG is too deep for your small(32 * 32) image data. Try building your own network with lesser number of parameters. or Try Lenet?

get same output when making prediction

I'm new in ML. I am trying to make a basic example of image classification containing digits. I created my own dataset but I get a bad accuracy (11%). I have 246 items for training and 62 for testing.
Here is my code:
#TRAINING
def load_data(input_path, img_height, img_width):
data = []
labels = []
for imagePath in os.listdir(input_path):
labels_path = os.path.join(input_path, imagePath)
if os.path.isdir(labels_path):
for img_path in os.listdir(labels_path):
labels.append(imagePath)
img_full_path = os.path.join(labels_path, img_path)
img = image.load_img(img_full_path, target_size=(img_height, img_width))
img = image.img_to_array(img)
data.append(img)
return data, labels
train_data = []
train_labels = []
test_data = []
test_labels = []
train_data, train_labels = load_data(train_path, 28, 28)
test_data, test_labels = load_data(test_path, 28, 28)
train_data = np.array(train_data)
train_data = train_data / 255.0
train_data = tf.reshape(train_data, train_data.shape[:3])
train_labels = np.array(train_labels)
train_labels = np.asfarray(train_labels,float)
test_data = np.array(test_data)
test_data = tf.reshape(test_data, test_data.shape[:3])
test_data = test_data / 255.0
test_labels = np.array(test_labels)
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(512, activation=tf.nn.relu),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_data, train_labels, batch_size=10, epochs=5, steps_per_epoch=246)
test_loss, test_acc = model.evaluate(test_data, test_labels, steps=1)
print('Test accuracy:', test_acc)
#CLASSIFICATION
def classify(input_path):
if os.path.isdir(input_path):
images = []
for file_path in os.listdir(input_path):
full_path = os.path.join(input_path, file_path)
img_tensor = preprocess_images(full_path, 28, 28, "L")
images.append(img_tensor)
images = np.array(images)
images = tf.reshape(images,(images.shape[0],images.shape[2],images.shape[3]))
predictions = model.predict(images, steps = 1)
for i in range(len(predictions)):
print("Image", i , "is", np.argmax(predictions[i]))
def preprocess_images(image_path, img_height, img_width, mode):
img = image.load_img(image_path, target_size=(img_height, img_width))
#convert 3-channel image to 1-channel
img = img.convert(mode)
img_tensor = image.img_to_array(img)
img_tensor = np.expand_dims(img_tensor, axis=0)
img_tensor /= 255.0
img_tensor = tf.reshape(img_tensor, img_tensor.shape[:3])
return tf.keras.backend.eval(img_tensor)
When I make predictions, I always get the result "Image is 5".So, I have 2 questions:
- How can I get the other classes [0-9] as output?
- Can I get better accuracy by increasing the number of data ?
Thanks.
TLDR;
Your load_data() function is to blame - you need to return the labels of the datasets as an integer rather than the string filepath
Much fuller explanation:
Can I get better accuracy by increasing the number of data ?
In general, yes.
There is nothing intrinsically wrong with your model. I obviously don't have access to the dataset you have created but I can test it on the MNIST dataset (which your dataset is presumably trying to mirror):
(train_data, train_labels),(test_data, test_labels) = tf.keras.datasets.mnist.load_data()
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(512, activation=tf.nn.relu),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_data, train_labels, batch_size=10, epochs=5)
test_loss, test_acc = model.evaluate(test_data, test_labels)
print('Test accuracy:', test_acc)
Having done so, we can train to an accuracy of roughly 93%:
Test accuracy: 0.9275
Your inference code then also works as expected on the test data:
predictions = model.predict(test_data)
for i in range(len(predictions)):
print("Image", i , "is", np.argmax(predictions[i]))
giving the output, you'd expect:
Image 0 is 7
Image 1 is 2
Image 2 is 1
Image 3 is 0
Image 4 is 4
...
So we know the model can work. So is the difference in performance simply down to the size of your dataset (246) compared to MNIST (60000)?
Well this is an easy thing to test - we can take a similarly sized slice of the MNIST data and repeat the exercise:
train_data = train_data[:246]
train_labels = train_labels[:246]
test_data = test_data[:62]
test_labels = test_labels[:62]
So this time I see a dramatic reduction in the accuracy (c. 66% this time) but I can train the model to a much higher degree of accuracy than you are seeing even with a much smaller subset.
Therefore the issue has to be with your data pre-processing (or the dataset itself).
In fact, looking at you load_data() function, I can see that the problem lies in the labels you are generating. Your labels just appear to the the image path? You have this:
# --snip--
for img_path in os.listdir(labels_path):
labels.append(imagePath) ## <-- this does not look right!
# --snip--
Whereas you need to populate labels with the integer value for the category your image belongs to (for the mnist digits this is an integer between 0 and 9)

Categories