Repository: NVIDIA/NeMo

Issue:
IntentSlotClassificationModel.restore_from fails with TypeError
**Overview**
I am unable to restore an IntentAndSlotClassificationModel from a `.nemo` file using the static `IntentAndSlotClassificationModel.restore_from(model_path)`.

**Steps to reproduce**
These steps rely heavily upon the existing [Joint_Intent_and_Slot_Classification](https://github.com/NVIDIA/NeMo/blob/main/tutorials/nlp/Joint_Intent_and_Slot_Classification.ipynb) tutorial.
1. Prepare training data
    a. Create a temporary directory, /tmp/nlu_evaluation, and two sub-directories: /tmp/nlu_evaluation/conf and /tmp/nlu_evaluation/data.
    b. Download [import_datasets.py](https://raw.githubusercontent.com/NVIDIA/NeMo/v1.2.0/examples/nlp/intent_slot_classification/data/assistant_utils.py) and [import_datasets.py](https://raw.githubusercontent.com/NVIDIA/NeMo/v1.2.0/examples/nlp/intent_slot_classification/data/assistant_utils.py) to /tmp/nlu_evaluation.
    c. Download [master.zip](https://github.com/xliuhw/NLU-Evaluation-Data/archive/master.zip) and extract it in /tmp/nlu_evaluation/data
    d. Process the evaluation data:
        `python /tmp/nlu_evaluation/import_datasets.py --dataset_name=assistant --source_dir=/tmp/nlu_evaluation/NLU-Evaluation-Data-master --target_dir=/tmp/nlu_evaluation/nemo_format`
    e. Download [intent_slot_classification_config.yaml](https://raw.githubusercontent.com/NVIDIA/NeMo/v1.2.0/examples/nlp/intent_slot_classification/conf/intent_slot_classification_config.yaml) to /tmp/nlu_evaluation/conf

2. Train and save the model
Run the following Python code
```
import nemo.collections.nlp as nemo_nlp
from omegaconf import OmegaConf


config = OmegaConf.load('/tmp/nlu_evaluation/conf/intent_slot_classification_config.yaml')
config.model.data_dir = '/tmp/nlu_evaluation/data/nemo_format')
config.trainer.gpus = 1
config.trainer.precision = 16
config.trainer.accelerator = None
config.trainer.max_epochs = 5
trainer = pl.Trainer(**config.trainer)

model = nemo_nlp.models.IntentSlotClassificationModel(config.model, trainer=trainer)
trainer.fit(model)

model.save_to('/tmp/nlu_evaluation/model.nemo')
```

3. Restore the saved model
Run the following Python code
```
import nemo.collections.nlp as nemo_nlp


nemo_nlp.models.IntentSlotClassificationModel.restore_from('/tmp/nlu_evaluation/model.nemo')
```

**Expected behavior**
The model is loaded successfully, without errors or exceptions

**Actual behavior**
The model fails to load because a TypeError is raised.

```
  File ".../lib/python3.8/site-packages/nemo/collections/nlp/models/nlp_model.py", line 429, in restore_from
    return super().restore_from(restore_path, override_config_path, map_location, strict, return_config)
  File ".../lib/python3.8/site-packages/nemo/core/classes/modelPT.py", line 481, in restore_from
    return cls._default_restore_from(restore_path, override_config_path, map_location, strict, return_config)
  File ".../lib/python3.8/site-packages/nemo/core/classes/modelPT.py", line 434, in _default_restore_from
    instance = cls.from_config_dict(config=conf)
  File ".../lib/python3.8/site-packages/nemo/core/classes/common.py", line 472, in from_config_dict
    instance = cls(cfg=config)
  File ".../lib/python3.8/site-packages/nemo/collections/nlp/models/intent_slot_classification/intent_slot_classification_model.py", line 56, in __init__
    self.setup_tokenizer(cfg.tokenizer)
  File ".../lib/python3.8/site-packages/nemo/collections/nlp/models/nlp_model.py", line 111, in setup_tokenizer
    vocab_file = self.register_artifact(config_path='tokenizer.vocab_file', src=cfg.vocab_file)
  File ".../lib/python3.8/site-packages/nemo/collections/nlp/models/nlp_model.py", line 62, in register_artifact
    return super().register_artifact(config_path, src, verify_src_exists=verify_src_exists)
  File ".../lib/python3.8/site-packages/nemo/core/classes/modelPT.py", line 222, in register_artifact
    return_path = os.path.abspath(os.path.join(_NEMO_FILE_FOLDER, src[5:]))
  File ".../lib/python3.8/posixpath.py", line 76, in join
    a = os.fspath(a)
TypeError: expected str, bytes or os.PathLike object, not NoneType
```

**Environment overview (please complete the following information)**
- NeMo version: 1.2.0
- Environment location: bare metal
- Method of NeMo install: pip

**Environment details**
- OS: Ubuntu 20.04
- PyTorch 1.8.1
- Python 3.8



Discussion:
After a little investigation it seems that when the BERT model is initialized the temporary directory where the files from the .nemo archive were unpacked is forgotten. In intent_slot_classification_model.py, line 73, the model restore state is cleared:

`IntentSlotClassificationModel._set_model_restore_state(is_being_restored=False)`

However, in line 76 when loading the BERT model an attempt is made to register an artifact from the archive:
```
self.bert_model = get_lm_model(
            pretrained_model_name=self.cfg.language_model.pretrained_model_name,
            config_file=self.register_artifact('language_model.config_file', cfg.language_model.config_file),
            config_dict=OmegaConf.to_container(self.cfg.language_model.config)
            if self.cfg.language_model.config
            else None,
            checkpoint_file=self.cfg.language_model.lm_checkpoint,
            vocab_file=self.register_artifact('tokenizer.vocab_file', cfg.tokenizer.vocab_file),
        )
```
Pay particular attention to the config_file parameter: `self.register_artifact('language_model.config_file', cfg.language_model.config_file)`

Perhaps line 73 should be moved after the BERT model is initialized rather than before.

Resolving patch:
diff --git a/nemo/collections/nlp/models/intent_slot_classification/intent_slot_classification_model.py b/nemo/collections/nlp/models/intent_slot_classification/intent_slot_classification_model.py
--- a/nemo/collections/nlp/models/intent_slot_classification/intent_slot_classification_model.py
+++ b/nemo/collections/nlp/models/intent_slot_classification/intent_slot_classification_model.py
@@ -69,9 +69,6 @@ def __init__(self, cfg: DictConfig, trainer: Trainer = None):
         # init superclass
         super().__init__(cfg=cfg, trainer=trainer)
 
-        # Enable setup methods.
-        IntentSlotClassificationModel._set_model_restore_state(is_being_restored=False)
-
         # Initialize Bert model
         self.bert_model = get_lm_model(
             pretrained_model_name=self.cfg.language_model.pretrained_model_name,
@@ -83,6 +80,9 @@ def __init__(self, cfg: DictConfig, trainer: Trainer = None):
             vocab_file=self.register_artifact('tokenizer.vocab_file', cfg.tokenizer.vocab_file),
         )
 
+        # Enable setup methods.
+        IntentSlotClassificationModel._set_model_restore_state(is_being_restored=False)
+
         # Initialize Classifier.
         self._reconfigure_classifier()
 


Test patch: