-
基于UNet的扩散模型架构拆解与重构实验扩散模型(Diffusion Models)近年来在生成模型领域表现出色,尤其在图像生成任务中获得了广泛关注。其核心思想是通过逐步向数据添加噪声形成正向扩散过程,再利用逆向过程去噪恢复数据分布。UNet作为一种强大的多尺度特征提取网络,因其优秀的编码-解码结构和跳跃连接,被广泛应用于扩散模型的逆向去噪网络设计中。本文简要拆解并实验基于UNet架构的扩散模型,探讨其结构设计对生成性能的影响。UNet在扩散模型中的作用UNet由编码器和解码器组成,编码器逐步下采样提取语义特征,解码器逐步上采样恢复图像分辨率。中间跳跃连接将编码器的浅层特征直接传递给解码器,有效保留空间细节。扩散模型中的去噪网络需要在多尺度上捕捉图像信息,UNet正符合这一需求,能够在逆向过程中精细还原图像细节。架构拆解基于经典UNet架构,扩散模型的改进主要体现在以下几点:时间步嵌入:为使模型感知当前的扩散步骤,时间步信息通过位置编码或MLP嵌入加入到网络中,影响卷积层的权重或激活。注意力机制:在UNet的中间层加入自注意力模块,增强模型对远程依赖和全局信息的捕捉,提升生成图像的质量。归一化层:采用条件归一化(如LayerNorm、GroupNorm)结合时间嵌入,实现动态调整特征表达,适应不同扩散阶段的去噪需求。重构实验实验中,我们基于公开实现对UNet架构进行重构,尝试以下改进:替换普通卷积为可变形卷积,增强局部感受野的灵活性。引入多头自注意力模块,提升对细节和纹理的捕捉能力。调整时间嵌入方式,从简单MLP到结合Transformer编码器,探索更丰富的时间特征表达。通过在CIFAR-10和CelebA数据集上的训练测试,重构后的模型在FID指标上表现出明显提升,图像细节更加丰富自然,噪声去除效果更加稳定。总结UNet作为扩散模型去噪网络的骨架,具有天然的多尺度特征提取优势。通过合理拆解其架构并结合时间嵌入、注意力机制等现代深度学习技术,可以有效提升扩散模型的生成质量。未来工作可进一步探索轻量化设计和动态结构调整,实现更高效的生成推理。这次基于UNet的扩散模型架构拆解与重构实验,为理解扩散模型的网络设计提供了实践参考,也为后续模型创新奠定了基础。
-
融合CLIP的文本引导扩散模型技术解析在生成式人工智能(AIGC)领域,文本引导的图像生成技术正迅速发展,成为实现人机自然交互的重要突破口。融合CLIP(Contrastive Language-Image Pretraining)与扩散模型的文本引导技术,凭借强大的跨模态理解能力,实现了从文本描述到高质量图像的精准生成,极大拓展了AI创作的边界。CLIP的作用CLIP由OpenAI提出,是一种通过大规模文本与图像对进行对比学习训练的多模态模型。它能够将文本和图像映射到同一潜空间,计算它们的相似度,从而实现文本和图像之间的精准匹配。这种能力使CLIP成为文本引导图像生成的理想“语言理解器”。融合机制传统的扩散模型依赖随机噪声逐步去噪恢复图像,而文本引导扩散模型则在去噪过程中引入文本信息,以引导图像生成更符合文本语义。具体实现方式多样,但核心是利用CLIP编码的文本特征,作为扩散模型中的条件输入:条件嵌入注入:将CLIP编码的文本向量注入到扩散模型的U-Net结构中,通过时序嵌入或条件注意力机制,实现对图像生成过程的语义控制。梯度引导:通过计算生成图像与文本的CLIP相似度梯度,动态调整扩散采样步骤,使生成图像逐步逼近文本描述的语义。技术优势强语义一致性:CLIP的跨模态能力保证生成图像与文本描述高度对应,减少语义偏差。多样化表达:支持复杂、抽象的文本提示,生成风格多样且细节丰富的图像。开源生态:结合开源的CLIP和扩散模型,推动了社区创新与应用快速迭代。应用示例如Stable Diffusion和DALL·E 2等知名模型均融合了CLIP的文本引导机制,实现了用户输入一句话即可生成高质量艺术图像的功能,极大激发了AI辅助创作的潜力。未来展望随着多模态学习和生成技术的融合加深,CLIP引导的扩散模型将在虚拟现实、数字内容创作、智能设计等领域发挥更大作用。未来可能进一步结合声音、视频等多种模态,实现更加丰富和智能的内容生成体验。总之,融合CLIP的文本引导扩散模型不仅提升了图像生成的准确性和表达力,也为人工智能与人类创意的深度融合开辟了新的路径。
-
加速扩散模型采样:用DDIM和Latent Space Diffusion优化推理速度扩散模型作为生成式AI的主流技术之一,凭借其稳定性和高质量输出,在图像生成、文本生成等领域表现出色。然而,扩散模型采样过程需要多步逐渐去噪,导致推理时间较长,成为限制实际应用的重要瓶颈。为了提升推理效率,研究者提出了多种加速方法,其中**DDIM(Denoising Diffusion Implicit Models)和潜空间扩散(Latent Space Diffusion)**是目前广泛应用且效果显著的两大技术。DDIM:非马尔可夫采样实现速度提升传统的扩散模型采样遵循马尔可夫链,需要数百到上千步去噪迭代,计算成本高昂。DDIM通过设计一种非马尔可夫链采样方法,允许以更少的步骤(几十步甚至更少)完成采样。其核心是通过确定性采样路径减少随机性,实现更快的去噪过程,同时保持生成图像的质量。DDIM的优势在于:采样速度明显加快,可缩短生成时间至原来的1/10甚至更低。兼容多数扩散模型结构,无需重新训练即可应用。生成质量基本保持稳定,适合对速度要求较高的应用场景。潜空间扩散:降低高维计算复杂度潜空间扩散技术则从数据表示角度入手。传统扩散模型在高维像素空间中进行逐步噪声添加和去除,计算开销巨大。潜空间扩散先利用自动编码器将高维图像映射到低维潜空间,在该潜空间内执行扩散过程。该方法优势包括:计算量大幅降低,因潜空间维度远小于原始图像空间。提高采样速度的同时,还能保持高分辨率图像生成质量。适合部署于资源有限的硬件环境,如边缘设备和移动端。联合应用与展望将DDIM采样方法应用于潜空间扩散模型,能够实现推理速度和生成质量的双重优化,极大提升扩散模型在实际应用中的可用性。例如,Stable Diffusion就采用了潜空间扩散结合DDIM采样,实现了高效且高质量的文本驱动图像生成。未来,随着采样算法的不断创新和硬件加速技术的发展,扩散模型的推理速度将进一步提升,推动其在游戏、影视制作、实时AI艺术创作等领域的广泛落地。总之,DDIM和潜空间扩散为扩散模型采样速度的突破提供了有力工具,是扩散模型迈向高效实用的关键技术路径。
-
如何从零实现一个简化版的扩散模型(含PyTorch代码)扩散模型因其生成质量高且训练稳定,近年来备受关注。本文将介绍如何用PyTorch从零实现一个简化版的扩散模型,帮助理解其核心思想与流程。核心原理回顾扩散模型由两个过程组成:正向过程(加噪):将清晰图像逐步加入噪声,直至变成纯噪声;逆向过程(去噪):训练神经网络逐步去除噪声,重建图像。训练目标是让模型预测每一步加的噪声,损失函数通常是均方误差(MSE)。简化实现思路数据准备:以MNIST或CIFAR-10小图像为例,归一化到 [−1,1][-1,1][−1,1]。正向加噪:根据时间步 ttt,对图像加入噪声。模型设计:一个简单的卷积神经网络(如小型U-Net)用于预测噪声。训练:随机采样时间步 ttt,训练模型预测对应噪声。采样:从纯噪声开始,迭代调用模型去噪,生成图像。关键代码示例import torch import torch.nn as nn import torch.nn.functional as F import numpy as np # 简单的噪声调度函数 T = 1000 beta = torch.linspace(1e-4, 0.02, T) alpha = 1 - beta alpha_bar = torch.cumprod(alpha, dim=0) # 正向加噪函数 def q_sample(x0, t, noise=None): if noise is None: noise = torch.randn_like(x0) sqrt_alpha_bar = alpha_bar[t].sqrt().view(-1,1,1,1) sqrt_one_minus_alpha_bar = (1 - alpha_bar[t]).sqrt().view(-1,1,1,1) return sqrt_alpha_bar * x0 + sqrt_one_minus_alpha_bar * noise # 简化的去噪网络 class SimpleUNet(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(1, 64, 3, padding=1) self.conv2 = nn.Conv2d(64, 64, 3, padding=1) self.conv3 = nn.Conv2d(64, 1, 3, padding=1) def forward(self, x, t): # 这里简单地将时间步t作为标量输入(可扩展为embedding) h = F.relu(self.conv1(x)) h = F.relu(self.conv2(h)) return self.conv3(h) # 训练示意 model = SimpleUNet() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) mse = nn.MSELoss() for epoch in range(10): for x0, _ in dataloader: # 假设dataloader加载归一化图像 t = torch.randint(0, T, (x0.size(0),)) noise = torch.randn_like(x0) x_noisy = q_sample(x0, t, noise) predicted_noise = model(x_noisy, t) loss = mse(predicted_noise, noise) optimizer.zero_grad() loss.backward() optimizer.step() 总结本文演示了如何用PyTorch实现一个极简化的扩散模型,涵盖正向加噪、噪声预测网络和训练流程。尽管简化,但足以帮助理解扩散模型的核心机制。实际项目中,可进一步优化模型结构、采样算法和噪声调度,提高生成质量与效率。这套方法为初学者搭建了学习扩散模型的良好基础,鼓励大家在此基础上逐步深入探索。
-
扩散模型在AIGC中的核心作用与未来潜力随着人工智能生成内容(AIGC, Artificial Intelligence Generated Content)技术的飞速发展,扩散模型(Diffusion Models)逐渐成为推动这一领域革新的核心算法。它以稳定的训练过程和卓越的生成效果,展现出强大的内容创造能力,深刻影响着图像、视频、音频乃至文本生成的技术格局。核心作用扩散模型的基本思路是通过学习如何将随机噪声逐步“还原”为清晰的目标数据。与传统生成对抗网络(GAN)相比,扩散模型训练更稳定,不易出现模式崩溃或训练不收敛的问题。这种稳健性使其成为AIGC生成高质量、多样化内容的理想选择。在图像生成领域,扩散模型不仅能够生成高清晰度、细节丰富的照片级别图像,还能够通过条件输入(如文本提示)实现跨模态内容创作,极大拓宽了创作的自由度和想象空间。例如,Stable Diffusion和DALL·E 2等开源或商用系统,均基于扩散模型实现了令人惊艳的AI绘画和设计能力。未来潜力多模态融合扩散模型天然支持条件生成,未来将更深入结合文本、语音、视频、动作捕捉等多种数据模态,推动跨领域内容生成,实现从文字描述到动态视频、虚拟现实场景的无缝转换。生成效率提升尽管扩散模型生成质量极高,但目前采样速度较慢。未来通过改进采样算法、模型压缩和硬件加速,扩散模型将实现实时甚至交互式生成,满足游戏、影视等行业对高效创作的需求。个性化与定制化扩散模型结合用户偏好、风格迁移和少样本学习技术,能够生成更加符合个体需求的内容,推动定制化数字艺术和虚拟人等新兴应用快速发展。内容安全与伦理随着AIGC内容影响力增大,扩散模型也将与内容审核、版权保护等技术结合,构建安全、可信赖的AI创作生态。总结扩散模型因其稳定性和卓越的生成效果,已成为AIGC领域的技术基石。它不仅提升了生成内容的质量和多样性,更通过跨模态条件生成扩展了应用边界。未来,随着算法优化和多领域融合,扩散模型将持续推动AI内容生成进入新的高度,助力智能创作全面升级。
-
多模态扩散模型在文图生成中的创新与挑战随着人工智能的发展,多模态扩散模型成为连接文本与图像生成的重要桥梁,推动了文图生成技术的快速进步。这类模型结合了扩散模型的强大生成能力和跨模态理解能力,能够根据文本描述生成高质量、语义一致的图像,极大地拓展了AIGC的应用边界。创新点多模态扩散模型通过联合训练文本编码器和扩散生成网络,实现了语义信息与视觉信息的深度融合。以Stable Diffusion、DALL·E 2为代表的模型,通过将文本嵌入映射到潜在空间,引导扩散过程,生成内容丰富且细节精细的图像。这种生成不仅在艺术创作、广告设计中表现出色,也为教育、游戏开发等领域带来创新工具。此外,多模态扩散模型引入了注意力机制和跨模态对齐技术,使模型能够精准捕捉文本细节,改善图像与描述的匹配度。同时,部分模型支持多语言文本输入,增强了跨文化适应性和普适性。挑战尽管取得显著突破,多模态扩散模型仍面临多方面挑战。首先,生成速度较慢,扩散模型本身的多步采样限制了实时应用场景的推广。其次,模型对文本语义的复杂理解尚不完美,尤其在处理抽象概念、多义词或长文本时,生成图像可能偏离预期。此外,训练多模态模型需要大量带标注的跨模态数据,这在数据收集和隐私保护上提出了高要求。模型还存在潜在的偏见和安全问题,如生成不当内容或反映训练数据中的偏见,亟需设计有效的监管机制。展望未来,多模态扩散模型的发展将侧重于提升生成效率与语义理解能力。结合轻量化架构、加速采样算法以及强化学习技术,或将显著改善用户体验。同时,跨模态数据的多样化与规范化采集,将为模型训练提供坚实基础。多模态扩散模型作为文图生成的重要引擎,必将在创意产业、智能交互等领域发挥越来越核心的作用。综上所述,多模态扩散模型在文图生成领域实现了技术与应用的双重突破,但仍需克服生成效率、语义准确性及伦理安全等挑战,未来前景广阔。
-
用扩散模型生成高质量动漫头像:技术与实现近年来,扩散模型凭借其卓越的图像生成能力,成为动漫头像生成领域的热门技术。与传统生成对抗网络(GAN)相比,扩散模型通过逐步去噪的过程,生成细节丰富且风格多样的动漫头像,极大提升了作品的质量和多样性。技术原理扩散模型的核心思想是从随机噪声开始,逐步还原出目标图像。具体来说,模型先将动漫头像数据加入噪声,训练神经网络学习去噪过程,逐步恢复清晰图像。该方法相较GAN更稳定,减少模式崩溃和生成图像质量不一致的问题。在动漫头像生成中,通常采用潜空间扩散模型(Latent Diffusion Model),先将高分辨率的动漫图像压缩到低维潜空间中进行扩散,大幅降低计算资源消耗,同时保持生成细节。模型结构多基于U-Net,结合时序嵌入和条件输入(如角色特征标签或文本描述),实现定制化生成。数据与训练高质量的动漫头像生成依赖于丰富且标注完善的数据集,如Danbooru、Getchu等动漫风格图片库。训练时,模型输入为带噪声的潜空间图像,目标是预测噪声或原始潜空间表示,训练过程中不断优化损失函数,提升还原能力。实现流程数据预处理:收集动漫头像,进行裁剪、归一化,并通过预训练自动编码器编码为潜空间表示。模型训练:基于U-Net架构设计扩散模型,利用带噪声的潜空间数据训练去噪网络。条件控制:引入文本或属性向量条件,允许用户指定发色、发型、表情等,实现多样化定制。图像生成:推理时从纯噪声潜空间开始,逐步迭代去噪,最后通过解码器输出高质量动漫头像。应用场景扩散模型生成的动漫头像广泛应用于虚拟主播、游戏角色设计、数字艺术创作等领域。其生成过程稳定、细节丰富,能够满足创作者对个性化和艺术性的双重需求。未来展望随着模型结构优化和算力提升,扩散模型有望实现实时生成和更精细的风格控制,推动动漫头像生成进入更高水平。同时,结合大规模预训练和多模态条件,扩散模型将助力打造更加智能和多样的动漫创作工具。总之,扩散模型为动漫头像生成带来了革命性的技术突破,其稳定性和高质量生成能力,正在不断推动动漫数字创作的创新与发展。
-
扩散模型在医学影像生成中的应用探索近年来,扩散模型(Diffusion Models)因其优异的生成能力和稳定性,逐渐被引入医学影像领域,成为解决医学影像生成与增强任务的新兴利器。医学影像数据通常具有高维度、复杂结构且标注成本高,这些特点使得传统生成模型面临诸多挑战,而扩散模型的优势恰好契合这些需求。首先,扩散模型通过逐步加噪与去噪的过程,能够生成细节丰富且结构连贯的医学图像,如MRI、CT、超声等。相比生成对抗网络(GAN)可能出现的模式崩溃和训练不稳定,扩散模型的训练过程更为稳定,生成结果更为多样化,这为医学图像的多样性与真实性提供了保障。在实际应用中,扩散模型被用于医学图像合成,帮助扩充稀缺的医学数据集,缓解数据不足带来的训练瓶颈。例如,在少量真实肺部CT图像基础上,生成大量合成样本,提升诊断模型的鲁棒性和泛化能力。此外,扩散模型在图像重建与去噪方面表现优异,尤其适用于低剂量CT重建和MRI加速扫描。通过学习噪声分布,扩散模型能够有效还原高清晰度图像,降低辐射剂量,减少病人检查风险。值得一提的是,扩散模型在跨模态图像合成中也展现潜力,例如将MRI转换为CT图像,有助于多模态信息融合,提高临床诊断的准确性和效率。尽管扩散模型在医学影像生成中取得了显著进展,但仍面临采样速度慢和计算资源需求高的挑战。未来,通过改进采样算法(如DDIM、FastDDPM)和模型结构优化,扩散模型有望更广泛地应用于临床实践。总之,扩散模型以其独特的生成机制和稳定性,正在推动医学影像生成技术的革新,为医疗诊断、辅助治疗和科研提供新的技术支撑。随着研究的深入,扩散模型将在医学影像领域发挥更大价值。
-
Stable Diffusion全解析:从原理到部署的全流程指南Stable Diffusion是一种基于扩散模型的开源图像生成技术,近年来在AI艺术和生成式人工智能领域引起广泛关注。它以高效的潜空间扩散机制,实现了高质量、灵活的图像生成,被广泛应用于创作、设计、游戏开发等多个领域。本文将简要介绍Stable Diffusion的核心原理、关键架构以及实际部署流程。一、核心原理Stable Diffusion的核心是“潜空间扩散模型(Latent Diffusion Model,LDM)”。传统扩散模型直接在高维像素空间逐步添加和去除噪声,计算资源消耗巨大。而Stable Diffusion首先使用预训练的自动编码器(Autoencoder)将高维图像压缩到一个低维的潜空间中,扩散过程在这个潜空间内进行。这样既大幅降低了计算复杂度,也提升了生成速度。具体流程分为两个阶段:编码:原始图像通过编码器转换为潜空间表示。潜空间扩散:在潜空间中执行正向噪声添加和反向去噪过程。解码:去噪后的潜空间表示通过解码器还原成清晰图像。二、关键架构Stable Diffusion使用了基于U-Net的神经网络作为噪声预测器,同时引入了时间步嵌入和文本条件(通过CLIP文本编码器)以支持文本引导的图像生成。模型训练时以噪声预测任务为目标,通过大量图像和对应文本描述训练,实现了强大的跨模态生成能力。三、部署流程环境准备:安装PyTorch、Transformers、Diffusers等库,配置GPU环境以加速推理。模型获取:下载预训练Stable Diffusion模型权重,通常托管于Hugging Face等平台。代码调用:利用Diffusers库提供的接口,加载模型、输入文本提示,运行采样生成图像。优化部署:根据需求,可结合ONNX、TensorRT等技术进行模型加速,或通过Web服务封装接口,实现远程调用。四、应用展望Stable Diffusion因其开源和高效的特点,已经被广泛应用于数字艺术创作、广告设计、游戏场景生成、虚拟人物建模等。未来,结合更强的多模态理解能力和实时生成技术,Stable Diffusion将为AI创造带来更多可能。总之,Stable Diffusion通过潜空间扩散策略和强大的神经网络架构,极大地提升了生成图像的效率与质量。掌握其原理和部署方法,能够让开发者和创作者快速进入生成式AI的前沿领域。
-
扩散模型中的正向过程与逆向过程:理论推导与可视化解析扩散模型(Diffusion Models)以其清晰的生成机制和优异的图像质量,成为生成模型的重要分支。其中,核心结构由两个关键阶段组成:正向过程(Forward Process)与逆向过程(Reverse Process)。这两个过程从理论到实践,构建了扩散模型的基本运行框架。正向过程:从数据到噪声的逐步破坏正向过程又称为前向扩散过程,表示为一个马尔可夫链。给定原始图像 x0x_0x0,我们通过在每一步加入高斯噪声,使其逐步接近各向同性的标准正态分布。其数学定义为:q(xt∣xt−1)=N(xt;1−βtxt−1,βtI)q(x_t | x_{t-1}) = \mathcal{N}(x_t; \sqrt{1 - \beta_t}x_{t-1}, \beta_t I) q(xt∣xt−1)=N(xt;1−βtxt−1,βtI)其中 βt\beta_tβt 是在第 ttt 步的噪声强度。经过 TTT 步处理后,图像将完全退化为高斯白噪声 xTx_TxT。一个关键性质是:我们可以显式采样任意一步 xtx_txt,公式为:q(xt∣x0)=N(xt;αˉtx0,(1−αˉt)I)q(x_t | x_0) = \mathcal{N}(x_t; \sqrt{\bar{\alpha}_t} x_0, (1 - \bar{\alpha}_t) I) q(xt∣x0)=N(xt;αˉtx0,(1−αˉt)I)这意味着可以一步“跳跃”从 x0x_0x0 添加噪声至任意时间步,极大地简化了训练。逆向过程:从噪声到图像的逐步重建逆向过程的目标是从纯噪声 xTx_TxT 开始,逐步“去噪”重构出原始图像。理想情况下,应构建真实的逆分布:p(xt−1∣xt)=N(xt−1;μθ(xt,t),Σθ(xt,t))p(x_{t-1} | x_t) = \mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \Sigma_\theta(x_t, t)) p(xt−1∣xt)=N(xt−1;μθ(xt,t),Σθ(xt,t))由于真实分布难以直接建模,我们用神经网络(如U-Net)去预测每一步噪声 ϵ\epsilonϵ,并通过重参数化推导得到均值 μθ\mu_\thetaμθ,完成逐步采样。可视化解析:从结构化降噪看生成之美可视化中,正向过程像是图像“融化”为随机点云;而逆向过程则像“风沙中雕塑”,一帧帧清晰地重构图像轮廓。这种逐步可控、结构化的生成过程,让扩散模型在图像、音频、视频等领域展现出极强的泛化与稳定性。综上,正向与逆向过程不仅体现了扩散模型的数学优雅,也构成了其强大生成能力的根基,是理解一切扩散架构(如DDPM、DDIM、Score-based模型)的出发点。
-
DDPM、DDIM与Score-based模型的区别与联系扩散模型家族中,DDPM(Denoising Diffusion Probabilistic Model)、DDIM(Denoising Diffusion Implicit Model)和Score-based模型是三个核心代表,它们在理论上密切相关,但在采样机制、速度与生成质量上各具特点。理解它们之间的关系,有助于深入掌握现代生成模型的发展脉络。DDPM 是扩散模型的基础形式,首次系统提出通过逐步加噪和反向去噪的方式生成高质量图像。其训练目标是预测每一步添加的噪声,使用均值为模型预测、方差预设的高斯分布进行反向采样。虽然图像质量出色,但采样过程通常需要上百甚至上千步,生成速度较慢。为了解决这一问题,DDIM 应运而生。DDIM与DDPM共享相同的训练框架,但在采样阶段采用了确定性推理路径。通过改写反向过程中的随机项,DDIM能够大幅减少采样步数(如从1000步降至50步以内),同时保持图像质量。这使得DDIM成为扩散模型在实际部署中的优选方案之一。Score-based模型(又称为Score Matching with Langevin Dynamics或SDE-based模型)则是从另一个角度理解扩散过程的生成原理。它直接学习数据分布的梯度(score function),使用随机微分方程(SDE)建模噪声演化,通过反向SDE或概率流ODE实现采样。其理论基础更接近连续时间建模,具备数学上的严谨性和灵活性。三者之间存在深刻联系:DDPM与Score-based模型可以看作是不同时间建模下的扩散等价形式,而DDIM则是DDPM采样过程的一种非随机变体。实际上,Score-based模型也可以转化为对应的离散形式,与DDPM形成统一框架。总结来说,DDPM奠定基础,DDIM优化采样效率,而Score-based模型提供更广泛的数学视角。三者共同构成了现代扩散模型理论的核心支柱。
-
扩散概率模型与生成对抗网络(GAN)的对比研究在人工智能生成模型的快速发展中,**扩散概率模型(Diffusion Probabilistic Models, DPM)和生成对抗网络(Generative Adversarial Networks, GAN)**作为两大主流图像生成技术,各自展现出强大的能力。它们的核心思想与实现路径大相径庭,本文将从生成机制、训练稳定性、图像质量与灵活性等角度进行简要对比。首先,从生成机制看,GAN采用博弈论思想,由一个生成器与一个判别器组成。生成器尝试欺骗判别器,判别器则学习分辨真实与伪造图像,二者在对抗中不断提升性能。而扩散模型的基本流程则是**“逐步加噪+逐步去噪”**。其前向过程向图像加入高斯噪声,后向过程利用深度神经网络学习如何从噪声中还原图像。从训练稳定性上,GAN较难训练,常常出现模式崩溃(mode collapse)或不收敛的问题,因为判别器与生成器在对抗中可能不平衡。而扩散模型采用最大似然估计(MLE)或噪声预测,训练过程更稳定,容易收敛。在图像质量方面,过去GAN因其高效率与清晰度常占优势,但近年来扩散模型如Stable Diffusion、Imagen、DALL·E 3等,生成图像的细节与多样性甚至已超越GAN,特别在复杂场景与语义控制上更为出色。此外,扩散模型在可控生成与**跨模态任务(如文本生成图像)**中表现尤为出色,而GAN更多用于图像增强、风格迁移等领域。总之,GAN凭借高效生成仍具优势,适合对实时性要求高的任务;而扩散模型则在高保真生成和多样性方面优势明显,尤其适用于AIGC等精细内容创作场景。未来,二者的融合与互补,可能催生出新一代更强大的生成模型。
-
什么是扩散模型?一文看懂Diffusion Models的基本架构与流程扩散模型(Diffusion Models)是一种生成式模型,通过模拟数据逐步被噪声污染,再反向还原出清晰数据的过程,实现图像、音频甚至文本的生成。近年来,扩散模型因其在图像生成中的高质量表现,迅速成为AIGC(生成式人工智能)的热门技术,如著名的Stable Diffusion和DALL·E 2等都采用了该技术。基本原理扩散模型的核心思想可以概括为“正向扰动 + 反向去噪”。在**正向过程(Forward Process)**中,模型将真实图像逐步添加高斯噪声,直到变成接近纯噪声的图像。这个过程通常由一个马尔可夫链定义,每一步的图像都被加入一小部分噪声。在**反向过程(Reverse Process)**中,模型训练一个神经网络来一步步去除噪声,逐步还原出原始图像。训练目标是最小化预测噪声与真实噪声之间的差异。模型架构主流扩散模型多采用U-Net结构作为噪声预测器,结合时间步嵌入(Timestep Embedding)与条件输入(如文本或标签),使模型具备良好的表达能力。为了提高采样速度,常会引入改进机制如DDIM(去噪扩散隐变量模型)或Latent Diffusion(潜空间扩散)等。流程总结数据准备:输入图像添加逐步噪声,形成训练对(噪声图,原图)。模型训练:学习如何从噪声图中预测噪声或原始数据。图像生成:从随机噪声开始,逐步去噪还原为高质量图像。应用领域扩散模型广泛应用于图像生成、图像修复、风格迁移、文本生成图像(如文生图)等领域,是当前AI创作中最具前景的技术之一。简而言之,扩散模型就像“教会AI如何擦掉噪声”,它不是直接画图,而是一步步“还原”图像,令人惊叹。
-
扩散模型原理详解:从随机噪声到图像生成的奇妙旅程扩散模型(Diffusion Models)是一类近年来在图像生成任务中大放异彩的生成模型,其核心思想源自物理中的扩散过程。与传统的生成对抗网络(GAN)不同,扩散模型以其稳定的训练过程和惊艳的生成质量,逐步成为AIGC领域的主力军。本文将简要讲解其原理及生成机制。扩散模型的生成过程可分为两个阶段:前向扩散(Forward Diffusion)和反向去噪(Reverse Denoising)。在前向过程里,一张真实图像会逐步被加入高斯噪声,最终变成一张近似纯噪声的图像。这个过程是有序且可控的,通常分为T步。在每一步中,图像都按照某个噪声调度函数加上一些随机扰动,使其信息逐渐丢失。而在反向过程中,模型的任务则是学习如何一步步从纯噪声中恢复原始图像。这个去噪的过程通过一个神经网络(通常是U-Net结构)来建模。模型学习在每一个时间步t,如何将当前的噪声图像“还原”回前一步。最终,当反向过程完成,就生成了一张高质量的图像。可以将其比作“蒙上一层又一层灰尘的玻璃”,前向过程是不断增加灰尘,而反向过程则是逐步擦去灰尘,直至玻璃变得清晰可见。在训练阶段,模型并不直接恢复整张图像,而是学习预测每一步的噪声成分。损失函数通常使用MSE来衡量模型预测噪声与真实添加噪声之间的差异。扩散模型的优势在于生成质量极高、细节丰富,且不易产生模式崩溃(mode collapse)问题。它已广泛应用于图像生成(如Stable Diffusion)、图像修复、超分辨率等任务,成为AIGC时代的核心技术之一。总的来说,扩散模型用“加噪-去噪”的思维方式,完成了从混沌中生出艺术的奇妙旅程。
-
NICEGANNICEGAN(Neural Image Compression Generative Adversarial Network)是一种基于生成对抗网络(GAN)的方法,用于高效的图像压缩与重建。它通过生成器学习从潜在空间中生成压缩图像,并利用判别器优化生成图像的质量,采用无监督学习的方式进行端到端训练。与传统压缩算法(如JPEG)相比,NICEGAN能在较低比特率下保持更高的图像质量,尤其在压缩比和重建效果上具有优势。NICEGAN的应用场景包括图像存储、传输、实时图像压缩以及高质量图像生成,适合网络带宽受限或对图像质量要求较高的领域。ResnetGenerator 类解析【生成器】ResnetGenerator 类是一个图像生成器,通常用于生成对抗网络(GAN)中。它使用了残差网络(ResNet)架构,并结合了自适应实例归一化(AdaIN)模块,能够从潜在空间 z 中生成图像。这个类的实现中包含了上采样和残差块的设计,以及参数 gamma 和 beta 来实现风格的控制。以下是 ResnetGenerator 类的重点解析:构造函数:__init__def __init__(self, input_nc, output_nc, ngf=64, n_blocks=6, img_size=256, light=False): input_nc:输入图像的通道数(例如,彩色图像为3,灰度图像为1)。output_nc:输出图像的通道数(通常是3,代表RGB图像)。ngf:生成器中每个卷积层的基本通道数(通常为64)。n_blocks:生成器中包含的残差块的数量。img_size:输入图像的尺寸(例如,256x256)。light:控制是否使用轻量化的全连接层设计。网络层构建:1. UpBlock0(初始卷积块)UpBlock0 = [nn.ReflectionPad2d(1), nn.Conv2d(int(ngf * mult / 2), ngf * mult, kernel_size=3, stride=1, padding=0, bias=True), ILN(ngf * mult), nn.ReLU(True)] ReflectionPad2d(1):反射填充,用于避免卷积操作带来的边界效应。nn.Conv2d:卷积层,将输入特征图的通道数从 ngf * mult / 2 变为 ngf * mult。ILN(ngf * mult):自定义的实例归一化(Instance Normalization)层。它将归一化参数传递给后续的层。nn.ReLU(True):ReLU激活函数。这个块的作用是初始化卷积处理,并通过实例归一化层来增强训练稳定性。2. FC(全连接层,用于计算gamma和beta)if self.light: FC = [nn.Linear(ngf * mult, ngf * mult, bias=False), nn.ReLU(True), nn.Linear(ngf * mult, ngf * mult, bias=False), nn.ReLU(True)] else: FC = [nn.Linear(img_size // mult * img_size // mult * ngf * mult, ngf * mult, bias=False), nn.ReLU(True), nn.Linear(ngf * mult, ngf * mult, bias=False), nn.ReLU(True)] 如果 light=True,则使用简单的全连接层结构,将输入大小映射到 ngf * mult,适合用于轻量化生成器。否则,采用更复杂的全连接结构,输入大小是经过多次卷积后得到的特征图大小。3. gamma 和 betaself.gamma = nn.Linear(ngf * mult, ngf * mult, bias=False) self.beta = nn.Linear(ngf * mult, ngf * mult, bias=False) 这两个参数 (gamma 和 beta) 是用于自适应实例归一化(AdaIN)的重要参数。它们控制生成图像的风格。4. 残差块:UpBlock1(n_blocks个残差块)for i in range(n_blocks): setattr(self, 'UpBlock1_' + str(i+1), ResnetAdaILNBlock(ngf * mult, use_bias=False)) 这些残差块使用了 ResnetAdaILNBlock,它们结合了残差连接和自适应实例归一化(AdaIN)。每个残差块在图像生成中起到了提取特征、保持信息和提升生成质量的作用。5. UpBlock2(上采样块)UpBlock2 = [] for i in range(n_downsampling): mult = 2**(n_downsampling - i) UpBlock2 += [nn.ReflectionPad2d(1), nn.Conv2d(ngf * mult, int(ngf * mult / 2), kernel_size=3, stride=1, padding=0, bias=False), ILN(int(ngf * mult / 2)), nn.ReLU(True), nn.Conv2d(int(ngf * mult / 2), int(ngf * mult / 2)*4, kernel_size=1, stride=1, bias=True), nn.PixelShuffle(2), ILN(int(ngf * mult / 2)), nn.ReLU(True)] 这个部分包含了上采样操作,用于增加图像的分辨率。使用了 PixelShuffle 技术,这是一个高效的上采样方法,能够提高生成图像的分辨率,同时减少计算量。在每次上采样后,采用卷积和激活函数进一步提取特征。6. 输出层UpBlock2 += [nn.ReflectionPad2d(3), nn.Conv2d(ngf, output_nc, kernel_size=7, stride=1, padding=0, bias=False), nn.Tanh()] ReflectionPad2d(3):再次使用反射填充,确保卷积后图像尺寸正确。nn.Conv2d:将特征图通道数从 ngf 转换为 output_nc,即生成最终输出图像。nn.Tanh():Tanh 激活函数将输出图像的像素值限制在 [-1, 1] 范围,适用于图像生成任务。前向传播:forwarddef forward(self, z): x = z x = self.UpBlock0(x) if self.light: x_ = torch.nn.functional.adaptive_avg_pool2d(x, 1) x_ = self.FC(x_.view(x_.shape[0], -1)) else: x_ = self.FC(x.view(x.shape[0], -1)) gamma, beta = self.gamma(x_), self.beta(x_) for i in range(self.n_blocks): x = getattr(self, 'UpBlock1_' + str(i+1))(x, gamma, beta) out = self.UpBlock2(x) return out输入:接受一个潜在向量 z,这是生成器的输入。UpBlock0:首先通过初始的卷积块进行处理。gamma 和 beta:计算 gamma 和 beta,这些参数控制生成图像的风格。残差块:通过循环应用 n_blocks 个残差块(UpBlock1_1 到 UpBlock1_n_blocks)。UpBlock2:最后通过上采样块(UpBlock2)生成最终的图像。输出:返回生成的图像。总结ResnetGenerator 类的核心在于通过逐步上采样和残差块来生成图像,同时使用 AdaIN 来控制风格。其结构包括:初始的卷积和归一化层多个残差块上采样操作和输出生成的图像这种设计适用于生成任务,尤其是在风格迁移和图像生成对抗网络(GAN)中。ResnetBlock 类解析【残差网络块】ResnetBlock 类是一个基本的残差网络块,典型地用于解决深层网络中的梯度消失或爆炸问题。它通过跳跃连接(skip connection)允许梯度直接通过网络进行反向传播,保持信息流通。构造函数:__init__def __init__(self, dim, use_bias): super(ResnetBlock, self).__init__() conv_block = [] conv_block += [nn.ReflectionPad2d(1), nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias), nn.InstanceNorm2d(dim), nn.ReLU(True)] conv_block += [nn.ReflectionPad2d(1), nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias), nn.InstanceNorm2d(dim)] self.conv_block = nn.Sequential(*conv_block) dim:表示输入和输出的通道数。通常情况下,输入和输出通道数是相同的(例如在卷积操作后,通道数不改变)。use_bias:一个布尔值,表示是否使用偏置项。在卷积操作中,可以选择是否使用偏置,通常在使用批归一化(Batch Normalization)或实例归一化(Instance Normalization)时,会关闭偏置。构建卷积块:nn.ReflectionPad2d(1):反射填充:在卷积操作前对输入进行填充。填充方式为反射填充,这有助于减小边界效应,提高边缘的特征表示能力。填充大小为1,即为每个边缘补充1个像素的反射填充。nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias):这是一个卷积层,使用的卷积核大小为3x3,步幅为1。通道数保持不变,输入和输出的通道数为 dim。padding=0 表示没有额外的零填充。bias=use_bias:偏置项根据构造函数中的参数决定。nn.InstanceNorm2d(dim):实例归一化:对每个样本的每个通道进行归一化。它有助于去除样本间的差异,且不依赖于批次的统计信息(与 BatchNorm 不同)。归一化会计算每个通道的均值和方差,然后将其标准化。nn.ReLU(True):ReLU激活函数:激活函数用于非线性变换,True 参数表示使用原地操作,直接修改输入。第二个卷积块:与第一个卷积块非常类似,但是第二个卷积块中去除了 ReLU 激活函数,因为在 ResNet 结构中,两个卷积操作后通常只有最后一层有激活,而中间不加激活函数。conv_blockconv_block 是一个由上面两层卷积层、归一化和激活函数堆叠而成的 nn.Sequential 容器。这样做的好处是便于管理多个层次,并且使得代码简洁。前向传播:forwarddef forward(self, x): out = x + self.conv_block(x) return outx:输入数据。x + self.conv_block(x):这是典型的残差连接(skip connection),它的作用是将输入数据 x 与经过卷积操作和归一化的特征图相加。这样的操作帮助解决了深层网络中的梯度消失或爆炸问题。最终,返回经过跳跃连接的输出 out。总结:残差连接:最重要的特点是将输入数据 x 与卷积网络输出相加,这样可以更好地传递梯度,避免网络过深时梯度消失。实例归一化:帮助提升训练的稳定性,特别是在图像生成任务中。卷积和归一化:保持特征的表达能力,并通过激活函数引入非线性特征。ResnetBlock 主要用于构建更深的神经网络,它确保每一层学习到的信息可以有效地流动,而不会被前面的层过滤掉。通过这种方式,网络可以学习到更加复杂的特征表示。ResnetAdaILNBlock 类解析【结合了残差学习和自适应实例归一化】ResnetAdaILNBlock 是一个结合了残差学习和自适应实例归一化(AdaILN)的模块。这个类继承自 nn.Module,用于构建网络中的一个模块块,通常用于生成任务中,以增强模型的表达能力和稳定性。构造函数:__init__def __init__(self, dim, use_bias): super(ResnetAdaILNBlock, self).__init__() self.pad1 = nn.ReflectionPad2d(1) self.conv1 = nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias) self.norm1 = adaILN(dim) self.relu1 = nn.ReLU(True) self.pad2 = nn.ReflectionPad2d(1) self.conv2 = nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias) self.norm2 = adaILN(dim) dim:表示输入和输出的通道数。在此模块中,dim 保持一致,即输入和输出的通道数不变。use_bias:是否在卷积操作中使用偏置项,通常和归一化操作一起使用时会关闭偏置项。模块内部操作:self.pad1 = nn.ReflectionPad2d(1) 和 self.pad2 = nn.ReflectionPad2d(1):反射填充:这两个操作对输入的张量进行反射填充,填充大小为1。这有助于减少卷积操作中可能出现的边界效应。self.conv1 = nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias) 和 self.conv2 = nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias):卷积操作:这两个卷积层分别使用大小为 3x3 的卷积核,步幅为1。输入和输出通道数均为 dim,即输入与输出的通道数保持一致。self.norm1 = adaILN(dim) 和 self.norm2 = adaILN(dim):自适应实例归一化(AdaILN):这是一个定制化的归一化方法,能够根据每一层的输入信息调整归一化的参数。adaILN 类的实现可以根据训练中的动态信息自适应地调整归一化过程的影响。这里对每一层输出进行归一化。self.relu1 = nn.ReLU(True):ReLU激活函数:用于非线性激活。True 表示使用原地操作(in-place),即直接修改输入。前向传播:forwarddef forward(self, x, gamma, beta): out = self.pad1(x) out = self.conv1(out) out = self.norm1(out, gamma, beta) out = self.relu1(out) out = self.pad2(out) out = self.conv2(out) out = self.norm2(out, gamma, beta) return out + x输入:x:输入特征图(tensor)。gamma 和 beta:这些是通过自适应归一化方法(AdaILN)进行调整的参数,通常来自于网络中的其他部分(例如网络的其他层)。操作步骤:填充:对输入 x 使用反射填充,增加周围的像素边缘,避免卷积时出现边界效应。第一层卷积:将填充后的输入通过卷积层 conv1 进行处理。归一化:通过 norm1(即 adaILN)对卷积后的特征图进行归一化,gamma 和 beta 用于调整归一化的尺度和偏移。ReLU激活:对归一化后的特征图应用 ReLU 激活函数。第二层卷积:将经过激活后的特征图进行第二次卷积处理(卷积核为3x3,步幅为1)。归一化:同样通过 norm2(即 adaILN)对第二次卷积的输出进行归一化。跳跃连接:将处理后的输出与输入 x 相加(残差连接)。这是典型的 ResNet 结构,旨在缓解深层网络中的梯度消失问题。总结ResnetAdaILNBlock 是一个基于残差学习(ResNet)和自适应实例归一化(AdaILN)方法的模块,它的设计目标是通过结合残差连接和动态归一化来提升神经网络的表现,尤其是在图像生成任务中。关键步骤包括:通过残差连接确保梯度在深层网络中的流动。自适应归一化(AdaILN)根据输入特征动态调整归一化过程,提高模型的表达能力。使用反射填充和卷积操作进一步提取和处理特征。Discriminator 类解析【判别器】该类是一个生成对抗网络(GAN)中的判别器(Discriminator),用于判定输入图像是来自真实数据分布还是生成的假数据(即生成器生成的图像)。该网络采用了多个卷积层,并结合了自适应池化、分类激活映射(CAM)等技术,提升了模型对图像特征的判别能力。以下是对该类代码的详细解析:1. 构造函数 __init__def __init__(self, input_nc, ndf=64, n_layers=7): super(Discriminator, self).__init__() input_nc: 输入图像的通道数,通常为 3(RGB图像)。ndf: 基础通道数,即最初的卷积核数量,默认为 64。该值随着层数增加而翻倍。n_layers: 网络的层数。默认值为 7,表示卷积网络的深度。2. 网络结构初始卷积层和 LeakyReLU 激活函数model = [nn.ReflectionPad2d(1), nn.utils.spectral_norm( nn.Conv2d(input_nc, ndf, kernel_size=4, stride=2, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] nn.ReflectionPad2d(1): 对输入进行反射填充,填充大小为 1,防止卷积操作时丢失边缘信息。nn.utils.spectral_norm: 使用谱归一化对卷积层进行正则化,防止梯度爆炸,提升稳定性。nn.Conv2d: 卷积操作,input_nc 为输入通道数,ndf 为输出通道数(卷积核数量)。使用步幅 stride=2 进行下采样。nn.LeakyReLU(0.2, True): LeakyReLU 激活函数,负部分斜率设置为 0.2。第二层卷积for i in range(1, 2): mult = 2 ** (i - 1) model += [nn.ReflectionPad2d(1), nn.utils.spectral_norm( nn.Conv2d(ndf * mult, ndf * mult * 2, kernel_size=4, stride=2, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] 该块用于构建第二层卷积,输出通道数为上一层的两倍(ndf * mult * 2)。stride=2 继续进行下采样,每经过一次卷积,特征图大小会减半。类激活映射(CAM)模块self.fc = nn.utils.spectral_norm(nn.Linear(ndf * mult * 2, 1, bias=False)) self.conv1x1 = nn.Conv2d(ndf * mult * 2, ndf * mult, kernel_size=1, stride=1, bias=True) self.leaky_relu = nn.LeakyReLU(0.2, True) self.lamda = nn.Parameter(torch.zeros(1)) fc: 通过全连接层计算类激活映射(Class Activation Map, CAM)特征,输出大小为 1。conv1x1: 1x1 卷积操作,进一步处理特征图。lamda: 一个训练过程中可学习的参数,用于调整模型的加权和。额外的卷积层和处理层Dis0_0 = [] for i in range(2, n_layers - 4): mult = 2 ** (i - 1) Dis0_0 += [nn.ReflectionPad2d(1), nn.utils.spectral_norm( nn.Conv2d(ndf * mult, ndf * mult * 2, kernel_size=4, stride=2, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] Dis0_0: 更多的卷积层,每经过一层,通道数将翻倍,进一步提取图像特征。最终的卷积层self.conv0 = nn.utils.spectral_norm( nn.Conv2d(ndf * mult, 1, kernel_size=4, stride=1, padding=0, bias=False)) 最后的卷积层,用于将特征图转换为最终的判别结果,输出单通道(1),用于输出真假图像的判断。3. 前向传播:forwarddef forward(self, input): x = self.model(input) x_0 = x gap = torch.nn.functional.adaptive_avg_pool2d(x, 1) gmp = torch.nn.functional.adaptive_max_pool2d(x, 1) x = torch.cat([x, x], 1) cam_logit = torch.cat([gap, gmp], 1) cam_logit = self.fc(cam_logit.view(cam_logit.shape[0], -1)) 卷积操作:首先通过 self.model 提取输入图像的特征。池化操作:gap: 自适应平均池化,输出形状为 (batch_size, ndf * mult * 2, 1, 1)。gmp: 自适应最大池化,输出形状也为 (batch_size, ndf * mult * 2, 1, 1)。特征拼接:将 gap 和 gmp 拼接在一起,得到最终的 cam_logit,并通过全连接层 self.fc 得到类激活映射。处理和加权x = x * weight.unsqueeze(2).unsqueeze(3) x = self.conv1x1(x) x = self.lamda * x + x_0加权处理:通过 weight 对特征图进行加权。self.lamda * x + x_0: 在 x_0 和加权后的 x 之间做线性加权。lamda 是网络在训练过程中学到的可调参数。生成热图和最终输出heatmap = torch.sum(x, dim=1, keepdim=True) heatmap: 通过对 x 进行求和得到热图,表示每个像素的贡献。x0 = self.Dis0_0(x) x1 = self.Dis1_0(x0) x0 = self.Dis0_1(x0) x1 = self.Dis1_1(x1) x0 = self.pad(x0) x1 = self.pad(x1) out0 = self.conv0(x0) out1 = self.conv1(x1) Dis0_0, Dis1_0, Dis0_1, Dis1_1: 分别经过多个卷积层,进一步提取特征。conv0, conv1: 最终的卷积层,输出判别器的判断结果,通常为单通道的真假判断值。4. 返回的结果return out0, out1, cam_logit, heatmap, zout0, out1: 判别器的最终输出,表示输入图像为真实或假的概率。cam_logit: 类激活映射(Class Activation Map),指示图像哪些区域对判别有更大贡献。heatmap: 热图,展示图像中不同区域的重要性。z: 用于后续操作的中间特征。总结Discriminator 类是一个复杂的卷积神经网络,通过多个卷积层、池化层、LeakyReLU 激活以及自适应池化等技术,构建了一个强大的图像判别器。在生成对抗网络中,判别器的作用是判定输入图像是否为真实数据,或者是生成器生成的假数据。该网络特别注重特征提取和加权,通过类激活映射(CAM)进一步提升其判别能力。核心网络源码【完整】import torch import torch.nn as nn from torch.nn.parameter import Parameter class ResnetGenerator(nn.Module): def __init__(self, input_nc, output_nc, ngf=64, n_blocks=6, img_size=256, light=False): assert(n_blocks >= 0) super(ResnetGenerator, self).__init__() self.input_nc = input_nc self.output_nc = output_nc self.ngf = ngf self.n_blocks = n_blocks self.img_size = img_size self.light = light n_downsampling = 2 mult = 2**n_downsampling UpBlock0 = [nn.ReflectionPad2d(1), nn.Conv2d(int(ngf * mult / 2), ngf * mult, kernel_size=3, stride=1, padding=0, bias=True), ILN(ngf * mult), nn.ReLU(True)] self.relu = nn.ReLU(True) # Gamma, Beta block if self.light: FC = [nn.Linear(ngf * mult, ngf * mult, bias=False), nn.ReLU(True), nn.Linear(ngf * mult, ngf * mult, bias=False), nn.ReLU(True)] else: FC = [nn.Linear(img_size // mult * img_size // mult * ngf * mult, ngf * mult, bias=False), nn.ReLU(True), nn.Linear(ngf * mult, ngf * mult, bias=False), nn.ReLU(True)] self.gamma = nn.Linear(ngf * mult, ngf * mult, bias=False) self.beta = nn.Linear(ngf * mult, ngf * mult, bias=False) # Up-Sampling Bottleneck for i in range(n_blocks): setattr(self, 'UpBlock1_' + str(i+1), ResnetAdaILNBlock(ngf * mult, use_bias=False)) # Up-Sampling UpBlock2 = [] for i in range(n_downsampling): mult = 2**(n_downsampling - i) # Experiments show that the performance of Up-sample and Sub-pixel is similar, # although theoretically Sub-pixel has more parameters and less FLOPs. # UpBlock2 += [nn.Upsample(scale_factor=2, mode='nearest'), # nn.ReflectionPad2d(1), # nn.Conv2d(ngf * mult, int(ngf * mult / 2), kernel_size=3, stride=1, padding=0, bias=False), # ILN(int(ngf * mult / 2)), # nn.ReLU(True)] UpBlock2 += [nn.ReflectionPad2d(1), nn.Conv2d(ngf * mult, int(ngf * mult / 2), kernel_size=3, stride=1, padding=0, bias=False), ILN(int(ngf * mult / 2)), nn.ReLU(True), nn.Conv2d(int(ngf * mult / 2), int(ngf * mult / 2)*4, kernel_size=1, stride=1, bias=True), nn.PixelShuffle(2), ILN(int(ngf * mult / 2)), nn.ReLU(True) ] UpBlock2 += [nn.ReflectionPad2d(3), nn.Conv2d(ngf, output_nc, kernel_size=7, stride=1, padding=0, bias=False), nn.Tanh()] self.FC = nn.Sequential(*FC) self.UpBlock0 = nn.Sequential(*UpBlock0) self.UpBlock2 = nn.Sequential(*UpBlock2) def forward(self, z): x = z x = self.UpBlock0(x) if self.light: x_ = torch.nn.functional.adaptive_avg_pool2d(x, 1) x_ = self.FC(x_.view(x_.shape[0], -1)) else: x_ = self.FC(x.view(x.shape[0], -1)) gamma, beta = self.gamma(x_), self.beta(x_) for i in range(self.n_blocks): x = getattr(self, 'UpBlock1_' + str(i+1))(x, gamma, beta) out = self.UpBlock2(x) return out class ResnetBlock(nn.Module): def __init__(self, dim, use_bias): super(ResnetBlock, self).__init__() conv_block = [] conv_block += [nn.ReflectionPad2d(1), nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias), nn.InstanceNorm2d(dim), nn.ReLU(True)] conv_block += [nn.ReflectionPad2d(1), nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias), nn.InstanceNorm2d(dim)] self.conv_block = nn.Sequential(*conv_block) def forward(self, x): out = x + self.conv_block(x) return out class ResnetAdaILNBlock(nn.Module): def __init__(self, dim, use_bias): super(ResnetAdaILNBlock, self).__init__() self.pad1 = nn.ReflectionPad2d(1) self.conv1 = nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias) self.norm1 = adaILN(dim) self.relu1 = nn.ReLU(True) self.pad2 = nn.ReflectionPad2d(1) self.conv2 = nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=0, bias=use_bias) self.norm2 = adaILN(dim) def forward(self, x, gamma, beta): out = self.pad1(x) out = self.conv1(out) out = self.norm1(out, gamma, beta) out = self.relu1(out) out = self.pad2(out) out = self.conv2(out) out = self.norm2(out, gamma, beta) return out + x class adaILN(nn.Module): def __init__(self, num_features, eps=1e-5, momentum=0.9, using_moving_average=True, using_bn=False): super(adaILN, self).__init__() self.eps = eps self.momentum = momentum self.using_moving_average = using_moving_average self.using_bn = using_bn self.num_features = num_features if self.using_bn: self.rho = Parameter(torch.Tensor(1, num_features, 3)) self.rho[:,:,0].data.fill_(3) self.rho[:,:,1].data.fill_(1) self.rho[:,:,2].data.fill_(1) self.register_buffer('running_mean', torch.zeros(1, num_features, 1,1)) self.register_buffer('running_var', torch.zeros(1, num_features, 1,1)) self.running_mean.zero_() self.running_var.zero_() else: self.rho = Parameter(torch.Tensor(1, num_features, 2)) self.rho[:,:,0].data.fill_(3.2) self.rho[:,:,1].data.fill_(1) def forward(self, input, gamma, beta): in_mean, in_var = torch.mean(input, dim=[2, 3], keepdim=True), torch.var(input, dim=[2, 3], keepdim=True) out_in = (input - in_mean) / torch.sqrt(in_var + self.eps) ln_mean, ln_var = torch.mean(input, dim=[1, 2, 3], keepdim=True), torch.var(input, dim=[1, 2, 3], keepdim=True) out_ln = (input - ln_mean) / torch.sqrt(ln_var + self.eps) softmax = nn.Softmax(2) rho = softmax(self.rho) if self.using_bn: if self.training: bn_mean, bn_var = torch.mean(input, dim=[0, 2, 3], keepdim=True), torch.var(input, dim=[0, 2, 3], keepdim=True) if self.using_moving_average: self.running_mean.mul_(self.momentum) self.running_mean.add_((1 - self.momentum) * bn_mean.data) self.running_var.mul_(self.momentum) self.running_var.add_((1 - self.momentum) * bn_var.data) else: self.running_mean.add_(bn_mean.data) self.running_var.add_(bn_mean.data ** 2 + bn_var.data) else: bn_mean = torch.autograd.Variable(self.running_mean) bn_var = torch.autograd.Variable(self.running_var) out_bn = (input - bn_mean) / torch.sqrt(bn_var + self.eps) rho_0 = rho[:,:,0] rho_1 = rho[:,:,1] rho_2 = rho[:,:,2] rho_0 = rho_0.view(1, self.num_features, 1,1) rho_1 = rho_1.view(1, self.num_features, 1,1) rho_2 = rho_2.view(1, self.num_features, 1,1) rho_0 = rho_0.expand(input.shape[0], -1, -1, -1) rho_1 = rho_1.expand(input.shape[0], -1, -1, -1) rho_2 = rho_2.expand(input.shape[0], -1, -1, -1) out = rho_0 * out_in + rho_1 * out_ln + rho_2 * out_bn else: rho_0 = rho[:,:,0] rho_1 = rho[:,:,1] rho_0 = rho_0.view(1, self.num_features, 1,1) rho_1 = rho_1.view(1, self.num_features, 1,1) rho_0 = rho_0.expand(input.shape[0], -1, -1, -1) rho_1 = rho_1.expand(input.shape[0], -1, -1, -1) out = rho_0 * out_in + rho_1 * out_ln out = out * gamma.unsqueeze(2).unsqueeze(3) + beta.unsqueeze(2).unsqueeze(3) return out class ILN(nn.Module): def __init__(self, num_features, eps=1e-5, momentum=0.9, using_moving_average=True, using_bn=False): super(ILN, self).__init__() self.eps = eps self.momentum = momentum self.using_moving_average = using_moving_average self.using_bn = using_bn self.num_features = num_features if self.using_bn: self.rho = Parameter(torch.Tensor(1, num_features, 3)) self.rho[:,:,0].data.fill_(1) self.rho[:,:,1].data.fill_(3) self.rho[:,:,2].data.fill_(3) self.register_buffer('running_mean', torch.zeros(1, num_features, 1,1)) self.register_buffer('running_var', torch.zeros(1, num_features, 1,1)) self.running_mean.zero_() self.running_var.zero_() else: self.rho = Parameter(torch.Tensor(1, num_features, 2)) self.rho[:,:,0].data.fill_(1) self.rho[:,:,1].data.fill_(3.2) self.gamma = Parameter(torch.Tensor(1, num_features, 1, 1)) self.beta = Parameter(torch.Tensor(1, num_features, 1, 1)) self.gamma.data.fill_(1.0) self.beta.data.fill_(0.0) def forward(self, input): in_mean, in_var = torch.mean(input, dim=[2, 3], keepdim=True), torch.var(input, dim=[2, 3], keepdim=True) out_in = (input - in_mean) / torch.sqrt(in_var + self.eps) ln_mean, ln_var = torch.mean(input, dim=[1, 2, 3], keepdim=True), torch.var(input, dim=[1, 2, 3], keepdim=True) out_ln = (input - ln_mean) / torch.sqrt(ln_var + self.eps) softmax = nn.Softmax(2) rho = softmax(self.rho) if self.using_bn: if self.training: bn_mean, bn_var = torch.mean(input, dim=[0, 2, 3], keepdim=True), torch.var(input, dim=[0, 2, 3], keepdim=True) if self.using_moving_average: self.running_mean.mul_(self.momentum) self.running_mean.add_((1 - self.momentum) * bn_mean.data) self.running_var.mul_(self.momentum) self.running_var.add_((1 - self.momentum) * bn_var.data) else: self.running_mean.add_(bn_mean.data) self.running_var.add_(bn_mean.data ** 2 + bn_var.data) else: bn_mean = torch.autograd.Variable(self.running_mean) bn_var = torch.autograd.Variable(self.running_var) out_bn = (input - bn_mean) / torch.sqrt(bn_var + self.eps) rho_0 = rho[:,:,0] rho_1 = rho[:,:,1] rho_2 = rho[:,:,2] rho_0 = rho_0.view(1, self.num_features, 1,1) rho_1 = rho_1.view(1, self.num_features, 1,1) rho_2 = rho_2.view(1, self.num_features, 1,1) rho_0 = rho_0.expand(input.shape[0], -1, -1, -1) rho_1 = rho_1.expand(input.shape[0], -1, -1, -1) rho_2 = rho_2.expand(input.shape[0], -1, -1, -1) out = rho_0 * out_in + rho_1 * out_ln + rho_2 * out_bn else: rho_0 = rho[:,:,0] rho_1 = rho[:,:,1] rho_0 = rho_0.view(1, self.num_features, 1,1) rho_1 = rho_1.view(1, self.num_features, 1,1) rho_0 = rho_0.expand(input.shape[0], -1, -1, -1) rho_1 = rho_1.expand(input.shape[0], -1, -1, -1) out = rho_0 * out_in + rho_1 * out_ln out = out * self.gamma.expand(input.shape[0], -1, -1, -1) + self.beta.expand(input.shape[0], -1, -1, -1) return out class Discriminator(nn.Module): def __init__(self, input_nc, ndf=64, n_layers=7): super(Discriminator, self).__init__() model = [nn.ReflectionPad2d(1), nn.utils.spectral_norm( nn.Conv2d(input_nc, ndf, kernel_size=4, stride=2, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] #1+3*2^0 =4 for i in range(1, 2): #1+3*2^0 + 3*2^1 =10 mult = 2 ** (i - 1) model += [nn.ReflectionPad2d(1), nn.utils.spectral_norm( nn.Conv2d(ndf * mult, ndf * mult * 2, kernel_size=4, stride=2, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] # Class Activation Map mult = 2 ** (1) self.fc = nn.utils.spectral_norm(nn.Linear(ndf * mult * 2, 1, bias=False)) self.conv1x1 = nn.Conv2d(ndf * mult * 2, ndf * mult, kernel_size=1, stride=1, bias=True) self.leaky_relu = nn.LeakyReLU(0.2, True) self.lamda = nn.Parameter(torch.zeros(1)) Dis0_0 = [] for i in range(2, n_layers - 4): # 1+3*2^0 + 3*2^1 + 3*2^2 =22 mult = 2 ** (i - 1) Dis0_0 += [nn.ReflectionPad2d(1), nn.utils.spectral_norm( nn.Conv2d(ndf * mult, ndf * mult * 2, kernel_size=4, stride=2, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] mult = 2 ** (n_layers - 4 - 1) Dis0_1 = [nn.ReflectionPad2d(1), #1+3*2^0 + 3*2^1 + 3*2^2 +3*2^3 = 46 nn.utils.spectral_norm( nn.Conv2d(ndf * mult, ndf * mult * 2, kernel_size=4, stride=1, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] mult = 2 ** (n_layers - 4) self.conv0 = nn.utils.spectral_norm( #1+3*2^0 + 3*2^1 + 3*2^2 +3*2^3 + 3*2^3= 70 nn.Conv2d(ndf * mult, 1, kernel_size=4, stride=1, padding=0, bias=False)) Dis1_0 = [] for i in range(n_layers - 4, n_layers - 2): # 1+3*2^0 + 3*2^1 + 3*2^2 + 3*2^3=46, 1+3*2^0 + 3*2^1 + 3*2^2 +3*2^3 +3*2^4 = 94 mult = 2 ** (i - 1) Dis1_0 += [nn.ReflectionPad2d(1), nn.utils.spectral_norm( nn.Conv2d(ndf * mult, ndf * mult * 2, kernel_size=4, stride=2, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] mult = 2 ** (n_layers - 2 - 1) Dis1_1 = [nn.ReflectionPad2d(1), #1+3*2^0 + 3*2^1 + 3*2^2 +3*2^3 +3*2^4 + 3*2^5= 94 + 96 = 190 nn.utils.spectral_norm( nn.Conv2d(ndf * mult, ndf * mult * 2, kernel_size=4, stride=1, padding=0, bias=True)), nn.LeakyReLU(0.2, True)] mult = 2 ** (n_layers - 2) self.conv1 = nn.utils.spectral_norm( #1+3*2^0 + 3*2^1 + 3*2^2 +3*2^3 +3*2^4 + 3*2^5 + 3*2^5 = 286 nn.Conv2d(ndf * mult, 1, kernel_size=4, stride=1, padding=0, bias=False)) # self.attn = Self_Attn( ndf * mult) self.pad = nn.ReflectionPad2d(1) self.model = nn.Sequential(*model) self.Dis0_0 = nn.Sequential(*Dis0_0) self.Dis0_1 = nn.Sequential(*Dis0_1) self.Dis1_0 = nn.Sequential(*Dis1_0) self.Dis1_1 = nn.Sequential(*Dis1_1) def forward(self, input): x = self.model(input) x_0 = x gap = torch.nn.functional.adaptive_avg_pool2d(x, 1) gmp = torch.nn.functional.adaptive_max_pool2d(x, 1) x = torch.cat([x, x], 1) cam_logit = torch.cat([gap, gmp], 1) cam_logit = self.fc(cam_logit.view(cam_logit.shape[0], -1)) weight = list(self.fc.parameters())[0] x = x * weight.unsqueeze(2).unsqueeze(3) x = self.conv1x1(x) x = self.lamda*x + x_0 # print("lamda:",self.lamda) x = self.leaky_relu(x) heatmap = torch.sum(x, dim=1, keepdim=True) z = x x0 = self.Dis0_0(x) x1 = self.Dis1_0(x0) x0 = self.Dis0_1(x0) x1 = self.Dis1_1(x1) x0 = self.pad(x0) x1 = self.pad(x1) out0 = self.conv0(x0) out1 = self.conv1(x1) return out0, out1, cam_logit, heatmap, z
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签