【问题标题】:In Lua Torch, the product of two zero matrices has nan entries在 Lua Torch 中,两个零矩阵的乘积有 nan
【发布时间】:2015-10-11 15:49:59
【问题描述】:

我在 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。似乎程序应该永远运行,因为 product 应该总是等于 0,因此 ent 应该是 0。但是,程序打印:

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

为什么会这样?

更新:

  1. 如果我将 prod = torch.mm(a,b) 替换为 torch.mm(prod,a,b),问题就消失了,这表明存在问题内存分配错误。
  2. 我的 Torch 版本是在没有 BLAS 和 LAPACK 库的情况下编译的。在我用 OpenBLAS 重新编译了 torch 之后,问题就消失了。不过,我仍然对其原因感兴趣。

【问题讨论】:

  • 了解更多有关您的环境的信息会很有用,例如硬件、操作系统、torch/distro 的 SHA 和解释器(LuaJIT 2.0?2.1?还是 Lua 5.1?5.2?)。
  • 这是我的环境信息:Linux CentOS、LuaJIT 2.0.3、Torch 7。Torch 是在没有 LAPACK 和 BLAS 库的情况下编译的。在我用 OpenBLAS 重新编译后,问题就消失了。但我仍然对它的原因感兴趣。

标签: lua torch


【解决方案1】:

torch.mm 自动生成 Lua 包装器的代码部分可以在here 找到。

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

/* 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 张量来存储结果(在这种情况下,在幕后不需要创建专用张量)。由于在您的代码 sn-p 中您没有使用给定值初始化/填充它,因此它也可能包含垃圾。

在这两种情况下,核心运算都是gemm 乘法,例如 C = beta * C + alpha * A * B,beta=0 和 alpha=1。 naive implementation 看起来像这样:

  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. torch.mm(a,b):在每次迭代中,都会创建一个新的结果张量而不进行初始化(它可能包含NaN-s)。 所以每次迭代都有返回 NaN-s 的风险(见上面的警告),
  2. torch.mm(prod,a,b):存在相同的风险,因为您没有初始化prod 张量。但是:这种风险只存在于重复/直到循环的第一次迭代中,因为在 prod 之后立即填充 0-s 并重新用于后续迭代。

所以这就是为什么你在这里没有发现问题(它不太频繁)。

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

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

--

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

【讨论】:

  • 感谢您的回答和修复!
猜你喜欢
  • 2013-03-31
  • 2017-08-25
  • 2019-09-22
  • 2020-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-05
相关资源
最近更新 更多