I have the following model. It is training well. The shapes of my splits are:
However, I am getting the following error at x = F.relu(self.fc1(x))
in forward
. When I attempt to interpret the model on the validation set.
# Create a DataLoader for the validation set
valid_dl = learn.dls.test_dl(X_val, y_val)
# Get predictions and interpret them on the validation set
interp = ClassificationInterpretation.from_learner(learn, dl=valid_dl)
RuntimeError: mat1 and mat2 shapes cannot be multiplied (32x2110 and 67520x128)
I have checked dozens of similar questions but I am unable to find a solution. Here is the code.
from fastai.vision.all import *
import librosa
import numpy as np
from sklearn.model_selection import train_test_split
import torch
import torch.nn as nn
from torchsummary import summary
[...] #labels in y can be [0,1,2,3]
# Split the data
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.3, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42)
# Reshape data for CNN input (add channel dimension)
X_train = X_train[:, np.newaxis, :, :]
X_val = X_val[:, np.newaxis, :, :]
X_test = X_test[:, np.newaxis, :, :]
#X_train.shape, X_val.shape, X_test.shape
#((98, 1, 40, 844), (21, 1, 40, 844), (21, 1, 40, 844))
class DraftCNN(nn.Module):
def __init__(self):
super(DraftCNN, self).__init__()
self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1)
self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
# Calculate flattened size based on input dimensions
with torch.no_grad():
dummy_input = torch.zeros(1, 1, 40, 844) # shape of one input sample
dummy_output = self.pool(self.conv2(self.pool(F.relu(self.conv1(dummy_input)))))
self.flattened_size = dummy_output.view(dummy_output.size(0), -1).size(1)
self.fc1 = nn.Linear(self.flattened_size, 128)
self.fc2 = nn.Linear(128, 4)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(x.size(0), -1) # Flatten the output of convolutions
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
# Initialize the model and the Learner
model = AudioCNN()
learn = Learner(dls, model, loss_func=CrossEntropyLossFlat(), metrics=[accuracy, Precision(average='macro'), Recall(average='macro'), F1Score(average='macro')])
# Train the model
learn.fit_one_cycle(8)
print(summary(model, (1, 40, 844)))
# Create a DataLoader for the validation set
valid_dl = learn.dls.test_dl(X_val, y_val)
# Get predictions and interpret them on the validation set
interp = ClassificationInterpretation.from_learner(learn, dl=valid_dl)
interp.plot_confusion_matrix()
interp.plot_top_losses(5)
I tried changing the forward function and the shapes of the layers but I keep getting the same error.
Edit. Upon request, I have added more code.
I was able to pass the data to the fastai.Learner
to train the model and get results from plot_confusion_matrix
. My conclusion that fastai
is not designed to work with custom Datasets and DataLoaders and expecting you to use their API for loading the data. I think that in your case it might be worth to switch to
TabularDataLoaders
and load the data using TabularDataLoaders.from_df
. Or alternatively use ImageBlock
if you are working with images.
Basically to give the most optimal solution to the question it is important to know what data are you using. Are you working with images? Are images stored in files? What type of files? Or the input data are simple arrays?
Also function plot_top_losses
doesn't work well if you have numpy dataset as the input. Function plots worst_k
examples, and most probably it works only with data that is loaded using ImageBlocks
and etc.
Given current inputs there is two options how to fix the code:
Solution 1: Building custom numpy dataloader for fastai Learner using fastai DataBlock API:
from fastai.vision.all import *
from fastai.data.all import *
def make_dataloaders_from_numpy_data(image, label, loader=False):
def pass_index(idx):
return idx
def get_x(i):
val = image[i]
return torch.Tensor(val)
def get_y(i):
# val = [label[i]]
# res = torch.Tensor(val).to(torch.int64)
return label[i]
dblock = DataBlock(
blocks=(DataBlock, CategoryBlock),
get_items=pass_index,
get_x=get_x,
get_y=get_y)
# pass in a list of index
num_images = image.shape[0]
source = list(range(num_images))
if not loader:
ds = dblock.datasets(source)
return ds
return dblock.dataloaders(source, batch_size = 1)
train_ds = make_dataloaders_from_numpy_data(X_train, y_train)
test_ds = make_dataloaders_from_numpy_data(X_test, y_test)
train_ld = DataLoader(train_ds, batch_size=64)
test_ld = DataLoader(test_ds, batch_size=64)
dls = DataLoaders(train_ld, test_ld)
dls_val = make_dataloaders_from_numpy_data(X_val, y_val,loader=True)
# Initialize the model and the Learner
model = DraftCNN()
learn = Learner(dls,
model,
loss_func=CrossEntropyLossFlat(),
metrics=[accuracy, Precision(average='macro'), Recall(average='macro'), F1Score(average='macro')])
# # # # Train the model
learn.fit_one_cycle(1)
# Get predictions and interpret them on the validation set
interp = ClassificationInterpretation.from_learner(learn, dl=dls_val)
interp.plot_confusion_matrix()
plt.show()
Solution 2
Building custom numpy dataloader for fastai Learner using pytorch API for Dataset
and DataLoader
from torch.utils.data import Dataset
from fastai.data.core import DataLoaders
class CustomDataclass(Dataset):
def __init__(self, X: np.ndarray, y: np.ndarray):
"""
Will iterate over the dataset
"""
self.data = X
self.labels = y
# Not clear what is self.vocab for
# However part of this attribute is used for plotting labels in `ClassificationInterpretation`
self.vocab = (None,
['class_0', 'class_1', 'class_2', 'class_3'])
def __len__(self):
return self.data.shape[0]
def __getitem__(self, idx: int):
data = self.data[idx,...]
# labels can't be single values and must be converted to a list
labels = [self.labels[idx]]
return (torch.Tensor(data),
torch.Tensor(labels).to(torch.int64) # labels must be integers
)
train_ds = CustomDataclass(X_train, y_train)
test_ds = CustomDataclass(X_test, y_test)
val_ds = CustomDataclass(X_val, y_val)
from torch.utils.data import DataLoader
bs = 64
train_loader = DataLoader(train_ds, batch_size = bs)
test_loader = DataLoader(test_ds, batch_size = bs)
# Val dataset used in interpretation phase where pytorch dataloaders doesn't work
from fastai.data.core import DataLoader
val_loader = DataLoader(val_ds, batch_size = bs)
dls = DataLoaders(train_loader, test_loader)
# Initialize the model and the Learner
model = DraftCNN()
learn = Learner(dls,
model,
loss_func=CrossEntropyLossFlat(),
metrics=[accuracy, Precision(average='macro'), Recall(average='macro'), F1Score(average='macro')])
# # # # Train the model
learn.fit_one_cycle(4)
# Get predictions and interpret them on the validation set
interp = ClassificationInterpretation.from_learner(learn, dl=val_loader)
interp.plot_confusion_matrix()
plt.show()
Other errors that are fixed by my code:
learn.fit_one_cycle(4)
...
return torch.stack(batch, 0, out=out)
RuntimeError: stack expects each tensor to be equal size, but got [3] at entry 0 and [0] at entry 1
vocab
attribute to pytorch DataClass:interp = ClassificationInterpretation.from_learner(learn, dl=val_loader)
...
File "/Users/ivanpetrov/.pyenv/versions/3.11.6/envs/stack_overflow_env/lib/python3.11/site-packages/fastcore/basics.py", line 507, in __getattr__
if attr is not None: return getattr(attr,k)
^^^^^^^^^^^^^^^
AttributeError: 'CustomDataclass' object has no attribute 'vocab'