Ошибка Arrayfire с условными выражениями

Я пытаюсь насытить свои массивы в ArrayFire. Я хочу, чтобы все значения больше 0,75 насыщали до 1,0, а все менее 0,25 — до 0,0. Я использую следующие выражения.

a(a > 0.75) = 1.0;
a(a < 0.25) = 0.0;

Вот тип af :: array. Это работает некоторое время, но как только я получаю массив, в котором нет значений больше 0,75, я получаю следующее исключение.

terminate called after throwing an instance of 'af::exception'
what():  ArrayFire Exception (Invalid input size:203):
In function verifyDims
In file src/api/c/data.cpp:36
Invalid dimension for argument 1
Expected: ndims >= 1

In function af::array af::constant(T, const af::dim4&, af::dtype) [with T = double; af::dtype = af_dtype]
In file src/api/cpp/data.cpp:28

Если я позвоню af::print("", a > 0.75); Я получаю следующий вывод прямо перед сбоем.

[10 1 1 1]
0
0
0
0
0
0
0
0
0
0

Каким-то образом он видит, что в этом массиве все нули (что должно быть, поскольку non больше 0,75), а затем говорит, что размерность равна нулю? Это что-то я делаю неправильно или это ошибка в их коде?

Следующий код, кажется, исправляет это, но я чувствую, что это решение несколько неэффективно.

af::array bellow = a[levels - 1] < 0.25f;
af::array above = a[levels - 1] > 0.75f;

if(af::anyTrue<bool>(above))
a[levels - 1](above) = 0.75f;

if(af::anyTrue<bool>(bellow))
a[levels - 1](bellow) = 0.25f;

Для тех из вас, кто хочет увидеть всю функцию, я делаю градиент приличный в нейронной сети. А на самом деле и массив типа af :: array. Я упустил это, чтобы упростить вопрос.

void train(const float* in, const float* expected_out, float learning_rate)
{
std::unique_ptr<af::array[]> a(new af::array[levels]),
z(new af::array[levels]), d(new af::array[levels]);

af::array in_array(inputs, in);
af::array y(dims[levels - 1], expected_out);

z[0] = af::matmul(weights[0], in_array) + biases[0];
a[0] = sigma(z[0]);for(size_t i = 1; i < levels; i++)
{
z[i] = af::matmul(weights[i], a[i - 1]) + biases[i];
a[i] = sigma(z[i]);
}a[levels - 1](a[levels - 1] < 0.25f) = 0.0f;
a[levels - 1](a[levels - 1] > 0.75f) = 1.0f;

d[levels - 1] = (y - a[levels - 1]) * sigma_prime(z[levels - 1]);
for(size_t i = levels - 1; i-- > 0;)
d[i] = af::matmul(weights[i + 1].T(), d[i + 1]) * sigma_prime(z[i]);

for(size_t i = 0; i < levels; i++)
{
biases[i] += learning_rate * d[i];
weights[i] += learning_rate * af::matmul(d[i], (i ? a[i - 1] : in_array).T());
}
}

2

Решение

Ошибка, которую вы видите, из-за этого открытый баг с массивами нулевой длины (РЕДАКТИРОВАТЬ: Исправлено с v3.4.0). Это распространенная проблема, которую мы пытаемся исправить в течение некоторого времени.

Вот обходной путь для вашего случая. Вам даже не нужно индексировать, чтобы достичь того, что вы пытаетесь сделать.

a[levels - 1] = af::min(0.75, af::max(0.25, a[levels - 1]));

РЕДАКТИРОВАТЬ: Начиная с 3.4, вы можете сделать следующее для достижения той же функциональности в arrayfire:

a[levels - 1] = af::clamp(a[levels - 1], 0.25, 0.75);

Этот метод намного быстрее, чем индексирование для вашего случая.


Тем не менее, есть некоторые случаи, когда вы не можете использовать af::min а также af::max заменить индексацию. В этих случаях вы можете сделать что-то вроде этого как обходной путь:

af::array cond = arr < some_val;
arr = arr * (1 - cond) + cond * other_val;

Это также должно быть быстрее, чем индексирование. Однако арифметика не будет работать, если массивы NAN в них и ты пытаешься их заменить. В этом случае вы можете вернуться к одной из следующих функций.

Используя select (использует дополнительную память):

arr = af::select(af::isNaN(arr), arr, other_val));

Используя replace (заменяет на месте, дополнительная память не используется):

af::replace(arr, af::isNaN(arr) other_val));

Однако некоторые тесты показали, что select а также replace в некоторых случаях может быть медленнее, чем индексирование (которое мы пытаемся исправить). Таким образом, вы можете попробовать использовать следующую работу для индексации, если select / replace медленно в вашем алгоритме.

af::array idx = af::where(af::isNaN(arr));
if (idx.elements()) arr(idx) = replace_val;

Обратите внимание, что индексация на логическом af::array звонки af::where внутренне. Так что это так же эффективно, как следующие

arr(arr < some_val) = other_val;

с дополнительным преимуществом не подведет для массивов нулевого размера.

РЕДАКТИРОВАТЬ: Добавлены дополнительные обходные пути для потомков.

4

Другие решения

Других решений пока нет …