Подтвердить что ты не робот

Scikit GridSearch и Python вообще не освобождают память

Я сделал некоторые странные наблюдения о том, что мои GridSearches продолжают терпеть неудачу через пару часов, и я изначально не мог понять, почему. Затем я отслеживал использование памяти, но со временем увидел, что он начался с нескольких гигабайт (~ 6 Гб) и продолжал расти, пока он не разбил node, когда достиг максимального значения. 128 Гб, которое может принять оборудование. Я экспериментировал со случайными лесами для классификации большого количества текстовых документов. Для простоты - выяснить, что происходит - я вернулся к наивным Байесам.

Используемые версии

  • Python 3.4.2
  • scikit-learn 0.15.2

Я нашел некоторую связанную дискуссию по списку scikit-issue на GitHub по этой теме: https://github.com/scikit-learn/scikit-learn/issues/565 и https://github.com/scikit-learn/scikit-learn/pull/770

И похоже, что он уже был успешно рассмотрен!

Итак, соответствующий код, который я использую,

grid_search = GridSearchCV(pipeline, 
                           parameters, 
                           n_jobs=1, # 
                           cv=5, 
                           scoring='roc_auc',
                           verbose=2,
                           pre_dispatch='2*n_jobs',
                           refit=False)  # tried both True and False

grid_search.fit(X_train, y_train)  
print('Best score: {0}'.format(grid_search.best_score_))  
print('Best parameters set:') 

Просто из любопытства я позже решил сделать поиск сетки быстрым и грязным способом через вложенный цикл

for p1 in parameterset1:
    for p2 in parameterset2:
        ...
            pipeline = Pipeline([
                        ('vec', CountVectorizer(
                                   binary=True,
                                   tokenizer=params_dict[i][0][0],
                                   max_df=params_dict[i][0][1],
                                   max_features=params_dict[i][0][2],
                                   stop_words=params_dict[i][0][3],
                                   ngram_range=params_dict[i][0][4],)),
                         ('tfidf', TfidfTransformer(
                                      norm=params_dict[i][0][5],
                                      use_idf=params_dict[i][0][6],
                                      sublinear_tf=params_dict[i][0][7],)),
                         ('clf', MultinomialNB())])

            scores = cross_validation.cross_val_score(
                                        estimator=pipeline,
                                        X=X_train, 
                                        y=y_train, 
                                        cv=5, 
                                        scoring='roc_auc',
                                        n_jobs=1)

           params_dict[i][1] = '%s,%0.4f,%0.4f' % (params_dict[i][1], scores.mean(), scores.std())
           sys.stdout.write(params_dict[i][1] + '\n')

Пока все хорошо. Выполняется поиск сетки и записывает результаты в stdout. Однако через некоторое время он снова превысит объем памяти 128 Гб. Такая же проблема, как с GridSearch в scikit. После некоторых экспериментов я наконец узнал, что

gc.collect()
len(gc.get_objects()) # particularly this part!

в цикле for решает проблему, и использование памяти постоянно остается на уровне 6.5 Гб в течение времени ~ 10 часов.

В конце концов, я получил его для работы с вышеупомянутым исправлением, однако мне любопытно услышать ваши идеи о том, что может вызвать эту проблему, ваши советы и предложения!

4b9b3361

Ответ 1

RandomForest в 0.15.2 не поддерживает разреженные входы.

Обновите sklearn и повторите попытку... надеюсь, это позволит нескольким копиям, которые в конечном итоге будут потреблять меньше памяти. (и ускорить работу)

Ответ 2

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

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

У меня была аналогичная проблема, я взорвал память с помощью аналогичного процесса, затем мне удалось сохранить нагрузку на 10%.

UPDATE: Теперь я вижу фрагмент кода с pandas DataFrame. Это может быть проблема с оценкой.

Ответ 3

Я не знаком с GridSearch sir, но я бы предложил, когда память и огромные списки - проблема, напишите небольшой пользовательский генератор. Его можно использовать повторно для всех ваших предметов, просто используйте тот, который принимает любой список. Если внедрение за пределами нижнего решения здесь сначала прочитайте эту статью, я бы нашел лучшую генераторную статью. Я набрал все это и пошел по частям, любые вопросы, которые у вас есть после прочтения, я тоже могу попробовать.

https://www.jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/

Не нужно: for p1 in parameterset1:

Try

 def listerator(this_list):
    i = 0
    while True:
       yield this_list[i]
       i += 1

Слово "yield" (в любом месте объявления) делает это генератором, а не регулярной функцией. Это пробегает и говорит, что я равно 0, а True я должен делать что-то, они хотят, чтобы я уступил this_list [0], здесь вы пойдете, я буду ждать вас в i += 1, если вам понадобится снова. В следующий раз, когда он вызывается, он выбирает и делает i += 1, и отмечает, что он все еще находится в цикле while и дает this_list [1], и записывает свое местоположение (i += 1 снова... он будет ждать там до вызова снова). Обратите внимание, как только я подаю его список и создаю генератор (здесь), он исчерпает ваш список.

In [141]: x = listerator([1,2,3])

In [142]: next(x)
Out[142]: 1

In [143]: next(x)
Out[143]: 2

In [144]: next(x)
Out[144]: 3

In [148]: next(x)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-148-5e4e57af3a97> in <module>()
----> 1 next(x)

<ipython-input-139-ed3d6d61a17c> in listerator(this_list)
      2     i = 0
      3     while True:
----> 4             yield this_list[i]
      5             i += 1
      6 

IndexError: list index out of range

Посмотрим, можем ли мы использовать его в для:

In [221]: for val in listerator([1,2,3,4]):
   .....:     print val
   .....:     
1
2
3
4
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-221-fa4f59138165> in <module>()
----> 1 for val in listerator([1,2,3,4]):
      2     print val
      3 

<ipython-input-220-263fba1d810b> in listerator(this_list, seed)
      2         i = seed or 0
      3         while True:
----> 4             yield this_list[i]
      5             i += 1

IndexError: list index out of range

Неа. Попробуем обработать это:

def listerator(this_list):
   i = 0
   while True:
       try:
           yield this_list[i]
       except IndexError:
           break
       i += 1

In [223]: for val in listerator([1,2,3,4]):
    print val
   .....:     
1
2
3
4

Это работает. Теперь он не будет слепо пытаться вернуть элемент списка, даже если его там нет. Из того, что вы сказали, я могу почти гарантировать, что вам нужно будет засеять его (забрать с определенного места или начать только с определенного места):

def listerator(this_list, seed=None):
   i = seed or 0
   while True:
       try:
           yield this_list[i]
       except IndexError:
           break
       i += 1

In [150]: l = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]

In [151]: x = listerator(l, 8)

In [152]: next(x)
Out[152]: 9

In [153]: next(x)
Out[153]: 10

In [154]: next(x)
Out[154]: 11

i = seed or 0 - это вещь, которая ищет семя, но значение по умолчанию по умолчанию равно None, поэтому обычно начинается только с логического места, 0, начало списка

Как вы можете использовать этот зверь, не используя (почти) любую память?

parameterset1 = [1,2,3,4]
parameterset2 = ['a','b','c','d']

In [224]: for p1 in listerator(parameterset1):
    for p2 in listerator(parameterset2):
        print p1, p2
   .....:         
1 a
1 b
1 c
1 d
2 a
2 b
2 c
2 d
3 a
3 b
3 c
3 d
4 a
4 b
4 c
4 d

который выглядит знакомым, да? Теперь вы можете обрабатывать триллион значений один за другим, выбирать важные для записи на диск и никогда не взорвать свою систему. Наслаждайтесь!