Тензорное скалярное произведение с тензором ранга один из вектора

Вопрос или проблема

Я пытаюсь вычислить скалярное произведение между тензорами в 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.

Если у вас есть какие-либо дополнительные вопросы или требуется помощь с реализацией, не стесняйтесь задавать их!

Оцените материал
Добавить комментарий

Капча загружается...