定制你的DeepSeek专家:Unsloth 大模型微调教程

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

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

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

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

指令微调了解下?

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

1. 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)

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

2. Unsloth 微调教程

2.1 Unsloth 安装

pip 一键安装:

pip install unsloth

2.2 模型准备

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

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

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

首先,从 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

数据集格式如下:

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",只有当单卡显存不够时,才占用其他卡。

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

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

step 8:模型保存

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

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

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

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

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

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 进行格式转换:

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

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

vllm serve ckpts/merged/unsloth.Q4_K_M.gguf --api-key 123 --port 3002

写在最后

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

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

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


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

内容概要:本文详细介绍了如何将DeePseek-R1大模型微调为特定领域的专家系统,涵盖了从理论解释到实际操作的全流程。文章首先阐述了为何需要微调模型(如提升特定领域的专业知识和适应不同的任务需求、保证数据的安全性和节约成本),接着对比长文本处理、知识库利用及模型微调三种方法之间的差异,并深入讲解微调的基本流程(预训练模型的选择、数据集准备、超参数设置)。随后,针对非专业人士易入门的角度,文章示范了一个具体案例——如何用unsloth工具在Google Colab环境下构建一个微调版本的算命大师模型。文中还涉及微调后的模型测试比较及其后续部署至HuggingFace和Ollama平台的具体方法。 适合人群:具有基础编程能力并对自然语言处理感兴趣的研发人员和AI爱好者。 使用场景及目标:本文的目标读者可通过本指南学习并实践怎样根据自己所需的垂直领域(如金融、医学、心理咨询等领域),对现有的通用大模型进行针对性的优化和定制化训练;了解何时及为何应选用微调手段而不是直接采用标准模型或构建全新模型的方法来达成目的。 其他说明:阅读者应在开始动手之前先熟悉一些预备知识点(参考前一篇文章),并且建议初次接触这类工作的学习者优先选择简易实例来进行练习。另外,文中提到的所有工具和服务都是开放给公众使用的,比如Colab提供云计算服务支持、unsloth提高本地训练效率、而HuggingFace则是分享和部署模型的好地方。
### 使用Unsloth微调DeepSeek-R1蒸馏模型构建医疗领域专家模型 为了利用Unsloth框架对DeepSeek-R1蒸馏模型进行微调并构建专门针对医疗领域的专家模型,可以遵循以下方法论和技术细节。 #### 准备工作 确保已经安装了必要的依赖库以及配置好了运行环境。对于特定于医疗领域的数据集准备至关重要,这些数据应该包含大量的医学术语、病例描述以及其他有助于提高模型理解能力的信息[^1]。 #### 数据预处理 在开始训练之前,需对收集到的数据执行一系列预处理操作: - 清洗文本:去除无关字符、标准化格式等; - 分词标注:依据中文特点采用合适的分词工具; - 特殊标记识别:如疾病名称、药物名等实体识别; ```python import jieba from sklearn.model_selection import train_test_split def preprocess_data(texts): processed_texts = [] for text in texts: words = " ".join(jieba.cut(text)) processed_texts.append(words) return processed_texts # 假设texts是一个列表形式的原始语料集合 X_train, X_val = train_test_split(preprocess_data(texts), test_size=0.2) ``` #### 加载基础模型与定义新架构 加载预先训练好的DeepSeek-R1蒸馏版模型,并在此基础上添加额外层来适应新的任务需求。这一步骤通常涉及到迁移学习的概念,在保持原有参数不变的情况下仅调整最后一部分网络结构以匹配目标任务特性。 ```python from transformers import AutoModelForSequenceClassification, Trainer, TrainingArguments model_name_or_path = 'path_to_deepseek_r1_distilled_model' num_labels = 2 # 或者更类别数取决于具体应用场景 base_model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, num_labels=num_labels) class CustomMedicalExpertModel(AutoModelForSequenceClassification): def __init__(self, config): super().__init__(config) self.additional_layers = ... # 定义附加神经元层 custom_model = CustomMedicalExpertModel(base_model.config) ``` #### 训练过程设置 通过`Trainer`类简化整个训练流程管理,包括但不限于损失函数的选择、优化器设定等方面的工作。同时考虑到GPU资源的有效利用率问题,建议合理规划batch size大小及epoch次数等超参选项。 ```python training_args = TrainingArguments( output_dir='./results', evaluation_strategy="steps", eval_steps=500, save_total_limit=2, per_device_train_batch_size=8, gradient_accumulation_steps=4, learning_rate=5e-5, weight_decay=0.01, adam_epsilon=1e-8, max_grad_norm=1.0, logging_dir='./logs', logging_first_step=True, logging_steps=10, fp16=True, dataloader_num_workers=4 ) trainer = Trainer( model=custom_model, args=training_args, train_dataset=train_dataset, eval_dataset=val_dataset ) trainer.train() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值