机器学习之k近邻回归算法实现与应用

在解决分类问题的过程中,K 近邻算法(简称:KNN)是一种简单而且实用的方法。但KNN不仅可以实现分类问题也可以实现回归问题

K-最近邻(K-Nearest Neighbors,KNN)算法是一种基本的分类和回归方法,它的工作原理非常直观:通过测量不同特征值之间的距离来进行预测。

基本概念

  • 距离度量:KNN 算法通常使用欧氏距离来衡量样本之间的相似度,但也可以采用曼哈顿距离、切比雪夫距离等其他距离度量方式。
  • K 值:K 值决定了在进行决策时考虑的最近邻居的数量。较小的 K 值意味着噪声对预测结果的影响更大,而较大的 K 值可能导致模型过于平滑,无法捕捉数据的局部变化。

分类问题中的 KNN

在分类问题中,KNN 的工作流程如下:

  1. 确定 K 值:选择一个正整数 K 作为邻居的数量。
  2. 计算距离:对于每个测试数据点,计算它与训练集中每个点的距离。
  3. 找到 K 个最近邻居:根据计算得到的距离,找出距离最近的 K 个训练数据点。
  4. 进行决策:在 K 个最近邻居中,根据多数投票原则(即出现次数最多的类别)来预测测试数据点的类别。

回归问题中的 KNN

在回归问题中,KNN 的工作流程略有不同:

  1. 确定 K 值:同样选择一个正整数 K。
  2. 计算距离:计算测试数据点与训练集中每个点的距离。
  3. 找到 K 个最近邻居:找出距离最近的 K 个训练数据点。
  4. 预测结果:计算这 K 个邻居的目标值的平均值(或加权平均值),作为测试数据点的预测结果。

优点

  • 简单易懂:KNN 算法的原理简单,易于理解和实现。
  • 无需训练:KNN 是一种惰性学习算法,所有的计算都是在预测时进行,不需要在训练阶段花费时间。
  • 可用于非线性问题:KNN 不需要假设数据的分布,因此可以用于非线性问题的分类和回归。

缺点

  • 计算成本高:由于在预测时需要计算测试数据点与所有训练数据点之间的距离,因此计算成本较高,尤其是在大数据集上。
  • 存储成本高:KNN 需要存储全部数据集,因此存储成本较高。
  • 对异常值敏感:KNN 算法对噪声和异常值敏感,这可能会影响模型的预测性能。
  • 需要合适的距离度量和 K 值:选择合适的距离度量和 K 值对模型的性能至关重要,但找到最佳参数可能需要大量的实验。

实现

KNN 算法可以使用各种编程语言和库实现,如 Python 中的 scikit-learn 库提供了 KNeighborsClassifierKNeighborsRegressor 类来分别处理分类和回归问题。

KNN 是一种非常灵活的算法,适用于多种类型的数据和问题,尽管它有其局限性,但在许多实际应用中仍然是一种有效的机器学习算法。

KNN算法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

def knn_classify(test_data, train_data, labels, k):
# KNN 方法完整实现
distances = np.array([]) # 创建一个空的数组用于存放距离

for each_data in train_data: # 使用欧式距离计算数据相似度
d = d_euc(test_data, each_data)
distances = np.append(distances, d)

sorted_distance_index = distances.argsort() # 获取按距离从小到大排序后的索引
sorted_distance = np.sort(distances)
r = (sorted_distance[k] + sorted_distance[k - 1]) / 2 # 计算

class_count = {}
for i in range(k): # 多数表决
vote_label = labels[sorted_distance_index[i]]
class_count[vote_label] = class_count.get(vote_label, 0) + 1

final_label = majority_voting(class_count)
return final_label, r

KNN解决分类问题:

使用sklearn库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
from sklearn.neighbors import KNeighborsClassifier

from sklearn.model_selection import train_test_split

# 得到 lilac 数据集中 feature 的全部序列: sepal_length,sepal_width,petal_length,petal_width
feature_data = lilac_data.iloc[:, :-1]
label_data = lilac_data["labels"] # 得到 lilac 数据集中 label 的序列

X_train, X_test, y_train, y_test = train_test_split(
feature_data, label_data, test_size=0.3, random_state=2
)

def sklearn_classify(train_data, label_data, test_data, k_num):
# 使用 sklearn 构建 KNN 预测模型
knn = KNeighborsClassifier(n_neighbors=k_num)
# 训练数据集
knn.fit(train_data, label_data)
# 预测
predict_label = knn.predict(test_data)
# 返回预测值
return predict_label

# 使用测试数据进行预测
y_predict = sklearn_classify(X_train, y_train, X_test, 3)
def get_accuracy(test_labels, pred_labels):
# 准确率计算函数
correct = np.sum(test_labels == pred_labels) # 计算预测正确的数据个数
n = len(test_labels) # 总测试集数据个数
accur = correct / n
return accur

get_accuracy(y_test, y_predict)

k值选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
normal_accuracy = []  # 建立一个空的准确率列表
k_value = range(2, 11)
for k in k_value:
y_predict = sklearn_classify(X_train, y_train, X_test, k)
accuracy = get_accuracy(y_test, y_predict)
normal_accuracy.append(accuracy)

plt.xlabel("k")
plt.ylabel("accuracy")
new_ticks = np.linspace(0.6, 0.9, 10) # 设定 y 轴显示,从 0.6 到 0.9
plt.yticks(new_ticks)
plt.plot(k_value, normal_accuracy, c="r")
plt.grid(True) # 给画布增加网格

KNN解决回归问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import numpy as np
from sklearn.neighbors import KNeighborsRegressor

def knn_regression(train_data, train_labels, test_data, k):
### 代码开始 ### (≈ 10 行代码)
# 默认情况下 KNeighborsClassifier 也会使用欧式距离
# test_labels = None
model = KNeighborsRegressor(n_neighbors=k)
model.fit(train_data,train_labels)
test_labels = model.predict(test_data)
### 代码结束 ###
return test_labels

# 训练样本特征
train_data = np.array(
[[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10]]
)

# 训练样本目标值
train_labels = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

# 测试样本特征
test_data = np.array([[1.2, 1.3], [3.7, 3.5], [5.5, 6.2], [7.1, 7.9]])

# 测试样本目标值
pre = knn_regression(train_data, train_labels, test_data, k=3)
1
2
3
from sklearn.model_selection import train_test_split

X_train,X_test, y_train, y_test =train_test_split(train_data,train_target,test_size=0.4, random_state=0)

其中: * X_train,X_test, y_train, y_test分别表示,切分后的特征的训练集,特征的测试集,标签的训练集,标签的测试集;其中特征和标签的值是一一对应的。

  • train_data,train_target分别表示为待划分的特征集和待划分的标签集。

  • test_size:测试样本所占比例。

  • random_state:随机数种子,在需要重复实验时,保证在随机数种子一样时能得到一组一样的随机数

sklearn.neighbors.KNeighborsClassifier((n_neighbors=5, weights='uniform', algorithm='auto') * n_neighbors : k 值,表示邻近个数,默认为 5。

  • weights : 决策规则选择,多数表决或加权表决,可用参数('uniform','distance')

  • algorithm : 搜索算法选择(auto,kd_tree, ball_tree),包括逐一搜索,kd 树搜索或 ball 树搜索


机器学习之k近邻回归算法实现与应用
https://fu-jingqi.github.io/2024/07/22/机器学习之k近邻回归算法实现与应用/
作者
coderfjq
发布于
2024年7月22日
许可协议