K 最近隣アルゴリズム学習 (KNN)

機械学習 - K 最近傍アルゴリズム (KNN)

基本知識

基本的

テスト サンプルが与えられると、何らかの距離測定に基づいてトレーニング セット内のk 個の最も近いトレーニング サンプルを見つけ出し、これらの k 個の「近傍」の情報に基づいて予測を行います。

——周志華、スイカブック

トレーニング サンプル セットとも呼ばれるサンプル データ セットがあり、サンプル セット内の各データにはラベルが付いています。つまり、サンプル セット内の各データとそれが属するカテゴリの間の対応関係がわかっています。ラベルなしで新しいデータを入力した後、新しいデータの各特徴がサンプル セット内のデータの対応する特徴と比較され、アルゴリズムによっていくつかのデータの最も類似したデータ (最近傍) の分類ラベル (上位 k) が抽出されます。サンプルの特徴。——機械学習の実践

自己理解: つまり、適切にラベル付けされたトレーニング セット サンプルが多数あり、予測用のサンプルを投げ、最も近い k 個のトレーニング サンプルのラベル付けによって、予測対象のサンプルがどのカテゴリに属する​​かを判断します。予測するサンプルの点。

原理を読んだ後は、この KNN アルゴリズムの基本を理解できるはずです。次の例を見てください。(型パターンは教科書からのもので、データはアルゴリズムを理解するために自分で書きました。)


喧嘩の回数、キスの回数、映画ごとの映画評価の種類

映画のタイトル 戦闘シーン キスカメラ 映画の種類
夜明け前の愛 3 104 ロマンス
ドキドキ 2 100 ロマンス
注意深く聞いてください 1 81 ロマンス
ルオ・シャオヘイ・ジ・ジ 101 5 アクション映画
アセンブリ番号 99 2 アクション映画
終末戦争 98 2 アクション映画
18 90 知らない

上の表から、これまでにわかっている 6 つの情報を使用して次の座標図を構築できます。

ここに画像の説明を挿入

次に、距離公式を使用して「 ? 」に最も近いk 個の点を計算し、これらの k 点によって「 ? 」の映画の種類を判断します。明らかに、彼に最も近い k ポイントによって、それが恋愛映画であると判断できます。

次に、次のように図で理解します。

ここに画像の説明を挿入

オレンジ色の四角と青い三角形はトレーニングの結果であり、緑色の丸は予測する必要があるサンプルです。図から、テストとトレーニング対象のサンプルを検出するために使用される ⭕ が 2 つあることがわかります。サンプル距離 (私自身) の最小距離円。k=1とk=3では得られる結果が異なることがわかり、k=1の場合は予測結果は正方形、k=3の場合は予測結果は三角形となるはずです。異なる k が予測結果に大きな影響を与えることがわかりました。では、この k の値はどのように選択すればよいでしょうか? 上の表から、なぜ k が基数であるのに、なぜ偶数を定義しないのかも簡単にわかります。

KNN に関する基本的な質問

距離はどのように計算されますか?

このアルゴリズムを見たとき、私が最初に考えたのは、このアルゴリズムの最短距離をどのように計算するかということでした。思い浮かぶのは白紙の記事ですが、距離はどうやって計算されているのでしょうか、目で見ていませんか?(その後、私は本当に年をとって、もう何もできないことに気づきました)

ユークリッド距離: 2 点間の直線距離

公式:

ここに画像の説明を挿入

もちろん、この式を使用する場合は、テスト対象のサンプルと各トレーニング サンプルの間の距離を計算し、下位 k 個のサンプルを残すようにフィルター処理し、k 個のサンプルのラベルを使用して予測結果を判断する必要があります。テストされるサンプル。

マンハッタン距離: 都市ブロック範囲とも呼ばれます。座標軸上の 2 点間の絶対距離の合計。

公式

ここに画像の説明を挿入

これは、より高い次元 (より多くの特徴) を使用した予測分類により適しています。

上記のほとんどはユークリッド距離を使用していますが、結局のところ、これはシンプルかつ直接的であり、最も重要なことは、誰もがそれを理解しているということです。

私がどの方法を好むかを教えてください。

テストするサンプルの点を円の中心点として直接取得し、最小半径を決定し、円内のトレーニング サンプルの数>= k になるまで半径を徐々に拡大してから、トレーニング サンプルの数を判断します。サークル内のトレーニング サンプルの数に従ってテストされるサンプルの予測タイプ。

k はサイズをどのように定義しますか?

例の四角と三角形のケースを通じて、k の異なる値の影響が異なり、一般化能力が比較的低いことがわかります。結局のところ、他のアルゴリズムと比較して、学習 (トレーニング) がありません。 )のプロセス。

k値 影響
大きすぎる 予測ラベルは安定しており、平坦すぎるため、分類があいまいですが、遠く離れた隣接サンプルにも機能します。
小さすぎる 過学習を引き起こしやすく、近傍のサンプル点に敏感すぎる

ネットワーク上の結果は次のとおりです。相互検証を通じて最適な K 値を常に試し、より小さい K 値の選択から開始し、継続的に K 値を増加させ、次に検証セットの分散を計算し、最終的により適切な K 値を見つけます。 。

なぜ k は偶数として定義されないのでしょうか?

なぜ偶数を定義しないのかというと、完全に絡み合いを避けるためです。KNN の学習サンプルには、 もも もありません、あれもこれもありません、それは確かです。奇数を定義すると、結果が同点になることはあり得ません。(もちろん、ここでは 2 項分類について話しています。分類の残りの部分は k 用に設計する必要があります。たとえば、4、7... は 3 つの分類に使用できます。要するに、相対的な状況を避けるためです)

KNNのメリットとデメリット

まず、次のように KNN の一般的なプロセスを見てみましょう。

  1. データの収集: あらゆる合法的な手段
  2. データの準備: トレーニング サンプルの x、y、および (x, y) を決定するための、構造化データ形式、つまりバイナリ分類におけるトレーニング サンプルの座標内の点
  3. データの分析: あらゆる合法的な手段
  4. トレーニング アルゴリズム: 適用されません! だから - いいえ
  5. テストアルゴリズム: エラー率の計算
  6. アルゴリズムを使用します。最初にサンプル データと構造化された出力結果を入力し、knn アルゴリズムを実行して入力サンプルがどのカテゴリに属する​​かを判断し、それを処理します。
アドバンテージ 欠点がある
精度高 トレーニングプロセスがない
外れ値に対して鈍感である 高い計算複雑性
データ入力は想定されていません 空間の複雑性が高い

コード

初書き込み(2022.10.25)

データの収集、処理、コードの作成については、以下をお読みください。

百度地図から集美大学の本部とその周辺地域を抽出し、下図のようにデータを 2 つの部分に分割し、1 つは jmu キャンパス内のデータサンプル、もう 1 つは jmu というラベルを定義します。一部は集美大学キャンパス外にありますが、データサンプルではラベルをunjmuとして定義し、その水平座標と垂直座標からjmu学校本部内かjmu学校本部外かを判定します。

ここに画像の説明を挿入

トレーニングセット:

地図上の建物を選択してください カスタム位置情報 ラベル
豫州 (3,85) ジム
尚大 (15,70) ジム
ル・ダ (7,58) ジム
魯振湾 (17,62) ジム
アトゥール ホテル (33,28) ウンジュム
カーキー図書館 (30,100) ジム
ワンダ (10,10) ウンジュム
周麻浦 (2,1) ウンジュム
新街自動車修理 (45,31) ウンジュム
集美区政府 (50,40) ウンジュム
広沙園 (53,55) ウンジュム
ジメイラジオとテレビ (60,58) ウンジュム
地震局 (52,15) ウンジュム

テストセット:

位置 ラベル
(5、7) ウンジュム
(10,100) ジム
(49,49) ジム
(35,40 ) ウンジュム

あまり言うことはありませんが、コードを投稿してください。

import matplotlib.pyplot as plt
import numpy as np
import math
class KNN:
    def __init__(self, x_train, x_test, k):
        # 保留测试点与所以训练样本的距离
        self.distance =  np.zeros((len(x_test), len(x_train)))
        # 保留预测结果
        self.predicted = []
        # KNN中k的取值(不懂看上面基本知识点)
        self.k = k

   # KNN核心算法
    def knn(self, x_test, x_train, y_train):
        print(y_train)
        for i in range(len(x_test)):
            for j in range(len(x_train)):
                self.distance[i][j] = self.knn_distance(x_test[i], x_train[j])
            self.predicted.append(self.knn_predicted(self.distance[i], y_train))
        return self.predicted

    # 利用欧拉公式计算距离
    def knn_distance(self, x1, x2):
        dis = math.sqrt(math.pow((x1[0]-x2[0]),2) + math.pow((x1[1]-x2[1]),2))
        return dis

    def knn_predicted(self, distances, y_train):
        #利用numpy的argsort方法获取前K小样本的索引
        k_predicted_index = distances.argsort()[:self.k]
        # 由于对一些库的函数学习不深,所以选择下面我自己可以实现的方法
        count_jmu = 0
        count_other =0
        for i in range(len(k_predicted_index)):
            if(y_train[k_predicted_index[i]] == 'jmu'):
                count_jmu += 1
            else:
                count_other += 1
        if(count_jmu > count_other):
            return 'jmu'
        else:
            return 'unjmu'
# 自定义训练数据集
x_train = [[3, 85], [15, 70], [7, 58], [17,62], [33,28], [30,100], [10,10], [2,1], [45,31], [50,40], [53,55], [60,58], [52,15]]
y_train = ['jmu', 'jmu', 'jmu', 'jmu', 'unjmu','jmu' ,'unjmu' ,'unjmu' ,'unjmu' ,'unjmu' ,'unjmu' ,'unjmu' ,'unjmu']

# 自定义测试数据集
x_test = [[5,7], [10,100], [19,49], [35,40]]
y_test = ['unjum','jum','unjum','jum']

# 设置KNN中的k
k = 3
knn = KNN(x_train, x_test, k)

# 获得测试集的预测结果
pred = knn.knn(x_test, x_train, y_train)
print(pred)

出力には次のことが示されます。

ここに画像の説明を挿入

強化(2022.10.28)

数据集:
链接:https://pan.baidu.com/s/1yrDGiK9yXFxB_JyC3Q5ycg
提取码:1234

如果你觉得上面的描述或者代码不够清晰,请看这里,对于上述的代码,如果想要改变数据集好像很困难,而且变化不大,不易于修改,所以进行了一定的精炼,请看下面:

代码:

首先,对于python来说,典型的黑盒子,我们需要导入我们所需方法的库进行调用。

import matplotlib.pyplot as plt
import numpy as np
import math
import pandas as pd
from sklearn.model_selection import train_test_split

然后,根据KNN的算法思想进行编写KNN主体函数

class KNN:
    def __init__(self, x_train, x_test, k):
        # 保存距离
        self.distance =  np.zeros((len(x_test), len(x_train)))
        # 预测结果
        self.predicted = []
        # knn中的k值
        self.k = k  
    # knn的主要函数 
    def knn(self, x_test, x_train, y_train):
        for i in range(len(x_test)):
            for j in range(len(x_train)):
                self.distance[i][j] = self.knn_distance(x_test[i], x_train[j])
            self.predicted.append(self.knn_predicted(self.distance[i], y_train))
        return self.predicted

# 欧式距离的计算
    def knn_distance(self, x1, x2):
        dis = math.sqrt(math.pow((x1[0]-x2[0]),2) + math.pow((x1[1]-x2[1]),2))
        return dis

# 预测knn函数
    def knn_predicted(self, distances, y_train):
        k_predicted_index = distances.argsort()[:self.k]
        count_jmu = 0
        count_other =0
        for i in range(len(k_predicted_index)):
            if(y_train[k_predicted_index[i]] == 'jmu'):
                count_jmu += 1
            else:
                count_other += 1
        if(count_jmu > count_other):
            return 'jmu'
        else:
            return 'unjmu'

通过绘制测试集和训练集的样本分布来视觉上查看预测结果

# 绘图(看数据集分布)
def paint(x_train, x_test):
# 绘制图像, X、Y是存储unjmu的数据,X1、Y1存储的是jmu的数据,Z是用于过渡
    X = []
    X1 = []
    X2 = []
    Y = []
    Y1 = []
    X2 = []
    Z = []
    # 根据训练样本获取x、y
    x_train = np.array( x_train)
    X = x_train[:,0]
    Y = x_train[:,1]

    # 对数据进行处理,根据训练集的数据以及label划分出jmu的点和unjum的点
    for i in range(len(y_train)):
        if(y_train[i] == 'jmu'):
            Z.append(i)
            X1.append(X[i])
            Y1.append(Y[i])
    X = np.delete(X,Z)
    Y = np.delete(Y,Z)
    
    # 绘制测试集的数据准备
    x_test = np.array(x_test)
    X2 = x_test[:,0]
    Y2 = x_test[:,1] 

    # 绘图,红色为jmu的数据,绿色是unjmu数据,蓝色为测试样本
    plt.scatter(X, Y, color = 'g')
    plt.scatter(X1, Y1, color ='r')
    plt.scatter(X2, Y2, color ='b')
# 数据处理,将csv获得的数据变成列表
def data_tolist(x_train, x_test, y_train, y_test):
    x_train = np.array(x_train)
    x_train = x_train.tolist()

    y_train = np.array(y_train)
    y_train = y_train.tolist()

    x_test = np.array(x_test)
    x_test = x_test.tolist()

    y_test = np.array(y_test)
    y_test = y_test.tolist()

    return x_train, x_test, y_train, y_test
# 计算精确度
def predicted(pred, y_test):
    count = 0
    for i in range(len(pred)):
        if(y_test[i] == pred[i]):
            count += 1
    pred1 = count / len(y_test)
    return pred1
# 利用panda库进行对csv文件的读取和处理操作
data=pd.read_csv("D:/桌面/1.csv")
X = data.iloc[:,:2]
Y = data.iloc[:,2]

# 划分数据集,并且将数据集转换成list类型,0.8的训练集,0.2的测试集
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2)

x_train, x_test, y_train, y_test = data_tolist(x_train, x_test, y_train, y_test)

paint(x_train, x_test)

for i in range(len(x_train)):
    if (i%2 != 0):
        k = i
        knn = KNN(x_train, x_test, k)
        pred = knn.knn(x_test, x_train, y_train)
        print(y_test)
        print(f"预测结果:{
      
      pred}")
        predicte = predicted(pred, y_test)
        print(f"k = {
      
      k}时,测试精度为:{
      
      predicte}")

ここに画像の説明を挿入

注:红色定义为jmu样本,蓝色为待遇测样本,绿色为unjmu样本

结果分析

以上面增强代码和运行结果进行分析,去十次结果(理应进行对k=0,到k=len(x_train)进行分析),之所以取10十因为k取值越大,其实结果过于模糊,说白了k越大,等于比较数据集那个label的样本数更多了。

k = ? predicate
1 1
3 0.83333
5 1
7 1
9 1
11 0.83333
13 0.83333
15 0.83333
21 0.66666
23 0.66666

从上表看:貌似k取越小越好,k越大预测的精度就越差了,这是为什么呢?难道k真的取值越小越好吗?

首先来说第一个问题:

k越大精度就越差,为什么呢?

首先,先分析一下我的数据集,我的数据集中label为unjmu的样本和jmu的样本数量上是不匹配的,unjmu的样本明显大于jmu,那么在k取值越大的情况下unjmu的样本就会在那些label标签为jmu中的作用越大,导致将label将jmu样本预测成unjmu。所以说,当k大于一定的值时,预测结果和样本数据集标签种类的数量关系会被放大。

再说一下第二个问题:

k取越小越好吗?
看下图:

ここに画像の説明を挿入

ボックス内で予測されるサンプルは unjmu ですが、彼に最も近いラベルは人間が誤ってラベル付けしたラベルです。k が小さいほど良い場合は、k=1 (最近傍) を取る必要がありますが、この場合は、サンプルはかなりの範囲でエラーが 0 である必要がありますが、ラベルが間違っていると予測結果にエラーが生じる可能性があり、手動でラベル付けされたデータセットでエラーをゼロにすることは困難です。(最初にデータを書き始めたときと同じように、ラベルの書き込みエラーが発生しました)。したがって、k の値はできるだけ小さくなりません。

要約すると、k はどのようにサイズを定義すればよいでしょうか。KNN の基本的な質問の上記の提案では、相互検証の方法が言及されていますので、試してみることができます。個人的には、k の値は主に次の側面に関連していると考えています。

  1. データセットのサイズ。(大きすぎると k は小さすぎる値を取ることができなくなり、過学習が深刻になります。小さすぎると k は大きな値を取ることができなくなり、曖昧さが強くなりすぎます)
  2. サンプルラベルの種類。(種類が多いため、表記ミスの可能性が高くなります)
  3. サンプルのデータ次元。(次元ごとに異なる距離計算式を使用するのが最善であり、計算方法も異なるため、k は選択する価値があり、調整する必要があります)

おすすめ

転載: blog.csdn.net/weixin_51961968/article/details/127534931