在3DN,我们已经在本地工作站GPU(RTX 3090)上运行本地推理,用于家庭网站机器翻译和混合AI工程。大量的EN→NL和EN→ZH草稿通过Ollama的Tower进行;泰语一直是薄弱环节,所以今天我们依赖Typhoon进行TH。下一步不是“购买更多的API”——而是在我们自己的屋檐下,在相同的基础设施上进行有意的LoRA实验,以实现数字主权和降低长期的token成本。
这篇文章是公共计划:我们训练什么,为什么选择那个基础模型,我们如何保持生产安全,以及Unbabel的工作如何融入其中。下面的片段是实验室的形状——不是一个粘贴和遗忘的食谱。
为什么要有LoRA?
对数十亿参数的翻译模型进行全精调很昂贵且容易过拟合。LoRA(低秩适配器)在冻结的基础上训练一组薄矩阵。对于已经托管Tower权重的桌面来说,这是正确的学习曲线:可衡量的EN→TH收益,一个我们可以发布的Hugging Face适配器,以及如果我们想要一个专门的Ollama插头,稍后可以选择合并。
我们不训练Ollama日常使用的GGUF blob。训练使用Hugging Face / PEFT风格的权重;服务可以保持在当前的Tower-Plus Q4路径,直到评估结果另行通知。
# 精神模型:冻结基础,训练薄适配器
# ΔW ≈ B @ A with rank r << d_model
# 只有A、B(和可能偏置)获得梯度。
from peft import LoraConfig, get_peft_model, TaskType
lora = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"], # 小规模开始;如果欠拟合则扩展
lora_dropout=0.05,
bias="none",
task_type=TaskType.CAUSAL_LM,
)
# model = get_peft_model(base_model, lora)
# model.print_trainable_parameters() # 预期可训练的比例为~0.1–1%
基础模型:TowerInstruct-7B v0.2
社区指导(和我们自己的阅读)指出从TowerInstruct开始,而不是一个通用的聊天LLM。这个系列是为翻译形状的提示构建的——与我们已经在桌面上的相同模式。
我们正在安装Unbabel/TowerInstruct-7B-v0.2作为第一个训练基础:
- 7B适合QLoRA在台式机3090上舒适运行,有迭代的空间。
- v0.2比v0.1更干净的instruct/MT行为。
- 13B或Tower-Plus-9B只有在7B配方有效后才考虑——更多容量,更慢的循环。
Unbabel 的 Tower 产品线可在 unbabel.com 查看。公共权重:Hugging Face Unbabel/TowerInstruct-7B-v0.2。
# 在本地工作站上的实验室布局(HF 权重,不是 Ollama GGUF)
# .../tower-lora/
# modelsTowerInstruct-7B-v0.2 # safetensors 分片
# datasetsen-th # jsonl 对
# adaptersen-th-r16 # PEFT 输出
# out
# 在构建主机上下载阶段,然后通过局域网复制(多 GB,稍后离线训练):
python -c "from huggingface_hub import snapshot_download;
snapshot_download('Unbabel/TowerInstruct-7B-v0.2',
local_dir='/var/tmp/hf-models/TowerInstruct-7B-v0.2')"
数据形状(程序员关心模式)
一行,一个任务。与我们已经在生产 MT 中使用的提示风格相匹配,以便训练≈服务:
{
"instruction": "将以下英文源文本翻译成泰语。",
"input": "Every voter deserves a market.",
"output": "ผู้มีสิทธิเลือกตั้งทุกคนสมควรได้รับตลาด"
}
# Tiny loader sketch — holdout 是神圣的
import json
from pathlib import Path
def load_jsonl(path: Path):
rows = []
for line in path.read_text(encoding="utf-8").splitlines():
if line.strip():
rows.append(json.loads(line))
return rows
def split_holdout(rows, frac=0.1, seed=7):
import random
rng = random.Random(seed)
idx = list(range(len(rows)))
rng.shuffle(idx)
n = max(1, int(len(rows) * frac))
hold = {idx[i] for i in range(n)}
train = [rows[i] for i in idx if i not in hold]
test = [rows[i] for i in idx if i in hold]
return train, test
# 优先:我们的 EN 桌面副本 + Typhoon 草稿 + 轻量级人工黄金编辑
# 避免:未编辑的大批量 MT 汤,HTML 碎片,混合双语行
我们如何进行工作
- 在本地工作站上安装 HF 基础 —
.../tower-lora/models/下的完整 safetensors 树(下载阶段在我们的构建主机上进行,然后通过局域网复制)。 - 数据优先于排名 — 几百到几千个干净的 EN→TH 对;永远保留 ~10%。
- 训练 QLoRA — 排名 8–16,短上下文,一次在 3090 上进行一个繁重任务(卸载竞争的 Ollama 模型)。
- 诚实评估 — 基于 Tower vs LoRA(以及稍后的 Typhoon);chrF/BLEU 作为指南,人类用于名称和产品术语。第一个保留表在上面。
- 发布获胜时 — Hugging Face 适配器卡;只有那时才合并→GGUF / 可选的
local workstation-tower-th标记。
# QLoRA 加载草图(4位基础 + LoRA)——适合 3090
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch
bnb = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
base_id = r"…/tower-lora/modelsTowerInstruct-7B-v0.2"
tok = AutoTokenizer.from_pretrained(base_id, use_fast=True)
model = AutoModelForCausalLM.from_pretrained(
base_id,
quantization_config=bnb,
device_map="auto",
torch_dtype=torch.bfloat16,
)
# 然后 get_peft_model(model, lora_config) 和 Trainer / 自定义循环
# 我们已经使用的生产 MT 提示形状 (训练应该押韵)
def tower_prompt(en: str, lang_name: str = "Thai") -> str:
return (
f"将以下英文源文本翻译成 {lang_name}.n"
f"英文: {en.strip()}n"
f"{lang_name}:n"
)
# 规则,直到 LoRA 胜出:
# NL/ZH -> 本地工作站塔 (Tower-Plus)
# TH -> 本地 Typhoon-translate
# 判断 / SEO -> 弗伦德 Grok (不是批量 MT)
# 装运门:永远不要相信感觉
def better_than_baseline(scores: dict) -> bool:
# scores = {"tower": chrF, "lora": chrF, "typhoon": chrF}
return scores["lora"] > scores["tower"] + 0.5 # 剩余利润待定
# 如果不比 baseline 好:保持 Typhoon 在生产中;LoRA 留在实验室
生产 MT 政策保持双轨制,直到评估结果另行规定:NL/ZH 使用 Tower,TH 使用 Typhoon,判断使用 Frontier Grok — 不用于批量翻译。
成功的表现
- 在本地工作站上可复现的训练脚本和数据集模式。
- 一个在我们的泰语测试集上击败库存 TowerInstruct 的适配器,而不破坏我们稍后添加的配对。
- 一个 Hugging Face 卡片,其他人(和未来的我们)可以在相同的基础上使用 PEFT。
- 没有浪漫:如果 Typhoon 仍然获胜,TH 生产将继续使用 Typhoon。
# 训练后 — 我们在磁盘上期望的内容
# adapters/en-th-r16/
# adapter_config.json
# adapter_model.safetensors
# README.md # 基础 ID、配对、r、数据大小、许可证、评估表
计算和操作
这是我们控制的金属上的托管托管和实验室计算 — 与混合编码代理和本地 Flux 拇指相同的理念。长时间会话仍然受益于导演模型上的仔细提示缓存和会话连续性;LoRA 作业本身是一个有界 GPU 批处理,不是一个始终在线的 API。
在家庭资金方面,同样的诚实原则适用:虚拟信用和DutchBud / 金融科技轨道只有在国家符合承诺时才能发挥作用。翻译适配器只有在评估与卡片匹配时才会“出货”。
# 3090 上的一个繁重任务 - 不要同时运行 train + Flux + Ollama-14B
# (操作清单,不是诗歌)
# 1) 停止竞争 GPU 消费者
# 2) 训练 QLoRA
# 3) 写适配器
# 4) 释放 VRAM,恢复 Ollama 桌面插槽
状态 – 第一个实验室数据(测试,非生产)
第一个 QLoRA 适配器已上盘。训练集:110 个精选 EN→TH 对;留出:20 行未见数据。配方:TowerInstruct-7B-v0.2 基础,在 q/k/v/o 上排名 r=16,ChatML 提示,3 个时代,训练损失 ≈ 1.77 (~3.5 分钟后在单台桌面 3090 上加载权重)。
留出自动评分(sacrebleu 语料库 BLEU / chrF,贪婪解码,EOS 停止):
| 模型 | BLEU | chrF |
|---|---|---|
| 基础 TowerInstruct-7B-v0.2 (4-bit) | 2.06 | 14.54 |
| 基础 + EN→TH LoRA r16 | 2.88 | 16.22 |
| Delta | +0.82 | +1.68 |
冷读:适配器以微弱优势击败了这个微小的留出集中的库存塔。绝对分数仍然很低 – 基础在我们的泰国语上表现不佳,110 行是一个管道证明,不是生产语料库。我们不根据这些数据关闭家庭网站的泰国 MT。计划中的船闸仍然有效:LoRA 只有在留出集 + 人类审查在更大的黄金集上说时才会替换 Typhoon。
我们证明了:QLoRA 训练 → PEFT 适配器保存 → 在同一工作站堆栈上使用 BLEU/chrF 评估基础与适配器,在 HF 权重树本地化后脱机。下一个实验室步骤是更多的 EN→TH 黄金(以及谨慎的 Typhoon 草稿 + 编辑),而不是浪漫的声明。
发表回复