本节摘要:官方提供在 Kaggle 2×T4 免费 GPU 上跑的微调 notebook(官方口径)——这句话的实际含义是:laya 的微调门槛被压到了「一个 Kaggle 账号」的程度,不买卡、不租卡也能上手。本节走复现路径:环境准备(开启 GPU 加速器、确认两张 T4 可见)、数据上传的三种方式(Kaggle Datasets 为主)、notebook 与 5.1 节骨架的对应关系、双卡并行的注意点;然后是 Kaggle 特有的两条生存规则——会话有时长上限,免费 GPU 有每周配额(均以 Kaggle 平台页面实时为准,本书只给形态),所以断点保存与续跑要从第一天就设计好;最后是产出物回迁:把微调后的检查点带回本地环境接进第 3、4 章的调用链。命令与配置为写法示意。
微调 laya 的算力需求不高——BERT 级底座(第 2.1 节的参数量)在小数据量(5.1 节的小样本特性)下,单卡几十分钟到几小时量级(示意值)就能完成一轮训练。但「不高」不等于「没有」:纯 CPU 训练仍会慢到影响迭代节奏。官方把微调 notebook 放在 Kaggle 2×T4 免费 GPU 上(官方口径),等于把试错成本降到零:改数据、改题面、重跑一轮,代价只是等待时间。这背后的教学意图与第 0.1 节的定位一致——laya 是被设计成「人人可微调」的轻量决策引擎,免费算力是这个承诺的一部分。
先做准备动作(均以 Kaggle 平台当前界面为准,写法示意):
# bash —— Kaggle 环境准备(步骤示意,界面与选项名以平台页面为准) # 1. 注册并完成手机验证(免费 GPU 需要验证,平台规则) # 2. 复制官方微调 notebook(官方仓库 README 给出入口,官方口径) # 3. 在 notebook 设置里把 Accelerator 开为 GPU T4 x2 # 4. 首次运行前确认会话已挂上加速器,再执行后续单元
notebook 第一个自写单元永远是环境自检——别假设平台配置生效了,要验证:
# kaggle_env_check.py —— Kaggle 双 T4 环境自检(写法示意) import torch print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) print("gpu count:", torch.cuda.device_count()) # 预期 2(T4 x2) for i in range(torch.cuda.device_count()): print(f"gpu {i}:", torch.cuda.get_device_name(i))
gpu count 打出 2 才继续往下走;打出 0 说明加速器没开成功,回设置页检查。这个自检单元同时是后续断点排障的第一现场——很多「训练怎么这么慢」的疑问,答案就在这一行的输出里。
依赖安装随官方 notebook 走(官方口径提供依赖单元),只需要一条纪律:装完打印一次 laya 的版本号并抄进笔记。Kaggle 环境每次会话都是新的,半年后你想复现「当时那次效果很好的训练」,版本号是唯一的凭据——这是第 3.1 节可复现性三件套在训练侧的镜像。
| 方式 | 做法 | 适合 | 注意 |
|---|---|---|---|
| Kaggle Datasets | 数据传成私有数据集,notebook 挂载读取(推荐) | 千条以上的正式数据 | 私有数据集只有自己和挂载的 notebook 能读 |
| notebook 直传 | 直接往会话里上传小文件 | 几十条的快速试验 | 会话结束即丢,只当草稿用 |
| 代码内生成 | 在单元里现场构造样本 | 教学与冒烟 | 不能替代真实数据 |
正式路径用 Datasets:上传 5.1 节格式的偏好样本文件(JSONL 一行一条最顺手),在 notebook 的 Input 里挂载,路径形如挂载目录加数据集名(以平台实际路径为准)。上传前的本地预检两分钟值得花:JSONL 逐行能被解析、preferred 与 rejected 都在 options 里、留出集已经切出去另存——把脏数据挡在 Kaggle 之外,比在云端 debug 便宜得多。
# load_prefs.py —— 从挂载的数据集读取偏好样本(写法示意,路径以平台为准) import json TRAIN_PATH = "/kaggle/input/laya-prefs/train.jsonl" # 挂载路径(示意) HOLDOUT_PATH = "/kaggle/input/laya-prefs/holdout.jsonl" def read_jsonl(path): with open(path, encoding="utf-8") as f: return [json.loads(line) for line in f if line.strip()] train_samples = read_jsonl(TRAIN_PATH) holdout_samples = read_jsonl(HOLDOUT_PATH) print("train:", len(train_samples), "holdout:", len(holdout_samples))
官方 notebook 的训练单元与 5.1 节的骨架是同一件事的两种呈现:骨架负责让你看懂每一步,notebook 负责把数据加载、双卡并行、日志打印这些工程件配齐。对齐着读,重点看两处。
其一,双卡并行:2×T4 意味着数据并行的机会——每张卡各拿半个批,梯度同步后更新(官方 notebook 的并行写法以其实际代码为准)。小数据量下双卡的加速比达不到 2(通信与同步有开销,示意判断),但缩短的是「一轮实验」的墙钟时间,迭代节奏的收益是实打实的。其二,留出集评估单元:官方 notebook 在训练间隙用留出集打指标(官方口径提供该环节),这就是 5.1 节「三查」的第一查在 notebook 里的落点——盯着它决定早停,别盯训练损失。
# ckpt_save.py —— 断点保存(写法示意) import os CKPT_DIR = "/kaggle/working/checkpoints" # working 目录随会话产出,可下载 def save_checkpoint(model, optimizer, step, path=None): path = path or os.path.join(CKPT_DIR, f"step-{step}.pt") os.makedirs(CKPT_DIR, exist_ok=True) torch.save({"model": model.state_dict(), "optimizer": optimizer.state_dict(), "step": step}, path) print("saved:", path)
Kaggle 免费 GPU 有两条硬约束,均以平台页面实时显示为准(历史上有过调整,本书不抄具体数字,只讲形态):单次会话有时长上限,超时会被切断;免费 GPU 每周有使用配额,用完要等刷新。这两条决定了训练必须按「随时会断」来设计,三个动作把损失兜住。
第一,断点保存进 working 目录:每个固定步数存一次(上面的 ckpt_save),会话被切时最多损失一个保存间隔的训练量。第二,重要产物及时下载:会话结束后的 working 输出有一段保留期(以平台规则为准),但「依赖保留期」不是工程习惯——每轮训练结束就把检查点与指标日志下载到本地,或转存到自己的 Kaggle Datasets。第三,续跑设计:把「读最新断点、从其步数继续」写成 notebook 的开关单元,新会话开头跑一次就接上断点——没有这个单元,每次超时都从零开始,配额会在反复重跑中白白烧掉。
# ckpt_resume.py —— 找最新断点续跑(写法示意) import glob import os import re def latest_checkpoint(ckpt_dir="/kaggle/working/checkpoints"): ckpts = glob.glob(os.path.join(ckpt_dir, "step-*.pt")) if not ckpts: return None, 0 def step_of(p): return int(re.search(r"step-(\d+)", p).group(1)) latest = max(ckpts, key=step_of) return latest, step_of(latest) # path, step = latest_checkpoint() # if path: 恢复 model/optimizer 状态并从 step 继续(写法随训练框架)
训练的终点不是 Kaggle 里的一个好指标,而是回到第 3、4 章的调用链里干活。回迁四步:下载检查点文件;放进本地模型缓存或以显式路径加载(加载方式以官方文档为准);用第 4.1 节的冒烟脚本跑一遍四步自检;把「数据版本、训练步数、留出集指标、laya 版本」四元组记录在检查点旁边。最后一步最容易省略也最致命——三个月后你有五个检查点文件时,没有伴随记录的文件等于未知物体。
复现路径通了,下一节把官方给出的两个提升案例拆开复盘:typed-decisions 基准上 0.362 到 0.766(官方口径)的跃迁从哪来,browser-agent 的「下一步点哪里」又怎么变成一道 choice 题。