主题
第29章 三种界面,同一条推理链路
把模型接到聊天框,并不会让预训练模型自动学会遵循指令。上游的三个UI目录分别演示文本续写、短信分类、指令回答。共同部分是加载、编码、推理和显示;差异在权重结构、输入格式和输出解释。先把这些差异写成接口约定,再考虑流式显示。
29.1 四个入口对应三类任务
ch05包含 app_orig.py 与 app_own.py,前者加载OpenAI GPT-2权重,后者加载第5章自行训练得到的 model.pth。两者都调用generate,把输入与续写一起解码。它们属于一种任务的两个权重来源,不能因为都是124M结构就交换state_dict:自训示例上下文为256、qkv_bias=False;原始权重配置上下文为1024、qkv_bias=True。源码:预训练UI
ch06的 app.py 重建二分类头,再加载 review_classifier.pth,调用 classify_review,输出一个标签。它不该调用语言模型generate,因为最后一维已经只有两个类别。ch07加载355M指令模型,先构造任务模板,生成后 extract_response 去掉提示和响应分隔符。分类UI;指令UI
| 路线 | 模型输出 | 用户输入处理 | 显示内容 |
|---|---|---|---|
| 预训练续写 | B×T×V | 原文本编码 | 输入加续写 |
| 分类 | B×T×2 | 截断补齐,取约定位置 | 类别标签 |
| 指令回答 | B×T×V | 加训练模板 | 新生成回答 |
这些应用在模块加载阶段调用 get_model_and_tokenizer,所以import本身可能触发文件读取甚至下载。自动测试不要把“导入UI”当成无副作用单元测试;本书没有运行这些入口,也没有启动Web服务。
29.2 加载成功不是推理准备完成
路径采用相对工作目录,启动位置不对便找不到权重。更稳妥的本地项目可从 Path(__file__).resolve().parent 推导路径,并允许显式配置。加载时先核对结构、词表、位置长度、输出头,再用受信文件的state_dict;weights_only=True减少非权重反序列化用途,但不能把任意来源文件视为可信。
固定源码中app_orig与分类UI显式 eval();app_own与指令UI的加载函数没有同样显式调用。指令配置dropout为0,因此此处不一定改变输出;自训配置dropout为0.1时则需要特别检查。no_grad()不会关闭dropout,二者必须分开理解。这是静态审查结论,不是本书运行界面后的故障报告。
设备也需一致:模型、输入token都放到相同device;载入GPU保存的权重到CPU时考虑 map_location。用户界面上的“正在生成”不能掩盖模型尚未成功加载,错误应区分文件缺失、shape不匹配、设备问题和推理预算不足。
29.3 上游是一次返回,流式是补充接口
@chainlit.on_message 回调接收一条消息,完整调用generate后 .send() 一次。它没有逐token streaming,也没有跨轮历史累积。界面长得像聊天,不代表下一轮会读上一轮内容。若要多轮对话,需要明确会话历史如何转换成训练匹配的模板,并按会话隔离缓存。
补充实现可以让生成器每次yield新token ID,前端把增量显示出来。但token不等于完整字符,尤其UTF-8和BPE边界处,逐token单独decode再拼接不一定等于整体decode。可靠方法积累token并使用支持增量解码的decoder,或只发送已经稳定的文本前缀。

29.4 上下文预算属于接口,不应留给崩溃处理
设窗口上限C,编码后提示长度P,期望新增N。对于固定位置范围的缓存生成,通常应保证P+N≤C;超出时明确拒绝、缩短输出或截短历史。普通无缓存generate可以每步取最近C个输入窗口,但它会失去较早上下文,这与“保留完整对话的缓存”不是同一语义。第8章解释缓存位置与重置,本章要求UI把选择告诉调用者。
全局模型对象可以共享只读权重,却不能共享未经隔离的会话KV状态。会话A的末尾位置若流到B,B可能继承无关上下文。并发请求还要处理取消、异常和释放状态,不能只在成功结束时重置。
离线fixture刻意不调用神经模型:输入 [1,2] 按确定规则产生 [3,4,5],另一会话输入 [7] 产生 [8,9];超预算请求被拒绝。这个测试验证流式顺序、请求状态独立和预算判断,不证明Chainlit部署、网络吞吐或真实tokenizer解码正确。
练习与答案
补充题:为什么分类权重不能直接接续写UI?答案:分类头只有C=2个logit,输出ID不再是词表分布;即使代码能运行,也不具备语言生成含义。
补充题:no_grad()之后重复输入仍输出不同,可能是什么?答案:dropout仍处于train模式,或采样随机数/并发状态不同。先检查eval,再检查采样设置,不要立即归咎权重损坏。
来源
三个UI目录及其README、requirements-extra分别说明启动依赖与模型准备,固定版本见本章链接。UI静态检查、补充fixture和真实服务验收是三个层次,本书只完成前两者。