变分自编码器(七):球面上的VAE(vMF-VAE)
在《变分自编码器(五):VAE + BN = 更好的VAE》中,我们讲到了NLP中训练VAE时常见的KL散度消失现象,并且提到了通过BN来使得KL散度项有一个正的下界,从而保证KL散度项不会消失。事实上,早在2018年的时候,就有类似思想的工作就被提出了,它们是通过在VAE中改用新的先验分布和后验分布,来使得KL散度项有一个正的下界。
该思路出现在2018年的两篇相近的论文中,分别是《Hyperspherical Variational Auto-Encoders》和《Spherical Latent Spaces for Stable Variational Autoencoders》,它们都是用定义在超球面的von Mises–Fisher(vMF)分布来构建先后验分布。某种程度上来说,该分布比我们常用的高斯分布还更简单和有趣~
KL散度消失 #
我们知道,VAE的训练目标是
其中第一项是重构项,第二项是KL散度项,在《变分自编码器(一):原来是这么一回事》中我们就说过,这两项某种意义上是“对抗”的,KL散度项的存在,会加大解码器利用编码信息的难度,如果KL散度项为0,那么说明解码器完全没有利用到编码器的信息。
在NLP中,输入和重构的对象是句子,为了保证效果,解码器一般用自回归模型。然而,自回归模型是非常强大的模型,强大到哪怕没有输入,也能完成训练(退化为无条件语言模型),而刚才我们说了,KL散度项会加大解码器利用编码信息的难度,所以解码器干脆弃之不用,这就出现了KL散度消失现象。
早期比较常见的应对方案是逐渐增加KL项的权重,以引导解码器去利用编码信息。现在比较流行的方案就是通过某些改动,直接让KL散度项有一个正的下界。将先后验分布换为vMF分布,就是这种方案的经典例子之一。
vMF分布 #
vMF分布是定义在
其中
这时候
从归一化因子
可以看到
至于参数
从vMF采样 #
对于vMF分布来说,需要解决的第一个难题是如何实现从它里边采样出具体的样本来。尤其是如果我们要将它应用到VAE中,那么这一步是至关重要的。
均匀分布 #
最简单是
特殊方向 #
接着,对于
此时概率密度正比于
那么(超球坐标的积分变换,请直接参考“维基百科”)
这个分解表明,从该vMF分布中采样,等价于先从概率密度正比于
设
所以我们主要研究从概率密度正比于
然而,笔者所不理解的是,大多数涉及到vMF分布的论文,都采用了1994年的论文《Simulation of the von mises fisher distribution》提出的基于beta分布的拒绝采样方案,整个采样流程还是颇为复杂的。但现在都2021年了,对于一维分布的采样,居然还需要拒绝采样这么低效的方案?
事实上,对于任意一维分布
import numpy as np
def sample_from_pw(size, kappa, dims, epsilon=1e-7):
x = np.arange(-1 + epsilon, 1, epsilon)
y = kappa * x + np.log(1 - x**2) * (dims - 3) / 2
y = np.cumsum(np.exp(y - y.max()))
y = y / y[-1]
return np.interp(np.random.random(size), y, x)
这里的实现中,计算量最大的是变量y
的计算,而一旦计算好之后,可以缓存下来,之后只需要执行最后一步来完成采样,其速度是非常快的。这样再怎么看,也比从beta分布中拒绝采样要简单方便吧。顺便说,实现上这里还用到了一个技巧,即先计算对数值,然后减去最大值,最后才算指数,这样可以防止溢出,哪怕
一般情形 #
现在我们已经实现了从
同样由于各向同性的原因,对于一般的
对于
vMF-VAE #
至此,我们可谓是已经完成了本篇文章最艰难的部分,剩下的构建vMF-VAE可谓是水到渠成了。vMF-VAE选用球面上的均匀分布(
简单起见,我们将
前面我们已经讨论过,vMF分布的均值方向跟
那么现在就剩下重构项了,我们需要用“重参数(Reparameterization)”来完成采样并保留梯度,在前面我们已经研究了vMF的采样过程,所以也不难实现,综合的流程为:
这里的重构loss以MSE为例,如果是句子重构,那么换用交叉熵就好。其中
此外,从该流程我们也可以看出,除了“简单起见”之外,不将
参考实现 #
vMF-VAE的实现难度主要是重参数部分,也就还是从vMF分布中采样,而关键之处就是np.interp
的函数,因此不容易转换为纯tf的实现。当然,如果是torch或者tf2这种动态图框架,直接跟numpy的代码混合使用也无妨,但这里还是想构造一种比较通用的方案。
其实也不难,由于batch_size
个采样结果,所以我们完全可以事先用numpy函数采样好足够多(几十万)个
def sampling(mu):
"""vMF分布重参数操作
"""
dims = K.int_shape(mu)[-1]
# 预先计算一批w
epsilon = 1e-7
x = np.arange(-1 + epsilon, 1, epsilon)
y = kappa * x + np.log(1 - x**2) * (dims - 3) / 2
y = np.cumsum(np.exp(y - y.max()))
y = y / y[-1]
W = K.constant(np.interp(np.random.random(10**6), y, x))
# 实时采样w
idxs = K.random_uniform(K.shape(mu[:, :1]), 0, 10**6, dtype='int32')
w = K.gather(W, idxs)
# 实时采样z
eps = K.random_normal(K.shape(mu))
nu = eps - K.sum(eps * mu, axis=1, keepdims=True) * mu
nu = K.l2_normalize(nu, axis=-1)
return w * mu + (1 - w**2)**0.5 * nu
一个基于MNIST的完整例子可见:
至于vMF-VAE用于NLP的例子,我们日后有机会再分享。本文主要还是以理论介绍和简单演示为主~
文章小结 #
本文介绍了基于vMF分布的VAE实现,其主要难度在于vMF分布的采样。总的来说,vMF分布建立在余弦相似度度量之上,在某些方面的性质更符合我们的直观认知,将其用于VAE中,能够使得KL散度项为一个常数,从而防止了KL散度消失现象,并且简化了VAE结构。
转载到请包括本文地址:https://spaces.ac.cn/archives/8404
更详细的转载事宜请参考:《科学空间FAQ》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧