我无法使贝叶斯线性回归与Tensorflow概率一起使用。这是我的代码:
func showOptions(sender: AnyObject) {
let alertController = UIAlertController.init(title: "Choose Option", message: "", preferredStyle: .actionSheet)
let cameraAction = UIAlertAction.init(title: "Camera", style: .default) { (action) in
self.selectImageFrom(.camera)
}
alertController.addAction(cameraAction)
let photoLibraryAction = UIAlertAction.init(title: "Photo Library", style: .default) { (action) in
self.selectImageFrom(.photoLibrary)
}
alertController.addAction(photoLibraryAction)
let cancelAction = UIAlertAction.init(title: "Cancel", style: .cancel) { (action) in
}
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
有什么想法吗?
答案 0 :(得分:2)
令人惊讶的是,如此简单的问题有多难!原始HMC在设置推理时可能对相对较小的细节非常敏感。像Stan这样的系统会尝试通过为您做很多调整来解决这个问题,但是到目前为止,TFP的自动调整已变得更加基础。
我发现一些更改似乎在这里使推理工作正常。简而言之,它们是:
DualAveragingStepSizeAdaptation
代替SimpleStepSizeAdaptation
。第一个技巧是使用TransformedTransitionKernel
重新参数化,以便scale参数位于不受限制的空间中。例如,我们可以使用Exp
双射器来定义对数比例参数:
tfb = tfp.bijectors
kernel = tfp.mcmc.TransformedTransitionKernel(
inner_kernel=kernel,
bijector=[tfb.Identity(), tfb.Identity(), tfb.Exp()]
)
这可以确保推理仅考虑刻度的正值,因此不必拒绝使刻度小于零的所有动作。当我这样做时,尽管混合效果仍然不佳,但接受率却上升了。
第二个更改是对三个变量使用非均匀步长(这等效于对角线预处理)。看起来此模型中的后验条件不佳:二十个数据点确定斜率 much 比确定截距或比例更精确。 TFP中的“步长调整”只是找到一个步长,以使指定百分比的样本可以被接受,这通常由后验中受最严格约束的分量控制:如果其他分量具有更宽的后验,则小步长将阻止他们从混合。估算合理步长的一种方法是,使用因变量推断的标准偏差和正态替代后验因子:
surrogate_posterior = tfp.experimental.vi.build_factored_surrogate_posterior(
event_shape=[[], [], []],
constraining_bijectors=[None, None, tfb.Exp()])
losses = tfp.vi.fit_surrogate_posterior(
target_log_prob_fn, surrogate_posterior,
optimizer=tf.optimizers.Adam(
learning_rate=0.1,
# Decay second-moment estimates to aid optimizing scale parameters.
beta_2=0.9),
num_steps=1000)
approximate_posterior_stddevs = [np.std(x) for x in surrogate_posterior.sample(50)]
另一个通用技巧是增加越级步数。思考HMC的一种方法是,在跨越式集成器中,它类似于具有动量的优化器,但是每次停止接受/拒绝时,它都会失去动量(通过重新采样)。因此,在极端情况下,我们每一个步骤(num_leapfrog_steps=1
,即Langevin动力学)都没有动量,增加跳越步数往往会提高导航棘手几何形状的能力,类似于动量改进优化器。我没有严格调整任何内容,但是在此设置num_leapfrog_steps=16
而不是3似乎很有帮助。
这是我的代码修改版,其中包含了这些技巧。在大多数执行中,它似乎混合得很好(尽管我确定它并不完美):
!pip install tensorflow==2.0.0-rc1
!pip install tensorflow-probability==0.8.0rc0
import numpy as np
import tensorflow as tf
import tensorflow_probability as tfp
tfd = tfp.distributions
tfb = tfp.bijectors
N = 20
std = 1
m = np.random.normal(0, scale=5, size=1).astype(np.float32)
b = np.random.normal(0, scale=5, size=1).astype(np.float32)
x = np.linspace(0, 100, N).astype(np.float32)
y = m*x+b+ np.random.normal(loc=0, scale=std, size=N).astype(np.float32)
num_results = 1000
num_burnin_steps = 500
def joint_log_prob(x, y, m, b, std):
rv_m = tfd.Normal(loc=0, scale=5)
rv_b = tfd.Normal(loc=0, scale=5)
rv_std = tfd.HalfCauchy(0., scale=1.)
y_mu = m*x+b
rv_y = tfd.Normal(loc=y_mu, scale=rv_std[..., None])
return (rv_m.log_prob(m) + rv_b.log_prob(b)
+ rv_std.log_prob(std)
+ tf.reduce_sum(rv_y.log_prob(y)))
# Define a closure over our joint_log_prob.
def target_log_prob_fn(m, b, std):
return joint_log_prob(x, y, m, b, std)
# Run variational inference to initialize per-variable step sizes.
surrogate_posterior = tfp.experimental.vi.build_factored_surrogate_posterior(
event_shape=[[], [], []],
constraining_bijectors=[None, None, tfb.Exp()])
losses = tfp.vi.fit_surrogate_posterior(
target_log_prob_fn,
surrogate_posterior,
optimizer=tf.optimizers.Adam(
learning_rate=0.1,
# Decay second-moment estimates to aid optimizing scale parameters.
beta_2=0.9),
num_steps=1000)
approximate_posterior_stddevs = [np.std(z) for z in surrogate_posterior.sample(50)]
@tf.function(autograph=False)
def do_sampling():
kernel=tfp.mcmc.HamiltonianMonteCarlo(
target_log_prob_fn=target_log_prob_fn,
step_size=approximate_posterior_stddevs,
num_leapfrog_steps=16)
kernel = tfp.mcmc.TransformedTransitionKernel(
inner_kernel=kernel,
bijector=[tfb.Identity(),
tfb.Identity(),
tfb.Exp()]
)
kernel = tfp.mcmc.DualAveragingStepSizeAdaptation(
inner_kernel=kernel,
num_adaptation_steps=int(num_burnin_steps * 0.8))
return tfp.mcmc.sample_chain(
num_results=num_results,
num_burnin_steps=num_burnin_steps,
current_state=[
0.01 * tf.ones([], name='init_m', dtype=tf.float32),
0.01 * tf.ones([], name='init_b', dtype=tf.float32),
1. * tf.ones([], name='init_std', dtype=tf.float32)
],
kernel=kernel,
trace_fn=lambda _, pkr: [pkr.inner_results.inner_results.accepted_results.step_size,
pkr.inner_results.inner_results.log_accept_ratio])
samples, [step_size, log_accept_ratio] = do_sampling()
m_posterior, b_posterior, std_posterior = samples
p_accept = tf.reduce_mean(tf.exp(tf.minimum(log_accept_ratio, 0.)))
print('Acceptance rate: {}'.format(p_accept))
n_v = len(samples)
true_values = [m, b, std]
plt.figure(figsize=(12, 12))
plt.title('Visualizing trace and posterior distributions')
for i, (sample, true_value) in enumerate(zip(samples, true_values)):
plt.subplot(2*n_v, 2, 2*i+1)
plt.plot(sample)
plt.subplot(2*n_v, 2, 2*i+2)
plt.hist(sample.numpy())
plt.axvline(x=true_value)
答案 1 :(得分:1)
首先,提出的问题非常好-我很容易复制/粘贴您的代码并进行复制:)
第一个问题(与HMC一样)是step_size / num_leapfrog_steps设置。通过减小步长(〜.007)和减小越级步长(〜3),可以得到不错的接受率(〜.7)。通常,您希望将越级越步越小越好,因为每一次步阶都会导致昂贵的梯度计算。
下一个问题是,看起来这些链仍然没有很好地与这些参数混合。我怀疑这可能是居中与非居中(请参阅https://discourse.mc-stan.org/t/centered-vs-noncentered/1502)参数化的问题;您可能会更幸运地更改模型的参数化,尽管我对这个细节并不十分熟悉...
出于自动查找更好的步长参数的目的,您可以尝试使用2个步长自适应内核之一-SimpleStepSizeAdaptation或DualAveragingStepSizeAdaptation-可以将其包装在HMC内核实例周围。此外,您可以尝试使用NoUTurnSampler作为香草HMC的替代方法,以避免必须调整跳越步骤参数。对于一个简单的贝叶斯线性回归示例,所有这些都是相当沉重的,但是我想这个示例突出了正确实现MCMC的难度:)
希望这会有所帮助!随时通过tfprobability@tensorflow.org与我们联系以进行更多讨论-也许其他一些对参数化问题有更多了解的人也会加入。再次感谢您提出的重大问题。