簡體   English   中英

將3D矩陣與2D矩陣相乘

[英]Multiply a 3D matrix with a 2D matrix

假設我有一個AxBxC矩陣X和一個BxD矩陣Y

是否有一種非循環方法可以將每個C AxB矩陣與Y相乘?

作為個人喜好,我喜歡我的代碼盡可能簡潔易讀。

這就是我要做的,雖然它不符合你的'無循環'要求:

for m = 1:C

    Z(:,:,m) = X(:,:,m)*Y;

end

這導致A x D x C矩陣Z.

當然,你總是可以通過使用Z = zeros(A,D,C);來預先分配Z以加快速度Z = zeros(A,D,C);

您可以使用NUM2CELL函數在一行中執行此操作,將矩陣X分解為單元格數組,並使用CELLFUN在單元格中操作:

Z = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false);

結果Z1-by-C單元陣列,其中每個單元包含A-by-D矩陣。 如果您希望ZA-by-D-by-C矩陣,則可以使用CAT函數:

Z = cat(3,Z{:});



注意:我的舊解決方案使用MAT2CELL而不是NUM2CELL ,這並不簡潔:

[A,B,C] = size(X);
Z = cellfun(@(x) x*Y,mat2cell(X,A,B,ones(1,C)),'UniformOutput',false);

這是一個單行解決方案(如果你想分成第三維,則為兩個):

A = 2;
B = 3;
C = 4;
D = 5;

X = rand(A,B,C);
Y = rand(B,D);

%# calculate result in one big matrix
Z = reshape(reshape(permute(X, [2 1 3]), [A B*C]), [B A*C])' * Y;

%'# split into third dimension
Z = permute(reshape(Z',[D A C]),[2 1 3]);

因此現在: Z(:,:,i)包含X(:,:,i) * Y


說明:

以上可能看起來令人困惑,但這個想法很簡單。 首先,我從X的第三個維度開始,沿着第一個暗點進行垂直連接:

XX = cat(1, X(:,:,1), X(:,:,2), ..., X(:,:,C))

...困難在於C是一個變量,因此你無法使用catvertcat來推廣該表達式。 接下來我們將其乘以Y

ZZ = XX * Y;

最后,我將其分解為第三維:

Z(:,:,1) = ZZ(1:2, :);
Z(:,:,2) = ZZ(3:4, :);
Z(:,:,3) = ZZ(5:6, :);
Z(:,:,4) = ZZ(7:8, :);

所以你可以看到它只需要一個矩陣乘法,但你必須在前后重新整形矩陣。

我正在接近完全相同的問題,着眼於最有效的方法。 我看到大約有三種方法,沒有使用外部庫(即mtimesx ):

  1. 循環通過3D矩陣的切片
  2. repmat-and-permute魔法
  3. cellfun乘法

我最近比較了所有三種方法,看看哪種方法最快。 我的直覺是(2)將成為勝利者。 這是代碼:

% generate data
A = 20;
B = 30;
C = 40;
D = 50;

X = rand(A,B,C);
Y = rand(B,D);

% ------ Approach 1: Loop (via @Zaid)
tic
Z1 = zeros(A,D,C);
for m = 1:C
    Z1(:,:,m) = X(:,:,m)*Y;
end
toc

% ------ Approach 2: Reshape+Permute (via @Amro)
tic
Z2 = reshape(reshape(permute(X, [2 1 3]), [A B*C]), [B A*C])' * Y;
Z2 = permute(reshape(Z2',[D A C]),[2 1 3]);
toc


% ------ Approach 3: cellfun (via @gnovice)
tic
Z3 = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false);
Z3 = cat(3,Z3{:});
toc

所有這三種方法產生了相同的輸出(p!),但令人驚訝的是,循環是最快的:

Elapsed time is 0.000418 seconds.
Elapsed time is 0.000887 seconds.
Elapsed time is 0.001841 seconds.

請注意,從一個試驗到另一個試驗,時間可能會有很大變化,有時(2)出現的時間最慢。 數據量越大,這些差異就越大。 更大的數據,(3)次(2)。 循環方法仍然是最好的。

% pretty big data...
A = 200;
B = 300;
C = 400;
D = 500;
Elapsed time is 0.373831 seconds.
Elapsed time is 0.638041 seconds.
Elapsed time is 0.724581 seconds.

% even bigger....
A = 200;
B = 200;
C = 400;
D = 5000;
Elapsed time is 4.314076 seconds.
Elapsed time is 11.553289 seconds.
Elapsed time is 5.233725 seconds.

但是如果循環維度比其他維度大得多,則循環方法可能比(2)慢。

A = 2;
B = 3;
C = 400000;
D = 5;
Elapsed time is 0.780933 seconds.
Elapsed time is 0.073189 seconds.
Elapsed time is 2.590697 seconds.

所以(2)在這個(可能是極端的)情況下以一個重要因素獲勝。 可能沒有一種方法在所有情況下都是最佳的,但循環仍然相當不錯,在許多情況下最好。 在可讀性方面也是最好的。 環走!

不。 有幾種方法,但它總是以循環形式出現,直接或間接。

為了取悅我的好奇心,為什么你還要這樣呢?

要回答這個問題, 並且為了便於閱讀,請參閱:

  • ndmult ,ajuanpi(Juan Pablo Carbajal),2013,GNU GPL

輸入

  • 2個陣列
  • 暗淡

 nT = 100;
 t = 2*pi*linspace (0,1,nT)’;

 # 2 experiments measuring 3 signals at nT timestamps
 signals = zeros(nT,3,2);
 signals(:,:,1) = [sin(2*t) cos(2*t) sin(4*t).^2];
 signals(:,:,2) = [sin(2*t+pi/4) cos(2*t+pi/4) sin(4*t+pi/6).^2];

 sT(:,:,1) = signals(:,:,1)’;
 sT(:,:,2) = signals(:,:,2)’;
   G = ndmult (signals,sT,[1 2]);

資源

原始來源。 我添加了內聯評論。

function M = ndmult (A,B,dim)
  dA = dim(1);
  dB = dim(2);

  # reshape A into 2d
  sA = size (A);
  nA = length (sA);
  perA = [1:(dA-1) (dA+1):(nA-1) nA dA](1:nA);
  Ap = permute (A, perA);
  Ap = reshape (Ap, prod (sA(perA(1:end-1))), sA(perA(end)));

  # reshape B into 2d
  sB = size (B);
  nB = length (sB);
  perB = [dB 1:(dB-1) (dB+1):(nB-1) nB](1:nB);
  Bp = permute (B, perB);
  Bp = reshape (Bp, sB(perB(1)), prod (sB(perB(2:end))));

  # multiply
  M = Ap * Bp;

  # reshape back to original format
  s = [sA(perA(1:end-1)) sB(perB(2:end))];
  M = squeeze (reshape (M, s));
endfunction

我強烈建議你使用matlab的MMX工具箱 它可以盡可能快地乘以n維矩陣。

MMX的優點是:

  1. 易於使用。
  2. 乘以n維矩陣 (實際上它可以乘以2-D矩陣的數組)
  3. 它執行其他矩陣運算 (轉置,二次乘法,Chol分解等)
  4. 它使用C編譯器多線程計算來加速。

對於此問題,您只需編寫此命令:

C=mmx('mul',X,Y);

這是所有可能方法的基准。 有關更多詳細信息,請參閱此問題

    1.6571 # FOR-loop
    4.3110 # ARRAYFUN
    3.3731 # NUM2CELL/FOR-loop/CELL2MAT
    2.9820 # NUM2CELL/CELLFUN/CELL2MAT
    0.0244 # Loop Unrolling
    0.0221 # MMX toolbox  <===================

我會考慮遞歸,但這是你可以做的唯一其他非循環方法

您可以“展開”循環,即按順序寫出循環中發生的所有乘法

我想分享我對以下問題的答案:

1)制作兩個張量(任何化合價)的張量積;

2)沿任何維度進行兩個張量的收縮。

以下是我的第一個和第二個任務的子程序:

張力產品:

function [C] = tensor(A,B)
   C = squeeze( reshape( repmat(A(:), 1, numel(B)).*B(:).' , [size(A),size(B)] ) );
end

2)收縮:這里A和B分別是沿着尺寸i和j收縮的張量。 當然,這些尺寸的長度應該相等。 沒有檢查這個(這會使代碼模糊不清)但除此之外它運作良好。

   function [C] = tensorcontraction(A,B, i,j)
      sa = size(A);
      La = length(sa);
      ia = 1:La;
      ia(i) = [];
      ia = [ia i];

      sb = size(B);
      Lb = length(sb);
      ib = 1:Lb;
      ib(j) = [];
      ib = [j ib];

      % making the i-th dimension the last in A
      A1 = permute(A, ia);
      % making the j-th dimension the first in B
      B1 = permute(B, ib);

      % making both A and B 2D-matrices to make use of the
      % matrix multiplication along the second dimension of A
      % and the first dimension of B
      A2 = reshape(A1, [],sa(i));
      B2 = reshape(B1, sb(j),[]);

      % here's the implicit implication that sa(i) == sb(j),
      % otherwise - crash
      C2 = A2*B2;

      % back to the original shape with the exception
      % of dimensions along which we've just contracted
      sa(i) = [];
      sb(j) = [];
      C = squeeze( reshape( C2, [sa,sb] ) );
   end

有評論家嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM