【自己动手写机器学习算法】第3章 k近邻法描述

11 篇文章 0 订阅
11 篇文章 0 订阅

K近邻算法本身是个很简单的算法,某些时候甚至不能称之为机器学习算法,因为它没有“学习”的过程,仅仅通过判定和要预测的 x x x相近的点都呈现的表征来确定 x x x的对应类标 y y y就可以了,但是《统计学习方法》这本书使用了KD-tree的方法来寻找最近邻,其主要目的是在数据量比较大的时候能够优化查找效率,从原理来讲,树结构的查找的时间复杂度 O ( l o g N ) O\left( logN \right) O(logN)比线性查找的复杂度 O ( N ) O\left( N \right) O(N)低,因此效率比较高。

3.1k近邻算法(KNN)

输入:训练数据集
T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x N , y N ) } T = \{ (x_1,y_1),(x_2,y_2),...,(x_N,y_N) \} T={(x1,y1),(x2,y2),...,(xN,yN)}
其中, x i ∈ X ⊆ R n x_i \in \mathcal{X} \subseteq R^n xiXRn为实例的特征向量, y i ∈ Y = { c 1 , c 2 , . . . , c K } y_i \in \mathcal{Y} =\{ c_1,c_2,...,c_K\} yiY={c1,c2,...,cK}为实例的类别, i = 1 , 2 , . . . N i = 1,2,...N i=1,2,...N; 实例特征向量 x x x.
输出:实例 x x x所属类别 y y y

  • 根据距离度量,在训练集 T T T中找到与 x x x最近邻的 k k k个点,涵盖这 k k k个点的邻域记作 N k ( x ) N_k(x) Nk(x)
  • N k ( x ) N_k(x) Nk(x)中根据分类决策规则(如多数表决)决定 x x x的类别 y y y
    y = arg ⁡ max ⁡ c j ∑ x i ∈ N k ( x ) I ( y i = c j ) , i = 1 , 2 , . . . , N ; j = 1 , 2 , . . . , k y = \arg\underset{c_j}{\max} \sum_{x_i \in N_k(x)} I(y_i = c_j),i=1,2,...,N;j=1,2,...,k y=argcjmaxxiNk(x)I(yi=cj),i=1,2,...,N;j=1,2,...,k
    上式中, I I I为指示函数,当 y i = c j y_i = c_j yi=cj的时候, I I I为1,否则置0

3.2距离度量方法以及定义

  • L p L_p Lp distance: L p L_p Lp距离
    L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p L_p(x_i,x_j) = (\sum^n_{l=1} {\left| x^{(l)}_i - x^{(l)}_j \right|}^p)^\frac{1}{p} Lp(xi,xj)=(l=1nxi(l)xj(l)p)p1
  • Euclidean distance: 欧式距离
    L 2 ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j) = (\sum^n_{l=1} {\left| x^{(l)}_i - x^{(l)}_j \right|}^2)^\frac{1}{2} L2(xi,xj)=(l=1nxi(l)xj(l)2)21
  • Manhattan distance: 曼哈顿距离
    L 1 ( x i , x j ) = ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ L_1(x_i,x_j) = \sum^n_{l=1} {\left| x^{(l)}_i - x^{(l)}_j \right|} L1(xi,xj)=l=1nxi(l)xj(l)
def calDis(x_1,x_2,dis_type = None):
    """
    距离度量公式,包含2种,分别记为'E','M',E为欧式距离,M是曼哈顿距离
    x_1,x_2为两个向量S
    """
    distance = 0
    if (dis_type == None)|(dis_type == 'E'):
        distance = math.sqrt(sum((x_1-x_2)**2))
    elif (dis_type == 'M'):
        distance = sum(abs(x_1-x_2))
    else:
        print('WARNING: 函数calDis(self,x_1,x_2,dis_type)没有dis_type='+ str(dis_type) +' 这种距离度量方法,请重新定义dis_type=E或者M')
    return distance

测试距离计算

import numpy as np
import math
a = np.array([1,2])
b = np.array([2,1])
print('欧式距离为:' ,calDis(a,b,'E'))
print('曼哈顿距离为:' , calDis(a,b,'M'))

欧式距离为: 1.4142135623730951
曼哈顿距离为: 2


3.3 常规遍历算法,并设计自己的KNN算法框架

这里使用遍历计算的方法直接及计算距离并找到最近的k个元素

data = np.array([[2,3],[5,4],[9,6],[4,7],[8,1],[7,2]])
data_label = np.array([1,1,0,1,0,0])

这里首先定义训练部分“train”,但是实质不是训练过程,这里用来保存模型的参数,即x,y,k,distance_type

class myKNN():
    def train(self, x, y, k = None, dis_type = None):
        model = []
        self.x = x
        self.y = y
        if k == None:
            self.k = 1
        else:
            self.k = k
        if dis_type == None:
            self.dis_type = 'E'
        else:
            self.dis_type = dis_type
        model.append(self.x)
        model.append(self.y)
        model.append(self.k)
        model.append(self.dis_type)
        return model
    
    def predict(self,x,model,dis_type = None):
        if x.ndim == 1:             # 如果是一个样本
            x = x.reshape(1,len(x)) # 转化为1×n的张量样本
        predicted_y = []            # 用于保存预测类别
        for i in range(len(x)):     # 预测x的每个元素
            distance = []           # 保存x[j]与原有数据所有样本之间的距离
            for j in range(len(model[0])): # 计算x[j]与原有数据所有样本之间的距离
                distance.append(calDis(model[0][j],x[i],model[3]))
            predicted_y_distance = np.column_stack((model[1],distance)) # 合并得到(y,distance)两列数据
            # 排序,以下步骤实现argmax的那个步骤
            sorted_y_distance = predicted_y_distance[
                predicted_y_distance.argsort(0)[:,1]] # 对得到的(y,distance)按照第二列排序
            knn_data = sorted_y_distance[:model[2]]
            nearestky_list = knn_data[:,0].tolist()
            count = {c:nearestky_list.count(c) for c in set(nearestky_list)}
            y = sorted(count.keys())[0]
            predicted_y.append(y)
        return predicted_y
    
    def accuracy(self,precited_y,real_y):
        self.y = precited_y
        self.realy = real_y
        return 1-sum(np.sign(np.abs(self.y-self.realy)))/len(self.y)

测试:

mk = myKNN()

model1 = mk.train(data,data_label,k=3,dis_type='M') # 曼哈顿距离,k=3
predicted_y = mk.predict(data,model1)
print('使用曼哈顿距离得到的正确率:',mk.accuracy(predicted_y,data_label))

model2 = mk.train(data,data_label) # 欧式距离,k为默认值k=1
predicted_y = mk.predict(data,model2)
print('使用欧式距离得到的正确率:',mk.accuracy(predicted_y,data_label))

使用曼哈顿距离得到的正确率: 1.0
使用欧式距离得到的正确率: 1.0

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值