Модифицированный BPMF в PyMC3 с использованием априорных значений LKJCorr: PositiveDefiniteError с использованием NUTS

Ранее я реализовал оригинальную модель байесовской вероятностной матричной факторизации (BPMF). в 1_. см. мой предыдущий вопрос для справки, источника данных и проблемы настраивать. Согласно ответу на этот вопрос от @twiecki, я реализовал вариант модели, используя LKJCorr априорных значений для матриц корреляции и однородных априорных значений для стандартных отклонений. В исходной модели ковариационные матрицы берутся из распределений Уишарта, но из-за текущих ограничений pymc3 выборка из распределения Уишарта невозможна. Этот ответ на слабо связанный вопрос дает краткое объяснение выбора LKJCorr априорных значений. Ниже представлена ​​новая модель.

import pymc3 as pm
import numpy as np
import theano.tensor as t


n, m = train.shape
dim = 10  # dimensionality
beta_0 = 1  # scaling factor for lambdas; unclear on its use
alpha = 2  # fixed precision for likelihood function
std = .05  # how much noise to use for model initialization

# We will use separate priors for sigma and correlation matrix.
# In order to convert the upper triangular correlation values to a
# complete correlation matrix, we need to construct an index matrix:
n_elem = dim * (dim - 1) / 2
tri_index = np.zeros([dim, dim], dtype=int)
tri_index[np.triu_indices(dim, k=1)] = np.arange(n_elem)
tri_index[np.triu_indices(dim, k=1)[::-1]] = np.arange(n_elem)

logging.info('building the BPMF model')
with pm.Model() as bpmf:
    # Specify user feature matrix
    sigma_u = pm.Uniform('sigma_u', shape=dim)
    corr_triangle_u = pm.LKJCorr(
        'corr_u', n=1, p=dim,
        testval=np.random.randn(n_elem) * std)

    corr_matrix_u = corr_triangle_u[tri_index]
    corr_matrix_u = t.fill_diagonal(corr_matrix_u, 1)
    cov_matrix_u = t.diag(sigma_u).dot(corr_matrix_u.dot(t.diag(sigma_u)))
    lambda_u = t.nlinalg.matrix_inverse(cov_matrix_u)

    mu_u = pm.Normal(
        'mu_u', mu=0, tau=beta_0 * lambda_u, shape=dim,
         testval=np.random.randn(dim) * std)
    U = pm.MvNormal(
        'U', mu=mu_u, tau=lambda_u,
        shape=(n, dim), testval=np.random.randn(n, dim) * std)

    # Specify item feature matrix
    sigma_v = pm.Uniform('sigma_v', shape=dim)
    corr_triangle_v = pm.LKJCorr(
        'corr_v', n=1, p=dim,
        testval=np.random.randn(n_elem) * std)

    corr_matrix_v = corr_triangle_v[tri_index]
    corr_matrix_v = t.fill_diagonal(corr_matrix_v, 1)
    cov_matrix_v = t.diag(sigma_v).dot(corr_matrix_v.dot(t.diag(sigma_v)))
    lambda_v = t.nlinalg.matrix_inverse(cov_matrix_v)

    mu_v = pm.Normal(
        'mu_v', mu=0, tau=beta_0 * lambda_v, shape=dim,
         testval=np.random.randn(dim) * std)
    V = pm.MvNormal(
        'V', mu=mu_v, tau=lambda_v,
        testval=np.random.randn(m, dim) * std)

    # Specify rating likelihood function
    R = pm.Normal(
        'R', mu=t.dot(U, V.T), tau=alpha * np.ones((n, m)),
        observed=train)

# `start` is the start dictionary obtained from running find_MAP for PMF.
# See the previous post for PMF code.
for key in bpmf.test_point:
    if key not in start:
        start[key] = bpmf.test_point[key]

with bpmf:
    step = pm.NUTS(scaling=start)

Цель этой повторной реализации состояла в том, чтобы создать модель, которую можно было бы оценить с помощью сэмплера NUTS. К сожалению, я все еще получаю ту же ошибку в последней строке:

PositiveDefiniteError: Scaling is not positive definite. Simple check failed. Diagonal contains negatives. Check indexes [   0    1    2    3    ...   1030 1031 1032 1033 1034   ]

Я сделал весь код для PMF, BPMF и этого модифицированного BPMF доступным в этой сути чтобы упростить повторение ошибки. Все, что вам нужно сделать, это загрузить данные (также указанные в сути).


person Mack    schedule 20.04.2015    source источник


Ответы (1)


Похоже, вы передаете полную матрицу точности в нормальное распределение:

mu_u = pm.Normal(
    'mu_u', mu=0, tau=beta_0 * lambda_u, shape=dim,
     testval=np.random.randn(dim) * std)

Я предполагаю, что вы хотите передать только диагональные значения:

mu_u = pm.Normal(
    'mu_u', mu=0, tau=beta_0 * t.diag(lambda_u), shape=dim,
     testval=np.random.randn(dim) * std)

Это изменится на mu_u и mu_v исправит это для вас?

person Kiudee    schedule 21.04.2015
comment
Да, это решает проблему. Я обновил суть соответственно. - person Mack; 21.04.2015