近邻算法详解

一、介绍

近邻算法(k-Nearest Neighbors,简称k-NN)是一种常用的机器学习算法,用于分类和回归问题。它的基本思想是根据样本之间的相似性来进行预测。在分类问题中,k-NN算法会将待预测样本的k个最近邻样本的标签进行统计,然后将出现次数最多的标签作为预测结果。在回归问题中,k-NN算法会将待预测样本的k个最近邻样本的标签进行加权平均,然后将得到的平均值作为预测结果。

二、算法步骤

  1. 计算待预测样本与每个训练样本之间的距离。常用的距离度量方法包括欧氏距离、曼哈顿距离等。

  2. 根据距离的大小,选择与待预测样本最近的k个训练样本。

  3. 统计k个最近邻样本中各类别的出现次数(对于分类问题)或计算k个最近邻样本的加权平均(对于回归问题)。

  4. 如果是分类问题,选择出现次数最多的标签作为预测结果;如果是回归问题,将加权平均作为预测结果。

三、优缺点

k-NN算法的优点是简单、易于理解和实现。它不需要进行模型的训练,直接使用训练样本进行预测。因此,k-NN算法适用于数据量较小、特征维度较低的问题。然而,k-NN算法的计算复杂度较高,特别是当训练样本数量较大时,需要计算大量的距离。此外,k-NN算法对于噪声数据较为敏感,因此需要进行数据清洗和特征选择的工作。

四、使用注意

在应用k-NN算法时,需要注意以下几点:

  1. 选择合适的k值:k值的选择会对算法的性能产生影响。较小的k值会使算法对噪声数据更敏感,较大的k值会使算法更加平滑,但可能忽略了局部结构。

  2. 特征归一化:在计算距离时,需要将特征进行归一化,以避免某些特征对距离的计算产生过大的影响。

  3. 处理数据不平衡问题:如果训练样本的类别分布存在不平衡情况,例如某一类样本数量较少,需要对样本进行采样或调整权重,以保证算法的性能。

五、算法实现

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库加载了鸢尾花数据集,并将数据集划分为训练集和测试集。然后,我们分别使用KNeighborsClassifierNearestNeighborsRadiusNeighborsClassifierKDTree实现了k-NN、最近邻、半径最近邻和KD树算法。接着,我们使用训练集拟合模型,并使用测试集进行预测。最后,我们使用accuracy_score计算了每个算法的准确率,并输出结果进行对比。

请注意,这里只是一个简单示例,实际应用中可能需要根据具体问题选择合适的算法,并进行参数调优和性能评估。

总结来说,k-NN算法是一种简单而有效的机器学习算法,适用于小规模和低维度的问题。它的原理简单,但需要注意调整参数和处理数据不平衡问题。

##欢迎关注交流,开发逆商潜力,提升个人反弹力:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

runqu

你的鼓励是我创作的最大动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值