AutoTokenizer registration not working as expected
Author: ghost
Pull request: no

### System Info

- `transformers` version: 4.29.1
- Platform: Linux-3.10.0-1160.80.1.el7.x86_64-x86_64-with-glibc2.17
- Python version: 3.9.2
- Huggingface_hub version: 0.14.1
- Safetensors version: not installed
- PyTorch version (GPU?): 1.13.1+cu117 (False)
- Tensorflow version (GPU?): not installed (NA)
- Flax version (CPU?/GPU?/TPU?): not installed (NA)
- Jax version: not installed
- JaxLib version: not installed
- Using GPU in script?: no
- Using distributed or parallel set-up in script?: no

### Who can help?

@ArthurZucker this seems to be an issue with `AutoTokenizer` registration, so maybe you're the right person to take a look?

### Information

- [ ] The official example scripts
- [X] My own modified scripts

### Tasks

- [ ] An officially supported task in the `examples` folder (such as GLUE/SQuAD, ...)
- [ ] My own task or dataset (give details below)

### Reproduction

- Create a directory containing the `config.json`, `pytorch_model.bin`, and `tokenizer.json` from [gpt2](https://huggingface.co/gpt2/tree/main)
- Set `"model_type": "example"` in the `config.json`
- Run the following code excerpt, replacing the dummy path with the path to the prepared directory
```python
from transformers import (
    GPT2LMHeadModel,
    GPT2Config,
    AutoConfig,
    AutoModelForCausalLM,
    AutoTokenizer,
)
from transformers.tokenization_utils_fast import PreTrainedTokenizerFast
from transformers.models.auto.tokenization_auto import (
    CONFIG_MAPPING_NAMES, TOKENIZER_MAPPING
)

from tokenizers import Tokenizer


class ExampleConfig(GPT2Config):
    model_type = "example"


class ExampleModel(GPT2LMHeadModel):
    config_class = ExampleConfig


ExampleTokenizer = PreTrainedTokenizerFast

AutoConfig.register("example", ExampleConfig)
AutoModelForCausalLM.register(ExampleConfig, ExampleModel)
AutoTokenizer.register(ExampleConfig, fast_tokenizer_class=ExampleTokenizer)

print(", ".join(c.__name__ for c in TOKENIZER_MAPPING))
print(CONFIG_MAPPING_NAMES)

pretrain_path = "/path/to/downloaded/artifacts"
config = AutoConfig.from_pretrained(pretrain_path) # This works just fine
model = AutoModelForCausalLM.from_pretrained(pretrain_path) # This works just fine
tokenizer = AutoTokenizer.from_pretrained(pretrain_path) # This throws an exception
```
A few things to note about the behavior of this script. First, as noted in the comments, loading the `AutoTokenizer` throws an error. The error message I see is
```
Traceback (most recent call last):
  File "[base_path]/autotokenizer_reproducer.py", line 36, in <module>
    tokenizer = AutoTokenizer.from_pretrained(pretrain_path) # This throws an exception
  File "[path_to_conda_env]/lib/python3.9/site-packages/transformers/models/auto/tokenization_auto.py", line 721, in from_pretrained
    raise ValueError(
ValueError: Unrecognized configuration class <class '__main__.ExampleConfig'> to build an AutoTokenizer.
Model type should be one of AlbertConfig, AlignConfig, BartConfig, BertConfig, BertGenerationConfig, BigBirdConfig, BigBirdPegasusConfig, BioGptConfig, BlenderbotConfig, BlenderbotSmallConfig, BlipConfig, Blip2Config, BloomConfig, BridgeTowerConfig, CamembertConfig, CanineConfig, ChineseCLIPConfig, ClapConfig, CLIPConfig, CLIPSegConfig, CodeGenConfig, ConvBertConfig, CpmAntConfig, CTRLConfig, Data2VecTextConfig, DebertaConfig, DebertaV2Config, DistilBertConfig, DPRConfig, ElectraConfig, ErnieConfig, ErnieMConfig, EsmConfig, FlaubertConfig, FNetConfig, FSMTConfig, FunnelConfig, GitConfig, GPT2Config, GPT2Config, GPTBigCodeConfig, GPTNeoConfig, GPTNeoXConfig, GPTNeoXJapaneseConfig, GPTJConfig, GPTSanJapaneseConfig, GroupViTConfig, HubertConfig, IBertConfig, JukeboxConfig, LayoutLMConfig, LayoutLMv2Config, LayoutLMv3Config, LEDConfig, LiltConfig, LlamaConfig, LongformerConfig, LongT5Config, LukeConfig, LxmertConfig, M2M100Config, MarianConfig, MBartConfig, MegaConfig, MegatronBertConfig, MgpstrConfig, MobileBertConfig, MPNetConfig, MT5Config, MvpConfig, NezhaConfig, NllbMoeConfig, NystromformerConfig, OneFormerConfig, OpenAIGPTConfig, OPTConfig, OwlViTConfig, PegasusConfig, PegasusXConfig, PerceiverConfig, Pix2StructConfig, PLBartConfig, ProphetNetConfig, QDQBertConfig, RagConfig, RealmConfig, ReformerConfig, RemBertConfig, RetriBertConfig, RobertaConfig, RobertaPreLayerNormConfig, RoCBertConfig, RoFormerConfig, RwkvConfig, Speech2TextConfig, Speech2Text2Config, SpeechT5Config, SplinterConfig, SqueezeBertConfig, SwitchTransformersConfig, T5Config, TapasConfig, TransfoXLConfig, ViltConfig, VisualBertConfig, Wav2Vec2Config, Wav2Vec2ConformerConfig, WhisperConfig, XCLIPConfig, XGLMConfig, XLMConfig, XLMProphetNetConfig, XLMRobertaConfig, XLMRobertaXLConfig, XLNetConfig, XmodConfig, YosoConfig, ExampleConfig.
```
When I print out `TOKENIZER_MAPPING`, I see `ExampleConfig` in it (as in the error message), meaning that the tokenizer registration indeed occurred. However, when I print out `CONFIG_MAPPING_NAMES`, which is the check that fails generating the error, `ExampleConfig` is not in that dictionary.

I have played around with a few different ways of creating the tokenizer class (e.g. actually subclassing `PreTrainedTokenizerFast` instead of just aliasing it), but none of these modifications worked, and the issue seems to be with the registration part rather than the choice of tokenizer.

### Expected behavior

I would expect the `AutoTokenizer.from_pretrained(...)` line from above to run without error

Comments:
I was able to reproduce the error. I will take a look at this problem.

Hi @william-cerebras @ArthurZucker! 
I have done some quick debugging and the problem is really simple. In `configuration_auto.py` we have `OrderedDict` `CONFIG_MAPPING_NAMES` as well as `CONFIG_MAPPING = _LazyConfigMapping(CONFIG_MAPPING_NAMES)`. When we register new config we are calling `AutoConfig.register` which underneath updates `CONFIG_MAPPING` by calling `CONFIG_MAPPING.register`. `CONFIG_MAPPING` is an instance of `_LazyConfigMapping` class which has two variables `self._mapping` which is just `CONFIG_MAPPING_NAMES` as well as `self._extra_content`. When `CONFIG_MAPPING.register` is being called it is updating `self._extra_content` dict and not `self._mapping` itself. But later in the code when we  call `AutoTokenizer.from_pretrained` we search for config class name to convert it into corresponding model type. It is done inside `config_class_to_model_type` function. Now to the clue, inside this function we iterate over `CONFIG_MAPPING_NAMES` which as I pointed out earlier is not being updated while registering new config.  So in order to make it work we can do a simple fix: 
Change this:
```python
def config_class_to_model_type(config):
    """Converts a config class name to the corresponding model type"""
    for key, cls in CONFIG_MAPPING_NAMES.items():
        if cls == config:
            return key
    return None
```
to this:
```python
def config_class_to_model_type(config):
    """Converts a config class name to the corresponding model type"""
    for key, cls in CONFIG_MAPPING.items():
        if cls.__name__ == config:
            return key
    return None
```
To make it clear why this will work is because when we call `CONFIG_MAPPING.items()` it underneath merges `self._mapping` and `self._extra_content` and as a result includes newly registered config:
```python
def items(self):
    return [(k, self[k]) for k in self._mapping.keys()] + list(self._extra_content.items())
```

cc @sgugger 

Thanks for the ping @Bearnardd. The change you suggest is a bit too strong as it will actually go import everything config to build `CONFIG_MAPPING.items()`. I think we can keep the check on the names as it is, then add a check looping `CONFIG_MAPPING_extra_content` while keeping the spirit of your fix (if that makes sense).

I can work on that or you can open a PR if you prefer @Bearnardd 

Hi @sgugger! I will fix that

Thanks for the fix @Bearnardd @sgugger!