2017-02-21 1 views
1

Lorsque je tente de compiler le code ci-dessous, je reçois l'erreurCython lâcher gil quand j'ai besoin taille du tableau numpy

return sign_match.sum()/y_true.shape[0] 
            ^

Conversion en Python objet non autorisé sans gil

Existe-t-il un moyen facile de surmonter cela? La solution la plus viable que je peux penser est de passer dans la longueur des tableaux comme un autre argument. J'utilise Python 3.3.5.

cimport cython 
cimport numpy as np 


# Returns negative mean-squared error 
cdef double negative_mse(np.ndarray[double, ndim=1] y_true, 
         np.ndarray[double, ndim=1] y_pred) nogil: 
    cdef np.ndarray[double, ndim=1] err 
    err = y_true - y_pred 

    return -(err * err).sum()/y_true.shape[0] 

Répondre

3

y_true et y_pred sont des tableaux, et donc des objets Python. Par conséquent toute opération les utilisant nécessitera le gil, et pas seulement la forme.

Essayez de compiler sans le nogil, et regardez le -a html. Quelles lignes sont jaune foncé, avec de nombreuses références d'objets Python?

+11:  return -(err * err).sum()/y_true.shape[0] 
    __pyx_t_7 = PyNumber_Multiply(((PyObject *)__pyx_v_err), ((PyObject *)__pyx_v_err)); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 11, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_7); 
    __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_7, __pyx_n_s_sum); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 11, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_8); 
    __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; 
    .... 

Juste une partie du code C développé pour votre fichier. Voir tous les appels Pyx... Ils ont tous besoin du gil.

http://docs.cython.org/en/latest/src/userguide/memoryviews.html montre que vous pouvez utiliser nogil sur une vue mémoire d'un tableau numpy.

Dessin du guide memoryview j'ai écrit cette autre fonction

cpdef double neg_mse_view(double[:] y_true, double[:] y_pred): 
    cdef double x, res 
    cdef int I 
    I = y_true.shape[0] 
    res = 0 
    for i in range(I): 
     x = y_true[i]-y_pred[i] 
     res += -(x*x) 
    res = res/I 
    return res 

Cela peut être appelé de la même manière. Ces timings montrent une accélération de 2x. nogil fonctionne, mais ne fait pas de différence.

In [10]: a=np.arange(1000000.) 
In [11]: timeit negmse.negative_mse(a,a-10) 
10 loops, best of 3: 16.9 ms per loop 
In [12]: timeit negmse.neg_mse_view(a,a-10) 
100 loops, best of 3: 7.17 ms per loop 
In [13]: timeit negmse.neg_mse_nogil(a,a-10) 
100 loops, best of 3: 7.19 ms per loop 

Pour une fonction de cette simple, la version pure numpy est fondamentalement aussi bon:

In [20]: timeit ((a-(a-10))**2).sum()/a.shape[0] 
100 loops, best of 3: 16.8 ms per loop 
+0

ty ty! haha je pense que numpy serait aussi rapide. Je voulais utiliser la fonction nogil (une petite partie d'un processus plus important) à l'intérieur d'une boucle. Je voulais juste un exemple simple pour commencer. Ceci est extrêmement utile. – itzjustricky