一、KNN 算法与模型评估基础

1.1 KNN 算法原理

K 近邻(K-Nearest Neighbors,KNN)是一种简单直观的监督学习算法,其核心思想可以概括为 "物以类聚":对于未知样本,通过计算它与训练集中所有样本的距离,选取距离最近的 k 个样本(近邻),并以这 k 个样本中出现次数最多的类别作为未知样本的预测类别。

KNN 算法的关键要素:

  • 距离度量:常用欧氏距离(适用于连续特征),计算公式为distance(x,y)=∑i=1n​(xi​−yi​)2​
  • k 值选择:k 过小易受噪声影响(过拟合),k 过大会导致类别边界模糊(欠拟合),通常通过验证集选择最优 k
  • 投票机制:多数表决(分类问题)或平均值(回归问题)

1.2 模型评估指标

  • 准确率(Accuracy):正确预测的样本数占总样本数的比例,适用于均衡数据集
  • 混淆矩阵(Confusion Matrix):直观展示各类别预测结果的分布,行代表真实类别,列代表预测类别,对角线元素表示正确预测数

二、问题引入:约会网站匹配预测

在约会网站场景中,我们需要根据海伦的三个特征预测其对潜在约会对象的喜好程度:

  • 飞行里程数(每年的飞行常客里程)
  • 游戏时间百分比(每周花在玩视频游戏上的时间百分比)
  • 冰淇淋公升数(每周消费的冰淇淋公升数)

目标变量为用户的评价结果,分为三类:

  • didntLike(不喜欢)
  • smallDoses(有点喜欢)
  • largeDoses(很喜欢)

我们将使用 KNN 算法构建分类模型,通过历史数据训练模型,实现对新用户喜好的自动预测。

三、代码算法模块详解

3.1 数据读取与初始化模块

python

def load_data(file_path):
    # 读取txt文件并指定列名
    columns = ["飞行里程数", "游戏时间百分比", "冰淇淋公升数", "标签"]
    data = pd.read_csv(file_path, sep="\t", header=None, names=columns)
    
    # 分离特征与标签
    X = data[["飞行里程数", "游戏时间百分比", "冰淇淋公升数"]].values
    y = data["标签"].values
    
    # 标签字符串转整数(便于计算)
    label_map = {"didntLike": 0, "smallDoses": 1, "largeDoses": 2}
    y = np.array([label_map[label] for label in y])
    
    return X, y, label_map

该模块负责:

  • 读取 TSV 格式数据集
  • 分离特征矩阵(X)和标签向量(y)
  • 标签编码(字符串转整数)

3.2 数据预处理模块(归一化)

def normalize_features(X):
    min_vals = X.min(axis=0)  # 每列最小值
    max_vals = X.max(axis=0)  # 每列最大值
    ranges = max_vals - min_vals  # 取值范围
    
    # 归一化到[0,1]区间,处理除零问题
    X_norm = (X - min_vals) / np.where(ranges == 0, 1, ranges)
    
    return X_norm, min_vals, max_vals

特征归一化的作用:消除不同特征量纲差异(如飞行里程数远大于冰淇淋消费量),确保各特征在距离计算中权重一致。

3.3 KNN 核心算法模块

def knn_classify(X_train, y_train, X_test, k):
    y_pred = []
    for test_sample in X_test:
        # 1. 计算与所有训练样本的欧氏距离
        distances = np.sqrt(np.sum((X_train - test_sample) **2, axis=1))
        
        # 2. 按距离排序,取前k个样本索引
        k_indices = np.argsort(distances)[:k]
        
        # 3. 多数表决:取k个近邻中最频繁的标签
        k_labels = y_train[k_indices]
        pred_label = np.bincount(k_labels).argmax()
        
        y_pred.append(pred_label)
    
    return np.array(y_pred)

核心步骤:

  1. 计算测试样本与所有训练样本的欧氏距离
  2. 按距离升序排序,选取前 k 个近邻
  3. 对 k 个近邻的标签进行投票,取众数作为预测结果

3.4 模型评估与超参数选择模块

# 准确率计算
def calculate_accuracy(y_true, y_pred):
    return np.sum(y_true == y_pred) / len(y_true)

# 混淆矩阵构建
def create_confusion_matrix(y_true, y_pred, n_classes):
    cm = np.zeros((n_classes, n_classes), dtype=int)
    for true, pred in zip(y_true, y_pred):
        cm[true][pred] += 1
    return cm

# 最优k值选择
def find_best_k(X_train, y_train, X_val, y_val, k_candidates):
    best_accuracy = 0.0
    best_k = 1
    for k in k_candidates:
        y_pred = knn_classify(X_train, y_train, X_val, k)
        accuracy = calculate_accuracy(y_val, y_pred)
        if accuracy > best_accuracy:
            best_accuracy = accuracy
            best_k = k
    return best_k, best_accuracy

通过验证集评估不同 k 值的模型性能,选择准确率最高的 k 作为最优超参数。

3.5 新样本预测接口

def predict_new_sample(new_sample, X_train, y_train, min_vals, max_vals, k, label_map):
    # 归一化新样本(使用训练集的归一化参数)
    ranges = max_vals - min_vals
    new_sample_norm = (new_sample - min_vals) / np.where(ranges == 0, 1, ranges)
    
    # 预测并转换为原始标签
    pred_label_code = knn_classify(X_train, y_train, new_sample_norm.reshape(1, -1), k)[0]
    inverse_map = {v: k for k, v in label_map.items()}
    return inverse_map[pred_label_code]

提供工程化预测接口,支持对新样本的直接预测,包含特征归一化和标签反向映射步骤。

四、结果展示与分析

4.1 最优 k 值选择结果

通过验证集测试,当 k=5 时模型性能最佳,验证集准确率达到 95.71%。

4.2 测试集评估结果

  • 测试集准确率为 96.67%,模型泛化能力良好
  • 混淆矩阵显示:大多数样本被正确分类,少量 smallDoses 样本被误分为 didntLike 或 largeDoses

4.3 新样本预测结果

对 3 个示例样本的预测结果均与预期一致,验证了模型的实用性。

总结

本文实现了一个基于 KNN 算法的约会网站匹配预测模型,完整覆盖了数据处理、模型构建、超参数选择和性能评估的全流程。通过特征归一化和最优 k 值选择,模型达到了 96.67% 的测试集准确率,能够有效预测用户对约会对象的喜好程度。该实现方案具有良好的可扩展性,可直接应用于类似的分类问题场景。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐