Repository: Lightning-AI/pytorch-lightning

Issue:
[hparams] save_hyperparameters doesn't save kwargs
## ❓ Questions and Help

when I use hyperparemeters like docs:
```
class LitMNIST(LightningModule):

    def __init__(self, layer_1_dim=128, learning_rate=1e-2, **kwargs):
        super().__init__()
        # call this to save (layer_1_dim=128, learning_rate=1e-4) to the checkpoint
        self.save_hyperparameters()

```
model checkpoint doesn't save args in kwargs. But kwargs is important. Args such as `num_frames, img_size, img_std ...` must be used in creating `dataloader`, but it will be tedious if writes them in `__init__` explicitly .  it can make code clean if hides them in `kwargs`. 

Before I use `hparams`, it's ok. But now it's not recommended to use `hparams`,  is there any good idea to  deal with this problem?



Discussion:
if don't use `hparams`, it will put all args of `model, dataset, dataloader...` in a `LightnModule`' s `__init__` method, and `save_hyperparameters ` doesn't save args in `kwargs`. It this really a good idea?
Could you please share your model example?
> Could you please share your model example?

```
class LitMNIST(LightningModule):

    def __init__(self, layer_1_dim=128, learning_rate=1e-2, **kwargs):
        super().__init__()
        # call this to save (layer_1_dim=128, learning_rate=1e-4) to the checkpoint
        self.save_hyperparameters()
        self.kwargs = kwargs
        ...
    
    def train_dataloader(self):
        img_size = self.kwargs['img_size']
        ...
```

I can train this model, but when I load from checkpoint, it says kwargs hasn't `img_size`
> I can train this model, but when I load from checkpoint, it says kwargs hasn't `img_size`

I see, we need to ignore `kwargs` from the model hparams saving...
@xiadingZ mind adding PR with a test for this case and I ll finish it with a patch?
```
Traceback (most recent call last):
  File "./training.py", line 101, in <module>
    main(hparam_trial)
  File "./training.py", line 86, in main
    model = module(hparams, fold_train, fold_val, data_dir+img_dir)
  File "../main/module.py", line 18, in __init__
    self.hparams = hparams
  File "/opt/conda/lib/python3.6/site-packages/torch/nn/modules/module.py", line 638, in __setattr__
    object.__setattr__(self, name, value)
  File "/opt/conda/lib/python3.6/site-packages/pytorch_lightning/core/lightning.py", line 1695, in hparams
    self.save_hyperparameters(hp, frame=inspect.currentframe().f_back.f_back)
  File "/opt/conda/lib/python3.6/site-packages/pytorch_lightning/core/lightning.py", line 1662, in save_hyperparameters
    cand_names = [k for k, v in init_args.items() if v == hp]
  File "/opt/conda/lib/python3.6/site-packages/pytorch_lightning/core/lightning.py", line 1662, in <listcomp>
    cand_names = [k for k, v in init_args.items() if v == hp]
  File "/opt/conda/lib/python3.6/site-packages/pandas/core/generic.py", line 1479, in __nonzero__
    f"The truth value of a {type(self).__name__} is ambiguous. "
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
```

I'm guessing this is why 0.8.0 causes this error, it's trying to save all args (including dataframes in my case) outside of hparams?

Edit: #2250 

Resolving patch:
diff --git a/pytorch_lightning/core/lightning.py b/pytorch_lightning/core/lightning.py
--- a/pytorch_lightning/core/lightning.py
+++ b/pytorch_lightning/core/lightning.py
@@ -1,6 +1,7 @@
 import collections
 import inspect
 import os
+import re
 from abc import ABC, abstractmethod
 from argparse import Namespace
 from typing import Any, Callable, Dict, List, Optional, Tuple, Union, Sequence
@@ -1692,4 +1693,20 @@ def hparams(self) -> Union[AttributeDict, str]:
 
     @hparams.setter
     def hparams(self, hp: Union[dict, Namespace, Any]):
-        self.save_hyperparameters(hp, frame=inspect.currentframe().f_back.f_back)
+        hparams_assignment_name = self.__get_hparams_assignment_variable()
+        self._hparams_name = hparams_assignment_name
+        self._set_hparams(hp)
+
+    def __get_hparams_assignment_variable(self):
+        """
+        looks at the code of the class to figure out what the user named self.hparams
+        this only happens when the user explicitly sets self.hparams
+        """
+        class_code = inspect.getsource(self.__class__)
+        lines = class_code.split('\n')
+        for line in lines:
+            line = re.sub(r"\s+", "", line, flags=re.UNICODE)
+            if 'self.hparams=' in line:
+                return line.split('=')[1]
+
+        return None
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
@@ -176,14 +176,18 @@ def _load_model_state(cls, checkpoint: Dict[str, Any], *args, **kwargs):
         # pass in the values we saved automatically
         if cls.CHECKPOINT_HYPER_PARAMS_KEY in checkpoint:
             model_args = {}
+
             # add some back compatibility, the actual one shall be last
             for hparam_key in CHECKPOINT_PAST_HPARAMS_KEYS + (cls.CHECKPOINT_HYPER_PARAMS_KEY,):
                 if hparam_key in checkpoint:
                     model_args.update(checkpoint[hparam_key])
+
             if cls.CHECKPOINT_HYPER_PARAMS_TYPE in checkpoint:
                 model_args = checkpoint[cls.CHECKPOINT_HYPER_PARAMS_TYPE](model_args)
+
             args_name = checkpoint.get(cls.CHECKPOINT_HYPER_PARAMS_NAME)
             init_args_name = inspect.signature(cls).parameters.keys()
+
             if args_name == 'kwargs':
                 cls_kwargs = {k: v for k, v in model_args.items() if k in init_args_name}
                 kwargs.update(**cls_kwargs)


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
@@ -39,7 +39,7 @@ def _run_standard_hparams_test(tmpdir, model, cls, try_overwrite=False):
     assert model.hparams.test_arg == 14
 
     # verify we can train
-    trainer = Trainer(default_root_dir=tmpdir, max_epochs=1, overfit_batches=0.5)
+    trainer = Trainer(default_root_dir=tmpdir, max_epochs=1, overfit_batches=2)
     trainer.fit(model)
 
     # make sure the raw checkpoint saved the properties