model
#4
by zhao8445 - opened
- .gitignore +9 -0
- src/easytranslate/model/attention.py +129 -4
- src/easytranslate/model/decoder.py +111 -21
- src/easytranslate/model/encoder.py +70 -27
- src/easytranslate/model/finetune.py +43 -22
- src/easytranslate/model/positional.py +42 -5
- src/easytranslate/model/transformer.py +142 -43
.gitignore
CHANGED
|
@@ -31,6 +31,15 @@ outputs/
|
|
| 31 |
logs/
|
| 32 |
wandb/
|
| 33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
# OS
|
| 35 |
.DS_Store
|
| 36 |
Thumbs.db
|
|
|
|
| 31 |
logs/
|
| 32 |
wandb/
|
| 33 |
|
| 34 |
+
# Test cache
|
| 35 |
+
.pytest_cache/
|
| 36 |
+
|
| 37 |
+
# Jupyter
|
| 38 |
+
.ipynb_checkpoints/
|
| 39 |
+
|
| 40 |
+
# Log files
|
| 41 |
+
*.log
|
| 42 |
+
|
| 43 |
# OS
|
| 44 |
.DS_Store
|
| 45 |
Thumbs.db
|
src/easytranslate/model/attention.py
CHANGED
|
@@ -57,8 +57,15 @@ class MultiHeadAttention(nn.Module):
|
|
| 57 |
self.d_model = d_model
|
| 58 |
self.nhead = nhead
|
| 59 |
self.d_k = d_model // nhead
|
|
|
|
| 60 |
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
def forward(
|
| 64 |
self,
|
|
@@ -67,8 +74,61 @@ class MultiHeadAttention(nn.Module):
|
|
| 67 |
value: torch.Tensor, # [B, L_v, D]
|
| 68 |
key_padding_mask: Optional[torch.BoolTensor] = None, # [B, L_k]
|
| 69 |
attn_mask: Optional[torch.Tensor] = None, # [L_q, L_k]
|
|
|
|
| 70 |
) -> torch.Tensor:
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
|
| 74 |
class FlashMultiHeadAttention(nn.Module):
|
|
@@ -93,7 +153,19 @@ class FlashMultiHeadAttention(nn.Module):
|
|
| 93 |
use_rotary_embedding: bool = False,
|
| 94 |
):
|
| 95 |
super().__init__()
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
def forward(
|
| 99 |
self,
|
|
@@ -103,4 +175,57 @@ class FlashMultiHeadAttention(nn.Module):
|
|
| 103 |
key_padding_mask: Optional[torch.BoolTensor] = None,
|
| 104 |
is_causal: bool = False,
|
| 105 |
) -> torch.Tensor:
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
self.d_model = d_model
|
| 58 |
self.nhead = nhead
|
| 59 |
self.d_k = d_model // nhead
|
| 60 |
+
self.use_rotary_embedding = use_rotary_embedding
|
| 61 |
|
| 62 |
+
self.q_proj = nn.Linear(d_model, d_model)
|
| 63 |
+
self.k_proj = nn.Linear(d_model, d_model)
|
| 64 |
+
self.v_proj = nn.Linear(d_model, d_model)
|
| 65 |
+
self.out_proj = nn.Linear(d_model, d_model)
|
| 66 |
+
|
| 67 |
+
self.dropout = nn.Dropout(p=dropout)
|
| 68 |
+
self.rope: Optional[nn.Module] = None
|
| 69 |
|
| 70 |
def forward(
|
| 71 |
self,
|
|
|
|
| 74 |
value: torch.Tensor, # [B, L_v, D]
|
| 75 |
key_padding_mask: Optional[torch.BoolTensor] = None, # [B, L_k]
|
| 76 |
attn_mask: Optional[torch.Tensor] = None, # [L_q, L_k]
|
| 77 |
+
is_causal: bool = False,
|
| 78 |
) -> torch.Tensor:
|
| 79 |
+
B, L_q, _ = query.size()
|
| 80 |
+
L_k = key.size(1)
|
| 81 |
+
L_v = value.size(1)
|
| 82 |
+
|
| 83 |
+
# 1. 线性投影
|
| 84 |
+
Q = self.q_proj(query) # [B, L_q, D]
|
| 85 |
+
K = self.k_proj(key) # [B, L_k, D]
|
| 86 |
+
V = self.v_proj(value) # [B, L_v, D]
|
| 87 |
+
|
| 88 |
+
# 2. reshape 为 [B, nhead, L, d_k]
|
| 89 |
+
Q = Q.view(B, L_q, self.nhead, self.d_k).transpose(1, 2) # [B, H, L_q, d_k]
|
| 90 |
+
K = K.view(B, L_k, self.nhead, self.d_k).transpose(1, 2) # [B, H, L_k, d_k]
|
| 91 |
+
V = V.view(B, L_v, self.nhead, self.d_k).transpose(1, 2) # [B, H, L_v, d_k]
|
| 92 |
+
|
| 93 |
+
# 3. (可选) 应用 RoPE
|
| 94 |
+
if self.use_rotary_embedding and self.rope is not None:
|
| 95 |
+
Q, K = self.rope.apply_rotary_pos_emb(Q, K)
|
| 96 |
+
|
| 97 |
+
# 4. 计算 attention scores: QK^T / sqrt(d_k)
|
| 98 |
+
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k) # [B, H, L_q, L_k]
|
| 99 |
+
|
| 100 |
+
# 5. 应用 masks
|
| 101 |
+
if key_padding_mask is not None:
|
| 102 |
+
# key_padding_mask: [B, L_k] -> [B, 1, 1, L_k]
|
| 103 |
+
scores = scores.masked_fill(
|
| 104 |
+
key_padding_mask.unsqueeze(1).unsqueeze(2), float("-inf")
|
| 105 |
+
)
|
| 106 |
+
if is_causal:
|
| 107 |
+
# 生成 causal mask
|
| 108 |
+
L_q, L_k_local = scores.size(-2), scores.size(-1)
|
| 109 |
+
causal_mask = torch.triu(
|
| 110 |
+
torch.ones(L_q, L_k_local, device=scores.device), diagonal=1
|
| 111 |
+
).bool()
|
| 112 |
+
scores = scores.masked_fill(
|
| 113 |
+
causal_mask.unsqueeze(0).unsqueeze(0), float("-inf")
|
| 114 |
+
)
|
| 115 |
+
if attn_mask is not None:
|
| 116 |
+
# attn_mask: [L_q, L_k] -> [1, 1, L_q, L_k]
|
| 117 |
+
scores = scores.masked_fill(attn_mask.unsqueeze(0).unsqueeze(0), float("-inf"))
|
| 118 |
+
|
| 119 |
+
# 6. Softmax + Dropout
|
| 120 |
+
attn_weights = F.softmax(scores, dim=-1)
|
| 121 |
+
attn_weights = self.dropout(attn_weights)
|
| 122 |
+
|
| 123 |
+
# 7. 加权求和 V
|
| 124 |
+
attn_output = torch.matmul(attn_weights, V) # [B, H, L_q, d_k]
|
| 125 |
+
|
| 126 |
+
# 8. reshape 回 [B, L_q, d_model]
|
| 127 |
+
attn_output = attn_output.transpose(1, 2).contiguous().view(B, L_q, self.d_model)
|
| 128 |
+
|
| 129 |
+
# 9. 输出投影
|
| 130 |
+
output = self.out_proj(attn_output)
|
| 131 |
+
return output
|
| 132 |
|
| 133 |
|
| 134 |
class FlashMultiHeadAttention(nn.Module):
|
|
|
|
| 153 |
use_rotary_embedding: bool = False,
|
| 154 |
):
|
| 155 |
super().__init__()
|
| 156 |
+
assert d_model % nhead == 0, "d_model 必须能被 nhead 整除"
|
| 157 |
+
self.d_model = d_model
|
| 158 |
+
self.nhead = nhead
|
| 159 |
+
self.d_k = d_model // nhead
|
| 160 |
+
self.use_rotary_embedding = use_rotary_embedding
|
| 161 |
+
self.dropout_p = dropout
|
| 162 |
+
|
| 163 |
+
self.q_proj = nn.Linear(d_model, d_model)
|
| 164 |
+
self.k_proj = nn.Linear(d_model, d_model)
|
| 165 |
+
self.v_proj = nn.Linear(d_model, d_model)
|
| 166 |
+
self.out_proj = nn.Linear(d_model, d_model)
|
| 167 |
+
|
| 168 |
+
self.rope: Optional[nn.Module] = None
|
| 169 |
|
| 170 |
def forward(
|
| 171 |
self,
|
|
|
|
| 175 |
key_padding_mask: Optional[torch.BoolTensor] = None,
|
| 176 |
is_causal: bool = False,
|
| 177 |
) -> torch.Tensor:
|
| 178 |
+
B, L_q, _ = query.size()
|
| 179 |
+
L_k = key.size(1)
|
| 180 |
+
L_v = value.size(1)
|
| 181 |
+
|
| 182 |
+
# 1. 线性投影
|
| 183 |
+
Q = self.q_proj(query) # [B, L_q, D]
|
| 184 |
+
K = self.k_proj(key) # [B, L_k, D]
|
| 185 |
+
V = self.v_proj(value) # [B, L_v, D]
|
| 186 |
+
|
| 187 |
+
# 2. reshape 为 [B, nhead, L, d_k]
|
| 188 |
+
Q = Q.view(B, L_q, self.nhead, self.d_k).transpose(1, 2) # [B, H, L_q, d_k]
|
| 189 |
+
K = K.view(B, L_k, self.nhead, self.d_k).transpose(1, 2) # [B, H, L_k, d_k]
|
| 190 |
+
V = V.view(B, L_v, self.nhead, self.d_k).transpose(1, 2) # [B, H, L_v, d_k]
|
| 191 |
+
|
| 192 |
+
# 3. (可选) 应用 RoPE
|
| 193 |
+
if self.use_rotary_embedding and self.rope is not None:
|
| 194 |
+
Q, K = self.rope.apply_rotary_pos_emb(Q, K)
|
| 195 |
+
|
| 196 |
+
# 4. 构建 attn_mask 以适配 scaled_dot_product_attention
|
| 197 |
+
# PyTorch >= 2.0 支持 [B, nhead, L, d_k] 的 4D 输入
|
| 198 |
+
# 注意: scaled_dot_product_attention 不允许同时设置 attn_mask 和 is_causal=True
|
| 199 |
+
attn_mask: Optional[torch.Tensor] = None
|
| 200 |
+
if is_causal or key_padding_mask is not None:
|
| 201 |
+
attn_mask = torch.zeros(
|
| 202 |
+
B, self.nhead, L_q, L_k, dtype=Q.dtype, device=Q.device
|
| 203 |
+
)
|
| 204 |
+
if is_causal:
|
| 205 |
+
# 生成 causal mask (上三角为 -inf)
|
| 206 |
+
causal_mask = torch.triu(
|
| 207 |
+
torch.ones(L_q, L_k, device=Q.device), diagonal=1
|
| 208 |
+
).bool()
|
| 209 |
+
attn_mask = attn_mask.masked_fill(
|
| 210 |
+
causal_mask[None, None, :, :], float("-inf")
|
| 211 |
+
)
|
| 212 |
+
if key_padding_mask is not None:
|
| 213 |
+
# key_padding_mask: True = padding (忽略)
|
| 214 |
+
_bool_mask = key_padding_mask.unsqueeze(1).unsqueeze(2)
|
| 215 |
+
_bool_mask = _bool_mask.expand(B, self.nhead, L_q, L_k)
|
| 216 |
+
attn_mask = attn_mask.masked_fill(_bool_mask, float("-inf"))
|
| 217 |
+
|
| 218 |
+
# 5. Flash Attention (PyTorch 原生)
|
| 219 |
+
attn_output = F.scaled_dot_product_attention(
|
| 220 |
+
Q, K, V,
|
| 221 |
+
attn_mask=attn_mask,
|
| 222 |
+
dropout_p=self.dropout_p if self.training else 0.0,
|
| 223 |
+
is_causal=False, # 已通过 attn_mask 处理
|
| 224 |
+
) # [B, H, L_q, d_k]
|
| 225 |
+
|
| 226 |
+
# 6. reshape 回 [B, L_q, d_model]
|
| 227 |
+
attn_output = attn_output.transpose(1, 2).contiguous().view(B, L_q, self.d_model)
|
| 228 |
+
|
| 229 |
+
# 7. 输出投影
|
| 230 |
+
output = self.out_proj(attn_output)
|
| 231 |
+
return output
|
src/easytranslate/model/decoder.py
CHANGED
|
@@ -13,21 +13,23 @@ Transformer Decoder 模块 — Person B 负责实现
|
|
| 13 |
|
| 14 |
from __future__ import annotations
|
| 15 |
|
|
|
|
|
|
|
| 16 |
import torch
|
| 17 |
import torch.nn as nn
|
| 18 |
from typing import Optional
|
| 19 |
|
|
|
|
|
|
|
| 20 |
|
| 21 |
class TransformerDecoderLayer(nn.Module):
|
| 22 |
"""
|
| 23 |
单层 Transformer Decoder。
|
| 24 |
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
4. 三个 LayerNorm
|
| 30 |
-
5. Residual connections + Dropout
|
| 31 |
"""
|
| 32 |
|
| 33 |
def __init__(
|
|
@@ -42,7 +44,42 @@ class TransformerDecoderLayer(nn.Module):
|
|
| 42 |
pre_norm: bool = True,
|
| 43 |
):
|
| 44 |
super().__init__()
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
def forward(
|
| 48 |
self,
|
|
@@ -52,28 +89,71 @@ class TransformerDecoderLayer(nn.Module):
|
|
| 52 |
memory_key_padding_mask: Optional[torch.BoolTensor] = None, # [B, S]
|
| 53 |
tgt_key_padding_mask: Optional[torch.BoolTensor] = None, # [B, T]
|
| 54 |
) -> torch.Tensor:
|
| 55 |
-
"""
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
|
| 65 |
class TransformerDecoder(nn.Module):
|
| 66 |
"""
|
| 67 |
多层 Transformer Decoder。
|
| 68 |
-
|
| 69 |
-
TODO [Person B]:
|
| 70 |
-
1. 堆叠 N 个 TransformerDecoderLayer
|
| 71 |
-
2. 最终加一个 LayerNorm
|
| 72 |
"""
|
| 73 |
|
| 74 |
def __init__(self, decoder_layer: TransformerDecoderLayer, num_layers: int):
|
| 75 |
super().__init__()
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
def forward(
|
| 79 |
self,
|
|
@@ -83,4 +163,14 @@ class TransformerDecoder(nn.Module):
|
|
| 83 |
memory_key_padding_mask: Optional[torch.BoolTensor] = None,
|
| 84 |
tgt_key_padding_mask: Optional[torch.BoolTensor] = None,
|
| 85 |
) -> torch.Tensor:
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
from __future__ import annotations
|
| 15 |
|
| 16 |
+
import copy
|
| 17 |
+
|
| 18 |
import torch
|
| 19 |
import torch.nn as nn
|
| 20 |
from typing import Optional
|
| 21 |
|
| 22 |
+
from easytranslate.model.attention import MultiHeadAttention, FlashMultiHeadAttention
|
| 23 |
+
|
| 24 |
|
| 25 |
class TransformerDecoderLayer(nn.Module):
|
| 26 |
"""
|
| 27 |
单层 Transformer Decoder。
|
| 28 |
|
| 29 |
+
架构 (Pre-LayerNorm):
|
| 30 |
+
x → LN → Masked Self-Attention → Residual
|
| 31 |
+
→ LN → Cross-Attention → Residual
|
| 32 |
+
→ LN → FFN → Residual
|
|
|
|
|
|
|
| 33 |
"""
|
| 34 |
|
| 35 |
def __init__(
|
|
|
|
| 44 |
pre_norm: bool = True,
|
| 45 |
):
|
| 46 |
super().__init__()
|
| 47 |
+
self.pre_norm = pre_norm
|
| 48 |
+
self.d_model = d_model
|
| 49 |
+
|
| 50 |
+
attn_cls = FlashMultiHeadAttention if use_flash_attention else MultiHeadAttention
|
| 51 |
+
|
| 52 |
+
# 1. Masked Self-Attention
|
| 53 |
+
self.self_attn = attn_cls(
|
| 54 |
+
d_model=d_model,
|
| 55 |
+
nhead=nhead,
|
| 56 |
+
dropout=dropout,
|
| 57 |
+
use_rotary_embedding=use_rotary_embedding,
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
# 2. Cross-Attention (decoder queries encoder memory)
|
| 61 |
+
self.multihead_attn = attn_cls(
|
| 62 |
+
d_model=d_model,
|
| 63 |
+
nhead=nhead,
|
| 64 |
+
dropout=dropout,
|
| 65 |
+
use_rotary_embedding=False, # cross-attention 不使用 RoPE
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
# 3. Feed-Forward Network
|
| 69 |
+
self.linear1 = nn.Linear(d_model, dim_feedforward)
|
| 70 |
+
self.activation = nn.GELU() if activation == "gelu" else nn.ReLU()
|
| 71 |
+
self.dropout = nn.Dropout(p=dropout)
|
| 72 |
+
self.linear2 = nn.Linear(dim_feedforward, d_model)
|
| 73 |
+
|
| 74 |
+
# 4. LayerNorms
|
| 75 |
+
self.norm1 = nn.LayerNorm(d_model)
|
| 76 |
+
self.norm2 = nn.LayerNorm(d_model)
|
| 77 |
+
self.norm3 = nn.LayerNorm(d_model)
|
| 78 |
+
|
| 79 |
+
# 5. Dropouts for residuals
|
| 80 |
+
self.dropout1 = nn.Dropout(p=dropout)
|
| 81 |
+
self.dropout2 = nn.Dropout(p=dropout)
|
| 82 |
+
self.dropout3 = nn.Dropout(p=dropout)
|
| 83 |
|
| 84 |
def forward(
|
| 85 |
self,
|
|
|
|
| 89 |
memory_key_padding_mask: Optional[torch.BoolTensor] = None, # [B, S]
|
| 90 |
tgt_key_padding_mask: Optional[torch.BoolTensor] = None, # [B, T]
|
| 91 |
) -> torch.Tensor:
|
| 92 |
+
"""Pre-LayerNorm 前向传播。"""
|
| 93 |
+
# 适配 Flash Attention: 使用 is_causal 替代显式 causal mask
|
| 94 |
+
is_causal = tgt_mask is not None
|
| 95 |
+
|
| 96 |
+
if self.pre_norm:
|
| 97 |
+
# 1. Masked Self-Attention
|
| 98 |
+
residual = tgt
|
| 99 |
+
tgt = self.norm1(tgt)
|
| 100 |
+
tgt = self.self_attn(
|
| 101 |
+
tgt, tgt, tgt,
|
| 102 |
+
key_padding_mask=tgt_key_padding_mask,
|
| 103 |
+
is_causal=is_causal,
|
| 104 |
+
)
|
| 105 |
+
tgt = residual + self.dropout1(tgt)
|
| 106 |
+
|
| 107 |
+
# 2. Cross-Attention
|
| 108 |
+
residual = tgt
|
| 109 |
+
tgt = self.norm2(tgt)
|
| 110 |
+
tgt = self.multihead_attn(
|
| 111 |
+
tgt, memory, memory,
|
| 112 |
+
key_padding_mask=memory_key_padding_mask,
|
| 113 |
+
)
|
| 114 |
+
tgt = residual + self.dropout2(tgt)
|
| 115 |
+
|
| 116 |
+
# 3. FFN
|
| 117 |
+
residual = tgt
|
| 118 |
+
tgt = self.norm3(tgt)
|
| 119 |
+
tgt = self.linear2(self.dropout(self.activation(self.linear1(tgt))))
|
| 120 |
+
tgt = residual + self.dropout3(tgt)
|
| 121 |
+
else:
|
| 122 |
+
# Post-LayerNorm (备用)
|
| 123 |
+
residual = tgt
|
| 124 |
+
tgt = self.self_attn(
|
| 125 |
+
tgt, tgt, tgt,
|
| 126 |
+
key_padding_mask=tgt_key_padding_mask,
|
| 127 |
+
is_causal=is_causal,
|
| 128 |
+
)
|
| 129 |
+
tgt = self.norm1(residual + self.dropout1(tgt))
|
| 130 |
+
|
| 131 |
+
residual = tgt
|
| 132 |
+
tgt = self.multihead_attn(
|
| 133 |
+
tgt, memory, memory,
|
| 134 |
+
key_padding_mask=memory_key_padding_mask,
|
| 135 |
+
)
|
| 136 |
+
tgt = self.norm2(residual + self.dropout2(tgt))
|
| 137 |
+
|
| 138 |
+
residual = tgt
|
| 139 |
+
tgt = self.linear2(self.dropout(self.activation(self.linear1(tgt))))
|
| 140 |
+
tgt = self.norm3(residual + self.dropout3(tgt))
|
| 141 |
+
|
| 142 |
+
return tgt
|
| 143 |
|
| 144 |
|
| 145 |
class TransformerDecoder(nn.Module):
|
| 146 |
"""
|
| 147 |
多层 Transformer Decoder。
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
"""
|
| 149 |
|
| 150 |
def __init__(self, decoder_layer: TransformerDecoderLayer, num_layers: int):
|
| 151 |
super().__init__()
|
| 152 |
+
self.layers = nn.ModuleList(
|
| 153 |
+
[copy.deepcopy(decoder_layer) for _ in range(num_layers)]
|
| 154 |
+
)
|
| 155 |
+
self.num_layers = num_layers
|
| 156 |
+
self.norm = nn.LayerNorm(decoder_layer.d_model)
|
| 157 |
|
| 158 |
def forward(
|
| 159 |
self,
|
|
|
|
| 163 |
memory_key_padding_mask: Optional[torch.BoolTensor] = None,
|
| 164 |
tgt_key_padding_mask: Optional[torch.BoolTensor] = None,
|
| 165 |
) -> torch.Tensor:
|
| 166 |
+
output = tgt
|
| 167 |
+
for layer in self.layers:
|
| 168 |
+
output = layer(
|
| 169 |
+
output,
|
| 170 |
+
memory,
|
| 171 |
+
tgt_mask=tgt_mask,
|
| 172 |
+
memory_key_padding_mask=memory_key_padding_mask,
|
| 173 |
+
tgt_key_padding_mask=tgt_key_padding_mask,
|
| 174 |
+
)
|
| 175 |
+
output = self.norm(output)
|
| 176 |
+
return output
|
src/easytranslate/model/encoder.py
CHANGED
|
@@ -11,23 +11,21 @@ Transformer Encoder 模块 — Person B 负责实现
|
|
| 11 |
|
| 12 |
from __future__ import annotations
|
| 13 |
|
|
|
|
|
|
|
| 14 |
import torch
|
| 15 |
import torch.nn as nn
|
| 16 |
from typing import Optional
|
| 17 |
|
|
|
|
|
|
|
| 18 |
|
| 19 |
class TransformerEncoderLayer(nn.Module):
|
| 20 |
"""
|
| 21 |
单层 Transformer Encoder。
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
2. Feed-Forward Network: Linear → Activation → Dropout → Linear
|
| 26 |
-
3. 两个 LayerNorm
|
| 27 |
-
4. Residual connections
|
| 28 |
-
5. Dropout
|
| 29 |
-
|
| 30 |
-
注意: 使用 Pre-LayerNorm 架构 (先 norm 再 attention/ffn)
|
| 31 |
"""
|
| 32 |
|
| 33 |
def __init__(
|
|
@@ -42,43 +40,88 @@ class TransformerEncoderLayer(nn.Module):
|
|
| 42 |
pre_norm: bool = True,
|
| 43 |
):
|
| 44 |
super().__init__()
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
def forward(
|
| 48 |
self,
|
| 49 |
src: torch.Tensor, # [B, S, D]
|
| 50 |
src_key_padding_mask: Optional[torch.BoolTensor] = None, # [B, S]
|
| 51 |
) -> torch.Tensor:
|
| 52 |
-
"""
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
|
| 66 |
class TransformerEncoder(nn.Module):
|
| 67 |
"""
|
| 68 |
多层 Transformer Encoder。
|
| 69 |
-
|
| 70 |
-
TODO [Person B]:
|
| 71 |
-
1. 堆叠 N 个 TransformerEncoderLayer
|
| 72 |
-
2. 最终加一个 LayerNorm (Pre-Norm 架构需要)
|
| 73 |
"""
|
| 74 |
|
| 75 |
def __init__(self, encoder_layer: TransformerEncoderLayer, num_layers: int):
|
| 76 |
super().__init__()
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
def forward(
|
| 80 |
self,
|
| 81 |
src: torch.Tensor,
|
| 82 |
src_key_padding_mask: Optional[torch.BoolTensor] = None,
|
| 83 |
) -> torch.Tensor:
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
from __future__ import annotations
|
| 13 |
|
| 14 |
+
import copy
|
| 15 |
+
|
| 16 |
import torch
|
| 17 |
import torch.nn as nn
|
| 18 |
from typing import Optional
|
| 19 |
|
| 20 |
+
from easytranslate.model.attention import MultiHeadAttention, FlashMultiHeadAttention
|
| 21 |
+
|
| 22 |
|
| 23 |
class TransformerEncoderLayer(nn.Module):
|
| 24 |
"""
|
| 25 |
单层 Transformer Encoder。
|
| 26 |
|
| 27 |
+
架构 (Pre-LayerNorm):
|
| 28 |
+
x → LayerNorm → MultiHeadAttention → Residual → LayerNorm → FFN → Residual
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
"""
|
| 30 |
|
| 31 |
def __init__(
|
|
|
|
| 40 |
pre_norm: bool = True,
|
| 41 |
):
|
| 42 |
super().__init__()
|
| 43 |
+
self.pre_norm = pre_norm
|
| 44 |
+
|
| 45 |
+
# Self-Attention
|
| 46 |
+
attn_cls = FlashMultiHeadAttention if use_flash_attention else MultiHeadAttention
|
| 47 |
+
self.self_attn = attn_cls(
|
| 48 |
+
d_model=d_model,
|
| 49 |
+
nhead=nhead,
|
| 50 |
+
dropout=dropout,
|
| 51 |
+
use_rotary_embedding=use_rotary_embedding,
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
# Feed-Forward Network
|
| 55 |
+
self.linear1 = nn.Linear(d_model, dim_feedforward)
|
| 56 |
+
self.activation = nn.GELU() if activation == "gelu" else nn.ReLU()
|
| 57 |
+
self.dropout = nn.Dropout(p=dropout)
|
| 58 |
+
self.linear2 = nn.Linear(dim_feedforward, d_model)
|
| 59 |
+
|
| 60 |
+
# LayerNorm
|
| 61 |
+
self.norm1 = nn.LayerNorm(d_model)
|
| 62 |
+
self.norm2 = nn.LayerNorm(d_model)
|
| 63 |
+
|
| 64 |
+
# Dropout for residual
|
| 65 |
+
self.dropout1 = nn.Dropout(p=dropout)
|
| 66 |
+
self.dropout2 = nn.Dropout(p=dropout)
|
| 67 |
|
| 68 |
def forward(
|
| 69 |
self,
|
| 70 |
src: torch.Tensor, # [B, S, D]
|
| 71 |
src_key_padding_mask: Optional[torch.BoolTensor] = None, # [B, S]
|
| 72 |
) -> torch.Tensor:
|
| 73 |
+
"""Pre-LayerNorm 前向传播。"""
|
| 74 |
+
if self.pre_norm:
|
| 75 |
+
# 1. Self-Attention sublayer
|
| 76 |
+
residual = src
|
| 77 |
+
src = self.norm1(src)
|
| 78 |
+
src = self.self_attn(
|
| 79 |
+
src, src, src,
|
| 80 |
+
key_padding_mask=src_key_padding_mask,
|
| 81 |
+
)
|
| 82 |
+
src = residual + self.dropout1(src)
|
| 83 |
+
|
| 84 |
+
# 2. FFN sublayer
|
| 85 |
+
residual = src
|
| 86 |
+
src = self.norm2(src)
|
| 87 |
+
src = self.linear2(self.dropout(self.activation(self.linear1(src))))
|
| 88 |
+
src = residual + self.dropout2(src)
|
| 89 |
+
else:
|
| 90 |
+
# Post-LayerNorm (备用)
|
| 91 |
+
residual = src
|
| 92 |
+
src = self.self_attn(
|
| 93 |
+
src, src, src,
|
| 94 |
+
key_padding_mask=src_key_padding_mask,
|
| 95 |
+
)
|
| 96 |
+
src = self.norm1(residual + self.dropout1(src))
|
| 97 |
+
|
| 98 |
+
residual = src
|
| 99 |
+
src = self.linear2(self.dropout(self.activation(self.linear1(src))))
|
| 100 |
+
src = self.norm2(residual + self.dropout2(src))
|
| 101 |
+
|
| 102 |
+
return src
|
| 103 |
|
| 104 |
|
| 105 |
class TransformerEncoder(nn.Module):
|
| 106 |
"""
|
| 107 |
多层 Transformer Encoder。
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
"""
|
| 109 |
|
| 110 |
def __init__(self, encoder_layer: TransformerEncoderLayer, num_layers: int):
|
| 111 |
super().__init__()
|
| 112 |
+
self.layers = nn.ModuleList(
|
| 113 |
+
[copy.deepcopy(encoder_layer) for _ in range(num_layers)]
|
| 114 |
+
)
|
| 115 |
+
self.num_layers = num_layers
|
| 116 |
+
self.norm = nn.LayerNorm(encoder_layer.self_attn.d_model)
|
| 117 |
|
| 118 |
def forward(
|
| 119 |
self,
|
| 120 |
src: torch.Tensor,
|
| 121 |
src_key_padding_mask: Optional[torch.BoolTensor] = None,
|
| 122 |
) -> torch.Tensor:
|
| 123 |
+
output = src
|
| 124 |
+
for layer in self.layers:
|
| 125 |
+
output = layer(output, src_key_padding_mask=src_key_padding_mask)
|
| 126 |
+
output = self.norm(output)
|
| 127 |
+
return output
|
src/easytranslate/model/finetune.py
CHANGED
|
@@ -50,7 +50,23 @@ def load_pretrained_model(
|
|
| 50 |
Returns:
|
| 51 |
(model, tokenizer)
|
| 52 |
"""
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
|
| 56 |
def setup_lora(
|
|
@@ -63,33 +79,38 @@ def setup_lora(
|
|
| 63 |
"""
|
| 64 |
为模型配置 LoRA 微调。
|
| 65 |
|
| 66 |
-
TODO [Person B]: 实现以下逻辑:
|
| 67 |
-
1. 定义 LoraConfig:
|
| 68 |
-
- r: LoRA 秩 (低秩分解维度)
|
| 69 |
-
- lora_alpha: 缩放因子
|
| 70 |
-
- lora_dropout: LoRA dropout
|
| 71 |
-
- target_modules: 需要加 LoRA 的模块 (如 q_proj, v_proj)
|
| 72 |
-
- task_type: SEQ_2_SEQ_LM
|
| 73 |
-
2. 使用 get_peft_model(model, config) 包装模型
|
| 74 |
-
3. 打印可训练参数数量和比例
|
| 75 |
-
4. 返回 LoRA 模型
|
| 76 |
-
|
| 77 |
-
参考: https://huggingface.co/docs/peft
|
| 78 |
-
|
| 79 |
Returns:
|
| 80 |
peft_model: LoRA 包装后的模型
|
| 81 |
"""
|
|
|
|
|
|
|
| 82 |
if target_modules is None:
|
| 83 |
target_modules = ["q_proj", "v_proj"]
|
| 84 |
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
|
|
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
Returns:
|
| 51 |
(model, tokenizer)
|
| 52 |
"""
|
| 53 |
+
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
|
| 54 |
+
|
| 55 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 56 |
+
model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
|
| 57 |
+
|
| 58 |
+
# 设置语言对
|
| 59 |
+
if hasattr(tokenizer, "lang_code_to_id"):
|
| 60 |
+
tokenizer.src_lang = src_lang
|
| 61 |
+
tokenizer.tgt_lang = tgt_lang
|
| 62 |
+
if hasattr(model.config, "forced_bos_token_id"):
|
| 63 |
+
model.config.forced_bos_token_id = tokenizer.lang_code_to_id.get(
|
| 64 |
+
tgt_lang, tokenizer.bos_token_id
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
model = model.to(device)
|
| 68 |
+
logger.info(f"Loaded pretrained model: {model_name}")
|
| 69 |
+
return model, tokenizer
|
| 70 |
|
| 71 |
|
| 72 |
def setup_lora(
|
|
|
|
| 79 |
"""
|
| 80 |
为模型配置 LoRA 微调。
|
| 81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
Returns:
|
| 83 |
peft_model: LoRA 包装后的模型
|
| 84 |
"""
|
| 85 |
+
from peft import LoraConfig, get_peft_model, TaskType
|
| 86 |
+
|
| 87 |
if target_modules is None:
|
| 88 |
target_modules = ["q_proj", "v_proj"]
|
| 89 |
|
| 90 |
+
config = LoraConfig(
|
| 91 |
+
r=r,
|
| 92 |
+
lora_alpha=alpha,
|
| 93 |
+
lora_dropout=dropout,
|
| 94 |
+
target_modules=target_modules,
|
| 95 |
+
task_type=TaskType.SEQ_2_SEQ_LM,
|
| 96 |
+
)
|
| 97 |
|
| 98 |
+
model = get_peft_model(model, config)
|
| 99 |
|
| 100 |
+
# 打印可训练参数信息
|
| 101 |
+
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
|
| 102 |
+
total_params = sum(p.numel() for p in model.parameters())
|
| 103 |
+
ratio = 100 * trainable_params / total_params if total_params > 0 else 0
|
| 104 |
+
logger.info(
|
| 105 |
+
f"LoRA setup: trainable params={trainable_params:,} "
|
| 106 |
+
f"/ total={total_params:,} ({ratio:.4f}%)"
|
| 107 |
+
)
|
| 108 |
|
| 109 |
+
return model
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def freeze_model_except_lora(model: nn.Module):
|
| 113 |
+
"""冻结模型所有参数,只保留 LoRA 参数可训练。"""
|
| 114 |
+
for name, param in model.named_parameters():
|
| 115 |
+
if "lora" not in name.lower():
|
| 116 |
+
param.requires_grad = False
|
src/easytranslate/model/positional.py
CHANGED
|
@@ -32,7 +32,17 @@ class SinusoidalPositionalEncoding(nn.Module):
|
|
| 32 |
|
| 33 |
def __init__(self, d_model: int, max_seq_len: int = 5000, dropout: float = 0.1):
|
| 34 |
super().__init__()
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 38 |
"""
|
|
@@ -41,7 +51,8 @@ class SinusoidalPositionalEncoding(nn.Module):
|
|
| 41 |
Returns:
|
| 42 |
x + positional_encoding: [B, L, D]
|
| 43 |
"""
|
| 44 |
-
|
|
|
|
| 45 |
|
| 46 |
|
| 47 |
class RotaryPositionalEmbedding(nn.Module):
|
|
@@ -72,10 +83,29 @@ class RotaryPositionalEmbedding(nn.Module):
|
|
| 72 |
|
| 73 |
def __init__(self, dim: int, max_seq_len: int = 2048, base: float = 10000.0):
|
| 74 |
super().__init__()
|
| 75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
def _compute_rope(self, seq_len: int, device: torch.device):
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
@staticmethod
|
| 81 |
def _rotate_half(x: torch.Tensor) -> torch.Tensor:
|
|
@@ -94,4 +124,11 @@ class RotaryPositionalEmbedding(nn.Module):
|
|
| 94 |
Returns:
|
| 95 |
(q_rotated, k_rotated)
|
| 96 |
"""
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
def __init__(self, d_model: int, max_seq_len: int = 5000, dropout: float = 0.1):
|
| 34 |
super().__init__()
|
| 35 |
+
self.dropout = nn.Dropout(p=dropout)
|
| 36 |
+
|
| 37 |
+
pe = torch.zeros(max_seq_len, d_model)
|
| 38 |
+
position = torch.arange(0, max_seq_len, dtype=torch.float).unsqueeze(1)
|
| 39 |
+
div_term = torch.exp(
|
| 40 |
+
torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)
|
| 41 |
+
)
|
| 42 |
+
pe[:, 0::2] = torch.sin(position * div_term)
|
| 43 |
+
pe[:, 1::2] = torch.cos(position * div_term)
|
| 44 |
+
pe = pe.unsqueeze(0) # [1, max_seq_len, d_model]
|
| 45 |
+
self.register_buffer("pe", pe)
|
| 46 |
|
| 47 |
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 48 |
"""
|
|
|
|
| 51 |
Returns:
|
| 52 |
x + positional_encoding: [B, L, D]
|
| 53 |
"""
|
| 54 |
+
x = x + self.pe[:, : x.size(1), :]
|
| 55 |
+
return self.dropout(x)
|
| 56 |
|
| 57 |
|
| 58 |
class RotaryPositionalEmbedding(nn.Module):
|
|
|
|
| 83 |
|
| 84 |
def __init__(self, dim: int, max_seq_len: int = 2048, base: float = 10000.0):
|
| 85 |
super().__init__()
|
| 86 |
+
self.dim = dim
|
| 87 |
+
self.max_seq_len = max_seq_len
|
| 88 |
+
self.base = base
|
| 89 |
+
|
| 90 |
+
inv_freq = 1.0 / (
|
| 91 |
+
base ** (torch.arange(0, dim, 2).float() / dim)
|
| 92 |
+
)
|
| 93 |
+
self.register_buffer("inv_freq", inv_freq)
|
| 94 |
+
|
| 95 |
+
# 预缓存 cos/sin
|
| 96 |
+
self._cached_seq_len = 0
|
| 97 |
+
self._cached_cos: torch.Tensor | None = None
|
| 98 |
+
self._cached_sin: torch.Tensor | None = None
|
| 99 |
|
| 100 |
def _compute_rope(self, seq_len: int, device: torch.device):
|
| 101 |
+
if seq_len > self._cached_seq_len or self._cached_cos is None:
|
| 102 |
+
t = torch.arange(seq_len, device=device, dtype=self.inv_freq.dtype)
|
| 103 |
+
freqs = torch.outer(t, self.inv_freq) # [seq_len, dim//2]
|
| 104 |
+
emb = torch.cat((freqs, freqs), dim=-1) # [seq_len, dim]
|
| 105 |
+
self._cached_cos = emb.cos()[None, None, :, :] # [1, 1, seq_len, dim]
|
| 106 |
+
self._cached_sin = emb.sin()[None, None, :, :] # [1, 1, seq_len, dim]
|
| 107 |
+
self._cached_seq_len = seq_len
|
| 108 |
+
return self._cached_cos, self._cached_sin
|
| 109 |
|
| 110 |
@staticmethod
|
| 111 |
def _rotate_half(x: torch.Tensor) -> torch.Tensor:
|
|
|
|
| 124 |
Returns:
|
| 125 |
(q_rotated, k_rotated)
|
| 126 |
"""
|
| 127 |
+
cos, sin = self._compute_rope(q.size(2), q.device)
|
| 128 |
+
q_embed = (q * cos[:, :, : q.size(2), :]) + (
|
| 129 |
+
self._rotate_half(q) * sin[:, :, : q.size(2), :]
|
| 130 |
+
)
|
| 131 |
+
k_embed = (k * cos[:, :, : k.size(2), :]) + (
|
| 132 |
+
self._rotate_half(k) * sin[:, :, : k.size(2), :]
|
| 133 |
+
)
|
| 134 |
+
return q_embed, k_embed
|
src/easytranslate/model/transformer.py
CHANGED
|
@@ -20,8 +20,8 @@ import torch
|
|
| 20 |
import torch.nn as nn
|
| 21 |
import torch.nn.functional as F
|
| 22 |
|
| 23 |
-
from easytranslate.model.encoder import TransformerEncoder
|
| 24 |
-
from easytranslate.model.decoder import TransformerDecoder
|
| 25 |
from easytranslate.model.positional import SinusoidalPositionalEncoding, RotaryPositionalEmbedding
|
| 26 |
|
| 27 |
logger = logging.getLogger(__name__)
|
|
@@ -78,33 +78,94 @@ class TransformerTranslationModel(nn.Module):
|
|
| 78 |
share_embedding: bool = False,
|
| 79 |
):
|
| 80 |
super().__init__()
|
| 81 |
-
# TODO [Person B]: 实现模型初始化
|
| 82 |
-
# 保存超参数
|
| 83 |
self.d_model = d_model
|
| 84 |
self.pad_id = pad_id
|
|
|
|
|
|
|
| 85 |
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
def forward(
|
| 110 |
self,
|
|
@@ -119,19 +180,50 @@ class TransformerTranslationModel(nn.Module):
|
|
| 119 |
Returns:
|
| 120 |
logits: [B, T, tgt_vocab_size]
|
| 121 |
"""
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
@torch.no_grad()
|
| 125 |
def encode(self, src_ids: torch.Tensor, src_padding_mask: Optional[torch.BoolTensor] = None) -> torch.Tensor:
|
| 126 |
-
"""
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
raise NotImplementedError("TODO: Person B 实现 encode")
|
| 135 |
|
| 136 |
@torch.no_grad()
|
| 137 |
def decode_step(
|
|
@@ -140,17 +232,24 @@ class TransformerTranslationModel(nn.Module):
|
|
| 140 |
encoder_output: torch.Tensor,
|
| 141 |
src_padding_mask: Optional[torch.BoolTensor] = None,
|
| 142 |
) -> torch.Tensor:
|
| 143 |
-
"""
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
def count_parameters(self) -> int:
|
| 156 |
"""返回可训练参数数量。"""
|
|
|
|
| 20 |
import torch.nn as nn
|
| 21 |
import torch.nn.functional as F
|
| 22 |
|
| 23 |
+
from easytranslate.model.encoder import TransformerEncoder, TransformerEncoderLayer
|
| 24 |
+
from easytranslate.model.decoder import TransformerDecoder, TransformerDecoderLayer
|
| 25 |
from easytranslate.model.positional import SinusoidalPositionalEncoding, RotaryPositionalEmbedding
|
| 26 |
|
| 27 |
logger = logging.getLogger(__name__)
|
|
|
|
| 78 |
share_embedding: bool = False,
|
| 79 |
):
|
| 80 |
super().__init__()
|
|
|
|
|
|
|
| 81 |
self.d_model = d_model
|
| 82 |
self.pad_id = pad_id
|
| 83 |
+
self.use_rotary_embedding = use_rotary_embedding
|
| 84 |
+
self.max_seq_len = max_seq_len
|
| 85 |
|
| 86 |
+
# Embeddings
|
| 87 |
+
self.src_embed = nn.Embedding(src_vocab_size, d_model)
|
| 88 |
+
self.tgt_embed = nn.Embedding(tgt_vocab_size, d_model)
|
| 89 |
+
self.embed_scale = math.sqrt(d_model)
|
| 90 |
|
| 91 |
+
# Positional encoding
|
| 92 |
+
if use_rotary_embedding:
|
| 93 |
+
# RoPE 在 attention 内部应用到 Q/K,不需要额外的位置编码层
|
| 94 |
+
self.pos_encoding: Optional[nn.Module] = None
|
| 95 |
+
rope = RotaryPositionalEmbedding(
|
| 96 |
+
dim=d_model // nhead,
|
| 97 |
+
max_seq_len=max_seq_len,
|
| 98 |
+
)
|
| 99 |
+
else:
|
| 100 |
+
self.pos_encoding = SinusoidalPositionalEncoding(
|
| 101 |
+
d_model=d_model,
|
| 102 |
+
max_seq_len=max_seq_len,
|
| 103 |
+
dropout=dropout,
|
| 104 |
+
)
|
| 105 |
+
rope = None
|
| 106 |
|
| 107 |
+
# Encoder
|
| 108 |
+
encoder_layer = TransformerEncoderLayer(
|
| 109 |
+
d_model=d_model,
|
| 110 |
+
nhead=nhead,
|
| 111 |
+
dim_feedforward=dim_feedforward,
|
| 112 |
+
dropout=dropout,
|
| 113 |
+
activation=activation,
|
| 114 |
+
use_flash_attention=use_flash_attention,
|
| 115 |
+
use_rotary_embedding=use_rotary_embedding,
|
| 116 |
+
pre_norm=pre_norm,
|
| 117 |
+
)
|
| 118 |
+
self.encoder = TransformerEncoder(encoder_layer, num_encoder_layers)
|
| 119 |
|
| 120 |
+
# Decoder
|
| 121 |
+
decoder_layer = TransformerDecoderLayer(
|
| 122 |
+
d_model=d_model,
|
| 123 |
+
nhead=nhead,
|
| 124 |
+
dim_feedforward=dim_feedforward,
|
| 125 |
+
dropout=dropout,
|
| 126 |
+
activation=activation,
|
| 127 |
+
use_flash_attention=use_flash_attention,
|
| 128 |
+
use_rotary_embedding=use_rotary_embedding,
|
| 129 |
+
pre_norm=pre_norm,
|
| 130 |
+
)
|
| 131 |
+
self.decoder = TransformerDecoder(decoder_layer, num_decoder_layers)
|
| 132 |
|
| 133 |
+
# 将 RoPE 注入到 encoder/decoder 的 attention 模块中
|
| 134 |
+
if rope is not None:
|
| 135 |
+
for layer in self.encoder.layers:
|
| 136 |
+
layer.self_attn.rope = rope
|
| 137 |
+
for layer in self.decoder.layers:
|
| 138 |
+
layer.self_attn.rope = rope
|
| 139 |
+
# cross-attention 不使用 RoPE
|
| 140 |
+
layer.multihead_attn.rope = None
|
| 141 |
+
|
| 142 |
+
# Output projection
|
| 143 |
+
self.output_projection = nn.Linear(d_model, tgt_vocab_size)
|
| 144 |
+
|
| 145 |
+
# 可选: 共享目标语言 embedding 和输出投影权重
|
| 146 |
+
self.share_embedding = share_embedding
|
| 147 |
+
if share_embedding:
|
| 148 |
+
self.output_projection.weight = self.tgt_embed.weight
|
| 149 |
+
|
| 150 |
+
self._init_weights()
|
| 151 |
+
|
| 152 |
+
def _init_weights(self):
|
| 153 |
+
"""参数初始化。"""
|
| 154 |
+
for p in self.parameters():
|
| 155 |
+
if p.dim() > 1:
|
| 156 |
+
nn.init.xavier_uniform_(p)
|
| 157 |
+
for module in self.modules():
|
| 158 |
+
if isinstance(module, nn.Embedding):
|
| 159 |
+
nn.init.normal_(module.weight, mean=0, std=self.d_model ** -0.5)
|
| 160 |
+
elif isinstance(module, nn.LayerNorm):
|
| 161 |
+
nn.init.ones_(module.weight)
|
| 162 |
+
nn.init.zeros_(module.bias)
|
| 163 |
+
|
| 164 |
+
def _generate_square_subsequent_mask(self, sz: int, device: torch.device) -> torch.Tensor:
|
| 165 |
+
"""生成因果注意力掩码 (causal mask)。"""
|
| 166 |
+
mask = torch.triu(torch.ones(sz, sz, device=device), diagonal=1)
|
| 167 |
+
mask = mask.masked_fill(mask == 1, float("-inf"))
|
| 168 |
+
return mask
|
| 169 |
|
| 170 |
def forward(
|
| 171 |
self,
|
|
|
|
| 180 |
Returns:
|
| 181 |
logits: [B, T, tgt_vocab_size]
|
| 182 |
"""
|
| 183 |
+
# 1. Embedding
|
| 184 |
+
src_emb = self.src_embed(src_ids) * self.embed_scale # [B, S, D]
|
| 185 |
+
tgt_emb = self.tgt_embed(tgt_input_ids) * self.embed_scale # [B, T, D]
|
| 186 |
+
|
| 187 |
+
# 2. Positional encoding (如果不使用 RoPE)
|
| 188 |
+
if self.pos_encoding is not None:
|
| 189 |
+
src_emb = self.pos_encoding(src_emb)
|
| 190 |
+
tgt_emb = self.pos_encoding(tgt_emb)
|
| 191 |
+
|
| 192 |
+
# 3. Masks
|
| 193 |
+
if src_padding_mask is None:
|
| 194 |
+
src_padding_mask = src_ids.eq(self.pad_id)
|
| 195 |
+
if tgt_padding_mask is None:
|
| 196 |
+
tgt_padding_mask = tgt_input_ids.eq(self.pad_id)
|
| 197 |
+
|
| 198 |
+
tgt_seq_len = tgt_input_ids.size(1)
|
| 199 |
+
tgt_mask = self._generate_square_subsequent_mask(tgt_seq_len, tgt_input_ids.device)
|
| 200 |
+
|
| 201 |
+
# 4. Encoder
|
| 202 |
+
encoder_output = self.encoder(src_emb, src_key_padding_mask=src_padding_mask)
|
| 203 |
+
|
| 204 |
+
# 5. Decoder
|
| 205 |
+
decoder_output = self.decoder(
|
| 206 |
+
tgt_emb,
|
| 207 |
+
encoder_output,
|
| 208 |
+
tgt_mask=tgt_mask,
|
| 209 |
+
memory_key_padding_mask=src_padding_mask,
|
| 210 |
+
tgt_key_padding_mask=tgt_padding_mask,
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
# 6. Output projection
|
| 214 |
+
logits = self.output_projection(decoder_output)
|
| 215 |
+
return logits
|
| 216 |
|
| 217 |
@torch.no_grad()
|
| 218 |
def encode(self, src_ids: torch.Tensor, src_padding_mask: Optional[torch.BoolTensor] = None) -> torch.Tensor:
|
| 219 |
+
"""仅编码(用于推理时复用 encoder 输出)。"""
|
| 220 |
+
src_emb = self.src_embed(src_ids) * self.embed_scale
|
| 221 |
+
if self.pos_encoding is not None:
|
| 222 |
+
src_emb = self.pos_encoding(src_emb)
|
| 223 |
+
if src_padding_mask is None:
|
| 224 |
+
src_padding_mask = src_ids.eq(self.pad_id)
|
| 225 |
+
encoder_output = self.encoder(src_emb, src_key_padding_mask=src_padding_mask)
|
| 226 |
+
return encoder_output
|
|
|
|
| 227 |
|
| 228 |
@torch.no_grad()
|
| 229 |
def decode_step(
|
|
|
|
| 232 |
encoder_output: torch.Tensor,
|
| 233 |
src_padding_mask: Optional[torch.BoolTensor] = None,
|
| 234 |
) -> torch.Tensor:
|
| 235 |
+
"""解码一步(用于自回归推理)。"""
|
| 236 |
+
tgt_emb = self.tgt_embed(tgt_input_ids) * self.embed_scale
|
| 237 |
+
if self.pos_encoding is not None:
|
| 238 |
+
tgt_emb = self.pos_encoding(tgt_emb)
|
| 239 |
+
|
| 240 |
+
tgt_seq_len = tgt_input_ids.size(1)
|
| 241 |
+
tgt_mask = self._generate_square_subsequent_mask(tgt_seq_len, tgt_input_ids.device)
|
| 242 |
+
|
| 243 |
+
decoder_output = self.decoder(
|
| 244 |
+
tgt_emb,
|
| 245 |
+
encoder_output,
|
| 246 |
+
tgt_mask=tgt_mask,
|
| 247 |
+
memory_key_padding_mask=src_padding_mask,
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
# 取最后一个 token 的 logits
|
| 251 |
+
logits = self.output_projection(decoder_output[:, -1, :])
|
| 252 |
+
return logits
|
| 253 |
|
| 254 |
def count_parameters(self) -> int:
|
| 255 |
"""返回可训练参数数量。"""
|