KNN算法-分类
1 样本距离判断
(1)欧式距离
欧式距离(Euclidean distance),也称为欧氏度量,是用来衡量两个点之间直线距离的方法。
(2)曼哈顿距离
曼哈顿距离(Manhattan distance),也称为城市街区距离,是在网格状坐标系统中,从一个点到另一个点的距离之和。
2 KNN 算法原理
K-近邻算法(K-Nearest Neighbors,简称KNN)是一种基于样本相似性的分类方法。它通过比较目标样本与其最近的 ( K ) 个邻居样本的类别来决定目标样本的类别。
具体步骤如下:
-
选择 ( K ):确定要考虑的邻居数量 ( K )。
-
计算距离:计算目标样本与所有其他样本的距离,找到距离最近的 ( K ) 个样本。
-
投票决策:统计这 ( K ) 个邻居中各类别的出现次数,目标样本将被分配到出现次数最多的类别。
假设我们有 10000 个样本,并且选择 ( K = 5 )。我们需要对一个待分类的样本 B 进行分类。首先,我们找到与样本 B 距离最近的 5 个样本。假设这 5 个邻居的类别分布如下:
-
类别 A:3 个样本
-
类别 B:1 个样本
-
类别 C:1 个样本
在这种情况下,由于类别 A 的邻居样本最多,样本 B 将被分类为类别 A。
弊端:计算量大,维度灾难,需要选择合适的 ( K ) 值和距离度量
sklearn.neighbors.KNeighborsClassifier 的主要参数和方法如下: 参数 n_neighbors:邻居数量,默认为 5。 algorithm:寻找邻居的算法,默认为 'auto'。 方法 fit(X, y):训练模型。 predict(X):进行预测。
使用KNN算法预测《唐人街探案》电影属于哪种类型?分别计算每个电影和预测电影的距离然后求解:
import pandas as pd from collections import Counter # 读取电影数据 data = pd.read_csv("./src/movies.csv") df = pd.DataFrame(data) # 目标电影数据 target_movie = { '电影名称': '唐人街探案', '搞笑镜头': 23, '拥抱镜头': 3, '打斗镜头': 17, '距离': None # 不需要实际的距离值 } # 按距离排序并选择最近的3个邻居 k = 3 nearest_neighbors = df.sort_values(by='距离').head(k)#head(k) 是 pandas 的一个方法,用于从 DataFrame 的顶部获取前 k 行数据 # 统计K个邻居的电影类型 neighbor_types = nearest_neighbors['电影类型'] predicted_type = Counter(neighbor_types).most_common(1)[0][0] #most_common(n) 是 Counter 类的一个方法,它返回出现频率最高的 n 个元素及其计数,返回一个列表,其中包含元组 (元素, 频次)。 #[0][0] 表示取第一个元组,然后取第一个元素,即电影类型 print(f"唐人街探案的预测电影类型是: {predicted_type}")
2.sklearn 实现KNN示例
用KNN算法对葡萄酒进行分类
from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.datasets import load_wine import joblib #模型保存与加载需导入 def knn(path): # 加载酒类数据集 wine = load_wine() x = wine.data # 特征数据 y = wine.target # 目标标签 # 划分训练集和测试集 x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) # 创建并训练 KNN 模型 model = KNeighborsClassifier(n_neighbors=7) model.fit(x_train, y_train) # 评估模型准确率 accuracy = model.score(x_test, y_test) print("准确率:", accuracy) # 保存模型 joblib.dump(model, path) # 定义模型保存路径并调用函数 path = './src/knn.pkl' knn(path) # 加载保存的模型 model = joblib.load(path) # 预测新样本的类别 y_predict = model.predict([[2.16, 5.51, 1.34, 1.9, 2.76, 1.35, 4.43, 2.05, 0.94, 1.36, 4.36, 3.59, 1.86]]) print(y_predict) # 输出预测结果对应的标签名 print(load_wine().target_names[y_predict])
模型选择与调优
1 交叉验证
(1) 保留交叉验证HoldOut
HoldOut 交叉验证将数据集分为训练集和验证集,通常70%用于训练,30%用于验证。这是一种简单直接的方法。
优点:
-
实现简单,操作方便。
缺点:
-
不适用于不平衡数据集:在不平衡数据集中,训练集和验证集可能不均衡,导致模型无法有效学习少数类的数据。(比如80%的数据属于 “0 “类,其余20%的数据属于 “1 “类)
-
数据利用不充分:在小数据集上,一部分数据用于验证,可能导致模型错过关键特征,影响性能。
(2) K-折交叉验证(K-fold)
K-Fold 交叉验证将数据集划分为K个大小相同的部分(Fold)。每次用一个Fold作为验证集,其余K-1个Fold作为训练集。这一过程重复K次,确保每个Fold都被用作验证集。
最终模型的准确度通过计算K次验证结果的平均值来获得。
(3) 分层k-折交叉验证Stratified k-fold
分层 K-Fold 交叉验证是 K-Fold 交叉验证的变种。在每一折中保持原始数据中各类别的比例。例如,如果原始数据有三类,比例为 1:2:1,那么每一折中的类别比例也保持 1:2:1。这种方法使得每一折的验证结果更加可靠。
(4) 其它验证
去除p交叉验证) 留一交叉验证) 蒙特卡罗交叉验证 时间序列交叉验证
(5)API的使用
strat_k_fold=sklearn.model_selection.KNeighborsClassifier(n_splits=5, shuffle=True, random_state=42) • n_splits划分为几个折叠 • shuffle是否在拆分之前被打乱(随机化),False则按照顺序拆分 • random_state随机因子
from sklearn.datasets import load_wine from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import StratifiedKFold import numpy as np # 加载葡萄酒数据集 wine = load_wine() x = np.array(wine.data) y = np.array(wine.target) dts = [] # 初始化分层 K-Fold 交叉验证器 kf = StratifiedKFold(n_splits=5, shuffle=True, random_state=44) # 遍历每个折叠 for train_index, test_index in kf.split(x, y): x_train, x_test = x[train_index], x[test_index] # 划分训练集和测试集 y_train, y_test = y[train_index], y[test_index] # 初始化 KNN 分类器并训练 knn = KNeighborsClassifier(n_neighbors=5) knn.fit(x_train, y_train) # 计算测试集的准确率并存储 dt = knn.score(x_test, y_test) dts.append(dt) print("每个折叠的准确率:", dts) print("平均准确率:", np.mean(dts))
2 超参数搜索及其API使用
超参数搜索也叫网格搜索(Grid Search)
比如在KNN算法中,k是一个可以人为设置的参数,所以就是一个超参数。网格搜索能自动的帮助我们找到最好的超参数值。
sklearn.model_selection.GridSearchCV
是一个工具,用于同时进行网格搜索和交叉验证。它也被视为一个估计器(estimator)。
class sklearn.model_selection.GridSearchCV(estimator, param_grid) best_params_ 最佳参数 best_score_ 在训练集中的准确率 best_estimator_ 最佳估计器 cv_results_ 交叉验证过程描述 best_index_最佳k在列表中的下标 参数: estimator:待优化的 scikit-learn 估计器 param_grid:参数名称(字符串)和对应值列表的字典,如 {"n_neighbors": [1, 3, 5, 7, 9, 11]} cv:交叉验证策略 None:默认5折 integer:指定折数 分类器使用“分层K折交叉验证”(StratifiedKFold),其他情况使用KFold。
3 示例-葡萄酒分类
用KNN算法对葡萄酒进行分类,添加网格搜索和交叉验证
from sklearn.model_selection import GridSearchCV from sklearn.neighbors import KNeighborsClassifier from sklearn.datasets import load_wine import joblib x, y = load_wine(return_X_y=True) knn = KNeighborsClassifier() model = GridSearchCV(knn, {"n_neighbors": [5, 6, 9, 10]}) re = model.fit(x, y) print("最佳参数:\n", model.best_params_) print("最佳k在列表中的下标", model.best_index_) print("最佳准确率:\n", model.best_score_) print("最佳估计器:\n", model.best_estimator_) print("交叉验证过程描述:\n", model.cv_results_) # 使用最佳模型进行预测 y_predict = re.predict([[2.06, 5.41, 1.34, 1.85, 2.73, 1.65, 3.83, 2.05, 1.04, 1.36, 4.36, 3.49, 1.96]]) print(y_predict) # 验证是否模型与最佳估计器相同 print(re == model.best_estimator_) joblib.dump(model.best_estimator_, "./src/knn2.pkl") model = joblib.load("./src/knn2.pkl") # 使用加载的模型进行预测 y_predict = model.predict([[2.16, 5.51, 1.34, 1.9, 2.76, 1.35, 4.43, 2.05, 0.94, 1.36, 4.36, 3.59, 1.86]]) print(y_predict)