Многопроцессорность: пул.imap_unordered; не могу замариновать функцию

Я работаю над функцией из книги Лопеса де Прадо. Вот минимальный пример того, что я пытаюсь сделать:

import numpy as np
import pandas as pd
import multiprocessing as mp

# Define test function
def test_func(idx, a, b):
    return [idx, a**1 + b**2]

test_func = np.vectorize(test_func)

test_df = pd.DataFrame(np.random.normal(size = (10, 2)), columns = ['a', 'b'])

test_df = test_df.reset_index()

func_dict = {col:test_df[col] for col in test_df.columns}

# Function used to evaluate test_func
def expandCall(kargs):

    # Get the function argument
    func = kargs['func']

    # Delete it from the fictionary
    del kargs['func']

    # Evaluate function with other arguments
    out = func(**kargs)

    return out

parts = np.linspace(0, test_df.shape[0], 2).astype(int)

jobs = []

for i, j in zip(parts[:-1], parts[1:]):

    job = {key:func_dict[key][i:j] for key in func_dict}
    job.update({'func':test_func})
    jobs.append(job)
    
pool = mp.Pool(processes = 2)

outputs = pool.imap_unordered(expandCall, jobs)

out = []

# Process asynchronous output, report progress
for out_ in outputs:

    out.append(out_)

pool.close()
pool.join()

Я получаю ошибку

PicklingError: невозможно выбрать <функцию test_func по адресу 0x0000027FBF960F40>: это не тот же объект, что и --main--.test_func

Если я правильно читаю книгу, Лопес де Прадо предлагает решение:

def _unpickle_method(func_name, obj, cls):
    
    for cls in cls.mro():
        
        try:
            
            func = cls.__dict__[func_name]
            
        except KeyError:
            
            pass
        
        else:
            
            break
        
    return func.__get__(obj, cls)


def _pickle_method(method):
    
    func_name = method.im_func.__name__
    
    obj = method.im_self
    
    cls = method.im_class
    
    return _unpickle_method, (func_name, obj, cls)

import copyreg, types

copyreg.pickle(types.MethodType, _pickle_method, _unpickle_method)

Однако у меня это не работает. Возможно, из-за изменений в модуле многопроцессорности с момента выхода книги. К сожалению, я недостаточно хорошо понимаю решение, чтобы обновлять его. Буду признателен за любую помощь в запуске моего минимального примера.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Опубликованный вами метод позволяет вам выбирать методы класса, лямбды не являются методом класса, по крайней мере, в python3, а стандартный сборщик Python не позволяет выбирать лямбды.

вы можете использовать cloudpickle, который позволяет вам мариновать лямбды, вам нужно сделать obj = cloudpickle.dumps(real_object), а затем передать это obj в пул, а затем real_object = cloudpickle.loads(obj) на другую сторону.

np.vectorize написан на C и требует добавления поддержки травления в C, но вы можете обойти это, обернув его в простую «обертку» для облегчения травления, см. документы __getstate__ и __setstate__

import numpy as np
import pandas as pd
import multiprocessing as mp


class vectorize_wrapper:
    def __init__(self, pyfunc):
        self.func = np.vectorize(pyfunc)

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

    def __setstate__(self, state):
        self.func = np.vectorize(state)

    def __getstate__(self):
        return self.func.pyfunc

def test_func(idx, a, b):
    return [idx, a**1 + b**2]


test_func_vectorized = vectorize_wrapper(test_func)

import pickle
a = pickle.dumps(test_func_vectorized)
b = pickle.loads(a)

Основное ограничение заключается в том, что векторизованная функция должна быть импортируемой, вы не можете иметь другую функцию или объект с таким же именем в том же файле, и она должна находиться в глобальной области видимости, другими словами, Python может получить к ней доступ, набрав

from some_module import my_python_function

потому что это то, что делает стандартный сборщик. Я думаю, что у Cloudpickle нет такого строгого требования.

Извините, я не знал, что лямбда-функции нельзя собирать. Использование лямбда-функции вообще не обязательно (хотя векторизация важна). Я запускал минимальные примеры без лямбда-функций, и это не сработало. Я попробую обновить код.

Charles0349 11.04.2024 05:28

@Charles0349 Charles0349 Я добавил ответ для части векторизации, «обертка» должна действовать точно так же, как np.vectorize, за исключением того, что ее можно мариновать, но функции маринования - это то, что не очень переносимо, и в будущем у вас будет гораздо больше проблем.

Ahmed AEK 11.04.2024 13:35

@Charles0349 Charles0349 вместо функций травления вам следует выбирать классы, способ настройки классов обычно гарантирует, что пиклер сможет выполнять свою работу.

Ahmed AEK 11.04.2024 13:53

Я больше не получаю эту ошибку, но, как ни странно, мой минимальный пример работает уже больше часа, когда test_df содержит всего 20 строк, а задача разбита на 5 частей. (Для такого маленького примера я ожидаю, что он будет работать немного медленнее, чем прямой подход). Тем не менее, я не хочу, чтобы миссия в этом посте была полна, поэтому я собираюсь дать вам чек. Спасибо за помощь!

Charles0349 11.04.2024 17:19

Другие вопросы по теме