lijn14 commited on
Commit
c1a46f7
·
1 Parent(s): 70e674c

创建工程

Browse files
.gitignore ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # IDE
18
+ .idea/
19
+ .vscode/
20
+ *.swp
21
+ *.swo
22
+
23
+ # Data
24
+ data/
25
+ *.tsv
26
+ *.csv
27
+
28
+ # Checkpoints & Outputs
29
+ checkpoints/
30
+ outputs/
31
+ logs/
32
+ wandb/
33
+
34
+ # OS
35
+ .DS_Store
36
+ Thumbs.db
37
+
38
+ # Models
39
+ *.pt
40
+ *.pth
41
+ *.bin
42
+ *.safetensors
43
+ *.onnx
44
+
45
+ # Tokenizer artifacts
46
+ *.model
47
+ *.vocab
48
+ tokenizer.json
README.md CHANGED
@@ -1,4 +1,77 @@
1
  ---
2
  license: mit
3
  ---
4
- EasyTranslate: Transformer-Based Translation Model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: mit
3
  ---
4
+
5
+ # EasyTranslate: 基于 Transformer 的英中翻译系统
6
+
7
+ > 研究生 NLP 期末项目 — 使用前沿技术实现高质量英文到中文机器翻译
8
+
9
+ ## 亮点
10
+
11
+ - **Pre-LayerNorm Transformer**: 从零构建,训练更稳定
12
+ - **RoPE 旋转位置编码**: 替代传统正弦编码,更好捕获相对位置信息
13
+ - **Flash Attention 2**: 利用 PyTorch 2.0+ 原生支持,训练显著加速
14
+ - **NLLB 预训练微调**: 基于 Meta NLLB-200 模型,支持 LoRA 参数高效微调
15
+ - **完整评估体系**: SacreBLEU + COMET + chrF++ 多维度评估
16
+ - **6 组消融实验**: 系统验证各前沿组件的效果
17
+
18
+ ## 快速开始
19
+
20
+ ```bash
21
+ # 安装
22
+ conda create -n easytranslate python=3.11 && conda activate easytranslate
23
+ pip install torch --index-url https://download.pytorch.org/whl/cu121
24
+ pip install -r requirements.txt && pip install -e .
25
+
26
+ # 从头训练 Transformer
27
+ python scripts/train.py --config configs/default_config.yaml
28
+
29
+ # 微调 NLLB + LoRA
30
+ python scripts/train.py --config configs/default_config.yaml model.type=finetune_nllb
31
+
32
+ # 评估
33
+ python scripts/evaluate.py --checkpoint checkpoints/best_model.pt
34
+
35
+ # 交互翻译
36
+ python scripts/translate.py --checkpoint checkpoints/best_model.pt
37
+ ```
38
+
39
+ ## 项目结构
40
+
41
+ ```
42
+ src/easytranslate/
43
+ ├── data/ # 数据加载、分词、预处理、动态批处理
44
+ ├── model/ # Transformer (Encoder-Decoder) + 注意力 + 位置编码 + LoRA
45
+ ├── training/ # 训练循环、优化器、损失函数、分布式训练
46
+ ├── evaluation/ # BLEU/COMET 指标、Beam Search 解码、评估器
47
+ └── utils/ # 配置管理、日志、随机种子
48
+ ```
49
+
50
+ ## 技术架构
51
+
52
+ ```
53
+ 英文输入 → BPE Tokenizer → Source Embedding + RoPE
54
+ → Transformer Encoder (6 layers, Flash Attention, Pre-LN)
55
+ → Transformer Decoder (6 layers, Causal Mask, Cross-Attention)
56
+ → Linear Projection → Beam Search → 中文输出
57
+ ```
58
+
59
+ ## 团队分工
60
+
61
+ 详见 [TASK_ASSIGNMENT.md](TASK_ASSIGNMENT.md)
62
+
63
+ | 角色 | 负责模块 | 核心任务 |
64
+ |------|----------|----------|
65
+ | Person A | `data/` | 数据集加载、BPE 分词器、预处理流水线 |
66
+ | Person B | `model/` | Transformer 架构、Flash Attention、RoPE、LoRA |
67
+ | Person C | `training/` | 训练循环、混合精度、梯度累积、早停 |
68
+ | Person D | `evaluation/` | BLEU/COMET 评估、Beam Search、推理服务 |
69
+ | Person E | `utils/` + 实验 | 工具模块、消融实验、可视化、报告 |
70
+
71
+ ## 依赖
72
+
73
+ - Python >= 3.10
74
+ - PyTorch >= 2.1 (Flash Attention 支持)
75
+ - HuggingFace Transformers / Datasets / Tokenizers
76
+ - PEFT (LoRA)
77
+ - SacreBLEU, COMET (评估)
TASK_ASSIGNMENT.md ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EasyTranslate 任务分工文档
2
+
3
+ ## 项目概述
4
+
5
+ **项目名称**: EasyTranslate — 基于 Transformer 的英中翻译系统
6
+ **课程**: 研究生 NLP 期末作业
7
+ **团队规模**: 5 人
8
+ **技术栈**: PyTorch + HuggingFace Transformers + Flash Attention 2 + RoPE + LoRA
9
+
10
+ ---
11
+
12
+ ## 📂 项目结构
13
+
14
+ ```
15
+ UCAS-EasyTranslate/
16
+ ├── configs/
17
+ │ ├── default_config.yaml # 主配置文件
18
+ │ └── deepspeed_config.json # DeepSpeed 分布式训练配置
19
+ ├── src/easytranslate/
20
+ │ ├── data/ # [Person A] 数据模块
21
+ │ │ ├── dataset.py # 数据集加载
22
+ │ │ ├── tokenizer.py # 分词器
23
+ │ │ ├── preprocessing.py # 数据预处理
24
+ │ │ └── collator.py # 数据整理 & 动态批处理
25
+ │ ├── model/ # [Person B] 模型模块
26
+ │ │ ├── transformer.py # Transformer 主模型
27
+ │ │ ├── encoder.py # 编码器
28
+ │ │ ├── decoder.py # 解码器
29
+ │ │ ├── attention.py # 注意力机制 (标准 + Flash Attention)
30
+ │ │ ├── positional.py # 位置编码 (Sinusoidal + RoPE)
31
+ │ │ └── finetune.py # 预训练模型微调 (NLLB + LoRA)
32
+ │ ├── training/ # [Person C] 训练模块
33
+ │ │ ├── trainer.py # 训练器 (完整训练循环)
34
+ │ │ ├── optimizer.py # 优化器 & 学习率调度
35
+ │ │ └── loss.py # 损失函数 (标签平滑)
36
+ │ ├── evaluation/ # [Person D] 评估模块
37
+ │ │ ├── metrics.py # 评估指标 (BLEU, COMET, chrF)
38
+ │ │ ├── decoding.py # 解码策略 (贪心, Beam Search, 采样)
39
+ │ │ └── evaluator.py # 评估器
40
+ │ └── utils/ # [Person E] 工具模块
41
+ │ ├── config.py # 配置管理
42
+ │ ├── seed.py # 随机种子
43
+ │ └── logging.py # 日志管理
44
+ ├── scripts/
45
+ │ ├── train.py # 训练入口
46
+ │ ├── evaluate.py # 评估入口
47
+ │ ├── translate.py # 翻译推理 (CLI + Web UI)
48
+ │ ├── run_experiments.py # [Person E] 实验运行器
49
+ │ └── visualize.py # [Person E] 可视化分析
50
+ ├── tests/
51
+ │ ├── test_data.py # 数据模块测试
52
+ │ ├── test_model.py # 模型模块测试
53
+ │ ├── test_training.py # 训练模块测试
54
+ │ └── test_evaluation.py # 评估模块测试
55
+ ├── requirements.txt
56
+ ├── setup.py
57
+ └── README.md
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 👥 人员分工
63
+
64
+ ### Person A — 数据工程师
65
+
66
+ **负责文件**: `src/easytranslate/data/` 目录下所有文件
67
+ **预计工作量**: ~800 行代码
68
+ **截止日期建议**: 第 1-2 周
69
+
70
+ #### 核心任务
71
+
72
+ | 优先级 | 文件 | 任务 | 要点 |
73
+ |--------|------|------|------|
74
+ | P0 | `dataset.py` | `TranslationDataset.__getitem__` | 实现 tokenize + padding + teacher forcing 输入构造 |
75
+ | P0 | `dataset.py` | `load_wmt_dataset` | 使用 HuggingFace datasets 加载 WMT19 zh-en |
76
+ | P0 | `tokenizer.py` | `TokenizerWrapper` | 统一分词器接口 (encode/decode/vocab_size/special_tokens) |
77
+ | P0 | `tokenizer.py` | `train_bpe_tokenizer` | 使用 HuggingFace tokenizers 库训练 BPE |
78
+ | P0 | `tokenizer.py` | `build_tokenizer` | 根据配置构建分词器 |
79
+ | P1 | `preprocessing.py` | `clean_text` | Unicode 标准化 + 控制字符去除 |
80
+ | P1 | `preprocessing.py` | `filter_by_length` | 按长度和长度比过滤 |
81
+ | P1 | `preprocessing.py` | `preprocess_pipeline` | 完整预处理流水线 |
82
+ | P1 | `collator.py` | `TranslationCollator` | batch padding + mask 生成 |
83
+ | P2 | `collator.py` | `DynamicBatchSampler` | 按 token 数动态构建 batch |
84
+ | P2 | `dataset.py` | `load_opus_dataset` | OPUS-100 数据集加载 |
85
+ | P2 | `dataset.py` | `load_custom_dataset` | 自定义语料加载 |
86
+
87
+ #### 技术参考
88
+ - HuggingFace datasets: https://huggingface.co/docs/datasets
89
+ - HuggingFace tokenizers: https://huggingface.co/docs/tokenizers
90
+ - WMT19 zh-en: `datasets.load_dataset("wmt19", "zh-en")`
91
+
92
+ #### 验收标准
93
+ - [ ] `pytest tests/test_data.py` 全部通过
94
+ - [ ] 能够成功加载 WMT19 数据集并完成预处理
95
+ - [ ] BPE 分词器训练成功,encode/decode 往返一致
96
+ - [ ] DataLoader 能正常迭代,batch 维度正确
97
+
98
+ ---
99
+
100
+ ### Person B — 模型架构师
101
+
102
+ **负责文件**: `src/easytranslate/model/` 目录下所有文件
103
+ **预计工作量**: ~1000 行代码
104
+ **截止日期建议**: 第 1-2 周
105
+
106
+ #### 核心任务
107
+
108
+ | 优先级 | 文件 | 任务 | 要点 |
109
+ |--------|------|------|------|
110
+ | P0 | `attention.py` | `MultiHeadAttention` | 标准多头注意力: QKV 投影 + scaled dot-product + mask |
111
+ | P0 | `attention.py` | `FlashMultiHeadAttention` | 使用 F.scaled_dot_product_attention (PyTorch 2.0+) |
112
+ | P0 | `positional.py` | `SinusoidalPositionalEncoding` | 经典正弦余弦位置编码 |
113
+ | P0 | `positional.py` | `RotaryPositionalEmbedding` | RoPE 旋转位置编码 (前沿技术!) |
114
+ | P0 | `encoder.py` | `TransformerEncoderLayer/Encoder` | Pre-LayerNorm Encoder |
115
+ | P0 | `decoder.py` | `TransformerDecoderLayer/Decoder` | Pre-LayerNorm Decoder (self-attn + cross-attn + FFN) |
116
+ | P0 | `transformer.py` | `TransformerTranslationModel` | 完整 Enc-Dec 模型: embedding + encoder + decoder + projection |
117
+ | P1 | `transformer.py` | `encode / decode_step` | 推理用的编码和单步解码 |
118
+ | P1 | `finetune.py` | `load_pretrained_model` | 加载 NLLB/mBART 预训练模型 |
119
+ | P1 | `finetune.py` | `setup_lora` | 使用 PEFT 库配置 LoRA 微调 |
120
+
121
+ #### 关键技术点
122
+
123
+ 1. **Pre-LayerNorm** (比 Post-LN 训练更稳定):
124
+ ```
125
+ x → LayerNorm → Attention → Add(x) → LayerNorm → FFN → Add
126
+ ```
127
+
128
+ 2. **RoPE 旋转位置编码** (LLaMA/GPT-NeoX 使用):
129
+ ```python
130
+ q' = q * cos(θ) + rotate_half(q) * sin(θ)
131
+ k' = k * cos(θ) + rotate_half(k) * sin(θ)
132
+ # attention(q', k') 自动编码相对位置信息
133
+ ```
134
+
135
+ 3. **Flash Attention 2** (PyTorch 2.0+ 原生支持):
136
+ ```python
137
+ F.scaled_dot_product_attention(Q, K, V, attn_mask, dropout_p, is_causal)
138
+ ```
139
+
140
+ 4. **LoRA 微调** (参数高效):
141
+ ```python
142
+ from peft import LoraConfig, get_peft_model, TaskType
143
+ config = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"], task_type=TaskType.SEQ_2_SEQ_LM)
144
+ model = get_peft_model(model, config)
145
+ ```
146
+
147
+ #### 验收标准
148
+ - [ ] `pytest tests/test_model.py` 全部通过
149
+ - [ ] 模型前向传播输出维度正确: `[B, T, vocab_size]`
150
+ - [ ] Flash Attention 和标准 Attention 输出一致
151
+ - [ ] RoPE 编码能正确应用到 Q, K
152
+ - [ ] LoRA 模型可训练参数量远小于全量参数
153
+
154
+ ---
155
+
156
+ ### Person C — 训练工程师
157
+
158
+ **负责文件**: `src/easytranslate/training/` 目录下所有文件
159
+ **预计工作量**: ~700 行代码
160
+ **截止日期建议**: 第 2-3 周 (依赖 Person A, B)
161
+
162
+ #### 核心任务
163
+
164
+ | 优先级 | 文件 | 任务 | 要点 |
165
+ |--------|------|------|------|
166
+ | P0 | `loss.py` | `LabelSmoothedCrossEntropyLoss` | 带标签平滑的交叉熵损失 + 忽略 padding |
167
+ | P0 | `optimizer.py` | `build_optimizer` | 构建 AdamW,支持参数分组 |
168
+ | P0 | `optimizer.py` | `build_scheduler` | Cosine with warmup / Inverse sqrt 调度器 |
169
+ | P0 | `trainer.py` | `Trainer.__init__` | 初始化训练环境 (设备, 精度, 分布式) |
170
+ | P0 | `trainer.py` | `_train_one_epoch` | 单 epoch 训练循环 (混合精度 + 梯度累积) |
171
+ | P0 | `trainer.py` | `_validate` | 验证循环 |
172
+ | P1 | `trainer.py` | `train` | 主训练循环 (多 epoch) |
173
+ | P1 | `trainer.py` | `_save/_load_checkpoint` | 检查点保存和加载 |
174
+ | P1 | `trainer.py` | `_should_early_stop` | 早停逻辑 |
175
+ | P2 | `trainer.py` | `_setup_distributed` | DDP/FSDP/DeepSpeed 分布式训练 |
176
+ | P2 | `trainer.py` | `_log_metrics` | TensorBoard/WandB 日志 |
177
+
178
+ #### 关键技术点
179
+
180
+ 1. **混合精度训练**:
181
+ ```python
182
+ scaler = torch.amp.GradScaler('cuda')
183
+ with torch.amp.autocast('cuda', dtype=torch.float16):
184
+ logits = model(src, tgt)
185
+ loss = criterion(logits, labels)
186
+ scaler.scale(loss).backward()
187
+ scaler.step(optimizer)
188
+ scaler.update()
189
+ ```
190
+
191
+ 2. **梯度累积**:
192
+ ```python
193
+ loss = loss / gradient_accumulation_steps
194
+ loss.backward()
195
+ if (step + 1) % gradient_accumulation_steps == 0:
196
+ optimizer.step()
197
+ optimizer.zero_grad()
198
+ ```
199
+
200
+ 3. **标签平滑** (smoothing=0.1):
201
+ ```
202
+ 对于 target token: prob = 1 - smoothing = 0.9
203
+ 对于其他 token: prob = smoothing / (V - 1) ≈ 0.000003
204
+ ```
205
+
206
+ #### 验收标准
207
+ - [ ] `pytest tests/test_training.py` 全部通过
208
+ - [ ] 能在小数据集上完成完整训练流程
209
+ - [ ] Loss 能正常下降
210
+ - [ ] 检查点能正确保存和加载
211
+ - [ ] 早停机制正常工作
212
+
213
+ ---
214
+
215
+ ### Person D — 评估与推理工程师
216
+
217
+ **负责文件**: `src/easytranslate/evaluation/` + `scripts/evaluate.py` + `scripts/translate.py`
218
+ **预计工作量**: ~800 行代码
219
+ **截止日期建议**: 第 2-3 周 (依赖 Person B)
220
+
221
+ #### 核心任务
222
+
223
+ | 优先级 | 文件 | 任务 | 要点 |
224
+ |--------|------|------|------|
225
+ | P0 | `metrics.py` | `compute_bleu` | SacreBLEU (tokenize="zh" 对中文分词) |
226
+ | P0 | `metrics.py` | `compute_comet` | COMET 神经网络评估指标 |
227
+ | P0 | `metrics.py` | `compute_chrf` / `compute_ter` | chrF++ 和 TER 指标 |
228
+ | P0 | `decoding.py` | `greedy_decode` | 贪心解码 (逐 token argmax) |
229
+ | P0 | `decoding.py` | `beam_search_decode` | 束搜索 (最关键的解码算法!) |
230
+ | P1 | `decoding.py` | `sample_decode` | 采样解码 (temperature + top-k + top-p) |
231
+ | P1 | `evaluator.py` | `Evaluator` | 统一评估接口 |
232
+ | P1 | `scripts/evaluate.py` | 评估入口脚本 | 加载模型 → 评估 → 输出结果 |
233
+ | P2 | `scripts/translate.py` | 交互翻译 | CLI 交互 + 文件翻译 |
234
+ | P2 | `scripts/translate.py` | `launch_web_ui` | Gradio Web 界面 |
235
+
236
+ #### 关键技术点
237
+
238
+ 1. **Beam Search** (翻译最核心的算法):
239
+ ```
240
+ 维护 beam_size 个候选序列
241
+ 每步扩展所有候选 → 选 top-k → 继续
242
+ 完成后按 score / length^penalty 排序
243
+ ```
244
+
245
+ 2. **SacreBLEU** (标准化评估):
246
+ ```python
247
+ import sacrebleu
248
+ bleu = sacrebleu.corpus_bleu(hypotheses, [references], tokenize="zh")
249
+ ```
250
+
251
+ 3. **COMET** (最准确的评估指标):
252
+ ```python
253
+ from comet import download_model, load_from_checkpoint
254
+ model = load_from_checkpoint(download_model("Unbabel/wmt22-comet-da"))
255
+ data = [{"src": s, "mt": h, "ref": r} for s, h, r in zip(sources, hyps, refs)]
256
+ output = model.predict(data)
257
+ ```
258
+
259
+ #### 验收标准
260
+ - [ ] `pytest tests/test_evaluation.py` 全部通过
261
+ - [ ] BLEU 计算结果与 sacrebleu CLI 一致
262
+ - [ ] Beam search 翻译质量 ≥ Greedy
263
+ - [ ] 能完成端到端评估流程
264
+ - [ ] 交互翻译模式正常工作
265
+
266
+ ---
267
+
268
+ ### Person E — 实验与报告
269
+
270
+ **负责文件**: `src/easytranslate/utils/` + `scripts/run_experiments.py` + `scripts/visualize.py` + 实验报告
271
+ **预计工作量**: ~600 行代码 + 实验报告
272
+ **截止日期建议**: 第 1 周 (utils) + 第 3-4 周 (实验)
273
+
274
+ #### 核心任务
275
+
276
+ | 优先级 | 文件 | 任务 | 要点 |
277
+ |--------|------|------|------|
278
+ | P0 | `utils/config.py` | 配置管理 | OmegaConf 加载 + CLI 覆盖 |
279
+ | P0 | `utils/seed.py` | 随机种子 | 全局种子设置 (可复现) |
280
+ | P0 | `utils/logging.py` | 日志系统 | Rich 美化 + 文件日志 |
281
+ | P1 | `scripts/run_experiments.py` | 实验运行器 | 自动化运行 6 组消融实验 |
282
+ | P1 | `scripts/visualize.py` | 训练曲线 | Loss/BLEU/LR 曲线绘制 |
283
+ | P1 | `scripts/visualize.py` | 实验对比 | 柱状图 + 表格对比 |
284
+ | P2 | `scripts/visualize.py` | 注意力可视化 | Cross-attention 热力图 |
285
+ | P2 | `scripts/visualize.py` | 翻译样例 | 好坏案例展示 |
286
+ | P0 | — | 实验报告 | 撰写完整实验报告 |
287
+
288
+ #### 实验设计 (6 组消融实验)
289
+
290
+ | 实验编号 | 实验名称 | 关键变量 | 目的 |
291
+ |----------|----------|----------|------|
292
+ | Exp1 | Baseline Transformer | 标准 6 层, d=512, 正弦位置编码 | 基线 |
293
+ | Exp2 | + RoPE | 替换正弦编码为 RoPE | 验证旋转位置编码效果 |
294
+ | Exp3 | + Flash Attention | 使用 Flash Attention 2 | 验证训练加速效果 |
295
+ | Exp4 | Full (RoPE + Flash) | RoPE + Flash Attention | 完整前沿方案 |
296
+ | Exp5 | NLLB + LoRA | 预训练 NLLB-600M + LoRA r=16 | 预训练微调效果 |
297
+ | Exp6 | NLLB Full FT | 预训练 NLLB-600M 全量微调 | 微调上界 |
298
+
299
+ #### 报告结构建议
300
+ 1. **摘要**: 项目目标、方法、主要结果
301
+ 2. **引言**: 机器翻译背景、Transformer 发展、研究动机
302
+ 3. **相关工作**: Transformer、NLLB、LoRA、Flash Attention、RoPE
303
+ 4. **方法**:
304
+ - 模型架构 (附架构图)
305
+ - 训练策略 (混合精度、标签平滑、调度器)
306
+ - 预训练微调方案 (NLLB + LoRA)
307
+ 5. **实验**:
308
+ - 数据集 (WMT19 zh-en)
309
+ - 实验设置 (超参数表)
310
+ - 实验结果 (BLEU/COMET/chrF 表格)
311
+ - 消融分析 (各组件贡献)
312
+ - 训练效率对比 (Flash Attention 加速比)
313
+ 6. **分析**:
314
+ - 注意力可视化
315
+ - 翻译案例分析
316
+ - 错误分析
317
+ 7. **结论与展望**
318
+
319
+ #### 验收标准
320
+ - [ ] utils 模块功能正常
321
+ - [ ] 6 组实验能自动化运行
322
+ - [ ] 生成完整的可视化图表
323
+ - [ ] 实验报告完整,图表清晰
324
+
325
+ ---
326
+
327
+ ## 🗓️ 时间线
328
+
329
+ ```
330
+ 第 1 周: Person A (数据) + Person B (模型) + Person E (utils) 并行开发
331
+
332
+ 第 2 周: Person C (训练, 依赖 A+B) + Person D (评估, 依赖 B) 开始
333
+ Person A, B 完善和联调
334
+
335
+ 第 3 周: 全体联调 → scripts/train.py 整合
336
+ Person E 开始跑实验
337
+
338
+ 第 4 周: Person E 完成实验 + 报告
339
+ 全体 Review + 优化
340
+ ```
341
+
342
+ ## 🔌 模块接口约定
343
+
344
+ ### 分词器接口 (Person A 定义, Person B/C/D 使用)
345
+ ```python
346
+ tokenizer.encode(text: str) -> list[int]
347
+ tokenizer.decode(ids: list[int]) -> str
348
+ tokenizer.vocab_size -> int
349
+ tokenizer.pad_id -> int
350
+ tokenizer.bos_id -> int
351
+ tokenizer.eos_id -> int
352
+ ```
353
+
354
+ ### 模型接口 (Person B 定义, Person C/D 使用)
355
+ ```python
356
+ # 训练
357
+ logits = model(src_ids, tgt_input_ids, src_padding_mask, tgt_padding_mask)
358
+ # logits: [B, T, vocab_size]
359
+
360
+ # 推理
361
+ encoder_output = model.encode(src_ids, src_padding_mask)
362
+ next_logits = model.decode_step(tgt_input_ids, encoder_output, src_padding_mask)
363
+ ```
364
+
365
+ ### 数据 Batch 格式 (Person A 定义, Person C 使用)
366
+ ```python
367
+ batch = {
368
+ "src_ids": Tensor[B, S],
369
+ "tgt_input_ids": Tensor[B, T],
370
+ "labels": Tensor[B, T],
371
+ "src_padding_mask": BoolTensor[B, S],
372
+ "tgt_padding_mask": BoolTensor[B, T],
373
+ }
374
+ ```
375
+
376
+ ### 评估接口 (Person D 定义, Person C/E 使用)
377
+ ```python
378
+ evaluator = Evaluator(model, tokenizer, config)
379
+ results = evaluator.evaluate(dataloader)
380
+ # results: {"bleu": 25.6, "comet": 0.82, "chrf": 45.3, "ter": 55.2}
381
+ ```
382
+
383
+ ---
384
+
385
+ ## ⚙️ 开发环境
386
+
387
+ ```bash
388
+ # 1. 创建虚拟环境
389
+ conda create -n easytranslate python=3.11
390
+ conda activate easytranslate
391
+
392
+ # 2. 安装 PyTorch (CUDA 12.1)
393
+ pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
394
+
395
+ # 3. 安装项目依赖
396
+ pip install -r requirements.txt
397
+
398
+ # 4. 安装项目 (开发模式)
399
+ pip install -e .
400
+
401
+ # 5. 运行测试
402
+ pytest tests/ -v
403
+ ```
404
+
405
+ ---
406
+
407
+ ## ✅ 最终交付清单
408
+
409
+ - [ ] 代码: 所有 `TODO: Person X` 均已实现
410
+ - [ ] 测试: `pytest tests/` 全部通过
411
+ - [ ] 训练: 至少完成 Exp1 (基线) 和 Exp5 (NLLB+LoRA) 两组实验
412
+ - [ ] 评估: 在 WMT19 测试集上报告 BLEU / COMET 分数
413
+ - [ ] 报告: 完整实验报告 (含图表)
414
+ - [ ] 演示: 交互翻译 Demo 可运行
configs/deepspeed_config.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "fp16": {
3
+ "enabled": true,
4
+ "loss_scale": 0,
5
+ "loss_scale_window": 1000,
6
+ "initial_scale_power": 16,
7
+ "hysteresis": 2,
8
+ "min_loss_scale": 1
9
+ },
10
+ "zero_optimization": {
11
+ "stage": 2,
12
+ "offload_optimizer": {
13
+ "device": "cpu",
14
+ "pin_memory": true
15
+ },
16
+ "allgather_partitions": true,
17
+ "allgather_bucket_size": 2e8,
18
+ "overlap_comm": true,
19
+ "reduce_scatter": true,
20
+ "reduce_bucket_size": 2e8,
21
+ "contiguous_gradients": true
22
+ },
23
+ "gradient_accumulation_steps": 4,
24
+ "gradient_clipping": 1.0,
25
+ "train_batch_size": "auto",
26
+ "train_micro_batch_size_per_gpu": "auto",
27
+ "wall_clock_breakdown": false
28
+ }
configs/default_config.yaml ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================================
2
+ # EasyTranslate 默认配置文件
3
+ # 基于 Transformer 架构的英中翻译模型
4
+ # ============================================================================
5
+
6
+ # ---------- 模型配置 ----------
7
+ model:
8
+ # 模型类型: "transformer_scratch" | "finetune_nllb" | "finetune_mbart"
9
+ type: "transformer_scratch"
10
+
11
+ # === Transformer from scratch 配置 ===
12
+ transformer:
13
+ d_model: 512 # 模型维度
14
+ nhead: 8 # 多头注意力头数
15
+ num_encoder_layers: 6 # 编码器层数
16
+ num_decoder_layers: 6 # 解码器层数
17
+ dim_feedforward: 2048 # FFN 中间维度
18
+ dropout: 0.1 # Dropout 概率
19
+ activation: "gelu" # 激活函数: "relu" | "gelu"
20
+ max_seq_len: 512 # 最大序列长度
21
+ use_flash_attention: true # 是否使用 Flash Attention 2
22
+ use_rotary_embedding: true # 是否使用旋转位置编码 (RoPE)
23
+ pre_norm: true # Pre-LayerNorm (更稳定)
24
+
25
+ # === 预训练模型微调配置 ===
26
+ pretrained:
27
+ model_name: "facebook/nllb-200-distilled-600M" # HuggingFace 模型名
28
+ src_lang: "eng_Latn" # 源语言代码
29
+ tgt_lang: "zho_Hans" # 目标语言代码
30
+ use_lora: true # 是否使用 LoRA 微调
31
+ lora:
32
+ r: 16 # LoRA 秩
33
+ alpha: 32 # LoRA alpha
34
+ dropout: 0.05 # LoRA dropout
35
+ target_modules: # LoRA 目标模块
36
+ - "q_proj"
37
+ - "v_proj"
38
+ - "k_proj"
39
+ - "o_proj"
40
+
41
+ # ---------- 分词器配置 ----------
42
+ tokenizer:
43
+ # 分词器类型: "bpe" | "sentencepiece" | "pretrained"
44
+ type: "bpe"
45
+ vocab_size: 32000 # 词汇表大小
46
+ min_frequency: 2 # 最小词频
47
+ special_tokens:
48
+ pad: "<pad>"
49
+ unk: "<unk>"
50
+ bos: "<s>"
51
+ eos: "</s>"
52
+ max_length: 512 # 最大 token 长度
53
+
54
+ # ---------- 数据配置 ----------
55
+ data:
56
+ # 数据集: "wmt" | "opus" | "custom"
57
+ dataset_name: "wmt"
58
+ wmt:
59
+ year: "19" # WMT 年份
60
+ language_pair: "zh-en" # 语言对
61
+ opus:
62
+ subset: "UNPC" # OPUS 子集名
63
+ custom:
64
+ train_src: "data/train.en" # 自定义训练集源语言
65
+ train_tgt: "data/train.zh"
66
+ val_src: "data/val.en"
67
+ val_tgt: "data/val.zh"
68
+ test_src: "data/test.en"
69
+ test_tgt: "data/test.zh"
70
+
71
+ # 数据处理
72
+ preprocessing:
73
+ lowercase_src: false # 源语言是否小写化
74
+ remove_punctuation: false # 是否去除标点
75
+ max_src_len: 256 # 源语言最大长度
76
+ max_tgt_len: 256 # 目标语言最大长度
77
+ filter_by_length: true # 是否按长度过滤
78
+ length_ratio_threshold: 3.0 # 长度比阈值
79
+
80
+ # DataLoader
81
+ dataloader:
82
+ batch_size: 32
83
+ num_workers: 4
84
+ pin_memory: true
85
+ dynamic_batching: true # 动态 batch (按 token 数)
86
+ max_tokens_per_batch: 8192 # 动态 batch 最大 token 数
87
+
88
+ # ---------- 训练配置 ----------
89
+ training:
90
+ # 基础训练参数
91
+ epochs: 30
92
+ max_steps: -1 # -1 表示按 epoch 训练
93
+ gradient_accumulation_steps: 4
94
+ fp16: true # 混合精度训练
95
+ bf16: false # BF16 (A100+)
96
+ gradient_checkpointing: false
97
+
98
+ # 优化器
99
+ optimizer:
100
+ type: "adamw" # "adam" | "adamw" | "adafactor"
101
+ lr: 3.0e-4
102
+ weight_decay: 0.01
103
+ betas: [0.9, 0.98]
104
+ eps: 1.0e-8
105
+
106
+ # 学习率调度
107
+ scheduler:
108
+ type: "cosine_with_warmup" # "cosine_with_warmup" | "inverse_sqrt" | "linear"
109
+ warmup_steps: 4000
110
+ min_lr: 1.0e-6
111
+
112
+ # 正则化
113
+ regularization:
114
+ label_smoothing: 0.1 # 标签平滑
115
+ dropout: 0.1
116
+
117
+ # 检查点
118
+ checkpoint:
119
+ save_dir: "checkpoints/"
120
+ save_every_n_steps: 5000
121
+ save_best: true # 保存最佳模型
122
+ metric_for_best: "bleu" # 选择最佳模型的指标
123
+ max_checkpoints: 5 # 最大保存检查点数
124
+
125
+ # 早停
126
+ early_stopping:
127
+ enabled: true
128
+ patience: 5
129
+ min_delta: 0.1
130
+
131
+ # 分布式训练
132
+ distributed:
133
+ strategy: "ddp" # "ddp" | "fsdp" | "deepspeed"
134
+ deepspeed_config: "configs/deepspeed_config.json"
135
+
136
+ # ---------- 评估配置 ----------
137
+ evaluation:
138
+ # 评估指标
139
+ metrics:
140
+ - "bleu" # SacreBLEU
141
+ - "comet" # COMET (神经网络指标)
142
+ - "chrf" # chrF++
143
+ - "ter" # TER
144
+
145
+ # 解码策略
146
+ decoding:
147
+ strategy: "beam_search" # "greedy" | "beam_search" | "sampling"
148
+ beam_size: 5
149
+ length_penalty: 1.0
150
+ no_repeat_ngram_size: 3
151
+ max_decode_len: 256
152
+
153
+ # Sampling 参数
154
+ sampling:
155
+ temperature: 0.7
156
+ top_k: 50
157
+ top_p: 0.9
158
+
159
+ # 评估频率
160
+ eval_every_n_steps: 1000
161
+ eval_on_epoch_end: true
162
+
163
+ # ---------- 日志配置 ----------
164
+ logging:
165
+ # 日志工具: "wandb" | "tensorboard" | "both"
166
+ backend: "tensorboard"
167
+ project_name: "EasyTranslate"
168
+ log_every_n_steps: 100
169
+ log_dir: "logs/"
170
+
171
+ # ---------- 推理/部署配置 ----------
172
+ inference:
173
+ model_path: "checkpoints/best_model"
174
+ device: "cuda"
175
+ batch_size: 16
176
+ quantization: null # null | "int8" | "int4"
177
+
178
+ # ---------- 实验配置 ----------
179
+ experiment:
180
+ seed: 42
181
+ name: "baseline"
182
+ output_dir: "outputs/"
pytest.ini ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ [pytest]
2
+ testpaths = tests
3
+ python_files = test_*.py
4
+ python_functions = test_*
5
+ python_classes = Test*
6
+ addopts = -v --tb=short
requirements.txt ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Core
2
+ torch>=2.1.0
3
+ transformers>=4.36.0
4
+ datasets>=2.16.0
5
+ tokenizers>=0.15.0
6
+ sentencepiece>=0.1.99
7
+ accelerate>=0.25.0
8
+
9
+ # Training
10
+ deepspeed>=0.12.0
11
+ bitsandbytes>=0.41.0
12
+ peft>=0.7.0
13
+ wandb>=0.16.0
14
+ tensorboard>=2.15.0
15
+
16
+ # Evaluation
17
+ sacrebleu>=2.4.0
18
+ unbabel-comet>=2.2.0
19
+ rouge-score>=0.1.2
20
+ nltk>=3.8.0
21
+
22
+ # Utilities
23
+ numpy>=1.24.0
24
+ pandas>=2.0.0
25
+ tqdm>=4.66.0
26
+ pyyaml>=6.0.0
27
+ omegaconf>=2.3.0
28
+ rich>=13.0.0
29
+ matplotlib>=3.8.0
30
+ seaborn>=0.13.0
31
+
32
+ # Serving (optional)
33
+ fastapi>=0.108.0
34
+ uvicorn>=0.25.0
35
+ gradio>=4.10.0
scripts/evaluate.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 评估入口脚本
3
+
4
+ 使用方式:
5
+ # 在测试集上评估
6
+ python scripts/evaluate.py --config configs/default_config.yaml --checkpoint checkpoints/best_model.pt
7
+
8
+ # 指定解码策略
9
+ python scripts/evaluate.py --checkpoint checkpoints/best_model.pt evaluation.decoding.strategy=beam_search evaluation.decoding.beam_size=10
10
+ """
11
+
12
+ import argparse
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
17
+
18
+
19
+ def parse_args():
20
+ parser = argparse.ArgumentParser(description="EasyTranslate Evaluation")
21
+ parser.add_argument("--config", type=str, default="configs/default_config.yaml")
22
+ parser.add_argument("--checkpoint", type=str, required=True, help="模型检查点路径")
23
+ parser.add_argument("--output", type=str, default="outputs/evaluation_results.json", help="结果保存路径")
24
+ args, unknown = parser.parse_known_args()
25
+ return args, unknown
26
+
27
+
28
+ def main():
29
+ """
30
+ 评估主流程。
31
+
32
+ TODO [Person D]:
33
+ 1. 加载配置和检查点
34
+ 2. 重建模型并加载权重
35
+ 3. 加载测试数据
36
+ 4. 构建 Evaluator
37
+ 5. 运行评估
38
+ 6. 打印和保存结果
39
+ """
40
+ args, cli_overrides = parse_args()
41
+
42
+ print("=" * 60)
43
+ print(" EasyTranslate - Evaluation")
44
+ print("=" * 60)
45
+
46
+ raise NotImplementedError("TODO: Person D 实现评估主流程")
47
+
48
+
49
+ if __name__ == "__main__":
50
+ main()
scripts/run_experiments.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 实验运行脚本 — Person E 负责
3
+
4
+ 自动化运行消融实验,比较不同配置的效果。
5
+
6
+ 使用方式:
7
+ python scripts/run_experiments.py
8
+ """
9
+
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
14
+
15
+
16
+ # 实验配置列表
17
+ EXPERIMENTS = [
18
+ {
19
+ "name": "exp1_baseline_transformer",
20
+ "description": "基线: 从头训练标准 Transformer (6层, d=512)",
21
+ "overrides": {
22
+ "model.type": "transformer_scratch",
23
+ "model.transformer.num_encoder_layers": 6,
24
+ "model.transformer.num_decoder_layers": 6,
25
+ "model.transformer.d_model": 512,
26
+ "model.transformer.use_flash_attention": False,
27
+ "model.transformer.use_rotary_embedding": False,
28
+ },
29
+ },
30
+ {
31
+ "name": "exp2_transformer_rope",
32
+ "description": "消融: Transformer + RoPE 位置编码",
33
+ "overrides": {
34
+ "model.type": "transformer_scratch",
35
+ "model.transformer.use_rotary_embedding": True,
36
+ },
37
+ },
38
+ {
39
+ "name": "exp3_transformer_flash_attn",
40
+ "description": "消融: Transformer + Flash Attention 2",
41
+ "overrides": {
42
+ "model.type": "transformer_scratch",
43
+ "model.transformer.use_flash_attention": True,
44
+ },
45
+ },
46
+ {
47
+ "name": "exp4_transformer_full",
48
+ "description": "完整: Transformer + RoPE + Flash Attention",
49
+ "overrides": {
50
+ "model.type": "transformer_scratch",
51
+ "model.transformer.use_flash_attention": True,
52
+ "model.transformer.use_rotary_embedding": True,
53
+ },
54
+ },
55
+ {
56
+ "name": "exp5_nllb_lora",
57
+ "description": "预训练微调: NLLB-600M + LoRA",
58
+ "overrides": {
59
+ "model.type": "finetune_nllb",
60
+ "model.pretrained.use_lora": True,
61
+ "model.pretrained.lora.r": 16,
62
+ },
63
+ },
64
+ {
65
+ "name": "exp6_nllb_full_finetune",
66
+ "description": "预训练全量微调: NLLB-600M",
67
+ "overrides": {
68
+ "model.type": "finetune_nllb",
69
+ "model.pretrained.use_lora": False,
70
+ },
71
+ },
72
+ ]
73
+
74
+
75
+ def run_single_experiment(exp_config: dict):
76
+ """
77
+ 运行单个实验。
78
+
79
+ TODO [Person E]:
80
+ 1. 从 base config 创建实验配置
81
+ 2. 应用 overrides
82
+ 3. 设置 experiment.name 和 output_dir
83
+ 4. 调用训练流程
84
+ 5. 在测试集上评估
85
+ 6. 保存实验结果
86
+ """
87
+ raise NotImplementedError("TODO: Person E 实现 run_single_experiment")
88
+
89
+
90
+ def main():
91
+ """
92
+ 运行所有实验。
93
+
94
+ TODO [Person E]:
95
+ 1. 遍历 EXPERIMENTS 列表
96
+ 2. 对每个实验调用 run_single_experiment
97
+ 3. 收集所有实验结果
98
+ 4. 生成对比表格
99
+ 5. 保存汇总报告
100
+ """
101
+ print("=" * 60)
102
+ print(" EasyTranslate - Experiment Runner")
103
+ print("=" * 60)
104
+
105
+ for exp in EXPERIMENTS:
106
+ print(f"\n>>> Running: {exp['name']} - {exp['description']}")
107
+ # run_single_experiment(exp)
108
+
109
+ raise NotImplementedError("TODO: Person E 实现实验运行主流程")
110
+
111
+
112
+ if __name__ == "__main__":
113
+ main()
scripts/train.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 训练入口脚本
3
+
4
+ 使用方式:
5
+ # 从头训练 Transformer
6
+ python scripts/train.py --config configs/default_config.yaml
7
+
8
+ # 微调 NLLB
9
+ python scripts/train.py --config configs/default_config.yaml model.type=finetune_nllb
10
+
11
+ # 命令行覆盖参数
12
+ python scripts/train.py --config configs/default_config.yaml training.optimizer.lr=1e-4
13
+
14
+ # 分布式训练
15
+ torchrun --nproc_per_node=4 scripts/train.py --config configs/default_config.yaml
16
+ """
17
+
18
+ import argparse
19
+ import sys
20
+ from pathlib import Path
21
+
22
+ # 将 src 目录加入 Python path
23
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
24
+
25
+
26
+ def parse_args():
27
+ parser = argparse.ArgumentParser(description="EasyTranslate Training")
28
+ parser.add_argument("--config", type=str, default="configs/default_config.yaml", help="配置文件路径")
29
+ parser.add_argument("--resume", type=str, default=None, help="检查点路径 (断点续训)")
30
+ args, unknown = parser.parse_known_args()
31
+ return args, unknown
32
+
33
+
34
+ def main():
35
+ """
36
+ 训练主流程。
37
+
38
+ TODO [整合阶段 - 所有人协作]:
39
+ 1. 解析命令行参数
40
+ 2. 加载配置 (config_from_cli)
41
+ 3. set_seed
42
+ 4. setup_logging
43
+ 5. 根据 model.type 选择训练模式:
44
+
45
+ A) model.type == "transformer_scratch":
46
+ a. 加载数据集 (load_wmt_dataset / load_opus_dataset)
47
+ b. 预处理 (preprocess_pipeline)
48
+ c. 训练分词器 (train_bpe_tokenizer) 或加载已有分词器
49
+ d. 构建 TranslationDataset + DataLoader
50
+ e. 构建 TransformerTranslationModel
51
+ f. 构建 Trainer 并开始训练
52
+
53
+ B) model.type == "finetune_nllb" / "finetune_mbart":
54
+ a. 加载预训练模型和分词器 (load_pretrained_model)
55
+ b. 配置 LoRA (setup_lora)
56
+ c. 加载数据集,使用预训练分词器处理
57
+ d. 构建 Trainer 并开始训练
58
+
59
+ 6. 训练完成后保存最终模型
60
+ 7. 在测试集上评估
61
+ """
62
+ args, cli_overrides = parse_args()
63
+
64
+ print("=" * 60)
65
+ print(" EasyTranslate - English to Chinese Translation")
66
+ print("=" * 60)
67
+
68
+ # TODO: 实现训练主流程
69
+ raise NotImplementedError(
70
+ "TODO: 整合阶段实现训练主流程\n"
71
+ "请在所有模块完成后,协作完成此脚本"
72
+ )
73
+
74
+
75
+ if __name__ == "__main__":
76
+ main()
scripts/translate.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 交互式翻译推理脚本
3
+
4
+ 使用方式:
5
+ # 命令行交互翻译
6
+ python scripts/translate.py --checkpoint checkpoints/best_model.pt
7
+
8
+ # 翻译文件
9
+ python scripts/translate.py --checkpoint checkpoints/best_model.pt --input input.txt --output output.txt
10
+
11
+ # 启动 Gradio Web UI
12
+ python scripts/translate.py --checkpoint checkpoints/best_model.pt --web
13
+ """
14
+
15
+ import argparse
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
20
+
21
+
22
+ def parse_args():
23
+ parser = argparse.ArgumentParser(description="EasyTranslate Inference")
24
+ parser.add_argument("--config", type=str, default="configs/default_config.yaml")
25
+ parser.add_argument("--checkpoint", type=str, required=True)
26
+ parser.add_argument("--input", type=str, default=None, help="输入文件路径")
27
+ parser.add_argument("--output", type=str, default=None, help="输出文件路径")
28
+ parser.add_argument("--web", action="store_true", help="启动 Gradio Web UI")
29
+ return parser.parse_args()
30
+
31
+
32
+ def interactive_translate(evaluator):
33
+ """
34
+ 命令行交互翻译。
35
+
36
+ TODO [Person D]:
37
+ 1. 循环读取用户输入
38
+ 2. 调用 evaluator.translate_single()
39
+ 3. 打印翻译结果
40
+ 4. 输入 'quit' 退出
41
+ """
42
+ raise NotImplementedError("TODO: Person D 实现 interactive_translate")
43
+
44
+
45
+ def translate_file(evaluator, input_path: str, output_path: str):
46
+ """
47
+ 文件翻译。
48
+
49
+ TODO [Person D]:
50
+ 1. 读取输入文件 (一行一句)
51
+ 2. 批量翻译
52
+ 3. 将结果写入输出文件
53
+ """
54
+ raise NotImplementedError("TODO: Person D 实现 translate_file")
55
+
56
+
57
+ def launch_web_ui(evaluator):
58
+ """
59
+ 启动 Gradio Web UI。
60
+
61
+ TODO [Person D]:
62
+ 1. 创建 Gradio Interface
63
+ 2. 输入: 英文文本框
64
+ 3. 输出: 中文翻译结果
65
+ 4. 调用 evaluator.translate_single()
66
+ """
67
+ raise NotImplementedError("TODO: Person D 实现 launch_web_ui")
68
+
69
+
70
+ def main():
71
+ args = parse_args()
72
+
73
+ print("=" * 60)
74
+ print(" EasyTranslate - Translation")
75
+ print("=" * 60)
76
+
77
+ # TODO: 加载模型、构建 evaluator,然后根据参数选择模式
78
+ raise NotImplementedError("TODO: 实现推理主流程")
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()
scripts/visualize.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 可视化与分析脚本 — Person E 负责
3
+
4
+ 功能:
5
+ 1. 训练曲线绘制 (loss, BLEU, learning rate)
6
+ 2. 实验结果对比图
7
+ 3. 注意力权重可视化
8
+ 4. 翻译样例展示
9
+ """
10
+
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
15
+
16
+
17
+ def plot_training_curves(log_dir: str, output_path: str = "outputs/training_curves.png"):
18
+ """
19
+ 绘制训练曲线。
20
+
21
+ TODO [Person E]:
22
+ 1. 从 TensorBoard 日志或 CSV 读取训练数据
23
+ 2. 绘制子图:
24
+ - Train Loss vs Steps
25
+ - Val Loss vs Steps
26
+ - BLEU vs Epochs
27
+ - Learning Rate vs Steps
28
+ 3. 保存图片
29
+ """
30
+ raise NotImplementedError("TODO: Person E 实现 plot_training_curves")
31
+
32
+
33
+ def plot_experiment_comparison(results_dir: str, output_path: str = "outputs/experiment_comparison.png"):
34
+ """
35
+ 绘制实验对比图。
36
+
37
+ TODO [Person E]:
38
+ 1. 读取所有实验的评估结果
39
+ 2. 绘制柱状图: BLEU / COMET / chrF 对比
40
+ 3. 绘制表格: 所有指标汇总
41
+ 4. 保存图片
42
+ """
43
+ raise NotImplementedError("TODO: Person E 实现 plot_experiment_comparison")
44
+
45
+
46
+ def visualize_attention(
47
+ model,
48
+ src_text: str,
49
+ tgt_text: str,
50
+ tokenizer,
51
+ output_path: str = "outputs/attention_map.png",
52
+ ):
53
+ """
54
+ 注意力权重可视化。
55
+
56
+ TODO [Person E]:
57
+ 1. 获取模型的 encoder self-attention 和 cross-attention 权重
58
+ 2. 绘制热力图 (matplotlib / seaborn)
59
+ 3. x 轴: 源语言 tokens, y 轴: 目标语言 tokens
60
+ 4. 支持多头注意力的分别可视化和平均可视化
61
+ """
62
+ raise NotImplementedError("TODO: Person E 实现 visualize_attention")
63
+
64
+
65
+ def generate_translation_examples(
66
+ evaluator,
67
+ test_pairs: list[tuple[str, str]],
68
+ output_path: str = "outputs/translation_examples.md",
69
+ ):
70
+ """
71
+ 生成翻译样例展示。
72
+
73
+ TODO [Person E]:
74
+ 1. 翻译测试样例
75
+ 2. 生成 Markdown 格式的对比表格:
76
+ | 源文 (英) | 参考翻译 (中) | 模型翻译 (中) | BLEU |
77
+ 3. 包含好的和差的翻译案例
78
+ 4. 保存为 Markdown 文件
79
+ """
80
+ raise NotImplementedError("TODO: Person E 实现 generate_translation_examples")
81
+
82
+
83
+ if __name__ == "__main__":
84
+ print("请指定要运行的可视化任务")
85
+ print(" python scripts/visualize.py --task training_curves --log_dir logs/")
86
+ print(" python scripts/visualize.py --task comparison --results_dir outputs/")
setup.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="easytranslate",
5
+ version="0.1.0",
6
+ description="Transformer-based English-to-Chinese Translation Model",
7
+ packages=find_packages(where="src"),
8
+ package_dir={"": "src"},
9
+ python_requires=">=3.10",
10
+ install_requires=[
11
+ "torch>=2.1.0",
12
+ "transformers>=4.36.0",
13
+ "datasets>=2.16.0",
14
+ "sentencepiece>=0.1.99",
15
+ "accelerate>=0.25.0",
16
+ "sacrebleu>=2.4.0",
17
+ "omegaconf>=2.3.0",
18
+ "rich>=13.0.0",
19
+ "tqdm>=4.66.0",
20
+ ],
21
+ )
src/easytranslate/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ """EasyTranslate: Transformer-based English-to-Chinese Translation."""
2
+
3
+ __version__ = "0.1.0"
src/easytranslate/evaluation/__init__.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """评估与推理模块 (Person D 负责)"""
2
+
3
+ from easytranslate.evaluation.metrics import compute_bleu, compute_comet, compute_chrf, compute_all_metrics
4
+ from easytranslate.evaluation.decoding import greedy_decode, beam_search_decode, sample_decode
5
+ from easytranslate.evaluation.evaluator import Evaluator
6
+
7
+ __all__ = [
8
+ "compute_bleu",
9
+ "compute_comet",
10
+ "compute_chrf",
11
+ "compute_all_metrics",
12
+ "greedy_decode",
13
+ "beam_search_decode",
14
+ "sample_decode",
15
+ "Evaluator",
16
+ ]
src/easytranslate/evaluation/decoding.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 解码策略模块 — Person D 负责实现
3
+
4
+ 功能要求:
5
+ 1. greedy_decode: 贪心解码
6
+ 2. beam_search_decode: 束搜索解码
7
+ 3. sample_decode: 采样解码 (temperature, top-k, top-p)
8
+
9
+ 技术要点:
10
+ - Beam Search 是翻译任务最常用的解码策略
11
+ - 需要高效处理批量解码
12
+ - 支持长度惩罚 (length penalty) 和重复惩罚 (no_repeat_ngram)
13
+ - 对于预训练模型,可以直接使用 model.generate()
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ from typing import Optional
20
+
21
+ import torch
22
+ import torch.nn as nn
23
+ import torch.nn.functional as F
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @torch.no_grad()
29
+ def greedy_decode(
30
+ model: nn.Module,
31
+ src_ids: torch.Tensor, # [B, S]
32
+ src_padding_mask: torch.BoolTensor, # [B, S]
33
+ bos_id: int,
34
+ eos_id: int,
35
+ max_len: int = 256,
36
+ ) -> torch.Tensor:
37
+ """
38
+ 贪心解码。
39
+
40
+ TODO [Person D]: 实现以下逻辑:
41
+ 1. encoder_output = model.encode(src_ids, src_padding_mask)
42
+ 2. 初始化 decoder input: [B, 1] 全为 bos_id
43
+ 3. for step in range(max_len):
44
+ a. logits = model.decode_step(decoder_input, encoder_output, src_padding_mask)
45
+ b. next_token = logits.argmax(dim=-1)
46
+ c. decoder_input = concat(decoder_input, next_token)
47
+ d. 如果所有序列都生成了 eos_id,则提前终止
48
+ 4. 返回生成的 token ids [B, T]
49
+ """
50
+ raise NotImplementedError("TODO: Person D 实现 greedy_decode")
51
+
52
+
53
+ @torch.no_grad()
54
+ def beam_search_decode(
55
+ model: nn.Module,
56
+ src_ids: torch.Tensor, # [B, S]
57
+ src_padding_mask: torch.BoolTensor,
58
+ bos_id: int,
59
+ eos_id: int,
60
+ beam_size: int = 5,
61
+ max_len: int = 256,
62
+ length_penalty: float = 1.0,
63
+ no_repeat_ngram_size: int = 0,
64
+ ) -> torch.Tensor:
65
+ """
66
+ 束搜索解码。
67
+
68
+ TODO [Person D]: 实现以下逻辑:
69
+ 1. encoder_output = model.encode(src_ids, src_padding_mask)
70
+ 2. 将 encoder_output 扩展为 beam_size 份: [B*beam, S, D]
71
+ 3. 初始化 beam:
72
+ - beam_scores: [B, beam_size] 初始为 0
73
+ - beam_tokens: [B, beam_size, 1] 初始为 bos_id
74
+ 4. for step in range(max_len):
75
+ a. 对每个 beam 计算 logits
76
+ b. log_probs = log_softmax(logits)
77
+ c. (可选) 应用 no_repeat_ngram 约束
78
+ d. scores = beam_scores + log_probs
79
+ e. 选择 top-k candidates (k = beam_size)
80
+ f. 更新 beam_tokens 和 beam_scores
81
+ g. 将已完成的 beam 移到 finished pool
82
+ 5. 对 finished beams 应用 length_penalty:
83
+ score = score / (length ^ length_penalty)
84
+ 6. 选择得分最高的序列
85
+ 7. 返回最佳翻译 [B, T]
86
+
87
+ 这是翻译任务最关键的解码算法,请仔细实现。
88
+ """
89
+ raise NotImplementedError("TODO: Person D 实现 beam_search_decode")
90
+
91
+
92
+ @torch.no_grad()
93
+ def sample_decode(
94
+ model: nn.Module,
95
+ src_ids: torch.Tensor,
96
+ src_padding_mask: torch.BoolTensor,
97
+ bos_id: int,
98
+ eos_id: int,
99
+ max_len: int = 256,
100
+ temperature: float = 1.0,
101
+ top_k: int = 0,
102
+ top_p: float = 1.0,
103
+ ) -> torch.Tensor:
104
+ """
105
+ 采样解码 (支持 temperature, top-k, top-p/nucleus sampling)。
106
+
107
+ TODO [Person D]: 实现以下逻辑:
108
+ 1. 与贪心解码类似,但每步采样而非取 argmax
109
+ 2. 应用 temperature: logits = logits / temperature
110
+ 3. 应用 top-k: 只保留概率最高的 k 个 token
111
+ 4. 应用 top-p (nucleus): 只保留累积概率达到 p 的 token
112
+ 5. 从过滤后的分布中采样: torch.multinomial
113
+ """
114
+ raise NotImplementedError("TODO: Person D 实现 sample_decode")
115
+
116
+
117
+ def _apply_no_repeat_ngram(
118
+ logits: torch.Tensor,
119
+ generated_tokens: torch.Tensor,
120
+ ngram_size: int,
121
+ ) -> torch.Tensor:
122
+ """
123
+ 防止生成重复的 n-gram。
124
+
125
+ TODO [Person D]:
126
+ 1. 从 generated_tokens 中提取所有已出现的 (ngram_size-1)-gram
127
+ 2. 对于每个可能导致重复 ngram 的 next token,将其 logits 设为 -inf
128
+ """
129
+ raise NotImplementedError("TODO: Person D 实现 _apply_no_repeat_ngram")
src/easytranslate/evaluation/evaluator.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 评估器模块 — Person D 负责实现
3
+
4
+ 功能要求:
5
+ 将解码和评估指标整合为统一的评估接口。
6
+
7
+ 使用方法:
8
+ evaluator = Evaluator(model, tokenizer, config)
9
+ results = evaluator.evaluate(test_loader)
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ from typing import Optional
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+ from torch.utils.data import DataLoader
20
+ from tqdm import tqdm
21
+
22
+ from easytranslate.evaluation.metrics import compute_all_metrics
23
+ from easytranslate.evaluation.decoding import greedy_decode, beam_search_decode, sample_decode
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class Evaluator:
29
+ """
30
+ 翻译模型评估器。
31
+
32
+ TODO [Person D]: 实现以下方法。
33
+ """
34
+
35
+ def __init__(self, model: nn.Module, tokenizer, config: dict):
36
+ """
37
+ TODO [Person D]:
38
+ 1. 保存 model, tokenizer, config
39
+ 2. 从 config 读取解码策略和评估指标配置
40
+ 3. 根据策略选择解码函数
41
+ """
42
+ raise NotImplementedError("TODO: Person D 实现 Evaluator.__init__")
43
+
44
+ def evaluate(
45
+ self,
46
+ dataloader: DataLoader,
47
+ src_texts: Optional[list[str]] = None,
48
+ ref_texts: Optional[list[str]] = None,
49
+ ) -> dict:
50
+ """
51
+ 在给定数据上进行评估。
52
+
53
+ TODO [Person D]: 实现以下逻辑:
54
+ 1. model.eval()
55
+ 2. 遍历 dataloader,使用选定的解码策略生成翻译
56
+ 3. 将生成的 token ids 解码为文本
57
+ 4. 调用 compute_all_metrics 计算指标
58
+ 5. 返回评估结果 dict
59
+ """
60
+ raise NotImplementedError("TODO: Person D 实现 evaluate")
61
+
62
+ def translate(self, texts: list[str]) -> list[str]:
63
+ """
64
+ 翻译一批文本。
65
+
66
+ TODO [Person D]:
67
+ 1. tokenize 输入文本
68
+ 2. 调用解码函数生成翻译
69
+ 3. 解码为文本
70
+ 4. 返回翻译结果列表
71
+ """
72
+ raise NotImplementedError("TODO: Person D 实现 translate")
73
+
74
+ def translate_single(self, text: str) -> str:
75
+ """翻译单条文本。"""
76
+ return self.translate([text])[0]
src/easytranslate/evaluation/metrics.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 评估指标模块 — Person D 负责实现
3
+
4
+ 功能要求:
5
+ 1. compute_bleu: 计算 SacreBLEU 分数
6
+ 2. compute_comet: 计算 COMET 分数 (神经网络评估指标)
7
+ 3. compute_chrf: 计算 chrF++ 分数
8
+ 4. compute_ter: 计算 TER (Translation Edit Rate)
9
+ 5. compute_all_metrics: 计算所有指标
10
+
11
+ 技术要点:
12
+ - SacreBLEU: 标准化的 BLEU 实现,结果可复现
13
+ - COMET: 基于预训练语言模型的评估指标,与人类评价相关性最高
14
+ - chrF++: 基于字符 n-gram 的 F-score,对中文尤其有用
15
+ - TER: 编辑距离,衡量翻译后编辑量
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import logging
21
+ from typing import Optional
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ def compute_bleu(
27
+ hypotheses: list[str],
28
+ references: list[str],
29
+ tokenize: str = "zh",
30
+ ) -> dict:
31
+ """
32
+ 计算 SacreBLEU 分数。
33
+
34
+ TODO [Person D]: 实现以下逻辑:
35
+ 1. 使用 sacrebleu.corpus_bleu(hypotheses, [references], tokenize=tokenize)
36
+ 2. tokenize="zh" 对中文进行字符级别分词
37
+ 3. 返回 {"bleu": score, "bleu_1": ..., "bleu_2": ..., "bleu_3": ..., "bleu_4": ..., "bp": ...}
38
+
39
+ 注意: references 需要包装为 list of list (支持多参考)
40
+ """
41
+ raise NotImplementedError("TODO: Person D 实现 compute_bleu")
42
+
43
+
44
+ def compute_comet(
45
+ sources: list[str],
46
+ hypotheses: list[str],
47
+ references: list[str],
48
+ model_name: str = "Unbabel/wmt22-comet-da",
49
+ batch_size: int = 16,
50
+ gpus: int = 1,
51
+ ) -> dict:
52
+ """
53
+ 计算 COMET 分数。
54
+
55
+ TODO [Person D]: 实现以下逻辑:
56
+ 1. 加载 COMET 模型: comet.download_model(model_name)
57
+ 2. 构建输入数据: [{"src": s, "mt": h, "ref": r} for s, h, r in zip(...)]
58
+ 3. 调用 model.predict(data, batch_size, gpus)
59
+ 4. 返回 {"comet": system_score, "comet_scores": segment_scores}
60
+
61
+ COMET 需要源语言、翻译结果和参考翻译三者。
62
+ """
63
+ raise NotImplementedError("TODO: Person D 实现 compute_comet")
64
+
65
+
66
+ def compute_chrf(
67
+ hypotheses: list[str],
68
+ references: list[str],
69
+ ) -> dict:
70
+ """
71
+ 计算 chrF++ 分数。
72
+
73
+ TODO [Person D]:
74
+ 1. 使用 sacrebleu.corpus_chrf(hypotheses, [references])
75
+ 2. 返回 {"chrf": score}
76
+ """
77
+ raise NotImplementedError("TODO: Person D 实现 compute_chrf")
78
+
79
+
80
+ def compute_ter(
81
+ hypotheses: list[str],
82
+ references: list[str],
83
+ ) -> dict:
84
+ """
85
+ 计算 TER 分数。
86
+
87
+ TODO [Person D]:
88
+ 1. 使用 sacrebleu.corpus_ter(hypotheses, [references])
89
+ 2. 返回 {"ter": score}
90
+ """
91
+ raise NotImplementedError("TODO: Person D 实现 compute_ter")
92
+
93
+
94
+ def compute_all_metrics(
95
+ sources: list[str],
96
+ hypotheses: list[str],
97
+ references: list[str],
98
+ metrics: list[str] = ["bleu", "comet", "chrf", "ter"],
99
+ ) -> dict:
100
+ """
101
+ 计算所有指定的评估指标。
102
+
103
+ TODO [Person D]:
104
+ 1. 遍历 metrics 列表
105
+ 2. 调用对应的计算函数
106
+ 3. 合并结果并返回
107
+ 4. 记录每个指标的计算时间
108
+ """
109
+ raise NotImplementedError("TODO: Person D 实现 compute_all_metrics")
src/easytranslate/model/__init__.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """模型架构模块 (Person B 负责)"""
2
+
3
+ from easytranslate.model.transformer import TransformerTranslationModel
4
+ from easytranslate.model.encoder import TransformerEncoder
5
+ from easytranslate.model.decoder import TransformerDecoder
6
+ from easytranslate.model.attention import MultiHeadAttention, FlashMultiHeadAttention
7
+ from easytranslate.model.positional import SinusoidalPositionalEncoding, RotaryPositionalEmbedding
8
+ from easytranslate.model.finetune import load_pretrained_model, setup_lora
9
+
10
+ __all__ = [
11
+ "TransformerTranslationModel",
12
+ "TransformerEncoder",
13
+ "TransformerDecoder",
14
+ "MultiHeadAttention",
15
+ "FlashMultiHeadAttention",
16
+ "SinusoidalPositionalEncoding",
17
+ "RotaryPositionalEmbedding",
18
+ "load_pretrained_model",
19
+ "setup_lora",
20
+ ]
src/easytranslate/model/attention.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 注意力机制模块 — Person B 负责实现
3
+
4
+ 包含:
5
+ 1. MultiHeadAttention: 标准多头注意力 (支持 RoPE)
6
+ 2. FlashMultiHeadAttention: Flash Attention 2 加速版本
7
+
8
+ 技术要点:
9
+ - Scaled Dot-Product Attention
10
+ - 支持 key_padding_mask 和 attn_mask
11
+ - Flash Attention 2 使用 torch.nn.functional.scaled_dot_product_attention
12
+ - RoPE 旋转位置编码集成
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import math
18
+ from typing import Optional
19
+
20
+ import torch
21
+ import torch.nn as nn
22
+ import torch.nn.functional as F
23
+
24
+
25
+ class MultiHeadAttention(nn.Module):
26
+ """
27
+ 标准多头注意力机制。
28
+
29
+ TODO [Person B]: 实现以下内容:
30
+
31
+ __init__:
32
+ 1. Q, K, V 线性投影: nn.Linear(d_model, d_model)
33
+ 2. 输出投影: nn.Linear(d_model, d_model)
34
+ 3. Dropout
35
+
36
+ forward(query, key, value, key_padding_mask=None, attn_mask=None):
37
+ 1. 线性投影 Q, K, V
38
+ 2. reshape 为 [B, nhead, L, d_k]
39
+ 3. (可选) 应用 RoPE 旋转位置编码
40
+ 4. 计算 attention scores: QK^T / sqrt(d_k)
41
+ 5. 应用 masks (padding mask + causal mask)
42
+ 6. Softmax + Dropout
43
+ 7. 加权求和 V
44
+ 8. reshape 回 [B, L, d_model]
45
+ 9. 输出投影
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ d_model: int = 512,
51
+ nhead: int = 8,
52
+ dropout: float = 0.1,
53
+ use_rotary_embedding: bool = False,
54
+ ):
55
+ super().__init__()
56
+ assert d_model % nhead == 0, "d_model 必须能被 nhead 整除"
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,
65
+ query: torch.Tensor, # [B, L_q, D]
66
+ key: torch.Tensor, # [B, L_k, D]
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):
75
+ """
76
+ Flash Attention 2 加速的多头注意力。
77
+
78
+ TODO [Person B]: 使用 PyTorch 2.0+ 的 F.scaled_dot_product_attention 实现:
79
+ 1. 与 MultiHeadAttention 结构相同
80
+ 2. 在 forward 中使用 F.scaled_dot_product_attention(Q, K, V, attn_mask, dropout, is_causal)
81
+ 3. 会自动选择最优的 attention kernel (Flash Attention / Memory-Efficient Attention)
82
+
83
+ 注意:
84
+ - 需要 PyTorch >= 2.0
85
+ - is_causal=True 时自动生成因果掩码,不需要手动传入 attn_mask
86
+ """
87
+
88
+ def __init__(
89
+ self,
90
+ d_model: int = 512,
91
+ nhead: int = 8,
92
+ dropout: float = 0.1,
93
+ use_rotary_embedding: bool = False,
94
+ ):
95
+ super().__init__()
96
+ raise NotImplementedError("TODO: Person B 实现 FlashMultiHeadAttention.__init__")
97
+
98
+ def forward(
99
+ self,
100
+ query: torch.Tensor,
101
+ key: torch.Tensor,
102
+ value: torch.Tensor,
103
+ key_padding_mask: Optional[torch.BoolTensor] = None,
104
+ is_causal: bool = False,
105
+ ) -> torch.Tensor:
106
+ raise NotImplementedError("TODO: Person B 实现 FlashMultiHeadAttention.forward")
src/easytranslate/model/decoder.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Transformer Decoder 模块 — Person B 负责实现
3
+
4
+ 包含:
5
+ - TransformerDecoderLayer: 单层解码器
6
+ - TransformerDecoder: 多层解码器堆叠
7
+
8
+ 架构 (Pre-LayerNorm):
9
+ x → LN → Masked Self-Attention → Residual
10
+ → LN → Cross-Attention → Residual
11
+ → LN → FFN → Residual
12
+ """
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__(
34
+ self,
35
+ d_model: int = 512,
36
+ nhead: int = 8,
37
+ dim_feedforward: int = 2048,
38
+ dropout: float = 0.1,
39
+ activation: str = "gelu",
40
+ use_flash_attention: bool = True,
41
+ use_rotary_embedding: bool = True,
42
+ pre_norm: bool = True,
43
+ ):
44
+ super().__init__()
45
+ raise NotImplementedError("TODO: Person B 实现 TransformerDecoderLayer.__init__")
46
+
47
+ def forward(
48
+ self,
49
+ tgt: torch.Tensor, # [B, T, D]
50
+ memory: torch.Tensor, # [B, S, D] (encoder output)
51
+ tgt_mask: Optional[torch.Tensor] = None, # [T, T] causal mask
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,
80
+ tgt: torch.Tensor,
81
+ memory: torch.Tensor,
82
+ tgt_mask: Optional[torch.Tensor] = None,
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")
src/easytranslate/model/encoder.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Transformer Encoder 模块 — Person B 负责实现
3
+
4
+ 包含:
5
+ - TransformerEncoderLayer: 单层编码器
6
+ - TransformerEncoder: 多层编码器堆叠
7
+
8
+ 架构 (Pre-LayerNorm):
9
+ x → LayerNorm → MultiHeadAttention → Residual → LayerNorm → FFN → Residual
10
+ """
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__(
34
+ self,
35
+ d_model: int = 512,
36
+ nhead: int = 8,
37
+ dim_feedforward: int = 2048,
38
+ dropout: float = 0.1,
39
+ activation: str = "gelu",
40
+ use_flash_attention: bool = True,
41
+ use_rotary_embedding: bool = True,
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")
src/easytranslate/model/finetune.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 预训练模型微调模块 — Person B 负责实现
3
+
4
+ 功能要求:
5
+ 1. load_pretrained_model: 加载 NLLB / mBART 等预训练翻译模型
6
+ 2. setup_lora: 配置 LoRA 参数高效微调
7
+
8
+ 技术要点:
9
+ - 使用 HuggingFace transformers 加载预训练模型
10
+ - 使用 PEFT (Parameter-Efficient Fine-Tuning) 库配置 LoRA
11
+ - 支持 NLLB-200 (Meta, 200种语言) 和 mBART-50 (Meta, 50种语言)
12
+ - 冻结预训练参数,只训练 LoRA adapter
13
+
14
+ 这是实验对比的关键部分:
15
+ - 从头训练 vs 预训练微调
16
+ - Full fine-tuning vs LoRA
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ from typing import Optional
23
+
24
+ import torch
25
+ import torch.nn as nn
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ def load_pretrained_model(
31
+ model_name: str = "facebook/nllb-200-distilled-600M",
32
+ src_lang: str = "eng_Latn",
33
+ tgt_lang: str = "zho_Hans",
34
+ device: str = "cuda",
35
+ ) -> tuple:
36
+ """
37
+ 加载预训练翻译模型和分词器。
38
+
39
+ TODO [Person B]: 实现以下逻辑:
40
+ 1. 使用 AutoModelForSeq2SeqLM.from_pretrained(model_name) 加载模型
41
+ 2. 使用 AutoTokenizer.from_pretrained(model_name) 加载分词器
42
+ 3. 设置源语言和目标语言
43
+ 4. 将模型移到指定设备
44
+
45
+ 支持的模型:
46
+ - facebook/nllb-200-distilled-600M (推荐,轻量级)
47
+ - facebook/nllb-200-1.3B (中等规模)
48
+ - facebook/mbart-large-50-many-to-many-mmt
49
+
50
+ Returns:
51
+ (model, tokenizer)
52
+ """
53
+ raise NotImplementedError("TODO: Person B 实现 load_pretrained_model")
54
+
55
+
56
+ def setup_lora(
57
+ model: nn.Module,
58
+ r: int = 16,
59
+ alpha: int = 32,
60
+ dropout: float = 0.05,
61
+ target_modules: Optional[list[str]] = None,
62
+ ) -> nn.Module:
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")
src/easytranslate/model/positional.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 位置编码模块 — Person B 负责实现
3
+
4
+ 包含:
5
+ 1. SinusoidalPositionalEncoding: 经典正弦余弦位置编码
6
+ 2. RotaryPositionalEmbedding (RoPE): 旋转位置编码
7
+
8
+ RoPE 是当前最前沿的位置编码方案,被 LLaMA, GPT-NeoX 等大模型广泛采用。
9
+ 优势: 更好地捕获相对位置信息,支持长度外推。
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import math
15
+
16
+ import torch
17
+ import torch.nn as nn
18
+
19
+
20
+ class SinusoidalPositionalEncoding(nn.Module):
21
+ """
22
+ 经典正弦余弦位置编码 (Vaswani et al., 2017)。
23
+
24
+ PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
25
+ PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
26
+
27
+ TODO [Person B]: 实现以下内容:
28
+ 1. 在 __init__ 中预计算位置编码矩阵 [max_seq_len, d_model]
29
+ 2. 注册为 buffer (不参与梯度更新)
30
+ 3. forward(x): 返回 x + pe[:, :x.size(1), :]
31
+ """
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
+ """
39
+ Args:
40
+ x: [B, L, D]
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):
48
+ """
49
+ 旋转位置编码 (RoPE) — Su et al., 2021
50
+
51
+ 核心思想: 通过旋转变换将位置信息编码到 Q, K 向量中。
52
+ q' = q * cos(θ) + rotate_half(q) * sin(θ)
53
+ k' = k * cos(θ) + rotate_half(k) * sin(θ)
54
+
55
+ TODO [Person B]: 实现以下内容:
56
+
57
+ __init__:
58
+ 1. 计算频率: inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2) / dim))
59
+ 2. 注册为 buffer
60
+
61
+ _compute_rope(seq_len):
62
+ 1. 计算 position indices: t = [0, 1, ..., seq_len-1]
63
+ 2. 计算 freqs = torch.outer(t, inv_freq)
64
+ 3. 构建 cos_cached, sin_cached
65
+
66
+ apply_rotary_pos_emb(q, k):
67
+ 1. 对 q 和 k 应用旋转变换
68
+ 2. 返回旋转后的 q', k'
69
+
70
+ 参考: https://arxiv.org/abs/2104.09864
71
+ """
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:
82
+ """将 x 的后半部分取反并与前半部分交换。"""
83
+ x1, x2 = x.chunk(2, dim=-1)
84
+ return torch.cat((-x2, x1), dim=-1)
85
+
86
+ def apply_rotary_pos_emb(
87
+ self,
88
+ q: torch.Tensor, # [B, nhead, L, d_k]
89
+ k: torch.Tensor, # [B, nhead, L, d_k]
90
+ ) -> tuple[torch.Tensor, torch.Tensor]:
91
+ """
92
+ 对 Q, K 应用 RoPE。
93
+
94
+ Returns:
95
+ (q_rotated, k_rotated)
96
+ """
97
+ raise NotImplementedError("TODO: Person B 实现 apply_rotary_pos_emb")
src/easytranslate/model/transformer.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Transformer 翻译模型主体 — Person B 负责实现
3
+
4
+ 这是整个模型的核心文件,定义 Encoder-Decoder 架构。
5
+
6
+ 架构设计:
7
+ - Pre-LayerNorm Transformer (训练更稳定)
8
+ - 可选 Flash Attention 2 (加速注意力计算)
9
+ - 可选 RoPE 旋转位置编码 (替代传统正弦位置编码)
10
+ - 共享 Embedding 权重 (可选)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ import math
17
+ from typing import Optional
18
+
19
+ 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__)
28
+
29
+
30
+ class TransformerTranslationModel(nn.Module):
31
+ """
32
+ 完整的 Transformer 英中翻译模型。
33
+
34
+ 架构:
35
+ Source Embedding + Positional Encoding
36
+ → Transformer Encoder (N layers)
37
+ → Transformer Decoder (N layers)
38
+ → Linear Projection → Softmax
39
+
40
+ TODO [Person B]: 实现以下内容:
41
+
42
+ __init__:
43
+ 1. 源语言 Embedding: nn.Embedding(src_vocab_size, d_model)
44
+ 2. 目标语言 Embedding: nn.Embedding(tgt_vocab_size, d_model)
45
+ 3. 位置编码: SinusoidalPositionalEncoding 或 RotaryPositionalEmbedding
46
+ 4. Transformer Encoder
47
+ 5. Transformer Decoder
48
+ 6. 输出投影层: nn.Linear(d_model, tgt_vocab_size)
49
+ 7. (可选) 共享 target embedding 和 output projection 的权重
50
+ 8. 调用 _init_weights() 初始化参数
51
+
52
+ forward:
53
+ 1. 源语言 embedding + 位置编码 → encoder_input
54
+ 2. 目标语言 embedding + 位置编码 → decoder_input
55
+ 3. 生成 masks (src_key_padding_mask, tgt_key_padding_mask, tgt_mask)
56
+ 4. encoder_output = encoder(encoder_input, src_key_padding_mask)
57
+ 5. decoder_output = decoder(decoder_input, encoder_output, masks...)
58
+ 6. logits = output_projection(decoder_output)
59
+ 7. 返回 logits [B, T, tgt_vocab_size]
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ src_vocab_size: int,
65
+ tgt_vocab_size: int,
66
+ d_model: int = 512,
67
+ nhead: int = 8,
68
+ num_encoder_layers: int = 6,
69
+ num_decoder_layers: int = 6,
70
+ dim_feedforward: int = 2048,
71
+ dropout: float = 0.1,
72
+ activation: str = "gelu",
73
+ max_seq_len: int = 512,
74
+ use_flash_attention: bool = True,
75
+ use_rotary_embedding: bool = True,
76
+ pre_norm: bool = True,
77
+ pad_id: int = 0,
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,
111
+ src_ids: torch.Tensor, # [B, S]
112
+ tgt_input_ids: torch.Tensor, # [B, T]
113
+ src_padding_mask: Optional[torch.BoolTensor] = None, # [B, S]
114
+ tgt_padding_mask: Optional[torch.BoolTensor] = None, # [B, T]
115
+ ) -> torch.Tensor:
116
+ """
117
+ 前向传播。
118
+
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(
138
+ self,
139
+ tgt_input_ids: torch.Tensor,
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
+ """返回可训练参数数量。"""
157
+ return sum(p.numel() for p in self.parameters() if p.requires_grad)
src/easytranslate/training/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """训练模块 (Person C 负责)"""
2
+
3
+ from easytranslate.training.trainer import Trainer
4
+ from easytranslate.training.optimizer import build_optimizer, build_scheduler
5
+ from easytranslate.training.loss import LabelSmoothedCrossEntropyLoss
6
+
7
+ __all__ = [
8
+ "Trainer",
9
+ "build_optimizer",
10
+ "build_scheduler",
11
+ "LabelSmoothedCrossEntropyLoss",
12
+ ]
src/easytranslate/training/loss.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 损失函数模块 — Person C 负责实现
3
+
4
+ 功能要求:
5
+ 1. LabelSmoothedCrossEntropyLoss: 带标签平滑的交叉熵损失
6
+
7
+ 技术要点:
8
+ - 标签平滑 (Label Smoothing) 是 Transformer 训练的标准技巧
9
+ - smoothing=0.1 意味着将 10% 的概率均匀分配给非目标类
10
+ - 需要忽略 padding token 的损失
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import torch
16
+ import torch.nn as nn
17
+ import torch.nn.functional as F
18
+
19
+
20
+ class LabelSmoothedCrossEntropyLoss(nn.Module):
21
+ """
22
+ 带标签平滑的交叉熵损失。
23
+
24
+ TODO [Person C]: 实现以下逻辑:
25
+
26
+ __init__:
27
+ 1. 保存 smoothing 系数和 pad_id
28
+
29
+ forward(logits, targets):
30
+ 1. logits: [B, T, V] — 模型输出
31
+ 2. targets: [B, T] — 目标 token ids
32
+ 3. 展平为 [B*T, V] 和 [B*T]
33
+ 4. 创建 smooth label distribution:
34
+ - target token 概率 = 1 - smoothing
35
+ - 其他 token 概率 = smoothing / (V - 1)
36
+ 5. 计算 KL 散度作为损失
37
+ 6. 忽略 pad_id 位置的损失
38
+ 7. 返回平均损失
39
+
40
+ 参考: "Rethinking the Inception Architecture for Computer Vision" (Szegedy et al.)
41
+ """
42
+
43
+ def __init__(self, smoothing: float = 0.1, pad_id: int = 0):
44
+ super().__init__()
45
+ self.smoothing = smoothing
46
+ self.pad_id = pad_id
47
+ raise NotImplementedError("TODO: Person C 实现 LabelSmoothedCrossEntropyLoss.__init__")
48
+
49
+ def forward(self, logits: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
50
+ """
51
+ Args:
52
+ logits: [B, T, V]
53
+ targets: [B, T]
54
+ Returns:
55
+ loss: scalar
56
+ """
57
+ raise NotImplementedError("TODO: Person C 实现 LabelSmoothedCrossEntropyLoss.forward")
src/easytranslate/training/optimizer.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 优化器与学习率调度器 — Person C 负责实现
3
+
4
+ 功能要求:
5
+ 1. build_optimizer: 根据配置创建优化器
6
+ 2. build_scheduler: 根据配置创建学习率调度器
7
+ 3. InverseSqrtScheduler: 自定义 inverse square root 调度器
8
+
9
+ 技术要点:
10
+ - AdamW 是 Transformer 训练的标准优化器
11
+ - Cosine with warmup 是目前最流行的调度策略
12
+ - Inverse sqrt 是经典 Transformer 论文使用的调度策略
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import math
18
+ from typing import Optional
19
+
20
+ import torch
21
+ from torch.optim import Adam, AdamW
22
+ from torch.optim.lr_scheduler import LambdaLR
23
+
24
+
25
+ def build_optimizer(model, config: dict) -> torch.optim.Optimizer:
26
+ """
27
+ 根据配置创建优化器。
28
+
29
+ TODO [Person C]: 实现以下逻辑:
30
+ 1. 从 config 中读取 optimizer type, lr, weight_decay, betas, eps
31
+ 2. 根据 type 创建 Adam / AdamW / Adafactor
32
+ 3. (可选) 对不同参数组设置不同学习率:
33
+ - embedding 层可以用较小的 lr
34
+ - LayerNorm 的 bias 不加 weight_decay
35
+ """
36
+ raise NotImplementedError("TODO: Person C 实现 build_optimizer")
37
+
38
+
39
+ def build_scheduler(
40
+ optimizer: torch.optim.Optimizer,
41
+ config: dict,
42
+ num_training_steps: Optional[int] = None,
43
+ ) -> torch.optim.lr_scheduler._LRScheduler:
44
+ """
45
+ 根据配置创建学习率调度器。
46
+
47
+ TODO [Person C]: 实现以下逻辑:
48
+ 1. 从 config 中读取 scheduler type, warmup_steps, min_lr
49
+ 2. type == "cosine_with_warmup":
50
+ 使用 get_cosine_schedule_with_warmup (transformers 库)
51
+ 3. type == "inverse_sqrt":
52
+ 实现经典的 lr = d_model^(-0.5) * min(step^(-0.5), step * warmup^(-1.5))
53
+ 4. type == "linear":
54
+ 使用 get_linear_schedule_with_warmup
55
+
56
+ 参考: Attention Is All You Need, Section 5.3
57
+ """
58
+ raise NotImplementedError("TODO: Person C 实现 build_scheduler")
59
+
60
+
61
+ class InverseSqrtScheduler(LambdaLR):
62
+ """
63
+ Inverse Square Root 学习率调度器。
64
+
65
+ lr = base_lr * min(step^{-0.5}, step * warmup_steps^{-1.5})
66
+
67
+ 这是原始 Transformer 论文使用的调度策略。
68
+
69
+ TODO [Person C]:
70
+ 1. 实现 lr_lambda 函数
71
+ 2. warmup 阶段线性增长
72
+ 3. warmup 后按 step^{-0.5} 衰减
73
+ """
74
+
75
+ def __init__(self, optimizer, warmup_steps: int = 4000):
76
+ raise NotImplementedError("TODO: Person C 实现 InverseSqrtScheduler.__init__")
src/easytranslate/training/trainer.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 训练器模块 — Person C 负责实现
3
+
4
+ 这是训练的核心控制器,负责:
5
+ 1. 训练循环 (training loop)
6
+ 2. 验证循环 (validation loop)
7
+ 3. 检查点保存/加载
8
+ 4. 日志记录
9
+ 5. 分布式训练支持
10
+ 6. 混合精度训练
11
+ 7. 梯度累积
12
+ 8. 早停
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ import os
19
+ import time
20
+ from pathlib import Path
21
+ from typing import Optional
22
+
23
+ import torch
24
+ import torch.nn as nn
25
+ from torch.utils.data import DataLoader
26
+ from tqdm import tqdm
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ class Trainer:
32
+ """
33
+ 翻译模型训练器。
34
+
35
+ TODO [Person C]: 实现以下所有方法。
36
+
37
+ 使用方法:
38
+ trainer = Trainer(model, train_loader, val_loader, config)
39
+ trainer.train()
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ model: nn.Module,
45
+ train_loader: DataLoader,
46
+ val_loader: DataLoader,
47
+ config: dict,
48
+ optimizer=None,
49
+ scheduler=None,
50
+ criterion=None,
51
+ evaluator=None,
52
+ ):
53
+ """
54
+ TODO [Person C]: 初始化训练器:
55
+ 1. 保存模型、数据加载器、配置
56
+ 2. 如果 optimizer 为 None,调用 build_optimizer 创建
57
+ 3. 如果 scheduler 为 None,调用 build_scheduler 创建
58
+ 4. 如果 criterion 为 None,创建 LabelSmoothedCrossEntropyLoss
59
+ 5. 设置混合精度: torch.amp.GradScaler (如果配置了 fp16/bf16)
60
+ 6. 设置分布式训练: 根据配置初始化 DDP/FSDP/DeepSpeed
61
+ 7. 初始化日志记录器 (TensorBoard / WandB)
62
+ 8. 初始化训练状态 (epoch, step, best_metric)
63
+ """
64
+ self.model = model
65
+ self.train_loader = train_loader
66
+ self.val_loader = val_loader
67
+ self.config = config
68
+ raise NotImplementedError("TODO: Person C 实现 Trainer.__init__")
69
+
70
+ def train(self):
71
+ """
72
+ 主训练循环。
73
+
74
+ TODO [Person C]: 实现以下逻辑:
75
+ 1. for epoch in range(start_epoch, num_epochs):
76
+ 2. train_loss = self._train_one_epoch(epoch)
77
+ 3. val_metrics = self._validate(epoch)
78
+ 4. self._log_metrics(epoch, train_loss, val_metrics)
79
+ 5. self._save_checkpoint(epoch, val_metrics)
80
+ 6. if self._should_early_stop(val_metrics): break
81
+ 7. self._log_final_results()
82
+ """
83
+ raise NotImplementedError("TODO: Person C 实现 train")
84
+
85
+ def _train_one_epoch(self, epoch: int) -> float:
86
+ """
87
+ 训练一个 epoch。
88
+
89
+ TODO [Person C]: 实现以下逻辑:
90
+ 1. model.train()
91
+ 2. 遍历 train_loader:
92
+ a. 将 batch 移到设备
93
+ b. 混合精度上下文: with torch.amp.autocast('cuda'):
94
+ c. logits = model(src_ids, tgt_input_ids, masks...)
95
+ d. loss = criterion(logits, labels)
96
+ e. loss = loss / gradient_accumulation_steps
97
+ f. scaler.scale(loss).backward()
98
+ g. 每 gradient_accumulation_steps 步:
99
+ - scaler.unscale_(optimizer)
100
+ - torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
101
+ - scaler.step(optimizer)
102
+ - scaler.update()
103
+ - scheduler.step()
104
+ - optimizer.zero_grad()
105
+ h. 记录 loss, lr 等指标
106
+ 3. 返回平均训练 loss
107
+ """
108
+ raise NotImplementedError("TODO: Person C 实现 _train_one_epoch")
109
+
110
+ def _validate(self, epoch: int) -> dict:
111
+ """
112
+ 验证。
113
+
114
+ TODO [Person C]: 实现以下逻辑:
115
+ 1. model.eval()
116
+ 2. with torch.no_grad():
117
+ 3. 遍历 val_loader,计算 loss
118
+ 4. 每 N 步调用 evaluator 计算 BLEU 等指标
119
+ 5. 返回 {"val_loss": ..., "bleu": ..., "comet": ...}
120
+ """
121
+ raise NotImplementedError("TODO: Person C 实现 _validate")
122
+
123
+ def _save_checkpoint(self, epoch: int, metrics: dict):
124
+ """
125
+ 保存检查点。
126
+
127
+ TODO [Person C]: 实现以下逻辑:
128
+ 1. 构建 checkpoint dict:
129
+ {
130
+ "epoch": epoch,
131
+ "step": self.global_step,
132
+ "model_state_dict": model.state_dict(),
133
+ "optimizer_state_dict": optimizer.state_dict(),
134
+ "scheduler_state_dict": scheduler.state_dict(),
135
+ "metrics": metrics,
136
+ "config": config,
137
+ }
138
+ 2. 保存到 checkpoint_dir/checkpoint_epoch_{epoch}.pt
139
+ 3. 如果是最佳模型,额外保存为 best_model.pt
140
+ 4. 清理旧的检查点 (保留最近 max_checkpoints 个)
141
+ """
142
+ raise NotImplementedError("TODO: Person C 实现 _save_checkpoint")
143
+
144
+ def _load_checkpoint(self, checkpoint_path: str):
145
+ """
146
+ 加载检查点继续训练。
147
+
148
+ TODO [Person C]:
149
+ 1. torch.load(checkpoint_path)
150
+ 2. 恢复 model, optimizer, scheduler 状态
151
+ 3. 恢复 epoch, step 计数器
152
+ """
153
+ raise NotImplementedError("TODO: Person C ��现 _load_checkpoint")
154
+
155
+ def _should_early_stop(self, metrics: dict) -> bool:
156
+ """
157
+ 判断是否应该早停。
158
+
159
+ TODO [Person C]:
160
+ 1. 比较当前指标与最佳指标
161
+ 2. 如果连续 patience 个 epoch 没有改善,返回 True
162
+ """
163
+ raise NotImplementedError("TODO: Person C 实现 _should_early_stop")
164
+
165
+ def _log_metrics(self, epoch: int, train_loss: float, val_metrics: dict):
166
+ """
167
+ 记录训练指标到 TensorBoard / WandB。
168
+
169
+ TODO [Person C]:
170
+ 1. 使用 self.logger 记录 train_loss, val_loss, bleu, lr 等
171
+ 2. 打印到控制台 (使用 rich 库的 table 格式)
172
+ """
173
+ raise NotImplementedError("TODO: Person C 实现 _log_metrics")
174
+
175
+ def _setup_distributed(self):
176
+ """
177
+ 设置分布式训练。
178
+
179
+ TODO [Person C]: 根据 config["training"]["distributed"]["strategy"]:
180
+ 1. "ddp": 使用 torch.nn.parallel.DistributedDataParallel
181
+ 2. "fsdp": 使用 torch.distributed.fsdp.FullyShardedDataParallel
182
+ 3. "deepspeed": 使用 deepspeed.initialize()
183
+
184
+ 也可以使用 HuggingFace Accelerate 统一处理。
185
+ """
186
+ raise NotImplementedError("TODO: Person C 实现 _setup_distributed")
src/easytranslate/utils/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ """工具模块"""
2
+
3
+ from easytranslate.utils.config import load_config, merge_configs
4
+ from easytranslate.utils.seed import set_seed
5
+ from easytranslate.utils.logging import setup_logging
6
+
7
+ __all__ = ["load_config", "merge_configs", "set_seed", "setup_logging"]
src/easytranslate/utils/config.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 配置管理模块 — 公共模块 (Person E 可以先完成)
3
+
4
+ 功能: 加载 YAML 配置,支持命令行覆盖和多配置合并。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from pathlib import Path
11
+ from typing import Any, Optional
12
+
13
+ from omegaconf import OmegaConf, DictConfig
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def load_config(config_path: str | Path) -> DictConfig:
19
+ """
20
+ 加载 YAML 配置文件。
21
+
22
+ TODO [Person E]: 实现以下逻辑:
23
+ 1. 使用 OmegaConf.load(config_path) 加载配置
24
+ 2. 验证配置完整性
25
+ 3. 返回 DictConfig 对象
26
+ """
27
+ raise NotImplementedError("TODO: Person E 实现 load_config")
28
+
29
+
30
+ def merge_configs(base_config: DictConfig, override_config: DictConfig) -> DictConfig:
31
+ """
32
+ 合并配置 (override 覆盖 base)。
33
+
34
+ TODO [Person E]:
35
+ 使用 OmegaConf.merge(base_config, override_config)
36
+ """
37
+ raise NotImplementedError("TODO: Person E 实现 merge_configs")
38
+
39
+
40
+ def config_from_cli(config_path: str, cli_args: list[str]) -> DictConfig:
41
+ """
42
+ 从配置文件 + 命令行参数构建最终配置。
43
+
44
+ TODO [Person E]:
45
+ 1. 加载 config_path
46
+ 2. 使用 OmegaConf.from_cli(cli_args) 解析命令行参数
47
+ 3. 合并并返回
48
+
49
+ 使用方式: python train.py --config configs/default.yaml training.lr=1e-4 model.nhead=16
50
+ """
51
+ raise NotImplementedError("TODO: Person E 实现 config_from_cli")
src/easytranslate/utils/logging.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 日志管理模块 — 公共模块
3
+ """
4
+
5
+ import logging
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+
11
+ def setup_logging(
12
+ log_dir: Optional[str] = None,
13
+ level: int = logging.INFO,
14
+ log_file: Optional[str] = None,
15
+ ):
16
+ """
17
+ 配置日志系统。
18
+
19
+ TODO [Person E]: 实现以下逻辑:
20
+ 1. 创建 root logger
21
+ 2. 设置 StreamHandler (控制台输出)
22
+ 3. (可选) 设置 FileHandler (文件输出)
23
+ 4. 使用 rich.logging.RichHandler 美化控制台输出
24
+ 5. 设置日志格式: [时间] [级别] [模块名] 消息
25
+ """
26
+ raise NotImplementedError("TODO: Person E 实现 setup_logging")
src/easytranslate/utils/seed.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 随机种子管理模块 — 公共模块
3
+ """
4
+
5
+ import random
6
+
7
+ import numpy as np
8
+ import torch
9
+
10
+
11
+ def set_seed(seed: int = 42):
12
+ """
13
+ 设置全局随机种子以确保实验可复现。
14
+
15
+ TODO [Person E]:
16
+ 1. random.seed(seed)
17
+ 2. np.random.seed(seed)
18
+ 3. torch.manual_seed(seed)
19
+ 4. torch.cuda.manual_seed_all(seed)
20
+ 5. torch.backends.cudnn.deterministic = True
21
+ 6. torch.backends.cudnn.benchmark = False
22
+ """
23
+ raise NotImplementedError("TODO: Person E 实现 set_seed")
tests/test_data.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 单元测试 — 数据模块 (Person A 实现后需通过)
3
+ """
4
+
5
+ import pytest
6
+ import torch
7
+
8
+
9
+ class TestTranslationDataset:
10
+ """测试 TranslationDataset 类。"""
11
+
12
+ def test_dataset_length(self):
13
+ """测试数据集长度。"""
14
+ # TODO: 创建 mock tokenizer,构建小型数据集,验证 len() 正确
15
+ pass
16
+
17
+ def test_getitem_returns_correct_keys(self):
18
+ """测试 __getitem__ 返回正确的字段。"""
19
+ # TODO: 验证返回 dict 包含 src_ids, tgt_input_ids, labels, src_len, tgt_len
20
+ pass
21
+
22
+ def test_getitem_tensor_types(self):
23
+ """测试返回的 tensor 类型正确。"""
24
+ pass
25
+
26
+ def test_src_tgt_mismatch_raises(self):
27
+ """测试源目标数量不匹配时抛出异常。"""
28
+ pass
29
+
30
+
31
+ class TestTokenizer:
32
+ """测试分词器。"""
33
+
34
+ def test_bpe_train_and_encode(self):
35
+ """测试 BPE 训练和编码。"""
36
+ pass
37
+
38
+ def test_encode_decode_roundtrip(self):
39
+ """测试编码-解码往返一致性。"""
40
+ pass
41
+
42
+ def test_special_tokens(self):
43
+ """测试特殊 token 正确。"""
44
+ pass
45
+
46
+
47
+ class TestPreprocessing:
48
+ """测试预处理。"""
49
+
50
+ def test_clean_text_unicode(self):
51
+ """测试 Unicode 标准化。"""
52
+ pass
53
+
54
+ def test_filter_by_length(self):
55
+ """测试按长度过滤。"""
56
+ pass
57
+
58
+ def test_deduplicate(self):
59
+ """测试去重。"""
60
+ pass
61
+
62
+
63
+ class TestCollator:
64
+ """测试数据整理器。"""
65
+
66
+ def test_padding(self):
67
+ """测试 padding 正确。"""
68
+ pass
69
+
70
+ def test_attention_mask(self):
71
+ """测试 attention mask 正确。"""
72
+ pass
tests/test_evaluation.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 单元测试 — 评估模块 (Person D 实现后需通过)
3
+ """
4
+
5
+ import pytest
6
+ import torch
7
+
8
+
9
+ class TestMetrics:
10
+ """测试评估指标。"""
11
+
12
+ def test_bleu_perfect(self):
13
+ """完美翻译的 BLEU 应接近 100。"""
14
+ pass
15
+
16
+ def test_bleu_zero(self):
17
+ """完全不相关的翻译 BLEU 应接近 0。"""
18
+ pass
19
+
20
+ def test_chrf_score(self):
21
+ """测试 chrF 分数范围合理。"""
22
+ pass
23
+
24
+
25
+ class TestDecoding:
26
+ """测试解码策略。"""
27
+
28
+ def test_greedy_decode_shape(self):
29
+ """测试贪心解码输出维度。"""
30
+ pass
31
+
32
+ def test_greedy_produces_eos(self):
33
+ """测试贪心解码能生成 EOS。"""
34
+ pass
35
+
36
+ def test_beam_search_better_than_greedy(self):
37
+ """束搜索质量应不低于贪心。"""
38
+ pass
39
+
40
+ def test_no_repeat_ngram(self):
41
+ """测试 n-gram 不重复约束。"""
42
+ pass
43
+
44
+
45
+ class TestEvaluator:
46
+ """测试评估器。"""
47
+
48
+ def test_translate_single(self):
49
+ """测试单句翻译。"""
50
+ pass
51
+
52
+ def test_translate_batch(self):
53
+ """测试批量翻译。"""
54
+ pass
tests/test_model.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 单元测试 — 模型模块 (Person B 实现后需通过)
3
+ """
4
+
5
+ import pytest
6
+ import torch
7
+
8
+
9
+ class TestMultiHeadAttention:
10
+ """测试多头注意力。"""
11
+
12
+ def test_output_shape(self):
13
+ """测试输出维度正确。"""
14
+ # TODO: 创建 MHA,输入 [2, 10, 512],验证输出 [2, 10, 512]
15
+ pass
16
+
17
+ def test_padding_mask(self):
18
+ """测试 padding mask 效果。"""
19
+ pass
20
+
21
+
22
+ class TestPositionalEncoding:
23
+ """测试位置编码。"""
24
+
25
+ def test_sinusoidal_shape(self):
26
+ """测试正弦位置编码输出维度。"""
27
+ pass
28
+
29
+ def test_rope_shape(self):
30
+ """测试 RoPE 输出维度。"""
31
+ pass
32
+
33
+ def test_rope_relative_distance(self):
34
+ """测试 RoPE 捕获相对距离。"""
35
+ pass
36
+
37
+
38
+ class TestTransformerModel:
39
+ """测试完整 Transformer 模型。"""
40
+
41
+ def test_forward_shape(self):
42
+ """测试前向传播输出维度。"""
43
+ # TODO: 构建小模型,输入 mock 数据,验证 logits shape [B, T, V]
44
+ pass
45
+
46
+ def test_encode_shape(self):
47
+ """测试 encode 输出维度。"""
48
+ pass
49
+
50
+ def test_decode_step_shape(self):
51
+ """测试 decode_step 输出维度。"""
52
+ pass
53
+
54
+ def test_parameter_count(self):
55
+ """测试参数数量计算。"""
56
+ pass
57
+
58
+ def test_causal_mask(self):
59
+ """测试因果掩码生成。"""
60
+ pass
tests/test_training.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 单元测试 — 训练模块 (Person C 实现后需通过)
3
+ """
4
+
5
+ import pytest
6
+ import torch
7
+
8
+
9
+ class TestLabelSmoothedLoss:
10
+ """测试标签平滑损失。"""
11
+
12
+ def test_no_smoothing_equals_ce(self):
13
+ """smoothing=0 时应等于标准交叉熵。"""
14
+ pass
15
+
16
+ def test_smoothing_reduces_confidence(self):
17
+ """标签平滑应降低损失对正确预测的自信度。"""
18
+ pass
19
+
20
+ def test_ignore_padding(self):
21
+ """应忽略 padding 位置的损失。"""
22
+ pass
23
+
24
+
25
+ class TestOptimizer:
26
+ """测试优化器构建。"""
27
+
28
+ def test_build_adamw(self):
29
+ """测试构建 AdamW。"""
30
+ pass
31
+
32
+ def test_weight_decay_groups(self):
33
+ """测试权重衰减参数组正确。"""
34
+ pass
35
+
36
+
37
+ class TestScheduler:
38
+ """测试学习率调度器。"""
39
+
40
+ def test_warmup_phase(self):
41
+ """测试 warmup 阶段 lr 线性增长。"""
42
+ pass
43
+
44
+ def test_decay_phase(self):
45
+ """测试衰减阶段 lr 下降。"""
46
+ pass