Lecture 04 · LAB 04 · 遮挡文本:并行候选与迭代相容性

LAB 04 · 遮挡文本:并行候选与迭代相容性

先阅读保存结果和解释,再按本册步骤选择是否运行。在 Deepnote 阅读与运行 · 下载 Notebook

遮挡文本:多个位置各自合理,整句就相容吗?

本册的文本世界只有两句:“猫 会 喵 , 猫 喵”和“狗 会 汪 , 狗 汪”,各占一半。先预测:全句遮住时,若每个位置独立按其边际概率填入 token,得到合法句子的概率是多少?

然后比较三条采样轨迹:一次并行填充、使用已揭示内容重新预测、从左到右自回归。这里的预测器是手工分布的精确条件计算,不是训练过的扩散语言模型,也不是 LLaDA 复现。

本轮入口:先看下面的保存图,再只改一个参数。 原有可手算代码与推导完整保留在后面。

先看问题与保存结果

本实验比较同一小语法任务:逐个提交、多位置独立候选、迭代相容。先看保存的 token 状态和合法率,再改变提交策略。规则轮数并不是大语言模型的端到端延迟。

本实验比较同一小语法任务:逐个提交、多位置独立候选、迭代相容。先看保存的 token 状态和合法率,再改变提交策略。规则轮数并不是大语言模型的端到端延迟。

连续概念解释。读完后留下一个结果:固定量、改变项、实际观察,以及它支持的机制。

先看保存结果:相同遮罩输入,三种填词次序

相同遮罩输入,三种填词次序:图1

相同遮罩输入,三种填词次序:图2

图中 cat/can/meow 对应“猫/会/喵”,dog/woof 对应“狗/汪”;问号是未知,金色是本轮新提交,蓝色是此前保留。三种方法均从相同全遮罩输入出发。默认一次并行的精确合法率1/8,4000种子的实测为0.1325;迭代与AR在此构造语法中均为1,分别用3与6个依赖轮次。本调度器只提交,不重遮罩或修正已知token;看见“修订”需求时应把相应位置明确重新设为未知,不能把保留图色误读为已实现自动纠错。

结果身份: 2026-09-28由下方同源Python代码在本地CPU实际生成并核对。解析两句语法,不调用真实语言模型。轮数是依赖关系,不代表GPU延迟;此结果不为扩散LLM和AR排名。 图不是模型训练输出。

保存数据、图片与代码随下载材料提供;静态图无需GPU。学生重算需要Python、NumPy、matplotlib,不发起网络请求、不打印密钥,只在当前目录写入本册输出文件夹。

只改一个变量:COMMIT_THRESHOLD

把 COMMIT_THRESHOLD 从0.99改为0.5,保持语法、输入与4000枚种子固定。较低门槛使不确定位置同时提交,迭代法在这个特例退化为一次并行。

在下面参数区改值后运行本格;重算图会显示在输出中,并保存在 lab04-visual-output。接着用图中的具体变化解释,不从一次样本判断整个分布。

查看可执行代码
import random, json, platform
from collections import Counter
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle

# Change one variable: threshold. At <=0.5 uncertain positions commit together.
COMMIT_THRESHOLD=.99
VISUAL_SEED=20260928
VISUAL_N=4000
OUT=Path('lab04-visual-output');OUT.mkdir(exist_ok=True)
GRAMMAR_VIS={('cat','can','meow',',','cat','meow'):.5,('dog','can','woof',',','dog','woof'):.5}

def vis_conditionals(state):
    compatible={s:p for s,p in GRAMMAR_VIS.items() if all(v is None or v==s[i] for i,v in enumerate(state))}
    total=sum(compatible.values())
    if not total:raise ValueError('Visible tokens conflict with the fixed grammar')
    out={}
    for i,v in enumerate(state):
        if v is None:
            d=Counter()
            for s,p in compatible.items():d[s[i]]+=p/total
            out[i]=dict(d)
    return out

def vis_draw(d,rng):return rng.choices(list(d),weights=list(d.values()),k=1)[0]
def vis_sampler(mode,seed,threshold=COMMIT_THRESHOLD,initial=None):
    rng=random.Random(seed);state=list(initial) if initial else [None]*6;trace=[state.copy()];new=[]
    while None in state:
        dist=vis_conditionals(state)
        if mode=='parallel':selected=list(dist)
        elif mode=='AR':selected=[min(dist)]
        else:
            selected=[i for i,p in dist.items() if max(p.values())>=threshold]
            if not selected:selected=[rng.choice(list(dist))]
        updates={i:vis_draw(dist[i],rng) for i in selected}
        for i,value in updates.items():state[i]=value
        trace.append(state.copy());new.append(selected)
    return {'tokens':state,'rounds':len(new),'valid':tuple(state) in GRAMMAR_VIS,'trace':trace,'new':new}

fig,axes=plt.subplots(1,3,figsize=(13,5.2));traces={}
for ax,mode in zip(axes,['parallel','iterative','AR']):
    r=vis_sampler(mode,2);traces[mode]=r
    for row,state in enumerate(r['trace']):
        changed=[] if row==0 else r['new'][row-1]
        for col,value in enumerate(state):
            color='#edf0f2' if value is None else '#f8dc8b' if col in changed else '#cbdde8'
            ax.add_patch(Rectangle((col,-row),.94,.8,facecolor=color,edgecolor='white'))
            ax.text(col+.47,-row+.4,'?' if value is None else value,ha='center',va='center',fontsize=9)
        ax.text(-.15,-row+.4,str(row),ha='right',va='center',fontsize=9)
    ax.set(xlim=(-.5,6),ylim=(-6.5,1),title=f'{mode}: {r["rounds"]} rounds; valid={r["valid"]}');ax.axis('off')
fig.suptitle('Same fully masked input: gray unknown, gold newly committed, blue retained',fontsize=12)
fig.tight_layout();fig.savefig(OUT/'lab04-token-traces.png',dpi=160);plt.show();plt.close(fig)

records=[]
for mode in ['parallel','iterative','AR']:
    outputs=[vis_sampler(mode,VISUAL_SEED+i) for i in range(VISUAL_N)]
    records.append({'mode':mode,'valid_rate':sum(r['valid'] for r in outputs)/VISUAL_N,'mean_rounds':float(np.mean([r['rounds'] for r in outputs])),'cat_mass':sum(tuple(r['tokens'])==next(iter(GRAMMAR_VIS)) for r in outputs)/VISUAL_N})
fig,axes=plt.subplots(1,2,figsize=(10,3.6))
axes[0].bar([r['mode'] for r in records],[r['valid_rate'] for r in records],color=['#a3b4c2','#003262','#c18f16']);axes[0].axhline(.125,color='#a5563c',ls='--',label='one-shot exact 1/8');axes[0].set(ylim=(0,1.08),ylabel='Joint validity');axes[0].legend(fontsize=8)
axes[1].bar([r['mode'] for r in records],[r['mean_rounds'] for r in records],color=['#a3b4c2','#003262','#c18f16']);axes[1].set(ylabel='Dependency rounds (not wall-clock time)',ylim=(0,7))
fig.suptitle(f'{VISUAL_N} seeds; iterative threshold={COMMIT_THRESHOLD:g}',fontsize=12);fig.tight_layout();fig.savefig(OUT/'lab04-validity-rounds.png',dpi=160);plt.show();plt.close(fig)
assert abs(records[0]['valid_rate']-.125)<.03
if COMMIT_THRESHOLD>.5:assert records[1]['valid_rate']==1
assert records[2]['valid_rate']==1
payload={'kind':'exact two-sentence grammar, no language-model inference','seed':VISUAL_SEED,'n':VISUAL_N,'threshold':COMMIT_THRESHOLD,'traces':traces,'results':records,'legend':{'gray':'unknown','gold':'newly committed this round','blue':'retained; no remasking/revision in this scheduler'},'python':platform.python_version()}
(OUT/'lab04-results.json').write_text(json.dumps(payload,indent=2));print(json.dumps(records,indent=2))
LAB 04 · 遮挡文本:并行候选与迭代相容性的保存输出
已保存输出;运行地点及条件见本册说明。
LAB 04 · 遮挡文本:并行候选与迭代相容性的保存输出
已保存输出;运行地点及条件见本册说明。
查看保存的计算输出
[
  {
    "mode": "parallel",
    "valid_rate": 0.1325,
    "mean_rounds": 1.0,
    "cat_mass": 0.06825
  },
  {
    "mode": "iterative",
    "valid_rate": 1.0,
    "mean_rounds": 3.0,
    "cat_mass": 0.49375
  },
  {
    "mode": "AR",
    "valid_rate": 1.0,
    "mean_rounds": 6.0,
    "cat_mass": 0.51
  }
]

本轮运行与后续阅读

本地执行与云端复核均已完成。 2026-09-28,本册全部代码在本地CPU从空命名空间顺序执行;另通过Deepnote全本运行,状态为success,运行ID为 31d43ed5-a356-481c-9e72-9a48e0a078ab,完成时间 2026-09-28T15:30:48.794Z(UTC)。运行快照检查到本册图形输出且无失败代码块。token轨迹与合法率图已输出;4000种子一次并行0.1325、迭代1、AR1;轮数分别1/3/6。

云端运行使用Python 3.13数据科学环境。平台总用时包含启动、Notebook执行与输出保存,不作为算法速度基准;五本均为小规模CPU计算,不调用外部模型或付费API。上方静态图仍明确保留其本地生成来源,云端输出是另一次实际复核。学生修改参数后得到的是自己的新结果。

下面保留此前逐步计算与推导。历史本地数字保留原身份,可把图中一个关系追到对应公式。

什么是已知,什么还未知

None 表示遮挡位置;已有 token 必须保持不变。我们先找出与所有可见 token 相容的完整句子,再计算每个遮挡位置的条件边际。这是一个小型精确 oracle,可以排除学习误差,把注意力放在采样组织上。

随机遮挡是离散 token 的破坏方式,与对 token ID 加高斯噪声不同。下一格只展示一次前向遮挡和条件预测,不执行任何训练。

查看可执行代码
import itertools
import random
from collections import Counter

SENTENCES = {
    ("猫", "会", "喵", ",", "猫", "喵"): 0.5,
    ("狗", "会", "汪", ",", "狗", "汪"): 0.5,
}
LENGTH = 6
def show(state):
    return " ".join(token if token is not None else "[MASK]" for token in state)

def conditionals(state):
    compatible = {seq: p for seq,p in SENTENCES.items()
                  if all(token is None or token == seq[i] for i,token in enumerate(state))}
    total = sum(compatible.values())
    if total == 0:
        raise ValueError("Visible tokens contradict every sentence in this fixed model.")
    output = {}
    for i, token in enumerate(state):
        if token is None:
            dist = Counter()
            for seq,p in compatible.items():
                dist[seq[i]] += p/total
            output[i] = dict(dist)
    return output

source = next(iter(SENTENCES))
rng_corrupt = random.Random(10)
mask_rate = 0.7
corrupted = [None if rng_corrupt.random() < mask_rate else t for t in source]
print("clean:", show(source))
print("masked:", show(corrupted))
print("conditional predictions:", conditionals(corrupted))
print("fully masked predictions:", conditionals([None]*LENGTH))
查看保存的计算输出
clean: 猫 会 喵 , 猫 喵
masked: [MASK] [MASK] [MASK] [MASK] 猫 喵
conditional predictions: {0: {'猫': 1.0}, 1: {'会': 1.0}, 2: {'喵': 1.0}, 3: {',': 1.0}}
fully masked predictions: {0: {'猫': 0.5, '狗': 0.5}, 1: {'会': 1.0}, 2: {'喵': 0.5, '汪': 0.5}, 3: {',': 1.0}, 4: {'猫': 0.5, '狗': 0.5}, 5: {'喵': 0.5, '汪': 0.5}}

历史本地保存输出(2026-09-26;本轮执行记录见开篇)

clean: 猫 会 喵 , 猫 喵
masked: [MASK] [MASK] [MASK] [MASK] 猫 喵
conditional predictions: {0: {'猫': 1.0}, 1: {'会': 1.0}, 2: {'喵': 1.0}, 3: {',': 1.0}}
fully masked predictions: {0: {'猫': 0.5, '狗': 0.5}, 1: {'会': 1.0}, 2: {'喵': 0.5, '汪': 0.5}, 3: {',': 1.0}, 4: {'猫': 0.5, '狗': 0.5}, 5: {'喵': 0.5, '汪': 0.5}}

一次并行独立采样丢了什么?

四个内容位置都各自有两个等概率 token;“会”和逗号则确定。一次性独立采样因此产生 16 种等概率组合,只有 2 种属于原分布,合法率为 $2/16=1/8$。

问题不在每个位置的边际概率算错,而在把相依位置当成相互独立。下一格穷举这一步产生的完整分布,不能把“各位置概率准确”误写成“联合分布准确”。

查看可执行代码
marginals = conditionals([None]*LENGTH)
parallel_joint = {}
for candidate in itertools.product(*(list(marginals[i]) for i in range(LENGTH))):
    probability = 1.0
    for i, token in enumerate(candidate):
        probability *= marginals[i][token]
    parallel_joint[candidate] = probability
valid_mass = sum(p for seq,p in parallel_joint.items() if seq in SENTENCES)
print("parallel support size:", len(parallel_joint))
print("exact valid probability:", valid_mass)
print("one incompatible candidate:", next(show(s) for s in parallel_joint if s not in SENTENCES))
assert abs(sum(parallel_joint.values()) - 1) < 1e-12
assert valid_mass == 0.125
查看保存的计算输出
parallel support size: 16
exact valid probability: 0.125
one incompatible candidate: 猫 会 喵 , 猫 汪

历史本地保存输出(2026-09-26;本轮执行记录见开篇)

parallel support size: 16
exact valid probability: 0.125
one incompatible candidate: 猫 会 喵 , 猫 汪

一个可逐步检查的迭代规则

每轮预测所有仍被遮挡的位置。如果有至少 0.99 置信度的位置,先同时提交这些位置;否则只随机提交一个不确定位置,再重新计算条件分布。当前例子中,“会、逗号”先确定,随后选定一个内容 token,最后其他内容 token 都由相容性确定。

这是教学调度规则,不是从扩散方程推导的通用采样器。对于本册精确 oracle,阈值 0.99 只选中概率 1 的确定项。真实模型的高置信度可能错;换一个存在多种相关模式的分布,同样规则也不保证正确联合采样。

查看可执行代码
def draw(dist, rng):
    tokens = list(dist)
    return rng.choices(tokens, weights=[dist[t] for t in tokens], k=1)[0]

def parallel_once(rng, initial=None):
    state = list(initial) if initial is not None else [None]*LENGTH
    prediction = conditionals(state)
    for i, dist in prediction.items():
        state[i] = draw(dist, rng)
    return tuple(state), 1, [show(state)]

def iterative(rng, initial=None, threshold=0.99):
    state = list(initial) if initial is not None else [None]*LENGTH
    trace = [show(state)]
    rounds = 0
    while None in state:
        prediction = conditionals(state)
        confident = [i for i,dist in prediction.items() if max(dist.values()) >= threshold]
        selected = confident if confident else [rng.choice(list(prediction))]
        # 所有 selected 都使用本轮旧状态;不会在同一轮内偷偷传递刚生成的 token。
        updates = {i: draw(prediction[i], rng) for i in selected}
        for i, token in updates.items():
            state[i] = token
        rounds += 1
        trace.append(show(state))
    return tuple(state), rounds, trace

def autoregressive(rng, initial=None):
    state = list(initial) if initial is not None else [None]*LENGTH
    trace = [show(state)]
    rounds = 0
    for i in range(LENGTH):
        if state[i] is None:
            state[i] = draw(conditionals(state)[i], rng)
            rounds += 1
            trace.append(show(state))
    return tuple(state), rounds, trace

for name, sampler in [("parallel once", parallel_once), ("iterative", iterative), ("AR", autoregressive)]:
    result, rounds, trace = sampler(random.Random(4))
    print("\n", name, "rounds=", rounds, "valid=", result in SENTENCES)
    for step, state in enumerate(trace):
        print(step, state)
查看保存的计算输出
 parallel once rounds= 1 valid= True
0 猫 会 喵 , 猫 喵

 iterative rounds= 3 valid= True
0 [MASK] [MASK] [MASK] [MASK] [MASK] [MASK]
1 [MASK] 会 [MASK] , [MASK] [MASK]
2 [MASK] 会 [MASK] , [MASK] 喵
3 猫 会 喵 , 猫 喵

 AR rounds= 6 valid= True
0 [MASK] [MASK] [MASK] [MASK] [MASK] [MASK]
1 猫 [MASK] [MASK] [MASK] [MASK] [MASK]
2 猫 会 [MASK] [MASK] [MASK] [MASK]
3 猫 会 喵 [MASK] [MASK] [MASK]
4 猫 会 喵 , [MASK] [MASK]
5 猫 会 喵 , 猫 [MASK]
6 猫 会 喵 , 猫 喵

历史本地保存输出(2026-09-26;本轮执行记录见开篇)


 parallel once rounds= 1 valid= True
0 猫 会 喵 , 猫 喵

 iterative rounds= 3 valid= True
0 [MASK] [MASK] [MASK] [MASK] [MASK] [MASK]
1 [MASK] 会 [MASK] , [MASK] [MASK]
2 [MASK] 会 [MASK] , [MASK] 喵
3 猫 会 喵 , 猫 喵

 AR rounds= 6 valid= True
0 [MASK] [MASK] [MASK] [MASK] [MASK] [MASK]
1 猫 [MASK] [MASK] [MASK] [MASK] [MASK]
2 猫 会 [MASK] [MASK] [MASK] [MASK]
3 猫 会 喵 [MASK] [MASK] [MASK]
4 猫 会 喵 , [MASK] [MASK]
5 猫 会 喵 , 猫 [MASK]
6 猫 会 喵 , 猫 喵

重复采样:合法率与轮次是两个量

下一格使用各自独立、固定的随机流生成 4,000 个样本。首先检查合法率及两种合法句子的质量分配,再报告调度轮次。

“3 轮”和“6 轮”仅是这个 Python 程序的依赖轮次,不是 GPU 测速。真实比较还涉及一次前向计算的长度、缓存、批量、网络结构、硬件和样本质量;本册没有测出扩散语言模型比自回归更快。

查看可执行代码
SAMPLE_COUNT = 4000
print("sampler | valid rate | cat mass | dog mass | mean rounds")
for index, (name, sampler) in enumerate([
    ("parallel once", parallel_once), ("iterative", iterative), ("AR", autoregressive)
]):
    rng = random.Random(20260926 + index)
    counts = Counter()
    round_sum = 0
    for _ in range(SAMPLE_COUNT):
        output, rounds, _ = sampler(rng)
        counts[output] += 1
        round_sum += rounds
    valid = sum(counts[s] for s in SENTENCES) / SAMPLE_COUNT
    cat_mass, dog_mass = [counts[s]/SAMPLE_COUNT for s in SENTENCES]
    print(name, f"{valid:.4f}", f"{cat_mass:.4f}", f"{dog_mass:.4f}", f"{round_sum/SAMPLE_COUNT:.2f}")
    if name == "parallel once":
        assert abs(valid - valid_mass) < 0.025
    else:
        assert valid == 1.0 and abs(cat_mass - 0.5) < 0.04

prompt = ["狗", None, None, None, None, None]
output, rounds, trace = iterative(random.Random(8), initial=prompt)
assert output[0] == "狗" and output in SENTENCES
print("\nFixed condition:", show(prompt), "->", show(output), "rounds=", rounds)
contradictory = ["猫", None, "汪", None, None, None]
try:
    conditionals(contradictory)
except ValueError as error:
    print("Contradiction detected:", str(error))
else:
    raise AssertionError("Contradictory context must not be silently repaired.")
查看保存的计算输出
sampler | valid rate | cat mass | dog mass | mean rounds
parallel once 0.1235 0.0625 0.0610 1.00
iterative 1.0000 0.4925 0.5075 3.00
AR 1.0000 0.4943 0.5058 6.00

Fixed condition: 狗 [MASK] [MASK] [MASK] [MASK] [MASK] -> 狗 会 汪 , 狗 汪 rounds= 1
Contradiction detected: Visible tokens contradict every sentence in this fixed model.

历史本地保存输出(2026-09-26;本轮执行记录见开篇)

sampler | valid rate | cat mass | dog mass | mean rounds
parallel once 0.1235 0.0625 0.0610 1.00
iterative 1.0000 0.4925 0.5075 3.00
AR 1.0000 0.4943 0.5058 6.00

Fixed condition: 狗 [MASK] [MASK] [MASK] [MASK] [MASK] -> 狗 会 汪 , 狗 汪 rounds= 1
Contradiction detected: Visible tokens contradict every sentence in this fixed model.

只改一个条件:提交门槛

把 iterative 的 threshold 从 0.99 改成 0.5。先预测:哪些位置会在同一轮一起提交?它会不会退化成一次并行独立采样?再固定种子重复试验,报告合法率和轮次。

另一个独立问题是给定“狗”作为条件;上格显示在本模型中条件足以决定所有剩余内容,不能把这种很容易的补全与完全无条件生成混在一起排名。若给定相互矛盾的可见 token,程序直接报错,不静默改写用户条件。

查看可执行代码
rng_threshold = random.Random(20260930)
counts_threshold = Counter()
for _ in range(SAMPLE_COUNT):
    output, rounds, _ = iterative(rng_threshold, threshold=0.5)
    counts_threshold[output] += 1
    assert rounds == 1
validity_low_threshold = sum(counts_threshold[s] for s in SENTENCES)/SAMPLE_COUNT
print("threshold=0.5: observed valid rate =", round(validity_low_threshold, 4))
print("exact one-shot independent valid rate =", valid_mass)
assert abs(validity_low_threshold-valid_mass) < 0.025
print("All exact support, seeded sampling, condition and scheduler checks passed.")
查看保存的计算输出
threshold=0.5: observed valid rate = 0.1185
exact one-shot independent valid rate = 0.125
All exact support, seeded sampling, condition and scheduler checks passed.

历史本地保存输出(2026-09-26;本轮执行记录见开篇)

threshold=0.5: observed valid rate = 0.1185
exact one-shot independent valid rate = 0.125
All exact support, seeded sampling, condition and scheduler checks passed.

与扩散语言模型的联系和距离

LLaDA 原论文以 token 遮挡和恢复组织语言建模,使用网络学习遮挡位置的预测。本册只借用“破坏—条件预测—逐步揭示”的解释结构。

我们没有训练损失优化、时间条件网络、大词表、重遮挡或论文采样器;轮次与合法率只是手工有限世界的计算结果。当前例子能说明并行候选之间仍需相容性,不能支持关于大语言模型能力或速度的结论。

从课堂展开:推导、反例与变量实验

先完成上面的有限例子,再按自己尚未弄清的问题选择推导。已有代码只覆盖本册明确列出的计算;后面的研究练习是可继续提出的实验,不是已经运行的结果。

原有解释按连续主题拆到下方;本入口继续保留。

保持条件、计算损失,再评价迭代成本

离散状态先定义破坏规则:对 a、b、[MASK] 的单步转移,可令 a/b 各以0.8保留、0.2变为[MASK],遮罩保持自身。连续 n 步保留率为0.8^n;token 编号的数值大小没有参与。

训练问题应固定可见提示,只遮住回答;监督原句用于评分,不作为未知位置的输入。在线性遮罩概率 t 的一种目标中,只对 mask 位置累加 −(1/t)log pθ(x_i|x_t)。这里说明 LLaDA 的目标结构;本册 oracle 代码没有优化该损失,也没有训练时间条件网络。

前面的代码已经精确展示一次并行独立抽样合法率1/8,而本例迭代规则在更新条件后恢复相容性。它只支持这个有限分布的结论。比较3轮与6轮时,还缺每轮网络工作、序列长度、缓存、批量、设备与同等质量的延迟测量;轮次减少不能直接报速度胜负。

编辑评价:保持、改变与多样性分开检查

原图差异小,是否就代表编辑做得好?

一个什么也不改的系统,在像素差异指标上可能表现很好,却没有完成编辑。一个大幅重画的系统可能满足新描述,却破坏了应保留的背景。编辑评价因此至少要拆开目标完成、非目标保持与整体协调;对存在多解的任务,还要观察合理输出的覆盖。Palette 使用多种自动评价与人工评价研究图像到图像结果,提示我们不同指标关注不同性质。课堂讨论可以设计一个“完全复制原图”的简单基线,检查评价方案是否会错误奖励它。

追问:评价项越来越多,会不会只是让我们更容易挑好看的结果?

有这个风险,所以应在看结果前说明每项服务哪个主张,并保留固定样本和失败例。多个指标不是任意挑选的菜单,而是检查不同要求的工具。比如目标颜色改变和背景保持应分别报告,不能因为一项好就隐藏另一项差。一个复制原图的基线还能帮助检验指标是否真的辨认了任务完成程度。

返回当前页说明

相关阅读:Palette: Image-to-Image Diffusion Models

动手:只改变条件注入的位置

是先验、起点还是遮罩造成了变化?

设计一个小型修补观察:固定同一幅图、遮罩、模型与采样预算,对照已知区域是否在每步参与更新。记录已知区域误差、缺口边界和若干随机种子的输出,再讨论条件信息进入过程的位置为何重要。若计算资源不足,可先用低维离散网格和预存轨迹练习读图;必须标明这是机制演示,不是 RePaint 的性能复现。扩展时再加入不同遮罩大小和局部回跳,避免同时更换模型、预算与遮罩而无法解释差异。

追问:如果这次两组看起来差不多,实验是不是没有意义?

仍然有意义。它说明在当前图片、遮罩和预算下,差异可能不明显;也可能观察方式太粗。先检查操作是否真的改变、数据是否保存,再看多个固定种子和更敏感的边界指标。不能因为一次不符合预期就更换所有设置。把结论限制在已比较的范围,反而能为下一次选取更有区分力的条件提供依据。

返回当前页说明

相关阅读:RePaint: Inpainting using Denoising Diffusion Probabilistic Models

视频评价:流畅、遵循条件与物理一致分开问

一段令人信服的视频能支持多大的结论?

视频扩散论文使用分布指标和具体任务评价研究样本质量,但一个总体分数不能替代所有行为检查。课堂可以将观察分为单帧质量、跨帧身份与运动、文字条件符合,以及需要外部知识的因果或物理约束。每一项都对应不同失败:画面清晰但对象增减、运动流畅但动作错误,或表面连续却违反几何关系。本页提出教学性的检查方法,不宣称这些指标已经充分定义理解。若要提出世界建模能力的主张,需要进一步设计有区分力的任务与反例。

追问:视频看起来符合物理规律,为什么不能说它理解了物理?

可以说它在这个例子中产生了符合某些规律的表现,但更强结论需要跨场景、干预和反例检验。外观相似可能来自训练分布中的相关性,也可能包含可迁移结构;单个视频不足以区分。先明确你说的物理理解要支持什么预测或行动,再设置能够区分两种解释的测试,才知道已有证据支持到哪里。

返回当前页说明

相关阅读:Video Diffusion Models

D3PM:用转移矩阵写出破坏规则

每一步,一个类别可能转到哪里?

设状态只有 a、b 和 [MASK]。一个简单教学转移让 a、b 各以 0.8 概率保持原值、以 0.2 概率进入遮罩,而遮罩以概率 1 保持遮罩;每行概率和均为 1。连续应用这些矩阵会逐渐减少可见信息。D3PM 的一般框架允许更丰富的离散转移,本例只取吸收状态这一种。理解矩阵时要先固定行列约定,再区分单步转移与累计转移:一次保留率为 0.8,不表示经过许多步仍保留 80%。

追问:每一步只遮掉 20%,为什么最后可以几乎全被遮住?

因为未被遮住的 token 下一步仍面临再次被遮住的机会,而已经进入遮罩的状态不会在前向过程返回。经过 n 步,原 token 的保留概率是 0.8 的 n 次方,会随步数下降。这里假设每步采用相同规则;实际模型可以改变日程。关键是累计破坏由多个条件转移组成,不能把单步比例直接当作最终比例。

返回当前页说明

相关阅读:Structured Denoising Diffusion Models in Discrete State-Spaces

训练信号:只对被遮住的位置算损失

为什么不能奖励网络复制已经可见的词?

训练时保留原序列 x₀,同时构造受损序列 xₜ。模型读取 xₜ,为被遮住的位置输出词表概率;交叉熵检查它给原 token 分配了多少概率。未遮住的位置是上下文,不应通过简单复制贡献主要训练奖励。LLaDA 的目标使用指示函数选择遮罩位置,再按时间进行加权。可以把一个五 token 句子的损失展开,逐项标记哪些参与梯度。这比只看“交叉熵”三个字更能说明训练问题,也能帮助学生发现实现中的答案泄漏或损失掩码错误。

追问:模型训练时不是有原句吗,为什么说它不知道答案?

原句用于提供监督标签,受损句子才是模型前向计算的输入。只要实现正确,原词不会通过输入或其他旁路泄漏给被遮住的位置。训练比较预测与标签,随后更新参数;这与考试结束后核对答案类似,不能把批改者持有答案误认为作答时已经看到。检查代码时应特别核对输入构造与损失掩码是否独立正确。

返回当前页说明

相关阅读:Large Language Diffusion Models

并行预测,不等于各位置真正独立

两个空位都合理,放在一起为什么可能不合理?

考虑一句含两个相互制约空位的句子,每个位置单独都有多个合理候选。如果同一轮在不完整上下文下独立采样,组合可能出现语法或语义不一致。遮罩扩散通过多轮恢复,让已确定部分进入后续条件;这有助于表达依赖,但不保证每次组合都正确。不能把“每轮同时计算多个位置”误讲成联合分布被精确地一次分解成独立项。教学中可以用主谓一致或数量对应的小语法展示矛盾,观察生成日程怎样改变信息流。

追问:这不就说明并行生成一定不如自回归吗?

不能这样推断。例子揭示一次独立采样可能忽略依赖,而迭代、条件预测和不同采样器正是在处理这个问题。自回归也会把早期错误传到后面,并有自己的顺序约束。比较应固定任务、模型与计算预算,观察错误类型和整体表现。这个机制分析帮助提出实验问题,并不能独自证明某个模型家族在所有场景中的优劣。

返回当前页说明

相关阅读:Simple and Effective Masked Diffusion Language Models

动手:用小语法观察生成顺序

在可枚举的语言里,怎样检查联合关系?

设计一个只有少量合法句子的玩具语法,让两个相隔位置必须满足配对关系。可以精确列出合法序列及其概率,再比较一次独立填空、逐位置生成和多轮遮罩恢复的输出分布。记录非法组合率、覆盖与调用次数,而不只展示一条流畅句子。这个实验不复现 LLaDA 的大规模能力,也不能代表真实语言难度;它把依赖关系与生成顺序隔离出来,帮助学生理解并行预测为何仍可能需要迭代。后续运行真实模型时,再保留同样的观察问题。

追问:这么小的实验,能对大语言模型得出什么结论?

它不能直接预测大模型排名,但能检验我们的机制解释是否自洽。例如,是否把边缘概率误当成联合概率,是否漏记多轮调用,是否把候选当成最终输出。这些问题在大系统中更难看清。小实验给出可核查的认识起点,真实模型还需要另行评测;两者的作用是相互补充,而不是用简单例子替代规模证据。

返回当前页说明

相关阅读:Simple and Effective Masked Diffusion Language Models