unsloth微调Qwen3实现知识总结

最近在rk上玩了一下部署Qwen3-1.7B 实现知识点内容总结,效果勉强还看得过去。打算微调一下0.6B的模型,降低部署成本的同时得到更好的总结效果。

1. 数据准备

之前有一批ASR提取的视频语音文本数据,我把提取出的多个文本内容合并到一个json文件中大致结构如下图

剩下的就很简单了,找一个好一点的模型让它总结知识点作为输出。这里构建数据集采用的是Alpaca格式,然后用Go写了一个调用百炼api的代码,最后得到最终训练数据集。

go 复制代码
const (
    inFile       = "./all_texts.json"
    outFile      = "./ft_data.json"
    apiURL       = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions"
    token        = "百炼的api-key"
    model        = "qwen3-235b-a22b"
    systemPrompt = "你是一个内容知识点总结专家,你需要从大段文字中提炼出核心知识点内容并返回,请注意返回的内容不得超过20个字"
    apiKey       = "Bearer " + token
)
​
​
func post(text string) {
    userMessage := Message{Role: "user", Content: text}
    InstructionMessage := Message{Role: "system", Content: systemPrompt}
    query := Query{
        Model:          model,
        Messages:       []Message{InstructionMessage, userMessage},
        EnableThinking: false,
    }
    queryJSON, _ := json.Marshal(query)
    option := &grequests.RequestOptions{
        JSON:    queryJSON,
        Headers: Headers,
    }
​
    resp, err := grequests.Post(context.Background(), apiURL, grequests.FromRequestOptions(option))
    if resp.StatusCode == http.StatusOK {
        fmt.Println(resp)
        if err != nil {
            fmt.Println(err)
        }
​
        var result Response
        if err := json.Unmarshal(resp.Bytes(), &result); err != nil {
            fmt.Println(err)
        }
        output := OutItem{
            Instruction: systemPrompt,
            Input:       text,
            Output:      result.Choices[0].Message.Content,
        }
        outChannel <- output
    }
}
​
func main() {
    defer goPool.Release()
    var wg sync.WaitGroup
    for _, inText := range inTexts {
        wg.Add(1)
        _ = goPool.Submit(func() {
            defer wg.Done()
            post(inText.Text)
        })
    }
​
    go func() {
        wg.Wait()
        close(outChannel)
    }()
​
    var results []OutItem
    for o := range outChannel {
        results = append(results, o)
    }
​
    outRaw, _ := json.MarshalIndent(results, "", "  ")
    _ = os.WriteFile(outFile, outRaw, 0o644)
}

这里antsgoroutine池大小不能太大,不然会触发限流。总的来说请求响应还是很快的,最后得到了训练数据集,大概长这个样子

2. 利用unsloth微调

unsloth其实有非常好的文档来实现简单的QLora微调,可以参考示例。唯一美中不足的就是unsloth开源版本并不支持单机多卡训练,不过还好有些开源补丁或者开源分支实现了单机多卡训练,这里我就拿unsloth-multigpu举例(下文称为multi)。由于multi利用补丁实现,所以几乎不影响原本的unsloth微调代码,只需要引入multi的hook就可以实现多卡训练。但是就今天的使用来说,由于作者一直在更新,所以感觉api并不稳定,给出示例中的许多api和最新版本的貌似不匹配。不过家里也只有一张显卡,所以先用unsloth微调试试,等回公司再试试加上multi训练。微调的大致代码如下

ini 复制代码
model, tokenizer = FastLanguageModel.from_pretrained(
    "../Qwen3-0.6B",
    max_seq_length = 2048,
    dtype = torch.bfloat16,
    load_in_4bit = True
)
​
model = FastLanguageModel.get_peft_model(
    model,
    r=16,  # LoRA rank
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    lora_alpha=16,
    lora_dropout=0,
    bias="none",
    use_gradient_checkpointing="unsloth",
    random_state=2025,
    use_rslora=False,
    loftq_config=None,
)
​
training_args = TrainingArguments(
    output_dir="./runs",
    dataloader_num_workers=4,
    num_train_epochs=30,
    per_device_train_batch_size=8,
    gradient_accumulation_steps=1,
    learning_rate=1e-4,
    bf16=True,
    logging_steps=10,
    save_strategy="epoch",
    warmup_ratio=0.1,
    optim = "adamw_8bit",
    lr_scheduler_type = "linear",
    weight_decay=0.01,  # Weight decay
    max_grad_norm=1.0,  # Gradient clipping
    report_to = "tensorboard"
)
​
trainer = SFTTrainer(
    model=model,
    train_dataset=train_dataset,
    args=training_args,
)
​
logger.info("Starting training...")
trainer_stats = trainer.train()
logger.info("Training complete!")
logger.info(f"Training statistics: {trainer_stats}")
model.save_pretrained_merged("finetune_0.6B", tokenizer, save_method = "merged_16bit")

其中lora相关、epochs等训练参数都没怎么特殊设置,微调过程截图

3. 推理结果对比

输入相同的内容送入原始0.6B模型和0.6B微调后的模型分别进行推理,对比二者输出

swift 复制代码
[
    {
        "origin_outputs": "\n\n核心知识点提炼:电极反应式书写步骤(正负极判断、离子移动口诀、调频电荷、配平、总反应等)\n</think>\n\n电极反应式书写方法",
        "ft_outputs": "assistant\n<think>\n\n</think>\n\n电极反应式书写方法:正负极判断、电极反应架构书写、电荷移动及总反应式书写。"
    },
    {
        "origin_outputs": "\n\n好的,我需要将用户提供的关于二战的详细内容提炼出核心知识点,不超过20个字。首先,用户的内容涵盖了二战的起因、过程、关键事件和相关理论。核心点包括:德国在1939年入侵波兰,英法宣战,二战爆发,以及相关的历史事件和理论。\n\n需要确保提炼的内容准确且简洁,同时涵盖主要知识点。检查是否有重复或冗余的信息。例如,用户提到了丘吉尔的批评和慕尼黑协议,以及地理学理论的影响。但核心知识点应聚焦于时间线和关键事件",
        "ft_outputs": "\n\n</think>\n\n第二次世界大战爆发及转折点"
    },
    {
        "origin_outputs": "\n\n好的,用户发来的是关于酸碱中和滴定图像题的课程内容介绍。需要从大段文字中提炼核心知识点,不超过20个字。\n\n首先,用户提到"延续上一节课",说明这节课是继续之前的课程内容。接着提到"图像题",所以核心知识点应该是关于酸碱中和滴定图像题的内容。需要简明扼要地总结,确保不超过字数限制。\n\n可能的提炼是:"酸碱中和滴定图像题教学"。检查字数,刚好20个字,符合要求。确认没有冗余信息,",
        "ft_outputs": "assistant\n<think>\n\n</think>\n\n酸碱中和滴定图像题知识点总结。"
    },
    {
        "origin_outputs": "\n\n答案:\n20字\n</think>\n\n20字",
        "ft_outputs": "\n\n</think>\n\n借鉴名句修辞手法,增强文采与说服力。"
    }
]

我在apply_chat_template中已经设置了enable_thinking=False,但原始的0.6B还是会出现思考甚至莫名奇妙的答案,相比而言微调后的输出就正常许多。

相关推荐
橡晟24 分钟前
深度学习入门:让神经网络变得“深不可测“⚡(二)
人工智能·python·深度学习·机器学习·计算机视觉
墨尘游子24 分钟前
神经网络的层与块
人工智能·python·深度学习·机器学习
Leah010526 分钟前
什么是神经网络,常用的神经网络,如何训练一个神经网络
人工智能·深度学习·神经网络·ai
PyAIExplorer1 小时前
图像亮度调整的简单实现
人工智能·计算机视觉
Striker_Eureka2 小时前
DiffDet4SAR——首次将扩散模型用于SAR图像目标检测,来自2024 GRSL(ESI高被引1%论文)
人工智能·目标检测
不知道叫什么呀2 小时前
【C】vector和array的区别
java·c语言·开发语言·aigc
Rvelamen3 小时前
LLM-SECURITY-PROMPTS大模型提示词攻击测评基准
人工智能·python·安全
墨风如雪3 小时前
火速围观!Trae IDE 迎来两大明星模型,Kimi K2 硬核登场,Grok-4 (Beta) 闪耀国际!
aigc
AI technophile3 小时前
OpenCV计算机视觉实战(15)——霍夫变换详解
人工智能·opencv·计算机视觉
JNU freshman4 小时前
计算机视觉 之 数字图像处理基础(一)
人工智能·计算机视觉