AIGC实战——像素卷积神经网络(PixelCNN)

AIGC实战——像素卷积神经网络

    • 0. 前言
    • 1. PixelCNN 工作原理
      • 1.1 掩码卷积层
    • 1.2 残差块
    • 2. 训练 PixelCNN
    • 3. PixelCNN 分析
    • 4. 使用混合分布改进 PixelCNN
    • 小结
    • 系列链接

0. 前言

像素卷积神经网络 (Pixel Convolutional Neural Network, PixelCNN) 是于 2016 年提出的一种图像生成模型,其根据前面的像素预测下一个像素的概率来逐像素地生成图像,模型可以通过自回归的方式进行训练以生成图像。在本节中,将使用 Keras 实现 PixelCNN 模型并将其应用于图像数据生成中。

1. PixelCNN 工作原理

为了理解 PixelCNN,我们需要介绍两个关键技术:掩码卷积层 (Masked Convolutional Layer) 和残差块 (Residual Block)。

1.1 掩码卷积层

我们已经知道,卷积层可以通过应用一系列卷积核从图像中提取特征。在特定像素点处,卷积层的输出是卷积核权重与以该像素为中心的区域上的值的加权和。通过应用一系列卷积层可以检测到图像中的边缘、纹理以及在更深层的形状和高级特征。
虽然卷积层在特征检测中十分有效,但无法直接用于自回归模型,因为像素之间没有明确的顺序关系。在图像中所有像素均会被平等对待,没有像素会被视为图像的起始或结束点,这与文本数据不同,文本数据中的符号具有明确的顺序性,因此可以方便地应用循环模型,如长短期记忆网络 (Long Short-Term Memory Network, LSTM)。
为了能够以自回归的方式下将卷积层应用于图像生成,我们首先必须将像素进行排序,并确保卷积核只能看到前面的像素。然后,通过将由 10 组成的掩码与卷积核权重矩阵相乘,使得在每个像素处,层的输出仅受到前面像素值的影响,从而逐像素地生成图像,通过将卷积卷积核应用于当前图像来预测下一个像素的值。
首先,需要选择像素的排序方式,一种可行的方法是从左上到右下对像素进行排序,首先沿行移动,然后沿列移动。
然后,我们对卷积核进行掩码处理,以使得每个像素处的层的输出仅受到前面的像素值的影响。为此,我们将由 10 组成的掩码与卷积核权重矩阵相乘,将目标像素后面的其余像素的值置零。
PixelCNN 中实际上有两种不同类型的掩码:

  • A 型,中心像素的值为掩码像素
  • B 型,中心像素的值不为掩码像素

掩码卷积层

初始的掩码卷积层(即直接应用于输入图像的层)不能使用中心像素,因为这恰是我们希望网络预测的像素,而后续的层可以使用中心像素,因为它已经由初始输入图像之前的像素信息计算出来。
使用 Keras 构建掩码卷积层 (MaskedConvLayer):

class MaskedConv2D(layers.Layer):
    def __init__(self, mask_type, **kwargs):
        super(MaskedConv2D, self).__init__()
        self.mask_type = mask_type
        # 掩码卷积层 (MaskedConvLayer) 基于普通的 Conv2D 层
        self.conv = layers.Conv2D(**kwargs)

    def build(self, input_shape):
        # 初始化卷积核
        self.conv.build(input_shape)
        # 创建掩码
        kernel_shape = self.conv.kernel.get_shape()
        # 掩码初始化为全零向量
        self.mask = np.zeros(shape=kernel_shape)
        # 前面行中的像素使用 1 解除掩码
        self.mask[: kernel_shape[0] // 2, ...] = 1.0
        # 同一行中前面列中的像素使用 1 解除掩码
        self.mask[kernel_shape[0] // 2, : kernel_shape[1] // 2, ...] = 1.0
        # 如果掩码类型为 B,则中心像素使用 1 解除掩码
        if self.mask_type == "B":
            self.mask[kernel_shape[0] // 2, kernel_shape[1] // 2, ...] = 1.0

    def call(self, inputs):
        # 掩码与卷积核权重相乘
        self.conv.kernel.assign(self.conv.kernel * self.mask)
        return self.conv(inputs)

需要注意的是,我们假设使用灰度图像(即只有一个通道)。如果我们使用彩色图像,则可以对三个颜色通道进行排序,例如红色通道在蓝色通道之前,蓝色通道在绿色通道之前。

1.2 残差块

我们已经学习了如何对卷积层进行掩码处理,接下来开始构建 PixelCNN,我们将使用残差块 (Residual Block) 作为核心构建块。

残差块是一组网络层,包含两个主要部分:

  • 主路径 (Main Path):由一系列卷积层和激活函数构成,用于学习特征表示
  • 跳跃连接 (Skip Connection):直接将输入信息绕过一部分主路径,与输出相加。这样可以确保输入信息更容易传播到后续层,并且有助于避免梯度消失问题

也就是说,在残差块中,输入有一条捷径连接到输出,而无需经过中间层。跳跃连接的理论基础可以描述为,如果最优的变换是保持输入不变,那么通过简单地将中间层的权重置零就可以实现;如果没有跳跃连接,网络就必须通过中间层找到一个恒等映射,这显然更加困难。PixelCNN 中的残差块的结构如下图所示。

残差块

使用 Keras 构建残差块 (ResidualBlock):

class ResidualBlock(layers.Layer):
    def __init__(self, filters, **kwargs):
        super(ResidualBlock, self).__init__(**kwargs)
        # 初始的 Conv2D 层将通道数量减半
        self.conv1 = layers.Conv2D(filters=filters // 2, kernel_size=1, activation="relu")
        # 类型 B 的 MaskedConv2D 层,使用尺寸为 3 的卷积核,仅利用五个像素的信息,即上方行的三个像素、左边一个像素以及中心像素本身
        self.pixel_conv = MaskedConv2D(
            mask_type="B",
            filters=filters // 2,
            kernel_size=3,
            activation="relu",
            padding="same",
        )
        # 最后的 Conv2D 层将通道数量加倍,以再次匹配输入形状
        self.conv2 = layers.Conv2D(filters=filters, kernel_size=1, activation="relu")

    def call(self, inputs):
        x = self.conv1(inputs)
        x = self.pixel_conv(x)
        x = self.conv2(x)
        # 将卷积层的输出与输入相加,即跳跃连接
        return layers.add([inputs, x])

2. 训练 PixelCNN

PixelCNN 中,输出层是一个具有 256 个卷积核的 Conv2D 层,使用 softmax 激活函数。换句话说,网络通过预测正确的像素值来尝试重新创建其输入,类似于编码器。不同之处在于,网络采用了 MaskedConv2D 层,像素预测使用的像素信息并不相同。
使用这种方法,PixelCNN 必须独立学习每个像素的输出值,但像素值 220221 的差异并不明显,这意味着即使对于最简单的数据集,训练速度也可能非常慢。因此,我们需要简化输入,使得每个像素值只有四种输出类型。这样,我们可以使用一个具有 4 个卷积核的 Conv2D 输出层,而不是 256 个卷积核。

# 模型的输入是一个尺寸为 16×16×1 的灰度图像,输入值缩放为 0 到1之间
inputs = layers.Input(shape=(IMAGE_SIZE, IMAGE_SIZE, 1))
# 首先使用 A 类型的 MaskedConv2D 层,卷积核大小为 7,使用了 24 个像素的信息——中点像素上方三行中的 21 个像素以及左边3个像素(中心像素本身没有使用)
x = MaskedConv2D(
    mask_type="A",
    filters=N_FILTERS,
    kernel_size=7,
    activation="relu",
    padding="same",
)(inputs)
# 连续堆叠五个 ResidualBlock 块
for _ in range(RESIDUAL_BLOCKS):
    x = ResidualBlock(filters=N_FILTERS)(x)
# 使用两个 B 类型的 MaskedConv2D 层,卷积核大小为 1
for _ in range(2):
    x = MaskedConv2D(
        mask_type="B",
        filters=N_FILTERS,
        kernel_size=1,
        strides=1,
        activation="relu",
        padding="valid",
    )(x)
# 最后的 Conv2D 层将通道数量减少为 4,即像素级别数
out = layers.Conv2D(
    filters=PIXEL_LEVELS,
    kernel_size=1,
    strides=1,
    activation="softmax",
    padding="valid",
)(x)
# 构建模型,其接收一张图像并输出相同尺寸的图像
pixel_cnn = models.Model(inputs, out)
print(pixel_cnn.summary())

adam = optimizers.Adam(learning_rate=0.0005)
pixel_cnn.compile(optimizer=adam, loss="sparse_categorical_crossentropy")
# 训练模型时,输入数据被缩放到 [0, 1] 的范围内(浮点数);输出数据为 [0, 3] 的范围内的整数
pixel_cnn.fit(input_data, output_data, batch_size=BATCH_SIZE, epochs=EPOCHS)

3. PixelCNN 分析

使用 Fashion-MNIST 数据集来训练 PixelCNN 模型,为了生成新图像,我们需要让模型根据前面的所有像素逐个像素地预测下一个像素。与变分自编码器等模型相比,生成过程非常缓慢。对于一个尺寸 32×32 的灰度图像,我们需要使用模型进行 1024 次连续预测,而对于变分自编码器 (Variational Autoencoder, VAE),我们只需要进行一次预测。这是自回归模型(如 PixelCNN )的一个主要缺点,由于采样过程的顺序性,进行采样的速度较慢。
因此,为了加快新图像的生成速度,我们将图像尺寸缩放为 16×16

class ImageGenerator(callbacks.Callback):
    def __init__(self, num_img):
        self.num_img = num_img

    def sample_from(self, probs, temperature):  # <2>
        probs = probs ** (1 / temperature)
        probs = probs / np.sum(probs)
        return np.random.choice(len(probs), p=probs)

    def generate(self, temperature):
        generated_images = np.zeros(shape=(self.num_img,) + (pixel_cnn.input_shape)[1:])
        # 从空白图像(全零)开始
        batch, rows, cols, channels = generated_images.shape

        for row in range(rows):
            for col in range(cols):
                for channel in range(channels):
                    # 遍历当前图像的行、列和通道,预测下一个像素值的分布
                    probs = self.model.predict(generated_images, verbose=0)[:, row, col, :]
                    # 从预测的分布中抽样一个像素级别(在本例中像素级别范围为 [0, 3 ])
                    generated_images[:, row, col, channel] = [self.sample_from(x, temperature) for x in probs]
                    # 将像素级别转换到[0, 1]范围内,并覆盖当前图像中的像素值,准备进行下一次循环迭代
                    generated_images[:, row, col, channel] /= PIXEL_LEVELS

        return generated_images

    def on_epoch_end(self, epoch, logs=None):
        generated_images = self.generate(temperature=1.0)
        for i in range(10):
            plt.subplot(2,5,i+1)
            plt.imshow(generated_images[i], cmap='gray')
        plt.tight_layout()
        plt.savefig("./output/generated_img_{}.png".format(epoch))

img_generator_callback = ImageGenerator(num_img=10)

在下图中,我们对比了来自原始训练集和由 PixelCN 生成的几张图像。可以看到,PixelCNN 模型能够成功的学习到原始图像的整体形状和风格。因此,我们可以将图像视为一系列符号(像素值),并应用如 PixelCNN 之类的自回归模型生成逼真的样本。

生成结果

自回归模型的一个缺点是在进行采样时速度较慢,这就是为什么本节我们对输入和输出进行缩放。然而,但我们也可以使用更复杂形式的自回归模型以生成逼真图像,在这种情况下,生成速度缓慢是为了获得卓越质量输出所必须进行的取舍。
接下来,使用混合分布对 PixelCNN 的架构和训练过程进行改进,并使用内置的 TensorFlow 函数来训练改进的 PixelCNN 模型。

4. 使用混合分布改进 PixelCNN

在上一节构建的 PixelCNN 模型中,为了避免训练速度过慢的问题,令网络不必学习 256 个独立像素值上的分布,我们将 PixelCNN 的输出减少到 4 个像素级别。但是,这种方法并非最佳解决方案,对于彩色图像,我们不希望仅使用少数几种颜色。
为了解决这一问题,我们可以将网络的输出设为混合分布 (Mixture Distribution),而不是对 256 个离散像素值使用 softmax,混合分布简单来说就是两个或多个其他概率分布的混合。例如,我们可以使用一个由五个逻辑分布组成的混合分布,每个分布都有不同的参数。混合分布还需要离散分类分布,用于指示选择混合中包含的每个分布的概率。
要从混合分布中进行采样,我们首先从分类分布中进行采样,选择一个特定的子分布,然后按照正常的方式从该子分布中进行采样。这样,我们可以用相对较少的参数创建复杂的分布。例如,上图中的混合分布仅需要八个参数——两个用于分类分布,其余六个参数用于每个正态分布都有均值和方差,这与在整个像素范围内定义分类分布所需的 255 个参数相比要少得多。
TensorFlow Probability 库提供了一个能够创建具有混合分布输出的 PixelCNN 函数,使用此函数构建 PixelCNN

# 定义 PixelCNN 模型
dist = tfp.distributions.PixelCNN(
    image_shape=(IMAGE_SIZE, IMAGE_SIZE, 1),
    num_resnet=1,
    num_hierarchies=2,
    num_filters=32,
    num_logistic_mix=N_COMPONENTS,
    dropout_p=0.3,
)
# 输入是尺寸为 32×32×1 的灰度图像
image_input = layers.Input(shape=(IMAGE_SIZE, IMAGE_SIZE, 1))
# 模型以灰度图像作为输入,并输出在 PixelCNN 计算的混合分布下图像的对数似然
log_prob = dist.log_prob(image_input)
pixelcnn = models.Model(inputs=image_input, outputs=log_prob)
# 损失函数是批输入图像的平均负对数似然
pixelcnn.add_loss(-tf.reduce_mean(log_prob))

模型的训练方式与普通 PixelCNN 相同,但其接受整数像素值作为输入,取值范围为 [0, 255]。可以使用 sample 函数从分布中生成输出:

dist.sample(10).numpy()

生成的示例图像如下图所示,与普通 PixelCNN 不同的是,此模型利用了完整的像素值范围。

生成结果

小结

在本节中,介绍了如何使用 PixelCNN 以自回归的方式生成图像,使用 Keras 构建 PixelCNN 模型,实现掩码卷积层和残差块,以便信息可以在网络中传递,只有前面的像素可以用于生成当前的像素。最后,使用 TensorFlow Probability 库提供的 PixelCNN 函数,该函数使用混合分布作为输出层,从而能够进一步改善学习过程。

系列链接

AIGC实战——生成模型简介
AIGC实战——深度学习 (Deep Learning, DL)
AIGC实战——卷积神经网络(Convolutional Neural Network, CNN)
AIGC实战——自编码器(Autoencoder)
AIGC实战——变分自编码器(Variational Autoencoder, VAE)
AIGC实战——使用变分自编码器生成面部图像
AIGC实战——生成对抗网络(Generative Adversarial Network, GAN)
AIGC实战——WGAN(Wasserstein GAN)
AIGC实战——条件生成对抗网络(Conditional Generative Adversarial Net, CGAN)
AIGC实战——自回归模型(Autoregressive Model)
AIGC实战——改进循环神经网络

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LINUX基础培训九之网络管理

前言、本章学习目标 了解LINUX网络接口和主机名配置的方法熟悉网络相关的几个配置文件了解网关和路由熟悉网络相关的命令使用 一、网络IP地址配置 在Linux中配置IP地址的方法有以下这么几种&#xff1a; 1、图形界面配置IP地址&#xff08;操作方式如Windows系统配置IP&…

机器学习:线性回归模型的原理、应用及优缺点

一、原理 线性回归是一种统计学和机器学习中常用的方法&#xff0c;用于建立变量之间线性关系的模型。其原理基于假设因变量&#xff08;或响应变量&#xff09;与自变量之间存在线性关系。 下面是线性回归模型的基本原理&#xff1a; 模型拟合&#xff1a; 通过最小二乘法&…

1、机器学习模型的工作方式

第一步,如果你是机器学习新手。 本课程所需数据集夸克网盘下载链接:https://pan.quark.cn/s/9b4e9a1246b2 提取码:uDzP 文章目录 1、简介2、决策树优化3、继续1、简介 我们将从机器学习模型如何工作以及如何使用它们的概述开始。如果你以前做过统计建模或机器学习,这可能感…

【Web】CTFSHOW 文件上传刷题记录(全)

期末考完终于可以好好学ctf了&#xff0c;先把这些该回顾的回顾完&#xff0c;直接rushjava&#xff01; 目录 web151 web152 web153 web154-155 web156-159 web160 web161 web162-163 web164 web165 web166 web167 web168 web169-170 web151 如果直接上传php文…

生物制药厂污水处理需要哪些工艺设备

生物制药厂是一种特殊的工业场所&#xff0c;由于其生产过程中涉及的有机物较多&#xff0c;导致废水中含有高浓度的有机物和微生物等污染物&#xff0c;因此需要采用一些特殊的工艺设备来进行污水处理。本文将介绍生物制药厂污水处理中常用的工艺设备。 首先&#xff0c;对于生…

Java NIO (二)NIO Buffer类的重要方法(备份)

1 allocate()方法 在使用Buffer实例前&#xff0c;我们需要先获取Buffer子类的实例对象&#xff0c;并且分配内存空间。需要获取一个Buffer实例对象时&#xff0c;并不是使用子类的构造器来创建&#xff0c;而是调用子类的allocate()方法。 public class AllocateTest {static…

【FastAPI】路径参数(二)

预设值 如果你有一个接收路径参数的路径操作&#xff0c;但你希望预先设定可能的有效参数值&#xff0c;则可以使用标准的 Python Enum 类型。 导入 Enum 并创建一个继承自 str 和 Enum 的子类。通过从 str 继承&#xff0c;API 文档将能够知道这些值必须为 string 类型并且能…

PromptCast-时间序列预测的好文推荐

前言 这是关于大语言模型和时间序列预测结合的好文推荐&#xff0c;发现这篇文章&#xff0c;不仅idea不错和代码开源维护的不错&#xff0c;论文也比较详细&#xff08;可能是顶刊而不是顶会&#xff0c;篇幅大&#xff0c;容易写清楚&#xff09;&#xff0c;并且关于它的Br…

STM32+HAL库驱动ADXL345传感器(SPI协议)

STM32HAL库驱动ADXL345传感器&#xff08;SPI协议&#xff09; ADXL345传感器简介实物STM32CubeMX配置SPI配置片选引脚配置串口配置 特别注意&#xff08;重点部分&#xff09;核心代码效果展示 ADXL345传感器简介 ADXL345 是 ADI 公司推出的基于 iMEMS 技术的 3 轴、数字输出加…

Spring Security- 基于角色的访问控制

基于角色 或权限 进行访问控制 hasAuthority方法 如果当前的主体具有指定的权限,则返回true,否则返回false 修改配置类 //当前登录用户 只有具备admins权限才可以访问这个路径.antMatchers("/test/index").hasAuthority("admins") 代码如下: package c…

达芬奇调色软件DaVinci Resolve Studio 18 中文激活版

DaVinci Resolve Studio 18是一款功能强大的视频编辑软件&#xff0c;它可以帮助用户轻松完成视频剪辑、调色、音频处理和特效合成等任务。 软件下载&#xff1a;DaVinci Resolve Studio 18 中文激活版下载 这款软件具有友好的用户界面和易于使用的功能&#xff0c;使得用户能够…

云服务器CVM_云主机_云计算服务器_弹性云服务器

腾讯云服务器CVM提供安全可靠的弹性计算服务&#xff0c;腾讯云明星级云服务器&#xff0c;弹性计算实时扩展或缩减计算资源&#xff0c;支持包年包月、按量计费和竞价实例计费模式&#xff0c;CVM提供多种CPU、内存、硬盘和带宽可以灵活调整的实例规格&#xff0c;提供9个9的数…

如何安装“MySQL在虚拟机ubuntu”win10系统?

1、 更新列表 sudo apt-get update 2、 安装MySQL服务器 sudo apt-get install mysql-server 3、 安装MySQL客户端 sudo apt-get install mysql-client 4、 配置MySQL sudo mysql_secure_installation 5、 测试MySQL systemctl status mysql.service MySQL数据库基本…

transbigdata笔记:轨迹停止点和行程提取

1 traj_stay_move——标识停靠点和行程 1.1 方法介绍 如果两个连续轨迹数据点&#xff08;栅格化处理之后&#xff09;之间的持续时间超过设定的阈值&#xff0c;将其视为停靠点。两个停靠点之间的时间段被视为一个行程 1.2 使用方法 transbigdata.traj_stay_move(data, pa…

从零开始搭建ubuntu 16.04 pwndocker环境

1.安装VMware-tools 1.1遇到问题 在使用 VMware Workstation时遇到了VMware Tools不能安装的问题&#xff0c;具体表现为&#xff1a;在要安装VMware Tools的虚拟机上右键 ----》安装VMware Tools(T)… 为灰色&#xff0c;不能够点击。 1.2解决方案    1. 关闭虚拟机&…

设计Twitter时间线和搜索功能

设计Twitter时间线和搜索功能 设计 facebook feed 和 设计 facebook search是相同的问题 第一步&#xff1a;定义用例和约束 定义问题的需求和范围&#xff0c;询问问题去声明用例和约束&#xff0c;讨论假设 ps: 没有一个面试官会展示详细的问题&#xff0c;我们需要定义一些用…

【软件测试】学习笔记-测试基础架构

这篇文章探讨什么是测试基础架构。 什么是测试基础架构&#xff1f; 测试基础架构指的是&#xff0c;执行测试的过程中用到的所有基础硬件设施以及相关的软件设施。因此&#xff0c;我们也把测试基础架构称之为广义的测试执行环境。通常来讲&#xff0c;测试基础 架构主要包括…

Leetcode23-数组能形成多少数对(2341)

1、题目 给你一个下标从 0 开始的整数数组 nums 。在一步操作中&#xff0c;你可以执行以下步骤&#xff1a; 从 nums 选出 两个 相等的 整数 从 nums 中移除这两个整数&#xff0c;形成一个 数对 请你在 nums 上多次执行此操作直到无法继续执行。 返回一个下标从 0 开始、长…

Spring Security-用户注销及记住我

用户注销 在配置类增加退出映射地址 Overrideprotected void configure(HttpSecurity http) throws Exception {//退出/注销http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();} 完整代码: package com.config;​import o…

礼贺新春,徐坊大曲新品【中国红】

梁山徐坊大曲新推出中国风礼盒&#xff0c;以中国红为主题&#xff0c;为即将到来的新春佳节增添了浓厚的节日气氛。为您呈现一场视觉与味觉的盛宴。从礼盒的颜色到图案设计&#xff0c;无不体现出中国红的热情与活力&#xff0c;象征着吉祥、喜庆与团圆。梁山徐坊大曲&#xf…