-
-
Save danoneata/9927923 to your computer and use it in GitHub Desktop.
import numpy as np | |
import pdb | |
from sklearn.datasets import make_classification | |
from sklearn.mixture import GaussianMixture as GMM | |
def fisher_vector(xx, gmm): | |
"""Computes the Fisher vector on a set of descriptors. | |
Parameters | |
---------- | |
xx: array_like, shape (N, D) or (D, ) | |
The set of descriptors | |
gmm: instance of sklearn mixture.GMM object | |
Gauassian mixture model of the descriptors. | |
Returns | |
------- | |
fv: array_like, shape (K + 2 * D * K, ) | |
Fisher vector (derivatives with respect to the mixing weights, means | |
and variances) of the given descriptors. | |
Reference | |
--------- | |
J. Krapac, J. Verbeek, F. Jurie. Modeling Spatial Layout with Fisher | |
Vectors for Image Categorization. In ICCV, 2011. | |
http://hal.inria.fr/docs/00/61/94/03/PDF/final.r1.pdf | |
""" | |
xx = np.atleast_2d(xx) | |
N = xx.shape[0] | |
# Compute posterior probabilities. | |
Q = gmm.predict_proba(xx) # NxK | |
# Compute the sufficient statistics of descriptors. | |
Q_sum = np.sum(Q, 0)[:, np.newaxis] / N | |
Q_xx = np.dot(Q.T, xx) / N | |
Q_xx_2 = np.dot(Q.T, xx ** 2) / N | |
# Compute derivatives with respect to mixing weights, means and variances. | |
d_pi = Q_sum.squeeze() - gmm.weights_ | |
d_mu = Q_xx - Q_sum * gmm.means_ | |
d_sigma = ( | |
- Q_xx_2 | |
- Q_sum * gmm.means_ ** 2 | |
+ Q_sum * gmm.covariances_ | |
+ 2 * Q_xx * gmm.means_) | |
# Merge derivatives into a vector. | |
return np.hstack((d_pi, d_mu.flatten(), d_sigma.flatten())) | |
def main(): | |
# Short demo. | |
K = 64 | |
N = 1000 | |
xx, _ = make_classification(n_samples=N) | |
xx_tr, xx_te = xx[: -100], xx[-100: ] | |
gmm = GMM(n_components=K, covariance_type='diag') | |
gmm.fit(xx_tr) | |
fv = fisher_vector(xx_te, gmm) | |
pdb.set_trace() | |
if __name__ == '__main__': | |
main() |
@sobhanhemati Equations (16–18) from (Sanchez et al., 2013) provide the Fisher vectors that include the analytical approximation; hence, you can modify the computation of d_pi
, d_mu
, d_sigma
in the gist above as follows:
# at line 43
s = np.sqrt(gmm.weights_)[:, np.newaxis]
d_pi = (Q_sum.squeeze() - gmm.weights_) / s.squeeze()
d_mu = (Q_xx - Q_sum * gmm.means_) * np.sqrt(gmm.covariances_) ** -1 / s
d_sigma = - (
- Q_xx_2
- Q_sum * gmm.means_ ** 2
+ Q_sum * gmm.covariances_
+ 2 * Q_xx * gmm.means_) / (s * np.sqrt(2))
Note that I haven't tested this implementation, so you might want to double check it. And I would suggest to try both methods for estimating the diagonal Fisher information matrix and see which one works better for you — Sanchez et al. mention in their paper:
Note that we do not claim that this difference is significant nor that the closed-form approximation is superior to the empirical one in general.
Finally, do not forget to L2 and power normalise the Fisher vectors — these transformations yield much more substantial improvements (about 6-7% points each) than the choice of the approximation for the Fisher information matrix (see Table 1 from Sanchez et al.).
Thank you so much for the comprehensive answer.
I really appreciate that.
Hai I'm beginner so i don't know working of fisher vector encoding. Please help to understand this
Thank you for clarification.
Do you have any implementation of the analytical diagonal approximation so that I can add that the current implementation?
It seems that analytical diagonal approximation works about 1 percent better :))
Thank you in advance