diff --git a/function.py b/function.py index 0db9a18..6bd78c4 100644 --- a/function.py +++ b/function.py @@ -1,12 +1,15 @@ import numpy as np -import os +from sklearn.decomposition import PCA +from sklearn.model_selection import cross_val_score, KFold +from sklearn.metrics import accuracy_score +from sklearn.preprocessing import StandardScaler id = "202210423103" name = "王坤" -def classify0(input_vector, dataSet, labels, k=3, weighted=False): +def classify0(input_vector, dataSet, labels, k=3, weighted=False, distance_metric='euclidean'): """ - KNN 分类算法(支持加权KNN和归一化特征处理)。 + KNN 分类算法(支持加权KNN、不同距离度量以及降维处理)。 Args: - input_vector (numpy array): 输入待分类的样本。 @@ -14,14 +17,23 @@ def classify0(input_vector, dataSet, labels, k=3, weighted=False): - labels (list or numpy array): 训练集对应的标签。 - k (int): kNN 算法参数。 - weighted (bool): 是否使用加权KNN。 + - distance_metric (str): 距离度量方法,支持 'euclidean', 'manhattan', 'minkowski'。 Returns: - int: 预测的分类标签。 """ - # 计算输入向量与训练集中每个样本的欧氏距离 - distances = np.linalg.norm(dataSet - input_vector, axis=1) + # 根据选择的距离度量计算距离 + if distance_metric == 'euclidean': + distances = np.linalg.norm(dataSet - input_vector, axis=1) + elif distance_metric == 'manhattan': + distances = np.sum(np.abs(dataSet - input_vector), axis=1) + elif distance_metric == 'minkowski': + p = 3 # 可以调整为合适的 p 值 + distances = np.sum(np.abs(dataSet - input_vector) ** p, axis=1) ** (1 / p) + else: + raise ValueError("Unsupported distance metric") - # 按照距离从小到大排序并获取前k个距离最小的索引 + # 按距离从小到大排序并获取前k个距离最小的索引 sorted_indices = distances.argsort()[:k] # 如果加权,使用距离的倒数作为权重 @@ -31,16 +43,29 @@ def classify0(input_vector, dataSet, labels, k=3, weighted=False): for i, index in enumerate(sorted_indices): label = labels[index] weighted_labels[label] = weighted_labels.get(label, 0) + weights[i] - # 返回权重和最大的标签 predicted_label = max(weighted_labels, key=weighted_labels.get) else: # 统计前k个最近邻的标签 nearest_labels = [labels[i] for i in sorted_indices] - # 返回出现次数最多的标签 predicted_label = max(set(nearest_labels), key=nearest_labels.count) return predicted_label +def pca_reduction(data, n_components=2): + """ + 使用PCA对数据进行降维。 + + Args: + - data (numpy array): 原始特征数据。 + - n_components (int): 保留的主成分数量。 + + Returns: + - numpy array: 降维后的特征数据。 + """ + pca = PCA(n_components=n_components) + reduced_data = pca.fit_transform(data) + return reduced_data + def normalize_data(data): """ 归一化特征数据,将每个特征值缩放到 [0, 1] 之间。 @@ -67,40 +92,62 @@ def load_data(file_path): Returns: - np.array: 归一化后的特征数据。 - np.array: 标签数据。 - - np.array: 数据范围。 - - np.array: 最小值。 """ data = np.loadtxt(file_path, delimiter='\t') data_set = data[:, :-1] # 假设最后一列是标签 labels = data[:, -1].astype(int) - norm_data_set, ranges, min_vals = normalize_data(data_set) - return norm_data_set, labels, ranges, min_vals + return data_set, labels -def normalize_test_sample(test_sample, ranges, min_vals): +def cross_validate_knn(data_set, labels, k_range, weighted=False, distance_metric='euclidean'): """ - 归一化测试样本,与训练数据保持相同的归一化尺度。 + 使用交叉验证来选择最佳的K值。 Args: - - test_sample (numpy array): 测试样本。 - - ranges (numpy array): 训练数据的范围。 - - min_vals (numpy array): 训练数据的最小值。 + - data_set (numpy array): 训练数据集。 + - labels (numpy array): 训练集标签。 + - k_range (list): K值的候选范围。 + - weighted (bool): 是否使用加权KNN。 + - distance_metric (str): 距离度量方法。 Returns: - - numpy array: 归一化后的测试样本。 + - tuple: 最佳K值和对应的最高准确率。 """ - return (test_sample - min_vals) / ranges + best_k = k_range[0] + best_accuracy = 0 + for k in k_range: + kf = KFold(n_splits=5) + accuracies = [] + for train_index, test_index in kf.split(data_set): + train_data, test_data = data_set[train_index], data_set[test_index] + train_labels, test_labels = labels[train_index], labels[test_index] + predictions = [classify0(test_sample, train_data, train_labels, k, weighted, distance_metric) + for test_sample in test_data] + accuracy = accuracy_score(test_labels, predictions) + accuracies.append(accuracy) + mean_accuracy = np.mean(accuracies) + if mean_accuracy > best_accuracy: + best_accuracy = mean_accuracy + best_k = k + return best_k, best_accuracy if __name__ == "__main__": # 加载数据集 train_file = "datingTrainSet2.tsv" - train_data, train_labels, ranges, min_vals = load_data(train_file) + train_data, train_labels = load_data(train_file) - # 示例测试样本,确保维度与训练数据一致 + # 归一化和PCA降维处理 + scaler = StandardScaler() + scaled_train_data = scaler.fit_transform(train_data) + pca_train_data = pca_reduction(scaled_train_data, n_components=3) + + # 使用交叉验证选择最佳K值 + k_range = range(1, 21) + best_k, best_accuracy = cross_validate_knn(pca_train_data, train_labels, k_range, weighted=True, distance_metric='euclidean') + print(f"最佳K值: {best_k}, 交叉验证准确率: {best_accuracy}") + + # 使用最佳K值进行预测 test_sample = np.array([65915, 3.868359, 0.128078]) - - # 归一化测试样本 - norm_test_sample = normalize_test_sample(test_sample, ranges, min_vals) - - # 使用加权KNN算法进行预测 - prediction = classify0(norm_test_sample, train_data, train_labels, k=5, weighted=True) + norm_test_sample = scaler.transform([test_sample]) + pca_test_sample = pca_reduction(norm_test_sample, n_components=3) + prediction = classify0(pca_test_sample[0], pca_train_data, train_labels, k=best_k, weighted=True) print(f"预测的分类标签: {prediction}")