简述
试验参考了mini_qwen 的开源实现
分词器使用Qwen/Qwen2.5-0.5B-Instruct,通过扩充模型隐藏状态层数、嵌入层维度和注意力头数,增加参数量到1B,使用flash_attention_2进行加速
主要特点:
低资源需求:预训练和微调仅需 12GB 显存,DPO 训练需要 14GB 显存
训练数据:使用来自 BAAI(北京智源人工智能研究院)的数据集,包括用于预训练的 160 亿 tokens、用于微调的 900 万条样本,以及用于偏好优化的 6 万条样本。
数据集
魔塔社区 BAAI/IndustryCorpus2 数据集,根据需要下载
# 下载预训练数据集
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'film_entertainment/*/high*' --local_dir 'data/pt' # 数据量较大,英文文件选择前3个文件
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'computer_programming_code/*/high*' --local_dir 'data/pt'
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'computer_communication/*/high*' --local_dir 'data/pt' # 数据量较大,英文文件选择前3个文件
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'tourism_geography/*/high*' --local_dir 'data/pt'
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'artificial_intelligence_machine_learning/*/high*' --local_dir 'data/pt'
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'news_media/*/high*' --local_dir 'data/pt'
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'literature_emotion/*/high*' --local_dir 'data/pt' # 数据量较大,英文文件选择前3个文件
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'accommodation_catering_hotel/*/high*' --local_dir 'data/pt'
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'current_affairs_government_administration/*/high*' --local_dir 'data/pt' # 数据量较大,英文文件选择前3个文件
modelscope download --dataset 'BAAI/IndustryCorpus2' --include 'mathematics_statistics/*/high*' --local_dir 'data/pt'
查看下数据
dataset = load_dataset("parquet", data_files="mini_data/pt/accommodation_catering_hotel/chinese/high/rank_00000.parquet", split="train")
print(dataset[0])
# 大概这么个结构 { "text": "马亮:如何破解外卖骑手的\"生死劫\"\n在消费至上的今天,企业不应道德绑架消费者,让消费者为企业的伪善埋单。。。。。。", "alnum_ratio": 0.9146919431, "avg_line_length": 158.25, "char_rep_ratio": 0.044444444400000005, "flagged_words_ratio": 0.0, "max_line_length": 223, "num_words": 404, "perplexity": 858.6, "quality_score": 4.0625, "special_char_ratio": 0.1000526593, "word_rep_ratio": 0.088772846, "_id": 200200005357, "industry_type": "住宿_餐饮_酒店" }
这个结构是一个经过质量分析或过滤的训练样本的 JSON 表示,用于语言模型训练前的数据评估或筛选阶段。它除了包含原始文本(text
)外还包含了一系列用来衡量数据质量的统计特征指标,用于判断该样本是否值得保留用于训练。
🔹 质量指标字段说明:
字段名 | 说明 |
---|---|
alnum_ratio |
字母数字字符所占比例。用于判断文本是否主要为自然语言(而非乱码或表格类数据) |
avg_line_length |
平均每行字符数。可能反映文本结构是否合理(过长/过短) |
char_rep_ratio |
字符重复率。例如“哈哈哈哈哈哈”这种重复率就很高 |
flagged_words_ratio |
敏感词或不良词汇占比(0 表示未检测到敏感词) |
max_line_length |
最长一行的字符数。可用于过滤极端异常格式文本 |
num_words |
词数总计。用于衡量样本长度 |
perplexity |
使用某个语言模型评估的困惑度(perplexity)。数值越低,表示文本越“正常”或模型越容易预测它 |
quality_score |
综合质量评分。可能是上述特征加权后的结果,衡量样本是否值得用于训练 |
special_char_ratio |
特殊字符(如 #¥%&* 等)在文本中的占比 |
word_rep_ratio |
单词重复率(如“外卖外卖外卖平台”) |
训练逻辑
加载数据集
# 加载数据集并进行预处理
directories = [
"accommodation_catering_hotel",
"artificial_intelligence_machine_learning",
"computer_communication",
"computer_programming_code",
"film_entertainment",
"literature_emotion",
"news_media",
"tourism_geography",
"current_affairs_government_administration",
"mathematics_statistics",
]
data_files = find_files(directories)
dataset = load_dataset("parquet", data_files=data_files, split="train", columns=["text"]) # 只保留text字段
dataset = dataset.shuffle(seed=42)
数据清洗,将原始文本 → 添加结束符 → 分词 → 拼接成一长串 → 按 block_size 切成多个训练用的样本块(每块长度一致),给每条文本加上自定义的“结束符” <|im_end|>,
把所有样本的 token 串接在一起(例如把多个 [101,102]
合并为 [101,102,103,104,...]
),这是因为 GPT 模型的预训练目标是连续预测序列,所以训练输入是一个“连续的 token 流”。
计算总长度并对齐
得到拼接后 token 总长度(例如 10,356)
只保留整除
block_size
(1024)的部分,截断掉尾部多余部分,例如:10356 → 10240(保留完整的 10 块)切成 1024 个 token 一块的样本,每隔 1024 个 token 分一块,生成多个训练样本,输出结构:
{
"input_ids": [[token1...token1024], [token1025...token2048], ...],
"attention_mask": 同理
}
参考预训练代码
def preprocess_dataset(examples):
"""预处理预训练数据集,将文本分词并分块"""
eos_token = "<|im_end|>"
text_examples = [text + eos_token for text in examples["text"]] # 添加结束符
tokenized_examples = tokenizer(text_examples, add_special_tokens=False)
# 将分词结果拼接并分块
concatenated_examples = {
k: list(chain(*tokenized_examples[k])) for k in tokenized_examples.keys()
}
total_length = len(concatenated_examples[list(concatenated_examples.keys())[0]])
block_size = 1024 # 分块大小
total_length = (total_length // block_size) * block_size # 对齐块大小
result = {
k: [t[i : i + block_size] for i in range(0, total_length, block_size)]
for k, t in concatenated_examples.items()
}
return result
# 应用预处理函数
train_dataset = dataset.map(
preprocess_dataset,
batched=True,
batch_size=5000,
remove_columns=dataset.column_names,
num_proc=16,
)
,原来有很多条文本,现在经过这段预处理函数:
文本 → 拼接 → 分词 → 连续 token 序列 → 按块切分
输出的每个样本都是
1024
个 token 的一段,可直接送入语言模型进行训练(如 GPT)
数据预处理
训练配置
预训练
accelerate_config.yaml
文件包含了用于配置训练环境的参数。以下是各个配置项的含义:
- compute_environment: 指定计算环境,这里为本地机器 (
LOCAL_MACHINE
)。- debug: 调试模式,设置为
false
表示不启用调试。- deepspeed_config: 包含与 DeepSpeed 相关的配置:
- gradient_accumulation_steps: 梯度累积步数,这里设置为 16。
- gradient_clipping: 梯度裁剪值,防止梯度过大,这里为 1.0。
- offload_optimizer_device: 优化器的卸载设备,这里为
none
表示不卸载。- offload_param_device: 参数的卸载设备,这里为
none
。- zero3_init_flag: 是否启用 ZeRO-3 初始化,这里为
false
。- zero_stage: ZeRO 优化的阶段,这里设置为 2。
- distributed_type: 分布式训练类型,这里为
DEEPSPEED
。- downcast_bf16: 是否降低 bf16 精度,这里设置为 'no'。
- enable_cpu_affinity: 是否启用 CPU 亲和性,设置为
false
。- machine_rank: 当前机器在分布式训练中的排名,这里为 0。
- main_training_function: 主训练函数的名称,这里为
main
。- mixed_precision: 混合精度训练,这里使用 bf16。
- num_machines: 参与训练的机器数量,这里为 1。
- num_processes: 每台机器上的进程数量,这里为 2。
- rdzv_backend: rendezvous 后端,这里为
static
。- same_network: 是否在同一网络中,设置为
true
。- tpu_env: TPU 环境配置,这里为空。
- tpu_use_cluster: 是否使用 TPU 集群,设置为
false
。- tpu_use_sudo: 是否使用 sudo 权限,设置为
false
。- use_cpu: 是否使用 CPU 进行训练,设置为
false
。
这些配置项帮助用户设置和优化模型训练过程,尤其是在使用 DeepSpeed 进行分布式训练时, 配置参考
compute_environment: LOCAL_MACHINE
debug: false
deepspeed_config:
gradient_accumulation_steps: 16
gradient_clipping: 1.0
offload_optimizer_device: none
offload_param_device: none
zero3_init_flag: false
zero_stage: 2
distributed_type: DEEPSPEED
downcast_bf16: 'no'
enable_cpu_affinity: false
machine_rank: 0
main_training_function: main
mixed_precision: bf16
num_machines: 1
num_processes: 2
rdzv_backend: static
same_network: true
tpu_env: []
tpu_use_cluster: false
tpu_use_sudo: false
use_cpu: false
训练逻辑
# 已经下载了Qwen2.5-0.5B-Instruct地址
model_path = "./models/Qwen2.5-0.5B-Instruct"
config = AutoConfig.from_pretrained(model_path)
# 调整模型配置
config.num_attention_heads = 16
config.num_key_value_heads = 4
config.hidden_size = 1024
config.num_hidden_layers = 48
# 加载模型
model = AutoModelForCausalLM.from_config(config, torch_dtype=torch.bfloat16, attn_implementation="flash_attention_2")
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained(model_path)
# 训练参数配置
training_args = TrainingArguments(
output_dir=output_path,
overwrite_output_dir=True,
learning_rate=1e-4,
warmup_ratio=0.1,
lr_scheduler_type="cosine",
num_train_epochs=1,
per_device_train_batch_size=12,
gradient_accumulation_steps=16,
save_steps=100_000,
save_total_limit=3,
bf16=True,
# save_only_model=True,
logging_steps=20,
)
# 初始化Trainer
trainer = Trainer(
model=model,
args=training_args,
data_collator=collator,
train_dataset=train_dataset,
)
使用accelerate 加速训练
模型参数量: 576851712
表示模型总共有 576,851,712 个参数,也就是约 5.77 亿参数(≈ 577M)。
和之前用的MiniMind架构的模型比训练速度要慢很多,所以直接跳过预训练使用基座进行微调
SFT微调
trl 配置
# 训练参数配置
training_args = SFTConfig(
output_dir=output_path, # 训练完成后模型保存的目录
overwrite_output_dir=True, # 如果目录已存在则覆盖原模型
learning_rate=1e-5, # 学习率,SFT阶段建议小一点
warmup_ratio=0.1, # 热身步数比例,用于逐渐增加学习率
lr_scheduler_type="cosine", # 学习率调度策略:余弦退火
num_train_epochs=3, # 训练轮数
per_device_train_batch_size=12, # 每张显卡的batch大小(显存不够就调小)
gradient_accumulation_steps=16, # 梯度累计步数,总batch大小 = 12 × 16 = 192
save_strategy="epoch", # 每轮结束保存一次模型
save_total_limit=3, # 最多保存3个checkpoint,旧的自动删掉
bf16=True, # 使用 bfloat16 进行训练(比 fp16 更稳定,NVIDIA A100/H100 支持)
logging_steps=20, # 每20步打印一次日志
)
# 初始化Trainer
trainer = SFTTrainer(
model=model, # 使用的模型(已初始化)
args=training_args, # 上面定义的训练参数
train_dataset=dataset, # 训练数据集
tokenizer=tokenizer, # 分词器
formatting_func=formatting_prompts_func, # 格式化数据的函数,把样本转换成 prompt + completion
data_collator=collator, # 数据整理器(例如自动填充、构建input_ids等)
)