Как и предполагалось, я попробовал решение Cython. Поскольку я никогда раньше не использовал Cython, я завершу шаги, которые я использовал для реализации решения Cython.
Сначала я установил Cython. Затем я написал файл с именем fastllf.pyx
, который содержал следующий код Cython:
#cython: boundscheck=False, wraparound=False, nonecheck=False
from libc.math cimport exp, sqrt, pi, log, isnan
cdef double SQ_PI = sqrt(2*pi)
cdef double norm_pdf(double x, double loc, double scale):
return (1/(SQ_PI*scale))*exp(-(0.5)*((x - loc)**2)/(scale**2))
cdef double llf_c(double[:, :] X, double[:] params):
cdef double logll = 0
cdef int N = X.shape[0]
cdef int K = X.shape[1]
cdef int i, j
cdef double m, sd
for i in range(N):
for j in range(K):
if not isnan(X[i, j]):
m = params[j]
sd = exp(params[j+K])
logll += log(norm_pdf(X[i, j], m, sd))
return -logll
def llf(double[:, :] X, double[:] params):
return llf_c(X, params)
Затем я создал setup.py
файл, который включал следующее:
from distutils.core import setup
from Cython.Build import cythonize
setup(name="fastllf", ext_modules=cythonize('fastllf.pyx'))
Затем я скомпилировал код Cython, используя следующую команду в терминале.
$ python3 setup.py build_ext --inplace
Наконец, я сравнил результаты между моей старой, чистой реализацией Python (слегка модифицированной для использования массивов вместо фреймов данных) и реализацией Cython.
import numpy as np
from scipy.stats import norm
import time
from fastllf import llf as cython_llf
# simulating data
means = np.array([10, 20, 30])
cov = np.diag([1, 4, 10])
N = 100000
np.random.seed(10)
X = np.random.multivariate_normal(mean=means, cov=cov, size=N)
X[np.random.choice([True, False], size=(N, 3), p=[0.3, 0.7])] = np.nan
def norm_pdf(x, loc, scale):
return (1/(np.sqrt(2*np.pi)*scale))*np.exp(-(0.5)*((x-loc)**2)/(scale**2))
def llf(X, params):
logll = 0
N = X.shape[0]
K = X.shape[1]
for i in range(N):
for j in range(K):
if not np.isnan(X[i, j]):
m = params[j]
sd = np.exp(params[j+K])
logll += np.log(norm_pdf(X[i, j], loc=m, scale=sd))
return -logll
def timeit(fun, *args):
start = time.time()
rslt = fun(*args)
end = time.time()
print(rslt)
print(end - start)
params = np.array([1.,1,1,1,1,1])
timeit(llf, X, params)
timeit(cython_llf, X, params)
И я получил следующие результаты:
Python Value: 6570173.7597125955
Python Time: 1.9558300971984863 seconds
Cython Value: 6570173.7597125955
Cython Time: 0.016242027282714844 seconds
Это делает оптимизацию по максимальному правдоподобию гораздо более осуществимой, особенно когда моя проблема усложняется. Единственная проблема заключается в том, что мне нужно найти математические и статистические функции, которые мне нужны, чтобы написать llf
функцию на Cython, или мне нужно написать свою собственную, как я делал для обычного pdf выше.
Будем благодарны за любые комментарии по поводу моей реализации.