01
引言
DeepSeek R1 引入了一种全新的 LLM 训练方式,使这些模型在思考和执行一系列推理后做出反应的方式发生了令人印象深刻的变化。
在响应前进行思考和推理的这一微小变化,在大多数指标上都取得了非常显著的效果。这就是为什么 DeepSeek R1 成为几乎所有大模型开发人员的首选。
在本文中,我们将对 DeepSeek-R1 模型的蒸馏版本进行微调,特别是对生成的响应进行定制,使其与我们人类一样富有情感和吸引力!
闲话少说,我们直接开始吧!
02
在对 LLM 进行实际微调之前,我们先简要介绍一下相关准备工作。
-
Python 库和框架
unsloth,这个软件包使 Llama-3、Mistral、Phi-4 和 Gemma 等大型语言模型的微调速度提高了 2 倍,内存使用量减少了 70%,而且准确性没有降低!更多信息,请参考官网,https://docs.unsloth.ai/。
torch,这个软件包是使用 PyTorch 进行深度学习的基本构件。它提供了一个功能强大的张量库,与 NumPy 类似,但具有 GPU 加速的额外优势,这在使用 LLM 时至关重要。
transformers 是一个强大而流行的自然语言处理(NLP)开源库。它为各种最先进的预训练模型提供了易于使用的接口。由于预训练模型是任何微调任务的基础,该软件包有助于轻松访问训练好的模型。
trl 软件包是一个专门用于使用Transformer模型进行强化学习(RL)的库。它建立在 Hugging Face 的Transformer库之上,利用其优势使使用Transformer的强化学习变得更易用、更高效。
算力要求
对模型进行微调是一种使 LLM 的响应更有条理和针对特定领域的技术。微调模型的技术有很多,其中有些技术无需进行完整的参数训练就能完成微调过程。
然而,由于所有可训练参数和实际 LLM 都存储在 GPU 的 vRAM中,因此对于大多数普通计算机硬件来说,微调更大 LLM 的过程仍然不可行,而 LLM 的巨大体积则是实现微调的主要障碍。
因此,在本文中,我们将微调 DeepSeek-R1 LLM 的蒸馏版本,即 DeepSeek-R1-Distill,其中包含 47.4 亿个参数。这个 LLM 至少需要 8-12 GB 的 vRAM,为了让所有人都能使用,我们将使用谷歌 Colab 的免费 T4 GPU,它有 16 GB 的 vRAM。
03
DPO数据集:
https://huggingface.co/datasets/HumanLLMs/Human-Like-DPO-Dataset
04
pip install unsloth
from unsloth import FastLanguageModelmodel, tokenizer = FastLanguageModel.from_pretrained(model_name = "unsloth/DeepSeek-R1-Distill-Llama-8B-unsloth-bnb-4bit",max_seq_length = 2048,dtype = None,load_in_4bit = True,# token = "hf_...", # use one if using gated models like meta-llama/Llama-2-7b-hf)
-
我们指定了model_name为 "unsloth/DeepSeek-R1-Distill-Llama-8B-unsloth-bnb-4bit",用于访问预先训练好的 DeepSeek-R1-Distill 模型。 -
我们将 max_seq_length 定义为 2048,它设定了模型可以处理的输入序列的最大长度。通过合理设置,我们可以优化内存使用和处理速度。 -
dtype 设置为 None,这有助于映射获取模型的数据类型,使其与现有硬件兼容。有了它,我们就不必明确检查和提及数据类型,unsloth 会处理好一切。 -
load_in_4bit 可以增强推理能力,减少内存使用量。基本上,我们将模型量化为 4 比特精度。
model = FastLanguageModel.get_peft_model(model,r = 64,target_modules = ["q_proj", "k_proj", "v_proj", "o_proj","gate_proj", "up_proj", "down_proj",],lora_alpha = 16,lora_dropout = 0, # Can be set to any, but = 0 is optimizedbias = "none", # Can be set to any, but = "none" is optimizeduse_gradient_checkpointing = "unsloth", # True or "unsloth" for very long contextrandom_state = 3927,use_rslora = False, # unsloth also supports rank stabilized LoRAloftq_config = None, # And LoftQ)
-
我们使用 FastLanguageModel 中的 get_peft_model 函数重新初始化了模型,以便使用 PEFT 技术。 -
我们还需要传递上一步中获取的预训练模型 model。 -
在这里,r=64 参数定义了 LoRA 自适应中低秩矩阵的秩。一般来说,秩在 8-128 之间时效果最好。
-
lora_dropout参数在训练该LoRA适配器模型时,会向低秩矩阵引入随机dropout机制。该参数能有效防止模型过拟合。通过将lora_dropout设置为0,unsloth框架可自动为我们选择最佳优化值。 -
target_modules参数用于指定模型中需要应用LoRA适配的具体类或模块名称列表。
06
-
Instructions: 表示向LLM提出的主要问题。 -
Response: 表示 LLM 的输出。它用于说明如何根据具体指令调整 LLM 的响应。
human_prompt = """Below is an instruction that describes a task. Write a response that appropriately completes the request.### Instruction:{}### Response:{}"""
EOS_TOKEN = tokenizer.eos_token # Must add EOS_TOKENdef formatting_human_prompts_func(examples):instructions = examples["prompt"]outputs = examples["chosen"]texts = []for instruction, output in zip(instructions, outputs):# Must add EOS_TOKEN, otherwise your generation will go on forever!text = human_prompt.format(instruction, output) + EOS_TOKENtexts.append(text)return { "text" : texts, }
from datasets import load_datasetdataset = load_dataset("HumanLLMs/Human-Like-DPO-Dataset", split = "train")dataset = dataset.map(formatting_human_prompts_func, batched = True,)
from trl import SFTTrainerfrom transformers import TrainingArgumentsfrom unsloth import is_bfloat16_supportedtrainer = SFTTrainer(model = model, # The model with LoRA adapterstokenizer = tokenizer, # The tokenizer of the modeltrain_dataset = dataset, # The dataset to use for trainingdataset_text_field = "text", # The field in the dataset that contains the structured datamax_seq_length = 2048, # Max length of input sequence that the model can processdataset_num_proc = 2, # Noe of processes to use for loading and processing the datapacking = False, # Can make training 5x faster for short sequences.args = TrainingArguments(per_device_train_batch_size = 2, # Batch size per GPUgradient_accumulation_steps = 4, # Step size of gradient accumulationwarmup_steps = 5,# num_train_epochs = 1, # Set this for 1 full training run.max_steps = 120, # Maximum steps of traininglearning_rate = 2e-4, # Initial learning ratefp16 = not is_bfloat16_supported(),bf16 = is_bfloat16_supported(),logging_steps = 1,optim = "adamw_8bit", # The optimizer that will be used for updating the weightsweight_decay = 0.01,lr_scheduler_type = "linear",seed = 3407,output_dir = "outputs",report_to = "none", # Use this for WandB etc),)
trainer_stats = trainer.train()
FastLanguageModel.for_inference(model) # Enable native 2x faster inferenceinputs = tokenizer([human_prompt.format("Oh, I just saw the best meme - have you seen it?", # instruction"", # output - leave this blank for generation!)], return_tensors = "pt").to("cuda")outputs = model.generate(**inputs, max_new_tokens = 1024, use_cache = True)tokenizer.batch_decode(outputs)
-
我们使用 unsloth 软件包中的 FastLanguageModel 来加载微调模型进行推理。这种方法能产生更快的结果。 -
为了推理模型,我们必须首先将查询转换为结构化提示,然后对提示进行Token化处理。 -
我们还设置了 return_tensors="pt",表明返回 PyTorch 张量,然后使用 .to("cuda") 将该张量加载到 GPU,以提高处理速度。 -
然后,我们调用 model.generate() 为查询生成响应。 -
在生成时,我们提到了 max_new_tokens=1024,它设置了模型可以生成的最大Token数。 -
use_cache=True 有助于加快生成速度,尤其是对于较长的序列。 -
最后,我们将微调模型的输出从张量解码为文本。
Query1: I love reading and writing,what are your hobbies?
Query2: What’s your favourite type of cuisine to cook or eat?
# Pushing with 4bit precisionmodel.push_to_hub_merged("<YOUR_HF_ID>/<MODEL_NAME>", tokenizer, save_method = "merged_4bit", token = "<YOUR_HF_TOKEN>")# Pushing with 16 bit precisionmodel.push_to_hub_merged("<YOUR_HF_ID>/<MODEL_NAME>", tokenizer, save_method = "merged_16bit", token = "<YOUR_HF_TOKEN>")
-
在这里,我们必须设置模型的名称,该名称将用于在 Hugging Face Hub 上设置模型的 ID。 -
我们可以上传精度为 4 位或 16 位的完整合并后的模型。合并后的模型表示将预训练模型和 LoRA 矩阵一起上传到hub上。
链接:
https://huggingface.co/krishanwalia30/DeepSeek-R1-Distill-HumanLikeDPO-FineTuned-16bit
欢迎大家持续关注!
参考:
[1] DeepSeekV2: https://arxiv.org/abs/2405.04434
[2] DeepSeekV3: https://arxiv.org/abs/2412.19437
[3] DeepSeekR1: https://arxiv.org/abs/2501.12948
点击上方小卡片关注我
添加个人微信,进专属粉丝群!

