# VAE_STUDY **Repository Path**: xiao_peipei/vae_-study ## Basic Information - **Project Name**: VAE_STUDY - **Description**: 变分自编码器(Variational Autoencoder, VAE ) 算法学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-27 - **Last Updated**: 2026-06-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # VAE 变分自编码器 — 学习笔记 > 本文档是对 vae_tutorial 项目的 PyTorch 改造版 (vae_pytorch) 的学习指南,帮助你理解代码结构、调用关系、模型架构以及如何使用。 --- 参考论文: [Tutorial on Variational Autoencoders (arXiv:1606.05908)](https://arxiv.org/abs/1606.05908) — Carl Doersch 论文与代码学习:[ **仓库地址** ](https://gitee.com/xiao_peipei/vae_-study) [原论文官方代码仓库](http://github.com/cdoersch/vae_tutorial) **VAE 架构(encoder->decoder)** ![alt text](image.png) **代码跑出来的实验效果(手写数字图像生成)** ![alt text](vae_out.png) **学习经历** 原论文《Auto-encoding variational》完全看不懂,来自deepseek的安慰: ![alt text](image-1.png) AI 推荐了 arXiv 其他解析该论文的论文,借助 AI 完成论文与代码的学习。 感谢 AI , 让普通修士也能学习天阶功法。 下面是 AI 总结。 ## 📁 项目文件结构 ``` vae_pytorch/ ├── requirements.txt # 依赖包列表 ├── model.py # 三个模型: VAE, CVAE, Regressor ├── utils.py # 工具函数: 图像拼接 (imtile) ├── train_vae.py # VAE 训练脚本 ├── train_cvae.py # CVAE 训练脚本 ├── train_regressor.py # Regressor 训练脚本 ├── VAE学习笔记.md # 本文档 ├── data/ # MNIST 数据集 (自动下载) ├── snapshots/ # 模型检查点 (训练后生成) ├── vae_out.png # VAE 输出 (训练后生成) ├── cvae.png # CVAE 输出 ├── regressor.png # Regressor 输出 └── groundtruth.png # 原始图像对比 ``` --- ## 📐 VAE 模型架构详解 ### 🔷 VAE (标准变分自编码器) ``` ┌─────────────────────────────────────┐ │ 输入: MNIST 28x28 │ │ (784维) │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ encode1: Linear(784→1000) │ │ + ReLU │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ encode2: Linear(1000→500) │ │ + ReLU │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ encode3: Linear(500→250) │ │ (无激活 → pre-ReLU) │ └──────┬───────────────┬──────────────┘ │ │ ┌────────▼────────┐ ┌─────▼────────────┐ │ ReLU (后) │ │ skip ReLU │ │ encode3neuron │ │ (保留负值) │ └────────┬────────┘ └─────┬────────────┘ │ │ ┌────────▼────────┐ ┌─────▼────────────┐ │ mu: Linear( │ │ logvar: Linear( │ │ 250→30) │ │ 250→30) │ │ (均值) │ │ (对数方差) │ └────────┬────────┘ └─────┬────────────┘ │ │ ▼ ▼ ┌──────────────────────────────┐ │ 重参数化: z = μ + σ·ε │ │ σ = exp(0.5·logvar) │ │ ε ~ N(0, I) │ └──────────────┬───────────────┘ │ ┌──────────────▼──────────────────────┐ │ decode4: Linear(30→250) + ReLU │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ decode3: Linear(250→500) + ReLU │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ decode2: Linear(500→1000) + ReLU │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ decode1: Linear(1000→784) + Sigmoid│ │ (输出 logits) │ └─────────────────────────────────────┘ ``` #### 损失函数 $$ \mathcal{L} = \underbrace{\mathbb{E}_{z \sim q(z|x)}[-\log p(x|z)]}_{\text{重建损失 (BCE)}} + \underbrace{KL(q(z|x) \| p(z))}_{\text{KL 散度}} $$ **重建损失**: `BCEWithLogitsLoss` — 等价于 Caffe 的 `SigmoidCrossEntropyLoss` **KL 散度** (解析解,因为 q 和 p 都是高斯分布): $$ KL(\mathcal{N}(\mu, \sigma^2) \| \mathcal{N}(0, 1)) = -\frac{1}{2} \sum(1 + \log\sigma^2 - \mu^2 - \sigma^2) $$ #### 关键细节 - **mu 用 ReLU 后激活** (encode3neuron), **logvar 用 ReLU 前激活** (encode3) - 原因: log(sd) 需要能输出负值 (当 sd < 1 时) - **返回 logits 而非概率**: 数值稳定性更好 --- ### 🔷 CVAE (条件变分自编码器) CVAE 解决的是**条件生成**问题: 给定中心列像素 → 生成完整图像。 #### 数据预处理 (preprocess) ``` 原始 28x28 图像 │ ├── data_left [:, :, 0:13] 左13列 (参与编码目标) ├── data_center [:, :, 13:14] 中间1列 (输入条件) └── data_right [:, :, 14:28] 右14列 (参与编码目标) ``` **中心列二值化** (模拟带噪声的观测): 1. 生成均匀噪声 U(0,1) 2. 将噪声与像素值逐元素比较 3. 像素 > 噪声 → 1, 像素 < 噪声 → 0 #### 网络结构 ``` 条件网络: 编码器: 解码器: 28(中心列) 756(左+右) 30(z) │ │ │ ▼ ▼ ▼ 1000 → 500 → 250 1000 → 500 250 (+ 跳连) │ │ │ + 跳连 │ │ │ ▼ ▼ │ └──► encode2sum 500 → 1000 → 756 │ │ │ ▼ 結──────────► 250 → [mu, logvar] ``` **跳连 (Skip Connection)** 的作用: - `input2(500维)` → 注入编码器的 `encode2` 层: 让编码器知道"中心列长什么样" - `input4(250维)` → 注入解码器的 `decode4` 层: 让解码器知道"要生成什么东西" --- ### 🔷 Regressor (确定性回归基线) 与 CVAE **完全相同的输入和目标**,但**没有隐变量**: ``` 中心列(28) → 1000 → 500 → 250 → 250 → 500 → 1000 → 756(输出) ``` | 对比维度 | CVAE | Regressor | |---------|------|-----------| | 模型类型 | 生成模型 | 判别模型 | | 隐变量 | ✅ 有 (30维 z) | ❌ 无 | | 随机性 | ✅ 可生成多样结果 | ❌ 唯一确定输出 | | 损失函数 | BCE + KL | BCE 仅重建 | | 输出多样性 | ✅ 丰富 | ❌ 过于平滑 | --- ## 🔄 代码调用关系图 ``` train_vae.py train_cvae.py train_regressor.py │ │ │ ├── model.VAE ├── model.CVAE ├── model.Regressor │ │ │ │ │ │ │ ├── encode() │ ├── preprocess() │ ├── preprocess() │ ├── reparameterize() │ ├── conditioning() │ └── forward() │ ├── decode() │ ├── encode() │ └── forward() │ ├── reparameterize() │ │ ├── decode() ├── utils.imtile │ ├── forward() │ │ └── generate_from_center() └── matplotlib.pyplot │ .imsave() ├── utils.imtile │ └── matplotlib.pyplot .imsave() ``` **关键调用路径:** ``` # VAE 训练 model(x) → encode(x) → reparameterize(mu, logvar) → decode(z) → loss # CVAE 训练 model(images) → preprocess(images) → conditioning(center_binary) → encode(target, cond_all) → reparameterize(mu, logvar) → decode(z, cond_all) → loss # CVAE 推理 preprocess(images) → generate_from_center(center_binary) → conditioning(center_binary) → reparameterize(mu=0, logvar=0) # 从先验采样 → decode(z, cond_all) → sigmoid(recon) # Regressor 训练 model(images) → preprocess(images) → forward() → BCE loss ``` --- ## 🚀 运行指南 ### 1. 环境准备 ```bash conda activate vae cd /home/jack/code/VAE/vae_pytorch pip install -r requirements.txt ``` ### 2. 训练模型 | 命令 | 模型 | 大约时间 (GPU) | 输出文件 | |------|-----|--------------|---------| | `python train_vae.py` | 标准 VAE | ~10 分钟 | `vae_out.png` | | `python train_cvae.py` | 条件 VAE | ~10 分钟 | `cvae.png`, `groundtruth.png` | | `python train_regressor.py` | 回归基线 | ~10 分钟 | `regressor.png`, `groundtruth.png` | ### 3. 查看结果 训练完成后会生成以下输出图像: **vae_out.png** — VAE 生成/重建的 100 张 MNIST 图像拼接为 10×10 网格: ``` ┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐ │ 重建│ 重建│ 重建│ 重建│ 重建│ 重建│ 重建│ 重建│ 重建│ 重建│ ├────┼────┼────┼────┼────┼────┼────┼────┼────┼────┤ │ ... │ │ │ │ │ │ │ │ │ │ ├────┼────┼────┼────┼────┼────┼────┼────┼────┼────┤ │ 10行│... │... │... │... │... │... │... │... │... │ └────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘ ``` **cvae.png / regressor.png** — 每张图由三部分并排组成: ``` ┌─────────────────────────────────────────────────────┐ │ [重建左13列] [原始右14列(红色高亮)] [重建右13列] │ │ ↑ 对比 CVAE/Regressor 能否准确重建 │ └─────────────────────────────────────────────────────┘ ``` --- ## 📊 实验解读 ### 观察 1: VAE vs 普通自编码器 VAE 的输出通常比普通自编码器更模糊,这是因为: - KL 散度强制隐变量接近 N(0,1),限制了编码能力 - 噪声采样使得解码器必须学会"鲁棒"重建 ### 观察 2: CVAE vs Regressor 同时对比 `cvae.png` 和 `regressor.png`: - **Regressor** 的结果往往更光滑,但缺少细节 - **CVAE** 的结果细节更丰富,但每次运行结果不同 (因为 z 是随机采样的) - 这体现了 VAE 的**生成多样性** — 给定相同条件可以产生不同但都合理的输出 ### 观察 3: CVAE 中的跳连 CVAE 比普通 VAE 生成效果更好,因为: - 跳连直接将中心列信息注入编解码器 - 编码器不需要"猜测"中心列长什么样 - 解码器直接利用中心列特征,生成更精准的细节 --- ## 🧪 进阶练习 1. **改变隐变量维度**: 将 `mu/logvar` 的 `30` 改为 `10` 或 `100`,观察生成质量 2. **调整 KL 权重**: 乘以 β (β-VAE),观察重建质量和隐空间结构的权衡 3. **替换编码器/解码器**: 将全连接层改为卷积层,观察对生成质量的影响 4. **测试时多次采样**: 对同一张输入运行多次 CVAE 生成,观察输出的多样性 --- ## 📚 参考文献 - [Tutorial on Variational Autoencoders (arXiv:1606.05908)](https://arxiv.org/abs/1606.05908) — Carl Doersch - [原始 Caffe 实现](https://github.com/cdoersch/vae_tutorial) — 本项目的原始代码 - [Auto-Encoding Variational Bayes (arXiv:1312.6114)](https://arxiv.org/abs/1312.6114) — Kingma & Welling (原始 VAE 论文) - [β-VAE: Learning Basic Visual Concepts with a Constrained Variational Framework](https://openreview.net/forum?id=Sy2fzU9gl) — Higgins et al.