Day07 【手动实现bert网络结构】

目标

今天旨在通过手动实现一个简化版的 BERT 模型 (DiyBert 类),并与 Hugging Face 提供的 BERT 模型进行对比,验证自定义实现是否正确。代码的核心内容涉及矩阵运算来实现 BERT 的各个部分,包括 embeddingself-attentionfeed-forward 网络、LayerNormpooler 层等。
实现过程

1. 导入所需的库

import torch
import math
import numpy as np
from transformers import BertModel
  • torch:PyTorch库,用于深度学习相关操作。
  • math:提供数学函数,如平方根、幂运算等。
  • np:NumPy库,用于处理矩阵运算和向量化操作。
  • BertModel:从Hugging Face的transformers库中导入BERT预训练模型。

2. softmax函数

def softmax(x):
    return np.exp(x) / np.sum(np.exp(x), axis=-1, keepdims=True)
  • 该函数对输入进行归一化处理,返回每个元素相对其总和的概率分布。用于 self-attention 计算时,对注意力权重进行规范化。

3. GELU激活函数

def gelu(x):
    return 0.5 * x * (1 + np.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * np.power(x, 3))))
  • GELU(Gaussian Error Linear Unit)激活函数是BERT模型中常用的激活函数,基于高斯误差线性单元。

4. 自定义DiyBert 类

该类构造了一个自定义的 BERT 模型,试图通过手动矩阵运算实现 BERT 模型的各个部分。

__init__初始化
def __init__(self, state_dict):
    self.num_attention_heads = 12
    self.hidden_size = 768
    self.num_layers = 1  # 模型的层数,通常是12
    self.load_weights(state_dict)
  • 初始化时,定义了 BERT 模型的结构,如注意力头的数量、隐藏层的维度、Transformer层的数量等。
  • load_weights 方法将从预训练模型加载的权重存储到相应的变量中。
权重参数加载
def load_weights(self, state_dict):
    self.word_embeddings = state_dict["embeddings.word_embeddings.weight"].numpy()
    ...
    self.pooler_dense_weight = state_dict["pooler.dense.weight"].numpy()
    self.pooler_dense_bias = state_dict["pooler.dense.bias"].numpy()
  • 该方法从预训练的模型中加载词嵌入层、位置编码、Transformer的权重等。
embedding编码层
def embedding_forward(self, x):
    we = self.get_embedding(self.word_embeddings, x)
    pe = self.get_embedding(self.position_embeddings, np.array(list(range(len(x)))))
    te = self.get_embedding(self.token_type_embeddings, np.array([0] * len(x)))
    embedding = we + pe + te
    embedding = self.layer_norm(embedding, self.embeddings_layer_norm_weight, self.embeddings_layer_norm_bias)
    return embedding
  • 该方法计算输入的 word_embeddingsposition_embeddingstoken_type_embeddings 的加和,并对结果应用 LayerNorm 层。
get_embedding解码
def get_embedding(self, embedding_matrix, x):
    return np.array([embedding_matrix[index] for index in x])
  • 该方法用于根据输入索引从词嵌入矩阵中获取对应的嵌入。
多层注意力
def all_transformer_layer_forward(self, x):
    for i in range(self.num_layers):
        x = self.single_transformer_layer_forward(x, i)
    return x
  • all_transformer_layer_forward 会遍历所有 Transformer 层并对输入进行处理。
  • single_transformer_layer_forward 处理单层 Transformer 层,包括计算 self-attentionfeed-forward 网络,并使用 LayerNorm 层进行残差连接。
自注意力层
def self_attention(self, x, q_w, q_b, k_w, k_b, v_w, v_b, attention_output_weight, attention_output_bias, num_attention_heads, hidden_size):
    q = np.dot(x, q_w.T) + q_b
    k = np.dot(x, k_w.T) + k_b
    v = np.dot(x, v_w.T) + v_b
    ...
    qk = np.matmul(q, k.swapaxes(1, 2))
    qk /= np.sqrt(attention_head_size)
    qk = softmax(qk)
    qkv = np.matmul(qk, v)
    qkv = qk
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值