644. WSDM Cup - Multilingual Chatbot Arena | wsdm-cup-multilingual-chatbot-arena
祝贺所有获奖者!感谢组织者举办如此有趣的比赛。LMSYS 是我第一次获得银牌的 LLM 竞赛。这次我非常高兴能在 WSDM Cup 获得金牌。在这里让我分享我的解决方案。
使用 fasttext 推断 prompt 的语言。
我重写了 SFTTrainer 的 compute_loss 函数,仅计算 Token "A" 和 "B" 的 loss。
class SFTChoiceTrainer(SFTTrainer):
def compute_loss(self, model, inputs, return_outputs=False, num_items_in_batch=None, return_choice_logit=False):
labels = inputs['labels']
labels[labels > 57] = -100
inputs['labels'] = labels
_, outputs = super().compute_loss(model, inputs, return_outputs=True, num_items_in_batch=num_items_in_batch)
logits = outputs.logits
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
logits_target = []
labels_target = []
for i in range(len(shift_labels)):
lbl = shift_labels[i].cpu().numpy()
target_idx = np.where(lbl != -100)[0][0]
# token id for "A" and "B" is 32 and 33
logits_target.append(shift_logits[i][target_idx][32:34])
labels_target.append(shift_labels[i][target_idx]-32)
# (batch_size, 2)
logits_target = torch.stack(logits_target, dim=0)
# (batch_size)
labels_target = torch.tensor(labels_target).to(outputs.logits.device)
loss = F.cross_entropy(logits_target, labels_target)
if return_choice_logit:
return (loss, outputs, logits_target) if return_outputs else (loss, logits_target)
else:
return (loss, outputs) if return_outputs else loss
prompt、response a 和 response b 的最大长度限制为 2048。因此输入的最大长度约为 6144。
class PromptManager(object):
preference_prompt_predict_template = '''<| ( performance|>system
You are a highly skilled AI assistant. You will be provided a request from user and several responses from different assistants. Your job is to judge which response is the best. Only answer the letter of the best response.<| ( eyes|>
<| ( performance|>user
<Request>
{prompt}
</Request>
<Language>
{language}
</Language>
{responses}<| ( eyes|>
<| ( performance|>assistant
'''
preference_prompt_train_template = preference_prompt_predict_template + '{answer}<| ( eyes|>\n'
response_template = '''<Response_{choice}>
{response}
</Response_{choice}>\n'''
sep = '<| ( performance|>assistant\n'
我使用了以下数据进行后预训练。后预训练与训练共享相同的训练 pipeline。
我使用了以下数据进行训练。
对于教师模型 (Qwen2.5-72B 和 Athene-V2-Chat),我在新年假期期间使用 deepspeed 在多 GPU 上进行训练。为了避免调整学习率,我只需将 gradient_accumulation_steps 设置为 1,以保持与单 GPU 训练相同的 batch size。
基本上,14B 模型的学习率 10e-5 是好的。Loss spike 开始在 14e-5 左右出现。对于 72B 模型,较小的学习率更好。
| 编号 | 实验 | 折 (Fold) | 学习率 (lr) | 准确率 (Accuracy) |
|---|---|---|---|---|
| 1 | Qwen2.5 14B (后预训练,仅 WSDM 数据) | 0 | 7e-5 | 0.7055360462714315 |
| 2 | - | 0 | 8e-5 | 0.7007849617847552 |
| 3 | - | 0 | 9e-5 | 0.699958686221855 |
| 4 | - | 0 | 10e-5 | 0.7051229084899814 |
| 5 | - | 0 | 12e-5 | 0.7059491840528817 |
| 6 | - | 0 | 14e-5 | 0.5084693245197274 |
| 7 | Phi4 (仅 WSDM 数据) | 0 | 7e-5 | 0.6983061350960545 |
| 8 | - | 0 | 8e-5 | 0.6965502995248916 |
| 9 | - | 0 | 9e-5 | 0.6995455484404048 |
| 10 | - | 0 | 10e-5 | 0.6966535839702541 |
| 11 | - | 0 | 12e-5 | 0.6973765750877918 |
| 12 | - | 0 | 14e-5 | 0.7016112373476554 |
对我来说最好的 Temperature 是 5.0 (实验 2~5),最好的 soft loss 权重是 0.9 (实验 2~7)。
遵循 LMSYS 第一名的做法,我尝试平均 KLDivLoss 和 CosineEmbeddingLoss,但效果不明显。KLDivLoss 已经足够。
| 编号 | 实验 | 折 (Fold) | 准确率 | T | 蒸馏权重 | 学习率 |
|---|---|---|---|---|---|---|
| 1 | Qwen2.5 14B (基线无蒸馏,仅 WSDM 数据) | 0 | 0.7051229084899814 | - | - | 10e-5 |
| 2 | Qwen2.5 14B (蒸馏实验,仅 WSDM 数据) | 0 | 0.7061557529436067 | 1 | 0.5 | 10e-5 |
| 3 | - | 0 | 0.7082214418508572 | 10 | 0.5 | 10e-5 |
| 4 | - | 0 | 0.7076017351786821 | 3 | 0.5 | 10e-5 |
| 5 | - | 0 | 0.7102871307581078 | 5 | 0.5 | 10e-5 |
| 6 | - | 0 | 0.7121462507746333 | 5 | 0.7 | 10e-5 |
| 7 | - | 0 | 0.7134889485643462 | 5 | 0.9 | 10e-5 |
| 8 | Qwen2.5 14B (更多教师模型蒸馏,仅 WSDM 数据) - 权重参数集 1 | 0 | 0.7168973352613096 | 5 | 0.9 | 10e-5 |
| 9 | Qwen2.5 14B (更多教师模型蒸馏,仅 WSDM 数据) - 权重参数集 2 | 0 | 0.7164841974798596 | 5 | 0.9 | 10e-5 |
我使用 auto-round 进行量化。使用较重的量化设置可以抑制准确率下降。量化实验基于 Qwen2.5 3B 模型。
我最终的量化设置是 seqlen=6144, nsamples=512, iters=3000。
| 编号 | 实验 | 序列长度 | 样本数 | 迭代次数 | 折 (Fold) | 准确率 |
|---|---|---|---|---|---|---|
| 1 | 基线 - 无量化 | 0 | 0.681057 | |||
| 2 | 1 + 轻量设置 | 2048 | 256 | 500 | 0 | 0.674653 |
| 3 | 1 + 官方最佳设置 | 2048 | 512 | 1000 | 0 | 0.677752 |
| 4 | 3 + 增加迭代 | 2048 | 512 | 2000 | 0 | 0.678062 |
| 5 | 3 + 增加迭代 | 2048 | 512 | 3000 | 0 | 0.677649 |
| 6 | 4 + 增加迭代 | 2048 | 512 | 4000 | 0 | 0.673311 / 0.678785 |
| 7 | 3 + 增加序列长度 | 4096 | 512 | 1000 | 0 | 0.676822 |
| 8 | 7 + 增加迭代 | 4096 | 512 | 2000 | 0 | 0.678888 |
| 9 | 8 + 增加迭代 | 4096 | 512 | 3000 | 0 | 0.673001 |
| 10 | 9 + 增加迭代 | 4096 | 512 | 4000 | 0 | 0.678888 |
| 11 | 10 + 增加迭代 | 4096 | 512 | 5000 | 0 | 0.680024 |
| 12 | 11 + 增加序列长度 | 4096 | 512 | 6000 | 0 | 0.673104 |
| 13 | 7 + 增加序列长度 | 6144 | 512 | 2000 | 0 | 0.678578 |
| 14 | 13 + 增加迭代 | 6144 | 512 | 3000 | 0 | 0.680851 / 0.676513 / 0.681160 / 0.678578 |
| 15 | 14 + 增加迭代 | 6144 | 512 | 4000 | 0 | 0.678682 / 0.674757 |
我使用 vllm 进行推理。推理部分没有什么特别的,全是工程优化。我使用了以下技巧。
基本上,我选择排行榜分数最高的提交。由于排行榜分数不稳定,我使用不同的种子训练了相同的模型并线性合并了模型权重。
我发现 Phi4 对短 prompt 表现良好,但在排行榜上对长 prompt 表现不佳。所以我根据 token 长度改变了 Phi4 的集成权重。
# CoT
system_prompt_template = 'You are an excellant AI assistant. You will be given a prompt from the user. Your task is to infer the task user request you to do.'
user_prompt_template = '''### Prompt Start ###
{text}
### Prompt End ###
Please answer the task user request you to do with one sentence in English and then explain the key factors to complete the task step by step briefly. No need to provide the answer for the prompt.
'''
本文档基于 Kaggle 竞赛解决方案翻译整理。