如何在不循环的情况下在 MATLAB 中乘以张量?
How to multiply tensors in MATLAB without looping?
假设我有:
A = rand(1,10,3);
B = rand(10,16);
我想得到:
C(:,1) = A(:,:,1)*B;
C(:,2) = A(:,:,2)*B;
C(:,3) = A(:,:,3)*B;
我能否以某种方式将其乘以一行以使其更快?
如果我像这样创建新的张量 b 会怎样
for i = 1:3
b(:,:,i) = B;
end
我可以将 A 和 b 相乘以获得相同的 C 但速度更快吗?通过上面的循环创建 b 所花费的时间并不重要,因为我将需要 C 来处理许多不同的 A,而 B 保持不变。
编辑: @LuisMendo 指出对于这个特定的用例,这确实是可能的。但是,如果 A 的第一个维度不是 1,则(通常)不可能。
我已经为这个问题苦苦挣扎了一段时间,但一直没能想出解决办法。 bsxfun
可以很好地执行逐元素计算,但不幸的是,张量乘法不受支持。抱歉,祝你好运!
您可以查看 this mathworks file exchange file,这将使您更轻松并支持您正在寻找的行为,但我相信它也依赖于循环。 编辑:它依赖于MEX/C++,所以如果你正在寻找它,它不是一个纯粹的 MATLAB 解决方案。
我不得不同意@GJSein,for循环真的很快
time
0.7050 0.3145
这是定时器功能
function time
n = 1E7;
A = rand(1,n,3);
B = rand(n,16);
t = [];
C = {};
tic
C{length(C)+1} = squeeze(cell2mat(cellfun(@(x) x*B,num2cell(A,[1 2]),'UniformOutput',false)));
t(length(t)+1) = toc;
tic
for i = 1:size(A,3)
C{length(C)+1}(:,i) = A(:,:,i)*B;
end
t(length(t)+1) = toc;
disp(t)
end
置换 A
和 B
的维度,然后应用矩阵乘法:
C = B.'*permute(A, [2 3 1]);
如果 A
是 true 3D 数组,类似于 A = rand(4,10,3)
并假设 B
保持为 2D 数组,则每个A(:,:,1)*B
会产生一个二维数组。
因此,假设您想将这些二维数组作为切片存储在输出数组的第三维中,C
就像这样 -
C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B; and so on.
要以矢量化方式解决此问题,其中一种方法是使用 reshape A
合并第一维和第三维的二维数组,然后执行矩阵乘法。最后,为了使输出大小与前面列出的 C
相同,我们需要最后一步整形。
实现看起来像这样 -
%// Get size and then the final output C
[m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);
样本运行-
>> A = rand(4,10,3);
B = rand(10,16);
C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B;
>> [m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);
>> all(C(:)==out(:)) %// Verify results
ans =
1
根据 , if A
is a 3D array with always a singleton dimension at the start, you can just use squeeze
然后像这样进行矩阵乘法 -
C = B.'*squeeze(A)
假设我有:
A = rand(1,10,3);
B = rand(10,16);
我想得到:
C(:,1) = A(:,:,1)*B;
C(:,2) = A(:,:,2)*B;
C(:,3) = A(:,:,3)*B;
我能否以某种方式将其乘以一行以使其更快?
如果我像这样创建新的张量 b 会怎样
for i = 1:3
b(:,:,i) = B;
end
我可以将 A 和 b 相乘以获得相同的 C 但速度更快吗?通过上面的循环创建 b 所花费的时间并不重要,因为我将需要 C 来处理许多不同的 A,而 B 保持不变。
编辑: @LuisMendo 指出对于这个特定的用例,这确实是可能的。但是,如果 A 的第一个维度不是 1,则(通常)不可能。
我已经为这个问题苦苦挣扎了一段时间,但一直没能想出解决办法。 bsxfun
可以很好地执行逐元素计算,但不幸的是,张量乘法不受支持。抱歉,祝你好运!
您可以查看 this mathworks file exchange file,这将使您更轻松并支持您正在寻找的行为,但我相信它也依赖于循环。 编辑:它依赖于MEX/C++,所以如果你正在寻找它,它不是一个纯粹的 MATLAB 解决方案。
我不得不同意@GJSein,for循环真的很快
time
0.7050 0.3145
这是定时器功能
function time
n = 1E7;
A = rand(1,n,3);
B = rand(n,16);
t = [];
C = {};
tic
C{length(C)+1} = squeeze(cell2mat(cellfun(@(x) x*B,num2cell(A,[1 2]),'UniformOutput',false)));
t(length(t)+1) = toc;
tic
for i = 1:size(A,3)
C{length(C)+1}(:,i) = A(:,:,i)*B;
end
t(length(t)+1) = toc;
disp(t)
end
置换 A
和 B
的维度,然后应用矩阵乘法:
C = B.'*permute(A, [2 3 1]);
如果 A
是 true 3D 数组,类似于 A = rand(4,10,3)
并假设 B
保持为 2D 数组,则每个A(:,:,1)*B
会产生一个二维数组。
因此,假设您想将这些二维数组作为切片存储在输出数组的第三维中,C
就像这样 -
C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B; and so on.
要以矢量化方式解决此问题,其中一种方法是使用 reshape A
合并第一维和第三维的二维数组,然后执行矩阵乘法。最后,为了使输出大小与前面列出的 C
相同,我们需要最后一步整形。
实现看起来像这样 -
%// Get size and then the final output C
[m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);
样本运行-
>> A = rand(4,10,3);
B = rand(10,16);
C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B;
>> [m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);
>> all(C(:)==out(:)) %// Verify results
ans =
1
根据 A
is a 3D array with always a singleton dimension at the start, you can just use squeeze
然后像这样进行矩阵乘法 -
C = B.'*squeeze(A)