chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
class RelevanceVectorClassifier(object):
|
||||
|
||||
def __init__(self, kernel, alpha=1.):
|
||||
"""
|
||||
construct relevance vector classifier
|
||||
|
||||
Parameters
|
||||
----------
|
||||
kernel : Kernel
|
||||
kernel function to compute components of feature vectors
|
||||
alpha : float
|
||||
initial precision of prior weight distribution
|
||||
"""
|
||||
self.kernel = kernel
|
||||
self.alpha = alpha
|
||||
|
||||
def _sigmoid(self, a):
|
||||
return np.tanh(a * 0.5) * 0.5 + 0.5
|
||||
|
||||
def _map_estimate(self, X, t, w, n_iter=10):
|
||||
for _ in range(n_iter):
|
||||
y = self._sigmoid(X @ w)
|
||||
g = X.T @ (y - t) + self.alpha * w
|
||||
H = (X.T * y * (1 - y)) @ X + np.diag(self.alpha)
|
||||
w -= np.linalg.solve(H, g)
|
||||
return w, np.linalg.inv(H)
|
||||
|
||||
def fit(self, X, t, iter_max=100):
|
||||
"""
|
||||
maximize evidence with respect ot hyperparameter
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : (sample_size, n_features) ndarray
|
||||
input
|
||||
t : (sample_size,) ndarray
|
||||
corresponding target
|
||||
iter_max : int
|
||||
maximum number of iterations
|
||||
|
||||
Attributes
|
||||
----------
|
||||
X : (N, n_features) ndarray
|
||||
relevance vector
|
||||
t : (N,) ndarray
|
||||
corresponding target
|
||||
alpha : (N,) ndarray
|
||||
hyperparameter for each weight or training sample
|
||||
cov : (N, N) ndarray
|
||||
covariance matrix of weight
|
||||
mean : (N,) ndarray
|
||||
mean of each weight
|
||||
"""
|
||||
if X.ndim == 1:
|
||||
X = X[:, None]
|
||||
assert X.ndim == 2
|
||||
assert t.ndim == 1
|
||||
Phi = self.kernel(X, X)
|
||||
N = len(t)
|
||||
self.alpha = np.zeros(N) + self.alpha
|
||||
mean = np.zeros(N)
|
||||
for _ in range(iter_max):
|
||||
param = np.copy(self.alpha)
|
||||
mean, cov = self._map_estimate(Phi, t, mean, 10)
|
||||
gamma = 1 - self.alpha * np.diag(cov)
|
||||
self.alpha = gamma / np.square(mean)
|
||||
np.clip(self.alpha, 0, 1e10, out=self.alpha)
|
||||
if np.allclose(param, self.alpha):
|
||||
break
|
||||
mask = self.alpha < 1e8
|
||||
self.X = X[mask]
|
||||
self.t = t[mask]
|
||||
self.alpha = self.alpha[mask]
|
||||
Phi = self.kernel(self.X, self.X)
|
||||
mean = mean[mask]
|
||||
self.mean, self.covariance = self._map_estimate(Phi, self.t, mean, 100)
|
||||
|
||||
def predict(self, X):
|
||||
"""
|
||||
predict class label
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : (sample_size, n_features)
|
||||
input
|
||||
|
||||
Returns
|
||||
-------
|
||||
label : (sample_size,) ndarray
|
||||
predicted label
|
||||
"""
|
||||
if X.ndim == 1:
|
||||
X = X[:, None]
|
||||
assert X.ndim == 2
|
||||
phi = self.kernel(X, self.X)
|
||||
label = (phi @ self.mean > 0).astype(np.int)
|
||||
return label
|
||||
|
||||
def predict_proba(self, X):
|
||||
"""
|
||||
probability of input belonging class one
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : (sample_size, n_features) ndarray
|
||||
input
|
||||
|
||||
Returns
|
||||
-------
|
||||
proba : (sample_size,) ndarray
|
||||
probability of predictive distribution p(C1|x)
|
||||
"""
|
||||
if X.ndim == 1:
|
||||
X = X[:, None]
|
||||
assert X.ndim == 2
|
||||
phi = self.kernel(X, self.X)
|
||||
mu_a = phi @ self.mean
|
||||
var_a = np.sum(phi @ self.covariance * phi, axis=1)
|
||||
return self._sigmoid(mu_a / np.sqrt(1 + np.pi * var_a / 8))
|
||||
Reference in New Issue
Block a user