Умножение маскированной матрицы - PullRequest
2 голосов
/ 02 августа 2020

У меня есть матрица A = N x M и другой массив B = N x P x M, где P обычно 9 или 15. Для каждого вектора a из A его необходимо умножить на каждый pi из B той же строки, чтобы получить на выходе размеры N x P.

Я использую numpy и Python и будет выполнять эту операцию на графическом процессоре.

Для небольшого примера пусть N = 4, M = 5, P = 3.

Пусть A будет:

array([[0.18503431, 0.2628188 , 0.26343728, 0.8356702 , 0.47581551],
       [0.70827725, 0.04006919, 0.58975722, 0.90874113, 0.43946412],
       [0.40669507, 0.63328008, 0.95832881, 0.59041436, 0.63578578],
       [0.12129919, 0.74470057, 0.62271405, 0.97760796, 0.6499647 ]])

Пусть B будет:

array([[[4.29031165e-01, 6.17324572e-01, 6.54726975e-02, 1.72218768e-02, 3.53970827e-01],
        [3.38821841e-01, 3.80128792e-01, 7.70995505e-01, 7.38437494e-03, 5.87395036e-02],
        [4.75661932e-01, 3.75617802e-01, 1.28564731e-01, 3.66302247e-01, 6.70953890e-01]],

       [[8.96228996e-02, 1.67135584e-02, 4.56921778e-01, 8.25731354e-01, 7.66242539e-01],
        [5.16651815e-01, 4.27179773e-01, 9.34673912e-01, 2.04687170e-01, 7.68417953e-01],
        [5.90980849e-01, 5.03013376e-01, 8.41765736e-02, 8.08221224e-01, 7.76765422e-01]],

       [[3.25802668e-01, 8.58148960e-01, 9.47505735e-01, 1.01405305e-01, 8.34114717e-01],
        [1.65308159e-01, 9.74572631e-01, 2.69886016e-01, 7.44036253e-02, 4.73350521e-01],
        [8.59030672e-01, 3.96972621e-01, 7.34687493e-01, 2.84647032e-02, 7.19723378e-01]],

       [[1.35751242e-01, 1.74882898e-01, 5.48875709e-01, 7.33443675e-01, 4.05282650e-01],
        [8.41298770e-01, 6.24323279e-01, 5.83482185e-01, 4.28514313e-01, 1.96797205e-01],
        [7.93345700e-04, 3.01441721e-01, 7.59451146e-01, 9.09102382e-01, 7.11518948e-01]]])

Вот как я хочу, чтобы мой вывод был:

[[np.dot(a[0], b[0][0]), np.dot(a[0], b[0][1]), np.dot(a[0], b[0][2])],
[np.dot(a[1], b[1][0]), np.dot(a[1], b[1][1]), np.dot(a[1], b[1][2])],
[np.dot(a[2], b[2][0]), np.dot(a[2], b[2][1]), np.dot(a[2], b[2][2])],
[np.dot(a[3], b[3][0]), np.dot(a[3], b[3][1]), np.dot(a[3], b[3][2])]]

Выполнение этого вручную дает:

[[0.44169455751462816, 0.3998276862221848, 0.845960080871557],
[1.4207326179275017, 1.4579799277670968, 1.564201768913105],
[2.174162453912622, 1.287925491552765, 1.779226448174152],
[1.4689343122491012, 1.4771555510001255, 2.0487088726424365]]

Поскольку я хочу сделать это на графическом процессоре, это, очевидно, требует преобразования моей задачи в матричное умножение (это верно, если я не использую графический процессор также для это важно). Но я точно не знаю, как преобразовать его в это.

Одна идея, которая у меня возникла, заключалась в том, чтобы изменить форму B на Q x M, где Q=NxP. А затем выполняю своего рода разреженное умножение, при котором для каждой строки i булевой разреженной матрицы я включаю (0:P) + P*i -ые элементы. (Рисование имеет смысл), однако я определенно считаю, что есть гораздо более элегантный способ сделать это, поскольку создание разреженных матриц и выполнение операций может занять время, и что разреженность моей матрицы вовсе не случайна.

Как я могу решить эту проблему элегантно.

Обратите внимание, что я не могу выполнять некоторые операции, такие как широковещательная передача / повторение моей A матрицы P раз и выполнение огромного умножения матриц и выбор соответствующих значений, поскольку обычно N и M будут довольно большими (2000 i sh и 256 соответственно), но P будет довольно маленьким, поэтому выполнение глобального умножения матриц для всех векторов означает, что я буду выполнение> 95% ненужных вычислений!.

Ответы [ 2 ]

2 голосов
/ 02 августа 2020

Вы можете использовать здесь einsum, чтобы вычислить это эффективно.

np.einsum('ij,ikj->ik', A, B) # or torch.einsum
>>> np.allclose(np.einsum('ij,ikj->ik', A, B), manual)
True
2 голосов
/ 02 августа 2020
(np.expand_dims(A, 1) * B).sum(axis=2)

своего рода "ручное" умножение матриц. Во-первых, A преобразуется в N x 1 x M. Затем он поэлементно умножается на B. Numpy будет транслировать форму A, чтобы соответствовать B, поэтому в конце мы получим N x P x M. каждая строка будет содержать A[i] * B[i], все, что остается сделать, это просуммировать по последней оси, чтобы получить скалярное произведение.

...