2026年Unsloth 微调了一个Qwen2.5 1.5B算命大模型

Unsloth 微调了一个Qwen2.5 1.5B算命大模型前两天 和大家分享了阿里最新开源的推理模型 比肩满血 DS 阿里新王 QwQ 32B 本地部署 Ollma vLLM 实测对比 有朋友反馈 模型还是太大了 其实 很多情况下 我们只需解决一些特定场景的问题 完全没必要搞这么大的模型 指令微调 了解下 最近 DeepSeek 算命大师 很火 今日分享 以算命 为例 用 Unsloth 来微调一个垂直领域的推理模型 项目地址

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



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

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

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

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

指令微调了解下?

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

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

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

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

当时微调用的 peft 框架。

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

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

图片

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

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

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

图片

pip 一键安装:

pip install unsloth 

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

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

图片

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

图片

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

cd /home/luyanjie21/Desktop/microtrain-qwen3/ export HF_ENDPOINT=https://hf-mirror.com hf download unsloth/Qwen2.5-0.5B --local-dir ./ckpts/qwen-0.5b 

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

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

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

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

不妨先下载来试试:

export HF_ENDPOINT=https://hf-mirror.com hf download --repo-type dataset Conard/fortune-telling --local-dir data/fortune-telling 

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

数据集格式如下:

图片

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

模型微调,只需按照以下 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="""请遵循指令回答用户问题。在回答之前,请仔细思考问题,并创建一个逻辑连贯的思考过程,以确保回答准确无误。 指令:你是一位精通八字算命、紫微斗数、风水、易经卦象、塔罗牌占卜、星象、面相手相和运势预测等方面的算命大师。请回答以下算命问题。 问题:{} 回答: 
  
    
    
      {} 
    {}"""# 加载数据集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",只有当单卡显存不够时,才占用其他卡。

图片

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

图片

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

图片

step 8:模型保存

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

 model.save_pretrained( "ckpts/lora_model") tokenizer.save_pretrained( "ckpts/lora_model") 

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

图片

至此,我们成功走完了模型微调之旅,完整训练代码如下:

from unsloth import FastLanguageModel, is_bfloat16_supported from transformers import TrainingArguments from trl import SFTTrainer from datasets import load_dataset max_seq_length = 8192 # 模型处理文本的最大长度 # 加载模型 model, tokenizer = FastLanguageModel.from_pretrained( model_name = "ckpts/qwen-0.5b", max_seq_length = max_seq_length, dtype=None, # 自动检测合适的类型 load_in_4bit = True, # device_map="balanced" # 多卡训练时均衡分布模型权重,默认为sequential ) # 定义训练数据格式化字符串模板 train_prompt_style="""请遵循指令回答用户问题。 在回答之前,请仔细思考问题,并创建一个逻辑连贯的思考过程,以确保回答准确无误。 指令: 你是一位精通八字算命、紫微斗数、风水、易经卦象、塔罗牌占卜、星象、面相手相和运势预测等方面的算命大师。 请回答以下算命问题。 问题: {} 回答: 
  
    
    
      {} 
     {} """ # 加载数据集 dataset = load_dataset("data/fortune-telling", split="train") # train[0:200]可以指定前200条数据 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) # 添加 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 ) 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, ), ) train_stats = trainer.train() # 保存模型权重 model.save_pretrained("ckpts/lora_model") tokenizer.save_pretrained("ckpts/lora_model") 

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

图片

模型测试代码如下:

from unsloth import FastLanguageModel import torch import re max_seq_length = 8192 # 加载模型 model, tokenizer = FastLanguageModel.from_pretrained( model_name="ckpts/lora_model", max_seq_length=max_seq_length, dtype=None, load_in_4bit=True, ) FastLanguageModel.for_inference(model) # 设置pad token if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token # 更简洁的提示模板 prompt_style = """你是一个算命大师。请直接回答用户的问题,不要输出任何思考过程、推理步骤或 
  
    
    
      标签。 用户问题:{} 你的回答:""" # 五个问题 questions = [ '1995年七月初十出生的人,2026年的整体运势如何?', '1995年七月初十出生的人,2026年的财运如何?', '1995年七月初十出生的人,2026年的感情运势如何?', '1995年七月初十出生的人,2026年的事业运势如何?', '1995年七月初十出生的人,2026年的健康运势如何?' ] for i, question in enumerate(questions, 1): print(f" {'='*50}") print(f"问题 {i}: {question}") print(f"{'='*50}") # 构造输入 text = prompt_style.format(question) inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512).to("cuda") # 生成参数 with torch.no_grad(): outputs = model.generate( inputs, max_new_tokens=300, temperature=0.7, do_sample=True, top_p=0.9, repetition_penalty=1.1, pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id, ) # 解码 full_response = tokenizer.decode(outputs[0], skip_special_tokens=True) # 提取回答部分(去掉问题) if "你的回答:" in full_response: answer = full_response.split("你的回答:")[-1].strip() else: answer = full_response[len(text):].strip() # 清理可能残留的think标签 answer = re.sub(r' 
     
       .*? 
     ', '', answer, flags=re.DOTALL) answer = re.sub(r'<|.*?|>', '', answer) # 移除特殊token # 如果答案为空,尝试另一种方式 if not answer or len(answer) < 10: print("正在重试...") # 更直接的提示 direct_prompt = f"问题:{question} 答案:" inputs2 = tokenizer(direct_prompt, return_tensors="pt", truncation=True, max_length=256).to("cuda") with torch.no_grad(): outputs2 = model.generate( inputs2, max_new_tokens=200, temperature=0.5, do_sample=True, pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id, ) answer = tokenizer.decode(outputs2[0], skip_special_tokens=True) answer = answer.replace(direct_prompt, "").strip() print(answer if answer else "无法生成答案") print() # 可选:保存模型 # model.save_pretrained_gguf("ckpts/merged", tokenizer, quantization_method="q4_k_m") # 定义训练数据格式化字符串模板 prompt_style="""请遵循指令回答用户问题。 在回答之前,请仔细思考问题,并创建一个逻辑连贯的思考过程,以确保回答准确无误。 指令: 你是一位精通八字算命、紫微斗数、风水、易经卦象、塔罗牌占卜、星象、面相手相和运势预测等方面的算命大师。 请回答以下算命问题。 问题: {} 回答: """ 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) # model.save_pretrained_gguf("ckpts/merged", tokenizer, quantization_method="q4_k_m") 
    

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

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

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

 model.save_pretrained_gguf(

 "ckpts/merged", tokenizer, quantization_method="q4_k_m") 

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

图片

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

图片

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

vllm serve ckpts/merged/

 unsloth.Q4_K_M.gguf --api-key 123 --port 3002 

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

小讯
上一篇 2026-04-20 10:44
下一篇 2026-04-20 10:42

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/272188.html