一.KNN算法-分类 1.1 样本距离判断 这里的距离指的是样本之间的差异,机器学习基于数学,所以叫距离。下面是常见的两种距离:
欧式距离:
也就是我们熟知的两点之间,直线段最短。距离是两点直接的连线即横坐标差与纵坐标差的平方和。
曼哈顿距离:
两点之间不同方向上坐标的距离相加,主要用于离散空间(街区距离)
闵可夫斯基距离:
包含欧式距离、曼哈顿距离(这两种距离的广义形式)以及切比雪夫距离。
当p分别为1,2,∞时,就是欧式距离、曼哈顿距离、切比雪夫距离。
切比雪夫距离:
棋盘游戏是很经典的例子,但简单来说就是看不同方向上坐标距离哪个大就用哪个,即
max(|x1-x2|,|y1-y2|)
余弦相似度:
即两个向量间夹角的余弦值,关注两个向量之间的方向上的关系。
马氏距离:
可以看作是欧氏距离的一种修正,修正了欧式距离中各个维度尺度不一致且相关的问题。其中包含一个协方差矩阵,用于修正距离。如果各维度独立同分布,它也就是欧式距离。
杰卡德距离:
用于计算两个集合之间的差异,公式为:
J (A,B) = 1 - J (A,B) = 1 - |A∩B| / |A∪B|
汉明距离:
通常用于离散型数据,比如二进制或等长字符串。
示例:
“hello” “hallo” ↑ ↑ ↑ ↑ ↑ h e l l o h a l l o
→ 第2个字符不同 → 汉明距离 = 1
半正矢距离
常用于地球表面两个经纬度点之间的距离计算。
1.2 KNN 算法原理 K-近邻算法(K-Nearest Neighbors,简称KNN),根据K个距离最近的邻居样本的类别来判断当前样本类别。如下图(欧式距离):
1.2 KNN缺点
对于大规模数据集,计算量大,因为需要计算测试样本与所有训练样本的距离。
对于高维数据,距离度量可能变得不那么有意义,这就是所谓的“维度灾难”(数据点之间距离相似)
需要选择合适的k值和距离度量,这可能需要一些实验和调整
1.3 API sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, algorithm='auto')
这个API返回一个对象,有参数与方法
参数:
n_neighbors: int, default=5, 默认情况下用于kneighbors查询的近邻数,就是K
algorithm:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’。找到近邻的方式,注意不是计算距离的方式,与机器学习算法没有什么关系,开发中请使用默认值’auto’
方法:
我们以鸢尾花为例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from sklearn.model_selection import train_test_splitfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.datasets import load_iris iris = load_iris() X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3 , random_state=42 ) knn = KNeighborsClassifier(n_neighbors=5 ) knn.fit(X_train, y_train) y_pred = knn.predict(X_test)print (y_pred)print ("Accuracy:" , knn.score(X_test, y_test))
结果:
1 2 3 [1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0 0 0 1 0 0 2 1 0 0 0 2 1 1 0 0] Accuracy: 1.0
1.4 模型保存与加载 需要用到joblib库,可以将训练好的模型保存为pkl文件,并在需要时加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import joblibfrom sklearn.model_selection import train_test_splitfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.datasets import load_iris iris = load_iris() X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3 , random_state=42 ) knn = KNeighborsClassifier(n_neighbors=5 ) knn.fit(X_train, y_train) joblib.dump(knn, '../model/knn_model.pkl' ) knn_model = joblib.load('../model/knn_model.pkl' ) data_new = [[5.1 , 3.5 , 1.4 , 0.2 ]] y_pred = knn_model.predict(data_new)print (y_pred)
结果:
二.模型选择与调优 2.1 保留交叉验证HoldOut 在这种交叉验证技术中,整个数据集被随机地划分为训练集和验证集 。根据经验法则,整个数据集的近70%被用作训练集,其余30%被用作验证集。也就是我们最常使用的,直接划分数据集的方法。不过现在train_test_split已经完善,其中加入了stratify参数,当stratify = y(测试集)时,会根据测试集数据的比例关系来随机划分训练集和测试集,保证训练集和测试集的数据比例相同,改善了数据偏斜问题。这也是目前最常用的验证方法。
2.2 K-折交叉验证(K-fold) K-Fold交叉验证技术中,整个数据集被划分为K个大小相同的部分。每个分区被称为 一个”Fold”。所以我们有K个部分,我们称之为K-Fold。一个Fold被用作验证集,其余的K-1个Fold被用作训练集。该技术重复K次,直到每个Fold都被用作验证集,其余的作为训练集。
模型的最终准确度是通过取k个模型验证数据的平均准确度来计算的。如图:
API:
from sklearn.model_selection import KFold
2.3 分层k-折交叉验证Stratified k-fold K-折交叉验证的变种, 分层的意思是说在每一折中都保持着原始数据中各个类别的比例关系,比如说:原始数据有3类,比例为1:2:1,那么划分的几折中,每一折中的数据类别保持着1:2:1的比例。
API:
from sklearn.model_selection import StratifiedKFold
strat_k_fold=sklearn.model_selection.StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
indexs=strat_k_fold.split(X,y)
返回一个可迭代对象,一共有5个折叠,每个折叠对应的是训练集和测试集的下标
然后可以用for循环取出每一个折叠对应的X和y下标来访问到对应的测试数据集和训练数据集 以及测试目标集和训练目标集,即:
1 2 3 for train_index, test_index in indexs: X[train_index] y[train_index] X[test_index ] y[test_index ]
使用时只是KFold这个类名不一样其他代码完全一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from sklearn.model_selection import KFoldfrom sklearn.datasets import load_irisfrom sklearn.neighbors import KNeighborsClassifier iris = load_iris() data = iris.data target = iris.target kf = KFold(n_splits=5 ) knn = KNeighborsClassifier(n_neighbors=5 )for train_index, test_index in kf.split(data,target): X_train, X_test = data[train_index], data[test_index] y_train, y_test = target[train_index], target[test_index] print ("Train:" , train_index) print ("Test:" , test_index) knn.fit(X_train, y_train) y_pred = knn.predict(X_test)print ("Accuracy:" , knn.score(X_test, y_test))
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Train: [ 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149] Test: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29] Train: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149] Test: [30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59] Train: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149] ... 108 109 110 111 112 113 114 115 116 117 118 119] Test: [120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149] Accuracy: 0.8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from sklearn.model_selection import StratifiedKFoldfrom sklearn.datasets import load_irisfrom sklearn.neighbors import KNeighborsClassifier iris = load_iris() data = iris.data target = iris.target kf = StratifiedKFold(n_splits=5 ) knn = KNeighborsClassifier(n_neighbors=5 )for train_index, test_index in kf.split(data,target): X_train, X_test = data[train_index], data[test_index] y_train, y_test = target[train_index], target[test_index] print (y_test) break
结果:
1 [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2]
可见其比例确实是一个固定值。
三.超参数搜索 超参数搜索也叫网格搜索(Grid Search)
比如在KNN算法中,k是一个可以人为设置的参数,所以就是一个超参数。网格搜索能自动的帮助我们找到最好的超参数值。
API:
sklearn.model_selection.GridSearchCV(estimator, param_grid)
参数:
estimator: scikit-learn估计器实例
param_grid:以参数名称(str)作为键,将参数设置列表尝试作为值的字典,然后依次找到最好的值 示例: {“n_neighbors”: [1, 3, 5, 7, 9, 11]}
cv: 确定交叉验证切分策略,值为: (1)None 默认5折 (2)integer 设置多少折 如果估计器是分类器,使用”分层k-折交叉验证(StratifiedKFold)”。在所有其他情况下,使用KFold。
可调用参数:
best_params_ 最佳参数
best_score_ 在训练集中的准确率
best_estimator_ 最佳估计器
cv_results_ 交叉验证过程描述
best_index_最佳k在列表中的下标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from sklearn.datasets import load_irisfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.model_selection import train_test_splitfrom sklearn.model_selection import GridSearchCV iris = load_iris() data = iris.data target = iris.target X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.3 , random_state=42 ) param_grid = {'n_neighbors' : [1 , 3 , 5 , 7 , 9 ]} knn = KNeighborsClassifier() grid_search = GridSearchCV(knn, param_grid, cv=5 ) grid_search.fit(X_train, y_train)print ("Best parameters: " , grid_search.best_params_)print ("Best score: " , grid_search.best_score_)print ("Test score: " , grid_search.score(X_test, y_test))print ("Best estimator: " , grid_search.best_estimator_)print ("Best index: " , grid_search.best_index_)
结果:
1 2 3 4 5 Best parameters: {'n_neighbors': 1} Best score: 0.9523809523809523 Test score: 1.0 Best estimator: KNeighborsClassifier(n_neighbors=1) Best index: 0