☰
超详细的GLM-4微调落地复盘:用TaoToken统一Key跑通Swift+LoRA全流程
2026/10/18 11:57:40 网站建设 项目流程

1. 为什么我选择在 Swift 框架下给 GLM-4 做 LoRA 微调

GLM-4 是智谱 AI 推出的新一代基座大语言模型,支持最长 128k 上下文,在中文理解、代码生成、多轮对话上都有不错的表现。但基座模型再强,落到具体业务里总会遇到“答非所问”的情况——比如你想让它按公司内部工单格式输出、想让它学会某个垂直领域的术语、想让它稳定遵循一套 JSON Schema,这时候微调就是绕不开的一步。

LoRA(Low-Rank Adaptation)是目前性价比最高的微调方式之一。它的思路很朴素:不动原始权重,只在注意力层的部分矩阵旁边挂两个小矩阵 A 和 B,训练时只更新这两个小矩阵。好处是显存占用低、训练快、产物小(通常几十到几百 MB),而且可以随时 merge 回原模型或者按需切换。对于个人开发者和小团队来说,用一张 24G 显存的卡跑 GLM-4-9B 的 LoRA 微调是完全可行的。

Swift 是魔搭社区开源的大模型训练框架,它对 GLM-4 系列的支持比较完整,命令行参数设计得也相对友好,swift sft一条命令就能拉起训练。我这次复现的链路是:环境准备 → 模型下载 → 数据集构造 → LoRA 训练 → 推理验证,中间所有需要调用外部 API 的环节(比如数据清洗时调用大模型做格式转换、训练完做效果对比)统一走 TaoToken 的 Key 和 API 通道,避免在多个平台之间来回切换凭证。

这篇文章面向的是想低成本复现 GLM-4 LoRA 微调的开发者。你不需要有分布式训练经验,只要会基本的 Linux 命令和 Python 就能跟着做。下面我会把每一步的命令、参数、踩过的坑都写清楚,代码可以直接复制。

2. TaoToken 统一 Key 的前置准备与凭证管理

在开始训练之前,先把“调用凭证”这件事理顺。微调过程中其实有好几个地方需要调用大模型 API:一是数据构造阶段,你可能要用强模型把原始语料改写成指令格式;二是训练完之后做效果对比,需要调用基座模型和微调模型分别生成结果;三是如果你用 Claude Code 或者 Cline 这类工具辅助写训练脚本,也需要一个稳定的 API 入口。

TaoToken 在这里的作用是提供一个统一的 Key 和 API 通道,把模型对话、Coding Plan、API Keys 管理都收拢到一个控制台里。你不需要为每个模型单独申请一套凭证,也不用在代码里硬编码多个平台的地址。

先到控制台创建 API Key。入口在 https://taotoken.net/console ,登录后左侧菜单找到 API Keys,点“创建新密钥”,复制出来保存好。这个 Key 后面会用在环境变量里,不要直接写进代码提交到 Git。

创建完 Key 之后,你需要确认两件事:Base URL 和 Model ID。Base URL 统一用 https://taotoken.net/api ,Model ID 根据你实际要调用的模型填,比如glm-4或者glm-4-plus。如果你不确定当前有哪些模型可用,可以到模型对话页面 https://taotoken.net/model-chat 里试一下,那里会列出可选的模型列表。

对于长期做编码和 Agent 任务的场景,可以了解一下 Coding Plan,入口在 https://taotoken.net/coding-plan 。它适合那种需要持续调用模型、按量计费不太划算的情况。不过这篇文章的重点是微调,Coding Plan 只是顺带提一句,你按需选择就行。

环境变量建议这样设置,写进~/.bashrc或者训练脚本的头部:

export TAOTOKEN_API_KEY="sk-你的密钥" export TAOTOKEN_BASE_URL="https://taotoken.net/api" export TAOTOKEN_MODEL="glm-4"

设置完之后source ~/.bashrc生效。验证一下能不能读到:

echo $TAOTOKEN_API_KEY echo $TAOTOKEN_BASE_URL

如果输出正常,说明凭证已经就位。接下来所有需要调用 API 的地方,都从这两个环境变量里取,不要散落在各个脚本里。

有一点要注意:TaoToken 是合规的 API 聚合通道,不是那种来路不明的中转。你在控制台里能看到每个 Key 的调用记录和用量,方便排查问题。如果遇到 401 报错,先检查 Key 有没有复制完整、有没有多余空格,再去控制台确认 Key 的状态是否正常。

3. Swift + LoRA 的可复制配置与训练脚本

这一节是全文的核心,我会把环境安装、模型下载、数据集格式、训练命令、推理验证全部串起来。你按顺序执行就行。

3.1 环境安装与依赖

先克隆 Swift 仓库并安装。建议用 Python 3.10 或 3.11,太新的版本有些依赖还没跟上。

git clone https://github.com/modelscope/swift.git cd swift pip install -e '.[llm]'

安装完之后验证一下:

swift --help

如果能看到一堆子命令(sft、infer、export 等),说明装好了。如果报错找不到命令,检查一下 pip 的 bin 目录有没有加到 PATH 里。

3.2 下载 GLM-4-9B-Chat 模型

用 modelscope 的命令行工具下载,先装一下:

pip install modelscope

然后下载模型:

git clone https://www.modelscope.cn/ZhipuAI/glm-4-9b-chat.git

如果 git clone 太慢,可以用 modelscope 的 snapshot_download:

from modelscope import snapshot_download model_dir = snapshot_download('ZhipuAI/glm-4-9b-chat') print(model_dir)

下载完之后确认目录里有config.json、tokenizer.json这些文件。模型大概 18G 左右,预留好磁盘空间。

3.3 数据集格式与构造

Swift 支持多种数据集格式,最常用的是 JSONL。我这次用的是单轮对话格式,每行一个 JSON 对象,包含system、query、response三个字段:

{"system": "你是一个工单分类助手", "query": "用户反馈登录后页面白屏", "response": "分类:前端渲染异常;建议:检查控制台报错并回滚最近一次发布"} {"system": "你是一个工单分类助手", "query": "支付成功但订单状态未更新", "response": "分类:订单状态同步延迟;建议:核对支付回调日志并手动补偿"}

如果你手头只有原始文本,可以用 TaoToken 的 API 批量改写成这个格式。写个小脚本:

import os import json import requests API_KEY = os.environ["TAOTOKEN_API_KEY"] BASE_URL = os.environ["TAOTOKEN_BASE_URL"] MODEL = os.environ["TAOTOKEN_MODEL"] def rewrite(text): resp = requests.post( f"{BASE_URL}/v1/chat/completions", headers={"Authorization": f"Bearer {API_KEY}"}, json={ "model": MODEL, "messages": [ {"role": "system", "content": "把用户输入改写成工单分类的训练样本,输出JSON,包含system、query、response三个字段。"}, {"role": "user", "content": text} ], "temperature": 0.3 }, timeout=60 ) return resp.json()["choices"][0]["message"]["content"] raw = ["登录后白屏", "支付成功订单未更新"] with open("train.jsonl", "w", encoding="utf-8") as f: for item in raw: f.write(rewrite(item) + "\n")

注意choices字段的读取,如果返回结构不对,先打印完整响应看看。这一步的 API 调用走的就是 TaoToken 的通道,Key 从环境变量取。

数据集准备好之后,放到data/train.jsonl,训练时用--dataset参数指定路径。

3.4 LoRA 训练命令

这是最关键的一步。下面这条命令可以直接复制,改一下模型路径和数据集路径就行:

CUDA_VISIBLE_DEVICES=0 swift sft \ --model_type glm4-9b-chat \ --model_id_or_path ./glm-4-9b-chat \ --dataset ./data/train.jsonl \ --lora_target_modules ALL \ --lora_rank 8 \ --lora_alpha 32 \ --lora_dropout 0.05 \ --learning_rate 1e-4 \ --num_train_epochs 3 \ --per_device_train_batch_size 1 \ --gradient_accumulation_steps 8 \ --max_length 2048 \ --output_dir ./output/glm4-lora \ --logging_steps 10 \ --save_steps 100 \ --gradient_checkpointing true \ --torch_dtype bfloat16

几个参数解释一下:lora_rank和lora_alpha控制 LoRA 的容量,rank 越大容量越大但显存也越高,8 和 32 是比较稳的组合;per_device_train_batch_size设 1 配合gradient_accumulation_steps8,等效 batch size 是 8,24G 卡能跑;gradient_checkpointing开起来省显存,代价是慢一点;max_length2048 对大多数指令数据够用,如果你的样本很长可以调到 4096。

如果你想用多卡,把CUDA_VISIBLE_DEVICES改成0,1,然后加--ddp_find_unused_parameters true。不过 LoRA 微调单卡通常就够了,多卡收益不明显。

训练开始后你会看到 loss 逐步下降。正常情况下前几十步 loss 会从 2.x 降到 1.x,然后缓慢收敛。如果 loss 一直不降,检查学习率是不是太小、数据格式是不是有问题。

3.5 推理验证与 merge LoRA

训练完 checkpoint 在./output/glm4-lora下面。先直接推理看看效果:

CUDA_VISIBLE_DEVICES=0 swift infer \ --ckpt_dir ./output/glm4-lora \ --load_dataset_config true

这会进入交互模式,你可以手动输入 query 看输出。如果想批量验证,可以指定一个测试集:

CUDA_VISIBLE_DEVICES=0 swift infer \ --ckpt_dir ./output/glm4-lora \ --load_dataset_config true \ --val_dataset ./data/test.jsonl

如果效果满意,可以把 LoRA 权重 merge 回原模型,得到一个独立的模型目录:

CUDA_VISIBLE_DEVICES=0 swift export \ --ckpt_dir ./output/glm4-lora \ --merge_lora true

merge 完之后再用swift infer指向 merged 目录验证一次,确认输出一致。

4. 验证请求与成功结果:loss 曲线与样例输出

训练跑完之后,怎么判断微调是真的生效了?我一般看三个东西:loss 曲线、样例输出对比、以及 API 调用是否正常。

loss 曲线在output/glm4-lora下面会有trainer_state.json,里面记录了每一步的 loss。你可以用 Python 画一下:

import json import matplotlib.pyplot as plt with open("output/glm4-lora/trainer_state.json") as f: state = json.load(f) steps = [x["step"] for x in state["log_history"] if "loss" in x] losses = [x["loss"] for x in state["log_history"] if "loss" in x] plt.plot(steps, losses) plt.xlabel("step") plt.ylabel("loss") plt.savefig("loss_curve.png")

正常的曲线应该是前期快速下降,后期趋于平缓。如果曲线震荡很厉害,可能是学习率太大或者 batch size 太小;如果一直平着不降,检查数据里 query 和 response 是不是反了。

样例输出对比更直观。我拿同一个 query 分别喂给基座模型和微调后的模型:

import os import requests API_KEY = os.environ["TAOTOKEN_API_KEY"] BASE_URL = os.environ["TAOTOKEN_BASE_URL"] def ask(model, query): resp = requests.post( f"{BASE_URL}/v1/chat/completions", headers={"Authorization": f"Bearer {API_KEY}"}, json={ "model": model, "messages": [{"role": "user", "content": query}], "temperature": 0.1 }, timeout=60 ) return resp.json()["choices"][0]["message"]["content"] query = "用户反馈登录后页面白屏" print("基座模型:", ask("glm-4", query)) print("微调模型:", ask("glm-4-lora", query))

注意这里的glm-4-lora是你部署后的模型名,如果你是把 merge 后的模型本地起服务,就把 Base URL 换成你本地的地址。如果走 TaoToken 的通道调用,需要确认你的微调模型有没有注册上去。

实测下来,微调后的模型在工单分类这个任务上,输出格式的稳定性明显好于基座模型。基座模型有时候会自由发挥,加一堆解释;微调后的模型基本能稳定输出“分类:xxx;建议:xxx”的格式。

如果你在验证阶段遇到reading choices相关的报错,通常是返回结构里没有choices字段,先打印完整响应看看是不是被限流或者 Key 失效了。

5. 本篇常见错误排查:401、local proxy failed、OAuth 与 choices 读取

微调链路上容易踩的坑其实就那么几个,我按报错类型整理一下。

401 Unauthorized:这个最常见。先检查TAOTOKEN_API_KEY环境变量有没有设置、有没有多余空格。然后去控制台确认 Key 的状态,是不是被禁用或者额度用完了。如果是在脚本里硬编码的 Key,检查有没有被截断。还有一种情况是 Base URL 写错了,比如漏了/api或者多了斜杠,都会导致鉴权失败。

local proxy failed:这个报错通常出现在你本地起了代理但配置不对的时候。如果你没有主动设置代理,检查一下HTTP_PROXY和HTTPS_PROXY环境变量是不是被其他工具污染了。用env | grep -i proxy看一下,如果有不需要的代理设置,unset掉再重试。TaoToken 的 API 通道本身不需要额外代理配置,直接访问就行。

OAuth 相关报错:如果你在用 Claude Code 或者 Cline 这类工具,可能会遇到 OAuth 认证失败。这类工具通常需要你配置 Base URL、API Key、Model ID 三件套。以 Claude Code 为例,配置文件里要写清楚:

{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的密钥", "model": "glm-4" }

Cline 的 MCP 配置类似,在 settings 里填 Base URL 和 Key,Model ID 选对应的模型。如果 OAuth 流程走不通,检查一下是不是把 Base URL 写成了首页地址而不是 API 地址。

reading choices 报错:这个报错说明代码在解析响应时找不到choices字段。先打印完整响应:

resp = requests.post(...) print(resp.status_code) print(resp.text)

常见原因有三个:一是 Key 失效返回了错误信息;二是模型名写错了,服务端返回了错误;三是请求体格式不对,比如messages字段拼写错误。确认响应结构之后再改代码。

训练时 CUDA out of memory:显存不够。先把per_device_train_batch_size降到 1,gradient_accumulation_steps调大,开gradient_checkpointing,max_length降到 1024。如果还不够,把lora_rank降到 4。24G 卡跑 GLM-4-9B 的 LoRA 是够的,如果还 OOM 检查是不是有其他进程占着显存。

数据集格式报错:Swift 对数据格式比较敏感。JSONL 每行必须是合法的 JSON,不能有多余逗号,不能有 BOM 头。用python -c "import json; [json.loads(l) for l in open('train.jsonl')]"验证一下。字段名必须是query和response,写成input和output会报错。

6. 长期编码与 Agent 场景下的凭证管理建议

微调只是整个链路的一环。如果你后续还要做模型部署、Agent 编排、持续迭代,凭证管理会变成一个越来越烦的问题。我的建议是:所有需要调用大模型 API 的地方,统一走 TaoToken 的 Key 和通道,不要每个工具单独配一套。

具体做法是:在服务器上设置全局环境变量,所有脚本从环境变量读取。如果你用 Docker,把环境变量写进docker-compose.yml的environment段。如果你用 CI/CD,把 Key 存到 Secrets 里,构建时注入。

对于长期跑编码任务的场景,可以看看 Coding Plan,入口在 https://taotoken.net/coding-plan 。它适合那种每天都要调用模型、按量计费波动比较大的情况。不过如果你只是偶尔跑一下微调验证,按量计费就够了。

API Keys 的管理入口在 https://taotoken.net/api-keys ,你可以给不同的项目创建不同的 Key,方便追踪用量和随时吊销。接入文档在 https://taotoken.net/doc ,里面有各个语言和工具的接入示例,遇到问题可以先翻文档。

模型对话页面 https://taotoken.net/model-chat 可以用来快速验证模型是否可用,不用写代码就能试。Claude Code 的接入配置可以参考 https://taotoken.net/claude-code ,里面有完整的 Base URL、Key、Model ID 三件套配置说明。

最后说一个实际经验:微调完的模型如果要在生产环境用,建议先做一轮 A/B 测试,用真实流量对比基座模型和微调模型的输出质量。不要只看 loss 曲线就上线,loss 低不代表业务效果好。测试的时候把两边的输出都记录下来,人工抽检一批,确认微调确实解决了你要解决的问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询