Я пытаюсь вычислить матрицу расстояний DTW, которая будет рассматривать 150 000 временных рядов, каждый из которых имеет от 13 до 24 наблюдений, то есть полученная матрица расстояний будет представлять собой список размером приблизительно (150 000 x 150 000) / 2 = 11 250 000 000.

Я запускаю это на большой кластер данных размером 200 ГБ, но я получаю ошибку памяти.

Я использую библиотеку dtaidisatance и использовал функцию distance_matrix_fast , которую я мог передать сразу всем временным рядам в списке, но у меня возникала похожая ошибка памяти, но она выходила из пакета. ошибка была сброшена сразу же, как только я ее запустил. Я также использовал блочную функцию в пакете, но кажется, что он не может взять все временные ряды сразу, чтобы начать.

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

Файл "/root/anaconda2/test/final_clustering_2.py", строка 93, в distance_matrix_scaled.append (dtw.distance_fast (Series_scaled [i], Series_scaled [j])) MemoryError

Это мой код ниже:

distance_matrix_scaled = []

m=len(Series_scaled)
#m=100000
for i in range(0, m - 1):
    for j in range(i + 1, m):

distance_matrix_scaled.append(dtw.distance_fast(Series_scaled[i], Series_scaled[j]))

# save it to the disk
np.save('distance_entire',  distance_matrix_scaled)

Не могли бы вы помочь ответить, почему я получаю эту ошибку памяти? это предел списка Python или размер моего кластера, вызывающий это? Есть ли умный способ или формат в NumPy я мог бы использовать для решения этой проблемы?

1
RomRom 24 Окт 2018 в 01:53

2 ответа

Лучший ответ

Ваш двойной цикл for имеет 4999950000 итераций. Вы добавляете это много раз в список. Все еще странно?

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

import numpy as np

m = 100000
distances = np.empty(m*(m-1)/2) # size: 4999950000
k = 0
for i in range(0, m - 1):
    for j in range(i + 1, m):
         distances[k] = dtw.distance_fast(Series_scaled[i], Series_scaled[j])
         k += 1

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

Если этот огромный массив не умещается в вашей памяти, у вас проблемы. Вы должны были бы сократить свои данные и работать небольшими кусками. Или, может быть, использовать какое-то отображение памяти.

Небольшое замечание: заполняемый нами массив (при условии 64-разрядных двойных чисел) занимает примерно 37 ГБ ОЗУ. Это много. и если вы можете разместить это в своей памяти, вам придется подождать 5 миллиардов итераций цикла python (double). Это займет ... много времени. Не задерживай дыхание.

2
Andras Deak 23 Окт 2018 в 23:17

Если вы вычисляете что-то вроде евклидова расстояния, посмотрите на стоимость памяти вашей вычислительной задачи, вы сгенерируете промежуточный временный массив размера 150000*149999/2*(13~24), где

  • 150000*149999/2 представляет количество неупорядоченных пар среди 1 50000 временных рядов (исключая самосознательную пару).

  • 13~24 представляет собой разницу между двумя векторами временных рядов, которые будут norm редактироваться позже и уменьшатся до одного числа на пару.

Каждое число обычно является числом с плавающей запятой или двойным числом, равным 4 или 8 байтам. Следовательно, алгоритм займет 1 ~ 4 Т памяти, что, очевидно, приведет к взрыву 200 ГБ.

Помимо ручного разделения на более мелкие задачи, есть несколько приемов для уменьшения затрат на память:

  • Если вы настаиваете на numpy, определенно выбирайте меньшие dtype, такие как float32. Если ваше число невелико, вы можете даже подумать о таких вещах, как int16 или int8. Не используйте float16, поскольку он не поддерживает вычисления на ЦП (очень медленный).

  • Если это не так, вы можете рассмотреть вариант numba, который позволяет вам скомпилировать цикл Python в высокоэффективный код ЦП и заставить его работать по всем ядрам, что должно быть оптимальным решением для ЦП (это не понадобится для создания временного массива).

  • У Scipy также есть scipy.spatial.distance.pdict. Не совсем уверен, как он будет работать с памятью, но вы можете попробовать.

2
ZisIsNotZis 24 Окт 2018 в 03:22
52958847