Я хочу сделать поэлементное внешнее произведение трех (или четырех) больших двумерных массивов в python (значения float32 округлены до 2 десятичных знаков).Все они имеют одинаковое количество строк «n», но разное количество столбцов «i», «j», «k».
Полученный массив должен иметь форму (n, i * j * k).Затем я хочу суммировать каждый столбец результата, чтобы получить массив 1D формы (i * j * k).
np.shape(a) = (75466, 10)
np.shape(b) = (75466, 28)
np.shape(c) = (75466, 66)
np.shape(intermediate_result) = (75466, 18480)
np.shape(result) = (18480)
Благодаря ruankesi и divakar , яполучил фрагмент кода, который работает:
# Multiply first two matrices
first_multi = a[...,None] * b[:,None]
# could use np.einsum('ij,ik->ijk',a,b), which is slightly faster
ab_fills = first_multi.reshape(a.shape[0], a.shape[1]*b.shape[1])
# Multiply the result with the third matrix
second_multi = ab_fills[..., None] * c[:,None]
abc_fills = second_multi.reshape(ab_fills.shape[0], ab_fills.shape[1] * c.shape[1])
# Get the result: sum columns and get a 1D array of length 10*28*66 = 18 480
result = np.sum(abc_fills, axis = 0)
Задача 1 : Производительность
Это занимает около 3 секунд, но мне приходится повторять эту операцию много раз, а некоторыеиз матриц еще больше (по количеству строк).Это приемлемо, но было бы неплохо сделать это быстрее.
Задача 2 : Мои матрицы редки
Действительно, например, «a» содержит 70% от 0.Я пытался играть с scipy csc_matrix, но действительно не мог получить рабочую версию.(чтобы получить поэлементный внешний продукт здесь, я иду через преобразование в трехмерную матрицу, которая не поддерживается в scipy sparse_matrix)
Проблема 3 : использование памяти
Если я пытаюсь также работать с 4-й матрицей, у меня возникают проблемы с памятью.
Я предполагаю, что преобразование этого кода в sparse_matrix сэкономит много памяти и ускорит вычисления, игнорируя многочисленные значения 0.Это правда?Если да, может ли кто-нибудь мне помочь?
Конечно, если у вас есть предложения по улучшению реализации, я также очень заинтересован.Мне не нужны никакие промежуточные результаты, только конечный результат 1D.
Уже несколько недель я застрял в этой части кода, я схожу с ума!
Спасибо!
Редактировать после ответа Дивакара
Подход № 1:
Очень хороший лайнер, но на удивление медленнее, чем оригинальный подход (?).
В моем тестовом наборе данных подход № 1 занимает 4,98 с ± 3,06 мс на цикл (без ускорения с optimize = True)
Первоначальный разложенный подход занимал 3,01 с ± 16,5 мс на цикл
Подход № 2:
Абсолютно здорово, спасибо!Какое впечатляющее ускорение!
62,6 мс ± 233 мкс на цикл
Что касается Numberxpr, я стараюсь максимально избегать требований к внешним модулям и не планирую использовать многоядерные потоки / потоки.Это «смущающая» распараллеливаемая задача с сотнями тысяч объектов для анализа, я просто распределю список по доступным процессорам во время производства.Я попробую оптимизировать память.
В качестве краткой попытки по Numberxpr с ограничением на 1 поток, выполняя 1 умножение, я получаю время выполнения 40 мс без Numberxpr и 52 мс с Numexpr.
Еще раз спасибо !!