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还是会出现思考甚至莫名奇妙的答案,相比而言微调后的输出就正常许多。

相关推荐
三只熊猫6 分钟前
一文打通 AI 知识脉络:大语言模型等关键内容详解
人工智能·python
Mintopia30 分钟前
AI UI 数据展示:Chart.js / Recharts + AI 总结文本的艺术
前端·javascript·aigc
居7然2 小时前
解锁AI大模型:Prompt工程全面解析
人工智能·prompt·提示词
思通数据6 小时前
AI视频监控:重构安防行业智能化新生态
人工智能·安全·目标检测·机器学习·计算机视觉·重构·数据挖掘
萤丰信息6 小时前
智慧工地从工具叠加到全要素重构的核心引擎
java·大数据·人工智能·重构·智慧城市·智慧工地
riveting6 小时前
明远智睿SSD2351:以技术突破重构嵌入式市场格局
大数据·人工智能·重构·边缘计算·嵌入式开发·智能交通
计算机sci论文精选7 小时前
CVPR2025敲门砖丨机器人结合多模态+时空Transformer直冲高分,让你的论文不再灌水
人工智能·科技·深度学习·机器人·transformer·cvpr
XIAO·宝8 小时前
机器学习----绪论
人工智能·机器学习
41号学员8 小时前
机器学习绪论
人工智能·机器学习
华清远见成都中心8 小时前
基于深度学习的异常检测算法在时间序列数据中的应用
人工智能·深度学习·算法