TLDR;Я бы выбрал @ Parfait для использования test_gen_sum
на основе сравнительного анализа различных значений n
, p
и y
.Сохранение старого ответа здесь для преемственности .
Оцените, как n
, p
, y
влияют на выбор алгоритма
Этот анализ выполняется с использованием функций @ Parfait каксредство определения, действительно ли существует одно лучшее решение или существует семейство решений на основе значений n
, p
и y
.
import numpy as np
import pytest # This code also requires the pytest-benchmark plugin
def test_for_sum(n, p, y):
random_state = np.random.RandomState(1)
X = random_state.rand(p, n)
X_sum = np.zeros((n, n))
# The length of weights are not related to X's dims,
# but will always be smaller
weights = random_state.rand(y)
for k in range(y):
X_sum += np.dot(X.T[:, k + 1:],
X[:p - (k + 1), :]) * weights[k]
return X_sum
def test_list_sum(n, p, y):
random_state = np.random.RandomState(1)
X = random_state.rand(p, n)
X_sum = np.zeros((n, n))
# The length of weights are not related to X's dims,
# but will always be smaller
weights = random_state.rand(y)
matrix_list = [np.dot(X.T[:, k + 1:],
X[:p - (k + 1), :]) * weights[k] for k in range(y)]
X_sum = np.sum(matrix_list, axis=0)
return X_sum
def test_reduce_sum(n, p, y):
random_state = np.random.RandomState(1)
X = random_state.rand(p, n)
X_sum = np.zeros((n, n))
# The length of weights are not related to X's dims,
# but will always be smaller
weights = random_state.rand(y)
matrix_list = [(X.T[:, k + 1:] @
X[:p - (k + 1), :]) * weights[k] for k in range(y)]
X_sum = reduce(lambda x,y: x + y, matrix_list)
return X_sum
def test_concat_sum(n, p, y):
random_state = np.random.RandomState(1)
X = random_state.rand(p, n)
X_sum = np.zeros((n, n))
# The length of weights are not related to X's dims,
# but will always be smaller
weights = random_state.rand(y)
x_mat = np.concatenate([np.matmul(X.T[:, k + 1:],
X[:p - (k + 1), :]) for k in range(y)])
wgt_mat = np.concatenate([np.full((n,1), weights[k]) for k in range(y)])
mul_res = x_mat * wgt_mat
X_sum = mul_res.reshape(-1, n, n).sum(axis=0)
return X_sum
def test_matmul_sum(n, p, y):
random_state = np.random.RandomState(1)
X = random_state.rand(p, n)
X_sum = np.zeros((n, n))
# The length of weights are not related to X's dims,
# but will always be smaller
weights = random_state.rand(y)
# Use list comprehension and np.matmul
matrices_list = [np.matmul(X.T[:, k + 1:],
X[:p - (k + 1), :]) * weights[k] for k in range(y)]
# Sum matrices in list of matrices to get the final result
X_sum = np.sum(matrices_list, axis=0)
return X_sum
def test_gen_sum(n, p, y):
random_state = np.random.RandomState(1)
X = random_state.rand(p, n)
X_sum = np.zeros((n, n))
# The length of weights are not related to X's dims,
# but will always be smaller
weights = random_state.rand(y)
matrix_gen = (np.dot(X.T[:, k + 1:],
X[:p - (k + 1), :]) * weights[k] for k in range(y))
X_sum = sum(matrix_gen)
return X_sum
parameters = [
pytest.param(400, 800, 3)
,pytest.param(400, 2000, 3)
,pytest.param(400, 800, 750)
,pytest.param(400, 2000, 750)
]
@pytest.mark.parametrize('n,p,y', parameters)
def test_test_for_sum(benchmark, n, p, y):
benchmark(test_for_sum, n=n, p=p, y=y)
@pytest.mark.parametrize('n,p,y', parameters)
def test_test_list_sum(benchmark, n, p, y):
benchmark(test_list_sum, n=n, p=p, y=y)
@pytest.mark.parametrize('n,p,y', parameters)
def test_test_reduce_sum(benchmark, n, p, y):
benchmark(test_reduce_sum, n=n, p=p, y=y)
@pytest.mark.parametrize('n,p,y', parameters)
def test_test_concat_sum(benchmark, n, p, y):
benchmark(test_concat_sum, n=n, p=p, y=y)
@pytest.mark.parametrize('n,p,y', parameters)
def test_test_matmul_sum(benchmark, n, p, y):
benchmark(test_matmul_sum, n=n, p=p, y=y)
@pytest.mark.parametrize('n,p,y', parameters)
def test_test_gen_sum(benchmark, n, p, y):
benchmark(test_gen_sum, n=n, p=p, y=y)
n=400
, p=800
, y=3
(100 итераций)
- победитель:
test_gen_sum
![enter image description here](https://i.stack.imgur.com/QEJRd.png)
n=400
, p=2000
, y=3
(100 итераций)
- победитель:
test_gen_sum
![enter image description here](https://i.stack.imgur.com/6MTbB.png)
n=400
, p=800
, y=750
(10 итераций)
- победитель:
test_gen_sum
![enter image description here](https://i.stack.imgur.com/6s6io.png)
n=400
, p=2000
,y=750
(10 итераций)
- победитель:
test_gen_sum
![enter image description here](https://i.stack.imgur.com/7QnSA.png)
СТАРЫЙ ОТВЕТ
Меньше y
значения
Я бы определенно использовал np.matmul
вместо np.dot
, это даст вам самый большой прирост производительности и фактическидокументация для np.dot
направит вас к np.matmul
для умножения двумерного массива вместо np.dot
.
Я тестировал np.dot
и np.matmul
с и без понимания списка, а также pytest-benchmark результаты приведены здесь:
![y=3](https://i.stack.imgur.com/NyR0E.png)
Кстати pytest-benchmark довольно ловкий, и я настоятельно рекомендую в такие моменты проверять, действительно ли подход действительно эффективен.
Простое использование списочного понимания имеетпочти незначительное влияние на np.matmul
результаты и отрицательное влияние на np.dot
(хотя это и лучшая форма) в схеме вещей, но комбинация обоих изменений дала наилучшие результаты в терминах.Я хотел бы предупредить, что использование списочных представлений обычно приводит к увеличению скорости.девиациявремени выполнения, поэтому вы можете увидеть большие диапазоны производительности, чем если бы вы просто использовали np.matmul
.
Вот код:
import numpy as np
def test_np_matmul_list_comprehension():
random_state = np.random.RandomState(1)
n = p = 1000
X = np.arange(n * n).reshape(p, n)
# The length of weights are not related to X's dims,
# but will always be smaller
y = 3
weights = [1, 1, 1]
# Use list comprehension and np.matmul
matrices_list = [np.matmul(X.T[:, k + 1:],
X[:p - (k + 1), :]) * weights[k] for k in range(y)]
# Sum matrices in list of matrices to get the final result
X_sum = np.sum(matrices_list, axis=0)
Большие y
значения
При больших значениях y
лучше не использовать списки.Среднее / среднее время выполнения имеет тенденцию быть больше как для np.dot
, так и np.matmul
в обоих этих случаях.Вот результаты pytest-benchmark
для (n=500
, p=5000
, y=750
):
![enter image description here](https://i.stack.imgur.com/jP104.png)
Это, вероятно, излишне, но япредпочел бы ошибиться на стороне того, чтобы быть слишком полезным:).