我试图在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
它是否以某种方式看到这个数组都是零(它应该是因为非大于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;
对于那些想要看到整个功能的人,我在神经网络中做渐变体面。 a实际上是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());
}
}
答案 0 :(得分:4)
您看到的错误是由于此open bug about zero length arrays(编辑:从v3.4.0开始的FIxed)。这是一个普遍存在的问题,我们现在正试图解决一段时间。
以下是针对您案例的解决方法。你甚至不需要编制索引来实现你想要做的事情。
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));
使用替换(替换原位,不使用额外的内存):
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;
还有一个额外的好处,即零大小的数组没有失败。
编辑:为后代添加了其他解决方法。