#performance #algorithm #matlab #nearest-neighbor
#Производительность #алгоритм #matlab #ближайший сосед
Вопрос:
Я выполняю анализ данных с использованием алгоритма k-ближайшего соседа в Matlab. Мои данные состоят примерно из матрицы данных 11795 x 88, где строки являются наблюдениями, а столбцы — переменными.
Моя задача — найти k ближайших соседей для n выбранных контрольных точек. В настоящее время я делаю это по следующей логике:
ДЛЯ всех тестовых точек
LOOP all the data and find the k-closest neighbors (by euclidean distance)
Другими словами, я перебираю все n контрольных точек. Для каждой контрольной точки я ищу данные (которые исключают саму контрольную точку) для k ближайших соседей по евклидову расстоянию. Для каждой контрольной точки это занимает приблизительно k x 11794 итераций. Таким образом, весь процесс занимает около n x k x 11794 итераций. Если n = 10000 и k = 7, это будет приблизительно 825,6 миллионов итераций.
Есть ли более эффективный способ вычисления k ближайших соседей? Большая часть вычислений теперь будет потрачена впустую, потому что мой алгоритм просто:
вычисляет евклидово расстояние до всех остальных точек, выбирает ближайшую и исключает ближайшую точку из дальнейшего рассмотрения -> вычисляет евклидово расстояние до всех остальных точек и выбирает ближайшую -> и т.д. -> и т.д.
Есть ли разумный способ избавиться от этого «вычисления отходов»?
В настоящее время этот процесс занимает около 7 часов на моем компьютере (3,2 ГГц, 8 ГБ ОЗУ, 64-разрядная версия Win 7) … : (
Вот часть логики, проиллюстрированной явно (это не весь мой код, но это та часть, которая съедает производительность):
for i = 1:size(testpoints, 1) % Loop all the test points
neighborcandidates = all_data_excluding_testpoints; % Use the rest of the data excluding the test points in search of the k-nearest neighbors
testpoint = testpoints(i, :); % This is the test point for which we find k-nearest neighbors
kneighbors = []; % Store the k-nearest neighbors here.
for j = 1:k % Find k-nearest neighbors
bdist = Inf; % The distance of the closest neighbor
bind = 0; % The index of the closest neighbor
for n = 1:size(neighborcandidates, 1) % Loop all the candidates
if pdist([testpoint; neighborcandidates(n, :)]) < bdist % Check the euclidean distance
bdist = pdist([testpoint; neighborcandidates(n, :)]); % Update the best distance so far
bind = n; % Save the best found index so far
end
end
kneighbors = [kneighbors; neighborcandidates(bind, :)]; % Save the found neighbour
neighborcandidates(bind, :) = []; % Remove the neighbor from further consideration
end
end
Комментарии:
1. добавьте небольшой пример, чтобы наглядно проиллюстрировать.
2. Это много циклов — что произойдет, если вы просто запустите
pdist2
всю матрицу как один вход, а затем подмножествоn
наблюдений в качестве второй входной матрицы? Может ли ваш компьютер справиться с этим / вы знаете, сколько времени это займет? Потому что тогда вы получаете попарное расстояние для всех элементов, которые вы ищете в одной строке, и найти вершинуk
для каждого из этихn
наблюдений должно быть довольно просто…3. Привет @Dan Я использовал
pdist2
для вычисления расстояний. Это заняло всего менее минуты. Остальное не должно быть проблемой =) Так что это значительное улучшение =)4. @jjepsuomi Нет проблем, я добавил ответ, показывающий, как я бы его использовал
5. @jjepsuomi Также см. Мой ответ об использовании встроенного Matlab
knnsearch
Ответ №1:
Использование pdist2
:
A = rand(20,5); %// This is your 11795 x 88
B = A([1, 12, 4, 8], :); %// This is your n-by-88 subset, i.e. n=4 in this case
n = size(B,1);
D = pdist2(A,B);
[~, ind] = sort(D);
kneighbours = ind(2:2 k, :);
Теперь вы можете использовать kneighbours
для индексации строки A
. Обратите внимание, что столбцы kneighbours
соответствуют строкам B
Но поскольку вы уже погружаетесь в панель инструментов статистики pdist
, почему бы просто не использовать Matlab knnsearch
?
kneighbours_matlab = knnsearch(A,B,'K',k 1);
обратите внимание, что kneighbours
это то же самое, что kneighbours_matlab(:,2:end)'
Комментарии:
1. 1 Спасибо за вашу помощь! =) Когда я закончу реализацию решения, я опубликую расчетное время выполнения =)
2. Привет @Dan Я решил использовать этот
pdist2
подход. Время выполнения теперь составляет около 30 секунд =) так что примерно в 840 раз быстрее времени выполнения x)
Ответ №2:
Я не знаком с конкретными функциями matlab, но вы можете удалить k из своей формулы.
Существует хорошо известный алгоритм выбора, который
- принимает массив A (размера n) и номер k в качестве входных данных.
- Дает перестановку массива A таким образом, что k-й самый большой / самый маленький элемент находится на k-м месте.
- Меньшие элементы находятся слева, большие — справа.
например
A=2,4,6,8,10,1,3,5,7,9; k=5
output = 2,4,1,3,5,10,6,8,7,9
Это выполняется за O (n) шагов и не зависит от k.
EDIT1: вы также можете предварительно вычислить все расстояния, поскольку похоже, что это место, где вы проводите большую часть вычислений. Это будет примерно 800-миллиметровая матрица, так что это не должно быть проблемой на современных машинах.
Комментарии:
1. Теперь, когда я взглянул на вопрос. Сначала вы должны попробовать ИЗМЕНИТЬ предложение, поскольку его проще реализовать. Помните, что dist[i,j] = dist[j,i]
2. 1 Спасибо за помощь! =) Я попробую предложения и сообщу всем об улучшении времени =)
Ответ №3:
Я не уверен, ускорит ли это код, но он удаляет два внутренних цикла
for i = 1:size(testpoints, 1) % //Loop all the test points
temp = repmat(testpoints(i,:),size(neighborcandidates, 1),1);
euclead_dist = (sum((temp - neighborcandidates).^2,2).^(0.5));
[sort_dist ind] = sort(euclead_dist);
lowest_k_ind = ind(1:k);
kneighbors = neighborcandidates(lowest_k_ind, :);
neighborcandidates(lowest_k_ind, :) = [];
end
Ответ №4:
Разве это не сработает?
adjk = adj;
for i=1:k-1
adj_k = adj_k*adj;
end
kneigh = find(adj_k(n,:)>0)
учитывая узел n и индекс k?
Ответ №5:
Возможно, это более быстрый код в контексте Matlab. Вы также можете попробовать параллельные функции, индексы данных и приближенные алгоритмы ближайшего соседа, чтобы быть теоретически более эффективными.
% a slightly faster way to find k nearest neighbors in matlab
% find neighbors for data Y from data X
m=size(X,1);
n=size(Y,1);
IDXs_out=zeros(n,k);
distM=(repmat(X(:,1),1,n)-repmat(Y(:,1)',m,1)).^2;
for d=2:size(Y,2)
distM=distM (repmat(X(:,d),1,n)-repmat(Y(:,d)',m,1)).^2;
end
distM=sqrt(distM);
for i=1:k
[~,idx]=min(distM,[],1);
id=sub2ind(size(distM),idx',(1:n)');
distM(id)=inf;
IDXs_out(:,i)=idx';
end