Если вы проверяете функцию kmeans.m
, то код косинусного расстояния сводится к двум критическим разделам, которые могут вызвать ошибки нехватки памяти.Сначала позвольте мне представить основные переменные:
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
. В вашем случае это около 80 МБ против 11,5Требуется ГБ памяти!)
Эта строка могла бы быть написана другим (возможно, более медленным) способом, который позволяет избежать огромного пространства, которое обычно является обратной стороной векторизации.Просто зациклите каждый ряд и разделите на норму.Более того, мы можем использовать функцию BSXFUN, которая была специально разработана для таких случаев (избегая использования REPMAT и трюков индексирования):
X = bsxfun(@rdivide, X, Xnorm);
Самое смешное, что в других местах есть закомментированные участки кодафайла KMEANS, где эта проблема была четко рассмотрена, и поэтому он решил использовать более медленный цикл for, но гарантированно не исчерпал память ...
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
По сути, он вычисляет внутреннее произведение каждого экземпляра данных для каждого центроида кластера (по одному центроиду за раз по всем векторам данных).Опять же, в случае, если это вызывает проблему, вы можете просто развернуть векторизованный продукт в пошаговый цикл, например:
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
Обратите внимание, что у меня нетне пытался кластеризовать все данные до завершения.Я работаю на 32-разрядной машине с 4 ГБ (из которых MATLAB может получить доступ только к 3 ГБ из-за ограничений архитектуры), поэтому, пожалуйста, сообщите, действительно ли предлагаемые изменения имеют значение для вашего 64-разрядного / 8 ГБ оборудования;)