第 7 节 · 45 分钟
Vibe Coding · 中文 Semantle

词库里有什么

你猜了六节课的词,从没把这 5 万个词当成整体看过。今天拿一个你关心的社会议题当筛子,看它知道什么、不知道什么、偏向哪边。

开始 Guoliang · 共 12 页
第 7 节 · 词库的来历

这 5 万个词不是词典,是一份词频快照

词频名次 = 下标 + 1那附近的词(按词表下标顺序读出)
1–10是 在 和 我 有 就 你 他 都 我们
1,001–1,010中间 脸 仍 那你 一条 正式 长期 车辆 恢复 使得
10,001–10,010浴室 气得 七天 光彩 收视率 原名 欢呼 洋葱 王思聪 攻势
30,001–30,010成群结队 大早 荣登 贪吃 太阳光 内网 平底 可向 不惯 救救
49,991–50,000十只 嫁过来 大海捞针 改朝换代 逢场作戏 贪图便宜 意外事件 夺权 那阵 拔火罐
来源:腾讯 AI Lab 中文词向量轻量版,143,613 词按词频降序排。

前面是虚词,中间是生活,末尾是成语和冷门词 —— 这就是「常用」的真实样子。

类比

手机输入法的联想词库:你常打什么,它就记什么,从不问对不对。词频快照也一样 —— 它记的不是「哪些词是对的」,是「那时候的人常写什么」。

1,467
1 字词
34,932
2 字词
7,292
3 字词
6,309
4 字词
你的词表

你的 data/vocab.json 是它的前 50,000 个 1–4 字词。单字只收 HSK(汉语水平考试)名单里的字;课堂净化挡掉 160 个不适合上课的词,由后面的词补上。

第 7 节 · 词库的年代

有的词它没见过:不是不重要,是那时候还没人这么说

在词表里 · # 后面是词频名次,不是年份
微博 #1046 淘宝 #1401 微信 #1506 特朗普 #1973 直播 #2228 人工智能 #2495 支付宝 #3358 网红 #5253 雄安 #9493
不在词表里 · 猜它会得到「词表里没有这个词」
新冠 健康码 内卷 躺平 元宇宙 碳中和

这几个词在原始的 143,613 个词里也没有,不只是没挤进前 5 万。

语料有截止日期,词向量只知道那之前的世界。你猜「新冠」,得到的永远是「词表里没有这个词」。

类比:一个断网好几年的朋友。聊什么都行,就是别问他这几年才出现的新词 —— 他不是不关心,是真没听说过。

第 7 节 · 最近邻当筛子

种子词的向量平均一下,离它最近的词就是议题的地盘

1
选议题

挑一个你真关心的:环境、就业、留学……第 7 页有菜单。

2
写 5 个种子词

环境 污染 环保 气候 垃圾。都得在词表里,游戏里猜一下就知道。

3
平均、归一化

5 个 200 维向量加起来除以 5,再把长度归一 —— 只要方向。

4
余弦 × 100

拿它和 5 万个词逐个算余弦,乘 100,和游戏分数一个口径。

5
取前 200

从高到低排,前 200 就是这个议题在词库里的地盘。

#相似度#相似度
1环境污染75.665自然环境72.32
2污染物72.726生活环境71.06
3生态环境72.477废弃物70.57
4空气污染72.398空气69.64
老师参考版:npm run topic -- --seeds 环境,污染,环保,气候,垃圾
完整 200 行:reference-impl/docs/topics/环境.md
你早就用过它

游戏里的「提示」做的就是这件事(engine.nearest:找目标词的最近邻),只是种子只有一个词。课堂净化挡掉 160 个词,是老师筛「不要什么」;今天你筛「要什么」—— 同一个词库,两种筛法。

类比

五个人的口味平均一下,再给整个食堂的菜排序 —— 排最前的,就是这五个人共同爱吃的。

第 7 节 · 读表

同一个议题,词库有自己的说法:这里的「环境」首先是「污染」

前 200 里含「污 / 废 / 排 / 脏」含「气候 / 碳」气候类的词排第几干脆不在词表
233二氧化碳 第 49 · 气候变化 第 53 · 低碳 第 126全球变暖 · 碳中和 · 垃圾分类
标记一 · 意料之中

环境污染 75.66 · 空气质量 69.22
生态环境 72.47

看到就点头的词。它们是议题的骨架,也是你挑主题目标词的候选。

标记二 · 意外

治理 65.40 · 卫生 65.36
微生物 65.11 · 脏乱差 61.01

你事先想不到,但它们排得很靠前:这份语料里「环境」常和「整治 / 卫生」连着说。

标记三 · 噪音

这些 60.57 · 其次 57.87
再者 57.32 · 所以 55.57

高频虚词跟谁都沾边,任何议题都会混进来。词向量的通病,标掉就好。

再加负种子筛一次。负种子是你想让结果离开的方向,这里用污染 污水 垃圾,从中心里减掉一半。气候变化升到第 12,前 5 换成右边这些 —— 筛子换一下,地盘就换一块。

自然环境 75.39 生活环境 71.84 生存环境 71.72 生态环境 71.23 自然条件 69.08
第 7 节 · 向量方向与语料偏见

语料里的偏见会原样存进向量:
这条轴上,护士在一头,程序员在另一头

「女性 − 男性」轴 · 投影 × 100 · 探针词全是职业名 ← 偏「男性」 偏「女性」 → 家庭主妇+17.4 模特+17.1 总裁+13.1 护士+9.9 老师+9.1 秘书+8.8 医生+8.2 保姆+7.3 记者+0.1 教授−0.1 科学家−1.1 工程师−5.0 程序员−8.4 保安−12.1 −10 0 +10
怎么算的

轴 = 「女性」的向量 − 「男性」的向量,归一化后只留方向。探针词 = 你拿来测「偏哪一头」的词,这里全用职业名。

投影 = 探针词和这条轴的点积 × 100:正数偏「女性」,负数偏「男性」,0 附近两边都不沾。

参考版:npm run topic -- --axis 女性,男性 --probe …

国王 − 男人 + 女人 ≈ 王后 70.50

爸爸 − 男人 + 女人 ≈ 妈妈 91.71

北京 − 中国 + 日本 ≈ 东京 69.36

这不是词典的真理,是这份语料的统计。它会进你的游戏 —— 提示功能递给你的邻居,就是从这里来的。

第 7 节 · 议题菜单

挑一个你真的关心的议题,种子词已经替你验过在词表里

议题种子词(都在词表里)筛出来是什么样
环境环境 污染 环保 气候 垃圾「环境」首先是「污染」,气候变化排第 53
性别女性 男性 性别 平等 歧视前 5:性别歧视、男女、同性恋、男女平等、同性
老龄化老人 养老 老龄化 退休 养老院退休金、赡养、敬老院、孤寡老人、护工 —— 养老在这里是家里的事和钱的事
教育高考 学校 考试 补习 升学中考 87.31 第一,前 30 里 13 个带「考」
留学留学 移民 签证 海外 出国出国留学、留学生、绿卡、回国、海归,第 17 名是「米国」
就业就业 失业 工作 裁员 加班下岗 73.90 第一,排在找工作 72.11、辞职 71.84 前面
心理健康抑郁 焦虑 心理 压力 睡眠情绪、精神压力、焦虑症、恐惧、失眠
自己定议题

种子词先在游戏里各猜一次。看到「词表里没有这个词」就换近义词,对人不计次,随便试。

选性别

就用表里这五个种子词。筛出来的表里可能混进擦边词,标成噪音即可。

课堂活动20分钟
第 7 节 · 动手

让 agent 给你写一个筛子,
然后读它筛出来的表

  1. 选议题、写 5 个种子词,在游戏里各猜一次,确认都在词表里。
  2. 把右边这段 briefing 发给 agent,尖括号里换成你的议题和种子词。
  3. 跑起来,打开 docs/topic-<议题>.md,先读前 60 行。
  4. 逐行打标记:意料之中 / 意外 / 噪音,数一数三种各有几个。
  5. 加 2–3 个负种子再跑一次,另存一份,和原表比前 10 换了几个。
  6. 在文件末尾写三句话:知道什么 / 不知道什么 / 偏向哪边。
你应该看到什么

一张 200 行的表:前几名是带议题字眼的复合词(「环境」→ 环境污染 75.66),往下开始出现意外的词,再往下混进「这些」「所以」这类虚词。

卡住了先做什么

种子词全被跳过 → 换常见的近义词。分数全是零点几 → 忘了乘 100。前几名是种子词本身 → 没排除种子词。用第 4 页的种子跑,顺序对、分数整体偏低 → 平均完没归一化。卡死了对照老师参考版 scripts/topic-filter.mjs,别直接抄。

<goal>
写一个脚本 scripts/topic.mjs:读 data/vocab.json 和
data/vectors.f32(每词 200 个 float32,已归一化),
把 --seeds 里几个种子词的向量平均并归一化,算它和
词表里每个词的余弦 × 100,取前 200,按分数从高到低
写成 Markdown 表到 docs/topic-<议题>.md。
列:序号、词、分数、词频名次(词在 vocab.json 里的
下标 + 1)、标记(留空)。
</goal>

<constraints>
- 支持 --minus 词,词:减去负种子平均向量的一半再归一化
- 带 --minus 时另存成
  docs/topic-<议题>-减<负种子>.md,不覆盖原表
- 种子词不在词表里要打印警告并跳过,一个都不在就报错退出
- 种子词本身不出现在结果里
- 不引入任何数值计算库,只用 for 循环
- 跑完把命令和前 10 行原样贴给我看
</constraints>

<fallback>
拿不准 vectors.f32 的格式,先去读 server/ 里
加载它的那段代码,不要猜</fallback>
第 7 节 · 加分概念

筛出来的 200 个词想画到纸上?先找那个最能拉开它们的方向

留下:拉得最开 丢掉:挤成一团 白点 = 六个词(这里先画成 2 维好看懂)
PCA · 主成分分析 · principal component analysis
200 个数 → 2 个数
1先把这团点的中心挪到原点:每一维都减掉平均值。
2找一个方向,让点投影上去后最分散:第一主成分,图上绿色那条。
3在垂直方向里再找一次:第二主成分。两条当 x、y 轴,就画得出来。
4注意:主成分是 200 维的加权组合,是新造的方向,不是原来某一维。
类比:手在灯下的影子。转一个角度,影子从一团黑变成看得出五根手指 —— PCA 就是自动去转那个角度。

参考实现在 server/pca.mjs 里只用 for 循环自己算,不引入任何数值库。

第 7 节 · 加分概念

这张图能看出「越猜越近」,但看不出「谁分数高」

向量图页:八个猜测词与目标词「阳台」PCA 到二维的散点图,虚线按猜测顺序连接,黄色五角星是目标词
这是你游戏里的向量图(一局真实对局)

目标词「阳台」,按 篮球 → 城市 → 天气 → 衣服 → 房子 → 沙发 → 窗户 → 阳台 的顺序猜完。点色 = 分数(冷 0 → 暖 100),星形 = 目标词。

图脚那两个数

前两个主成分各解释 28% 与 19% 的方差,合计 47%,离 100% 还差一半多 —— 差的那部分信息压扁时被扔掉了。两个数之和离 100% 越远,图越不可信。

屏幕上挨在一起的两个点,分数可以差很多:「衣服」46.93(第 457 名)和「房子」57.69(第 109 名)几乎叠在一张图上,200 维里它们差着 348 个名次。

作业 · 第 7 节

两件事:整理主题目标词,写好 Demo Day 那一页

  1. docs/topic-<议题>.md 的标记补完,写完末尾那三句话:知道什么 / 不知道什么 / 偏向哪边。
  2. 从前 200 里挑 20 个具体名词,存成 data/targets-<议题>.json(格式和 data/targets.json 一样);让 agent 证明这 20 个都在 vocab.json 里。
  3. Demo Day 准备:名次差是第 6 节作业算过的;没算的下一节之前按 docs/benchmark-sheet.md 的三步算出来。
  4. docs/hire.md,300–500 字:结论一句话 → 证据 → 对照 → 局限 → 变条件。大声念一遍并计时,1 分钟内念完。
  5. 列 10 分钟提纲,四段:做了什么 → 怎么验证的 → 数据说明什么 → 局限在哪。第一段开着游戏演示 3 分钟;后三段共 4 分钟,念 hire.md 占其中 1 分钟;最后 3 分钟老师提问,不算在四段里。
  6. 加分(二选一):让 agent 把这一局的 PCA 散点图加进游戏(briefing 在作业单里);或给游戏加一个「主题局」开关,用你那 20 个词当目标词。
交什么

docs/topic-<议题>.mddata/targets-<议题>.json(附证明都在词表里的那条命令输出)、docs/hire.md、10 分钟提纲。加分:散点图截图 1 张,或主题局那一局的证明(放弃后揭晓的目标词 + 那 20 个词并排)。

自检三条:20 个目标词全是具体名词且都在词表里;hire.md 至少 3 个自己的数字 + 那个模型的名次差;1 分钟内念得完。

下一节 Demo Day 上要念
约 45 分钟:词库 15 + 准备 30
下次见

最后一节:先看两张榜,
再听你讲 10 分钟。

带三样东西来:能在你电脑上跑起来的游戏、A 和 B 两张排行榜(12 个结果文件,36 局)、那一页念得完的 hire.md。

回到封面 Vibe Coding · 第 7 节 · 完
01 / 12