一、介绍
近邻算法(k-Nearest Neighbors,简称k-NN)是一种常用的机器学习算法,用于分类和回归问题。它的基本思想是根据样本之间的相似性来进行预测。在分类问题中,k-NN算法会将待预测样本的k个最近邻样本的标签进行统计,然后将出现次数最多的标签作为预测结果。在回归问题中,k-NN算法会将待预测样本的k个最近邻样本的标签进行加权平均,然后将得到的平均值作为预测结果。
二、算法步骤
-
计算待预测样本与每个训练样本之间的距离。常用的距离度量方法包括欧氏距离、曼哈顿距离等。
-
根据距离的大小,选择与待预测样本最近的k个训练样本。
-
统计k个最近邻样本中各类别的出现次数(对于分类问题)或计算k个最近邻样本的加权平均(对于回归问题)。
-
如果是分类问题,选择出现次数最多的标签作为预测结果;如果是回归问题,将加权平均作为预测结果。
三、优缺点
k-NN算法的优点是简单、易于理解和实现。它不需要进行模型的训练,直接使用训练样本进行预测。因此,k-NN算法适用于数据量较小、特征维度较低的问题。然而,k-NN算法的计算复杂度较高,特别是当训练样本数量较大时,需要计算大量的距离。此外,k-NN算法对于噪声数据较为敏感,因此需要进行数据清洗和特征选择的工作。
四、使用注意
在应用k-NN算法时,需要注意以下几点:
-
选择合适的k值:k值的选择会对算法的性能产生影响。较小的k值会使算法对噪声数据更敏感,较大的k值会使算法更加平滑,但可能忽略了局部结构。
-
特征归一化:在计算距离时,需要将特征进行归一化,以避免某些特征对距离的计算产生过大的影响。
-
处理数据不平衡问题:如果训练样本的类别分布存在不平衡情况,例如某一类样本数量较少,需要对样本进行采样或调整权重,以保证算法的性能。
五、算法实现
1、Python
下面是一个使用Python实现k-NN算法的简单示例:
import numpy as np
from collections import Counter
# 计算两个样本之间的欧氏距离
def euclidean_distance(x1, x2):
return np.sqrt(np.sum((x1 - x2)**2))
# k-NN算法
def k_nearest_neighbors(X_train, y_train, X_test, k):
y_pred = []
for test_sample in X_test:
# 计算测试样本与所有训练样本之间的距离
distances = [euclidean_distance(test_sample, train_sample) for train_sample in X_train]
# 按照距离从小到大排序,并取出前k个样本的索引
k_indices = np.argsort(distances)[:k]
# 根据索引找到对应的标签
k_labels = [y_train[i] for i in k_indices]
# 统计标签出现的次数,选择出现次数最多的标签作为预测结果
most_common = Counter(k_labels).most_common(1)
y_pred.append(most_common[0][0])
return y_pred
# 使用示例
X_train = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
y_train = np.array([0, 0, 1, 1])
X_test = np.array([[2, 3], [6, 7]])
k = 2
y_pred = k_nearest_neighbors(X_train, y_train, X_test, k)
print(y_pred)
上述代码中,我们定义了一个euclidean_distance
函数来计算两个样本之间的欧氏距离。然后,我们实现了k_nearest_neighbors
函数来执行k-NN算法。在该函数中,我们首先计算测试样本与训练样本之间的距离,然后按照距离排序并取出前k个最近邻样本的索引。接下来,我们根据索引找到对应的标签,并统计出现次数最多的标签作为预测结果。最后,我们使用示例数据对算法进行了简单的测试。
请注意,这只是一个简单示例,实际应用中可能需要考虑更多的因素,如特征归一化、处理数据不平衡等。另外,这里的示例是针对分类问题,如果是回归问题,需要做相应的修改,例如计算加权平均值作为预测结果。
2、Java
以下是一个使用Java实现k-NN算法的简单示例:
import java.util.*;
public class KNN {
// 计算两个样本之间的欧氏距离
public static double euclideanDistance(double[] x1, double[] x2) {
double sum = 0.0;
for (int i = 0; i < x1.length; i++) {
sum += Math.pow(x1[i] - x2[i], 2);
}
return Math.sqrt(sum);
}
// k-NN算法
public static int[] kNearestNeighbors(double[][] X_train, int[] y_train, double[][] X_test, int k) {
int[] y_pred = new int[X_test.length];
for (int i = 0; i < X_test.length; i++) {
double[] test_sample = X_test[i];
// 计算测试样本与所有训练样本之间的距离
double[] distances = new double[X_train.length];
for (int j = 0; j < X_train.length; j++) {
distances[j] = euclideanDistance(test_sample, X_train[j]);
}
// 按照距离从小到大排序,并取出前k个样本的索引
int[] k_indices = new int[k];
for (int j = 0; j < k; j++) {
k_indices[j] = j;
}
for (int j = k; j < distances.length; j++) {
double maxDistance = distances[k_indices[0]];
int maxIndex = 0;
for (int l = 1; l < k; l++) {
if (distances[k_indices[l]] > maxDistance) {
maxDistance = distances[k_indices[l]];
maxIndex = l;
}
}
if (distances[j] < maxDistance) {
k_indices[maxIndex] = j;
}
}
// 根据索引找到对应的标签
int[] k_labels = new int[k];
for (int j = 0; j < k; j++) {
k_labels[j] = y_train[k_indices[j]];
}
// 统计标签出现的次数,选择出现次数最多的标签作为预测结果
Map<Integer, Integer> labelCounts = new HashMap<>();
for (int label : k_labels) {
labelCounts.put(label, labelCounts.getOrDefault(label, 0) + 1);
}
int mostCommon = -1;
int maxCount = -1;
for (Map.Entry<Integer, Integer> entry : labelCounts.entrySet()) {
if (entry.getValue() > maxCount) {
mostCommon = entry.getKey();
maxCount = entry.getValue();
}
}
y_pred[i] = mostCommon;
}
return y_pred;
}
// 使用示例
public static void main(String[] args) {
double[][] X_train = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
int[] y_train = {0, 0, 1, 1};
double[][] X_test = {{2, 3}, {6, 7}};
int k = 2;
int[] y_pred = kNearestNeighbors(X_train, y_train, X_test, k);
for (int label : y_pred) {
System.out.println(label);
}
}
}
上述代码中,我们定义了一个euclideanDistance
方法来计算两个样本之间的欧氏距离。然后,我们实现了kNearestNeighbors
方法来执行k-NN算法。在该方法中,我们首先计算测试样本与训练样本之间的距离,然后按照距离排序并取出前k个最近邻样本的索引。接下来,我们根据索引找到对应的标签,并统计出现次数最多的标签作为预测结果。最后,我们使用示例数据对算法进行了简单的测试。
请注意,这只是一个简单示例,实际应用中可能需要考虑更多的因素,如特征归一化、处理数据不平衡等。另外,这里的示例是针对分类问题,如果是回归问题,需要做相应的修改,例如计算加权平均值作为预测结果。
3、MATLAB
以下是一个使用MATLAB实现k-NN算法的简单示例:
function y_pred = k_nearest_neighbors(X_train, y_train, X_test, k)
% 计算两个样本之间的欧氏距离
euclidean_distance = @(x1, x2) sqrt(sum((x1 - x2).^2));
% 初始化预测结果数组
y_pred = zeros(size(X_test, 1), 1);
% 对每个测试样本进行预测
for i = 1:size(X_test, 1)
test_sample = X_test(i, :);
% 计算测试样本与所有训练样本之间的距离
distances = zeros(size(X_train, 1), 1);
for j = 1:size(X_train, 1)
distances(j) = euclidean_distance(test_sample, X_train(j, :));
end
% 按照距离从小到大排序,并取出前k个样本的索引
[~, k_indices] = mink(distances, k);
% 根据索引找到对应的标签
k_labels = y_train(k_indices);
% 统计标签出现的次数,选择出现次数最多的标签作为预测结果
label_counts = histcounts(k_labels, unique(y_train));
[~, max_idx] = max(label_counts);
y_pred(i) = label_counts(max_idx);
end
end
% 使用示例
X_train = [1 2; 3 4; 5 6; 7 8];
y_train = [0; 0; 1; 1];
X_test = [2 3; 6 7];
k = 2;
y_pred = k_nearest_neighbors(X_train, y_train, X_test, k);
disp(y_pred);
上述代码中,我们定义了一个匿名函数euclidean_distance
来计算两个样本之间的欧氏距离。然后,我们实现了k_nearest_neighbors
函数来执行k-NN算法。在该函数中,我们首先计算测试样本与训练样本之间的距离,然后按照距离排序并取出前k个最近邻样本的索引。接下来,我们根据索引找到对应的标签,并统计出现次数最多的标签作为预测结果。最后,我们使用示例数据对算法进行了简单的测试。
请注意,这只是一个简单示例,实际应用中可能需要考虑更多的因素,如特征归一化、处理数据不平衡等。另外,这里的示例是针对分类问题,如果是回归问题,需要做相应的修改,例如计算平均值作为预测结果。
六、同类算法代码对照
以下是一个使用Python实现k-NN算法和其他常见近邻算法(包括最近邻、半径最近邻、KD树)的代码对比:
from sklearn.neighbors import KNeighborsClassifier, NearestNeighbors, RadiusNeighborsClassifier, KDTree
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 加载鸢尾花数据集
iris = load_iris()
X = iris.data
y = iris.target
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# k-NN算法
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
y_pred_knn = knn.predict(X_test)
accuracy_knn = accuracy_score(y_test, y_pred_knn)
# 最近邻算法
nn = NearestNeighbors(n_neighbors=3)
nn.fit(X_train)
distances, indices = nn.kneighbors(X_test)
# 半径最近邻算法
rnn = RadiusNeighborsClassifier(radius=1.0)
rnn.fit(X_train, y_train)
y_pred_rnn = rnn.predict(X_test)
accuracy_rnn = accuracy_score(y_test, y_pred_rnn)
# KD树算法
tree = KDTree(X_train)
distances, indices = tree.query(X_test, k=3)
print("k-NN Accuracy:", accuracy_knn)
print("Radius Neighbors Accuracy:", accuracy_rnn)
在上述代码中,我们首先使用sklearn
库加载了鸢尾花数据集,并将数据集划分为训练集和测试集。然后,我们分别使用KNeighborsClassifier
、NearestNeighbors
、RadiusNeighborsClassifier
和KDTree
实现了k-NN、最近邻、半径最近邻和KD树算法。接着,我们使用训练集拟合模型,并使用测试集进行预测。最后,我们使用accuracy_score
计算了每个算法的准确率,并输出结果进行对比。
请注意,这里只是一个简单示例,实际应用中可能需要根据具体问题选择合适的算法,并进行参数调优和性能评估。
总结来说,k-NN算法是一种简单而有效的机器学习算法,适用于小规模和低维度的问题。它的原理简单,但需要注意调整参数和处理数据不平衡问题。
##欢迎关注交流,开发逆商潜力,提升个人反弹力: