関数を調べるkmeans.m
と、余弦距離のコードは、メモリ不足エラーをスローする可能性のある2つのクリティカルセクションに要約されます。まず、関係する主な変数を紹介します。
X
:行は観測ベクトル、列はディメンション(データ)
C
:行は重心、列はディメンション(クラスター重心)
1)
最初のコードは、データ行を単位長に正規化しています(これは、間違った理由で、@ Johnの削除された回答で以前に指摘されていました):
[n,p] = size(X); %# in your case, X is a matrix of size 1000000x1000
Xnorm = sqrt(sum(X.^2, 2)); %# norm of each instance vector
X = X ./ Xnorm(:,ones(1,p)); %# normalize to unit length
上記は、ONEインデックスを使用して操作をベクトル化し、データが持つ列の数だけノルムベクトルを繰り返してから、要素ごとの除算を実行しようとします。このようなアプローチの問題を理解するには、変数のサイズを確認してください。
>> whos X Xnorm
Name Size Bytes Class Attributes
X 1017564x1000 83056640 double sparse
Xnorm 1017564x1 12210776 double sparse
したがって、明らかにメモリ不足エラーの原因となるXnorm(:,ones(1,p))
サイズの一時マトリックスを割り当てようとします。12210776*1000 bytes = 11.3722 GB
(興味のある人のために、二重スパース行列X
は内部的12*nnz(X) + 4*size(X,2) bytes
にストレージを必要としますが、完全な表現は必要prod(size(X))*8 bytes
です。あなたの場合、それは約80MBであるのに対し、11.5GBのメモリが必要です!)
この行は、通常はベクトル化の欠点である巨大なスペース要件を回避する、別の(おそらく遅い)方法で記述されている可能性があります。各行をループして、標準で除算するだけです。さらに良いことに、そのような場合のために特別に設計されたBSXFUN関数を使用できます(REPMATとインデックス作成のトリックの使用を回避します)。
X = bsxfun(@rdivide, X, Xnorm);
面白いことに、KMEANSファイルの他の場所にコメント付きのコードセクションがあり、この問題が明確に考慮されているため、低速のforループを使用することを選択しましたが、メモリが不足しないことが保証されています...
2)
2番目のクリティカルセクションは、距離の実際の計算が行われる場所です。対象となるコードは次のとおりです。
n = size(X,1);
nclusts = size(C,1);
D = zeros(n,nclusts);
for i = 1:nclusts
D(:,i) = max(1 - X*C(i,:)', 0);
end
基本的に、すべてのクラスター重心(データベクトル全体に対して一度に1つの重心)を使用して、各データインスタンスの内積を計算します。繰り返しになりますが、これが問題を引き起こす可能性がある場合は、ベクトル化された製品を次のようなステップバイステップのループに展開するだけです。
for i = 1:nclusts
for j = 1:n
D(j,i) = max(1 - dot(X(j,:),C(i,:)), 0);
end
end
だからあなたはアイデアを得る。行列が非常に大きい場合は、大きな中間結果を生成する操作に注意し、可能な場合は、より小さなスケールで動作する明示的なループに置き換える必要があります。
ところで、ユークリッド距離を使用する場合、単一行のベクトル化されたソリューションではなくループで記述されているため、同じ問題は発生しません。距離関数を計算するセクションは次のとおりです。
for i = 1:nclusts
D(:,i) = (X(:,1) - C(i,1)).^2;
for j = 2:p
D(:,i) = D(:,i) + (X(:,j) - C(i,j)).^2;
end
% D(:,i) = sum((X - C(repmat(i,n,1),:)).^2, 2); %# <--- commented code
end
それでも、BSXFUNが代わりに使用されなかったことに驚いています。
for i=1:nclusts
D(:,i) = sum(bsxfun(@minus, X, C(i,:)).^2, 2);
end
完了するまで、データ全体をクラスター化しようとはしていません。私は4GBの32ビットマシンで実行しています(MATLABはアーキテクチャの制限により3GBにしかアクセスできません)。提案された変更が実際に64ビット/ 8GBハードウェアに違いをもたらすかどうかを報告してください;)