extract_glue_representations.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #!/usr/bin/env python
  2. """
  3. 提取 GLUE 领域的模型表示(限制 5000 条样本)
  4. """
  5. import os
  6. os.environ["PYTORCH_ALLOC_CONF"] = "expandable_segments:True"
  7. import gc
  8. import torch
  9. import json
  10. from pathlib import Path
  11. from typing import List
  12. from transformers import AutoTokenizer, AutoModel
  13. import warnings
  14. warnings.filterwarnings("ignore")
  15. def load_texts(filepath: str, limit: int = 5000) -> List[str]:
  16. """从 JSONL 文件加载文本,限制数量"""
  17. texts = []
  18. with open(filepath, "r", encoding="utf-8") as f:
  19. for i, line in enumerate(f):
  20. if i >= limit:
  21. break
  22. record = json.loads(line.strip())
  23. texts.append(record.get("text", ""))
  24. return texts
  25. def extract_representations(
  26. model_path: str,
  27. texts: List[str],
  28. batch_size: int = 8,
  29. max_length: int = 512
  30. ) -> torch.Tensor:
  31. """提取模型表示"""
  32. print(f" 加载模型:{model_path}")
  33. tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
  34. if tokenizer.pad_token is None:
  35. tokenizer.pad_token = tokenizer.eos_token
  36. model = AutoModel.from_pretrained(
  37. model_path,
  38. torch_dtype=torch.float32,
  39. device_map="auto",
  40. trust_remote_code=True,
  41. output_hidden_states=True
  42. )
  43. model.eval()
  44. print(f" 模型设备:{model.device}")
  45. print(f" 文本数量:{len(texts)}")
  46. all_embeddings = []
  47. with torch.no_grad():
  48. for i in range(0, len(texts), batch_size):
  49. batch_texts = texts[i:i + batch_size]
  50. inputs = tokenizer(
  51. batch_texts,
  52. return_tensors="pt",
  53. padding=True,
  54. truncation=True,
  55. max_length=max_length
  56. )
  57. inputs = inputs.to(model.device)
  58. outputs = model(**inputs)
  59. hidden = outputs.last_hidden_state
  60. attention_mask = inputs["attention_mask"].unsqueeze(-1).float()
  61. sum_embeddings = (hidden * attention_mask).sum(dim=1)
  62. valid_tokens = attention_mask.sum(dim=1)
  63. mean_embeddings = sum_embeddings / (valid_tokens + 1e-9)
  64. all_embeddings.append(mean_embeddings.cpu())
  65. if (i // batch_size + 1) % 100 == 0:
  66. print(f" 进度:{min(i + batch_size, len(texts))}/{len(texts)}")
  67. embeddings = torch.cat(all_embeddings, dim=0)
  68. H = embeddings.T
  69. del model, tokenizer
  70. if torch.cuda.is_available():
  71. torch.cuda.empty_cache()
  72. return H
  73. def save_representation(H: torch.Tensor, model_name: str, domain: str, output_dir: str = "model/representations") -> str:
  74. """保存表示矩阵"""
  75. output_path = Path(output_dir) / f"{model_name}_{domain}.pt"
  76. output_path.parent.mkdir(parents=True, exist_ok=True)
  77. torch.save(H, output_path)
  78. meta_path = output_path.with_suffix(".json")
  79. meta = {
  80. "model_name": model_name,
  81. "domain": domain,
  82. "d": int(H.shape[0]),
  83. "N": int(H.shape[1]),
  84. "dtype": str(H.dtype)
  85. }
  86. with open(meta_path, "w") as f:
  87. json.dump(meta, f, indent=2)
  88. return str(output_path)
  89. def get_model_name(model_path: str) -> str:
  90. """从路径提取模型简称"""
  91. path = Path(model_path)
  92. name = path.name.lower()
  93. if "qwen" in name:
  94. return "qwen_7b"
  95. elif "mistral" in name:
  96. return "mistral_7b"
  97. elif "llama3" in name or "llama-3" in name:
  98. return "llama3_8b"
  99. return path.name.replace("-", "_")
  100. def main():
  101. print("=" * 70)
  102. print("GLUE 领域模型表示提取(限制 5000 条样本)")
  103. print("=" * 70)
  104. print()
  105. models = [
  106. "model/weights/Qwen2.5-7B-Instruct",
  107. "model/weights/Mistral-7B-v0.3",
  108. "model/weights/llama3-8b",
  109. ]
  110. # 仅 GLUE 领域
  111. glue_domains = [
  112. ("glue_mnli", "database/corpus/glue_mnli/texts.jsonl"),
  113. ("glue_qnli", "database/corpus/glue_qnli/texts.jsonl"),
  114. ("glue_sst2", "database/corpus/glue_sst2/texts.jsonl"),
  115. ("glue_stsb", "database/corpus/glue_stsb/texts.jsonl"),
  116. ("glue_cola", "database/corpus/glue_cola/texts.jsonl"),
  117. ]
  118. limit = 5000
  119. for model_path in models:
  120. if not Path(model_path).exists():
  121. print(f"跳过(模型不存在):{model_path}")
  122. continue
  123. model_name = get_model_name(model_path)
  124. print(f"\n[模型:{model_name}]")
  125. for domain, texts_path in glue_domains:
  126. print(f"\n [{domain}]")
  127. texts = load_texts(texts_path, limit=limit)
  128. print(f" 加载了 {len(texts)} 条文本(限制:{limit})")
  129. if len(texts) == 0:
  130. print(" 跳过:没有文本")
  131. continue
  132. H = extract_representations(model_path, texts, batch_size=8)
  133. print(f" 表示矩阵形状:{H.shape}")
  134. output_path = save_representation(H, model_name, domain)
  135. print(f" 已保存:{output_path}")
  136. del H
  137. gc.collect()
  138. if torch.cuda.is_available():
  139. torch.cuda.empty_cache()
  140. print()
  141. print("=" * 70)
  142. print("GLUE 领域提取完成!")
  143. print("=" * 70)
  144. if __name__ == "__main__":
  145. main()