模型每一刻只干一件事:给下一个 token 排概率。不是判断对错,不是求新,是问训练分布里哪个 token 最常跟在后面。这个动作重复一百万次,就是你拿到的每一段回复。这篇我们搭一个最小采样器,跑完你就知道“模型把你拽回主流”在代码里长什么样。
先给一个刻意到只有两个分支的训练分布,二十几行就能跑:
probs = {
'地球': {'是宇宙中心': 0.99, '绕着太阳': 0.01},
'是宇宙中心': {'</s>': 1.0},
'绕着太阳': {'</s>': 1.0},
'</s>': {},
}
import random
def sample_next(token):
words = list(probs[token].keys())
weights = list(probs[token].values())
return random.choices(words, weights=weights)[0]
def generate(start='地球', max_len=5):
out = [start]
for _ in range(max_len):
nxt = sample_next(out[-1])
out.append(nxt)
if nxt == '</s>':
break
return out
追一遍:sample_next('地球') 就是一行 random.choices,按权重抽。99% 的时间你拿到 '是宇宙中心',另外 1% 拿到 '绕着太阳'。这个动作里没有判断,也没有所谓“观点”——它只是把训练语料里的条件频率重新摇了一遍。所谓模型在“表达立场”,到这一层就只剩这个。
但有个问题会一直追着你:为什么这个系统会把新东西当错误?我一开始也以为是常识不够或者对齐没做好,后来翻到损失函数,发现根本轮不到谈对齐。训练用的是交叉熵,它的形状相当朴素:要让损失降下来,模型唯一能做的就是让训练分布里高频的地方更可能,低频的地方更不可能。一个新奇的输入和一句语法错误,在损失函数里是同一种东西——都是分布外的低概率事件。梯度对它们的处理方式一模一样:把概率质量往中心拉。模型没有“新颖”这个类别,训练目标里没有这个信号。它只会问一件事:这个序列在历史语料里的期望频率是多少?新想法的答案是,接近零。
你造一个新术语发给它,它在损失函数的坐标系里不是一个“新词”,是一个在训练语料里找不到的 token。它最可能的下一个动作,是把它吸附成最接近的已知词——这就是“模型把非标术语当拼写错误”的机制。它没有拼写检查器,只是沿着梯度的最短路径,把分布外的东西拉回分布内。
这听起来像历史哲学,其实只是采样。地心说被挑战那会儿、微生物理论刚提出来那会儿、大陆漂移还没被并成板块构造那会儿,这些今天写教科书的东西,在当时的历史分布里都是尾部事件。要是那时候有个基于语料的采样器,它给的下一个 token 大概率跟今天的教科书相反。它不是反对科学进步,只是忠实于训练分布。
不过单次采样还留着 1% 的小口子。真正把分布挤扁的,是把输出当输入再喂回去。我们把最朴素的采样器加一个反馈循环——每生成一次,就让峰更高一点、尾巴更低一点——模拟“AI 输出被人类当数据用”的循环:
def converge(probs, n_iters=50):
for _ in range(n_iters):
s = generate()
p = probs['地球']
if s[:2] == ['地球', '是宇宙中心']:
p['是宇宙中心'] *= 1.05
p['绕着太阳'] *= 0.95
else:
p['是宇宙中心'] *= 0.95
p['绕着太阳'] *= 1.05
total = p['是宇宙中心'] + p['绕着太阳']
p['是宇宙中心'] /= total
p['绕着太阳'] /= total
return probs
probs = converge(probs)
print(probs['地球'])
# {'是宇宙中心': 0.9994, '绕着太阳': 0.0006}
50 次之后,'绕着太阳' 掉到 0.0006,并且还在继续掉。这不是随机波动:每次采样再回灌,都是对峰的一次加权放大。采样噪声一开始或许会随机偏向某一侧,但只要某一侧被选中次数领先一点点,它就会把几乎全部概率质量吸走。文化扁平化不是风气问题,这十几行代码就是它的形状。
真实的 LLM 在这个骨架上多了两件大事:采样前加 temperature 和 top-p,给尾巴开一扇小窗;RLHF 把人类偏好作为第二个分布叠上来。但这都没改“按分布采样”这个根。temperature 只是把尾巴放大一点,分布还是偏的。RLHF 说白了是在原始文本分布上再乘一个偏好分布的权重,抽的还是某个期望值。一个东西在训练数据里不存在、在人类偏好数据里也不存在,对这两套系统来说概率都是零。
到这一步,结论很直接:采样成本趋近于零,分布峰上的内容会无限供给、便宜到不值钱;真正稀缺的只剩尾部——因为这个采样器几乎够不到尾部,而且每迭代一次还在指数级地把它压掉。一个输出越是“像那么回事”,离尾部就越远。所以重点不是非要写些没人看得懂的怪东西,随机偏离本身也是噪声。重点在于你知道自己正在选一个低概率的 token,也知道为什么不是最可能的那个。
所以模型给你的最可能句子,不是它在为你考虑,只是一条从历史语料里走出来的最短路径。你要真想做点新东西,得在采样前把这个问题问清楚:这个低概率 token 是你选的,还是随机噪声。知道它想在哪一步把你拉回均值,然后决定要不要被它拉。
