Repository: PaddlePaddle/PaddleNLP

Issue:
[Bug]:  AutoModel 无法正确加载由 `from_pretrained` 保存的 Config
### 软件环境

```Markdown
- paddlepaddle: None
- paddlepaddle-gpu: 2.4.1(develop)
- paddlenlp: 2.5.0(develop)
```


### 重复问题

- [X] I have searched the existing issues

### 错误描述

```Markdown
AutoModel 加载使用 `from_pretrained` 下载保存的 Config，缺少 `init_class` 参数，同时没有明确的报错提示。
```


### 稳定复现步骤 & 代码

```python
from paddlenlp.transformers import AutoModelForMaskedLM

model = AutoModelForMaskedLM.from_pretrained("ernie-3.0-base-zh")
model = AutoModelForMaskedLM.from_pretrained("~/.paddlenlp/models/ernie-3.0-base-zh")
```

```text
# 报错日志

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/ssd2/debug/PaddleNLP/paddlenlp/transformers/auto/modeling.py", line 757, in from_pretrained
    return cls._from_pretrained(pretrained_model_name_or_path, *model_args, **kwargs)
  File "/ssd2/debug/PaddleNLP/paddlenlp/transformers/auto/modeling.py", line 316, in _from_pretrained
    model_class = cls._get_model_class_from_config(pretrained_model_name_or_path, config_file)
  File "/ssd2/debug/PaddleNLP/paddlenlp/transformers/auto/modeling.py", line 209, in _get_model_class_from_config
    init_class = init_class[:-5] if init_class.endswith("Model") else init_class
AttributeError: 'NoneType' object has no attribute 'endswith'
```


Discussion:

Resolving patch:
diff --git a/paddlenlp/transformers/auto/modeling.py b/paddlenlp/transformers/auto/modeling.py
--- a/paddlenlp/transformers/auto/modeling.py
+++ b/paddlenlp/transformers/auto/modeling.py
@@ -206,7 +206,8 @@ def _get_model_class_from_config(cls, pretrained_model_name_or_path, config_file
             init_class = architectures.pop() if len(architectures) > 0 else None
         else:
             init_class = config.pop("init_class", None)
-        init_class = init_class[:-5] if init_class.endswith("Model") else init_class
+        init_class = init_class[:-5] if init_class is not None and init_class.endswith("Model") else init_class
+        model_name = None
         if init_class:
             for model_flag, name in MAPPING_NAMES.items():
                 if model_flag in init_class:
@@ -218,6 +219,10 @@ def _get_model_class_from_config(cls, pretrained_model_name_or_path, config_file
                 if name in pretrained_model_name_or_path.lower():
                     model_name = model_flag + "Model"
                     break
+        if model_name is None:
+            raise AttributeError(
+                f"Unable to parse 'architectures' or 'init_class' from {config_file_path}. Also unable to infer model class from 'pretrained_model_name_or_path'"
+            )
         init_class = cls._name_mapping[model_name + "_Import_Class"]
         class_name = cls._name_mapping[init_class]
         import_class = importlib.import_module(f"paddlenlp.transformers.{class_name}.modeling")


Test patch:
diff --git a/tests/transformers/auto/test_modeling.py b/tests/transformers/auto/test_modeling.py
new file mode 100644
--- /dev/null
+++ b/tests/transformers/auto/test_modeling.py
@@ -0,0 +1,58 @@
+# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
+# Copyright 2019 Hugging Face inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+import json
+import os
+import tempfile
+import unittest
+
+from paddlenlp.transformers import AutoModel, BertModel
+
+
+class AutoModelTest(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.model = AutoModel.from_pretrained("__internal_testing__/tiny-random-bert")
+
+    def test_from_pretrained_local(self):
+        with tempfile.TemporaryDirectory() as tmp_dir:
+            self.model.save_pretrained(tmp_dir)
+            model = AutoModel.from_pretrained(tmp_dir)
+            self.assertIsInstance(model, BertModel)
+
+    def test_from_pretrained_no_init_class_with_model_name(self):
+        with tempfile.TemporaryDirectory() as tmp_dir:
+            model = copy.deepcopy(self.model)
+            # when init_class is not found, we rely on the filename to get the import class
+            model_save_path = os.path.join(tmp_dir, "tiny-random-bert")
+            model.save_pretrained(model_save_path)
+            config = model.config.to_dict()
+            config.pop("architectures")
+            with open(os.path.join(model_save_path, "config.json"), "w", encoding="utf-8") as writer:
+                writer.write(json.dumps(config, indent=2, sort_keys=True) + "\n")
+            reloaded_model = AutoModel.from_pretrained(model_save_path)
+            self.assertIsInstance(reloaded_model, BertModel)
+
+    def test_from_pretrained_no_init_class_no_model_name(self):
+        with tempfile.TemporaryDirectory() as tmp_dir:
+            model = copy.deepcopy(self.model)
+            model.save_pretrained(tmp_dir)
+            config = model.config.to_dict()
+            config.pop("architectures")
+            with open(os.path.join(tmp_dir, "config.json"), "w", encoding="utf-8") as writer:
+                writer.write(json.dumps(config, indent=2, sort_keys=True) + "\n")
+            with self.assertRaises(AttributeError):
+                AutoModel.from_pretrained(tmp_dir)