ユークリッド距離のKNNを使用して単純なデータを分類する作業をしています。以下に示すように、MATLAB knnsearch
関数を使用して実行したいことの例を確認しました。
load fisheriris
x = meas(:,3:4);
gscatter(x(:,1),x(:,2),species)
newpoint = [5 1.45];
[n,d] = knnsearch(x,newpoint,'k',10);
line(x(n,1),x(n,2),'color',[.5 .5 .5],'marker','o','linestyle','none','markersize',10)
上記のコードは新しい点、つまり[5 1.45]
そして、新しいポイントに最も近い10個の値を見つけます。だれでも、knnsearch
関数の機能の詳細な説明を含むMATLABアルゴリズムを教えてもらえますか?これを行う他の方法はありますか?
K最近傍(KNN)アルゴリズムの基礎は、N
行とM
列で構成されるデータマトリックスがあることです。ここで、N
はデータポイントの数であり、M
は各データポイントの次元です。たとえば、データマトリックス内にデカルト座標を配置した場合、これは通常_N x 2
_または_N x 3
_マトリックスです。このデータマトリックスを使用してクエリポイントを指定し、このデータマトリックス内でこのクエリポイントに最も近いk
ポイントを検索します。
通常、クエリとデータマトリックス内の残りのポイントとの間のユークリッド距離を使用して、距離を計算します。ただし、L1やシティブロック/マンハッタン距離などの他の距離も使用されます。この操作の後、クエリとデータセット内の対応する各ポイントとの間の距離を表すN
ユークリッド距離またはマンハッタン距離が得られます。これらを見つけたら、距離を昇順に並べ替えて、データセットとクエリの間の距離が最小のk
ポイントを取得することにより、クエリに最も近いk
ポイントを検索するだけです。
データマトリックスがx
に格納されており、newpoint
がM
列を持つサンプルポイントである場合(つまり、_1 x M
_)は、ポイント形式で従う一般的な手順です。
newpoint
とx
のすべての点の間のユークリッド距離またはマンハッタン距離を求めます。k
に最も近いx
内のnewpoint
データポイントを返します。各ステップをゆっくり実行しましょう。
誰かがこれを行う方法の1つは、おそらく次のようなfor
ループにあります。
_N = size(x,1);
dists = zeros(N,1);
for idx = 1 : N
dists(idx) = sqrt(sum((x(idx,:) - newpoint).^2));
end
_
マンハッタン距離を実装したい場合、これは単に次のようになります:
_N = size(x,1);
dists = zeros(N,1);
for idx = 1 : N
dists(idx) = sum(abs(x(idx,:) - newpoint));
end
_
dists
は、N
とx
の各データポイント間の距離を含むnewpoint
要素ベクトルです。 newpoint
とx
のデータポイントの間で要素ごとの減算を行い、差を二乗してから、 sum
それらをすべて一緒にします。この合計は平方根となり、ユークリッド距離が完成します。マンハッタン距離の場合、要素ごとに減算を実行し、絶対値を取得してから、すべてのコンポーネントを合計します。これはおそらく、理解する実装の中で最も単純ですが、おそらく最も効率が悪い可能性があります...特に、サイズの大きいデータセットとデータの次元が大きい場合。
別の可能な解決策は、newpoint
を複製し、この行列をx
と同じサイズにしてから、この行列の要素ごとの減算を行い、各行のすべての列を合計して平方根を求めることです。したがって、次のようなことができます。
_N = size(x, 1);
dists = sqrt(sum((x - repmat(newpoint, N, 1)).^2, 2));
_
マンハッタンの距離については、次のようにします。
_N = size(x, 1);
dists = sum(abs(x - repmat(newpoint, N, 1)), 2);
_
repmat
は、行列またはベクトルを受け取り、それらを特定の方向に特定の回数繰り返します。私たちの場合、newpoint
ベクトルを取得し、このN
を積み重ねて_N x M
_行列を作成します。ここで、各行はM
要素の長さです。これら2つの行列を一緒に減算してから、各成分を二乗します。これを実行したら、各行のすべての列に対してsum
を実行し、最後にすべての結果の平方根を取得します。マンハッタン距離の場合は、減算を実行し、絶対値を取得してから合計します。
しかし、私の意見ではこれを行う最も効率的な方法は bsxfun
を使用することです。これは基本的に、内部で単一の関数呼び出しを使用して説明したレプリケーションを実行します。したがって、コードは次のようになります。
_dists = sqrt(sum(bsxfun(@minus, x, newpoint).^2, 2));
_
私にとっては、これははるかにすっきりとしていて、要点に見えます。マンハッタンの距離については、次のようにします。
_dists = sum(abs(bsxfun(@minus, x, newpoint)), 2);
_
これで距離がわかったので、並べ替えるだけです。 sort
を使用して、距離を並べ替えることができます。
_[d,ind] = sort(dists);
_
d
には、昇順で並べ替えられた距離が含まれますが、ind
は、unsorted配列の各値について、sorted結果。 ind
を使用し、このベクトルの最初のk
要素を抽出してから、ind
を使用してx
データマトリックスにインデックスを付け、newpoint
に最も近いポイントを返す必要があります。
最後のステップは、k
に最も近いnewpoint
データポイントを返すことです。これは、次のように非常に簡単に行うことができます。
_ind_closest = ind(1:k);
x_closest = x(ind_closest,:);
_
_ind_closest
_には、x
に最も近い、元のデータマトリックスnewpoint
のインデックスを含める必要があります。具体的には、_ind_closest
_には、x
に最も近いポイントを取得するためにnewpoint
でサンプリングする必要がある行が含まれています。 _x_closest
_には、実際のデータポイントが含まれます。
コピーと貼り付けを行うために、コードは次のようになります。
_dists = sqrt(sum(bsxfun(@minus, x, newpoint).^2, 2));
%// Or do this for Manhattan
% dists = sum(abs(bsxfun(@minus, x, newpoint)), 2);
[d,ind] = sort(dists);
ind_closest = ind(1:k);
x_closest = x(ind_closest,:);
_
例を実行して、実際のコードを見てみましょう。
_load fisheriris
x = meas(:,3:4);
newpoint = [5 1.45];
k = 10;
%// Use Euclidean
dists = sqrt(sum(bsxfun(@minus, x, newpoint).^2, 2));
[d,ind] = sort(dists);
ind_closest = ind(1:k);
x_closest = x(ind_closest,:);
_
_ind_closest
_と_x_closest
_を検査すると、次のようになります。
_>> ind_closest
ind_closest =
120
53
73
134
84
77
78
51
64
87
>> x_closest
x_closest =
5.0000 1.5000
4.9000 1.5000
4.9000 1.5000
5.1000 1.5000
5.1000 1.6000
4.8000 1.4000
5.0000 1.7000
4.7000 1.4000
4.7000 1.4000
4.7000 1.5000
_
knnsearch
を実行した場合、変数n
が_ind_closest
_と一致することがわかります。ただし、変数d
は、newpoint
から各ポイントx
までのdistancesを返します。実際のデータポイント自体ではありません。実際の距離が必要な場合は、私が書いたコードの後に次のことを行うだけです。
_dist_sorted = d(1:k);
_
上記の回答では、N
の例のバッチで1つのクエリポイントのみを使用しています。非常に頻繁にKNNは複数の例で同時に使用されます。 KNNでテストするQ
クエリポイントがあるとします。これにより、_k x M x Q
_マトリックスが生成され、各例または各スライスについて、次元がk
のM
の最も近い点が返されます。または、k
の最も近いポイントのIDsを返すことで、_Q x k
_マトリックスを生成できます。両方を計算してみましょう。
これを行う素朴な方法は、上記のコードをループに適用し、すべての例にループすることです。
このようなものは、_Q x k
_マトリックスを割り当て、bsxfun
ベースのアプローチを適用して、出力マトリックスの各行をデータセット内のk
の最も近いポイントに設定する場合に機能します。ここでは、Fisher Irisデータセットを使用します。以前あった。また、前の例と同じ次元を維持し、4つの例を使用するので、_Q = 4
_および_M = 2
_を使用します。
_%// Load the data and create the query points
load fisheriris;
x = meas(:,3:4);
newpoints = [5 1.45; 7 2; 4 2.5; 2 3.5];
%// Define k and the output matrices
Q = size(newpoints, 1);
M = size(x, 2);
k = 10;
x_closest = zeros(k, M, Q);
ind_closest = zeros(Q, k);
%// Loop through each point and do logic as seen above:
for ii = 1 : Q
%// Get the point
newpoint = newpoints(ii, :);
%// Use Euclidean
dists = sqrt(sum(bsxfun(@minus, x, newpoint).^2, 2));
[d,ind] = sort(dists);
%// New - Output the IDs of the match as well as the points themselves
ind_closest(ii, :) = ind(1 : k).';
x_closest(:, :, ii) = x(ind_closest(ii, :), :);
end
_
これはとてもいいことですが、もっとうまくやることができます。 2つのベクトルセット間の2乗ユークリッド距離を効率的に計算する方法があります。マンハッタンでこれを実行したい場合は、演習として残します。コンサルティング このブログ 、ただし、A
は_Q1 x M
_マトリックスであり、各行は次元のポイントM
と_Q1
_ポイントであり、B
は_Q2 x M
_マトリックスであり、行は_Q2
_ポイントを持つ次元M
でもあり、距離行列D(i, j)
を効率的に計算できます。ここで、行i
および列j
の要素は、i
の行A
と行j
の間の距離を示します次の行列式を使用したB
の例:
_nA = sum(A.^2, 2); %// Sum of squares for each row of A
nB = sum(B.^2, 2); %// Sum of squares for each row of B
D = bsxfun(@plus, nA, nB.') - 2*A*B.'; %// Compute distance matrix
D = sqrt(D); %// Compute square root to complete calculation
_
したがって、A
をクエリポイントの行列、B
を元のデータで構成されるデータセットとすると、各行を個別に並べ替え、最小であった各行のk
の場所を特定することで、k
の最も近いポイントを特定できます。これをさらに使用して、実際のポイント自体を取得することもできます。
したがって:
_%// Load the data and create the query points
load fisheriris;
x = meas(:,3:4);
newpoints = [5 1.45; 7 2; 4 2.5; 2 3.5];
%// Define k and other variables
k = 10;
Q = size(newpoints, 1);
M = size(x, 2);
nA = sum(newpoints.^2, 2); %// Sum of squares for each row of A
nB = sum(x.^2, 2); %// Sum of squares for each row of B
D = bsxfun(@plus, nA, nB.') - 2*newpoints*x.'; %// Compute distance matrix
D = sqrt(D); %// Compute square root to complete calculation
%// Sort the distances
[d, ind] = sort(D, 2);
%// Get the indices of the closest distances
ind_closest = ind(:, 1:k);
%// Also get the nearest points
x_closest = permute(reshape(x(ind_closest(:), :).', M, k, []), [2 1 3]);
_
距離行列の計算に使用したロジックは同じですが、一部の変数は例に合わせて変更されています。また、sort
の2つの入力バージョンを使用して各行を個別にソートするため、ind
には行ごとのIDが含まれ、d
には対応する距離が含まれます。次に、このマトリックスをk
列に切り捨てるだけで、各クエリポイントに最も近いインデックスを特定します。次に permute
と reshape
を使用して、関連付けられている最も近い点を特定します。最初に最も近いインデックスをすべて使用し、すべてのIDを積み重ねるポイントマトリックスを作成して、_Q * k x M
_マトリックスを取得します。 reshape
とpermute
を使用すると、3Dマトリックスを作成して、指定したような_k x M x Q
_マトリックスになるようにすることができます。実際の距離を取得したい場合は、d
にインデックスを付けて、必要なものを取得できます。これを行うには、線形インデックスを取得するために _sub2ind
_ を使用して、d
に一度にインデックスを付けることができるようにする必要があります。 _ind_closest
_の値は、アクセスする必要のある列をすでに提供しています。アクセスする必要がある行は、単純に1、k
回、2、k
回など、Q
までです。 k
は、返したいポイントの数です。
_row_indices = repmat((1:Q).', 1, k);
linear_ind = sub2ind(size(d), row_indices, ind_closest);
dist_sorted = D(linear_ind);
_
上記のクエリポイントに対して上記のコードを実行すると、これらは取得するインデックス、ポイント、距離です。
_>> ind_closest
ind_closest =
120 134 53 73 84 77 78 51 64 87
123 119 118 106 132 108 131 136 126 110
107 62 86 122 71 127 139 115 60 52
99 65 58 94 60 61 80 44 54 72
>> x_closest
x_closest(:,:,1) =
5.0000 1.5000
6.7000 2.0000
4.5000 1.7000
3.0000 1.1000
5.1000 1.5000
6.9000 2.3000
4.2000 1.5000
3.6000 1.3000
4.9000 1.5000
6.7000 2.2000
x_closest(:,:,2) =
4.5000 1.6000
3.3000 1.0000
4.9000 1.5000
6.6000 2.1000
4.9000 2.0000
3.3000 1.0000
5.1000 1.6000
6.4000 2.0000
4.8000 1.8000
3.9000 1.4000
x_closest(:,:,3) =
4.8000 1.4000
6.3000 1.8000
4.8000 1.8000
3.5000 1.0000
5.0000 1.7000
6.1000 1.9000
4.8000 1.8000
3.5000 1.0000
4.7000 1.4000
6.1000 2.3000
x_closest(:,:,4) =
5.1000 2.4000
1.6000 0.6000
4.7000 1.4000
6.0000 1.8000
3.9000 1.4000
4.0000 1.3000
4.7000 1.5000
6.1000 2.5000
4.5000 1.5000
4.0000 1.3000
>> dist_sorted
dist_sorted =
0.0500 0.1118 0.1118 0.1118 0.1803 0.2062 0.2500 0.3041 0.3041 0.3041
0.3000 0.3162 0.3606 0.4123 0.6000 0.7280 0.9055 0.9487 1.0198 1.0296
0.9434 1.0198 1.0296 1.0296 1.0630 1.0630 1.0630 1.1045 1.1045 1.1180
2.6000 2.7203 2.8178 2.8178 2.8320 2.9155 2.9155 2.9275 2.9732 2.9732
_
これをknnsearch
と比較するには、代わりに、各行がクエリポイントである2番目のパラメーターにポイントの行列を指定します。この実装とknnsearch
の間でインデックスと並べ替えられた距離が一致することがわかります。
これがお役に立てば幸いです。幸運を!