• BERT√


    BERT架构(基于Transformers)

    from transformers import BertModel
    tokenizer = BertTokenizer.from_pretrained('vocab.txt')
    bert_config = BertConfig.from_json_file('config.json')
    model = BertModel(bert_config)
    state_dict = model.state_dict()
    # bert_config、vocab.txt 下载地址:https://huggingface.co/cl-tohoku/bert-base-japanese-char/tree/main
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    embeddings

    在这里插入图片描述
    在这里插入图片描述
    token embedding (embeddings.word_embeddings)层是要将各个词转换成固定维度的向量。
    在这里插入图片描述
    segment embedding (embeddings . token_ type_ embeddings)BERT 能够处理对输入句子对的分类任务。
    在这里插入图片描述
    position embeddings 会让BERT理解下面下面这种情况, I think, therefore I am,第一个 “I” 和第二个 “I”应该有着不同的向量表示
    LayerNorm 归一化 将需要处理的数据在通过某种算法经过处理后,限制将其限定在你需要的一定的范围内。

    在这里插入图片描述

    encoder 12层

    在这里插入图片描述

    attention

    在这里插入图片描述
    Q、K、V

    intermediate

    output

    pooler

    在这里插入图片描述

    输入为什么有512这个长度限制?

    原文:To speed up pretraing in our experiments, we pre-train the model with sequence length of 128 for 90% of the steps. Then, we train the rest 10% of the steps of sequence of 512 to learn the positional embeddings.
    学习了512长度的positional embeddings,如果长于512则不存在位置信息,出错。BERT中的Positional Embedding和Transformer中的Positional Embedding实现方式并不一样。
    BERT中的Positional Embedding本质上是一个可学习的参数,也就是说每个位置所对应的位置向量就类似于Token Embedding中每个词对应的词向量。如下代码所示为Positional Embedding部分的代码实现,可以发现其本质上也就是一个普通的可学习的Embedding层。

     class PositionalEmbedding(nn.Module):
     
     def __init__(self, hidden_size, max_position_embeddings=512, initializer_range=0.02):
         super(PositionalEmbedding, self).__init__()
         self.embedding = nn.Embedding(max_position_embeddings, hidden_size)      
         
     def forward(self, position_ids):
         return self.embedding(position_ids).transpose(0, 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    因此,除非是你自己从零开始训练一个模型,否则如果你使用的是谷歌开源的预训练模型,那么这个词表的大小将会被限制在512。 当然,我们依旧可以突破这个限制,那就是重新初始化Positional Embedding中的向量,并将前512个向量用已有的进行替换,超出部分就使用随机初始化的权重在语料上进行微调或训练。代码实现:

     1     @classmethod
     2     def from_pretrained(cls, config, pretrained_model_dir=None):
     3         model = cls(config)  # 初始化模型,cls为未实例化的对象,即一个未实例化的BertModel对象
     4         pretrained_model_path = os.path.join(pretrained_model_dir, "pytorch_model.bin")
     5         if not os.path.exists(pretrained_model_path):
     6             raise ValueError(f"<路径:{pretrained_model_path} 中的模型不存在,请仔细检查!>")
     7         loaded_paras = torch.load(pretrained_model_path)
     8         state_dict = deepcopy(model.state_dict())
     9         loaded_paras_names = list(loaded_paras.keys())[:-8]
    10         model_paras_names = list(state_dict.keys())[1:]
    11         for i in range(len(loaded_paras_names)):
    12             state_dict[model_paras_names[i]] = loaded_paras[loaded_paras_names[i]]
    13             logging.debug(f"## 成功将参数:{loaded_paras_names[i]}赋值给{model_paras_names[i]},"
    14                           f"参数形状为:{state_dict[model_paras_names[i]].size()}")        
    15         model.load_state_dict(state_dict)
    16         return model
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在上述代码中,第3-10行用来载入本地的模型参数;第11-15行则是用来将载入的模型参数赋值到现有的模型。
    进一步,我们只需要判断在第12行之前判断当前参数是否为positional embeding层,如果是进行替换即可,代码如下:

    1     def replace_512_position(init_embedding, loaded_embedding, config):
     2         logging.info(f"模型参数max_positional_embedding > 512,采用替换处理!")
     3         init_embedding[:512, :] = loaded_embedding[:512, :]
     4         return init_embedding
     5 
     6     @classmethod
     7     def from_pretrained(cls, config, pretrained_model_dir=None):
     8         # 此处代码同上
     9         for i in range(len(loaded_paras_names)):
    10             if "position_embeddings" in model_paras_names[i]:
    11                 # 这部分代码用来消除预训练模型只能输入小于512个字符的限制
    12                 if config.max_position_embeddings > 512:
    13                     new_embedding = replace_512_position(state_dict[model_paras_names[i]],
    14                                                          loaded_paras[loaded_paras_names[i]],
    15                                                          config)
    16                     state_dict[model_paras_names[i]] = new_embedding
    17             else:
    18                 state_dict[model_paras_names[i]] = loaded_paras[loaded_paras_names[i]]
    19             logging.debug(f"## 成功将参数:{loaded_paras_names[i]}赋值给{model_paras_names[i]},"
    20                           f"参数形状为:{state_dict[model_paras_names[i]].size()}")
    21         model.load_state_dict(state_dict)
    22         return model
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    通过上述代码,我们在载入预训练模型的同时就成功将随机初始化positional embedding中的前512个向量替换为了预训练模型中positional embedding中的参数。在这之后,剩余部分的参数可以通过下游任务来进行微调,也可以在一些语料上根据NSP和MLM任务进行训练。
    Transformer中的Positional Embedding是通过公式得到的
    在这里插入图片描述

    参考

    https://zhuanlan.zhihu.com/p/493424507
    https://blog.csdn.net/weixin_48185819/article/details/122042452
    https://zhuanlan.zhihu.com/p/469570502

  • 相关阅读:
    消息队列实现进程间通信
    第三章. 业务功能开发--用户登录安全退出
    机器学习笔记 - 使用机器学习进行鸟类物种分类
    服装工业新消费·PLM
    leetcode每天5题-Day10
    ElementUI之登录与注册
    Python前后通吃,可在浏览器端运行
    《opencv学习笔记》-- Harris角点检测
    AD637使用笔记
    Spring-MVC
  • 原文地址:https://blog.csdn.net/qq_36372569/article/details/127648509