多次元信号処理・テンソル計算用のMATLAB/Octave関数
ちまたでは,多次元配列信号処理と呼ばれるものがひっそりと研究されている.
多次元配列処理を行うためのMATLAB/Octave用の基礎的な関数(unfoldとfold,Nモード積)をつくったので,需要があるか分からないが晒しておく.
ダウンロードはこちらから.
http://dl.dropbox.com/u/525570/d.hatena/tensor_funcs.zip
多次元配列信号処理
多次元配列信号処理が普通の信号処理と異なる点は,多次元配列を扱うことである.
単一のマイクでとった音声はベクトルとして表すことができる.
複数のマイクでとった音声だったり,グレイスケール画象は行列で表すことができる.
ベクトルと行列と言えば線形代数ですよね.
したがって,線形代数の授業ででてきた行列式・一次従属・固有値分解・特異値分解といった手法に基づいて,信号を解析・変換・分解などすることができる.
多次元配列とは行列よりインデックスを持つ配列のことを指す.
例えば,ベクトルの各要素は,行列の各要素はとかける.
この多次元配列では,要素を指す添字を増やすことができて,という感じになる.
ある要素を一つの立方体で表すと,ベクトル・行列・3次テンソルは以下のように描くことができる.
3次テンソルデータの例は動画(縦・横・時間という3つの配列軸を持ち,その中に画素値が入っている)などがある,
RGBの値をもつカラー画像も3次テンソルの中にデータを格納できる(縦・横・RGBという配列軸).
4次以上のテンソルは,想像力豊かに想いを馳せて欲しい.
まぁ,4次テンソルの場合だと3次テンソルが何個かあるようなデータを想像してもらえると良いと思います.
1次,2次という数え方をすると,ベクトルの次元と混同しやすいよね.
英語だとN配列次元はN-wayとか言ったりする.
さらに,それぞれの配列次元の数え方は1モード,2モード,...という感じで呼ばれている.
細かいことを言い出すとボロが出そうなので,もっと知りたい方は,文献
T. G. Kolda, B. W. Bader, ``Tensor Decompositions and applications,'' SIAM review, vol. 51, no. 3, pp. 455-500, 2009.
を参照されたい.
Tensor toolbox
多次元配列処理をMATLABで行うために,Tensor toolboxというツールボックスがフリーで公開されている.
これで,テンソルを定義すると"Whos"をやったときの"Class"が"tensor"になる.
こんな感じ.
>> x = tenrand([10 20 30]); >> whos Name Size Bytes Class Attributes x 10x20x30 48376 tensor
さらに,いろいろなテンソル計算が定義されていて,テンソル分解のアルゴリズムもいくつか実装されている.
ただOctaveでは使えない.
テンソルをつくる
まず,テンソルをつくる.
実は,Tensor toolboxを導入しなくても最近のMATLAB/Octaveは多次元配列をサポートしている.
なので,普通に
x = randn([10 15 20]); y = ones([10 15 20]); z = zeros([10 15 20]);
とすれば,サイズ10×15×20の多次元配列(テンソル)ができる.
UnfoldingとFolding
多次元配列信号処理ではNモード積を多用する.
そのNモード積の計算に必要なのが,UnfoldingとFolding.
Unfoldingとはテンソルをあるモードに沿って並び替えて行列にする操作で,Foldingはその行列を再びテンソルに戻す.
Unfolding
function y = unfold(x, md) % x: input tensor % md: mode for unfolding % y: output matrix d = size(x); if md == 1 d1 = d(1); d2 = prod(d(2:end)); y = reshape(x, d1, d2); else rmode = setxor(1:length(d), md); z = permute(x, [md rmode]); y = reshape(z, d(md), prod(d(rmode))); end
ifで場合分けしている理由は,モード1の展開の場合,permuteが要らないため.
Folding
function y = fold(x, md, s) % x: input matrix % md: mode which the matrix is transposed along % s: dimensions of output tensor % y: output tensor if md == 1 % if 1-mode is chosen for alonging y = reshape(x, s); else Nd = length(s); rmode = setxor(1:length(s), md); z = reshape(x, [s(md) s(rmode)]); y = ipermute(z, [md rmode]); end
テストプログラム
clear all %dim = randperm(3) + 1; dim = [2 3 4]; fprintf('tensor size:'); fprintf(' %d x', dim); fprintf('\b\b \n'); x = reshape(randperm(prod(dim)), dim); fprintf('tensor, x =\n') disp(x) % unfold and fold for ii = 1:length(dim) fprintf('\n') fprintf('Unfolding along mode %d\n', ii) xn = unfold(x, ii); disp(xn) y = fold(xn, ii, dim); max_err = max(abs(vec(x - y))); fprintf('Error between original tensor and unfolded-folded tensor: %f\n', max_err) end
Nモード積
Nモード積
function y = mprod(x, A, md); % x: input tensor % A: matrix for N-mode product % md: mode % y: N-mode producted tensor Xn = unfold(x, md); Yn = A'*Xn; sz = size(x); sz(md) = size(A, 2); y = fold(Yn, md, sz);
テストプログラム
clear all dim = [2 3 4]; x = reshape(randperm(prod(dim)), dim); fprintf('tensor (size:'); fprintf(' %d x', dim); fprintf('\b\b):\n'); disp(x) y = reshape(randperm(dim(2)*10), [dim(2) 10]); fprintf('matrix (size:'); fprintf(' %d x %d):\n', dim(2), 10); disp(y) % mode product z = mprod(x, y, 2); dimt = size(z); fprintf('2-mode product of x by y (size:'); fprintf(' %d x', dimt); fprintf('\b\b):\n'); disp(z)
Tensor toolboxと実行速度の比較
Tensor toolboxと実行速度を比べるために以下のスクリプトを使った.
test_sp.m
clear all addpath('tensor_toolbox') addpath('tensor_toolbox/algorithms') addpath('tensor_toolbox/met') A = reshape(1:6, [3 2]); dim = [2 3 4]; val = randperm(prod(dim)); %% tensor toolbox x = tensor(val, dim); y = ttm(x, A', 2); fprintf('N mode product of tensor x and matrix A:\n') disp(y) clear x y %% my functions and MATLAB built-in functions x = reshape(val, dim); y = mprod(x, A, 2); fprintf('N mode product of tensor x and matrix A:\n') disp(y) clear all %% calculation speed by = 10.^(0:.2:2.6) by = round(by); Nr = 500; t1 = zeros(length(by), Nr); t2 = zeros(length(by), Nr); u1 = zeros(length(by), Nr); u2 = zeros(length(by), Nr); for ii = 1:length(by) dim = [1 1 1]*by(ii); A = rand(dim(2), dim(2)); for jj = 1:Nr; fprintf('%d\r', jj) tic; x = rand(dim); u1(ii, jj) = toc; tic y = mprod(x, A, 2); u2(ii, jj) = toc; clear x y tic; x = tenrand(dim); t1(ii, jj) = toc; tic y = ttm(x, A, 2); t2(ii, jj) = toc; clear x y end fprintf('tensor size:'); fprintf(' %d x', dim); fprintf('\b\b \n'); fprintf(' - Creating random tensor:\n') fprintf('\t %1.3e \t %1.3e\n', mean(t1(ii, :)), mean(u1(ii, :))) fprintf(' - N-mode product:\n') fprintf('\t %1.3e \t %1.3e\n', mean(t2(ii, :)), mean(u2(ii, :))) end figure(1); plot(log(by), log(mean(t1, 2)), '-ob', 'linewidth', 10, 'markersize', 10,... log(by), log(mean(u1, 2)), '-xr', 'linewidth', 10, 'markersize', 10) legend('Tensor toolbox', 'My functions') xlabel('dimensions, log_{10}(N)') ylabel('calculation speed, log_{10}(time) [secs]') print sp_rand.eps -depsc -F:20 figure(2); plot(log(by), log(mean(t2, 2)), '-ob', 'linewidth', 10, 'markersize', 10,... log(by), log(mean(u2, 2)), '-xr', 'linewidth', 10, 'markersize', 10) legend('Tensor toolbox', 'My functions') xlabel('dimensions, log_{10}(N)') ylabel('calculation speed, log_{10}(time) [secs]') print sp_mprod.eps -depsc -F:20
ランダムテンソル(ランダムな値を持つテンソルを)を生成に要する時間と,テンソルと行列のNモード積に要する時間を測定した.
Tensor toolboxではttmという関数を用いた.
計算機はApple Xserve(Intel Xeon (2.93GHz),12GB)で,MATLAB R2010a上で測定した.
これが,ランダムテンソル生成に要した時間
まぁ,似たようなもんですね.