You are not splitting your data into batches, like you are doing in keras by passing batch size = 64,
instead you are passing “whole” x_train of shape (8964, 44) at eatch training iteration, try splitting your training data and pass a batch of x_train with shape of (batch_size, 44) at each training iteration. Also shuffle the data after each epoch.
try using code below-
train_iterator = torch.utils.data.TensorDataset(x_train_tensor, y_train_tensor) 
# make sure y_train_tensor has shape (8964, 1), not (8964)
train_data = torch.utils.data.DataLoader(train_iterator, batch_size = 64, shuffle = True)

for x, y in train_data:
    break
print(x.shape, y.shape) # will print (64, 44), (64, 1)

model = torch.nn.Sequential(
    torch.nn.Linear(44, 44),
    torch.nn.ReLU(),
    torch.nn.Linear(44, 32),
    torch.nn.ReLU(),
    torch.nn.Linear(32, 1),
).to(device)


criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.Adam(model.parameters())

epochs = 20
batches = len(train_data)
# number of batches in your dataset (where each batch contains 64 examples from your training data)

for epoch in range(epochs):
    epoch_loss = 0.0 # initializing epoch_loss to zero 
    for features, labels in train_data:
        outputs = model(features)
        loss = criterion(outputs, labels)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        epoch_loss += loss.item() # here we are accumulating loss for each training batch.
    print(f'Epoch: {epoch} -> Loss: {(epoch_loss/batches):.8f}')
    # dividing epoch_loss by number of batches in our train_data to get mean epoch_loss
    # and printing epoch loss round of to 8 decimals

Another minor error I’ve noticed (not related to your loss convergence) that while using summary function to get the summary of your model you are once again passing whole shape of your x_train which is (8964, 44) leading you wrong layer shapes in the results (I’m sure you didn’t realize it), instead you need to pass the shape of single example, so running summary(model, x_train_tensor[0].shape) will give correct summary (just as in keras)
----------------------------------------------------------------
        Layer (type)               Output Shape         Param # 
================================================================
            Linear-1                   [-1, 44]           1,980 
              ReLU-2                   [-1, 44]               0 
            Linear-3                   [-1, 32]           1,440 
              ReLU-4                   [-1, 32]               0 
            Linear-5                    [-1, 1]              33 
================================================================
Total params: 3,453
Trainable params: 3,453
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.01
Estimated Total Size (MB): 0.01
----------------------------------------------------------------