Как передать класс ModelResult из lmfit в многопроцессорный общий массив? - PullRequest
0 голосов
/ 05 июля 2019

У меня есть некоторые проблемы с использованием lmfit в другом процессе, чтобы сделать мой код быстрее.Как я могу определить некоторый общий ресурс, который содержит результаты каждого фитинга?

У меня есть куб данных с позициями a, b, x и f (x).Я сделал модель в lmfit , которая хорошо работает и настроил f (x) для одной точки, возвращая некоторые параметры.Lmfit возвращает класс с именем ModelResult () , который содержит каждый из этих параметров и некоторые дополнительные полезные данные.Итак, мне нужно запустить эту подгонку для каждого a и b, а затем создать куб с этими параметрами и, возможно, дополнительными данными.Я могу выполнить это линейным способом (без распараллеливания), но у меня более 1000 точек, и модель сложная, поэтому она занимает более 15000 секунд.

Моя проблема запускается при использовании многопроцессорная lib.Мне нужно обмениваться данными между каждым процессом, поэтому, когда процесс завершится, заблокируйте переменную и сохраните результаты внутри, а затем разблокируйте переменную.Многопроцессорная библиотека имеет Value () или Array () , чтобы делать то, что мне нужно.Я намеревался использовать Array и произвел некоторое изменение переменной, чтобы перейти от a и b к c, где c - диапазон a * b.Но я не могу определить массив для хранения ModelResult () для каждого c.

Вот код:

import multiprocessing as mp
import numpy as np
import time 
from lmfit import Model
from numpy import sqrt, exp, pi

#Set time zero
start_time = time.time()

#Example of functions to fit
def gaussian(x, amp, cen, wid):
    """1-d gaussian: gaussian(x, amp, cen, wid)"""
    return (amp / (sqrt(2*pi) * wid)) * exp(-(x-cen)**2 / (2*wid**2))

def linear(x, slope, intercept):
    """a linear function"""
    return slope*x + intercept

#Function to fit every point
def fit_point(a,b,data_cube,x,pars,mod):
        pos=int(b+(a*10))
        data_point = np.array(data_cube[:,a,b])
        #print(pos,mp.current_process().name)
        error_point= np.array((data_point*0)+0.002) #Example error
        res_point=mod.fit(data_point, pars, weights=1./error_point, x=x)
        print(res_point.fit_report())
        cube_res[pos]=res_point
        return #cube_res[:]

#Invented some data
x=np.arange(10)
data_cube=np.random.rand(10, 10, 10)

#Example of a model with 2 gaussians and a line
mod = Model(linear, prefix='l_')+Model(gaussian, prefix='g1_')+Model(gaussian, prefix='g2_')
pars= mod.make_params()
pars['g1_amp'].set(0.5)
pars['g2_amp'].set(0.5)
pars['g1_cen'].set(2)
pars['g2_cen'].set(3)
pars['g1_wid'].set(0.5)
pars['g2_wid'].set(0.5)
pars['l_slope'].set(1)
pars['l_intercept'].set(1)

#Definition of the shared Array #Where I think there is the problem!
cube_res = mp.Array('u', 100)

#Definition of the process and starts
processes = []
for a in np.arange(0,10):
    for b in np.arange(0,10):
        process = mp.Process(target=fit_point, args=(a,b,data_cube,x,pars,mod))
        process.start()
        processes.append(process)
for process in processes:
    process.join()

print('Time count')
print("--- %s seconds ---" % (time.time() - start_time))    

#Intent to print some results
#print(cube_res[20].fit_report)

#Final, to recover a,b
#final_cube_res = np.reshape(cube_res, (100,100))

Ошибки:

TypeError: unicode string expected instead of ModelResult instance

Это потому, что я определяю mp.Array ('u', 100) , где 'u' - это юникод, а 100 - диапазон.

Я не знаю, как определить массив, чтобы можно было сохранить ModelResult внутри.

Спасибо за чтение!

1 Ответ

0 голосов
/ 05 июля 2019

Сообщение об ошибке говорит вам, что вы не можете поместить lmfit.ModelResult в строку. На самом деле, ваш

cube_res = mp.Array('u', 100)

говорит, что cube_res - это массив из 100 символов Юникода. То есть каждый результат должен быть длиной 1 символ. Я думаю, что вместо этого вы хотите использовать multiprocessing.Manger().dict для хранения результатов.

cube_res = mp.Manager().dict()

Это позволит вам использовать позицию (x, y) в качестве ключа, а затем вы можете поместить ModelResult в качестве значения ....

... но: вы не сможете сохранить ModelResult напрямую, потому что данные, разделяемые между многопроцессорными процессами, нужно выбирать, а в целом сложные объекты, особенно объекты с методами, нелегко выбрать. Хорошей новостью является то, что lmfit.ModelResult имеет метод dumps(), который можно использовать для преобразования этого объекта в выбираемую строку json. Тогда ваш код будет использовать

    res_point=mod.fit(data_point, pars, weights=1./error_point, x=x)
    print(res_point.fit_report())
    cube_res[(a,b)] = res_point.dumps()
    return 

в вашей функции fit_point().

Мы еще не закончили, потому что восстановление ModelResult из выведенной строки json немного сложнее (lmfit имеет вспомогательные функции для этого, но они предполагают, что вы записали результаты в файл). Вам нужно будет сделать это после того, как вы joined выполните все свои процессы:

print('Time count')
print("--- %s seconds ---" % (time.time() - start_time))

import lmfit
# make a dummy ModelResult -- we'll overwrite everything for this
modres = lmfit.model.ModelResult(lmfit.Model(gaussian), lmfit.Parameters())

# make a dictionary of the functions you actually used (the function *names* 
# are included in the dumped string, but not the functions themselves)
funcdefs = {'gaussian': gaussian, 'linear': linear}
for pos, dumpval in cube_res.items():
    modelresult = modres.loads(dumpval, funcdefs=funcdefs)
    print('### Result for fit ', pos)
    print(modelresult.fit_report())

Я думаю, это должно помочь вам начать ...

...