从零实现机器学习算法(一) K近邻(KNearestNeighbor, KNN)

目录

1. KNN算法简介

2. KNN模型

2.1 距离度量

2.2 K值选择

2.3 分类规则

3. 总结与分析


1. KNN算法简介

KNN是较为简单的机器学习算法,其原理是计算待检测样本与训练集样本中的距离,然后选取最近的K个样本的标签,统计其结果,将类别最多的一类作为测试样本的标签。可以形式化的表达为

y = argmax\sum_{x_{j}\in N_{k}}^{k}{I(y_{i}=c_{i})}

其中N_{k}是训练集中与待测样本 x最近的 k个点的邻域。 I\left(x \right )是指示函数,其表达式为

I(x)= \begin{cases} 0,& \text{if x is true}\\ 1,& \text{else} \end{cases}

2. KNN模型

KNN模型包括以下三个方面:距离度量、K值选择和分类规则,下面分别介绍

2.1 距离度量

距离是用于衡量特征空间中两个样本的相似程度。对于距离度量有很多的选择,最简单的就是计算样本之间的欧式距离,其公式如下

L_{2}(x_{i},x_{j})=\left( \sum_{l=1}^{n}{|x_{i}^{(l)}-x_{j}^{(l)}|^{2}}\right)^\frac{1}{2}

其中 n为特征维度。对于其他的计算距离方法比如曼哈顿距离,余弦距离等,这些如果有兴趣了解可以查阅相关公式,本文就不再赘述了。由于特征的本身数值范围不同,大的数值会影响最终的结果,因此需要对数据进行标准化。数据标准化一般有两种形式一种是min-max标准化,其公式为:

x'=\frac{x-min(x)}{max(x)-min(x)}

另外一种是z-score标准化,其公式为:

x'=\frac{x-\mu}{\sigma}

其中 \mu为其x 的均值, \sigma为 x的标准差。上述两种标准化实现代码分别为:

   def Normalization(self, data):
        # get the max and min value of each column
        minValue = data.min(axis=0)
        maxValue = data.max(axis=0)
        diff = maxValue - minValue
        # normalization
        mindata = np.tile(minValue, (data.shape[0], 1))
        normdata = (data - mindata)/np.tile(diff, (data.shape[0], 1))
        return normdata

 

    def Standardization(self, data):
        # get the mean and the variance of each column
        meanValue = data.mean(axis=0)
        varValue = data.std(axis=0)
        standarddata = (data - np.tile(meanValue, (data.shape[0], 1)))/np.tile(varValue, (data.shape[0], 1))
        return standarddata

其中 np.tile() 函数的功能是将横向或者纵向的复制, 举个例子

data = np.array([[1,2], [3, 4]])
h_tile = np.tile(data, (1, 4))
v_tile = np.tile(data, (4, 1))
print(h_tile)
print(v_tile)

 其中 h_tile 和 v_tile 结果如下图所示

欧式距离的实现代码如下

train_num = train_data.shape[0]
# calculate the distances
distances = np.tile(input, (train_num, 1)) - train_data
distances = distances**2
distances = distances.sum(axis=1)
distances = distances**0.5

2.2 K值选择

如果K值选取过小,那么学习的邻域范围较小,也就是只有这么一小块范围起作用。这会导致模型具有较小的“近似误差”和较大的“估计误差”。总的来说,较小的K值会导致模型变得复杂,容易过拟合。

如果K值选取过大,其得到的效果与较小的K值相反,即较大的“近似误差”和较小的“估计误差”。总的来说,较大的K值会导致模型变得简单。另外,如果 K=N意味着,整个训练集的样本都用于预测,这样不仅会使模型变得简单并且会增加计算时间。

2.3 分类规则

确定了K值之后就需要最样本进行分类了,最简单的方式是投票规则,即少数服从多数的方法,其实现代码为

disIndex = distances.argsort()
labelCount = {}
for i in range(k):
    label = train_label[disIndex[i]]
    labelCount[label] = labelCount.get(label, 0) + 1
prediction = sorted(labelCount.items(), key=op.itemgetter(1), reverse=True)
label = prediction[0][0]

 在上述代码中,首先将使用 argsorts() 函数返回数组值从小到大的索引值;然后统计前K个样本(与待检测样本距离最近的K个样本)中对应的标签个数;最后通过对labelCount排序得到标签数目从大到小的结果。至此,perdiction中的第一个元素的第二个值即为待检测样本的类别。完整的检测函数如下:

def calcuateDistance(self, input_sample, train_data, train_label, k):
        train_num = train_data.shape[0]
        # calcuate the distances
        distances = np.tile(input, (train_num, 1)) - train_data
        distances = distances**2
        distances = distances.sum(axis=1)
        distances = distances**0.5

        # get the labels of the first k distances
        disIndex = distances.argsort()
        labelCount = {}
        for i in range(k):
            label = train_label[disIndex[i]]
            labelCount[label] = labelCount.get(label, 0) + 1

        prediction = sorted(labelCount.items(), key=op.itemgetter(1), reverse=True)
        label = prediction[0][0]
        return label

3. 总结与分析

对于KNN的实现本文采用最简单的线性遍历来实现,在实际中为了减少预测时间往往采用更高级的数据结构来存储相关信息,如kd树。此外,可以使用交叉验证来选取最优的K值。最后贴一下本文实现的KNN与Sklearn检测性能的比较:

可以发现本文具有更好的检测效果,但是所需时间更长,其差异的原因可能在于数据标准化和K值的选择上。

本文相关代码和数据集:https://github.com/Ryuk17/MachineLearning

参考文献

[1] 李航, 统计学习方法

[2] Peter Harrington, Machine Learning IN ACTION

[3] CS229: Machine Learning

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值