首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从 0 到 1 开发轻量化私有大模型:选型、微调、量化、部署

从 0 到 1 开发轻量化私有大模型:选型、微调、量化、部署

原创
作者头像
IT大佬 jzit-top
发布于 2026-10-22 15:14:26
发布于 2026-10-22 15:14:26
70
举报

“私有”意味着数据不出内网,“轻量”意味着单卡或消费级显卡能跑。这条路不需要从头预训练,正确姿势是:选一个 1B–8B 的开源基座 → QLoRA 微调注入领域能力 → 量化压缩 → 本地推理服务 → 评估闭环。下面给出每一环的可运行代码。

一、选基座与环境

选型三原则:许可证可商用(Apache-2.0 优先)、中文能力够用、生态工具齐(transformers / llama.cpp 都支持)。常见候选:Qwen2.5-1.5B/7B、Llama-3.1-8B、MiniCPM。显存预算:7B 模型 QLoRA 微调约需 16–24GB,1.5B 只需 8GB。

代码语言:javascript
复制
pip install torch transformers peft bitsandbytes datasets accelerate trl
# 量化与部署
pip install llama-cpp-python vllm fastapi uvicorn

二、数据工程:决定上限

私有大模型的价值全在数据。指令数据统一成 {"instruction", "input", "output"},用基座自带的 chat template 拼接,不要自己手写特殊 token。

代码语言:javascript
复制
from datasets import load_dataset

def to_chat(example, tokenizer):
    msgs = [
        {"role": "system", "content": "你是公司内部技术助手,只依据给定资料回答。"},
        {"role": "user", "content": example["instruction"] + "\n" + example.get("input", "")},
        {"role": "assistant", "content": example["output"]},
    ]
    text = tokenizer.apply_chat_template(msgs, tokenize=False)
    ids = tokenizer(text, truncation=True, max_length=2048)
    # 只对 assistant 部分算 loss:把 prompt 段 label 置 -100
    prompt_ids = tokenizer(
        tokenizer.apply_chat_template(msgs[:-1], tokenize=False, add_generation_prompt=True)
    )["input_ids"]
    ids["labels"] = [-100] * len(prompt_ids) + ids["input_ids"][len(prompt_ids):]
    return ids

数据量不必大:500–3000 条高质量样本,远胜 10 万条噪声。务必留 10% 做评估集。

三、QLoRA 微调

4-bit 量化基座 + 低秩适配器,只训练 1% 左右的参数,单卡可跑。

代码语言:javascript
复制
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from trl import SFTTrainer, SFTConfig

BASE = "Qwen/Qwen2.5-1.5B-Instruct"
tok = AutoTokenizer.from_pretrained(BASE, trust_remote_code=True)

bnb = BitsAndBytesConfig(
    load_in_4bit=True, bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
    BASE, quantization_config=bnb, device_map="auto", trust_remote_code=True,
)
model = prepare_model_for_kbit_training(model)

lora = LoraConfig(
    r=16, lora_alpha=32, lora_dropout=0.05, bias="none", task_type="CAUSAL_LM",
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
)
model = get_peft_model(model, lora)
model.print_trainable_parameters()   # 约 1%

ds = load_dataset("json", data_files="data/train.jsonl")["train"]
ds = ds.map(lambda x: to_chat(x, tok), remove_columns=ds.column_names)

trainer = SFTTrainer(
    model=model, train_dataset=ds,
    args=SFTConfig(
        output_dir="out/lora", num_train_epochs=3,
        per_device_train_batch_size=2, gradient_accumulation_steps=8,
        learning_rate=2e-4, lr_scheduler_type="cosine", warmup_ratio=0.03,
        logging_steps=10, save_strategy="epoch", bf16=True,
        gradient_checkpointing=True, max_length=2048,
    ),
)
trainer.train()
model.save_pretrained("out/lora"); tok.save_pretrained("out/lora")

超参经验:学习率 1e-4~2e-4、r=8~32、epoch 2~3。过拟合表现为评估集 loss 回升、回答开始复读训练原句。

四、合并与量化

部署前把 LoRA 合并回基座,再转 GGUF 做 4-bit 量化,体积可压到原模型的 1/4。

代码语言:javascript
复制
from peft import PeftModel
from transformers import AutoModelForCausalLM

base = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype=torch.bfloat16)
merged = PeftModel.from_pretrained(base, "out/lora").merge_and_unload()
merged.save_pretrained("out/merged"); tok.save_pretrained("out/merged")
代码语言:javascript
复制
# 转 GGUF 并量化(llama.cpp)
python convert_hf_to_gguf.py out/merged --outfile out/model-f16.gguf
./llama-quantize out/model-f16.gguf out/model-q4_k_m.gguf Q4_K_M

Q4_K_M 是质量/体积的平衡点;若对精度敏感,用 Q5_K_M 或 8-bit。

五、私有化推理服务

轻量场景用 llama.cpp 起 OpenAI 兼容服务,内网零依赖;高并发换 vLLM。

代码语言:javascript
复制
python -m llama_cpp.server --model out/model-q4_k_m.gguf \
  --host 0.0.0.0 --port 8000 --n_ctx 4096 --n_gpu_layers -1 \
  --chat_format qwen
代码语言:javascript
复制
from openai import OpenAI
cli = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="none")

def ask(q: str) -> str:
    r = cli.chat.completions.create(
        model="local",
        messages=[{"role": "system", "content": "你是公司内部技术助手。"},
                  {"role": "user", "content": q}],
        temperature=0.2, max_tokens=512,
    )
    return r.choices[0].message.content

外网隔离时它就是一台内网 API;配合 FastAPI 加鉴权、限流、日志即可交付。

六、评估闭环

微调后必须量化对比:基座 vs 微调后,在留出集上人工打分 + 规则校验。

代码语言:javascript
复制
def score(pred, ref):
    # 简易指标:关键词命中 + 长度合理 + 无复读
    hit = sum(k in pred for k in ref["keys"]) / max(len(ref["keys"]), 1)
    rep = 1 - pred.count(pred[:20]) / max(len(pred) // 20, 1)  # 复读惩罚
    return 0.7 * hit + 0.3 * max(rep, 0)

base_avg = sum(score(ask_base(c["q"]), c["ref"]) for c in cases) / len(cases)
ft_avg   = sum(score(ask(c["q"]),      c["ref"]) for c in cases) / len(cases)
print(f"base={base_avg:.3f} finetuned={ft_avg:.3f}")

同时记录首 token 延迟、吞吐(tokens/s)、显存占用——私有部署的验收标准是“业务可用 + 成本可算”。

七、常见坑

数据格式不跟 chat template 对齐导致效果暴跌;全参数微调硬上消费卡;量化后不重测;只测“看起来对”不做留出集;忽略 -100 掩码导致模型学会复述问题。记住:从 0 到 1 的难点从来不是训练脚本,而是数据质量与评估纪律。把这两件做扎实,1.5B 的小模型在内网场景完全可以替代通用大模型。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、选基座与环境
  • 二、数据工程:决定上限
  • 三、QLoRA 微调
  • 四、合并与量化
  • 五、私有化推理服务
  • 六、评估闭环
  • 七、常见坑
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档