Yeah, the gradients seem to be zeroed successfully in each iteration. I checked this as follows
for epoch in range(num_epochs):
    for batch_idx, (features, targets) in enumerate(train_loader):
        
        features = Variable(features.view(-1, 28*28))
        targets = Variable(targets)
        
        if torch.cuda.is_available():
            features, targets = features.cuda(), targets.cuda()
            
        outputs = model(features)
        cost = cost_fn(outputs, targets)
        optimizer.zero_grad()
        
        print(model.linear_1.weight.grad)
        print(model.linear_1.bias.grad)
        cost.backward()
        optimizer.step()