Skip to content
All AI projects

Academic project · University of Aberdeen · September 2026

Blood Cell Image Classification

Comparing a convolutional neural network with logistic regression across four blood-cell classes, and investigating overfitting, class imbalance, and generalisation to a small test set.

Image classification
CNNs
Image preprocessing
Error analysis
Blood-cell classes
4
Test images
71
Models compared
2

The problem

Classify microscopy images as eosinophil, lymphocyte, monocyte, or neutrophil. I compared a CNN that learns spatial image features with a logistic regression model operating on flattened pixels, then examined how training behaviour and class-level errors affected performance on the supplied test set.

My contribution

  • Prepared RGB images using a stratified training/validation split, resizing, pixel normalisation, and consistent class-label mapping.
  • Implemented a three-block CNN and a multiclass logistic regression baseline, with validation-loss monitoring and early stopping.
  • Compared test accuracy and confusion matrices, and analysed the gap between training, validation, and test performance.

Approach

  1. Prepare the image data

    Split the 9,957 supplied training images into 7,965 training and 1,992 validation images, preserving class proportions. Keep the 71 supplied test images separate.

  2. Resize and normalise

    Resize RGB images from 240 × 320 to 96 × 128 pixels while preserving aspect ratio. Scale pixel intensities to the range 0–1 and map the four classes to integer labels.

  3. Compare spatial and linear models

    The CNN uses 16, 32, and 64 convolutional filters with max pooling, followed by a dense layer and four output logits. Logistic regression uses flattened pixels and a four-output linear layer.

  4. Monitor learning and errors

    Use Adam and sparse categorical cross-entropy, with early stopping on validation loss and restoration of the best weights. Compare learning curves and per-class confusion matrices.

Results & findings

The CNN correctly classified 40 of 71 test images (56.34%); logistic regression classified 36 correctly (50.70%). For context, always predicting the most common test class, neutrophil, would classify 48 images correctly (67.61%). These results highlight the need to improve generalisation and evaluate class-level performance.

Blood Cell Image Classification: comparison results
Model / referenceCorrect predictionsTest accuracy
CNN40 / 7156.34%
Logistic regression36 / 7150.70%
Majority-class reference (always neutrophil)48 / 7167.61%

Test distribution: 13 eosinophils, 6 lymphocytes, 4 monocytes, and 48 neutrophils. The majority-class reference always predicts neutrophil; it is not a trained model.

Download results CSV

Experiments & analysis

Select a figure to view it at full size.

Four-class test confusion matrices for CNN and logistic regression. The CNN has 40 correct predictions and logistic regression has 36, out of 71 images each.
Test confusion matrices show the class imbalance and where each model makes errors.
Test accuracy comparison: CNN 56.338 percent and logistic regression 50.7042 percent.
CNN and logistic regression test accuracy. The majority-class reference is shown in the results table above.
CNN training and validation accuracy and loss, with best validation-loss epoch marked at 12 and a widening gap between training and validation performance.
CNN learning curves: continued improvement on training data accompanies a widening validation gap.
Logistic regression training and validation accuracy and loss, showing unstable learning and best validation-loss epoch at 20.
Logistic regression learning curves show unstable convergence and limited validation performance.
Inspect the CNN architecture

Three convolution-and-pooling blocks learn spatial features before a dense layer produces four class scores. Inputs are 96 × 128 RGB images.

Python / Keras · Source excerpt

cnn_model = Sequential([
    layers.Input(shape=(IMG_HEIGHT, IMG_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.Flatten(),
    layers.Dense(128, activation='relu'),
    layers.Dense(NUM_CLASSES)
])

Limitations & what I learned

Where I would take this next

  • Investigate image-source differences, label consistency, and error examples to understand the validation-to-test gap.
  • Evaluate augmentation, regularisation, and transfer learning using validation data, then measure macro F1, balanced accuracy, and per-class recall on a larger held-out set.