一文掌握BERT模型及在机器翻译中的应用

一、 BERT模型概述

1.1 BERT模型介绍

BERT(Bidirectional Encoder Representations from Transformers) 是由Google在2018年提出的革命性预训练语言模型,其核心创新在于:

  • 双向上下文理解:首次实现了真正意义上的双向语言表示学习
  • 预训练-微调范式:通过大规模无标注数据预训练,再针对特定任务微调
  • 掩码语言模型:作为预训练任务之一,使模型能够深入理解上下文

BERT的出现标志着NLP领域从特征工程向预训练模型的范式转变,其影响力深远,后续的RoBERTa、ALBERT、DistilBERT等模型都是基于BERT的改进。

1.2 BERT核心架构详解

BERT基于Transformer的Encoder部分构建,主要由以下组件组成:

  1. 词嵌入层
    • Token Embeddings:将词转换为向量
    • Segment Embeddings:区分句子A和句子B
    • Position Embeddings:表示词在序列中的位置
  2. Transformer Encoder层
    • 多层自注意力机制
    • 前馈神经网络
    • 层归一化和残差连接

1.3 关键技术创新

1、双向上下文表示
传统语言模型只能从左到右或从右到左学习上下文,而BERT通过掩码语言模型(MLM)实现了真正的双向表示:

输入:The cat sat on the mat
掩码:The cat sat on the [MASK]
预测:The cat sat on the mat

2、句对关系学习
BERT通过NSP任务学习句子间关系:

[CLS] I love Paris [SEP] Paris is the capital of France [SEP]

模型预测这两个句子是否是连续的。

1.4 模型变体

BERT主要有两个版本:

  • BERT-Base:110M参数,12层Transformer,768隐藏层,12个注意力头
  • BERT-Large:340M参数,24层Transformer,1024隐藏层,16个注意力头

1.5 BERT预训练任务

BERT采用两个无监督预训练任务:
1、掩码语言模型(MLM)
随机掩码输入中15%的词,然后预测这些被掩码的词:

原始:The quick brown fox jumps over the lazy dog
掩码:The quick [MASK] fox jumps over the lazy dog
预测:brown

2、下一句预测(NSP)
判断两个句子是否是连续的:

[CLS] How old are you? [SEP] I'm 20 years old [SEP]
预测:IsNext

1.6 BERT在机器翻译中的优势

  1. 双向上下文理解:BERT能够同时考虑左右上下文,更好地理解词义
  2. 预训练知识:利用大规模语料预训练,包含丰富的语言知识
  3. 多语言支持:多语言BERT版本可以处理多种语言对
  4. 灵活的架构:可以作为编码器或辅助组件集成到各种翻译系统中

1.7 局限性与改进方向

  1. 计算效率:BERT模型较大,推理速度较慢
    • 改进:使用知识蒸馏、模型量化、参数共享等技术
  2. 长文本处理:标准BERT有最大序列长度限制
    • 改进:使用Longformer、BigBird等处理长文本的变体
  3. 生成能力:BERT本身是编码器,不直接擅长生成任务
    • 改进:结合解码器架构,如BART、T5等
  4. 领域适应性:通用预训练模型在特定领域可能表现不佳
    • 改进:领域自适应预训练和微调

二、BERT在机器翻译中的应用

虽然BERT本身是编码器,不能直接用于生成式机器翻译,但它可以通过多种方式应用于翻译系统:

2.1 作为编码器组件

BERT可以作为编码器集成到Seq2Seq模型中:

输入句子 → BERT编码器 → 上下文表示 → 解码器 → 输出句子

2.2 双向编码器-单向解码器架构

结合BERT的编码能力和GPT风格解码器:

源语言 → BERT编码器 → 双向表示 → 单向解码器 → 目标语言

2.3 领域自适应

使用领域特定语料对BERT进行预训练,提高翻译质量:

通用BERT → 领域语料预训练 → 领域适配BERT → 翻译系统

2.4 多语言翻译

多语言BERT可以处理多种语言对:

[CLS] 源语言句子 [SEP] 目标语言句子 [SEP]

三、完整Python代码实现

3.1 环境准备

# 安装必要的库
!pip install transformers torch datasets sacremoses

3.2 使用预训练BERT进行机器翻译

import torch
from transformers import BertTokenizer, BertModel, BertConfig
from transformers import MarianMTModel, MarianTokenizer
from torch.nn import functional as F
# 加载多语言BERT模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
bert_model = BertModel.from_pretrained('bert-base-multilingual-cased')
# 加载预训练翻译模型
model_name = 'Helsinki-NLP/opus-mt-zh-en'
tokenizer_trans = MarianTokenizer.from_pretrained(model_name)
model_trans = MarianMTModel.from_pretrained(model_name)
def translate_with_bert(input_text, use_bert=True):
    """
    使用BERT增强的机器翻译函数
    
    参数:
        input_text: 输入文本
        use_bert: 是否使用BERT进行编码增强
        
    返回:
        翻译结果
    """
    # 分词和编码
    inputs = tokenizer_trans(input_text, return_tensors="pt", padding=True)
    
    if use_bert:
        # 使用BERT获取上下文表示
        bert_inputs = tokenizer(input_text, return_tensors="pt", padding=True, truncation=True)
        with torch.no_grad():
            bert_outputs = bert_model(**bert_inputs)
        
        # 获取[CLS]标记的表示作为句子表示
        sentence_embedding = bert_outputs.last_hidden_state[:, 0, :]
        
        # 将BERT表示添加到翻译模型输入
        # 这里简单地将BERT表示与输入嵌入相加
        inputs['encoder_hidden_state'] = sentence_embedding
    
    # 生成翻译
    with torch.no_grad():
        translated = model_trans.generate(**inputs)
    
    # 解码结果
    result = tokenizer_trans.decode(translated[0], skip_special_tokens=True)
    
    return result
# 示例使用
if __name__ == "__main__":
    input_text = "人工智能正在改变我们的生活方式。"
    print("输入文本:", input_text)
    
    # 不使用BERT的翻译
    result_without_bert = translate_with_bert(input_text, use_bert=False)
    print("不使用BERT的翻译:", result_without_bert)
    
    # 使用BERT的翻译
    result_with_bert = translate_with_bert(input_text, use_bert=True)
    print("使用BERT的翻译:", result_with_bert)

3.3 训练自定义BERT翻译模型

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from transformers import BertTokenizer, BertModel, AdamW, get_linear_schedule_with_warmup
from tqdm import tqdm, trange
import numpy as np
import random
# 设置随机种子
def set_seed(seed=42):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    if torch.cuda.is_available():
        torch.cuda.manual_seed_all(seed)
set_seed()
# 自定义数据集
class TranslationDataset(Dataset):
    def __init__(self, src_texts, tgt_texts, src_tokenizer, tgt_tokenizer, max_length=128):
        self.src_texts = src_texts
        self.tgt_texts = tgt_texts
        self.src_tokenizer = src_tokenizer
        self.tgt_tokenizer = tgt_tokenizer
        self.max_length = max_length
    
    def __len__(self):
        return len(self.src_texts)
    
    def __getitem__(self, idx):
        src_text = self.src_texts[idx]
        tgt_text = self.tgt_texts[idx]
        
        # 源语言编码
        src_encoding = self.src_tokenizer(
            src_text,
            max_length=self.max_length,
            padding='max_length',
            truncation=True,
            return_tensors='pt'
        )
        
        # 目标语言编码
        tgt_encoding = self.tgt_tokenizer(
            tgt_text,
            max_length=self.max_length,
            padding='max_length',
            truncation=True,
            return_tensors='pt'
        )
        
        return {
            'input_ids': src_encoding['input_ids'].flatten(),
            'attention_mask': src_encoding['attention_mask'].flatten(),
            'labels': tgt_encoding['input_ids'].flatten(),
            'decoder_attention_mask': tgt_encoding['attention_mask'].flatten()
        }
# BERT翻译模型
class BertTranslator(nn.Module):
    def __init__(self, bert_model_name, tgt_vocab_size, d_model=512, nhead=8, num_encoder_layers=6, num_decoder_layers=6, dim_feedforward=2048, dropout=0.1):
        super(BertTranslator, self).__init__()
        
        # BERT编码器
        self.bert = BertModel.from_pretrained(bert_model_name)
        self.bert.resize_token_embeddings(len(self.bert.config.vocab_size))
        
        # 解码器
        self.decoder = nn.TransformerDecoder(
            nn.TransformerDecoderLayer(d_model, nhead, dim_feedforward, dropout),
            num_decoder_layers
        )
        
        # 投影层
        self.linear = nn.Linear(self.bert.config.hidden_size, tgt_vocab_size)
        
        # 位置编码
        self.positional_encoding = self._generate_positional_encoding(self.max_seq_length, d_model)
        
        # 初始化参数
        self._init_weights()
    
    def _generate_positional_encoding(self, max_len, d_model):
        position = torch.arange(max_len).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
        pe = torch.zeros(max_len, 1, d_model)
        pe[:, 0, 0::2] = torch.sin(position * div_term)
        pe[:, 0, 1::2] = torch.cos(position * div_term)
        return pe
    
    def _init_weights(self):
        for p in self.decoder.parameters():
            if p.dim() > 1:
                nn.init.xavier_uniform_(p)
        for p in self.linear.parameters():
            if p.dim() > 1:
                nn.init.xavier_uniform_(p)
    
    def forward(self, src_input_ids, src_attention_mask, tgt_input_ids=None, tgt_attention_mask=None):
        # BERT编码
        bert_outputs = self.bert(input_ids=src_input_ids, attention_mask=src_attention_mask)
        memory = bert_outputs.last_hidden_state  # [batch_size, src_seq_len, hidden_size]
        
        if tgt_input_ids is not None:
            # 训练模式
            # 创建目标位置编码
            tgt_seq_len = tgt_input_ids.size(1)
            tgt_pos = torch.arange(0, tgt_seq_len).unsqueeze(0).expand(tgt_input_ids.size(0), tgt_seq_len).to(src_input_ids.device)
            tgt_pos_emb = self.bert.embeddings(tgt_input_ids)
            
            # 解码
            tgt_mask = self._generate_square_subsequent_mask(tgt_seq_len).to(src_input_ids.device)
            output = self.decoder(
                tgt=tgt_pos_emb,
                memory=memory,
                tgt_mask=tgt_mask,
                memory_mask=None,
                tgt_key_padding_mask=~tgt_attention_mask,
                memory_key_padding_mask=~src_attention_mask
            )
            
            # 投影到词汇表
            output = self.linear(output)
            return output
        else:
            # 推理模式
            return memory
    
    def _generate_square_subsequent_mask(self, sz):
        mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
        mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
        return mask
# 训练函数
def train_model(model, dataloader, optimizer, scheduler, device, epochs=3):
    model.train()
    model.to(device)
    
    for epoch in range(epochs):
        total_loss = 0
        progress_bar = tqdm(dataloader, desc=f'Epoch {epoch+1}/{epochs}')
        
        for batch in progress_bar:
            optimizer.zero_grad()
            
            input_ids = batch['input_ids'].to(device)
            attention_mask = batch['attention_mask'].to(device)
            labels = batch['labels'].to(device)
            decoder_attention_mask = batch['decoder_attention_mask'].to(device)
            
            # 前向传播
            outputs = model(
                src_input_ids=input_ids,
                src_attention_mask=attention_mask,
                tgt_input_ids=labels,
                tgt_attention_mask=decoder_attention_mask
            )
            
            # 计算损失
            loss = F.cross_entropy(
                outputs.view(-1, outputs.size(-1)),
                labels.view(-1),
                ignore_index=model.tgt_tokenizer.pad_token_id
            )
            
            # 反向传播
            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
            
            optimizer.step()
            scheduler.step()
            
            total_loss += loss.item()
            progress_bar.set_postfix({'loss': loss.item()})
        
        avg_loss = total_loss / len(dataloader)
        print(f'Epoch {epoch+1} - Average Loss: {avg_loss:.4f}')
# 评估函数
def evaluate_model(model, dataloader, device):
    model.eval()
    model.to(device)
    
    total_loss = 0
    with torch.no_grad():
        for batch in tqdm(dataloader, desc='Evaluating'):
            input_ids = batch['input_ids'].to(device)
            attention_mask = batch['attention_mask'].to(device)
            labels = batch['labels'].to(device)
            decoder_attention_mask = batch['decoder_attention_mask'].to(device)
            
            outputs = model(
                src_input_ids=input_ids,
                src_attention_mask=attention_mask,
                tgt_input_ids=labels,
                tgt_attention_mask=decoder_attention_mask
            )
            
            loss = F.cross_entropy(
                outputs.view(-1, outputs.size(-1)),
                labels.view(-1),
                ignore_index=model.tgt_tokenizer.pad_token_id
            )
            
            total_loss += loss.item()
    
    avg_loss = total_loss / len(dataloader)
    print(f'Evaluation Loss: {avg_loss:.4f}')
    return avg_loss
# 示例使用
if __name__ == "__main__":
    # 示例数据(实际应用中应使用更大的数据集)
    src_texts = [
        "人工智能正在改变我们的生活方式。",
        "机器学习是人工智能的一个重要分支。",
        "深度学习在图像识别领域取得了巨大成功。"
    ]
    
    tgt_texts = [
        "Artificial intelligence is changing our way of life.",
        "Machine learning is an important branch of artificial intelligence.",
        "Deep learning has achieved great success in the field of image recognition."
    ]
    
    # 初始化分词器
    src_tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
    tgt_tokenizer = MarianTokenizer.from_pretrained('Helsinki-NLP/opus-mt-zh-en')
    
    # 创建数据集和数据加载器
    dataset = TranslationDataset(src_texts, tgt_texts, src_tokenizer, tgt_tokenizer)
    dataloader = DataLoader(dataset, batch_size=2, shuffle=True)
    
    # 创建模型
    model = BertTranslator(
        bert_model_name='bert-base-multilingual-cased',
        tgt_vocab_size=len(tgt_tokenizer),
        d_model=768  # BERT隐藏层大小
    )
    
    # 设置优化器和学习率调度器
    optimizer = AdamW(model.parameters(), lr=5e-5, eps=1e-8)
    total_steps = len(dataloader) * 3  # 3个epoch
    scheduler = get_linear_schedule_with_warmup(
        optimizer,
        num_warmup_steps=int(0.1 * total_steps),
        num_training_steps=total_steps
    )
    
    # 训练模型
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    train_model(model, dataloader, optimizer, scheduler, device, epochs=3)
    
    # 评估模型
    evaluate_model(model, dataloader, device)
    
    # 保存模型
    torch.save(model.state_dict(), 'bert_translator.pth')
    print("模型已保存为 bert_translator.pth")

3.4 使用预训练BERT进行序列到序列翻译

from transformers import BertTokenizer, BertModel, EncoderDecoderModel, AdamW, get_linear_schedule_with_warmup
import torch
from torch.utils.data import Dataset, DataLoader
import torch.nn.functional as F
# 创建自定义数据集
class TranslationDataset(Dataset):
    def __init__(self, src_texts, tgt_texts, src_tokenizer, tgt_tokenizer, max_length=128):
        self.src_texts = src_texts
        self.tgt_texts = tgt_texts
        self.src_tokenizer = src_tokenizer
        self.tgt_tokenizer = tgt_tokenizer
        self.max_length = max_length
    
    def __len__(self):
        return len(self.src_texts)
    
    def __getitem__(self, idx):
        src_text = self.src_texts[idx]
        tgt_text = self.tgt_texts[idx]
        
        # 源语言编码
        src_encoding = self.src_tokenizer(
            src_text,
            max_length=self.max_length,
            padding='max_length',
            truncation=True,
            return_tensors='pt'
        )
        
        # 目标语言编码
        tgt_encoding = self.tgt_tokenizer(
            tgt_text,
            max_length=self.max_length,
            padding='max_length',
            truncation=True,
            return_tensors='pt'
        )
        
        return {
            'input_ids': src_encoding['input_ids'].flatten(),
            'attention_mask': src_encoding['attention_mask'].flatten(),
            'labels': tgt_encoding['input_ids'].flatten(),
            'decoder_attention_mask': tgt_encoding['attention_mask'].flatten()
        }
# 训练函数
def train_bert2bert(model, dataloader, optimizer, scheduler, device, epochs=3):
    model.train()
    model.to(device)
    
    for epoch in range(epochs):
        total_loss = 0
        progress_bar = tqdm(dataloader, desc=f'Epoch {epoch+1}/{epochs}')
        
        for batch in progress_bar:
            optimizer.zero_grad()
            
            input_ids = batch['input_ids'].to(device)
            attention_mask = batch['attention_mask'].to(device)
            labels = batch['labels'].to(device)
            decoder_attention_mask = batch['decoder_attention_mask'].to(device)
            
            # 前向传播
            outputs = model(
                input_ids=input_ids,
                attention_mask=attention_mask,
                labels=labels,
                decoder_attention_mask=decoder_attention_mask
            )
            
            loss = outputs.loss
            
            # 反向传播
            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
            
            optimizer.step()
            scheduler.step()
            
            total_loss += loss.item()
            progress_bar.set_postfix({'loss': loss.item()})
        
        avg_loss = total_loss / len(dataloader)
        print(f'Epoch {epoch+1} - Average Loss: {avg_loss:.4f}')
# 示例使用
if __name__ == "__main__":
    # 示例数据(实际应用中应使用更大的数据集)
    src_texts = [
        "人工智能正在改变我们的生活方式。",
        "机器学习是人工智能的一个重要分支。",
        "深度学习在图像识别领域取得了巨大成功。"
    ]
    
    tgt_texts = [
        "Artificial intelligence is changing our way of life.",
        "Machine learning is an important branch of artificial intelligence.",
        "Deep learning has achieved great success in the field of image recognition."
    ]
    
    # 初始化分词器
    src_tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
    tgt_tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
    
    # 创建数据集和数据加载器
    dataset = TranslationDataset(src_texts, tgt_texts, src_tokenizer, tgt_tokenizer)
    dataloader = DataLoader(dataset, batch_size=2, shuffle=True)
    
    # 创建BERT2BERT模型
    model = EncoderDecoderModel.from_encoder_decoder_pretrained(
        "bert-base-multilingual-cased", 
        "bert-base-multilingual-cased"
    )
    
    # 设置特殊token
    model.config.decoder_start_token_id = tgt_tokenizer.cls_token_id
    model.config.pad_token_id = tgt_tokenizer.pad_token_id
    model.config.vocab_size = model.config.encoder.vocab_size
    
    # 设置生成参数
    model.config.max_length = 128
    model.config.min_length = 56
    model.config.no_repeat_ngram_size = 3
    model.config.early_stopping = True
    model.config.length_penalty = 2.0
    
    # 设置优化器和学习率调度器
    optimizer = AdamW(model.parameters(), lr=5e-5, eps=1e-8)
    total_steps = len(dataloader) * 3  # 3个epoch
    scheduler = get_linear_schedule_with_warmup(
        optimizer,
        num_warmup_steps=int(0.1 * total_steps),
        num_training_steps=total_steps
    )
    
    # 训练模型
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    train_bert2bert(model, dataloader, optimizer, scheduler, device, epochs=3)
    
    # 保存模型
    model.save_pretrained("bert2bert-translator")
    src_tokenizer.save_pretrained("bert2bert-translator")
    tgt_tokenizer.save_pretrained("bert2bert-translator")
    print("模型已保存为 bert2bert-translator")
    
    # 测试翻译
    model.eval()
    input_text = "人工智能正在改变我们的生活方式。"
    input_ids = src_tokenizer(input_text, return_tensors="pt").input_ids.to(device)
    
    # 生成翻译
    outputs = model.generate(input_ids)
    translation = tgt_tokenizer.decode(outputs[0], skip_special_tokens=True)
    
    print(f"输入: {input_text}")
    print(f"翻译: {translation}")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据知道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值