LLM训练 23 分钟阅读

为LLM训练准备数据:Reader API如何简化网络内容获取

训练大语言模型需要海量高质量数据。Reader API将复杂的网页转换为干净的训练数据,大幅简化数据准备流程。探索如何高效构建LLM训练数据集。

8,917 字

训练一个高质量的大语言模型(LLM),你需要什么?

算力?当然。模型架构?必须的。但最关键的是:数据

海量、干净、多样化的文本数据。

问题是:如何获取这些数据?

传统方式是爬虫 + 清洗,耗时耗力。现在,有更好的方法。

LLM训练数据的要求

数量要求

规模

  • 小模型(1B参数):10-50GB文本
  • 中模型(7B参数):100-500GB文本
  • 大模型(70B+参数):数TB文本

来源

  • 网页内容
  • 书籍和文章
  • 代码仓库
  • 对话数据

质量要求

不是所有文本都适合训练:

好的训练数据

  • 语法正确
  • 逻辑清晰
  • 信息丰富
  • 格式统一

差的训练数据

  • 充满广告和垃圾信息
  • 格式混乱(HTML标签、乱码)
  • 重复内容
  • 低质量机器生成文本

多样性要求

领域多样性

  • 新闻
  • 科技
  • 医疗
  • 法律
  • 文学
  • 日常对话

风格多样性

  • 正式文体
  • 口语化
  • 专业术语
  • 通俗表达

传统数据准备的痛点

痛点1:网页爬取复杂

挑战

  • 反爬虫机制
  • 动态加载内容
  • 验证码
  • IP封禁

成本

  • 代理IP:数万元/月
  • 开发和维护:数人月
  • 法律风险

痛点2:内容提取困难

HTML充满噪音:

<div class="header">...</div>
<nav>...</nav>
<aside class="ads">广告...</aside>
<div class="social-share">...</div>
<article>
  <h1>真正的内容</h1>
  <p>正文...</p>
</article>
<footer>...</footer>
<script>大量JavaScript代码...</script>

如何准确提取<article>中的内容?

每个网站结构不同,需要定制化。

痛点3:格式统一耗时

原始HTML需要转换为训练友好的格式:

  • 去除HTML标签
  • 保留文本结构(标题、段落)
  • 统一格式(如Markdown)
  • 处理特殊字符

手动处理:不现实
自动化脚本:容易出错

痛点4:质量控制难

如何过滤低质量内容?

  • 广告和垃圾信息
  • 重复内容
  • 机器生成文本
  • 错误信息

需要复杂的启发式规则或ML模型。

痛点5:法律和伦理

爬虫面临:

  • 版权问题
  • 隐私问题
  • 服务条款违反
  • 法律风险

Reader API:简化数据准备

Reader API专为内容提取设计,完美契合LLM训练需求。

核心功能

1. 智能内容提取

自动识别主要内容,去除噪音:

const content = await readerAPI.extract('https://example.com/article');

// 返回干净的内容
{
  "title": "文章标题",
  "content": "# 文章标题\n\n正文内容...",
  "author": "作者",
  "publishedDate": "2024-01-01",
  "url": "https://example.com/article"
}

无需

  • 分析HTML结构
  • 写选择器
  • 处理每个网站的特殊性

2. Markdown输出

直接输出训练友好的格式:

# 文章标题

## 第一部分

段落内容...

## 第二部分

更多内容...

优势

  • 保留结构(标题、列表)
  • 纯文本,易处理
  • 统一格式

3. 元数据提供

获取有用的元数据:

  • 发布时间(过滤过时内容)
  • 作者(评估可信度)
  • 来源URL(溯源)
  • 语言(多语言训练)

4. 批量处理

高效处理大量URL:

const urls = [...]; // 数万个URL

const contents = await Promise.all(
  urls.map(url => readerAPI.extract(url))
);

5. 错误处理

自动处理:

  • 无法访问的页面
  • 格式异常的内容
  • 超时

返回状态码,易于重试。

实战:构建训练数据集

步骤1:收集URL列表

从哪里获取URL?

方法1:使用SERP API

搜索特定主题的文章:

const topics = ['人工智能', '机器学习', '深度学习', ...];

const urls = [];
for (const topic of topics) {
  const results = await serpAPI.search({
    query: topic,
    num: 100
  });
  urls.push(...results.map(r => r.url));
}

方法2:网站地图

大型网站的sitemap.xml:

const sitemap = await fetch('https://example.com/sitemap.xml');
const urls = parseSitemap(sitemap);

方法3:现有数据集

开源URL列表:

  • Common Crawl
  • C4数据集
  • 学术论文链接

步骤2:批量提取内容

const BATCH_SIZE = 100;
const DELAY = 1000; // 1秒延迟,避免过载

async function extractBatch(urls) {
  const results = [];
  
  for (let i = 0; i < urls.length; i += BATCH_SIZE) {
    const batch = urls.slice(i, i + BATCH_SIZE);
    
    console.log(`Processing batch ${i/BATCH_SIZE + 1}...`);
    
    const batchResults = await Promise.all(
      batch.map(async url => {
        try {
          const content = await readerAPI.extract(url);
          return { success: true, url, content };
        } catch (error) {
          return { success: false, url, error: error.message };
        }
      })
    );
    
    results.push(...batchResults);
    
    // 延迟,避免过载
    await new Promise(resolve => setTimeout(resolve, DELAY));
  }
  
  return results;
}

步骤3:质量过滤

function filterQuality(content) {
  // 1. 长度检查
  if (content.content.length < 500) {
    return false; // 太短
  }
  if (content.content.length > 50000) {
    return false; // 太长,可能是列表页
  }
  
  // 2. 语言检查
  if (!isChineseText(content.content)) {
    return false; // 非中文
  }
  
  // 3. 重复内容检查
  if (isDuplicate(content.content)) {
    return false;
  }
  
  // 4. 质量评分
  const score = calculateQualityScore(content.content);
  if (score < 0.6) {
    return false;
  }
  
  return true;
}

function calculateQualityScore(text) {
  let score = 1.0;
  
  // 惩罚因素
  if (text.includes('点击这里')) score -= 0.1;
  if (text.includes('广告')) score -= 0.1;
  if (text.match(/[\u4e00-\u9fa5]/g).length / text.length < 0.5) {
    score -= 0.2; // 中文占比太低
  }
  
  // 奖励因素
  if (hasClearStructure(text)) score += 0.1;
  if (hasRichInformation(text)) score += 0.1;
  
  return Math.max(0, Math.min(1, score));
}

步骤4:去重

import { createHash } from 'crypto';

const seenHashes = new Set();

function deduplication(contents) {
  return contents.filter(content => {
    const hash = createHash('sha256')
      .update(content.content)
      .digest('hex');
    
    if (seenHashes.has(hash)) {
      return false; // 重复
    }
    
    seenHashes.add(hash);
    return true;
  });
}

步骤5:格式转换

转换为训练格式(如JSONL):

function saveAsJSONL(contents, filename) {
  const stream = fs.createWriteStream(filename);
  
  for (const content of contents) {
    const record = {
      text: content.content,
      source: content.url,
      date: content.publishedDate,
      metadata: {
        title: content.title,
        author: content.author
draft: false
      }
    };
    
    stream.write(JSON.stringify(record) + '\n');
  }
  
  stream.end();
}

完整流程

async function buildTrainingDataset(topics, outputFile) {
  console.log('Step 1: Collecting URLs...');
  const urls = await collectURLs(topics);
  console.log(`Collected ${urls.length} URLs`);
  
  console.log('Step 2: Extracting content...');
  const results = await extractBatch(urls);
  const successful = results.filter(r => r.success);
  console.log(`Extracted ${successful.length} articles`);
  
  console.log('Step 3: Quality filtering...');
  const filtered = successful.filter(r => 
    filterQuality(r.content)
  );
  console.log(`${filtered.length} articles passed quality check`);
  
  console.log('Step 4: Deduplication...');
  const deduplicated = deduplication(filtered);
  console.log(`${deduplicated.length} unique articles`);
  
  console.log('Step 5: Saving...');
  saveAsJSONL(deduplicated.map(r => r.content), outputFile);
  
  console.log('Done!');
  return {
    total: urls.length,
    extracted: successful.length,
    filtered: filtered.length,
    final: deduplicated.length
  };
}

// 使用
const stats = await buildTrainingDataset(
  ['人工智能', '机器学习', '深度学习'],
  'training_data.jsonl'
);

高级技巧

技巧1:分类数据集

按领域分类,方便平衡:

const categories = {
  'tech': ['人工智能', '编程', '科技'],
  'news': ['时事', '财经', '政治'],
  'literature': ['小说', '散文', '诗歌']
};

for (const [category, topics] of Object.entries(categories)) {
  await buildTrainingDataset(
    topics,
    `training_data_${category}.jsonl`
  );
}

技巧2:时间范围控制

只要最近的内容:

function isRecent(content, months = 12) {
  if (!content.publishedDate) return true; // 未知日期,保留
  
  const publishDate = new Date(content.publishedDate);
  const cutoff = new Date();
  cutoff.setMonth(cutoff.getMonth() - months);
  
  return publishDate >= cutoff;
}

技巧3:多样性采样

避免某个来源占比过高:

function diversifySources(contents, maxPerSource = 100) {
  const bySource = {};
  
  for (const content of contents) {
    const domain = new URL(content.url).hostname;
    if (!bySource[domain]) bySource[domain] = [];
    bySource[domain].push(content);
  }
  
  const sampled = [];
  for (const source of Object.values(bySource)) {
    sampled.push(...source.slice(0, maxPerSource));
  }
  
  return sampled;
}

技巧4:增量更新

定期添加新数据:

async function incrementalUpdate(existingData, topics) {
  // 加载已有URL
  const existingURLs = new Set(existingData.map(d => d.source));
  
  // 收集新URL
  const allURLs = await collectURLs(topics);
  const newURLs = allURLs.filter(url => !existingURLs.has(url));
  
  console.log(`Found ${newURLs.length} new URLs`);
  
  // 处理新数据
  const newData = await processURLs(newURLs);
  
  // 合并
  return [...existingData, ...newData];
}

成本对比

方案A:自建爬虫

开发成本

  • 爬虫开发:1人月 = ¥30,000
  • 内容提取:1人月 = ¥30,000
  • 质量控制:0.5人月 = ¥15,000
  • 总计:¥75,000

运营成本(月)

  • 代理IP:¥10,000
  • 服务器:¥3,000
  • 维护:¥10,000
  • 总计:¥23,000/月

首年总成本:¥75,000 + ¥23,000 × 12 = ¥351,000

方案B:使用Reader API

开发成本

  • API集成:0.5人日 = ¥2,000
  • 数据管道:2人日 = ¥8,000
  • 总计:¥10,000

运营成本(月)

  • Reader API:¥5,000(100万次请求)
  • 服务器:¥1,000
  • 总计:¥6,000/月

首年总成本:¥10,000 + ¥6,000 × 12 = ¥82,000

节省:¥269,000(77%)

实际案例

案例:某AI公司的训练数据准备

背景

  • 训练7B中文模型
  • 需要100GB高质量中文文本
  • 团队3人,预算有限

方案

  • 使用SERP API收集URL
  • 使用Reader API提取内容
  • 自动化质量过滤和去重

执行

  • 收集了50万个URL
  • 提取了40万篇文章
  • 质量过滤后保留30万篇
  • 去重后得到25万篇独特文章
  • 总计120GB文本

时间

  • API集成和管道开发:3天
  • 数据收集和处理:1周(自动运行)
  • 总计:10天

成本

  • API费用:¥15,000
  • 人力:3人 × 10天 = ¥30,000
  • 总计:¥45,000

如果自建爬虫

  • 时间:至少2个月
  • 成本:¥100,000+
  • 法律风险:高

结果

  • 节省55%成本
  • 缩短80%时间
  • 降低法律风险
  • 数据质量高

案例:某大学研究项目

背景

  • 研究中文NLP
  • 需要特定领域数据(医疗)
  • 预算极其有限

方案

  • 使用SearchCans免费额度测试
  • 付费后按需使用
  • 专注于高质量来源

执行

  • 精选100个权威医疗网站
  • 提取1万篇高质量文章
  • 人工审核关键样本

成本

  • API费用:¥3,000
  • 学生劳动:免费
  • 总计:¥3,000

成果

  • 发表了2篇论文
  • 开源了数据集
  • 推动了领域发展

最佳实践

1. 选择高质量来源

不要追求数量,要追求质量:

  • 权威网站
  • 专业出版物
  • 经过编辑的内容

2. 平衡数据集

避免偏见:

  • 多个领域
  • 不同风格
  • 多样化来源

3. 持续更新

语言在演进,数据也要:

  • 定期添加新数据
  • 更新过时内容
  • 追踪新趋势

4. 记录来源

便于:

  • 合规性审查
  • 质量追溯
  • 引用和致谢

5. 尊重版权

  • 遵守robots.txt
  • 尊重版权声明
  • 合理使用原则
  • 必要时获得许可

结语

LLM训练数据准备曾经是繁琐、昂贵、耗时的过程。

Reader API将其简化为:

  1. 收集URL
  2. 调用API
  3. 过滤和保存

几行代码,几天时间,几千元成本,就能构建高质量训练数据集。

这让:

  • 小团队能训练自己的模型
  • 研究者能快速实验
  • 企业能降低成本

技术应该让AI民主化,而不是成为大公司的专利。

Reader API正是这样的工具。


相关阅读

开始构建你的训练数据集。免费注册SearchCans,使用Reader API高效提取网络内容,获取¥30体验额度。

标签:

LLM训练 训练数据 Reader API 数据准备

准备好用 SearchCans 构建你的 AI 应用了吗?

立即体验我们的 SERP API 和 Reader API。每千次调用仅需 ¥0.56 起,无需信用卡即可免费试用。