Ускорение матричной инверсии - PullRequest
0 голосов
/ 19 ноября 2018

Мне нужно запустить этот код тысячи раз подряд:

def update_posterior(y, x, prior_mu, prior_V, prior_a, prior_b):

    # Building blocks used to keep following calculation cleaner
    prior_cov_inverse = np.linalg.inv(prior_V)
    x_transpose = x.transpose()
    n = len(y)
    residuals = y - np.dot(x, prior_mu.transpose())

    # Calculation of posterior parameters
    V_posterior = np.linalg.inv((prior_cov_inverse + x_transpose * x))
    mu_posterior = (V_posterior * (prior_cov_inverse * prior_mu.transpose() + x_transpose * y)).transpose()
    a_posterior = prior_a + n/2
    b_posterior = np.asscalar(prior_b + (residuals.transpose() * np.linalg.inv((np.identity(n) + x * prior_V * x_transpose)) * residuals)/2)

    return mu_posterior, V_posterior, a_posterior, b_posterior

То, как он работает, заключается в том, что вывод функции возвращается в него, и mu_posterior становится prior_mu,V_posterior становится prior_V, a_posterior становится prior_a, а b_posterior становится prior_b для следующего вызова.Y и x различны в каждом вызове.

Эта функция ужасно медленная - мне требуется около 8 секунд для запуска.Это из-за масштаба.У меня ~ 5000 параметров, поэтому prior_mu равно (1, 5000), prior_V равно (5000,5000) и симметрично positive-definite, а prior_a и prior_b - скаляры.у - скаляр, а х - (1, 5000).

Ниже приводится разбивка по времени на строку:

3.75s: prior_cov_inverse = np.linalg.inv(prior_V)
3.86s: V_posterior = np.linalg.inv((prior_cov_inverse + x_transpose * x))
0.13s: b_posterior = np.asscalar(prior_b + (residuals.transpose() * np.linalg.inv((np.identity(n) + x * prior_V * x_transpose)) * residuals)/2)

Есть идеи, как мне это ускорить?Я пытался использовать разложение Холецкого, но оно еще медленнее ?!Я предполагаю, что есть более эффективный способ реализовать декомпозицию Холецкого в Python.

prior_cov_inverse2 = np.linalg.inv(np.linalg.cholesky(prior_V))
prior_cov_inverse2 = np.dot(prior_cov_inverse2.transpose(), prior_cov_inverse2)

РЕДАКТИРОВАТЬ: Вот несколько примеров данных, чтобы проиллюстрировать проблему ....

import numpy as np

prior_mu = np.asmatrix(np.full((1, 5040), 5))
prior_V = np.diagflat(np.asmatrix(np.full((1, 5040), 30))) #usually not diagonal, but always symmetric positive definitive
a = 2
b = 2
y = np.asmatrix([10])
x = np.asmatrix(np.concatenate(([1], np.zeros(5039))))

print(update_posterior(y, x, prior_mu, prior_V, a, b))

РЕДАКТИРОВАТЬII:

Мне удалось снизить это значение с ~ 8 с / прогон до ~ 1,4 с / прогон, убрав инверсии матриц в пользу решений, а также используя формулу Шермана Моррисона.Вот мой текущий код.Если у кого-то есть идеи, как его ускорить, поделитесь!:)

def update_posterior(y, x, prior_mu, prior_V, prior_a, prior_b, I):

    # Building blocks used to keep following calculation cleaner
    x_transpose = x.transpose()
    n = len(y)
    residuals = y - np.dot(x, prior_mu.transpose())

    # Calculation of posterior parameters
    # Below is equivalent to np.linalg.inv(prior_V_inverse + np.dot(x_transpose, x)) but significantly faster    
    V_posterior = prior_V - np.true_divide(np.linalg.multi_dot((prior_V, x_transpose, x, prior_V)), 1 + np.matmul(np.matmul(x, prior_V), x_transpose))

    # Below is equivalent to mu_posterior = np.dot(V_posterior, (np.matmul(prior_V_inverse, prior_mu.transpose()) + np.matmul(x_transpose, y))).transpose() but significantly faster
    mu_posterior = np.dot(V_posterior, np.linalg.solve(prior_V, prior_mu.transpose()) + np.matmul(x_transpose, y)).transpose()
    a_posterior = prior_a + n/2
    b_posterior = np.asscalar(prior_b + (np.matmul(np.matmul(residuals.transpose(), np.linalg.inv((np.identity(n) + np.matmul(np.matmul(x, prior_V), x_transpose)))), residuals))/2)

    return mu_posterior, V_posterior, a_posterior, b_posterior

1 Ответ

0 голосов
/ 19 ноября 2018

С точки зрения стабильности почти всегда лучше писать solve(A, unit_matrix) вместо inv(A).Это не поможет с производительностью.

Производительность линейной алгебры здесь почти наверняка фиксируется базовой библиотекой LAPACK.Фондовая ATLAS, вероятно, медленнее, OpenBLAS или MKL лучше, иногда намного лучше.

Тем не менее, я почти уверен, что главное улучшение действительно алгоритмическое.Во-первых, для PSD-матриц Cholecky (cholesky / cho_solve) должен быть лучше.Во-вторых, вы, кажется, делаете обновление первого ранга (x.T @x), и , который обычно может быть реализован в N**2 операциях через некоторый вариант формулы Шермана-Моррисона вместо N**3для прямой инверсии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...