Error: expected input to have 3 channels, but got 1 channel instead

I’m training a MNIST digits classifier in Kaggle in the following way.

  1. Download and reshape the data of the openml dataset
from sklearn.datasets import fetch_openml
import matplotlib.pyplot as plt
import numpy as np
import torch
from torch.utils.data import DataLoader, Dataset
from torch import nn
from torchvision import models
from fastai.data.block import DataBlock, CategoryBlock
from fastai.data.core import DataLoaders
from fastai.data.transforms import RandomSplitter
from fastai.vision.data import ImageBlock
from fastai.vision.learner import vision_learner
from fastai.metrics import accuracy
from fastai.interpret import ClassificationInterpretation
from fastai.learner import load_learner

mnist = fetch_openml('mnist_784', as_frame=False)

X, y = mnist.data, mnist.target
X = X.astype(float)
X = np.reshape(X, (-1, 28, 28))

training_validation_X = X[:60000]
testing_X = X[60000:]
training_validation_y = y[:60000]
testing_y = y[60000:]
  1. Create the DataLoaders
def create_mnist_data_block(X, y, splitter=None):
    def get_items(indexes):
        return indexes

    def get_x(idx):
        return X[idx]

    def get_y(idx):
        return y[idx]

    data_block = DataBlock(
        blocks=(ImageBlock, CategoryBlock),
        get_items=get_items,
        get_x=get_x,
        get_y=get_y,
        splitter=splitter
    )
    return data_block

training_validation_data_block = create_mnist_data_block(
    training_validation_X, training_validation_y, RandomSplitter(valid_pct=0.2, seed=42))
training_validation_data_loaders = training_validation_data_block.dataloaders(list(range(len(training_validation_X))), bs=64)

testing_data_block = create_mnist_data_block(testing_X, testing_y)
testing_data_loaders = testing_data_block.dataloaders(list(range(len(testing_X))), bs=64)
  1. Train the classifier
resnet18_learner = vision_learner(training_validation_data_loaders, models.resnet18, metrics=accuracy)

resnet18_learner.fit(1)
  1. Predict on testing data
resnet18_predictions = resnet18_learner.get_preds(dl=testing_data_loaders)

At this point I get the error RuntimeError: Given groups=1, weight of size [64, 3, 7, 7], expected input[64, 1, 28, 28] to have 3 channels, but got 1 channels instead.

Executing resnet18_prediction = resnet18_learner.predict(testing_X[0]) on a single input works as expected.

What am I doing wrong?

This is the full stack trace of the error:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
/tmp/ipykernel_58/1932151090.py in <cell line: 0>()
----> 1 resnet18_predictions = resnet18_learner.get_preds(dl=testing_data_loaders)
      2 resnet18_predictions

/usr/local/lib/python3.12/dist-packages/fastai/learner.py in get_preds(self, ds_idx, dl, with_input, with_decoded, with_loss, act, inner, reorder, cbs, **kwargs)
    315         if with_loss: ctx_mgrs.append(self.loss_not_reduced())
    316         with ContextManagers(ctx_mgrs):
--> 317             self._do_epoch_validate(dl=dl)
    318             if act is None: act = getcallable(self.loss_func, 'activation')
    319             res = cb.all_tensors()

/usr/local/lib/python3.12/dist-packages/fastai/learner.py in _do_epoch_validate(self, ds_idx, dl)
    251         if dl is None: dl = self.dls[ds_idx]
    252         self.dl = dl
--> 253         with torch.no_grad(): self._with_events(self.all_batches, 'validate', CancelValidException)
    254 
    255     def _do_epoch(self):

/usr/local/lib/python3.12/dist-packages/fastai/learner.py in _with_events(self, f, event_type, ex, final)
    206 
    207     def _with_events(self, f, event_type, ex, final=noop):
--> 208         try: self(f'before_{event_type}');  f()
    209         except ex: self(f'after_cancel_{event_type}')
    210         self(f'after_{event_type}');  final()

/usr/local/lib/python3.12/dist-packages/fastai/learner.py in all_batches(self)
    212     def all_batches(self):
    213         self.n_iter = len(self.dl)
--> 214         for o in enumerate(self.dl): self.one_batch(*o)
    215 
    216     def _backward(self): self.loss_grad.backward()

/usr/local/lib/python3.12/dist-packages/fastai/learner.py in one_batch(self, i, b)
    242         b = self._set_device(b)
    243         self._split(b)
--> 244         self._with_events(self._do_one_batch, 'batch', CancelBatchException)
    245 
    246     def _do_epoch_train(self):

/usr/local/lib/python3.12/dist-packages/fastai/learner.py in _with_events(self, f, event_type, ex, final)
    206 
    207     def _with_events(self, f, event_type, ex, final=noop):
--> 208         try: self(f'before_{event_type}');  f()
    209         except ex: self(f'after_cancel_{event_type}')
    210         self(f'after_{event_type}');  final()

/usr/local/lib/python3.12/dist-packages/fastai/learner.py in _do_one_batch(self)
    223 
    224     def _do_one_batch(self):
--> 225         self.pred = self.model(*self.xb)
    226         self('after_pred')
    227         if len(self.yb):

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/module.py in _wrapped_call_impl(self, *args, **kwargs)
   1774             return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1775         else:
-> 1776             return self._call_impl(*args, **kwargs)
   1777 
   1778     # torchrec tests the code consistency with the following code

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs)
   1785                 or _global_backward_pre_hooks or _global_backward_hooks
   1786                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1787             return forward_call(*args, **kwargs)
   1788 
   1789         result = None

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/container.py in forward(self, input)
    251         """
    252         for module in self:
--> 253             input = module(input)
    254         return input
    255 

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/module.py in _wrapped_call_impl(self, *args, **kwargs)
   1774             return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1775         else:
-> 1776             return self._call_impl(*args, **kwargs)
   1777 
   1778     # torchrec tests the code consistency with the following code

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs)
   1785                 or _global_backward_pre_hooks or _global_backward_hooks
   1786                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1787             return forward_call(*args, **kwargs)
   1788 
   1789         result = None

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/container.py in forward(self, input)
    251         """
    252         for module in self:
--> 253             input = module(input)
    254         return input
    255 

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/module.py in _wrapped_call_impl(self, *args, **kwargs)
   1774             return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1775         else:
-> 1776             return self._call_impl(*args, **kwargs)
   1777 
   1778     # torchrec tests the code consistency with the following code

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs)
   1785                 or _global_backward_pre_hooks or _global_backward_hooks
   1786                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1787             return forward_call(*args, **kwargs)
   1788 
   1789         result = None

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/conv.py in forward(self, input)
    551 
    552     def forward(self, input: Tensor) -> Tensor:
--> 553         return self._conv_forward(input, self.weight, self.bias)
    554 
    555 

/usr/local/lib/python3.12/dist-packages/torch/nn/modules/conv.py in _conv_forward(self, input, weight, bias)
    546             )
    547 
--> 548         return F.conv2d(
    549             input, weight, bias, self.stride, self.padding, self.dilation, self.groups
    550         )

RuntimeError: Given groups=1, weight of size [64, 3, 7, 7], expected input[64, 1, 28, 28] to have 3 channels, but got 1 channels instead