基于 Qwen3-0.6B 的中英选词翻译实验,覆盖数据准备、LoRA/QLoRA 监督微调、验证选模、GGUF 量化及 CPU/CUDA 本地服务。模型通过兼容 OpenAI 的 HTTP 接口为 Translator 桌面应用提供翻译。
本仓库负责模型与评测流程;桌面应用由独立的 Translator 项目维护。本项目是小规模实验原型,仍有词义和语言识别错误,尚不能替代成熟翻译服务。
词条与双向样例 → 分组划分与数据检查 → 原模型基线
↓
BF16 LoRA / NF4 QLoRA
↓
验证集选模 → 固定测试
↓
合并浮点权重 → BF16 GGUF
↓
Q8_0 / Q4_K_M
↓
CPU / CUDA 质量复测与 API 测速
↓
Translator 桌面应用
- 数据与训练:词条分组隔离、跨集合去重、聊天模板、仅答案参与 loss、EOS 与 padding 检查。
- 方案对照:固定主要训练超参数,对比 LoRA/QLoRA 和不同训练步数;根据验证集选择 checkpoint。
- 部署评测:比较量化体积、翻译质量、CPU/GPU 生成速度及完整请求延迟,保留原始响应与运行参数。
以下为本机已完成的实验,不同表格对应不同测量口径。
同一固定测试集、提示与 BF16 推理设置:
| 模型 | chrF2 | 参考匹配率 | JSON Schema 合法率 |
|---|---|---|---|
| 原始 Qwen3-0.6B | 38.88 | 0.00% | 100% |
| LoRA checkpoint-100 | 73.17 | 58.06% | 100% |
| QLoRA checkpoint-80 | 59.14 | 41.94% | 100% |
训练预算相同的100步实验中,QLoRA 峰值张量显存为 1.04 GiB,BF16 LoRA 为 1.64 GiB,降低约 36%;训练耗时分别为135.09秒和100.90秒。张量显存不等于整张GPU的总占用。这是单次对照,不代表所有模型和训练配置。
部署模型来自验证阶段选定的 LoRA100,不是 QLoRA adapter。
| GGUF 格式 | 文件体积 | CPU 测试 chrF2 | CUDA 测试 chrF2 |
|---|---|---|---|
| BF16 | 1142.68 MiB | 73.17 | 未测 |
| Q8_0 | 609.82 MiB | 73.17 | 未测 |
| Q4_K_M | 378.33 MiB | 72.33 | 69.00 |
Q4_K_M 比 BF16 文件小约 67%。CUDA 复测确认29/29层卸载到GPU;CPU/GPU有一条测试输出不同:fine-tuning 在CPU上译为“微调”,在GPU上复制原文。不能把后端切换视为答案完全等价。
同一Q4_K_M模型,20个开发输入各重复3轮;每端排除一次预热、单并发顺序请求、关闭跨请求前缀缓存,并启用JSON输出约束。
| 指标 | CPU 4线程 | CUDA |
|---|---|---|
| 生成速度中位数 | 79.59 token/s | 301.89 token/s |
| 完整请求延迟中位数 | 496.12 ms | 57.61 ms |
| 完整请求延迟 P95 | 557.56 ms | 69.40 ms |
本轮GPU中位请求延迟降低约 88.4%。生成速度来自llama.cpp内部decode计时;完整请求延迟包含HTTP往返,不包含模型启动。两者不是同一指标,也不是首token延迟或并发吞吐量。
测试硬件:Intel i7-13700H、RTX 4060 Laptop 8GB。每个后端仅测一个会话,输出为短JSON,中位输出长度13 token;不代表长文本性能。CPU/GPU有两条开发输入输出不同,GPU均有退化,不能据此声称效果提升。详见API对照和token速度统计。
{"type": "en", "translation": "配置"}type表示原文语言:ch为中文、en为英文、other为其他语言或仅数字符号。中文译成英文,英文译成简体中文;缩写和不明代码保留原样,不展开全称。
完整system prompt、严格JSON解析、数据校验和评分实现见 scripts/translation.py。提示约定不保证模型始终遵守,例如other识别仍有失败案例。
前提:已完成下方构建,并已有Q4 GGUF文件。 Git仓库不包含模型、虚拟环境、基座缓存或llama.cpp工具;新克隆不能直接运行本节命令,需先准备环境并生成或另行取得模型产物。
CPU服务:
tools/llama.cpp/build-cpu/bin/llama-server \
--model outputs/gguf/qwen3-0.6b-translator-q4_k_m.gguf \
--host 127.0.0.1 --port 8081 --ctx-size 512 --parallel 1 \
--threads 4 --threads-batch 4 --jinja --reasoning off --perf或者停止CPU服务后,在同一端口启动CUDA服务:
tools/llama.cpp/build-cuda/bin/llama-server \
--model outputs/gguf/qwen3-0.6b-translator-q4_k_m.gguf \
--host 127.0.0.1 --port 8081 --ctx-size 512 --parallel 1 \
--threads 4 --threads-batch 4 --gpu-layers 99 \
--jinja --reasoning off --verbosity 4 --perf \
> outputs/translator-gpu-server.log 2>&1保持服务终端运行,在另一个终端检查:
curl http://127.0.0.1:8081/health返回{"status":"ok"}后即可连接。接口为http://127.0.0.1:8081/v1/chat/completions。Translator选择本地后端即可调用;独立客户端需发送本仓库的system prompt、关闭思考,设置温度0、最多256新token。服务仅监听本机。
以下命令从仓库根目录执行。训练需要支持BF16的NVIDIA GPU;CPU推理无需GPU。
已验证的训练环境为Python 3.12、PyTorch 2.7.1+cu128、Transformers 4.56.2、PEFT 0.17.1、TRL 0.23.1、bitsandbytes 0.47.0。完整依赖见 requirements.lock.txt。
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python "torch==2.7.1" --index-url https://download.pytorch.org/whl/cu128
uv pip install --python .venv/bin/python -r requirements.lock.txt
uv pip check --python .venv/bin/python
source .venv/bin/activate
python -c 'from huggingface_hub import snapshot_download; snapshot_download("Qwen/Qwen3-0.6B", revision="c1899de289a04d12100db370d81485cdf75e47ca", cache_dir=".cache/huggingface/hub")'首次下载需要网络;训练与评测使用固定revision的本地缓存。
mkdir -p tools outputs/gguf
git clone https://github.com/ggml-org/llama.cpp.git tools/llama.cpp
git -C tools/llama.cpp checkout ecdd29e8b2076bb85da9e9ca7ee3d84264375bd1
cmake -S tools/llama.cpp -B tools/llama.cpp/build-cpu \
-DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=OFF \
-DLLAMA_BUILD_TESTS=OFF -DLLAMA_OPENSSL=OFF
cmake --build tools/llama.cpp/build-cpu \
--target llama-quantize llama-completion llama-server -j 4GPU版本需要独立安装CUDA Toolkit,nvidia-smi可用不代表已安装nvcc。以下为本机CUDA 12.4与GCC13的构建配置:
nvcc --version
g++-13 --version
cmake -S tools/llama.cpp -B tools/llama.cpp/build-cuda \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=/usr/bin/gcc-13 \
-DCMAKE_CXX_COMPILER=/usr/bin/g++-13 \
-DCMAKE_CUDA_HOST_COMPILER=/usr/bin/g++-13 \
-DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=89 \
-DLLAMA_BUILD_TESTS=OFF -DLLAMA_OPENSSL=OFF
cmake --build tools/llama.cpp/build-cuda \
--target llama-completion llama-server -j 2架构89对应本机RTX4060;其他GPU、CUDA与编译器组合需调整。已有失败构建缓存时,重新配置可加--fresh。CPU与CUDA构建目录相互独立。
当前数据为 data/lexicon-v3,源词条位于 data/lexicon-source/entries-v3.jsonl。
| 划分 | 词条组 | 样本数 |
|---|---|---|
| 训练 | 141 | 307 |
| 验证 | 16 | 32 |
| 测试 | 16 | 31 |
| 开发 | 10 | 20 |
同一词条及双向样例留在同一划分。已有固定数据无需重新生成,生成脚本默认拒绝覆盖。
先运行离线检查,不启动模型训练:
python scripts/check_translation.py
python scripts/train_lora.py --self-check
python scripts/merge_adapter.py --self-check
python scripts/benchmark_gguf.py --self-check
python scripts/benchmark_translation_api.py --self-check其中数据检查覆盖开发/训练/验证集的重复、答案loss mask和EOS,不读取测试集。训练配置为rank8、alpha16、all-linear、dropout0、batch1、梯度累积4、学习率1e-4、seed42、上下文512;QLoRA使用NF4、double quant和BF16计算。
python scripts/train_lora.py --max-steps 100
python scripts/train_lora.py --qlora --max-steps 100训练完成会打印新产物目录,每20步保存候选。下面路径是本次实验的历史产物,不随Git分发,重跑时须替换为实际输出:
LORA_RUN=outputs/translation-lora-r8-tune-20261014T154325724789Z
python scripts/evaluate_translation.py --dataset validation \
--adapter "$LORA_RUN/checkpoint-100"选模依次比较Schema合法率、chrF2、语言标签正确率,同分选较早步数。项目比较过训练步数与LoRA/QLoRA方案,未进行学习率或rank的系统搜索。最终选择LoRA100、QLoRA80;部署使用LoRA100。冻结选择后测试:
python scripts/evaluate_translation.py --dataset test --final-test \
--adapter "$LORA_RUN/checkpoint-100"验证loss和自由生成指标分别记录;不根据测试集结果继续调参。
在原始浮点基座上合并adapter,使用FP32检查合并前后logits,再保存BF16完整权重;不直接把adapter合入NF4基座。
python scripts/merge_adapter.py --adapter "$LORA_RUN/checkpoint-100"
# 替换为上一步打印的新目录;此处为本次实验的历史目录。
MERGED_DIR=outputs/merged-20261014T161306200263Z
python scripts/check_merged.py --model "$MERGED_DIR"使用独立CPU转换环境,避免改变训练环境依赖:
uv venv --python 3.12 .venv-gguf
uv pip install --python .venv-gguf/bin/python "torch==2.11.0" --index-url https://download.pytorch.org/whl/cpu
uv pip install --python .venv-gguf/bin/python -r docs/gguf-requirements.lock.txt
uv pip check --python .venv-gguf/bin/python
.venv-gguf/bin/python tools/llama.cpp/convert_hf_to_gguf.py \
"$MERGED_DIR" \
--outfile outputs/gguf/qwen3-0.6b-translator-bf16.gguf --outtype bf16
tools/llama.cpp/build-cpu/bin/llama-quantize \
outputs/gguf/qwen3-0.6b-translator-bf16.gguf \
outputs/gguf/qwen3-0.6b-translator-q8_0.gguf Q8_0
tools/llama.cpp/build-cpu/bin/llama-quantize \
outputs/gguf/qwen3-0.6b-translator-bf16.gguf \
outputs/gguf/qwen3-0.6b-translator-q4_k_m.gguf Q4_K_M两种量化均从BF16源文件生成,不从Q8再次量化;已有产物无需重复转换。
GGUF开发/验证评测默认使用CPU,GPU增加--gpu:
python scripts/evaluate_translation.py --dataset dev --gpu \
--gguf outputs/gguf/qwen3-0.6b-translator-q4_k_m.gguf
python scripts/evaluate_translation.py --dataset validation \
--gguf outputs/gguf/qwen3-0.6b-translator-q4_k_m.gguf--gpu选择build-cuda并要求日志确认全部层卸载,否则报错。固定测试复测使用--dataset test --final-test;这仍是原测试集,不增加独立泛化证据。CLI每次重新加载模型,进程耗时不能当作常驻API延迟。
按“快速启动”启动服务后,使用已激活的Python环境测速:
# CPU服务运行时
python scripts/benchmark_translation_api.py --backend cpu --port 8081
# 换为GPU服务后;日志必须来自正在测试的服务
python scripts/benchmark_translation_api.py --backend gpu --port 8081 \
--server-log outputs/translator-gpu-server.log两条命令分开运行,保持模型、线程数、上下文和输入一致。若同时保留两端服务,将GPU服务及测速端口改为8082。脚本输出新结果目录,保存完整响应、请求延迟和引擎timings;生成速度可从response.timings.predicted_per_second统计。GPU模式检查启动日志,CPU后端标签由操作者确保。
scripts/ 数据准备、训练、评测、合并与API测速
data/lexicon-v3/ 当前固定语料划分
data/lexicon-source/ 各版本词条源数据
data/lexicon/、lexicon-v2/ 历史翻译任务数据
docs/ 实验记录、结果摘要与依赖快照
requirements.lock.txt 训练环境依赖
outputs/ adapter、完整模型、GGUF和原始评测(忽略)
.cache/ 基座缓存(忽略)
.venv/、.venv-gguf/ Python环境(忽略)
tools/llama.cpp/ 固定版本外部工具(忽略)
Git保存代码、词条语料与docs中的结果摘要;模型和完整原始记录需另行备份。旧OPUS短句探索、已应用的Translator补丁和清理流水记录归档至本地outputs/cleanup-archive-20261018,不随Git发布。本仓库尚未提供可下载的微调权重,不能仅凭源码克隆恢复历史产物。
语料主要由助手编写,未经过独立专家审核。固定测试仅31条,分组隔离不能保证完全消除语义相近样例的影响;参考字符串匹配不等于语义准确率,chrF2也不能替代人工检查。当前仍存在多义词遗漏、术语错译和other识别错误。
量化和后端复测沿用原测试集,CPU/GPU输出也有差异。服务性能来自短输出单并发测试,尚未测量TTFT、并发吞吐量或GPU显存,也没有多seed训练和多会话性能复测。
历史记录描述的是当时实验状态,当前执行入口以本README为准。