训练数据:大模型能力的真正来源
“Garbage in, garbage out”——这句话在大模型领域体现得淋漓尽致。2026 年的研究表明,数据质量对模型性能的影响超过了参数量和计算量。本文系统解析从原始网页数据到高质量训练语料的完整清洗流程。
一、原始数据来源
1.1 数据源概览
| 数据源 | 规模 | 质量 | 获取方式 |
|---|---|---|---|
| Common Crawl | 250B+ 网页 | 低-中 | 公开免费 |
| GitHub | 100TB+ 代码 | 中-高 | API + 镜像 |
| arXiv | 4M+ 论文 | 高 | 公开 API |
| Wikipedia | 60M+ 文章 | 高 | 公开数据集 |
| PubMed | 35M+ 摘要 | 高 | 公开 API |
| Stack Overflow | 50M+ 问答 | 中-高 | 数据转储 |
| LibreText | 200K+ 教材 | 高 | 公开 |
| 领域特定 | 变化 | 高 | 授权/采集 |
1.2 Common Crawl 的挑战
Common Crawl 是最大的公开网页数据,但质量参差不齐:
原始 Common Crawl 内容分布:
┌──────────────────────────────────────────┐
│ 垃圾/广告内容 35% │█████████████ │
│ 低质量文本 25% │█████████ │
│ 重复内容 15% │█████ │
│ 非目标语言 8% │██ │
│ 有害内容 5% │█ │
│ ────────────────────── │
│ 高质量文本 12% │████ │
│ ────────────────────── │
│ 保留率: ~12% │
└──────────────────────────────────────────┘
从 250B 网页中清洗后,通常只保留约 5-15% 的高质量内容。
二、数据清洗流水线
2.1 总体流程
原始数据 → 1.格式提取 → 2.语言识别 → 3.去重 → 4.质量过滤 → 5.安全过滤 → 6.数据配比 → 训练语料
2.2 格式提取
从 HTML 中提取正文内容:
def extract_text(html):
# 1. 移除脚本和样式
soup = BeautifulSoup(html, 'html.parser')
for tag in soup(['script', 'style', 'nav', 'footer', 'header']):
tag.decompose()
# 2. 提取正文
text = soup.get_text(separator='\n')
# 3. 清理空白
lines = [line.strip() for line in text.split('\n')]
text = '\n'.join(line for line in lines if line)
# 4. 移除 boilerplate (模板文字)
text = remove_boilerplate(text)
return text
2.3 语言识别
使用 fastText 或 CLD3 进行语言分类:
import fasttext
model = fasttext.load_model('lid.176.bin')
def identify_language(text, threshold=0.5):
predictions = model.predict(text, k=1)
lang, confidence = predictions[0][0].replace('__label__', ''), predictions[1][0]
if confidence < threshold:
return 'unknown'
return lang
# 保留目标语言
target_langs = {'zh', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'ru', 'ar'}
2.4 去重(Deduplication)
去重是数据清洗中最重要的步骤,分为三个层级:
2.4.1 精确去重(Exact Deduplication)
使用哈希值检测完全相同的文档:
def exact_dedup(documents):
seen_hashes = set()
unique_docs = []
for doc in documents:
h = hashlib.md5(doc.encode()).hexdigest()
if h not in seen_hashes:
seen_hashes.add(h)
unique_docs.append(doc)
return unique_docs
2.4.2 模糊去重(Fuzzy Deduplication)
使用 MinHash + LSH 检测高度相似但不完全相同的文档:
from datasketch import MinHash, MinHashLSH
def fuzzy_dedup(documents, threshold=0.8, num_perm=128):
lsh = MinHashLSH(threshold=threshold, num_perm=num_perm)
for i, doc in enumerate(documents):
m = MinHash(num_perm=num_perm)
for word in doc.split():
m.update(word.encode('utf-8'))
lsh.insert(i, m)
# 保留每组相似文档中的一个
seen = set()
unique_docs = []
for i, doc in enumerate(documents):
if i in seen:
continue
m = MinHash(num_perm=num_perm)
for word in doc.split():
m.update(word.encode('utf-8'))
similar = lsh.query(m)
for j in similar[1:]: # 跳过自己
seen.add(j)
unique_docs.append(doc)
return unique_docs
2.4.3 段落级去重
文档级去重不够——同一段落可能出现在不同文档中:
def paragraph_dedup(documents, threshold=0.9):
para_lsh = MinHashLSH(threshold=threshold, num_perm=64)
para_map = {} # paragraph hash -> first occurrence
for doc_id, doc in enumerate(documents):
paragraphs = doc.split('\n\n')
clean_paragraphs = []
for para in paragraphs:
if len(para) < 50: # 太短的段落跳过
clean_paragraphs.append(para)
continue
m = MinHash(num_perm=64)
for word in para.split():
m.update(word.encode('utf-8'))
similar = para_lsh.query(m)
if not similar:
para_lsh.insert(f"{doc_id}_{len(clean_paragraphs)}", m)
clean_paragraphs.append(para)
# else: 跳过重复段落
documents[doc_id] = '\n\n'.join(clean_paragraphs)
return documents
去重效果:
| 去重层级 | 数据保留率 | 重复率 |
|---|---|---|
| 原始 | 100% | ~45% |
| 精确去重后 | 75% | ~25% |
| 模糊去重后 | 55% | ~8% |
| 段落级去重后 | 45% | ~3% |
2.5 质量过滤
2.5.1 启发式规则过滤
def heuristic_filter(text):
# 1. 长度过滤
if len(text) < 200 or len(text) > 100000:
return False
# 2. 语言模型困惑度 (太低=模板, 太高=乱码)
ppl = compute_perplexity(text)
if ppl < 5 or ppl > 500:
return False
# 3. 符号比例
symbol_ratio = count_symbols(text) / len(text)
if symbol_ratio > 0.1:
return False
# 4. 平均行长度
avg_line_len = len(text) / text.count('\n')
if avg_line_len < 10 or avg_line_len > 500:
return False
# 5. 重复行比例
lines = text.split('\n')
unique_ratio = len(set(lines)) / len(lines)
if unique_ratio < 0.5:
return False
return True
2.5.2 模型质量打分
使用小模型(如 fasttext 分类器)对文本质量打分:
class QualityScorer:
def __init__(self):
# 用 Wikipedia + 高质量数据训练的分类器
self.classifier = fasttext.load_model('quality_classifier.bin')
def score(self, text):
result = self.classifier.predict(text[:512])
label, confidence = result[0][0], result[1][0]
return confidence if label == '__label__high' else 1 - confidence
def filter(self, documents, threshold=0.5):
return [doc for doc in documents if self.score(doc) > threshold]
2.5.3 LLM 质量评估
2026 年的新方法:使用 LLM 对数据进行质量评估:
def llm_quality_filter(text, judge_model):
prompt = f"""Rate the following text on a scale of 1-10 for:
- Information density
- Factual accuracy
- Writing quality
- Educational value
Text: {text[:1000]}
Output only the scores as JSON."""
response = judge_model.generate(prompt)
scores = json.loads(response)
avg_score = sum(scores.values()) / len(scores)
return avg_score >= 6.0
2.6 安全过滤
class SafetyFilter:
def __init__(self):
self.pii_patterns = [
r'\b\d{3}-\d{2}-\d{4}\b', # SSN
r'\b\d{16}\b', # Credit card
r'\b[\w.-]+@[\w.-]+\.\w+\b', # Email
r'\b\d{11}\b', # Phone number
]
self.toxic_classifier = load_toxic_classifier()
def filter(self, text):
# 1. PII 脱敏
for pattern in self.pii_patterns:
text = re.sub(pattern, '[REDACTED]', text)
# 2. 有害内容过滤
toxicity = self.toxic_classifier.predict(text)
if toxicity > 0.7:
return None
# 3. 版权内容检测
if self.is_copyrighted(text):
return None
return text
三、数据配比
3.1 配比对模型能力的影响
| 配比方案 | MMLU | 代码能力 | 中文能力 |
|---|---|---|---|
| 60%网页+20%代码+15%学术+5%其他 | 82.1 | 71.3 | 78.5 |
| 40%网页+30%代码+20%数学+10%其他 | 88.5 | 82.1 | 76.2 |
| 30%网页+25%代码+25%学术+20%多语言 | 86.8 | 78.5 | 85.3 |
数据配比直接决定了模型在不同能力上的表现。
3.2 动态配比策略
2026 年的最佳实践是动态配比——在训练过程中调整数据分布:
class DynamicDataMixer:
def __init__(self, total_steps=300000):
self.total_steps = total_steps
self.schedule = {
# (start_step, end_step): {source: ratio}
(0, 50000): { # 阶段1: 基础语言能力
"web": 0.50, "code": 0.20, "academic": 0.15, "book": 0.15
},
(50000, 150000): { # 阶段2: 增加代码和数学
"web": 0.35, "code": 0.30, "academic": 0.20, "book": 0.15
},
(150000, 300000): { # 阶段3: 高质量数据精修
"web": 0.25, "code": 0.25, "academic": 0.25, "book": 0.15, "synthetic": 0.10
}
}
def get_distribution(self, current_step):
for (start, end), dist in self.schedule.items():
if start <= current_step < end:
return dist
return self.schedule[(0, 50000)]
四、代码数据清洗
代码数据需要特殊处理:
class CodeDataCleaner:
def clean(self, repo_name, files):
cleaned = []
for file_path, content in files:
# 1. 过滤自动生成文件
if self.is_auto_generated(content):
continue
# 2. 过滤大文件 (如 minified JS)
if len(content) / len(content.split('\n')) > 500:
continue
# 3. 移除注释中的个人信息
content = self.remove_pii_from_comments(content)
# 4. 质量打分 (基于 GitHub stars, commit 数等)
quality_score = self.score_repo_quality(repo_name)
if quality_score < 0.3:
continue
# 5. 保留有意义的代码结构
if self.has_meaningful_structure(content):
cleaned.append((file_path, content))
return cleaned
五、合成数据生成
5.1 合成数据的角色
2026 年,合成数据在训练数据中的占比逐渐增加:
| 数据类型 | 2023 | 2024 | 2025 | 2026 |
|---|---|---|---|---|
| 真实数据 | 95% | 85% | 70% | 50% |
| 合成数据 | 5% | 15% | 30% | 50% |
5.2 合成数据的质量控制
class SyntheticDataPipeline:
def generate_and_verify(self, topic, n_samples):
# 1. 生成
samples = self.teacher.generate_batch(topic, n=n_samples * 2)
# 2. 过滤低质量
filtered = [s for s in samples if self.quality_scorer(s) > 0.7]
# 3. 去重
unique = self.dedup(filtered)
# 4. 验证正确性 (对数学/代码)
verified = [s for s in unique if self.verify_correctness(s)]
# 5. 多样性筛选
diverse = self.diversity_filter(verified, target=n_samples)
return diverse
六、总结
训练数据清洗是大模型工程中最被低估的环节:
- 去重是最重要的步骤——减少 50%+ 的冗余数据
- 质量过滤决定模型上限——“质量胜于数量”
- 数据配比决定能力分布——需要根据目标调整
- 合成数据是未来方向——2026 年占比已达 50%
- 安全过滤不可忽视——PII、有害内容、版权
好的数据是好的模型的基础。在 Scaling Laws 逐渐饱和的时代,数据质量是新的 Scaling 方向。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
