概要
K 最近傍 (KNN) アルゴリズムは、分類、回帰、外れ値検出に使用される教師あり機械学習アルゴリズムの一種です。 最も基本的な形式で実装するのは非常に簡単ですが、かなり複雑なタスクを実行できます。 特別なトレーニング フェーズがないため、遅延学習アルゴリズムです。 むしろ、新しいデータ ポイントまたはインスタンスを分類 (または回帰) しながら、すべてのデータをトレーニングに使用します。
KNNは ノンパラメトリック学習アルゴリズム、つまり、基になるデータについて何も想定していません。 これは非常に便利な機能です。実際のデータのほとんどは、線形分離可能性、一様分布などの理論的仮定に実際には従っていないからです。
このガイドでは、Python の Scikit-Learn ライブラリを使用して KNN を実装する方法について説明します。 その前に、まず KNN をどのように使用できるかを探り、その背後にある理論を説明します。 その後、 カリフォルニア州住宅データセット KNN アルゴリズムとそのバリエーションのいくつかを説明するために使用します。 まず、回帰用の KNN アルゴリズムを実装する方法を見ていき、次に KNN 分類と外れ値検出の実装を見ていきます。 最後に、アルゴリズムの長所と短所をいくつか紹介します。
いつ KNN を使用する必要がありますか?
アパートを借りたいと思っていて、最近、友人の隣人が 2 週間以内にアパートを借りる可能性があることを知ったとします。 このアパートはまだ賃貸ウェブサイトに掲載されていないので、その賃貸価格をどのように見積もることができますか?
あなたの友人が 1,200 ドルの家賃を払っているとしましょう。 あなたの家賃の値はその程度かもしれませんが、アパートはまったく同じではない (向き、面積、家具の品質など) ため、他のアパートのデータをもっと持っておくとよいでしょう。
他の隣人に尋ねて、賃貸ウェブサイトに掲載されていた同じ建物のアパートを見ると、最も近い 1,200 つの近隣アパートの家賃は 1,210 ドル、1,210 ドル、1,215 ドル、XNUMX ドルです。 それらのアパートは、あなたの友人のアパートと同じブロックとフロアにあります.
同じフロアにあるが別のブロックにある、さらに離れた他のアパートの家賃は、$ 1,400、$ 1,430、$ 1,500、および$ 1,470です。 夕方は日差しが強いため、お値段が高めのようです。
アパートが近くにあることを考えると、あなたの推定家賃は約 $1,210 になるようです。 それが、 K最近傍法(KNN) アルゴリズムが行います! 既存のデータとの近接性に基づいて、新しいデータを分類または回帰します。
例を理論に変換する
家賃の値など、推定値が連続する数値の場合は、KNN を使用します。 回帰. ただし、たとえば、最低家賃と最高家賃に基づいてアパートをカテゴリに分類することもできます。 値が離散的でカテゴリになる場合、KNN が使用されます。 分類.
また、どの隣人が他の人と非常に異なっているため、おそらく家賃の支払いをやめる可能性があるかを推定する可能性もあります。 これは、どの値やカテゴリにも当てはまらないほど離れているデータ ポイントを検出するのと同じです。その場合、KNN が使用されます。 外れ値の検出.
この例では、各アパートの家賃も既にわかっているため、データにラベルが付けられています。 KNN は、以前にラベル付けされたデータを使用します。 教師あり学習アルゴリズム.
KNN は、最も基本的な形式で非常に簡単に実装できますが、非常に複雑な分類、回帰、または外れ値検出タスクを実行します。
データに新しい点が追加されるたびに、KNN はデータの一部のみを使用して、その追加された点の値 (回帰) またはクラス (分類) を決定します。 すべてのポイントをもう一度見る必要がないため、 遅延学習アルゴリズム.
また、KNN は、基礎となるデータの特性については何も想定しておらず、データが均一などの何らかのタイプの分布に適合することや、線形分離可能であることを期待していません。 これは、 ノンパラメトリック学習アルゴリズム. 実世界のデータのほとんどは実際には理論上の仮定に従っていないため、これは非常に便利な機能です。
KNN のさまざまな用途の視覚化
示されているように、KNN アルゴリズムの背後にある直感は、すべての教師あり機械学習アルゴリズムの中で最も直接的なアルゴリズムの XNUMX つです。 アルゴリズムは最初に 距離 他のすべてのトレーニング データ ポイントへの新しいデータ ポイントの。
注: 距離はさまざまな方法で測定できます。 ミンコフスキーを使用できます。 ユークリッド、マンハッタン、マハラノビス、またはハミングの式など、いくつかの指標を挙げることができます。 高次元データでは、ユークリッド距離はしばしば失敗し始め (高次元は… 奇妙です)、代わりにマンハッタン距離が使用されます。
距離を計算した後、KNN はいくつかの最も近いデータ ポイント (2、3、10、または実際には任意の整数) を選択します。 このポイント数 (2、3、10 など) は、 K K最近傍で!
最後のステップで、回帰タスクの場合、KNN は予測のために K 最近傍点の平均加重和を計算します。 分類タスクの場合、新しいデータ ポイントは、選択された K 最近傍ポイントの大部分が属するクラスに割り当てられます。
簡単な例を使って、実際のアルゴリズムを視覚化してみましょう。 3 つの変数と XNUMX の K を持つデータセットを考えてみましょう。
回帰を実行するときのタスクは、最も近い 3 つのポイントの平均加重合計に基づいて、新しいデータ ポイントの値を見つけることです。
とのKNN K = 3
、時 回帰に使用:
KNN アルゴリズムは、すべてのポイントから新しいポイントまでの距離を計算することから始めます。 次に、新しいポイントまでの距離が最小の 3 つのポイントを見つけます。 これは上の XNUMX 番目の図に示されています。 47
, 58
, 79
取り囲まれています。 その後、加重合計を計算します。 47
, 58
および 79
– この場合、重みは 1 です – すべてのポイントを等しいと見なしますが、距離に基づいて異なる重みを割り当てることもできます。 加重合計を計算した後、新しいポイント値は 61,33
.
分類を実行するとき、新しいデータ ポイントを分類する KNN タスクは、 "Purple"
or "Red"
とに提供されます。
とのKNN K = 3
、時 分類に使用:
KNN アルゴリズムは、すべてのポイントから新しいポイントまでの距離を計算し、新しいポイントまでの距離が最も近い 3 つの最も近いポイントを見つけ、数値を計算する代わりに、前と同じ方法で開始します。最も近い XNUMX つのポイントの過半数が属するクラス (赤のクラス) への新しいポイント。 したがって、新しいデータ ポイントは次のように分類されます。 "Red"
.
外れ値検出プロセスは上記の両方とは異なります。回帰と分類の実装後に実装するときに、それについて詳しく説明します。
Note: このチュートリアルで提供されるコードは、以下で実行およびテストされています。 Jupyter Notebook.
Scikit-Learn カリフォルニア州住宅データセット
を使用します カリフォルニア州の住宅データセット KNN アルゴリズムがどのように機能するかを説明します。 このデータセットは、1990 年の米国の国勢調査から派生したものです。 データセットの XNUMX 行は、XNUMX つのブロック グループの国勢調査を表します。
このセクションでは、カリフォルニア州住宅データセットの詳細について説明します。これにより、使用するデータを直感的に理解できるようになります。 作業を開始する前に、データについて理解することは非常に重要です。
A コロナ新型ウィルス(COVID-XNUMX)やメンタルヘルスの崩壊を避ける為の グループは、米国国勢調査局がサンプル データを公開する最小の地理的単位です。 ブロックグループのほかに、世帯という別の用語が使用されます。世帯は、家の中に住む人々のグループです。
データセットは次の XNUMX つの属性で構成されます。
MedInc
– ブロックグループの収入の中央値HouseAge
– ブロックグループの住宅年齢の中央値AveRooms
– 平均部屋数 (世帯ごとに提供)AveBedrms
– 寝室の平均数 (世帯ごとに提供)Population
– ブロックグループのポピュレーションAveOccup
– 世帯員の平均数Latitude
– ブロックグループの緯度Longitude
– ブロックグループの経度MedHouseVal
– カリフォルニア地区の住宅価格の中央値 (数十万ドル)
データセットは すでに Scikit-Learn ライブラリの一部、それをインポートしてデータフレームとしてロードするだけです。
from sklearn.datasets import fetch_california_housing
california_housing = fetch_california_housing(as_frame=True)
df = california_housing.frame
Scikit-Learn から直接データをインポートすると、列と数値だけでなく、データの説明が Bunch
オブジェクト – 抽出したばかりです frame
. データセットの詳細が利用可能です こちら.
Pandas をインポートして、データの最初の数行を見てみましょう。
import pandas as pd
df.head()
コードを実行すると、データセットの最初の XNUMX 行が表示されます。
MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude MedHouseVal
0 8.3252 41.0 6.984127 1.023810 322.0 2.555556 37.88 -122.23 4.526
1 8.3014 21.0 6.238137 0.971880 2401.0 2.109842 37.86 -122.22 3.585
2 7.2574 52.0 8.288136 1.073446 496.0 2.802260 37.85 -122.24 3.521
3 5.6431 52.0 5.817352 1.073059 558.0 2.547945 37.85 -122.25 3.413
4 3.8462 52.0 6.281853 1.081081 565.0 2.181467 37.85 -122.25 3.422
このガイドでは、 MedInc
, HouseAge
, AveRooms
, AveBedrms
, Population
, AveOccup
, Latitude
, Longitude
予測する MedHouseVal
. 私たちの動機の物語に似たもの。
それでは、回帰のための KNN アルゴリズムの実装に飛び込みましょう。
Scikit-Learn を使用した K 最近傍法による回帰
ここまでで、データセットについて理解できたので、KNN アルゴリズムの他のステップに進むことができます。
KNN 回帰のためのデータの前処理
前処理は、回帰タスクと分類タスクの最初の違いが現れる場所です。 このセクションはすべて回帰に関するものなので、それに応じてデータセットを準備します。
回帰のために、別の住宅価格の中央値を予測する必要があります。 そのために、私たちは割り当てます MedHouseVal
〜へ y
および他のすべての列 X
落とすだけで MedHouseVal
:
y = df['MedHouseVal']
X = df.drop(['MedHouseVal'], axis = 1)
変数の説明を見ると、測定値に違いがあることがわかります。 推測を避けるために、 describe()
チェックする方法:
X.describe().T
この結果:
count mean std min 25% 50% 75% max
MedInc 20640.0 3.870671 1.899822 0.499900 2.563400 3.534800 4.743250 15.000100
HouseAge 20640.0 28.639486 12.585558 1.000000 18.000000 29.000000 37.000000 52.000000
AveRooms 20640.0 5.429000 2.474173 0.846154 4.440716 5.229129 6.052381 141.909091
AveBedrms 20640.0 1.096675 0.473911 0.333333 1.006079 1.048780 1.099526 34.066667
Population 20640.0 1425.476744 1132.462122 3.000000 787.000000 1166.000000 1725.000000 35682.000000
AveOccup 20640.0 3.070655 10.386050 0.692308 2.429741 2.818116 3.282261 1243.333333
Latitude 20640.0 35.631861 2.135952 32.540000 33.930000 34.260000 37.710000 41.950000
Longitude 20640.0 -119.569704 2.003532 -124.350000 -121.800000 -118.490000 -118.010000 -114.310000
ここで、 mean
の値 MedInc
約です 3.87
と mean
の値 HouseAge
についてです 28.64
、それの7.4倍の大きさになります MedInc
. 他の機能も平均と標準偏差に違いがあります – それを確認するには、 mean
および std
値とそれらが互いにどのように離れているかを観察します。 為に MedInc
std
約です 1.9
、用 HouseAge
, std
is 12.59
同じことが他の機能にも当てはまります。
に基づいたアルゴリズムを使用しています 距離 また、距離ベースのアルゴリズムは、このデータのように同じ縮尺ではないデータに大きく影響されます。 ポイントのスケールは、値間の実際の距離を歪める可能性があります (実際には、ほとんどの場合そうなります)。
機能スケーリングを実行するには、Scikit-Learn の StandardScaler
クラスは後で。 スケーリングを今すぐ (トレーニングとテストの分割前に) 適用すると、計算にはテスト データが含まれます。 漏れ パイプラインの残りの部分にデータ情報をテストします。 この種の データ漏洩 残念ながら一般的にスキップされ、再現不可能または幻想的な結果が得られます。
トレーニング セットとテスト セットへのデータの分割
漏れなくデータをスケーリングできるだけでなく、結果を評価し、過学習を回避するために、データセットをトレーニングとテストの分割に分割します。
トレーニングとテストの分割を作成する簡単な方法は、 train_test_split
Scikit-Learn のメソッド。 分割はある時点で直線的に分割されませんが、X% と Y% をランダムにサンプリングします。 このプロセスを再現可能にする (メソッドが常に同じデータポイントをサンプリングするようにする) には、 random_state
ある議論 SEED
:
from sklearn.model_selection import train_test_split
SEED = 42
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=SEED)
このコードでは、トレーニング用にデータの 75% をサンプリングし、テスト用にデータの 25% をサンプリングします。 を変更することにより、 test_size
たとえば、0.3 にすると、データの 70% でトレーニングし、30% でテストできます。
データの 75% をトレーニングに使用し、25% をテストに使用すると、20640 レコードのうち、トレーニング セットには 15480 が含まれ、テスト セットには 5160 が含まれます。完全なデータセットと分割データの長さを出力することで、これらの数値をすばやく調べることができます。 :
len(X)
len(X_train)
len(X_test)
すごい! これで、データ スケーラーを X_train
両方を設定し、スケーリングします X_train
および X_test
からデータを漏らすことなく X_test
に X_train
.
KNN 回帰の特徴スケーリング
輸入することで StandardScaler
、それをインスタンス化し、トレーニング データに従ってフィッティングし (漏れを防ぎます)、トレーニング データセットとテスト データセットの両方を変換することで、特徴のスケーリングを実行できます。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
注: よく電話するから scaler.fit(X_train)
続い scaler.transform(X_train)
– シングルを呼び出すことができます scaler.fit_transform(X_train)
続い scaler.transform(X_test)
通話を短くするために!
これで、データがスケーリングされました。 スケーラーは、データ ポイントに適用されると、列名ではなく、データ ポイントのみを維持します。 DataFrame
. 列名を使用してデータを再度 DataFrame に整理し、使用します。 describe()
の変化を観察する mean
および std
:
col_names=['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude']
scaled_df = pd.DataFrame(X_train, columns=col_names)
scaled_df.describe().T
これは私たちに与えるでしょう:
count mean std min 25% 50% 75% max
MedInc 15480.0 2.074711e-16 1.000032 -1.774632 -0.688854 -0.175663 0.464450 5.842113
HouseAge 15480.0 -1.232434e-16 1.000032 -2.188261 -0.840224 0.032036 0.666407 1.855852
AveRooms 15480.0 -1.620294e-16 1.000032 -1.877586 -0.407008 -0.083940 0.257082 56.357392
AveBedrms 15480.0 7.435912e-17 1.000032 -1.740123 -0.205765 -0.108332 0.007435 55.925392
Population 15480.0 -8.996536e-17 1.000032 -1.246395 -0.558886 -0.227928 0.262056 29.971725
AveOccup 15480.0 1.055716e-17 1.000032 -0.201946 -0.056581 -0.024172 0.014501 103.737365
Latitude 15480.0 7.890329e-16 1.000032 -1.451215 -0.799820 -0.645172 0.971601 2.953905
Longitude 15480.0 2.206676e-15 1.000032 -2.380303 -1.106817 0.536231 0.785934 2.633738
すべての標準偏差が現在どのようになっているのかを観察します 1
そして手段は小さくなりました。 これが私たちのデータを作るものです より均一! KNN ベースのリグレッサーをトレーニングして評価しましょう。
KNN 回帰のトレーニングと予測
Scikit-Learn の直感的で安定した API により、リグレッサーと分類器のトレーニングが非常に簡単になります。 インポートしましょう KNeighborsRegressor
からのクラス sklearn.neighbors
モジュールを作成し、インスタンス化し、トレーニング データに適合させます。
from sklearn.neighbors import KNeighborsRegressor
regressor = KNeighborsRegressor(n_neighbors=5)
regressor.fit(X_train, y_train)
上記のコードでは、 n_neighbors
の値です K、またはアルゴリズムが新しい住宅の中央値を選択するために考慮する近隣の数。 5
のデフォルト値です KNeighborsRegressor()
. K には理想的な値はなく、テストと評価の後に選択されますが、最初は、 5
は KNN で一般的に使用される値であるため、デフォルト値として設定されました。
最後のステップは、テスト データで予測を行うことです。 これを行うには、次のスクリプトを実行します。
y_pred = regressor.predict(X_test)
これで、モデルがラベル (グラウンド トゥルース) を持つ新しいデータ (テスト セット) にどの程度一般化されているかを評価できます。
KNN 回帰のアルゴリズムの評価
アルゴリズムを評価するために最も一般的に使用される回帰指標は、平均絶対誤差 (MAE)、平均二乗誤差 (MSE)、二乗平均平方根誤差 (RMSE)、および決定係数 (R2):
- 平均絶対誤差(MAE): 実際の値から予測値を差し引いて誤差を求め、それらの誤差の絶対値を合計して平均値を求めます。 このメトリックは、モデルの各予測の全体的なエラーの概念を示します。小さいほど (0 に近いほど) 優れています。
$$
mae =(frac {1} {n})sum_ {i = 1}^{n}左| 実際–予測される権利|
$$
注: あなたも遭遇するかもしれません y
および ŷ
(y-hat と読みます) 式の表記。 の y
実際の値を参照し、 ŷ
予測値に。
- 平均二乗誤差(MSE): MAE メトリックに似ていますが、誤差の絶対値を 0 乗します。 また、MAE と同様に、小さいほど、または XNUMX に近いほど良いです。 MSE 値は、大きな誤差がさらに大きくなるように XNUMX 乗されます。 細心の注意を払うべきことの XNUMX つは、その値のサイズと、データと同じスケールではないという事実のために、通常は解釈が難しいメトリックであるということです。
$$
mse = sum_ {i = 1} ^ {D}(実際–予測)^ 2
$$
- 二乗平均平方根誤差(RMSE):データの同じ単位にスケールバックするために、最終値の平方根を取得することにより、MSEで発生した解釈の問題を解決しようとします。 エラーのあるデータの実際の値を表示または表示する必要がある場合は、解釈が簡単で適切です。 データがどの程度変化する可能性があるかを示しているため、RMSEが4.35の場合、モデルは実際の値に4.35を追加したか、実際の値に到達するために4.35が必要だったため、エラーが発生する可能性があります。 0に近いほど、同様に優れています。
$$
rmse = sqrt {sum_ {i = 1} ^ {D}(実際–予測)^ 2}
$$
mean_absolute_error()
および mean_squared_error()
の方法 sklearn.metrics
次のスニペットに示すように、これらの指標を計算するために使用できます。
from sklearn.metrics import mean_absolute_error, mean_squared_error
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = mean_squared_error(y_test, y_pred, squared=False)
print(f'mae: {mae}')
print(f'mse: {mse}')
print(f'rmse: {rmse}')
上記のスクリプトの出力は次のようになります。
mae: 0.4460739527131783
mse: 0.4316907430948294
rmse: 0.6570317671884894
R2 で直接計算できます。 score()
方法:
regressor.score(X_test, y_test)
どの出力:
0.6737569252627673
結果は、KNN アルゴリズムの全体的なエラーと平均エラーが約であることを示しています。 0.44
, 0.43
. また、RMSE は、追加することでデータの実際の値を上回ったり下回ったりできることを示しています。 0.65
または減算 0.65
. それはどれほど良いですか?
価格がどのように見えるかを確認しましょう。
y.describe()
count 20640.000000
mean 2.068558
std 1.153956
min 0.149990
25% 1.196000
50% 1.797000
75% 2.647250
max 5.000010
Name: MedHouseVal, dtype: float64
平均は 2.06
平均からの標準偏差は 1.15
〜のスコア0.44
本当に恒星ではありませんが、それほど悪くはありません。
Rで2、取得する 1 (または 100) に最も近いほど良いです。 R2 データまたはデータの変更の程度を示します 分散 理解されているか、 説明 KNNによる。
$$
R ^ 2 = 1 – frac {sum(実際–予測)^ 2} {sum(実際–実際の平均)^ 2}
$$
の値で 0.67
、モデルがデータ分散の 67% を説明していることがわかります。 すでに 50% を超えています。これは問題ありませんが、あまり良くありません。 もっとうまくやれる方法はありますか?
の値を持つ所定の K を使用しました。 5
、そのため、5 つの近傍を使用してターゲットを予測していますが、これは必ずしも最適な数ではありません。 K の理想的な数を理解するために、アルゴリズムのエラーを分析し、損失を最小限に抑える K を選択できます。
KNN 回帰に最適な K を見つける
理想的には、どのメトリックが自分のコンテキストにより適しているかを確認できますが、通常はすべてのメトリックをテストすることが興味深いものです。 それらすべてをテストできるときはいつでも、それを実行してください。 ここでは、平均絶対誤差のみを使用して最適な K を選択する方法を示しますが、他のメトリックに変更して結果を比較することもできます。
これを行うには、for ループを作成し、1 から X までの近傍を持つモデルを実行します。 各相互作用で、MAE を計算し、MAE の結果とともに K の数をプロットします。
error = []
for i in range(1, 40):
knn = KNeighborsRegressor(n_neighbors=i)
knn.fit(X_train, y_train)
pred_i = knn.predict(X_test)
mae = mean_absolute_error(y_test, pred_i)
error.append(mae)
さて、プロットしてみましょう error
s:
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(range(1, 40), error, color='red',
linestyle='dashed', marker='o',
markerfacecolor='blue', markersize=10)
plt.title('K Value MAE')
plt.xlabel('K Value')
plt.ylabel('Mean Absolute Error')
プロットを見ると、K が最小の MAE 値のようです。 12
. より少ないデータをプロットして、プロットを詳しく見てみましょう。
plt.figure(figsize=(12, 6))
plt.plot(range(1, 15), error[:14], color='red',
linestyle='dashed', marker='o',
markerfacecolor='blue', markersize=10)
plt.title('K Value MAE')
plt.xlabel('K Value')
plt.ylabel('Mean Absolute Error')
ベストプラクティス、業界で認められた標準、および含まれているチートシートを含む、Gitを学習するための実践的で実用的なガイドを確認してください。 グーグルGitコマンドを停止し、実際に 学ぶ それ!
組み込みを使用して、最小誤差とそのポイントのインデックスを取得することもできます min()
関数 (リストで動作) またはリストを NumPy 配列に変換して取得します argmin()
(最小値を持つ要素のインデックス):
import numpy as np
print(min(error))
print(np.array(error).argmin())
隣人は 1 から数え始めましたが、配列は 0 ベースなので、11 番目のインデックスは 12 個の隣人です!
これは、MAE エラーが最も少ないポイントを予測できるようにするには、12 個の近傍が必要であることを意味します。 結果を比較するために、12 個の近傍でモデルとメトリクスを再度実行できます。
knn_reg12 = KNeighborsRegressor(n_neighbors=12)
knn_reg12.fit(X_train, y_train)
y_pred12 = knn_reg12.predict(X_test)
r2 = knn_reg12.score(X_test, y_test)
mae12 = mean_absolute_error(y_test, y_pred12)
mse12 = mean_squared_error(y_test, y_pred12)
rmse12 = mean_squared_error(y_test, y_pred12, squared=False)
print(f'r2: {r2}, nmae: {mae12} nmse: {mse12} nrmse: {rmse12}')
次のコードの出力:
r2: 0.6887495617137436,
mae: 0.43631325936692505
mse: 0.4118522151025172
rmse: 0.6417571309323467
12 個の近傍がある場合、KNN モデルはデータの分散の 69% を説明し、損失が少し少なくなりました。 0.44
〜へ 0.43
, 0.43
〜へ 0.41
, 0.65
〜へ 0.64
それぞれの指標で。 それほど大きな改善ではありませんが、それでも改善です。
注: この分析をさらに進めて、探索的データ分析 (EDA) と残差分析を行うと、特徴を選択してより良い結果を得るのに役立つ場合があります。
回帰に KNN を使用する方法については既に説明しましたが、ポイントの値を予測する代わりにポイントを分類したい場合はどうすればよいでしょうか? ここで、分類に KNN を使用する方法を見ていきます。
Scikit-Learn で K 最近傍点を使用した分類
このタスクでは、連続値を予測する代わりに、これらのブロック グループが属するクラスを予測します。 これを行うには、地区の住宅価格の中央値を異なる住宅価格範囲のグループに分割するか、 ビン.
分類に連続値を使用する場合は、通常、データをビン化できます。 このようにして、値ではなくグループを予測できます。
分類のためのデータの前処理
データ ビンを作成して、連続値をカテゴリに変換しましょう。
df["MedHouseValCat"] = pd.qcut(df["MedHouseVal"], 4, retbins=False, labels=[1, 2, 3, 4])
次に、データセットを属性とラベルに分割できます。
y = df['MedHouseValCat']
X = df.drop(['MedHouseVal', 'MedHouseValCat'], axis = 1)
を使用して以来、 MedHouseVal
列を作成してビンを作成するには、ドロップする必要があります MedHouseVal
列と MedHouseValCat
からの列 X
。 このように、 DataFrame
データセットの最初の 8 列 (つまり、属性、機能) が含まれますが、 y
のみが含まれます MedHouseValCat
割り当てられたラベル。
注: を使用して列を選択することもできます .iloc()
それらをドロップする代わりに。 ドロップするときは、割り当てる必要があることに注意してください y
割り当てる前の値 X
の削除された列を割り当てることができないため、値 DataFrame
メモリ内の別のオブジェクトに。
トレーニング セットとテスト セットへのデータの分割
回帰で行われたように、データセットもトレーニングとテストの分割に分割します。 異なるデータがあるため、このプロセスを繰り返す必要があります。
from sklearn.model_selection import train_test_split
SEED = 42
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=SEED)
標準の Scikit-Learn 値である 75% のトレーニング データと 25% のテスト データを再度使用します。 これは、以前の回帰と同じ数のトレーニングとテストのレコードを持つことを意味します。
分類のための特徴スケーリング
同じ未処理のデータセットとそのさまざまな測定単位を扱っているため、回帰データに対して行ったのと同じ方法で、フィーチャのスケーリングを再度実行します。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
分類のためのトレーニングと予測
データのビニング、分割、およびスケーリングの後、最終的に分類子を適合させることができます。 予測のために、ベースラインとして再び 5 つのネイバーを使用します。 インスタンス化することもできます KNeighbors_
クラスを引数なしで作成すると、自動的に 5 つのネイバーが使用されます。 ここでは、インポートする代わりに KNeighborsRegressor
、インポートします KNeighborsClassifier
、 クラス:
from sklearn.neighbors import KNeighborsClassifier
classifier = KNeighborsClassifier()
classifier.fit(X_train, y_train)
フィッティング後 KNeighborsClassifier
、テスト データのクラスを予測できます。
y_pred = classifier.predict(X_test)
予測を評価する時が来ました! この場合、値を予測するよりもクラスを予測する方が良い方法でしょうか? アルゴリズムを評価して何が起こるか見てみましょう。
分類のための KNN の評価
KNN 分類器を評価するために、 score
メソッドですが、リグレッサーではなく分類子をスコアリングしているため、別のメトリックを実行します。 分類の基本的なメトリックは次のとおりです。 accuracy
– 分類子がどれだけ多くの予測を正しく行ったかを示します。 精度の最低値は 0 で、最高値は 1 です。通常、その値に 100 を掛けてパーセンテージを取得します。
$$
精度 = frac{text{正しい予測の数}}{text{予測の総数}}
$$
注: 実際のデータで 100% の精度を得ることは非常に困難です。そうなった場合は、何らかの漏れや何か問題が発生している可能性があることに注意してください。理想的な精度値についてはコンセンサスがなく、状況にも依存します。 に応じて エラーのコスト (分類器を信頼していて、それが間違っていることが判明した場合、それはどれほど悪いことでしょう)、許容されるエラー率は 5%、10%、さらには 30% かもしれません。
分類子にスコアを付けましょう。
acc = classifier.score(X_test, y_test)
print(acc)
結果のスコアを見ると、分類器がクラスの最大 62% を正しく取得したと推測できます。 これはすでに分析に役立ちますが、分類器が何を正しくしたかを知るだけでは、それを改善することは困難です。
データセットには 4 つのクラスがあります。 クラス 90、1、および 2 の 3% が正しい、 だけ クラス30の権利の4%?
クラス間で共有されるバランスの取れた障害とは対照的に、一部のクラスの全体的な障害は、両方とも 62% の精度スコアをもたらす可能性があります。 精度は、実際の評価にはあまり適した指標ではありませんが、良い代用としては役立ちます。 多くの場合、バランスの取れたデータセットを使用すると、62% の精度が比較的均等に分散されます。 また、多くの場合、データセットはバランスが取れていないため、精度が不十分なメトリックであるという振り出しに戻ります。
それを判断できるように、他のメトリックを使用して結果をさらに詳しく調べることができます。 このステップも回帰とは異なります。ここでは以下を使用します。
- 混乱マトリックス: どれくらい正解したか、または間違っていたかを知るため 各クラス. 正しく、正しく予測された値が呼び出されます 真陽性 陽性と予測されたが陽性ではなかったものが呼び出されます 偽陽性. の同じ命名法 真のネガティブ および 偽陰性 負の値に使用されます。
- 精度: 分類子によってどの正しい予測値が正しいと見なされたかを理解するため。 精度は、これらの真陽性の値を陽性と予測されたもので除算します。
$$
精度 = frac{text{真陽性}}{text{真陽性} + text{偽陽性}}
$$
- リコール: 分類器によって識別された真陽性の数を理解するため。 再現率は、真の陽性を、陽性と予測されるはずだったもので割ることによって計算されます。
$$
リコール = frac{text{真陽性}}{text{真陽性} + text{偽陰性}}
$$
- F1スコア: バランスが取れているか、または 調和平均 精度と再現率。 最小値は 0 で、最大値は 1 です。
f1-score
が 1 の場合、すべてのクラスが正しく予測されたことを意味します。これは、実際のデータで取得するのが非常に難しいスコアです (例外はほとんど常に存在します)。
$$
text{f1-score} = 2* frac{text{precision} * text{recall}}{text{precision} + text{recall}}
$$
注: 重み付けされた F1 スコアも存在します。これは、すべてのクラスに同じ重みを適用しない F1 にすぎません。 重みは通常、クラスによって決定されます サポート – F1 スコア (特定のクラスに属するラベルの割合) を「サポート」するインスタンスの数。 サポートが低い (クラスのインスタンスが少ない) ほど、信頼性が低くなるため、そのクラスの重み付けされた F1 が低くなります。
confusion_matrix()
および classification_report()
の方法 sklearn.metrics
モジュールを使用して、これらすべてのメトリックを計算および表示できます。 の confusion_matrix
ヒートマップを使用して視覚化することをお勧めします。 分類レポートはすでに私たちに与えています accuracy
, precision
, recall
, f1-score
、しかし、これらの各指標をからインポートすることもできます sklearn.metrics
.
メトリックを取得するには、次のスニペットを実行します。
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
classes_names = ['class 1','class 2','class 3', 'class 4']
cm = pd.DataFrame(confusion_matrix(yc_test, yc_pred),
columns=classes_names, index = classes_names)
sns.heatmap(cm, annot=True, fmt='d');
print(classification_report(y_test, y_pred))
上記のスクリプトの出力は次のようになります。
precision recall f1-score support
1 0.75 0.78 0.76 1292
2 0.49 0.56 0.53 1283
3 0.51 0.51 0.51 1292
4 0.76 0.62 0.69 1293
accuracy 0.62 5160
macro avg 0.63 0.62 0.62 5160
weighted avg 0.63 0.62 0.62 5160
結果は、KNN がテスト セット内の 5160 レコードすべてを 62% の精度で分類できたことを示しています。これは平均を上回っています。 サポートはかなり等しい (データセット内のクラスの均等な分布) ため、重み付けされた F1 と重み付けされていない F1 はほぼ同じになります。
また、4 つのクラスそれぞれのメトリックの結果も確認できます。 そこからわかるのは、 class 2
精度が最低だった recall
、および最低 f1-score
. Class 3
すぐ後ろです class 2
スコアが最も低いため、次のようになります。 class 1
最高のスコアに続いて class 4
.
混同行列を見ると、次のことがわかります。
class 1
ほとんど間違えたclass 2
238件class 2
forclass 1
256 エントリで、class 3
260件class 3
ほとんど間違えたclass 2
、374 エントリ、およびclass 4
、193件中class 4
と誤って分類されたclass 3
339 エントリの場合、およびclass 2
130の場合。
また、対角線が真の正の値を示していることに注意してください。これを見ると、 class 2
および class 3
正確に予測された値が最も少なくなります。
これらの結果を使用して、分析をさらに詳しく調べて、なぜそれが起こったのかを突き止め、4 つのクラスがデータをビン化する最善の方法であるかどうかを理解することもできます。 おそらくからの値 class 2
および class 3
お互いに近すぎて、見分けるのが難しくなりました。
常に異なる数のビンでデータをテストして、何が起こるかを確認してください。
データ ビンの任意の数に加えて、選択した別の任意の数、K 近傍の数もあります。 メトリック値を最大化または最小化する K の数を決定するときに、回帰タスクに適用したのと同じ手法を分類に適用できます。
KNN 分類に最適な K を見つける
回帰に対して行ったことを繰り返し、K 値のグラフとテスト セットの対応するメトリックをプロットしてみましょう。 コンテキストに適したメトリックを選択することもできます。ここでは、選択します f1-score
.
このようにして、 f1-score
1 ~ 40 のすべての K 値に対するテスト セットの予測値。
まず、インポートします f1_score
から sklearn.metrics
次に、K 最近傍分類器のすべての予測の値を計算します。ここで、K の範囲は 1 から 40 です。
from sklearn.metrics import f1_score
f1s = []
for i in range(1, 40):
knn = KNeighborsClassifier(n_neighbors=i)
knn.fit(X_train, y_train)
pred_i = knn.predict(X_test)
f1s.append(f1_score(y_test, pred_i, average='weighted'))
次のステップは、 f1_score
K 値に対する値。 回帰との違いは、誤差を最小化する K 値を選択する代わりに、今回は誤差を最大化する値を選択することです。 f1-score
.
次のスクリプトを実行して、プロットを作成します。
plt.figure(figsize=(12, 6))
plt.plot(range(1, 40), f1s, color='red', linestyle='dashed', marker='o',
markerfacecolor='blue', markersize=10)
plt.title('F1 Score K Value')
plt.xlabel('K Value')
plt.ylabel('F1 Score')
出力グラフは次のようになります。
出力から、 f1-score
K の値が 15
. 15 個の近傍を使用して分類子を再トレーニングし、分類レポートの結果がどうなるかを見てみましょう。
classifier15 = KNeighborsClassifier(n_neighbors=15)
classifier15.fit(X_train, y_train)
y_pred15 = classifier15.predict(X_test)
print(classification_report(y_test, y_pred15))
これは出力します:
precision recall f1-score support
1 0.77 0.79 0.78 1292
2 0.52 0.58 0.55 1283
3 0.51 0.53 0.52 1292
4 0.77 0.64 0.70 1293
accuracy 0.63 5160
macro avg 0.64 0.63 0.64 5160
weighted avg 0.64 0.63 0.64 5160
メトリクスが 15 個のネイバーで改善され、63% 以上の精度が得られていることに注意してください。 precision
, recall
, f1-scores
、しかし、ビンをさらに調べて、なぜ f1-score
クラスの場合 2
および 3
まだ低いです。
回帰、ブロック値の決定、分類、ブロック クラスの決定に KNN を使用する以外に、KNN を使用して、ほとんどのブロック値と異なる平均ブロック値 (ほとんどのデータが行っていることに従わないもの) を検出することもできます。 つまり、KNN を次の目的で使用できます。 外れ値の検出.
Scikit-Learn を使用した外れ値検出のための KNN の実装
外れ値の検出 回帰と分類のために以前に行った方法とは異なる別の方法を使用します。
ここでは、各近傍がデータ ポイントからどれだけ離れているかを確認します。 デフォルトの 5 つのネイバーを使用しましょう。 データ ポイントの場合、K 最近隣点のそれぞれまでの距離を計算します。 そのために、Scikit-learn から別の KNN アルゴリズムをインポートします。これは、単純に呼び出される回帰または分類のいずれにも固有のものではありません。 NearestNeighbors
.
インポート後、インスタンス化します NearestNeighbors
5 つの近傍を持つクラス – 回帰の例で外れ値を識別するために 12 の近傍でインスタンス化することも、分類の例で同じことを行うために 15 でインスタンス化することもできます。 次に、列車データを適合させ、 kneighbors()
各データポイントと近隣インデックスの計算された距離を見つける方法:
from sklearn.neighbors import NearestNeighbors
nbrs = NearestNeighbors(n_neighbors = 5)
nbrs.fit(X_train)
distances, indexes = nbrs.kneighbors(X_train)
これで、データ ポイントごとに 5 つの距離 (データ ポイントとその 5 つの隣接ポイントの間の距離、およびそれらを識別するインデックス) が得られました。 これをよりよく視覚化するために、最初の XNUMX つの結果と配列の形状を見てみましょう。
最初の XNUMX つの距離形状を見るには、次を実行します。
distances[:3], distances.shape
(array([[0. , 0.12998939, 0.15157687, 0.16543705, 0.17750354],
[0. , 0.25535314, 0.37100754, 0.39090243, 0.40619693],
[0. , 0.27149697, 0.28024623, 0.28112326, 0.30420656]]),
(3, 5))
それぞれ距離が 3 の行が 5 つあることに注意してください。 また、隣人のインデックスを調べることもできます。
indexes[:3], indexes[:3].shape
この結果:
(array([[ 0, 8608, 12831, 8298, 2482],
[ 1, 4966, 5786, 8568, 6759],
[ 2, 13326, 13936, 3618, 9756]]),
(3, 5))
上記の出力では、5 つの隣接ノードのそれぞれのインデックスを確認できます。 ここで、引き続き 5 つの距離の平均を計算し、X 軸に各行をカウントし、Y 軸に各平均距離を表示するグラフをプロットできます。
dist_means = distances.mean(axis=1)
plt.plot(dist_means)
plt.title('Mean of the 5 neighbors distances for each data point')
plt.xlabel('Count')
plt.ylabel('Mean Distances')
平均距離が一定の値を持つグラフの部分があることに注意してください。 平均が高すぎず低すぎない Y 軸のポイントは、外れ値を切り捨てるために特定する必要があるポイントです。
この場合、それは平均距離が 3 の場所です。それを見つけることができるように、水平の点線でグラフを再度プロットしてみましょう。
dist_means = distances.mean(axis=1)
plt.plot(dist_means)
plt.title('Mean of the 5 neighbors distances for each data point with cut-off line')
plt.xlabel('Count')
plt.ylabel('Mean Distances')
plt.axhline(y = 3, color = 'r', linestyle = '--')
この線は平均距離を示しており、それを超えるとすべての値が変化します。 これは、 mean
上の距離 3
私たちの外れ値です。 を使用して、これらのポイントのインデックスを見つけることができます np.where()
. このメソッドは次のいずれかを出力します True
or False
に関する各インデックスの mean
3上記 調子:
import numpy as np
outlier_index = np.where(dist_means > 3)
outlier_index
上記のコード出力:
(array([ 564, 2167, 2415, 2902, 6607, 8047, 8243, 9029, 11892,
12127, 12226, 12353, 13534, 13795, 14292, 14707]),)
これで、外れ値ポイント インデックスができました。 それらをデータフレームで見つけましょう。
outlier_values = df.iloc[outlier_index]
outlier_values
この結果:
MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude MedHouseVal
564 4.8711 27.0 5.082811 0.944793 1499.0 1.880803 37.75 -122.24 2.86600
2167 2.8359 30.0 4.948357 1.001565 1660.0 2.597809 36.78 -119.83 0.80300
2415 2.8250 32.0 4.784232 0.979253 761.0 3.157676 36.59 -119.44 0.67600
2902 1.1875 48.0 5.492063 1.460317 129.0 2.047619 35.38 -119.02 0.63800
6607 3.5164 47.0 5.970639 1.074266 1700.0 2.936097 34.18 -118.14 2.26500
8047 2.7260 29.0 3.707547 1.078616 2515.0 1.977201 33.84 -118.17 2.08700
8243 2.0769 17.0 3.941667 1.211111 1300.0 3.611111 33.78 -118.18 1.00000
9029 6.8300 28.0 6.748744 1.080402 487.0 2.447236 34.05 -118.78 5.00001
11892 2.6071 45.0 4.225806 0.903226 89.0 2.870968 33.99 -117.35 1.12500
12127 4.1482 7.0 5.674957 1.106998 5595.0 3.235975 33.92 -117.25 1.24600
12226 2.8125 18.0 4.962500 1.112500 239.0 2.987500 33.63 -116.92 1.43800
12353 3.1493 24.0 7.307323 1.460984 1721.0 2.066026 33.81 -116.54 1.99400
13534 3.7949 13.0 5.832258 1.072581 2189.0 3.530645 34.17 -117.33 1.06300
13795 1.7567 8.0 4.485173 1.120264 3220.0 2.652389 34.59 -117.42 0.69500
14292 2.6250 50.0 4.742236 1.049689 728.0 2.260870 32.74 -117.13 2.03200
14707 3.7167 17.0 5.034130 1.051195 549.0 1.873720 32.80 -117.05 1.80400
外れ値の検出が終了しました。 これは、一般的なデータの傾向から逸脱した各データ ポイントを特定する方法です。 結果を改善するために、さらに調べたり、調査したり、場合によっては処理したり、データから削除したりする (誤って入力された場合) 必要があるトレーニング データには 16 個のポイントがあることがわかります。 これらのポイントは、入力エラー、平均ブロック値の不一致、またはその両方が原因である可能性があります。
KNN の長所と短所
このセクションでは、KNN アルゴリズムを使用することの長所と短所をいくつか紹介します。
メリット
- 実装は簡単です
- これは遅延学習アルゴリズムであるため、すべてのデータ ポイントでトレーニングを行う必要はありません (予測には K 最近傍のみを使用します)。 これにより、KNN アルゴリズムは、次のようなデータセット全体でトレーニングを必要とする他のアルゴリズムよりもはるかに高速になります。 サポートベクターマシン, 線形回帰, etc.
- KNN は予測を行う前にトレーニングを必要としないため、新しいデータをシームレスに追加できます
- KNN を使用するために必要なパラメーターは XNUMX つだけです。つまり、K の値と距離関数です。
デメリット
- KNN アルゴリズムは高次元データではうまく機能しません。これは、次元数が多いとポイント間の距離が「奇妙」になり、使用する距離メトリックが維持されないためです。
- 最後に、KNN アルゴリズムは、カテゴリ特徴では次元間の距離を見つけるのが難しいため、カテゴリ特徴ではうまく機能しません。
さらに進む – ハンドヘルド エンド ツー エンド プロジェクト
このガイド付きプロジェクトでは、強力な従来の機械学習モデルとディープ ラーニング モデルを構築し、Ensemble Learning を利用してメタ学習者をトレーニングし、Scikit-Learn および Keras モデルのバッグから住宅価格を予測する方法を学習します。
Tensorflow の上に構築された深層学習 API である Keras を使用して、アーキテクチャを実験し、積み重ねられたモデルのアンサンブルを構築し、 メタ学習者 家の価格を計算するためのニューラル ネットワーク (レベル 1 モデル)。
深層学習は素晴らしいですが、それに頼る前に、次のようなより単純な手法で問題を解決することもお勧めします。 浅い学び アルゴリズム。 私たちのベースラインパフォーマンスは、 ランダム フォレスト回帰 アルゴリズム。 さらに、次のような手法を使用して、Scikit-Learn を介してモデルのアンサンブルを作成することを検討します。 バギング および 投票.
これはエンド ツー エンドのプロジェクトであり、すべての機械学習プロジェクトと同様に、ここから始めます。 探索的データ分析、続いて データの前処理 そして最後に 建物の浅い および ディープラーニングモデル 以前に調査してクリーニングしたデータに合わせます。
まとめ
KNN は単純ですが強力なアルゴリズムです。 回帰、分類、外れ値検出など、多くのタスクに使用できます。
KNN は、ドキュメントの類似性とパターン認識を見つけるために広く使用されています。 また、レコメンダー システムの開発や、コンピューター ビジョンの次元削減と前処理のステップ、特に顔認識タスクにも使用されています。
このガイドでは、Scikit-Learn の K-Nearest Neighbor アルゴリズムの実装を使用して、回帰、分類、外れ値検出について説明しました。