Thanks Frank, I seen now…
by setting
target = torch.randint (1000, (1,))
print(target.size())

instead
target = torch.rand(bs, 1000)
print(target.size())

I am telling I will have just a single target value ranging from 1000 ImageNet classes.
And really this code will work now.
(The # target = target.long() was something I saw in some other answers on the similar title, we may ignore that).
Now the code looks like this:
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import transforms
from torch.utils.data import DataLoader, Dataset, TensorDataset

bs=1
from torchvision.models import resnet18
model = resnet18(pretrained=False)
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)

input = torch.rand(bs,3, 256,256)
target = torch.randint (1000, (1,))

model.train()

# before we learn
p1 = model.fc.weight
od1 = model.state_dict()


output = model(input)
# print(output.size())
loss_fn = torch.nn.CrossEntropyLoss()
# loss_fn = torch.nn.BCEWithLogitsLoss()
loss = loss_fn(output, target)
print(loss)
if optimizer is not None:
        optimizer.zero_grad()
        # backward pass + optimize
        loss.backward()
        optimizer.step()

p2 = model.fc.weight
od2 = model.state_dict()

print(torch.equal(p1,p2))

or I may use p1 = model.conv1.weight (same for p2) but the print(torch.equal(p1,p2)) will return True.
Can anyone tell why the parameters haven’t been updated. I expected I should learn something with the single optimizer.step(). In that case p1 should not be equal to p2.