TLDR ; j'opterais pour l'utilisation de @Parfait de test_gen_sum
sur la base d'une analyse comparative de diverses valeurs de n
, p
y y
. Je garde l'ancienne réponse ici pour des raisons de continuité. .
Évaluer comment n
, p
, y
influencer le choix de l'algorithme
Cette analyse est faite en utilisant les fonctions de @Parfait comme un moyen de déterminer s'il y a vraiment un meilleure solution ou s'il existe une famille de solutions basées sur des valeurs de n
, p
y 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 itérations)
-
gagnant :
test_gen_sum
-
n=400
, p=2000
, y=3
(100 itérations)
-
gagnant :
test_gen_sum
-
n=400
, p=800
, y=750
(10 itérations)
-
gagnant :
test_gen_sum
-
n=400
, p=2000
, y=750
(10 itérations)
-
gagnant :
test_gen_sum
ANCIENNE RÉPONSE
Plus petit y
valeurs
J'utiliserais certainement np.matmul
au lieu de np.dot
Cela vous permettra d'obtenir le plus gros gain de performance et en fait la documentation pour np.dot
vous dirigera vers np.matmul
pour la multiplication des tableaux 2D au lieu de np.dot
.
J'ai testé les deux np.dot
y np.matmul
avec et sans compréhension de la liste et le pytest-benchmark les résultats sont ici :
Au fait, pytest-benchmark est assez astucieux et je le recommande vivement dans des cas comme celui-ci pour valider si une approche est vraiment performante.
Le simple fait d'utiliser la compréhension des listes a un effet presque négligeable sur les résultats de l'enquête. np.matmul
résultats et un effet négatif sur np.dot
(bien qu'il s'agisse d'une meilleure forme) dans le schéma des choses, mais la combinaison des deux changements a donné les meilleurs résultats en termes. Je tiens à vous avertir que l'utilisation des compréhensions de listes a tendance à augmenter l'écart-type du temps d'exécution, de sorte que vous pouvez constater des écarts plus importants dans les performances du temps d'exécution que si vous utilisiez simplement la fonction np.matmul
.
Voici le code :
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)
Plus grand y
valeurs
Pour des valeurs plus élevées de y
vous feriez mieux de ne pas utiliser la compréhension de liste. Le temps d'exécution moyen/médian tend à être plus important pour les deux types d'applications. np.dot
y np.matmul
dans ces deux cas. Voici les pytest-benchmark
résultats pour ( n=500
, p=5000
, y=750
) :
C'est probablement exagéré, mais je préfère éviter d'être trop utile :).