| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- #!/usr/bin/env python
- """
- 数据集下载脚本 - 下载各领域语料并保存到 database/corpus
- 用法:
- # 下载单个领域(测试链路)
- python download_data.py news_en --sample-num 10
- # 下载所有领域
- python download_data.py all --sample-num 50
- 领域列表:
- - news_en: 英文新闻 (CNN/DailyMail)
- - news_zh: 中文新闻 (CLUE news2016zh)
- - academic: 学术论文 (arXiv)
- - code: 代码 (GitHub Python)
- - dialogue: 对话 (OpenAssistant)
- - literature: 文学作品 (Gutenberg)
- """
- import os
- import re
- import json
- import random
- import argparse
- from pathlib import Path
- from typing import List, Optional, Dict
- # 设置 HuggingFace 镜像(国内加速)
- os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
- from datasets import load_dataset
- def preprocess_text(text: str, lang: str = "en", min_len: int = 100, max_len: int = 200) -> Optional[str]:
- """
- 统一预处理文本:去特殊符号、控制长度
- Args:
- text: 原始文本
- lang: 语言(en/zh)
- min_len: 最小长度(英文=词数,中文=字符数)
- max_len: 最大长度
- Returns:
- 预处理后的文本,None 表示不符合长度被过滤
- """
- text = text.strip()
- if not text:
- return None
- if lang == "en":
- # 英文:保留字母、数字、标点、空格
- text = re.sub(r'[^\w\s.,!?;:()"\']', '', text)
- words = text.split()
- if len(words) < min_len or len(words) > max_len:
- return None
- text = ' '.join(words[:max_len]).strip()
- else:
- # 中文:保留汉字、数字、标点
- text = re.sub(r'[^\u4e00-\u9fff0-9.,!?;:()""\']', '', text)
- chars = list(text)
- if len(chars) < min_len or len(chars) > max_len:
- return None
- text = ''.join(chars[:max_len]).strip()
- return text if text else None
- def load_news_en(sample_num: int = 50) -> List[str]:
- """加载英文新闻语料 (CNN/DailyMail)"""
- corpus = []
- print("加载 CNN/DailyMail 数据集...")
- try:
- ds = load_dataset("cnn_dailymail", "3.0.0", split="train")
- texts = [item["article"] for item in random.sample(list(ds), min(200, len(ds)))]
- for text in texts:
- processed = preprocess_text(text, lang="en")
- if processed and processed not in corpus:
- corpus.append(processed)
- if len(corpus) >= sample_num:
- break
- print(f" 成功提取 {len(corpus)} 条英文新闻")
- except Exception as e:
- print(f" 加载失败:{e}")
- return corpus
- def load_news_zh(sample_num: int = 50) -> List[str]:
- """加载中文新闻语料 (CLUE news2016zh)"""
- corpus = []
- print("加载 CLUE news2016zh 数据集...")
- try:
- ds = load_dataset("clue", "news2016zh", split="train")
- texts = [item["text"] for item in random.sample(list(ds), min(200, len(ds)))]
- for text in texts:
- processed = preprocess_text(text, lang="zh")
- if processed and processed not in corpus:
- corpus.append(processed)
- if len(corpus) >= sample_num:
- break
- print(f" 成功提取 {len(corpus)} 条中文新闻")
- except Exception as e:
- print(f" 加载失败:{e}")
- return corpus
- def load_academic(sample_num: int = 50) -> List[str]:
- """加载学术论文语料 (arXiv)"""
- corpus = []
- print("加载 arXiv 数据集...")
- try:
- ds = load_dataset("arxiv_dataset", split="train")
- texts = []
- for item in random.sample(list(ds), min(300, len(ds))):
- categories = item.get("categories", "")
- update_date = item.get("update_date", "")
- year = update_date.split("-")[0] if update_date else "0"
- if categories in ["cs.CL", "cs.LG", "stat.ML"] and year >= "2023":
- texts.append(item.get("abstract", ""))
- for text in texts:
- processed = preprocess_text(text, lang="en")
- if processed and processed not in corpus:
- corpus.append(processed)
- if len(corpus) >= sample_num:
- break
- print(f" 成功提取 {len(corpus)} 篇论文摘要")
- except Exception as e:
- print(f" 加载失败:{e}")
- return corpus
- def load_code(sample_num: int = 50) -> List[str]:
- """加载代码语料 (GitHub Python)"""
- corpus = []
- print("加载 codeparrot/github-code 数据集...")
- try:
- ds = load_dataset("codeparrot/github-code", streaming=True, split="train")
- count = 0
- for item in ds:
- if count >= 300:
- break
- count += 1
- lang = item.get("language", "")
- path = item.get("path", "").lower()
- if lang == "Python" and ".py" in path:
- code = item.get("code", "")
- processed = preprocess_text(code, lang="en")
- if processed and processed not in corpus:
- corpus.append(processed)
- if len(corpus) >= sample_num:
- break
- print(f" 成功提取 {len(corpus)} 段代码")
- except Exception as e:
- print(f" 加载失败:{e}")
- return corpus
- def load_dialogue(sample_num: int = 50) -> List[str]:
- """加载对话语料 (OpenAssistant)"""
- corpus = []
- print("加载 OpenAssistant/oasst1 数据集...")
- try:
- ds = load_dataset("OpenAssistant/oasst1", split="train")
- # 收集对话
- conversations = {}
- for item in random.sample(list(ds), min(200, len(ds))):
- msg_id = item.get("message_id", "")
- parent_id = item.get("parent_id", "")
- text = item.get("text", "")
- role = item.get("role", "")
- if parent_id and parent_id in conversations:
- conversations[parent_id].append(f"{role}: {text}")
- else:
- conversations[msg_id] = [f"{role}: {text}"]
- # 合并对话
- for msgs in conversations.values():
- if len(msgs) >= 2:
- text = " ".join(msgs[:4]) # 最多 4 轮
- processed = preprocess_text(text, lang="en")
- if processed and processed not in corpus:
- corpus.append(processed)
- if len(corpus) >= sample_num:
- break
- print(f" 成功提取 {len(corpus)} 段对话")
- except Exception as e:
- print(f" 加载失败:{e}")
- return corpus
- def load_literature(sample_num: int = 50) -> List[str]:
- """加载文学语料 (Gutenberg)"""
- corpus = []
- print("加载 gutenberg_english 数据集...")
- try:
- ds = load_dataset("gutenberg_english", split="train")
- texts = [item["text"] for item in random.sample(list(ds), min(200, len(ds)))]
- for text in texts:
- processed = preprocess_text(text, lang="en")
- if processed and processed not in corpus:
- corpus.append(processed)
- if len(corpus) >= sample_num:
- break
- print(f" 成功提取 {len(corpus)} 段文学文本")
- except Exception as e:
- print(f" 加载失败:{e}")
- return corpus
- # 领域加载器映射
- DOMAIN_LOADERS = {
- "news_en": load_news_en,
- "news_zh": load_news_zh,
- "academic": load_academic,
- "code": load_code,
- "dialogue": load_dialogue,
- "literature": load_literature,
- }
- def save_corpus(texts: List[str], domain: str, output_dir: str = "database/corpus") -> None:
- """保存语料到 JSONL 文件"""
- output_path = Path(output_dir) / domain / "texts.jsonl"
- output_path.parent.mkdir(parents=True, exist_ok=True)
- with open(output_path, "w", encoding="utf-8") as f:
- for i, text in enumerate(texts):
- record = {
- "text": text,
- "metadata": {
- "index": i,
- "domain": domain,
- }
- }
- f.write(json.dumps(record, ensure_ascii=False) + "\n")
- print(f" 已保存至:{output_path}")
- def main():
- parser = argparse.ArgumentParser(description="下载语料数据集")
- parser.add_argument(
- "domain",
- type=str,
- choices=list(DOMAIN_LOADERS.keys()) + ["all"],
- help="领域标识,或 'all' 下载所有"
- )
- parser.add_argument(
- "--sample-num",
- type=int,
- default=10,
- help="每个领域采样数量 (默认:10)"
- )
- parser.add_argument(
- "--output-dir",
- type=str,
- default="database/corpus",
- help="输出目录 (默认:database/corpus)"
- )
- args = parser.parse_args()
- print("=" * 50)
- print("语料数据集下载")
- print("=" * 50)
- print(f"目标领域:{args.domain}")
- print(f"采样数量:{args.sample_num}")
- print(f"输出目录:{args.output_dir}")
- print()
- if args.domain == "all":
- domains = list(DOMAIN_LOADERS.keys())
- else:
- domains = [args.domain]
- for domain in domains:
- print(f"\n[{domain}]")
- loader = DOMAIN_LOADERS[domain]
- texts = loader(args.sample_num)
- if texts:
- save_corpus(texts, domain, args.output_dir)
- else:
- print(f" 警告:未能获取有效语料")
- print("\n" + "=" * 50)
- print("下载完成!")
- print("=" * 50)
- if __name__ == "__main__":
- main()
|