我用 Unsloth 微调了一个DeepSeek专家

picture.image

猴哥的第 160 期分享,欢迎追看

前两天,和大家分享了阿里最新开源的推理模型:

比肩满血DS,阿里新王 QwQ-32B 本地部署,Ollma/vLLM 实测对比

有朋友反馈:模型还是太大了。。。

其实,很多情况下,我们只需解决一些特定场景的问题,完全没必要搞这么大的模型。

指令微调了解下?

最近,DeepSeek算命大师很火,今日分享,以算命为例,用 Unsloth 来微调一个垂直领域的推理模型。

  1. Unsloth 简介

项目地址:https://github.com/unslothai/unsloth

关于大模型指令微调,笔者之前有过分享:

【大模型指令微调实战】小说创作,一键直达天池挑战赛Top50

当时微调用的 peft 框架。

不得不感叹,这个领域技术更新太快,最新出的 Unsloth 极大加速了模型微调的速度,同时降低显存占用。

开源四个月,GitHub 已斩获 34k star。

picture.image

老规矩,简短介绍下 Unsloth 亮点:

  • 所有内核均基于 OpenAI 的 Triton 重写,大幅提升模型训练速度,降低显存占用。
  • 实现中不存在近似计算,模型训练的精度损失为零。
  • 支持绝大多数主流的 NVIDIA GPU 设备,CUDA 计算能力 7.0+。
  • 支持 4bit 和 16bit QLoRA / LoRA 微调(基于 bitsandbytes)

来看看惊人的加速比和显存节省:

picture.image

  1. Unsloth 微调教程

2.1 Unsloth 安装

pip 一键安装:

  
pip install unsloth  

2.2 模型准备

这里查看所有支持的模型列表:https://docs.unsloth.ai/get-started/all-our-models

最新的 QwQ-32B 也已支持,包括量化版和原始模型:

picture.image

为兼顾到绝大部分同学,本次微调选用 DeepSeek 蒸馏版 Qwen2.5 1.5B:

picture.image

首先,从 huggingface 下载模型:(国内伙伴可引入镜像)

  
export HF\_ENDPOINT=https://hf-mirror.com # 引入镜像地址  
huggingface-cli download --resume-download unsloth/DeepSeek-R1-Distill-Qwen-1.5B --local-dir ./ckpts/qwen-1.5b  

2.3 数据集准备

数据是燃料,是模型微调成功的关键。

就像是给孩子补课的教材,这些数据往往需要审核(标注),以便模型有样学样。

比如,如果要让模型学会算命,就得准备一些标注好的命理学知识。

开源社区已有这样的数据集:https://huggingface.co/datasets/Conard/fortune-telling

不妨先下载来试试:

  
export HF\_ENDPOINT=https://hf-mirror.com # 引入镜像地址  
huggingface-cli download --repo-type dataset --resume-download Conard/fortune-telling --local-dir data/fortune-telling  

注:采用 huggingface-cli 下载数据集时,加上 --repo-type dataset

数据集格式如下:

picture.image

OK,一切准备就绪,下面开始炼丹

2.4 模型微调

模型微调,只需按照以下 8 步走~

step 1:引入依赖

  
from unsloth import FastLanguageModel, is\_bfloat16\_supported  
from transformers import TrainingArguments  
from trl import SFTTrainer  
from datasets import load\_dataset  

step 2:加载模型

  
max\_seq\_length = 8192 # 模型处理文本的最大长度  
# 加载模型  
model, tokenizer = FastLanguageModel.from\_pretrained(  
    model\_name = "ckpts/qwen-1.5b",  
    max\_seq\_length = max\_seq\_length,  
    dtype=None, # 自动检测合适的类型  
    load\_in\_4bit = True,   
    # device\_map="balanced" # 多卡训练时均衡分布模型权重,默认为sequential  
)  

step 3:加载数据集

  
# 定义训练数据格式化字符串模板  
train\_prompt\_style="""请遵循指令回答用户问题。  
在回答之前,请仔细思考问题,并创建一个逻辑连贯的思考过程,以确保回答准确无误。  
### 指令:  
你是一位精通八字算命、紫微斗数、风水、易经卦象、塔罗牌占卜、星象、面相手相和运势预测等方面的算命大师。  
请回答以下算命问题。  
### 问题:  
{}  
### 回答:  
<think>{}</think>  
{}  
"""  
# 加载数据集  
dataset = load\_dataset("data/fortune-telling", split="train")  
def formatting\_data(examples):  
    questions = examples["Question"]  
    cots = examples["Complex\_CoT"]  
    responses = examples["Response"]  
    texts = []  
    for q, c, r in zip(questions, cots, responses):  
        text = train\_prompt\_style.format(q, c, r) + tokenizer.eos\_token  
        texts.append(text)  
    return {"text": texts}  
dataset = dataset.map(formatting\_data, batched=True)  

可以打印一行数据,输出看看:

  
Generating train split: 207 examples [00:00, 1319.25 examples/s]  
Dataset size: 200 ['Question', 'Response', 'Complex\_CoT']  

step 5:定义 LoRA

  
# 添加 LoRA 权重  
model = FastLanguageModel.get\_peft\_model(  
    model,  
    r = 16, # Rank of the LoRA matrix  
    target\_modules = ["q\_proj", "k\_proj", "v\_proj", "o\_proj", "gate\_proj", "up\_proj", "down\_proj",], # Layers to apply LoRA to  
    lora\_alpha = 16, # LoRA alpha value  
    lora\_dropout = 0, # Supports any, but = 0 is optimized,防止过拟合,0 表示不drop任何参数  
    bias = "none",    # Supports any, but = "none" is optimized  
    use\_gradient\_checkpointing = "unsloth", # True or "unsloth" for very long context  
    random\_state = 3407,  
    use\_rslora = False,  # We support rank stabilized LoRA  
    loftq\_config = None, # And LoftQ  
)  

step 6:定义 trainer

  
trainer = SFTTrainer(  
    model = model,  
    tokenizer = tokenizer,  
    train\_dataset = dataset,  
    dataset\_text\_field = "text",  
    max\_seq\_length = max\_seq\_length,  
    dataset\_num\_proc = 2,  
    packing = False, # Can make training 5x faster for short sequences.  
    args = TrainingArguments(  
        per\_device\_train\_batch\_size = 2, # 每个GPU上的batch size  
        gradient\_accumulation\_steps = 4, # 梯度累积步数  
        warmup\_steps = 10,  
        # max\_steps = 200, # 最大训练步数  
        num\_train\_epochs=3, # 训练轮数 和 max\_steps 二选一  
        learning\_rate = 2e-4, # 学习率,默认值是 2.0e-5  
        fp16 = not is\_bfloat16\_supported(),  
        bf16 = is\_bfloat16\_supported(),  
        logging\_steps = 2,  
        output\_dir = "outputs",  
        optim = "adamw\_8bit",  
        seed = 3407,  
    ),  
)  

step 7:开始训练

  
train\_stats = trainer.train()  

Unsloth 会根据加载的模型和设备情况自动选择 GPU 数量。

如果默认 device\_map="sequential",只有当单卡显存不够时,才占用其他卡。

picture.image

如果设定 device\_map="balanced",会占用所有卡,并均衡分布模型权重:

picture.image

看到损失下降,说明成功开始训练:

picture.image

step 8:模型保存

最后,别忘了保存训练好的模型权重:

  
model.save\_pretrained("ckpts/lora\_model")  
tokenizer.save\_pretrained("ckpts/lora\_model")  

注:这里只会保存 LoRA 权重,在adapter\_config.json会指定原始模型位置:

picture.image

至此,我们成功走完了模型微调之旅,需要完整代码的小伙伴,文末自取!

对于 qwen2.5 1.5B而言,4G 显存即可开启微调:

picture.image

2.5 模型测试

模型测试代码如下:

  
# 加载模型  
model, tokenizer = FastLanguageModel.from\_pretrained(  
    model\_name = "ckpts/lora\_model",  
    max\_seq\_length = max\_seq\_length,  
    load\_in\_4bit = True,  
)  
FastLanguageModel.for\_inference(model)  
question = '1995年七月初十生,今年是2025年,了解未来五年的运势'  
inputs = tokenizer([prompt\_style.format(question)], return\_tensors='pt', max\_length=max\_seq\_length).to("cuda")  
outputs = model.generate(inputs['input\_ids'], attention\_mask=inputs['attention\_mask'], max\_length=max\_seq\_length, use\_cache=True)  
answer = tokenizer.batch\_decode(outputs, skip\_special\_tokens=True)[0]  
print(answer)  

注:如果单卡放不下模型权重,会报错,因为模型权重切分了,但 inputs 并没有切分,因此可考虑采用 vLLM 推理

2.6 vLLM 推理

不了解 vLLM 推理 的小伙伴,可翻看上篇:比肩满血DS,阿里新王 QwQ-32B 本地部署,Ollma/vLLM 实测对比

首先,我们要将微调后的模型保存为 GGUF 格式:

  
model.save\_pretrained\_gguf("ckpts/merged", tokenizer, quantization\_method="q4\_k\_m")  

Unsloth 会自动下载编译 llama.cpp 进行格式转换:

picture.image

过程中先转成 BF16,然后再进行 4bit 量化,权重大小分别为 3G 和 1G:

picture.image

转换成功后,一键开启 vLLM 推理:

  
vllm serve ckpts/merged/unsloth.Q4\_K\_M.gguf --api-key 123 --port 3002  

写在最后

本文分享了开源大模型微调工具 Unsloth,并通过一个简单例子,带大家走完了微调全流程。

如果对你有帮助,欢迎点赞收藏 备用。

本文微调+测试完整代码已上传云盘,需要的朋友,公众号后台回复微调自取!


为方便大家交流,新建了一个 AI 交流群,公众号后台「联系我」,拉你进群。

👇 关注猴哥,快速入门AI工具

picture.image

# AI 工具:

本地部署大模型?看这篇就够了,Ollama 部署和实战

盘点9家免费且靠谱的AI大模型 API,统一封装,任性调用!

免费GPU算力本地跑DeepSeek R1,无惧官方服务繁忙!

# AI应用** :**

弃坑 Coze,我把 Dify 接入了个人微信,AI小助理太强了

我把「FLUX」接入了「小爱」,微信直接出图,告别一切绘画软件!

202K 下载!最强开源OCR:本地部署,邀你围观体验

阿里开源TTS CosyVoice 再升级!语音克隆玩出新花样,支持流式输出

借 WeChatFerry 东风,我把微信机器人复活了!

成本不到50的AI对话机器人,如何自建服务端?自定义角色+语音克隆

0
0
0
0
评论
未登录
暂无评论