我在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]
为什么会这样?
更新:
答案 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,最后一点意味着此返回张量与初始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_++;
}
评论是我的。
所以:
torch.mm(a,b)
:在每次迭代时,创建一个新的结果张量而不进行初始化(它可能包含NaN-s)。 所以每次迭代都会有返回NaN-s的风险(见上面的警告),torch.mm(prod,a,b)
:由于您未初始化prod
张量,因此存在相同的风险。但是:这种风险仅存在于repeat / until循环的第一次迭代中,因为prod
填充0-s后重新用于后续迭代。所以这就是为什么你没有在这里观察到一个问题(它不那么频繁)。
在案例1中:这应该在Torch级别进行改进,即确保包装器初始化输出(例如使用THDoubleTensor_fill(arg1, 0);
)。
在案例2中:您应该最初初始化prod
并使用torch.mm(prod,a,b)
构造来避免任何NaN问题。
-
编辑:此问题现已修复(请参阅此naive implementation)。