bert详解
huggingface中的模型
huggingface中的模型定义的基本结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def _forward_unimplemented (self, *input : Any ) -> None : raise NotImplementedError(f"Module [{type (self).__name__} ] is missing the required \"forward\" function" ) class Module : def __init__ (self ) -> None : self.basename = "BaseModel" def __call__ (self, *args: Any , **kwds: Any ) -> Any : print (f"now you call __call__,params={args} " ) self.forward(args[0 ]) forward: Callable [..., Any ] = _forward_unimplemented class MyModel (Module ): def __init__ (self, name ) -> None : super ().__init__() self.name = name def forward (self,x ): print (f"now you call forward,param={x} " ) model = MyModel("bert" ) model([1 ,2 ,3 ,4 ])
加载bert模型
1 2 3 4 5 6 7 8 9 10 from typing import Any from transformers import AutoTokenizer, AutoModelfrom typing import Callable tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-uncased" ) print ('tokenizer type=' ,type (tokenizer))model = AutoModel.from_pretrained("google-bert/bert-base-uncased" ) inputs = tokenizer("你是个好人" , return_tensors='pt' ) output = model(**inputs)
bert中分词tokenize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-uncased" ) print ("词典大小:" ,tokenizer.vocab_size)text = "the game has gone!unaffable I have a new GPU!" tokens = tokenizer.tokenize(text) print (f"英文分词, 英文:{text} ,分词:{tokens} " )text = "我爱北京天安门,吢吣" tokens = tokenizer.tokenize(text) print (f"中文分词, 中文:{text} ,分词:{tokens} " )input_ids = tokenizer.convert_tokens_to_ids(tokens) print ("id-token转换:" ,input_ids)sen_code = tokenizer.encode_plus("i like you much" , "but not him" ) print ("多句子encode:" ,sen_code)print ("decode:" ,tokenizer.decode(sen_code['input_ids' ]))inputs = tokenizer("你好" , return_tensors="pt" ) print (f'tokenizer("你好")={inputs} ' )inputs = tokenizer(["你好吗" ,"。不好的。哈哈" ], padding=True , truncation=True , max_length=128 , return_tensors="pt" ) print (f'tokenizer("你好吗。不好的。哈哈")={inputs} ' )
Bert的tokenizer中有特殊标记(Special Tokens)。它们的含义如下:
[PAD]:在batch中对齐序列长度时,用 [PAD]进行填充以使所有序列长度相同。可以通过将其添加到较短的序列末尾来实现对齐。
[CLS]:在输入序列的开头添加 [CLS] 标记,以表示该序列的分类结果。
[SEP]:用于分隔两个句子,例如在文本分类问题中,将两个句子拼接成一个输入序列时,可以使用 [SEP] 来分隔这两个句子。
[UNK]:此标记用于表示未知或词汇外的单词。当一个模型遇到一个它以前没有见过/无法识别的词时,它会用这个标记替换它。
AutoTokenizer到bert tokenizer
对于bert模型,模型文件格式如下:
1 2 3 4 5 6 7 . ├── config.json -> ../../blobs/45a2321a7ecfdaaf60a6c1fd7f5463994cc8907d ├── model.safetensors -> ../../blobs/68d45e234eb4a928074dfd868cead0219ab85354cc53d20e772753c6bb9169d3 ├── tokenizer_config.json -> ../../blobs/e5c73d8a50df1f56fb5b0b8002d7cf4010afdccb ├── tokenizer.json -> ../../blobs/949a6f013d67eb8a5b4b5b46026217b888021b88 └── vocab.txt -> ../../blobs/fb140275c155a9c7c5a3b3e0e77a9e839594a938
调用tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-uncased")
后,from_pretrained
函数首先调用get_tokenizer_config
读取tokenizer_config.json
文件中的数据,
1 2 3 {"do_lower_case" : true, "model_max_length" : 512 }
解析tokenizer_config.json
的tokenizer_class
,如果不存在,则读取config.json
中的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { "architectures" : [ "BertForMaskedLM" ], "attention_probs_dropout_prob" : 0.1 , "gradient_checkpointing" : false, "hidden_act" : "gelu" , "hidden_dropout_prob" : 0.1 , "hidden_size" : 768 , "initializer_range" : 0.02 , "intermediate_size" : 3072 , "layer_norm_eps" : 1e-12 , "max_position_embeddings" : 512 , "model_type" : "bert" , "num_attention_heads" : 12 , "num_hidden_layers" : 12 , "pad_token_id" : 0 , "position_embedding_type" : "absolute" , "transformers_version" : "4.6.0.dev0" , "type_vocab_size" : 2 , "use_cache" : true, "vocab_size" : 30522 }
可见,这里的model_type
表明了模型类型为bert
。tokenizer_class
和model_type
只要存在一个,都可以去内置的Tokenizer中匹配到对应的Tokenizer,然后使用PreTrainedTokenizerBase(PreTrainedTokenizerFast).from_pretrained
去创建一个使用词表vocab.txt
和分词器tokenizer.json, tokenizer_config.json
作为参数构建的Tokenizer。(BertTokenizer等都是继承自PreTrainedTokenizerBase)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 TOKENIZER_MAPPING_NAMES = OrderedDict( [ ( "albert" , ( "AlbertTokenizer" if is_sentencepiece_available() else None , "AlbertTokenizerFast" if is_tokenizers_available() else None , ), ), ("align" , ("BertTokenizer" , "BertTokenizerFast" if is_tokenizers_available() else None )), ("bark" , ("BertTokenizer" , "BertTokenizerFast" if is_tokenizers_available() else None )), ("bart" , ("BartTokenizer" , "BartTokenizerFast" )), ( "barthez" , ( "BarthezTokenizer" if is_sentencepiece_available() else None , "BarthezTokenizerFast" if is_tokenizers_available() else None , ), ), ("bartpho" , ("BartphoTokenizer" , None )), ("bert" , ("BertTokenizer" , "BertTokenizerFast" if is_tokenizers_available() else None )), ("bert-generation" , ("BertGenerationTokenizer" if is_sentencepiece_available() else None , None )), ("bert-japanese" , ("BertJapaneseTokenizer" , None )), ("bertweet" , ("BertweetTokenizer" , None )), ( "big_bird" , ( "BigBirdTokenizer" if is_sentencepiece_available() else None , "BigBirdTokenizerFast" if is_tokenizers_available() else None , ), ), ])
注意:
tokenizer.json 文件是 Hugging Face 的 tokenizers 库用来存储预训练的tokenizer的配置和词汇表的文件。这个文件包含了词汇表(vocabulary)以及tokenizer的设置(例如特殊的token,如 [CLS], [SEP], [PAD],以及词汇表的大小,是否使用lower case等等)。
这个文件通常在你使用预训练的模型(如BERT, GPT-2等)进行微调(fine-tuning)时会用到,因为你需要用到和原始预训练模型相同的tokenizer来确保输入的处理方式一致。这个文件在你调用 from_pretrained 方法加载预训练模型时会自动加载。
然而,如果你在构建自己的tokenizer时,你可能不会直接使用到这个文件。你可能会使用一些基础的tokenizer组件(如 BertTokenizer, GPT2Tokenizer 等)和你自己的词汇表来构建tokenizer。在这种情况下,你可能不会直接使用 tokenizer.json 文件,而是使用这些组件和你的词汇表来构建tokenizer。
总的来说,tokenizer.json 文件是一个用来存储预训练tokenizer配置的文件,它在加载预训练模型时会用到,但在构建自定义tokenizer时可能不会直接使用。
在Hugging Face的transformers库中,tokenizer.json文件主要是在使用fast版本的tokenizer时使用的。fast版本的tokenizer是用Rust编写的,性能更好,功能也更丰富。这些tokenizer可以通过tokenizers库单独使用,也可以通过transformers库使用。
1 2 3 tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-uncased" ) print ('tokenizer type=' ,type (tokenizer))
AutoModel到BertModel
那么,AutoModel是如何找到BertModel的?
在AutoModel类中,会局部变量model_mapping初始化为MODEL_MAPPING = _LazyAutoMapping(CONFIG_MAPPING_NAMES, MODEL_MAPPING_NAMES)
来映射内部所有模型和配置和模型类的映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 CONFIG_MAPPING_NAMES = OrderedDict( [ ("albert" , "AlbertConfig" ), ("align" , "AlignConfig" ), ("altclip" , "AltCLIPConfig" ), ("audio-spectrogram-transformer" , "ASTConfig" ), ("autoformer" , "AutoformerConfig" ), ("bark" , "BarkConfig" ), ("bart" , "BartConfig" ), ("beit" , "BeitConfig" ), ("bert" , "BertConfig" ), ]) MODEL_MAPPING_NAMES = OrderedDict( [ ("albert" , "AlbertModel" ), ("align" , "AlignModel" ), ("altclip" , "AltCLIPModel" ), ("audio-spectrogram-transformer" , "ASTModel" ), ("autoformer" , "AutoformerModel" ), ("bark" , "BarkModel" ), ("bart" , "BartModel" ), ("beit" , "BeitModel" ), ("bert" , "BertModel" ), ])
首先执行AutoModel.from_pretrained
函数,先拉取google-bert/bert-base-uncased中的config.json
文件,并使用AutoConfig.from_pretrained
构建一个类型为=<class 'transformers.models.bert.configuration_bert.BertConfig'>
的对象。原理为从config.json中的model_type为bert来从CONFIG_MAPPING_NAMES加载对应的Config类
根据构建的BertConfig结构,在model_mapping中找到映射到的具体Model类,即BertModel