通过训练代码来理解DLLM扩散语言模型

1. 引入

Diffusion-LLM(DLLM) 是扩散语言模型(Diffusion Large Language Model),它与LLM有什么区别呢?

(1)共同点:都是以Transformer结构为主的模型

(2)差异点:

  • LLM是 "从左到右的自回归(Autoregressive)生成" 逻辑,就是基于已生成的左侧上下文,逐一生成下一个令牌,直到触发终止符;
  • DLLM不是预测下一个token。而是采用迭代去噪的扩散生成:生成过程分为前向腐蚀和反向去噪两个阶段,核心是 "从全噪声 / 掩码序列中,通过多步迭代逐步恢复出有效序列",生成过程是双向、并行、可迭代修正的。

听上去有些抽象,我们下面从代码层面上来理解DLLM。

2. DLLM最简化代码

2.1. DLLM简化代码

下面是一个DLLM最简单的训练代码,包括了简单的数据集、处理、训练、生成过程:

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim

# ===================== 超参数 =====================
vocab_size = 1000
embed_dim = 64
seq_len = 16
hidden_dim = 128
timesteps = 20
batch_size = 4
lr = 1e-3

# ===================== 简易词汇表与英文句子 =====================
# 真实英文句子(已截断/补齐到 seq_len)
sentences = [
    "i love deep learning and diffusion models",
    "language models can generate coherent text",
    "diffusion models work by denoising gradually",
    "transformer is the backbone of modern llm",
    "we train a diffusion language model today"
]

# 简易分词(按空格)+ 构建词表
words = list({w for s in sentences for w in s.split()})
word2idx = {w: i+1 for i, w in enumerate(words)}  # 0 = pad
idx2word = {i: w for w, i in word2idx.items()}
vocab_size = len(word2idx) + 1

# 句子转 token,并补齐到 seq_len
def tokenize(s):
    tokens = [word2idx[w] for w in s.split() if w in word2idx]
    tokens = tokens[:seq_len]
    tokens += [0] * (seq_len - len(tokens))
    return torch.tensor(tokens)

dataset = torch.stack([tokenize(s) for s in sentences])
print('dataset.shape={0}'.format(dataset.shape))  # torch.Size([5, 16])

# ===================== 嵌入层 =====================
embedding = nn.Embedding(vocab_size, embed_dim)

# ===================== Diffusion 去噪模型 =====================
class DenoiseTransformer(nn.Module):
    def __init__(self):
        super().__init__()
        self.time_emb = nn.Embedding(timesteps, embed_dim)
        self.layer = nn.TransformerEncoderLayer(
            d_model=embed_dim, nhead=2, dim_feedforward=hidden_dim,
            batch_first=True, activation='gelu'
        )
        self.out = nn.Linear(embed_dim, embed_dim)

    def forward(self, x, t):
        # x: [B, L, D]
        # t: [B]
        t_emb = self.time_emb(t).unsqueeze(1)  # [B,1,D]
        x = x + t_emb
        x = self.layer(x)
        return self.out(x)

model = DenoiseTransformer()
opt = optim.Adam(model.parameters(), lr=lr)

# ===================== 扩散前向过程 =====================
def forward_process(x0, t):
    noise = torch.randn_like(x0)
    # 简单线性调度
    alpha = torch.linspace(0.05, 0.99, timesteps)[t]
    alpha = alpha.view(-1,1,1)
    xt = torch.sqrt(alpha) * x0 + torch.sqrt(1 - alpha) * noise
    return xt, noise

# ===================== 训练 =====================
print("开始训练 DLLM...\n")
for step in range(2000):
    idx = torch.randint(0, len(dataset), (batch_size,))
    x_ids = dataset[idx]
    x0 = embedding(x_ids)  # 干净的词嵌入 [B,L,D]

    t = torch.randint(0, timesteps, (batch_size,))
    xt, noise = forward_process(x0, t)

    pred_noise = model(xt, t)
    loss = (pred_noise - noise).pow(2).mean()

    opt.zero_grad()
    loss.backward()
    opt.step()

    if step % 200 == 0:
        print(f"step {step:04d} | loss {loss.item():.4f}")

# ===================== DLLM 反向生成(采样)=====================
print("\n=== DLLM 生成句子 ===")
x = torch.randn(1, seq_len, embed_dim)  # 从纯噪声开始
print('x.shape={0}'.format(x.shape)) # x.shape=torch.Size([1, 16, 64])

# 刚开始全是噪声
logits = x @ embedding.weight.T
pred_ids = logits.argmax(-1).squeeze().tolist()
pred_words = [idx2word.get(i, "<pad>") for i in pred_ids if i != 0]
print("初始噪声:", " ".join(pred_words))


for t_step in reversed(range(timesteps)):
    with torch.no_grad():
        pred_n = model(x, torch.tensor([t_step]))
    alpha = torch.linspace(0.05, 0.99, timesteps)[t_step]
    # 去噪一步
    x = (x - torch.sqrt(1 - alpha) * pred_n) / torch.sqrt(alpha)

# 映射回单词
logits = x @ embedding.weight.T
pred_ids = logits.argmax(-1).squeeze().tolist()
pred_words = [idx2word.get(i, "<pad>") for i in pred_ids if i != 0]

print("生成句子:", " ".join(pred_words))

运行后,程序输出:

bash 复制代码
dataset.shape=torch.Size([5, 16])
开始训练 DLLM...

step 0000 | loss 1.3353
step 0200 | loss 0.7625
step 0400 | loss 0.6536
step 0600 | loss 0.5462
step 0800 | loss 0.5811
step 1000 | loss 0.4207
step 1200 | loss 0.3732
step 1400 | loss 0.5663
step 1600 | loss 0.3291
step 1800 | loss 0.3746

=== DLLM 生成句子 ===
x.shape=torch.Size([1, 16, 64])
初始噪声: of is by models we train of love text deep is learning the the today work
生成句子: by language language language language models by by by by

可以从中看到,DLLM最有特色的地方,是:针对输入的噪声x(一个句子),进行去噪后,直接生成一个最终的句子。这个过程,一开始全是随机噪声x,模型一步步去掉噪声,最后直接输出一整句话。整个过程是并行生成,一步一步去噪,不是预测下一个词。

而普通LLM(GPT、LLaMA),输入比如是"I love",输出则是预测下一个词 deep;再输入:I love deep,再输出:learning。LLM是逐词生成,串行,自回归模型。

2.2 DLLM特点

(1)前向扩散(加噪)

python 复制代码
def forward_process(x0, t):
    noise = torch.randn_like(x0)
    xt = sqrt(alpha)*x0 + sqrt(1-alpha)*noise
    return xt, noise

(2)预测噪声

python 复制代码
pred_noise = model(xt, t)

(3)噪声损失 MSE

python 复制代码
loss = (pred_noise - noise).pow(2).mean()

(4)反向采样去噪生成

python 复制代码
x = torch.randn(...)  # 从纯噪声开始
for t in reversed(...):
    x = (x - sqrt(1-alpha)*pred_n) / sqrt(alpha)

3. 区别LLM与DLLM

根据上面的解释,我们再来理解这几个差异点,就容易一些:

  1. 生成内容的方式不同

    • LLM是 "从左到右的自回归(Autoregressive)生成" 逻辑,就是基于已生成的左侧上下文,逐一生成下一个令牌,直到触发终止符;
    • DLLM不是预测下一个token。而是采用迭代去噪的扩散生成:生成过程分为前向腐蚀和反向去噪两个阶段,核心是 "从全噪声 / 掩码序列中,通过多步迭代逐步恢复出有效序列",生成过程是双向、并行、可迭代修正的。
  2. 训练的目标不同

    • LLM训练目标主要是预测下一个单词,损失函数一般为CrossEntropyLoss
    • DLLM训练目标是预测加进去的高斯噪声,损失函数一般为 MSE(预测噪声 - 真实噪声),比如上面代码中loss = (pred_noise - noise).pow(2).mean()
  3. 数据处理方式不同

    • LLM直接对 token ID 建模,输出单词概率。LLM 不会对连续向量做扩散去噪。
    • DLLM对 词嵌入向量(continuous embedding) 加噪、去噪。最后才映射回单词。

4. 总结

DLLM:

  • ✅ 使用扩散
  • ✅ 对句子向量加噪
  • ✅ 训练模型预测噪声
  • ✅ 从噪声生成整句
  • ✅ 不是自回归
  • ✅ 不是预测下一个词

普通 LLM

  • ❌ 无扩散
  • ❌ 无加噪
  • ❌ 无去噪
  • ✅ 逐词生成

Diffusion = 加噪声 + 去噪声 的整套流程

5. 参考

  1. Large Language Diffusion Models. https://arxiv.org/abs/2502.09992
  2. https://github.com/Diffusion-LLM/Awesome-DiffusionLLM
  3. https://zhuanlan.zhihu.com/p/1913691243197752405
相关推荐
beyond阿亮2 小时前
OpenClaw在Windows上接入飞书完整指南
人工智能·windows·ai·openclaw
多年小白2 小时前
Anthropic发布Mythos模型:为什么网络安全板块先跌为敬
网络·人工智能·科技·ai编程
爱丽_2 小时前
多因素最优解到梯度下降:AI 训练的数学主线
人工智能
网络工程小王2 小时前
【Python数据分析基础】
大数据·数据库·人工智能·学习
skilllite作者2 小时前
开源项目推荐SkillLite,项目取得阶段性成果总结
人工智能
二十雨辰2 小时前
[RAG]-智能体开发
人工智能·ai
第七页独白2 小时前
全星研发项目管理 APQP 软件系统:驱动高端制造研发数智化升级
人工智能
FluxMelodySun2 小时前
机器学习(二十七) 降维:度量学习与随机梯度下降法求解
人工智能·学习·机器学习
蒸汽求职2 小时前
【蒸汽教育求职干货】OPT只剩3个月还没找到工作,怎么办?——留学生IT求职的“紧急预案”
人工智能·经验分享·面试·职场和发展·美国求职