Hermes 微调实操:用自己的数据训练一个专属模型

手把手教你微调 Hermes 模型,从数据准备到 LoRA 训练到合并导出,覆盖 Axolotl 和 LLaMA-Factory 两条工具链。

目录

  1. 什么时候该微调
  2. 准备工作
  3. 数据格式:ChatML 是关键
  4. 数据质量比数量重要得多
  5. 用 LLaMA-Factory 微调(推荐新手)
  6. 用 Axolotl 微调(更灵活)
  7. QLoRA:显存不够时的救星
  8. 合并和导出
  9. 训练过程的监控和调试
  10. 微调的常见坑

什么时候该微调

先泼一盆冷水:大部分场景不需要微调

如果你的需求是让模型掌握特定领域的知识(比如公司产品资料),用 RAG 方案 就够了。如果你的需求是让模型按特定格式输出,好好写 System Prompt 通常能解决。

微调真正有价值的场景是:

  1. 风格/人设固化 — 你想让模型说话方式固定为某种风格,不依赖复杂的 prompt
  2. 特定任务优化 — 模型在某个具体任务上表现不够好,需要用高质量数据专门提升
  3. 推理效率 — 通过微调把原本需要长 prompt 引导的行为”烧”进模型权重,减少每次推理的 token 消耗
  4. 指令遵循强化 — 让模型更好地遵循你的自定义指令格式

如果你属于上面某种情况,那继续往下看。

准备工作

硬件要求(以 Hermes-3 8B 为例):

微调方式 最低显存 推荐显卡
LoRA / QLoRA 12-16 GB RTX 3090 / RTX 4090
全参数微调 80+ GB A100 80GB

个人玩家请直接上 LoRA 或 QLoRA,全参数微调是有集群资源的团队才需要考虑的。

软件环境:

1
2
3
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install transformers datasets accelerate peft bitsandbytes
pip install wandb # 训练过程可视化(可选但强烈推荐)

数据格式:ChatML 是关键

Hermes 系列模型使用 ChatML 格式(Chat Markup Language)。你的训练数据必须符合这个格式,否则微调效果会很差。

ChatML 的结构长这样:

1
2
3
4
5
6
<|im_start|>system
你是一个专业的法律顾问。<|im_end|>
<|im_start|>user
劳动合同到期不续签,公司需要赔偿吗?<|im_end|>
<|im_start|>assistant
根据《劳动合同法》第四十六条的规定...<|im_end|>

对应到 JSON 训练数据格式:

1
2
3
4
5
6
7
8
9
[
{
"conversations": [
{"role": "system", "content": "你是一个专业的法律顾问。"},
{"role": "user", "content": "劳动合同到期不续签,公司需要赔偿吗?"},
{"role": "assistant", "content": "根据《劳动合同法》第四十六条的规定,劳动合同期满后,用人单位不续签的,应当向劳动者支付经济补偿。补偿标准为每满一年支付一个月工资。不满六个月的,支付半个月工资。"}
]
}
]

数据质量比数量重要得多

微调的一个常见误区是”数据越多越好”。实际上,500 条高质量数据的效果往往好于 5000 条低质量数据

高质量数据的标准:

  1. 答案准确 — 错误的数据会让模型学到错误的知识
  2. 多样化 — 覆盖你目标场景的各种变体
  3. 长度适中 — 回答不要太短也不要太长
  4. 格式一致 — 如果你想让模型输出 JSON,训练数据里的 assistant 回复都应该是 JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import json

def validate_dataset(filepath: str) -> dict:
"""校验训练数据格式"""
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)

stats = {"total": len(data), "valid": 0, "invalid": 0, "issues": []}

for i, item in enumerate(data):
conversations = item.get("conversations", [])
if not conversations:
stats["issues"].append(f"第{i}条: 缺少conversations字段")
stats["invalid"] += 1
continue

roles = [c["role"] for c in conversations]
if roles[0] == "system":
roles = roles[1:]

expected = ["user", "assistant"] * (len(roles) // 2)
if roles != expected[:len(roles)]:
stats["issues"].append(f"第{i}条: 角色顺序不正确")
stats["invalid"] += 1
continue

has_empty = any(not c.get("content", "").strip() for c in conversations)
if has_empty:
stats["issues"].append(f"第{i}条: 存在空内容")
stats["invalid"] += 1
continue

stats["valid"] += 1

return stats

用 LLaMA-Factory 微调(推荐新手)

LLaMA-Factory 是目前最易用的微调工具,支持 Web UI 操作,中文文档齐全。

1
2
3
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"

LoRA 微调配置文件(train_hermes.yaml):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
### 模型配置
model_name_or_path: NousResearch/Hermes-3-Llama-3.1-8B
template: chatml

### 训练方式
stage: sft
do_train: true
finetuning_type: lora

### LoRA 配置
lora_rank: 64
lora_alpha: 128
lora_dropout: 0.05
lora_target: all

### 数据配置
dataset: my_legal_data
cutoff_len: 2048
preprocessing_num_workers: 8

### 训练参数
output_dir: ./output/hermes3-legal-lora
num_train_epochs: 3
per_device_train_batch_size: 4
gradient_accumulation_steps: 4
learning_rate: 2.0e-4
lr_scheduler_type: cosine
warmup_ratio: 0.1
max_grad_norm: 1.0
bf16: true

### 报告
report_to: wandb
1
llamafactory-cli train train_hermes.yaml

关键参数解释:

  • lora_rank: 64 — LoRA 的秩,越大表达能力越强但训练越慢
  • lora_alpha: 128 — 通常设为 rank 的 2 倍
  • learning_rate: 2e-4 — LoRA 微调的学习率通常比全参数微调高一个数量级
  • num_train_epochs: 3 — 数据量小(<1000条)3-5 个 epoch,数据量大(>5000条)1-2 个

用 Axolotl 微调(更灵活)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
base_model: NousResearch/Hermes-3-Llama-3.1-8B
model_type: LlamaForCausalLM

datasets:
- path: ./my_training_data.json
type: sharegpt
conversation: chatml

chat_template: chatml
adapter: lora
lora_r: 64
lora_alpha: 128
lora_dropout: 0.05
lora_target_linear: true

sequence_len: 2048
sample_packing: true
micro_batch_size: 4
gradient_accumulation_steps: 4
num_epochs: 3
learning_rate: 2e-4
optimizer: adamw_torch
lr_scheduler: cosine
warmup_ratio: 0.1
bf16: auto
flash_attention: true
output_dir: ./output/hermes3-lora
1
accelerate launch -m axolotl.cli.train hermes_lora.yml

QLoRA:显存不够时的救星

QLoRA 先把基座模型量化到 4-bit(只读),然后在上面加 LoRA adapter 做训练。显存占用大幅降低——8B 模型在一张 12GB 的 RTX 3060 上就能训练。

在 LLaMA-Factory 里只需改一个参数:

1
2
quantization_bit: 4
quantization_method: bitsandbytes

合并和导出

训练完成后,LoRA adapter 是一个独立的小文件。你可以直接用 adapter 做推理,也可以合并进基座模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

base_model = AutoModelForCausalLM.from_pretrained(
"NousResearch/Hermes-3-Llama-3.1-8B",
torch_dtype="auto", device_map="auto"
)
model = PeftModel.from_pretrained(base_model, "./output/hermes3-legal-lora")
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./hermes3-legal-merged")

tokenizer = AutoTokenizer.from_pretrained("NousResearch/Hermes-3-Llama-3.1-8B")
tokenizer.save_pretrained("./hermes3-legal-merged")

合并后的模型就是一个标准的 HF 模型,可以直接转成 GGUF 做 量化 和部署。

训练过程的监控和调试

1. Loss 曲线

正常的 loss 曲线应该是:前期快速下降 -> 中期缓慢下降 -> 后期趋于平稳。

  • loss 不下降:学习率可能太小,或数据格式有问题
  • loss 下降后又上升:过拟合了,减少 epoch 数或增加数据量
  • loss 剧烈波动:学习率太大或 batch size 太小

2. 过拟合检测

微调很容易过拟合,尤其是数据量小的时候。建议留出 10-20% 的数据做验证集:

1
2
val_size: 0.1
eval_steps: 50

如果训练 loss 持续下降但验证 loss 开始上升,就该停止了。

3. 训练后快速验证

1
2
3
4
5
6
7
8
9
10
test_messages = [
{"role": "system", "content": "你是一个专业的法律顾问。"},
{"role": "user", "content": "公司能因为员工怀孕而辞退吗?"}
]

input_text = tokenizer.apply_chat_template(test_messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(input_text, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=512, temperature=0.3, do_sample=True)
response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
print(response)

微调的常见坑

1. 模板不匹配 — Hermes 用 ChatML 模板,训练代码必须用同样的模板。用了错误的模板效果急剧下降。

2. 学习率设太高 — LoRA 微调建议 1e-5 到 5e-4。设太高模型容易”忘掉”基座能力。

3. 数据泄露 — 确保训练数据和测试数据没有重叠。

4. 只看 loss 不看实际输出 — loss 低不代表模型好用。一定要用真实场景的问题做定性测试。

微调是一个需要反复实验的过程。从小数据集开始试,确认流程跑通后再逐步加大数据量和训练时长。如果你在过程中遇到问题,cocoloop 社区里有不少微调经验丰富的同学可以交流。

参与讨论

对这篇文章有疑问或想法?cocoloop 社区有不少开发者在讨论 Hermes 相关话题,欢迎加入交流。

前往 cocoloop 社区 →