🧠 第五章 模型微调:让AI训练效果达到预期
📄 一句话概括

沙箱里 既没有 API Key,也没有外网,所以这一步只能演示检查方法。 真正要分清的是两类失败: 不带密钥返回 401(改密钥),带了密钥但 403 或限额报错(开权限、充钱)。 这两类错在网上都叫「连不上」,修法却差得很远。

📝 本节和前面几节的关系

上一节把数据整理干净了。从这一节开始走平台流程:登录、上传、配置、训练、拿模型名。 第一步先确认「我到底连上了没有」。

一、登录之后,要验的是三件事

浏览器里能打开控制台,只说明网页能访问。它跟「我的程序能调通」是两回事。 ① 密钥能用。 发一个最简单的请求(比如列出模型),看是不是 200。 这一步排掉「密钥打错字」「密钥被撤销」这类问题。 ② 有没有权限。 微调接口通常是单独授权的,光能对话不代表能训练。 很多账号能正常聊天, 一调微调接口就 403。 ③ 余额够不够。 训练是按 token 一次性扣的。余额不足时任务会在中途失败——钱不退,数据白标。 三件事按这个顺序验,因为它们的错误码完全不同:401 是密钥,403 是权限,402 或 429 是钱和限额。 把错误码记下来,比记「报错了」有用一百倍。

二、401 和 403 是两回事

Demo 起了一个本地假服务,只有一条规则: 请求头里没有 Bearer 就返回 401。然后发两次请求——不带密钥的一次被 401 拒掉, 带密钥的一次拿到 200。 这个演示很小,但它固定了一个习惯:先分清「身份对不对」和「权限够不够」。 真实平台上,401 之后的排查路径是:密钥是不是复制多了空格、是不是用了别人的密钥、 项目和组织 ID 有没有配对。 403 之后的排查路径完全不同:账号有没有开通微调权限、这个密钥所属的项目有没有被限制、 所在地区是否在服务范围内。 「连不上」这三个字,把这两种情况混成了一件事故。而它们的负责人、处理时长、要动的开关, 全都不一样。

三、密钥怎么放,才不会漏

放进环境变量,不写进代码。 OPENAI_API_KEY 是通行名,绝大多数 SDK 会自动读。 不写进笔记、不写进截图、不写进 git。笔记和截图是最常见的泄露源——它们会被同步、被分享、被备份。 一个环境一套密钥。 开发、测试、生产分开,这样出问题能单独吊销,不用全体换钥匙。 定期轮换。 平台一般都支持建多个密钥,换的时候先建新的、切过去、再删旧的,业务不用停。 还有一条经验:第一次调用成功后,把「用的哪个密钥、哪个项目」记下来。一个月后账单异常, 这个记录能省掉半天排查。

四、沙箱里做到哪一步

📋 实操清单(要真账号和外网,沙箱里做不了): 第一步,浏览器打开 platform.openai.com,注册或登录。 第二步,左侧 API keys 页面,点 Create new secret key,弹窗里那串字符只显示一次, 复制走存进环境变量。 第三步,看 Billing 页面的余额和限额,确认能覆盖这次的训练费。 第四步,回到终端跑一次列模型的请求,确认 200。 🐍 沙箱等价物:见 Demo。本地起一个只认 Bearer 头的假服务,真发两次请求、真收 401 和 200。 沙箱里没有外网也没有密钥,这一步测的是「检查方法」而不是「平台连通性」。 把 base_url 换成真实地址,同一段代码就能用。

五、动手 Demo:不带密钥 401,带密钥 200:两种「连不上」要分开

先检查环境变量里有没有密钥,再起一个本地假服务,它只在请求头带 Bearer 时返回 200、否则返回 401。 发两次请求,比较状态码——这就是「先验身份、再验权限」的第一步。

key = os.environ.get('OPENAI_API_KEY', '')
print(u'环境变量是否存在', bool(key))

def ask(k):
    req = urllib.request.Request(base + '/v1/models',
        headers={'Authorization': 'Bearer ' + k} if k else {})
    try:
        with urllib.request.urlopen(req, timeout=3) as r:
            return r.status, json.loads(r.read())
    except urllib.error.HTTPError as e:
        return e.code, e.reason        # 401 是身份,403 是权限

print(ask(u''))        # 不带密钥
print(ask(u'sk-test'))  # 带一个假密钥

实跑输出:

--------------------------------------------------------------------
平台登录:真正要验的不是密码,是密钥和权限
--------------------------------------------------------------------
  环境变量是否存在         否
  **沙箱里没有配置 API Key,也没有外网**,所以这一步只能演示检查方法。
  但检查的代码本身是通用的:换成真环境,同样的脚本就能用。

  两类失败要分清,它们的修法完全不同:
  不带密钥                 HTTP 401(鉴权失败)
  带密钥                   HTTP 200
  能看到的模型             gpt-4o-mini

  第一次请求就被 401 拒掉,说明**密钥这一层没过去**;
  如果 401 过了但 403 或者限额报错,那是**账号权限或余额**的问题。
  **这两类错在网上都叫「连不上」,但一个改密钥、一个充钱,差得很远。**

  平台上的登录动作本身没法在沙箱里复现,但登录完之后要验的三件事是确定的:
  ① 密钥能用               发一个最简单的请求,看是不是 200
  ② 有没有权限             微调接口通常是单独的权限,光能对话不代表能训
  ③ 余额够不够             训练是按 token 一次性扣的,余额不足中途会停
  第三条最坑:**训练任务跑了一半因余额不足失败,钱不退,数据白标。**

第一行输出是「环境变量是否存在 = 否」。 沙箱里没有配密钥,这是事实,不是演示失败。所以这段代码在这里只能验「检查逻辑对不对」, 验不了「平台通不通」。 下面两次请求都对着本地假服务发:不带密钥返回 401,带 sk-test 返回 200 并列出 gpt-4o-mini。 这个假服务只认「有没有 Bearer 前缀」,不校验密钥真假——它演的是「身份这一层的开关」, 不是真的鉴权。 为什么要费劲起个假服务? 因为「分清 401 和 403」这个习惯,只有真收到过状态码才立得住。 只看文字描述, 下次照样一看到报错就去改密钥。 真实平台上还会遇到 429(限流或额度用尽)和 5xx(服务端故障)。 这两类不要动密钥——一个是等,一个是重试,改密钥只会更乱。 最后一屏讲的三件事(密钥能用、有权限、余额够)在沙箱里一件都验不了。 但它们恰好是真实项目里最容易翻车的三件事,尤其是第三条:训练到一半余额不足,钱不退。

30 秒实验

  1. 把 ask(u'') 的请求头改成 {'Authorization': 'sk-test'}(少了 Bearer 前缀),看返回什么。 格式不对也是 401,跟密钥错分不开——这是 401 最坑的地方。
  2. 把假服务的规则改成「有 Bearer 但没有 /v1/models 权限就 403」,再发一次请求。 体会一下 401 和 403 在排查路径上的区别。
  3. 在环境变量里临时设一个假密钥,重跑一遍,看输出的「是否存在」变成「是」。 这一步只是验环境变量读到了,不代表密钥能用。

常见误区

  1. 「网页能打开就说明能调」。网页能访问是浏览器的事,程序能不能调要看密钥和权限。两回事。
  2. 「报错就是密钥错了」。401 才是密钥,403 是权限,429 是限额或限流。先看状态码再动手。
  3. 「密钥先写在代码里,回头再改」。回头往往就是上线之后。写进代码的密钥,等于已经泄露。
自测三问(点开对答案) 1. 登录平台之后要验哪三件事? —— 密钥能用(200)、有微调权限(不是 403)、余额够(训练中途不会因欠费失败)。 2. 401 和 403 分别该查什么? —— 401 查密钥本身(打错、撤销、项目不匹配);403 查账号权限(微调权限、项目限制、地区)。 3. 为什么余额不足比别的失败更麻烦? —— 因为训练是异步的,中途失败时任务已经跑了一部分——钱不退,数据白标,还得重新排队。

密钥通了,接下来把数据传上去。下一节先算钱、再查格式,最后才点上传。

相关笔记