我正在尝试使用tf.keras重构一些强化学习方法。对于我用于值函数和预处理器的前馈网络,keras模型似乎工作得很好,但是在尝试使用keras实现某些概率模型(例如策略)时,我很费劲。特别是,将tfp.distributions和tfp.bijector与tf.keras.Model结合使用对我来说非常不直观,我所看到的所有示例(例如[1、2、3])都过于简单或依赖在我看来,“骇客”似乎破坏了使用keras模型的许多好处(例如,从模型本身隐藏输入处理,会话和数字评估的能力)。
假设我要实现一个潜在空间策略,如[4]中所述,该策略使用RealNVP流将以状态为条件的高斯样本转换为动作。该策略需要支持至少两个操作: 1.对动作Y进行采样,使得 Y = g(X | S) X〜普通(0,1), 其中g是[4,5]中描述的RealNVP转换,而S是条件变量(例如RL情况下的状态观察)。 2.计算采样的Y的对数概率。
一个简单的实现可能看起来像这样:
class LearnableConditionalRealNVP(object):
def __init__(self, input_shape, output_shape):
self._input_shape = input_shape
self._output_size = np.prod(output_shape)
conditions = tf.keras.layers.Input(shape=input_shape)
batch_size = tf.keras.layers.Lambda(
lambda x: tf.shape(x)[0])(conditions)
def samples_and_log_probs_fn(inputs):
conditions, batch_size = inputs
base_distribution = tfp.distributions.MultivariateNormalDiag(
loc=tf.zeros(output_shape),
scale_diag=tf.ones(output_shape))
real_nvp_bijector = tfp.bijectors.RealNVP(
num_masked=self._output_size // 2,
shift_and_log_scale_fn=conditioned_real_nvp_template(
hidden_layers=(128, 128),
activation=tf.nn.relu),
name='real_nvp')
distribution = (
tfp.distributions.ConditionalTransformedDistribution(
distribution=base_distribution,
bijector=real_nvp_bijector))
samples = distribution.sample(batch_size)
log_probs = distribution.log_prob(samples)
return [samples, tf.reshape(log_probs, (-1, 1))]
samples, log_probs = tf.keras.layers.Lambda(
samples_and_log_probs_fn)([conditions, batch_size])
self.samples_and_log_probs_model = tf.keras.Model(
conditions, [samples, log_probs])
def samples_and_log_probs(self, conditions):
return self.samples_and_log_probs_model(conditions)
def samples_and_log_probs_np(self, conditions):
return self.samples_and_log_probs_model.predict(conditions)
其中conditioned_real_nvp_template
创建一个前馈网络,该网络沿最后一个轴连接潜在样本和条件值,并将它们用作输入。完整的示例可以在这里找到:https://gist.github.com/hartikainen/17ac2ec102032e986cb4d31e225f592a
这种处理发行方式对我有两个主要好处。首先,我不必手动处理参数的重用。我可以在代码中多次调用samples_and_log_probs
,它会自动重用模型的参数。其次,如果我想获得数字输出,则无需了解会话。将中间层包装到自己的模型中,使我可以调用用于处理会话的预测方法。
在进一步扩展示例时会出现问题。假设我想修改LearnableConditionalRealNVP
,以便可以提供潜在样本x作为输入,而不是在distribution.sample()
中调用samples_and_log_probs_fn
,它将返回{{1} }。或者,也许我想将样本和log_probs从模型中分离出来。这将需要我将distribution.forward(x)
拆分为两个单独的lambda函数,但是如果我想共享RealNVP双射器的参数,这样做并不是一件容易的事(因为我无法将双射器作为输入/输出传递给/来自keras层。
我尝试通过将samples_and_log_probs_fn
的{{1}}子类化来解决这些问题,但是我的所有尝试都导致实现混乱,主要是由于输入和输出的变化。具体来说,我无法为模型创建LearnableConditionalRealNVP
方法,以至于该模型将保留与tf.keras.Model
一起使用的功能,因此我不得不在{{1 }}-方法。这些都不让人感到可怕,但是它们确实增加了使用keras模型的开销,以至于我可以更轻松地在普通的tensorflow中实现这些类型的事情,并手动处理会话,numpy输出等。
我的问题是:
call
,则会中断,因为图形未连接。编辑:发布此内容并使用上述实现进行一些其他测试后,我注意到该模型毕竟是不可训练的,因为RealNVP bijector的变量是在keras lambda层中创建的。这表明我完全不能将构建这些模型的功能方法用于这些类型的模型。
[2] https://blog.keras.io/building-autoencoders-in-keras.html