700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 机器学习:kNN算法(一)—— 原理与代码实现(不调用库)

机器学习:kNN算法(一)—— 原理与代码实现(不调用库)

时间:2022-05-07 17:51:14

相关推荐

机器学习:kNN算法(一)—— 原理与代码实现(不调用库)

一 理论基础

\(k\)近邻法是一种基本地分类与回归算法,属于判别模型。没有学习策略,不具备显式学习过程。本文主要讨论分类问题。原理:给定一个训练数据集,对于新的输入实例,在训练数据集中找到与该实例最邻近的\(k\)个点,这\(k\)个点的多数属于某个类,就把输入实例归为这个类。三个基本要素:\(k\)值得选择,距离度量以及分类决策规则

1.超参数k:选择较小的k值,对噪声比较敏感。意味着整体模型变得复杂,容易过拟合;选择较大的k值,较远的实例也会对预测起作用,使预测发生错误。意味着模型变得简单。

2.距离度量:距离是空间两个实力点相似程度的反映。\(\mathbf{x}_i=(x_i^{(1)},x_i^{(2)},...,x_i^{(n)})^T,\mathbf{x}_j=(x_j^{(1)},x_j^{(2)},...,x_j^{(n)})^T\),距离定义如下:\[\mathit{L_p}=(\sum_{l=1}^{n}|x_i^{(l)}-x_j^{(l)}|^p)^{\frac{1}{p}}\]

p=1时称为曼哈顿距离,p=2时称为欧氏距离。

3.分类决策规则:一般采用多数表决。

二 代码实现

编写的分类器实现功能:超参数k,超参数p,拟合训练集,对新的数据进行分类,对分类结果计算精度。感兴趣的还可以加入超参数距离的权重以及kd树,球树等实现方法。这里不再赘述。KNN算法实现代码如下

class KNNClassify():def __init__(self,k=5, p=2):self.k = kself.p = pself._X_train = Noneself._y_train = Nonedef fit(self, X_train, y_train):self._X_train = X_trainself._y_train = y_trainreturn selfdef predict_y(self, X_test):m = self._X_train.shape[0]y_pre = []for intX in X_test:minus_mat = np.fabs(np.tile(intX, (m, 1)) - self._X_train) # 将新的实例复制成m行1列,并进行相减sq_minus_mat = minus_mat ** self.psq_distance = sq_minus_mat.sum(axis=1)diff_sq_distance = sq_distance ** float(1/self.p)sorted_distance_index = diff_sq_distance.argsort()# 记录距离最近的k个点的索引class_count = {}vola = []for i in range(self.k):vola = self._y_train[sorted_distance_index[i]]class_count[vola] = class_count.get(vola, 0) + 1 # 统计k个点中所属各个类别的实例数目sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True) # 返回列表,元素为元组。每个类别以及对应的实例数目y_pre.append((sorted_class_count[0][0]))return (np.array(y_pre))def score(self, X_test, y_test):j = 0for i in range(len(self.predict_y(X_test))):if self.predict_y(X_test)[i] == y_test[i]:j += 1return ('accuracy: {:.10%}'.format(j / len(y_test)))

下面以鸢尾花数据集来评估编写的分类器效果

import numpy as npimport operatorfrom sklearn import datasetsfrom sklearn.model_selection import train_test_split# 获取数据集,并进行8:2切分iris = datasets.load_iris()X = iris.datay = iris.targetX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)# 定义分类器的实例,并进行拟合预测f = KNNClassify()f.fit(X_train, y_train)y_pre = f.predict_y(X_test)accuracy = f.score(X_test, y_test)print(y_test)print(y_pre)print(accuracy)

输出结果如下,可以看出分类效果还可以。

[1 2 2 2 0 1 2 0 0 0 1 2 0 0 2 1 2 0 2 2 2 2 0 2 1 1 0 2 0 1]

[1 2 2 2 0 1 2 0 0 0 1 2 0 0 2 1 1 0 2 2 2 2 0 2 1 1 0 2 0 1]

accuracy: 96.6666666667%需要注意的是KNN由于与距离有关,如果某个特征数值相比其他特征较大大,会对距离产生很大的影响。例如具有实例x1=[1 3 1000 2],x2=[1.5 4 1100 2],计算距离时第三个特征差基本起到了主导作用,弱化了其他特征。为避免这种情况,应先对数据进行预理。预处理方法。关于标准化与归一化,不同的人有不同看法,这里以sklearn为准,标准化对象为特征,归一化对象为样本,当然两者实质都是对训练集数据的改变。可以参考[/cn/0.19.0/modules/preprocessing.html#preprocessing-scaler]

1.标准化(对特征),也称去均值和方差按比例缩放。数据集的标准化对scikit-learn中实现的大多数机器学习算法来说是常见的要求 。(对象为特征,依赖于训练集中所有样本) 如果个别特征或多或少看起来不是很像标准正态分布(具有零均值和单位方差),那么它们的表现力可能会较差。在实际情况中,我们经常忽略特征的分布形状,直接减去均值来对某个特征进行中心化,再通过除以非常量特征(non-constant features)的标准差进行缩放。

\[x=\frac{x-\bar{x}}{\sigma }\]将特征缩放到给定的区间,通常为[0,1]。或者也可以将每个特征的最大值转换至单位大小,它通过除以每个特征的最大值将训练数据特征缩放至 [-1, 1] 范围内,这就意味着训练数据应该是已经零中心化或者是稀疏数据。两者分别使用MinMaxScaler和MaxAbsScaler实

现。对于两者计算公式分别为\[x=\frac{x-min(x)}{max(x)-min(x)},x=\frac{x}{max(x)}\]

2.归一化(对样本)。归一化 是缩放单个样本以具有单位范数的过程。注意对象为样本而非某个特征!!通常使用 L1 或 L2 范数。例如实例\(\mathbf{x}_1=(1,2,2)^T\),归一化后\(\mathbf{x}_1= (0.33,0.66,0.66)^T\)。不依赖于其他样本特征数值。

3.二值化。

4.非线性变换

5.特征编码L

6.缺失值插补

由此可以看出,对于kNN而言,当某个特征的差值相比其他特征较大时,可以采用标准化,以避免某个特征对距离影响。如果进行归一化,虽然也是改变了特征取值区间,但是特征间量级关系依然会相差很大。

当我们得出一个算法模型后,如何评判模型的好坏呢?包括许多性能度量方法,例如精度与错误率、查准率查全率与F1、ROC与AUC等等。假如我们选择精度作为衡量指标,我们采用多次留出法或者p次k折交叉验证,然后将各个精度取均值作为此模型的评估指标。

1.留出法。将数据集分成互斥的训练集与测试集,可以得出一个精度。因为划分方式存在多种方式,因此使用留出法时,采用若干次随机划分,重复进行实验取平均值作为留出法评估结果。

2.交叉验证。将数据划分成k个互斥大小相似的集合,每次采用k-1个作为训练集,剩下一个作为测试集,总共可进行k次,最终返回k次结果的均值。同样由于划分方式不同,可进行p次划分,每次都进行k折交叉验证,取最后均值作为评估结果,称为p次k折交叉验证。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。