0

合計されたインデックスとそうでないインデックスが繰り返される場合、配列操作を行う最良の方法は何ですか? これらの操作に使用する必要があるようですが、整列されているが合計されていないディメンションのフラグを使用 einsumする代替手段があればより良いでしょう。tensordot

一部の軸を合計せずに整列できることを除いて、テンソルドットのように動作する高速数値ルーチン(おそらくlapackで?)を知っている人はいますか?

==

必要な配列操作のタイプを示すコード例を次に示します。私が必要とする操作はmethod_sum、 、method_einsum、およびによって行われmethod_matmulます。同様の演算は、一致する j 軸で合計され、 および によって実行されmethod2_einsumますmethod2_tensordot

時間を比較すると、最初の問題tensordotを打ち負かすことができるはずです。einsumただし、軸を合計せずに軸を揃える機能はありません。

#import scipy
import scipy as sp

# Shapes of arrays
I = 200
J = 50
K = 200
L = 100

a = sp.ones((I, J, L))
b = sp.ones((J, K, L))


# The desired product has a sum over the l-axis

## Use broadcasting to multiply and sum over the last dimension
def method_sum(a, b):
    "Multiply arrays and sum over last dimension."   
    c = (a[:, :, None, :] * b[None, :, :, :]).sum(-1)
    return c

## Use einsum to multiply arrays and sum over the l-axis
def method_einsum(a, b):
    "Multiply arrays and sum over last dimension."
    c = sp.einsum('ijl,jkl->ijk', a, b)
    return c

## Use matmul to multiply arrays and sum over one of the axes
def method_matmul(a, b):
    "Multiply arrays using the new matmul operation."
    c = sp.matmul(a[:, :, None, None, :], 
                  b[None, :, :, :, None])[:, :, :, 0, 0]
    return c


# Compare einsum vs tensordot on summation over j and l

## Einsum takes about the same amount of time when j is not summed over) 
def method2_einsum(a, b):
    "Multiply arrays and sum over last dimension."
    c = sp.einsum('ijl,jkl->ik', a, b)
    return c

## Tensor dot can do this faster but it always sums over the aligned axes
def method2_tensordot(a, b):
    "Multiply and sum over all overlapping dimensions."
    c = sp.tensordot(a, b, axes=[(1, 2,), (0, 2,)])
    return c

ここに、私のコンピューターでのさまざまなルーチンのいくつかの時間があります。method2Tensordot は、複数のコアを使用するため、einsum を上回ることができます。tensordotJ 軸と L 軸の両方が揃っているが、L 軸のみが合計される計算のようなパフォーマンスを実現したいと考えています。

Time for method_sum:
1 loops, best of 3: 744 ms per loop

Time for method_einsum:
10 loops, best of 3: 95.1 ms per loop

Time for method_matmul:
10 loops, best of 3: 93.8 ms per loop

Time for method2_einsum:
10 loops, best of 3: 90.4 ms per loop

Time for method2_tensordot:
100 loops, best of 3: 10.9 ms per loop
4

1 に答える 1

1
In [85]: I,J,K,L=2,3,4,5
In [86]: a=np.ones((I,J,L))
In [87]: b=np.ones((J,K,L))

In [88]: np.einsum('ijl,jkl->ijk',a,b).shape
Out[88]: (2, 3, 4)

new@演算子をいじってみると、次のものが生成できることがわかりました。

In [91]: (a[:,:,None,None,:]@b[None,:,:,:,None]).shape
Out[91]: (2, 3, 4, 1, 1)

In [93]: (a[:,:,None,None,:]@b[None,:,:,:,None])[...,0,0].shape
Out[93]: (2, 3, 4)

形状は正しいですが、値を確認していません。いくつかは軸にNone沿って並んでおり、そのうちのijk2 つは通常のdot動作を生成します (最後の軸は 2 番目から最後まで)。

あなたのサイズでは、時間はほぼ同じです:

In [97]: np.einsum('ijl,jkl->ijk',a,b).shape
Out[97]: (200, 50, 200)
In [98]: (a[:,:,None,None,:]@b[None,:,:,:,None])[...,0,0].shape
Out[98]: (200, 50, 200)
In [99]: timeit np.einsum('ijl,jkl->ijk',a,b).shape
1 loop, best of 3: 2 s per loop
In [100]: timeit (a[:,:,None,None,:]@b[None,:,:,:,None])[...,0,0].shape
1 loop, best of 3: 2.06 s per loop
于 2016-10-10T17:37:13.597 に答える