最近傍点を使用した分類
ペアワイズ距離計量
学習データ セット内の点に対する距離に基づいてクエリ点を分類すると、新しい点を簡単かつ有効に分類できます。さまざまな計量を使用して、距離を調べることができます。pdist2 を使用して、データセットとクエリ点間の距離を検出します。
距離計量
mx 行 n 列のデータ行列 X (mx 個の 1 行 n 列の行ベクトル x1、x2、...、xmx として扱われる) と、my 行 n 列のデータ行列 Y (my 個の 1 行 n 列の行ベクトル y1、y2、...、ymy として扱われる) が与えられた場合、ベクトル xs と yt の間のさまざまな距離は次のように定義されます。
ユークリッド距離
ユークリッド距離はミンコフスキー距離の特殊なケース、p = 2 の場合です。
標準化されたユークリッド距離
ここで、V は j 番目の対角要素が (S(j))2 である n 行 n 列の対角行列です。S は各次元のスケーリング係数のベクトルです。
高速ユークリッド距離はユークリッド距離と同じで、予測子の数が 10 個以上の場合に時間の短縮になる代替アルゴリズムを使用して計算されます。このアルゴリズムは高速ですが、場合によっては精度が低下することがあります。スパース データはサポートされません。高速ユークリッド距離アルゴリズムを参照してください。
高速ユークリッド距離を指定するには、
Distanceパラメーターを'fasteuclidean'に設定します。標準化された高速ユークリッド距離は標準化されたユークリッド距離と同じで、予測子の数が 10 個以上の場合に時間の短縮になる代替アルゴリズムを使用して計算されます。このアルゴリズムは高速ですが、場合によっては精度が低下することがあります。スパース データはサポートされません。高速ユークリッド距離アルゴリズムを参照してください。
標準化された高速ユークリッド距離を指定するには、
Distanceパラメーターを'fastseuclidean'に設定します。マハラノビス距離
ここで、C は共分散行列です。
市街地距離
市街地距離はミンコフスキー距離の特殊なケース、p = 1 の場合です。
ミンコフスキー距離
p = 1 という特殊なケースでは、ミンコフスキー距離は市街地距離を与えます。p = 2 という特殊なケースでは、ミンコフスキー距離はユークリッド距離を与えます。p = ∞ という特殊なケースでは、ミンコフスキー距離はチェビシェフ距離を与えます。
チェビシェフ距離
チェビシェフ距離はミンコフスキー距離の特殊なケース、p = ∞ の場合です。
コサイン距離
相関距離
ここで
および
ハミング距離
ハミング距離は、一致しない座標の比率です。
Jaccard 距離は、1 からジャカード係数 (異なる非ゼロ座標の比率) を引きます。
スピアマン距離は、1 から一連の値として扱われる観測値間の標本スピアマン順位相関係数を引きます。
ここで
k 最近傍探索および半径探索
n 個の点と距離関数のセット X があれば、k 最近傍点 (kNN) 検索により、X 内のクエリ点またはクエリ点の集合 Y に対する k 最近傍点を検索できます。kNN 検索手法と kNN ベースのアルゴリズムは、ベンチマーク学習規則として広く使用されています。kNN 検索手法は比較的使いやすいので、他の分類手法による結果と kNN の結果を簡単に比較できます。この手法は次のようなさまざまな分野で使用されています。
バイオインフォマティクス
イメージ処理およびデータ圧縮
ドキュメンテーションの取得
コンピューター ビジョン
マルチメディア データベース
マーケティング データ解析
次のようなその他の機械学習アルゴリズムにも kNN 検索を使用できます。
kNN 分類
ローカル加重回帰
欠損データの補定と内挿
密度推定
k-means クラスタリングなどの多くの距離ベースの学習関数と共に、kNN 検索を使用することもできます。
これに対して、正の実数値 r の場合、rangesearch は Y 内の各点の距離 r 内にある X 内のすべての点を検索します。この固定半径探索は、同じ距離計量と検索クラスをサポートし、同じ検索アルゴリズムを使用するため、kNN 検索と密接に関連しています。
網羅的探索を使用した k 最近傍探索
入力するデータが次の条件のいずれかを満たす場合、knnsearch では既定の設定で網羅的探索手法を使用して、k 最近傍点を検索します。
Xの列数が 10 を超えている。Xがスパース。距離計量が以下のいずれか。
'seuclidean''mahalanobis''cosine''correlation''spearman''hamming''jaccard'カスタム距離関数
探索オブジェクトが ExhaustiveSearcher モデルのオブジェクトである場合、knnsearch では網羅的探索手法も使用します。網羅的探索手法では、各クエリ点から X 内のすべての点までの距離が検索され、昇順にランク付けされて、距離が最小の k 点が返されます。たとえば、次の図は k = 3 の最近傍点を示しています。

Kd 木を使用した k 最近傍探索
入力するデータが次の条件をすべて満たす場合、knnsearch では既定の設定で Kd ツリーを作成して、k 最近傍を検索します。
Xの列の数が 10 を下回っています。Xがスパースでない距離計量が以下のいずれか。
'euclidean'(既定の設定)'cityblock''minkowski''chebychev'
探索オブジェクトが KDTreeSearcher モデルのオブジェクトである場合、knnsearch では Kd ツリーも使用します。
Kd ツリーでは、データがノードに分割されます。その数は、座標に基づいて (カテゴリとは対照的に)、ノードあたり最大で BucketSize 点 (既定値は 50) です。次の図は、patch オブジェクトを使用してさまざまな "バケット" を色分けする概念を示しています。

与えられたクエリ点に対する k 最近傍点を検索する場合、knnsearch では次のようになります。
クエリ点が属しているノードを調べます。次の例では、クエリ点 (32,90) はノード 4 に属しています。
そのノード内の k 最近傍点と、クエリ点までの距離を求めます。次の例では、赤い丸で囲んだ点はクエリ点から等距離にあり、ノード 4 内でクエリ点に最も近い点です。
クエリ点から k 番目に最も近い点まで任意の方向へ等距離にある、任意の面積をもつ他のすべてのノードを選択します。この例では、ノード 4 内の最近傍点まで等距離にある半径内で、クエリ点を中心とする黒い実線の円と重なり合っているのはノード 3 だけです。
その範囲内のノードで、クエリ点により近い点を検索します。次の例では、赤の四角形で囲んだ点は、ノード 4 内の点よりもわずかにクエリ点の近くにあります。

10 次元 (列) 未満の大きいデータ セットに Kd ツリーを使用すると、knnsearch で必要な計算が距離のサブセットだけになるので、網羅的探索手法を使用する場合よりもはるかに効率的になります。Kd ツリーの効率を向上させるには、KDTreeSearcher モデルを使用します。
Hierarchical Navigable Small Worlds (HNSW) アルゴリズムを使用した近似 KNN 探索
大規模なデータ セットの場合、knnsearch にかなりの時間とメモリを要します。より効率的な探索 (ただし近似) を実行するには、hnswSearcher モデル オブジェクトを使用します。モデル オブジェクトは、関数 hnswSearcher を使用して作成するか、関数 createns で NSMethod="hnsw" を指定して作成できます。その後、その hnswSearcher モデル オブジェクトを使用して knnsearch を実行すると、特に行や列が多いデータの場合に KDTreeSearcher オブジェクトや ExhaustiveSearcher オブジェクトよりも高速になります。
メモ
hnswSearcher モデル オブジェクトは作成に時間がかかるため、knnsearch を呼び出す前にオブジェクトを作成しておく必要があります。つまり、knnsearch(X,NSMethod="hnsw") を呼び出すことはできません。代わりに、Mdl を既存の hnswSearcher モデル オブジェクトにして knnsearch(Mdl,...) を呼び出す必要があります。
HNSW アルゴリズムでは、複数の層からなる最近傍探索用のグラフを作成します。上位の層の方が下位の層よりも含まれる点が少なくなります。下位の各層には、上位の層のすべての点に加えて追加の点が含まれます。
近似 KNN 探索は最上位の層から始まり、その層で最も近い点を貪欲に探索してから、次にある下位の層の探索に進みます。探索は層 0 で停止します。次の図は、この探索プロセスを示しています。

HNSW アルゴリズムでは、次の手順を実行して近似最近傍探索モデルを作成します。
ランダムな層 J にデータ点を配置します。レベル J は幾何分布から抽出されます。
その層でデータ点の k 最近傍の探索を実行します。
点を層 J – 1 にコピーします。
点の新しい k 最近傍を探索します。
このプロセスを層 0 まで繰り返します。
次のデータ点がある場合は同じプロセスを使用して配置します。
HNSW 探索モデルを作成するプロセスについては、Malkov と Yashunin [1]で詳しく説明されていますが、あまり速くはありません。HNSW 探索モデルを使用する利点は、新しいデータ点の最近傍を探索する速度が速くなることにあります。HNSW を使用した KNN 探索手続きでは、ローカルな最小値で探索が止まることがあるため、真の最近傍が見つからない場合があります。ただし、HNSW 探索プロセスは他のどのタイプの KNN 探索よりも一般に高速であるため、真の最近傍をすべて探索する必要がない場合は HNSW 探索の使用を検討してください。
Malkov と Yashunin [1]のパラメーター M は、hnswSearcher の MaxNumLinksPerNode パラメーターに対応します。[1]のパラメーター ML は 1/log(MaxNumLinksPerNode) に設定されます。
探索モデル オブジェクトとは
基本的に、モデル オブジェクトは情報を格納するための便利な手段です。特定の探索モデルに関する値および型についてのプロパティは、関連するモデル間で同じになります。モデルに情報を格納することに加え、特定のアクションをモデルに対して実行できます。
knnsearch を使用すると、k 最近傍探索を探索モデルに対して効率的に実行できます。また、探索モデルと rangesearch を使用すると、指定した半径内に含まれているすべての近傍を探索できます。さらに、モデルの作成や使用を行わずに探索を実行する汎用の関数 knnsearch および rangesearch もあります。
どのタイプのモデルおよび探索方法がデータに最適であるかを判別するには、以下を考慮します。
データ セットに 10 列を超える多くの列があるかどうか。この場合、性能は
ExhaustiveSearcherモデルの方が高くなる可能性があります。行や列が多いデータの場合、hnswSearcherは他の探索オブジェクトより実行ははるかに高速になりますが、厳密な結果ではなく近似を返します。データがスパースかどうか。
ExhaustiveSearcherモデルを使用します。次の距離計量のいずれかを使用して厳密な最近傍を探索するかどうか。
ExhaustiveSearcherモデルを使用します。'seuclidean''mahalanobis''cosine''correlation''spearman''hamming''jaccard'カスタム距離関数
データ セットが大規模であるが 10 列未満かどうか。
KDTreeSearcherモデルを使用します。データ セットが大規模で列が多い場合は、hnswSearcherを試します。多くのクエリ点に対して最近傍点を検索するかどうか。
KDTreeSearcherまたはhnswSearcherを使用します。
クエリ データの分類
この例では、以下を行ってクエリ データを分類する方法を示します。
Kd 木を成長させる。
成長した木を使用して k 最近傍探索を実行する。
各最近傍間で最高の表現になるクラスにクエリ点を割り当てる。
フィッシャーのアヤメの分析データの最後の 2 列に基づいて新しい点を分類します。最後の 2 列のみを使用することにより、プロット作成が容易になります。
load fisheriris x = meas(:,3:4); gscatter(x(:,1),x(:,2),species) legend('Location','best')

新しい点をプロットします。
newpoint = [5 1.45]; line(newpoint(1),newpoint(2),'marker','x','color','k',... 'markersize',10,'linewidth',2)

Kd 木の近傍探索モデルを準備します。
Mdl = KDTreeSearcher(x)
Mdl =
KDTreeSearcher with properties:
BucketSize: 50
Distance: 'euclidean'
DistParameter: []
X: [150×2 double]
Mdl は KDTreeSearcher モデルです。既定の設定では、距離計量にユークリッド距離を使用して近傍を探索します。
新しい点に最も近い 10 個の標本点を検索します。
[n,d] = knnsearch(Mdl,newpoint,'k',10); line(x(n,1),x(n,2),'color',[.5 .5 .5],'marker','o',... 'linestyle','none','markersize',10)

knnsearch で検索された最近傍点は、8 つだけのようです。実際は、この特定のデータセットに重複する値が含まれています。
x(n,:)
ans = 10×2
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
計算された距離がプロット軸の見かけの距離に等しく対応するように軸を等しくし、近傍点がよく見えるように拡大します。
xlim([4.5 5.5]);
ylim([1 2]);
axis square
10 個の近傍点の種類を求めます。
tabulate(species(n))
Value Count Percent virginica 2 20.00% versicolor 8 80.00%
10 個の最近傍点の多数決に基づく規則を使用して、新しい点を versicolor として分類できます。
グループの周りに円を描画することによって、近傍点を視覚的に特定します。新しい点の位置に基づいて円の中心と直径を定義します。
ctr = newpoint - d(end); diameter = 2*d(end); % Draw a circle around the 10 nearest neighbors. h = rectangle('position',[ctr,diameter,diameter],... 'curvature',[1 1]); h.LineStyle = ':';

同じデータセットを使用して、3 つの新しい点に対する最近傍点を 10 個検索します。
figure newpoint2 = [5 1.45;6 2;2.75 .75]; gscatter(x(:,1),x(:,2),species) legend('location','best') [n2,d2] = knnsearch(Mdl,newpoint2,'k',10); line(x(n2,1),x(n2,2),'color',[.5 .5 .5],'marker','o',... 'linestyle','none','markersize',10) line(newpoint2(:,1),newpoint2(:,2),'marker','x','color','k',... 'markersize',10,'linewidth',2,'linestyle','none')

新しい点に対してそれぞれ 10 個の最近傍点の種類を検索します。
tabulate(species(n2(1,:)))
Value Count Percent virginica 2 20.00% versicolor 8 80.00%
tabulate(species(n2(2,:)))
Value Count Percent virginica 10 100.00%
tabulate(species(n2(3,:)))
Value Count Percent
versicolor 7 70.00%
setosa 3 30.00%
メソッド knnsearch と関数のその他の使用例については、個々のリファレンス ページを参照してください。
カスタム距離計量を使用した最近傍の検索
この例は、カイ二乗距離に関する Y の各観測値に最も近い観測値 X の 3 つのインデックスを検出する方法を示します。この距離計量はコレスポンデンス分析、特に環境アプリケーションで使用されます。
2 つの行列に正規分布データを無作為に生成します。行数は可変ですが、列数は同じでなくてはなりません。この例はプロットに 2 次元データを使用します。
rng(1) % For reproducibility X = randn(50,2); Y = randn(4,2); h = zeros(3,1); figure h(1) = plot(X(:,1),X(:,2),'bx'); hold on h(2) = plot(Y(:,1),Y(:,2),'rs','MarkerSize',10); title('Heterogeneous Data')

X および Y の行は観測値に対応し、列は一般的には次元 (たとえば予測子) です。
j 次元の x 点と z 点のカイ二乗距離は次のようになります。
ここで、 は次元 j に関連付けられている重みです。
各次元に対して重みを選択し、カイ二乗距離関数を指定します。距離関数は以下の手順を実行しなければなりません。
入力引数として
Xの 1 行 (たとえばx) および行列Zを取る。xをZの各行と比較する。長さ のベクトル
Dを返す。 はZの行数です。Dの各要素はxに対応する観測とZの各行に対応する観測との間の距離です。
w = [0.4; 0.6]; chiSqrDist = @(x,Z)sqrt(((x-Z).^2)*w);
この例では例示のために任意の重みを使用します。
Y の各観測値に最も近い 3 つのインデックスを、X の観測値で検出します。
k = 3; [Idx,D] = knnsearch(X,Y,'Distance',chiSqrDist,'k',k);
idx および D は、4 行 3 列の行列です。
idx(j,1)は、Xにおける最も近い観測値からYにおける観測値 j への行インデックスであり、D(j,1)はその距離です。idx(j,2)は、Xにおいて次に近い観測値とYにおける観測値 j の行インデックスであり、D(j,2)はその距離です。以下同様です。
プロットにおける最も近い観測値を特定します。
for j = 1:k h(3) = plot(X(Idx(:,j),1),X(Idx(:,j),2),'ko','MarkerSize',10); end legend(h,{'\texttt{X}','\texttt{Y}','Nearest Neighbor'},'Interpreter','latex') title('Heterogeneous Data and Nearest Neighbors') hold off

Y の複数の観測値では最近傍が共有されます。
カイ二乗距離計量はユークリッド距離計量と同等ですが、オプションでスケーリング パラメーターを使用することを検証します。
[IdxE,DE] = knnsearch(X,Y,'Distance','seuclidean','k',k, ... 'Scale',1./(sqrt(w))); AreDiffIdx = sum(sum(Idx ~= IdxE))
AreDiffIdx = 0
AreDiffDist = sum(sum(abs(D - DE) > eps))
AreDiffDist = 0
3 つの最近傍のうち 2 つの実装間において、インデックスおよび距離は実質的に等しくなります。
教師あり学習での k 最近傍分類
ClassificationKNN 分類モデルでは、以下を行うことができます。
教師あり学習のステップの手順に従って分類用のデータを準備します。次に、fitcknn を使用して分類器を構築します。
KNN 分類器の構築
次の例は、フィッシャーのアヤメのデータ用に k 最近傍分類器を構築する方法を示しています。
フィッシャーのアヤメのデータを読み込みます。
load fisheriris X = meas; % Use all data for fitting Y = species; % Response data
fitcknn を使用して分類器を構築します。
Mdl = fitcknn(X,Y)
Mdl =
ClassificationKNN
ResponseName: 'Y'
CategoricalPredictors: []
ClassNames: {'setosa' 'versicolor' 'virginica'}
ScoreTransform: 'none'
NumObservations: 150
Distance: 'euclidean'
NumNeighbors: 1
Properties, Methods
既定の k 最近傍分類器は、単一の最近傍のみを使用します。多くの場合、それより多くの近傍をもつ分類器のほうがロバストです。
Mdl の近傍サイズを 4 に変更します。これは、Mdl が 4 つの最近傍を使用して分類することを意味します。
Mdl.NumNeighbors = 4;
KNN 分類器の品質の確認
次の例は、再代入と交差検証を使用して k 最近傍分類器の品質を調べる方法を示しています。
KNN 分類器の構築 にあるように、フィッシャーのアヤメのデータ用の KNN 分類器を構築します。
load fisheriris X = meas; Y = species; rng(10); % For reproducibility Mdl = fitcknn(X,Y,'NumNeighbors',4);
再代入損失を調べます。これは既定では、Mdl の予測での誤分類率の比率です (既定の設定と異なるコスト、重み、または事前確率については、lossを参照してください。)
rloss = resubLoss(Mdl)
rloss = 0.0400
この分類器は学習データが 4% の場合は不正確な予測を行います。
このモデルから交差検証分類器を構築します。
CVMdl = crossval(Mdl);
交差検証損失を調べます。これは、学習に使用されないデータを予測したときの各交差検証済みモデルの平均損失です。
kloss = kfoldLoss(CVMdl)
kloss = 0.0333
交差検証分類器の精度は再代入精度とほぼ同じです。したがって、新しいデータが学習データとほぼ同じ分布であると仮定すると、Mdl では新しいデータの約 4% が誤判定されることを予想できます。
KNN 分類器を使用した分類の予測
次の例は、k 最近傍分類器での分類を予測する方法を示します。
KNN 分類器の構築 にあるように、フィッシャーのアヤメのデータ用の KNN 分類器を構築します。
load fisheriris X = meas; Y = species; Mdl = fitcknn(X,Y,'NumNeighbors',4);
平均の花の分類を予測します。
flwr = mean(X); % an average flower
flwrClass = predict(Mdl,flwr)flwrClass = 1×1 cell array
{'versicolor'}
KNN 分類器の変更
次の例は、k 最近傍分類器を変更する方法を示します。
KNN 分類器の構築 にあるように、フィッシャーのアヤメのデータ用の KNN 分類器を構築します。
load fisheriris X = meas; Y = species; Mdl = fitcknn(X,Y,'NumNeighbors',4);
既定の 1 つの最近傍ではなく 3 つの最近傍が使用されるよう、モデルを変更します。
Mdl.NumNeighbors = 3;
再代入予測と、新しい近傍の数をもつ交差検証損失を比較します。
loss = resubLoss(Mdl)
loss = 0.0400
rng(10); % For reproducibility CVMdl = crossval(Mdl,'KFold',5); kloss = kfoldLoss(CVMdl)
kloss = 0.0333
この場合、3 つの近傍があるモデルは 4 つの近傍があるモデルと交差検証損失が同じです (KNN 分類器の品質の確認を参照してください)。
既定値の代わりに余弦距離が使用されるようにモデルを変更し、損失を調べます。余弦距離を使用するには、網羅的探索手法を使用してモデルを再作成しなければなりません。
CMdl = fitcknn(X,Y,'NSMethod','exhaustive','Distance','cosine'); CMdl.NumNeighbors = 3; closs = resubLoss(CMdl)
closs = 0.0200
これで、分類器の再代入誤差が前より少なくなりました。
新しいモデルの交差検証バージョンの品質を調べます。
CVCMdl = crossval(CMdl); kcloss = kfoldLoss(CVCMdl)
kcloss = 0.0200
CVCMdl は、CVMdl より交差検証損失が小さくなっています。ただし一般的には、再代入誤差を小さくしても、テスト標本予測の精度が向上したモデルが作成されるとは限りません。
参照
[1] Malkov, Yu. A., and D. A. Yashunin. Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs. Available at https://arxiv.org/abs/1603.09320.
参考
fitcknn | ClassificationKNN | ExhaustiveSearcher | KDTreeSearcher | hnswSearcher