「機械学習の公式導出とコード実装」第 6 章-k 最近傍アルゴリズム

「機械学習の公式導出とコード実装」の学習ノート、自分の学習プロセスを記録します。詳細な内容については、著者の本を購入してください。

k 最近傍アルゴリズム

k 最近傍 ( k-nearest neighbork-NN) アルゴリズムは、古典的な分類アルゴリズムです。k 最近傍アルゴリズムは、k の最近傍インスタンスのカテゴリに従って、新しい入力インスタンスの分類を決定します。したがって、k 最近傍アルゴリズムには、主流の機械学習アルゴリズムのような明示的な学習およびトレーニング プロセスがありません。このため、k 最近傍アルゴリズムの実装は、前の章で説明した回帰モデルとは若干異なります。k值的选择距离度量方式および は分类决策规则、k 最近傍アルゴリズムの 3 つの要素です。

1 距離測定方法

特徴空間内の 2 つのインスタンス間の類似性を測定するには、距離を使用してそれを記述します。一般的に使用される距離の尺度には、闵氏距离など马氏距离が含まれます。

(1)闵氏距离つまり闵可夫斯基距离( Minkowski distance)、距離は次のように定義されます。m次元ベクトル サンプル のセットXxixjXxi=が与えられ(x1i,x2i,...xmi)Tた場合、サンプル xi とサンプル xj の間の最小距離は次のように定義できます。
dij = ( ∑ k = 1 m ∣ xki − xkj ∣ p ) 1 p , p ≥ 1 d_{ij}=\left ( \sum_{k=1}^{m}\left | x_{ki}-x_{kj} \right | ^ {p} \right )^{\frac{1}{p} }、ページ 1dイジ=(k = 1メートルxバツkjp )p1p1そのとき、 ()になること
容易にわかります: dij = ∑ k = 1 m ∣ xki − xkj ∣ d_{ij}=\sum_{k=1}^{m}\left | x_{ki }-x_{kj} \right |p=1闵氏距离曼哈顿距离Manhatan distance
dイジ=k = 1メートルxバツkj
そのときp=2()闵氏距离となる: dij = ( ∑ k = 1 m ∣ xki − xkj ∣ 2 ) 1 2 d_{ij}=\left ( \sum_{k=1}^{m}\left | x_ {ki}-x_{kj} \right | ^{2} \right )^{\frac{1}{2} }欧氏距离Euclidean distance
dイジ=(k = 1メートルxバツkj2 )21
このときp=∞( )闵氏距离とも呼ばれます: dij = max ∣ xki − xkj ∣ d_{ij}=max\left | x_{ki}-x_{kj} \right |切比雪夫距离Chebyshev distance
dイジ=×xバツkj
(2)马氏距离正式名称马哈拉诺比斯距离(Mahalanobis distance) は、さまざまな特徴間の相関を測定するクラスタリング測定手法です。サンプル セットが与えられX=(xij)mxn、サンプルの共分散行列が であると仮定するとS、サンプル xi とサンプル xj の間のマハラノビス距離は次のように定義できます。
dij = [ ( xi − xj ) TS − 1 ( xi − xj ) ] 1 2 d_ {ij}=\left [\left(x_{i}-x_{j}\right)^{T} S^{-1}\left(x_{i}-x_{j}\right)\right] ^{\frac{1}{2}}dイジ=[ ( x私はバツj)TS1( ×私はバツj) ]21
単位行列の場合S、つまりサンプルの特徴が互いに独立で分散が 1 の場合、マハラノビス距離はユークリッド距離になります。

k 最近傍アルゴリズムの特徴空間は n 次元の実数ベクトル空間であり、一般にユークリッド距離がインスタンス間の距離の尺度として直接使用されます。

2k 最近傍アルゴリズムの基本原理

トレーニング セットが与えられた場合、新しい入力インスタンスについて、トレーニング セット内のインスタンスに最も近い k 個のインスタンス、k 個のインスタンスの大部分が属するクラス、およびインスタンスが属するクラスを見つけます。したがって、いくつかの重要なポイントを要約します。

  • このインスタンスに最も近いインスタンスを見つけます。見つけ方は次のとおりです。通常、ユークリッド距離が使用されます。
  • k 個のインスタンスがあり、k のサイズを選択する方法。
  • k 個のインスタンスの過半数はどのカテゴリに属しますか? 通常、多数決の分類ルールが選択されます。

3 つのキー ポイントのうち、k 値の選択に注意する必要があります。k 値が小さい場合、予測結果はインスタンスに非常に敏感になり、分類器の耐ノイズ能力が低くなり、分類器のノイズが発生しやすくなります。過学習; k 値が大きい場合、対応する分類の誤差が増加し、モデル全体が単純になり、ある程度の過小学習が発生します。一般に、適切な k 値を選択するために相互検証が使用されます。

knnおよびk-means
k近邻法 ( knn) は、基本的な分類および回帰手法です。
k-meansこれはシンプルで効果的なクラスタリング方法です。

3 numpy に基づく k 最近傍アルゴリズムの実装

新しいサンプル インスタンスとトレーニング サンプルの間のユークリッド距離関数を定義します。

import numpy as np

# 定义欧式距离函数
def compute_distances(X_test, X_train): # 测试样本实例矩阵,训练样本实例矩阵

    num_test = X_test.shape[0] # 45
    num_train = X_train.shape[0] # 105
    dists = np.zeros((num_test, num_train)) # 基于训练和测试维度的欧式距离初始化 45*105
    M = np.dot(X_test, X_train.T) # 测试样本与训练样本的矩阵点乘 45*105
    te = np.square(X_test).sum(axis=1) # 测试样本矩阵平方 45*feature_dim
    tr = np.square(X_train).sum(axis=1) # 训练样本矩阵平方 105*feature_dim
    dists = np.sqrt(-2 * M + tr + np.matrix(te).T) # 计算欧式距离,广播
    return dists # 欧氏距离
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.utils import shuffle

# 加载鸢尾花数据集
iris = datasets.load_iris() # 加载鸢尾花数据集
X, y = shuffle(iris.data, iris.target, random_state=13) # 打乱数据
X = X.astype(np.float32)

offset = int(X.shape[0] * 0.7)
X_train, y_train, X_test, y_test = X[:offset], y[:offset], X[offset:], y[offset:]
y_train = y_train.reshape(-1, 1)
y_test = y_test.reshape(-1, 1)

dists = compute_distances(X_test, X_train)
plt.imshow(dists, interpolation='none')
plt.show()

ここに画像の説明を挿入
デフォルトの k 値と分類決定ルールを含むラベル予測機能

from collections import Counter

# 标签预测函数
def predict_labels(y_train, dists, k=1): # 训练集标签, 测试集与训练集的欧氏距离, k值

    num_test = dists.shape[0] # 测试样本量
    y_pred = np.zeros(num_test) # 初始化测试集预测结果
    for i in range(num_test):
        closest_y = [] # 初始化最近邻列表
        # 按欧式距离矩阵排序后取索引,并用训练集标签按排序后的索引取值,最后展开列表
        labels = y_train[np.argsort(dists[i, :])].flatten() # argsort函数返回的是数组值从小到大的索引值
        closest_y = labels[0:k] # 取最近的k个值进行计数统计
        c = Counter(closest_y) # Counter
        y_pred[i] = c.most_common(1)[0][0] # 取计数最多的那个类别
    return y_pred # 测试集预测结果
# 尝试对测试集进行预测,在默认k值取1的情况下,观察分类准确率
y_test_pred = predict_labels(y_train, dists, k=10)
y_test_pred = y_test_pred.reshape(-1, 1)
num_correct = np.sum(y_test_pred == y_test) # 找出预测正确的实例
accuracy = float(num_correct / y_test.shape[0])
print(accuracy)
0.9777777777777777

最適な k 値を見つけるために、5 分割交差検証法を使用して検索を試みます。

from sklearn.metrics import accuracy_score

num_folds = 5
k_choices = [1, 3, 5, 8, 10, 12, 15, 20, 50, 100]
X_train_folds = np.array_split(X_train, num_folds)
y_train_folds = np.array_split(y_train, num_folds)
k_to_accuracies = dict()

for k in k_choices:
    for fold in range(num_folds):
        # 为传入的训练集单独划分出一个验证集作为测试集
        validation_X_test = X_train_folds[fold]
        validation_y_test = y_train_folds[fold]
        temp_X_train = np.concatenate(X_train_folds[:fold] + X_train_folds[fold + 1:])
        temp_y_train = np.concatenate(y_train_folds[:fold] + y_train_folds[fold + 1:])
        temp_dists = compute_distances(validation_X_test, temp_X_train)
        temp_y_test_pred = predict_labels(temp_y_train, temp_dists, k)
        temp_y_test_pred = temp_y_test_pred.reshape(-1, 1)
        accuracy = accuracy_score(temp_y_test_pred, validation_y_test)
        k_to_accuracies[k] = k_to_accuracies.get(k, []) + [accuracy]
        
# 不同k值下的分类准确率    
for k in k_to_accuracies:
    for accuracy in k_to_accuracies[k]:
        print(f'k = {
      
      k}, accuracy = {
      
      accuracy}')
k = 1, accuracy = 0.9047619047619048
k = 1, accuracy = 1.0
k = 1, accuracy = 0.9523809523809523
k = 1, accuracy = 0.8571428571428571
k = 1, accuracy = 0.9523809523809523
k = 3, accuracy = 0.8571428571428571
k = 3, accuracy = 1.0
k = 3, accuracy = 0.9523809523809523
k = 3, accuracy = 0.8571428571428571
k = 3, accuracy = 0.9523809523809523
k = 5, accuracy = 0.8571428571428571
k = 5, accuracy = 1.0
k = 5, accuracy = 0.9523809523809523
k = 5, accuracy = 0.9047619047619048
k = 5, accuracy = 0.9523809523809523
k = 8, accuracy = 0.9047619047619048
k = 8, accuracy = 1.0
k = 8, accuracy = 0.9523809523809523
k = 8, accuracy = 0.9047619047619048
k = 8, accuracy = 0.9523809523809523
k = 10, accuracy = 0.9523809523809523
k = 10, accuracy = 1.0
k = 10, accuracy = 0.9523809523809523
k = 10, accuracy = 0.9047619047619048
k = 10, accuracy = 0.9523809523809523
...
k = 100, accuracy = 0.38095238095238093
k = 100, accuracy = 0.3333333333333333
k = 100, accuracy = 0.23809523809523808
k = 100, accuracy = 0.19047619047619047

信頼区間を使用して誤差範囲をプロットする

for k in k_choices:
    accuracies = k_to_accuracies[k]
    plt.scatter([k] * len(accuracies), accuracies)
accuracies_mean = np.array([np.mean(v) for k, v in k_to_accuracies.items()]) # 计算标准差
accuracies_std = np.array([np.std(v) for k, v in k_to_accuracies.items()]) # 计算方差
plt.errorbar(k_choices, accuracies_mean, yerr=accuracies_std) # 误差棒图 
plt.title('cross-validation on k')
plt.xlabel('k')
plt.ylabel('cross_validation accuracy')
plt.show()

ここに画像の説明を挿入

4 sklearnに基づくk最近傍アルゴリズムの実現

from sklearn.neighbors import KNeighborsClassifier
neigh = KNeighborsClassifier(n_neighbors=10)
neigh.fit(X_train, y_train)
y_pred = neigh.predict(X_test)
y_pred = y_pred.reshape(-1, 1)
accuracy = accuracy_score(y_pred, y_test)
print(accuracy)
0.9777777777777777

Notebook_Github アドレス

おすすめ

転載: blog.csdn.net/cjw838982809/article/details/131365728