Вопрос или проблема
Я пытаюсь вычислить скалярное произведение между тензорами в numpy.
У меня есть вектор $x$ формы (n,)
и тензор $y$ формы d*(n,)
с d > 1
, и я хотел бы вычислить $\langle y, x^{\otimes d} \rangle$. То есть, я хочу вычислить сумму
$$\langle y, x^{\otimes d} \rangle= \sum_{i_1,\dots,i_d\in\{1,\dots,n\}}y[i_1, \dots, i_d]x[i_1]\dots x[i_d].$$
Рабочая реализация, которую я имею, использует функцию для первоначального вычисления $x^{\otimes d}$, а затем использует np.tensordot
:
def d_fold_tensor_product(x, d) -> np.ndarray:
"""
Вычислить d-кратное тензорное произведение вектора.
"""
assert d > 1, "Порядок тензора должен быть больше 1."
xd = np.tensordot(x, x, axes=0)
for _ in range(d-2):
xd = np.tensordot(xd, x, axes=0)
return xd
n = 10
d = 4
x = np.random.random(n)
y = np.random.random(d * (n,))
result = np.tensordot(y, d_fold_tensor_product(x, d), axes=d)
Существует ли более эффективный и питонический способ? Возможно, без необходимости вычислять $x^{\otimes d}$.
Может быть, не намного эффективнее, но короче:
r=y
for i in range(d):
r=r @ x
Ответ или решение
Вопрос о вычислении скалярного произведения между тензорами в NumPy, особенно в контексте работы с тензорами и векторами, является актуальным для многих специалистов в области информатики и науки о данных. В данной статье мы рассматриваем более эффективный и лаконичный подход к вычислению скалярного произведения между ранг-одним тензором и вектором с помощью функции NumPy.
Постановка задачи
У вас есть вектор ( x ) формы ( (n,) ) и тензор ( y ) формы ( (d, n, \ldots) ) (где ( d > 1 )). Вы намерены вычислить выражение:
[
\langle y, x^{\otimes d} \rangle = \sum_{i_1,\dots,i_d \in {1,\dots,n}} y[i_1, \dots, i_d] x[i_1] \dots x[i_d].
]
Традиционный подход
Ваш изначальный подход заключался в вычислении ( x^{\otimes d} ) с помощью многократного вызова функции np.tensordot
, что действительно может быть не самым оптимальным решением с точки зрения производительности. Однако вы уже предложили более лаконичное решение с использованием оператора @ (матрица) в Python, которое также может просматриваться как преобразование.
r = y
for i in range(d):
r = r @ x
Альтернативный подход: использует NumPy без явного вычисления ( x^{\otimes d} )
Для улучшения читаемости и, возможно, эффективности можно использовать концепцию поэлементного умножения и np.einsum
, который предоставляет разнообразные способы манипуляции с многомерными массивами. Вот как можно вычислить желаемое выражение без явного создания тензора ( x^{\otimes d} ):
import numpy as np
def inner_product_tensor_vector(y, x, d):
return np.einsum('...i,i->...', y, x, optimize=True)
n = 10
d = 4
x = np.random.random(n)
y = np.random.random((n,) * d)
result = inner_product_tensor_vector(y, x, d)
Пояснение
Функция np.einsum
позволяет вам задать, как именно следует осуществить суммирование по индексам, избегая необходимости создавать временные массивы, что может привести к значительным выигрышам в производительности.
'...i,i->...'
говорит о том, что для всех размерностей тензора ( y ) (первый аргумент) и вектора ( x ) (второй аргумент) производится поэлементное умножение, а затем происходит суммирование по индексу ( i ).
Заключение
Оптимизированный подход к вычислению скалярного произведения тензора и вектора позволит вам значительно повысить как эффективность, так и читаемость вашего кода. Использование np.einsum
в данном контексте является более питоническим и соответствует современным стандартам проектирования кода в Python.
Если у вас есть какие-либо дополнительные вопросы или требуется помощь с реализацией, не стесняйтесь задавать их!