Hi, I followed a tutorial and trained a model that uses an image and 5 features to predict the price of a house.
But I am having trouble making predictions with the model. I’m most likely not passing data into the model the right way. Here’s the dataset code:
class ImageDataset(Dataset):
    """Tabular and Image dataset."""
    def __init__(self, pickle_file, image_dir):
        self.image_dir = image_dir
        self.pickle_file = pickle_file
        self.tabular = pd.read_pickle(pickle_file)
    def __len__(self):
        return len(self.tabular)
    def __getitem__(self, idx):
        if torch.is_tensor(idx):
            idx = idx.tolist()
        tabular = self.tabular.iloc[idx, 0:]
        y = tabular["price"]
        image = Image.open(f"{self.image_dir}/{tabular['zpid']}.png")
        image = np.array(image)
        image = image[..., :3]
        image = transforms.functional.to_tensor(image)
        tabular = tabular[["latitude", "longitude", "beds", "baths", "area"]]
        tabular = tabular.tolist()
        tabular = torch.FloatTensor(tabular)
        return image, tabular, y
def conv_block(input_size, output_size):
    block = nn.Sequential(
        nn.Conv2d(input_size, output_size, (3, 3)), nn.ReLU(), nn.BatchNorm2d(output_size), nn.MaxPool2d((2, 2)),
    )
    return block
Here’s the model:
class LitClassifier(pl.LightningModule):
    def __init__(
        self, lr: float = 1e-3, num_workers: int = 4, batch_size: int = 32,
    ):
        super().__init__()
        self.lr = lr
        self.num_workers = num_workers
        self.batch_size = batch_size
        self.conv1 = conv_block(3, 16)
        self.conv2 = conv_block(16, 32)
        self.conv3 = conv_block(32, 64)
        self.ln1 = nn.Linear(64 * 26 * 26, 16)
        self.relu = nn.ReLU()
        self.batchnorm = nn.BatchNorm1d(16)
        self.dropout = nn.Dropout2d(0.5)
        self.ln2 = nn.Linear(16, 5)
        self.ln4 = nn.Linear(5, 10)
        self.ln5 = nn.Linear(10, 10)
        self.ln6 = nn.Linear(10, 5)
        self.ln7 = nn.Linear(10, 1)
    def forward(self, img, tab):
        img = self.conv1(img)
        img = self.conv2(img)
        img = self.conv3(img)
        img = img.reshape(img.shape[0], -1)
        img = self.ln1(img)
        img = self.relu(img)
        img = self.batchnorm(img)
        img = self.dropout(img)
        img = self.ln2(img)
        img = self.relu(img)
        tab = self.ln4(tab)
        tab = self.relu(tab)
        tab = self.ln5(tab)
        tab = self.relu(tab)
        tab = self.ln6(tab)
        tab = self.relu(tab)
        x = torch.cat((img, tab), dim=1)
        x = self.relu(x)
        return self.ln7(x)
    def training_step(self, batch, batch_idx):
        image, tabular, y = batch
        criterion = torch.nn.L1Loss()
        y_pred = torch.flatten(self(image, tabular))
        y_pred = y_pred.double()
        loss = criterion(y_pred, y)
        tensorboard_logs = {"train_loss": loss}
        return {"loss": loss, "log": tensorboard_logs}
    def validation_step(self, batch, batch_idx):
        image, tabular, y = batch
        criterion = torch.nn.L1Loss()
        y_pred = torch.flatten(self(image, tabular))
        y_pred = y_pred.double()
        val_loss = criterion(y_pred, y)
        return {"val_loss": val_loss}
    def validation_epoch_end(self, outputs):
        avg_loss = torch.stack([x["val_loss"] for x in outputs]).mean()
        tensorboard_logs = {"val_loss": avg_loss}
        return {"val_loss": avg_loss, "log": tensorboard_logs}
    def test_step(self, batch, batch_idx):
        image, tabular, y = batch
        criterion = torch.nn.L1Loss()
        y_pred = torch.flatten(self(image, tabular))
        y_pred = y_pred.double()
        test_loss = criterion(y_pred, y)
        return {"test_loss": test_loss}
    def test_epoch_end(self, outputs):
        avg_loss = torch.stack([x["test_loss"] for x in outputs]).mean()
        logs = {"test_loss": avg_loss}
        return {"test_loss": avg_loss, "log": logs, "progress_bar": logs}
    def setup(self, stage):
      
        image_data = ImageDataset(pickle_file=f"{data_path}df.pkl", image_dir=f"{data_path}processed_images/")
        train_size = int(0.80 * len(image_data))
        val_size = int((len(image_data) - train_size) / 2)
        test_size = int((len(image_data) - train_size) / 2)
        self.train_set, self.val_set, self.test_set = random_split(image_data, (train_size, val_size, test_size))
    def configure_optimizers(self):
        #return torch.optim.Adam(self.parameters(), lr=(self.lr))
        return torch.optim.Adam(self.parameters(), lr=1e-4)
    def train_dataloader(self):
        return DataLoader(self.train_set, batch_size=self.batch_size)
    def val_dataloader(self):
        return DataLoader(self.val_set, batch_size=self.batch_size)
    def test_dataloader(self):
        return DataLoader(self.test_set, batch_size=self.batch_size)
And this is how I tried to make a prediction:
model = LitClassifier()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.load_from_checkpoint("lightning_logs/multi_input/version_2/checkpoints/epoch=30-step=2200.ckpt")
model.eval();
model.to(device);
image_data = ImageDataset(pickle_file=f"{data_path}df.pkl", image_dir=f"{data_path}processed_images/")
test_pred = model(image_data[0][0].unsqueeze(0).to(device),torch.FloatTensor((40.8127,-73.9191,5.0000,3.0000,2280.0000)).to(device))
Then I got this error:
RuntimeError                              Traceback (most recent call last)
Input In [55], in <cell line: 12>()
      5 model.to(device);
      8 #test_pred = model(torch.flatten(image_data[0][0].unsqueeze(0).to(device),torch.FloatTensor([[5,5,5,5,5]])).to(device))
      9 
     10 #test_pred = model(torch.flatten(image_data[0][0].unsqueeze(0)).to(device),torch.FloatTensor([  40.8127,  -73.9191,    5.0000,    3.0000, 2280.0000]).to(device))
---> 12 test_pred = model(image_data[0][0].unsqueeze(0).to(device),torch.FloatTensor((40.8127,-73.9191,5.0000,3.0000,2280.0000)).to(device))
File ~/anaconda3/envs/pytorchenv/lib/python3.8/site-packages/torch/nn/modules/module.py:1130, in Module._call_impl(self, *input, **kwargs)
   1126 # If we don't have any hooks, we want to skip the rest of the logic in
   1127 # this function, and just call forward.
   1128 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
   1129         or _global_forward_hooks or _global_forward_pre_hooks):
-> 1130     return forward_call(*input, **kwargs)
   1131 # Do not call functions when jit is used
   1132 full_backward_hooks, non_full_backward_hooks = [], []
Input In [4], in LitClassifier.forward(self, img, tab)
     42 tab = self.ln6(tab)
     43 tab = self.relu(tab)
---> 45 x = torch.cat((img, tab), dim=1)
     46 x = self.relu(x)
     48 return self.ln7(x)
RuntimeError: Tensors must have same number of dimensions: got 2 and 1
I’m clearly doing things wrong here. Would appreciate some help, thanks.