# img is a numpy array of shape (H,W)
img = img[..., None] # add extra channel dimension in the end to get shape (H, W, 1)
img = np.concatenate([img, img, img], 2) # concatenate 3 times in 3rd dimesnion to get shape (H, W, 3)
img = torch.from_numpy(img.transpose([2, 0, 1])) # transpose to get shape (3, H, W)


for this one can update model slightly like this

class Model(nn.Module):
   def __init__(self):
        self.conv = nn.Conv2d(1, 3, (1, 1)) # create conv layer with 1 input channel and 3 output channel
        # with kernel size (1, 1), This would lead to the same solution as earlier if the bias is 0 and the 
        # kernel weights (of shape (3, 1, 1, 1)) are ones, but we give the model here more flexibility in
        # coming up with the weights.
        self.base_model = torchvision.models.alexnet(pretrained=True)
        self.base_model.classifier[6] = nn.LInear(4096, 4)
  def forward(self, x):
       return self.base_model(self.conv(x))