generated from LufsX/Challenge-kNN
This commit is contained in:
+74
-27
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user