CMU 10423 Generative AI:lec7、8、9(专题1:AE、VAE详解)

news2024/9/22 23:24:10

文章目录

  • 1 AE(自编码器)
    • 1.1 自编码器干什么用的
    • 1.2 自编码器的架构图、输入输出、训练方法
    • 1.3 常见应用
    • 1.4 代码示例:图片的压缩存储和复原
  • 2 VAE(变分自编码器)
    • 2.1 概述
    • 2.2 AE存在的问题:隐空间不平滑,无法生成全新图像
    • 2.3 VAE如何解决上述问题
      • 举例说明
    • 2.4 VAE架构图
    • 2.5 理解架构图中几个点
      • 2.5.1 编码器(Encoder)层的输出是什么?
      • 2.5.2 为什么要让编码器预测 log σ^2,而不是直接预测正太分布中的标准差 σ ?
      • 2.5.3 编码器的输出经过了哪些操作再输入到解码器的?
      • 2.5.4 为什么需要噪声ϵ?
        • 1. **引入随机性,增加生成样本的多样性**
        • 2. **使模型可微分,便于反向传播**
        • 3. **实现隐空间的连续性**
        • 4. **避免过拟合**
        • 总结:
      • 2.5.5 VAE的解码器是把Z复原成跟输入一模一样的图像,还是复原成类似的图像?
        • 1. **目标是生成相似的图像,而不是一模一样的图像**
        • 2. **引入随机性增加了多样性**
        • 3. **隐空间的连续性**
    • 2.6 VAE训练的原理和损失函数
      • 2.6.1 背景知识:KL散度
        • 1. **KL散度是什么?**
        • 2. **KL散度的直观理解**
        • 3. **KL散度的特点**
        • 总结:
      • 2.6.2 VAE的训练过程与损失函数
        • 1. **VAE 的训练步骤**:
        • 2. **损失函数的组成**:
        • 3. **总损失函数**:
        • 4. **反向传播与优化**:
        • 总结:
      • 2.6.3 备注:VAE中KL loss公式的推导过程
        • 1. **KL散度定义**
        • 2 高斯分布(也就是正态分布)的概率密度函数
        • 3 **将高斯分布代入KL散度公式**
        • 4 **对数部分化简**
        • 5 **化简KL散度公式**
        • 6 **整合所有项**
    • 2.7 VAE的全套代码(源自CMU-11785,VAE手写数字生成)
      • 2.7.1 ipynb代码文件流程概述和结果截图
      • 2.7.2 VAE模型架构和损失函数代码

1 AE(自编码器)

1.1 自编码器干什么用的

自编码器主要用于无监督学习的任务。它的目的是通过神经网络学习数据的有效低维表示(编码),然后使用该编码重建原始数据。自编码器通常用于特征学习、数据压缩和去噪等任务。

1.2 自编码器的架构图、输入输出、训练方法

模型结构如下图所示:

在这里插入图片描述

输入:一维向量(由图像拉平而成),即原始图像被转换为一维向量形式,以便输入到自编码器的编码器部分。

Encoder:全连接层。使用全连接层对输入数据进行压缩和特征提取,编码器的目标是将高维的输入数据映射到低维的隐含表示(即“压缩表示”)。

Decoder:全连接层。同样由全连接层构成,负责从编码器生成的低维表示中重建出高维的原始数据,解码器的目的是复原原始输入数据。

输出:一维向量(reshape成二维向量,就是图片了)

损失函数与训练方法:自编码器的训练是通过最小化【输入向量】与【输出向量】的均方误差(MSE loss),这样可以迫使模型在编码器中学习如何提取重要特征,并在解码器中学习如何准确地重建输入数据。

1.3 常见应用

  1. 数据降维、压缩:减少存储量、减少计算量。
  2. 图像去噪:使用去噪自编码器(DAE),通过向输入图像添加噪声,并训练自编码器去重构原始的无噪声图像,可以有效地消除噪声。
  3. 数据生成:变分自编码器(VAE)是一种生成模型,可以用来生成与训练数据分布相似的新数据。
  4. 异常检测:自编码器可以用来检测数据中的异常点。通过训练自编码器重构正常数据,任何重构误差较大的数据点可能就是异常点。这个方法广泛应用于工业设备故障检测、网络入侵检测等领域。
  5. 数据填补:自编码器可以用于数据缺失值的填补。通过训练自编码器重构完整数据,可以用潜在空间的表示来推断并填补缺失的数据。

1.4 代码示例:图片的压缩存储和复原

如下代码说明:

  • 训练一个简单的自编码器对MNIST手写数字进行压缩表示。
  • 单个手写数字图片的信息量:28*28=784个0~255的整数。
  • 压缩表示的信息量:16个浮点数。
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 检测是否有可用的 GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# 定义自编码器类
class Autoencoder(nn.Module):
    def __init__(self):
        super(Autoencoder, self).__init__()
        # 编码器部分
        self.encoder = nn.Sequential(
            nn.Linear(784, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Linear(32, 16)  # 压缩到16维
        )
        # 解码器部分
        self.decoder = nn.Sequential(
            nn.Linear(16, 32),
            nn.ReLU(),
            nn.Linear(32, 64),
            nn.ReLU(),
            nn.Linear(64, 128),
            nn.ReLU(),
            nn.Linear(128, 784)  # 重建回原始784维
        )

    def forward(self, x):
        z = self.encoder(x)  # 编码
        y = self.decoder(z)  # 解码
        return y

# 处理 MNIST 数据集的 transforms
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])

# 加载 MNIST 数据集
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)

# 初始化模型、损失函数和优化器
model = Autoencoder().to(device)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.0005)

# 开始训练
num_epochs = 20
for epoch in range(num_epochs):
    running_loss = 0.0
    for data in train_loader:
imgs, _ = data # 不需要标签,只需要图像
imgs = imgs.view(imgs.size(0), -1).to(device)
        
        outputs = model(imgs)
loss = criterion(outputs, imgs)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}')

print("训练完成!")



# 测试模型的效果
import matplotlib.pyplot as plt

# 从训练集中取一批数据进行测试
dataiter = iter(train_loader)
images, _ = next(dataiter)
images_flat = images.view(images.size(0), -1).to(device)

# 获取模型的重建结果
reconstructed = model(images_flat).view(-1, 1, 28, 28).cpu().detach()  # 转回CPU以便显示

# 显示原始图像和重建图像对比
fig, axes = plt.subplots(2, 6, figsize=(12, 4))
for i in range(6):
    # 显示原始图像
    axes[0, i].imshow(images[i].view(28, 28), cmap='gray')
    axes[0, i].set_title("Original")
    axes[0, i].axis('off')
    
    # 显示重建图像
    axes[1, i].imshow(reconstructed[i].view(28, 28), cmap='gray')
    axes[1, i].set_title("Reconstructed")
    axes[1, i].axis('off')

plt.show()

在这里插入图片描述

2 VAE(变分自编码器)

2.1 概述

时间:2013.12

论文:Auto-Encoding Variational

作者:荷兰阿姆斯特丹大学,2人

成就:ICLR 2024首个时间检验奖

  • 获奖评语:概率建模是对世界进行推理的最基本方式之一。这篇论文率先将深度学习与可扩展概率推理(通过所谓的重新参数化技巧摊销均值场变分推理)相结合,从而催生了变分自动编码器 (VAE)。这项工作的持久价值源于其优雅性。用于开发 VAE 的原理加深了我们对深度学习和概率建模之间相互作用的理解,并引发了许多后续有趣的概率模型和编码方法的开发。这篇论文对于深度学习和生成模型领域产生了重大影响。

2.2 AE存在的问题:隐空间不平滑,无法生成全新图像

非生成式模型:自编码器的主要目标是将输入数据压缩到低维空间(编码),再从该低维空间重构原始数据(解码)。虽然它可以很好地复原数据,但无法生成训练集之外的全新数据,因其编码空间是离散且不连续的。

编码空间(隐空间)不平滑:在AE的隐空间中,相邻的编码点并没有表现出平滑的变化。例如,两个图像的编码点虽然距离很近,但解码器生成的图像可能差异巨大。因此,AE的隐空间缺乏结构化。

过拟合:自编码器往往仅仅记住了训练数据中样本与其编码的对应关系,无法有效地对编码空间中未见过的区域生成合理的输出,这导致AE在生成新样本时的表现不佳。

如下图,在满月与半月中间点的编码空间位置,其输入解码器后,其输出图像可能完全是四不像。

在这里插入图片描述

而变分自编码器(VAE)则旨在解决这一问题,通过使隐空间平滑化,使其成为一种生成式模型。

如下图所示,经过训练的VAE在隐空间中相邻的点输入解码器后,能够生成连续渐变的图像。

在这里插入图片描述

2.3 VAE如何解决上述问题

在自编码器(AE)中,我们让模型学习从图片中提取一些重要特征,把这些特征用几个数字表示。这就像我们把一张复杂的图片简化成一组特征数字,模型通过这组数字尽量还原原始图片。

但是,变分自编码器(VAE)在此基础上有了改进。它不仅要让模型学会提取特征,还要让这些特征不是固定的数字,而是每个特征都有一个范围(分布),这样模型能学会图片的“模糊性”或者“多样性”。

具体来说,在VAE中,模型会为每个特征生成两个值:一个是这个特征的平均值(大概的值),另一个是这个特征的变化范围(标准差)。然后,模型从这个范围里随机挑选一个值,作为这个特征的表示。这就让隐空间中的点更加连续,相似的图片会被编码到隐空间的相邻位置,这样通过这些相邻的特征可以生成相似的图片。

在训练过程中,我们通过两个目标让模型学会这些:

  1. 重构目标:模型生成的图片要尽量和原始图片相似;
  2. 正态分布目标:我们希望模型输出的特征符合某种规律(通常是标准正态分布),这样模型学会的是数据背后的通用规律,而不是仅仅记住图片本身。

通过这两点,模型就能学习到更有用的隐空间表达,并且这些表达是有“弹性”的,能够生成具有多样性但仍然相似的图片。

总结就是:AE学的是固定的特征,VAE学的是带有变化范围的特征,从而让模型不仅能还原图片,还能生成具有变化性的图片。

举例说明

比如说,当我们训练一个用于人脸生成的自编码器(AE)和变分自编码器(VAE)模型时,假设我们将隐空间的维度(即编码器输出的向量长度)设为6,AE的做法是将人脸压缩为6个特征,每个特征对应一个具体的数值(即特征提取的结果)。而在VAE中,模型不仅会将人脸编码为6个特征,还会为每个特征生成一个概率分布,即每个特征不再是一个固定的数值,而是一个带有变化范围的分布。这种分布能够让模型生成具有更多变化的图像,同时保持图像的相似性。如下图:

在这里插入图片描述

2.4 VAE架构图

在这里插入图片描述

(备注:上图有2处地方写错了,我已经校正了)

VAE的本质是让编码器(encoder)学会为输入数据生成一组正态分布的参数(均值 μ \mu μ 和方差 σ 2 \sigma^2 σ2),这些正态分布可以看作是对输入数据在隐空间中的概率表示。这些分布的作用是让相似的输入数据在隐空间中映射到相邻或连续的区域,同时允许通过采样生成具有一定随机性和多样性的隐变量 (Z)。每个隐变量 (Z) 是从这些正态分布中随机采样的结果,之后由解码器(decoder)将其还原为相应的数据(如图像)。

因此,这些正态分布不仅表示了输入数据的潜在特征,还赋予了模型在生成任务中的多样性和连续性,使得模型在生成新样本时能够从隐空间中进行有意义的采样。

2.5 理解架构图中几个点

2.5.1 编码器(Encoder)层的输出是什么?

  1. 输出内容

编码器的输出主要包括两部分:

  • **均值向量 ** μ \mu μ​:表示输入数据在隐空间中的中心点,每个维度对应隐空间中的一个特征。均值向量用于确定数据在隐空间中的位置。
  • 对数方差向量 log ⁡ σ 2 \log\sigma^2 logσ2​:表示输入数据的方差分布,通过取对数的形式保证方差非负。该向量用于表示数据在隐空间中的不确定性,即该输入数据在不同维度上的变动范围。
  1. 输出维度

这两个向量的长度由隐空间的维度决定,假设隐空间的维度是 d d d​,那么输出的 μ \mu μ​ 和 log ⁡ σ 2 \log \sigma^2 logσ2​ 的长度都是 d d d​。

隐空间的维度是模型设定的超参数,通常可以设为 6、10、100 等。例如,假设隐空间的维度为 6,那么编码器输出的会是 6 个均值和 6 个方差。

  1. 输出的意义

编码器输出的均值和方差共同构成了一个正态分布,用于描述输入数据在隐空间中的分布特征。这一分布使得VAE具备生成模型的能力,通过从隐空间中不同位置采样 Z Z Z​,可以生成具有多样性但依然保留输入数据主要特征的新样本。

2.5.2 为什么要让编码器预测 log σ^2,而不是直接预测正太分布中的标准差 σ ?

  1. 标准差必须非负:标准差 σ \sigma σ​ 是用于衡量数据分布广度的度量,表示数据点与均值的平均距离,因此它必须是非负的。如果直接让编码器预测 σ \sigma σ​,我们需要在输出层增加非负约束,例如使用 ReLU 激活函数。然而,这种做法会引入额外的复杂性,并且可能导致数值不稳定。
  2. 预测 ​ log ⁡ σ 2 \log\sigma^2 logσ2​ 的优势:
  • 预测范围更广 log ⁡ σ 2 \log\sigma^2 logσ2​ 的值可以为正或负,因此没有非负的限制,使得模型的输出范围更灵活,优化过程更加自由。
  • 转换简单:虽然预测的是 log ⁡ σ 2 \log\sigma^2 logσ2​,但可以简单的通过指数运算 σ = exp ⁡ ( 1 2 log ⁡ σ 2 ) \sigma=\exp\left(\frac{1}{2}\log\sigma^2\right) σ=exp(21logσ2)​ 轻松将其转换为标准差 σ \sigma σ​,并且这种转换确保了得到的 σ \sigma σ​ 值始终为正值。
  1. 数值稳定性:直接预测 σ \sigma σ​ 可能会在训练过程中导致数值不稳定(例如梯度更新时可能过大或过小)。使用 log ⁡ σ 2 \log\sigma^2 logσ2​ 可以避免这种问题,因为对数操作控制了输出值的范围,使优化过程更加平稳。

总结

让编码器预测 log ⁡ σ 2 \log \sigma^2 logσ2 而不是直接预测 σ \sigma σ,主要是为了保证标准差的非负性,同时使得输出更加灵活、数值稳定性更强,并简化了对标准差的处理。

2.5.3 编码器的输出经过了哪些操作再输入到解码器的?

编码器的输出经过以下几个步骤处理后,才会作为输入传递给解码器:

1. 编码器的输出:

编码器会为输入数据生成两个关键的输出:

  • 均值向量 μ \mu μ:表示输入数据在隐空间中的平均位置。
  • 对数方差向量 log ⁡ σ 2 \log \sigma^2 logσ2:表示每个隐变量在隐空间中的分布宽度(不确定性)。
2. 计算标准差 σ \sigma σ

为了从对数方差 log ⁡ σ 2 \log \sigma^2 logσ2 中得到标准差 σ \sigma σ,我们需要对其进行指数运算:

σ = exp ⁡ ( 1 2 log ⁡ σ 2 ) \sigma = \exp\left(\frac{1}{2} \log \sigma^2\right) σ=exp(21logσ2)

这一步将对数方差转换为标准差,从而确保每个维度的标准差始终为正。

3. 采样过程:重参数化技巧

为了使得VAE的采样过程可微分(以便通过反向传播训练模型),VAE使用了重参数化技巧(Reparameterization Trick)。具体步骤如下:

  • 从标准正态分布 N ( 0 , 1 ) \mathcal{N}(0, 1) N(0,1) 中采样一个随机噪声 ϵ \epsilon ϵ
  • 通过公式计算隐变量 Z Z Z

Z = μ + σ × ϵ Z = \mu + \sigma \times \epsilon Z=μ+σ×ϵ

其中, ϵ \epsilon ϵ 是一个从标准正态分布中采样的噪声。这样,通过 μ \mu μ σ \sigma σ 来控制生成的 Z Z Z,确保每次采样都带有一定的随机性。

4. 将隐变量 z​ 输入解码器

最终计算得到的隐变量 Z Z Z 是一个经过随机采样后的隐空间表示,它结合了输入数据的均值 μ \mu μ 和不确定性 σ \sigma σ。这个隐变量 Z Z Z 会作为解码器的输入,通过解码器生成与原始数据类似的输出(如图像、音频等)。

2.5.4 为什么需要噪声ϵ?

噪声 ϵ \epsilon ϵ 在VAE中是非常关键的一部分,原因如下:

1. 引入随机性,增加生成样本的多样性
  • VAE的目标是从隐空间中采样生成与原始输入相似的样本。通过添加噪声 ϵ \epsilon ϵ,我们确保了每次从正态分布中采样的隐变量 Z Z Z 都会带有一定的随机性。
  • 这种随机性能够使模型生成具有一定变化的样本,而不是每次都生成完全相同的输出。通过 ϵ \epsilon ϵ,我们可以在相同的均值 μ \mu μ 和标准差 σ \sigma σ 上生成不同的隐变量 Z Z Z,从而提高生成样本的多样性。
2. 使模型可微分,便于反向传播
  • 在普通的自编码器中,编码器直接输出隐空间的点。但是在VAE中,由于我们需要从隐空间中的正态分布采样,而采样本身是一个非确定性的过程,不能直接用于反向传播。
  • 为了解决这个问题,VAE采用了重参数化技巧,即将采样过程重写为 Z = μ + σ × ϵ Z = \mu + \sigma \times \epsilon Z=μ+σ×ϵ,其中 ϵ \epsilon ϵ 是从标准正态分布 N ( 0 , 1 ) \mathcal{N}(0, 1) N(0,1) 中采样的噪声。这样,采样的随机性被隔离到了 ϵ \epsilon ϵ,而 μ \mu μ σ \sigma σ 是可微的,可以通过反向传播进行优化。这个技巧确保了VAE在训练过程中可以正常计算梯度并更新参数。
3. 实现隐空间的连续性
  • 噪声 ϵ \epsilon ϵ 帮助确保隐空间的连续性,即相似的输入样本在隐空间中会被映射到相邻的区域。
  • 通过在正态分布上引入噪声,模型可以在隐空间中生成连续的表示。这样,模型不仅能够对训练样本进行重构,还可以通过在隐空间中进行插值生成新的样本。例如,在人脸生成模型中,隐变量 Z Z Z 可以表示不同的人脸特征,噪声确保了这些特征的平滑过渡。
4. 避免过拟合
  • 在没有噪声的情况下,模型可能会记住输入数据,导致过拟合。而通过引入噪声,模型在训练过程中学习的是数据的分布特征,而不仅仅是记住每个样本本身。
  • 这使得模型能够泛化到未见过的样本上,从而提高了模型的生成能力。
总结:

噪声 ϵ \epsilon ϵ 的作用是确保VAE中的随机性和多样性,同时通过重参数化技巧保持可微分性。它帮助模型生成更为丰富和多样的样本,并确保模型在训练过程中能够进行正常的梯度更新,保持隐空间的连续性和生成过程的泛化能力。

2.5.5 VAE的解码器是把Z复原成跟输入一模一样的图像,还是复原成类似的图像?

解码器在变分自编码器(VAE)中的目标是将隐变量 Z Z Z 还原成与输入类似的图像,而不是完全一模一样的图像。其背后的原因和设计初衷有以下几个方面:

1. 目标是生成相似的图像,而不是一模一样的图像
  • 变分自编码器的核心思想是学习输入数据的概率分布,并通过隐空间中的变量 Z Z Z 来生成具有相似特征的样本。因此,解码器通过隐变量 Z Z Z 重构的图像应该与输入图像相似,但由于引入了随机性(通过噪声 ϵ \epsilon ϵ 的采样),生成的图像不一定与输入图像完全一模一样。
  • 这一设计的目的是为了让模型具备生成能力,而不仅仅是重构能力。解码器可以通过隐空间中的不同点生成带有变化的图像,从而具备生成多样化数据的能力。
2. 引入随机性增加了多样性
  • 编码器通过预测均值 μ \mu μ 和方差 σ \sigma σ,生成隐变量 Z Z Z,其中引入了从正态分布中采样的噪声 ϵ \epsilon ϵ。这使得每次生成的 Z Z Z 都带有一定的随机性。因此,即使对相同的输入数据,生成的图像可能会有细微差异。
  • 这种随机性允许模型生成多种不同但相似的图像,而不是仅仅将输入图像逐字逐句地“记住”并一模一样地重现。
3. 隐空间的连续性
  • VAE的隐空间是连续的,意味着在隐空间中相邻的点生成的图像会有连续的变化。因此,解码器通过 Z Z Z 来重构图像时,生成的结果会和输入图像有相似的特征,但由于隐空间中的采样过程,生成图像是相似但不完全相同的。

2.6 VAE训练的原理和损失函数

2.6.1 背景知识:KL散度

KL散度(Kullback-Leibler Divergence),简称 KL散度,是用来衡量两个概率分布之间差异的一种方法。它常用于机器学习中,用来比较模型预测的概率分布和真实概率分布之间的差异。在变分自编码器(VAE)中,KL散度用于度量编码器生成的隐空间分布与标准正态分布之间的差异。

1. KL散度是什么?

KL散度是一种非对称的测量方式,它告诉我们一个分布 q ( z ∣ x ) q(z|x) q(zx) 相对于另一个参考分布 p ( z ) p(z) p(z) 有多么不同。它可以理解为:“如果我们使用分布 q q q 来近似分布 p p p,会损失多少信息?”

KL散度的公式如下:

D KL ( q ( z ∣ x ) ∣ ∣ p ( z ) ) = ∑ z q ( z ∣ x ) log ⁡ q ( z ∣ x ) p ( z ) D_{\text{KL}}(q(z|x) || p(z)) = \sum_{z} q(z|x) \log \frac{q(z|x)}{p(z)} DKL(q(zx)∣∣p(z))=zq(zx)logp(z)q(zx)

2. KL散度的直观理解

KL散度可以理解为“信息损失”的度量,表示了如果我们用分布 q ( z ∣ x ) q(z|x) q(zx) 来替代 p ( z ) p(z) p(z),我们会错过多少信息。

  • q ( z ∣ x ) q(z|x) q(zx) p ( z ) p(z) p(z) 完全相同(即两个分布一致时),KL散度为 0,表示没有任何信息损失。
  • q ( z ∣ x ) q(z|x) q(zx) p ( z ) p(z) p(z) 越不相同时,KL散度越大,表示信息损失越多,也就是说这两个分布的差异越大。

举例子:

  • 假设你认为今天会下雨的概率是 0.9,而实际的天气预报给出的下雨概率是 0.2。如果你按照你的预测去准备雨具,那么你实际上是错误地评估了这个概率,犯了一个错误。KL散度在这里就是衡量你预测的分布(即 0.9 下雨)和实际的天气分布(0.2 下雨)之间的差异。
  • 例子:抛硬币

假设我们有一枚不均匀的硬币:

  • 真实分布 ( P ):正面朝上的概率为 0.9,反面为 0.1。
  • 近似分布 ( Q ):我们误以为硬币是公平的,即正反面概率都是 0.5。

计算KL散度

D K L ( P ∥ Q ) = 0.9 log ⁡ ( 0.9 0.5 ) + 0.1 log ⁡ ( 0.1 0.5 ) D_{KL}(P \| Q) = 0.9 \log\left(\frac{0.9}{0.5}\right) + 0.1 \log\left(\frac{0.1}{0.5}\right) DKL(PQ)=0.9log(0.50.9)+0.1log(0.50.1)

计算得:

D K L ( P ∥ Q ) = 0.9 × 0.847 + 0.1 × ( − 1.609 ) = 0.762 − 0.161 = 0.601 D_{KL}(P \| Q) = 0.9 \times 0.847 + 0.1 \times (-1.609) = 0.762 - 0.161 = 0.601 DKL(PQ)=0.9×0.847+0.1×(1.609)=0.7620.161=0.601

这表示,如果我们用 ( Q ) 来近似 ( P ),每次抛硬币平均会损失约 0.601 个 nats 的信息(以自然对数为底)。

3. KL散度的特点
  • 非对称性:KL散度不是对称的,也就是说 D KL ( q ∣ ∣ p ) ≠ D KL ( p ∣ ∣ q ) D_{\text{KL}}(q || p) \neq D_{\text{KL}}(p || q) DKL(q∣∣p)=DKL(p∣∣q)。因此,它不是一个真正的“距离”度量,只能告诉我们一个分布相对于另一个分布的差异有多大。
  • 非负性:KL散度总是大于或等于0,也就是说:

D KL ( q ∣ ∣ p ) ≥ 0 D_{\text{KL}}(q || p) \geq 0 DKL(q∣∣p)0

当且仅当两个分布完全相同时,KL散度才为 0。

总结:

KL散度衡量的是两个概率分布之间的差异。在VAE中,KL散度确保编码器生成的隐变量分布尽可能接近标准正态分布,帮助模型在隐空间中生成结构化、连续性的表示,同时保证生成的样本具有多样性和泛化能力。

2.6.2 VAE的训练过程与损失函数

变分自编码器(VAE)的训练过程与传统自编码器类似,但增加了一些关键的步骤以确保模型能够学习到隐空间的分布特征。VAE 的训练目标是最小化重构误差和隐空间分布与标准正态分布之间的差异。具体来说,VAE 的训练依赖于两个关键的损失函数:重构损失KL 散度损失

1. VAE 的训练步骤
  • 编码:输入数据通过编码器,被编码为均值向量 μ \mu μ 和对数方差向量 log ⁡ σ 2 \log \sigma^2 logσ2
  • 重参数化技巧:通过重参数化技巧,使用均值 μ \mu μ 和标准差 σ \sigma σ,以及从标准正态分布中采样的噪声 ϵ \epsilon ϵ 生成隐变量 Z = μ + σ × ϵ Z = \mu + \sigma \times \epsilon Z=μ+σ×ϵ
  • 解码:隐变量 Z Z Z 传入解码器,解码器尝试将其重构为与输入数据相似的输出。
2. 损失函数的组成

VAE 的损失函数由两个部分组成:

2.1 重构损失(Reconstruction Loss)

  • 这部分损失衡量模型生成的图像与输入图像的相似程度。通常使用像素级别的差异来衡量,例如使用均方误差(MSE)或二元交叉熵(BCE)作为损失函数。
  • 如果输入图像为 x x x,解码器生成的图像为 x ^ \hat{x} x^,那么重构损失为:

Reconstruction Loss = E q ( z ∣ x ) [ log ⁡ p ( x ∣ z ) ] \text{Reconstruction Loss} = \mathbb{E}_{q(z|x)}[\log p(x|z)] Reconstruction Loss=Eq(zx)[logp(xz)]

  • 直观上,这部分损失的目标是让解码器生成的图像尽可能接近输入图像,确保解码器能够有效地还原信息。

2.2 KL 散度损失(KL Divergence Loss)

  • 在 VAE 中,我们希望编码器输出的分布接近标准正态分布,所以用KL散度进行约束。计算公式如下(推导过程见下附录):

KL Loss = D KL ( q ( z ∣ x ) ∣ ∣ p ( z ) ) = 1 2 ∑ i = 1 d ( μ i 2 + σ i 2 − log ⁡ σ i 2 − 1 ) \text{KL Loss} = D_{\text{KL}}(q(z|x) || p(z)) = \frac{1}{2} \sum_{i=1}^{d} \left( \mu_i^2 + \sigma_i^2 - \log \sigma_i^2 - 1 \right) KL Loss=DKL(q(zx)∣∣p(z))=21i=1d(μi2+σi2logσi21)

3. 总损失函数

VAE 的总损失函数是重构损失和 KL 散度损失的加和:

VAE Loss = Reconstruction Loss + KL Loss \text{VAE Loss} = \text{Reconstruction Loss} + \text{KL Loss} VAE Loss=Reconstruction Loss+KL Loss

  • 重构损失 确保生成的样本与输入相似。
  • KL 散度损失 确保编码器生成的隐空间分布与标准正态分布接近。
4. 反向传播与优化
  • VAE 使用反向传播和梯度下降来最小化上述损失函数。通过调整编码器和解码器的参数,模型逐步学会如何有效编码输入数据,同时保持隐空间中的连续性和可解释性。
总结:

VAE 的训练过程通过最小化两部分损失函数来进行:重构损失 用于确保生成图像与输入图像相似,KL 散度损失 用于让隐空间的分布接近标准正态分布。通过这两个目标的结合,VAE 能够学会如何生成多样化但相似的图像,同时保持生成空间的结构化。

2.6.3 备注:VAE中KL loss公式的推导过程

1. KL散度定义

在VAE中,KL散度衡量编码器输出的隐变量分布 q ( z ∣ x ) q(z|x) q(zx) 与先验分布 p ( z ) p(z) p(z) 之间的差异。KL散度的定义公式为:

D KL ( q ( z ∣ x ) ∣ ∣ p ( z ) ) = ∫ q ( z ∣ x ) log ⁡ q ( z ∣ x ) p ( z ) d z D_{\text{KL}}(q(z|x) || p(z)) = \int q(z|x) \log \frac{q(z|x)}{p(z)} dz DKL(q(zx)∣∣p(z))=q(zx)logp(z)q(zx)dz

在VAE中:

  • q ( z ∣ x ) q(z|x) q(zx) 是编码器输出的后验分布,通常假设为一个高斯分布 N ( μ , σ 2 ) \mathcal{N}(\mu, \sigma^2) N(μ,σ2)
  • p ( z ) p(z) p(z) 是标准正态分布 N ( 0 , 1 ) \mathcal{N}(0, 1) N(0,1)

我们需要计算这两个高斯分布之间的KL散度。

2 高斯分布(也就是正态分布)的概率密度函数

首先,我们写出高斯分布的概率密度函数:

  • 对于 q ( z ∣ x ) = N ( μ , σ 2 ) q(z|x) = \mathcal{N}(\mu, \sigma^2) q(zx)=N(μ,σ2),概率密度函数为:

q ( z ∣ x ) = 1 2 π σ 2 exp ⁡ ( − ( z − μ ) 2 2 σ 2 ) q(z|x) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(z - \mu)^2}{2\sigma^2}\right) q(zx)=2πσ2 1exp(2σ2(zμ)2)

  • 对于 p ( z ) = N ( 0 , 1 ) p(z) = \mathcal{N}(0, 1) p(z)=N(0,1),概率密度函数为:

p ( z ) = 1 2 π exp ⁡ ( − z 2 2 ) p(z) = \frac{1}{\sqrt{2\pi}} \exp\left(-\frac{z^2}{2}\right) p(z)=2π 1exp(2z2)

3 将高斯分布代入KL散度公式

将高斯分布的概率密度函数代入KL散度的定义公式:

D KL ( q ( z ∣ x ) ∣ ∣ p ( z ) ) = ∫ 1 2 π σ 2 exp ⁡ ( − ( z − μ ) 2 2 σ 2 ) log ⁡ 1 2 π σ 2 exp ⁡ ( − ( z − μ ) 2 2 σ 2 ) 1 2 π exp ⁡ ( − z 2 2 ) d z D_{\text{KL}}(q(z|x) || p(z)) = \int \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(z - \mu)^2}{2\sigma^2}\right) \log \frac{\frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(z - \mu)^2}{2\sigma^2}\right)}{\frac{1}{\sqrt{2\pi}} \exp\left(-\frac{z^2}{2}\right)} dz DKL(q(zx)∣∣p(z))=2πσ2 1exp(2σ2(zμ)2)log2π 1exp(2z2)2πσ2 1exp(2σ2(zμ)2)dz

4 对数部分化简

对数部分可以分为两部分:

log ⁡ 1 2 π σ 2 exp ⁡ ( − ( z − μ ) 2 2 σ 2 ) 1 2 π exp ⁡ ( − z 2 2 ) = log ⁡ 1 σ 2 − ( z − μ ) 2 2 σ 2 + z 2 2 \log \frac{\frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(z - \mu)^2}{2\sigma^2}\right)}{\frac{1}{\sqrt{2\pi}} \exp\left(-\frac{z^2}{2}\right)} = \log \frac{1}{\sqrt{\sigma^2}} - \frac{(z - \mu)^2}{2\sigma^2} + \frac{z^2}{2} log2π 1exp(2z2)2πσ2 1exp(2σ2(zμ)2)=logσ2 12σ2(zμ)2+2z2

5 化简KL散度公式

代入上面的化简结果后,KL散度公式变为:

D KL ( q ( z ∣ x ) ∣ ∣ p ( z ) ) = ∫ q ( z ∣ x ) ( log ⁡ 1 σ 2 − ( z − μ ) 2 2 σ 2 + z 2 2 ) d z D_{\text{KL}}(q(z|x) || p(z)) = \int q(z|x) \left( \log \frac{1}{\sqrt{\sigma^2}} - \frac{(z - \mu)^2}{2\sigma^2} + \frac{z^2}{2} \right) dz DKL(q(zx)∣∣p(z))=q(zx)(logσ2 12σ2(zμ)2+2z2)dz

这个公式可以进一步拆解为三项:

  1. 第一项

∫ q ( z ∣ x ) log ⁡ 1 σ 2 d z = − 1 2 log ⁡ σ 2 \int q(z|x) \log \frac{1}{\sqrt{\sigma^2}} dz = -\frac{1}{2} \log \sigma^2 q(zx)logσ2 1dz=21logσ2

  1. 第二项

∫ q ( z ∣ x ) ( − ( z − μ ) 2 2 σ 2 ) d z \int q(z|x) \left(- \frac{(z - \mu)^2}{2\sigma^2} \right) dz q(zx)(2σ2(zμ)2)dz

由于这是高斯分布的期望,已知 E q ( z ∣ x ) [ ( z − μ ) 2 ] = σ 2 \mathbb{E}_{q(z|x)}[(z - \mu)^2] = \sigma^2 Eq(zx)[(zμ)2]=σ2,所以这一项化简为:

− 1 2 - \frac{1}{2} 21

  1. 第三项

∫ q ( z ∣ x ) z 2 2 d z \int q(z|x) \frac{z^2}{2} dz q(zx)2z2dz

这可以拆解为:

1 2 E q ( z ∣ x ) [ z 2 ] = 1 2 ( μ 2 + σ 2 ) \frac{1}{2} \mathbb{E}_{q(z|x)}[z^2] = \frac{1}{2} (\mu^2 + \sigma^2) 21Eq(zx)[z2]=21(μ2+σ2)

6 整合所有项

将所有项整合后,KL散度的最终结果为:

D KL ( N ( μ , σ 2 ) ∣ ∣ N ( 0 , 1 ) ) = 1 2 ( μ 2 + σ 2 − log ⁡ σ 2 − 1 ) D_{\text{KL}}(\mathcal{N}(\mu, \sigma^2) || \mathcal{N}(0, 1)) = \frac{1}{2} \left( \mu^2 + \sigma^2 - \log \sigma^2 - 1 \right) DKL(N(μ,σ2)∣∣N(0,1))=21(μ2+σ2logσ21)

2.7 VAE的全套代码(源自CMU-11785,VAE手写数字生成)

【variational_autoencoder.ipynb】

2.7.1 ipynb代码文件流程概述和结果截图

  1. 引入与准备工作
  • 安装与导入PyTorch及相关库:确保环境准备好,包括必要的库和依赖,尤其在Google Colab等环境下需要安装特定的依赖。
  • 导入数据集(MNIST):加载手写数字数据集MNIST,并对其进行预处理,为模型训练做好准备。
  1. 参数设置
  • 设置VAE模型所需的超参数,例如隐空间维度、学习率等。这些参数控制模型的训练行为和最终的生成效果。
  1. VAE模型定义
  • 编码器(Encoder):将输入图像编码为隐空间表示(即潜在向量),并生成均值和方差向量,作为隐变量的分布。
  • 解码器(Decoder):从隐空间表示还原出原始图像,完成数据的重构。
  • VAE类整合:将编码器和解码器封装为一个整体VAE模型,并定义其前向传播过程。
  1. 训练模型
  • 使用MNIST数据集训练VAE模型,优化器设置为Adam,并且通过重构损失和KL散度损失来指导模型学习隐空间的分布和生成能力。
  1. 绘制训练曲线
  • 使用Matplotlib展示训练过程中损失函数的变化,帮助可视化模型的收敛情况。
    -在这里插入图片描述
  1. 加载预训练模型
  • 提供了加载预训练模型的选项,避免从头开始训练模型。如果有预训练的VAE模型,可以直接加载并进行推理或测试。
  1. 测试模型与评估
  • 在测试集上评估模型的表现,切换到评估模式,并对模型生成的图像与真实图像的相似性进行对比。

  • 重建图像的可视化

    • 可视化模型在测试集上的重建效果,即对给定的手写数字输入,模型如何在隐空间中找到对应的表示,并生成相似的图像。
    • 在这里插入图片描述
  1. 隐空间插值
  • 演示在隐空间中对两个潜在向量进行线性插值,生成从一个图像逐渐过渡到另一个图像的效果。
  • 在这里插入图片描述
  1. 从先验分布中采样
  • 演示如何从标准正态分布中随机采样潜在向量,经过VAE解码器生成新图像。这展示了VAE作为生成模型的能力。
  • 在这里插入图片描述
  1. 2D隐空间可视化
  • 如果VAE的隐空间维度为2,可以将隐空间中的样本可视化,并展示潜在变量与重建图像之间的对应关系。
  • 在这里插入图片描述

2.7.2 VAE模型架构和损失函数代码

class Encoder(nn.Module):
    def __init__(self):
        super(Encoder, self).__init__()
        c = capacity  # 基础的通道数量,模型的容量参数
        # 第一个卷积层,将输入图像从1通道转换为c通道,输出尺寸为(c, 14, 14)
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=c, kernel_size=4, stride=2, padding=1)  # out: c x 14 x 14
        # 第二个卷积层,将特征从c通道转换为2c通道,输出尺寸为(c*2, 7, 7)
        self.conv2 = nn.Conv2d(in_channels=c, out_channels=c*2, kernel_size=4, stride=2, padding=1)  # out: c*2 x 7 x 7
        # 全连接层,用于生成潜在空间的均值向量
        self.fc_mu = nn.Linear(in_features=c*2*7*7, out_features=latent_dims)
        # 全连接层,用于生成潜在空间的对数方差向量
        self.fc_logvar = nn.Linear(in_features=c*2*7*7, out_features=latent_dims)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = x.view(x.size(0), -1)  # 展平多通道特征图
        x_mu = self.fc_mu(x)
        x_logvar = self.fc_logvar(x)
        return x_mu, x_logvar # 返回均值和对数方差


class Decoder(nn.Module):
    def __init__(self):
        super(Decoder, self).__init__()
        c = capacity  # 基础的通道数量
        self.fc = nn.Linear(in_features=latent_dims, out_features=c*2*7*7)  # 全连接层,将潜在向量映射回特征图的尺寸
self.conv2 = nn.ConvTranspose2d(in_channels=c*2, out_channels=c, kernel_size=4, stride=2, padding=1) # 反卷积层,将特征从2c通道转换为c通道
        self.conv1 = nn.ConvTranspose2d(in_channels=c, out_channels=1, kernel_size=4, stride=2, padding=1)  # 反卷积层,将特征从c通道转换回1通道(即恢复到图像的大小)

    def forward(self, x):
        x = self.fc(x)  # 将全连接层的输出调整为合适的卷积输入形状
        x = x.view(x.size(0), capacity*2, 7, 7)  # 展平为批量多通道特征图
        x = F.relu(self.conv2(x))  # 反卷积层并应用ReLU激活函数
        x = torch.sigmoid(self.conv1(x))  # 最后一层使用Sigmoid激活(因为重构损失使用的是二元交叉熵)
        return x


class VariationalAutoencoder(nn.Module):
    def __init__(self):
        super(VariationalAutoencoder, self).__init__()
        self.encoder = Encoder()  # 初始化编码器
        self.decoder = Decoder()  # 初始化解码器

    def forward(self, x):
        # 使用编码器生成潜在空间的均值和对数方差
        latent_mu, latent_logvar = self.encoder(x)
        # 通过重参数化技巧采样潜在变量
        latent = self.latent_sample(latent_mu, latent_logvar)
        # 解码器将潜在变量解码为重构的图像
        x_recon = self.decoder(latent)
        return x_recon, latent_mu, latent_logvar

    def latent_sample(self, mu, logvar):
        if self.training:
            # 使用重参数化技巧
            std = logvar.mul(0.5).exp_()  # 计算标准差
            eps = torch.empty_like(std).normal_()  # 从标准正态分布中采样噪声
            return eps.mul(std).add_(mu)  # 返回重新参数化的潜在向量
        else:
            return mu  # 推理时直接返回均值作为潜在变量

def vae_loss(recon_x, x, mu, logvar):
    # 重构损失:计算像素级的二元交叉熵,用于衡量重构图像与原始图像的相似性
    recon_loss = F.binary_cross_entropy(recon_x.view(-1, 784), x.view(-1, 784), reduction='sum')

    # KL散度:衡量编码器生成的潜在分布与标准正态分布之间的差异
    kldivergence = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())

    # 返回总损失:重构损失 + KL散度损失
    return recon_loss + variational_beta * kldivergence  # variational_beta 是一个可调的超参数
 

损失函数的输入参数备注:

  • recon_x:解码器生成的重构图像,用于计算重构损失。
  • x:原始输入图像,用于与重构图像比较,计算像素级的二元交叉熵(BCE)损失。
  • mu:潜在空间的均值,用于计算KL散度,衡量潜在空间分布与标准正态分布之间的差异。
  • logvar:潜在空间的对数方差,用于计算KL散度,控制潜在空间的不确定性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2144291.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ad 明明已经给原理图库添加封装了,为什么还是看不到

不得不说,ad可能真的是比较庞大也不比较冗余,使用的过程中就会出现各种各样的问题,明明所有的操作都没问题,可是就是没能得到自己想要的,额 真的一言难尽 软件本身也很庞大了 各种东西可能一下子反应不过来&#xf…

谁的大一不迷茫?网络安全到底该怎么入门?

大一的新生,都在向我询问类似的问题: 网络安全要学的东西好杂,找不到方向了,迷茫了 同学们都在研究xxx,我要去学吗? 精力有限,我到底该学什么编程语言,好怕选错啊 最近下班比较早…

指针 (六)

OK,书接上回,咱们继续: 一 . 函数指针变量 (1)函数指针变量的创建 首先我们得明白,什么是函数指针变量呢?从我们之前学习过的整型指针,数组指针的相关知识当中,通过类…

【软件测试】什么叫测试?

生活中的测试 测试在生活中是处处可见的 例如:在商场购买衣服 外观测试:挑选符合个人审美的衣服试穿测试:选择尺码,试穿材质测试:考虑材质相关问题价格测试:价格是否符合预期 例如:在购物软件购…

【逐行注释】自适应观测协方差R的AUKF(自适应无迹卡尔曼滤波,MATLAB语言编写),附下载链接

文章目录 自适应R的UKF逐行注释的说明运行结果部分代码各模块解释 自适应R的UKF 自适应无迹卡尔曼滤波(Adaptive Unscented Kalman Filter,AUKF)是一种用于状态估计的滤波算法。它是基于无迹卡尔曼滤波(Unscented Kalman Filter&…

简单题58-最后一个单词的长度(Java and Python)20240918

问题的描述: python代码: class Solution(object):def lengthOfLastWord(self, s):""":type s: str:rtype: int"""# 去掉字符串前后的空格s s.strip()# 分割字符串,获取单词列表words s.split(" "…

9.1 溪降技术:游泳

目录 9.1 游泳概述观看视频课程电子书:游泳防御性游泳姿势**身体姿势** 积极游泳姿势**身体姿势** 总结 9.1 游泳 概述 深潭游泳 对于峡谷探险者来说,游泳是一项核心技能。我们的游泳水平和自信心将在很大程度上决定我们的路线选择。在这一阶段&#xff…

开放式耳机哪个好用?开放式耳机好还是入耳式耳机好?

开放式耳机这种新型的耳机类型最近几年,真的受到了越来越多人的欢迎了,所以很多人来问我如何才能选到一款合适自己的开放式耳机,毕竟一款合适自己的耳机才能够用的长久,作为一名合格的耳机测评师,在这里当然要为大家答…

Ollama:本地运行大模型【含UI界面】

文章目录 Ollama 简介安装 ollamaWindows 安装Docker 安装其它平台安装支持的模型模型清单模型参数与运行内存快速启动 llama 模型llama 模型介绍运行 llama3.1 模型通过 HTTP API 访问ollama 命令语法常用示例特别示例自定义模型创建 Modelfile创建模型并运行集成 Web 页面Ope…

Linux系统中的内建命令

当使用export命令设置环境变量时,既然export也是一个命令,那么一定也需要创建子进程,完成设置环境变量的任务。但是父子进程的数据是各自独立的(子进程会继承父进程的数据,但是),理论上来说父子…

客户说了算!精益产品开发,让中小企业精准触达用户需求!——张驰咨询

随着全球经济环境的波动,特别是疫情后经济复苏进程的反复,很多中小制造企业面临产品滞销、同质化严重和内卷竞争加剧的困境。市场饱和、利润微薄,还在新产品开发上遇到了研发人才不足、资金短缺等问题。许多企业在这场市场博弈中徘徊在生死边…

socket地址API(Linux网络编程基础API)

socket 目标进程的标识(套接字)由两部分组成: IP地址:标识出计算机所在的网络位置。端口号:标识出计算机上特定的进程。 字节序 字节序定义了多字节数据在内存中的存储顺序,是处理器架构的特性。字节序…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于模型-数据混合驱动的区域能源互联网韧性在线评估》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

不管你是网工小白还是大佬,一定要焊死在电脑上的6款工具!

号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部 你们好,我的网工朋友。 咱网工日常工作可谓状况多多,从配置复杂的网络设备到排除棘手的连接故障,再到维护网络…

GaussDB关键技术原理:高弹性(五)

书接上文GaussDB关键技术原理:高弹性(四)从扩容流程框架方面对hashbucket扩容技术进行了解读,本篇将从日志多流和事务相关方面继续介绍GaussDB高弹性技术。 目录 4.2 日志多流 4.2.1 日志多流总体流程 4.2.2 基线数据传输 4.…

统信服务器操作系统【d版系统上Ansible工具】配置方法

在统信服务器操作系统d版本上进行安装Ansible工具的方法以及配置方法。 文章目录 准备环境功能描述安装与配置一、 安装ansible工具1. 直接 命令安装2. 验证安装结果3. 配置ssh免密登录二、Ansible使用1. 主机清单配置2. ansible命令3. ansible模块三、 playbook配置文件1. 执行…

如何简化机器人模型,加速仿真计算与可视化

通常,我们希望将自己设计的机器人模型导入仿真环境。由于是通过 CAD 软件设计的,导出的 urdf 使用 STL 或 DAE 文件来表示 3D 几何。但原始的 STL 或 DAE 文件通常过于复杂(由数十万个三角面片组成),这会减慢仿真速度,有时也会导致仿真软件报错(如Webots)。为了在正确描述…

如何学懂C++语言:C++从入门到精通的全面指南(完整C++学习笔记)

数字人助手猿小美带你一起学编程 一、引言 作为一名拥有多年开发经验的技术人员,我的职业生涯涵盖了多种编程语言,包括 C 语言、C、C# 和 JavaScript 等。在我多年的编程生涯中,这些语言不仅丰富了我的知识储备,还极大地拓…

10个A/B测试指标以分析结果和衡量成功

为什么A/B测试分析很重要? 到现在,您可能已经熟悉了A/B测试的基本概念及其用途。 简而言之,A/B测试是将两个或多个着陆页、营销资产或单个元素(如标题)进行对比,以帮助您找出哪个表现最好。 这些测试帮助…

MUNIK谈ASPICE系列专题分享(五)如何选一家ASPICE评估服务认证公司

引言: 如何选一家适合企业自身的ASPICE评估公司,哪些点应该是企业关注的。 MUNIK公司作为一家全球认可的ASPICE服务公司,我们的专家由德国和印度,以及本地化的ASPICE资质评估老师构成。基于MUNIK丰富的项目经验,我们…