Изготовленный на заказ склеарн трубопроводный трансформатор, дающий "pickle.PicklingError"

Я пытаюсь создать собственный преобразователь для конвейера Python sklearn на основе руководства из этого руководства: http://danielhnyk.cz/creating-your-own-estimator-scikit-learn/

Прямо сейчас мой пользовательский класс / трансформер выглядит так:

class SelectBestPercFeats(BaseEstimator, TransformerMixin):
    def __init__(self, model=RandomForestRegressor(), percent=0.8,
                 random_state=52):
        self.model = model
        self.percent = percent
        self.random_state = random_state


    def fit(self, X, y, **fit_params):
        """
        Find features with best predictive power for the model, and
        have cumulative importance value less than self.percent
        """
        # Check parameters
        if not isinstance(self.percent, float):
            print("SelectBestPercFeats.percent is not a float, it should be...")
        elif not isinstance(self.random_state, int):
            print("SelectBestPercFeats.random_state is not a int, it should be...")

        # If checks are good proceed with fitting...
        else:
            try:
                self.model.fit(X, y)
            except:
                print("Error fitting model inside SelectBestPercFeats object")
                return self

            # Get feature importance
            try:
                feat_imp = list(self.model.feature_importances_)
                feat_imp_cum = pd.Series(feat_imp, index=X.columns) \
                    .sort_values(ascending=False).cumsum()

                # Get features whose cumulative importance is <= `percent`
                n_feats = len(feat_imp_cum[feat_imp_cum <= self.percent].index) + 1
                self.bestcolumns_ = list(feat_imp_cum.index)[:n_feats]
            except:
                print ("ERROR: SelectBestPercFeats can only be used with models with"\
                       " .feature_importances_ parameter")
        return self


    def transform(self, X, y=None, **fit_params):
        """
        Filter out only the important features (based on percent threshold)
        for the model supplied.

        :param X: Dataframe with features to be down selected
        """
        if self.bestcolumns_ is None:
            print("Must call fit function on SelectBestPercFeats object before transforming")
        else:
            return X[self.bestcolumns_]

Я интегрирую этот класс в конвейер sklearn следующим образом:

# Define feature selection and model pipeline components
rf_simp = RandomForestRegressor(criterion='mse', n_jobs=-1,
                                n_estimators=600)
bestfeat = SelectBestPercFeats(rf_simp, feat_perc)
rf = RandomForestRegressor(n_jobs=-1,
                           criterion='mse',
                           n_estimators=200,
                           max_features=0.4,
                           )

# Build Pipeline
master_model = Pipeline([('feat_sel', bestfeat), ('rf', rf)])

# define GridSearchCV parameter space to search, 
#   only listing one parameter to simplify troubleshooting
param_grid = {
    'feat_select__percent': [0.8],
}

# Fit pipeline model
grid = GridSearchCV(master_model, cv=3, n_jobs=-1,
                    param_grid=param_grid)

# Search grid using CV, and get the best estimator
grid.fit(X_train, y_train)

Всякий раз, когда я запускаю последнюю строку кода (grid.fit(X_train, y_train)) Я получаю следующее "PicklingError". Кто-нибудь может увидеть, что вызывает эту проблему в моем коде?

РЕДАКТИРОВАТЬ:

Или что-то не так в моей настройке Python... Могу ли я пропустить пакет или что-то подобное? Я только что проверил, что могу import pickle успешно

Трассировка (последний вызов был последним): файл "", строка 5, в файле "C:\Users\jjaaae\AppData\Local\Programs\Python\Python36\lib\site-packages\sklearn\model_selection_search.py", строка 945, в нужном случае возвращает self._fit(X, y, groups, ParameterGrid(self.param_grid)) Файл "C: \ Users \ jjaaae \ AppData \ Local \ Programs \ Python \ Python36 \ lib \ site-packages \ sklearn \ model_selection_search. py ", строка 564, в _fit для параметров в файле parameter_iterable "C:\Users\jjaaae\AppData\Local\Programs\Python\Python36\lib\site-packages\sklearn\externals\joblib\parallel.py", строка 768, в вызове self.retrieve() Файл "C: \ Users \ jjaaae \ AppData \ Local \ Programs \ Python \ Python36 \ lib \ site-packages \ sklearn \ externals \ joblib \ parallel.py", строка 719, в режиме извлечения Файл исключения "C: \ Users \ jjaaae \ AppData \ Local \ Programs \ Python \ Python36 \ lib \ site-packages \ sklearn \ externals \ joblib \ parallel.py", строка 682, в файле получения self._output.extend(job.get(timeout=self.timeout)) Файл "C:\Users\jjaaae\AppData\Local\Programs\Python\Python36\lib\multiprocessing\pool.py", строка 608, в файле get нять self._value" C: \ Users \ jjaaae \ AppData \ Local \ Programs \ Python \ Python36 \ lib \ multiprocessing \ pool.py ", строка 385, в _handle_tasks поставить (задача) Файл "C:\Users\jjaaae\AppData\Local\Programs\Python\Python36\lib\site-packages\sklearn\externals\joblib\pool.py", строка 371, в send CustomizablePickler(буфер, self._reducers).dump(obj) _pickle.PicklingError: Невозможно засолить: поиск атрибутов SelectBestPercFeats для встроенных объектов не удался

4 ответа

Решение

Пакет pickle требует, чтобы пользовательский класс (ы) был определен в другом модуле и затем импортирован. Итак, создайте другой файл пакета Python (например, transformation.py), а затем импортировать его так from transformation import SelectBestPercFeats, Это решит ошибку травления.

Когда вы кодируете свой собственный преобразователь, и ЕСЛИ этот преобразователь содержит код, который не может быть сериализован, весь конвейер не будет сериализован, если вы попытаетесь сериализовать его.

Не только это, но также вам нужна такая сериализация, чтобы иметь возможность распараллеливать ваши вещи, например, видимые с помощью n_jobs=-1 как вы сказали, использовать много потоков.

Плохая вещь с scikit-learn заключается в том, что у каждого объекта должна быть своя заставка. Надеюсь, выход есть. Это либо сделать ваш объект сериализуемым (и, следовательно, удалить то, что вы импортируете из внешних библиотек), либо иметь только одно задание (без потоковой передачи), либо сделать ваш объект заставкой, которая сохранит объект для его сериализации. Здесь будет рассмотрено второе решение.

Во-первых, вот определение проблемы и ее решение, взятые из этого источника:

Проблема: невозможно распараллелить или сохранить конвейеры, используя шаги, которые нельзя сериализовать "как есть" от Joblib.

Эта проблема всплывет только после некоторого момента использования Scikit-Learn. Это точка невозврата: вы закодировали весь свой производственный конвейер, но как только вы его обучили и выбрали лучшую модель, вы понимаете, что то, что вы только что закодировали, нельзя сериализовать.

Это означает, что после обучения ваш конвейер не может быть сохранен на диски, потому что один из его шагов импортирует вещи из странной библиотеки Python, закодированной на другом языке, и / или использует ресурсы графического процессора. Ваш код странно пахнет, и вы начинаете паниковать из-за того, что прошел полный год исследований.

Надеюсь, вы достаточно любезны, чтобы начать кодировать свою собственную среду с открытым исходным кодом на стороне, потому что вы будете жить в такой же ситуации в своих следующих 100 проектах кодирования, и у вас будут другие клиенты, которые скоро окажутся в такой же ситуации, и это дерьмо критично.

Ну, Neuraxle был создан из общей потребности.

Решение: используйте цепочку сбережений на каждом этапе

Каждый шаг отвечает за само сохранение, и вы должны определить один или несколько настраиваемых объектов-заставок для вашего странного объекта. Спасатель должен:

  1. Сохраните то, что важно на шаге, с помощью заставки (см.: заставку)
  2. Удалите это из шага (чтобы сделать его сериализуемым). Шаг теперь лишен Saver.
  3. Затем JoblibStepSaver по умолчанию будет выполняться (по цепочке) за этой точкой, сохраняя все, что осталось от удаленного объекта, и удаляя объект из ОЗУ вашего кода. Это означает, что у вас может быть много частичных заставок до окончательного JoblibStepSaver по умолчанию.

Например, конвейер будет выполнять следующие действия после вызова метода save(), поскольку у него есть собственный TruncableJoblibStepSaver:

  1. Сохраните все его подшаги в относительных подпапках в подпапке сериализации конвейера.
  2. Удалите их из объекта конвейера, за исключением их имен, чтобы найти их позже при загрузке. Трубопровод теперь зачищен.
  3. Позвольте заставке по умолчанию сохранить разделенный конвейер.

Вы не хотите делать грязный код. Говорят, не нарушайте Закон Деметры. На мой взгляд, это один из самых важных (и легко упускаемых из виду) законов программирования. Погуглите, смею вас. Нарушение этого закона - корень самого зла в вашей кодовой базе.

Я пришел к выводу, что лучший способ не нарушить здесь этот закон - иметь цепочку вкладчиков. Это делает каждый объект ответственным за наличие специальных заставок, если он не может быть сериализован с помощью joblib. Аккуратно. Поэтому, когда что-то ломается, у вас есть возможность создать свой собственный сериализатор только для объекта, который ломается, таким образом вам не нужно будет нарушать инкапсуляцию во время сохранения, чтобы копаться в ваших объектах вручную, что нарушило бы закон Деметры.

Обратите внимание, что хранители также должны иметь возможность перезагружать объект при загрузке сохранения. Мы уже написали заставку TensorFlow Neuraxle.

TL;DR: вы можете вызвать метод save() в любом конвейере в Neuraxle, и если некоторые шаги определяют настраиваемую заставку, тогда шаг будет использовать эту заставку перед использованием JoblibStepSaver по умолчанию.

Распараллеливание вашего не травмируемого конвейера

Итак, вы сделали все, что описано выше, с помощью Neuraxle. Аккуратно. Теперь используйте классы Neuraxle для AutoML, случайного поиска и тому подобного. У них должны быть соответствующие абстракции для распараллеливания с использованием заставок для сериализации. Вещи должны быть сериализованы, чтобы отправить ваш код другим процессам Python для распараллеливания.

У меня была та же проблема, но в моем случае проблема заключалась в использовании функциональных преобразователей, где pickle иногда возникают трудности с сериализацией функций. Решение для меня было использовать dill вместо этого, хотя это немного медленнее.

В моем случае мне просто пришлось перезапустить IDE Ipython, где я проверял трансформатор. После перезапуска IDE и повторного запуска кода он либо работает хорошо, либо начинает выдавать более значимую ошибку.

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