Liu Shouda coder

PixelRNN,PixelCNN, Conditional PixelCNN

2016-10-30

本文介绍两篇论文1 2。其中第一篇是ICML2016的最佳论文。第二篇对pixelcnn进行改进,是wavenet的基础。

以下图片是condition pixelcnn生成效果。

生成单类别图像

生成单个人图像

两个人插值(熊孩子机?)

1. 基本原理

1.1 生成模型

将联合概率转化为链式概率的乘积。当前像素值有所有前面像素值决定。

\[p(image) = p(x_0, x_1, x_2 … x_n)\] \[p(image)=p(x_0)p(x_1\mid x_0)p(x_2\mid x_1, x_0)...\]

使用离散值来估计像素值。最后一层网络结构使用256维softmax来表示像素取值的概率。

1.2 mask

在转化为链式概率之后,需要特别注意的是,在预测当前点像素值的时候,不能看到后面点的像素值。文中使用mask去除后面区域的影响。

在三通道图像,会将链式规则进一步转化成:

\[p(x_{i,R}\mid X_{<i})p(x_{i,G}\mid X_{<i},{x_{i,R}})p(x_{i,B}\mid X_{<i},{x_{i,R},x_{i,G}})\]

预测B通道时候,可以参考RG值。预测G通道可以参考R值,预测R通道时候只能参考前面的像素值。这种规则构成了mask a。如下图所示。

本文还介绍了另一种mask:mask b。mask b既可以参考前面像素值,也能参考当前像素值。

在第一层网络之后,不再需要mask。

mask的具体形式如下:

Mask A

Mask B

2 pixelrnn & 原始pixelcnn 1

作者提出了两种结构:row lstm,diagonal BiLSTM来预测当前像素值。主要计算当前点像素值的影响区域(receptive field)来对比两种结构的不同。

上图中,左图是理想的影响区域:所有当前像素点之前的所有结点都参与到当前像素值的预测。下面分别介绍两种rnn的影响域。

2.1 row lstm

使用mask筛选出影响当前像素点的区域。以mask核大小3为例,则row lstm的影响范围如上图中图所示。从图中可以看出很多区域并不参与预测当前像素点的值。所以只用row lstm还不够。作者又提出Diagonal BiLSTM。

2.2 diagonal bilstm

沿着对角线进行计算。从上图种的右图可以看出,右图的影响区域于理想区域一致。实验结果也表明,使用该类型的网络效果更好。

2.3 原始pixelcnn

直接使用带mask的卷积操作。并使用多层网络。

在实际使用时,pixelcnn速度比pixel rnn快。但是效果略差。接下来介绍pixel cnn的改进版本。效果与pixel rnn类似,速度较快。同时加入condition来控制合成图像内容。

3 conditional cnn 2

3.1 blind spot

从上文我们知道,diagonal bilstm效果比row lstm好的一部分原因在于影响域的覆盖不同。

作者发现通过简单的带mask的cnn会有盲点(blind spot)。比如下图右图种的灰色部分。在预测当前位置像素的时候,和灰色部分不会有任何关系。

在这篇文章种,作者将cnn拆成两部分。使用vertical,hozizon cnn来进行计算。

从上图的网络结构中,vertical cnn作为horizon cnn的一个分量参与计算。vertical cnn使用\(n\ast n\)的卷积核(当前行以上的n/2行),horizon cnn使用\(n\ast 1\)的卷积核。

上图红色标注部分可与后文代码一一对应。

vertical stack

对图片进行(filter_size // 2 + 1, filter_size // 2)的填充。

原来对图像的mask操作可以转化为对图像((filter_size // 2) + 1, filter_size)的区域的卷积。这样,图片所在行及其之下的区域到会被忽略。

horizontal stack

对图片进行(0,filter_size//2)填充。

当前行当前像素点之前的像素,并将上行末尾n/2-2个像素值添加当前行之前

第一层需要Mask A进行mask convolution以避免看到后面的像素。后面层次不再需要mask。每层卷积核大小是 (1*n)

3.2 gate

\[y=tanh(W_{k,f}*x)\odot \sigma(W_{k,g}*x)\]

3.3 condition

\[p(x\mid h)=\prod_{i=1}^{n^2}p(x_i\mid x_1,...x_{i-1},h)\] \[y=tanh(W_{k,f}*x+V_{k,f}^Th)\odot \sigma(W_{k,g}*x+V_{k,g}^Th)\]

这里的h可以是imagenet 1000类别里面的类别id,文章开头部分的图片可以看出,这个算法基本上能很好的生成每个类别的图像。

h也可以是人脸的隐含层表示。使用triplet loss(可以与center loss34比较)训练一个网络结构f,对于一张给定图片给出其latent representation h。triplet loss的作用是保证相同人的latent表示相近,而不同人距离较远。

有了上面的网络结构,对于每张图片可以有[x,h]数据对。使用pixelcnn训练\(p(x\mid h)\)。对于任意一张不在训练集种的图片x可以先通过f(x)求出h。再用pixelcnn生成出图片。

4. pixelcnn 源码分析5

该代码在mnist数据集上测试。使用10层网络,在gtx980ti上每个epoch约5分钟(12层网络跑不动),效果如下。

20次迭代,损失0.143:

100次迭代,损失0.141:

600次迭代,损失0.142:

另一份tensorflow代码,本地测试rnn效果不佳。暂时不知道原因。

pixel cnn核心代码:

vertical_stack = Conv2D(
	WrapperLayer(self.X),
	input_dim,
	DIM,
	((filter_size // 2) + 1, filter_size),
	masktype=None,
	border_mode=(filter_size // 2 + 1, filter_size // 2),
	name= name + ".vstack1",
	activation = None
	)

out_v = vertical_stack.output()

'''
while generating i'th row we can only use information upto i-1th row in the vertical stack.
Horizontal stack gets input from vertical stack as well as previous layer.

'''
vertical_and_input_stack = T.concatenate([out_v[:,:,:-(filter_size//2)-2,:], self.X], axis=1)

'''horizontal stack is straight forward. For first layer, I have used masked convolution as
 	we are not allowed to see the pixel we would generate.

'''

horizontal_stack = Conv2D(
	WrapperLayer(vertical_and_input_stack),
	input_dim+DIM, DIM,
	(1,filter_size),
	border_mode = (0,filter_size//2),
	masktype='a',
	name = name + ".hstack1",
	activation = None
	)

self.params = vertical_stack.params + horizontal_stack.params

X_h = horizontal_stack.output() #horizontal stack output
X_v = out_v[:,:,1:-(filter_size//2) - 1,:] #vertical stack output

filter_size = 3 #all layers beyond first has effective filtersize 3

for i in range(num_layers - 2):
	vertical_stack = Conv2D(
		WrapperLayer(X_v),
		DIM,
		DIM,
		((filter_size // 2) + 1, filter_size),
		masktype = None,
		border_mode = (filter_size // 2 + 1, filter_size // 2),
		name= name + ".vstack{}".format(i+2),
		activation = None
		)
	v2h = Conv2D(
		vertical_stack,
		DIM,
		DIM,
		(1,1),
		masktype = None,
		border_mode = 'valid',
		name= name + ".v2h{}".format(i+2),
		activation = None
		)
	out_v = v2h.output()
	vertical_and_prev_stack = T.concatenate([out_v[:,:,:-(filter_size//2)-2,:], X_h], axis=1)

	horizontal_stack = Conv2D(
		WrapperLayer(vertical_and_prev_stack),
		DIM*2,
		DIM,
		(1, (filter_size // 2) + 1),
		border_mode = (0, filter_size // 2),
		masktype = None,
		name = name + ".hstack{}".format(i+2),
		activation = activation
		)

	h2h = Conv2D(
		horizontal_stack,
		DIM,
		DIM,
		(1, 1),
		border_mode = 'valid',
		masktype = None,
		name = name + ".h2hstack{}".format(i+2),
		activation = activation
		)

	self.params += (vertical_stack.params + horizontal_stack.params + v2h.params + h2h.params)

	X_v = apply_act(vertical_stack.output()[:,:,1:-(filter_size//2) - 1,:])
	X_h = h2h.output()[:,:,:,:-(filter_size//2)] + X_h #residual connection added 对位相加

combined_stack1 = Conv2D(
		WrapperLayer(X_h),
		DIM,
		DIM,
		(1, 1),
		masktype = None,
		border_mode = 'valid',
		name=name+".combined_stack1",
		activation = activation
		)

combined_stack2 = Conv2D(
		combined_stack1,
		DIM,
		out_dim,
		(1, 1),
		masktype = None,
		border_mode = 'valid',
		name=name+".combined_stack2",
		activation = None
		)

pre_final_out = combined_stack2.output().dimshuffle(0,2,3,1)

生成图片机制

X = T.tensor3('X') # shape: (batchsize, height, width)
X_r = T.itensor3('X_r') #shape: (batchsize, height, width)

input_layer = WrapperLayer(X.dimshuffle(0,1,2,'x')) # input reshaped to (batchsize, height, width,1)

pixel_CNN = pixelConv(
	input_layer,
	1,
	DIM,
	name = model.name + ".pxCNN",
	num_layers = 2,
	Q_LEVELS = Q_LEVELS
	)

model.add_layer(pixel_CNN)

output_probab = Softmax(pixel_CNN).output()

cost = T.nnet.categorical_crossentropy(
	output_probab.reshape((-1,output_probab.shape[output_probab.ndim - 1])),
	X_r.flatten()
	).mean()

output_image = sample_from_softmax(output_probab) #按照概率采样。所以生成的图片会不一样

generate_routine = theano.function([X], output_image)

def generate_fn(generate_routine, HEIGHT, WIDTH, num):
	X = floatX(numpy.zeros((num, HEIGHT, WIDTH)))
	for i in range(HEIGHT):
		for j in range(WIDTH):
			samples = generate_routine(X)
			X[:,i,j] = downscale_images(samples[:,i,j,0], Q_LEVELS-1) #每次预测一个像素

	return X

X = generate_fn(generate_routine, 28, 28, 25)

注意,具体代码还包含了量化方案。

输入数据先量化到0-level,再降到0-1数据。输出到0-4,生成图片时候再降到0-1以输出

x_test_r: 0-4 x_test: 0-1。 而神经元的输出就是0-4,标注数据是x_test_r,所以可以直接使用交叉熵来进行优化.在theano计算交叉熵时候,如果真实数据是一个整数,则默认自动转换为one hot形式。

5. 引用

  1. Pixel Recurrent Neural Networks  2

  2. Conditional Image Generation with PixelCNN Decoders  2

  3. A Discriminative Feature Learning Approach for Deep Face Recognition》,Yandong Wen, Kaipeng Zhang, Zhifeng Li, and Yu Qiao, Shenzhen 

  4. https://github.com/pangyupo/mxnet_center_loss 

  5. https://github.com/kundan2510/pixelCNN 


上一篇 merlin

下一篇 python ctypes

Content