Hi,
Variables are not needed anymore. and predict methods should not be needed. An updated version of your code is:
import torch
from torch.autograd import Variable

class MatrixFactorization(torch.nn.Module):
    def __init__(self, n_items=len(movie_ids), n_factors=300):
        super().__init__()
        
        self.vectors = nn.Embedding(n_items, n_factors,sparse=True)
        

    def forward(self, i,j):
        # i and j are LongTensors here of size (batch)
        feat_i = self.vectors(i)
        feat_j = self.vectors(j)
        # feat_i and feat_j are of size (batch, n_factors)
        # Since you only want the interactions element-wise
        # for i and j in the batch dimension, you want diag(feat_i * feat_j.t())
        # This can be efficiently computed using element-wise product and summing
        result = (feat_i * feat_j).sum(-1)
        # result is of size (batch)
        return result


model = MatrixFactorization(n_items= len(movie_ids),n_factors=300)
loss_fn = nn.MSELoss() 
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

for i in range(len(movie_ids)):
    for j in range(len(movie_ids)):
    # get user, item and rating data
        rating =torch.FloatTensor([Xij[i, j]])
        # predict
        i = torch.LongTensor([int(i)])
        j = torch.LongTensor([int(j)])
        prediction = model(i, j)
        loss = loss_fn(prediction, rating)

        # Reset the gradients to 0
        optimizer.zero_grad()

        # backpropagate
        loss.backward()

        # update weights
        optimizer.step()

Note that with this code you can do real batch size and have:
# Generate a batch of 4 pairs i,j
i = torch.LongTensor([1, 3, 4, 5])
j = torch.LongTensor([2, 4, 6, 7])

# Get the ground truth and do forward for all of them at once
ratings = X[i, j]
pred = model(i, j)
# By default, MSELoss with compute the average MSELoss in the batch (you can change that if your need, check the doc how to do so)
loss = loss_fn(pred, ratings)