Numpy: Что означает выражение Einsum, и есть ли альтернатива?

Вопрос:

Как понять следующее выражение (A — массив [200, 2]):

B = numpy.einsum('...i,...j->...ij',A,A)

И как написать это по-другому, не используя numpy.einsum?

Лучший ответ:

Хорошо, что в основном делает элементное умножение между элементами вдоль последней оси для A для всех пар. Теперь, поскольку это выражение einsum не делает никакого сокращения суммы и просто выполняет работу по broacasting, мы можем избежать этого, вручную расширив входные массивы, чтобы иметь еще одно измерение и позволить вещанию выполнять свою работу. Это было бы более результативным, чем использование einsum и это хорошая причина для поиска альтернативы einsum для такого случая.

Реализация для 2D и даже n-dim A (с использованием эллиптической нотации) будет —

A[...,None,:]*A[...,None]

Пример прогона —

In [71]: A = np.random.rand(3,4,5,6)

In [72]: np.allclose(np.einsum('...i,...j->...ij',A,A), A[...,None,:]*A[...,None])
Out[72]: True

Описанный выше аспект производительности имеет больше смысла, когда ось расширения имеет приличную длину. Таким образом, с A формы (200,2) т.е. 2 в качестве длины оси для последней оси, улучшение с расширением тускнеет и с использованием broadcasting будет меньше/не совсем заметным, но будет видно только с приличной длиной. Позвольте проверить эти аспекты —

In [76]: A = np.random.rand(20000,2)

In [77]: %timeit np.einsum('...i,...j->...ij',A,A)
1000 loops, best of 3: 207 µs per loop

In [78]: %timeit A[...,None,:]*A[...,None]
1000 loops, best of 3: 364 µs per loop

In [79]: A = np.random.rand(200,200)

In [80]: %timeit np.einsum('...i,...j->...ij',A,A)
100 loops, best of 3: 12.1 ms per loop

In [81]: %timeit A[...,None,:]*A[...,None]
100 loops, best of 3: 9.74 ms per loop

Оцените статью
TechArks.Ru
Добавить комментарий