# fine-tuning **Repository Path**: renshengedu/fine-tuning ## Basic Information - **Project Name**: fine-tuning - **Description**: 模型微调自定义报销单核验数据集仓库 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2026-10-05 - **Last Updated**: 2026-10-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # fine-tuning / preview 报销制度智能助手的**微调数据处理 + 量化 + 评测**脚本集合。 整个项目围绕三类工作: 1. **造数据**:用大模型批量生成「报销单核验」训练/校验数据(Alpaca 格式)。 2. **转格式 / 评测**:把数据转成 EvalScope 评测集,并对 OpenAI 兼容接口上的模型打分。 3. **量化**:把 Qwen2.5-7B 量化成 4bit(AWQ / GPTQ),其中 GPTQ 用报销数据做校准集。 --- ## 一、目录总览 ``` preview/ ├── .gitignore # 忽略规则(含 .env / __pycache__ / 模型产物) ├── .env.example # 环境变量模板(复制成 .env 后填自己的值) ├── .env # 本地环境变量(含 API Key,已加入 .gitignore) ├── config/ # 配置层 │ ├── env.py # 读取 .env,导出 env 单例 │ └── ai_configs.py # 各家大模型平台的配置表 ai_configs ├── utils/ # 工具层 │ └── llm_utils.py # create_llm():按 platform_code 造 chat model ├── datasets/ # 数据集与数据处理脚本 │ ├── generate_datasets_from_llm.py # 调 LLM 批量生成报销单核验数据 │ ├── to_alpaca.py # jsonl(对话格式) → Alpaca json │ ├── to_evalscope.py # jsonl(对话格式) → EvalScope jsonl │ ├── run_evalscope_evaluate.py # 启动 EvalScope 评测任务 │ ├── alpaca.json # 报销制度问答训练集(Alpaca) │ ├── export_from_eds.jsonl # 从 EDS 平台导出的原始对话数据 │ ├── evalscope.jsonl # 由 export_from_eds.jsonl 转来的评测集 │ ├── reimburses/ # LLaMA-Factory 用的报销核验数据集目录 │ │ ├── dataset_info.json # 数据集注册描述文件 │ │ ├── datasets_reimburse.json # 训练集(LLM 生成,≈1400 条) │ │ └── validates_reimburse.json # 校验集(LLM 生成,244 条) │ └── reimburses_evalscope/ # EvalScope 评测集目录 │ ├── reimburse_evalscope.py # alpaca → EvalScope jsonl 的转换脚本 │ └── reimburses_evalscope.jsonl # 报销核验评测集(244 条) └── quantization/ # 模型量化脚本 ├── run_awq.py # AWQ 4bit 量化(无需校准数据) └── run_gptq.py # GPTQ 4bit 量化(用报销数据做校准) ``` --- ## 二、文件逐一说明 ### 根目录 | 文件 | 作用 | | --- | --- | | `.gitignore` | 忽略 `.idea`、`__pycache__/`、`.venv/`、`.env`(含 API Key)、`models/`、`quantization_models/` 与模型权重后缀。 | | `.env.example` | 环境变量模板:`LLM_KEY_BAILIAN`(百炼 API Key)、`BAILIAN_API_URL`(百炼兼容模式地址)。首次使用 `cp .env.example .env` 后填自己的值。 | | `.env` | 实际生效的环境变量文件,**不入库**(`.gitignore` 已忽略)。 | ### `config/` — 配置层 | 文件 | 作用 | | --- | --- | | `env.py` | 用 `python-dotenv` 加载根目录下的 `.env`(路径基于 `__file__` 定位,任意目录启动都找得到),再用 `pydantic-settings` 的 `EnvSettings` 校验必填字段:`llm_key_bailian`、`bailian_api_url`,导出全局单例 `env`。缺任一变量会直接报校验错。 | | `ai_configs.py` | 模型注册表 `ai_configs`,把平台代号映射到 `{model, url, key}`。当前注册了 4 项:`bailian-qwen-turbo`、`bailian-qwen3-max`、`bailian-qwen3.6-plus`、`bailian-embedding`。所有 URL/Key 都从 `config.env` 取值,不在代码里硬编码。 | ### `utils/` — 工具层 | 文件 | 作用 | | --- | --- | | `llm_utils.py` | 提供 `create_llm(platform_code='bailian-qwen3.6-plus', temperature=0.5, disable_streaming=False)`:按代号从 `ai_configs` 取配置,交给 LangChain 的 `init_chat_model(model_provider="openai")` 返回一个 OpenAI 兼容的 chat model(并对 qwen 关闭思考:`extra_body={"enable_thinking": False}`)。代号不存在时抛 `Unknown platform code`。 | ### `datasets/` — 数据生成与转换 | 文件 | 作用 | | --- | --- | | `generate_datasets_from_llm.py` | **数据生产脚本**。先把项目根目录加入 `sys.path`,再 `from utils.llm_utils import create_llm` 拿到模型;调用 `create_llm("bailian-qwen3-max", temperature=0.8)` 流式生成报销单核验样本:每次让模型产出 10 条(`size=10`),循环 100 轮(`page=100`),边生成边追加写入 `datasets/reimburses/datasets_reimburse.json`(路径基于 `__file__` 定位)。Prompt 要求:约 20% 样本核验通过(输出 `Y`)、其余输出不通过原因;每条的规则须由模型现写(≥300 字)且彼此不雷同;输出必须是合法 JSON 数组。入口已加 `if __name__ == "__main__":` 保护。
⚠️ 换模型请把代号改成 `config/ai_configs.py` 里注册过的名字。 | | `to_alpaca.py` | 把 EDS 导出的对话式 jsonl(`{"messages":[{"role":"user"/"assistant",...}]}`)转成 **Alpaca 格式**:`{instruction: 用户问题, input: "", output: 助手回答}`。默认输入 `export_from_eds.jsonl` → 输出 `alpaca.json`。只保留 user/assistant 都存在的样本。 | | `to_evalscope.py` | 同上来源,但转成 **EvalScope `general_qa` 格式**:每行 `{"query": 用户问题, "response": 助手回答}`,输出为 jsonl。默认 `export_from_eds.jsonl` → `evalscope.jsonl`。 | | `run_evalscope_evaluate.py` | **评测入口**。构造 `TaskConfig`:被评模型走 OpenAI 兼容接口 `http://127.0.0.1:8000/v1/chat/completions`(即 vLLM/SGLang 起的服务),数据集为本地 `general_qa` 子集 `reimburses_evalscope`;裁判模型 `qwen2.5-72b-instruct`,其 `api_url` / `api_key` 从 `config.env` 读取(不再硬编码),`score_type='pattern'`、`JudgeStrategy.LLM`、并发 5。文件顶部会把项目根目录插入 `sys.path`,**任意目录启动均可**。 | | `alpaca.json` | 由 `export_from_eds.jsonl` 转出的训练集,**92 条**报销制度问答(住宿标准、交通/培训费用、审批流程等),回答里含 Markdown 表格与决策树。 | | `export_from_eds.jsonl` | **原始导出数据**,**92 行**,每行一个 `messages` 多轮(单轮)对话。是 `to_alpaca.py` / `to_evalscope.py` 的共同输入。 | | `evalscope.jsonl` | 由 `export_from_eds.jsonl` 转出的评测集,**92 行**,`query`/`response` 结构。 | ### `datasets/reimburses/` — LLaMA-Factory 数据集目录 | 文件 | 作用 | | --- | --- | | `dataset_info.json` | LLaMA-Factory 的**数据集注册文件**。声明了两个数据集 `datasets_reimburse` 与 `validates_reimburse`,分别指向同名 json,`formatting: alpaca`。训练时在 `dataset_info.json` 中登记后,即可用 `--dataset datasets_reimburse` 引用。 | | `datasets_reimburse.json` | **训练集**,约 **1400 条** LLM 生成的报销单核验样本。每条 `instruction` 里内嵌一整套(≥300 字、彼此不同)报销规则,`input` 是一条报销信息(金额/项目/发票类型/备注),`output` 为 `Y` 或具体不通过原因。用途:教模型「读规则 → 判合规」。 | | `validates_reimburse.json` | **校验集**,**244 条**,格式同上,样例独立于训练集(规则文案不同)。用途:训练时看 loss/泛化、`run_gptq.py` 的校准样例、以及导出 EvalScope 评测集。 | ### `datasets/reimburses_evalscope/` — EvalScope 评测集目录 | 文件 | 作用 | | --- | --- | | `reimburse_evalscope.py` | 把 Alpaca 三元组拆成 EvalScope `general_qa` 三元组:`{system: instruction, query: input, response: output}`,即**把报销规则放进 system 提示**。输入 `datasets/reimburses/validates_reimburse.json`、输出 `同目录/reimburses_evalscope.jsonl`,两端路径都基于 `__file__` 定位,**在任意目录下运行都可以**。 | | `reimburses_evalscope.jsonl` | 报销核验评测集,**244 行**,每行含 `system`(规则)+ `query`(报销信息)+ `response`(参考答案)。`run_evalscope_evaluate.py` 通过 `dataset_id` 指向本目录加载。 | ### `quantization/` — 模型量化 | 文件 | 作用 | | --- | --- | | `run_awq.py` | **AWQ 量化**。用 `AutoAWQForCausalLM` 加载 `/root/s1/models/qwen2.5-7b`,配置 `{w_bit:4, q_group_size:128, zero_point:True, version:"GEMM"}` 执行 `quantize(tokenizer, ...)`,保存到 `/root/s1/quantization_models/qwen2.5-7b-awq`。AWQ 不依赖校准数据集。 | | `run_gptq.py` | **GPTQ 量化**。用 `auto_gptq` 的 `AutoGPTQForCausalLM`,配置 `bits=4, group_size=128, desc_act=False`;读取 `datasets/reimburses/validates_reimburse.json`(路径基于 `__file__` 定位,任意目录启动均可)构造 `instruction + "\n" + input` 的校准样例并 tokenize(`max_length=512`),再 `model.quantize(examples)`,最后保存到 `/root/s1/quantization_models/qwen2.5-7b-gptq`。 | --- ## 三、微调全流程 > 说明:实线 = 仓库里确凿的调用/数据依赖;虚线 = 根据配置推断或需人工操作(训练框架、`.json` 的切分来源未在仓库中体现)。 ### 3.1 总览:每个文件在流水线里的位置 ```mermaid flowchart TB subgraph P0["⓪ 环境与配置:所有脚本的公共底座"] ENV[".env.example → .env
LLM_KEY_BAILIAN / BAILIAN_API_URL"] ENVPY["config/env.py
load_dotenv + EnvSettings → env"] AICFG["config/ai_configs.py
模型注册表 ai_configs"] LLMU["utils/llm_utils.py
create_llm(platform_code, temperature)"] ENV --> ENVPY --> AICFG --> LLMU end subgraph P1["① 数据集生成:教师模型离线产数据(黑盒蒸馏)"] T["教师模型 qwen3-max
temperature=0.8 · 百炼 API"] GEN["datasets/generate_datasets_from_llm.py
100 轮 × 10 条 · 流式拼接 JSON"] TR["datasets/reimburses/datasets_reimburse.json
【训练集】~1400 条 · Alpaca"] VA["datasets/reimburses/validates_reimburse.json
【校验集】244 条 · Alpaca"] DI["datasets/reimburses/dataset_info.json
登记 datasets_reimburse / validates_reimburse"] LLMU -.->|注入 create_llm| GEN T -->|返回 JSON 数组| GEN GEN --> TR GEN -.->|后续人工切分| VA TR --> DI VA --> DI end subgraph P2["② 另一条数据来源:EDS 平台导出(真实问答,非合成)"] EDS["datasets/export_from_eds.jsonl
92 条 messages 格式"] TA["datasets/to_alpaca.py"] TE["datasets/to_evalscope.py"] ALP["datasets/alpaca.json
【制度问答训练集】92 条"] EVJ["datasets/evalscope.jsonl
【制度问答评测集】92 条"] EDS --> TA --> ALP EDS --> TE --> EVJ end subgraph P3["③ 微调:基座 Qwen2.5-7B"] BASE["基座模型 /root/s1/models/qwen2.5-7b"] LF["LLaMA-Factory 训练(未入库,依 dataset_info 推断)
train=datasets_reimburse · valid=validates_reimburse"] CKPT["微调后权重 = 学生模型"] BASE --> LF DI --> LF VA -.->|epoch 间算验证 loss| LF ALP -.->|可选:制度问答任务| LF LF --> CKPT end subgraph P4["④ 部署与评测"] SRV["推理服务 vLLM / SGLang
http://127.0.0.1:8000/v1/chat/completions"] CONV["datasets/reimburses_evalscope/reimburse_evalscope.py
Alpaca → system/query/response"] EVSET["reimburses_evalscope/reimburses_evalscope.jsonl
【核验评测集】244 条"] RUNEV["datasets/run_evalscope_evaluate.py
ModelScope EvalScope general_qa"] JUDGE["裁判模型 qwen2.5-72b-instruct
score_type=pattern · JudgeStrategy.LLM · 并发 5"] REPORT["评测报告(打分/准确率)"] CKPT --> SRV VA --> CONV --> EVSET --> RUNEV EVJ -.->|可选:换成制度问答评测集| RUNEV RUNEV -->|被测模型=Qwen2.5-7B| SRV RUNEV --> JUDGE --> REPORT end subgraph P5["⑤ 量化上线"] AWQ["quantization/run_awq.py
w_bit=4 · group_size=128 · GEMM"] GPTQ["quantization/run_gptq.py
bits=4 · group_size=128"] QA["/root/s1/quantization_models/qwen2.5-7b-awq
4bit"] QG["/root/s1/quantization_models/qwen2.5-7b-gptq
4bit"] BASE --> AWQ --> QA BASE --> GPTQ --> QG VA -.->|作为校准样例| GPTQ end REPORT -.->|分数不达标则回到 ① 补数据/调参| GEN ``` ### 3.2 时间线:谁在什么时候做了什么 ```mermaid sequenceDiagram autonumber participant DEV as 开发机(本机) participant TEA as 教师模型 qwen3-max(百炼) participant TRAIN as 训练机 · LLaMA-Factory participant SRV as vLLM 服务 127.0.0.1 端口 8000 participant JUD as 裁判模型 qwen2.5-72b participant QUANT as 量化机 Note over DEV: ⓪ cp .env.example .env 并填入百炼 Key DEV->>TEA: ① generate_datasets_from_llm.py 流式请求 100 轮 × 10 条 TEA-->>DEV: 每轮返回一个 JSON 数组 → 累加写入 DEV->>DEV: 产出 datasets_reimburse.json(~1400 条) DEV->>DEV: 人工切出 validates_reimburse.json(244 条) DEV->>DEV: to_alpaca.py / to_evalscope.py 处理 EDS 导出的 92 条 Note over TRAIN: ② copy 数据集 + 合并 dataset_info.json TRAIN->>TRAIN: SFT 微调 Qwen2.5-7B DEV->>SRV: ③ 用微调后权重起 OpenAI 兼容服务 DEV->>DEV: reimburse_evalscope.py 生成 244 条评测集 DEV->>JUD: ④ run_evalscope_evaluate.py 提交 query + 参考答案 SRV->>SRV: 被测模型(微调后的 7B)作答 JUD-->>DEV: 裁判打分 → 评测报告 alt 分数不达标 DEV->>TEA: 回到 ①:补数据 / 改 prompt / 调参 else 达标 QUANT->>QUANT: ⑤ run_awq.py 或 run_gptq.py → 4bit 部署 end ``` ### 3.3 参与的模型一览 | 角色 | 模型 | 出处 / 被调用的地方 | | --- | --- | --- | | 教师模型(造数据) | `qwen3-max`(原 `deepseek-v3` 未注册,已改) | `generate_datasets_from_llm.py` → `utils/llm_utils.py` → `config/ai_configs.py` | | 基座 / 学生模型 | `Qwen2.5-7B`(`/root/s1/models/qwen2.5-7b`) | 训练(LLaMA-Factory,未入库);量化脚本的 `model_path` | | 被评测对象 | 同一个 7B(微调后) | `run_evalscope_evaluate.py` 的 `model='qwen2.5-7b'`、`api_url='http://127.0.0.1:8000/...'` | | 裁判模型(打分) | `qwen2.5-72b-instruct`(DashScope) | `run_evalscope_evaluate.py` 的 `judge_model_args` | | Embedding(备用) | `text-embedding-v4` | `config/ai_configs.py` 的 `bailian-embedding`(当前脚本未用到) | ### 3.4 阶段 × 时间 × 执行者对照表 | # | 阶段 | 何时执行 | 执行者 | 输入 → 产出 | | --- | --- | --- | --- | --- | | ⓪ | 配置 | 一切开始之前(一次性) | 人 | `.env.example` → `.env`;`config/env.py`、`config/ai_configs.py`、`utils/llm_utils.py` 提供 `create_llm` | | ① | 造训练集/校验集 | 每次要扩数据、改生成 prompt 时 | `generate_datasets_from_llm.py` + 教师模型 | 无中生有 → `datasets_reimburse.json`(~1400)+ 切出 `validates_reimburse.json`(244) | | ② | 另一批数据 | 拿到 EDS 导出后 | `to_alpaca.py` / `to_evalscope.py` | `export_from_eds.jsonl`(92)→ `alpaca.json` / `evalscope.jsonl` | | ③ | 注册 + 微调 | 数据定稿后 | LLaMA-Factory(仓库外) | `dataset_info.json` + 两个 json → 微调权重 | | ④ | 评测准备 | 训练完、跑评测前 | `reimburse_evalscope.py` | `validates_reimburse.json`(244)→ `reimburses_evalscope.jsonl` | | ④ | 跑评测 | 服务起来之后 | `run_evalscope_evaluate.py` + 裁判模型 | 评测集 + 被测服务 → 评测报告 | | ⑤ | 量化上线 | 评测达标后 | `run_awq.py` / `run_gptq.py` | 基座 or 权重 → 4bit 模型(GPTQ 用校验集做校准) | --- ## 四、典型使用流程 1. **生成训练数据** ```bash python generate_datasets_from_llm.py ``` 2. **配置 LLaMA-Factory**:把 `reimburses/dataset_info.json` 的内容合并到框架的 `data/dataset_info.json`,然后把 `reimburses/` 整个目录拷到 `data/` 下,训练时指定 `dataset: datasets_reimburse`、`val_dataset: validates_reimburse`。 3. **起推理服务**:用 vLLM/SGLang 把微调后的模型以 OpenAI 兼容方式起在 `127.0.0.1:8000`(与 `run_evalscope_evaluate.py` 的 `api_url` 对应)。 4. **跑评测**(需先确认 `.env` 已填 Key、且 8000 端口的服务已起) ```bash python datasets/run_evalscope_evaluate.py ``` 5. **量化部署**(Linux + GPU 环境) ```bash cd quantization python run_awq.py # 或 python run_gptq.py ``` --- ## 五、依赖环境 | 用途 | 依赖 | | --- | --- | | 配置加载 | `python-dotenv`、`pydantic-settings` | | 数据生成 | `langchain`(`langchain.chat_models.init_chat_model`)、本项目 `config/` + `utils/`;运行时需要 `.env` 里的百炼 Key | | 评测 | `evalscope`(含 `TaskConfig`、`evalscope.constants.JudgeStrategy`) | | AWQ 量化 | `autoawq`(提供 `awq.AutoAWQForCausalLM`)、`transformers` | | GPTQ 量化 | `auto-gptq`(提供 `auto_gptq`)、`transformers` | --- ## 六、已修复的问题(本次检查) | 问题 | 处理 | | --- | --- | | `from app.config.ai_configs import ai_configs`(`utils/llm_utils.py`) | 本项目没有 `app` 包,改为 `from config.ai_configs import ai_configs` | | `from app.config.env import env`(`config/ai_configs.py`) | 改为 `from config.env import env` | | `from create_llm import create_llm`(`generate_datasets_from_llm.py`) | 仓库里没有 `create_llm.py`,改为 `from utils.llm_utils import create_llm`,并先把项目根目录插入 `sys.path`,任意目录启动都能 import | | 模型代号 `deepseek-v3` 未注册 | `create_llm` 会抛 `Unknown platform code`,改用已注册的 `bailian-qwen3-max` | | 数据文件用相对路径读写 | `generate_datasets_from_llm.py`、`run_gptq.py`、`reimburse_evalscope.py` 的输入/输出路径改为基于 `__file__` 定位;目标文件不存在时按空数组处理,不再 `FileNotFoundError` | | 训练/校验集会重复写入 | `generate_datasets_from_llm.py` 里 `temp_list` 跨轮累积,导致每轮把同批数据重复追加进已有文件;改为每轮重新取、另外 count 日志加上当前轮次 | | GPTQ 与 AWQ 输出目录同名 | `run_gptq.py` 输出改为 `qwen2.5-7b-gptq`,不再覆盖 AWQ 产物 | | `.gitignore` 只有 `.idea` | 补上 `.env`、`__pycache__/`、`.venv/`、模型目录与权重后缀 | | 脚本导入即执行 | `generate_datasets_from_llm.py` 加了 `if __name__ == "__main__":` 保护 | | 评测脚本硬编码 API Key | `run_evalscope_evaluate.py` 改为从 `config.env` 读 `env.bailian_api_url` / `env.llm_key_bailian`,密钥不再进代码 | | 评测脚本 `from config.env import env` 依赖 IDE/根目录 PYTHONPATH | 与 `generate_datasets_from_llm.py` 同一处理:先把项目根目录插入 `sys.path` 再 import | --- ## 七、仍然存在的注意事项 > 以下 6 条已逐条复核核验为真(2026-10-04 复检),仍未处理。 1. **本地 `.env` 属于密钥文件,别手动 `git add -f`**。 复核结果:**它目前是安全的** —— `git ls-files` 里没有 `.env`,`git check-ignore -v .env` 命中 `.gitignore:10:.env`。它和模板 `.env.example` 的哈希不同,说明里面是**真实 Key**,所以只是不能被强制入库;换机器/换人时重新 `cp .env.example .env` 即可。 2. **模型与评测集路径写死为 Linux 绝对路径**: - `quantization/run_awq.py`:`/root/s1/models/qwen2.5-7b` → `/root/s1/quantization_models/qwen2.5-7b-awq` - `quantization/run_gptq.py`:同上 → `/root/s1/quantization_models/qwen2.5-7b-gptq` - `datasets/run_evalscope_evaluate.py`:`dataset_id: /root/s1/datasets/reimburses_evalscope` 换机器必须手动改;建议抽成 `.env` 里的常量或直接改成相对 `BASE_DIR` 的路径。 3. **`to_alpaca.py` / `to_evalscope.py` 仍是「导入即执行」,且路径依赖运行目录**:底部直接调用转换函数;输入 `export_from_eds.jsonl`、输出 `alpaca.json` / `./evalscope.jsonl` 都是相对路径,**必须在 `datasets/` 目录下运行**。建议加 `__main__` 保护并把路径改成基于 `__file__` 定位(其余脚本都已改过,只剩这两个)。 4. **`config/env.py` 用了 pydantic v2 已废弃的写法**:`Field(..., env="...")` 与 `class Config`。当前能跑但会有弃用告警;建议换 `Field(..., alias="...")` + `SettingsConfigDict`。另外它对必填字段校验很严,缺 `LLM_KEY_BAILIAN` / `BAILIAN_API_URL` 时是**直接抛校验错**,跑之前先确认 `.env` 填全。 5. **`config/`、`utils/` 没有 `__init__.py`**(已确认:全仓库 0 个 `__init__.py`)。现在靠 Python 3 命名空间包正常工作,但如果将来打包、或用严格要求包结构的工具(部分 lint / 打包器)可能要补上。 6. **训练环节本身不在仓库里**:`dataset_info.json` 只说明数据格式是 alpaca,具体的训练命令、基座路径、LoRA 参数、产出目录都看不到,无法复现。建议把训练脚本或 `yaml` 一并入库。