Skills MCP Model 博客 提交 Skills
登录 注册

DeepSeek 模型微调教程

将 DeepSeek 模型定制化为你的专属 AI 助手。从数据准备到模型部署,完整微调流程,附可直接运行的代码和配置。

开始学习

微调概述

在开始微调之前,先理解什么是微调、什么时候需要微调、以及微调的类型。

什么是模型微调(Fine-tuning)?

模型微调,是指在预训练大语言模型的基础上,使用特定领域的数据对模型进行额外训练,使其在特定任务上表现更好。DeepSeek 模型本身已经具备强大的通用能力,但如果你需要它在医疗诊断、法律咨询、金融分析、代码审查等垂直领域表现更专业,微调是最有效的途径。

什么时候需要微调,什么时候用提示词工程?

这是很多开发者首先会问的问题。以下是判断标准:

建议微调的场景
  • 需要模型掌握特定领域知识(医学、法律、金融等)
  • 需要特定输出格式(JSON 结构、特定模板)
  • 需要模仿特定写作风格或语气
  • 提示词太长,无法在上下文中完整描述需求
  • 需要降低推理成本(微调后小模型可替代大模型)
  • 需要减少延迟(微调后不需要复杂提示词)
建议用提示词工程的场景
  • 任务比较简单,几句话就能描述清楚
  • 数据量不足(少于 100 条高质量样本)
  • 需求频繁变化,不想反复训练
  • 只是临时用一次,无需长期维护
  • 担心微调导致模型在其他能力上退化

微调的主要类型

最推荐

LoRA 微调

低秩适配(Low-Rank Adaptation),在原始模型旁添加小参数矩阵,只训练这些新增参数。显存需求低,训练速度快,单卡即可完成 7B 模型微调。是当前最主流、最推荐的微调方式。

  • 显存需求:7B 模型约 16GB
  • 训练速度:快(小时级)
  • 模型质量:接近全参数微调
  • 可合并回原始模型
显存友好

QLoRA 微调

在 LoRA 基础上增加 4-bit 量化,将模型参数压缩到 4 位精度。显存需求进一步降低,普通消费级显卡(如 RTX 3090 24GB)即可微调 7B 模型。速度略慢于 LoRA,但效果相当。

  • 显存需求:7B 模型约 8-10GB
  • 训练速度:中等(小时级)
  • 模型质量:与 LoRA 相当
  • 适合消费级显卡
最高质量

全参数微调

更新模型的所有参数,理论上效果最好,但需要大量 GPU 显存。7B 模型约需 60-80GB 显存,通常需要多卡训练或使用 DeepSpeed ZeRO 优化。适合有充足计算资源的团队。

  • 显存需求:7B 模型约 60-80GB
  • 训练速度:慢(天级)
  • 模型质量:最优
  • 需要多卡/A100

DeepSeek 模型微调的特殊优势

DeepSeek 模型采用 MoE(混合专家)架构,具有以下微调方面的优势:

  • Dense 模型(如 DeepSeek-Coder-6.7B):标准架构,LoRA/QLoRA 支持完善,社区资源丰富
  • MoE 模型(如 DeepSeek-V2-Lite):每次推理仅激活部分专家,推理效率高,微调时可针对性优化特定专家
  • 官方代码支持:DeepSeek 官方 GitHub 仓库提供了完整的微调代码,可直接使用
  • MIT 开源协议:微调后的模型可商用,无任何限制

更多 DeepSeek 模型架构信息请查看 DeepSeek 模型架构详解DeepSeek 开源模型列表

环境准备

在开始微调之前,需要安装必要的 Python 依赖库和配置 GPU 环境。

硬件要求

微调方式 1.5B 模型 7B 模型 16B 模型 推荐显卡
QLoRA (4-bit) ~4GB ~8-10GB ~16-20GB RTX 3060 12GB 以上
LoRA (FP16/BF16) ~8GB ~16-20GB ~32-40GB RTX 3090/4090 24GB
全参数微调 ~20GB ~60-80GB ~120-160GB A100 80GB / 多卡

安装依赖库

创建一个新的 Python 虚拟环境,安装以下依赖:

# 创建虚拟环境 python -m venv deepseek-finetune # Windows 激活 deepseek-finetune\Scripts\activate # Linux/Mac 激活 # source deepseek-finetune/bin/activate # 安装 PyTorch(根据你的 CUDA 版本选择) # CUDA 12.1 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 # CUDA 11.8 # pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装微调核心库 pip install transformers datasets accelerate peft bitsandbytes pip install trl # Transformer Reinforcement Learning,提供 SFTTrainer pip install wandb tensorboard # 训练监控 pip install deepspeed # 全参数微调需要(可选)

验证环境

安装完成后,运行以下 Python 脚本验证环境是否正常:

import torch import transformers import peft import datasets print(f"PyTorch 版本: {torch.__version__}") print(f"CUDA 可用: {torch.cuda.is_available()}") print(f"GPU 数量: {torch.cuda.device_count()}") print(f"Transformers 版本: {transformers.__version__}") print(f"PEFT 版本: {peft.__version__}") print(f"Datasets 版本: {datasets.__version__}") if torch.cuda.is_available(): print(f"当前 GPU: {torch.cuda.get_device_name(0)}") print(f"显存总量: {torch.cuda.get_device_properties(0).total_mem / 1024**3:.1f} GB") else: print("警告: 未检测到 GPU,训练将使用 CPU(速度极慢)")

环境注意事项

  • bitsandbytes 安装:Windows 用户可能需要从源码编译,建议使用 WSL2 或 Linux 环境
  • CUDA 版本匹配:PyTorch 的 CUDA 版本必须与系统安装的 CUDA 驱动版本兼容
  • 云 GPU 推荐:如果没有本地 GPU,推荐使用 AutoDL、恒源云等国内 GPU 云平台

数据集准备

高质量的数据是微调成功的关键。本节介绍数据格式、数据质量要求和数据准备的最佳实践。

数据格式

微调数据通常使用 JSON 或 JSONL 格式。每条数据包含一个指令(instruction)和对应的输出(output),可选地包含输入(input)字段。

# data.jsonl — 每行一个 JSON 对象 {"instruction": "将以下中文翻译成英文", "input": "人工智能正在改变世界", "output": "Artificial intelligence is changing the world."} {"instruction": "解释以下 Python 代码的作用", "input": "def foo(x): return x**2", "output": "该函数接收一个参数 x,返回 x 的平方值。"} {"instruction": "写一首关于春天的五言绝句", "input": "", "output": "春风拂柳绿,\n细雨润花红。\n燕子归巢急,\n人间四月同。"}

Alpaca 格式(推荐)

Alpaca 格式是微调社区最常用的数据格式,与 DeepSeek 模型兼容性最好:

[ { "instruction": "你是一个专业的 Python 编程助手,请回答以下问题。", "input": "如何在 Python 中实现单例模式?", "output": "在 Python 中实现单例模式有几种常用方法:\n\n1. 使用 __new__ 方法:\n```python\nclass Singleton:\n _instance = None\n def __new__(cls, *args, **kwargs):\n if cls._instance is None:\n cls._instance = super().__new__(cls)\n return cls._instance\n```\n\n2. 使用装饰器:\n```python\ndef singleton(cls):\n instances = {}\n def wrapper(*args, **kwargs):\n if cls not in instances:\n instances[cls] = cls(*args, **kwargs)\n return instances[cls]\n return wrapper\n```\n\n推荐使用第一种方法,因为它更 Pythonic。" } ]

Chat 格式(对话数据)

如果你有对话数据,可以使用 ShareGPT 格式或 conversations 格式:

{ "conversations": [ {"role": "system", "content": "你是一个专业的医疗咨询助手。"}, {"role": "user", "content": "我最近总是头痛,应该怎么办?"}, {"role": "assistant", "content": "头痛的原因很多,建议您...(此处省略具体内容)"} ] }

数据质量建议

数量
  • 最少 100-200 条高质量样本
  • 推荐 500-2000 条
  • 简单任务 500 条即可
  • 复杂任务建议 2000+ 条
多样性
  • 覆盖不同难度和场景
  • 避免重复或高度相似的样本
  • 长文本和短文本混合
  • 包含边界情况
准确性
  • 输出必须准确无误
  • 代码示例需要可运行
  • 专业领域知识需核实
  • 格式保持一致
一致性
  • 统一的输出格式
  • 一致的语气和风格
  • 相同的术语使用
  • 统一的标点符号规范

数据质量比数量更重要

500 条高质量数据的效果远好于 5000 条低质量数据。每个样本都应该经过人工审核,确保输出准确、格式规范。如果数据质量差,微调反而会损坏模型原有的能力。

使用 DeepSeek 生成训练数据

你可以利用 DeepSeek 本身来生成高质量的训练数据。以下是一个实用的数据生成策略:

# 使用 DeepSeek API 生成训练数据 from openai import OpenAI import json client = OpenAI( api_key="sk-your-api-key", base_url="https://api.deepseek.com", ) # 让 DeepSeek V3 生成训练数据 prompt = """请生成 50 条 Python 编程问答对,格式为 JSON 数组。 每条包含 instruction(问题)和 output(详细解答)。 覆盖以下主题:装饰器、异步编程、类型提示、上下文管理器、生成器。 {"instruction": "...", "output": "..."}""" response = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": prompt}], temperature=0.8, ) # 解析并保存 data = json.loads(response.choices[0].message.content) with open('training_data.json', 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2)

更多 DeepSeek API 使用方法请查看 DeepSeek 使用教程

LoRA 微调(推荐)

LoRA 是当前最推荐、最实用的微调方式。本节提供完整的 QLoRA 微调代码和详细的参数说明。

LoRA 微调原理

LoRA(Low-Rank Adaptation)的核心思想是:在预训练模型的权重旁添加低秩矩阵,只训练这些新增的矩阵参数,而保持原始模型权重不变。这大幅减少了需要训练的参数量和显存需求。

具体来说,对于原始权重矩阵 W(d x k),LoRA 将其更新分解为两个小矩阵的乘积:W + delta_W = W + B * A,其中 A 是 r x k 矩阵,B 是 d x r 矩阵,r 是秩(rank),通常取 8-64。由于 r 远小于 d 和 k,训练参数量大幅减少。

QLoRA 微调完整代码

以下是使用 QLoRA(4-bit 量化 + LoRA)微调 DeepSeek 模型的完整 Python 脚本:

# train_lora.py — QLoRA 微调 DeepSeek 模型 import torch from datasets import load_dataset from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, BitsAndBytesConfig, ) from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training from trl import SFTTrainer # ========== 1. 配置 ========== model_name = "deepseek-ai/deepseek-coder-6.7b-instruct" dataset_path = "./data/training_data.json" output_dir = "./output/deepseek-lora" # ========== 2. 4-bit 量化配置 ========== bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True, ) # ========== 3. 加载模型和分词器 ========== model = AutoModelForCausalLM.from_pretrained( model_name, quantization_config=bnb_config, device_map="auto", trust_remote_code=True, ) tokenizer = AutoTokenizer.from_pretrained( model_name, trust_remote_code=True, ) tokenizer.pad_token = tokenizer.eos_token tokenizer.padding_side = "right" # ========== 4. 准备模型用于 k-bit 训练 ========== model = prepare_model_for_kbit_training(model) # ========== 5. LoRA 配置 ========== lora_config = LoraConfig( r=16, # LoRA 秩,越大表达能力越强,但参数越多 lora_alpha=32, # LoRA 缩放因子,通常设为 r 的 2 倍 target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj", ], lora_dropout=0.05, # Dropout 防止过拟合 bias="none", # 不训练 bias task_type="CAUSAL_LM", # 因果语言模型任务 ) model = get_peft_model(model, lora_config) model.print_trainable_parameters() # ========== 6. 加载数据集 ========== dataset = load_dataset("json", data_files=dataset_path, split="train") def format_instruction(sample): """将数据格式化为训练用的文本""" if sample.get("input"): text = f"""Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request. ### Instruction: {sample['instruction']} ### Input: {sample['input']} ### Response: {sample['output']}""" else: text = f"""Below is an instruction that describes a task. Write a response that appropriately completes the request. ### Instruction: {sample['instruction']} ### Response: {sample['output']}""" return {"text": text} dataset = dataset.map(format_instruction) # ========== 7. 训练参数 ========== training_args = TrainingArguments( output_dir=output_dir, num_train_epochs=3, per_device_train_batch_size=4, gradient_accumulation_steps=4, warmup_steps=100, learning_rate=2e-4, fp16=True, logging_steps=10, save_steps=200, save_total_limit=3, optim="paged_adamw_8bit", lr_scheduler_type="cosine", report_to="tensorboard", ) # ========== 8. 创建 Trainer 并开始训练 ========== trainer = SFTTrainer( model=model, args=training_args, train_dataset=dataset, tokenizer=tokenizer, max_seq_length=2048, dataset_text_field="text", ) trainer.train() # ========== 9. 保存模型 ========== trainer.model.save_pretrained(output_dir) tokenizer.save_pretrained(output_dir)

关键参数说明

参数 推荐值 说明
r (rank) 8-64 秩越大,表达能力越强,但训练参数和显存也越多。简单任务 r=8 足够,复杂任务可用 r=32-64
lora_alpha r 的 2 倍 缩放因子,控制 LoRA 权重对原始模型的影响程度。通常设为 r 的 2 倍
learning_rate 1e-4 ~ 5e-4 学习率。LoRA 通常使用比全参数微调更高的学习率,推荐 2e-4
num_train_epochs 2-5 训练轮数。数据量少可适当增加轮数,数据量大 2-3 轮即可。注意观察 loss 曲线防止过拟合
max_seq_length 2048-4096 最大序列长度。长文本训练需要更多显存。DeepSeek-Coder 支持 16K 上下文

运行训练

将上述代码保存为 train_lora.py,然后运行:

python train_lora.py

训练加速技巧

  • 使用 gradient_checkpointing:以计算换显存,可在 TrainingArguments 中设置 gradient_checkpointing=True
  • 调整 batch_size:如果显存不足,减小 per_device_train_batch_size,增大 gradient_accumulation_steps
  • 使用 Flash Attention 2:安装 flash-attn 库,在模型加载时设置 use_flash_attention_2=True,可提速 2-3 倍

更多模型下载和部署信息请查看 DeepSeek 模型下载DeepSeek 部署教程

全参数微调

全参数微调理论上效果最好,但需要大量 GPU 显存。本节介绍使用 DeepSpeed ZeRO 进行全参数微调的方法。

全参数微调的需求

全参数微调更新模型的所有参数,需要将整个模型加载到显存中。对于 7B 模型,仅模型权重就需要约 14GB(FP16),加上优化器状态和梯度,总计需要约 60-80GB 显存。因此通常需要多卡训练或使用 A100/H100 等大显存 GPU。

DeepSpeed ZeRO 配置

DeepSpeed ZeRO 通过将优化器状态、梯度和模型参数分布到多个 GPU 上,大幅降低单卡显存需求。以下是 DeepSeek 模型全参数微调的 DeepSpeed 配置:

# ds_config_zero3.json — DeepSpeed ZeRO Stage 3 配置 { "bf16": { "enabled": true }, "zero_optimization": { "stage": 3, "offload_optimizer": { "device": "cpu", "pin_memory": true }, "offload_param": { "device": "cpu", "pin_memory": true }, "overlap_comm": true, "contiguous_gradients": true, "sub_group_size": 1e9, "reduce_bucket_size": "auto", "stage3_prefetch_bucket_size": "auto", "stage3_param_persistence_threshold": "auto", "stage3_max_live_parameters": 1e9, "stage3_max_reuse_distance": 1e9, "stage3_gather_16bit_weights_on_model_save": true }, "gradient_accumulation_steps": "auto", "gradient_clipping": "auto", "train_batch_size": "auto", "train_micro_batch_size_per_gpu": "auto", "wall_clock_breakdown": false }

全参数微调训练脚本

# train_full.py — DeepSeek 全参数微调 import torch from datasets import load_dataset from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, DataCollatorForLanguageModeling, ) model_name = "deepseek-ai/deepseek-coder-6.7b-instruct" dataset_path = "./data/training_data.json" output_dir = "./output/deepseek-full" # 加载模型(不量化) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.bfloat16, trust_remote_code=True, use_flash_attention_2=True, # 需要安装 flash-attn ) tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) tokenizer.pad_token = tokenizer.eos_token tokenizer.padding_side = "right" # 加载并处理数据集 dataset = load_dataset("json", data_files=dataset_path, split="train") def tokenize_function(examples): texts = [f"### Instruction:\n{inst}\n\n### Response:\n{out}" for inst, out in zip(examples["instruction"], examples["output"])] return tokenizer(texts, truncation=True, max_length=2048) tokenized_dataset = dataset.map(tokenize_function, batched=True, remove_columns=dataset.column_names) data_collator = DataCollatorForLanguageModeling( tokenizer=tokenizer, mlm=False, ) training_args = TrainingArguments( output_dir=output_dir, overwrite_output_dir=True, num_train_epochs=2, per_device_train_batch_size=2, gradient_accumulation_steps=8, warmup_ratio=0.03, learning_rate=5e-5, bf16=True, logging_steps=10, save_steps=200, save_total_limit=2, deepspeed="./ds_config_zero3.json", lr_scheduler_type="cosine", report_to="tensorboard", ) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_dataset, tokenizer=tokenizer, data_collator=data_collator, ) trainer.train() trainer.save_model(output_dir) tokenizer.save_pretrained(output_dir)

启动多卡训练

# 使用 DeepSpeed 在 4 张 GPU 上训练 deepspeed --num_gpus=4 train_full.py

全参数微调 vs LoRA 选择建议

  • 优先选择 LoRA:在大多数场景下,LoRA 的效果与全参数微调差距很小(通常 < 2%),但成本低得多
  • 全参数微调适用场景:需要在全新领域上大幅改变模型行为、有充足计算资源、追求极致性能
  • 混合策略:先用 LoRA 快速实验,找到最优数据和超参数,再用全参数微调做最终版本

使用 DeepSeek 官方微调代码

DeepSeek 官方在 GitHub 上提供了完整的微调代码,可以直接使用,无需从零编写训练脚本。

获取官方微调代码

DeepSeek 官方 GitHub 仓库包含训练和微调所需的所有代码。以 DeepSeek-Coder 为例:

# 克隆 DeepSeek-Coder 仓库 git clone https://github.com/deepseek-ai/DeepSeek-Coder.git cd DeepSeek-Coder # 微调代码位于 finetune/ 目录 ls finetune/ # 输出: deepseek_coder_finetune.py data/ README.md # 安装官方依赖 pip install -r finetune/requirements.txt

官方微调命令

DeepSeek 官方提供了简洁的命令行接口,支持 LoRA 和全参数微调:

# LoRA 微调 DeepSeek-Coder-6.7B python finetune/deepseek_coder_finetune.py \ --model_name_or_path deepseek-ai/deepseek-coder-6.7b-instruct \ --data_path ./data/training_data.jsonl \ --output_dir ./output/deepseek-coder-lora \ --num_train_epochs 3 \ --per_device_train_batch_size 4 \ --gradient_accumulation_steps 4 \ --learning_rate 2e-4 \ --lora_r 16 \ --lora_alpha 32 \ --use_lora True \ --bf16 True 官方数据格式要求

DeepSeek 官方微调代码期望的数据格式为 JSONL,每行包含 instruction 和 output 字段:

# training_data.jsonl {"instruction": "编写一个 Python 函数,计算两个数的最大公约数", "output": "def gcd(a, b):\n while b:\n a, b = b, a % b\n return a\n\n# 使用 math.gcd 也可以\n# from math import gcd"} {"instruction": "解释什么是递归", "output": "递归是一种编程技术,函数通过调用自身来解决问题。递归包含两个关键部分:\n\n1. 基准条件(Base Case):停止递归的条件\n2. 递归条件(Recursive Case):函数调用自身的条件\n\n例如,计算阶乘 n! = n * (n-1)!,当 n=1 时停止。"}

官方代码的优势

  • 针对 DeepSeek 模型架构优化,训练效率更高
  • 自动处理 DeepSeek 特有的 tokenizer 和模型配置
  • 内置数据处理和评估逻辑
  • 持续更新,与最新模型版本兼容

更多 DeepSeek 生态工具请查看 DeepSeek 生态工具

训练监控

训练过程中实时监控 loss 曲线、学习率变化和 GPU 使用情况,帮助你及时发现异常并调整参数。

TensorBoard 监控

TensorBoard 是 PyTorch 内置的训练可视化工具。在训练代码中设置 report_to="tensorboard" 后,启动 TensorBoard:

# 启动 TensorBoard tensorboard --logdir ./output --port 6006 # 浏览器打开 http://localhost:6006

Wandb 监控

Wandb(Weights & Biases)提供更丰富的云端训练监控功能,支持团队协作:

# 安装 wandb pip install wandb # 登录(首次使用需要 API Key) wandb login # 在训练代码中设置(或通过环境变量) # TrainingArguments 中设置 report_to="wandb" # 或设置环境变量: # export WANDB_PROJECT="deepseek-finetune"

关键监控指标

Loss 曲线
  • 训练 loss 应持续下降
  • 如果 loss 不降,检查学习率
  • 如果 loss 震荡剧烈,降低学习率
  • 如果 loss 快速下降后平台,正常
GPU 利用率
  • GPU 利用率应接近 90-100%
  • 利用率低 = 数据加载瓶颈
  • 增加 num_workers 加速数据加载
  • 使用 nvidia-smi 实时查看
学习率
  • 使用 cosine 调度器自动衰减
  • warmup 阶段逐步提升学习率
  • loss 不降可尝试提高学习率
  • loss 震荡可尝试降低学习率
Checkpoint
  • 定期保存 checkpoint
  • save_total_limit 控制保存数量
  • 训练中断可从 checkpoint 恢复
  • 保留 loss 最低的 checkpoint

从 Checkpoint 恢复训练

如果训练意外中断,可以从最近的 checkpoint 恢复:

# 在 TrainingArguments 中添加 resume_from_checkpoint=True # 或指定具体 checkpoint 路径 resume_from_checkpoint="./output/deepseek-lora/checkpoint-600"

过拟合预警信号

如果训练 loss 持续下降但验证 loss 开始上升,说明模型在过拟合。此时应:减少训练轮数、增加数据量、增大 lora_dropout、或使用更小的 learning_rate。

模型评估

微调完成后,需要评估模型在目标任务上的表现,确保微调达到了预期效果。

评估方法

模型评估分为自动评估和人工评估两种方式:

自动评估

定量指标

  • Perplexity(困惑度)
  • ROUGE / BLEU 分数
  • 准确率 / F1 分数
  • HumanEval(代码任务)
  • MMLU(知识问答)
人工评估

定性评估

  • 输出质量打分
  • 指令遵循度
  • 回答准确性
  • 风格一致性
  • A/B 对比测试

测试集评估脚本

# evaluate.py — 评估微调后的模型 import torch from transformers import AutoModelForCausalLM, AutoTokenizer from peft import PeftModel # 加载基础模型和 LoRA 权重 base_model = AutoModelForCausalLM.from_pretrained( "deepseek-ai/deepseek-coder-6.7b-instruct", torch_dtype=torch.float16, device_map="auto", trust_remote_code=True, ) tokenizer = AutoTokenizer.from_pretrained( "deepseek-ai/deepseek-coder-6.7b-instruct", trust_remote_code=True, ) # 加载 LoRA 适配器 model = PeftModel.from_pretrained(base_model, "./output/deepseek-lora") # 测试推理 test_questions = [ "编写一个 Python 函数,实现二分查找算法", "解释 Python 中的 GIL 是什么", "如何使用 asyncio 实现并发请求", ] for question in test_questions: prompt = f"### Instruction:\n{question}\n\n### Response:\n" inputs = tokenizer(prompt, return_tensors="pt").to(model.device) outputs = model.generate( **inputs, max_new_tokens=512, temperature=0.7, do_sample=True, top_p=0.9, ) response = tokenizer.decode(outputs[0], skip_special_tokens=True) print(f"问题: {question}\n回答: {response}\n{'='*60}")

基准对比

建议在相同的测试集上对比微调前和微调后模型的输出,评估微调带来的提升:

  • 横向对比:微调后模型 vs 原始模型 vs 其他同类模型
  • 纵向对比:不同 checkpoint 之间的效果差异
  • 消融实验:不同超参数组合的效果对比
  • 边界测试:测试模型在极端输入下的表现

评估建议

不要只看自动评估指标。一定要在实际场景中测试模型,尤其是边界情况和容易出错的场景。如果微调后模型在通用能力上退化严重,考虑混合原始数据和微调数据重新训练。

模型合并与导出

微调完成后,需要将 LoRA 权重合并回原始模型,并导出为不同格式以便部署使用。

合并 LoRA 权重

LoRA 训练产生的是适配器权重,需要合并回原始模型才能作为独立模型使用:

# merge_lora.py — 合并 LoRA 权重到原始模型 import torch from transformers import AutoModelForCausalLM, AutoTokenizer from peft import PeftModel base_model_name = "deepseek-ai/deepseek-coder-6.7b-instruct" lora_path = "./output/deepseek-lora" merged_path = "./output/deepseek-coder-merged" # 加载基础模型 base_model = AutoModelForCausalLM.from_pretrained( base_model_name, torch_dtype=torch.float16, trust_remote_code=True, ) # 加载并合并 LoRA 权重 model = PeftModel.from_pretrained(base_model, lora_path) model = model.merge_and_unload() # 保存合并后的模型 model.save_pretrained(merged_path) tokenizer = AutoTokenizer.from_pretrained(base_model_name, trust_remote_code=True) tokenizer.save_pretrained(merged_path) print(f"模型已合并并保存到: {merged_path}")

导出为 GGUF 格式(Ollama / llama.cpp)

GGUF 是 llama.cpp 和 Ollama 使用的模型格式,导出后可以在本地高效运行:

# 使用 llama.cpp 的 convert_hf_to_gguf.py 转换 # 克隆 llama.cpp git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp # 安装依赖 pip install -r requirements.txt # 转换模型为 GGUF 格式 python convert_hf_to_gguf.py ../output/deepseek-coder-merged \ --outfile ../output/deepseek-coder-finetuned.gguf \ --outtype f16 # 量化模型(可选,减小体积) # ./build/bin/llama-quantize ../output/deepseek-coder-finetuned.gguf Q4_K_M

创建 Ollama Modelfile

导出 GGUF 后,创建 Modelfile 即可在 Ollama 中使用:

# Modelfile FROM ./deepseek-coder-finetuned.gguf PARAMETER temperature 0.7 PARAMETER top_p 0.9 PARAMETER num_ctx 16384 SYSTEM """你是一个专业的 Python 编程助手,经过微调优化。""" # 创建 Ollama 模型 # ollama create deepseek-coder-custom -f Modelfile # 运行 # ollama run deepseek-coder-custom

推送到 Hugging Face

将微调后的模型分享到 Hugging Face 社区:

# push_to_hub.py from transformers import AutoModelForCausalLM, AutoTokenizer from huggingface_hub import login # 登录 Hugging Face(需要 Access Token) login(token="hf_your_access_token") model = AutoModelForCausalLM.from_pretrained( "./output/deepseek-coder-merged", trust_remote_code=True, ) tokenizer = AutoTokenizer.from_pretrained( "./output/deepseek-coder-merged", trust_remote_code=True, ) repo_id = "your-username/deepseek-coder-finetuned" model.push_to_hub(repo_id, private=True) tokenizer.push_to_hub(repo_id, private=True) print(f"模型已推送到: https://huggingface.co/{repo_id}")

更多部署方案请查看 DeepSeek 部署教程

部署上线

微调完成后的最后一步:将模型部署到生产环境,提供 API 服务。

部署方案对比

最简单

Ollama 本地部署

适合个人使用和小团队,一键部署微调后的模型。导出为 GGUF 格式后,通过 Ollama 运行,支持 API 调用。

  • 一行命令启动
  • 兼容 OpenAI API 格式
  • 资源占用低
  • 适合单机部署
高性能

vLLM 部署

适合生产环境,高性能推理引擎。支持 PagedAttention、连续批处理、多 GPU 并行推理,吞吐量远超 Ollama。

  • 高吞吐量
  • 兼容 OpenAI API
  • 支持多 GPU
  • 生产级稳定性
云端

Hugging Face Inference

将模型推送到 Hugging Face 后,可直接使用 Inference Endpoints 部署,无需管理服务器。

  • 免运维
  • 自动扩缩容
  • 按量付费
  • 全球节点

vLLM 部署命令

vLLM 是目前生产环境中最推荐的高性能推理引擎:

# 安装 vLLM pip install vllm # 启动 vLLM API 服务 python -m vllm.entrypoints.openai.api_server \ --model ./output/deepseek-coder-merged \ --served-model-name deepseek-coder-custom \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 16384 \ --gpu-memory-utilization 0.95 \ --trust-remote-code # API 调用(与 OpenAI 完全兼容) # curl http://localhost:8000/v1/chat/completions \ # -H "Content-Type: application/json" \ # -d '{"model":"deepseek-coder-custom","messages":[{"role":"user","content":"你好"}]}'

Docker 部署

使用 Docker 部署可以确保环境一致性,方便迁移和扩展:

# Dockerfile FROM vllm/vllm-openai:latest COPY ./output/deepseek-coder-merged /models/deepseek-coder-custom ENV VLLM_MODEL=/models/deepseek-coder-custom ENV VLLM_SERVED_MODEL_NAME=deepseek-coder-custom ENV VLLM_MAX_MODEL_LEN=16384 EXPOSE 8000 # 构建并运行 # docker build -t deepseek-coder-custom . # docker run --gpus all -p 8000:8000 deepseek-coder-custom

API 调用示例(Python)

from openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1", api_key="not-needed", ) response = client.chat.completions.create( model="deepseek-coder-custom", messages=[ {"role": "system", "content": "你是一个专业的 Python 编程助手。"}, {"role": "user", "content": "写一个快速排序的实现"}, ], temperature=0.7, max_tokens=512, ) print(response.choices[0].message.content)

生产环境注意事项

  • 健康检查:配置 /health 端点,确保服务可用性监控
  • 限流:通过 Nginx 或 API 网关设置请求频率限制
  • 并发控制:vLLM 支持 max_num_seqs 参数控制并发请求数
  • 日志记录:记录所有 API 请求和响应,方便排查问题
  • 模型热更新:使用蓝绿部署策略,实现零停机模型更新

更多部署详情请查看 DeepSeek 部署教程DeepSeek 使用教程

DeepSeek 模型微调常见问题

DeepSeek 模型微调需要多少数据? +
最少需要 100-200 条高质量样本,推荐 500-2000 条。数据质量比数量更重要。对于简单任务(如格式转换),500 条高质量数据即可获得明显效果。复杂任务(如专业领域问答)建议准备 2000 条以上。如果数据不足,可以先用 DeepSeek 生成初版数据,再人工审核修改。
LoRA 和全参数微调哪个更好? +
对于大多数场景,推荐 LoRA。LoRA 的效果与全参数微调差距很小(通常 < 2%),但成本低得多(显存需求降低 60-80%)。全参数微调适合需要在新领域大幅改变模型行为的场景,且需要充足的计算资源。建议先用 LoRA 快速实验,找到最优数据和超参数,再决定是否需要全参数微调。
普通电脑可以微调 DeepSeek 模型吗? +
可以。使用 QLoRA(4-bit 量化 + LoRA),带有 8GB 以上显存的消费级显卡(如 RTX 3060 12GB)即可微调 7B 模型。如果只有 CPU 或更低配置,建议使用云 GPU 服务(如 AutoDL),租用 A100 等 GPU 按小时计费,成本很低。也可以选择微调更小的模型版本(如 1.5B),显存需求仅 4GB。
微调后模型会丢失原有能力吗? +
有可能。这被称为"灾难性遗忘"(Catastrophic Forgetting)。预防方法:1)不要过度训练(epoch 不宜过多);2)混合原始通用数据与微调数据一起训练;3)使用较小的学习率;4)在训练过程中定期评估通用能力。如果发现退化,可以减少训练轮数或增加通用数据比例。
DeepSeek MoE 模型可以微调吗? +
可以。DeepSeek V2/V3 等 MoE 模型同样支持 LoRA 微调。MoE 架构的优势在于每次推理只激活部分专家,微调时可以针对性地优化特定专家。不过 MoE 模型参数量大,全参数微调门槛很高。建议使用 LoRA,并确保 target_modules 包含 MoE 特有的 gate 层。DeepSeek 官方 GitHub 仓库提供了 MoE 模型的微调示例。
微调后的模型可以商用吗? +
可以。DeepSeek 模型采用 MIT 开源协议,微调后的模型可以自由商用,无需获得额外授权。但需要注意:1)训练数据本身不能包含侵权内容;2)如果使用他人标注的数据集,需遵守该数据集的许可协议;3)建议在发布时注明基于 DeepSeek 模型微调。

DeepSeek 完整教程索引

以下是我们整理的全部 DeepSeek 相关教程,从入门到精通,一站式学习。

入门教程

DeepSeek 模型怎么用

零基础入门教程,四种方式手把手教你使用 DeepSeek 模型。

查看教程
模型下载

DeepSeek 模型下载

Ollama 一键下载、Hugging Face 原版权重、官方 GitHub 仓库。

查看下载
模型详解

DeepSeek 开源模型列表

全部开源模型规格、参数、下载链接、适用场景一览。

查看列表
部署教程

DeepSeek 部署教程

Ollama 本地部署、Docker 容器化、vLLM 高性能推理。

查看部署
架构详解

DeepSeek 模型架构

MoE 架构、MLA 注意力、多头潜在注意力等技术深度解析。

查看架构
生态工具

DeepSeek 生态工具

围绕 DeepSeek 的第三方工具、插件、集成方案汇总。

查看工具

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

完全免费,取消任意时间。我们不会发送垃圾邮件。