Нумба Джит и Сципи - PullRequest
       102

Нумба Джит и Сципи

0 голосов
/ 03 августа 2020

Я нашел здесь несколько сообщений по этой теме, но у большинства из них не было полезного ответа.

У меня есть 3D NumPy набор данных [images number, x, y], в котором вероятность того, что пиксель принадлежит в класс хранится как float (0-1). Я хотел бы исправить неправильные сегментированные пиксели (с высокой производительностью).

Вероятности являются частью mov ie, в котором объекты перемещаются справа налево и, возможно, обратно. Основная идея c состоит в том, что я подбираю пиксели с помощью функции Гаусса или сопоставимой функции и просматриваю примерно 15-30 изображений ([i-15: i + 15, x, y]). Очень вероятно, что если предыдущие 5 пикселей и следующие 5 пикселей относятся к этому классу, этот пиксель также принадлежит к этому классу.

Чтобы проиллюстрировать свою проблему, я добавляю образец кода, результаты были рассчитаны без использование numba:

from scipy.optimize import curve_fit
from scipy import exp
import numpy as np
from numba import jit

@jit
def fit(size_of_array, outputAI, correct_output):
    x = range(size_of_array[0])
    for i in range(size_of_array[1]):
        for k in range(size_of_array[2]):
            args, cov = curve_fit(gaus, x, outputAI[:, i, k])
            correct_output[2, i, k] = gaus(2, *args)
    return correct_output

@jit
def gaus(x, a, x0, sigma):
    return a*exp(-(x-x0)**2/(2*sigma**2))

if __name__ == '__main__':
    # output_AI = [imageNr, x, y] example 5, 2, 2
    # At position [2][1][1] is the error, the pixels before and after were classified to the class but not this pixel.
    # The objects do not move in such a speed, so the probability should be corrected.
    outputAI = np.array([[[0.1, 0], [0, 0]], [[0.8, 0.3], [0, 0.2]], [[1, 0.1], [0, 0.2]],
                         [[0.1, 0.3], [0, 0.2]], [[0.8, 0.3], [0, 0.2]]])
    correct_output = np.zeros(outputAI.shape)

    # I correct now in this example only all pixels in image 3, in the code a loop runs over the whole 3D array and
    # corrects every image and every pixel separately
    size_of_array = outputAI.shape
    
    correct_output = fit(size_of_array, outputAI, correct_output)
    # numba error: Compilation is falling back to object mode WITH looplifting enabled because Function "fit" failed 
    # type inference due to: Untyped global name 'curve_fit': cannot determine Numba type of <class 'function'> 
    
    print(correct_output[2])
    # [[9.88432346e-01 2.10068763e-01]
    # [6.02428922e-20 2.07921125e-01]]
    # The wrong pixel at position [0][0] was corrected from 0.2 to almost 1, the others are still not assigned
    # to the class.

К сожалению, numba НЕ работает. Я всегда получаю следующую ошибку:

Compilation is falling back to object mode WITH looplifting enabled because Function "fit" failed type inference due to: Untyped global name 'curve_fit': cannot determine Numba type of <class 'function'>

** -------------------------------- ---------------------------------------- **

Обновление 04.08.2020

В настоящее время я думаю об этом решении моей проблемы. Но я открыт для дальнейших предложений.

from scipy.optimize import curve_fit
from scipy import exp
import numpy as np
import time


def fit_without_scipy(input):
    x = range(input.size)
    x0 = outputAI[i].argmax()
    a = input.max()
    var = (input - input.mean())**2
    return a * np.exp(-(x - x0) ** 2 / (2 * var.mean()))


def fit(input):
    x = range(len(input))
    try:
        args, cov = curve_fit(gaus, x, outputAI[i])
        return gaus(x, *args)
    except:
        return input


def gaus(x, a, x0, sigma):
    return a * exp(-(x - x0) ** 2 / (2 * sigma ** 2))


if __name__ == '__main__':
    nr = 31
    N = 100000
    x = np.linspace(0, 30, nr)
    outputAI = np.zeros((N, nr))
    correct_output = outputAI.copy()
    correct_output_numba = outputAI.copy()
    perfekt_result = outputAI.copy()
    for i in range(N):
        perfekt_result[i] = gaus(x, np.random.random(), np.random.randint(-N, 2*N), np.random.random() * np.random.randint(0, 100))
        outputAI[i] = perfekt_result[i] + np.random.normal(0, 0.5, nr)

    start = time.time()
    for i in range(N):
        correct_output[i] = fit(outputAI[i])
    print("Time with scipy: " + str(time.time() - start))

    start = time.time()
    for i in range(N):
        correct_output_numba[i] = fit_without_scipy(outputAI[i])
    print("Time without scipy: " + str(time.time() - start))

    for i in range(N):
        correct_output[i] = abs(correct_output[i] - perfekt_result[i])
        correct_output_numba[i] = abs(correct_output_numba[i] - perfekt_result[i])

    print("Mean deviation with scipy: " + str(correct_output.mean()))
    print("Mean deviation without scipy: " + str(correct_output_numba.mean()))

Вывод [with nr = 31 and N = 100000]:

Time with scipy: 193.27853846549988 secs
Time without scipy: 2.782526969909668 secs
Mean deviation with scipy: 0.03508043754489116
Mean deviation without scipy: 0.0419951370808896

На следующем шаге я бы попытался еще больше ускорить код с помощью numba. В настоящее время это не работает из-за функции argmax.

1 Ответ

4 голосов
/ 03 августа 2020

Curve_fit в конечном итоге вызывает либо минимум_squares (чистый python), либо leastsq (C расширение). У вас есть три варианта:

  • выяснить, как заставить numba-jitted код взаимодействовать с расширением C, которое поддерживает наименьшее количество q

  • извлекать соответствующие части Minimum_squares и numba.jit them

  • реализуют поддержку LowLevelCallable для less_squares или минимизируют.

Ничто из этого не является простым. В случае успеха все это было бы интересно более широкой аудитории.

...