download_all_domains.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. #!/usr/bin/env python
  2. """
  3. 下载所有领域的数据集(每个领域 50 条样本)
  4. 领域列表:
  5. - news_en: 英文新闻 (CNN/DailyMail)
  6. - news_zh: 中文新闻 (CLUE news2016zh)
  7. - academic: 学术论文 (arXiv via Wikipedia 备用)
  8. - code: 代码 (GitHub Python)
  9. - dialogue: 对话 (OpenAssistant/oasst1)
  10. - literature: 文学作品 (Gutenberg)
  11. 用法:
  12. python database/download_all_domains.py
  13. """
  14. import os
  15. import re
  16. import json
  17. from pathlib import Path
  18. from typing import List, Optional, Callable, Dict
  19. # 设置 HuggingFace 镜像(国内加速)
  20. os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
  21. from datasets import load_dataset
  22. def preprocess_text(text: str, lang: str = "en", min_len: int = 30, max_len: int = 300) -> Optional[str]:
  23. """
  24. 预处理文本:去特殊符号、控制长度
  25. Args:
  26. text: 原始文本
  27. lang: 语言(en/zh)
  28. min_len: 最小长度(英文=词数,中文=字符数)
  29. max_len: 最大长度
  30. Returns:
  31. 预处理后的文本,None 表示不符合长度被过滤
  32. """
  33. text = text.strip()
  34. if not text:
  35. return None
  36. if lang == "en":
  37. # 英文:保留字母、数字、标点、空格
  38. text = re.sub(r'[^\w\s.,!?;:()"\']', '', text)
  39. words = text.split()
  40. if len(words) < min_len or len(words) > max_len:
  41. return None
  42. text = ' '.join(words[:max_len]).strip()
  43. else:
  44. # 中文:保留汉字、数字、标点
  45. text = re.sub(r'[^\u4e00-\u9fff0-9.,!?;:()"\',,。!?、;:""()【】《》]', '', text)
  46. chars = list(text)
  47. if len(chars) < min_len or len(chars) > max_len:
  48. return None
  49. text = ''.join(chars[:max_len]).strip()
  50. return text if text else None
  51. def save_corpus(texts: List[str], domain: str, output_dir: str = "database/corpus") -> bool:
  52. """保存语料到 JSONL 文件"""
  53. if not texts:
  54. print(f" ⚠️ 警告:没有语料可保存")
  55. return False
  56. output_path = Path(output_dir) / domain / "texts.jsonl"
  57. output_path.parent.mkdir(parents=True, exist_ok=True)
  58. with open(output_path, "w", encoding="utf-8") as f:
  59. for i, text in enumerate(texts):
  60. record = {
  61. "text": text,
  62. "metadata": {
  63. "index": i,
  64. "domain": domain,
  65. }
  66. }
  67. f.write(json.dumps(record, ensure_ascii=False) + "\n")
  68. print(f" ✅ 已保存至:{output_path} ({len(texts)} 条)")
  69. return True
  70. # ==================== 各领域加载函数 ====================
  71. def load_news_en(sample_num: int = 50) -> List[str]:
  72. """加载英文新闻语料 (CNN/DailyMail)"""
  73. corpus = []
  74. print("加载 CNN/DailyMail 数据集...")
  75. try:
  76. ds = load_dataset("cnn_dailymail", "3.0.0", split="train", trust_remote_code=True)
  77. count = 0
  78. for item in ds:
  79. if count >= 500:
  80. break
  81. count += 1
  82. text = item["article"]
  83. processed = preprocess_text(text, lang="en", min_len=30, max_len=300)
  84. if processed and processed not in corpus:
  85. corpus.append(processed)
  86. if len(corpus) >= sample_num:
  87. break
  88. print(f" 成功提取 {len(corpus)} 条英文新闻")
  89. except Exception as e:
  90. print(f" 加载失败:{e}")
  91. return corpus
  92. def load_news_zh(sample_num: int = 50) -> List[str]:
  93. """加载中文新闻语料 (CLUE news2016zh 备用:csl)"""
  94. corpus = []
  95. print("加载中文新闻数据集...")
  96. # 尝试 1: CLUE csl (中文科学文献)
  97. try:
  98. ds = load_dataset("clue", "csl", split="train", streaming=True)
  99. count = 0
  100. for item in ds:
  101. if count >= 1000:
  102. break
  103. count += 1
  104. text = item.get("text", "") or item.get("abstract", "")
  105. if text:
  106. processed = preprocess_text(text, lang="zh", min_len=50, max_len=200)
  107. if processed and processed not in corpus:
  108. corpus.append(processed)
  109. if len(corpus) >= sample_num:
  110. break
  111. if corpus:
  112. print(f" 成功提取 {len(corpus)} 条中文文献")
  113. return corpus
  114. except Exception as e:
  115. print(f" CSL 加载失败:{e}")
  116. # 尝试 2: 使用多语言维基百科
  117. print(" 使用备用方案:维基百科中文文章...")
  118. try:
  119. ds = load_dataset("wikimedia/wikipedia", "20231101.zh", split="train", streaming=True)
  120. count = 0
  121. for item in ds:
  122. if count >= 2000:
  123. break
  124. count += 1
  125. text = item.get("text", "")
  126. if text:
  127. processed = preprocess_text(text, lang="zh", min_len=50, max_len=200)
  128. if processed and processed not in corpus:
  129. corpus.append(processed)
  130. if len(corpus) >= sample_num:
  131. break
  132. print(f" 成功提取 {len(corpus)} 条中文维基百科文章")
  133. except Exception as e:
  134. print(f" 维基百科加载失败:{e}")
  135. return corpus
  136. def load_academic(sample_num: int = 50) -> List[str]:
  137. """
  138. 加载学术论文语料
  139. 使用维基百科科学类文章作为可靠数据源
  140. """
  141. corpus = []
  142. print("加载学术语料(维基百科科学文章)...")
  143. try:
  144. ds = load_dataset("wikimedia/wikipedia", "20231101.en", split="train", streaming=True)
  145. # 科学相关的关键词
  146. science_keywords = [
  147. "algorithm", "neural network", "machine learning",
  148. "computer science", "mathematics", "statistics",
  149. "artificial intelligence", "data science", "physics",
  150. "chemistry", "biology", "quantum", "relativity"
  151. ]
  152. count = 0
  153. for item in ds:
  154. if count >= 3000:
  155. break
  156. count += 1
  157. text = item.get("text", "")
  158. title = item.get("title", "")
  159. # 筛选科学相关文章
  160. if any(kw in title.lower() or kw in text[:500].lower() for kw in science_keywords):
  161. processed = preprocess_text(text, lang="en", min_len=30, max_len=300)
  162. if processed and processed not in corpus:
  163. corpus.append(processed)
  164. if len(corpus) >= sample_num:
  165. break
  166. print(f" 成功提取 {len(corpus)} 篇维基百科科学文章")
  167. except Exception as e:
  168. print(f" 加载失败:{e}")
  169. return corpus
  170. def load_code(sample_num: int = 50) -> List[str]:
  171. """加载代码语料 (KosmosCode 或其他 Python 数据集)"""
  172. corpus = []
  173. print("加载 Python 代码数据集...")
  174. # 尝试 1: KosmosCode
  175. try:
  176. from datasets import load_dataset
  177. ds = load_dataset("HuggingFaceTB/cosmopedia", "python", split="train", streaming=True)
  178. count = 0
  179. for item in ds:
  180. if count >= 500:
  181. break
  182. count += 1
  183. code = item.get("text", "") or item.get("code", "")
  184. if code:
  185. lines = code.strip().split('\n')
  186. if 5 <= len(lines) <= 80:
  187. code_clean = '\n'.join(line[:120] for line in lines[:40])
  188. if code_clean and code_clean not in corpus:
  189. corpus.append(code_clean)
  190. if len(corpus) >= sample_num:
  191. break
  192. if corpus:
  193. print(f" 成功提取 {len(corpus)} 段 Python 代码")
  194. return corpus
  195. except Exception as e:
  196. print(f" cosmopedia 加载失败:{e}")
  197. # 尝试 2: 使用 bigcode 数据集
  198. print(" 使用备用方案:bigcode/the-stack...")
  199. try:
  200. ds = load_dataset("bigcode/the-stack", data_dir="data/python", split="train", streaming=True)
  201. count = 0
  202. for item in ds:
  203. if count >= 500:
  204. break
  205. count += 1
  206. code = item.get("content", "")
  207. if code:
  208. lines = code.strip().split('\n')
  209. if 5 <= len(lines) <= 80:
  210. code_clean = '\n'.join(line[:120] for line in lines[:40])
  211. if code_clean and code_clean not in corpus:
  212. corpus.append(code_clean)
  213. if len(corpus) >= sample_num:
  214. break
  215. if corpus:
  216. print(f" 成功提取 {len(corpus)} 段 Python 代码(the-stack)")
  217. return corpus
  218. except Exception as e:
  219. print(f" the-stack 加载失败:{e}")
  220. # 尝试 3: 使用简单的 Python 代码示例
  221. print(" 使用备用方案:code alpaca...")
  222. try:
  223. ds = load_dataset("QingyiSi/Code-Alpaca-Code-Instruction-Following", split="train", streaming=True)
  224. count = 0
  225. for item in ds:
  226. if count >= 500:
  227. break
  228. count += 1
  229. code = item.get("code", "") or item.get("output", "")
  230. if code:
  231. lines = code.strip().split('\n')
  232. if 3 <= len(lines) <= 50:
  233. code_clean = '\n'.join(line[:120] for line in lines[:30])
  234. if code_clean and code_clean not in corpus:
  235. corpus.append(code_clean)
  236. if len(corpus) >= sample_num:
  237. break
  238. if corpus:
  239. print(f" 成功提取 {len(corpus)} 段 Python 代码(code-alpaca)")
  240. return corpus
  241. except Exception as e:
  242. print(f" code-alpaca 加载失败:{e}")
  243. return corpus
  244. def load_dialogue(sample_num: int = 50) -> List[str]:
  245. """加载对话语料 (OpenAssistant/oasst1)"""
  246. corpus = []
  247. print("加载 OpenAssistant/oasst1 数据集...")
  248. try:
  249. ds = load_dataset("OpenAssistant/oasst1", split="train", trust_remote_code=True)
  250. # 构建对话树
  251. messages = {}
  252. for item in ds:
  253. msg_id = item.get("message_id", "")
  254. parent_id = item.get("parent_id", "")
  255. text = item.get("text", "")
  256. role = item.get("role", "")
  257. if parent_id not in messages:
  258. messages[parent_id] = []
  259. messages[parent_id].append({
  260. "id": msg_id,
  261. "role": role,
  262. "text": text
  263. })
  264. # 提取对话
  265. for root_msgs in messages.values():
  266. if len(root_msgs) >= 2:
  267. # 合并对话
  268. dialogue_parts = []
  269. for msg in root_msgs[:4]: # 最多 4 轮
  270. dialogue_parts.append(f"{msg['role']}: {msg['text']}")
  271. dialogue = " ".join(dialogue_parts)
  272. processed = preprocess_text(dialogue, lang="en", min_len=30, max_len=300)
  273. if processed and processed not in corpus:
  274. corpus.append(processed)
  275. if len(corpus) >= sample_num:
  276. break
  277. print(f" 成功提取 {len(corpus)} 段对话")
  278. except Exception as e:
  279. print(f" 加载失败:{e}")
  280. return corpus
  281. def load_literature(sample_num: int = 50) -> List[str]:
  282. """加载文学语料 (English Wikipedia 作为备用)"""
  283. corpus = []
  284. print("加载文学语料...")
  285. # 尝试 1: 使用 pile-cc 或其他文学类数据集
  286. try:
  287. ds = load_dataset("Z-Code/BookCorpus", split="train", streaming=True)
  288. count = 0
  289. for item in ds:
  290. if count >= 1000:
  291. break
  292. count += 1
  293. text = item.get("text", "")
  294. if text:
  295. processed = preprocess_text(text, lang="en", min_len=30, max_len=300)
  296. if processed and processed not in corpus:
  297. corpus.append(processed)
  298. if len(corpus) >= sample_num:
  299. break
  300. if corpus:
  301. print(f" 成功提取 {len(corpus)} 段文学文本")
  302. return corpus
  303. except Exception as e:
  304. print(f" BookCorpus 加载失败:{e}")
  305. # 尝试 2: 使用英文维基百科中的人文/文学类文章
  306. print(" 使用备用方案:维基百科英文文章(人文类)...")
  307. try:
  308. ds = load_dataset("wikimedia/wikipedia", "20231101.en", split="train", streaming=True)
  309. # 人文/文学相关的关键词
  310. literature_keywords = [
  311. "novel", "poetry", "fiction", "literature", "shakespeare",
  312. "dickens", "austen", "tolkien", "hemingway", "orwell",
  313. "pride and prejudice", "great gatsby", "1984", "lord of the rings"
  314. ]
  315. count = 0
  316. for item in ds:
  317. if count >= 3000:
  318. break
  319. count += 1
  320. text = item.get("text", "")
  321. title = item.get("title", "")
  322. # 筛选文学相关文章
  323. if any(kw in title.lower() or kw in text[:500].lower() for kw in literature_keywords):
  324. processed = preprocess_text(text, lang="en", min_len=30, max_len=300)
  325. if processed and processed not in corpus:
  326. corpus.append(processed)
  327. if len(corpus) >= sample_num:
  328. break
  329. print(f" 成功提取 {len(corpus)} 段文学文本(备用)")
  330. except Exception as e:
  331. print(f" 维基百科加载失败:{e}")
  332. return corpus
  333. # ==================== 主流程 ====================
  334. DOMAIN_LOADERS: Dict[str, Callable] = {
  335. "news_en": load_news_en,
  336. "news_zh": load_news_zh,
  337. "academic": load_academic,
  338. "code": load_code,
  339. "dialogue": load_dialogue,
  340. "literature": load_literature,
  341. }
  342. def main():
  343. print("=" * 60)
  344. print("数据集下载 - 完整语料库(每个领域 50 条样本)")
  345. print("=" * 60)
  346. print()
  347. domains = list(DOMAIN_LOADERS.keys())
  348. sample_num = 50
  349. print(f"目标领域:{domains}")
  350. print(f"每个领域采样数:{sample_num}")
  351. print(f"输出目录:database/corpus/")
  352. print()
  353. results = {}
  354. for domain in domains:
  355. print(f"\n{'='*40}")
  356. print(f"[{domain}]")
  357. print(f"{'='*40}")
  358. loader = DOMAIN_LOADERS[domain]
  359. texts = loader(sample_num)
  360. if texts:
  361. success = save_corpus(texts, domain)
  362. results[domain] = len(texts) if success else 0
  363. else:
  364. print(f" ⚠️ 警告:未能获取有效语料")
  365. results[domain] = 0
  366. # 汇总
  367. print("\n" + "=" * 60)
  368. print("下载完成! 汇总:")
  369. print("=" * 60)
  370. for domain, count in results.items():
  371. status = "✅" if count > 0 else "❌"
  372. print(f" {status} {domain}: {count} 条")
  373. total = sum(results.values())
  374. print(f"\n总计:{total} 条")
  375. print()
  376. print("下一步:提取模型表示")
  377. print(" python model/extract_representations.py")
  378. if __name__ == "__main__":
  379. main()