Repository: Lightning-AI/pytorch-lightning

Issue:
load_from_checkpoint: TypeError: __init__() missing 1 required positional argument
## ❓ Questions and Help

#### What is your question?

load_from_checkpoint: TypeError: __init__() missing 1 required positional argument

I have read the issues before, but the things different is **my `LightningModule` is inherited from my self-defined `LightningModule`.**

How to solve this problem or what is the best practice better suited to my needs?

#### Code

To reproduce the error:

```python
import os
import torch
from torch.nn import functional as F
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision import transforms
import pytorch_lightning as pl
from pytorch_lightning import Trainer

from argparse import Namespace

class _LitModel(pl.LightningModule):

    def __init__(self, hparams):
        super().__init__()
        if isinstance(hparams, dict):
            hparams = Namespace(**hparams)
        self.hparams = hparams
        self.l1 = torch.nn.Linear(28 * 28, hparams.classes)

    def forward(self, x):
        return torch.relu(self.l1(x.view(x.size(0), -1)))

    def training_step(self, batch, batch_idx):
        x, y = batch
        y_hat = self(x)
        loss = F.cross_entropy(y_hat, y)
        tensorboard_logs = {'train_loss': loss}
        return {'loss': loss, 'log': tensorboard_logs}

    def validation_step(self, batch, batch_idx):
        x, y = batch
        y_hat = self(x)
        loss = F.cross_entropy(y_hat, y)
        return {'val_loss': loss}

    def validation_epoch_end(self, outputs):
        avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()
        return {'val_loss': avg_loss}

    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=0.001)

class LitModel(_LitModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('--classes', type=int, default=10)
parser.add_argument('--checkpoint', type=str, default=None)
hparams = parser.parse_args()

mnist_train = MNIST(os.getcwd(), train=True, download=True,
                    transform=transforms.ToTensor())
mnist_train = DataLoader(mnist_train, num_workers=1)
mnist_val = MNIST(os.getcwd(), train=False, download=False,
                  transform=transforms.ToTensor())
mnist_val = DataLoader(mnist_val, num_workers=1)

# A bit weird here. I just want to show `load_from_checkpoint` will fail.
if hparams.checkpoint is None:
    model = LitModel(hparams)
else:
    model = LitModel.load_from_checkpoint(hparams.checkpoint)

trainer = Trainer(max_epochs=2, limit_train_batches=2,
                  limit_val_batches=2, progress_bar_refresh_rate=0)
trainer.fit(model, mnist_train, mnist_val)
```

#### Error msg

```
Traceback (most recent call last):
  File "main.py", line 64, in <module>
    model = LitModel.load_from_checkpoint(hparams.checkpoint)
  File "/home/siahuat0727/.local/lib/python3.8/site-packages/pytorch_lightning/core/saving.py", line 138, in load_from_checkpoint
    model = cls._load_model_state(checkpoint, *args, **kwargs)
  File "/home/siahuat0727/.local/lib/python3.8/site-packages/pytorch_lightning/core/saving.py", line 174, in _load_model_state
    model = cls(*cls_args, **cls_kwargs)
  File "main.py", line 46, in __init__
    super().__init__(*args, **kwargs)
TypeError: __init__() missing 1 required positional argument: 'hparams'
```

#### How to run to get the error

```bash
$ python3 main.py 
$ python3 main.py --checkpoint lightning_logs/version_0/checkpoints/epoch\=1.ckpt
```


#### What's your environment?

 - OS: Linux
 - Packaging: pip
 - Version 0.9.0rc12



Discussion:
Did you try to call `self.save_hyperparameters()` in _LitModel? 
Because it looks like hparams were not saved to checkpoint.
@awaelchli 
Hihi, the result is the same.
It works if I directly use `_LitModel` instead of `LitModel`. So I think that's sth about inheritance.

https://pytorch-lightning.readthedocs.io/en/latest/hyperparameters.html

> Anything assigned to self.hparams will also be saved automatically.

Resolving patch:
diff --git a/pytorch_lightning/core/saving.py b/pytorch_lightning/core/saving.py
--- a/pytorch_lightning/core/saving.py
+++ b/pytorch_lightning/core/saving.py
@@ -167,8 +167,9 @@ def _load_model_state(cls, checkpoint: Dict[str, Any], *cls_args, **cls_kwargs):
             cls_kwargs = {k: v for k, v in cls_kwargs.items() if k in cls_init_args_name}
 
         # prevent passing positional arguments if class does not accept any
-        if len(cls_spec.args) <= 1 and not cls_spec.kwonlyargs:
+        if len(cls_spec.args) <= 1 and not cls_spec.varargs and not cls_spec.kwonlyargs:
             cls_args, cls_kwargs = [], {}
+
         model = cls(*cls_args, **cls_kwargs)
         # load the state_dict on the model automatically
         model.load_state_dict(checkpoint['state_dict'])


Test patch:
diff --git a/tests/models/test_hparams.py b/tests/models/test_hparams.py
--- a/tests/models/test_hparams.py
+++ b/tests/models/test_hparams.py
@@ -497,3 +497,29 @@ def __init__(self, batch_size=15):
     raw_checkpoint_path = _raw_checkpoint_path(trainer)
     model = LocalModel.load_from_checkpoint(raw_checkpoint_path, non_exist_kwarg=99)
     assert 'non_exist_kwarg' not in model.hparams
+
+
+class SuperClassPositionalArgs(EvalModelTemplate):
+
+    def __init__(self, hparams):
+        super().__init__()
+        self._hparams = None  # pretend EvalModelTemplate did not call self.save_hyperparameters()
+        self.hparams = hparams
+
+
+class SubClassVarArgs(SuperClassPositionalArgs):
+    """ Loading this model should accept hparams and init in the super class """
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+
+def test_args(tmpdir):
+    """ Test for inheritance: super class takes positional arg, subclass takes varargs. """
+    hparams = dict(test=1)
+    model = SubClassVarArgs(hparams)
+    trainer = Trainer(default_root_dir=tmpdir, max_epochs=1)
+    trainer.fit(model)
+
+    raw_checkpoint_path = _raw_checkpoint_path(trainer)
+    model = SubClassVarArgs.load_from_checkpoint(raw_checkpoint_path)
+    assert model.hparams == hparams