Hi everyone! Preparing to deploy a trained model to AWS (code below). Not sure how to configure Model.eval() to take in simulated user-input and provide an output.
IndexError: index 1 is out of bounds for dimension 1 with size 1
Any help would be much appreciated!
This is the model:
class Model(nn.Module):
def __init__(self, embedding_size, num_numerical_cols, output_size, layers, p=0.4):
super().__init__()
self.all_embeddings = nn.ModuleList([nn.Embedding(ni, nf) for ni, nf in embedding_size])
#ni is number of embeddings
#nf is the embedding dimensions(or number of features)
self.embedding_dropout = nn.Dropout(p) # p is the dropout rate
self.batch_norm_num = nn.BatchNorm1d(num_numerical_cols) #Only applied to the numerical column
all_layers = []
num_categorical_cols = sum((nf for ni, nf in embedding_size))
input_size = num_categorical_cols + num_numerical_cols
for i in layers:
all_layers.append(nn.Linear(input_size, i))
all_layers.append(nn.ReLU(inplace=False))
all_layers.append(nn.BatchNorm1d(i))
all_layers.append(nn.Dropout(p))
input_size = i
all_layers.append(nn.Linear(layers[-1], output_size))
self.layers = nn.Sequential(*all_layers)
def forward(self, x_categorical, x_numerical):
embeddings = []
for i,e in enumerate(self.all_embeddings):
embeddings.append(e(x_categorical[:,i]))
x = torch.cat(embeddings, 1)
x = self.embedding_dropout(x)
x_numerical = self.batch_norm_num(x_numerical) #Inputs the batch normalized numbers
x = torch.cat([x, x_numerical], 1)
x = self.layers(x)
return x
Based on the code x_categorical should have the shape [batch_size, len(self.all_embeddings), *], since you are indexing this tensor in dim1.
If you are passing x_categorical as a tensor with the shape [3, 1], self.all_embeddings can only contain a single nn.Embedding layer.
Could you explain your use case a bit more and how you would like to use the for loop?
@ptrblck I have used this open source model as the basis for mine. From the description:
Inside the constructor, a few variables are initialized. Firstly, the all_embeddings variable contains a list of ModuleList objects for all the categorical columns. The embedding_dropout stores the dropout value for all the layers. Finally, the batch_norm_num stores a list of BatchNorm1d objects for all the numerical columns.
Next, to find the size of the input layer, the number of categorical and numerical columns are added together and stored in the input_size variable. After that, a for loop iterates and the corresponding layers are added into the all_layers list. The layers added are:
Linear : Used to calculate the dot product between the inputs and weight matrixes
ReLu : Which is applied as an activation function
BatchNorm1d : Used to apply batch normalization to the numerical columns
Dropout : Used to avoid overfitting
After the for loop, the output layer is appended to the list of layers. Since we want all of the layers in the neural networks to execute sequentially, the list of layers is passed to the nn.Sequential class.
Next, in the forward method, both the categorical and numerical columns are passed as inputs. The embedding of the categorical columns takes place in the following lines.
embeddings = []
for i, e in enumerate(self.all_embeddings):
embeddings.append(e(x_categorical[:,i]))
x = torch.cat(embeddings, 1)
x = self.embedding_dropout(x)
The batch normalization of the numerical columns is applied with the following script:
x_numerical = self.batch_norm_num(x_numerical)
Finally, the embedded categorical columns x and the numeric columns x_numerical are concatenated together and passed to the sequential layers .
This main issue is that this model was not designed with a model.eval() mode. Thank you in advance for your help!
So, the solution is: came from the shape of your tensors being correct. In order for the tensor to be valid it must have a valid length, and in this case the same columns as the training data. So in our case we would want the tensor for categorical data to be (1,7) and (1,8) for numerical data
This is the data I used to mock it, keeping the columns we had in the original training dataset that was stacked, but just setting them to 0 and null
Thanks for the update.
Based on your description you are missing some categorical features during evaluation.
Replacing them with a zero or invalid value might yield bad performance or invalid outputs (e.g. NaNs).
You could try to replace the missing values with the mean or median from your training set for this particular feature.
Hi @ptrblck - duly noted! Since last post, I’ve made alot of progress (saved my models weights using torch.save(model.state_dict(), ‘.pth’) and have been able to load it in .eval() in a new file).
I am confused how to change my eval (formerly training loop) to iterate over the new data set just one time to produce a binary prediction? Any advice you could give is greatly appreciated. Cheers!
epochs = 1
aggregated_losses = []
for x in range(epochs):
x += 1
y_pred = model(inputCatColumns, inputNumColumns)
single_loss = loss_function(y_pred, train_outputs)
aggregated_losses.append(single_loss)
if x%25 == 1:
print(f'epoch: {x:3} loss: {single_loss.item():10.8f}')
optimizer.zero_grad()
single_loss.backward()
optimizer.step()
#scheduler.step(x)
print(f'epoch: {x:3} loss: {single_loss.item():10.10f}')
with torch.no_grad():
y_val = model(inputCatColumns, inputNumColumns)
loss = loss_function(y_val, test_outputs)
print(f'Loss: {loss:.8f}')
I assume your model outputs a single logit, since you are dealing with a binary prediction?
If that’s the case, this dummy code might be a good starter:
model.eval()
preds = []
with torch.no_grad():
y_val = model(inputCatColumns, inputNumColumns)
preds.append(y_val > 0.) # if y_val are logits
loss = loss_function(y_val, test_outputs)
print(f'Loss: {loss:.8f}')
preds = torch.stack(preds)
Let me know, if that helps or if I misunderstood your question.
@ptrblck That dummy code looks like a great start. When I run it, I get this an error. Should I not be converting my new data set to tensors like I had to for the model training? If so, would I just pass the values in ‘raw’ so to speak?
['POL_STATUS', 'P1_POLICY_REFUSED', 'FLOODING', 'Zipcode', 'Combined FIPS', 'State', 'Name']
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-62-c9aa9c799384> in <module>()
1 preds = []
2 with torch.no_grad():
----> 3 y_val = model(inputCatColumns, inputNumColumns)
4 preds.append(y_val > 0.) # if y_val are logits
5 loss = loss_function(y_val, test_outputs)
1 frames
/usr/local/lib/python3.6/dist-packages/torch/nn/modules/module.py in __call__(self, *input, **kwargs)
530 result = self._slow_forward(*input, **kwargs)
531 else:
--> 532 result = self.forward(*input, **kwargs)
533 for hook in self._forward_hooks.values():
534 hook_result = hook(self, input, result)
<ipython-input-35-5bbeaf977447> in forward(self, x_categorical, x_numerical)
28 print(x_categorical)
29 for i,e in enumerate(self.all_embeddings):
---> 30 print(x_categorical[:,i])
31 embeddings.append(e(x_categorical[:,i]))
32
TypeError: list indices must be integers or slices, not tuple
@ptrblck Great, thats what I am currently doing. The only difference between training the model, and eval mode is now I am only passing in a single row of data (but still preprocessing the same way). Is this the right construction of the eval loop or should I be doing it differently?
Also, to load in the single row, should I be using the DataLoader?
@ptrblck Thanks for that suggestion! The new prediction block is running into this error - I found this solve for it but am confused because the data being used has already been converted into a numpy array
name = userInput['Name'].cat.codes.values
inputColumns = np.stack([name, etc...], 1)