{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "be20ea2f14d84758a6837d38db86beba",
        "cloud_content_sha256": "sha256:1eaffd40043a1285f58c1802cd62774c6be73d560d2b81c5705db08e853da54c"
      },
      "source": "# LAB09｜把解析去噪换成学得的网络\n\n",
      "id": "lab09-000"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "97df84a14c7a4c60ae80e123851cb1dd",
        "cloud_content_sha256": "sha256:46a4ed7dbc9b22754b7470380ef437e63c3f8d0d9b1ae47a2f717400eac91091"
      },
      "source": "## 先看完整链条改变了什么\n\n本册把 LAB05 的“已知解析预测器”换成真正训练的小型时间条件网络。网络从许多带噪练习题中学习；生成时从一个新噪声状态出发，重复调用它。训练题的正确噪声、生成时的当前状态、评价时使用的解析参照，在代码里是三个分开的角色。\n\n![同一批2048个起点下，初始化、2000次与8000次更新后的采样，与同条件解析参照比较。上排20步，下排100步；灰点是真实分布，橙点是网络采样，青点是解析预测采样。](https://codingai-lec04.pages.dev/assets/experiments/learned/lab09-training-and-steps.png)\n\n先固定下排100步横向比较：初始化只能形成一团散点，训练2000步后出现环状结构，8000步后八个模式更清楚。再固定8000步纵向比较：20步仍把较多点留在簇间，100步更集中。最后看最右列：即使预测器是解析的，有限步采样仍会改变终点分布。因此“预测够不够准”和“怎样把预测接成生成”必须分别检查。\n\n图中初始化约1.46%（20步）／3.71%（100步）的点在坐标范围之外，结果数组与指标均保留这些点；其余训练态没有出框点。绘图没有删除离群样本后重算分数。\n\n",
      "id": "lab09-001"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "7cd9fb0b687442e4862e04d504263942",
        "cloud_content_sha256": "sha256:3417649ecf8c321c4b2618ac49f23ba40c4319b6179335e042cefa1c434b1611"
      },
      "source": "## 同一个八模数据，为什么能自己出题\n\n数据与LAB08相同：均匀选八个半径2的中心之一，再加标准差0.12的二维高斯噪声。每次训练重新抽一个x₀、一个时间t和一个标准正态噪声ε，然后构造：\n\n\\[\nx_t=\\alpha_t x_0+\\sigma_t\\epsilon,\\qquad\n\\alpha_t=e^{-5t},\\quad\\sigma_t=\\sqrt{1-e^{-10t}}.\n\\]\n\n因为ε是程序刚抽出来的，训练标签无需人工标注。网络只接收xₜ与t，输出对ε的预测；x₀与真实ε用于制造练习题和计算损失，不作为网络额外输入。时间告诉网络这次题大致损坏到什么程度。\n\n时间按 t=0.001+0.999u²、u∼Uniform(0,1) 抽样，让固定预算多见一些低噪声题；损失是在这个时间分布下对样本与两个坐标一起求平均的噪声MSE。这里没有声称采用均匀时间的原始DDPM损失权重。\n\n```python\nx0 = draw_data(rng, batch)\nt = .001 + .999 * rng.uniform(size=batch)**2\nepsilon = rng.normal(size=x0.shape)\na, s = schedule(t)\nxt = a*x0 + s*epsilon\nprediction, _ = predict(model, xt, t)\nloss = np.mean((prediction - epsilon)**2)\n```\n\n",
      "id": "lab09-002"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "31b0fcc4e2814eaa9a0a1a22a633520d",
        "cloud_content_sha256": "sha256:bac6214db4acd232100f6b9d2b7e4612fea06e3ab0fed0810d613a3fd8ccc20d"
      },
      "source": "## 网络到底学了什么，初始化已经知道什么\n\n输入由两个坐标、时间和四组时间正弦／余弦特征组成，共11维；后接64→64→2的tanh MLP。为避免把大量预算用在高噪声下近似恒等映射，预测器含一个固定线性支路：\n\n\\[\n\\widehat\\epsilon_\\theta(x,t)\n=\\underbrace{\\frac{\\sigma_t}{\\alpha_t^2v+\\sigma_t^2}x}_{\\text{同均值、同方差的高斯基线}}\n+\\alpha_t f_\\theta(x,t),\\qquad v=2+0.12^2=2.0144.\n\\]\n\n**初始化不是完全无知识的零网络。** 线性支路知道数据均值为0、每轴方差为2.0144，这些量取自本实验数据定义；它不知道八个模式的方向与位置分配。MLP的末层小随机初始化；真正被训练的是MLP所有共享权重与偏置。学习要补出高斯基线不能表达的多峰结构。代码和结果把这个初始化完整保留，不把预置部分当作网络学到的能力。\n\n网络从抽样ε的MSE反传。没有使用解析函数来给学习器蒸馏标签；解析预测只出现在评价和单独的参照采样分支。\n\n",
      "id": "lab09-003"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "57deb51dc31447e6ad37039e4aa92937",
        "cloud_content_sha256": "sha256:3c27e91f50e7257b764140c222f6f93e1fb31a9caf216a8bb31d75397b965810"
      },
      "source": "## 为什么“正确噪声”也不是唯一可推回的答案\n\n同一个附近的xₜ可能来自不同x₀和不同ε。只给xₜ、t，网络无法知道本题历史上究竟抽中了哪一对。平方误差下最佳预测是条件均值 E[ε|xₜ,t]；即使精确给出这个条件均值，针对逐题真实ε的MSE也一般不为0。\n\n因此本册同时记录两种误差：对抽样ε的误差，回答它在真实练习题上预测得怎样；对解析条件均值的误差，回答网络离这个任务的最佳均方预测还有多远。4096个固定未见测试题从独立随机流生成，训练从未读取它们。\n\n| 参数状态 | 对抽样ε的MSE | 对解析条件均值的MSE |\n|---|---:|---:|\n| 初始化 | 0.34665 | 0.11890 |\n| 更新2000次 | 0.27845 | 0.05058 |\n| 更新8000次 | 0.24462 | 0.01875 |\n| 解析条件均值 | 0.22569 | 0 |\n\n低噪声t<0.05处，最终网络对解析均值的误差为0.06720；0.5≤t≤1处为0.000649。整体平均下降掩盖了不同时间段难度，也提醒我们：采样末段恰恰可能遇到网络仍不精确的位置。\n\n![每100次更新记录的训练批次噪声MSE。虚线是固定未见样本上解析条件均值对逐题噪声的MSE；它们的样本集不同，不能把瞬时曲线穿过虚线理解成打败最优预测器。](https://codingai-lec04.pages.dev/assets/experiments/learned/lab09-loss.png)\n\n",
      "id": "lab09-004"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "09b7cc31ab2f4483b6999fcd6f95371c",
        "cloud_content_sha256": "sha256:4dabd90d03668f5a75289b30f2ec142ae97ea0116d8c15e8158e2860a8e34f0c"
      },
      "source": "## 从当前预测走向下一状态\n\n本册用η=0的确定性DDIM形式：在时间t预测ε，先得到当前对干净样本的估计，再合成较小时间s的状态：\n\n\\[\n\\widehat x_0=\\frac{x_t-\\sigma_t\\widehat\\epsilon(x_t,t)}{\\alpha_t},\\qquad\nx_s=\\alpha_s\\widehat x_0+\\sigma_s\\widehat\\epsilon(x_t,t).\n\\]\n\n这里α表示信号幅度，等于DDIM论文中累计噪声系数的平方根；不能把两个记号直接替换而漏掉平方根。时间网格从1均匀降到0.001，随后用最后一次干净估计到0。20步或100步指预测器调用次数，分别用20或100个正时间点。\n\n```python\nfor t, next_t in pairs(times):\n    eps_hat = epsilon_fn(current_x, t)\n    x0_hat = (current_x - sigma(t)*eps_hat) / alpha(t)\n    current_x = alpha(next_t)*x0_hat + sigma(next_t)*eps_hat\n```\n\n**采样器只取得当前坐标、时间、预测器和预定日程。** 它没有目标x₀，没有逐题真实ε，也没有“离哪个模式最近”的标签。在学得分支中，epsilon_fn只调用已训练网络；解析分支明确使用另一函数，不能把它偷偷混入学得轨迹。\n\n所有图使用同一组从N(0,I)抽出的2048个xT。t=1时真实前向边缘与标准正态已经很近，但并非严格相等；这一近似对两个分支相同。有限网格、最后从0.001到0的处理和起点近似都保留，不能宣称解析参照就等于无误差的数据生成器。\n\n",
      "id": "lab09-005"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "a2ff76738556452eb9e58b27402a7975",
        "cloud_content_sha256": "sha256:f3c9e616b8fe94866771973009b8e56979d99853318c55699c09089a89083703"
      },
      "source": "## 固定起点，只改变采样步数\n\n![固定前12个随机起点的逐步轨迹。空心圆为相同xT，黑叉为终点；上排20步，下排100步，左列学得预测，右列解析预测。每条线的所有中间状态均来自采样器实际更新。](https://codingai-lec04.pages.dev/assets/experiments/learned/lab09-trajectories.png)\n\n选一条轨迹读：从空心圆出发，每次只知道此刻状态与时间；不是沿着早已指定的目标点移动。20步的折线跨度较大，100步有更多中间修正。学得与解析分支有时走向不同终点，这可以来自预测近似误差，也可能在模式分界附近被步进放大。\n\n沿用LAB08的覆盖规则：半径0.36邻域、至少总样本1%的点才算覆盖一个模式。覆盖与近模式比例同时保留；距离更近也不能代替模式内部方差与占用均衡的检查。\n\n| 预测器／训练状态 | 采样步数 | 覆盖 | 近模式比例 | 最近中心距离中位数 |\n|---|---:|---:|---:|---:|\n| 初始化 | 20 | 4/8 | 8.69% | 0.8500 |\n| 初始化 | 100 | 6/8 | 9.38% | 0.8466 |\n| 2000次更新 | 20 | 8/8 | 61.23% | 0.2839 |\n| 2000次更新 | 100 | 8/8 | 62.45% | 0.2877 |\n| 8000次更新 | 20 | 8/8 | 58.89% | 0.3006 |\n| 8000次更新 | 100 | 8/8 | 80.03% | 0.1654 |\n| 同条件解析参照 | 20 | 8/8 | 70.95% | 0.2604 |\n| 同条件解析参照 | 100 | 8/8 | 94.68% | 0.1027 |\n| 独立真实样本 | — | 8/8 | 98.93% | 0.1450 |\n\n在8000次更新后，100步比20步有更多点落在模式附近。但2000→8000次训练，20步的近模式比例反而由61.23%降到58.89%，尽管测试噪声MSE下降。这直接回答“训练损失更低，生成就一定更好吗”：单个预测器的改善和某个离散采样器的终点分布不是同一指标。\n\n解析100步的最近中心距离甚至比真实样本更小，说明“更集中”并非完整质量目标。若只用这个距离排名，会奖励把真实模式压得过窄。我们用这些指标观察误差来源，不用教学小模型给GAN和扩散家族排名。\n\n",
      "id": "lab09-006"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "6934fb6ce46f41a5a0263073998d083a",
        "cloud_content_sha256": "sha256:50c3b64dcb973302ac604bb5771acf43888c28d162999fdf109e153bfff17536"
      },
      "source": "## 解析参照与 LAB05 怎样接上\n\n参照与学习器严格使用本册同一个八模数据、α/σ参数化、时间网格、初始xT和DDIM更新。解析式先计算给定xₜ、t时的八个分量后验权重，再求 E[x₀|xₜ,t]，最后用 (xₜ−αE[x₀|xₜ,t])/σ 得到条件平均噪声。\n\n它不是把采样终点吸附到最近中心，也不是知道某个生成起点对应的真答案。它只因本课数据分布被明确给定，才可以精确计算条件平均预测。LAB05帮助理解已知密度下如何核对预测与积分；本册重新提供匹配八模与本日程的解析函数。LAB05旧图若使用其他分布、速度参数化或更新规则，不能直接拿数值与本册横比。\n\n**学生常问：解析已经能算，为何还要学？** 在这个小世界里，它就是一个有价值的参照；真实图像的完整数据密度并没有这样的已知公式。网络从样本获得可反复调用的局部预测，正是为了在无法手写精确密度时构造生成过程。\n\n",
      "id": "lab09-007"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "576615494a2f4418ba6388c7b518c040",
        "cloud_content_sha256": "sha256:9bd3e9e943b18b696137b07f967114b6c82cd3a0f62c1393e969425aedb07c44"
      },
      "source": "## 运行、改变量与留下证据\n\n默认训练8000步、批量256、Adam学习率0.0008，单次固定训练约6.69秒；包括两种步数和三个检查点的采样与绘图。环境为Python 3.12.14、NumPy 2.3.5、单线程BLAS；设备不同会改变时间。训练前、训练中、训练后都用同测试题与同xT，不依赖其他Notebook状态。完整脚本只需NumPy和Matplotlib。\n\n第一次运行保持全部默认值，先定位保存的test_xt、test_t、test_epsilon及各检查点预测，再对应上表。默认 `run()` 会重新训练并生成20／100步对照；如果只想改采样步数，不要再次调用 `run()`。先运行定义类与函数的单元，跳过重新训练单元，再使用文末独立的“只重采样”单元，从已保存的权重与xT读取。文件可以来自随包保存结果，或本册第一次运行生成的 `outputs/`；若缺文件，先完成一次默认运行。\n\n下面是该单元的核心步骤，完整单元还保存新结果并检查权重没有改变：\n\n```python\nsaved = np.load('outputs/lab09-data.npz')\nweights = np.load('outputs/lab09-weights.npz')\nloaded_model = MLP(CONFIG['architecture'], CONFIG['seed_model'])\nfor index, parameter in enumerate(loaded_model.p):\n    parameter[...] = weights[f'p{index}']\nfixed_xT = saved['fixed_xT'].copy()\nsamples50, trajectory50, times50 = ddim(\n    lambda x, t: predict(loaded_model, x, t)[0], fixed_xT, 50\n)\nprint(coverage(samples50))\n```\n\n这一只重采样路径已经实际运行：训练更新0次，50次预测调用，约0.106秒，参数逐位未改变，起点与保存的xT完全一致。50步结果覆盖8/8、近模式比例76.07%、最近中心距离中位数0.1656。它位于已保存20步58.89%和100步80.03%之间，但单个距离指标并不需要严格随步数单调变化。\n\n这个干预只改变**生成阶段的采样步数**。它不同于LAB07改变训练目标中的β，也不同于LAB08改变训练过程的D:G更新次数；后二者必须重新训练，不能只加载同一份最终权重就声称完成对照。先提出一个关于轨迹或模式占用的预测，再读取结果；不要同时改模型、训练时间分布和步数。\n\n**自检。** 为什么不把ε的MSE降到0作为验收？因为给定xₜ、t仍有条件不确定性。为什么解析参照也要改变步数？这样才能观察同一预测器下离散更新的影响。为什么不从学习器里调用解析式修正末态？那会改变被检验的生成方法，不能再把结果全部归给网络学习。\n\n来源：Ho等，[Denoising Diffusion Probabilistic Models](https://arxiv.org/abs/2006.11239)，关注前向造题与噪声预测；Song、Meng、Ermon，[Denoising Diffusion Implicit Models](https://arxiv.org/abs/2010.02502)，重点看式12与η=0的更新。本册的连续指数日程、固定高斯线性支路、二维数据和有限训练预算是明确的教学实现选择。\n\n\n重算时，`run()` 用 threadpoolctl 将这一次实验的 BLAS 计算限为单线程，并在结果 JSON 保存实际线程信息。Notebook 内核可能已加载 NumPy，只在代码中设置环境变量不能可靠改变已启动的线程池。这个执行控制不改变数据、随机种子、训练预算或采样规则；运行时间仍按当前机器实测，不能将本地秒数当作云端承诺。\n\n\n**云端复核（2026-09-30）。** 本册在 Deepnote 的独立内核按自身步骤完整运行。完成8000次训练更新；固定最终权重与2048份起点，20/100步的近模式比例分别为58.89%/80.03%，两者均覆盖8/8。另用相同权重与起点重算50步，近模式比例76.07%，该格没有训练更新。上方原始保存图继续标为本地基线，两次测量分别记录；大图在插件的运行快照预览中可能因尺寸被省略，这不代替学生在同册查看自己的输出。\n",
      "id": "lab09-008"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "4e6a07380de54689910569845af32522",
        "cloud_content_sha256": "sha256:66d3835ab16346ac367aa99dd92d15e7589be388a89d44a33786ad40ae8e718f"
      },
      "source": "## 完整可运行实现\n\n下面代码与下载版 `.py` 同源。先读上面的已保存实测结果；从新内核按顺序运行以下代码，可重新训练并保存自己的图、权重、数组与指标。这里显示的既有图来自独立 Python 进程，不冒称已在当前 Deepnote 计算实例中运行。\n",
      "id": "lab09-009"
    },
    {
      "cell_type": "code",
      "metadata": {
        "deepnote_block_id": "777a8c51694f4f149dd27b636cf0c553",
        "output_provenance": {
          "origin": "Deepnote executed snapshot",
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:ee2ff65985392bd5e82c9d9ed92d22c5f2916638c251ef2322d9ce3bb170fcab"
        },
        "verified_cloud_execution": {
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:ee2ff65985392bd5e82c9d9ed92d22c5f2916638c251ef2322d9ce3bb170fcab",
          "output_preview_omitted": false,
          "completed_at": "2026-09-30T02:23:33.884Z"
        },
        "cloud_content_sha256": "sha256:ee2ff65985392bd5e82c9d9ed92d22c5f2916638c251ef2322d9ce3bb170fcab"
      },
      "execution_count": null,
      "source": "# Dependencies: Python 3.10+, numpy, matplotlib, threadpoolctl. No downloads or hidden notebook state.\nimport os\nos.environ.setdefault('OPENBLAS_NUM_THREADS','1')\nos.environ.setdefault('OMP_NUM_THREADS','1')\nimport json, time, math, hashlib, platform\nfrom pathlib import Path\nimport numpy as np\nfrom threadpoolctl import threadpool_limits, threadpool_info\nimport matplotlib\nmatplotlib.use('Agg')\nimport matplotlib.pyplot as plt\n\nCENTERS = 2.0*np.stack([np.cos(np.arange(8)*2*np.pi/8),np.sin(np.arange(8)*2*np.pi/8)],axis=1)\nDATA_STD = .12\n\ndef draw_data(rng,n):\n    k=rng.integers(0,8,n)\n    return CENTERS[k]+DATA_STD*rng.normal(size=(n,2))\n\ndef coverage(x):\n    dist=np.linalg.norm(x[:,None,:]-CENTERS[None,:,:],axis=2)\n    nearest=dist.argmin(1); inside=dist.min(1)<=3*DATA_STD\n    counts=np.bincount(nearest[inside],minlength=8)\n    return {'n':len(x),'covered_modes':int(np.sum(counts>=.01*len(x))),\n            'mode_counts_within_3sigma':counts.tolist(),'on_mode_fraction':float(inside.mean()),\n            'median_distance_to_center':float(np.median(dist.min(1))),\n            'off_plot_fraction':float(np.mean(np.any(abs(x)>3.1,axis=1)))}\n\nclass MLP:\n    def __init__(self,sizes,seed,final_scale=1.):\n        rng=np.random.default_rng(seed); self.p=[]\n        for k,(a,b) in enumerate(zip(sizes[:-1],sizes[1:])):\n            scale=final_scale if k==len(sizes)-2 else 1.\n            self.p.extend([rng.normal(size=(a,b))*np.sqrt(2/(a+b))*scale,np.zeros(b)])\n        self.m=[np.zeros_like(p) for p in self.p]; self.v=[np.zeros_like(p) for p in self.p]; self.step=0\n    def forward(self,x):\n        acts=[x]\n        for k in range(0,len(self.p),2):\n            x=x@self.p[k]+self.p[k+1]\n            if k<len(self.p)-2: x=np.tanh(x)\n            acts.append(x)\n        return x,acts\n    def backward(self,acts,grad):\n        grads=[None]*len(self.p)\n        for layer in range(len(acts)-2,-1,-1):\n            if layer<len(acts)-2: grad=grad*(1-acts[layer+1]**2)\n            grads[2*layer]=acts[layer].T@grad; grads[2*layer+1]=grad.sum(0)\n            grad=grad@self.p[2*layer].T\n        return grad,grads\n    def update(self,grads,lr,b1=.9,b2=.999):\n        self.step+=1\n        for k,(p,g) in enumerate(zip(self.p,grads)):\n            self.m[k]=b1*self.m[k]+(1-b1)*g; self.v[k]=b2*self.v[k]+(1-b2)*g*g\n            p-=lr*(self.m[k]/(1-b1**self.step))/(np.sqrt(self.v[k]/(1-b2**self.step))+1e-8)\n    def flat(self): return np.concatenate([p.ravel() for p in self.p])\n    def state(self): return {f'p{i}':p.copy() for i,p in enumerate(self.p)}\n\ndef sigmoid(x): return 1/(1+np.exp(-np.clip(x,-50,50)))\ndef bce_logits(logit,label): return np.mean(np.logaddexp(0,logit)-label*logit)\ndef dump_json(path,data): path.write_text(json.dumps(data,indent=2,ensure_ascii=False),encoding='utf8')\ndef check_gradient():\n    m=MLP([2,5,2],980); x=np.random.default_rng(981).normal(size=(3,2)); target=np.ones((3,2))\n    y,a=m.forward(x); _,g=m.backward(a,(y-target)/y.size)\n    errors=[]\n    for k,ij in [(0,(0,1)),(2,(1,0)),(3,(0,))]:\n        old=m.p[k][ij]; h=1e-5\n        m.p[k][ij]=old+h; plus=.5*np.mean((m.forward(x)[0]-target)**2)\n        m.p[k][ij]=old-h; minus=.5*np.mean((m.forward(x)[0]-target)**2)\n        m.p[k][ij]=old; errors.append(abs((plus-minus)/(2*h)-g[k][ij]))\n    assert max(errors)<1e-7,errors\n    return max(errors)\n\ndef scatter_base(ax,real):\n    ax.scatter(real[:,0],real[:,1],s=4,c='#bcc8cf',alpha=.35,rasterized=True)\n    ax.scatter(CENTERS[:,0],CENTERS[:,1],s=35,c='#e5ac33',marker='x',zorder=10)\n    ax.set(xlim=(-3.1,3.1),ylim=(-3.1,3.1),aspect='equal',xlabel='x₁',ylabel='x₂')\n\ndef setup_style():\n    plt.rcParams.update({'font.family':'DejaVu Sans','font.size':10,'axes.spines.top':False,'axes.spines.right':False,'figure.facecolor':'white','savefig.facecolor':'white'})\n",
      "outputs": [],
      "id": "lab09-010"
    },
    {
      "cell_type": "code",
      "metadata": {
        "deepnote_block_id": "dc5f4583f32247838cb3b4b3d7642a2d",
        "output_provenance": {
          "origin": "Deepnote executed snapshot",
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:6a8b9b84f0b9d5b68f7d9ab3962ceb0a30a6b6747edef32d42cb5aaeb6f7df8e"
        },
        "verified_cloud_execution": {
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:6a8b9b84f0b9d5b68f7d9ab3962ceb0a30a6b6747edef32d42cb5aaeb6f7df8e",
          "output_preview_omitted": false,
          "completed_at": "2026-09-30T02:23:33.884Z"
        },
        "cloud_content_sha256": "sha256:6a8b9b84f0b9d5b68f7d9ab3962ceb0a30a6b6747edef32d42cb5aaeb6f7df8e"
      },
      "execution_count": null,
      "source": "# Fixed, finite training budget. No analytic target is used by the learner.\nCONFIG={'seed_model':800,'seed_training':801,'seed_evaluation':802,'steps':8000,'batch':256,\n        'learning_rate':.0008,'architecture':[11,64,64,2],'checkpoints':[0,2000,8000],\n        'sampling_steps':[20,100],'beta':10.,'t_min':.001,\n        'time_training_distribution':'t=.001+.999*Uniform(0,1)^2; deliberately spends more training examples at low noise',\n        'noise_parameterization':'x_t=alpha*x0+sigma*epsilon; alpha=exp(-5t); sigma=sqrt(1-exp(-10t))',\n        'predictor':'Gaussian-covariance linear skip plus alpha(t)*MLP(x_t,time_features)',\n        'data':'8 equal Gaussian modes; radius=2; sigma=.12',\n        'coverage':'At least 1% of all 2048 generated points within radius .36 of a center',\n        'sampler':'deterministic DDIM eta=0; uniform t grid from 1 to .001, then final x0 estimate',\n        'initialization_note':'Tiny random neural residual plus a fixed Gaussian second-moment baseline; not a zero predictor',\n        'reference_note':'Exact conditional expected epsilon for the SAME mixture and SAME schedule; used only in reference branch/evaluation'}\nVAR0=2.+DATA_STD**2\n\ndef schedule(t):\n    t=np.asarray(t).reshape(-1,1); a=np.exp(-.5*CONFIG['beta']*t);s=np.sqrt(-np.expm1(-CONFIG['beta']*t));return a,s\n\ndef features(x,t):\n    t=np.asarray(t).reshape(-1,1)\n    return np.concatenate([x/np.sqrt(2),t,np.sin(2*np.pi*t*np.array([1,2,4,8])),np.cos(2*np.pi*t*np.array([1,2,4,8]))],axis=1)\n\ndef predict(model,x,t):\n    a,s=schedule(t); correction,cache=model.forward(features(x,t))\n    linear=s*x/(a*a*VAR0+s*s)\n    return linear+a*correction,(cache,a)\n\ndef analytic_epsilon(x,t):\n    # An independent oracle/reference, never used to form learned-network training labels.\n    a,s=schedule(t); v=a*a*DATA_STD**2+s*s\n    diff=x[:,None,:]-a[:,None,:]*CENTERS[None,:,:]\n    logp=-np.sum(diff*diff,axis=2)/(2*v)\n    weights=np.exp(logp-logp.max(1,keepdims=True)); weights/=weights.sum(1,keepdims=True)\n    mean_center=weights@CENTERS\n    # E[x0 | xt, component] = mu + alpha*sigma_data²/v*(xt-alpha*mu)\n    posterior=mean_center+a*DATA_STD**2/v*(x-a*mean_center)\n    return (x-a*posterior)/s\n\ndef ddim(epsilon_fn,initial,steps):\n    # Legal inputs only: current x, scalar t, predictor, schedule. No target sample.\n    x=initial.copy(); trajectory=[x[:24].copy()]\n    times=np.r_[np.linspace(1.,CONFIG['t_min'],steps),0.]\n    for t,snext in zip(times[:-1],times[1:]):\n        ts=np.full(len(x),t); a,s=schedule(ts);eps=epsilon_fn(x,ts)\n        x0hat=(x-s*eps)/a\n        an,sn=schedule(np.full(len(x),snext)); x=an*x0hat+sn*eps\n        trajectory.append(x[:24].copy())\n    return x,np.stack(trajectory),times\n",
      "outputs": [],
      "id": "lab09-011"
    },
    {
      "cell_type": "code",
      "metadata": {
        "deepnote_block_id": "b910b5d0e7bd4804b9e0e894f6524434",
        "output_provenance": {
          "origin": "Deepnote executed snapshot",
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:430c4e99e175dea0137cc79289ccc88f240ad8cb54176a4871813fd192703577"
        },
        "verified_cloud_execution": {
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:430c4e99e175dea0137cc79289ccc88f240ad8cb54176a4871813fd192703577",
          "output_preview_omitted": false,
          "completed_at": "2026-09-30T02:23:33.884Z"
        },
        "cloud_content_sha256": "sha256:430c4e99e175dea0137cc79289ccc88f240ad8cb54176a4871813fd192703577"
      },
      "execution_count": null,
      "source": "# Training labels are sampled epsilon; all evaluation examples are held out.\n@threadpool_limits.wrap(limits=1, user_api='blas')\ndef run(output_dir='outputs'):\n    begin=time.time();out=Path(output_dir);out.mkdir(parents=True,exist_ok=True);setup_style();check_gradient()\n    rng=np.random.default_rng(CONFIG['seed_training']); erng=np.random.default_rng(CONFIG['seed_evaluation'])\n    real=draw_data(erng,2048);initial=erng.normal(size=(2048,2))\n    test_x0=draw_data(erng,4096);test_t=CONFIG['t_min']+(1-CONFIG['t_min'])*erng.uniform(size=4096)**2\n    test_eps=erng.normal(size=(4096,2));ta,ts=schedule(test_t);test_xt=ta*test_x0+ts*test_eps\n    oracle_test=analytic_epsilon(test_xt,test_t)\n    model=MLP(CONFIG['architecture'],CONFIG['seed_model'],final_scale=.01)\n    saved={'real_evaluation':real,'centers':CENTERS,'fixed_xT':initial,'test_x0':test_x0,'test_xt':test_xt,'test_t':test_t,'test_epsilon':test_eps,'oracle_test_prediction':oracle_test}\n    records={};curve=[];fig,axes=plt.subplots(len(CONFIG['sampling_steps']),4,figsize=(15,3.75*len(CONFIG['sampling_steps'])),constrained_layout=True,squeeze=False)\n    cols={0:0,2000:1,8000:2}\n    for step in range(CONFIG['steps']+1):\n        if step in CONFIG['checkpoints']:\n            prediction=predict(model,test_xt,test_t)[0]\n            bytime=[]\n            for lo,hi in [(0,.05),(.05,.2),(.2,.5),(.5,1.01)]:\n                mask=(test_t>=lo)&(test_t<hi)\n                bytime.append({'t_range':[lo,hi],'n':int(mask.sum()),'noise_target_MSE':float(np.mean((prediction[mask]-test_eps[mask])**2)),\n                               'conditional_mean_error_MSE':float(np.mean((prediction[mask]-oracle_test[mask])**2))})\n            record={'noise_target_MSE':float(np.mean((prediction-test_eps)**2)),\n                    'conditional_mean_error_MSE':float(np.mean((prediction-oracle_test)**2)),\n                    'by_time':bytime,'samplings':{}}\n            saved[f'step{step}_heldout_prediction']=prediction\n            for row,n in enumerate(CONFIG['sampling_steps']):\n                result,traj,times=ddim(lambda x,t:predict(model,x,t)[0],initial,n)\n                metrics=coverage(result);record['samplings'][str(n)]=metrics\n                saved[f'learned_step{step}_n{n}_samples']=result;saved[f'learned_step{step}_n{n}_trajectory']=traj;saved[f'times_n{n}']=times\n                ax=axes[row,cols[step]];scatter_base(ax,real);ax.scatter(result[:,0],result[:,1],c='#df713e',s=4,alpha=.4,rasterized=True)\n                ax.set_title(f'Network updates {step} · {n} steps\\ncoverage {metrics[\"covered_modes\"]}/8 · near modes {metrics[\"on_mode_fraction\"]:.1%}')\n            records[str(step)]=record\n        if step==CONFIG['steps']:break\n        x0=draw_data(rng,CONFIG['batch']);t=CONFIG['t_min']+(1-CONFIG['t_min'])*rng.uniform(size=CONFIG['batch'])**2\n        epsilon=rng.normal(size=x0.shape);a,s=schedule(t);xt=a*x0+s*epsilon\n        pred,(cache,alpha)=predict(model,xt,t)\n        # mean((pred-epsilon)^2) over batch AND coordinate; sampled epsilon is known.\n        grad_eps=2*(pred-epsilon)/pred.size\n        _,grads=model.backward(cache,grad_eps*alpha);model.update(grads,CONFIG['learning_rate'])\n        if step%100==0:curve.append([step+1,float(np.mean((pred-epsilon)**2))])\n    oracle_records={}\n    for row,n in enumerate(CONFIG['sampling_steps']):\n        result,traj,times=ddim(analytic_epsilon,initial,n);metrics=coverage(result);oracle_records[str(n)]=metrics\n        saved[f'oracle_n{n}_samples']=result;saved[f'oracle_n{n}_trajectory']=traj\n        ax=axes[row,3];scatter_base(ax,real);ax.scatter(result[:,0],result[:,1],c='#267e83',s=4,alpha=.5,rasterized=True)\n        ax.set_title(f'Matched analytic reference · {n} steps\\ncoverage {metrics[\"covered_modes\"]}/8 · near modes {metrics[\"on_mode_fraction\"]:.1%}')\n    fig.suptitle('Same x_T in every panel · gray: real · orange: learned · teal: analytic reference',fontsize=13)\n    fig.savefig(out/'lab09-training-and-steps.png',dpi=170);plt.close(fig)\n    fig,axes=plt.subplots(len(CONFIG['sampling_steps']),2,figsize=(10,4*len(CONFIG['sampling_steps'])),constrained_layout=True,squeeze=False)\n    for row,n in enumerate(CONFIG['sampling_steps']):\n        for col,kind in enumerate(['learned_step8000','oracle']):\n            traj=saved[f'{kind}_n{n}_trajectory'];ax=axes[row,col];scatter_base(ax,real)\n            for j in range(12):ax.plot(traj[:,j,0],traj[:,j,1],lw=1.2,alpha=.85)\n            ax.scatter(initial[:12,0],initial[:12,1],marker='o',facecolors='none',edgecolors='black',s=30,label='same xT')\n            ax.scatter(traj[-1,:12,0],traj[-1,:12,1],c='black',marker='x',s=20,label='final')\n            ax.set_title(f'{\"Learned\" if col==0 else \"Analytic\"} · {n} DDIM steps');ax.legend(fontsize=8)\n    fig.savefig(out/'lab09-trajectories.png',dpi=170);plt.close(fig)\n    curve=np.array(curve);saved['training_losses']=curve\n    fig,ax=plt.subplots(figsize=(8,3.4),constrained_layout=True);ax.plot(curve[:,0],curve[:,1],lw=1,label='sampled minibatch noise MSE')\n    ax.axhline(float(np.mean((oracle_test-test_eps)**2)),c='#267e83',ls='--',label='held-out analytic expected-noise MSE')\n    ax.set(xlabel='Network updates',ylabel='Mean squared error');ax.legend();fig.savefig(out/'lab09-loss.png',dpi=170);plt.close(fig)\n    report={'config':CONFIG,'checkpoints':records,'analytic_reference':oracle_records,\n            'analytic_noise_target_MSE':float(np.mean((oracle_test-test_eps)**2)),\n            'runtime_seconds':time.time()-begin,'python':platform.python_version(),'numpy':np.__version__,\n            'blas_threadpools':threadpool_info(),\n            'run_utc':time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime()),'real_reference_coverage':coverage(real)}\n    np.savez_compressed(out/'lab09-weights.npz',**model.state());np.savez_compressed(out/'lab09-data.npz',**saved)\n    dump_json(out/'lab09-metrics.json',report);print(json.dumps(report,ensure_ascii=False,indent=2));return report\n",
      "outputs": [],
      "id": "lab09-012"
    },
    {
      "cell_type": "code",
      "metadata": {
        "deepnote_block_id": "d9fdb260314c402d8ad9e07f79db7117",
        "output_provenance": {
          "origin": "Deepnote executed snapshot",
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:409c877c813182d86579c3db92e2bcb6040861770755bad887117da62f2ba875"
        },
        "verified_cloud_execution": {
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:409c877c813182d86579c3db92e2bcb6040861770755bad887117da62f2ba875",
          "output_preview_omitted": false,
          "completed_at": "2026-09-30T02:23:33.884Z"
        },
        "cloud_content_sha256": "sha256:409c877c813182d86579c3db92e2bcb6040861770755bad887117da62f2ba875"
      },
      "execution_count": null,
      "source": "report = run('outputs')\n",
      "outputs": [
        {
          "name": "stdout",
          "text": "{\n  \"config\": {\n    \"seed_model\": 800,\n    \"seed_training\": 801,\n    \"seed_evaluation\": 802,\n    \"steps\": 8000,\n    \"batch\": 256,\n    \"learning_rate\": 0.0008,\n    \"architecture\": [\n      11,\n      64,\n      64,\n      2\n    ],\n    \"checkpoints\": [\n      0,\n      2000,\n      8000\n    ],\n    \"sampling_steps\": [\n      20,\n      100\n    ],\n    \"beta\": 10.0,\n    \"t_min\": 0.001,\n    \"time_training_distribution\": \"t=.001+.999*Uniform(0,1)^2; deliberately spends more training examples at low noise\",\n    \"noise_parameterization\": \"x_t=alpha*x0+sigma*epsilon; alpha=exp(-5t); sigma=sqrt(1-exp(-10t))\",\n    \"predictor\": \"Gaussian-covariance linear skip plus alpha(t)*MLP(x_t,time_features)\",\n    \"data\": \"8 equal Gaussian modes; radius=2; sigma=.12\",\n    \"coverage\": \"At least 1% of all 2048 generated points within radius .36 of a center\",\n    \"sampler\": \"deterministic DDIM eta=0; uniform t grid from 1 to .001, then final x0 estimate\",\n    \"initialization_note\": \"Tiny random neural residual plus a fixed Gaussian second-moment baseline; not a zero predictor\",\n    \"reference_note\": \"Exact conditional expected epsilon for the SAME mixture and SAME schedule; used only in reference branch/evaluation\"\n  },\n  \"checkpoints\": {\n    \"0\": {\n      \"noise_target_MSE\": 0.3466462386461925,\n      \"conditional_mean_error_MSE\": 0.11890371735823559,\n      \"by_time\": [\n        {\n          \"t_range\": [\n            0,\n            0.05\n          ],\n          \"n\": 908,\n          \"noise_target_MSE\": 0.9195597783847196,\n          \"conditional_mean_error_MSE\": 0.4757193204463834\n        },\n        {\n          \"t_range\": [\n            0.05,\n            0.2\n          ],\n          \"n\": 943,\n          \"noise_target_MSE\": 0.5202881992324372,\n          \"conditional_mean_error_MSE\": 0.05819971073333699\n        },\n        {\n          \"t_range\": [\n            0.2,\n            0.5\n          ],\n          \"n\": 1045,\n          \"noise_target_MSE\": 0.08680401267912498,\n          \"conditional_mean_error_MSE\": 0.00018575966494943397\n        },\n        {\n          \"t_range\": [\n            0.5,\n            1.01\n          ],\n          \"n\": 1200,\n          \"noise_target_MSE\": 0.002967291329670833,\n          \"conditional_mean_error_MSE\": 3.105217327371017e-08\n        }\n      ],\n      \"samplings\": {\n        \"20\": {\n          \"n\": 2048,\n          \"covered_modes\": 4,\n          \"mode_counts_within_3sigma\": [\n            19,\n            36,\n            20,\n            14,\n            22,\n            13,\n            25,\n            29\n          ],\n          \"on_mode_fraction\": 0.0869140625,\n          \"median_distance_to_center\": 0.8499968196468977,\n          \"off_plot_fraction\": 0.0146484375\n        },\n        \"100\": {\n          \"n\": 2048,\n          \"covered_modes\": 6,\n          \"mode_counts_within_3sigma\": [\n            25,\n            34,\n            14,\n            25,\n            17,\n            22,\n            30,\n            25\n          ],\n          \"on_mode_fraction\": 0.09375,\n          \"median_distance_to_center\": 0.8465869181647522,\n          \"off_plot_fraction\": 0.037109375\n        }\n      }\n    },\n    \"2000\": {\n      \"noise_target_MSE\": 0.27844650124564085,\n      \"conditional_mean_error_MSE\": 0.05058195301449061,\n      \"by_time\": [\n        {\n          \"t_range\": [\n            0,\n            0.05\n          ],\n          \"n\": 908,\n          \"noise_target_MSE\": 0.6370114333852833,\n          \"conditional_mean_error_MSE\": 0.19032023129840403\n        },\n        {\n          \"t_range\": [\n            0.05,\n            0.2\n          ],\n          \"n\": 943,\n          \"noise_target_MSE\": 0.4797693768483786,\n          \"conditional_mean_error_MSE\": 0.019883639289979473\n        },\n        {\n          \"t_range\": [\n            0.2,\n            0.5\n          ],\n          \"n\": 1045,\n          \"noise_target_MSE\": 0.10091981474832874,\n          \"conditional_mean_error_MSE\": 0.014130709723696854\n        },\n        {\n          \"t_range\": [\n            0.5,\n            1.01\n          ],\n          \"n\": 1200,\n          \"noise_target_MSE\": 0.0035222990069025636,\n          \"conditional_mean_error_MSE\": 0.0007133716805740587\n        }\n      ],\n      \"samplings\": {\n        \"20\": {\n          \"n\": 2048,\n          \"covered_modes\": 8,\n          \"mode_counts_within_3sigma\": [\n            128,\n            200,\n            96,\n            163,\n            176,\n            185,\n            171,\n            135\n          ],\n          \"on_mode_fraction\": 0.6123046875,\n          \"median_distance_to_center\": 0.283906749946691,\n          \"off_plot_fraction\": 0.0\n        },\n        \"100\": {\n          \"n\": 2048,\n          \"covered_modes\": 8,\n          \"mode_counts_within_3sigma\": [\n            126,\n            192,\n            103,\n            163,\n            172,\n            199,\n            169,\n            155\n          ],\n          \"on_mode_fraction\": 0.62451171875,\n          \"median_distance_to_center\": 0.28774819704597443,\n          \"off_plot_fraction\": 0.0\n        }\n      }\n    },\n    \"8000\": {\n      \"noise_target_MSE\": 0.2446225852379858,\n      \"conditional_mean_error_MSE\": 0.01874684663416279,\n      \"by_time\": [\n        {\n          \"t_range\": [\n            0,\n            0.05\n          ],\n          \"n\": 908,\n          \"noise_target_MSE\": 0.510103650470085,\n          \"conditional_mean_error_MSE\": 0.06719602036125287\n        },\n        {\n          \"t_range\": [\n            0.05,\n            0.2\n          ],\n          \"n\": 943,\n          \"noise_target_MSE\": 0.46653668758865086,\n          \"conditional_mean_error_MSE\": 0.010645675059477246\n        },\n        {\n          \"t_range\": [\n            0.2,\n            0.5\n          ],\n          \"n\": 1045,\n          \"noise_target_MSE\": 0.09053742017363885,\n          \"conditional_mean_error_MSE\": 0.004742436762024722\n        },\n        {\n          \"t_range\": [\n            0.5,\n            1.01\n          ],\n          \"n\": 1200,\n          \"noise_target_MSE\": 0.003536911692001908,\n          \"conditional_mean_error_MSE\": 0.0006486494400919221\n        }\n      ],\n      \"samplings\": {\n        \"20\": {\n          \"n\": 2048,\n          \"covered_modes\": 8,\n          \"mode_counts_within_3sigma\": [\n            248,\n            91,\n            122,\n            180,\n            141,\n            130,\n            170,\n            124\n          ],\n          \"on_mode_fraction\": 0.5888671875,\n          \"median_distance_to_center\": 0.3005931038037627,\n          \"off_plot_fraction\": 0.0\n        },\n        \"100\": {\n          \"n\": 2048,\n          \"covered_modes\": 8,\n          \"mode_counts_within_3sigma\": [\n            292,\n            170,\n            162,\n            218,\n            201,\n            233,\n            208,\n            155\n          ],\n          \"on_mode_fraction\": 0.80029296875,\n          \"median_distance_to_center\": 0.1654350773147341,\n          \"off_plot_fraction\": 0.0\n        }\n      }\n    }\n  },\n  \"analytic_reference\": {\n    \"20\": {\n      \"n\": 2048,\n      \"covered_modes\": 8,\n      \"mode_counts_within_3sigma\": [\n        183,\n        204,\n        159,\n        178,\n        195,\n        169,\n        190,\n        175\n      ],\n      \"on_mode_fraction\": 0.70947265625,\n      \"median_distance_to_center\": 0.26043647358286615,\n      \"off_plot_fraction\": 0.0\n    },\n    \"100\": {\n      \"n\": 2048,\n      \"covered_modes\": 8,\n      \"mode_counts_within_3sigma\": [\n        228,\n        261,\n        222,\n        248,\n        255,\n        235,\n        250,\n        240\n      ],\n      \"on_mode_fraction\": 0.94677734375,\n      \"median_distance_to_center\": 0.10271187832943826,\n      \"off_plot_fraction\": 0.0\n    }\n  },\n  \"analytic_noise_target_MSE\": 0.22568853355937113,\n  \"runtime_seconds\": 20.518462419509888,\n  \"python\": \"3.13.12\",\n  \"numpy\": \"2.4.6\",\n  \"blas_threadpools\": [\n    {\n      \"user_api\": \"blas\",\n      \"internal_api\": \"openblas\",\n      \"num_threads\": 1,\n      \"prefix\": \"libscipy_openblas\",\n      \"filepath\": \"/root/venv/lib/python3.13/site-packages/numpy.libs/libscipy_openblas64_-32a4b2a6.so\",\n      \"version\": \"0.3.31.188.0\",\n      \"threading_layer\": \"pthreads\",\n      \"architecture\": \"Haswell\"\n    }\n  ],\n  \"run_utc\": \"2026-09-30T02:23:07Z\",\n  \"real_reference_coverage\": {\n    \"n\": 2048,\n    \"covered_modes\": 8,\n    \"mode_counts_within_3sigma\": [\n      270,\n      251,\n      261,\n      241,\n      235,\n      238,\n      287,\n      243\n    ],\n    \"on_mode_fraction\": 0.9892578125,\n    \"median_distance_to_center\": 0.1450050819067562,\n    \"off_plot_fraction\": 0.0\n  }\n}\n",
          "output_type": "stream"
        }
      ],
      "id": "lab09-013"
    },
    {
      "cell_type": "code",
      "metadata": {
        "deepnote_block_id": "f45ec5852d2d4152a3ef6bebea1ac593",
        "verified_cloud_execution": {
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:92f51e80f3d0414ffda53ec97a1d588138661570a6ed884633ae8f8622f1cf75",
          "output_preview_omitted": true,
          "completed_at": "2026-09-30T02:23:33.884Z"
        },
        "cloud_content_sha256": "sha256:92f51e80f3d0414ffda53ec97a1d588138661570a6ed884633ae8f8622f1cf75"
      },
      "execution_count": null,
      "source": "from IPython.display import display, Image\ndisplay(Image(filename='outputs/lab09-training-and-steps.png'))\ndisplay(Image(filename='outputs/lab09-trajectories.png'))\ndisplay(Image(filename='outputs/lab09-loss.png'))\n",
      "outputs": [],
      "id": "lab09-014"
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "deepnote_block_id": "2b93e5730d9848a79d9faf6cebab8772",
        "cloud_content_sha256": "sha256:17f15bfb3dd54b61698ee578f622b929d79ac3d90621a581b1a3574acecf44cc"
      },
      "source": "## 只重采样：加载保存权重，改为50步\n\n已保存结果或已运行过一次实验时，先执行前面的类、配置与函数定义单元，跳过 `report = run(...)` 重新训练单元，直接执行本单元。它只读取 `outputs/lab09-weights.npz` 和 `outputs/lab09-data.npz`，训练更新为0；改变的是生成阶段采样步数。若缺文件，先运行一次默认实验。\n",
      "id": "lab09-015"
    },
    {
      "cell_type": "code",
      "metadata": {
        "deepnote_block_id": "842b14c946044f1eb7919f2e286bc7f5",
        "output_provenance": {
          "origin": "Deepnote executed snapshot",
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:9eaf55be7aea88e192dee1fa1f8c386d48a8b7688ca82d322babd77a186724e9"
        },
        "verified_cloud_execution": {
          "run_id": "e5985c0f-6bee-48b6-b4af-4d3fe57df02f",
          "content_hash": "sha256:9eaf55be7aea88e192dee1fa1f8c386d48a8b7688ca82d322babd77a186724e9",
          "output_preview_omitted": false,
          "completed_at": "2026-09-30T02:23:33.884Z"
        },
        "cloud_content_sha256": "sha256:9eaf55be7aea88e192dee1fa1f8c386d48a8b7688ca82d322babd77a186724e9"
      },
      "execution_count": null,
      "source": "# Optional sampling-only cell: run the definition cells above, skip run().\n# Files come from the saved experiment bundle or one previous run of this notebook.\nresample_start = time.time()\nsaved_arrays = np.load('outputs/lab09-data.npz')\nsaved_weights = np.load('outputs/lab09-weights.npz')\nloaded_model = MLP(CONFIG['architecture'], CONFIG['seed_model'])\nfor index, parameter in enumerate(loaded_model.p):\n    parameter[...] = saved_weights[f'p{index}']\nfixed_xT = saved_arrays['fixed_xT'].copy()\nparameters_before = loaded_model.flat().copy()\n\n# The only change is the sampler's call count: no backward(), update(), or run().\nsamples50, trajectory50, times50 = ddim(\n    lambda x, t: predict(loaded_model, x, t)[0], fixed_xT, 50\n)\nassert np.array_equal(loaded_model.flat(), parameters_before)\nassert np.array_equal(trajectory50[0], fixed_xT[:24])\nmetrics50 = coverage(samples50)\nnp.savez_compressed('outputs/lab09-resample50-data.npz',\n                    samples=samples50, trajectory=trajectory50, times=times50,\n                    fixed_xT=fixed_xT)\nresample_record = {\n    'sampling_steps': 50,\n    'training_updates_this_run': 0,\n    'weights_source': 'lab09-weights.npz; final 8000-update model',\n    'initial_states_source': 'lab09-data.npz:fixed_xT',\n    'parameters_unchanged': True,\n    'trajectory_starts_at_saved_xT': True,\n    'metrics': metrics50,\n    'runtime_seconds': time.time() - resample_start,\n    'run_utc': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())\n}\ndump_json(Path('outputs/lab09-resample50-metrics.json'), resample_record)\nprint(json.dumps(resample_record, ensure_ascii=False, indent=2))\n",
      "outputs": [
        {
          "name": "stdout",
          "text": "{\n  \"sampling_steps\": 50,\n  \"training_updates_this_run\": 0,\n  \"weights_source\": \"lab09-weights.npz; final 8000-update model\",\n  \"initial_states_source\": \"lab09-data.npz:fixed_xT\",\n  \"parameters_unchanged\": true,\n  \"trajectory_starts_at_saved_xT\": true,\n  \"metrics\": {\n    \"n\": 2048,\n    \"covered_modes\": 8,\n    \"mode_counts_within_3sigma\": [\n      285,\n      156,\n      154,\n      211,\n      191,\n      216,\n      196,\n      149\n    ],\n    \"on_mode_fraction\": 0.7607421875,\n    \"median_distance_to_center\": 0.16563237005659642,\n    \"off_plot_fraction\": 0.0\n  },\n  \"runtime_seconds\": 8.828055620193481,\n  \"run_utc\": \"2026-09-30T02:23:30Z\"\n}\n",
          "output_type": "stream"
        }
      ],
      "id": "lab09-016"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.12"
    },
    "course_execution": {
      "origin": "independent Python process",
      "saved_results": "2026-09-29",
      "script": "lab09_denoiser.py",
      "script_sha256": "dc1fca28beccb650a3f9f3469e619276b4a4e139312944ed0e4e89ec0d43f869",
      "deepnote_execution_claimed": false
    },
    "lecture04": {
      "source_notebook_id": "e6fcbdfe3ca2418c9ed09344b4ae2b33",
      "saved_results_origin": "Static baseline figures retained; code outputs carry per-cell run provenance."
    },
    "deepnote_readback": {
      "notebook_id": "e6fcbdfe3ca2418c9ed09344b4ae2b33",
      "updated_at": "2026-09-30T02:27:58.255Z",
      "block_count": 17,
      "all_source_blocks_match": true
    },
    "execution_verification": "Every code block matches a successful Deepnote run snapshot by ID and hash. Per-cell metadata distinguishes imported cloud outputs from retained local baseline outputs."
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
