在Lua Torch中,两个零矩阵的乘积具有nan条目

时间:2015-10-09 21:50:10

标签: lua torch

我在Lua / Torch中遇到过torch.mm函数的奇怪行为。这是一个演示问题的简单程序。

iteration = 0;
a = torch.Tensor(2, 2);
b = torch.Tensor(2, 2);
prod = torch.Tensor(2,2);

a:zero();
b:zero();

repeat
   prod = torch.mm(a,b);
   ent  = prod[{2,1}]; 
   iteration = iteration + 1;
until ent ~= ent

print ("error at iteration " .. iteration);
print (prod);

该程序由一个循环组成,其中程序将两个零2x2矩阵相乘,并测试产品矩阵的入口 ent 是否等于nan。程序似乎应该永远运行,因为产品应该总是等于0,因此ent应该是0.但是,程序打印:

error at iteration 548   
0.000000 0.000000
nan nan
[torch.DoubleTensor of size 2x2]

为什么会这样?

更新:

  1. 如果我用 torch.mm(prod,a,b)替换 prod = torch.mm(a,b),问题就会消失,这表明某事是内存分配错误。
  2. 我的Torch版本是在没有BLAS& amp ;; LAPACK库。在我用OpenBLAS重新编译火炬后,问题就消失了。但是,我仍然对它的原因感兴趣。

1 个答案:

答案 0 :(得分:2)

可以找到为torch.mm自动生成Lua包装器的代码部分{。{3}}。

当你在循环中编写prod = torch.mm(a,b)时,它对应于幕后的以下C代码(由于here而由此包装器生成):

/* this is the tensor that will hold the results */
arg1 = THDoubleTensor_new(); 
THDoubleTensor_resize2d(arg1, arg5->size[0], arg6->size[1]);
arg3 = arg1;
/* .... */
luaT_pushudata(L, arg1, "torch.DoubleTensor");
/* effective matrix multiplication operation that will fill arg1 */
THDoubleTensor_addmm(arg1,arg2,arg3,arg4,arg5,arg6);

所以:

  • 创建新的结果张量并使用适当的尺寸调整大小
  • 此新张量未初始化,即此处没有calloc或显式填充,因此它指向垃圾内存且可能包含NaN-s,
  • 这个张量被推到堆栈上,以便在Lua一侧作为返回值。

最后一点意味着此返回张量与初始prod不同(即在循环内,prod遮蔽初始值)。

另一方面,调用torch.mm(prod,a,b) 确实使用您的初始prod张量来存储结果(在这种情况下,无需创建专用张量) )。因为在你的代码片段中你没有用给定的值初始化/填充它,它也可能包含垃圾。

在这两种情况下,核心操作都是gemm乘法,如C = beta * C + alpha * A * B,其中beta = 0且alpha = 1。 cwrap看起来像这样:

  real *a_ = a;
  for(i = 0; i < m; i++)
  {
    real *b_ = b;
    for(j = 0; j < n; j++)
    {
      real sum = 0;
      for(l = 0; l < k; l++)
        sum += a_[l*lda]*b_[l];
      b_ += ldb;
      /*
       * WARNING: beta*c[j*ldc+i] could give NaN even if beta=0
       *          if the other operand c[j*ldc+i] is NaN!
       */
      c[j*ldc+i] = beta*c[j*ldc+i]+alpha*sum;
    }
    a_++;
  }

评论是我的。

所以:

  1. with torch.mm(a,b):在每次迭代时,创建一个新的结果张量而不进行初始化(它可能包含NaN-s)。 所以每次迭代都会有返回NaN-s的风险(见上面的警告),
  2. torch.mm(prod,a,b):由于您未初始化prod张量,因此存在相同的风险。但是:这种风险仅存在于repeat / until循环的第一次迭代中,因为prod填充0-s后重新用于后续迭代。
  3. 所以这就是为什么你没有在这里观察到一个问题(它不那么频繁)。

    在案例1中:这应该在Torch级别进行改进,即确保包装器初始化输出(例如使用THDoubleTensor_fill(arg1, 0);)。

    在案例2中:您应该最初初始化prod并使用torch.mm(prod,a,b)构造来避免任何NaN问题。

    -

    编辑:此问题现已修复(请参阅此naive implementation)。