.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
- raise NotImplementedError("TODO: Person B 实现 MultiHeadAttention.__init__")
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 MultiHeadAttention.forward")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 FlashMultiHeadAttention.__init__")
 
 
 
 
 
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 FlashMultiHeadAttention.forward")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- TODO [Person B]: 实现以下组件:
26
- 1. Masked Self-Attention (因果掩码,防止看到未来)
27
- 2. Cross-Attention (decoder 查询 encoder 输出)
28
- 3. Feed-Forward Network
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
- raise NotImplementedError("TODO: Person B 实现 TransformerDecoderLayer.__init__")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- TODO [Person B]: Pre-LayerNorm 前向传播:
57
- 1. Masked Self-Attention with causal mask
58
- 2. Cross-Attention with encoder output
59
- 3. FFN
60
- 每步都有 residual connection 和 dropout
61
- """
62
- raise NotImplementedError("TODO: Person B 实现 TransformerDecoderLayer.forward")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 TransformerDecoder.__init__")
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 TransformerDecoder.forward")
 
 
 
 
 
 
 
 
 
 
 
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
- TODO [Person B]: 实现以下组件:
24
- 1. Self-Attention: MultiHeadAttention (支持 Flash Attention)
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
- raise NotImplementedError("TODO: Person B 实现 TransformerEncoderLayer.__init__")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- TODO [Person B]: Pre-LayerNorm 前向传播:
54
- 1. residual = src
55
- 2. src = layer_norm_1(src)
56
- 3. src = self_attention(src, src, src, key_padding_mask=src_key_padding_mask)
57
- 4. src = residual + dropout(src)
58
- 5. residual = src
59
- 6. src = layer_norm_2(src)
60
- 7. src = ffn(src)
61
- 8. src = residual + dropout(src)
62
- """
63
- raise NotImplementedError("TODO: Person B 实现 TransformerEncoderLayer.forward")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 TransformerEncoder.__init__")
 
 
 
 
78
 
79
  def forward(
80
  self,
81
  src: torch.Tensor,
82
  src_key_padding_mask: Optional[torch.BoolTensor] = None,
83
  ) -> torch.Tensor:
84
- raise NotImplementedError("TODO: Person B 实现 TransformerEncoder.forward")
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 load_pretrained_model")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 setup_lora")
 
 
 
 
 
 
86
 
 
87
 
88
- def freeze_model_except_lora(model: nn.Module):
89
- """
90
- 冻结模型所有参数,只保留 LoRA 参数可训练。
 
 
 
 
 
91
 
92
- TODO [Person B]: 遍历 model.named_parameters(),
93
- 如果参数名中不包含 "lora",则设置 requires_grad = False。
94
- """
95
- raise NotImplementedError("TODO: Person B 实现 freeze_model_except_lora")
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 SinusoidalPositionalEncoding.__init__")
 
 
 
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 SinusoidalPositionalEncoding.forward")
 
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
- raise NotImplementedError("TODO: Person B 实现 RotaryPositionalEmbedding.__init__")
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
  def _compute_rope(self, seq_len: int, device: torch.device):
78
- raise NotImplementedError("TODO: Person B 实现 _compute_rope")
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 apply_rotary_pos_emb")
 
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 TransformerTranslationModel.__init__")
 
 
 
87
 
88
- def _init_weights(self):
89
- """
90
- 参数初始化。
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- TODO [Person B]: 实现 Xavier/Kaiming 初始化:
93
- - Embedding: normal_(0, d_model^-0.5)
94
- - Linear: xavier_uniform_
95
- - LayerNorm: ones_ / zeros_
96
- """
97
- raise NotImplementedError("TODO: Person B 实现 _init_weights")
 
 
 
 
 
 
98
 
99
- def _generate_square_subsequent_mask(self, sz: int, device: torch.device) -> torch.Tensor:
100
- """
101
- 生成因果注意力掩码 (causal mask)。
 
 
 
 
 
 
 
 
 
102
 
103
- TODO [Person B]:
104
- 返回上三角矩阵 mask,shape [sz, sz],
105
- mask[i][j] = -inf if j > i else 0
106
- """
107
- raise NotImplementedError("TODO: Person B 实现 _generate_square_subsequent_mask")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- raise NotImplementedError("TODO: Person B 实现 forward")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  @torch.no_grad()
125
  def encode(self, src_ids: torch.Tensor, src_padding_mask: Optional[torch.BoolTensor] = None) -> torch.Tensor:
126
- """
127
- 仅编码(用于推理时复用 encoder 输出)。
128
-
129
- TODO [Person B]:
130
- 1. src embedding + positional encoding
131
- 2. encoder forward
132
- 3. 返回 encoder_output
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
- TODO [Person B]:
147
- 1. tgt embedding + positional encoding
148
- 2. 生成 causal mask
149
- 3. decoder forward
150
- 4. 取最后一个 token 的 logits
151
- 5. 返回 logits [B, vocab_size]
152
- """
153
- raise NotImplementedError("TODO: Person B 实现 decode_step")
 
 
 
 
 
 
 
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
  """返回可训练参数数量。"""