代码演示bert的12层和一层 代码上区别

在代码实现上,BERT 的层数差异主要体现在Transformer 编码器堆叠的数量和参数规模上。以下通过 PyTorch 代码对比单层 BERT 和 12 层 BERT 的核心差异(为简化说明,省略部分非关键细节):

一、核心结构对比

1. 单层 BERT(仅 1 个 Transformer 编码器)

python
 
运行
 
 
 
 
import torch
import torch.nn as nn
import torch.nn.functional as F

class SingleLayerBERT(nn.Module):
    def __init__(self, vocab_size, d_model=768, nhead=12, dim_feedforward=3072):
        super().__init__()
        # 词嵌入和位置嵌入
        self.token_embedding = nn.Embedding(vocab_size, d_model)
        self.position_embedding = nn.Embedding(512, d_model)
        self.layer_norm = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(0.1)
        
        # 单层Transformer编码器
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=d_model,
            nhead=nhead,
            dim_feedforward=dim_feedforward,
            dropout=0.1,
            activation="gelu"
        )
        self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=1)  # 仅1层
        
        # MLM预测头
        self.mlm_head = nn.Sequential(
            nn.Linear(d_model, d_model),
            nn.GELU(),
            nn.LayerNorm(d_model),
            nn.Linear(d_model, vocab_size)
        )
        
        # NSP预测头
        self.nsp_head = nn.Linear(d_model, 2)
        
    def forward(self, input_ids, attention_mask=None):
        # 构建输入表示
        seq_length = input_ids.size(1)
        position_ids = torch.arange(seq_length, device=input_ids.device).unsqueeze(0)
        
        # 嵌入层
        x = self.token_embedding(input_ids) + self.position_embedding(position_ids)
        x = self.layer_norm(x)
        x = self.dropout(x)
        
        # 注意力掩码转换(Transformer期望的形状)
        if attention_mask is not None:
            attention_mask = attention_mask.unsqueeze(1).unsqueeze(2)
            attention_mask = attention_mask.to(dtype=torch.float32)
            attention_mask = (1.0 - attention_mask) * -10000.0
        
        # 通过编码器(仅1层)
        encoder_output = self.encoder(x.transpose(0, 1), src_key_padding_mask=None).transpose(0, 1)
        
        # MLM预测
        mlm_output = self.mlm_head(encoder_output)
        
        # NSP预测(取[CLS] token)
        cls_output = encoder_output[:, 0, :]
        nsp_output = self.nsp_head(cls_output)
        
        return mlm_output, nsp_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

2. 12 层 BERT(标准配置)

python
 
运行
 
 
 
 
class TwelveLayerBERT(nn.Module):
    def __init__(self, vocab_size, d_model=768, nhead=12, dim_feedforward=3072):
        super().__init__()
        # 词嵌入和位置嵌入(与单层相同)
        self.token_embedding = nn.Embedding(vocab_size, d_model)
        self.position_embedding = nn.Embedding(512, d_model)
        self.layer_norm = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(0.1)
        
        # 12层Transformer编码器
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=d_model,
            nhead=nhead,
            dim_feedforward=dim_feedforward,
            dropout=0.1,
            activation="gelu"
        )
        self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=12)  # 12层
        
        # 预测头(与单层相同)
        self.mlm_head = nn.Sequential(
            nn.Linear(d_model, d_model),
            nn.GELU(),
            nn.LayerNorm(d_model),
            nn.Linear(d_model, vocab_size)
        )
        
        self.nsp_head = nn.Linear(d_model, 2)
        
    def forward(self, input_ids, attention_mask=None):
        # 构建输入表示(与单层相同)
        seq_length = input_ids.size(1)
        position_ids = torch.arange(seq_length, device=input_ids.device).unsqueeze(0)
        
        x = self.token_embedding(input_ids) + self.position_embedding(position_ids)
        x = self.layer_norm(x)
        x = self.dropout(x)
        
        # 注意力掩码转换
        if attention_mask is not None:
            attention_mask = attention_mask.unsqueeze(1).unsqueeze(2)
            attention_mask = attention_mask.to(dtype=torch.float32)
            attention_mask = (1.0 - attention_mask) * -10000.0
        
        # 通过编码器(12层)
        encoder_output = self.encoder(x.transpose(0, 1), src_key_padding_mask=None).transpose(0, 1)
        
        # 预测头(与单层相同)
        mlm_output = self.mlm_head(encoder_output)
        cls_output = encoder_output[:, 0, :]
        nsp_output = self.nsp_head(cls_output)
        
        return mlm_output, nsp_output
 

二、核心差异点分析

对比项单层 BERT12 层 BERT
编码器层数 num_layers=1 num_layers=12
参数量 约 11M(主要来自嵌入层) 约 110M(12 层编码器的堆叠)
计算复杂度 低(单遍 Transformer 计算) 高(12 遍 Transformer 计算)
特征提取能力 浅层次特征(如局部语法) 深层次特征(如全局语义关系)
训练难度 易收敛,数据需求少 需大规模数据和更长训练时间
下游任务表现 简单任务(如短文本分类) 复杂任务(如问答、长文本理解)

三、实际应用中的选择

1. 单层 BERT 的适用场景

  • 资源受限的设备(如移动终端)。
  • 简单任务或小规模数据集(如关键词提取)。
  • 快速原型验证。

2. 12 层 BERT 的适用场景

  • 标准 NLP 任务(如 GLUE 基准测试)。
  • 需要深层语义理解的场景(如语义相似度计算)。
  • 有足够计算资源和训练数据。

四、性能对比示例

python
 
运行
 
 
 
 
# 对比参数量
single_layer_bert = SingleLayerBERT(vocab_size=30522)
twelve_layer_bert = TwelveLayerBERT(vocab_size=30522)

def count_parameters(model):
    return sum(p.numel() for p in model.parameters() if p.requires_grad)

print(f"单层BERT参数量: {count_parameters(single_layer_bert):,}")  # 约11M
print(f"12层BERT参数量: {count_parameters(twelve_layer_bert):,}")  # 约110M

# 对比前向传播速度(伪代码)
import time
input_ids = torch.randint(0, 30522, (1, 128))
attention_mask = torch.ones(1, 128)

# 单层BERT推理时间
start_time = time.time()
single_layer_bert(input_ids, attention_mask)
print(f"单层BERT推理时间: {time.time() - start_time:.4f}秒")

# 12层BERT推理时间
start_time = time.time()
twelve_layer_bert(input_ids, attention_mask)
print(f"12层BERT推理时间: {time.time() - start_time:.4f}秒")  # 通常慢10倍以上
 

五、层数对模型能力的影响

  • 单层 BERT:只能捕获局部特征,类似于 LSTM 的能力,难以处理长距离依赖。
  • 多层 BERT:通过堆叠,每层可以捕获不同抽象级别的信息(如底层捕获语法,高层捕获语义),符合人类语言理解的层级结构。

实际应用中,若算力有限但任务简单,可选择单层 BERT 并增加训练数据;若任务复杂,建议使用预训练的多层 BERT 并微调。
posted @ 2025-06-22 16:15  m516606428  阅读(42)  评论(0)    收藏  举报