Numpy: быстрее np.dot/multiple(элементарное умножение), когда один и тот же массив

Мне нужно выполнить умножение точечного произведения между двумя одномерными массивами, а затем взять сумму списка как:

import numpy as np
import numba
import time
import random

a = np.array([1, 2, 3, 4, 5, 6],dtype=int8)  # Same first array
b = [np.array([random.randint(0,100) for y in range(6)],dtype=float) for x in range(1, 1000000)]  # iterable of 2nd array

c = np.zeros(len(b))  # to store results

@numba.njit()  # benefits of numba
def sum_of_mul(list1, list2):
    return np.dot(list1, list2)

tstart = time.perf_counter()
for i in range(len(c)):
    c[i] = sum_of_mul(a, b[i])

final_average = np.sum(c) / len(c)
print('time taken', round(time.perf_counter() - tstart, 1))

Меня интересует только скорость умножения массива, а не настройка. Точечный продукт занимает около 1,2 секунды на моем компьютере.

Учитывая, что мой первый массив такой же, и оба моих массива имеют размер 6 элементов, есть ли более быстрый способ сделать это?

Каким-то образом превратить это в многомерное матричное умножение? Панды? Или использовать какую-нибудь причудливую библиотеку? У меня есть графический процессор AMD.

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

numba помогла ускорить это.

Обновлено: Согласно принятому в настоящее время ответу, это также было огромным ускорением

np.mean(np.dot(a, b.T))

и убедившись, что a и b (/все) являются массивами numpy

Разве это не просто np.dot(b,a)? np.array(b) это (1000000,6) форма и a (6,)`

hpaulj 27.05.2023 00:10

На уровне Python numpy они разные. np.dot включает шаг sum.

hpaulj 27.05.2023 00:55

Я понимаю вашу точку зрения, и я отредактировал вопрос. Не было улучшения скорости с np.dot по сравнению с np.sum(np.multiply))

azazelspeaks 27.05.2023 01:02
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Базовый уровень

Ваш код в моей системе занимает 1,16 секунды для последней части (без инициализации, так же, как вы ее измерили):

time taken 1.2
CPU times: user 1.16 s, sys: 176 ms, total: 1.33 s
Wall time: 1.16 s

Часть инициализации занимает 5,5 сек.

Мы можем сократить его до 6 мс с помощью чистого Numpy и до 3 мс с CuPy (CUDA).

Улучшения

  1. Правильно управляйте своими типами. Вы используете int и float, которые являются 64-битными типами Python.

С np.float32 в качестве базового типа для всех ваших массивов время сокращается вдвое:

a = np.array([1, 2, 3, 4, 5, 6],dtype=np.float32)  # Same first array
b = [np.array([random.randint(0,100) for y in range(6)],dtype=np.float32) for x in range(1, 1000000)]  # iterable of 2nd array
time taken 0.8
CPU times: user 743 ms, sys: 62 µs, total: 743 ms
Wall time: 762 ms
  1. Используйте типы numpy для типов данных и алгоритмов.

Ваш исходный код смешивает списки Python и массивы numpy. На моем компьютере для инициализации требуется 5,5.

Вместо списка 1_000_000 np.arrays(6,) каждый вы можете иметь один np.array(1_000_000, 6). Вы можете переписать эту часть как:

a = np.array([1, 2, 3, 4, 5, 6],dtype=np.float32)  # Same first array
b = np.random.uniform(0, 100, (1000000, 6)).astype(np.float32)  # 2nd array

Это занимает всего 59 мс. Обратите внимание, что np.random возвращает результаты с dtype=np.float64.

Расчет будет очень простым с чистым Numpy:

np.mean(np.dot(a, b.T))

Это занимает 6 мс.

CPU times: user 13.7 ms, sys: 8.03 ms, total: 21.7 ms
Wall time: 6.1 ms
1050.432
  1. Ускорение с помощью графического процессора

Вы можете использовать CuPy - Nupy на CUDA. Cupy великолепен, потому что его API почти идентичен Numpy:

%pip install cupy

import cupy as cp

# initialization
a = cp.array([1, 2, 3, 4, 5, 6],dtype=np.float32)  # Same first array
b = cp.random.uniform(0,100, (1000000, 6)).astype(np.float32)  # 2nd array

# calculation
cp.mean(cp.dot(a, b.T))

Это занимает 2,9 мс (у меня CUDA 11.3 на карте Tesla K80).

CPU times: user 1.22 ms, sys: 11.8 ms, total: 13.1 ms
Wall time: 2.94 ms
1050.432

CuPy совместим с несколькими версиями CUDA. Установка CUDA — это немного другая тема, я считаю, что документация CuPy хорошо справляется с задачей, направляя ее в нужные места. https://docs.cupy.dev/ru/stable/install.html

Сначала проверьте, действительно ли вам нужен GPU. Чистый Numpy при правильном использовании дает действительно приличные скорости.

какие-нибудь сроки с cupy? У меня AMD GPU, так что заставить его работать будет сложно.

azazelspeaks 27.05.2023 01:50

Сделанный. Я также обновил свой ответ, так как apply_along_axis был ненужным. Сроки исполнения сейчас разумные. 6 мс с чистым Numpy и 2,9 мс с Cupy. Я считаю, что чем больше массивы, тем больше может ускориться GPU.

Pawel 27.05.2023 02:20

Это здорово, делать np.mean(np.dot(a, b.T)) и следить за тем, чтобы все они были np.arrays, пока что это самое быстрое

azazelspeaks 27.05.2023 04:11

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