Woodbury行列反転公式を使って、逆行列演算を高速化。

回帰の問題などでは(X'X)^-1をよく使う。
Xが横長であるときX'Xが非常に大きな行列になるので、
逆行列を求めるのが大変で高速化が重要になる。

そこで、Woodbury行列反転公式(Woodbury matrix inversion formula)
(A+BCD)^-1 = A^-1 - A^-1 B(C^-1+DA^-1 B)^-1DA^-1
という超有名公式を使って、どれくらい高速化できるか実験した。

コード

%% setting
nLoop = 10;
h = 100;    %1000;
w = 2000; %1000;
X = rand(h,w);

%% normal
tic;
for i = 1:nLoop
    Y = X'*X + 0.05*eye(w,w);
    Yi = inv(Y);
end
t1 = (toc/nLoop);

%% woodbury matrix inversion formula
%(A+BCD)^-1 = A^-1 - A^-1 B(C^-1+DA^-1 B)^-1DA^-1
tic;
for i = 1:nLoop
    Ai = 1/0.05*eye(w,w);
    XAi = X*Ai;
    Yi = Ai - Ai*X'/(XAi*X')*XAi;
end
t2 = (toc/nLoop);

%%
disp([t1 t2]);

結果

h=100、w= 100のとき、通常 0.0009s 高速化 0.0012s
h=100、w= 500のとき、通常 0.0320s 高速化 0.0205s
h=100、w=1000のとき、通常 0.1977s 高速化 0.0625s
h=100、w=2000のとき、通常 1.3256s 高速化 0.1901s

通常の場合に逆行列を求める際にinv()を使用しているが、実際の回帰の問題などではバックスラッシュ(/)などの演算子を使うので、もう少し早くなると思われる。

結論

行列の縦横比が5以上であれば、高速化の価値があると思われる。

追記(2013-02-25)

コメントで指摘をもらったので,訂正.
実験に使ったコードの21行目は

    Yi = Ai - Ai*X'/(XAi*X')*XAi;

ではなく,正しくは

    Yi = Ai - Ai*X'/(eye(h) + XAi*X')*XAi;

結果自体にはそれほど影響はないかと.