Spaces:
Running
Running
zhan1206 commited on
Commit ·
0dafe0f
1
Parent(s): 82bba0c
fix(v11): resolve remaining 5 defects (S-NEW-1/2, M-NEW-2/3, MI-NEW-1)
Browse filesSERIOUS:
- S-NEW-1: dashboard token count len(tokens[0]) -> .shape[1] for correct seq_len
- S-NEW-2: dyquant convert() relies on load_model side effect -> check return value
MODERATE:
- M-NEW-2: 4 overlapping data scripts merged into manage_mini_data.py (create|fix|enrich|dedup|all)
- M-NEW-3: replace all emoji in print/logger with ASCII tags for GBK console compatibility
MINOR:
- MI-NEW-1: test_sbla_integration has_sblla typo -> has_sbla
- data_pipeline/bilingual_filter.py +22 -22
- data_pipeline/t_kd_distillation.py +8 -8
- data_pipeline/t_kd_distillation_train.py +12 -12
- inference/dashboard.py +2 -1
- inference/dyquant.py +4 -2
- inference/ollama_deploy.py +25 -25
- models/__init__.py +4 -4
- models/fusion_mini.py +7 -7
- scripts/add_depth3_samples.py +0 -42
- scripts/create_mini_data.py +0 -126
- scripts/dedup_mini_data.py +0 -120
- scripts/fix_mini_data.py +0 -61
- scripts/manage_mini_data.py +165 -0
- tests/run_tests.py +12 -12
- tests/test_sbla_integration.py +2 -2
data_pipeline/bilingual_filter.py
CHANGED
|
@@ -61,7 +61,7 @@ class BilingualTrueFilter:
|
|
| 61 |
else:
|
| 62 |
logger.warning("langid not installed, language detection disabled")
|
| 63 |
|
| 64 |
-
logger.info(f"
|
| 65 |
|
| 66 |
def process(self, data: List[str]) -> List[str]:
|
| 67 |
"""
|
|
@@ -73,7 +73,7 @@ class BilingualTrueFilter:
|
|
| 73 |
返回:
|
| 74 |
清洗后的文本列表
|
| 75 |
"""
|
| 76 |
-
logger.info(f"
|
| 77 |
|
| 78 |
clean_data = []
|
| 79 |
|
|
@@ -99,7 +99,7 @@ class BilingualTrueFilter:
|
|
| 99 |
|
| 100 |
clean_data.append(text)
|
| 101 |
|
| 102 |
-
logger.info(f"
|
| 103 |
|
| 104 |
return clean_data
|
| 105 |
|
|
@@ -141,17 +141,17 @@ class BilingualTrueFilter:
|
|
| 141 |
"""
|
| 142 |
# 1. 剔除"小编体"
|
| 143 |
if self._is_xiaobian_style(text):
|
| 144 |
-
logger.debug("
|
| 145 |
return False
|
| 146 |
|
| 147 |
# 2. 剔除机翻内容
|
| 148 |
if self._is_machine_translation(text):
|
| 149 |
-
logger.debug("
|
| 150 |
return False
|
| 151 |
|
| 152 |
# 3. 剔除低质量内容
|
| 153 |
if self._is_low_quality_chinese(text):
|
| 154 |
-
logger.debug("
|
| 155 |
return False
|
| 156 |
|
| 157 |
return True
|
|
@@ -166,12 +166,12 @@ class BilingualTrueFilter:
|
|
| 166 |
"""
|
| 167 |
# 1. 剔除直译中文语料
|
| 168 |
if self._is_translated_from_chinese(text):
|
| 169 |
-
logger.debug("
|
| 170 |
return False
|
| 171 |
|
| 172 |
# 2. 剔除低质量内容
|
| 173 |
if self._is_low_quality_english(text):
|
| 174 |
-
logger.debug("
|
| 175 |
return False
|
| 176 |
|
| 177 |
return True
|
|
@@ -298,7 +298,7 @@ class BalancedSampler:
|
|
| 298 |
self.en_data = en_data
|
| 299 |
self.zh_ratio = zh_ratio
|
| 300 |
|
| 301 |
-
logger.info(f"
|
| 302 |
logger.info(f" 中文数据:{len(zh_data)} 条")
|
| 303 |
logger.info(f" 英文数据:{len(en_data)} 条")
|
| 304 |
logger.info(f" 中文占比:{zh_ratio:.1%}")
|
|
@@ -331,7 +331,7 @@ class BalancedSampler:
|
|
| 331 |
|
| 332 |
random.shuffle(sampled)
|
| 333 |
|
| 334 |
-
logger.info(f"
|
| 335 |
logger.info(f" 中文:{n_zh} 条,英文:{n_en} 条")
|
| 336 |
|
| 337 |
return sampled
|
|
@@ -352,10 +352,10 @@ def process_data_pipeline(
|
|
| 352 |
output_path: 输出路径
|
| 353 |
n_samples: 采样数量
|
| 354 |
"""
|
| 355 |
-
logger.info("
|
| 356 |
|
| 357 |
# 1. 加载原始数据
|
| 358 |
-
logger.info("
|
| 359 |
|
| 360 |
with open(zh_raw_path, 'r', encoding='utf-8') as f:
|
| 361 |
zh_raw = json.load(f)
|
|
@@ -367,33 +367,33 @@ def process_data_pipeline(
|
|
| 367 |
logger.info(f" 英文原始数据:{len(en_raw)} 条")
|
| 368 |
|
| 369 |
# 2. 清洗中文数据
|
| 370 |
-
logger.info("\n
|
| 371 |
zh_filter = BilingualTrueFilter(lang="zh")
|
| 372 |
zh_clean = zh_filter.process(zh_raw)
|
| 373 |
|
| 374 |
# 3. 清洗英文数据
|
| 375 |
-
logger.info("\n
|
| 376 |
en_filter = BilingualTrueFilter(lang="en")
|
| 377 |
en_clean = en_filter.process(en_raw)
|
| 378 |
|
| 379 |
# 4. 平衡采样
|
| 380 |
-
logger.info("\n
|
| 381 |
sampler = BalancedSampler(zh_clean, en_clean, zh_ratio=0.5)
|
| 382 |
balanced_data = sampler.sample(n_samples)
|
| 383 |
|
| 384 |
# 5. 保存
|
| 385 |
-
logger.info(f"\n
|
| 386 |
with open(output_path, 'w', encoding='utf-8') as f:
|
| 387 |
json.dump(balanced_data, f, ensure_ascii=False, indent=2)
|
| 388 |
|
| 389 |
-
logger.info("
|
| 390 |
|
| 391 |
return balanced_data
|
| 392 |
|
| 393 |
|
| 394 |
if __name__ == "__main__":
|
| 395 |
# 单元测试(模拟数据)
|
| 396 |
-
print("
|
| 397 |
|
| 398 |
# 模拟中文数据
|
| 399 |
zh_test_data = [
|
|
@@ -413,16 +413,16 @@ if __name__ == "__main__":
|
|
| 413 |
# 测试中文过滤器
|
| 414 |
zh_filter = BilingualTrueFilter(lang="zh")
|
| 415 |
zh_clean = zh_filter.process(zh_test_data)
|
| 416 |
-
print(f"
|
| 417 |
|
| 418 |
# 测试英文过滤器
|
| 419 |
en_filter = BilingualTrueFilter(lang="en")
|
| 420 |
en_clean = en_filter.process(en_test_data)
|
| 421 |
-
print(f"
|
| 422 |
|
| 423 |
# 测试平衡采样
|
| 424 |
sampler = BalancedSampler(zh_clean, en_clean, zh_ratio=0.5)
|
| 425 |
balanced = sampler.sample(10)
|
| 426 |
-
print(f"
|
| 427 |
|
| 428 |
-
print("\n
|
|
|
|
| 61 |
else:
|
| 62 |
logger.warning("langid not installed, language detection disabled")
|
| 63 |
|
| 64 |
+
logger.info(f"[OK] 初始化 {lang.upper()} 数据过滤器")
|
| 65 |
|
| 66 |
def process(self, data: List[str]) -> List[str]:
|
| 67 |
"""
|
|
|
|
| 73 |
返回:
|
| 74 |
清洗后的文本列表
|
| 75 |
"""
|
| 76 |
+
logger.info(f"[CHART] 开始处理 {len(data)} 条数据...")
|
| 77 |
|
| 78 |
clean_data = []
|
| 79 |
|
|
|
|
| 99 |
|
| 100 |
clean_data.append(text)
|
| 101 |
|
| 102 |
+
logger.info(f"[OK] 清洗完成:{len(clean_data)}/{len(data)} 条保留")
|
| 103 |
|
| 104 |
return clean_data
|
| 105 |
|
|
|
|
| 141 |
"""
|
| 142 |
# 1. 剔除"小编体"
|
| 143 |
if self._is_xiaobian_style(text):
|
| 144 |
+
logger.debug("[FAIL] 剔除小编体")
|
| 145 |
return False
|
| 146 |
|
| 147 |
# 2. 剔除机翻内容
|
| 148 |
if self._is_machine_translation(text):
|
| 149 |
+
logger.debug("[FAIL] 剔除机翻内容")
|
| 150 |
return False
|
| 151 |
|
| 152 |
# 3. 剔除低质量内容
|
| 153 |
if self._is_low_quality_chinese(text):
|
| 154 |
+
logger.debug("[FAIL] 剔除低质量内容")
|
| 155 |
return False
|
| 156 |
|
| 157 |
return True
|
|
|
|
| 166 |
"""
|
| 167 |
# 1. 剔除直译中文语料
|
| 168 |
if self._is_translated_from_chinese(text):
|
| 169 |
+
logger.debug("[FAIL] 剔除直译中文语料")
|
| 170 |
return False
|
| 171 |
|
| 172 |
# 2. 剔除低质量内容
|
| 173 |
if self._is_low_quality_english(text):
|
| 174 |
+
logger.debug("[FAIL] 剔除低质量内容")
|
| 175 |
return False
|
| 176 |
|
| 177 |
return True
|
|
|
|
| 298 |
self.en_data = en_data
|
| 299 |
self.zh_ratio = zh_ratio
|
| 300 |
|
| 301 |
+
logger.info(f"[CHART] 平衡采样器初始化")
|
| 302 |
logger.info(f" 中文数据:{len(zh_data)} 条")
|
| 303 |
logger.info(f" 英文数据:{len(en_data)} 条")
|
| 304 |
logger.info(f" 中文占比:{zh_ratio:.1%}")
|
|
|
|
| 331 |
|
| 332 |
random.shuffle(sampled)
|
| 333 |
|
| 334 |
+
logger.info(f"[OK] 采样 {len(sampled)} 条平衡数据")
|
| 335 |
logger.info(f" 中文:{n_zh} 条,英文:{n_en} 条")
|
| 336 |
|
| 337 |
return sampled
|
|
|
|
| 352 |
output_path: 输出路径
|
| 353 |
n_samples: 采样数量
|
| 354 |
"""
|
| 355 |
+
logger.info("[GO] 启动双母语数据处理管道...")
|
| 356 |
|
| 357 |
# 1. 加载原始数据
|
| 358 |
+
logger.info("[LOAD] 加载原始数据...")
|
| 359 |
|
| 360 |
with open(zh_raw_path, 'r', encoding='utf-8') as f:
|
| 361 |
zh_raw = json.load(f)
|
|
|
|
| 367 |
logger.info(f" 英文原始数据:{len(en_raw)} 条")
|
| 368 |
|
| 369 |
# 2. 清洗中文数据
|
| 370 |
+
logger.info("\n[CLEAN] 清洗中文数据...")
|
| 371 |
zh_filter = BilingualTrueFilter(lang="zh")
|
| 372 |
zh_clean = zh_filter.process(zh_raw)
|
| 373 |
|
| 374 |
# 3. 清洗英文数据
|
| 375 |
+
logger.info("\n[CLEAN] 清洗英文数据...")
|
| 376 |
en_filter = BilingualTrueFilter(lang="en")
|
| 377 |
en_clean = en_filter.process(en_raw)
|
| 378 |
|
| 379 |
# 4. 平衡采样
|
| 380 |
+
logger.info("\n[BALANCE][LOGO] 平衡采样...")
|
| 381 |
sampler = BalancedSampler(zh_clean, en_clean, zh_ratio=0.5)
|
| 382 |
balanced_data = sampler.sample(n_samples)
|
| 383 |
|
| 384 |
# 5. 保存
|
| 385 |
+
logger.info(f"\n[SAVE] 保存到 {output_path}...")
|
| 386 |
with open(output_path, 'w', encoding='utf-8') as f:
|
| 387 |
json.dump(balanced_data, f, ensure_ascii=False, indent=2)
|
| 388 |
|
| 389 |
+
logger.info("[OK] 数据处理管道完成!")
|
| 390 |
|
| 391 |
return balanced_data
|
| 392 |
|
| 393 |
|
| 394 |
if __name__ == "__main__":
|
| 395 |
# 单元测试(模拟数据)
|
| 396 |
+
print("[LOGO] 测试 Bi-Lingual TrueFilter...")
|
| 397 |
|
| 398 |
# 模拟中文数据
|
| 399 |
zh_test_data = [
|
|
|
|
| 413 |
# 测试中文过滤器
|
| 414 |
zh_filter = BilingualTrueFilter(lang="zh")
|
| 415 |
zh_clean = zh_filter.process(zh_test_data)
|
| 416 |
+
print(f"[OK] 中文过滤:{len(zh_clean)}/{len(zh_test_data)} 条保留")
|
| 417 |
|
| 418 |
# 测试英文过滤器
|
| 419 |
en_filter = BilingualTrueFilter(lang="en")
|
| 420 |
en_clean = en_filter.process(en_test_data)
|
| 421 |
+
print(f"[OK] 英文过滤:{len(en_clean)}/{len(en_test_data)} 条保留")
|
| 422 |
|
| 423 |
# 测试平衡采样
|
| 424 |
sampler = BalancedSampler(zh_clean, en_clean, zh_ratio=0.5)
|
| 425 |
balanced = sampler.sample(10)
|
| 426 |
+
print(f"[OK] 平衡采样:{len(balanced)} 条")
|
| 427 |
|
| 428 |
+
print("\n[OK] Bi-Lingual TrueFilter 测试通过!")
|
data_pipeline/t_kd_distillation.py
CHANGED
|
@@ -47,7 +47,7 @@ class TKDDistiller:
|
|
| 47 |
device: 设备(cuda/cpu)
|
| 48 |
torch_dtype: 数据类型
|
| 49 |
"""
|
| 50 |
-
print(f"
|
| 51 |
|
| 52 |
self.device = device
|
| 53 |
self.tokenizer = AutoTokenizer.from_pretrained(
|
|
@@ -64,7 +64,7 @@ class TKDDistiller:
|
|
| 64 |
|
| 65 |
self.model.eval()
|
| 66 |
|
| 67 |
-
print(f"
|
| 68 |
print(f" 设备:{self.model.device}")
|
| 69 |
print(f" 参数量:{sum(p.numel() for p in self.model.parameters()) / 1e9:.2f}B")
|
| 70 |
|
|
@@ -164,7 +164,7 @@ class TKDDistiller:
|
|
| 164 |
返回:
|
| 165 |
蒸馏结果列表
|
| 166 |
"""
|
| 167 |
-
print(f"\n
|
| 168 |
print(f" 主题数:{len(topics)}")
|
| 169 |
print(f" 数据源:{source_type}")
|
| 170 |
|
|
@@ -195,7 +195,7 @@ class TKDDistiller:
|
|
| 195 |
with open(output_path, 'a', encoding='utf-8') as f:
|
| 196 |
f.write(json.dumps(result, ensure_ascii=False) + '\n')
|
| 197 |
|
| 198 |
-
print(f"
|
| 199 |
|
| 200 |
# 避免 GPU 过热
|
| 201 |
if i % 10 == 0:
|
|
@@ -203,10 +203,10 @@ class TKDDistiller:
|
|
| 203 |
time.sleep(1)
|
| 204 |
|
| 205 |
except Exception as e:
|
| 206 |
-
print(f"
|
| 207 |
continue
|
| 208 |
|
| 209 |
-
print(f"\n
|
| 210 |
|
| 211 |
return results
|
| 212 |
|
|
@@ -330,9 +330,9 @@ def main():
|
|
| 330 |
output_path=output_path,
|
| 331 |
)
|
| 332 |
|
| 333 |
-
print(f"\n
|
| 334 |
|
| 335 |
-
print(f"\n
|
| 336 |
print(f" 输出目录:{Path(args.output_path).parent}")
|
| 337 |
|
| 338 |
|
|
|
|
| 47 |
device: 设备(cuda/cpu)
|
| 48 |
torch_dtype: 数据类型
|
| 49 |
"""
|
| 50 |
+
print(f"[BOOK] 加载教师模型:{teacher_model}")
|
| 51 |
|
| 52 |
self.device = device
|
| 53 |
self.tokenizer = AutoTokenizer.from_pretrained(
|
|
|
|
| 64 |
|
| 65 |
self.model.eval()
|
| 66 |
|
| 67 |
+
print(f"[OK] 教师模型加载成功")
|
| 68 |
print(f" 设备:{self.model.device}")
|
| 69 |
print(f" 参数量:{sum(p.numel() for p in self.model.parameters()) / 1e9:.2f}B")
|
| 70 |
|
|
|
|
| 164 |
返回:
|
| 165 |
蒸馏结果列表
|
| 166 |
"""
|
| 167 |
+
print(f"\n[BOOK] 开始 T-KD 蒸馏...")
|
| 168 |
print(f" 主题数:{len(topics)}")
|
| 169 |
print(f" 数据源:{source_type}")
|
| 170 |
|
|
|
|
| 195 |
with open(output_path, 'a', encoding='utf-8') as f:
|
| 196 |
f.write(json.dumps(result, ensure_ascii=False) + '\n')
|
| 197 |
|
| 198 |
+
print(f"[OK] 完成(生成 {len(text)} 字符)")
|
| 199 |
|
| 200 |
# 避免 GPU 过热
|
| 201 |
if i % 10 == 0:
|
|
|
|
| 203 |
time.sleep(1)
|
| 204 |
|
| 205 |
except Exception as e:
|
| 206 |
+
print(f"[FAIL] 失败:{e}")
|
| 207 |
continue
|
| 208 |
|
| 209 |
+
print(f"\n[DONE] 蒸馏完成!共生成 {len(results)} 个样本")
|
| 210 |
|
| 211 |
return results
|
| 212 |
|
|
|
|
| 330 |
output_path=output_path,
|
| 331 |
)
|
| 332 |
|
| 333 |
+
print(f"\n[OK] {source} 蒸馏完成,结果保存至:{output_path}")
|
| 334 |
|
| 335 |
+
print(f"\n[DONE] 所有数据源蒸馏完成!")
|
| 336 |
print(f" 输出目录:{Path(args.output_path).parent}")
|
| 337 |
|
| 338 |
|
data_pipeline/t_kd_distillation_train.py
CHANGED
|
@@ -57,7 +57,7 @@ class DistillationDataset(Dataset):
|
|
| 57 |
if line.strip():
|
| 58 |
self.data.append(json.loads(line))
|
| 59 |
|
| 60 |
-
logger.info(f"
|
| 61 |
|
| 62 |
def __len__(self):
|
| 63 |
return len(self.data)
|
|
@@ -127,7 +127,7 @@ class DistillationTrainer:
|
|
| 127 |
self.grad_accum_steps = grad_accum_steps
|
| 128 |
|
| 129 |
# 1. 加载教师模型(冻结)
|
| 130 |
-
logger.info(f"
|
| 131 |
self.teacher_tokenizer = AutoTokenizer.from_pretrained(
|
| 132 |
teacher_model_name,
|
| 133 |
trust_remote_code=True,
|
|
@@ -144,10 +144,10 @@ class DistillationTrainer:
|
|
| 144 |
for param in self.teacher_model.parameters():
|
| 145 |
param.requires_grad = False
|
| 146 |
|
| 147 |
-
logger.info(f"
|
| 148 |
|
| 149 |
# 2. 加载学生模型(可训练)
|
| 150 |
-
logger.info(f"
|
| 151 |
self.student_tokenizer = AutoTokenizer.from_pretrained(
|
| 152 |
student_model_name,
|
| 153 |
trust_remote_code=True,
|
|
@@ -162,7 +162,7 @@ class DistillationTrainer:
|
|
| 162 |
|
| 163 |
self.student_model.train()
|
| 164 |
|
| 165 |
-
logger.info(f"
|
| 166 |
|
| 167 |
# 3. 优化器 + 学习率调度器
|
| 168 |
self.optimizer = torch.optim.AdamW(
|
|
@@ -171,7 +171,7 @@ class DistillationTrainer:
|
|
| 171 |
weight_decay=0.01,
|
| 172 |
)
|
| 173 |
|
| 174 |
-
logger.info(f"
|
| 175 |
|
| 176 |
def compute_distillation_loss(
|
| 177 |
self,
|
|
@@ -269,7 +269,7 @@ class DistillationTrainer:
|
|
| 269 |
)
|
| 270 |
|
| 271 |
# 3. 训练循环
|
| 272 |
-
logger.info(f"
|
| 273 |
logger.info(f" 轮数:{num_epochs}")
|
| 274 |
logger.info(f" 批次大小:{self.batch_size}")
|
| 275 |
logger.info(f" 梯度累积:{self.grad_accum_steps}")
|
|
@@ -365,7 +365,7 @@ class DistillationTrainer:
|
|
| 365 |
self.student_model.save_pretrained(checkpoint_dir)
|
| 366 |
self.student_tokenizer.save_pretrained(checkpoint_dir)
|
| 367 |
|
| 368 |
-
logger.info(f"
|
| 369 |
|
| 370 |
# 4. 保存最终模型
|
| 371 |
output_path = Path(output_dir) / "final"
|
|
@@ -374,7 +374,7 @@ class DistillationTrainer:
|
|
| 374 |
self.student_model.save_pretrained(output_path)
|
| 375 |
self.student_tokenizer.save_pretrained(output_path)
|
| 376 |
|
| 377 |
-
logger.info(f"
|
| 378 |
|
| 379 |
def evaluate(
|
| 380 |
self,
|
|
@@ -390,7 +390,7 @@ class DistillationTrainer:
|
|
| 390 |
max_length: 最大序列长度
|
| 391 |
num_samples: 评估样本数
|
| 392 |
"""
|
| 393 |
-
logger.info(f"
|
| 394 |
|
| 395 |
self.student_model.eval()
|
| 396 |
|
|
@@ -430,7 +430,7 @@ class DistillationTrainer:
|
|
| 430 |
|
| 431 |
avg_loss = total_loss / max(num_batches, 1)
|
| 432 |
|
| 433 |
-
logger.info(f"
|
| 434 |
logger.info(f" Average Loss: {avg_loss:.4f}")
|
| 435 |
logger.info(f" Perplexity: {torch.exp(torch.tensor(avg_loss)).item():.2f}")
|
| 436 |
|
|
@@ -547,7 +547,7 @@ def main():
|
|
| 547 |
max_length=args.max_length,
|
| 548 |
)
|
| 549 |
|
| 550 |
-
logger.info("
|
| 551 |
|
| 552 |
|
| 553 |
if __name__ == "__main__":
|
|
|
|
| 57 |
if line.strip():
|
| 58 |
self.data.append(json.loads(line))
|
| 59 |
|
| 60 |
+
logger.info(f"[OK] 加载数据:{len(self.data)} 条")
|
| 61 |
|
| 62 |
def __len__(self):
|
| 63 |
return len(self.data)
|
|
|
|
| 127 |
self.grad_accum_steps = grad_accum_steps
|
| 128 |
|
| 129 |
# 1. 加载教师模型(冻结)
|
| 130 |
+
logger.info(f"[BOOK] 加载教师模型:{teacher_model_name}")
|
| 131 |
self.teacher_tokenizer = AutoTokenizer.from_pretrained(
|
| 132 |
teacher_model_name,
|
| 133 |
trust_remote_code=True,
|
|
|
|
| 144 |
for param in self.teacher_model.parameters():
|
| 145 |
param.requires_grad = False
|
| 146 |
|
| 147 |
+
logger.info(f"[OK] 教师模型加载完成(参数已冻结)")
|
| 148 |
|
| 149 |
# 2. 加载学生模型(可训练)
|
| 150 |
+
logger.info(f"[GRAD] 加载学生模型:{student_model_name}")
|
| 151 |
self.student_tokenizer = AutoTokenizer.from_pretrained(
|
| 152 |
student_model_name,
|
| 153 |
trust_remote_code=True,
|
|
|
|
| 162 |
|
| 163 |
self.student_model.train()
|
| 164 |
|
| 165 |
+
logger.info(f"[OK] 学生模型加载完成(可训练)")
|
| 166 |
|
| 167 |
# 3. 优化器 + 学习率调度器
|
| 168 |
self.optimizer = torch.optim.AdamW(
|
|
|
|
| 171 |
weight_decay=0.01,
|
| 172 |
)
|
| 173 |
|
| 174 |
+
logger.info(f"[OK] 优化器初始化完成(lr={learning_rate})")
|
| 175 |
|
| 176 |
def compute_distillation_loss(
|
| 177 |
self,
|
|
|
|
| 269 |
)
|
| 270 |
|
| 271 |
# 3. 训练循环
|
| 272 |
+
logger.info(f"[GO] 开始蒸馏训练...")
|
| 273 |
logger.info(f" 轮数:{num_epochs}")
|
| 274 |
logger.info(f" 批次大小:{self.batch_size}")
|
| 275 |
logger.info(f" 梯度累积:{self.grad_accum_steps}")
|
|
|
|
| 365 |
self.student_model.save_pretrained(checkpoint_dir)
|
| 366 |
self.student_tokenizer.save_pretrained(checkpoint_dir)
|
| 367 |
|
| 368 |
+
logger.info(f" [OK] 检查点保存至:{checkpoint_dir}")
|
| 369 |
|
| 370 |
# 4. 保存最终模型
|
| 371 |
output_path = Path(output_dir) / "final"
|
|
|
|
| 374 |
self.student_model.save_pretrained(output_path)
|
| 375 |
self.student_tokenizer.save_pretrained(output_path)
|
| 376 |
|
| 377 |
+
logger.info(f"[DONE] 蒸馏训练完成!模型保存至:{output_path}")
|
| 378 |
|
| 379 |
def evaluate(
|
| 380 |
self,
|
|
|
|
| 390 |
max_length: 最大序列长度
|
| 391 |
num_samples: 评估样本数
|
| 392 |
"""
|
| 393 |
+
logger.info(f"[CHART] 开始评估...")
|
| 394 |
|
| 395 |
self.student_model.eval()
|
| 396 |
|
|
|
|
| 430 |
|
| 431 |
avg_loss = total_loss / max(num_batches, 1)
|
| 432 |
|
| 433 |
+
logger.info(f"[OK] 评估完成")
|
| 434 |
logger.info(f" Average Loss: {avg_loss:.4f}")
|
| 435 |
logger.info(f" Perplexity: {torch.exp(torch.tensor(avg_loss)).item():.2f}")
|
| 436 |
|
|
|
|
| 547 |
max_length=args.max_length,
|
| 548 |
)
|
| 549 |
|
| 550 |
+
logger.info("[DONE] 蒸馏训练完成!")
|
| 551 |
|
| 552 |
|
| 553 |
if __name__ == "__main__":
|
inference/dashboard.py
CHANGED
|
@@ -252,7 +252,8 @@ class InferenceEngine:
|
|
| 252 |
self.kv_cache = outputs.past_key_values
|
| 253 |
|
| 254 |
# Decode only new tokens
|
| 255 |
-
|
|
|
|
| 256 |
return self._detokenize(new_tokens)
|
| 257 |
|
| 258 |
def _generate_stream(self, input_ids, cfg) -> Generator:
|
|
|
|
| 252 |
self.kv_cache = outputs.past_key_values
|
| 253 |
|
| 254 |
# Decode only new tokens
|
| 255 |
+
prompt_len = self._tokenize(prompt).shape[1]
|
| 256 |
+
new_tokens = generated[prompt_len:]
|
| 257 |
return self._detokenize(new_tokens)
|
| 258 |
|
| 259 |
def _generate_stream(self, input_ids, cfg) -> Generator:
|
inference/dyquant.py
CHANGED
|
@@ -432,8 +432,10 @@ class DyQuantConverter:
|
|
| 432 |
|
| 433 |
# 1. 加载模型
|
| 434 |
if self.model is None:
|
| 435 |
-
self.load_model()
|
| 436 |
-
|
|
|
|
|
|
|
| 437 |
if self.model is None:
|
| 438 |
print(f"[DyQuant] 无法加载模型,返回 None")
|
| 439 |
return None
|
|
|
|
| 432 |
|
| 433 |
# 1. 加载模型
|
| 434 |
if self.model is None:
|
| 435 |
+
result = self.load_model()
|
| 436 |
+
if result is not None:
|
| 437 |
+
self.model = result
|
| 438 |
+
|
| 439 |
if self.model is None:
|
| 440 |
print(f"[DyQuant] 无法加载模型,返回 None")
|
| 441 |
return None
|
inference/ollama_deploy.py
CHANGED
|
@@ -34,14 +34,14 @@ def check_dependencies():
|
|
| 34 |
"""
|
| 35 |
检查依赖项
|
| 36 |
"""
|
| 37 |
-
logger.info("
|
| 38 |
|
| 39 |
# 检查 llama.cpp 转换脚本
|
| 40 |
llama_cpp_dir = os.environ.get("LLAMA_CPP_DIR", "")
|
| 41 |
convert_script = os.path.join(llama_cpp_dir, "convert-hf-to-gguf.py")
|
| 42 |
|
| 43 |
if not os.path.exists(convert_script):
|
| 44 |
-
logger.warning(f"
|
| 45 |
logger.warning(" 请设置环境变量 LLAMA_CPP_DIR 或手动下载 llama.cpp")
|
| 46 |
logger.warning(" 下载地址:<ADDRESS_REMOVED>
|
| 47 |
return False
|
|
@@ -54,17 +54,17 @@ def check_dependencies():
|
|
| 54 |
text=True,
|
| 55 |
)
|
| 56 |
if result.returncode == 0:
|
| 57 |
-
logger.info(f"
|
| 58 |
else:
|
| 59 |
-
logger.warning("
|
| 60 |
logger.warning(" 请访问 https://ollama.com 安装")
|
| 61 |
return False
|
| 62 |
except FileNotFoundError:
|
| 63 |
-
logger.warning("
|
| 64 |
logger.warning(" 请访问 https://ollama.com 安装")
|
| 65 |
return False
|
| 66 |
|
| 67 |
-
logger.info("
|
| 68 |
return True
|
| 69 |
|
| 70 |
|
|
@@ -81,7 +81,7 @@ def convert_to_gguf(
|
|
| 81 |
output_path: 输出路径
|
| 82 |
quantize: 量化级别(q4_k_m, q5_k_m, q8_0 等)
|
| 83 |
"""
|
| 84 |
-
logger.info("
|
| 85 |
|
| 86 |
llama_cpp_dir = os.environ.get("LLAMA_CPP_DIR", "")
|
| 87 |
convert_script = os.path.join(llama_cpp_dir, "convert-hf-to-gguf.py")
|
|
@@ -99,14 +99,14 @@ def convert_to_gguf(
|
|
| 99 |
result = subprocess.run(cmd, capture_output=True, text=True)
|
| 100 |
|
| 101 |
if result.returncode != 0:
|
| 102 |
-
logger.error(f"
|
| 103 |
raise RuntimeError("GGUF 转换失败")
|
| 104 |
|
| 105 |
-
logger.info(f"
|
| 106 |
|
| 107 |
# 量化(可选)
|
| 108 |
if quantize:
|
| 109 |
-
logger.info(f"
|
| 110 |
|
| 111 |
quantize_cmd = [
|
| 112 |
os.path.join(llama_cpp_dir, "llama-quantize"),
|
|
@@ -118,11 +118,11 @@ def convert_to_gguf(
|
|
| 118 |
result = subprocess.run(quantize_cmd, capture_output=True, text=True)
|
| 119 |
|
| 120 |
if result.returncode != 0:
|
| 121 |
-
logger.warning(f"
|
| 122 |
logger.warning(" 继续使用未量化模型")
|
| 123 |
else:
|
| 124 |
output_path = output_path.replace(".gguf", f"_{quantize}.gguf")
|
| 125 |
-
logger.info(f"
|
| 126 |
|
| 127 |
return output_path
|
| 128 |
|
|
@@ -144,7 +144,7 @@ def create_modelfile(
|
|
| 144 |
context_size: 上下文窗口大小
|
| 145 |
thinking_dial: 是否启用 Thinking Dial
|
| 146 |
"""
|
| 147 |
-
logger.info("
|
| 148 |
|
| 149 |
# Modelfile 内容
|
| 150 |
content = f"""# Fusion 模型:{model_name}
|
|
@@ -189,7 +189,7 @@ TEMPLATE \"\"\"{{ if .System }}<|im_start|>system
|
|
| 189 |
with open(modelfile_path, 'w', encoding='utf-8') as f:
|
| 190 |
f.write(content)
|
| 191 |
|
| 192 |
-
logger.info(f"
|
| 193 |
|
| 194 |
|
| 195 |
def create_ollama_model(modelfile_path: str, model_name: str):
|
|
@@ -200,7 +200,7 @@ def create_ollama_model(modelfile_path: str, model_name: str):
|
|
| 200 |
modelfile_path: Modelfile 路径
|
| 201 |
model_name: 模型名称
|
| 202 |
"""
|
| 203 |
-
logger.info(f"
|
| 204 |
|
| 205 |
# 删除已存在的模型
|
| 206 |
subprocess.run(
|
|
@@ -216,10 +216,10 @@ def create_ollama_model(modelfile_path: str, model_name: str):
|
|
| 216 |
result = subprocess.run(cmd, capture_output=True, text=True)
|
| 217 |
|
| 218 |
if result.returncode != 0:
|
| 219 |
-
logger.error(f"
|
| 220 |
raise RuntimeError("Ollama 模型创建失败")
|
| 221 |
|
| 222 |
-
logger.info(f"
|
| 223 |
logger.info(f" 运行 `ollama run {model_name}` 开始使用")
|
| 224 |
|
| 225 |
|
|
@@ -242,13 +242,13 @@ def deploy(
|
|
| 242 |
context_size: 上下文窗口
|
| 243 |
thinking_dial: 是否启用 Thinking Dial
|
| 244 |
"""
|
| 245 |
-
logger.info("
|
| 246 |
logger.info(f" 模型路径:{model_path}")
|
| 247 |
logger.info(f" 模型名称:{model_name}")
|
| 248 |
|
| 249 |
# 1. 检查依赖
|
| 250 |
if not check_dependencies():
|
| 251 |
-
logger.error("
|
| 252 |
return False
|
| 253 |
|
| 254 |
# 2. 创建输出目录
|
|
@@ -263,7 +263,7 @@ def deploy(
|
|
| 263 |
quantize=quantize,
|
| 264 |
)
|
| 265 |
except RuntimeError as e:
|
| 266 |
-
logger.error(f"
|
| 267 |
return False
|
| 268 |
|
| 269 |
# 4. 创建 Modelfile
|
|
@@ -283,14 +283,14 @@ def deploy(
|
|
| 283 |
model_name=model_name,
|
| 284 |
)
|
| 285 |
except RuntimeError as e:
|
| 286 |
-
logger.error(f"
|
| 287 |
return False
|
| 288 |
|
| 289 |
# 6. 生成使用示例
|
| 290 |
example_path = os.path.join(output_dir, "USAGE.md")
|
| 291 |
generate_usage_example(model_name, example_path)
|
| 292 |
|
| 293 |
-
logger.info("
|
| 294 |
logger.info(f" 运行:`ollama run {model_name}`")
|
| 295 |
logger.info(f" 示例:见 {example_path}")
|
| 296 |
|
|
@@ -392,7 +392,7 @@ ollama run {model_name} --top_p 0.95
|
|
| 392 |
with open(output_path, 'w', encoding='utf-8') as f:
|
| 393 |
f.write(content)
|
| 394 |
|
| 395 |
-
logger.info(f"
|
| 396 |
|
| 397 |
|
| 398 |
def main():
|
|
@@ -426,9 +426,9 @@ def main():
|
|
| 426 |
)
|
| 427 |
|
| 428 |
if success:
|
| 429 |
-
logger.info("
|
| 430 |
else:
|
| 431 |
-
logger.error("
|
| 432 |
|
| 433 |
|
| 434 |
if __name__ == "__main__":
|
|
|
|
| 34 |
"""
|
| 35 |
检查依赖项
|
| 36 |
"""
|
| 37 |
+
logger.info("[SEARCH] 检查依赖项...")
|
| 38 |
|
| 39 |
# 检查 llama.cpp 转换脚本
|
| 40 |
llama_cpp_dir = os.environ.get("LLAMA_CPP_DIR", "")
|
| 41 |
convert_script = os.path.join(llama_cpp_dir, "convert-hf-to-gguf.py")
|
| 42 |
|
| 43 |
if not os.path.exists(convert_script):
|
| 44 |
+
logger.warning(f"[WARN][LOGO] 未找到 llama.cpp 转换脚本:{convert_script}")
|
| 45 |
logger.warning(" 请设置环境变量 LLAMA_CPP_DIR 或手动下载 llama.cpp")
|
| 46 |
logger.warning(" 下载地址:<ADDRESS_REMOVED>
|
| 47 |
return False
|
|
|
|
| 54 |
text=True,
|
| 55 |
)
|
| 56 |
if result.returncode == 0:
|
| 57 |
+
logger.info(f"[OK] Ollama 已安装:{result.stdout.strip()}")
|
| 58 |
else:
|
| 59 |
+
logger.warning("[WARN][LOGO] Ollama 未安装或无法运行")
|
| 60 |
logger.warning(" 请访问 https://ollama.com 安装")
|
| 61 |
return False
|
| 62 |
except FileNotFoundError:
|
| 63 |
+
logger.warning("[WARN][LOGO] Ollama 未安装")
|
| 64 |
logger.warning(" 请访问 https://ollama.com 安装")
|
| 65 |
return False
|
| 66 |
|
| 67 |
+
logger.info("[OK] 依赖项检查通过")
|
| 68 |
return True
|
| 69 |
|
| 70 |
|
|
|
|
| 81 |
output_path: 输出路径
|
| 82 |
quantize: 量化级别(q4_k_m, q5_k_m, q8_0 等)
|
| 83 |
"""
|
| 84 |
+
logger.info("[SYNC] 转换为 GGUF 格式...")
|
| 85 |
|
| 86 |
llama_cpp_dir = os.environ.get("LLAMA_CPP_DIR", "")
|
| 87 |
convert_script = os.path.join(llama_cpp_dir, "convert-hf-to-gguf.py")
|
|
|
|
| 99 |
result = subprocess.run(cmd, capture_output=True, text=True)
|
| 100 |
|
| 101 |
if result.returncode != 0:
|
| 102 |
+
logger.error(f"[FAIL] 转换失败:{result.stderr}")
|
| 103 |
raise RuntimeError("GGUF 转换失败")
|
| 104 |
|
| 105 |
+
logger.info(f"[OK] GGUF 转换完成:{output_path}")
|
| 106 |
|
| 107 |
# 量化(可选)
|
| 108 |
if quantize:
|
| 109 |
+
logger.info(f"[TOOL] 量化模型({quantize})...")
|
| 110 |
|
| 111 |
quantize_cmd = [
|
| 112 |
os.path.join(llama_cpp_dir, "llama-quantize"),
|
|
|
|
| 118 |
result = subprocess.run(quantize_cmd, capture_output=True, text=True)
|
| 119 |
|
| 120 |
if result.returncode != 0:
|
| 121 |
+
logger.warning(f"[WARN][LOGO] 量化失败:{result.stderr}")
|
| 122 |
logger.warning(" 继续使用未量化模型")
|
| 123 |
else:
|
| 124 |
output_path = output_path.replace(".gguf", f"_{quantize}.gguf")
|
| 125 |
+
logger.info(f"[OK] 量化完成:{output_path}")
|
| 126 |
|
| 127 |
return output_path
|
| 128 |
|
|
|
|
| 144 |
context_size: 上下文窗口大小
|
| 145 |
thinking_dial: 是否启用 Thinking Dial
|
| 146 |
"""
|
| 147 |
+
logger.info("[NOTE] 创建 Modelfile...")
|
| 148 |
|
| 149 |
# Modelfile 内容
|
| 150 |
content = f"""# Fusion 模型:{model_name}
|
|
|
|
| 189 |
with open(modelfile_path, 'w', encoding='utf-8') as f:
|
| 190 |
f.write(content)
|
| 191 |
|
| 192 |
+
logger.info(f"[OK] Modelfile 创建完成:{modelfile_path}")
|
| 193 |
|
| 194 |
|
| 195 |
def create_ollama_model(modelfile_path: str, model_name: str):
|
|
|
|
| 200 |
modelfile_path: Modelfile 路径
|
| 201 |
model_name: 模型名称
|
| 202 |
"""
|
| 203 |
+
logger.info(f"[GO] 创建 Ollama 模型:{model_name}...")
|
| 204 |
|
| 205 |
# 删除已存在的模型
|
| 206 |
subprocess.run(
|
|
|
|
| 216 |
result = subprocess.run(cmd, capture_output=True, text=True)
|
| 217 |
|
| 218 |
if result.returncode != 0:
|
| 219 |
+
logger.error(f"[FAIL] 创建失败:{result.stderr}")
|
| 220 |
raise RuntimeError("Ollama 模型创建失败")
|
| 221 |
|
| 222 |
+
logger.info(f"[OK] Ollama 模型创建成功:{model_name}")
|
| 223 |
logger.info(f" 运行 `ollama run {model_name}` 开始使用")
|
| 224 |
|
| 225 |
|
|
|
|
| 242 |
context_size: 上下文窗口
|
| 243 |
thinking_dial: 是否启用 Thinking Dial
|
| 244 |
"""
|
| 245 |
+
logger.info("[GO] 开始 Ollama 部署流程...")
|
| 246 |
logger.info(f" 模型路径:{model_path}")
|
| 247 |
logger.info(f" 模型名称:{model_name}")
|
| 248 |
|
| 249 |
# 1. 检查依赖
|
| 250 |
if not check_dependencies():
|
| 251 |
+
logger.error("[FAIL] 依赖项检查失败,请先安装所需工具")
|
| 252 |
return False
|
| 253 |
|
| 254 |
# 2. 创建输出目录
|
|
|
|
| 263 |
quantize=quantize,
|
| 264 |
)
|
| 265 |
except RuntimeError as e:
|
| 266 |
+
logger.error(f"[FAIL] GGUF 转换失败:{e}")
|
| 267 |
return False
|
| 268 |
|
| 269 |
# 4. 创建 Modelfile
|
|
|
|
| 283 |
model_name=model_name,
|
| 284 |
)
|
| 285 |
except RuntimeError as e:
|
| 286 |
+
logger.error(f"[FAIL] Ollama 模型创建失败:{e}")
|
| 287 |
return False
|
| 288 |
|
| 289 |
# 6. 生成使用示例
|
| 290 |
example_path = os.path.join(output_dir, "USAGE.md")
|
| 291 |
generate_usage_example(model_name, example_path)
|
| 292 |
|
| 293 |
+
logger.info("[OK] 部署完成!")
|
| 294 |
logger.info(f" 运行:`ollama run {model_name}`")
|
| 295 |
logger.info(f" 示例:见 {example_path}")
|
| 296 |
|
|
|
|
| 392 |
with open(output_path, 'w', encoding='utf-8') as f:
|
| 393 |
f.write(content)
|
| 394 |
|
| 395 |
+
logger.info(f"[LOGO] 使用示例已生成:{output_path}")
|
| 396 |
|
| 397 |
|
| 398 |
def main():
|
|
|
|
| 426 |
)
|
| 427 |
|
| 428 |
if success:
|
| 429 |
+
logger.info("[DONE] 部署成功!")
|
| 430 |
else:
|
| 431 |
+
logger.error("[FAIL] 部署失败")
|
| 432 |
|
| 433 |
|
| 434 |
if __name__ == "__main__":
|
models/__init__.py
CHANGED
|
@@ -2,10 +2,10 @@
|
|
| 2 |
Fusion 模型架构
|
| 3 |
|
| 4 |
包含:
|
| 5 |
-
- fusion_mini.py: 极简可运行版本(用于验证流程)
|
| 6 |
-
- fusion_model.py: 完整 Transformer 模型定义(SBLA + Thinking Dial)
|
| 7 |
-
- sbla_attention.py: SBLA 注意力(滑动分块潜注意力)
|
| 8 |
-
- thinking_dial.py: 动态推理强度调节器(Thinking Dial)
|
| 9 |
|
| 10 |
使用方法:
|
| 11 |
# 极简版本(字符级训练验证)
|
|
|
|
| 2 |
Fusion 模型架构
|
| 3 |
|
| 4 |
包含:
|
| 5 |
+
- fusion_mini.py: 极简可运行版本(用于验证流程)[OK] 已实现
|
| 6 |
+
- fusion_model.py: 完整 Transformer 模型定义(SBLA + Thinking Dial)[OK] 已实现
|
| 7 |
+
- sbla_attention.py: SBLA 注意力(滑动分块潜注意力)[OK] 已实现
|
| 8 |
+
- thinking_dial.py: 动态推理强度调节器(Thinking Dial)[OK] 已实现
|
| 9 |
|
| 10 |
使用方法:
|
| 11 |
# 极简版本(字符级训练验证)
|
models/fusion_mini.py
CHANGED
|
@@ -481,7 +481,7 @@ class FusionMini(PreTrainedModel):
|
|
| 481 |
|
| 482 |
if __name__ == "__main__":
|
| 483 |
# 单元测试
|
| 484 |
-
print("
|
| 485 |
|
| 486 |
# 创建配置
|
| 487 |
config = FusionMiniConfig(
|
|
@@ -492,7 +492,7 @@ if __name__ == "__main__":
|
|
| 492 |
intermediate_size=512,
|
| 493 |
)
|
| 494 |
|
| 495 |
-
print(f"
|
| 496 |
print(f" 词表大小:{config.vocab_size}")
|
| 497 |
print(f" 隐层大小:{config.hidden_size}")
|
| 498 |
print(f" 层数:{config.num_hidden_layers}")
|
|
@@ -500,7 +500,7 @@ if __name__ == "__main__":
|
|
| 500 |
# 创建模型
|
| 501 |
model = FusionMini(config)
|
| 502 |
|
| 503 |
-
print(f"\n
|
| 504 |
print(f" 参数量:{sum(p.numel() for p in model.parameters()) / 1e3:.1f}K")
|
| 505 |
|
| 506 |
# 测试前向传播
|
|
@@ -517,7 +517,7 @@ if __name__ == "__main__":
|
|
| 517 |
return_dict=True,
|
| 518 |
)
|
| 519 |
|
| 520 |
-
print(f"\n
|
| 521 |
print(f" Loss: {outputs['loss'].item():.4f}")
|
| 522 |
print(f" Logits 形状: {outputs['logits'].shape}")
|
| 523 |
|
|
@@ -527,11 +527,11 @@ if __name__ == "__main__":
|
|
| 527 |
max_new_tokens=20,
|
| 528 |
)
|
| 529 |
|
| 530 |
-
print(f"\n
|
| 531 |
print(f" 生成形状: {generated.shape}")
|
| 532 |
|
| 533 |
-
print("\n
|
| 534 |
-
print("\n
|
| 535 |
print(" 1. 使用真实数据训练这个 mini 模型")
|
| 536 |
print(" 2. 验证训练流程")
|
| 537 |
print(" 3. 然后实现 SBLA 和 Thinking Dial")
|
|
|
|
| 481 |
|
| 482 |
if __name__ == "__main__":
|
| 483 |
# 单元测试
|
| 484 |
+
print("[LOGO] 测试 Fusion Mini 模型...")
|
| 485 |
|
| 486 |
# 创建配置
|
| 487 |
config = FusionMiniConfig(
|
|
|
|
| 492 |
intermediate_size=512,
|
| 493 |
)
|
| 494 |
|
| 495 |
+
print(f"[OK] 配置创建成功")
|
| 496 |
print(f" 词表大小:{config.vocab_size}")
|
| 497 |
print(f" 隐层大小:{config.hidden_size}")
|
| 498 |
print(f" 层数:{config.num_hidden_layers}")
|
|
|
|
| 500 |
# 创建模型
|
| 501 |
model = FusionMini(config)
|
| 502 |
|
| 503 |
+
print(f"\n[OK] 模型创建成功")
|
| 504 |
print(f" 参数量:{sum(p.numel() for p in model.parameters()) / 1e3:.1f}K")
|
| 505 |
|
| 506 |
# 测试前向传播
|
|
|
|
| 517 |
return_dict=True,
|
| 518 |
)
|
| 519 |
|
| 520 |
+
print(f"\n[OK] 前向传播测试通过")
|
| 521 |
print(f" Loss: {outputs['loss'].item():.4f}")
|
| 522 |
print(f" Logits 形状: {outputs['logits'].shape}")
|
| 523 |
|
|
|
|
| 527 |
max_new_tokens=20,
|
| 528 |
)
|
| 529 |
|
| 530 |
+
print(f"\n[OK] 生成测试通过")
|
| 531 |
print(f" 生成形状: {generated.shape}")
|
| 532 |
|
| 533 |
+
print("\n[DONE] Fusion Mini 测试完成!")
|
| 534 |
+
print("\n[TIP] 下一步:")
|
| 535 |
print(" 1. 使用真实数据训练这个 mini 模型")
|
| 536 |
print(" 2. 验证训练流程")
|
| 537 |
print(" 3. 然后实现 SBLA 和 Thinking Dial")
|
scripts/add_depth3_samples.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Add depth=3 samples to mini_data.json for Thinking Dial coverage."""
|
| 3 |
-
|
| 4 |
-
import json
|
| 5 |
-
|
| 6 |
-
DEPTH_3_SAMPLES = [
|
| 7 |
-
{"prompt": "Prove the Pythagorean theorem", "response": "Consider a right triangle with legs a, b and hypotenuse c. Construct a square of side (a+b) and arrange four copies of the triangle inside. The inner quadrilateral is a square of side c, so (a+b)^2 = c^2 + 4*(ab/2), which simplifies to a^2 + b^2 = c^2.", "think_rank": 3},
|
| 8 |
-
{"prompt": "Derive the quadratic formula", "response": "Starting from ax^2 + bx + c = 0, divide by a, complete the square: (x + b/2a)^2 = (b^2 - 4ac)/4a^2. Taking the square root gives x = (-b +- sqrt(b^2 - 4ac)) / 2a.", "think_rank": 3},
|
| 9 |
-
{"prompt": "Prove that sqrt(2) is irrational", "response": "Assume sqrt(2) = p/q in lowest terms. Then 2q^2 = p^2, so p is even. Write p = 2k, then 2q^2 = 4k^2, so q^2 = 2k^2, meaning q is also even. This contradicts p/q being in lowest terms.", "think_rank": 3},
|
| 10 |
-
{"prompt": "Prove there are infinitely many primes", "response": "Suppose finitely many primes p1,...,pn exist. Let N = p1*p2*...*pn + 1. N is not divisible by any pi (remainder 1). So N is either prime itself or has a prime factor not in the list. Contradiction.", "think_rank": 3},
|
| 11 |
-
{"prompt": "Derive the derivative of sin(x)", "response": "Using the limit definition: d/dx sin(x) = lim(h->0) [sin(x+h) - sin(x)] / h. Apply the angle addition formula: sin(x+h) = sin(x)cos(h) + cos(x)sin(h). This gives lim [sin(x)(cos(h)-1)/h + cos(x)sin(h)/h] = sin(x)*0 + cos(x)*1 = cos(x).", "think_rank": 3},
|
| 12 |
-
{"prompt": "Analyze the time complexity of merge sort", "response": "Merge sort divides the array in half (O(1)), recurses on both halves (2T(n/2)), and merges in O(n). The recurrence T(n) = 2T(n/2) + O(n) solves to O(n log n) by the master theorem (case 2). This holds for worst, average, and best cases.", "think_rank": 3},
|
| 13 |
-
{"prompt": "Prove the sum of first n natural numbers is n(n+1)/2", "response": "By induction: Base case n=1: 1 = 1(2)/2 = 1. Inductive step: assume S(k) = k(k+1)/2. Then S(k+1) = S(k) + (k+1) = k(k+1)/2 + (k+1) = (k+1)(k/2 + 1) = (k+1)(k+2)/2. QED.", "think_rank": 3},
|
| 14 |
-
{"prompt": "Prove that e^x converges for all x", "response": "The Taylor series e^x = sum(x^n/n!) has ratio test: |a_(n+1)/a_n| = |x|/(n+1) -> 0 as n -> infinity. Since the limit is 0 < 1 for all x, the series converges absolutely for all real x by the ratio test.", "think_rank": 3},
|
| 15 |
-
]
|
| 16 |
-
|
| 17 |
-
def main():
|
| 18 |
-
with open('data/mini_data.json', 'r', encoding='utf-8') as f:
|
| 19 |
-
data = json.load(f)
|
| 20 |
-
|
| 21 |
-
old_dist = {}
|
| 22 |
-
for item in data:
|
| 23 |
-
r = item.get('think_rank', 0)
|
| 24 |
-
old_dist[r] = old_dist.get(r, 0) + 1
|
| 25 |
-
print(f"Before: {old_dist}")
|
| 26 |
-
|
| 27 |
-
data.extend(DEPTH_3_SAMPLES)
|
| 28 |
-
|
| 29 |
-
new_dist = {}
|
| 30 |
-
for item in data:
|
| 31 |
-
r = item.get('think_rank', 0)
|
| 32 |
-
new_dist[r] = new_dist.get(r, 0) + 1
|
| 33 |
-
print(f"After: {new_dist}")
|
| 34 |
-
|
| 35 |
-
with open('data/mini_data.json', 'w', encoding='utf-8') as f:
|
| 36 |
-
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 37 |
-
|
| 38 |
-
print(f"Total: {len(data)} items")
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
if __name__ == '__main__':
|
| 42 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/create_mini_data.py
DELETED
|
@@ -1,126 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
创建 Fusion Mini 训练数据
|
| 3 |
-
|
| 4 |
-
生成极简的训练数据(字符级),用于验证完整训练流程。
|
| 5 |
-
|
| 6 |
-
使用方法:
|
| 7 |
-
python tests/create_mini_data.py
|
| 8 |
-
|
| 9 |
-
# 会生成 data/mini_data.json
|
| 10 |
-
|
| 11 |
-
作者:zhan1206
|
| 12 |
-
项目:Fusion - 六边形开源大模型
|
| 13 |
-
许可证:Apache 2.0
|
| 14 |
-
"""
|
| 15 |
-
|
| 16 |
-
import json
|
| 17 |
-
import random
|
| 18 |
-
from pathlib import Path
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
def create_mini_dataset(output_path: str, num_samples: int = 100):
|
| 22 |
-
"""
|
| 23 |
-
创建 mini 训练数据集
|
| 24 |
-
|
| 25 |
-
参数:
|
| 26 |
-
output_path: 输出文件路径
|
| 27 |
-
num_samples: 样本数量
|
| 28 |
-
"""
|
| 29 |
-
print("[数据] 创建 mini 训练数据集...")
|
| 30 |
-
print(f" 输出路径:{output_path}")
|
| 31 |
-
print(f" 样本数量:{num_samples}")
|
| 32 |
-
|
| 33 |
-
data = []
|
| 34 |
-
|
| 35 |
-
# 预定义一些简单的中文和英文句子
|
| 36 |
-
chinese_samples = [
|
| 37 |
-
("你好", "你好!我是 Fusion Mini 模型。"),
|
| 38 |
-
("什么是人工智能", "人工智能是计算机科学的一个分支,致力于创建智能机器。"),
|
| 39 |
-
("解释机器学习", "机器学习是人工智能的子领域,使计算机能够从数据中学习。"),
|
| 40 |
-
("深度学习是什么", "深度学习是机器学习的一个分支,使用多层神经网络模拟人脑。"),
|
| 41 |
-
("什么是自然语言处理", "自然语言处理是AI的一个分支,帮助计算机理解人类语言。"),
|
| 42 |
-
("Python 有什么特点", "Python 是一种简单易学、功能强大的编程语言。"),
|
| 43 |
-
("如何学习编程", "学习编程需要理论与实践相结合,多写代码多思考。"),
|
| 44 |
-
("什么是大数据", "大数据是指规模巨大、类型多样的数据集合。"),
|
| 45 |
-
("云计算的优势", "云计算提供弹性扩展、成本节约、易于维护等优势。"),
|
| 46 |
-
("区块链的原理", "区块链是一种分布式账本技术,确保数据不可篡改。"),
|
| 47 |
-
]
|
| 48 |
-
|
| 49 |
-
english_samples = [
|
| 50 |
-
("Hello", "Hello! I am Fusion Mini model."),
|
| 51 |
-
("What is AI", "AI stands for Artificial Intelligence."),
|
| 52 |
-
("Explain machine learning", "Machine learning is a subset of AI."),
|
| 53 |
-
("What is deep learning", "Deep learning uses neural networks with many layers."),
|
| 54 |
-
("What is NLP", "NLP helps computers understand human language."),
|
| 55 |
-
("Python features", "Python is simple, powerful, and versatile."),
|
| 56 |
-
("How to learn coding", "Practice coding regularly and build projects."),
|
| 57 |
-
("What is big data", "Big data refers to extremely large datasets."),
|
| 58 |
-
("Benefits of cloud computing", "Cloud computing offers scalability and cost savings."),
|
| 59 |
-
("How blockchain works", "Blockchain is a distributed ledger technology."),
|
| 60 |
-
]
|
| 61 |
-
|
| 62 |
-
# 生成样本
|
| 63 |
-
for i in range(num_samples):
|
| 64 |
-
# 随机选择中文或英文
|
| 65 |
-
if random.random() > 0.5:
|
| 66 |
-
prompt, response = random.choice(chinese_samples)
|
| 67 |
-
else:
|
| 68 |
-
prompt, response = random.choice(english_samples)
|
| 69 |
-
|
| 70 |
-
# Assign think_rank based on content depth
|
| 71 |
-
if any(kw in prompt for kw in ["Prove", "Derive", "Analyze", "\u8bc1\u660e", "\u63a8\u5bfc", "\u5206\u6790"]):
|
| 72 |
-
think_rank = 3
|
| 73 |
-
elif any(kw in prompt for kw in ["Explain", "How", "Why", "\u89e3\u91ca", "\u5982\u4f55", "\u4e3a\u4ec0\u4e48"]):
|
| 74 |
-
think_rank = 2
|
| 75 |
-
elif any(kw in prompt for kw in ["Write", "Implement", "\u5199", "\u5b9e\u73b0"]):
|
| 76 |
-
think_rank = 1
|
| 77 |
-
else:
|
| 78 |
-
think_rank = 0
|
| 79 |
-
|
| 80 |
-
data.append({
|
| 81 |
-
"prompt": prompt,
|
| 82 |
-
"response": response,
|
| 83 |
-
"think_rank": think_rank,
|
| 84 |
-
})
|
| 85 |
-
|
| 86 |
-
# 保存为 JSON
|
| 87 |
-
output_file = Path(output_path)
|
| 88 |
-
output_file.parent.mkdir(parents=True, exist_ok=True)
|
| 89 |
-
|
| 90 |
-
with open(output_file, 'w', encoding='utf-8') as f:
|
| 91 |
-
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 92 |
-
|
| 93 |
-
print("[完成] 数据集创建成功!")
|
| 94 |
-
print(f" 文件路径:{output_path}")
|
| 95 |
-
print(f" 样本数量:{len(data)}")
|
| 96 |
-
|
| 97 |
-
# 显示几个示例
|
| 98 |
-
print("\n[示例] 数据示例:")
|
| 99 |
-
for i, item in enumerate(data[:3]):
|
| 100 |
-
print(f" [{i+1}] Prompt: {item['prompt']}")
|
| 101 |
-
print(f" Response: {item['response'][:50]}...")
|
| 102 |
-
print()
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
def main():
|
| 106 |
-
print("=" * 60)
|
| 107 |
-
print("创建 Fusion Mini 训练数据")
|
| 108 |
-
print("=" * 60)
|
| 109 |
-
|
| 110 |
-
# 创建输出目录
|
| 111 |
-
output_dir = Path("data")
|
| 112 |
-
output_dir.mkdir(exist_ok=True)
|
| 113 |
-
|
| 114 |
-
# 生成训练数据
|
| 115 |
-
output_path = output_dir / "mini_data.json"
|
| 116 |
-
create_mini_dataset(output_path, num_samples=100)
|
| 117 |
-
|
| 118 |
-
print(f"\n[完成] 数据创建完成!")
|
| 119 |
-
print(f"\n下一步:")
|
| 120 |
-
print(f" 1. 检查数据文件:{output_path}")
|
| 121 |
-
print(f" 2. 开始训练:python train/train_mini.py")
|
| 122 |
-
print(f" 3. 或者运行完整测试:python tests/run_tests.py")
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
if __name__ == "__main__":
|
| 126 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/dedup_mini_data.py
DELETED
|
@@ -1,120 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Deduplicate and expand mini_data.json to reduce duplication rate.
|
| 4 |
-
Target: <20% duplication, >= 80 unique prompts.
|
| 5 |
-
|
| 6 |
-
Author: Zhu Zizhan
|
| 7 |
-
Project: Fusion-LLM
|
| 8 |
-
License: Apache 2.0
|
| 9 |
-
"""
|
| 10 |
-
|
| 11 |
-
import json
|
| 12 |
-
import random
|
| 13 |
-
from pathlib import Path
|
| 14 |
-
|
| 15 |
-
# Diverse samples covering all think_rank levels
|
| 16 |
-
NEW_SAMPLES = [
|
| 17 |
-
# think_rank=0: Simple Q&A
|
| 18 |
-
{"prompt": "Hello", "response": "Hello! How can I help you today?", "think_rank": 0},
|
| 19 |
-
{"prompt": "What is Python", "response": "Python is a high-level programming language known for its readability and versatility.", "think_rank": 0},
|
| 20 |
-
{"prompt": "Define API", "response": "API stands for Application Programming Interface, a set of rules for software communication.", "think_rank": 0},
|
| 21 |
-
{"prompt": "What is HTML", "response": "HTML is the standard markup language for creating web pages.", "think_rank": 0},
|
| 22 |
-
{"prompt": "Name a search engine", "response": "Google is the most widely used search engine.", "think_rank": 0},
|
| 23 |
-
{"prompt": "What does CPU stand for", "response": "CPU stands for Central Processing Unit, the brain of a computer.", "think_rank": 0},
|
| 24 |
-
{"prompt": "What is RAM", "response": "RAM is Random Access Memory, used for temporary data storage during program execution.", "think_rank": 0},
|
| 25 |
-
{"prompt": "What is WiFi", "response": "WiFi is a wireless networking technology for internet connectivity.", "think_rank": 0},
|
| 26 |
-
{"prompt": "What is a database", "response": "A database is an organized collection of structured data stored electronically.", "think_rank": 0},
|
| 27 |
-
{"prompt": "What is an operating system", "response": "An operating system manages hardware and software resources on a computer.", "think_rank": 0},
|
| 28 |
-
{"prompt": "What is cloud storage", "response": "Cloud storage saves data on remote servers accessed via the internet.", "think_rank": 0},
|
| 29 |
-
{"prompt": "What is encryption", "response": "Encryption converts data into a coded format to prevent unauthorized access.", "think_rank": 0},
|
| 30 |
-
{"prompt": "What is a URL", "response": "A URL is a web address that specifies the location of a resource on the internet.", "think_rank": 0},
|
| 31 |
-
{"prompt": "What is a browser", "response": "A browser is software for accessing and viewing websites on the internet.", "think_rank": 0},
|
| 32 |
-
{"prompt": "What is open source", "response": "Open source software has source code that anyone can inspect, modify, and distribute.", "think_rank": 0},
|
| 33 |
-
|
| 34 |
-
# think_rank=1: Writing/implementation tasks
|
| 35 |
-
{"prompt": "Write a function to reverse a string", "response": "def reverse_string(s): return s[::-1]", "think_rank": 1},
|
| 36 |
-
{"prompt": "Implement a function to check if a number is prime", "response": "def is_prime(n): return n > 1 and all(n % i != 0 for i in range(2, int(n**0.5) + 1))", "think_rank": 1},
|
| 37 |
-
{"prompt": "Write a function to calculate factorial", "response": "def factorial(n): return 1 if n <= 1 else n * factorial(n - 1)", "think_rank": 1},
|
| 38 |
-
{"prompt": "Implement binary search in Python", "response": "def binary_search(arr, target):\n lo, hi = 0, len(arr) - 1\n while lo <= hi:\n mid = (lo + hi) // 2\n if arr[mid] == target: return mid\n elif arr[mid] < target: lo = mid + 1\n else: hi = mid - 1\n return -1", "think_rank": 1},
|
| 39 |
-
{"prompt": "Write a function to sort a list", "response": "def sort_list(lst): return sorted(lst)", "think_rank": 1},
|
| 40 |
-
{"prompt": "Implement a stack data structure", "response": "class Stack:\n def __init__(self): self.items = []\n def push(self, item): self.items.append(item)\n def pop(self): return self.items.pop() if self.items else None", "think_rank": 1},
|
| 41 |
-
{"prompt": "Write code to read a CSV file", "response": "import csv\nwith open('file.csv') as f:\n reader = csv.reader(f)\n for row in reader: print(row)", "think_rank": 1},
|
| 42 |
-
{"prompt": "Implement a linked list node", "response": "class Node:\n def __init__(self, val, next=None):\n self.val = val\n self.next = next", "think_rank": 1},
|
| 43 |
-
{"prompt": "Write a function to count words in text", "response": "def count_words(text): return len(text.split())", "think_rank": 1},
|
| 44 |
-
{"prompt": "Implement a simple HTTP GET request", "response": "import requests\nresponse = requests.get('https://api.example.com/data')\nprint(response.json())", "think_rank": 1},
|
| 45 |
-
|
| 46 |
-
# think_rank=2: Explain/compare/analyze
|
| 47 |
-
{"prompt": "Explain how neural networks learn", "response": "Neural networks learn through backpropagation: they compute predictions, measure error using a loss function, calculate gradients of the loss with respect to weights, and update weights using gradient descent to minimize error.", "think_rank": 2},
|
| 48 |
-
{"prompt": "Compare REST and GraphQL", "response": "REST uses fixed endpoints returning predetermined data structures. GraphQL uses a single endpoint where clients specify exactly which fields they need, reducing over-fetching but adding query complexity.", "think_rank": 2},
|
| 49 |
-
{"prompt": "Explain the difference between SQL and NoSQL", "response": "SQL databases use structured tables with fixed schemas and ACID transactions. NoSQL databases use flexible document/key-value/graph models optimized for scale and schema evolution, often trading consistency for availability.", "think_rank": 2},
|
| 50 |
-
{"prompt": "How does garbage collection work in Python", "response": "Python uses reference counting as the primary mechanism and a cyclic garbage collector for detecting and collecting reference cycles. Objects with zero references are freed immediately; cycles are detected periodically.", "think_rank": 2},
|
| 51 |
-
{"prompt": "Explain the Transformer attention mechanism", "response": "Self-attention computes queries, keys, and values from input. Attention scores are the dot product of queries and keys, scaled by sqrt(d_k), softmaxed, then multiplied by values. This lets each position attend to all other positions.", "think_rank": 2},
|
| 52 |
-
{"prompt": "Why is batch normalization important", "response": "Batch normalization stabilizes training by normalizing layer inputs to zero mean and unit variance. This reduces internal covariate shift, allows higher learning rates, and acts as a regularizer, improving convergence.", "think_rank": 2},
|
| 53 |
-
{"prompt": "How does DNS resolution work", "response": "DNS resolution follows a hierarchy: browser cache -> OS cache -> recursive resolver -> root server -> TLD server -> authoritative server. Each step either returns the answer or delegates to the next level.", "think_rank": 2},
|
| 54 |
-
{"prompt": "Explain the difference between threads and processes", "response": "Threads share memory within a process, making communication fast but requiring synchronization. Processes have separate memory spaces, providing isolation but slower inter-process communication. Threads are lighter; processes are safer.", "think_rank": 2},
|
| 55 |
-
{"prompt": "How does caching improve performance", "response": "Caching stores frequently accessed data in fast-access storage (memory vs disk). This reduces latency, decreases backend load, and improves throughput. Cache invalidation strategies (TTL, LRU) balance freshness with hit rate.", "think_rank": 2},
|
| 56 |
-
{"prompt": "Explain how gradient descent optimization works", "response": "Gradient descent iteratively updates parameters in the opposite direction of the gradient of the loss function. Learning rate controls step size. Variants include SGD (mini-batches), Adam (adaptive rates), and momentum (acceleration).", "think_rank": 2},
|
| 57 |
-
|
| 58 |
-
# think_rank=3: Prove/derive/complex analysis
|
| 59 |
-
{"prompt": "Prove the Pythagorean theorem", "response": "Consider a right triangle with legs a, b and hypotenuse c. Construct a square of side (a+b) with four copies of the triangle. The inner quadrilateral has side c and is a square, so (a+b)^2 = c^2 + 4*(ab/2), yielding a^2 + b^2 = c^2.", "think_rank": 3},
|
| 60 |
-
{"prompt": "Derive the quadratic formula", "response": "From ax^2 + bx + c = 0, divide by a, complete the square: (x + b/2a)^2 = (b^2 - 4ac)/4a^2. Taking the square root gives x = (-b +/- sqrt(b^2 - 4ac)) / 2a.", "think_rank": 3},
|
| 61 |
-
{"prompt": "Prove that sqrt(2) is irrational", "response": "Assume sqrt(2) = p/q in lowest terms. Then 2q^2 = p^2, so p is even. Write p = 2k, then 2q^2 = 4k^2, so q^2 = 2k^2, meaning q is also even. Contradiction: both p and q are even, not in lowest terms.", "think_rank": 3},
|
| 62 |
-
{"prompt": "Prove there are infinitely many primes", "response": "Suppose finitely many primes p1,...,pn. Let N = p1*p2*...*pn + 1. N is not divisible by any pi (remainder 1). So N is prime or has a prime factor not in the list. Either way, contradiction.", "think_rank": 3},
|
| 63 |
-
{"prompt": "Derive the derivative of sin(x)", "response": "Using the limit definition: d/dx sin(x) = lim(h->0) [sin(x+h) - sin(x)] / h. Apply angle addition: sin(x+h) = sin(x)cos(h) + cos(x)sin(h). This gives sin(x)(cos(h)-1)/h + cos(x)sin(h)/h -> sin(x)*0 + cos(x)*1 = cos(x).", "think_rank": 3},
|
| 64 |
-
{"prompt": "Analyze the time complexity of merge sort", "response": "Merge sort divides the array in half O(1), recurses on both halves 2T(n/2), and merges in O(n). The recurrence T(n) = 2T(n/2) + O(n) solves to O(n log n) by the master theorem case 2. This holds for worst, average, and best cases.", "think_rank": 3},
|
| 65 |
-
{"prompt": "Prove that the harmonic series diverges", "response": "Group terms: 1 + 1/2 + (1/3+1/4) + (1/5+...+1/8) + ... Each group >= 1/2 (since each of the 2^k terms in group k is >= 1/2^(k+1)). The partial sums grow without bound, so the series diverges.", "think_rank": 3},
|
| 66 |
-
{"prompt": "Derive the normal distribution formula", "response": "The normal distribution maximizes entropy subject to fixed mean and variance. Using Lagrange multipliers on the variational problem with constraints on integral, mean, and variance, we get f(x) = (1/sqrt(2*pi*sigma^2)) * exp(-(x-mu)^2/(2*sigma^2)).", "think_rank": 3},
|
| 67 |
-
|
| 68 |
-
# Chinese samples (all ranks)
|
| 69 |
-
{"prompt": "你好", "response": "你好!很高兴见到你,有什么可以帮助你的吗?", "think_rank": 0},
|
| 70 |
-
{"prompt": "什么是���法", "response": "算法是解决特定问题的一系列明确步骤或规则。", "think_rank": 0},
|
| 71 |
-
{"prompt": "什么是互联网", "response": "互联网是全球计算机网络的互联系统,提供信息交流和资源共享。", "think_rank": 0},
|
| 72 |
-
{"prompt": "什么是编程", "response": "编程是使用编程语言编写指令,让计算机执行特定任务的过程。", "think_rank": 0},
|
| 73 |
-
{"prompt": "什么是数据结构", "response": "数据结构是组织和存储数据的方式,如数组、链表、树和图。", "think_rank": 0},
|
| 74 |
-
{"prompt": "什么是人工智能", "response": "人工智能是计算机科学的一个分支,致力于创建智能机器。", "think_rank": 0},
|
| 75 |
-
{"prompt": "写一个快速排序函数", "response": "def quicksort(arr):\n if len(arr) <= 1: return arr\n pivot = arr[len(arr)//2]\n left = [x for x in arr if x < pivot]\n middle = [x for x in arr if x == pivot]\n right = [x for x in arr if x > pivot]\n return quicksort(left) + middle + quicksort(right)", "think_rank": 1},
|
| 76 |
-
{"prompt": "写一个斐波那契数列函数", "response": "def fibonacci(n):\n if n <= 1: return n\n a, b = 0, 1\n for _ in range(2, n+1):\n a, b = b, a+b\n return b", "think_rank": 1},
|
| 77 |
-
{"prompt": "实现一个简单的HTTP服务器", "response": "from http.server import HTTPServer, BaseHTTPRequestHandler\nclass Handler(BaseHTTPRequestHandler):\n def do_GET(self):\n self.send_response(200)\n self.end_headers()\n self.wfile.write(b'Hello')\nHTTPServer(('', 8000), Handler).serve_forever()", "think_rank": 1},
|
| 78 |
-
{"prompt": "解释深度学习与传统机器学习的区别", "response": "传统机器学习需要手动特征工程,模型较浅。深度学习使用多层神经网络自动学习特征表示,在图像、语音、文本等任务上表现更优,但需要更多数据和计算资源。", "think_rank": 2},
|
| 79 |
-
{"prompt": "为什么需要正则化", "response": "正则化防止模型过拟合训练数据。L1正则化产生稀疏权重(特征选择),L2正则化惩罚大权重(权重衰减)。Dropout是另一种正则化方式,随机屏蔽神经元防止共适应。", "think_rank": 2},
|
| 80 |
-
{"prompt": "解释TCP三次握手", "response": "客户端发送SYN包,服务端回复SYN-ACK包,客户端再发送ACK包确认。三次握手确保双方都具备收发能力,防止旧连接请求导致的资源浪费,建立可靠的双向通信通道。", "think_rank": 2},
|
| 81 |
-
{"prompt": "证明勾股定理", "response": "构造直角三角形三边为a,b,c。以(a+b)为边构造正方形,内部放置四个全等直角三角形,中心形成边长c的正方形。面积关系:(a+b)^2 = c^2 + 4*(ab/2),化简得a^2+b^2=c^2。", "think_rank": 3},
|
| 82 |
-
{"prompt": "推导欧拉公式", "response": "由泰勒展开:e^(ix) = 1 + ix + (ix)^2/2! + (ix)^3/3! + ... = (1-x^2/2!+...) + i(x-x^3/3!+...) = cos(x) + i*sin(x)。令x=pi得e^(i*pi) + 1 = 0。", "think_rank": 3},
|
| 83 |
-
]
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
def main():
|
| 87 |
-
data_path = Path("data/mini_data.json")
|
| 88 |
-
with open(data_path, 'r', encoding='utf-8') as f:
|
| 89 |
-
old_data = json.load(f)
|
| 90 |
-
|
| 91 |
-
# Deduplicate by prompt (keep first occurrence)
|
| 92 |
-
seen_prompts = set()
|
| 93 |
-
deduped = []
|
| 94 |
-
for item in old_data:
|
| 95 |
-
if item['prompt'] not in seen_prompts:
|
| 96 |
-
deduped.append(item)
|
| 97 |
-
seen_prompts.add(item['prompt'])
|
| 98 |
-
|
| 99 |
-
# Merge deduplicated old data with new diverse samples
|
| 100 |
-
data = deduped + list(NEW_SAMPLES)
|
| 101 |
-
|
| 102 |
-
# Count
|
| 103 |
-
from collections import Counter
|
| 104 |
-
prompts = [d['prompt'] for d in data]
|
| 105 |
-
counter = Counter(prompts)
|
| 106 |
-
dup_rate = (len(prompts) - len(counter)) / len(prompts) * 100
|
| 107 |
-
rank_dist = Counter(d['think_rank'] for d in data)
|
| 108 |
-
|
| 109 |
-
print(f"Old: {len(old_data)} items, unique: {len(set(d['prompt'] for d in old_data))}")
|
| 110 |
-
print(f"New: {len(data)} items, unique: {len(counter)}, dup rate: {dup_rate:.1f}%")
|
| 111 |
-
print(f"Think rank distribution: {dict(sorted(rank_dist.items()))}")
|
| 112 |
-
|
| 113 |
-
with open(data_path, 'w', encoding='utf-8') as f:
|
| 114 |
-
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 115 |
-
|
| 116 |
-
print(f"Written {len(data)} items to {data_path}")
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
if __name__ == '__main__':
|
| 120 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/fix_mini_data.py
DELETED
|
@@ -1,61 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Fix mini_data.json: distribute think_rank 0-3 based on prompt content."""
|
| 3 |
-
|
| 4 |
-
import json
|
| 5 |
-
import re
|
| 6 |
-
|
| 7 |
-
# Keywords suggesting different thinking depths
|
| 8 |
-
DEPTH_3_KEYWORDS = ['prove', 'theorem', 'proof', 'derive', 'mathematical', 'complex',
|
| 9 |
-
'prove', 'derive', 'calculate', 'analyze deeply',
|
| 10 |
-
'\u8bc1\u660e', '\u63a8\u5bfc', '\u5b9a\u7406', '\u590d\u6742', '\u6df1\u5165\u5206\u6790']
|
| 11 |
-
DEPTH_2_KEYWORDS = ['explain', 'why', 'how does', 'compare', 'difference',
|
| 12 |
-
'algorithm', 'design', 'optimize',
|
| 13 |
-
'\u89e3\u91ca', '\u4e3a\u4ec0\u4e48', '\u5982\u4f55', '\u6bd4\u8f83', '\u7b97\u6cd5', '\u8bbe\u8ba1', '\u4f18\u5316']
|
| 14 |
-
DEPTH_1_KEYWORDS = ['write', 'implement', 'code', 'function', 'create',
|
| 15 |
-
'\u5199', '\u5b9e\u73b0', '\u7f16\u5199', '\u4ee3\u7801', '\u521b\u5efa']
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def assign_depth(item):
|
| 19 |
-
text = (item.get('prompt', '') + ' ' + item.get('response', '')).lower()
|
| 20 |
-
for kw in DEPTH_3_KEYWORDS:
|
| 21 |
-
if kw.lower() in text:
|
| 22 |
-
return 3
|
| 23 |
-
for kw in DEPTH_2_KEYWORDS:
|
| 24 |
-
if kw.lower() in text:
|
| 25 |
-
return 2
|
| 26 |
-
for kw in DEPTH_1_KEYWORDS:
|
| 27 |
-
if kw.lower() in text:
|
| 28 |
-
return 1
|
| 29 |
-
return 0
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
def main():
|
| 33 |
-
with open('data/mini_data.json', 'r', encoding='utf-8') as f:
|
| 34 |
-
data = json.load(f)
|
| 35 |
-
|
| 36 |
-
# Count current distribution
|
| 37 |
-
old_dist = {}
|
| 38 |
-
for item in data:
|
| 39 |
-
r = item.get('think_rank', 0)
|
| 40 |
-
old_dist[r] = old_dist.get(r, 0) + 1
|
| 41 |
-
print(f"Before fix: {old_dist}")
|
| 42 |
-
|
| 43 |
-
# Fix
|
| 44 |
-
for item in data:
|
| 45 |
-
item['think_rank'] = assign_depth(item)
|
| 46 |
-
|
| 47 |
-
# Count new distribution
|
| 48 |
-
new_dist = {}
|
| 49 |
-
for item in data:
|
| 50 |
-
r = item.get('think_rank', 0)
|
| 51 |
-
new_dist[r] = new_dist.get(r, 0) + 1
|
| 52 |
-
print(f"After fix: {new_dist}")
|
| 53 |
-
|
| 54 |
-
with open('data/mini_data.json', 'w', encoding='utf-8') as f:
|
| 55 |
-
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 56 |
-
|
| 57 |
-
print(f"Fixed {len(data)} items")
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
if __name__ == '__main__':
|
| 61 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/manage_mini_data.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Manage mini_data.json: create, fix think_rank, add depth samples, dedup.
|
| 3 |
+
|
| 4 |
+
Usage:
|
| 5 |
+
python manage_mini_data.py create # Create initial dataset
|
| 6 |
+
python manage_mini_data.py fix # Re-assign think_rank by keywords
|
| 7 |
+
python manage_mini_data.py enrich # Add diverse samples for all depths
|
| 8 |
+
python manage_mini_data.py dedup # Remove duplicates
|
| 9 |
+
python manage_mini_data.py all # Run fix + enrich + dedup
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import json
|
| 13 |
+
import re
|
| 14 |
+
import sys
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
|
| 17 |
+
DATA_PATH = Path("data/mini_data.json")
|
| 18 |
+
|
| 19 |
+
# --- Depth assignment keywords ---
|
| 20 |
+
DEPTH_3_KEYWORDS = ['prove', 'theorem', 'proof', 'derive', 'mathematical', 'complex',
|
| 21 |
+
'calculate', 'analyze deeply',
|
| 22 |
+
'\u8bc1\u660e', '\u63a8\u5bfc', '\u5b9a\u7406', '\u590d\u6742', '\u6df1\u5165\u5206\u6790']
|
| 23 |
+
DEPTH_2_KEYWORDS = ['explain', 'why', 'how does', 'compare', 'difference',
|
| 24 |
+
'algorithm', 'design', 'optimize',
|
| 25 |
+
'\u89e3\u91ca', '\u4e3a\u4ec0\u4e48', '\u5982\u4f55', '\u6bd4\u8f83', '\u7b97\u6cd5', '\u8bbe\u8ba1', '\u4f18\u5316']
|
| 26 |
+
DEPTH_1_KEYWORDS = ['write', 'implement', 'code', 'function', 'create',
|
| 27 |
+
'\u5199', '\u5b9e\u73b0', '\u7f16\u5199', '\u4ee3\u7801', '\u521b\u5efa']
|
| 28 |
+
|
| 29 |
+
# --- Extra diverse samples ---
|
| 30 |
+
EXTRA_SAMPLES = [
|
| 31 |
+
# Depth 0 (simple)
|
| 32 |
+
{"prompt": "What is 2+2?", "response": "4", "think_rank": 0},
|
| 33 |
+
{"prompt": "Capital of France?", "response": "Paris", "think_rank": 0},
|
| 34 |
+
{"prompt": "Hello", "response": "Hi! How can I help you?", "think_rank": 0},
|
| 35 |
+
# Depth 1 (coding)
|
| 36 |
+
{"prompt": "Write a function to reverse a string", "response": "def reverse_string(s): return s[::-1]", "think_rank": 1},
|
| 37 |
+
{"prompt": "Write a function to calculate factorial", "response": "def factorial(n): return 1 if n <= 1 else n * factorial(n - 1)", "think_rank": 1},
|
| 38 |
+
{"prompt": "Write a function to sort a list", "response": "def sort_list(lst): return sorted(lst)", "think_rank": 1},
|
| 39 |
+
{"prompt": "Write code to read a CSV file", "response": "import csv\nwith open('file.csv') as f:\n reader = csv.reader(f)\n for row in reader: print(row)", "think_rank": 1},
|
| 40 |
+
{"prompt": "Write a function to count words in text", "response": "def count_words(text): return len(text.split())", "think_rank": 1},
|
| 41 |
+
# Depth 2 (reasoning)
|
| 42 |
+
{"prompt": "Explain how neural networks learn", "response": "Neural networks learn by adjusting weights via backpropagation. The loss function measures prediction error; gradients indicate how each weight affects loss. An optimizer (e.g., SGD, Adam) updates weights in the direction that reduces loss, iteratively improving predictions.", "think_rank": 2},
|
| 43 |
+
{"prompt": "Compare BFS and DFS graph traversal", "response": "BFS explores level by level using a queue (O(V+E)), guaranteeing shortest path in unweighted graphs. DFS goes deep before backtracking using a stack (O(V+E)), useful for topological sort and cycle detection. BFS uses more memory; DFS can be iterative or recursive.", "think_rank": 2},
|
| 44 |
+
{"prompt": "Why does quicksort have O(n^2) worst case?", "response": "Quicksort's worst case occurs when the pivot is consistently the smallest or largest element, creating maximally unbalanced partitions. Each partition takes O(n) and we need n partitions, giving O(n^2). Median-of-three or random pivot selection mitigates this.", "think_rank": 2},
|
| 45 |
+
# Depth 3 (math proof)
|
| 46 |
+
{"prompt": "Prove that sqrt(2) is irrational", "response": "Assume sqrt(2) = p/q in lowest terms. Then 2q^2 = p^2, so p is even. Write p = 2k, then 2q^2 = 4k^2, so q^2 = 2k^2, meaning q is also even. This contradicts p/q being in lowest terms.", "think_rank": 3},
|
| 47 |
+
{"prompt": "Prove there are infinitely many primes", "response": "Suppose finitely many primes p1,...,pn exist. Let N = p1*p2*...*pn + 1. N is not divisible by any pi (remainder 1). So N is either prime itself or has a prime factor not in the list. Contradiction.", "think_rank": 3},
|
| 48 |
+
{"prompt": "Derive the quadratic formula", "response": "Starting from ax^2 + bx + c = 0, divide by a, complete the square: (x + b/2a)^2 = (b^2 - 4ac)/4a^2. Taking the square root gives x = (-b +- sqrt(b^2 - 4ac)) / 2a.", "think_rank": 3},
|
| 49 |
+
{"prompt": "Analyze the time complexity of merge sort", "response": "Merge sort divides the array in half (O(1)), recurses on both halves (2T(n/2)), and merges in O(n). The recurrence T(n) = 2T(n/2) + O(n) solves to O(n log n) by the master theorem (case 2).", "think_rank": 3},
|
| 50 |
+
{"prompt": "Prove the Pythagorean theorem", "response": "Consider a right triangle with legs a, b and hypotenuse c. Construct a square of side (a+b) and arrange four copies of the triangle inside. The inner quadrilateral is a square of side c, so (a+b)^2 = c^2 + 4*(ab/2), which simplifies to a^2 + b^2 = c^2.", "think_rank": 3},
|
| 51 |
+
{"prompt": "Derive the derivative of sin(x)", "response": "Using the limit definition: d/dx sin(x) = lim(h->0) [sin(x+h) - sin(x)] / h. Apply the angle addition formula: sin(x+h) = sin(x)cos(h) + cos(x)sin(h). This gives lim [sin(x)(cos(h)-1)/h + cos(x)sin(h)/h] = sin(x)*0 + cos(x)*1 = cos(x).", "think_rank": 3},
|
| 52 |
+
# Chinese samples
|
| 53 |
+
{"prompt": "Explain the difference between TCP and UDP", "response": "TCP is connection-oriented, reliable, and ordered. UDP is connectionless, unreliable, and unordered. TCP handles retransmission and flow control, UDP does not. TCP is used for web, email; UDP for streaming, DNS.", "think_rank": 2},
|
| 54 |
+
{"prompt": "Implement a simple HTTP server in Python", "response": "from http.server import HTTPServer, BaseHTTPRequestHandler\nclass Handler(BaseHTTPRequestHandler):\n def do_GET(self):\n self.send_response(200)\n self.end_headers()\n self.wfile.write(b'Hello')\nHTTPServer(('', 8000), Handler).serve_forever()", "think_rank": 1},
|
| 55 |
+
]
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def load_data():
|
| 59 |
+
if DATA_PATH.exists():
|
| 60 |
+
with open(DATA_PATH, 'r', encoding='utf-8') as f:
|
| 61 |
+
return json.load(f)
|
| 62 |
+
return []
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def save_data(data):
|
| 66 |
+
DATA_PATH.parent.mkdir(parents=True, exist_ok=True)
|
| 67 |
+
with open(DATA_PATH, 'w', encoding='utf-8') as f:
|
| 68 |
+
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 69 |
+
print(f"Saved {len(data)} items to {DATA_PATH}")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def show_dist(data):
|
| 73 |
+
dist = {}
|
| 74 |
+
for item in data:
|
| 75 |
+
r = item.get('think_rank', 0)
|
| 76 |
+
dist[r] = dist.get(r, 0) + 1
|
| 77 |
+
print(f"Distribution: {dict(sorted(dist.items()))}")
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def assign_depth(item):
|
| 81 |
+
text = (item.get('prompt', '') + ' ' + item.get('response', '')).lower()
|
| 82 |
+
for kw in DEPTH_3_KEYWORDS:
|
| 83 |
+
if kw.lower() in text:
|
| 84 |
+
return 3
|
| 85 |
+
for kw in DEPTH_2_KEYWORDS:
|
| 86 |
+
if kw.lower() in text:
|
| 87 |
+
return 2
|
| 88 |
+
for kw in DEPTH_1_KEYWORDS:
|
| 89 |
+
if kw.lower() in text:
|
| 90 |
+
return 1
|
| 91 |
+
return 0
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def cmd_fix(data):
|
| 95 |
+
for item in data:
|
| 96 |
+
item['think_rank'] = assign_depth(item)
|
| 97 |
+
print("Fixed think_rank distribution:")
|
| 98 |
+
show_dist(data)
|
| 99 |
+
return data
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def cmd_enrich(data):
|
| 103 |
+
existing_prompts = {item['prompt'] for item in data}
|
| 104 |
+
added = 0
|
| 105 |
+
for sample in EXTRA_SAMPLES:
|
| 106 |
+
if sample['prompt'] not in existing_prompts:
|
| 107 |
+
data.append(sample)
|
| 108 |
+
existing_prompts.add(sample['prompt'])
|
| 109 |
+
added += 1
|
| 110 |
+
print(f"Added {added} new diverse samples")
|
| 111 |
+
show_dist(data)
|
| 112 |
+
return data
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def cmd_dedup(data):
|
| 116 |
+
seen = set()
|
| 117 |
+
deduped = []
|
| 118 |
+
for item in data:
|
| 119 |
+
key = item.get('prompt', '')
|
| 120 |
+
if key not in seen:
|
| 121 |
+
seen.add(key)
|
| 122 |
+
deduped.append(item)
|
| 123 |
+
removed = len(data) - len(deduped)
|
| 124 |
+
print(f"Removed {removed} duplicates")
|
| 125 |
+
return deduped
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def cmd_create(data):
|
| 129 |
+
"""Create initial mini dataset."""
|
| 130 |
+
print("Creating initial dataset...")
|
| 131 |
+
return EXTRA_SAMPLES.copy()
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def cmd_all(data):
|
| 135 |
+
data = cmd_fix(data)
|
| 136 |
+
data = cmd_enrich(data)
|
| 137 |
+
data = cmd_dedup(data)
|
| 138 |
+
return data
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
COMMANDS = {
|
| 142 |
+
'create': cmd_create,
|
| 143 |
+
'fix': cmd_fix,
|
| 144 |
+
'enrich': cmd_enrich,
|
| 145 |
+
'dedup': cmd_dedup,
|
| 146 |
+
'all': cmd_all,
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def main():
|
| 151 |
+
if len(sys.argv) < 2 or sys.argv[1] not in COMMANDS:
|
| 152 |
+
print(f"Usage: {sys.argv[0]} [{'|'.join(COMMANDS)}]")
|
| 153 |
+
sys.exit(1)
|
| 154 |
+
|
| 155 |
+
cmd = sys.argv[1]
|
| 156 |
+
data = load_data()
|
| 157 |
+
print(f"Loaded {len(data)} items")
|
| 158 |
+
show_dist(data)
|
| 159 |
+
|
| 160 |
+
result = COMMANDS[cmd](data)
|
| 161 |
+
save_data(result)
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
if __name__ == '__main__':
|
| 165 |
+
main()
|
tests/run_tests.py
CHANGED
|
@@ -24,7 +24,7 @@ from pathlib import Path
|
|
| 24 |
project_root = Path(__file__).parent.parent
|
| 25 |
sys.path.insert(0, str(project_root))
|
| 26 |
|
| 27 |
-
print("
|
| 28 |
print("=" * 50)
|
| 29 |
|
| 30 |
|
|
@@ -54,7 +54,7 @@ class TestSBLAAttention(unittest.TestCase):
|
|
| 54 |
output, _ = attn(hidden_states=x, attention_mask=attention_mask)
|
| 55 |
|
| 56 |
self.assertEqual(output.shape, (batch_size, seq_len, hidden_size))
|
| 57 |
-
print("
|
| 58 |
|
| 59 |
def test_long_sequence(self):
|
| 60 |
"""测试长序列处理"""
|
|
@@ -75,7 +75,7 @@ class TestSBLAAttention(unittest.TestCase):
|
|
| 75 |
output, _ = attn(hidden_states=x, attention_mask=attention_mask)
|
| 76 |
|
| 77 |
self.assertEqual(output.shape, (1, 8192, 256))
|
| 78 |
-
print("
|
| 79 |
|
| 80 |
|
| 81 |
class TestThinkingDial(unittest.TestCase):
|
|
@@ -92,7 +92,7 @@ class TestThinkingDial(unittest.TestCase):
|
|
| 92 |
|
| 93 |
self.assertEqual(depth, 2)
|
| 94 |
self.assertEqual(clean, "证明勾股定理")
|
| 95 |
-
print("
|
| 96 |
|
| 97 |
def test_inject_token(self):
|
| 98 |
"""测试注入控制 token"""
|
|
@@ -104,7 +104,7 @@ class TestThinkingDial(unittest.TestCase):
|
|
| 104 |
)
|
| 105 |
|
| 106 |
self.assertIn("<|think_depth_1|>", result)
|
| 107 |
-
print("
|
| 108 |
|
| 109 |
|
| 110 |
class TestBilingualFilter(unittest.TestCase):
|
|
@@ -126,7 +126,7 @@ class TestBilingualFilter(unittest.TestCase):
|
|
| 126 |
"量子纠缠是量子力学中的一种现象,指两个或多个粒子之间存在一种特殊的关联。"
|
| 127 |
))
|
| 128 |
|
| 129 |
-
print("
|
| 130 |
|
| 131 |
def test_english_filter(self):
|
| 132 |
"""测试英文质量过滤"""
|
|
@@ -139,7 +139,7 @@ class TestBilingualFilter(unittest.TestCase):
|
|
| 139 |
"Quantum entanglement is a phenomenon in quantum mechanics."
|
| 140 |
))
|
| 141 |
|
| 142 |
-
print("
|
| 143 |
|
| 144 |
|
| 145 |
class TestFusionModel(unittest.TestCase):
|
|
@@ -159,7 +159,7 @@ class TestFusionModel(unittest.TestCase):
|
|
| 159 |
model = FusionModel(config)
|
| 160 |
|
| 161 |
self.assertIsNotNone(model)
|
| 162 |
-
print("
|
| 163 |
|
| 164 |
def test_forward_pass(self):
|
| 165 |
"""测试前向传播"""
|
|
@@ -186,7 +186,7 @@ class TestFusionModel(unittest.TestCase):
|
|
| 186 |
|
| 187 |
self.assertIn("loss", outputs)
|
| 188 |
self.assertIn("logits", outputs)
|
| 189 |
-
print("
|
| 190 |
|
| 191 |
|
| 192 |
class TestDataPipeline(unittest.TestCase):
|
|
@@ -212,7 +212,7 @@ class TestDataPipeline(unittest.TestCase):
|
|
| 212 |
self.assertIn("think_rank", item)
|
| 213 |
self.assertIn(item["think_rank"], [0, 1, 2, 3])
|
| 214 |
|
| 215 |
-
print("
|
| 216 |
|
| 217 |
|
| 218 |
def run_all_tests():
|
|
@@ -258,9 +258,9 @@ def run_all_tests():
|
|
| 258 |
success = result.wasSuccessful()
|
| 259 |
|
| 260 |
if success:
|
| 261 |
-
print("\n
|
| 262 |
else:
|
| 263 |
-
print("\n
|
| 264 |
|
| 265 |
return success
|
| 266 |
|
|
|
|
| 24 |
project_root = Path(__file__).parent.parent
|
| 25 |
sys.path.insert(0, str(project_root))
|
| 26 |
|
| 27 |
+
print("[LOGO] Fusion 项目单元测试")
|
| 28 |
print("=" * 50)
|
| 29 |
|
| 30 |
|
|
|
|
| 54 |
output, _ = attn(hidden_states=x, attention_mask=attention_mask)
|
| 55 |
|
| 56 |
self.assertEqual(output.shape, (batch_size, seq_len, hidden_size))
|
| 57 |
+
print("[OK] SBLA 前向传播测试通过")
|
| 58 |
|
| 59 |
def test_long_sequence(self):
|
| 60 |
"""测试长序列处理"""
|
|
|
|
| 75 |
output, _ = attn(hidden_states=x, attention_mask=attention_mask)
|
| 76 |
|
| 77 |
self.assertEqual(output.shape, (1, 8192, 256))
|
| 78 |
+
print("[OK] SBLA 长序列测试通过")
|
| 79 |
|
| 80 |
|
| 81 |
class TestThinkingDial(unittest.TestCase):
|
|
|
|
| 92 |
|
| 93 |
self.assertEqual(depth, 2)
|
| 94 |
self.assertEqual(clean, "证明勾股定理")
|
| 95 |
+
print("[OK] Thinking Dial 解析测试通过")
|
| 96 |
|
| 97 |
def test_inject_token(self):
|
| 98 |
"""测试注入控制 token"""
|
|
|
|
| 104 |
)
|
| 105 |
|
| 106 |
self.assertIn("<|think_depth_1|>", result)
|
| 107 |
+
print("[OK] Thinking Dial 注入测试通过")
|
| 108 |
|
| 109 |
|
| 110 |
class TestBilingualFilter(unittest.TestCase):
|
|
|
|
| 126 |
"量子纠缠是量子力学中的一种现象,指两个或多个粒子之间存在一种特殊的关联。"
|
| 127 |
))
|
| 128 |
|
| 129 |
+
print("[OK] 中文过滤器测试通过")
|
| 130 |
|
| 131 |
def test_english_filter(self):
|
| 132 |
"""测试英文质量过滤"""
|
|
|
|
| 139 |
"Quantum entanglement is a phenomenon in quantum mechanics."
|
| 140 |
))
|
| 141 |
|
| 142 |
+
print("[OK] 英文过滤器测试通过")
|
| 143 |
|
| 144 |
|
| 145 |
class TestFusionModel(unittest.TestCase):
|
|
|
|
| 159 |
model = FusionModel(config)
|
| 160 |
|
| 161 |
self.assertIsNotNone(model)
|
| 162 |
+
print("[OK] Fusion 模型创建测试通过")
|
| 163 |
|
| 164 |
def test_forward_pass(self):
|
| 165 |
"""测试前向传播"""
|
|
|
|
| 186 |
|
| 187 |
self.assertIn("loss", outputs)
|
| 188 |
self.assertIn("logits", outputs)
|
| 189 |
+
print("[OK] Fusion 模型前向传播测试通过")
|
| 190 |
|
| 191 |
|
| 192 |
class TestDataPipeline(unittest.TestCase):
|
|
|
|
| 212 |
self.assertIn("think_rank", item)
|
| 213 |
self.assertIn(item["think_rank"], [0, 1, 2, 3])
|
| 214 |
|
| 215 |
+
print("[OK] 示例数据格式测试通过")
|
| 216 |
|
| 217 |
|
| 218 |
def run_all_tests():
|
|
|
|
| 258 |
success = result.wasSuccessful()
|
| 259 |
|
| 260 |
if success:
|
| 261 |
+
print("\n[DONE] 所有测试通过!")
|
| 262 |
else:
|
| 263 |
+
print("\n[FAIL] 部分测试失败,请检查代码")
|
| 264 |
|
| 265 |
return success
|
| 266 |
|
tests/test_sbla_integration.py
CHANGED
|
@@ -44,8 +44,8 @@ print()
|
|
| 44 |
|
| 45 |
# 4. 验证 SBLA 是否使用
|
| 46 |
print("[4] 验证 SBLA 注意力...")
|
| 47 |
-
|
| 48 |
-
if
|
| 49 |
print(" SBLA 注意力已集成到模型中")
|
| 50 |
else:
|
| 51 |
print(" 未检测到 SBLA 注意力(可能使用了标准注意力)")
|
|
|
|
| 44 |
|
| 45 |
# 4. 验证 SBLA 是否使用
|
| 46 |
print("[4] 验证 SBLA 注意力...")
|
| 47 |
+
has_sbla = any("SBLAttention" in str(module) for module in model.modules())
|
| 48 |
+
if has_sbla:
|
| 49 |
print(" SBLA 注意力已集成到模型中")
|
| 50 |
else:
|
| 51 |
print(" 未检测到 SBLA 注意力(可能使用了标准注意力)")
|