Repository: Lightning-AI/pytorch-lightning
Pull request: Cast hparams to dict when not using omegaconf
## What does this PR do?

<!--
Please include a summary of the change and which issue is fixed.
 Please also include relevant motivation and context.
 List any dependencies that are required for this change.

If we didn't discuss your PR in Github issues there's a high chance it will not be merged.
-->

Fixes #4542

## Before submitting
- [x] Was this discussed/approved via a Github issue? (no need for typos and docs improvements)
- [x] Did you read the [contributor guideline](https://github.com/PyTorchLightning/pytorch-lightning/blob/master/.github/CONTRIBUTING.md), Pull Request section?
- [x] Did you make sure your PR does only one thing, instead of bundling different changes together? Otherwise, we ask you to create a separate PR for every change.
- [x] Did you make sure to update the documentation with your changes?
- [x] Did you write any new necessary tests? 
- [x] Did you verify new and existing tests pass locally with your changes?
- [x] If you made a notable change (that affects users), did you update the [CHANGELOG](https://github.com/PyTorchLightning/pytorch-lightning/blob/master/CHANGELOG.md)?

<!-- For CHANGELOG separate each item in unreleased section by a blank line to reduce collisions -->

## PR review
Anyone in the community is free to review the PR once the tests have passed.
Before you start reviewing make sure you have read [Review guidelines](https://github.com/PyTorchLightning/pytorch-lightning/wiki/Review-guidelines). In in short, see following bullet-list:  

 - [x] Is this pull request ready for review? (if not, please submit in draft mode)
 - [x] Check that all items from **Before submitting** are resolved
 - [x] Make sure the title is self explanatory and the description concisely explains the PR
 - [x] Add labels and milestones (and optionally projects) to the PR so it can be classified; _Bugfixes should be including in bug-fix release milestones (m.f.X) and features should be included in (m.X.b) releases._
 

## Did you have fun?
Make sure you had fun coding 🙃


Problem statement:
Replace AttributeDict in with dict in checkpoint
## 🚀 Feature

When we save hyperparameters to the checkpoint, we save them in a Lightning datastructure called AttributeDict. 
We should replace this with a regular dict when saving the checkpoint.

### Motivation

Allows loading checkpoints with `torch.load` in environments where Lightning is not installed.

### Pitch

Convert AttributeDict to dict when saving, convert dict to AttributeDict when loading.

cc @Borda

Hints from issue discussion:
wasn't this resolved here: https://github.com/PyTorchLightning/pytorch-lightning/issues/2653
maybe someone changed it again.
thanks for pointing to the linked pr. A week ago I inspected the checkpoint and the type was AttributeDict. 
I will check again 
Yeah this is odd, last I tested it was loading fine (without pl) as well... though there has been quite a bit of refactoring...

Edit:
relevant code block is here now and seems fine: https://github.com/PyTorchLightning/pytorch-lightning/blob/4f3160ba2ebc3a4de230e092e98bbbbf9651db21/pytorch_lightning/trainer/connectors/checkpoint_connector.py#L334
aha, but that means the logic there with omegaconf is making it break. because even when omegaconf is  installed but not used, we have the attribute dict in the checkpoint.
oh I did not realize `OMEGACONF_AVAILABLE` is only checking if it's installed...

Commit message: Merge branch 'master' into hparams_dict

Patch to review:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a03085024776..1a4da285179ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -60,6 +60,29 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 
 
 
+## [unreleased.BugFix] - YYYY-MM-DD
+
+### Added
+
+
+
+### Changed
+
+
+
+### Deprecated
+
+
+
+### Removed
+
+
+
+### Fixed
+
+- Fixed checkpoint hparams dict casting when omegaconf is available ([#4770](https://github.com/PyTorchLightning/pytorch-lightning/pull/4770))
+
+
 ## [1.0.7] - 2020-11-17
 
 ### Added
diff --git a/pytorch_lightning/trainer/connectors/checkpoint_connector.py b/pytorch_lightning/trainer/connectors/checkpoint_connector.py
index d98a3137be34a..eff7a1c7ae9dc 100644
--- a/pytorch_lightning/trainer/connectors/checkpoint_connector.py
+++ b/pytorch_lightning/trainer/connectors/checkpoint_connector.py
@@ -328,10 +328,9 @@ def dump_checkpoint(self, weights_only: bool = False) -> dict:
             if hasattr(model, '_hparams_name'):
                 checkpoint[LightningModule.CHECKPOINT_HYPER_PARAMS_NAME] = model._hparams_name
             # dump arguments
-            if OMEGACONF_AVAILABLE:
+            if OMEGACONF_AVAILABLE and isinstance(model.hparams, Container):
                 checkpoint[LightningModule.CHECKPOINT_HYPER_PARAMS_KEY] = model.hparams
-                if isinstance(model.hparams, Container):
-                    checkpoint[LightningModule.CHECKPOINT_HYPER_PARAMS_TYPE] = type(model.hparams)
+                checkpoint[LightningModule.CHECKPOINT_HYPER_PARAMS_TYPE] = type(model.hparams)
             else:
                 checkpoint[LightningModule.CHECKPOINT_HYPER_PARAMS_KEY] = dict(model.hparams)
 
diff --git a/tests/checkpointing/test_model_checkpoint.py b/tests/checkpointing/test_model_checkpoint.py
index 371e0cd6b2cd4..0d2fe895e86ac 100644
--- a/tests/checkpointing/test_model_checkpoint.py
+++ b/tests/checkpointing/test_model_checkpoint.py
@@ -27,6 +27,9 @@
 import cloudpickle
 import pytest
 import torch
+from omegaconf import Container
+from omegaconf import OmegaConf
+from argparse import Namespace
 
 import tests.base.develop_utils as tutils
 from pytorch_lightning import Trainer, seed_everything
@@ -911,3 +914,35 @@ def training_step(self, *args):
     expected = float("inf" if mode == "min" else "-inf")
     assert model_checkpoint.best_model_score == expected
     assert model_checkpoint.current_score == expected
+
+
+@pytest.mark.parametrize("hparams_type", [dict, Container])
+def test_hparams_type(tmpdir, hparams_type):
+    class TestModel(BoringModel):
+        def __init__(self, hparams):
+            super().__init__()
+            self.save_hyperparameters(hparams)
+
+    model_checkpoint = ModelCheckpoint(
+        dirpath=tmpdir,
+        save_top_k=1,
+        monitor="foo",
+    )
+    trainer = Trainer(
+        default_root_dir=tmpdir,
+        fast_dev_run=True,
+        callbacks=[model_checkpoint],
+        logger=False,
+        weights_summary=None,
+        progress_bar_refresh_rate=0,
+    )
+    hp = {"test_hp_0": 1, "test_hp_1": 2}
+    hp = OmegaConf.create(hp) if hparams_type == Container else Namespace(**hp)
+    model = TestModel(hp)
+    trainer.fit(model)
+    ckpt = trainer.checkpoint_connector.dump_checkpoint()
+    if hparams_type == Container:
+        assert isinstance(ckpt[model.CHECKPOINT_HYPER_PARAMS_KEY], hparams_type)
+    else:
+        # make sure it's not AttributeDict
+        assert type(ckpt[model.CHECKPOINT_HYPER_PARAMS_KEY]) == hparams_type


Code review comments:
diff_hunk: @@ -27,6 +27,9 @@
 import cloudpickle
 import pytest
 import torch
+from omegaconf import Container
+from omegaconf import OmegaConf
path: tests/checkpointing/test_model_checkpoint.py
text: @user1:
```suggestion
from omegaconf import Container, OmegaConf
```