导读:本期聚焦于赵六创作的《Qwen1.5-32B-Chat微调时如何构建多轮对话数据并设置Loss Mask?》,敬请观看详情。在拿Qwen1.5-32B-Chat做监督微调时,最容易被忽略却直接影响收敛效果的就是多轮对话样本的组织方式与损失掩码。模型虽然能读入拼接后的长文本,但若把用户提问也计入反向传播,梯度会被无关目标稀释。正确做法是用特殊标记切分角色,并为每轮助手回复构造对应的掩码张量,只让模型学习生成答案部分。本文从对话模板拼接、token级标签赋值、常见格式错误三个角度,说明如何写出能被训练框架直接消费的jsonl样本,以及如何在自定义DataCollator里动态生成labels,避免padding部分参与计算。理清这些细节,能让三十多B参数的聊天模型更快学会指定话术而不偏离指令。

Qwen1.5-32B-Chat作为三十亿级别参数的对话模型,在垂直场景落地时往往需要通过监督微调注入领域话术。与单轮指令微调不同,多轮对话要求模型理解上下文承接关系,而数据构建的核心难点并不在于堆量,而在于如何让训练目标精准落在助手回复上。如果直接把整段对话都算进损失,模型会被用户侧文本带偏;若掩码设置不当,又会出现梯度为空或padding污染。下面从数据格式、掩码原理和代码实现三个层面展开。

Qwen1.5-32B-Chat微调时如何构建多轮对话数据并设置Loss Mask?

多轮对话数据的标准拼接格式

Qwen1.5系列在微调时沿用ChatML风格,每一轮都用特殊标签包裹角色与内容。一个典型的三轮样本在原始jsonl中应以对话数组形式存在,由systemuserassistant交替组成。训练框架在读取后会将它们拼为单一序列:先写系统提示,再依次放入用户提问与助手回答,轮间不留额外分隔,但角色标签本身提供了边界信号。

很多人在构造数据时习惯自己用换行符硬拼字符串,这会带来两个问题。其一是丢失了模型原本在预训练阶段见过的角色标记分布,导致微调初期困惑度偏高;其二是后续做损失掩码时难以自动定位助手片段起止。推荐做法是保留结构化字段,在Dataset的__getitem__里调用官方的tokenizer.apply_chat_template,让模板函数完成拼接与特殊token插入,这样既能对齐原厂分布,也方便下一步按角色偏移打标。

下面展示一条合规的多轮数据及基础加载方式。注意messages字段严格区分角色,不要将用户和助手内容混在同一个字符串里,否则掩码逻辑会彻底失效。

import json

sample = {
    "messages": [
        {"role": "system", "content": "你是有害信息过滤助手"},
        {"role": "user", "content": "怎么看待暴雨预警"},
        {"role": "assistant", "content": "暴雨预警由气象台发布,建议关注官方渠道"},
        {"role": "user", "content": "那台风呢"},
        {"role": "assistant", "content": "台风预警分蓝黄橙红四级,按等级做好防护"}
    ]
}

with open("train.jsonl", "w", encoding="utf-8") as f:
    f.write(json.dumps(sample, ensure_ascii=False) + "n")

Loss Mask的底层原理与标签赋值

损失掩码的本质是一个与input_ids等长的标签张量labels。在因果语言建模中,模型对位置i的预测目标本是位置i+1的真实token;但当我们需要忽略某些位置(如用户输入、系统提示、padding)时,就把对应labels设为-100。PyTorch的CrossEntropyLoss遇到-100会直接跳过该位置梯度,从而实现只训练助手回复。

具体赋值不能简单按字数切分,因为中文经过BPE分词后一个汉字可能对应多个token,且角色标签本身(如<|im_start|>)也会占用token位。正确流程是:先用apply_chat_template得到拼接文本与token,再借助模板返回的token_type_ids或二次解析,定位每个assistant段在token序列中的起止索引,将这段区间(通常包含起始角色标记后的内容,但不含下一段的用户标记)标为原token,其余全部填-100。

一个常见误区是只掩码用户文本却忘了掩码system提示。实际上system部分在推理时虽会传入,但微调目标应是让模型学会从用户问题到助手答案的映射,而非背诵系统设定,因此system同样需标-100。以下代码演示了基于偏移量的标签构建思路。

from transformers import AutoTokenizer

tok = AutoTokenizer.from_pretrained("Qwen/Qwen1.5-32B-Chat", trust_remote_code=True)
text = tok.apply_chat_template(sample["messages"], tokenize=False)
ids = tok.encode(text)

# 假设我们已知两轮assistant内容在解码文本中的字符起点
# 实际中可用正则定位 <|im_start|>assistantn 与 <|im_end|>
labels = [-100] * len(ids)
assistant_spans = [(20, 45), (60, 88)]  # 示意token区间
for s, e in assistant_spans:
    for i in range(s, e):
        labels[i] = ids[i]

DataCollator中的动态掩码与padding处理

当批次内样本长度不一致时,Collator会做右侧padding。如果padding位置的labels不是-100,框架会试图让模型预测pad token,引发无意义梯度甚至loss爆炸。因此Collator在拼接attention_mask的同时,必须把padding对应的labels强制置为-100,并保证input_ids里的pad符与labels里的-100一一对齐。

更稳健的实现是在Collator里重新生成labels,而非依赖Dataset预先写死。因为不同样本在同一batch中被pad到相同长度后,原绝对索引会错位。我们可以在Collator内调用一个辅助函数,依据每句的对话角色记录(随sample传入)实时计算掩码,这样即使max_length变动也不会出错。下面给出一个简化版Collator框架。

该方式将掩码逻辑与数据读取解耦,便于在混合单轮与多轮数据时统一处理。配合DeepSpeed或FSDP做三十多B模型微调时,能显著降低因格式错误导致的重启成本。

class MultiTurnCollator:
    def __init__(self, tokenizer, max_len=4096):
        self.tok = tokenizer
        self.max_len = max_len

    def __call__(self, batch):
        input_ids, labels = [], []
        for item in batch:
            ids = item["input_ids"]
            lab = item["labels"]
            pad_len = self.max_len - len(ids)
            ids = ids + [self.tok.pad_token_id] * pad_len
            lab = lab + [-100] * pad_len
            input_ids.append(ids)
            labels.append(lab)
        return {
            "input_ids": torch.tensor(input_ids),
            "labels": torch.tensor(labels),
            "attention_mask": (torch.tensor(input_ids) != self.tok.pad_token_id).long()
        }

常见格式错误与排查清单

实际训练Qwen1.5-32B-Chat时,大约四成报错来自数据侧。其一是把多轮写成纯文本不加角色标签,模型无法区分该学什么;其二是labels长度与input_ids不一致,框架报shape mismatch;其三是误将assistant段起始的<|im_start|>assistant也算进目标,导致模型学会输出角色头。排查时建议先抽一条样本,打印解码后的input_ids与labels对照,确认只有助手正文位置不是-100。

另一个隐蔽问题是特殊token未加入分词器词汇表。若使用旧版transformers加载新模板,可能认不出<|im_end|>,此时需升级库或手动add_tokens。完成上述数据构建与掩码设置后,可用小步长跑几十步观察loss是否稳定在合理下降区间,再放开全量训练。

总结来看,多轮对话微调的关键不在模型多大,而在样本是否让模型看清该模仿哪一部分。只要格式对齐原厂模板、掩码精准落到助手回复、padding妥善处理,Qwen1.5-32B-Chat就能在有限算力下学会指定多轮行为。

Qwen1.5-32B-Chat多轮对话微调损失掩码修改时间:2026-08-16 14:40:43

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。