来源: DeepHub IMBA
本文约4500字,建议阅读10分钟
本篇文章我们将关注 PixelCNNs 的最大限制之一(即盲点)以及如何改进以修复它。

在这篇文章中我们将介绍盲点的概念,讨论 PixelCNN 是如何受到影响的,并实现一种解决方案——Gated PixelCNN。

盲点

PixelCNN 学习图像中所有像素的条件分布并使用此信息进行预测。PixelCNN 将学习像素从左到右和从上到下的分布,通常使用掩码来确保“未来”像素(即正在预测的像素右侧或下方的像素)不能用于给定像素的预测。如下图A所示,掩码将当前被预测的像素(这对应于掩码中心的像素)“之后”的像素清零。但是这种操作导致并不是所有“过去”的像素都会被用来计算新点,丢失的信息会产生盲点。

要了解盲点问题,让我们看上图B。在图B 中,深粉色点 (m) 是我们要预测的像素,因为它位于过滤器的中心。如果我们使用的是 3x3 掩码 (上图A.),像素 m 取决于 l、g、h、i。另一方面,这些像素取决于之前的像素。例如,像素g依赖于f、a、b、c,像素i依赖于h、c、d、e。从上图 B 中,我们还可以看到,尽管出现在像素 m 之前,但从未考虑像素 j 来计算 m 的预测。同样,如果我们想对 q、j、n、o 进行预测,则永远不会考虑(上图C橙色部分)。所以并非所有先前的像素都会影响预测,这种情况就被称为盲点问题。

我们将首先看看pixelcnn的实现,以及盲点将如何影响结果。下面的代码片段展示了使用Tensorflow 2.0的PixelCNN实现掩码。

class MaskedConv2D(keras.layers.Layer):"""Convolutional layers with masks.Convolutional layers with simple implementation of masks type A and B forautoregressive models.Arguments:mask_type: one of `"A"` or `"B".`filters: Integer, the dimensionality of the output space(i.e. the number of output filters in the convolution).kernel_size: An integer or tuple/list of 2 integers, specifying theheight and width of the 2D convolution window.Can be a single integer to specify the same value forall spatial dimensions.strides: An integer or tuple/list of 2 integers,specifying the strides of the convolution along the height and width.Can be a single integer to specify the same value forall spatial dimensions.Specifying any stride value != 1 is incompatible with specifyingany `dilation_rate` value != 1.padding: one of `"valid"` or `"same"` (case-insensitive).kernel_initializer: Initializer for the `kernel` weights matrix.bias_initializer: Initializer for the bias vector."""def __init__(self,mask_type,filters,kernel_size,strides=1,padding='same',kernel_initializer='glorot_uniform',bias_initializer='zeros'):super(MaskedConv2D, self).__init__()assert mask_type in {'A', 'B'}self.mask_type = mask_typeself.filters = filtersself.kernel_size = kernel_sizeself.strides = stridesself.padding = padding.upper()self.kernel_initializer = initializers.get(kernel_initializer)self.bias_initializer = initializers.get(bias_initializer)def build(self, input_shape):self.kernel = self.add_weight('kernel',shape=(self.kernel_size,self.kernel_size,int(input_shape[-1]),self.filters),initializer=self.kernel_initializer,trainable=True)self.bias = self.add_weight('bias',shape=(self.filters,),initializer=self.bias_initializer,trainable=True)center = self.kernel_size // 2mask = np.ones(self.kernel.shape, dtype=np.float64)mask[center, center + (self.mask_type == 'B'):, :, :] = 0.mask[center + 1:, :, :, :] = 0.self.mask = tf.constant(mask, dtype=tf.float64, name='mask')def call(self, input):masked_kernel = tf.math.multiply(self.mask, self.kernel)x = nn.conv2d(input,masked_kernel,strides=[1, self.strides, self.strides, 1],padding=self.padding)x = nn.bias_add(x, self.bias)return x

观察原始PixelCNN的接收域(下图2中用黄色标记),我们可以看到盲点以及它是如何在不同层上传播的。在这本篇文章的第二部分,我们将使用改进版的PixelCNN,门控PixelCNN,它引入了一种新的机制来避免盲点的产生。

图2:PixelCNN上盲点区域的可视化

Gated PixelCNN

为了解决这些问题,van den Oord等人(2016)引入了门控PixelCNN。门控PixelCNN不同于PixelCNN在两个主要方面:

  • 它解决了盲点问题

  • 使用门控卷积层提高了模型的性能

Gated PixelCNN 如何解决盲点问题

这个新模型通过将卷积分成两部分来解决盲点问题:垂直和水平堆栈。让我们看看垂直和水平堆栈是如何工作的。

图 3:垂直(绿色)和水平堆栈(蓝色 )

在垂直堆栈中,目标是处理当前行之前所有行的上下文信息。用于确保使用所有先前信息并保持因果关系(当前预测的像素不应该知道其右侧的信息)的方法是分别将掩码的中心向上移动到被预测的像素上一行。如图3所示,中心是浅绿色的像素(m),但垂直堆栈收集的信息不会用于预测它,而是用于预测它下面一行的像素(r)。

单独使用垂直堆栈会在黑色预测像素 (m) 的左侧产生盲点。为避免这种情况,垂直堆栈收集的信息与来自水平堆栈的信息(图 3 中以蓝色表示的 p-q)相结合,从而预测需要预测的像素 (m) 左侧的所有像素。水平和垂直堆栈的结合解决了两个问题:

(1)不会使用预测像素右侧的信息,

(2)因为我们作为一个块来考虑,不再有盲点。

原始论文通过感受野具有 2x3的卷积实现了垂直堆栈 。在本篇文章中我们则通过使用 3x3 卷积并屏蔽掉最后一行来实现这一点。在水平堆栈中,卷积层将预测值与来自当前分析像素行的数据相关联。这可以使用 1x3 卷积来实现,这样就可以屏蔽未来的像素以保证自回归模型的因果关系条件。与 PixelCNN 类似,我们实现了 A 型掩码(用于第一层)和 B 型掩码(用于后续层)。

下面的代码片段展示了使用 Tensorflow 2.0 框架实现的掩码。

class MaskedConv2D(keras.layers.Layer):"""Convolutional layers with masks.Convolutional layers with simple implementation of masks type A and B forautoregressive models.Arguments:mask_type: one of `"A"` or `"B".`filters: Integer, the dimensionality of the output space(i.e. the number of output filters in the convolution).kernel_size: An integer or tuple/list of 2 integers, specifying theheight and width of the 2D convolution window.Can be a single integer to specify the same value forall spatial dimensions.strides: An integer or tuple/list of 2 integers,specifying the strides of the convolution along the height and width.Can be a single integer to specify the same value forall spatial dimensions.Specifying any stride value != 1 is incompatible with specifyingany `dilation_rate` value != 1.padding: one of `"valid"` or `"same"` (case-insensitive).kernel_initializer: Initializer for the `kernel` weights matrix.bias_initializer: Initializer for the bias vector."""def __init__(self,mask_type,filters,kernel_size,strides=1,padding='same',kernel_initializer='glorot_uniform',bias_initializer='zeros'):super(MaskedConv2D, self).__init__()assert mask_type in {'A', 'B', 'V'}self.mask_type = mask_typeself.filters = filtersself.kernel_size = kernel_sizeself.strides = stridesself.padding = padding.upper()self.kernel_initializer = initializers.get(kernel_initializer)self.bias_initializer = initializers.get(bias_initializer)def build(self, input_shape):kernel_h = self.kernel_sizekernel_w = self.kernel_sizeself.kernel = self.add_weight('kernel',shape=(self.kernel_size,self.kernel_size,int(input_shape[-1]),self.filters),initializer=self.kernel_initializer,trainable=True)self.bias = self.add_weight('bias',shape=(self.filters,),initializer=self.bias_initializer,trainable=True)mask = np.ones(self.kernel.shape, dtype=np.float64)# Get centre of the filter for even or odd dimensionsif kernel_h % 2 != 0:center_h = kernel_h // 2else:center_h = (kernel_h - 1) // 2if kernel_w % 2 != 0:center_w = kernel_w // 2else:center_w = (kernel_w - 1) // 2if self.mask_type == 'V':mask[center_h + 1:, :, :, :] = 0.else:mask[:center_h, :, :] = 0.mask[center_h, center_w + (self.mask_type == 'B'):, :, :] = 0.mask[center_h + 1:, :, :] = 0.self.mask = tf.constant(mask, dtype=tf.float64, name='mask')def call(self, input):masked_kernel = tf.math.multiply(self.mask, self.kernel)x = nn.conv2d(input,masked_kernel,strides=[1, self.strides, self.strides, 1],padding=self.padding)x = nn.bias_add(x, self.bias)return x

通过在整个网络中添加这两个堆栈的特征图,我们得到了一个具有一致感受野且不会产生盲点的自回归模型(图 4)。

图 4:Gated PixelCNN 的感受野可视化。我们注意到,使用垂直和水平堆栈的组合,我们能够避免在 PixelCNN 的初始版本中观察到的盲点问题(对比图2)。

门控激活单元(或门控块)

从原始的PixelCNNs 到 Gated CNNs 的第二个主要改进是引入了门控块和乘法单元(以 LSTM 门的形式)。因此,而不是像原始PixelCNNs 那样在掩码卷积之间使用ReLU;Gated PixelCNN 使用门控激活单元来模拟特征之间更复杂的交互。这个门控激活单元使用 sigmoid(作为遗忘门)和 tanh(作为真正的激活)。在原始论文中,作者认为这可能是 PixelRNN(使用 LSTM 的)优于 PixelCNN 的原因之一,因为它们能够通过循环更好地捕获过去的像素——它们可以记住过去的信息。因此,Gated PixelCNN 添加并使用了以下内容:

σ 是 sigmoid ,k 是层数,⊙ 是元素乘积,* 是卷积算子,W 是来自前一层的权重。有了这个公式我们就可以更详细地介绍 PixelCNN 中的单个层。

Gated PixelCNN 中的单层块

堆栈和门是 Gated PixelCNN 的基本块(下图 5)。但是它们是如何连接的,信息将如何处理?我们将把它分解成 4 个处理步骤,我们将在下面的会话中讨论。

图 5:Gated PixelCNN 架构概述(来自原始论文的图像)。颜色代表不同的操作(即,绿色:卷积;红色:元素乘法和加法;蓝色:具有权重的卷积

1、计算垂直堆栈特征图

作为第一步,来自垂直堆栈的输入由3x3 卷积层和垂直掩码处理。然后生成的特征图通过门控激活单元并输入到下一个块的垂直堆栈中。

2、将垂直地图送入水平堆栈

对于自回归模型,需要结合垂直和水平堆栈的信息。为此在每个块中垂直堆栈也用作水平层的输入之一。由于垂直堆栈的每个卷积步骤的中心对应于分析的像素,所以我们不能只添加垂直信息,这将打破自回归模型的因果关系条件,因为它将允许使用未来像素的信息来预测水平堆栈中的值。下图 8A 中的第二幅图就是这种情况,其中黑色像素右侧(或未来)的像素用于预测它。因为这个原因在将垂直信息提供给水平堆栈之前,需要使用填充和裁剪将其向下移动(图 8B)。通过对图像进行零填充并裁剪图像底部,可以确保垂直和水平堆栈之间的因果关系。我们将在以后的文章中深入研究裁剪如何工作的更多细节,所以如果它的细节不完全清楚,请不要担心。

图8:如何保证像素之间的因果关系

3、计算水平特征图

在这一步中,处理水平卷积层的特征图。事实上,首先从垂直卷积层到水平卷积层输出的特征映射求和。这种组合的输出具有理想的接收格式,它考虑了所有以前像素的信息,然后就是通过门控激活单元。

4、计算水平叠加上的残差连接

在这最后一步中,如果该块不是网络的第一个块,则使用残差连接合并上一步的输出(经过1x1卷积处理),然后送入下一个块的水平堆栈。如果是网络的第一个块,则跳过这一步。

使用Tensorflow 2的代码如下:

class GatedBlock(tf.keras.Model):""" Gated block that compose Gated PixelCNN."""def __init__(self, mask_type, filters, kernel_size):super(GatedBlock, self).__init__(name='')self.mask_type = mask_typeself.vertical_conv = MaskedConv2D(mask_type='V',filters=2 * filters,kernel_size=kernel_size)self.horizontal_conv = MaskedConv2D(mask_type=mask_type,filters=2 * filters,kernel_size=kernel_size)self.padding = keras.layers.ZeroPadding2D(padding=((1, 0), 0))self.cropping = keras.layers.Cropping2D(cropping=((0, 1), 0))self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1)self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1)def _gate(self, x):tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1)return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation)def call(self, input_tensor):v = input_tensor[0]h = input_tensor[1]vertical_preactivation = self.vertical_conv(v)# Shifting vertical stack feature map down before feed into horizontal stack to# ensure causalityv_to_h = self.padding(vertical_preactivation)v_to_h = self.cropping(v_to_h)v_to_h = self.v_to_h_conv(v_to_h)horizontal_preactivation = self.horizontal_conv(h)v_out = self._gate(vertical_preactivation)horizontal_preactivation = horizontal_preactivation + v_to_hh_activated = self._gate(horizontal_preactivation)h_activated = self.horizontal_output(h_activated)if self.mask_type == 'A':h_out = h_activatedelif self.mask_type == 'B':h_out = h + h_activatedreturn v_out, h_out

综上所述,利用门控块解决了接收域盲点问题,提高了模型性能。

结果对比

原始论文中,PixelCNN使用以下架构:第一层是一个带有7x7过滤器的掩码卷积(a型)。然后使用15个残差块。每个块采用掩码B类的3x3层卷积层和标准1x1卷积层的组合处理数据。在每个卷积层之间,使用ReLU进行激活。最后还包括一些残差链接。

所以我们训练了一个PixelCNN和一个Gated PixelCNN,并在下面比较结果。

当比较PixelCNN和Gated PixelCNN的MNIST预测时(上图),我们并没有发现到在MNIST上有很大的改进。一些先前被修正的预测数字现在被错误地预测了。这并不意味着pixelcnn表现不好,在下一篇博文中,我们将讨论带门控的PixelCNN++,所以请继续关注!

Gated PixelCNN论文地址:

https://arxiv.org/abs/1606.05328

作者:

Walter Hugo Lopez Pinaya, Pedro F. da Costa, and Jessica Dafflon

编辑:王菁

校对:林亦霖

自回归模型PixelCNN 的盲点限制以及如何修复相关推荐

  1. 图片生成模型——gated pixelCNN

    google DeepMind团队在<pixel recurrent neural networks>中提出了pixelRNN/CNN之后又发表了一篇论文--<Conditional ...

  2. 李沐论文精读系列五:DALL·E2(生成模型串讲,从GANs、VE/VAE/VQ-VAE/DALL·E到扩散模型DDPM/ADM)

    文章目录 一. 前言 1.1 DALL·E简介 1.2 DALL·E2简介 1.3 文生图模型进展 二. 引言 2.1 摘要 2.2 引言&模型结构 三. 算法铺垫 3.1 GANs 3.2 ...

  3. AI艺术的背后:详解文本生成图像模型【基于 VQ-VAE】

    系列文章链接: AI艺术的背后:详解文本生成图像模型[基于 VQ-VAE] AI艺术的背后:详解文本生成图像模型[基于GAN] AI艺术的背后:详解文本生成图像模型[基于Diffusion Model ...

  4. 极棒 CAAD 登陆 DEF CON:腾讯安全云鼎实验室上演防御病毒的高端操作

    人工智能的飞速发展正在将世界带入一个全新的维度,但这同时也将网络世界的正邪对抗推入下一个战场. 美国当地时间8月10日,由  GeekPwn 主办的 CAAD Village 登陆世界顶级极客大会 D ...

  5. 生成模型——自回归模型详解与PixelCNN构建

    生成模型--自回归模型详解与PixelCNN构建 自回归模型(Autoregressive models) 简介 PixelRNN 使用TensorFlow 2构建PixelCNN模型 输入和标签 掩 ...

  6. PaperNotes(13)-Conditional Image Generation with PixelCNN Decoders

    conditional Image generation with PixelCNN Decoders ICML的best paper pixel cnn 属于完全可见的信念网络,需要对 概率密度 建 ...

  7. 为什么Bugs没有被修复?

    近来我遇到越来越多的人对我们会发布还有bug的产品大为惊讶.而让我大吃一惊的是,这些人中还有许多是软件测试人员,我本以为他们应该对此早已经有所了解.建议大家先阅读Eric Sink较早写的(但是很棒的 ...

  8. 凝思系统改时间_大众改原厂盲点监测系统,中山大众原厂改装,途观L改盲点监测...

    [锋程车改]东莞大众改盲点监测.大众途观L改盲点监测.大众途岳改盲点监测. 大众途锐改盲点监测. 大众途观改盲点监测. 大众途观L改盲点监测. 大众途昂改盲点监测. 大众途昂X改盲点监测. 大众POL ...

  9. 【图像压缩】自回归模型 《Joint Autoregressive and Hierarchical Priors for Learned Image Compression》

    1. 绪论 第一个在PSNR和MS-SSIM都优于BPG的学习模型.引入自回归模型改善熵模型,虽然自回归模型计算很慢,但作者发现在图像压缩领域,自回归模型与多层先验模型互补,能够比之前的模型更能挖掘隐 ...

最新文章

  1. 自然灾害能否被利用?
  2. Win32 鼠标绘图代码研究
  3. Dubbo的使用及原理浅析
  4. android 支付宝沙箱测试环境,Android支付宝沙箱环境使用教程
  5. InstallShield 2015 生成单个EXE包和 MSI包
  6. numpy中tile函数
  7. 微软+开源,那些亲爱的以及热爱的
  8. Thonny -- 简洁的 python 轻量级 IDE
  9. 常见排序之——插入排序
  10. LeetCode--95. 不同的二叉树搜索Ⅱ(动态规划)
  11. oracle伪列查询第一行,用ORACLE 中伪列rownum对查询结果进行排序
  12. linux tar 命令
  13. 数据操作类:增删改查
  14. 互联网招聘数据分析处理与可视化
  15. jsp 默认select option 默认选中方法
  16. 计算机三维建模方法,三维建模知识介绍
  17. Go-Excelize API源码阅读(二十五)——GetSheetName、GetSheetIndex、GetSheetMap()
  18. 编程序将“china”译成密码
  19. CentOS 7 安装rar解压rar
  20. adb 连接逍遥游模拟器搭配monkey命令 抓取 logcat日志

热门文章

  1. opencv resize_树莓派监控摄像头python+picamera或openCV
  2. excel的if函数中android,excel中if函数嵌套式使用教程
  3. mysql 单实例部署_MySQL 5.5单实例 编译安装
  4. mysql btmp 删除_mysql-如何在VDS上运行docker(lxd /容器)
  5. Python-----多线程threading用法
  6. Sangfor_AC用户不在线但在“在线用户管理”里有显示
  7. Google发布App Engine第二代运行时,提供Python 3.7和PHP 7.2支持
  8. Modeling Filters and Whitening Filters
  9. QuickPart : 用户控件包装器 for SharePoint 2007
  10. 2021年4月19日 深圳头条后台开发实习面试(二面)(含总结)