У меня проблема с векторизацией некоторого кода в pytorch.Помогло бы и решение с клочками, но решение с кюветами было бы лучше.Я собираюсь использовать array
и Tensor
взаимозаменяемо.
Проблема, с которой я сталкиваюсь, заключается в следующем:
С учетом двумерного массива с плавающей точкой X
размера (n, x)и логический двумерный массив A
размера (n, n) вычисляют среднее значение для строк в X
, проиндексированных строками в A
.Проблема состоит в том, что строки в A
содержат переменное число True
индексов.
Пример (numpy):
import numpy as np
A = np.array([[0, 1, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0],
[0, 0, 0, 1, 0, 0],
[0, 1, 1, 1, 0, 0]])
X = np.arange(6 * 3, dtype=np.float32).reshape(6, 3)
# Compute the mean in numpy with a for loop
means_np = np.array([X[A.astype(np.bool)[i]].mean(axis=0) for i in np.arange(len(A)])
Так что этот пример работает, но эта формулировка имеет 3проблемы:
Цикл for медленный для больших A
и X
.Мне нужно перебрать несколько 10 тысяч индексов.
Может случиться, что A[i]
не содержит True
индексов.В результате получается np.mean(np.array([]))
, что составляет NaN
.Вместо этого я хочу, чтобы это было 0.
Реализация этого способа в pytorch приводит к SIGFPE (Ошибка с плавающей запятой) во время обратного прохождения обратного распространения через эту функцию.Причина в том, что ничего не выбрано.
Обходное решение, которое я использую сейчас, (см. Также код ниже):
- Установите диагональные элементы *От 1043 * до
True
, чтобы всегда был хотя бы один элемент для выбора - суммы всех выбранных элементов, вычтите значения в
X
из этой суммы (диагональ гарантированно будет False
вначало), и разделите на число True
элементов - 1, зажатых как минимум до 1 в каждом ряду.
Это работает, дифференцируется в pytorch и не производит NaN
, ноМне все еще нужен цикл по всем показателям.Как я могу избавиться от этой петли?
Это мой текущий код pytorch:
import torch
A = torch.from_numpy(A).bytes()
X = torch.from_numpy(X)
A[np.diag_indices(len(A)] = 1 # Set the diagonal to 1
means = [(X[A[i]].sum(dim=0) - X[i]) / torch.clamp(A[i].sum() - 1, min=1.) # Compute the mean safely
for i in range(len(A))] # Get rid of the loop somehow
means = torch.stack(means)
Я не против, если ваша версия выглядит совершенно иначе, если она дифференцируема и дает тот же результат.