Skip to content

Latest commit

 

History

History
176 lines (112 loc) · 7.33 KB

3.2 模型框架构建.md

File metadata and controls

176 lines (112 loc) · 7.33 KB

1 模型框架构建

本项目使用PyTorch构建BERT模型。BERT是一种预训练深度双向Transformer模型,用于自然语言处理任务。模型核心两个主要部分:BertModel类和Model类。BertModel类负责实现BERT模型的核心架构,而Model类则在此基础上添加了预训练任务的输出层和损失函数。

1.1 Bert模型构建

BertModel类是BERT模型的核心,它包含了BERT的嵌入层、编码层和池化层。

1.1.1 BertModel

在初始化过程中,BertModel接收config参数,字典config中包含了隐藏层大小、前馈网络大小、注意力头数和层数。

  • 嵌入层:将输入的索引转换为对应的嵌入表示,包括词嵌入、位置嵌入和段嵌入。

  • 编码层:由多个Transformer编码器层组成,这些编码器层堆叠在一起,形成了BERT的主体结构。每个编码器层都包含自注意力机制和前馈网络,能够处理序列数据并捕捉长距离依赖关系。

  • 池化层:对编码层的输出进行聚合,得到整个序列的表示,这通常用于分类任务。

如下代码所示,输入嵌入后,在进入编码层,最后进入池化层,将所得结果返回。

class BertModel(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.embedding = BertEmbedding(config)

        self.bert_layer = nn.Sequential(
            *[BertEncoder(config["hidden_size"], config["feed_num"], config["head_num"]) for i in
              range(config["layer_num"])])

        self.pool = BertPooler(config)

    def forward(self, batch_idx, batch_seg_idx):
        x = self.embedding(batch_idx, batch_seg_idx)

        x = self.bert_layer(x)

        bertout2 = self.pool(x)

        return x, bertout2

1.1.2 BertEmbeddding

以下是BERT嵌入层(BertEmbeddding)的实现。

初始化

  • 在初始化方法中,BERT定义了三个嵌入层(embeddings),这与Transfomer的嵌入方法一致:
    • word_embeddings:用于将词汇索引映射到嵌入向量。
    • position_embeddings:用于将位置索引映射到位置嵌入向量,以捕获单词在句子中的位置信息。
    • token_type_embeddings:用于将令牌类型索引映射到类型嵌入向量。
  • 每个嵌入层都是一个nn.Embedding实例,其权重(weight)被设置为可训练(requires_grad=True),这里也可定义为不可训练。

前向传播

  • forward方法接收两个参数(batch_idxbatch_seg_idx),这两个变量分别代表句子的token表示和句子的划分(是第一个句子还是后一个句子)。
  • word_emb通过word_embeddings层获取词汇嵌入。
  • pos_idx生成一个位置索引张量,其大小与批量大小相匹配,并用于通过position_embeddings层获取位置嵌入。这里使用torch.arangerepeat来生成位置索引。
  • token_emb通过token_type_embeddings层获取令牌类型嵌入。
  • 将词汇嵌入、位置嵌入和令牌类型嵌入相加,得到最终的嵌入表示。
  • 应用层归一化和丢弃操作,然后返回最终的嵌入向量。

代码如下所示:

class BertEmbedding(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.word_embeddings = nn.Embedding(config["vocab_size"], config["hidden_size"])
        self.word_embeddings.weight.requires_grad = True

        self.position_embeddings = nn.Embedding(config["max_position_embeddings"], config["hidden_size"])
        self.position_embeddings.weight.requires_grad = True

        self.token_type_embeddings = nn.Embedding(config["type_vocab_size"], config["hidden_size"])
        self.token_type_embeddings.weight.requires_grad = True

        self.LayerNorm = nn.LayerNorm(config["hidden_size"])
        self.dropout = nn.Dropout(config["hidden_dropout_prob"])

    def forward(self, batch_idx, batch_seg_idx):
        word_emb = self.word_embeddings(batch_idx)

        pos_idx = torch.arange(0, self.position_embeddings.weight.data.shape[0], device=batch_idx.device)
        pos_idx = pos_idx.repeat(batch_idx.shape[0], 1)
        pos_emb = self.position_embeddings(pos_idx)

        token_emb = self.token_type_embeddings(batch_seg_idx)

        emb = word_emb + pos_emb + token_emb

        emb = self.LayerNorm(emb)
        emb = self.dropout(emb)

        return emb

1.1.3 BertEncoder

BertEncoder部分即为TransformerEncoder部分。

class BertEncoder(nn.Module):
    def __init__(self,hidden_size,feed_num,head_num):
        super().__init__()

        self.multi_head_att = Multi_Head_Att(hidden_size,head_num) 
        self.add_norm1 = Add_Norm(hidden_size)
        self.feed_forward = Feed_Forward(hidden_size,feed_num)
        self.add_norm2 = Add_Norm(hidden_size)

    def forward(self,x): 
        multi_head_out = self.multi_head_att(x) 
        add_norm1_out = self.add_norm1(multi_head_out) 

        add_norm1_out = x + add_norm1_out

        feed_forward_out = self.feed_forward(add_norm1_out)  
        add_norm2_out = self.add_norm2(feed_forward_out) 

        add_norm2_out = add_norm1_out + add_norm2_out

        return add_norm2_out

1.2 损失函数计算

Model类在BertModel的基础上添加了预训练任务的输出层和损失函数,用于执行BERT的两种主要预训练任务:“masked language model”(MLM)和“next sentence prediction”(NSP)。

初始化

Model定义了两个线性层用于MLMNSP任务的输出,以及对应的损失函数。MLM任务的损失函数会忽略填充索引,而NSP任务的损失函数则正常计算。

前向传播

在前向传播过程中,Model类首先调用BertModel的前向传播方法,获取编码层的输出和池化层的输出。然后,这些输出分别通过MLM和NSP的线性层,得到预测结果。如果提供了真实值和标签,模型将计算两种任务的损失,并返回总损失。如果没有提供真实值和标签,模型将返回预测结果。

class Model(nn.Module):
    def __init__(self, config):
        super().__init__()

        self.bert = BertModel(config)

        self.cls_mask = nn.Linear(config["hidden_size"], config["vocab_size"])
        self.cls_next = nn.Linear(config["hidden_size"], 2)

        self.loss_fun_mask = nn.CrossEntropyLoss(ignore_index=0)
        self.loss_fun_next = nn.CrossEntropyLoss()

    def forward(self, batch_idx, batch_seg_idx, batch_mask_val=None, batch_label=None):
        bertout1, bertout2 = self.bert(batch_idx, batch_seg_idx)

        pre_mask = self.cls_mask(bertout1)
        pre_next = self.cls_next(bertout2)

        if batch_mask_val is not None and batch_label is not None:
            loss_mask = self.loss_fun_mask(pre_mask.reshape(-1, pre_mask.shape[-1]), batch_mask_val.reshape(-1))
            loss_next = self.loss_fun_next(pre_next, batch_label)

            loss = loss_mask + loss_next

            return loss
        else:
            return torch.argmax(pre_mask, dim=-1), torch.argmax(pre_next, dim=-1)

2 总结

本章节介绍了BERT模型的构建过程,包括其核心组件和预训练任务的实现。BERT模型通过深度双向预训练,能够学习到丰富的语言特征,并在多种自然语言处理任务中取得优异的性能。通过BertModel类和Model类的实现,BERT模型可以有效地进行预训练,并适应不同的下游任务。