部分行列を利用して行列演算を行なう方法

[OS] ALL
[リリース] ALL
[キーワード] MEMORY

[質問]

SAS/IMLを利用して、データセットから数値を読み込んで行列演算を行なっていますが、たとえば7000×7000の行列X,Yを読み込んで「Z=X*Y」を計算をしようとすると、次のエラーメッセージがログウィンドウに出力されてしまい、計算できません。どのような対処方法がありますか。


ERROR: (execution) Unable to allocate sufficient memory. At least 392000032 more bytes required.

[回答]

SAS/IMLでは、行列の成分を全てメモリ上で保持しています。
7000×7000の行列は、概算で400Mバイトほどのメモリを占有するので、ご質問のような計算をすると1Gバイト以上のメモリを使用することになり、エラーが発生します。対処法としては、Windowsであれば仮想メモリのサイズを上げる、UNIXではMEMSIZEの値を大きなものにするといった方法が考えられます。
ただし、いずれにしても、大きな行列演算を直接行なうためにはメモリを大量に必要とするので、計算が正しく行なわれない場合があります。ここでは、「Z=X*Y」を計算する際に行列Xを「区分け」してから演算を行なう方法を紹介します。具体的には、次のような行列演算の手法を用いています。

image

この方法は、行列Xの一部分のみを順次IMLで読み込んで計算するため、メモリの使用量が少なくてすみ、ハードディスクの容量が十分ある場合には有効な方法です。なお、行列Zはデータセットとして保存されます。

/* IMLの起動 */
proc iml;
/* 行列yは全て読み込む */
use y;
read all into y;
close y;

/* 行列zをデータセットに書き出すために、出力先のデータセットzを作成 */
create z from y;
close z;

/* pは一度に読み込む行列xの行数。大きすぎても小さすぎてもメモリを消費する */
p=150;

/* 行列xxは行列xを区分けしたもの */
do i=1 to ceil(ncol(y)/p);

/* 行列xの一部を読み込む */
use x;
  read point (p*(i-1)+1: min(p*i,ncol(y)) ) into x;
close x;

/* z(の一部)を計算 */
z=x*y;
free x;  /* 行列xを開放 */

/* 計算したzをデータセットzにAPPEND */
edit z;
 append from z;
close z;

free z; /* zを開放 */

end;
quit;