Python numpy не выпускает swap после большого вычисления

#python #numpy #memory #multidimensional-array

#python #numpy #память #многомерный массив

Вопрос:

У меня есть два больших набора массивов float32

 centers = np.random.uniform(-1, 1, (64, 40, 42)).astype(dtype=np.float32)
data = np.random.uniform(-1, 1, (60000, 40, 42)).astype(dtype=np.float32)
  

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

 diff = [np.abs(data - c) for c in centers]
  

На моем ПК с Linux с 16 ГБ оперативной памяти и 2 ГБ подкачки это не только дает MemoryError ; после сбоя с ошибкой из htop я вижу, что пространство подкачки все еще заполнено.

Для ошибки памяти я переписал код, чтобы использовать фрагменты data вместо всего массива сразу. (РЕДАКТИРОВАТЬ: нет MemoryError сейчас, но процесс завершается через некоторое время с SIGKILL указанием) Но это все еще довольно медленно, и своп, который не выпускается, все еще сохраняется. Есть ли утечка памяти в том, как я это делаю?

Бонусный вопрос: существует ли эффективный способ вычисления разницы? Кажется, для создания массивов требуется слишком много времени, чем для выполнения вычислений, даже если я делаю это с помощью фрагментов data . Если я уменьшу фрагменты, цикл займет слишком много времени

Ответ №1:

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

Генератор

Если вам не нужно получать доступ ко всем результатам сразу, вы можете использовать generator для уменьшения объема потребляемой памяти и выполнения вычислений по требованию:

 import itertools
import numpy as np

def generate_distance(a, b):
    for i, j in itertools.product(range(a.shape[0]), range(b.shape[0])):
        yield np.abs(a[i,:,:] - b[j,:,:])
  

Затем вы можете получить все результаты сразу, выполнив выдачу (это займет много памяти, поскольку в нем хранятся все результаты):

 x = np.array(list(generate_distance(data, centers)))
  

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

 for d in generate_distance(data, centers):
    # perform some computation on distance
  

Память

Вы можете проверить размер объекта с помощью:

 import sys
sys.getsizeof(data)/1024**2 # 384.5216064453125 Mb
  

Вы также можете оценить объем требуемой памяти, если вы стремитесь сохранить все результаты, в основном это будет немного больше, чем произведение размеров на размер dtype . В вашем случае это было бы примерно:

 64*60000*40*42*4 bytes ~ 24.609,375 Mb
  

Которые определенно превышают сумму вашей оперативной памяти и емкости подкачки.

Обходной путь

Итак, у вас есть несколько вариантов:

  • Если вам нужно сохранить результаты в оперативной памяти, вам придется выполнить пакетную обработку, потому что полный результат не будет соответствовать данной настройке;
  • Если вам просто нужно, чтобы все результаты были сохранены на вашем диске, используйте generator для снижения потребления памяти и записи расстояния один за другим в файл;
  • Если вам нужен результат в оперативной памяти, но не все сразу, используйте generator для цепочки вашего потока вычислений и отправки distance по требованию другим процессам.

Комментарии:

1. Это дает мне почти тот же результат, что и использование фрагментов data для вычисления разницы. Этот и мой оба кода работают с меньшими массивами без проблем (да data это 384 МБ)

2. @TeshanShanukaJ, не могли бы вы лучше объяснить операцию, которую вы хотите выполнить, какова форма ожидаемых результатов? Также не могли бы вы сказать нам, нужны ли вам все результаты как один раз или нет. Потому что, если вы используете генератор по требованию вместо сохранения всех результатов, это резко снизит требуемый объем памяти.

3. На самом деле я не хотел сохранять все результаты один раз. Я просто сосредоточился на том, чтобы сделать код быстрее, я не думал об этом. Спасибо за понимание

4. Пожалуйста, из любопытства, правильно ли я выполнил ваши вычисления? Приветствия.

5. Ваш код генератора должен работать, но я хотел вычислить их на месте, и я хотел разницу между одной точкой данных и всеми центрами одновременно. Ранее я делал все точки данных для всех центров одновременно и выполнял следующие вычисления. Итак, я изменил код на цикл, подобный for c in centers: get diff -> do calculations -> store result