机器学习(一) KNN算法原理与代码实现


title: 机器学习(一) KNN
date: 2021-08-12 18:31:35
categories: 机器学习
tags:
- 机器学习
- 人工智能
- 算法
- KNN算法

KNN算法

KNN算法的基本原理

KNN(K-Nearest Neighbor)最邻近分类算法是数据挖掘分类(classification)技术中最简单的算法之一,其指导思想是”近朱者赤,近墨者黑“,即由你的邻居来推断出你的类别。

如果一个样本在特征空间中的K个最相似(即特征空间中最邻近,用上面的距离公式描述)的样本中的大多数属于某一个类别,则该样本也属于这个类别。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。

在这里插入图片描述

如图, 距离蓝点最近的三个点都是红点,这是就可以把蓝点也归类为红色方。

在这里插入图片描述

当蓝点位于(5,2.5)时, 距离蓝点最近的三个点有两个为绿点, 一个为红,此时把蓝点归为红色方

实现KNN的步骤

  1. 计算想要分类的点到其余点的距离
  2. 按距离升序排列,并选出前K(KNN的K)个点,也就是距离样本点最近的K个点
  3. 加权平均,得到答案

三种加权距离

1.欧几里德距离(又称欧式距离)(Euclidean Distance)
欧式距离是最常见的距离度量,衡量的是多维空间中各个点之间的绝对距离。

公式如下:

image.png

2.明可夫斯基距离(Minkowski Distance)
明氏距离是欧式距离的推广,是对多个距离度量公式的概括性的表述。

公式如下:

image.png

这里的p值是一个变量,当p=2的时候就得到了上面的欧式距离,当p为时为曼哈顿距离,当p→∞时为切比雪夫距离。

3.曼哈顿距离(Manhattan Distance)
曼哈顿距离来源于城市区块距离,是将多个维度上的距离进行求和后的结果,即当上面的明氏距离中p=1时得到的距离度量公式

公式如下:

image.png

KNN的代码实现

基本KNN分类函数

from math import sqrt
import numpy as np
from collections import Counter

def KNN_classify(k, X_train, y_train, x):

    distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in X_train]
    nearest = np.argsort(distances)
    topk_y = [y_train[i] for i in nearest[:k]]
    votes = Counter(topk_y)
    return votes.most_common(1)[0][0]

# 模拟实现
raw_data_X = [[3.39, 2.33],
              [3.11,1.78],
              [1.34, 3.36],
              [3.58, 4.67],
              [2.28, 2.86],
              [7.42, 4.69],
              [5.74, 3.53],
              [9.17, 2.51],
              [7.79, 3.42],
              [7.93, 0.79]
             ]
raw_data_y = [0,0,0,0,0,1,1,1,1,1]
predict_y = KNN_classify(6, X_train, y_train, x) #返回raw_data_y 的类别

最基本KNN分类函数,实现基本的分类功能

KNN函数的封装

from math import sqrt
import numpy as np
from collections import Counter
from sklearn.metrics import accuracy_score

class KNNClassifier:

    def __init__(self, k):
        assert k>=1, "k must be valid"
        self.k = k
        self._X_train = None
        self._y_train = None

    def fit(self, X_train, y_train):
        assert X_train.shape[0] == y_train.shape[0], \
            "size of x_train must equal to size of y_train"
        assert self.k <= X_train.shape[0], \
            "the size of X_train must be at least K"
        self._X_train = X_train
        self._y_train = y_train
        return self

    def predict(self, X_predict):
        assert self._X_train is not None and self._y_train is not None, \
            "must fit before predict"
        assert X_predict.shape[1] == self._X_train.shape[1], \
            "the feature number of X_predict must be equal to X_train"

        y_predict = [self._predict(x) for x in X_predict]
        return np.array(y_predict)

    def _predict(self, x):
        assert x.shape[0] == self._X_train.shape[1], \
            "the feature number of x must be equal to X_train"
        distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in self._X_train]
        nearest = np.argsort(distances)
        topK_y = [self._y_train[i] for i in nearest[:self.k]]
        votes = Counter(topK_y)
        return votes.most_common(1)[0][0]

    def score(self, X_test, y_test):
        y_predict = self.predict(X_test)
        return accuracy_score(y_test, y_predict)
# KNN的手写数字识别实例
import numpy as np
from sklearn import datasets

digits = datasets.load_digits()
X = digits.data
y = digits.target

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

knn_clf = KNeighborsClassifier(n_neighbors=3)
y_predict = knn_clf.predict(X_test) # 预测结果
knn_clf.score(X_test, y_test) # 预测准确度

fit函数实现对数据的训练拟合

predict函数实现对数据的预测

score函数对预测结果做出评估

scikit-learn中的KNN

from sklearn.neighbors import KNeighborsClassifier

knn_clf = KNeighborsClassifier(n_neighbors=3)
knn_clf.score(X_test, y_test) # 预测准确度

超参数

寻找最好的k与距离权值

best_method = ""
best_score = 0.0
best_k = -1
for method in ["uniform", "distance"]:
    for k in range(1, 11):
        knn_clf = KNeighborsClassifier(n_neighbors=k, weights=method)
        knn_clf.fit(X_train, y_train)
        score = knn_clf.score(X_test, y_test)
        if score > best_score:
            best_k = k
            best_score = score
            best_method = method
print(f"best_k = {best_k}")
print(f"best_score = {best_score}")
print(f"best_methon = {best_method}")

网格搜索

param_grid = [
    {
        'weights': ['uniform'],
        'n_neighbors': [i for i in range(1, 11)]
    },
    {
        'weights': ['distance'],
        'n_neighbors': [i for i in range(1, 11)],
        'p': [i for i in range(1, 6)]
    }
]
knn_clf = KNeighborsClassifier()
from sklearn.model_selection import GridSearchCV

grid_search = GridSearchCV(knn_clf, param_grid, n_jobs=-1, verbose=2)
grid_search.fit(X_train,y_train)

"""
输出结果
GridSearchCV(cv='warn', error_score='raise-deprecating',
             estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30,
                                            metric='minkowski',
                                            metric_params=None, n_jobs=None,
                                            n_neighbors=3, p=3,
                                            weights='distance'),
             iid='warn', n_jobs=-1,
             param_grid=[{'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
                          'weights': ['uniform']},
                         {'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
                          'p': [1, 2, 3, 4, 5], 'weights': ['distance']}],
             pre_dispatch='2*n_jobs', refit=True, return_train_score=False,
             scoring=None, verbose=2)
"""

一些代码的底层

train_test_split
import numpy as np


def train_test_split(X, y, test_ratio=0.2, seed=None):
    assert X.shape[0] ==y.shape[0], \
        "the size of x must equal to y"
    assert 0.0<=test_ratio<=1.0, \
        "test_ratio must be valid"

    if seed:
        np.random.seed(seed)
    shuffle_indexes = np.random.permutation(len(X))
    test_size = int(len(X) * test_ratio)
    test_indexes = shuffle_indexes[:test_size]
    train_indexes = shuffle_indexes[test_size:]
    X_train = X[train_indexes]
    y_train = y[train_indexes]

    X_test = X[test_indexes]
    y_test = y[test_indexes]

    return X_train, X_test, y_train, y_test

StandardScaler
import numpy as np

class StandardScaler:

    def __init__(self):
        self.mean_ = None
        self.scale_ = None

    def fit(self, X):
        assert X.ndim == 2, "the dimension of x must be 2"

        self.mean_ = np.array([np.mean(X[:, i])] for i in range(X.shapep[1]))
        self.scale_ = np.array([np.std(X[:, i])] for i in range(X.shapep[1]))

    def tranform(self, X):
        assert X.ndim == 2, "the dimension of x must be 2"

        resX = np.empty(shape=X.shape, dtype=float)
        for col in range(X.shape[1]):
            resX[:, col] = (X[:,col]-self.mean_[col]) / self.scale_[col]

        return resX
accuracy_score
import numpy as np
from math import sqrt

def accuracy_score(y_true, y_predict):
    """计算y_true和y_predict之间的准确率"""
    assert len(y_true) == len(y_predict), \
        "the size of y_true must be equal to the size of y_predict"

    return np.sum(y_true == y_predict) / len(y_true)

更多机器学习笔记

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值