chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import numpy as np
|
||||
from prml.dimreduction.pca import PCA
|
||||
|
||||
|
||||
class BayesianPCA(PCA):
|
||||
|
||||
def fit(self, X, iter_max=100, initial="random"):
|
||||
"""
|
||||
empirical bayes estimation of pca parameters
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : (sample_size, n_features) ndarray
|
||||
input data
|
||||
iter_max : int
|
||||
maximum number of em steps
|
||||
|
||||
Returns
|
||||
-------
|
||||
mean : (n_features,) ndarray
|
||||
sample mean fo the input data
|
||||
W : (n_features, n_components) ndarray
|
||||
projection matrix
|
||||
var : float
|
||||
variance of observation noise
|
||||
"""
|
||||
initial_list = ["random", "eigen"]
|
||||
self.mean = np.mean(X, axis=0)
|
||||
self.I = np.eye(self.n_components)
|
||||
if initial not in initial_list:
|
||||
print("availabel initializations are {}".format(initial_list))
|
||||
if initial == "random":
|
||||
self.W = np.eye(np.size(X, 1), self.n_components)
|
||||
self.var = 1.
|
||||
elif initial == "eigen":
|
||||
self.eigen(X)
|
||||
self.alpha = len(self.mean) / np.sum(self.W ** 2, axis=0).clip(min=1e-10)
|
||||
for i in range(iter_max):
|
||||
W = np.copy(self.W)
|
||||
stats = self._expectation(X - self.mean)
|
||||
self._maximization(X - self.mean, *stats)
|
||||
self.alpha = len(self.mean) / np.sum(self.W ** 2, axis=0).clip(min=1e-10)
|
||||
if np.allclose(W, self.W):
|
||||
break
|
||||
self.n_iter = i + 1
|
||||
|
||||
def _maximization(self, X, Ez, Ezz):
|
||||
self.W = X.T @ Ez @ np.linalg.inv(np.sum(Ezz, axis=0) + self.var * np.diag(self.alpha))
|
||||
self.var = np.mean(
|
||||
np.mean(X ** 2, axis=-1)
|
||||
- 2 * np.mean(Ez @ self.W.T * X, axis=-1)
|
||||
+ np.trace((Ezz @ self.W.T @ self.W).T) / len(self.mean))
|
||||
|
||||
def maximize(self, D, Ez, Ezz):
|
||||
self.W = D.T.dot(Ez).dot(np.linalg.inv(np.sum(Ezz, axis=0) + self.var * np.diag(self.alpha)))
|
||||
self.var = np.mean(
|
||||
np.mean(D ** 2, axis=-1)
|
||||
- 2 * np.mean(Ez.dot(self.W.T) * D, axis=-1)
|
||||
+ np.trace(Ezz.dot(self.W.T).dot(self.W).T) / self.ndim)
|
||||
Reference in New Issue
Block a user