声音生成项目(6)——在矢量量化变分编码器上使用自回归模型PixelCNN模型生成新的样本

文章目录

      • 引言
      • PixelCNN论文简读
      • 模型介绍
        • 自回归模型
        • PixelCNN
          • 模型结构
        • 基础知识回顾
      • 代码实现
        • PixelConvLayer
        • 具体运行过程
        • 卷积模块
        • 整体网络结构
      • 模型执行效果
      • 问题解决
        • 训练好的模型在生成新的图片时,为什么要逐个元素进行生成?
          • 掩码卷积仅仅是考虑了一部分的数据,并没有考虑当前像素之前所有的像素?
      • 总结
      • 参考连接

引言

  • 在上一篇就是介绍了矢量量化变分模型的具体实现,就是一个编码器和解码器,只能生成和原来图片一样的图片,没啥意义。这里需要生成一个新的码字序列,解码器能够接受这部分数据,然后解码成对应的新的图片。

  • 作者使用PixelCNN去训练这些码本

    • pixelCNN的论文链接
    • 代码样例
  • PixelCNN是一个自回归模型,根据已有的序列生成下一个位置的值。在这个任务里,就是生辰新的码字序列,然后使用训练好的解码器生成对应的新的图片。

  • 注意:

    • 这里的代码是局部代码,是vqvae项目中的一部分,是生成新的索引序列,并不是原来的图片 ,最后还是需要一个解码器来将索引序列还原成对应的图片。
    • 项目地址,vqvae生成手写字符的地址

PixelCNN论文简读

  • Abstract
    • 这篇文章主要讲的是根据PixelCNN按照条件生成图片。这个模型可以根据向量,描述性的符号或者标记,甚至是别的网络模型生成的潜在embedding来生成新的图片。这个模型可以根据ImageNet中数据的类别标记,生成逼真并且完全不同的图片。如果输入模型的是一个其他卷积网络生成的embedding,并且这个embedding是表示人脸的,他会自动生成具有不同面部表情、姿势和光照条件的,同一个人的不同的各种新的画像。PixelCNN在图片自编码器中,也可以当作一个强大的图片解码器使用。
  • Introduction
    • 图片生成应用广泛,常见的比如说降噪、去模糊、修复、超分辨和着色都是使用图片生成技术,他们都需要根据不完整的图片或者带有噪声的图片来生成原始的图片。
    • 这篇文章改良PixelRNN中的卷积变量实现的,基本的思路是使用自回归链接逐个像素对图片进行建模,将联合图像分布,分解成条件概率分布的乘积。原文中主要有两个变种,分别是PixelRNN,是使用LSTM进行预测,PixelCNN是使用卷积进行预测。
    • 我们还引入了门控PixelCNN(条件PixelCNN)的条件变体,它允许我们在给定潜在向量嵌入的情况下对自然图像的复杂条件分布进行建模。我们表明,通过简单地对类的 one-hot 编码进行条件调节,可以使用单个条件 PixelCNN 模型从狗、草坪扇和珊瑚礁等不同类别的图像生成图像。类似地,可以使用捕获图像高级信息的嵌入来生成具有各种具有相似特征的图像。这让我们深入了解嵌入中编码的不变性——例如,我们可以根据单个图像生成同一个人的不同姿势。相同的框架也可用于分析和解释深度神经网络中的不同层和激活。

模型介绍

  • 本来是想读论文的,一看已经是2016年的文章了,网上已经有很多相关的介绍了。
  • PixelCNN是通过链式法则,将概率密度转成一系列条件概率之积,然后通过网络来估计条件分布。

自回归模型

  • 自回归创建一个显式密度模型,该模型学习训练数据的最大似然。但是处理多个维度\特征的数据时,需要完整如下的步骤

    • 首先,输入空间需要为其特征确定顺序,对于图像而言,通过像素的空间位置,来定义顺序,左侧像素在右侧像素之前,顶部像素在底部像素之前等。
    • 其次,自回归模型需要将特征的联合分布p(x)转换为条件分布的乘积,以对数据观察中的特征的联合分布进行精确建模。
    • 给定先前特征的值,自回归模型使用每一个特征的条件定义联合分布
  • 图像中某一个像素具有特定强度值的概率由先前像素的值确定

  • 图像的概率(所有像素的联合分布)是其所有像素的概率的组合

  • 因此,自回归模型使用链式法则,将数据样本x的可能性分解成一维分布的乘积,将联合建模问题变成了序列问题,学习了在给定所有先前像素的情况下,预测下一个像素的过程。

在这里插入图片描述

PixelCNN

  • PixelCNN是生成神经网络,一次生成一个像素,并且使用之前已经训练好的像素去生成下一个像素,类似于根据一个序列,去预测下一个像素点的值。如下图,是使用蓝色的像素块,来预测红色的像素块。

在这里插入图片描述

  • 因为常规的卷积是计算周围所有的像素点,和PixelCNN的预测方式不同,所以这里出现了掩码卷积,来控制没有被预测的像素点。主要有两类掩码卷积类型,分别如下
    • Mask Type A仅仅是用于第一层卷积,中间值被屏蔽,因为没有预测过
    • Mask Type B可以用于出第一层以外的所有的层卷积,除了第一层,后续每一层的自身值都是被预测过的。

在这里插入图片描述

模型结构
  • 第一层使用77的A类型掩码卷积模块,然后使用15个残差模块。每一個残拆模块的构成图如下,使用33的B类型的掩码卷积模块,首尾两个1*1的卷积模块。

在这里插入图片描述
在这里插入图片描述

  • 经历过残差序列模块的处理之后,经过两个带有relu的1*1的卷积层,然后经过softmax进行输出,预测像素所有可能的预测值。模型的输出是具有与输入图像大小相同的格式乘以可能值的数量。

基础知识回顾

  • 极大似然:利用已知样本结果信息,反推最具有可能导致当前样本结果的模型参数值。相当于数据的样本是已经知道的,对应的模型分布类型不知道,缺的是对应类型的模型参数,所以是根据样本估计模型参数。
  • 概率和统计
    • 概率:已知模型和参数,推断数据的分布。
    • 统计:已知数据的具体内容,推断具体模型和参数。
  • 联合概率和条件概率
    • 联合概率:同一个时间,两个独立事件同时发生的可能性,事件相交的概率
    • 条件概率:P(B|A)事件B在事件A发生的情况下,B发生的概率。两个相依事件的联合概率变为P(A 和B)= P(A)P(B | A)
  • 贝叶斯公式
    • 后验概率等于先验概率乘以似然比
    • P(H|E)=P(H)P(E|H)/P(E)
  • 先验概率和后颜概率
    • 后验概率:所有证据被采用之后,事件发生的概率
    • 先验概率:未采用证据之前,事件发生的概率,经验性概率
    • 后验概率= (可能性*先验)/证据
  • 最大似然估计
    • 根据样本,也就是数据,来估计模型的参数,使得似然函数的值最大

代码实现

PixelConvLayer

  • 这部分就是掩码卷积,通过之前已经预测的点的概率,来预测当前的点。在原来的卷积上,增加了一个掩码的部分。
  • 主要是两类,差别在于A类中心点的坐标并没有标记,B类的中心点已经被预测过的,具体看上面的图。
# 掩码卷积
class PixelConvLayer(Layer):"""掩码卷积层,分别是两种类型,A类和B类"""def __init__(self,mask_type,**kwargs):super().__init__()self.mask_type = mask_typeself.conv = Conv2D(**kwargs)self.mask = Nonedef build(self,input_shape):# 创建二维卷积层,并初始化对应的卷积核权重参数self.conv.build(input_shape)kernel_shape = self.conv.kernel.get_shape()# 生成同等大小的掩码层,用来抑制没有预测的卷积层self.mask = np.zeros(shape = kernel_shape)self.mask[:kernel_shape[0] // 2,...] = 1.0self.mask[kernel_shape[0] // 2,:kernel_shape[1] // 2,...] = 1.0if self.mask_type == "B":self.mask[kernel_shape[0] // 2,kernel_shape[1]//2,...] = 1.0def call(self,inputs):# 根据掩码层,来修改更正卷积层的结果self.conv.kernel.assign(self.conv.kernel * self.mask)return self.conv(inputs)

具体运行过程

  • 这里用一个33的掩码卷积作为样例,对一个88的图片,进行A类型的掩码卷积,然后使用的padding模式是same.这里并不是完全的使用之前所有的已经预测过的标记点,进行预测当前的标记点,是根据卷积核中的已经预测过的点进行预测,具体过程如下:
  • 第一次预测红框中心的点

在这里插入图片描述

  • 后续,第一个位置已经被预测过了,就会考虑到第一个点的预测结果
    在这里插入图片描述
  • 以此类推,不断向后迭代

在这里插入图片描述

  • 最多的情况下,就是考虑了四个点的情况

在这里插入图片描述

  • 代码主要有两部分构成,分别是掩码和前向传播的实际计算
# 掩码卷积
class PixelConvLayer(Layer):"""掩码卷积层,分别是两种类型,A类和B类"""def __init__(self,mask_type,**kwargs):super().__init__()self.mask_type = mask_typeself.conv = Conv2D(**kwargs)self.mask = Nonedef build(self,input_shape):# 创建二维卷积层self.conv.build(input_shape)kernel_shape = self.conv.kernel.get_shape()# 将卷积层改成,掩码卷积,这里是生成掩码层self.mask = np.zeros(shape = kernel_shape)self.mask[:kernel_shape[0] // 2,...] = 1.0self.mask[kernel_shape[0] // 2,:kernel_shape[1] // 2,...] = 1.0if self.mask_type == "B":self.mask[kernel_shape[0] // 2,kernel_shape[1]//2,...] = 1.0def call(self,inputs):self.conv.kernel.assign(self.conv.kernel * self.mask)return self.conv(inputs)

卷积模块

  • 这部分就是在原来的基础上,使用残差网络将B类的掩码卷积套在残差模块中,具体形成如下
    • 首尾分别是两个1*1的卷积
    • 中间是3*3的B类掩码卷积

在这里插入图片描述

class ResidualBlock(Layer):"""基于掩码卷积层的残差模块"""def __init__(self,filters,**kwargs):super().__init__()# 第一个卷积模块self.conv1 = Conv2D(filters = filters,kernel_size = 1,activation="relu")# 中间的B类掩码卷积,进行概率估计self.pixel_conv = PixelConvLayer(mask_type="B",kernel_size = 3,activation = "relu",padding = "same",filters = filters // 2)# 最后一个卷积模块self.conv2 = 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)# 残差模块中的直连res = add([inputs,x])return res

整体网络结构

  • 具体网络结构如下图,但是教程的代码实现起来最后做了修改,我是觉得如果使用11的掩码卷积其实和常规的11的卷积是一个效果
    在这里插入图片描述
class PixelCNN:"""PixelCNN:像素卷积模型功能:用于生成新的码本序列"""def __init__(self,input_shape,num_residual_blocks,num_pixelcnn_layers,num_embeddings):self.input_shape = input_shapeself.num_residual_blocks = num_residual_blocksself.num_pixelcnn_layers = num_pixelcnn_layersself.num_embeddings = num_embeddings# 定义不同的层self.model = None# 构建模型self._build()def _build(self):"""网络结构为:Aconv3*3,residual815,Rconv,Rconv,Softmax:return:"""PixelCNN_Input = Input(shape = self.input_shape,name = "PixelCNN input")x = PixelConvLayer(mask_type="A",filters = 128,kernel_size = 7,strides = 1,activation = "relu",padding = "same")(PixelCNN_Input)# 添加卷积模块for i in range(self.num_residual_blocks):x = ResidualBlock(filters = 128)(x)# 添加后续的像素卷积层for i in range(self.num_pixelcnn_layers):x = PixelConvLayer(mask_type="B",filters = 128,kernel_size = 1,strides = 1,activation = "relu",padding = "valid")(x)out = Conv2D(filters=1,kernel_size=1,strides=1,activation="sigmoid",padding="valid")(x)self.model = Model(PixelCNN_Input,out)def summary(self):self.model.summary()def compile(self, learning_rate=0.0001):""" 指定损失函数和优化器,并对模型进行优化 """optimizer = Adam(learning_rate=learning_rate)self.model.compile(optimizer=optimizer,loss="binary_crossentropy",)# 3.3 增加训练函数def train(self, x_train, batch_size, num_epochs):self.model.fit(x_train,x_train,batch_size=batch_size,epochs=num_epochs,shuffle=True)

模型执行效果

在这里插入图片描述

  • 这里生成的模型比较简单,没有按照他的弄了几层。

问题解决

训练好的模型在生成新的图片时,为什么要逐个元素进行生成?

  • 模型在训练的过程中,会逐个预测每一个像素的像素值的概率分布,然后计算预测结果和真实值之间的差异,也就是损失函数,然后使用反向传播来更新模型的参数,使得模型预测的每一个像素的概率分布能够更加接近真实的像素值。对于这个手写数字生成的模型而言,是使用交叉熵损失函数来当作损失函数。如下图

在这里插入图片描述

  • 所以,训练过程中,使用多重掩码卷积逐个扫描每一个区域,预测出一个和输入图像同样大小的结果,然后计算输出图像和输入图像的像素误差。在生成过程中,一开始给的序列是空白序列,所有的像素值都是无效的,如果像训练过程一样,直接生成完整的子图,那么后续像素所参考的像素就没有任何意义,参考价值就不大。生成的图片如下图

在这里插入图片描述

  • 而逐个元素进行生成确认,即迭代生成每一个元素,并将当前元素加入到序列中,重新进行预测。后续生成的序列会和之前的生成的像素值相关,也就是后续的像素值,有之前的像素值决定。如下图。

在这里插入图片描述

  • 生成的效果明显就要好很多,如下图。

在这里插入图片描述

  • 说白了,在训练过程中,用的是极大似然估计,调整模型的参数,使得模型的效果尽可能地好,预测的结果尽可能地贴近实际的图片,这里还没有用到具体的条件概率,然后在预测的过程中,迭代逐个像素进行预测,就用到了条件概率,由之前预测的像素值,来决定之后的像素值。
掩码卷积仅仅是考虑了一部分的数据,并没有考虑当前像素之前所有的像素?
  • 问题:我们知道,PixelCNN的原理是将联合分布拆解成多个条件概率的分布,也就是说预测每一个像素需要考虑之前每一个值,但是掩码卷积仅仅是使用了卷积核之内的数据,并没有考虑到每一个数据。

  • PixelCNN使用了掩码卷积(masked convolution)来限制卷积核只能看到先前生成的像素值,从而确保每个像素的生成仅依赖于其前面的像素。确实并不能考虑到之前所有的像素点,只能考虑到当前像素点的感受野中的元素,还是条件概率,但是并不是之前所有的像素点。

  • 具体可以看这个图片,五角星的那个像素点,确实只能考虑圆圈圈出来的点,计算他们的条件概率,并不能考虑到没有圈处来的其他的点,但这是只有一层卷积,如果有多层卷积,感受野会更大,考虑的也会更加全面。

在这里插入图片描述

  • 如果是二层卷积,可以看到他的感受野更大了,所以能够考虑到的像素点越多。

在这里插入图片描述

  • 通过使用掩码卷积,PixelCNN可以将联合分布拆解成多个条件概率的分布,从而将生成过程转化为逐个像素的条件生成。在预测每个像素的时候,模型只考虑了之前已经生成的像素值,并没有考虑到后面的像素值。
  • 掩码卷积保证了模型的生成过程是自回归的,能够逐步地生成图像,并且可以捕捉到像素之间的条件依赖关系。每个像素的生成都受限于其前面已经生成的像素,确保了生成的图像具有一致性和合理性。
  • 需要注意的是,掩码卷积仅在训练过程中使用,用于限制卷积核的感受野。在生成过程中,模型可以自由地逐个像素生成,而不受掩码卷积的限制。
  • 因为生成图片的过程中,是迭代预测生成了每一个像素,每一个像素都要做一次完整的预测迭代,所以是不受掩码卷积的限制。
  • 总结起来,PixelCNN使用掩码卷积将联合分布拆解成条件概率分布,并通过逐个像素的条件生成来保持自回归性质。掩码卷积限制了卷积核只能看到先前生成的像素值,从而确保每个像素的生成仅依赖于其前面的像素。

总结

  • 一开始这里弄了很久都没有弄懂,尤其是条件概率那里,我知道他是考虑了之前所有的像素点,也理解掩码卷积,但是就是不理解为什么生成的时候要逐个元素进行预测,预测那么多次,现在理解了。通过做实验可以知道,这样的效果更好,而且最终生成的像素也完全不受感受野的大小,完全考虑到了所有的确定的像素点。
  • 同时这样给出了一个思路,对于第一个像素点,随即指定不同的初值,可以生成不同风格,不同类型的数据。同时可以适当减少预测的类别,来提高准确率,但是要对数据尽心有效的分割,能够分成不同的部分。
  • 这一章拖了蛮久的,终于写完了,找机会录一个视频,发一下。代码还是要好好写一下。

参考连接

  • 参考并引用下述文章的内容
    • 生成模型之PixelCNN
    • 什么是PixelCNN
    • 自回归模型–PixelCNN

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DAY47:动态规划(十)零钱兑换Ⅱ+组合总和Ⅳ(完全背包求方案总数类型,排列+组合)

文章目录 518.零钱兑换Ⅱ(装满背包方案数,本题是组合方案数)思路DP数组含义递推公式DP数组初始化遍历顺序(重要,不能颠倒)外层物品内层背包的情况外层背包内层物品的情况完全背包求排列数和组合数 完整版总…

产品流程图

流程图设计 1.什么是流程图2.流程图元素定义3.几种常见的产品流程图 3.1业务流程图 | 泳道图(给产品经理看)3.2任务流程图(给程序员看)3.3页面流程图(给UI设计人员看) 4.如何绘制流程图 4.1调查研究4.2梳理…

S32K系列MCU介绍和资料搜集

1. S32K系列微控制器概述 S32K系列微控制器,是NXP推出的专门面向汽车电子和工业应用场合的微控制器。基于ArmCortex-M系列的可扩展、低功耗微控制器,获得了AEC-Q100认证,具有高级功能安全、信息安全和软件支持,适用于工业和汽车A…

身份证读卡器安卓SDK在安卓12版本targetSdkVersion=32报错解决办法

之前的东信智能的EST-100身份证读卡器安卓SDK版本V1.0.40在安卓12版本,targetSdkVersion32的时候会出现以下错误: Targeting S (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingInten…

利用缓存模拟进度条(c实现)

代码如下&#xff1a; #include <stdio.h> #include <Windows.h>int main() {int total 100;int progress 0;char progressBar[101] {};while (progress < total) {progressBar[progress] #;printf("[%-100s] %d%%", progressBar, progress);ffl…

【UE4】在控件蓝图上播放视频

UE版本&#xff1a;4.26 在上一篇文章中&#xff08;【UE】场景内播放视频、音频&#xff09;介绍了如何在场景中播放视频&#xff0c;本篇文章将介绍如何在UI上播放视频 效果 步骤 1. 首先在“Content”文件夹中新建一个名为“Movies”的文件夹 2. 在文件夹中随便添加一个.…

apache ranger

简介&#xff1a; ranger 是一个用于启用、监控和管理跨hadoop平台的全面的数据安全框架。 ranger的愿景是在hadoop系统中提供全面的安全管理。随着yarn的出现&#xff0c;hadoop 平台能够支持真正的数据糊架构。企业能够在多租户环境中运行多个任务负载。hadoop 数据安全需要…

在阿里云linux上安装MySql数据库

我们先远程连接服务器 然后输入 sudo yum update重新运行一下 然后 sudo yum install mysql-server安装 mysql 服务 其中有两次 y n 选择 都选y就好了 然后 运行 sudo service mysqld start启动MySql 然后 我们查看一下MySql sudo service mysqld status

用github的copilot;tmux中进去了> 怎么退出

1、首先要学籍认证 &#xff08;前提&#xff1a;(241条消息) Copilot使用的关卡——GitHub教育认证方法和注意事项_github教师认证_石去皿的博客-CSDN博客&#xff09; 网址&#xff1a;Are you a student? - GitHub Education (241条消息) GitHub学生认证&#xff0c;可…

Linux内核模块开发 第 10 章 系统调用

The Linux Kernel Module Programming Guide Peter Jay Salzman, Michael Burian, Ori Pomerantz, Bob Mottram, Jim Huang译 断水客&#xff08;WaterCutter&#xff09;源 LKMPG 10 系统调用 到目前为止&#xff0c;我们所做的唯一一件事就是使用定义明确的内核机制来注册…

Android 多边形导圆角(Path画折线导圆角)

前言&#xff1a;用path画折线&#xff0c;2条线相交处导圆角 简介&#xff1a;为开发者提供工具类&#xff0c;方便对使用path画折线设置圆角。使用path画多边形&#xff0c;如三角形、矩形、平行四边形等&#xff0c;同时设置圆角。另外提供计算直线上某点坐标。 实现原理 …

开源大模型发展汇总

1. 大模型发展&概况 a. 发展线路图 其中基础模型如下&#xff1a; 大部分不开源&#xff0c;而OPT、BLOOM、LLaMA 三个模型是主要面向开源促进研究&#xff0c;聊天机器人场景开源的Open Assistant&#xff08;huggingface&#xff09;中文有一些GLM&#xff0c;百川&…