18、用于神经压缩的深度生成建模

用于神经压缩的深度生成建模

1. 深度生成建模在神经压缩中的重要性

在神经压缩领域,基于神经网络的编解码器通常会与量化和熵编码一起进行训练。那么,为什么深度生成建模在神经压缩中如此重要呢?Claude Shannon 早就给出了答案,他指出消息长度与数据熵成正比。但我们往往不知道数据的熵,因为不清楚数据的概率分布 p(x),不过可以利用之前讨论过的深度生成模型来进行估计。

近年来,利用深度生成建模来改进神经压缩的兴趣日益浓厚。我们既可以用深度生成模型为熵编码器建模概率分布,也能通过引入新的推理和重建方案,显著提高最终的重建和压缩质量。

2. 通用压缩方案

2.1 图像压缩方法分类

在深入探讨神经压缩之前,有必要回顾一下图像(或一般数据)压缩的基本概念。图像压缩方法主要分为两类:
- 无损压缩 :能保留所有信息,重建结果无误差。
- 有损压缩 :压缩过程中会丢失部分信息。

2.2 压缩算法设计原则

设计压缩算法的通用方法是设计一种唯一可解码的代码,其期望长度尽可能接近数据的熵。通用压缩系统由编码器和解码器两个组件组成。需要注意的是,它和确定性变分自编码器(VAE)不同,虽然有相似之处,但在压缩任务中,我们更关注发送比特流,而 VAE 通常不关心这一点。

2.3 编码器

编码器的目标是将图像转换为离散信号,该信号不一定是二进制的。所使用的变换可以是可逆的,但并非必需。若变换可逆,可在解码器中使用其逆变换,理论上可实现无损压缩;若不可逆,则会丢失部分信息,属于有损压缩方法。对输入图像应用变换后,离散信号会以无损方式编码为比特流,即把离散符号映射为二进制变量(比特)。通常,熵编码器会利用符号出现概率的信息,如霍夫曼编码器或算术编码器,而很多熵编码器需要知道 p(x),这时就可以使用深度生成模型。

2.4 解码器

消息(即比特)发送和接收后,熵解码器会将比特流解码为离散信号,它是熵编码器的逆过程。熵编码方法能让我们从比特流中恢复原始符号。最后,应用逆变换(不一定是编码器变换的逆)来重建原始图像。

2.5 完整方案

压缩系统(编解码器)的通用方案包含多尺度图像分解(如小波表示),并进一步进行量化。特定的离散变换(如离散余弦变换 DCT)会产生特定的编解码器(如 JPEG)。

2.6 性能评估

编解码器的最终性能通过重建误差和压缩比来评估。重建误差称为失真度量,通常使用均方误差(MSE)或感知指标(如多尺度结构相似性指数 MS - SSIM)计算;压缩比称为速率,通常用每像素比特数(bpp)表示,即编码器输出的总比特大小除以编码器输入的总像素大小。通常,通过检查速率 - 失真平面(在平面上绘制速率为 x 轴、失真为 y 轴的曲线)来比较编解码器的性能。

形式上,假设采用自编码器架构,编码变换 fe : X → Y 将输入 x 转换为离散信号 y(代码),解码器 fd : Y → X 给出重建结果 ˆx。此外,还有一个自适应熵编码模型,用于学习分布 p(y),并通过熵编码器将离散信号 y 转换为比特流。若压缩方法有自适应(超)参数,可通过优化以下目标函数来学习:
[L(x) = d(x, \hat{x}) + \beta r(y)]
其中,d(·, ·) 是失真度量(如 PSNR、MS - SSIM),r(·) 是速率度量(如 r(y) = -ln p(y)),β > 0 是控制速率和失真平衡的加权因子。需要注意的是,失真度量需要编码器和解码器,速率度量需要编码器和熵模型。

3. JPEG 压缩

JPEG 是最常用的编解码器之一。在 JPEG 编解码器中,RGB 图像首先线性转换为 YCbCr 格式:
[\begin{bmatrix}Y\Cb\Cr\end{bmatrix}=\begin{bmatrix}0\128\128\end{bmatrix}+\begin{bmatrix}0.299&0.587&0.114\ - 0.168736&- 0.331264&0.5\0.5&- 0.48688&- 0.081312\end{bmatrix}\begin{bmatrix}R\G\B\end{bmatrix}]
然后,Cb 和 Cr 通道通常会进行 2 到 3 次下采样(第一次压缩阶段)。接着,每个通道被分割成 8×8 块,并进行离散余弦变换(DCT),最后进行量化(第二次压缩阶段)。最后可使用霍夫曼编码。解码时,使用逆 DCT,对 Cb 和 Cr 通道进行上采样,并恢复 RGB 表示。整个过程步骤清晰,超参数也有明确的解释。

4. 神经压缩组件

4.1 编码器和解码器

在神经压缩中,编码器和解码器由神经网络组成,无额外功能。我们更关注架构,而非如何参数化分布。编码器的输出是连续代码(浮点数),解码器的输出是图像的重建结果。以下是使用 PyTorch 实现的编码器和解码器类示例:

import torch
import torch.nn as nn

# The encoder is simply a neural network that takes an image and outputs a corresponding code.
class Encoder(nn.Module):
    def __init__(self, encoder_net):
        super(Encoder, self).__init__()
        self.encoder = encoder_net

    def encode(self, x):
        h_e = self.encoder(x)
        return h_e

    def forward(self, x):
        return self.encode(x)

# The decoder is simply a neural network that takes a quantized code and returns an image.
class Decoder(nn.Module):
    def __init__(self, decoder_net):
        super(Decoder, self).__init__()
        self.decoder = decoder_net

    def decode(self, z):
        h_d = self.decoder(z)
        return h_d

    def forward(self, z, x=None):
        x_rec = self.decode(z)
        return x_rec

# ENCODER
D = 10  # Example input dimension
M = 20  # Example hidden dimension
C = 5   # Example output dimension
e_net = nn.Sequential(
    nn.Linear(D, M*2), nn.BatchNorm1d(M*2), nn.ReLU(),
    nn.Linear(M*2, M), nn.BatchNorm1d(M), nn.ReLU(),
    nn.Linear(M, M//2), nn.BatchNorm1d(M//2), nn.ReLU(),
    nn.Linear(M//2, C)
)
encoder = Encoder(encoder_net=e_net)

# DECODER
d_net = nn.Sequential(
    nn.Linear(C, M//2), nn.BatchNorm1d(M//2), nn.ReLU(),
    nn.Linear(M//2, M), nn.BatchNorm1d(M), nn.ReLU(),
    nn.Linear(M, M*2), nn.BatchNorm1d(M*2), nn.ReLU(),
    nn.Linear(M*2, D)
)
decoder = Decoder(decoder_net=d_net)

4.2 可微量化

在压缩中使用神经网络时,需要确保通过反向传播进行训练,即只使用可微操作。但神经网络的离散输出会破坏可微性,需要应用梯度近似(如直通估计器)。不过,我们可以通过相对简单的技巧使代码 y 的量化可微。

假设编码器输出代码 y ∈ RM,存在一个代码本 c ∈ RK,可将代码本视为额外的参数向量(可学习)。量化的思路是将 y 中的每个元素替换为代码本中最接近的值。具体实现步骤如下:
1. 将 y 重复 K 次,c 重复 M 次,得到矩阵 Y ∈ RM×K 和 C ∈ RM×K。
2. 计算相似度矩阵 S = exp{- ||(Y - C)²||} ∈ RM×K,S 在 ym 最接近 ck 的位置有最大值。
3. 对 S 的第二维应用带温度的 softmax 非线性函数,得到 ˆS = softmax2(τ · S),其中 τ ≫ 1(如 τ = 10⁷)。
4. 通过 ˆy = ˆSc 计算量化代码,ˆy 仅包含代码本中的值。

虽然 ˆy 仍然是浮点数,但代码本中只有 K 种可能的值,所以值是离散的。而且,计算 ˆS 的矩阵是离散的,每行只有一个位置为 1,其余为 0。这种量化方法允许我们应用反向传播算法,在许多神经压缩方法中都有应用。以下是实现可微量化的 PyTorch 类:

import torch
import torch.nn as nn

class Quantizer(nn.Module):
    def __init__(self, input_dim, codebook_dim, temp=1.e7):
        super(Quantizer, self).__init__()
        # temperature for softmax
        self.temp = temp
        # dimensionality of the inputs and the codebook
        self.input_dim = input_dim
        self.codebook_dim = codebook_dim
        # codebook layer (a codebook)
        # initialize uniformly and a Parameter (learnable)
        self.codebook = nn.Parameter(torch.FloatTensor(1, self.codebook_dim).uniform_(-1/self.codebook_dim, 1/self.codebook_dim))

    # A function for codebook indices (a one−hot representation) to values in the codebook.
    def indices2codebook(self, indices_onehot):
        return torch.matmul(indices_onehot, self.codebook.t()).squeeze()

    # A function to change integers to a one−hot representation.
    def indices_to_onehot(self, inputs_shape, indices):
        indices_hard = torch.zeros(inputs_shape[0], inputs_shape[1], self.codebook_dim)
        indices_hard.scatter_(2, indices, 1)
        return indices_hard

    # The forward function:
    # −First, distances are calculated between input values and codebook values.
    # −Second, indices (soft −differentiable , hard −non−differentiable) between the encoded values and the codebook values are calculated.
    # −Third, the quantizer returns indices and quantized code (the output of the encoder).
    # −Fourth, the decoder maps the quantized code to the observable space (i.e., it decodes the code back).
    def forward(self, inputs):
        # inputs −a matrix of floats , B x M
        inputs_shape = inputs.shape
        # repeat inputs
        inputs_repeat = inputs.unsqueeze(2).repeat(1, 1, self.codebook_dim)
        # calculate distances between input values and the codebook values
        distances = torch.exp(-torch.sqrt(torch.pow(inputs_repeat - self.codebook.unsqueeze(1), 2)))

        # indices (hard, i.e., nondiff)
        indices = torch.argmax(distances, dim=2).unsqueeze(2)
        indices_hard = self.indices_to_onehot(inputs_shape=inputs_shape, indices=indices)

        # indices (soft, i.e., diff)
        indices_soft = torch.softmax(self.temp * distances, -1)

        # quantized values: we use soft indices here because it allows backpropagation
        quantized = self.indices2codebook(indices_onehot=indices_soft)

        return (indices_soft, indices_hard, quantized)

2.3 自适应熵编码模型

熵编码是整个压缩过程的最后一环,常用的熵编码器有霍夫曼编码和算术编码。算术编码在压缩系统中更受青睐,因为它速度更快、精度更高。

我们需要估计代码的概率分布 p(y),以便熵编码器进行无损压缩。可以使用深度生成模型来估计 p(y),例如自回归模型。自回归模型以量化代码为输入,输出代码本中每个值的概率。

以下是一个使用自回归模型的自适应熵编码模型的 PyTorch 类:

import torch
import torch.nn as nn

class ARMEntropyCoding(nn.Module):
    def __init__(self, code_dim, codebook_dim, arm_net):
        super(ARMEntropyCoding, self).__init__()
        self.code_dim = code_dim
        self.codebook_dim = codebook_dim
        self.arm_net = arm_net  # it takes B x 1 x code_dim and outputs B x codebook_dim x code_dim

    def f(self, x):
        h = self.arm_net(x.unsqueeze(1))
        h = h.permute(0, 2, 1)
        p = torch.softmax(h, 2)
        return p

    def sample(self, quantizer=None, B=10):
        x_new = torch.zeros((B, self.code_dim))
        for d in range(self.code_dim):
            p = self.f(x_new)
            indx_d = torch.multinomial(p[:, d, :], num_samples=1)
            codebook_value = quantizer.codebook[0, indx_d].squeeze()
            x_new[:, d] = codebook_value
        return x_new

    def forward(self, z, x):
        p = self.f(x)
        return -torch.sum(z * torch.log(p), 2)

2.4 神经压缩系统

神经压缩系统将通用压缩方案中的变换替换为神经网络,并结合可微量化过程。与传统编解码器相比,神经压缩器可以进行端到端训练,并针对特定数据进行优化。

神经压缩器的训练目标可以看作是自编码器的惩罚性重建误差。假设我们有训练数据 D = {x₁, …, xₙ} 及其经验分布 pdata(x),编码器网络权重为 φ,可微量化器代码本为 c,解码器网络权重为 θ,熵编码模型权重为 λ。通过最小化以下目标函数来训练模型(β > 0):
[L(\theta, \varphi, \lambda, c) = E_{x \sim p_{data}(x)}[(x - f_{d, \theta}(Q(f_{e, \varphi}(x); c)))^2] + \beta E_{\hat{y} \sim p_{data}(x) \delta(Q(f_{e, \varphi}(x); c) - \hat{y})}[-\ln p_{\lambda}(\hat{y})]]
其中,第一项是均方误差(MSE)损失,即重建误差;第二项是 q(ˆy) = pdata(x) δ(Q(fe,φ(x); c) - ˆy) 和 pλ(ˆy) 之间的交叉熵。

以下是神经压缩器的 PyTorch 类实现:

import torch
import torch.nn as nn

class NeuralCompressor(nn.Module):
    def __init__(self, encoder, decoder, entropy_coding, quantizer, beta=1., detaching=False):
        super(NeuralCompressor, self).__init__()
        print('Neural Compressor by JT.')
        self.encoder = encoder
        self.decoder = decoder
        self.entropy_coding = entropy_coding
        self.quantizer = quantizer
        # beta determines how strongly we focus on compression against reconstruction quality
        self.beta = beta
        # We can detach inputs to the rate, then we learn rate and distortion separately
        self.detaching = detaching

    def forward(self, x, reduction='avg'):
        # encoding
        # −non−quantized values
        z = self.encoder(x)
        # −quantizing
        quantizer_out = self.quantizer(z)

        # decoding
        x_rec = self.decoder(quantizer_out[2])

        # Distortion (e.g., MSE)
        Distortion = torch.mean(torch.pow(x - x_rec, 2), 1)

        # Rate: we use the entropy coding here
        Rate = torch.mean(self.entropy_coding(quantizer_out[0], quantizer_out[2]), 1)

        # Objective
        objective = Distortion + self.beta * Rate

        if reduction == 'sum':
            return objective.sum(), Distortion.sum(), Rate.sum()
        else:
            return objective.mean(), Distortion.mean(), Rate.mean()

2.5 压缩过程

假设模型已经训练好,神经压缩的完整过程如下:
1. 对输入图像进行编码:y = fe,φ(x)。
2. 对编码进行量化:ˆy = Q(y; c)。
3. 使用 pλ(ˆy) 和算术编码将量化代码 ˆy 转换为比特流。
4. 发送比特流。
5. 使用 pλ(ˆy) 和算术解码将比特流解码为 ˆy。
6. 对 ˆy 进行解码:ˆx = fd,θ(ˆy)。

2.6 示例

在单独的文件(https://github.com/jmtomczak/intro_dgm)中可以找到上述神经压缩器的实现,并进行实践。例如,当 β = 1 时,可能会得到如图 8.5 所示的结果,包括失真曲线、速率曲线以及真实图像、重建图像和自回归熵编码器的采样结果。

综上所述,神经压缩利用神经网络的灵活性,通过可微量化和自适应熵编码模型,在图像(或一般数据)压缩中展现出了很大的潜力。不同的组件设计和参数选择可以适应不同的数据特点和压缩需求。

5. 神经压缩的优势与应用场景

5.1 优势分析

相较于传统的压缩方法,神经压缩具有显著的优势。传统编解码器如 JPEG 采用预定义的变换和数学操作,虽然过程清晰,但缺乏灵活性。以 DCT 变换为例,它并非对所有图像都是最优的变换方式。而神经压缩通过用神经网络替换这些数学操作,将原本的“白盒”变成“黑盒”,大大增加了灵活性。

从性能上看,神经压缩可以针对特定的数据进行端到端的训练,从而在失真和速率方面都有可能取得更好的表现。通过优化目标函数,能够平衡重建误差和压缩比,以满足不同应用场景的需求。

5.2 应用场景

神经压缩在多个领域都有广泛的应用前景:
- 图像和视频存储 :在大规模的图像和视频数据库中,使用神经压缩可以显著减少存储空间的需求。例如,在云存储服务中,对用户上传的大量图像和视频进行神经压缩,能够降低存储成本,提高存储效率。
- 实时通信 :在视频会议、直播等实时通信场景中,需要在有限的带宽下传输高质量的图像和视频。神经压缩可以在保证一定重建质量的前提下,降低数据传输量,减少延迟,提高通信的流畅性。
- 医学影像 :医学影像如 X 光、CT 等数据量较大,且对图像质量要求较高。神经压缩可以在不损失重要诊断信息的情况下,减少数据存储和传输的负担,方便医生进行远程诊断和病例共享。

6. 技术挑战与未来发展方向

6.1 技术挑战

尽管神经压缩具有很多优势,但也面临着一些技术挑战:
- 计算资源需求高 :训练神经网络需要大量的计算资源和时间,特别是对于大规模的数据集和复杂的网络架构。这限制了神经压缩在一些资源受限的设备上的应用。
- 模型解释性差 :神经网络是一个“黑盒”模型,其决策过程难以解释。在一些对安全性和可靠性要求较高的应用场景中,如医疗和金融领域,模型的可解释性是一个重要的问题。
- 量化误差 :虽然可微量化技术可以使量化过程可微,但仍然存在量化误差,这可能会影响重建图像的质量。如何进一步减少量化误差,提高重建质量,是一个需要解决的问题。

6.2 未来发展方向

为了克服这些挑战,神经压缩的未来发展可能会朝着以下方向进行:
- 轻量级模型设计 :研究人员将致力于设计轻量级的神经网络架构,减少计算资源的需求,使其能够在移动设备和嵌入式系统中运行。
- 可解释性研究 :探索提高神经网络可解释性的方法,例如通过引入可解释的模块或特征,使模型的决策过程更加透明。
- 联合优化 :将神经压缩与其他技术如深度学习中的图像增强、超分辨率等进行联合优化,进一步提高压缩性能和重建质量。

7. 总结与建议

7.1 总结

神经压缩是一种基于神经网络的新型压缩技术,它通过深度生成建模、可微量化和自适应熵编码等方法,在图像和数据压缩领域展现出了巨大的潜力。与传统压缩方法相比,神经压缩具有更高的灵活性和更好的性能,能够适应不同的数据特点和应用需求。

7.2 建议

对于想要深入了解和应用神经压缩技术的读者,以下是一些建议:
- 学习基础知识 :掌握深度学习、信息论和压缩算法的基础知识,这是理解神经压缩的前提。
- 实践项目 :通过实践项目,如在 GitHub 上运行示例代码,深入理解神经压缩的实现过程和参数调整。
- 关注研究动态 :关注相关领域的研究进展,了解最新的技术和方法,不断提升自己的技术水平。

8. 附录:关键概念与公式总结

8.1 关键概念

概念 解释
无损压缩 一种保留所有信息,重建结果无误差的压缩方法
有损压缩 压缩过程中会丢失部分信息的压缩方法
熵编码 利用符号出现概率的信息,将离散信号无损编码为比特流的方法
可微量化 通过相对简单的技巧使代码量化可微,以支持反向传播训练的方法
自适应熵编码模型 学习代码概率分布 p(y),并用于熵编码的模型

8.2 关键公式

  • 目标函数 :[L(x) = d(x, \hat{x}) + \beta r(y)]
  • 训练目标函数 :[L(\theta, \varphi, \lambda, c) = E_{x \sim p_{data}(x)}[(x - f_{d, \theta}(Q(f_{e, \varphi}(x); c)))^2] + \beta E_{\hat{y} \sim p_{data}(x) \delta(Q(f_{e, \varphi}(x); c) - \hat{y})}[-\ln p_{\lambda}(\hat{y})]]

8.3 神经压缩流程图

graph TD;
    A[输入图像] --> B[编码 y = fe,φ(x)];
    B --> C[量化 ˆy = Q(y; c)];
    C --> D[熵编码为比特流];
    D --> E[发送比特流];
    E --> F[接收比特流];
    F --> G[熵解码为 ˆy];
    G --> H[解码 ˆx = fd,θ(ˆy)];
    H --> I[输出重建图像];

通过以上内容,我们对神经压缩有了全面的了解。从基本概念、组件实现到优势应用、挑战与发展方向,希望能够帮助读者深入认识这一前沿技术,并为相关的研究和实践提供参考。

【四轴飞行器】非线性三自由度四轴飞行器模拟器研究(Matlab代码实现)内容概要:本文围绕非线性三自由度四轴飞行器的建模与仿真展开,重点介绍了基于Matlab的飞行器动力学模型构建与控制系统设计方法。通过对四轴飞行器非线性运动方程的推导,建立其在三维空间中的姿态与位置动态模型,并采用数值仿真手段实现飞行器在复杂环境下的行为模拟。文中详细阐述了系统状态方程的构建、控制输入设计以及仿真参数设置,并结合具体代码实现展示了如何对飞行器进行稳定控制与轨迹跟踪。此外,文章还提到了多种优化与控制策略的应用背景,如模型预测控制、PID控制等,突出了Matlab工具在无人机系统仿真中的强大功能。; 适合人群:具备一定自动控制理论基础和Matlab编程能力的高校学生、科研人员及从事无人机系统开发的工程师;尤其适合从事飞行器建模、控制算法研究及相关领域研究的专业人士。; 使用场景及目标:①用于四轴飞行器非线性动力学建模的教学与科研实践;②为无人机控制系统设计(如姿态控制、轨迹跟踪)提供仿真验证平台;③支持高级控制算法(如MPC、LQR、PID)的研究与对比分析; 阅读建议:建议读者结合文中提到的Matlab代码与仿真模型,动手实践飞行器建模与控制流程,重点关注动力学方程的实现与控制器参数调优,同时可拓展至多自由度或复杂环境下的飞行仿真研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值