• 【扩散模型 李宏毅B站教学以及基础代码运用】


    李宏毅教学视频:
    Link1

    B站DDPM公式推导以及代码实现:
    Link2

    这个视频里面有论文里面的公式推导,并且1小时10分开始讲解实例代码。
    后面一段时间看了一些其他博客,更加明白了。
    就是通过将概率变为均匀分布的形式。正向添加噪声通过重采样技巧可以直接从t和噪声得到。反向推的时候借助一个多条件分布,然后通过各种变换,求得这个概率分布的均值和方差,然后根据公式Sample。
    https://segmentfault.com/a/1190000043744225 扩散模型(Diffusion Model)详解:直观理解、数学原理、PyTorch (代码实现)
    https://blog.csdn.net/qq_40714949/article/details/126643111 简单基础入门理解Denoising Diffusion Probabilistic Model,DDPM扩散模型
    https://blog.csdn.net/zhL816/article/details/127990163 DDPM(Denoising Diffusion Probabilistic Models)扩散模型简述
    https://blog.csdn.net/Little_White_9/article/details/124435560 VLB公式推导
    思维图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    扩散模型概念:

    就像石头里面已经有了雕塑,只需要看我们怎么把其他多余的部分去掉。
    在这里插入图片描述
    注意观察,我们每一个Denoise阶段都不一样,因为每一个阶段传入的图片以及需要处理的noise都不一样,并且直接产生图片比直接产生噪音更难,所以我们通过预测noise来解决问题。
    在这里插入图片描述

    比如下图所示:step2是我们加的噪声,那么传入input和2的时候就希望预测出gt了,然后进行相减得到step1的图片。
    在这里插入图片描述

    Diffusion Model工作原理:

    VAE和Diffusion的区别
    在这里插入图片描述
    先看整个训练过程:
    在这里插入图片描述

    实际结果和我们想的是不一样的。训练时通过X0和噪声得到一个图,逆向的时候输入t和生成的图来得到噪音。想象的是一点一点加入噪音,实际上是直接加进去的。在这里插入图片描述
    推断时刻:theat是带有参数的网络。
    在这里插入图片描述

    影像生成模型本质上的共同目标

    通过采样一个高深distribution生成一个图片。希望生成的图片和真实的图片的distribution很接近。
    在这里插入图片描述
    那么怎么衡量这两个分布的接近程度呢?多数采用的都是Maximum liklihood Estimation.
    我们希望我们采样的数据能够通过theta网络计算出来的概率越大越好。 在这里插入图片描述
    通过数学变换,将概率最大变为Pdata和Ptheat这两个distribution的KL散度最小。
    在这里插入图片描述
    VAE的下界
    Ptheat(x)表示:通过theta产生x的概率。
    在这里插入图片描述

    在这里插入图片描述
    DDPM计算Ptheta(x)的方法 下图表示产生X0的概率。
    在这里插入图片描述
    两者对比
    在这里插入图片描述
    接下来需要计算q(x1|x0)此类公式。
    计算方法:X1到X2的计算方法在论文中有提及。
    在这里插入图片描述
    两个高斯分布都是服从N(0,1),相加的话还是一个高斯分布,并且还是服从N(0,1),只是前面系数会发生变化。系数的话是根号下面数字相加。所以相加之后均值还是为0,方差a方加b方即可,这个在另外一个视频里面有讲解。
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    经过一番推导之后得到:
    在这里插入图片描述
    之后计算最下面三项:
    在这里插入图片描述
    通过以下推导:
    在这里插入图片描述
    之后通过X0,Xt可以得到Xt-1的分布。
    在这里插入图片描述
    可以看到前面一项的mean 和 variance是固定的,第二项的variance也是固定的,因此我们需要把第二项的mean变得和第一项的接近。
    在这里插入图片描述
    那么怎么minimiaze这个mean呢?希望用Xt去预测出来那个mean。
    在这里插入图片描述
    经过推导:
    在这里插入图片描述
    最终得到下图:
    在这里插入图片描述
    里面beta可以学习,但是效果不好,所以使用线性固定。最后加上一个噪声猜测是为了增强鲁棒性,并且本身就是从噪声开始,不加噪声的话可能不会生成图片。

    B站简单示例代码讲解

    # 加载数据集
    %matplotlib inline
    import matplotlib.pyplot as plt
    import numpy as np
    from sklearn.datasets import make_s_curve
    import torch
    
    s_curve,_ = make_s_curve(10**4,noise=0.1)
    print(np.shape(s_curve))
    s_curve = s_curve[:,[0,2]]/10.0
    
    print("shape of s:",np.shape(s_curve))
    
    data = s_curve.T
    
    fig,ax = plt.subplots()
    ax.scatter(*data,color='blue',edgecolor='white');
    
    ax.axis('off')
     
    dataset = torch.Tensor(s_curve).float()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    # 2确定超参数的值
    num_steps = 100
    #制定每一步的beta
    betas = torch.linspace(-6,6,num_steps)
    betas = torch.sigmoid(betas)*(0.5e-2 - 1e-5)+1e-5#计算alpha、alpha_prod、alpha_prod_previous、alpha_bar_sqrt等变量的值
    alphas = 1-betas
    alphas_prod = torch.cumprod(alphas,0)
    # print(alphas_prod)
    alphas_prod_p = torch.cat([torch.tensor([1]).float(),alphas_prod[:-1]],0)
    # print(alphas_prod_p)
    alphas_bar_sqrt = torch.sqrt(alphas_prod)
    one_minus_alphas_bar_log = torch.log(1 - alphas_prod)
    one_minus_alphas_bar_sqrt = torch.sqrt(1 - alphas_prod)assert alphas.shape==alphas_prod.shape==alphas_prod_p.shape==\
    alphas_bar_sqrt.shape==one_minus_alphas_bar_log.shape\
    ==one_minus_alphas_bar_sqrt.shape
    print("all the same shape",betas.shape)
    
    、确定扩散过程任意时刻的采样值
    
    #3 计算任意时刻的x采样值,基于x_0和重参数化
    def q_x(x_0,t):
        """可以基于x[0]得到任意时刻t的x[t]"""
        noise = torch.randn_like(x_0)
        alphas_t = alphas_bar_sqrt[t]
        alphas_1_m_t = one_minus_alphas_bar_sqrt[t]
        return (alphas_t * x_0 + alphas_1_m_t * noise)#在x[0]的基础上添加噪声
    j
    # 4 演示原始数据分布加噪100步后的结果
    
    num_shows = 20
    fig,axs = plt.subplots(2,10,figsize=(28,3))
    plt.rc('text',color='black')#共有10000个点,每个点包含两个坐标
    #生成100步以内每隔5步加噪声后的图像
    for i in range(num_shows):
        j = i//10
        k = i%10
        q_i = q_x(dataset,torch.tensor([i*num_steps//num_shows]))#生成t时刻的采样数据
        axs[j,k].scatter(q_i[:,0],q_i[:,1],color='red',edgecolor='white')
        axs[j,k].set_axis_off()
        axs[j,k].set_title('$q(\mathbf{x}_{'+str(i*num_steps//num_shows)+'})$')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    在这里插入图片描述

    # 5 编写拟合逆扩散过程高斯分布的模型
    
    import torch
    import torch.nn as nn
    ​
    class MLPDiffusion(nn.Module):
        def __init__(self,n_steps,num_units=128):
            super(MLPDiffusion,self).__init__()
            
            self.linears = nn.ModuleList(
                [
                    nn.Linear(2,num_units),
                    nn.ReLU(),
                    nn.Linear(num_units,num_units),
                    nn.ReLU(),
                    nn.Linear(num_units,num_units),
                    nn.ReLU(),
                    nn.Linear(num_units,2),
                ]
            )
            self.step_embeddings = nn.ModuleList(
                [
                    nn.Embedding(n_steps,num_units),
                    nn.Embedding(n_steps,num_units),
                    nn.Embedding(n_steps,num_units),
                ]
            )
        def forward(self,x,t):
    #         x = x_0
            for idx,embedding_layer in enumerate(self.step_embeddings):
                t_embedding = embedding_layer(t)
                x = self.linears[2*idx](x)
                x += t_embedding
                x = self.linears[2*idx+1](x)
                
            x = self.linears[-1](x)
            
            return x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    loss_fn 就是Lsimple得表达式。通过传入参数,生成一个随机噪声,并且送入model里面,那么上面也讲了,model的作用是根据X0,和t预测出我们应该减去的噪声,所以损失函数就是用生成的噪声减去预测的噪声。
    在这里插入图片描述

    # 6 编写训练的误差函数
    def diffusion_loss_fn(model,x_0,alphas_bar_sqrt,one_minus_alphas_bar_sqrt,n_steps):
        """对任意时刻t进行采样计算loss"""
        batch_size = x_0.shape[0]
        
        #对一个batchsize样本生成随机的时刻t
        t = torch.randint(0,n_steps,size=(batch_size//2,))
        t = torch.cat([t,n_steps-1-t],dim=0)
        t = t.unsqueeze(-1)
        
        #x0的系数
        a = alphas_bar_sqrt[t]
        
        #eps的系数
        aml = one_minus_alphas_bar_sqrt[t]
        
        #生成随机噪音eps
        e = torch.randn_like(x_0)
        
        #构造模型的输入
        x = x_0*a+e*aml
        
        #送入模型,得到t时刻的随机噪声预测值
        output = model(x,t.squeeze(-1))
        
        #与真实噪声一起计算误差,求平均值
        return torch.pow((e - output),2).mean()
    # 7、编写逆扩散采样函数(inference)
    
    def p_sample_loop(model,shape,n_steps,betas,one_minus_alphas_bar_sqrt):
        """从x[T]恢复x[T-1]、x[T-2]|...x[0]"""
        cur_x = torch.randn(shape)
        x_seq = [cur_x]
        for i in reversed(range(n_steps)):
            cur_x = p_sample(model,cur_x,i,betas,one_minus_alphas_bar_sqrt)
            x_seq.append(cur_x)
        return x_seq
    ​
    def p_sample(model,x,t,betas,one_minus_alphas_bar_sqrt):
        """从x[T]采样t时刻的重构值"""
        t = torch.tensor([t])
        
        coeff = betas[t] / one_minus_alphas_bar_sqrt[t]
        
        eps_theta = model(x,t)
        
        mean = (1/(1-betas[t]).sqrt())*(x-(coeff*eps_theta))
        
        z = torch.randn_like(x)
        sigma_t = betas[t].sqrt()
        
        sample = mean + sigma_t * z
        
        return (sample)
    # 8、开始训练模型,打印loss及中间重构效果
    
    seed = 1234class EMA():
        """构建一个参数平滑器"""
        def __init__(self,mu=0.01):
            self.mu = mu
            self.shadow = {}
            
        def register(self,name,val):
            self.shadow[name] = val.clone()
            
        def __call__(self,name,x):
            assert name in self.shadow
            new_average = self.mu * x + (1.0-self.mu)*self.shadow[name]
            self.shadow[name] = new_average.clone()
            return new_average
        
    print('Training model...')
    batch_size = 128
    dataloader = torch.utils.data.DataLoader(dataset,batch_size=batch_size,shuffle=True)
    num_epoch = 4000
    plt.rc('text',color='blue')
    ​
    model = MLPDiffusion(num_steps)#输出维度是2,输入是x和step
    optimizer = torch.optim.Adam(model.parameters(),lr=1e-3)for t in range(num_epoch):
        for idx,batch_x in enumerate(dataloader):
            loss = diffusion_loss_fn(model,batch_x,alphas_bar_sqrt,one_minus_alphas_bar_sqrt,num_steps)
            optimizer.zero_grad()
            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(),1.)
            optimizer.step()
            
        if(t%100==0):
            print(loss)
            x_seq = p_sample_loop(model,dataset.shape,num_steps,betas,one_minus_alphas_bar_sqrt)
            
            fig,axs = plt.subplots(1,10,figsize=(28,3))
            for i in range(1,11):
                cur_x = x_seq[i*10].detach()
                axs[i-1].scatter(cur_x[:,0],cur_x[:,1],color='red',edgecolor='white');
                axs[i-1].set_axis_off();
                axs[i-1].set_title('$q(\mathbf{x}_{'+str(i*10)+'})$')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    最后的演示

    动画演示扩散过程和逆扩散过程
    
    import io
    from PIL import Image
    ​
    imgs = []
    for i in range(100):
        plt.clf()
        q_i = q_x(dataset,torch.tensor([i]))
        plt.scatter(q_i[:,0],q_i[:,1],color='red',edgecolor='white',s=5);
        plt.axis('off');
        
        img_buf = io.BytesIO()
        plt.savefig(img_buf,format='png')
        img = Image.open(img_buf)
        imgs.append(img)
    mg = Image.open(img_buf)
        reverse.append(img)
    reverse = []
    for i in range(100):
        plt.clf()
        cur_x = x_seq[i].detach()
        plt.scatter(cur_x[:,0],cur_x[:,1],color='red',edgecolor='white',s=5);
        plt.axis('off')
        
        img_buf = io.BytesIO()
        plt.savefig(img_buf,format='png')
        img = Image.open(img_buf)
        reverse.append(img)
    ​
    imgs = imgs +reverse
    imgs[0].save("diffusion.gif",format='GIF',append_images=imgs,save_all=True,duration=100,loop=0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
  • 相关阅读:
    go strings 包
    includes问题
    什么是React属性钻取(Prop Drilling)
    anaconda打开闪退解决
    Node.js
    计算机网络——HTTPS协议
    Pytest教程:Pytest参数化测试
    RK3588 linux内核中断之上半部(二)
    基于python的django框架选题推荐生鲜超市供应平台
    深度学习基础 2D卷积(1)
  • 原文地址:https://blog.csdn.net/aaatomaaa/article/details/132714492