接下来,我将从Prompt设计,数据合成/增强,模型训练,量化,推理的顺序逐步介绍本次比赛的解决方案。
本次比赛的核心是让大模型学会做逻辑推理选择题,基于题干「Problem」和问题「Question」,从选项「Options」中选择出答案。因此 ,首先是设计一个合理的Prompt,提取「Problem」,「Question」和「Options」让大模型进行回答。Prompt样例如下:
f"""你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。请逐步分析问题并在最后一行输出答案,最后一行的格式为"答案是:A"。
### 题目:
{problem}
### 问题:
{question}
{options}
"""由于每条训练数据有一个「Problem」,但是可能对应多个「Question」和「Options」,因此最终将其拆解成独立的训练数据可以大概得到1400+条数据样本,每条样本的输入就是上述的Prompt文字,输出则为答案对应的选项。
本次提出的解决方案的核心在于数据合成与增强,用于提升大模型的逻辑推理能力。采用数据合成的动机源于当前提供的训练集存在以下几个问题:
鉴于本次比赛的逻辑推理题目特点,需要引导模型学习如何组织语言进行推理,因此引入了思维链(Chain-of-Thought, CoT)技术。与直接提供答案的传统训练数据不同,CoT的核心在于通过逐步生成逻辑推理过程(step-by-step)来输出答案。然而,高质量的CoT回复依赖更强大的模型生成,同时直接对原始数据集进行合成存在以下挑战:
针对上述问题,本方案提出了多阶段的数据合成策略,通过Easy-to-Hard的方式进行高效的数据合成:
通过这种多阶段数据合成策略,不仅显著降低了数据合成的成本,还有效提升了训练数据的覆盖面和质量,从而赋予大模型更强的逻辑推理能力和泛化性能。
该阶段采用的Prompt样例如下:
f"""你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。请认真思考,逐步分析问题并在最后一行输出答案,最后一行输出的格式为"答案是:<>"。
### 题目:
{problem}
### 问题:
{question}
{options}
***Instructions***
请根据上述题目和问题,逐步思考推理过程,并最终输出答案。
1. 问题形式为单项选择题,选项中存在且仅存在一个正确答案。
2. 所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。
3. 逐步输出思考过程和解答推理过程,过程尽可能简要精炼。
4. 输出为:答案是:<>。
"""同时,为了赋予大模型更好地测试集泛化能力,本方案进一步设计了数据增强方案,具体的思路是结合训练集的特点,基于同样的「Problem」,引导大模型生成不同的「Question」和「Options」,并生成CoT回复。为了规范大模型的输出格式,在数据增强的过程中,从训练集采样类似的样本提供大模型作为few-shot样本。最终,数据增强的Prompt设计样例如下:
f"""你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目:
### 题目
{problem}
### 问题
{question}
{options}
***Instructions***
请根据上述题目,以同样的格式生成类似的题目和相对应的选项,注意:
1. 基于同样的题目,生成不同的问题和选项,选项中必须有且只有一个正确答案。
2. 每次只生成一个问题,形式为单项选择题,确保选项中存在且仅存在一个正确答案。
3. 所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。
4. 请认真思考,逐步分析问题并在最后一行输出答案。
5. 输出格式如下:
### 问题
生成的问题
生成的选项
答案
***Example***
### 问题
{question}
{options}
{answer}
"""在该数据合成/增强阶段,本方案使用了GPT-4o-mini,GPT-4o,DeepSeek,Qwen等模型的API,对于不同的API调用采用不一样的prompt,从而生成更加多样性的回复标签。
针对合成好的数据集,采用ms-swift框架进行微调。SWIFT(Scalable lightWeight Infrastructure for Fine-Tuning)是魔搭ModelScope开源社区推出的一套完整的轻量级训练推理工具,基于PyTorch的轻量级、开箱即用的模型微调、推理框架,让AI爱好者用自己的消费级显卡就能玩转大模型和AIGC。
本方案采用LoRA进行参数高效微调,微调命令如下:
swift sft \
--model_type qwen2_5-32b-instruct \
--model_id_or_path /root/autodl-tmp/Qwen2.5-32B-Instruct \
--model_revision master \
--sft_type lora \
--template_type AUTO \
--dtype bf16 \
--output_dir /root/autodl-tmp/qwen \
--dataset $TRAIN_DATA \
--num_train_epochs 3 \
--max_length 4096 \
--check_dataset_strategy warning \
--lora_target_modules ALL \
--gradient_checkpointing true \
--lora_rank 256 \
--lora_alpha 512 \
--batch_size 1 \
--learning_rate 1e-4 \
--gradient_accumulation_steps 8 \
--max_grad_norm 1.0 \
--warmup_ratio 0.03 \
--save_steps 2000 \
--save_total_limit 3 \
--use_flash_attn true \
--save_only_model true \
--logging_steps 100注:本方案采用的ms-swift版本为2.5.2,当前ms-swift最新版本3.x版本的训练/推理命令有较大改动。
其中,TRAIN_DATA为给定的合成数据集。基于同样的训练命令,指定不同的数据集进行训练得到不同的模型,这里只需要修改-dataset参数指定不同的数据集TRAIN_DATA即可。
由于比赛限定的显存是32G,而Qwen2.5-32B模型推理需要的显存将近80G,因此需要对训练好的模型进行量化。在量化之前需要先进行LoRA参数合并:
swift export \
--model_type qwen2_5-32b-instruct \
--model_id_or_path /root/autodl-tmp/Qwen2.5-32B-Instruct \
--ckpt_dir $OUTPUT_DIR \
--dtype fp16 \
--merge_device_map auto \
--merge_lora true这里的$OUTPUT_DIR需要指定训练好的LoRA权重路径。模型合并成一个完整的新模型后,下一步就是进行量化。本方案采用的量化方法是gptq进行4bit量化,在比赛过程中该量化方案损失最小,量化运行的代码如下:
swift export \
--model_type qwen2_5-32b-instruct \
--quant_bits 4 \
--quant_method gptq \
--model_id_or_path $OUTPUT_DIR-merged \
--dataset $TRAIN_DATA alpaca-zh alpaca-en sharegpt-gpt4:default这里的OUTPUT_DIR-merged就是上述合并后的模型路径。gptq量化还需要选择校准的数据集,这里本方案使用的是训练集TRAIN_DATA和公开的数据集alpaca-zh alpaca-en sharegpt-gpt4:default进行量化,量化的精度为4bit。
量化完成后,模型的运行显存大大降低,大约为20GB,足够在32GB的环境下进行推理。
由于模型在训练过程中采用的CoT方式训练,在推理过程也会具备思维链式推理的能力,虽然极大地增强了模型的推理能力,但也牺牲了推理的速度(输出的token数目变多了导致速度变慢)。具体来讲,从原来输出的个位数token以内变成上百上千的token,大大降低了模型的推理速度。根据实验,即使是经过量化后的Qwen 2.5-32B模型在测试集上推理也需要将近3-4h的时间。为了加快推理速度,本方案采用了vLLM进行加速推理。vLLM(Vectorized Large Language Model Serving System)是一个大型语言模型推理加速工具,它通过优化内存管理、连续批处理、CUDA核心优化和分布式推理支持等技术手段,显著提高了大型语言模型的推理速度和效率。本方案采用的vLLM推理代码如下:
from swift.llm import (
ModelType, get_vllm_engine, get_default_template_type,
get_template, inference_vllm, inference_stream_vllm
)
def chat_swift(texts, model_path,
model_type='qwen2_5_32b_instruct',
max_tokens=2048,
max_model_len=2048,
temperature=0.1,
top_p=0.7,
top_k=20,
use_tqdm=True,
**kwargs):
model_type = getattr(ModelType, model_type)
llm_engine = get_vllm_engine(model_type, model_id_or_path=model_path, **kwargs)
template_type = get_default_template_type(model_type)
template = get_template(template_type, llm_engine.hf_tokenizer)
llm_engine.generation_config.max_new_tokens = max_tokens
llm_engine.generation_config.temperature = temperature
llm_engine.generation_config.top_p = top_p
llm_engine.generation_config.top_k = top_k
generation_info = {}
request_list = [{'query': x} for x in texts]
outputs = inference_vllm(llm_engine, template, request_list, generation_info=generation_info, use_tqdm=use_tqdm)
print(generation_info)
outputs = [out['response'] for out in outputs]
return outputs
# vllm调用
# prompts为构造好的测试集问题prompt
# model_path 为量化后的模型路径
outputs = chat_swift(prompts, model_path, quantization='gptq')最终,经过vLLM优化,模型的推理时间降到了30-40min,对于4h的推理时间还有很大的空余,因此可以考虑进行多个模型集成。
本方案训练得到了四个模型,单模的效果分别为:0.8769,0.8894,0.8823, 0.8752,其中最优的0.8894是基于GPT-4o的API合成的数据,训练得到的模型效果远远优于其他数据、由于模型之间性能差别较大,简单地进行投票融合容易「拖累」最优模型的效果,因此本方案采用了新的一致性集成方式:当三个较差的模型答案一致时,则采纳其答案,否则采纳最优模型的答案。具体的代码如下所示:
def ensemble(answers):
# answers 为四个模型的预测答案,第0位为最优模型的答案
a = answers[0]
b = np.unique(answers[1:])
if len(b) == 1:
return b[0]
return a训练好的模型链接:https://www.modelscope.cn/models/EdisonLeeeee/sais2024_qwen2.5
其中,四个模型对应的终榜准确率为:
本方案介绍了第二届世界科学智能大赛的冠军方案,方案基于通义千问2.5模型进行微调、量化和推理,通过大模型进行数据合成与增强提高泛化能力与逻辑推理能力,最终在终榜上取得了第一名的成绩。