前言
上篇文章《大模型训练全流程实战指南实战篇(十六)——预训练数据集构建》中,笔者通过结构化切片和质量判别,得到了结构清晰、内容干净、可供LLaMA-Factory使用的专业领域数据集。但严格来说这还不是LLaMA-Factory可直接用于预训练的完整数据文件。
本篇文章将按照“数据准备 → 参数设置 → 启动训练 → 效果查看”四步递进:先说明如何将专业领域数据整理、扩充并配置为LLaMA-Factory可直接使用的完整的预训练数据;再介绍如何使用LLaMA-Factory启动大模型预训练。训练结束后大家将得到一个经过专业知识扩充的大模型,并将它作为后续微调的模型基座。
一、完整的预训练数据准备
1.1 构建专业领域数据集
在将 大模型训练全流程实战指南实战篇(十六)——预训练数据集构建中的内容整理为专业领域数据集之前,笔者先介绍一个小技巧。大家是否还记得在 大模型训练全流程实战指南实战篇(十四)——网络安全大模型数据获取 文章中笔者提到可以使用EasyDataset蒸馏数据集,不过蒸馏得到的是问答对数据。但因为预训练只关心“根据上文预测下一个 token”,并不关心文本是论文还是问答;问答文本同样包含领域术语、问题表述和答案组织逻辑。因此可以将高质量微调数据集中的问题和答案拼接成自然文本,作为预训练数据使用。
大家可以将 EasyDataset 蒸馏得到的数据导出为alpaca格式:
然后可以使用如下代码提取问题和答案,将其拼接为问答对文本:
import json
def alpaca_to_text(item):
q = item.get("instruction", "").strip()
if item.get("input"):
q += "\n" + item["input"].strip()
return f"{q}?{item.get('output', '').strip()}"
接着可以将《大模型训练全流程实战指南实战篇(十六)——预训练数据集构建》处理得到的数据集中的 text 字段提取出来,与蒸馏得到的数据集拼接,得到专业领域预训练数据集:
import json, hashlib
DISTILL = './llamafactory_data/cyber_distill.jsonl'
SRC = "./llamafactory_data/chunks_judged.jsonl"
DST = "./llamafactory_data/cyber_pt.jsonl"
dataset = []
with open(SRC, encoding="utf-8") as fin:
for line in fin:
rec = json.loads(line)
if not rec.get("keep"):
continue
text = rec["text"].strip()
if len(text) < 100:
continue
dataset.append({"text": text})
kept= kept + 1
with open(DISTILL, encoding="utf-8") as f:
data = json.load(f)
for item in data:
text = alpaca_to_text(item)
kept= kept + 1
dataset.append({"text": text})
with open(DST, 'w', encoding="utf-8") as fout:
for item in dataset:
fout.write(json.dumps(item, ensure_ascii=False) + "\n")
print(f"{kept} 条")
执行完成后,本次蒸馏和数据治理得到的数据总计30,847 条。
1.2 引入通用数据集,避免通用能力丧失
专业领域数据集只覆盖网络安全知识,而预训练阶段的知识注入在很大程度上决定了模型的相关能力(大模型训练全流程实战指南工具篇(五)——大模型训练全流程步骤详解与对应工具推荐文章中笔者提过)。如果只用专业领域数据集训练,模型长时间只学习单一分布,原本具备的通用问答和常识知识反而可能下降,这就是所谓的“灾难性遗忘”。因此预训练通常不会使用单一领域语料,而是将通用语料与领域语料混合使用。
那么通用数据集应该配多少?这个问题并没有统一标准,各大公司也鲜有公开。结合笔者日常工作中的尝试,如果使用增量预训练,通用数据集的比例一般略高于专业领域数据集,笔者最常用的比例是通用数据集占全部数据集的 60%~70%,专业领域语料占 30%~40%(可以粗略按条数计算比例)。
那么通用数据集该如何构建?是不是随便爬取一些网页、清洗后即可使用?实际并没有这么简单。通用数据集需要按类别进行分层采样,保证各类数据都有一定比例分布,不同类别数据的配比通常也是各公司的商业机密。笔者在这里分享一个基本思路:先确定 6~10 个大类,再在每个大类中细分为若干小类,并为每个类别设置配额比例,例如计算机 15%、数学 10%、地理/历史/人文各 15%、其余百科网页 30%;最后根据预训练效果动态调整类别比例。
通用数据集的来源比较丰富,目前社区和部分公司已经发布了开源的通用数据集,例如HuggingFace上的维基百科数据;如果需要完整的中文数据集,也可以使用OpenDataLab的MNBVC、北京智源人工智能研究院(BAAI)构建的悟道 WuDaoCorpora 或 CLUECorpus2020 等中文预训练数据集。
下面以悟道 220G 版本(WuDaoCorpora Text,共 59,100,001 条)中文预训练数据集为例,演示如何构建预训练语料。
注意: 悟道数据集的官方数据需要在Science Data Bank申请下载。下面的代码读取的是社区搬运到HuggingFace的 p208p2002/wudao,约 125GB。该数据集被切分为多个parquet分片,每个分片约 100MB。parquet是面向大数据场景的列式压缩存储格式,可理解为“按列存储、压缩后的类 Excel 表格”。
由于悟道数据只能按分片逐个读取,下面代码会先打乱分片顺序,再限制读取的分片数量,避免一次性读取全部几百 GB 数据。核心代码如下:
import os, json, random, hashlib
os.environ.setdefault("HF_ENDPOINT", "https://hf-mirror.com")
from datasets import load_dataset
from huggingface_hub import list_repo_files
random.seed(42)
N = 45610
TARGET = {"百科": 0.40, "博客": 0.25, "科技": 0.10,
"教育": 0.08, "文化": 0.08, "经济": 0.05, "新闻": 0.04}
BAD = ["蜘蛛池", "站群", "seo", "代发", "加微信", "网站优化"]
repo = "p208p2002/wudao"
files = [f for f in list_repo_files(repo, repo_type="dataset")
if f.endswith(".parquet")]
base = f"{os.environ['HF_ENDPOINT']}/datasets/{repo}/resolve/main/"
files = [base + f for f in files]
random.shuffle(files) # 分片按时间切,打乱后才能覆盖不同类别
quota = {k: int(N * v) for k, v in TARGET.items()}
counts, seen = {k: 0 for k in quota}, set()
ds = load_dataset("parquet", data_files=files[:80], split="train", streaming=True)
with open("./llamafactory_data/wudao_pt.jsonl", "w", encoding="utf-8") as fout:
for item in ds:
cat = item.get("dataType", "")
if cat not in quota or counts[cat] >= quota[cat]:
continue
text = f"{item['title']}\n\n{item['content']}".strip()
if len(text) < 200 or any(w in text.lower() for w in BAD):
continue
digest = hashlib.md5(text.encode("utf-8")).hexdigest()
if digest in seen:
continue
seen.add(digest)
counts[cat] += 1
fout.write(json.dumps({"text": text, "category": cat},
ensure_ascii=False) + "\n")
if all(counts[k] >= quota[k] for k in quota):
break
执行结果如下:
1.3 根据数据规模选择训练方式
将 ./llamafactory_data/wudao_pt.jsonl 合并到 ./llamafactory_data/cyber_pt.jsonl,完成预训练数据集制作后,一般还需要统计数据集的 token 数。预训练更关心 token 数而不是条数,不同 token 规模对应的训练策略差别很大:
- 小于 1M(1,000,000)tokens:不建议使用增量预训练,直接进入微调阶段更合适。
- 1M~100M tokens:个人和小团队通常能够制作的合理数据规模,可使用 LoRA 增量预训练。
- 100M~1B(1,000,000,000)tokens:大型团队和中型公司能够制作的合理数据规模,建议使用 LoRA 增量预训练。
- 1B tokens 以上:面向企业级大模型,建议直接进行全参数预训练。
那么如何测算当前数据拥有的token数?在《大模型训练全流程实战指南基础篇(二)——大模型文件结构解读与原理解析》中提到,模型原始文件中包含一个名为tokenizer.json的分词器文件。可以先将./llamafactory_data/cyber_pt.jsonl中的text字段读出来,利用大模型的分词器统计token数:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B")
total, docs = 0, 0
with open('./llamafactory_data/cyber_pt.jsonl', encoding="utf-8") as f:
batch = []
for line in f:
text = (json.loads(line).get("text") or "").strip()
if not text:
continue
batch.append(text)
docs += 1
if len(batch) == 500:
enc = tokenizer(batch, add_special_tokens=False, return_length=True)
total += sum(enc["length"])
batch = []
if batch:
enc = tokenizer(batch, add_special_tokens=False, return_length=True)
total += sum(enc["length"])
print(f"{docs} 条,{total / 1e6:.2f}M tokens")
经统计,本次得到的预训练数据集共 76,457 条数据,约 926M tokens。根据这一数据规模,本次预训练将采用 LoRA 增量预训练。
1.4 在 LLamaFactory中注册数据集
数据集制作完成后,下一步需要将其注册到LLaMA-Factory中。笔者在《大模型训练全流程实战指南工具篇(九)——LLamaFactory大模型训练工具使用指南》中介绍过如何使用 LLaMA-Factory 进行大模型微调;预训练流程与之类似,第一步同样是注册数据集。
首先,将 cyber_pt.jsonl 放到 LlamaFactory/data/ 下:
然后,在LlamaFactory/data/dataset_info.json中新增如下配置:
"cyber_pt": {
"file_name": "cyber_pt.jsonl",
"columns": {
"prompt": "text"
}
}
其中columns的key是LLaMA-Factory内部字段名,value是文件中真实的列名。预训练内部使用prompt字段承载正文,因此需要将 text 映射到 prompt。
二、LLamaFactory 进行 预训练
LLaMA-Factory 通过“命令行 + 配置文件”的方式启动训练。可以说,只要配置文件编写正确,预训练就成功了一半。本次笔者基于 Qwen3-32B 模型进行预训练。
注意: Qwen3-32B 模型的训练一般需要 80~160 GB 显存,约合 1~2 张 A100 或 H100。
2.1 编写配置文件
在LLaMA-Factory目录下新建 params文件夹,并在其中新建 cyber_pt.yaml,写入如下配置:
### model(包括模型权重路径)
model_name_or_path: ~/Qwen3-32B
trust_remote_code: true
### method
stage: pt
do_train: true
finetuning_type: lora
lora_rank: 8
lora_alpha: 8
lora_target: all
### dataset
dataset: cyber_pt
cutoff_len: 4096
overwrite_cache: true
preprocessing_num_workers: 16
### output
output_dir: ~/saves/qwen3-32b/lora/pretrain
logging_steps: 10
save_steps: 50
plot_loss: true
overwrite_output_dir: true
### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 5.0e-5
num_train_epochs: 20.0
lr_scheduler_type: cosine
warmup_ratio: 0.05
bf16: true
gradient_checkpointing: true
group_by_length: true
ddp_timeout: 180000000
相关参数的详细说明,笔者已经在《大模型训练全流程实战指南工具篇(十)——小白也能懂的大模型训练参数万字详解》中介绍过,如有遗忘可以回顾。这里重点说明几个关键参数:stage 设为 pt,表示进行预训练;finetuning_type 设为 lora,表示采用 LoRA 增量预训练;lora_rank / lora_alpha 设置为 8/8,在预训练阶段保持 1:1,且数值不宜设置过高,因为还需要兼顾模型原有的通用能力;num_train_epochs 虽然设置为 20,但并不一定要选择最后一个checkpoint,完全可以选择中间某个checkpoint进行合并。
2.2 运行命令
配置完成后,在 LlamaFactory-main 目录下执行:
llamafactory-cli train ~/LlamaFactory-main/params/cyber_pt.yaml
日志会按照 logging_steps 打印 loss、learning_rate、epoch 和 grad_norm。同时,由于设置了 plot_loss: true,训练过程会将整体 loss 变化趋势绘制为一张 PNG 图片。本次训练持续了约2天,训练完成后绘制的整体loss曲线如下:
2.3 合并LORA权重
在《大模型训练全流程实战指南工具篇(九)——LLamaFactory大模型训练工具使用指南》中,笔者介绍过,在设定的存储目录下保存的是大模型的LoRA适配器。由于配置文件中设置了output_dir为~/saves/qwen3-32b/lora/pretrain,save_steps 为50,因此每隔50步都会保存一个适配器。接下来需要选择合适的适配器,与Qwen3-32B模型进行合并。
那这么多checkpoint到底应该选择哪个进行合并?从笔者的经验来看,一般选择loss在0.5~0.8之间所对应的checkpoint。可以将这个范围内对应的checkpoint分别与Qwen3-32B模型合并,得到多个模型后分别进行评测,最终选用评测结果最好的checkpoint作为预训练结果。该结果也是下一阶段微调SFT的起点。
将 checkpoint 与原始模型合并同样需要编写配置文件。在配置文件目录下新建 cyber_pt_merge.yaml,写入如下配置:
model_name_or_path: ~/Qwen3-32B
adapter_name_or_path: ~/saves/qwen3-32b/lora/pretrain/checkpoint-6250
template: qwen3_nothink
trust_remote_code: true
export_dir: ~/pretrain/Qwen3_32B_cyber_pt
export_size: 5
export_device: cpu
export_legacy_format: false
由于导出的模型会有 60 多 GB,export_size表示导出的单个.safetensors文件不超过 5 GB。执行如下命令导出模型:
llamafactory-cli export ~/LlamaFactory-main/params/cyber_pt_merge.yaml
三、预训练效果查看
完成上述操作后,大家会得到多个合并后的模型。接下来需要根据各模型的性能选择最终的预训练模型。可以先使用vllm部署合并后的模型,再通过笔者常用的两种方法挑选最合适的模型参数:
vllm serve 合并后模型的绝对路径 \
--served-model-name cyber-pt --max-model-len 4096 --port 6667
3.1 文本续写
挑选30~50个专业领域数据样本,给大模型一个领域开头,让大模型接着向下续写。例如:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:6667/v1", api_key="EMPTY")
resp = client.completions.create(
model="cyber-pt",
prompt="CVE-2021-44228 是 Apache Log4j2 中的一个",
max_tokens=150,
temperature=0.2,
)
print(resp.choices[0].text)
这个环节重点观察术语使用和句子连贯性。可以分别用基座模型和预训练模型跑相同的prompt做对比:如果模型胡言乱语或反复重复同一句话,说明学习率或训练轮次可能过大,应选择更早的checkpoint;如果生成内容与专业领域数据差距较大,说明训练还不充分,应选择更晚的checkpoint。
那么如何判断模型训练后在专业领域的回答是否明显变好?可以采用关键词命中率做粗略评估:先让基座模型(未合并的 Qwen3-32B)跑一遍,再让合并后的模型跑一遍,对比两者的关键词命中率。不要只看训练后关键词的绝对命中率,因为基座模型本身可能已经知道一部分答案,绝对命中率的意义有限。
3.2 评测集评测
可以按照 1:1 的比例,从通用数据集和专业领域数据集中选择几百条数据构建评测集,然后使用《大模型训练全流程实战指南工具篇(十三)——大模型评测实战(数据集评测+自动化评测)》中介绍的EvalScope工具进行评测。这项评测重点看相对变化,而不是绝对分数;两次评测应使用同一套接口和模板。如果领域loss下降但通用集指标下降明显,说明灾难性遗忘较明显,应减少训练轮次、降低学习率或提高通用语料比例;如果两边都没有明显变化,说明这次预训练大概率没有起作用。
注意: 预训练阶段的评测结果是方向性的,不要拿它当最终结论。真正能体现模型价值的评测,要等SFT之后再做
3.3 必要时重新调整参数训练
选择checkpoint本质上是在同一轮训练结果中做低成本筛选。如果多个checkpoint的文本续写和评测集结果都不理想,就不能只盯着checkpoint继续挑,而应回到配置文件和数据集层面,重新调整参数后再训练一轮。
可以从以下几类现象入手判断:
- 领域loss下降,但通用能力退化明显:说明灾难性遗忘较重,可以降低学习率、减少num_train_epochs,或提高通用语料在预训练数据中的比例。
- 领域loss下降不明显,续写也几乎没有改善:说明领域知识注入不足,可以适当提高学习率或增加训练轮次,同时检查专业领域数据是否过少、质量是否达标、cyber_pt注册和prompt映射是否正确。
- loss震荡明显或长期不收敛:可以降低学习率、增大warmup_ratio,并检查batch size、梯度累积步数、bf16和gradient_checkpointing等训练配置是否匹配当前硬件。
- 显存不足或训练频繁中断:可以降低cutoff_len、减小per_device_train_batch_size、增大gradient_accumulation_steps,并确认gradient_checkpointing已开启。
- LoRA容量不足或遗忘明显:可以适当调整 lora_rank / lora_alpha,但不建议一次改动过多,最好保持少量变量做对照实验。
重新训练时,建议每次只调整1~2个关键变量,并记录对应的配置文件、数据比例和评测结果,避免“一次改太多、最后不知道哪个参数起作用”。重训完成后,仍然按照“文本续写 + 评测集评测”的流程重新筛选 checkpoint,最终以评测结果最好的模型为准。
最终,经过评测集评测,笔者在本次训练结果中选定checkpoint-6250作为预训练结果模型;如果后续调整参数重新训练后得到更优结果,则以新一轮评测最优的模型作为下一阶段 SFT 的起点。
以上就是本篇文章的全部内容。本文完整代码可关注笔者同名公众号:大模型真好玩,并私信 大模型训练 免费获取。
四、总结
本篇按照“数据准备 → 参数设置 → 启动训练 → 效果查看”四步,完成了网络安全大模型LoRA增量预训练:数据侧将chunks_judged.jsonl转为{"text": ...}格式,按通用语料约60%~70%专业领域语料约30%~40%的比例混合,并用高质量问答数据补充语料;参数侧给出可用的LoRA预训练配置;训练侧通过一条命令启动训练并合并适配器;评测侧从验证loss、文本续写、领域问答和通用benchmark 等角度确认效果。
至此模型已经“读完了领域教材”,但它还不知道如何将知识组织成有用的回答。下一篇将进入微调数据集的构建:从领域文档出发,如何设计问题、生成答案、进行质量筛选,并转换为 LLaMA-Factory 可直接使用的 SFT 格式。敬请期待~
除大模型训练外,笔者也在同步更新《深入浅出LangChain&LangGraph AI Agent 智能体开发》免费专栏,要说明该专栏适合所有对 LangChain 感兴趣的学习者,无论之前是否接触过 LangChain。该专栏基于笔者在实际项目中的深度使用经验,系统讲解了使用LangChain/LangGraph如何开发智能体,目前已更新 50 讲,并持续补充实战与拓展内容。欢迎感兴趣的同学关注笔者的掘金账号与专栏,也可关注笔者的同名微信公众号大模型真好玩,每期分享涉及的代码均可在公众号私信: LangChain智能体开发免费获取。
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
Foreword
In the previous article, LLM Training Process Practical Guide (Sixteen) — Building Pre-Training Data Sets, the author obtained, through structured slices and quality differentiation, a well-structured, clean, specialized field data set for use by LaMA-Factory. This, however, is not, strictly speaking, a complete data file that LLIMA-Factory can use directly for pre-training.
This article will evolve in four steps according to the Data Preparation Parameter Settings Quest Start Training Quest: first, to show how to organize, expand and configure the specialized field data to the full pre-training data that LaMA-Factory can use directly; then to describe how to use LaMA-Factory to start Z43Z pre-training. Upon completion of the training, you will be provided with an expanded expertise of LLM, which will serve as a model base for subsequent fine-tuning.
Full pre-training data preparation
1.1 Build data sets in specialized fields
A small technique was introduced before the operational section (16) of the LLM training-process-based field guide - the building of the pre-training data set - was organized into a specialized field data set. Do you remember that in LLM's training of a full-process operational guide (fourteen) - Cybersecurity LLM data acquisition, the article mentioned the possibility of using the EasyDataset distillation data set, but the distillation was based on question-and-answer data. However, since the pre-training is concerned only with “the next token as predicted above”, it is not concerned with whether the text is a paper or a question-and-answer session; the question-and-answer text also contains the terminology of the field, the presentation of the question and the organizational logic of the answer. High-quality fine-tuning of data concentration questions and answers can therefore be combined into natural text for use as pre-trained data.
You can export the data from the distillation of EasyDataset to the alpaca format:
The following code can then be used to extract questions and answers and to spell them into question-and-answer text:
import json
def alpaca_to_text(item):
q = item.get("instruction", "").strip()
if item.get("input"):
q += "\n" + item["input"].strip()
return f"{q}?{item.get('output', '').strip()}"
This can be followed by extracting text field from the centralized data set processed by the LLM Training Process Practice Guide (Sixteen) — Pre-Training Data Set Construction, which is linked to distilled datasets, and obtaining pre-training data sets in the professional field:
import json, hashlib
DISTILL = './llamafactory_data/cyber_distill.jsonl'
SRC = "./llamafactory_data/chunks_judged.jsonl"
DST = "./llamafactory_data/cyber_pt.jsonl"
dataset = []
with open(SRC, encoding="utf-8") as fin:
for line in fin:
rec = json.loads(line)
if not rec.get("keep"):
continue
text = rec["text"].strip()
if len(text) < 100:
continue
dataset.append({"text": text})
kept= kept + 1
with open(DISTILL, encoding="utf-8") as f:
data = json.load(f)
for item in data:
text = alpaca_to_text(item)
kept= kept + 1
dataset.append({"text": text})
with open(DST, 'w', encoding="utf-8") as fout:
for item in dataset:
fout.write(json.dumps(item, ensure_ascii=False) + "\n")
print(f "{kept}")
Following the completion of implementation, a total of 30,847 pieces of data were obtained from distillation and data governance.
1.2 Introduction of common data sets to avoid loss of universal capability
The specialized domain data sets cover only knowledge of cybersecurity, while knowledge injection at the pre-training stage determines to a large extent the capabilities of the model (article (v) of the LLM Training Process-Wide Guide Tool - LLM Training Process-Wide step details and references in the Recommendations for Response Tools). If trained in specialized data sets only, the model would have studied only a single distribution over a long period of time and the general question-and-answer and common-sense knowledge available could have declined, which is what is known as “catastrophe oblivion”. Thus, pre-training does not normally use a single field language, but rather a mix of generic and field language.
So, what's the generic data set? There is no uniform standard on this issue and there is little openness on the part of major companies. In combination with the daily work of the writer, the percentage of the general data set is generally slightly higher than that of the specialized area, with the most common ratio being 60 to 70 per cent of the general data set and 30 to 40 per cent of the language material in the specialized area (which can be calculated in rough terms).
So how do we build a common data set? Is it easy to climb some web pages and clean them up? It's not that simple. Generic datasets need to be stratified by category to ensure a proportional distribution of data across categories, and the matching of data across categories is usually also a commercial secret for companies. The author shares a basic idea here: first, to identify 6 to 10 broad categories, then to disaggregate them into several subcategories in each broad category, and to establish quotas for each category, such as 15 per cent computers, 10 per cent mathematics, 15 per cent geography/historical/humanistic, and 30 per cent of the remaining encyclopedia pages; and finally, to adjust the categories to pre-training effects dynamically.
Common data sets are more widely available, and communities and some companies now have publish open-source common data sets, such as Wikipedia data on HuggingFace; if a complete Chinese data set is required, it can also be used in the pre-training data sets in Chinese, such as WuDaoCoorpora or CLUCorpus 2020, built by the OpenDataLab MNBVC and Beijing Institute of Intellectual Intelligence (BAAI).
The following is an example of how to build pre-trained language using the 220G version (WuDaoCorpora Text, 59,100,001) of the Chinese Pre-Training Data Set.
Note: Official data from the Gospel data set needs to be downloaded from Science Data Bank. The code below is read at p. 208p2002/wudao of the community moving to HuggingFace, about 125 GB. The data set is divided into several paraquets, each about 100 MB. Parquet is a column compression storage format for large data scenes, which can be understood as a " column-based storage, compressed class Excel table ".
Since the epidemiology data can only be read in fractions, the following code will break the sequence and limit the number of fractions to read and avoid reading all the hundreds of GB data at once. The core code is as follows:
import os, json, random, hashlib
os.environ.setdefault("HF_ENDPOINT", "https://hf-mirror.com")
from datasets import load_dataset
from huggingface_hub import list_repo_files
random.seed(42)
N = 45610
TARGET = { encyclopedia: 0.40, blog: 0.25, technology: 0.10,
Education 0.08, Culture 0.08, Economy 0.05, News 0.04
BAD = ["Spider pool", "Seo", "Design," "and WeChat," "Web site optimization"]
repo = "p208p2002/wudao"
files = [f for f in list_repo_files(repo, repo_type="dataset")
if f.endswith(".parquet")]
base = f"{os.environ['HF_ENDPOINT']}/datasets/{repo}/resolve/main/"
files = [base + f for f in files]
# Split by time, break by break to cover different categories
quota = {k: int(N * v) for k, v in TARGET.items()}
counts, seen = {k: 0 for k in quota}, set()
ds = load_dataset("parquet", data_files=files[:80], split="train", streaming=True)
with open("./llamafactory_data/wudao_pt.jsonl", "w", encoding="utf-8") as fout:
for item in ds:
cat = item.get("dataType", "")
if cat not in quota or counts[cat] >= quota[cat]:
continue
text = f"{item['title']}\n\n{item['content']}".strip()
if len(text) < 200 or any(w in text.lower() for w in BAD):
continue
digest = hashlib.md5(text.encode("utf-8")).hexdigest()
if digest in seen:
continue
seen.add(digest)
counts[cat] += 1
fout.write(json.dumps({"text": text, "category": cat},
ensure_ascii=False) + "\n")
if all(counts[k] >= quota[k] for k in quota):
break
The results are as follows:
1.3 Selection of training options based on data scale
The combination of./llamafactory data/wudao pt.jsonl into./llalamafactory data/cyber pt.jsonl generally requires the number of tokens for the statistical set once the pre-training data set has been produced. Pre-training is more concerned with token numbers than with bar numbers, and training strategies vary widely depending on the size of the token:
- Less than 1M (1,000,000) tokens: Incremental pre-training is not recommended and it is more appropriate to go straight to fine-tuning.
- 1M to 100M tokens: a reasonable amount of data that can normally be produced by individuals and small teams, with the use of LoRA incremental pre-training.
- 100M-1B (1,000,000,000) tokens: a reasonable scale of data that can be produced by large teams and medium-sized companies is recommended for incremental training in LoRA.
- 1B tokens and above: for enterprise level LLM, it is recommended that all parameters be pre-trained directly.
So how do you measure the number of tokens that the current data has? In the LLM Training Process Practice Guide Basic (ii) - LLM Document Structure Interpretation and Analysis, it was mentioned that the original model document contained a symmetry file called Tokenizer.json. You can first read textZ109Z from.../llamafactory data/cyber pt.jsonl, counting tokens using LLM's symmetry:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B")
total, docs = 0, 0
with open('./llamafactory_data/cyber_pt.jsonl', encoding="utf-8") as f:
batch = []
for line in f:
text = (json.loads(line).get("text") or "").strip()
if not text:
continue
batch.append(text)
docs += 1
if len(batch) == 500:
enc = tokenizer(batch, add_special_tokens=False, return_length=True)
total += sum(enc["length"])
batch = []
if batch:
enc = tokenizer(batch, add_special_tokens=False, return_length=True)
total += sum(enc["length"])
print(f"{docs} bar, {total / 1e6:2f}M tokens")
According to statistics, a total of 76,457 pre-training data sets were obtained, approximately 926 M tokens. Based on the size of the data, this pre-training will be based on the LoRA incremental pre-training.
1.4 Registered data in LLamaFactory Set
Upon completion of the data set, next step is required to register it in LaMA-Factory. In the LLM Training Practice Guide Tool (ix) - LLamaFactoryZ43Z Training Tool Use Guide, the writer described how LLamar-Factory used Z43Z fine-tuning; the pre-training process was similar and the first step was the registration of data sets.
First, place cyber pt.jsonl under LlamaFactory/data/:
The following configuration was then added in LlamaFactory/data/dataset info.json:
"cyber_pt": {
"file_name": "cyber_pt.jsonl",
"columns": {
"prompt": "text"
}
}
Of these, the columns' key is field inside LaMA-Factory and the value is the real listing in the document. Pre-training uses promptfield to carry the body, so text needs to be mapped to prompt.
LLamaFactory Conducts pre-training
LLAMA-Factory initiates training by "command line + Profile". It can be said that pre-training is half as successful as long as the configuration is properly prepared. The author is pre-trained based on the Qwen3-32B model.
Note: Training in the Qwen3-32B model typically requires 80-160 GB VRAM, approximately 1-2 A100 or H100.
2.1 Preparation of profiles
New Params folder in the LLAMA-Factory directory and new cyber pt.yaml with the following configuration:
## model (including model weight path)
model_name_or_path: ~/Qwen3-32B
trust_remote_code: true
### method
stage: pt
do_train: true
finetuning_type: lora
lora_rank: 8
lora_alpha: 8
lora_target: all
### dataset
dataset: cyber_pt
cutoff_len: 4096
overwrite_cache: true
preprocessing_num_workers: 16
### output
output_dir: ~/saves/qwen3-32b/lora/pretrain
logging_steps: 10
save_steps: 50
plot_loss: true
overwrite_output_dir: true
### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 5.0e-5
num_train_epochs: 20.0
lr_scheduler_type: cosine
warmup_ratio: 0.05
bf16: true
gradient_checkpointing: true
group_by_length: true
ddp_timeout: 180000000
The detailed description of the parameters has already been presented in the LLM Training Process Practice Guide Tool (x) — Detailed Parameters of LLM Training that White can understand, and can be recalled if forgotten. The following key parameters are highlighted: stage is set as pt, indicating pre-training; finetunning type is set as lora, indicating the use of LoRA incremental pre-training; lora rank / lora alpha is set at 8/8, at the pre-training stage at 1:1 and the values are not set too high because the original generic capability of the model needs to be taken into account; Num train epochs, although set at 20, does not necessarily choose the last checkpoint, and it is perfectly possible to select one of the middle checkpoints for consolidation.
2.2 Operational commands
After the configuration is completed, execute under the LlamaFactory-main directory:
llamafactory-cli train ~/LlamaFactory-main/params/cyber_pt.yaml
Logs will be printed according to logging steps Lose, learning rate, epoch and grad norm. At the same time, as a result of the setup of plot loss: true, the training process draws the overall trend of loss into a PNG. The training lasted about two days, and the overall loss curve drawn after the training was completed was as follows:
2.3 Merge LORA weights
In the LLM Training Process Practice Guide Tool (ix) - LLamaFactoryZ43Z Training Tool Use Guide, the writer described the LoRA adaptor kept under the LLM defined storage catalogue. A adapter is saved every 50 steps as ~/saves/qwen3-32b/lora/pretrain and save steps are set in the configuration file. An appropriate adapter will be selected and merged with the Qwen3-32B model.
So which should we choose from all these checkpoints? From the experience of writers, the general choice is between 0.5 and 0.8 for checkpoints. The corresponding checkpoint in this range can be combined with the Qwen3-32B model, and evaluated separately after having obtained several models, and ultimately selected the best checkpoint for pre-training results. The result is also the starting point for the next phase of fine-tuning SFT.
Combining checkpoint with the original model also requires the preparation of a profile. Creates a new cyber pt merge.yaml under the profile directory, with the following configuration:
model_name_or_path: ~/Qwen3-32B
adapter_name_or_path: ~/saves/qwen3-32b/lora/pretrain/checkpoint-6250
template: qwen3_nothink
trust_remote_code: true
export_dir: ~/pretrain/Qwen3_32B_cyber_pt
export_size: 5
export_device: cpu
export_legacy_format: false
As the exported model will have more than 60 GB, the export size indicates that the exported individual.safetensors file does not exceed 5 GB. Export a model to perform the following command:
llamafactory-cli export ~/LlamaFactory-main/params/cyber_pt_merge.yaml
III. Pre-training impact check
Upon completion of the above, you will have several consolidated models. The final pre-training model needs to be selected according to the performance of each model. The most appropriate model parameters can be selected using the combined model of the vllm deployment, followed by the two methods commonly used by the writer:
Absolute path to a merged model \\
--served-model-name cyber-pt --max-model-len 4096 --port 6667
3.1 Continuation of text
Selecting data for 30 to 50 areas of specialization sample, giving LLM an area to start and LLM to continue. For example:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:6667/v1", api_key="EMPTY")
resp = client.completions.create(
model="cyber-pt",
"CVE-2021-44228 is one of Apache Log4j2"
max_tokens=150,
temperature=0.2,
)
print(resp.choices[0].text)
This link focuses on the use of terminology and consistency of sentences. The same prompt can be compared with the base model and the pre-training model: If the model is obfuscated or repeats the same sentence, indicating that the learning rate or training cycle may be too large, an earlier checkpoint should be chosen; and a later checkpoint should be chosen if there is a large gap between content generation and data in the professional field, indicating that training is inadequate.
How then would it be judged that the response to model training in the field of expertise has clearly improved? A rough assessment can be made using a keyword hit rate: let the base model (unintegrated Qwen3-32B) run it, then let the merged model run it, comparing the key hit rate of both. Instead of looking at the absolute hit rate of post-training keywords, the base model itself may already know some of the answers and the absolute hit rate is of limited significance.
3.2 Evaluation and evaluation
Based on the 1:1 ratio, several hundred data sets can be selected from the General Data Set and the Professional Area Data Pool to build the assessment collection, and then the EvalScope tool presented in the LLM Training Full Process Practice Guide Book (XIII) - LLM Assessment of War (DS+ Automation Assessment) is used for the evaluation. This assessment focuses on relative changes rather than absolute scores; the same set of interfaces and templates should be used for both evaluations. If the decline in the field of Loses and the decrease in the CFI are more obvious, training rotations should be reduced, the learning rate reduced or the common language ratio increased; if there is no significant change on either side, the pre-training rate does not work.
Note: The assessment of the pre-training stage is directional, not final. A real assessment of the value of the model.
3.3 Parameter training adjusted as necessary
The selection of checkpoint is essentially a low-cost screening exercise in the same training cycle. If multiple checkpoint text continuation and evaluation results are not ideal, one cannot simply stare at checkpoint and go back to the profile and data set levels and re-engineer the parameters and train again.
This can be seen in the following categories:
- Area Loses has declined, but general competence has deteriorated significantly: it is more indicative of catastrophic oblivion, which can reduce learning rates, reduce num train epochs, or increase the share of common language in pre-training data.
- The decline in the field of loss is not significant, and there has been little improvement in renewal: insufficient knowledge in the field of illustrations can improve the learning rate appropriately or increase the number of training rounds, while checking whether the data in the field of specialization are too low, the quality is satisfactory, the ciber pt registration and the prompt mapping is correct.
- The oscillation of ross is either visible or chronic: it reduces the learning rate, increases the warmup ratio, and checks whether the training configurations, such as catch size, gradient cumulative steps, bf16 and gradient checkpointing, match the current hardware.
- VRAM is inadequate or interrupted: it can reduce the number of cautoff len, reduce per device train batch size, increase the number of gradiant accumulation steps and confirm that gradient checkpointing is on.
- LoRAs are not sufficient in capacity or forgotten clearly: they can be adjusted appropriately, but they are not recommended for too much change, and it is best to keep a small number of variables compared to the experiment.
In re-training, it is recommended that only one or two key variables be adjusted at a time and that the corresponding configuration documents, data ratios and evaluation results be recorded so as to avoid “too many changes at a time and ultimately not knowing which parameters work”. Following the completion of the retrainment, checkpoint is re-screened according to the process of “text renewal + assessment” and is ultimately based on the best model of the results.
Ultimately, after evaluation, the author selected checkpoint-6250 as a pre-training result model in the results of this training; if the subsequent adjustment parameters are retrained with better results, the next best assessment model is used as the starting point for the next phase of SFT.
This is the whole article. The complete code of this article is very interesting for the author under the same name as WeChat Official Account: LLM, which is available free of charge for personal trust training.
Summary
In accordance with the "Data Preparation Parameters Set-up → Start Training → Effect View" in four steps, the Web Security LLMLORA incremental pre-training was completed: the data side converted chunks judged.jsonl to {"text": } format, blended around 60% to 70% of the generic language in terms of approximately 30% to 40% of the generic language and supplemented with high-quality question and answer data; the LoRA pre-training profile was given on the side of the parameters; the training side started training with a command and combined adapter; and the evaluation side confirmed effects from the perspective of validation of loss, text renewal, field question and answer, and generic benchmark.
The model is “read out of field teaching materials”, but it does not know how to organize knowledge as a useful answer. The next one will enter the fine-tuned construction of the data set: from the field document, how to design questions, generate answers, screen for quality and convert to the direct SFT format of LLAMA-Factory. Please look forward to it
In addition to the LLM training, the author is simultaneously updating the free column " Deep Out LangChain & LangGraph AI Agent Z42Z Development ", which is intended for all learners interested in Langchain, regardless of whether they have previously been in contact with Langchain. Based on the author ' s in-depth use experience in the actual project, the system explains how to develop AI agent using Langchain/LangGraph, which has been updated to date and continuously complements the operational and outreach content. It is also interesting to note that the same name of the author, WeChatWeChat Official Account LLM, is also available for free at the WeChat Official Account private letter: LangchainZ42Z Development.
Auto-fetched and translated by AI Atlas. All rights belong to the original author.