一字一诗
LESSON 22 / 26

让关键词进入训练

这一课完成什么

把“围绕关键词写”变成模型训练时真正见过的任务。仅训练原诗正文,不会自动让模型明白 CLI 中某个参数的意义。我们需要把关键词和诗文放在同一条学习序列里。

关键词要在训练题里出现,才有机会被利用

不能只在生成时临时贴一句说明,就期待小模型懂得服从。训练时也要让模型见过“条件在前、正文在后”的同类格式。

关键词要在训练题里出现,才有机会被利用
用文字逐步读这张图
  1. 诗文线索:明月、故乡等;来自正文或规则
  2. 提取条件:关键词不是人工真标签;可能有误判
  3. 拼进前缀:格式 → 关键词 → 正文;训练生成保持一致
  4. 只评分正文:已知条件不计误差;接出的字才是答案
  5. 生成时沿用:输入“秋雨,故乡”;作为前缀线索
  6. 检查是否出现:直接出现与主题相关;两者不要混淆
关键词没直接出现,就一定完全没影响吗?

不能这样断言。但至少字面约束没有满足,应如实记录,不能把软引导称为保证。

关键词从哪里来

第一类是正文实际出现的常见古典词,例如“明月、春风、故乡”。第二类是用公开线索规则推测的主题,例如正文出现“乡、归、客”之一,会加入“思乡”候选。这些主题规则会误判,称作弱标签;课程明确记录规则,不把它们说成人工精校。

本项目没有把整首诗交给外部聊天服务生成标签。你可以查看 poetry_gpt/data.pyWORDSTHEMES,逐项修改与审查。新增主题前,先人工检查几十条命中记录,记录典型误判。

bash
./poet lesson 22

例如“客”也可能出现在宴客、待客的诗里,并不一定是思乡。若希望标签更准确,可以要求多个线索同时出现、加入排除条件,或人工整理一小份可靠的主题训练材料。

条件序列怎样构造

text
<bos> <七言> <关键词> 明 月 <隔> 故 乡 <正文> ……诗文…… <eos>

每次取到一首诗,程序从它的候选关键词中随机取零个、一个或两个。零关键词练习让模型也能在没有主题条件时接着开头写;有关键词练习让它学习条件与诗文之间的联系。相同原诗派生出的所有条件练习始终属于同一份材料,不能把它们拆到训练与考试两边。

评分只覆盖正文与结束标记,但误差仍能通过正文对条件的关注,传回关键词的表示与相关计算。条件区本身不评分,并不意味着模型完全不学习使用它。

对照关键词的影响

bash
./poet write --keywords 明月 --form five --seed 12
./poet write --keywords 春雨 --form five --seed 12
./poet write --keywords 秋雨,故乡 --form seven --seed 12

比较全部输出,记录哪些有对应意象、哪些偏题。CLI 最多接收三个关键词,每个 1—4 个汉字;训练主要见过零到两个条件,三个组合属于更困难的使用情形。字表支持某个词不等于模型已充分学习这个词的主题含义。

字面出现与切题是两个检查

“明月”出现在诗里是容易自动检查的事实;整首是否表达月夜意境,需要阅读。反过来,诗可能用“清辉”等相关意象表达主题,却没有字面出现“明月”。当前候选排序提供词面与线索分数,只把它当辅助。不会保证每个关键词必然出现。

验收与排错

真实例子:两个词,只直接出现了一个

下面的实际命令输入“秋雨、故乡”,最终诗中只有“故乡”直接出现。用 JSON 中的 keywords 与 keyword_literal_hits 对照,能看见软引导的限制,而不依赖模糊印象。

图 16 / 浏览器截图本次复现命令 · 正式模型
交付效果:关键词与可核对的 JSON

交付效果:关键词与可核对的 JSON

先看哪里
输入是“秋雨、故乡”;直接出现列表里只有“故乡”。
这说明什么
这个真实输出展示了软引导的限制:两组关键词没有全部命中。model_step=18000 说明用了哪版模型;整首精确查重通过不表示每句都原创。
你接着做
对照 keywords 与 keyword_literal_hits,分别记录外形、主题、重复、语义,不要把 selection_score 当成人工诗歌评分。

打开原图,放大阅读 · 可复制的文字版

查看来源

reports/illustrated/write-keywords.txt

小练习与答案

只出现“客”的诗被标成“思乡”,你会怎么改善规则?

查看答案

先抽查误判,再考虑要求“客”与“归、乡、愁”等线索共同出现,或直接使用正文含“故乡”的高精度子集。更严格规则会减少可用例子,因此应同时报告命中数量与人工抽查准确情况。不能靠在网页里把标签称为高质量来解决误判。

动手补全一小段

先运行上面的完整实验,再复制本课起始文件为自己的练习。starter 有意留空 solve 函数;补全后运行文件,底部检查会告诉你是否符合本课要求。卡住时打开参考答案,比较每一步。

bash
cp lessons/22/starter.py lessons/22/my_exercise.py
# 编辑 my_exercise.py 中的 solve 函数,然后运行:
.venv/bin/python lessons/22/my_exercise.py
# 对照完整答案:
.venv/bin/python lessons/22/solution.py

下载起始代码 · 下载参考答案

展开本课补全练习的完整参考答案
python
"""第 22 课补全练习:提取正文真的出现的词。修改 solve,保持下方检查不变。"""
import math, io, argparse
import torch
from torch import nn
from torch.nn import functional as F
torch.set_num_threads(2)
torch.manual_seed(26)

def solve(text, vocabulary):
    return sorted({word for word in vocabulary if word in text})

assert solve('明月照归舟',['明月','故乡','明月'])==['明月']
print("本课补全练习通过。")
展开本课完整、可独立运行的实验代码
python
"""本课独立实验;在仓库根目录执行 .venv/bin/python lessons/22/experiment.py。"""
import sys, json, math
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
import torch
from torch import nn
from torch.nn import functional as F
from poetry_gpt.common import ROOT, DEFAULT_DATA, read_json, read_jsonl
from poetry_gpt.model import ModelConfig, PoetryGPT, CausalAttention, Block
from poetry_gpt.data import Tokenizer, SPECIAL, clean_record, keywords_for
from poetry_gpt.labs import show
torch.set_num_threads(2)
torch.manual_seed(26)

text = '明月照归舟,江风动客愁。故乡千里外,夜雨过西楼。'
show('教学示例的自动关键词', keywords_for(text))
print('思乡 是线索规则给出的弱标签;明月、故乡 是正文原有词。')
下载本课实验