Hi, you are right there is no difference between the models. The thing is that they all are wrong. Consider the following models:
class Model1(nn.Module):
    def __init__(self, num_cats):
        super(Model1, self).__init__()    
        self.model = torchvision.models.resnet18(pretrained=True)
        self.model.fc = nn.Linear(self.model.fc.in_features, num_cats)
        self.model = torch.jit.trace(self.model, torch.rand(1,3,224,224))
    def forward(self, x):
        x = self.model(x)
        return x

class Model2(nn.Module):
    def __init__(self, num_cats):
        super(Model2, self).__init__()    
        self.model = torchvision.models.resnet18(pretrained=True)
        self.model.fc = nn.Linear(self.model.fc.in_features, num_cats)
    def forward(self, x):
        x = self.model(x)
        return x

I can train both models with the same dataset and hyperparameters and achieve similar expected results (good results). I can then export them as follows:
scripted1 = torch.jit.script(model1)
torch.jit.save(scripted1, 'scripted_model1.pth')

scripted2 = torch.jit.trace(model2)
torch.jit.save(scripted2, 'scripted_model2.pth')

And load them
loaded1 = torch.jit.load('scripted_model1.pth')
loaded2 = torch.jit.load('scripted_model2.pth')

In all cases the 3 versions of each model give the same results, the difference is that using the first version only works fine when performing inference with a large batch size (and failing in single image inference). The second model, however, gives good predictions in all cases.
PS: I tried the same experiment than the one proposed here but with a custom CNN in place of resnet18 and I observe the same problem. Hence it seems to be something related with the interaction between torch.jit.script and torch.jit.trace.