KL-Дивергенция двух GMM

У меня есть два GMM, которые я использовал для размещения двух разных наборов данных в одном пространстве, и я хотел бы рассчитать KL-расхождение между ними.

В настоящее время я использую GMM, определенные в sklearn ( http://scikit-learn.org/stable/modules/generated/sklearn.mixture.GMM.html) и реализацию SciPy KL-расхождения ( http://docs.scipy.org/doc/scipy-dev/reference/generated/scipy.stats.entropy.html)

Как бы я поступил так? Хочу ли я просто создать тонны случайных точек, получить их вероятности для каждой из двух моделей (назовите их P и Q) и затем использовать эти вероятности в качестве входных данных? Или есть еще какой-то канонический способ сделать это в среде SciPy/SKLearn?

2 ответа

Решение

Там нет закрытой формы для расхождения KL между GMM. Вы можете легко сделать Монте-Карло, хотя. Напомним, что KL(p||q) = \int p(x) log(p(x) / q(x)) dx = E_p[ log(p(x) / q(x)), Так:

def gmm_kl(gmm_p, gmm_q, n_samples=10**5):
    X = gmm_p.sample(n_samples)
    log_p_X, _ = gmm_p.score_samples(X)
    log_q_X, _ = gmm_q.score_samples(X)
    return log_p_X.mean() - log_q_X.mean()

(mean(log(p(x) / q(x))) = mean(log(p(x)) - log(q(x))) = mean(log(p(x))) - mean(log(q(x))) несколько дешевле в вычислительном отношении.)

Вы не хотите использовать scipy.stats.entropy; это для дискретных распределений.

Если вы хотите симметризованную и сглаженную дивергенцию Дженсена-Шеннона KL(p||(p+q)/2) + KL(q||(p+q)/2) вместо этого это довольно похоже:

def gmm_js(gmm_p, gmm_q, n_samples=10**5):
    X = gmm_p.sample(n_samples)
    log_p_X, _ = gmm_p.score_samples(X)
    log_q_X, _ = gmm_q.score_samples(X)
    log_mix_X = np.logaddexp(log_p_X, log_q_X)

    Y = gmm_q.sample(n_samples)
    log_p_Y, _ = gmm_p.score_samples(Y)
    log_q_Y, _ = gmm_q.score_samples(Y)
    log_mix_Y = np.logaddexp(log_p_Y, log_q_Y)

    return (log_p_X.mean() - (log_mix_X.mean() - np.log(2))
            + log_q_Y.mean() - (log_mix_Y.mean() - np.log(2))) / 2

(log_mix_X/log_mix_Y на самом деле лог двойной плотности смеси; вытаскивая это из средней операции, вы экономите некоторые провалы.)

Логика в ответе Даники верна. Пробовал со sklearn 1.3.0, но результат ненадежный (особенно функция оценки). Поэтому я написал код с помощью ChatGPT для расчета KL.

      import numpy as np
from scipy.stats import multivariate_normal
from sklearn.mixture import GaussianMixture


def gmm_pdf(x, weights, means, covariances):

    K = len(weights)
    pdf = 0.0
    
    for k in range(K):
        # Calculate the PDF for each Gaussian component
        component_pdf = weights[k] * multivariate_normal.pdf(x, mean=means[k], cov=covariances[k])
        pdf += component_pdf

    return pdf

def gmm_kl(gmm_p, gmm_q, n_samples=1e7):
    X = gmm_p.sample(n_samples)[0]
    p_X = (gmm_pdf(X, gmm_p.weights_, gmm_p.means_, gmm_p.covariances_))
    q_X = (gmm_pdf(X, gmm_q.weights_, gmm_q.means_, gmm_q.covariances_))
    return np.mean(np.log(p_X/q_X))
Другие вопросы по тегам