2014-05-10 3 views
3

numpy에서 행렬 연산을 찾으려고하는데 다음 계산이 빨라질 것입니다.Python Numpy 행렬의 고차원 곱하기

두 개의 3D 행렬 AB이 있습니다. 첫 번째 차원은 예제를 나타내며 둘 다 n_examples 예제를 갖습니다. 내가 달성하고자하는 제품을 A와 B의 각 예제를 점하고 결과를 요약하는 것입니다

import numpy as np 

n_examples = 10 
A = np.random.randn(n_examples, 20,30) 
B = np.random.randn(n_examples, 30,5) 
sum = np.zeros([20,5]) 
for i in range(len(A)): 
    sum += np.dot(A[i],B[i]) 

답변

3

이가이다 np.tensordot()의 일반적인 응용 프로그램 :

sum = np.tensordot(A, B, [[0,2],[0,1]]) 

타이밍

다음 코드를 사용 :

import numpy as np 

n_examples = 100 
A = np.random.randn(n_examples, 20,30) 
B = np.random.randn(n_examples, 30,5) 

def sol1(): 
    sum = np.zeros([20,5]) 
    for i in range(len(A)): 
     sum += np.dot(A[i],B[i]) 
    return sum 

def sol2(): 
    return np.array(map(np.dot, A,B)).sum(0) 

def sol3(): 
    return np.einsum('nmk,nkj->mj',A,B) 

def sol4(): 
    return np.tensordot(A, B, [[2,0],[1,0]]) 

def sol5(): 
    return np.tensordot(A, B, [[0,2],[0,1]]) 

결과 : 내 컴퓨터에

timeit sol1() 
1000 loops, best of 3: 1.46 ms per loop 

timeit sol2() 
100 loops, best of 3: 4.22 ms per loop 

timeit sol3() 
1000 loops, best of 3: 1.87 ms per loop 

timeit sol4() 
10000 loops, best of 3: 205 µs per loop 

timeit sol5() 
10000 loops, best of 3: 172 µs per loop 

tensordot()이었다 가장 빠른 솔루션 및 순서 그 변경 축 평가 결과가 변경되지 않았습니다. 성능.

+0

자세한 답변을 보내 주셔서 감사합니다.그것은 내 컴퓨터에서도 가장 빠른 솔루션을 만들어냅니다!. 그러나'20x30','30x5'에서'600x300','300x10'까지 매트릭스 크기를 늘리면'sol1()'이 다시 가장 빠르며'tensordot'보다 5 배 빠릅니다. 왜 파이썬에서 루핑이'tensordot'와 같은 네이티브 C 구현보다 빠를지 의아하게 생각합니다. – aha

+0

@aha, 저 역시 놀랍습니다.'tensordot()'가 더 빠를 것으로 기대합니다. 'sol4()'와'sol5()'를 비교해 축의 평가 순서를 변경 했습니까? 아마도 이것은 차이를 만들 수 있습니다 ... –

+1

'600x300','300x10','sol1()'은'16.5ms','sol4()'는'113ms','sol5()'는' 89ms' – aha

2

하, 그것은 단지 하나의 라인으로 수행 할 수 있습니다 np.einsum('nmk,nkj->mj',A,B)를.

참조 아인슈타인 요약 : numpy multiply matrices preserve third axis

은 변수 sum 이름을하지 마십시오 http://docs.scipy.org/doc/numpy/reference/generated/numpy.einsum.html

하지 동일한 문제가 있지만, 아이디어가 꽤 많이 동일합니다, 우리가 설명이 항목의 토론과 다른 방법을 참조 빌드 sum을 덮어 씁니다.

@Jaime이 지적했듯이 루프는 실제로 이러한 크기의 차원에서 더 빠릅니다. 사실 솔루션은 map을 기반으로 sum 간단는, 심지어 느린 불구하고 있습니다 :

n_examples = 1000 
A = np.random.randn(n_examples, 20,1000) 
B = np.random.randn(n_examples, 1000,5) 

그리고 :

In [19]: 

%%timeit 
SUM = np.zeros([20,5]) 
for i in range(len(A)): 
    SUM += np.dot(A[i],B[i]) 
10000 loops, best of 3: 115 µs per loop 
In [20]: 

%timeit np.array(map(np.dot, A,B)).sum(0) 
1000 loops, best of 3: 445 µs per loop 
In [21]: 

%timeit np.einsum('nmk,nkj->mj',A,B) 
1000 loops, best of 3: 259 µs per loop 

큰 치수가 다른

In [46]: 

%%timeit 
SUM = np.zeros([20,5]) 
for i in range(len(A)): 
    SUM += np.dot(A[i],B[i]) 
1 loops, best of 3: 191 ms per loop 
In [47]: 

%timeit np.array(map(np.dot, A,B)).sum(0) 
1 loops, best of 3: 164 ms per loop 
In [48]: 

%timeit np.einsum('nmk,nkj->mj',A,B) 
1 loops, best of 3: 451 ms per loop 
+1

문제 크기에 대한 OP 코드보다 50 % 더 느리고 실제로 큰 입력에 대해서는 훨씬 더 나쁩니다. – Jaime

+0

더 큰 차원을위한 약간 더 빠른 방법으로 인상적인 것은 아닙니다. 'einsum '은 더 느리게된다. 지금 잠 들어야하고 서해안의 해결책으로 깨달을 수 있기를 바랍니다. : P –

+0

가 텐 드로도보다 느립니다? – Martian2049