手动实现bert网络结构
目标
今天旨在通过手动实现一个简化版的 BERT 模型 (DiyBert 类),并与 Hugging Face 提供的 BERT 模型进行对比,验证自定义实现是否正确。代码的核心内容涉及矩阵运算来实现 BERT 的各个部分,包括 embedding、self-attention、feed-forward 网络、LayerNorm 和 pooler 层等。
实现过程
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_embeddings、position_embeddings、token_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-attention和feed-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

最低0.47元/天 解锁文章
1447

被折叠的 条评论
为什么被折叠?



