2019 年 3 月 8 日 - 作者:Ian Fischer、Alex Alemi、Joshua V. Dillon 和 TFP 团队
在 2019 年的 TensorFlow 开发者峰会上,我们 宣布 了 TensorFlow Probability (TFP) 层。在演示中,我们展示了如何在极少的代码行中构建一个强大的回归模型。这里,我们将展示使用 TFP 层构建变分自编码器 (VAE) 的简单性。
TensorFlow Probability 层TFP 层提供…
tfd = tfp.distributions
encoded_size = 16
prior = tfd.Independent(tfd.Normal(loc=tf.zeros(encoded_size), scale=1),
reinterpreted_batch_ndims=1)
在这里,我们只创建了一个 TFP 独立高斯分布,没有学习参数,并且我们指定了我们的潜在变量 z 将具有 16 维。tfpl = tfp.layers
encoder = tfk.Sequential([
tfkl.InputLayer(input_shape=input_shape),
tfkl.Lambda(lambda x: tf.cast(x, tf.float32) - 0.5),
tfkl.Conv2D(base_depth, 5, strides=1,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2D(base_depth, 5, strides=2,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2D(2 * base_depth, 5, strides=1,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2D(2 * base_depth, 5, strides=2,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2D(4 * encoded_size, 7, strides=1,
padding='valid', activation=tf.nn.leaky_relu),
tfkl.Flatten(),
tfkl.Dense(tfpl.MultivariateNormalTriL.params_size(encoded_size),
activation=None),
tfpl.MultivariateNormalTriL(
encoded_size,
activity_regularizer=tfpl.KLDivergenceRegularizer(prior, weight=1.0)),
])
编码器只是一个普通的 Keras Sequential 模型,由卷积和密集层组成,但输出传递给 TFP 层 MultivariateNormalTril()
,该层透明地将来自最终 Dense()
层的激活拆分为指定均值和(下三角)协方差矩阵(多元正态分布的参数)所需的各个部分。我们使用了一个助手 tfpl.MultivariateNormalTriL.params_size(encoded_size)
,使 Dense()
层输出正确的激活数量(即分布的参数)。最后,我们说这个分布应该对最终损失贡献一个“正则化”项。具体来说,我们将编码器和先验分布之间的 KL 散度添加到损失中,这是我们上面描述的 ELBO 中的KL 项。(有趣的事实:我们可以通过将 weight
参数更改为 1 以外的值,简单地将这个 VAE 转换为 β-VAE!)decoder = tfk.Sequential([
tfkl.InputLayer(input_shape=[encoded_size]),
tfkl.Reshape([1, 1, encoded_size]),
tfkl.Conv2DTranspose(2 * base_depth, 7, strides=1,
padding='valid', activation=tf.nn.leaky_relu),
tfkl.Conv2DTranspose(2 * base_depth, 5, strides=1,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2DTranspose(2 * base_depth, 5, strides=2,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2DTranspose(base_depth, 5, strides=1,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2DTranspose(base_depth, 5, strides=2,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2DTranspose(base_depth, 5, strides=1,
padding='same', activation=tf.nn.leaky_relu),
tfkl.Conv2D(filters=1, kernel_size=5, strides=1,
padding='same', activation=None),
tfkl.Flatten(),
tfpl.IndependentBernoulli(input_shape, tfd.Bernoulli.logits),
])
这里形式与编码器基本相同,但现在我们使用转置卷积将我们的潜在表示(一个 16 维向量)转换为一个 28 x 28 x 1 张量。该最终张量参数化了像素独立的伯努利分布。vae = tfk.Model(inputs=encoder.inputs,
outputs=decoder(encoder.outputs[0]))
我们的模型只是一个 Keras 模型,其中输出被定义为编码器和解码器的组合。由于编码器已经将 KL 项添加到损失中,因此我们只需要指定重建损失(上面 ELBO 的第一项)。negative_log_likelihood = lambda x, rv_x: -rv_x.log_prob(x)
vae.compile(optimizer=tf.optimizers.Adam(learning_rate=1e-3),
loss=negative_log_likelihood)
损失函数接受两个参数——原始输入 x 和模型的输出。我们将其称为 rv_x
,因为它是一个随机变量。此示例演示了 TFP 层的核心魔力之一——即使 Keras 和 Tensorflow 将 TFP 层视为输出张量,TFP 层实际上也是Distribution 对象。因此,我们可以使损失函数成为给定模型的数据的负对数似然:-rv_x.log_prob(x)
。x = eval_dataset.make_one_shot_iterator().get_next()[0][:10]
xhat = vae(x)
assert isinstance(xhat, tfd.Distribution)
但是,如果 TFP 层返回一个 Distribution,那么当我们将解码器与编码器的输出组合时会发生什么:decoder_model(encoder_model.outputs[0]))
?为了使 Keras 将编码器分布视为一个张量,TFP 层实际上会将分布“具体化”为来自该分布的一个样本,这只是一个花哨的说法,意思是 Keras 将 Distribution 对象视为我们本来会得到的张量,如果我们调用了 encoder_model.sample()
。但是,当我们需要直接访问 Distribution 对象时,我们可以——就像我们在损失函数中调用 rv_x.log_prob(x)
时一样。TFP 层自动提供分布式和张量式行为,因此你无需担心 Keras 会感到困惑。vae_model.fit()
vae.fit(train_dataset,
epochs=15,
validation_data=eval_dataset)
使用这个模型,我们可以获得大约 115 奈特的 ELBO(奈特 是比特的自然对数等价物——115 奈特大约是 165 比特)。当然,这种性能不是最先进的,但从这个基本设置开始,很容易使三个组件中的任何一个变得更强大。此外,它已经可以生成看起来不错的数字了!通过对来自 MNIST 测试集的图像进行编码而生成的解码器模式。 |
通过从先验分布中采样而生成的解码器模式。 |
2019 年 3 月 8 日 - 作者:Ian Fischer、Alex Alemi、Joshua V. Dillon 和 TFP 团队
在 2019 年的 TensorFlow 开发者峰会上,我们 宣布 了 TensorFlow Probability (TFP) 层。在演示中,我们展示了如何在极少的代码行中构建一个强大的回归模型。这里,我们将展示使用 TFP 层构建变分自编码器 (VAE) 的简单性。
TensorFlow Probability 层TFP 层提供…