微调Bart模型添加说话人-话语信息引发填充错误及方案咨询
问题解答
一、当前添加显式信息方式的问题
你的思路本身是可行的,但问题出在数据整理环节未处理自定义字段的填充逻辑:
- 你在
preprocess_function中保证了单条数据的input_ids、attention_mask、spk_utt_pos长度一致,但DataCollatorForSeq2Seq默认只对框架内置的seq2seq字段(如input_ids、attention_mask、labels)执行padding操作,不会自动处理你自定义的spk_utt_pos字段。 - 这就导致batch内的
spk_utt_pos仍保持原样本的长度,无法统一转成张量,从而触发错误。
修复方法
自定义数据整理逻辑,让spk_utt_pos和其他字段一起被padding:
from transformers import DataCollatorForSeq2Seq import torch class CustomDataCollator(DataCollatorForSeq2Seq): def __call__(self, features): # 先处理默认字段的padding batch = super().__call__(features) # 对自定义的spk_utt_pos做padding,填充值用0(和你的编码规则匹配) spk_utt_pos = [f["spk_utt_pos"] for f in features] max_len = max(len(p) for p in spk_utt_pos) padded_spk_pos = [p + [0]*(max_len - len(p)) for p in spk_utt_pos] batch["spk_utt_pos"] = torch.tensor(padded_spk_pos, dtype=torch.long) return batch
训练时替换原collator为这个自定义类即可。
二、传递说话人-话语信息的其他方法
除了你当前的位置标记法,还有以下几种常用方案:
1. 文本级特殊标记法
直接在每个说话轮次前添加专属特殊标记,比如:
<SPK_AMANDA> Amanda: I baked cookies. Do you want some? <SPK_JERRY> Jerry: Sure! <SPK_AMANDA> Amanda: I'll bring you tomorrow :-)
- 实现简单:无需修改模型结构,只需预处理时插入标记;若用预训练模型,可选用
<unused>系列token(避免词表扩容),或添加新token后重新训练embedding层。 - 模型能通过特殊token快速区分不同说话人的话语边界。
2. 说话人嵌入(Speaker Embedding)
给每个说话人分配一个可训练的嵌入向量,在输入阶段将其与对应话语的token嵌入拼接/相加:
- 需修改模型的embedding层:比如在Bart的
BartEmbeddings类中新增speaker_embeddings模块,forward时根据说话人ID取出对应嵌入,和词嵌入、位置嵌入合并后作为输入。 - 这种方法能让模型更直接地学习说话人特征,适合说话人数量固定的场景。
3. 轮次分段编码
将对话拆分为独立的说话轮次,用模型的分隔符(如<sep>)连接,同时保留说话人信息:
- 示例输入:
Amanda: I baked cookies... <sep> Jerry: Sure! <sep> Amanda: I'll bring you tomorrow... - 若模型支持分段输入(如Bart的文档摘要场景),可让模型学习轮次间的依赖关系,同时显式区分不同说话人的内容。
4. 多任务辅助训练
在摘要任务之外,添加辅助任务强制模型学习说话人信息:
- 比如添加说话人分类任务:让模型预测每个token属于哪个说话人;或话语边界检测任务:预测token是否为说话人切换的位置。
- 你可以在重写
compute_loss时,将摘要损失和辅助任务损失按比例加权求和,让模型同时优化两个目标。
5. 注意力掩码增强
修改模型的注意力掩码逻辑,让模型对不同说话人的内容分配不同的注意力权重:
- 比如,让同一说话人的话语token之间注意力权重更高,或在生成摘要时,强制模型关注目标说话人的内容(若摘要需要侧重特定说话人)。
- 这种方法需要修改模型的注意力计算模块,适合对模型控制粒度要求较高的场景。
内容的提问来源于stack exchange,提问作者starc52
相关产品推荐
相关产品推荐

