{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":737475,"sourceType":"datasetVersion","datasetId":379764}],"dockerImageVersionId":30527,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 导入资源\n\nPytorch实现DCGAN的资源：\n\n* 数据准备：使用torchvision.datasets加载图像数据集\n\n* 预处理：使用transforms进行图像归一化和增强\n\n* 模型构建：使用nn.Module定义生成器和判别器\n\n* 训练循环：使用optim优化器交替训练两个网络\n\n* 可视化：使用vutils.make_grid和matplotlib展示生成结果\n\n* 进度显示：使用tqdm显示训练进度","metadata":{}},{"cell_type":"code","source":"# 导入操作系统接口：用于文件和目录操作（如路径管理、环境变量访问）\nimport os\n# 导入随机数生成：用于设置随机种子、打乱数据等操作，确保实验可复现\nimport random\n\n# 导入数值计算库：提供高效的数组操作和数学函数，别名np是行业标准\nimport numpy as np\n# 导入绘图库：用于数据可视化（如绘制损失曲线、生成图像），别名plt是标准用法\nimport matplotlib.pyplot as plt\n# 导入动画模块：用于创建训练过程的动态可视化（如展示GAN生成图像的演化过程）\nimport matplotlib.animation as animation\n\n# 导入PyTorch核心库：深度学习框架，提供张量计算和自动微分功能\nimport torch\n# 导入神经网络模块：包含各种神经网络层（如卷积层、线性层）、激活函数和损失函数\nimport torch.nn as nn\n# 导入并行处理模块：支持多GPU训练（如DataParallel）\nimport torch.nn.parallel\n# 导入CUDA优化：用于加速卷积神经网络（通常设置cudnn.benchmark = True）\nimport torch.backends.cudnn as cudnn\n# 导入优化器模块：包含SGD、Adam等优化算法\nimport torch.optim as optim\n# 导入数据工具：提供Dataset和DataLoader类，用于高效加载数据\nimport torch.utils.data\n# 导入视觉数据集：提供常用数据集（如MNIST、CIFAR-10），别名datasets是标准用法\nimport torchvision.datasets as datasets \n# 导入图像预处理：提供数据增强和归一化操作（如Resize、ToTensor）\nimport torchvision.transforms as transforms\n# 导入视觉工具：包含图像网格制作函数make_grid，用于可视化批量图像\nimport torchvision.utils as vutils\n\n# 导入Jupyter进度条：在Notebook中显示训练进度条（比标准tqdm更美观），但是报错了，先不用美化\n# from tqdm.notebook import tqdm\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-21T20:09:00.485709Z","iopub.execute_input":"2025-06-21T20:09:00.486069Z","iopub.status.idle":"2025-06-21T20:09:03.904123Z","shell.execute_reply.started":"2025-06-21T20:09:00.486041Z","shell.execute_reply":"2025-06-21T20:09:03.903528Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 全局变量\n参数用于配置生成对抗网络(GAN)的训练环境\n\n\n1. 生成器(G)：\n\n* 输入：100维噪声向量(nz=100)\n\n* 输出：64x64 RGB图像(image_size=64, nc=3)\n\n特征图基数：64(G=64)\n\n2. 判别器(D)：\n\n* 输入：64x64 RGB图像\n\n* 输出：真假概率标量\n\n* 特征图基数：64(D=64)\n\n3. 在Kaggle环境中，需要确认：\n\n* 数据集路径是否正确\n\n* GPU数量是否可用（ngpu=2可能需要调整为实际可用数量）","metadata":{}},{"cell_type":"code","source":"# 数据集路径：指定动漫人脸数据集的存储位置。Kaggle输入数据集通常位于/kaggle/input/目录下，后接数据集名称。\ndataroot = \"/kaggle/input/animefacedataset\"\n# 数据加载工作线程数：设置DataLoader使用的子进程数量，用于并行加载数据。值设为2表示使用2个工作线程，这有助于提高数据加载效率。\nworkers = 2\n# 批量大小：每次训练迭代处理的图像数量。128是常用的批量大小，在显存允许范围内提供较好的训练速度和稳定性。\nbatch_size = 128\n# 图像尺寸：定义训练图像的尺寸。64x64像素是GAN训练的常用尺寸，平衡了细节保留和计算效率。\nimage_size = 64\n# 图像通道数：nc代表\"number of channels\"。3表示RGB彩色图像（红、绿、蓝三个通道）。\nnc = 3\n# 噪声向量维度：nz代表\"noise vector size\"。这是生成器输入的随机噪声向量的长度，100是GAN中常用的维度大小。\nnz = 100\n# 生成器特征图基数：G代表生成器的基本特征图数量。在DCGAN架构中，这是生成器第一层卷积的输出通道数。\nG = 64\n# 判别器特征图基数：D代表判别器的基本特征图数量。在DCGAN架构中，这是判别器最后一层卷积的输入通道数。\nD = 64\n# 学习率：Adam优化器的初始学习率。0.0002是GAN训练中常用的学习率值，有助于稳定训练过程。\nlr = 0.0002\n# Adam优化器参数：Adam优化器的第一个动量衰减率。在GAN中常用0.5而非默认的0.9，这有助于防止模式崩溃。\nbeta1 = 0.5\n# GPU数量：指定要使用的GPU数量。这里设置为2，表示将使用两个GPU进行训练（通过nn.DataParallel实现）。\nngpu = 2\n# 确保模型训练在GPU上进行（如果可用），利用GPU的并行计算加速训练。如果无可用GPU，则自动回退到CPU（训练速度会较慢）。\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","metadata":{"execution":{"iopub.status.busy":"2025-06-21T20:09:10.732012Z","iopub.execute_input":"2025-06-21T20:09:10.732939Z","iopub.status.idle":"2025-06-21T20:09:10.795435Z","shell.execute_reply.started":"2025-06-21T20:09:10.732909Z","shell.execute_reply":"2025-06-21T20:09:10.794390Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 加载数据集（训练集与验证集划分）\n完成数据集的加载、预处理、划分以及可视化。\n\n**数据集加载解释：**\n\n**1. ImageFolder：**\n\n* 适用于按文件夹分类的图像数据集\n\n* 即使所有图像在同一文件夹（单类），也可使用\n\n* 自动为每个图像分配标签（尽管在GAN中通常不需要标签）\n\n**2. 预处理流程：**\n\n* Resize(image_size)：将图像短边缩放到64像素（保持宽高比）\n\n* CenterCrop(image_size)：从中心裁剪64x64像素区域（确保统一尺寸）\n\n* ToTensor()：将PIL图像转换为PyTorch张量（C×H×W格式），并归一化到[0,1]\n\n* Normalize(mean, std)：标准化到[-1,1]范围，公式：image = (image - mean) / std\n\n  * 这里(0.5,0.5,0.5)表示RGB三通道的均值\n\n  * (0.5,0.5,0.5)表示标准差\n\n**数据集划分解释：**\n**1. 80-20划分：**\n\n* 80%用于训练，20%用于验证\n\n* random_split：随机划分数据集，保持原始分布\n \n**数据加载器解释：**\n**1. 关键参数：**\n\n* batch_size=128：每次迭代加载128张图像\n\n* shuffle=True：每个epoch打乱数据顺序（防止模型记忆顺序）\n\n* num_workers=2：使用2个子进程并行加载数据（加速数据加载）\n\n**设备选择更新：**\n增加了ngpu > 0条件：只有显式要求使用GPU时才使用GPU\n\n**可视化过程详解：**\n**1. next(iter(train_dataloader))：**\n\n* 获取数据加载器的第一个批次\n\n* real_batch[0]是图像张量，real_batch[1]是标签（GAN中通常不用）\n\n**2. vutils.make_grid()：**\n\n* 将多个图像拼接成网格\n\n* real_batch[0].to(device)[:64]：取前64张图像并移到设备\n\n* padding=2：图像间留2像素空白\n\n* normalize=True：将[-1,1]范围映射回[0,1]（Matplotlib需要）\n\n**3. .cpu()：**\n\n* 将GPU上的张量移回CPU（Matplotlib无法直接处理GPU张量）\n\n**4. np.transpose(..., (1,2,0))：**\n\n* 维度重排：从PyTorch的(C,H,W)格式变为Matplotlib的(H,W,C)格式\n\n* 原始维度：0=通道数，1=高度，2=宽度 → 变为：1=高度，2=宽度，0=通道\n\n**5. plt.imshow()：**\n\n* 显示图像网格\n\n* 自动处理[0,1]范围内的浮点图像数据\n\n\n**整体流程：**\n\n数据准备：加载→预处理→划分\n\n数据加载：创建高效加载器（支持批处理和并行）\n\n设备配置：智能选择CPU/GPU\n\n数据检查：可视化样本确保预处理正确\n\n**注意事项：**\n\n在GAN中，验证集通常用于监控训练过程而非模型选择\n\nshuffle=True对训练集很重要，但对验证集通常设为False\n\n图像标准化到[-1,1]是GAN的常见做法，有助于模型收敛\n\n可视化时的normalize=True确保正确显示图像","metadata":{}},{"cell_type":"code","source":"# 创建ImageFolder数据集\ndataset = datasets.ImageFolder(root=dataroot,\n                           transform=transforms.Compose([\n                               transforms.Resize(image_size),   # 调整图像大小\n                               transforms.CenterCrop(image_size),  # 中心裁剪\n                               transforms.ToTensor(),  # 转换为张量\n                               transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),  # 标准化\n                           ]))\n\n# 划分训练集和验证集\ntrain_size = int(0.8 * len(dataset))\nval_size = len(dataset) - train_size\n\ntrain_dataset, val_dataset = torch.utils.data.random_split(dataset, [train_size, val_size])\n\n# 创建数据加载器\ntrain_dataloader = torch.utils.data.DataLoader(\n    train_dataset, \n    batch_size=batch_size,           \n    shuffle=True,\n    num_workers=workers)\nval_dataloader = torch.utils.data.DataLoader(\n    val_dataset, \n    batch_size=batch_size,                            \n    shuffle=True, \n    num_workers=workers)\n\n# 设备选择（更新版）\ndevice = torch.device(\"cuda:0\" if (torch.cuda.is_available() and ngpu > 0) else \"cpu\")\n# 可视化训练图像\nreal_batch = next(iter(train_dataloader))  # 获取一个批次的数据\nplt.figure(figsize=(8,8))  # 创建8x8英寸的画布\nplt.axis(\"off\")  # 关闭坐标轴\nplt.title(\"Training Images\")  # 设置标题\n# 创建并显示图像网格\nplt.imshow(\n    np.transpose(\n        vutils.make_grid(\n            real_batch[0].to(device)[:64],  # 取前64张图像\n            padding=2,  # 图像间2像素间距\n            normalize=True  # 从[-1,1]反归一化到[0,1]\n        ).cpu(),  # 移回CPU\n        (1,2,0)  # 维度重排(CHW→HWC)\n    )\n)","metadata":{"execution":{"iopub.status.busy":"2025-06-21T20:09:15.813619Z","iopub.execute_input":"2025-06-21T20:09:15.814008Z","iopub.status.idle":"2025-06-21T20:10:00.010548Z","shell.execute_reply.started":"2025-06-21T20:09:15.813979Z","shell.execute_reply":"2025-06-21T20:10:00.009583Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 权重初始化函数定义\n\n定义了一个权重初始化函数 `weights_init`，用于初始化生成器和判别器的权重，它在GAN训练中起着关键作用\n\n这个函数会根据层的类型应用不同的初始化策略，这是GAN训练中常见的做法。\n\n**函数功能解析：**\n\n**1. classname = m.__class__.__name__：**\n\n* 获取传入模块m的类名（如Conv2d、BatchNorm2d）\n\n* 这是通过Python的内省机制实现的\n\n**1. 卷积层初始化：**\n\n* 检查类名是否包含\"Conv\"（适用于Conv2d等卷积层）\n\n* 使用正态分布初始化权重：\n\n  * 均值 = 0.0\n\n  * 标准差 = 0.02\n\n* 这是GAN训练的标准做法，有助于稳定训练\n\n**3. 批归一化层初始化：**\n\n* 检查类名是否包含\"BatchNorm\"（适用于BatchNorm2d等）\n\n* 权重初始化：\n\n  * 正态分布，均值=1.0，标准差=0.02\n\n  * 这样初始缩放因子接近1\n\n* 偏置初始化：\n\n  * 常数0，确保初始无偏移\n \n# 在GAN训练中的重要性：\n**为什么需要特殊初始化？**\n\n* GAN训练非常敏感，不良初始化可能导致模式崩溃或梯度消失\n\n* DCGAN论文(https://arxiv.org/abs/1511.06434)推荐这种初始化策略\n\n**各层初始化的理由：**\n\n* 卷积层：小标准差(0.02)防止初始输出过大\n\n* 批归一化层：\n\n  * 权重均值1.0：保持初始缩放不变\n\n  *  偏置0：初始无偏移\n\n  * 小标准差(0.02)：避免初始激活值过大\n\n**未处理层：**\n\n* 线性层(Linear)：未在此函数处理，应单独处理\n\n* 其他层：保持PyTorch默认初始化\n\n\n# 为什么这对动漫人脸生成重要：\n\n1. 稳定训练：动漫人脸通常有清晰的边缘和鲜明色彩，良好初始化有助于保留这些特征\n\n1. 加速收敛：避免初始权重过大导致的梯度爆炸\n\n1. 防止模式崩溃：均匀的初始权重减少生成单一类型图像的风险","metadata":{}},{"cell_type":"code","source":"# 权重初始化函数，用于初始化生成器和判别器的权重\ndef weights_init(m):\n    classname = m.__class__.__name__\n    if classname.find('Conv') != -1:  # 卷积层初始化\n        nn.init.normal_(m.weight.data, 0.0, 0.02)  \n    elif classname.find('BatchNorm') != -1:   #批归一化层初始化\n        nn.init.normal_(m.weight.data, 1.0, 0.02)  \n        nn.init.constant_(m.bias.data, 0)","metadata":{"execution":{"iopub.status.busy":"2025-06-21T20:10:21.550991Z","iopub.execute_input":"2025-06-21T20:10:21.551357Z","iopub.status.idle":"2025-06-21T20:10:21.557159Z","shell.execute_reply.started":"2025-06-21T20:10:21.551326Z","shell.execute_reply":"2025-06-21T20:10:21.556132Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 发生器与鉴别器\n\n在GAN中，发生器起着上采样和下采样的作用。发生器将低分辨率图像或随机噪声矢量作为输入，并逐步对其进行上采样。另一方面，生成器对图像进行降维采样。\n    \n## 上采样-发生器\n\n上采样允许生成器将低分辨率输入转换为类似于真实数据的高分辨率输出。通常使用转置卷积、最近邻插值、双线性插值或像素洗牌。在DCGAN中，使用转置卷积。\n\n## 关于转置cnn的注意事项\n\n转置卷积可以有效地提高分辨率，同时保持一定的结构模式。它们扩展了特征图的空间维度。它们通过扩展输入张量的空间维度，同时通过反向传播学习适当的权重，从而逆转了常规cnn的这一过程。这些卷积不是在输入上滑动滤波器并对其进行下采样，而是使用大于1的分数阶跃来上采样并提高分辨率。\n\n## 下采样-鉴别器\n\n下采样操作是发电机网络中常用的一种逐步减小数据大小和捕获高级特征的方法。下采样执行以下操作\n1. 降维\n2. 通过逐步降采样，gan可以学习分层表示，其中低分辨率特征映射捕获全局上下文，而高分辨率特征映射专注于更精细的细节\n3. 通过不聚焦单个像素，使GAN能够学习更鲁棒和广义的特征","metadata":{}},{"cell_type":"markdown","source":"# 生成器定义\n\n**关键组件解析：**\n\n**1. 转置卷积块(block)：**\n\n* 核心组件：nn.ConvTranspose2d（转置卷积/反卷积）\n\n* 作用：实现上采样（低分辨率→高分辨率）\n\n* 参数：\n\n  * in_channels/out_channels：输入/输出通道数\n\n  * kernel_size=4：4x4卷积核（GAN标准）\n\n  * stride=2：步长2（尺寸加倍）\n\n  * padding=1：填充1（保持尺寸计算一致）\n\n**2. 批归一化(BatchNorm)：**\n\n* 加速训练并稳定收敛\n\n* 第一层禁用（因为输入是随机噪声）\n\n**3. 激活函数：**\n\n* nn.ReLU：内部块使用ReLU提供非线性\n\n* nn.Tanh：输出层使用Tanh将像素值压缩到[-1,1]\n\n**尺寸变化过程：**\n\n输入噪声向量 z 形状：(batch_size, 100, 1, 1)\n\n1. 第一层：ConvTranspose2d(100→512)\n\n* 输入：100×1×1\n\n* 输出：512×4×4（公式：(1-1)×1 + 4 - 2×0 = 4）\n\n2. 第二层：ConvTranspose2d(512→256)\n\n* 输出：256×8×8（(4-1)×2 + 4 - 2×1 = 8）\n\n3. 第三层：ConvTranspose2d(256→128)\n\n* 输出：128×16×16（(8-1)×2 + 4 - 2×1 = 16）\n\n4. 第四层：ConvTranspose2d(128→64)\n\n* 输出：64×32×32（(16-1)×2 + 4 - 2×1 = 32）\n\n5. 输出层：ConvTranspose2d(64→3)\n\n* 输出：3×64×64（(32-1)×2 + 4 - 2×1 = 64）\n\n**设计特点：**\n\n1. 渐进式上采样：\n\n* 4×4 → 8×8 → 16×16 → 32×32 → 64×64\n\n* 每次分辨率翻倍，通道数减半（512→256→128→64→3）\n\n2. 通道数配置：\n\n* 使用全局参数G=64控制特征图基数\n\n* 最高通道数：G*8=512（平衡表达能力和计算效率）\n\n3. 偏置处理：\n\n* 所有卷积层设置bias=False\n\n* 因为后续使用批归一化，它本身包含可学习的偏置参数\n\n**在动漫人脸生成中的优势：**\n\n1. 细节保留：\n\n* 转置卷积层逐步重建面部特征\n\n* 64×64分辨率足以捕捉动漫人脸的关键特征\n\n2. 风格适应：\n\n* 批归一化帮助模型学习动漫特有的色彩分布\n\n* Tanh输出匹配预处理范围（[-1,1]）\n\n3. 参数效率：\n\n* 约500-1000万参数（取决于具体实现）\n\n* 在Kaggle GPU环境中训练速度合理","metadata":{}},{"cell_type":"code","source":"class Generator(nn.Module):\n    def __init__(self, latent_dim=100):\n        super().__init__()\n        self.latent_dim = latent_dim  # 噪声向量的维度\n\n        # 定义生成器块（转置卷积块）\n        def block(in_channels, out_channels, normalize=True, ks=4, s=2, pad=1):\n            layers = []\n            # 添加转置卷积层\n            layers.append(nn.ConvTranspose2d(in_channels, out_channels,\n                                             kernel_size=ks, stride=s,\n                                             padding=pad, bias=False))\n            if normalize:\n                # 添加批归一化层\n                layers.append(nn.BatchNorm2d(out_channels))\n            # 添加激活函数\n            layers.append(nn.ReLU(True))  # inplace=True节省内存\n            return layers\n\n        # 构建生成器序列\n        self.model = nn.Sequential(\n            # 第一块：将100x1x1噪声向量转换为512x4x4特征图\n            *block(self.latent_dim, G * 8, normalize=False, s=1, pad=0),\n            # 第二块：上采样到256x8x8\n            *block(G * 8, G * 4),\n            # 第三块：上采样到128x16x16\n            *block(G * 4, G * 2),\n            # 第四块：上采样到64x32x32\n            *block(G * 2, G),\n            # 最终输出层：上采样到3x64x64 RGB图像\n            nn.ConvTranspose2d(in_channels=G, out_channels=3,\n                               kernel_size=4, stride=2, padding=1, bias=False),\n            nn.Tanh()  # 将输出压缩到[-1,1]范围\n        )\n        \n    def forward(self, z):\n        return self.model(z)  # 前向传播\n\n# 创建生成器实例并移至设备\nnetG = Generator().to(device)\n\n# 多GPU支持（如果可用）\nif (device.type == 'cuda') and (ngpu > 1):\n    netG = nn.DataParallel(netG, list(range(ngpu)))\n# 应用权重初始化\nnetG.apply(weights_init)\n\n# 打印模型结构\nprint(netG)","metadata":{"execution":{"iopub.status.busy":"2025-06-21T20:10:27.555682Z","iopub.execute_input":"2025-06-21T20:10:27.556384Z","iopub.status.idle":"2025-06-21T20:10:27.616954Z","shell.execute_reply.started":"2025-06-21T20:10:27.556355Z","shell.execute_reply":"2025-06-21T20:10:27.616106Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 判别器定义\n\n**关键组件解析：**\n\n**1. 卷积块(block)：**\n\n* 核心组件：nn.Conv2d（标准卷积）\n\n* 作用：实现下采样（高分辨率→低分辨率）\n\n* 参数：\n\n  * kernel_size=4：4x4卷积核（GAN标准）\n\n  * stride=2：步长2（尺寸减半）\n\n  * padding=1：填充1（保持尺寸计算一致）\n\n**2. 批归一化(BatchNorm)：**\n\n* 加速训练并稳定收敛\n\n* 第一层禁用（因为输入是真实/生成图像）\n\n**3. 激活函数：**\n\n* nn.LeakyReLU(0.2)：负斜率0.2，防止梯度消失\n\n* nn.Sigmoid：输出层压缩到[0,1]范围，表示真实概率\n\n**尺寸变化过程：**\n\n输入图像形状：(batch_size, 3, 64, 64)\n\n1. 第一层：Conv2d(3→64)\n\n* 输出：64×32×32（公式：(64+2×1-4)/2 + 1 = 32）\n\n2. 第二层：Conv2d(64→128)\n\n* 输出：128×16×16（(32+2×1-4)/2 + 1 = 16）\n\n3. 第三层：Conv2d(128→256)\n\n* 输出：256×8×8（(16+2×1-4)/2 + 1 = 8）\n\n4. 第四层：Conv2d(256→512)\n\n* 输出：512×4×4（(8+2×1-4)/2 + 1 = 4）\n\n5. 输出层：Conv2d(512→1)\n\n* 输出：1×1×1（(4+2×0-4)/1 + 1 = 1）\n\n**设计特点：**\n\n1. 渐进式下采样：\n\n* 64×64 → 32×32 → 16×16 → 8×8 → 4×4 → 1×1\n\n* 每次分辨率减半，通道数加倍（3→64→128→256→512→1）\n\n2. 与生成器对称：\n\n* 通道数：生成器递减 vs 判别器递增\n\n* 结构：生成器使用转置卷积上采样 vs 判别器使用卷积下采样\n\n3. LeakyReLU的优势：\n\n* 允许负值梯度传播（标准ReLU在负值区梯度为0）\n\n* 斜率0.2是GAN标准值，平衡信息流动\n\n**在动漫人脸识别中的优势：**\n\n1. 特征提取能力：\n\n* 多级卷积层有效捕捉动漫人脸特征（大眼睛、鲜明色彩等）\n\n* 从局部纹理到全局结构的分层理解\n\n2. 真假区分：\n\n* 最终1×1输出表示输入图像为真实的概率\n\n* Sigmoid激活提供清晰的决策边界\n\n3. 对抗训练平衡：\n\n* 与生成器复杂度匹配（约500-1000万参数）\n\n* 防止判别器过强导致生成器无法学习","metadata":{}},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        # 定义判别器块（卷积块）\n        def block(in_channels, out_channels, normalize=True):\n            layers=[]\n            # 添加卷积层\n            layers.append(nn.Conv2d(in_channels, out_channels,\n                                    kernel_size=4, stride=2,\n                                    padding=1, bias=False))\n            if normalize:\n                # 添加批归一化层\n                layers.append(nn.BatchNorm2d(out_channels))\n            # 添加LeakyReLU激活函数\n            layers.append(nn.LeakyReLU(0.2, inplace=True))\n            return layers\n\n        # 构建判别器序列\n        self.model = nn.Sequential(\n            # 第一块：从3x64x64输入到64x32x32\n            *block(3, D, normalize=False),  # D=64\n            # 第二块：下采样到128x16x16\n            *block(D, D*2),  # 64→128\n            # 第三块：下采样到256x8x8\n            *block(D*2, D*4),  # 128→256\n            # 第四块：下采样到512x4x4\n            *block(D*4, D*8),  # 256→512\n            # 最终输出层：卷积到1x1x1（真假概率）\n            nn.Conv2d(D*8, 1, kernel_size=4, stride=1, padding=0, bias=False),\n            nn.Sigmoid()  # 输出概率值[0,1]\n        )\n\n    def forward(self, img):\n        return self.model(img)  # 前向传播\n\n# 创建判别器实例并移至设备\nnetD = Discriminator().to(device)\n\n# 多GPU支持（如果可用）\nif (device.type == 'cuda') and (ngpu > 1):\n    netD = nn.DataParallel(netD, list(range(ngpu)))\n# 应用权重初始化\nnetD.apply(weights_init)\n\n# 打印模型结构\nprint(netD)","metadata":{"execution":{"iopub.status.busy":"2025-06-21T20:10:31.562477Z","iopub.execute_input":"2025-06-21T20:10:31.563254Z","iopub.status.idle":"2025-06-21T20:10:31.597877Z","shell.execute_reply.started":"2025-06-21T20:10:31.563225Z","shell.execute_reply":"2025-06-21T20:10:31.597071Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 设置损失函数和优化器\n\n**损失函数解释：**\n\n1. nn.BCELoss()：\n\n* 二元交叉熵损失（Binary Cross Entropy Loss）\n\n* 适用于二元分类任务（真/假）\n\n* 公式：$L = -[y \\cdot \\log(x) + (1 - y) \\cdot \\log(1 - x)]$\n\n  * 其中 $y$ 是标签（0或1），$x$ 是预测概率\n\n* 在GAN中：\n\n  * 真实图像标签：1\n\n  * 生成图像标签：0\n \n\n**固定噪声解释：**\n\n1. 目的：\n\n* 在训练过程中定期用相同的噪声生成图像\n\n* 直观展示生成器学习进度\n\n2. 参数：\n\n* batch_size=128：生成128张图像\n\n* nz=100：噪声向量维度\n\n* 1, 1：空间维度（适应卷积输入格式）\n\n* device=device：确保噪声在GPU上（如果可用）\n\n\n**优化器配置详解：**\n\n1. Adam优化器：\n\n* 自适应矩估计优化器，结合了动量和自适应学习率\n\n* 特别适合GAN训练（相比SGD更稳定）\n\n2. 关键参数：\n\n* lr=0.0002：学习率（DCGAN推荐值）\n\n* betas=(beta1, 0.999)：\n\n  * beta1=0.5：一阶矩估计的衰减率（DCGAN特有设置）\n\n  * 0.999：二阶矩估计的衰减率（标准值）\n\n3. 为什么分开优化器？\n\n* 生成器和判别器需要独立优化\n\n* 参数互不影响（netD.parameters() vs netG.parameters()）\n\n4. 为什么beta1=0.5？\n\n* 在GAN中，较低的动量减少振荡风险\n\n* 防止优化器\"冲过头\"，提高训练稳定性\n\n* DCGAN论文中的经验性设置\n\n\n**在动漫人脸生成中的重要性：**\n\n1. 固定噪声：\n\n* 随着训练进行，可以看到生成器如何逐步学会生成更清晰的动漫人脸\n\n* 帮助诊断模式崩溃等问题\n\n2. BCELoss：\n\n* 直接衡量判别器区分真假的能力\n\n* 提供清晰的优化目标（真→1，假→0）\n\n3. Adam优化器：\n\n* 自适应学习率处理稀疏梯度（GAN常见问题）\n\n* 动量加速收敛，同时beta1=0.5防止振荡","metadata":{}},{"cell_type":"code","source":"# 定义损失函数\ncriterion = nn.BCELoss()\n# 创建固定噪声用于可视化训练进度\nfixed_noise = torch.randn(batch_size, nz, 1, 1, device=device)\n\n# 判别器优化器\noptimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))\n# 生成器优化器\noptimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))","metadata":{"execution":{"iopub.status.busy":"2025-06-21T20:10:36.146238Z","iopub.execute_input":"2025-06-21T20:10:36.146565Z","iopub.status.idle":"2025-06-21T20:10:36.152387Z","shell.execute_reply.started":"2025-06-21T20:10:36.146540Z","shell.execute_reply":"2025-06-21T20:10:36.151312Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train-Validation过程\n\n生成器生成假图像。我们用生成的真实图像和假图像训练鉴别器。在两个步骤上发现的损失的平均值（使用BCE损失函数）给出了鉴别器的损失。类似的方法也用于生成器，我们启用生成器优化的梯度，并向鉴别器发送另一个转发\n\n**关键概念解释：**\n\n1. 训练/验证分离：\n\n* 训练阶段更新权重，验证阶段仅评估性能\n\n* 防止过拟合，监控泛化能力\n\n2. 判别器训练：\n\n* 真实图像：希望判别器输出1 → 损失最小化\n\n* 假图像：希望判别器输出0 → 损失最小化\n\n* 使用detach()：防止生成器权重更新\n\n3. 生成器训练：\n\n* 希望判别器对假图像输出1 → 损失最小化\n\n* 这是对抗训练的核心：生成器学习\"欺骗\"判别器\n\n4. 指标含义：\n\n* D_x：判别器对真实图像的平均输出（理想值≈1）\n\n* D_G_z1：训练判别器时对假图像的平均输出（理想值≈0）\n\n* D_G_z2：训练生成器时对假图像的平均输出（理想值≈1）\n\n* 训练良好时：D_x ≈ 0.5, D_G_z1 ≈ 0.5, D_G_z2 ≈ 0.5（双方势均力敌）\n\n5. 损失曲线解读：\n\n* 健康GAN：G_loss和D_loss在0.5附近波动\n\n* 模式崩溃：G_loss突然下降，D_loss接近0\n\n* 判别器主导：D_loss接近0，G_loss很高\n\n**在动漫人脸生成中的重要性：**\n\n1. 训练稳定性：\n\n* 交替训练防止一方主导\n\n* 验证监控防止过拟合特定风格\n\n2. 视觉质量提升：\n\n* 随着训练进行，D_G_z2应逐渐接近1（生成图像更真实）\n\n* 损失曲线波动减小表示模型收敛\n\n3. 模式多样性：\n\n* 健康指标表示生成器学习多种动漫人脸\n\n* 避免生成单一类型的人脸","metadata":{}},{"cell_type":"code","source":"# 初始化记录结构\nimg_list = []  # 存储生成的图像（用于可视化）\ntrain={  # 训练指标记录\n    \"G_losses\" : [],  # 生成器损失\n    \"D_losses\" : [],  # 判别器损失\n    \"D_xs\" : [],  # 判别器对真实图像的平均输出（应接近1）\n    \"D_G_z1s\" : [],   # 判别器对生成图像的平均输出（训练判别器时，应接近0）\n    \"D_G_z2s\" : []  # 判别器对生成图像的平均输出（训练生成器时，应接近1）\n}\n\nval={  # 验证指标记录（与train结构相同）\n    \"G_losses\" : [],\n    \"D_losses\" : [],\n    \"D_xs\" : [],\n    \"D_G_z1s\" : [],\n    \"D_G_z2s\" : []\n}\n\niters = 0  # 迭代计数器\nnum_epochs = 30  # 训练轮数\n\n# 训练循环\nfor epoch in tqdm(range(num_epochs)):\n    \n    # 训练阶段\n    for i, data in enumerate(train_dataloader, 0):\n        # 1. 训练判别器\n        optimizerD.zero_grad()  # 清除判别器梯度\n        \n        real_data = data[0].to(device)  # 获取真实图像\n        b_size = real_data.size(0)  # 当前批次的实际大小\n        \n        # ???\n        targets = torch.ones(batch_size, 1, device=device)  # 用于生成器训练的标签（全1）\n        # 生成假图像\n        noise = torch.randn(b_size, nz, 1, 1, device=device)\n        fake_data = netG(noise)\n        \n        # 判别器的损失计算\n        # 真实图像损失\n        output = netD(real_data).view(-1)  # 判别器对真实图像的输出\n        real_targets = torch.ones(real_data.size(0), 1, device=device)  # 目标标签为1（真实）\n        errD_real = criterion(output, real_targets.squeeze(1))  # 计算损失\n        errD_real.backward()  # 反向传播\n        D_x = output.mean().item()  # 记录判别器对真实图像的平均输出\n\n        # 假图像损失\n        fake_targets = torch.zeros(fake_data.size(0), 1, device=device)  # 目标标签为0（假）\n        output = netD(fake_data.detach()).view(-1)  # 使用detach()防止梯度传播到生成器\n        errD_fake = criterion(output, fake_targets.squeeze(1))  # 计算损失\n        errD_fake.backward()  # 反向传播\n        D_G_z1 = output.mean().item()  # 记录判别器对假图像的平均输出\n\n        # 总判别器损失\n        errD = (errD_real + errD_fake)/2\n\n        optimizerD.step()  # 更新判别器参数\n\n        # 2. 训练生成器\n        #GENERATOR LOSS CALCULATION\n        optimizerG.zero_grad()  # 清除生成器梯度\n        output = netD(fake_data).view(-1)  # 再次通过判别器\n        Gfake_targets = torch.ones_like(output)  # 目标标签为1（希望判别器认为假图像是真实的）\n\n        errG = criterion(output, Gfake_targets)  # 计算损失\n        errG.backward()  # 反向传播\n        D_G_z2 = output.mean().item()  # 记录判别器对假图像的平均输出\n\n        optimizerG.step()  # 更新生成器参数\n\n        # 保存训练指标\n        train[\"G_losses\"].append(errG.item())\n        train[\"D_losses\"].append(errD.item())\n        train[\"D_xs\"].append(D_x)\n        train[\"D_G_z1s\"].append(D_G_z1)\n        train[\"D_G_z2s\"].append(D_G_z2)\n\n    # 验证阶段（不更新权重）\n    with torch.no_grad():\n        #VALIDATION LOOP\n        for i, data in enumerate(val_dataloader, 0):\n        \n            real_data = data[0].to(device)\n            b_size = real_data.size(0)\n\n            targets = torch.ones(batch_size, 1, device=device)\n            # 生成假图像\n            noise = torch.randn(b_size, nz, 1, 1, device=device)\n            fake_data = netG(noise)\n\n            output = netD(real_data).view(-1)\n            real_targets = torch.ones(real_data.size(0), 1, device=device)\n            errD_real = criterion(output, real_targets.squeeze(1))\n            D_x = output.mean().item()\n\n            fake_targets = torch.zeros(fake_data.size(0), 1, device=device)\n            output = netD(fake_data.detach()).view(-1)\n            errD_fake = criterion(output, fake_targets.squeeze(1))\n            D_G_z1 = output.mean().item()\n\n            errD = (errD_real + errD_fake)/2\n\n            output = netD(fake_data).view(-1)\n            Gfake_targets = torch.ones_like(output)\n\n            errG = criterion(output, Gfake_targets)\n            D_G_z2 = output.mean().item()\n\n            # 保存验证指标\n            val[\"G_losses\"].append(errG.item())\n            val[\"D_losses\"].append(errD.item())\n            val[\"D_xs\"].append(D_x)\n            val[\"D_G_z1s\"].append(D_G_z1)\n            val[\"D_G_z2s\"].append(D_G_z2)\n        \n    # 打印当前epoch的指标\n    print('Train Logs - [Epoch %d] : G_Loss : %.4f | D_Loss : %.4f | D(x) : %.4f | D(G(z)): %.4f/%.4f'%(epoch,train[\"G_losses\"][epoch],\n                                                                                           train[\"D_losses\"][epoch],\n                                                                                           train[\"D_xs\"][epoch],\n                                                                                           train[\"D_G_z1s\"][epoch],\n                                                                                           train[\"D_G_z2s\"][epoch]))","metadata":{"execution":{"iopub.status.busy":"2025-06-21T20:36:07.678513Z","iopub.execute_input":"2025-06-21T20:36:07.678937Z","iopub.status.idle":"2025-06-21T21:01:27.911652Z","shell.execute_reply.started":"2025-06-21T20:36:07.678902Z","shell.execute_reply":"2025-06-21T21:01:27.910475Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 可视化\n\n**关键组件解析：**\n\n1. 子图布局：\n\n* fig, axes = plt.subplots(1, 2)：创建1行2列的子图网格\n\n* figsize=(15, 6)：设置整体图形宽15英寸，高6英寸\n\n* axes[0]和axes[1]分别代表左右两个子图\n\n2. 训练损失可视化：\n\n* train[\"G_losses\"]：生成器在训练集上的损失值列表\n\n* train[\"D_losses\"]：判别器在训练集上的损失值列表\n\n* 两条曲线共用一个坐标系，便于比较\n\n3. 验证损失可视化：\n\n* val[\"G_losses\"]：生成器在验证集上的损失值列表\n\n* val[\"D_losses\"]：判别器在验证集上的损失值列表\n\n* 与训练损失并列显示，用于检测过拟合\n\n4. 坐标轴标签：\n\n* X轴：Epochs（训练轮数）\n\n* Y轴：Loss（损失值）\n\n* 清晰的标签帮助理解图表含义\n\n\n**如何解读这些图表：**\n\n1. 健康GAN训练的特征：\n\n* 两条曲线在0.5附近波动\n\n* 生成器和判别器损失同步下降\n\n* 训练和验证损失趋势一致\n\n2. 常见问题诊断：\n\n* 判别器主导：D_loss接近0，G_loss很高\n\n* 生成器主导：G_loss接近0，D_loss很高\n\n* 模式崩溃：G_loss突然下降后保持低位\n\n* 过拟合：训练损失持续下降，验证损失上升\n\n3. 在动漫人脸生成中的具体表现：\n\n* 初期：损失值较高且波动大（模型学习基本特征）\n\n* 中期：损失值稳定下降（学习细节特征）\n\n* 后期：损失值稳定在0.5左右（达到纳什均衡）","metadata":{}},{"cell_type":"code","source":"# 创建1行2列的子图布局，整体图形大小为15x6英寸\nfig, axes = plt.subplots(1, 2, figsize=(15, 6))\n\n# 第一个子图：训练损失\naxes[0].plot(train[\"G_losses\"])  # 绘制生成器损失曲线\naxes[0].plot(train[\"D_losses\"])  # 绘制判别器损失曲线\naxes[0].set_title(\"Train Logs\")  # 设置标题\naxes[0].set_xlabel(\"Epochs\")  # 设置X轴标签\naxes[0].set_ylabel(\"Loss\")  # 设置Y轴标签\n\n# 第一个子图：训练损失\naxes[1].plot(val[\"G_losses\"])  # 绘制生成器验证损失\naxes[1].plot(val[\"D_losses\"])  # 绘制判别器验证损失\naxes[1].set_title(\"Validation Logs\")  # 设置标题\naxes[1].set_xlabel(\"Epochs\")  # 设置X轴标签\naxes[1].set_ylabel(\"Loss\")  # 设置Y轴标签\n\n# 显示图形\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2025-06-21T21:11:56.174133Z","iopub.execute_input":"2025-06-21T21:11:56.174521Z","iopub.status.idle":"2025-06-21T21:11:56.731005Z","shell.execute_reply.started":"2025-06-21T21:11:56.174492Z","shell.execute_reply":"2025-06-21T21:11:56.730115Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 改进建议（增强可视化效果）\n\n**增强版改进说明：**\n\n1. 视觉区分：\n\n* 实线表示训练损失，虚线表示验证损失\n\n* 蓝色表示生成器，红色表示判别器\n\n* 添加图例说明\n\n2. 辅助元素：\n\n* 网格线提高可读性\n\n* 固定Y轴范围便于比较\n\n* 更大的字体和标题\n\n3. 布局优化：\n\n* tight_layout()自动调整间距\n\n* 整体标题说明图表主题\n\n* 子图标题更明确\n\n**在动漫人脸生成任务中的价值：**\n\n1. 训练监控：\n\n* 实时观察模型学习进度\n\n* 判断何时停止训练（损失稳定时）\n\n2. 超参数调优：\n\n* 学习率过大：曲线剧烈振荡\n\n* 学习率过小：收敛速度慢\n\n3. 模型健康检查：\n\n* 验证损失明显高于训练损失 → 过拟合\n\n* 判别器损失持续下降 → 需要减弱判别器\n\n* 生成器损失不下降 → 需要增强生成器或调整架构\n\n这些损失曲线是GAN训练最重要的诊断工具之一，特别对于动漫人脸生成这种视觉任务，它们提供了模型学习过程的量化视角，帮助您理解模型何时学会生成高质量、多样化的动漫人脸。","metadata":{}},{"cell_type":"code","source":"fig, axes = plt.subplots(1, 2, figsize=(18, 6))\n\n# 训练损失子图\naxes[0].plot(train[\"G_losses\"], 'b-', label=\"Generator Loss\")\naxes[0].plot(train[\"D_losses\"], 'r-', label=\"Discriminator Loss\")\naxes[0].set_title(\"Training Loss Curves\", fontsize=14)\naxes[0].set_xlabel(\"Epochs\", fontsize=12)\naxes[0].set_ylabel(\"Loss\", fontsize=12)\naxes[0].grid(True, linestyle='--', alpha=0.7)\naxes[0].legend()\naxes[0].set_ylim(0, 3)  # 设置固定Y轴范围便于比较\n\n# 验证损失子图\naxes[1].plot(val[\"G_losses\"], 'b--', label=\"Generator Val Loss\")\naxes[1].plot(val[\"D_losses\"], 'r--', label=\"Discriminator Val Loss\")\naxes[1].set_title(\"Validation Loss Curves\", fontsize=14)\naxes[1].set_xlabel(\"Epochs\", fontsize=12)\naxes[1].set_ylabel(\"Loss\", fontsize=12)\naxes[1].grid(True, linestyle='--', alpha=0.7)\naxes[1].legend()\naxes[1].set_ylim(0, 3)  # 与训练图相同的Y轴范围\n\n# 添加整体标题\nplt.suptitle(\"Generator and Discriminator Loss Progression\", fontsize=16, y=1.05)\n\n# 调整布局\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-21T21:15:21.042799Z","iopub.execute_input":"2025-06-21T21:15:21.043167Z","iopub.status.idle":"2025-06-21T21:15:21.947772Z","shell.execute_reply.started":"2025-06-21T21:15:21.043140Z","shell.execute_reply":"2025-06-21T21:15:21.946925Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 测试生成器\n\n**关键组件详解：**\n\n1. 输入参数：\n\n* fake_data：生成器输出的张量，形状为(batch_size, channels, height, width)\n\n* title：可选标题（当前未使用）\n\n2. 网格计算：\n\n* np.ceil(np.sqrt(batch_size))：计算最接近平方根的行数\n\n* 确保网格尽可能接近正方形（视觉上更美观）\n\n3. 图像处理：\n\n* .cpu().detach().numpy()：将GPU张量转为CPU上的numpy数组\n\n* np.transpose(img, (1, 2, 0))：将PyTorch的通道优先格式转为Matplotlib的通道最后格式\n\n4. 网格布局：\n\n* 使用整数除法(//)和取模(%)定位每个子图位置\n\n* 对超出图像数量的子图位置关闭坐标轴显示\n\n5. 视觉优化：\n\n* figsize=(15,15)：大尺寸显示细节\n\n* wspace=0.01：极小水平间距，使图像更紧凑","metadata":{}},{"cell_type":"code","source":"def generated_image_plot(fake_data, title=\"Generated Images Grid\"):\n    # 获取生成图像的形状信息\n    batch_size, channels, height, width = fake_data.shape\n\n    # 计算网格的行列数\n    num_rows = int(np.ceil(np.sqrt(batch_size)))  # 向上取整的平方根\n    num_cols = int(np.ceil(batch_size / num_rows))  # 计算所需列数\n\n    # 创建子图网格\n    fig, axes = plt.subplots(num_rows, num_cols, figsize=(15, 15))\n\n    # 遍历所有生成的图像\n    for i in range(batch_size):\n        # 处理单个图像\n        img = fake_data[i].cpu().detach().numpy()  # 从GPU移出并转为numpy\n        img = np.transpose(img, (1, 2, 0))  # 从(C,H,W)转为(H,W,C)\n\n        # 确定当前图像在网格中的位置\n        ax = axes[i // num_cols, i % num_cols]  # 整数除法求行，取模求列\n        ax.imshow(img)  # 显示图像\n        ax.axis(\"off\")  # 关闭坐标轴\n\n    # 隐藏多余的子图位置\n    for i in range(batch_size, num_rows * num_cols):\n        axes.flatten()[i].axis(\"off\")\n\n    # 调整子图间距\n    plt.subplots_adjust(wspace=0.01)  # 水平间距很小\n\n    # 显示图形\n    plt.show()\n    \n    batch_size, channels, height, width = fake_data.shape\n\n# 使用固定噪声生成假图像\nfake_data = netG(fixed_noise)\n# 调用自定义可视化函数\ngenerated_image_plot(fake_data)","metadata":{"execution":{"iopub.status.busy":"2025-06-21T21:15:47.909556Z","iopub.execute_input":"2025-06-21T21:15:47.910635Z","iopub.status.idle":"2025-06-21T21:15:51.459013Z","shell.execute_reply.started":"2025-06-21T21:15:47.910602Z","shell.execute_reply":"2025-06-21T21:15:51.458092Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generated_image_plot(fake_data, title=\"Generated Anime Faces\"):\n    batch_size, channels, height, width = fake_data.shape\n    \n    # 计算网格布局\n    num_rows = int(np.ceil(np.sqrt(batch_size)))\n    num_cols = int(np.ceil(batch_size / num_rows))\n    \n    # 创建子图\n    fig, axes = plt.subplots(num_rows, num_cols, figsize=(15, 15))\n    fig.suptitle(title, fontsize=20, y=0.95)  # 添加标题\n    \n    # 处理每张图像\n    for i in range(batch_size):\n        # 转换和反归一化\n        img = fake_data[i].cpu().detach().numpy()\n        img = np.transpose(img, (1, 2, 0))\n        img = (img + 1) / 2.0  # 反归一化到[0,1]\n        \n        # 获取子图位置\n        row = i // num_cols\n        col = i % num_cols\n        ax = axes[row, col] if num_rows > 1 else axes[col]\n        \n        # 显示图像\n        vmin, vmax = np.percentile(img, [1, 99])  # 自适应颜色范围\n        ax.imshow(img, vmin=vmin, vmax=vmax)\n        ax.axis(\"off\")\n        ax.set_title(f\"Image {i+1}\", fontsize=8)  # 添加小标题\n    \n    # 隐藏多余子图\n    for i in range(batch_size, num_rows * num_cols):\n        if num_rows > 1:\n            axes.flatten()[i].axis(\"off\")\n        else:\n            axes[i].axis(\"off\")\n    \n    # 调整布局\n    plt.subplots_adjust(wspace=0.05, hspace=0.05)\n    plt.tight_layout(rect=[0, 0, 1, 0.95])  # 为标题留空间\n    \n    # 保存和显示\n    plt.savefig(f\"/kaggle/working/{title.replace(' ', '_')}.png\", \n                bbox_inches='tight', dpi=100)\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-21T21:17:12.061849Z","iopub.execute_input":"2025-06-21T21:17:12.062661Z","iopub.status.idle":"2025-06-21T21:17:12.070967Z","shell.execute_reply.started":"2025-06-21T21:17:12.062630Z","shell.execute_reply":"2025-06-21T21:17:12.070063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 生成不同噪声的图像\nrandom_noise = torch.randn(16, nz, 1, 1, device=device)\ndiverse_faces = netG(random_noise)\n\n# 可视化结果\ngenerated_image_plot(diverse_faces, title=\"Diverse Anime Faces\")\n\n# 生成插值序列\ninterp_noise = []\nfor alpha in np.linspace(0, 1, 16):\n    interp = alpha * fixed_noise[0] + (1-alpha) * fixed_noise[1]\n    interp_noise.append(interp)\ninterp_noise = torch.stack(interp_noise)\ninterp_faces = netG(interp_noise)\n\ngenerated_image_plot(interp_faces, title=\"Face Morphing Sequence\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-21T21:17:15.275205Z","iopub.execute_input":"2025-06-21T21:17:15.275908Z","iopub.status.idle":"2025-06-21T21:17:19.615082Z","shell.execute_reply.started":"2025-06-21T21:17:15.275876Z","shell.execute_reply":"2025-06-21T21:17:19.614280Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<h1>| Conclusion</h1>\n<p style=\"font-size: 17px\">The model performs quite well, but can be drastically improvised. From the graphs, we can see that the the gap between the <b>G_Loss</b> and <b>D_Loss</b> on Validation is bit far apart and hence it has to be brought closer. <br />\n","metadata":{}}]}