AIGC基础,从VAE到DDPM原理、代码详解

admin 2023年2月17日20:39:00评论99 views字数 4181阅读13分56秒阅读模式

一.前言

AIGC目前是一个非常火热的方向,DALLE-2,ImageGen,Stable Diffusion的图像在以假乱真的前提下,又有着脑洞大开的艺术性,以下是用开源的Stable Diffusion生成的一些图片。

                 AIGC基础,从VAE到DDPM原理、代码详解

AIGC基础,从VAE到DDPM原理、代码详解

         

AIGC基础,从VAE到DDPM原理、代码详解

AIGC基础,从VAE到DDPM原理、代码详解

这些模型后边都使用了Diffusion Model的技术,但是缺乏相关背景知识去单纯学习Diffusion Model门槛会比较高,不过沿着AE、VAE、CVAE、DDPM这一系列的生成模型的路线、循序学习会更好的理解和掌握,本文将从原理、数学推导、代码详细讲述这些模型。

二. AE (AutoEncoder)

AE模型作用是提取数据的核心特征(Latent Attributes),如果通过提取的低维特征可以完美复原原始数据,那么说明这个特征是可以作为原始数据非常优秀的表征。

AE模型的结构如下图

AIGC基础,从VAE到DDPM原理、代码详解

训练数据通过Encoder得到Latent,Latent再通过Decoder得到重建数据,通过重建数据和训练的数据差异来构造训练Loss,代码如下(本文所有的场景都是mnist,编码器和解码器都用了最基本的卷积网络):

Python                  
class DownConvLayer(tf.keras.layers.Layer):                  
    def __init__(self, dim):                  
        super(DownConvLayer, self).__init__()                  
        self.conv = tf.keras.layers.Conv2D(dim, 3, activation=tf.keras.layers.ReLU(), use_bias=False, padding='same')                  
        self.pool = tf.keras.layers.MaxPool2D(2)                  
                 
    def call(self, x, training=False, **kwargs):                  
        x = self.conv(x)                  
        x = self.pool(x)                  
        return x                  
                 
                 
class UpConvLayer(tf.keras.layers.Layer):                  
    def __init__(self, dim):                  
        super(UpConvLayer, self).__init__()                  
        self.conv = tf.keras.layers.Conv2D(dim, 3, activation=tf.keras.layers.ReLU(), use_bias=False, padding='same')                  
        # 通过UpSampling2D上采样                  
        self.pool = tf.keras.layers.UpSampling2D(2)                  
                 
    def call(self, x, training=False, **kwargs):                  
        x = self.conv(x)                  
        x = self.pool(x)                  
        return x                  
                 
# 示例代码都是通过非常简单的卷积操作实现编码器和解码器                  
class Encoder(tf.keras.layers.Layer):                  
    def __init__(self, dim, layer_num=3):                  
        super(Encoder, self).__init__()                  
        self.convs = [DownConvLayer(dim) for _ in range(layer_num)]                  
                 
    def call(self, x, training=False, **kwargs):                  
        for conv in self.convs:                  
            x = conv(x, training)                  
        return x                  
                 
                 
class Decoder(tf.keras.layers.Layer):                  
    def __init__(self, dim, layer_num=3):                  
        super(Decoder, self).__init__()                  
        self.convs = [UpConvLayer(dim) for _ in range(layer_num)]                  
        self.final_conv = tf.keras.layers.Conv2D(1, 3, strides=1)                  
                 
    def call(self, x, training=False, **kwargs):                  
        for conv in self.convs:                  
            x = conv(x, training)                  
        # 将图像转成和输入图像shape一致                  
        reconstruct = self.final_conv(x)                  
        return reconstruct                  
                 
                 
class AutoEncoderModel(tf.keras.Model):                  
    def __init__(self):                  
        super(AutoEncoderModel, self).__init__()                  
        self.encoder = Encoder(64, layer_num=3)                  
        self.decoder = Decoder(64, layer_num=3)                  
                 
    def call(self, inputs, training=None, mask=None):                  
        image = inputs[0]                  
        # 得到图像的特征表示                  
        latent = self.encoder(image, training)                  
        # 通过特征重建图像                  
        reconstruct_img = self.decoder(latent, training)                  
        return reconstruct_img                  
                 
    @tf.function                  
    def train_step(self, data):                  
        img = data["image"]                  
        with tf.GradientTape() as tape:                  
            reconstruct_img = self((img,), True)                  
        trainable_vars = self.trainable_variables                  
        # 利用l2 loss 来判断重建图片和原始图像的一致性                  
        l2_loss = (reconstruct_img - img) ** 2                  
        l2_loss = tf.reduce_mean(tf.reduce_sum(                  
            l2_loss, axis=(1, 2, 3)                  
        ))                  
        gradients = tape.gradient(l2_loss, trainable_vars)                  
        self.optimizer.apply_gradients(zip(gradients, trainable_vars))                  
        return {"l2_loss": l2_loss}

通过AE模型可以看到,只要有有效的数据的Latent Attribute表示,那么就可以通过Decoder来生成新数据,但是在AE模型中,Latent是通过已有数据生成的,所以没法生成已有数据外的新数据。

所以我们设想,是不是可以假设Latent 符合一定分布规律,只要通过有限参数能够描述这个分布,那么就可以通过这个分布得到不在训练数据中的新Latent ,利用这个新Latent就能生成全新数据,基于这个思路,有了VAE(Variational AutoEncoder 变分自编码器)

三.VAE

VAE 中假设Latent Attributes (公式中用z)符合正态分布,也就是通过训练数据得到的z满足以下条件

AIGC基础,从VAE到DDPM原理、代码详解

因为z是向量,所AIGC基础,从VAE到DDPM原理、代码详解都是向量,分别为正态分布的均值和方差。有了学习得到正态分布的参数 AIGC基础,从VAE到DDPM原理、代码详解,那么就可以从这个正态分布中采样新的z,新的z通过解码器得到新的数据。

所以在训练过程中需要同时优化两点:

1.重建的数据和训练数据差异足够小,也就是生成x的对数似然越高,一般依然用L2 或者L1 loss;

2. AIGC基础,从VAE到DDPM原理、代码详解定义的正态分布需要和标准正态分布的一致,这里用了KL散度来约束两个分布一致;

         

(因为全文公式较多,有140多公式,公众号无法很好支持公式,可以点击原文跳转到我们公开的飞书文档阅读,建议在PC端阅读,未来我们还会持续输出AIGC相关的技术文章)

原文始发于微信公众号(来也技术团队):AIGC基础,从VAE到DDPM原理、代码详解

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月17日20:39:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   AIGC基础,从VAE到DDPM原理、代码详解https://cn-sec.com/archives/1557538.html

发表评论

匿名网友 填写信息