恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
开源大模型训练全流程:100亿Token从数据到部署
首页
资讯中心
/
开源大模型训练全流程:100亿Token从数据到部署
开源大模型训练全流程:100亿Token从数据到部署
发布时间:2026/10/12 2:43:50
这两年 AI 圈的阵仗确实大新模型一个接一个但我始终觉得真正让技术圈热闹起来的不是那些只发技术报告不开源的团队而是愿意把模型、代码、数据管线老老实实放出来的项目。今天想聊的就是我最近刚完成并开源的一个项目。不止是模型权重包括整个数据处理流程、训练脚本、评测脚本全都整理好放出来了。说实话做这个项目的初衷很简单我自己在微调和部署小模型时发现很多开源仓库要么只给一个训练完的权重文件要么训练代码和数据处理逻辑写得比较随意很难复现。我希望能有一个相对完整、可复现的“大语言模型从数据准备到开源发布”全流程项目于是花了两个多月时间前后累计消耗了约 100 亿 Token 的数据量用于清洗、配比和训练验证最终把一套可运行的开源方案整理了出来。这篇文章我不会只贴一个 GitHub 地址就完事。而是把整个项目从立项、数据工程、模型训练、评测、到开源打包部署的完整链路拆开来讲。如果你正准备开始自己的第一个开源模型项目或者正在为团队搭建私有化 LLM 推理服务这篇文章应该能帮你少走不少弯路。1. 背景与核心概念为什么值得投入两个月做一个开源项目1.1 这个项目到底是什么先解释一下标题中的几个关键词避免刚接触大模型的朋友产生误解。“100 亿 Token”指的是整个项目在数据处理、模型训练、评测验证过程中累计消耗的 Token 数量。Token 是 LLM 处理文本时的最小单位可以粗略理解为一个词或一个子词片段。100 亿 Token 大约相当于 7.5 亿英文单词或者 4~5 亿汉字这个量级已经足够训练一个亿级参数的小模型也足够做一遍高质量的数据筛选和配比实验。项目最终交付物是一套完整的数据清洗与配比脚本一个在清洗后数据上训练的基座模型一套可复现的指令微调流程一个轻量的推理服务包装详细的评测结果和失败案例分析。整体架构可以概括为数据工程 - 预训练 - 指令微调 - 评测 - 开源部署。1.2 为什么要做“开源”而不是自己闷头用很多开发者会问模型训出来了自己用不就行了为什么非要开源我的理由有三个。第一可复现性是技术沉淀的基础。如果训练脚本、数据配比、超参数都不公开那这次训练就是一次不可复现的“玄学”。开源相当于强迫自己把每一个环节写成文档和脚本对工程化能力是很好的锻炼。第二社区反馈能加速迭代。开源之后会有使用者反馈新场景下的表现也可能有贡献者提交更好的数据清洗策略。这种外部输入比闭门造车高效得多。第三降低团队试错成本。真正在企业里落地的开发者往往需要参考成熟的模型仓库来设计自己的训练管线。一个结构清晰的开源项目能直接作为模板复用。1.3 哪些场景适合参考这个项目想训练一个特定领域的小模型但不知道数据怎么清洗、怎么配比想微调开源模型做私有化部署但希望补全数据处理到评测的完整链路想学习大模型训练工程的细节尤其是 Token 消耗和成本控制的关系实验室或小团队需要一套可复现的训练代码。如果你只是想直接用模型做应用不需要训练那这个项目的推理部署部分仍然有参考价值尤其是量化部署部分。2. 环境准备与版本说明在跑任何训练或推理之前先把环境信息说清楚。以下版本是我实际使用的环境你的环境不一定完全一致但思路是通用的。2.1 硬件与运行环境资源配置GPU单机 8 x A800-80G / 或多机多卡CPU32 核以上内存256GB 以上系统Ubuntu 20.04 / 22.04存储至少 2TB 可用空间如果你是个人开发者硬件条件达不到也完全可以用单卡 RTX 4090 跑通数据处理和微调流程只是预训练阶段需要调小模型参数规模。2.2 软件依赖版本Python 3.10 CUDA 11.8 / 12.1 PyTorch 2.1 transformers 4.36 datasets 2.16 accelerate 0.26 peft 0.8 deepspeed 0.12 vllm 0.3这里单独提醒一句大模型训练项目的依赖版本非常敏感尤其是 transformers、peft、deepspeed 三者之间经常存在兼容性问题。建议先创建独立的 conda 环境再按顺序安装。conda create -n llm-project python3.10 conda activate llm-project pip install torch2.1.0 --index-url https://download.pytorch.org/whl/cu118 pip install transformers4.36.0 datasets2.16.0 accelerate0.26.0 pip install peft0.8.0 deepspeed0.12.02.3 项目目录结构构建开源项目从第一天起就要保持良好的目录结构下面是我实际使用的目录组织方式。llm-open-project/ ├── configs/ # 配置文件 ├── data/ # 数据处理脚本 │ ├── raw/ # 原始数据存放 │ ├── cleaned/ # 清洗后数据 │ ├── tokenized/ # Token 化后数据 │ └── process_pipeline.py # 数据管道 ├── train/ # 训练脚本 │ ├── pretrain.py # 预训练入口 │ ├── sft.py # 指令微调入口 │ └── deepspeed_config/ # DeepSpeed 配置 ├── eval/ # 评测脚本 │ ├── run_benchmark.py │ └── eval_prompts.json ├── deploy/ # 推理部署 │ ├── vllm_server.py │ └── quantize.py ├── scripts/ # 一键执行脚本 ├── README.md └── requirements.txt3. 核心原理拆解Token 消耗在哪数据工程怎么做3.1 100 亿 Token 到底消耗在哪里很多朋友看到 100 亿 Token 的第一反应是“好多”但实际拆开看这个数字是有清晰去向的。环节Token 消耗占比说明数据清洗与去重约 20%多次跑规则过滤和语义去重模型需要反复读取全量数据预训练基座约 60%在通用语料上训练模型是 Token 消耗的大头指令微调约 15%构建指令样本并完成模型对齐评测与验证约 5%多次跑基准测试尤其是对比不同版本效果时这里要特别说明一个容易踩坑的点数据清洗环节消耗的 Token 远比你以为的多。因为去重阶段需要把全量文本发送给语义去重模型每跑一遍都要消耗与语料规模相当的 Token 量。如果清洗策略设计得不好返工次数一多成本很快就会失控。3.2 数据清洗的三个关键步骤大模型训练圈有句名言数据决定模型的上限训练只是逼近这个上限。整个项目花费时间最长的不是训练而是数据处理。第一步语言过滤和质量过滤使用fasttext或cld3做语言识别过滤掉目标语言之外的语料。质量过滤则依赖规则和分类模型相结合# 文件路径data/process_pipeline.py import re import fasttext # 加载语言识别模型 lang_model fasttext.load_model(lid.176.bin) MIN_TEXT_LENGTH 100 MAX_TEXT_LENGTH 5000 REPETITION_RATIO_THRESHOLD 0.3 def is_high_quality(text: str) - bool: 基础规则过滤长度、重复率、语言 if len(text) MIN_TEXT_LENGTH or len(text) MAX_TEXT_LENGTH: return False # 重复字符比例检测过滤“哈哈哈哈...”这类无意义文本 char_set set(text) if len(char_set) / max(len(text), 1) 0.05: return False # 语言过滤检测是否为中文/英文 lang lang_model.predict(text.replace(\n, ))[0][0] if lang not in [__label__zh, __label__en]: return False return True第二步MinHash 去重互联网语料中重复内容非常多一篇热门文章可能被转载上百次。使用 MinHash LSH 做近似去重是数据工程的标配做法。from datasketch import MinHash, MinHashLSH def build_minhash(text: str, num_perm128): m MinHash(num_permnum_perm) # 使用 5-gram 进行分片 for i in range(len(text) - 5): m.update(text[i:i5].encode(utf-8)) return m这里使用 5-gram 分片的原因是太短的 n-gram 容易把完全不相关的文本误判为重复太长的 n-gram 又可能漏掉改写后的重复文本。5-gram 在中文语料上效果比较均衡。第三步数据配比数据配比是一个经常被人忽略但影响极大的环节。如果你直接把所有清洗后的语料混在一起训练模型可能会被灌入大量低质量或特定领域的文本导致下游任务表现失衡。我的配比策略是按“通用语料:领域语料 7:3”进行混合。领域语料又细分为代码、数学、法律、医学等子领域每个子领域按 token 数量加权采样。# 数据配比采样逻辑 import random def sample_by_domain(domain_and_texts, target_domain_ratio0.3): 按领域比例对流式数据做采样 # 实现细节根据数据源结构调整 pass3.3 为什么预训练要分阶段预训练阶段我采用了两阶段策略而不是一次性把所有数据灌进去。第一阶段在海量通用语料上训练让模型学习基本的语言能力和世界知识。第二阶段用中道清洗后的领域语料继续训练增强模型在目标领域的效果。这种“通用预训练 领域继续训练”的思路比直接混合训练能更容易控制领域能力的增强幅度也能减少灾难性遗忘。3.4 指令微调把“会说话”变成“会办事”基座模型只能做文本续写比如你输入“Python 的 GIL 是”它会尝试补全。但如果你希望它回答“什么是 Python 的 GIL”就需要指令微调。指令微调的关键是样本质量。这里的原则是样本宁缺毋滥。我最后用来微调的高质量指令样本大约是 10 万条但中间清洗掉的不合格样本超过 30 万。一个典型指令样本格式如下{ instruction: 解释什么是 Python 的 GIL, input: , output: GIL 是 CPython 解释器中的全局解释器锁它保证同一时刻只有一个线程执行 Python 字节码... }4. 完整实战案例从数据处理到开源发布下面进入整篇文章最核心的部分我会把整个项目从训练到发布的流程走一遍。如果你想完整复现可以直接对照本节操作。4.1 数据准备与 Token 化首先是准备原始数据。这里假设你已经有一批文本语料放在data/raw/目录下。第一步是执行清洗管道python data/process_pipeline.py \ --input_dir data/raw \ --output_dir data/cleaned \ --language zh,en \ --min_length 100 \ --max_length 5000清洗完成后需要将文本 Token 化为模型输入的 ID 序列。使用 Hugging Face 的datasets库进行批量处理# 文件路径data/tokenize_dataset.py from datasets import load_dataset from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(your-base-model) def tokenize_function(examples): # 拼接文本并按 chunk 长度截断 texts [t tokenizer.eos_token for t in examples[text]] tokenized tokenizer( texts, truncationTrue, max_length2048, paddingFalse, return_tensorsNone ) return tokenized dataset load_dataset(json, data_filesdata/cleaned/train.jsonl) tokenized_dataset dataset.map( tokenize_function, batchedTrue, remove_columns[text], num_proc32 ) tokenized_dataset.save_to_disk(data/tokenized/train)Token 化这一步是计算密集型的强烈建议使用num_proc开启多进程单进程处理几十 GB 数据会慢到怀疑人生。4.2 配置 DeepSpeed 训练环境由于项目在预训练阶段需要处理大量 Token单卡显存远远不够因此多卡分布式训练是必须的。这里选择 DeepSpeed ZeRO-2 来切分优化器状态和梯度。// 文件路径train/deepspeed_config/zero2.json { train_batch_size: 64, gradient_accumulation_steps: 4, train_micro_batch_size_per_gpu: 2, optimizer: { type: AdamW, params: { lr: 3e-5, weight_decay: 0.01 } }, zero_optimization: { stage: 2, offload_optimizer: { device: cpu, pin_memory: true } }, gradient_clipping: 1.0, fp16: { enabled: true } }关键参数解释train_micro_batch_size_per_gpu每张卡一次前向传播的 batch 大小这里设为 2 是因为显存有限gradient_accumulation_steps梯度累积步数用于模拟更大的 batchoffload_optimizer将优化器状态放到 CPU节省 GPU 显存。4.3 编写预训练脚本预训练脚本的核心逻辑是加载配置、初始化模型、设置数据加载器、启动训练循环。# 文件路径train/pretrain.py import os import torch from transformers import ( AutoConfig, AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments ) from datasets import load_from_disk def main(): model_name your-base-model config AutoConfig.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name, configconfig) tokenizer AutoTokenizer.from_pretrained(model_name) # 设置填充 token避免训练时报错 if tokenizer.pad_token is None: tokenizer.pad_token tokenizer.eos_token train_dataset load_from_disk(data/tokenized/train) training_args TrainingArguments( output_dir./checkpoints/pretrain, evaluation_strategysteps, eval_steps500, save_steps1000, logging_steps50, per_device_train_batch_size2, per_device_eval_batch_size2, gradient_accumulation_steps4, learning_rate3e-5, warmup_steps200, weight_decay0.01, fp16True, deepspeedtrain/deepspeed_config/zero2.json, save_total_limit3, report_tonone, ) trainer Trainer( modelmodel, argstraining_args, train_datasettrain_dataset, eval_datasettrain_dataset.select(range(1000)), # 示例用法实际应单独划分 tokenizertokenizer, ) trainer.train() trainer.save_model(./checkpoints/pretrain/final) tokenizer.save_pretrained(./checkpoints/pretrain/final) if __name__ __main__: main()运行命令cd train deepspeed --num_gpus8 pretrain.py4.4 指令微调让模型学会对话基座模型训练完成后需要进行监督微调SFT。这一步的核心是使用高质量的指令数据让模型对齐用户意图。# 文件路径train/sft.py from transformers import AutoModelForCausalLM, AutoTokenizer from peft import LoraConfig, get_peft_model from datasets import load_dataset model AutoModelForCausalLM.from_pretrained(./checkpoints/pretrain/final) tokenizer AutoTokenizer.from_pretrained(./checkpoints/pretrain/final) # LoRA 配置 lora_config LoraConfig( r8, lora_alpha16, target_modules[q_proj, v_proj, k_proj, o_proj], lora_dropout0.05, biasnone, task_typeCAUSAL_LM ) model get_peft_model(model, lora_config) model.print_trainable_parameters() # 输出 LoRA 层可训练参数量使用 LoRA 的核心原因是效率。虽然你也可以全量微调但 LoRA 只需要训练约 0.5% 的参数显存占用和训练时间都大幅缩减效果在指令跟随场景下已经足够好。指令数据集的格式参考前面提到的 JSON 结构。训练完成后保存 LoRA 权重model.save_pretrained(./checkpoints/sft/lora) tokenizer.save_pretrained(./checkpoints/sft/lora)如果需要合并 LoRA 权重到基座模型from peft import PeftModel base_model AutoModelForCausalLM.from_pretrained(./checkpoints/pretrain/final) merged_model PeftModel.from_pretrained(base_model, ./checkpoints/sft/lora) merged_model merged_model.merge_and_unload() merged_model.save_pretrained(./checkpoints/sft/merged)4.5 评测不要只盯着 Loss 曲线模型训练完成后评测环节直接决定你是否能发布。我的评测分为三类通用能力评测如 C-Eval、MMLU、专项任务评测领域问答、代码生成、人工抽检。# 文件路径eval/run_benchmark.py from datasets import load_dataset from transformers import pipeline generator pipeline( text-generation, model./checkpoints/sft/merged, device0 ) eval_prompts [ 请解释什么是反向传播算法, 用 Python 实现一个快速排序, 列出数据库索引失效的三种场景, ] for prompt in eval_prompts: output generator( prompt, max_new_tokens256, temperature0.3, top_p0.9, do_sampleTrue ) print(Prompt:, prompt) print(Output:, output[0][generated_text]) print( * 50)评测过程中要有“对比基线”我的建议是至少拿一个同参数规模的开源模型作为对照否则你无法判断自己的模型到底是好还是坏。4.6 开源打包与部署4.6.1 模型权重发布开源大模型项目权重发布建议使用 Hugging Face 仓库或 ModelScope。权重文件会包含config.json、tokenizer.json、model-*.safetensors等文件。在 README 中必须写清楚基座模型来源与许可证训练数据来源与清洗说明评测结果模型限制与已知问题。4.6.2 推理服务部署模型部署最终需要做成可调用的服务。这里用 vLLM 搭建一个高吞吐的推理服务# 文件路径deploy/vllm_server.py from vllm import LLM, SamplingParams llm LLM(model./checkpoints/sft/merged, tensor_parallel_size2) sampling_params SamplingParams( temperature0.3, top_p0.9, max_tokens512 ) prompts [ 用一句话解释大语言模型中的 Token 是什么, 写一段 Python 代码读取 CSV 文件并计算每列平均值, ] outputs llm.generate(prompts, sampling_params) for output in outputs: print(output.outputs[0].text) print( * 50)使用 vLLM 可以实现连续批处理大幅提升吞吐是生产环境推荐的方案。如果你的显存有限可以先对模型做量化再部署python deploy/quantize.py \ --model_path ./checkpoints/sft/merged \ --quant_method gptq \ --bits 4 \ --output_path ./checkpoints/sft/merged-int44.6.3 开源仓库编写最后一步写 README。这步千万不能敷衍因为开源项目的 README 就是项目的门面。我建议的 README 结构是项目简介三句话内说清楚整体架构图使用文字描述快速开始环境安装、推理 Demo训练复现数据处理、预训练、微调评测结果用表格已知问题与后续规划许可证与引用。5. 常见问题与排查思路整个项目踩过的坑实在不少这里把高频问题整理成表方便遇到类似报错的读者直接对照排查。问题现象常见原因解决思路训练 Loss 为 NaN学习率过大 / 数据中包含异常值降低学习率检查数据清洗流程增加梯度裁剪显存不足 OOMbatch size 过大 / 序列过长减小 micro batch size开启 DeepSpeed offload降低 max_lengthToken 化速度极慢未开启多进程使用num_proc参数开启多进程提高数据读取效率微调后模型胡言乱语指令样本格式不一致 / 基座模型未充分训练统一模板格式减少低质量样本必要时对基座模型继续预训练DeepSpeed 启动失败版本不匹配 / 未正确设置环境变量对照官方文档检查deepspeed、transformers、torch版本评测分数低于基线模型数据配比不合理 / 评测 prompt 不一致检查数据配比统一评测 prompt 模板推理速度太慢未使用 vLLM / 未开启 tensor parallel使用 vLLM 推理框架显存充足时开启多卡并行6. 最佳实践与工程建议6.1 数据永远是第一优先级这个项目做完我最深的体会就是80% 的问题可以通过更好的数据解决而不是更大的模型。如果你准备做自己的模型项目先不要急着买 GPU先用 3~5 天时间把数据管道做到位。数据清洗、去重、配比这三步值得反复迭代。6.2 训练工程要留好监控点训练跑起来不代表万事大吉。我在训练每个阶段都会做以下监控每 50 步看一次 Loss 曲线每 500 步跑一次小样本评测而不是等全部训练结束每个 checkpoint 都保存优化器状态方便中途恢复训练日志必须带有时间戳和 Token 消耗计数。6.3 开源许可证要提前确定开源项目最容易忽略的是许可证问题。如果你的基座模型使用了 Llama 或 ChatGLM 等模型权重请先确认基座的许可证允许派生作品商用。另外训练数据如果来自网上抓取需要评估数据集的合规性。6.4 版本控制与可复现性训练工程和普通软件开发一样需要可复现性。我强烈建议使用requirements.txt锁定依赖版本每个训练实验记录完整超参数和配置文件使用实验管理工具记录每次实验的代码版本、数据版本和训练结果。6.5 安全与合规红线内容安全审核是部署大模型前必须做的环节。建议增加输入输出过滤至少对以下类型内容做拦截违法和不良信息个人隐私数据未经授权的商业化内容。可以引入开源的安全审核模型或关键词过滤方案避免模型生成风险内容后再被动处理。7. 总结与学习路线这篇文章从头到尾梳理了一个开源大模型项目的完整链路从项目立项、数据清洗与配比、Token 消耗分析、预训练与指令微调到评测、量化、部署和开源打包。核心内容可以归纳为以下几条Token 消耗不只是训练数据清洗和评测同样是大头数据质量决定模型上限数据配比是容易被忽视的关键点预训练最好分阶段指令微调优先尝试 LoRA评测不能只看 Loss必须有基线和人工抽检开源不等于丢权重README、许可证、文档同样重要。如果你想继续深入建议按以下顺序学习先熟悉 Hugging Face Transformers 库的基本用法理解 Tokenizer 的原理与不同分词策略的区别自己实现一套小规模数据清洗流程体会数据质量的差异用单卡完成一次小模型的 LoRA 微调再扩展到多卡训练和 DeepSpeed最后才是完整的预训练和开源发布。做开源模型项目最重要的一点是不要追求一步到位先把最小可用的闭环跑通再逐步迭代数据和训练策略。希望这篇文章能帮你省下一些摸索时间也欢迎在使用过程中交流问题。如果对你有帮助可以收藏备用后续我会继续补充推理优化和评测相关的实战内容。