ITPub博客

首页 > 应用开发 > IT综合 > Transformer模型架构解析:自注意力机制在自然语言处理中的创新应用

Transformer模型架构解析:自注意力机制在自然语言处理中的创新应用

翻译 IT综合 作者:bhluo 时间:2026-02-03 15:32:25 0 删除 编辑

# Transformer模型架构解析:自注意力机制在自然语言处理中的创新应用


## 架构设计的核心思想


Transformer模型在2017年由Vaswani等人提出,通过完全基于注意力机制的架构革新了序列建模范式。该架构摒弃了循环神经网络和卷积神经网络的传统设计,采用自注意力机制实现序列元素的全局依赖建模,在处理长距离依赖关系方面展现出显著优势。


## 自注意力机制实现原理


### 缩放点积注意力


自注意力机制的核心是缩放点积注意力,通过查询、键、值三元组实现信息聚合:


```python

import torch

import torch.nn as nn

import torch.nn.functional as F

import math


class ScaledDotProductAttention(nn.Module):

    """缩放点积注意力机制"""

    def __init__(self, dropout_rate=0.1):

        super().__init__()

        self.dropout = nn.Dropout(dropout_rate)

    

    def forward(self, queries, keys, values, mask=None):

        """

        计算缩放点积注意力

        

        参数:

            queries: [batch_size, seq_len_q, d_k]

            keys: [batch_size, seq_len_k, d_k]

            values: [batch_size, seq_len_v, d_v]

            mask: 可选的注意力掩码

            

        返回:

            attention_output: [batch_size, seq_len_q, d_v]

            attention_weights: [batch_size, seq_len_q, seq_len_k]

        """

        batch_size, seq_len_q, d_k = queries.size()

        _, seq_len_k, _ = keys.size()

        

        # 计算点积并缩放

        scores = torch.matmul(queries, keys.transpose(1, 2)) / math.sqrt(d_k)

        

        # 应用掩码(如因果掩码)

        if mask is not None:

            scores = scores.masked_fill(mask == 0, -1e9)

        

        # 计算注意力权重

        attention_weights = F.softmax(scores, dim=-1)

        attention_weights = self.dropout(attention_weights)

        

        # 加权求和

        output = torch.matmul(attention_weights, values)

        

        return output, attention_weights


def causal_attention_mask(seq_len):

    """

    生成因果注意力掩码(用于自回归任务)

    """

    mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1)

    return mask == 0  # 下三角为True,允许访问;上三角为False,禁止访问

```


### 多头注意力机制


多头注意力通过并行多个注意力头捕获不同类型的依赖关系:


```python

class MultiHeadAttention(nn.Module):

    """多头注意力机制"""

    def __init__(self, d_model=512, num_heads=8, dropout_rate=0.1):

        super().__init__()

        assert d_model % num_heads == 0, "d_model必须能被num_heads整除"

        

        self.d_model = d_model

        self.num_heads = num_heads

        self.d_k = d_model // num_heads

        self.d_v = d_model // num_heads

        

        # 线性变换层

        self.w_q = nn.Linear(d_model, d_model)

        self.w_k = nn.Linear(d_model, d_model)

        self.w_v = nn.Linear(d_model, d_model)

        self.w_o = nn.Linear(d_model, d_model)

        

        self.attention = ScaledDotProductAttention(dropout_rate)

        self.dropout = nn.Dropout(dropout_rate)

    

    def split_heads(self, x, batch_size):

        """将输入分割为多个注意力头"""

        # x形状: [batch_size, seq_len, d_model]

        x = x.view(batch_size, -1, self.num_heads, self.d_k)

        # 重排维度: [batch_size, num_heads, seq_len, d_k]

        return x.transpose(1, 2)

    

    def combine_heads(self, x, batch_size):

        """合并多个注意力头的输出"""

        # x形状: [batch_size, num_heads, seq_len, d_k]

        x = x.transpose(1, 2).contiguous()

        # 合并: [batch_size, seq_len, d_model]

        return x.view(batch_size, -1, self.d_model)

    

    def forward(self, q, k, v, mask=None):

        batch_size = q.size(0)

        

        # 线性变换并分割头

        q = self.split_heads(self.w_q(q), batch_size)

        k = self.split_heads(self.w_k(k), batch_size)

        v = self.split_heads(self.w_v(v), batch_size)

        

        # 计算注意力

        attention_output, attention_weights = self.attention(q, k, v, mask)

        

        # 合并头并输出变换

        combined = self.combine_heads(attention_output, batch_size)

        output = self.w_o(combined)

        output = self.dropout(output)

        

        return output, attention_weights

```


## Transformer编码器实现


### 前馈网络层


```python

class PositionwiseFeedForward(nn.Module):

    """位置感知前馈网络"""

    def __init__(self, d_model=512, d_ff=2048, dropout_rate=0.1):

        super().__init__()

        self.linear1 = nn.Linear(d_model, d_ff)

        self.linear2 = nn.Linear(d_ff, d_model)

        self.dropout = nn.Dropout(dropout_rate)

        self.relu = nn.ReLU()

    

    def forward(self, x):

        # 两层线性变换,中间有ReLU激活

        return self.linear2(self.dropout(self.relu(self.linear1(x))))


class EncoderLayer(nn.Module):

    """Transformer编码器层"""

    def __init__(self, d_model=512, num_heads=8, d_ff=2048, dropout_rate=0.1):

        super().__init__()

        

        self.self_attention = MultiHeadAttention(d_model, num_heads, dropout_rate)

        self.feed_forward = PositionwiseFeedForward(d_model, d_ff, dropout_rate)

        

        # 层归一化

        self.norm1 = nn.LayerNorm(d_model)

        self.norm2 = nn.LayerNorm(d_model)

        

        # Dropout层

        self.dropout1 = nn.Dropout(dropout_rate)

        self.dropout2 = nn.Dropout(dropout_rate)

    

    def forward(self, x, mask=None):

        # 自注意力子层(含残差连接)

        attention_output, _ = self.self_attention(x, x, x, mask)

        x = x + self.dropout1(attention_output)

        x = self.norm1(x)

        

        # 前馈网络子层(含残差连接)

        ff_output = self.feed_forward(x)

        x = x + self.dropout2(ff_output)

        x = self.norm2(x)

        

        return x

```


### 位置编码机制


```python

class PositionalEncoding(nn.Module):

    """正弦位置编码"""

    def __init__(self, d_model, max_len=5000):

        super().__init__()

        

        # 创建位置编码矩阵

        pe = torch.zeros(max_len, d_model)

        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)

        

        # 计算位置编码

        div_term = torch.exp(torch.arange(0, d_model, 2).float() * 

                           (-math.log(10000.0) / d_model))

        

        pe[:, 0::2] = torch.sin(position * div_term)

        pe[:, 1::2] = torch.cos(position * div_term)

        

        # 注册为缓冲区(不参与训练)

        self.register_buffer('pe', pe.unsqueeze(0))

    

    def forward(self, x):

        # x形状: [batch_size, seq_len, d_model]

        seq_len = x.size(1)

        return x + self.pe[:, :seq_len, :]


class LearnedPositionalEncoding(nn.Module):

    """可学习的位置编码"""

    def __init__(self, d_model, max_len=512):

        super().__init__()

        self.position_embedding = nn.Embedding(max_len, d_model)

        

    def forward(self, x):

        batch_size, seq_len = x.size(0), x.size(1)

        positions = torch.arange(seq_len, device=x.device).unsqueeze(0).expand(batch_size, seq_len)

        position_embeddings = self.position_embedding(positions)

        return x + position_embeddings

```


### 完整编码器实现


```python

class TransformerEncoder(nn.Module):

    """Transformer编码器"""

    def __init__(self, num_layers=6, d_model=512, num_heads=8, 

                 d_ff=2048, vocab_size=30000, max_len=512, 

                 dropout_rate=0.1, use_learned_pos=True):

        super().__init__()

        

        self.d_model = d_model

        

        # 词嵌入层

        self.token_embedding = nn.Embedding(vocab_size, d_model)

        

        # 位置编码

        if use_learned_pos:

            self.position_encoding = LearnedPositionalEncoding(d_model, max_len)

        else:

            self.position_encoding = PositionalEncoding(d_model, max_len)

        

        # 编码器层堆叠

        self.layers = nn.ModuleList([

            EncoderLayer(d_model, num_heads, d_ff, dropout_rate)

            for _ in range(num_layers)

        ])

        

        # 输出层归一化

        self.norm = nn.LayerNorm(d_model)

        self.dropout = nn.Dropout(dropout_rate)

    

    def forward(self, token_ids, mask=None):

        # 词嵌入

        x = self.token_embedding(token_ids) * math.sqrt(self.d_model)

        

        # 位置编码

        x = self.position_encoding(x)

        x = self.dropout(x)

        

        # 通过编码器层

        for layer in self.layers:

            x = layer(x, mask)

        

        # 最终归一化

        return self.norm(x)

```


## Transformer解码器实现


### 解码器层设计


```python

class DecoderLayer(nn.Module):

    """Transformer解码器层"""

    def __init__(self, d_model=512, num_heads=8, d_ff=2048, dropout_rate=0.1):

        super().__init__()

        

        # 掩码自注意力

        self.self_attention = MultiHeadAttention(d_model, num_heads, dropout_rate)

        

        # 编码器-解码器注意力

        self.cross_attention = MultiHeadAttention(d_model, num_heads, dropout_rate)

        

        # 前馈网络

        self.feed_forward = PositionwiseFeedForward(d_model, d_ff, dropout_rate)

        

        # 层归一化

        self.norm1 = nn.LayerNorm(d_model)

        self.norm2 = nn.LayerNorm(d_model)

        self.norm3 = nn.LayerNorm(d_model)

        

        # Dropout层

        self.dropout1 = nn.Dropout(dropout_rate)

        self.dropout2 = nn.Dropout(dropout_rate)

        self.dropout3 = nn.Dropout(dropout_rate)

    

    def forward(self, x, encoder_output, self_mask=None, cross_mask=None):

        # 掩码自注意力子层

        attention_output, _ = self.self_attention(x, x, x, self_mask)

        x = x + self.dropout1(attention_output)

        x = self.norm1(x)

        

        # 编码器-解码器注意力子层

        cross_output, _ = self.cross_attention(x, encoder_output, encoder_output, cross_mask)

        x = x + self.dropout2(cross_output)

        x = self.norm2(x)

        

        # 前馈网络子层

        ff_output = self.feed_forward(x)

        x = x + self.dropout3(ff_output)

        x = self.norm3(x)

        

        return x

```


### 完整解码器实现


```python

class TransformerDecoder(nn.Module):

    """Transformer解码器"""

    def __init__(self, num_layers=6, d_model=512, num_heads=8,

                 d_ff=2048, vocab_size=30000, max_len=512,

                 dropout_rate=0.1, use_learned_pos=True):

        super().__init__()

        

        self.d_model = d_model

        

        # 词嵌入层

        self.token_embedding = nn.Embedding(vocab_size, d_model)

        

        # 位置编码

        if use_learned_pos:

            self.position_encoding = LearnedPositionalEncoding(d_model, max_len)

        else:

            self.position_encoding = PositionalEncoding(d_model, max_len)

        

        # 解码器层堆叠

        self.layers = nn.ModuleList([

            DecoderLayer(d_model, num_heads, d_ff, dropout_rate)

            for _ in range(num_layers)

        ])

        

        # 输出层

        self.norm = nn.LayerNorm(d_model)

        self.output_projection = nn.Linear(d_model, vocab_size)

        self.dropout = nn.Dropout(dropout_rate)

    

    def forward(self, target_ids, encoder_output, 

                target_mask=None, memory_mask=None):

        # 目标序列嵌入

        x = self.token_embedding(target_ids) * math.sqrt(self.d_model)

        

        # 位置编码

        x = self.position_encoding(x)

        x = self.dropout(x)

        <"cec.a8k1.org.cn"><"vds.a8k1.org.cn">

        # 通过解码器层

        for layer in self.layers:

            x = layer(x, encoder_output, target_mask, memory_mask)

        

        # 最终归一化和线性投影

        x = self.norm(x)

        logits = self.output_projection(x)

        

        return logits

```


## 完整Transformer模型


```python

class Transformer(nn.Module):

    """完整的Transformer模型(编码器-解码器架构)"""

    def __init__(self, src_vocab_size, tgt_vocab_size,

                 num_layers=6, d_model=512, num_heads=8,

                 d_ff=2048, max_len=512, dropout_rate=0.1):

        super().__init__()

        

        # 编码器

        self.encoder = TransformerEncoder(

            num_layers=num_layers,

            d_model=d_model,

            num_heads=num_heads,

            d_ff=d_ff,

            vocab_size=src_vocab_size,

            max_len=max_len,

            dropout_rate=dropout_rate

        )

        

        # 解码器

        self.decoder = TransformerDecoder(

            num_layers=num_layers,

            d_model=d_model,

            num_heads=num_heads,

            d_ff=d_ff,

            vocab_size=tgt_vocab_size,

            max_len=max_len,

            dropout_rate=dropout_rate

        )

    

    def encode(self, src_ids, src_mask=None):

        """编码源序列"""

        return self.encoder(src_ids, src_mask)

    

    def decode(self, tgt_ids, encoder_output, 

               tgt_mask=None, memory_mask=None):

        """解码目标序列"""

        return self.decoder(tgt_ids, encoder_output, tgt_mask, memory_mask)

    

    def forward(self, src_ids, tgt_ids, 

                src_mask=None, tgt_mask=None, memory_mask=None):

        """前向传播"""

        # 编码

        encoder_output = self.encode(src_ids, src_mask)

        <"kfc.a8k1.org.cn"><"doro.a8k1.org.cn"><"cctc.a8k1.org.cn">

        # 解码

        decoder_output = self.decode(tgt_ids, encoder_output, 

                                   tgt_mask, memory_mask)

        

        return decoder_output

```


## 训练与推理框架


### 训练循环实现


```python

class TransformerTrainer:

    """Transformer训练器"""

    def __init__(self, model, optimizer, criterion, device):

        self.model = model

        self.optimizer = optimizer

        self.criterion = criterion

        self.device = device

        self.model.to(device)

    

    def train_step(self, src_batch, tgt_batch, src_mask, tgt_mask):

        """单个训练步骤"""

        self.model.train()

        

        # 准备数据

        src_batch = src_batch.to(self.device)

        tgt_batch = tgt_batch.to(self.device)

        

        # 前向传播(使用教师强制)

        decoder_input = tgt_batch[:, :-1]

        decoder_target = tgt_batch[:, 1:]

        

        logits = self.model(src_batch, decoder_input, src_mask, tgt_mask)

        

        # 计算损失

        loss = self.criterion(

            logits.view(-1, logits.size(-1)),

            decoder_target.contiguous().view(-1)

        )

        

        # 反向传播

        self.optimizer.zero_grad()

        loss.backward()

        

        # 梯度裁剪

        torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=1.0)

        

        # 参数更新

        self.optimizer.step()

        

        return loss.item()

    

    def evaluate(self, dataloader):

        """模型评估"""

        self.model.eval()

        total_loss = 0

        total_tokens = 0

        

        with torch.no_grad():

            for src_batch, tgt_batch, src_mask, tgt_mask in dataloader:

                src_batch = src_batch.to(self.device)

                tgt_batch = tgt_batch.to(self.device)

                

                decoder_input = tgt_batch[:, :-1]

                decoder_target = tgt_batch[:, 1:]

                

                logits = self.model(src_batch, decoder_input, src_mask, tgt_mask)

                

                loss = self.criterion(

                    logits.view(-1, logits.size(-1)),

                    decoder_target.contiguous().view(-1)

                )

                

                batch_tokens = decoder_target.ne(self.criterion.ignore_index).sum().item()

                total_loss += loss.item() * batch_tokens

                total_tokens += batch_tokens

        

        return total_loss / total_tokens if total_tokens > 0 else float('inf')

```


### 推理生成策略


```python

class GreedyDecoder:

    """贪心解码器"""

    def __init__(self, model, max_len=100, start_token=1, end_token=2):

        self.model = model

        self.max_len = max_len

        self.start_token = start_token

        self.end_token = end_token

    

    def decode(self, src_sequence, src_mask=None):

        """贪心搜索解码"""

        batch_size = src_sequence.size(0)

        

        # 编码输入序列

        encoder_output = self.model.encode(src_sequence, src_mask)

        

        # 初始化目标序列(以起始符开始)

        decoder_input = torch.full((batch_size, 1), 

                                 self.start_token,

                                 dtype=torch.long,

                                 device=src_sequence.device)

        

        # 逐个生成标记

        for step in range(self.max_len):

            # 生成注意力掩码(防止看到未来信息)

            target_mask = causal_attention_mask(decoder_input.size(1))

            target_mask = target_mask.to(src_sequence.device)

            

            # 解码

            logits = self.model.decode(decoder_input, encoder_output, target_mask)

            next_token = logits[:, -1, :].argmax(dim=-1, keepdim=True)

            

            # 添加到序列

            decoder_input = torch.cat([decoder_input, next_token], dim=1)

            

            # 检查是否所有序列都已生成结束符

            if (next_token == self.end_token).all():

                break

        

        return decoder_input



class BeamSearchDecoder:

    """束搜索解码器"""

    def __init__(self, model, beam_size=5, max_len=100, 

                 start_token=1, end_token=2, length_penalty=0.6):

        self.model = model

        self.beam_size = beam_size

        self.max_len = max_len

        self.start_token = start_token

        self.end_token = end_token

        self.length_penalty = length_penalty

    

    def decode(self, src_sequence, src_mask=None):

        """束搜索解码"""

        batch_size = src_sequence.size(0)

        device = src_sequence.device

        

        # 编码输入序列

        encoder_output = self.model.encode(src_sequence, src_mask)

        

        # 扩展编码器输出用于束搜索

        encoder_output = encoder_output.repeat_interleave(self.beam_size, dim=0)

        

        # 初始化束

        beams = torch.full((batch_size * self.beam_size, 1),

                          self.start_token,

                          dtype=torch.long,

                          device=device)

        

        beam_scores = torch.zeros(batch_size, self.beam_size, device=device)

        beam_scores[:, 1:] = -float('inf')

        beam_scores = beam_scores.view(-1)

        

        finished_beams = [[] for _ in range(batch_size)]

        

        for step in range(self.max_len):

            # 生成注意力掩码

            target_mask = causal_attention_mask(beams.size(1))

            target_mask = target_mask.to(device)

            

            # 解码

            logits = self.model.decode(beams, encoder_output, target_mask)

            next_token_logits = logits[:, -1, :]

            

            # 计算分数

            vocab_size = next_token_logits.size(-1)

            scores = F.log_softmax(next_token_logits, dim=-1)

            scores = scores + beam_scores.unsqueeze(1)

            

            # 重新组织分数以便选择top-k

            if step == 0:

                scores = scores.view(batch_size, vocab_size)

            else:

                scores = scores.view(batch_size, self.beam_size * vocab_size)

            

            # 选择top-k

            topk_scores, topk_indices = scores.topk(self.beam_size, dim=1)

            

            # 计算束索引和词索引

            beam_indices = topk_indices // vocab_size

            token_indices = topk_indices % vocab_size

            

            # 更新束

            new_beams = []

            for i in range(batch_size):

                batch_beams = []

                for j in range(self.beam_size):

                    beam_idx = beam_indices[i, j].item()

                    token_idx = token_indices[i, j].item()

                    

                    # 获取之前的序列

                    if step == 0:

                        prev_beam = beams[i]

                    else:

                        prev_beam = beams[i * self.beam_size + beam_idx]

                    

                    # 添加新标记

                    new_beam = torch.cat([prev_beam, torch.tensor([token_idx], device=device)])

                    

                    # 检查是否结束

                    if token_idx == self.end_token:

                        # 计算最终分数(带长度惩罚)

                        length = len(new_beam)

                        score = topk_scores[i, j].item() / (length ** self.length_penalty)

                        finished_beams[i].append((score, new_beam))

                    else:

                        batch_beams.append(new_beam)

                

                new_beams.extend(batch_beams)

            

            if not new_beams:

                break

            

            beams = torch.stack(new_beams)

            beam_scores = topk_scores.view(-1)

        

        # 选择每个批次的最佳序列

        outputs = []

        for i in range(batch_size):

            if finished_beams[i]:

                best_beam = max(finished_beams[i], key=lambda x: x[0])[1]

            else:

                best_beam = beams[i * self.beam_size]

            outputs.append(best_beam)

        

        return torch.stack(outputs)

```


## 模型优化技术


```python

class TransformerOptimizer:

    """Transformer专用优化器(含学习率预热)"""

    def __init__(self, optimizer, d_model, warmup_steps=4000):

        self.optimizer = optimizer

        self.d_model = d_model

        self.warmup_steps = warmup_steps

        self.current_step = 0

    

    def step(self):

        """执行优化步骤"""

        self.current_step += 1

        lr = self.get_learning_rate()

        

        for param_group in self.optimizer.param_groups:

            param_group['lr'] = lr

        

        self.optimizer.step()

    

    def get_learning_rate(self):

        """计算当前学习率"""

        step = self.current_step

        warmup = self.warmup_steps

        

        # 学习率预热公式

        return self.d_model ** (-0.5) * min(step ** (-0.5), step * warmup ** (-1.5))

    

    def zero_grad(self):

        """清空梯度"""

        self.optimizer.zero_grad()

```


## 结语


Transformer架构通过自注意力机制彻底改变了序列建模的方法论,其并行计算特性和长距离依赖建模能力为自然语言处理带来了革命性进步。从机器翻译到大规模预训练模型,Transformer已经成为现代自然语言处理的基石架构。


理解Transformer的架构细节和实现原理,对于深入掌握当前自然语言处理技术发展趋势具有重要意义。随着研究的深入,Transformer架构的变体和改进不断涌现,但核心的自注意力机制和编码器-解码器框架仍然是这一领域的重要基础。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3109832/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    934074