第9章 生成式深度学习:从概率中创造新内容
本章技术主题: 自回归、潜空间与扩散模型的生成机制。
本章技术实验: 固定条件或随机种子,观察自回归、潜空间和扩散生成的变化。
家庭项目: “制作家庭记忆故事盒”是本章主项目;阳台植物科普卡是低隐私替代项目。
跨章位置: 接收第7章的来源编号与检索证据,交付带事实分层、授权和撤回记录的作品。
技术主线: 训练数据分布 → 条件与随机起点 → 生成模型逐步采样 → 候选作品 → 真实性与授权检查。
学习目标
学完本章,你将能够:
- 区分分类模型与生成模型,说明生成并非简单查表复制,同时可能记忆或近似复现训练样本;
- 用通俗语言比较自回归生成、潜空间生成与扩散去噪;
- 通过固定条件和随机种子,观察生成结果的可控性与多样性;
- 从条件符合度、伪影、事实分层、材料授权、撤回要求和发布说明判断作品能否使用。
项目导入
分类模型从预设类别中选择结果,生成模型则要产生新的文字、图像或声音。它学习训练数据中复杂的概率分布,再从这个分布中采样。相同条件可能得到不同作品,不同条件也可能得到相似结果。生成内容看起来完整,并不表示其中的文字、结构和事实都正确。
判别式模型关注“输入属于什么”或“数值是多少”,生成式模型关注“可能的数据样本怎样产生”。二者都可以使用深度神经网络,区别主要在学习目标和输出方式,不在于界面是否带有生成按钮。调用一个通用模型写宣传语属于生成应用;继续追问训练目标、概率分布和采样过程,才进入本章的技术本质。
本章的正式项目是“制作家庭记忆故事盒”,成品必须有来源。学生只使用家庭成员明确授权、已经脱敏的照片副本和口述文字,为每份材料建立来源编号;作品把“照片中可直接看到的事实”“讲述者的口述记忆”和“模型为连贯表达生成的连接想象”分层呈现,不能把三者混写成已经证实的历史事实。参与者可以在制作和展示阶段撤回材料,项目必须能按来源编号找到并移除相关内容。
为了观察图像生成的条件控制与随机性,封面实验不使用人物照片,而生成“旧收音机、两张旧车票、木桌、留白、无文字”的记忆物件画面;随后固定种子,把“两张车票”改为“三张车票”。项目不做人脸识别、身份推断、声音克隆或模仿亲属声线,也不把私人作品默认公开。原“阳台植物观察科普卡”保留为低隐私替代项目:不能取得家庭材料授权的学生,可用同样的条件、种子和采用判断完成植物卡。
本章成果
- 一份按照片事实、口述记忆和模型连接想象分层,并带来源编号的故事盒;
- 一组有条件、种子和生成记录的无人物封面候选;
- 一张“数据分布—随机起点—逐步生成—候选”的机制图;
- 一份自回归、潜空间与扩散方法的比较记录;
- 一张包含授权、撤回、来源、事实分层、相似风险与发布说明的作品判断卡。
考点提示
生成模型学习的是数据模式和概率分布,不保存一套可检索的标准答案。自回归模型逐个产生后续元素;变分自编码器等模型在潜空间采样并解码;扩散模型从噪声出发逐步去噪。条件控制生成方向,随机种子帮助复现起点,但不能保证作品正确、原创或可发布。
第一节 用授权材料制作有来源的家庭记忆故事盒
一、把创作要求分成条件和验收
故事盒的材料先登记为三类:照片副本记录来源编号、授权范围和可见事实;口述文字记录讲述者、记录日期和允许使用范围;模型生成内容记录模型版本、条件和随机种子。姓名、住址、证件、联系方式、车牌、精确地点等无关信息先删除。照片中出现人物并不授权系统识别其身份;本项目优先裁取物件,不做人脸识别。
正文必须显式分层。例如“照片事实/P-01:桌面上有一台旧收音机”和“口述记忆/O-01:讲述者说它曾陪伴一家人听晚间节目”可以并列保留;“模型连接想象/G-01:旋钮转动时,房间仿佛又安静下来”只能标为创作性连接。生成条件不是历史事实来源,语言自然也不能把“仿佛”改写成“当年确实如此”。
封面条件分为三类。内容条件规定“旧收音机、两张旧车票和木桌”;形式条件规定横向4:3、低饱和插画和右侧留白;禁止条件规定不得出现人物、人脸、姓名、地址、号码、品牌、伪文字和未经授权角色。验收表检查物件数量、结构、留白、伪文字和相似风险。
二、与开发与学习AI共同设计最小实验
应用中的AI包括整理故事草稿的文字生成模型和生成无人物封面的图像模型。开发与学习AI帮助学生把模糊创意改成可比较的条件、建立来源与生成记录、解释采样参数和检查结果。学生负责取得授权、核对事实、标注想象、响应撤回以及决定是否展示。
作品:家庭记忆故事盒封面,横向4:3,不使用人物材料。
固定条件:低饱和插画,木桌上的旧收音机和两张旧车票,右侧留白。
禁止内容:人物、人脸、姓名、地址、号码、品牌、现成角色和自动生成文字。
实验A:条件和模型固定,只改变随机种子,生成4张。
实验B:种子和模型固定,只把“两张车票”改为“三张车票”。
记录:条件、种子、生成时间、物件数量、伪影、相似风险和是否采用。
学生不必手写模型代码,却要能在完整Notebook中找到条件怎样编码、随机噪声在哪里产生、模型怎样逐步更新样本以及图像怎样保存。规划中的数字资源应提供小规模教学模型和预训练推理两条路径;前者观察机制,后者完成作品。
两条路径不能混为一次实验。教学模型使用小规模授权数据,能看到训练损失和中间样本,但生成质量有限;预训练模型生成质量较高,学生通常看不到完整训练数据和训练过程。记录中应分别写“我们亲自训练了什么”和“我们调用了什么”,不能因会调用预训练模型就声称完成了大模型训练。
三、保留候选而不是只展示最好结果
第一次生成四张图,可能出现车票数量不符、收音机旋钮和刻度断裂、留白被占满或票面出现似字非字纹理。故事草稿还可能把口述内容改成照片事实,或擅自补出年代、地点和人物关系。全部候选、分层文本和参数都应保存;只展示最好的一份会隐藏模型的随机性,也无法检查事实层是否被改写。
首轮记录可以采用五项简单尺度:条件符合、结构完整、可编辑、事实分层、授权有效。每项用“通过、需修改、不通过”标记并写具体位置。评分不用于证明艺术价值,而用于比较同一任务的不同生成结果。
第二节 比较自回归、潜空间与扩散去噪
一、三种生成路线解决同一个问题
自回归模型把已经生成的部分作为条件,预测下一个词元、像素块或音频单元,再把新结果加入序列。它适合解释语言模型逐词元生成,也可用于图像和声音。前面生成的错误会成为后续条件,因此可能逐步累积。
自编码器先把输入压缩到较短表示,再从表示重建数据。变分自编码器让潜空间具有可采样的连续结构,可以在两个点之间插值,观察形状或风格平滑变化。潜空间不是人手设计的属性表,某个方向不一定能直接命名。
潜空间方法训练时既要求重建结果接近输入,也约束潜变量分布便于采样。压缩过强会丢失细节,约束过弱又可能形成难以连续采样的空洞。课堂通过在两个潜向量之间取若干中间点,观察生成图怎样渐变,而不是把潜空间解释为可随意拨动的“创意按钮”。
扩散模型训练时逐步向数据加入噪声,并学习预测噪声或去噪方向;生成时从随机噪声开始,多步去噪形成样本[20]。文字等条件会影响每一步朝什么方向更新。一次去噪不是“从图库找相似图”,而是模型根据学到的分布估计当前噪声样本应如何变化。
许多图像系统会先把图片压到潜空间,在较小的潜表示上扩散,再解码回像素,这称为潜扩散。它降低计算量,但不改变“逐步去噪采样”的核心。条件引导越强,结果通常越贴近文字,变化空间也可能减小,甚至出现结构僵硬或伪影;条件强度应像其他变量一样通过对照实验选择。
| 路线 | 起点 | 生成动作 | 课堂观察重点 |
|---|---|---|---|
| 自回归 | 已有序列或起始标记 | 预测下一个元素 | 顺序、条件和错误累积 |
| 潜空间 | 一个潜变量 | 解码为完整样本 | 插值和整体结构 |
| 扩散 | 随机噪声 | 多步去噪 | 随机起点和逐步形成 |
二、读懂扩散Notebook的完整链
规划中的教学Notebook应按六段组织:加载经授权的小图数据并归一化;随机选择噪声强度;把原图与噪声混合;让小型神经网络预测加入的噪声;依据预测误差训练;从固定种子噪声开始反复去噪并保存中间图。
训练时的目标不是判断图片类别,而是让预测噪声接近真实加入的噪声。若用(x_0)表示原图、(\epsilon)表示随机噪声、(t)表示噪声阶段,可以把加噪写成:
[ x_t=\sqrt{\bar{\alpha}_t}x_0+\sqrt{1-\bar{\alpha}_t}\epsilon ]
学生不需要推导公式,但要读出两件事:阶段较早时原图成分多,阶段较晚时噪声成分多;网络接收当前带噪样本和阶段信息,学习预测噪声。采样过程按多个阶段反向更新,最后形成候选图。
完整Notebook必须显示数据样例、带噪程度、训练损失、固定种子的中间去噪图和最终样本。只给一个生成按钮而隐藏这些步骤,不足以支撑本章学习。
训练损失下降表示模型在当前训练目标上改善,不等于作品质量、事实或授权已经合格。评价至少分三层:模型层观察损失和固定种子样本;作品层检查条件符合、结构和多样性;使用层检查事实、来源、风险和发布场景。三层结论不能用一个“生成成功”代替。
下面的微型条件扩散实验在二维点上完整展示“加噪—学习噪声—按条件去噪”。类别0的数据中心在左侧,类别1在右侧;类别独热向量作为条件输入。二维点不是故事盒封面图片,但训练和反向采样与更大扩散模型共享核心逻辑,几秒到数分钟即可观察。
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow import keras
tf.keras.utils.set_random_seed(7)
rng = np.random.default_rng(7)
count, steps = 2400, 30
labels = rng.integers(0, 2, size=count)
centers = np.array([[-1.2, 0.0], [1.2, 0.0]], dtype="float32")
clean = centers[labels] + rng.normal(0, 0.25, size=(count, 2))
clean = clean.astype("float32")
betas = np.linspace(0.0005, 0.05, steps, dtype="float32")
alphas = 1.0 - betas
alpha_bar = np.cumprod(alphas)
time_index = rng.integers(0, steps, size=count)
noise = rng.normal(size=clean.shape).astype("float32")
strength = alpha_bar[time_index, None]
noisy = np.sqrt(strength) * clean + np.sqrt(1.0 - strength) * noise
condition = np.eye(2, dtype="float32")[labels]
features = np.concatenate([
noisy,
(time_index[:, None] / (steps - 1)).astype("float32"),
condition,
], axis=1)
denoiser = keras.Sequential([
keras.layers.Input((5,)),
keras.layers.Dense(64, activation="swish"),
keras.layers.Dense(64, activation="swish"),
keras.layers.Dense(2),
])
denoiser.compile(optimizer="adam", loss="mse")
history = denoiser.fit(features, noise, batch_size=64, epochs=35, verbose=0)
def sample(class_id, sample_count=200, seed=21):
local_rng = np.random.default_rng(seed)
points = local_rng.normal(size=(sample_count, 2)).astype("float32")
snapshots = {"initial": points.copy()}
class_condition = np.eye(2, dtype="float32")[
np.full(sample_count, class_id)]
for step in reversed(range(steps)):
time_column = np.full((sample_count, 1), step / (steps - 1),
dtype="float32")
model_input = np.concatenate(
[points, time_column, class_condition], axis=1)
predicted_noise = denoiser.predict(model_input, verbose=0)
mean = (points - betas[step] / np.sqrt(1.0 - alpha_bar[step])
* predicted_noise) / np.sqrt(alphas[step])
if step > 0:
previous = alpha_bar[step - 1]
variance = betas[step] * (1.0 - previous) / (1.0 - alpha_bar[step])
random_noise = local_rng.normal(size=points.shape).astype("float32")
points = mean + np.sqrt(variance) * random_noise
else:
points = mean
if step in {20, 10, 0}:
snapshots[f"step_{step}"] = points.copy()
return points, snapshots
left, left_steps = sample(0, seed=21)
right, right_steps = sample(1, seed=21)
assert np.allclose(left_steps["initial"], right_steps["initial"])
print("类别0生成中心:", np.round(left.mean(axis=0), 2))
print("类别1生成中心:", np.round(right.mean(axis=0), 2))
for name, points in left_steps.items():
print("类别0", name, "中心:", np.round(points.mean(axis=0), 2))
preview_count = 400
view_step = 20
view_rng = np.random.default_rng(99)
view_noise = view_rng.normal(size=(preview_count, 2)).astype("float32")
fixed_noisy = (
np.sqrt(alpha_bar[view_step]) * clean[:preview_count]
+ np.sqrt(1.0 - alpha_bar[view_step]) * view_noise
)
figure, axes = plt.subplots(2, 3, figsize=(12, 8))
for class_id, color in [(0, "tab:blue"), (1, "tab:orange")]:
selected = labels[:preview_count] == class_id
axes[0, 0].scatter(
clean[:preview_count][selected, 0], clean[:preview_count][selected, 1],
s=8, alpha=0.45, color=color, label=f"class {class_id}"
)
axes[0, 0].set_title("clean training points")
axes[0, 0].legend()
axes[0, 1].scatter(fixed_noisy[:, 0], fixed_noisy[:, 1], s=8, alpha=0.35)
axes[0, 1].set_title(f"noise at fixed step {view_step}")
axes[0, 2].plot(history.history["loss"])
axes[0, 2].set_title("noise-prediction loss")
axes[0, 2].set_xlabel("epoch")
axes[0, 2].set_ylabel("MSE")
axes[1, 0].scatter(
left_steps["initial"][:, 0], left_steps["initial"][:, 1],
s=8, alpha=0.35, color="tab:gray")
axes[1, 0].set_title("shared initial noise")
axes[1, 1].scatter(left[:, 0], left[:, 1], s=8, alpha=0.45, color="tab:blue")
axes[1, 1].scatter(*centers[0], marker="x", s=80, color="black")
axes[1, 1].set_title("generated: class 0")
axes[1, 2].scatter(right[:, 0], right[:, 1], s=8, alpha=0.45, color="tab:orange")
axes[1, 2].scatter(*centers[1], marker="x", s=80, color="black")
axes[1, 2].set_title("generated: class 1")
for axis in [axes[0, 0], axes[0, 1], axes[1, 0], axes[1, 1], axes[1, 2]]:
axis.set_aspect("equal", adjustable="box")
axis.set_xlim(-3, 3)
axis.set_ylim(-3, 3)
axis.grid(alpha=0.2)
figure.tight_layout()
figure.savefig("diffusion_points.png", dpi=150)
plt.show()
程序保存的diffusion_points.png把干净训练点、固定第20步的加噪点、训练损失、共同初始噪声和两个类别的生成点并列显示;各散点图固定相同坐标范围,避免自动缩放制造“更接近”的错觉。left_steps还保留第20、10、0步的中间状态。学生应先看加噪后结构怎样变弱,再检查去噪状态怎样变化,最后核对生成点是否分别回到左右训练分布;不能只看打印出来的两个中心。固定种子只改变class_id时,断言保证两类从完全相同的初始噪声出发;固定类别只改变种子,则观察同一条件下的多样性。代码中没有文字条件,因此它不能执行“两张车票—三张车票”实验。故事盒封面实验调用另一个已经具备文字条件编码的预训练模型,两条证据分别记录,不互相冒充。
三、分别改变种子和条件
实验A固定条件,只改变随机种子。四张图应保持旧收音机、两张车票和木桌主题大致一致,同时在物件姿态和布局上有所变化。多样性太低可能说明条件过强或模型模式单一;变化太大则说明条件控制不足。
实验B固定随机种子,只把“两张旧车票”改为“三张旧车票”。若主要变化集中在数量,说明条件有一定控制作用;若收音机、构图、颜色和风格也大幅改变,说明条件之间存在耦合。不能同时改变条件、种子、模型和采样步数,否则无法解释差异来源。
种子用于复现随机起点。在完全相同的模型、软件环境和采样算法下,同一种子通常有助于复现;模型版本或计算过程变化后仍可能不同。种子不是作品身份证,也不能证明训练材料来源。
第三节 检查随机性、材料来源与作品真实性
一、主动寻找生成失败
正常样例是常见物件构图与清楚条件;边界样例要求精确车票数量或遮挡关系;陌生样例要求训练分布中很少见的对象组合;故障样例包括模型文件缺失、显存不足、生成中断、来源编号丢失和材料已撤回但衍生文本仍被保留。
图像测试关注物件数量错误、旋钮或票面结构异常、局部重复、伪文字和不合理阴影。文字生成还要测试虚构事实、来源错配和前后矛盾;若比较声音生成,则只使用授权的非人物材料,测试突变和噪声,不进行亲属声音模仿。失败类型随数据形态变化,但都要保留原始候选和条件。
还可以加入简单基线:用已授权的物件照片和确定性排版模板制作同一封面。生成路线若只节省少量构图时间,却增加大量伪影、相似性和授权核对,模板可能更合适;若任务需要多种低风险创意草图,生成模型可能有价值。不能取得家庭材料授权时,应切换到阳台植物科普卡等低隐私替代项目,而不是降低授权标准。
生成过程不是简单从图库检索一张图片,但不能据此断言模型绝不复制。模型可能记住少见训练样本,某些条件下产生高度近似内容。课堂可用与授权样例的近邻比较作为风险筛查,但相似度低也不能完成法律判断;不清楚来源和许可时应限制发布。
二、区分生成、编辑与事实表达
生成模型产生候选素材和连接句,普通编辑软件负责裁切、排版和文字,人工负责来源核对与最终表达。故事盒每段应保留“照片事实/来源编号”“口述记忆/讲述者授权”或“模型连接想象/模型与条件记录”标签;无人物封面也应说明使用了生成式工具。若作品过度接近现成角色、标志或某位创作者的独特作品,应停止展示并更换条件或素材路线。
开发与学习AI可以帮助列检查项、比较候选和生成替代条件,但不能替家庭成员授权,也不能推断照片中人物身份。材料提供者撤回P-01或O-01后,学生应依据来源索引移出原文件、相关故事段落和衍生封面,更新展示清单并保留最少撤回记录;已经由他人另行保存的公开副本未必能自动收回,因此本项目默认在受控范围展示。训练数据通常无法由单张输出反推出完整清单,“没有看到复制痕迹”也不等于没有权利风险。
三、形成作品采用判断
判断卡至少记录:材料来源编号与授权范围;照片事实、口述记忆和模型连接想象怎样分层;撤回后删除哪些原件和衍生物;所用条件、种子、模型和资源版本;候选数量;条件符合和伪影情况;生成使用怎样说明;哪些位置由人重新编辑;遇到相似风险或无法核实来源时怎样停止。
适合采用的范围是获得授权、可人工检查、允许创意变化的辅助表达。故事盒不是家族史档案,模型连接想象不能作为历史证据;要求精确结构、真实记录、专业诊断或身份证明的场景,也不应把生成结果当作原始事实。决定不用生成模型,改用经授权照片、手绘、模板或低隐私植物卡,同样体现了对技术的理解。
本章小结
生成式深度学习不是让聊天模型写一句话的操作名称,而是让模型学习数据分布并从中采样。自回归、潜空间和扩散模型采用不同生成路径;条件与随机起点共同影响结果。
保存所有候选、固定变量、观察逐步生成和检查失败,能够把创作体验转成技术学习。模型生成素材和明确标注的连接想象,普通软件编辑,人工核对照片事实、口述来源、授权与撤回。可生成不等于可展示,随机新颖也不自动等于原创。
关键术语
- 生成式模型:学习数据分布并产生新样本的模型。
- 自回归生成:依据已有序列逐步预测下一个元素。
- 潜空间:模型用较少维度表达数据主要变化的内部空间。
- 变分自编码器:学习可采样潜空间并由潜变量重建或生成数据的模型。
- 扩散模型:学习逐步去除噪声、从噪声形成样本的生成模型。
- 条件生成:在文字、类别、图像等条件约束下生成内容。
- 随机种子:控制伪随机过程起点、帮助复现实验的数值。
- 伪影:生成结果中不符合目标或现实结构的异常细节。
目标测试
一、单项选择题
- 生成模型与分类模型的主要区别是( )。A.生成新样本与选择预设类别 B.是否用电脑 C.是否有文件 D.是否需要人
- 扩散模型生成时通常从( )开始。A.标准答案 B.随机噪声 C.分类标签表 D.网页按钮
- 固定条件只改变随机种子主要观察( )。A.数据授权 B.同一条件下的多样性 C.模型是否训练 D.文字来源
- 故事盒中由模型补写的连接句应( )。A.改成照片事实 B.标为模型连接想象并由人决定是否保留 C.删除来源编号 D.当作讲述者原话
二、判断并改错
- “相同随机种子在任何模型版本上都保证得到完全相同作品。”请改正。
- “生成结果没有明显复制痕迹,就能证明不存在授权风险。”请改正。
三、简答与操作题
- 分别用一句话说明自回归、潜空间和扩散生成的起点与过程。
- 以“旧收音机和两张车票”的无人物封面为例,设计一个固定条件、只改变种子的对照实验,并列出两项记录指标。
- 为什么故事盒必须区分照片事实、口述记忆和模型连接想象?若一份材料被撤回,应怎样处理?
- 写出一项适合采用生成模型的低风险任务和一项不适合的任务,并说明理由;不能取得家庭材料授权时可选择什么替代项目?
答案编号:A1-9-01—A1-9-10。完整答案和评价要点统一放入书后“目标测试参考答案”。