Probabilistic Principal Component Analysis
引
PPCA 通过高斯过程给出了普通PCA一个概率解释,这是很有意义的。论文还利用PPCA进行缺失数据等方面的处理(不过这方面主要归功于高斯过程吧)。
其中为观测变量,也就是样本,而是隐变量,将二者联系起来。另外,是噪声。
令是样本协方差矩阵,其中是样本均值。论文的主要工作,就是将的列空间和联系起来。
主要内容
假设,,二者独立。那么,容易知道在已知的情况下的条件概率为:
然后论文指出,通过其可求得的边际分布:
其中。这个证明,在贝叶斯优化中有提过,不过我发现,因为,是服从正态分布随机变量的线性组合,所以也服从正态分布,所以通过和也可以得到的分布。
其似然函数为:
将视为参数,我们可以得到其极大似然估计:
其中的列是的主特征向量,而的对角线元素为特征向量对应的特征值(为所有特征值的前个,否则将成为鞍点),是一个旋转矩阵。注意到,的列向量并不一定正交。
这部分的推导见附录。
同样的,我们可以推导出,在已知的情况下的条件分布:
其中
这个推导需要利用贝叶斯公式:
为什么要提及这个东西,因为可以引出一个很有趣的性质,注意到的均值为:
令,且假设,那么均值就成为:
实际上就是在主成分载荷向量上的正交投影,当然这里不要计较是否可逆。这就又将PPCA与普通的PCA联系在了一起。
EM算法求解
论文给出了的显式解(虽然有点地方不是很明白),也给出了如何利用EM算法来求解。
构造似然估计:
对求条件期望(条件概率为):
步是对上述求极大值,注意里面的是已知的(实际上,用来表述更加合适):
有更加简练的表述形式:
符号虽然多,但是推导并不麻烦,自己推导的时候并没有花多大工夫。
附录
极大似然估计
已知对数似然函数为:
先考察对的微分:
所以,要想取得极值,需满足:
论文说这个方程有三种解:
- ,0解,此时对数似然函数取得最小值(虽然我没看出来)。
- :
其解为:
其中。
- 第三种,也是最有趣的解,但是。假设,其中, 为对角矩阵,。通过一系列的变换(我没有把握能完成这部分证明,感觉好像是对的),可以得到:
于是,其中为的第j列,为的第j个对角线元素。因此,就成了的对应特征值的特征向量(注意到这个特征值是必须大于等于)。于是,有:
其中:
实际上就是
注意,上面的分析只能说明其为驻定解,事实上只说明了其为的特征向量,而没有限定是哪些特征向量。
将解代入对数似然函数可得():
其中是非零的个数。
上面的是蛮好证明的,注意中第2项和第4项和为,第3,5项构成。
对求极值,可得:
且是极大值,因为显然会导致。代入原式可得:
最大化上式等价于最小化下式:
注意到,上式只与被舍弃的的有关,又,再结合(18),可以知道最小的特征值一定是被舍弃的。但是论文说,应当是最小的个特征值作为被舍弃的(因为这些特征值必须在一块?)。
仔细想来,似然函数可以写成:
好吧,还是不知道该如何证明。
代码
"""
瞎写的,测试结果很诡异啊
"""
import numpy as np
class PPCA:
def __init__(self, data, q):
self.__data = np.array(data, dtype=float)
self.__n, self.__p = data.shape
self.__mean = np.mean(self.data, 0)
self.q = q
assert q < self.__p, "Invalid q"
@property
def data(self):
return self.__data
@property
def n(self):
return self.__n
@property
def p(self):
return self.__p
def explicit(self):
data = self.data - self.__mean
S = data.T @ data / self.n
value, vector = np.linalg.eig(S)
U = vector[:, :self.q]
sigma = np.mean(value[self.q:])
newvalue = value[:self.q] - sigma
return U * newvalue
def EM(self):
data = self.data - self.__mean
S = data.T @ data / self.n
W_old = np.random.randn(self.p, self.q)
sigma = np.random.randn()
count = 0
while True:
count += 1
M = W_old.T @ W_old + sigma
M_inv = np.linalg.inv(M)
W_new = S @ W_old @ np.linalg.inv(sigma + M_inv @ W_old.T @ S @ W_old)
sigma_new = np.trace(S - S @ W_old @ M_inv @ W_new.T) / self.p
if np.sum(np.abs(W_new - W_old)) / np.sum(np.abs(W_old)) < 1e-13 and \
np.abs(sigma_new - sigma) < 1e-13:
return W_new
else:
W_old = W_new
sigma = sigma_new
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix