KNN算法原理及应用(一)+https://developer.aliyun.com/article/1544027?spm=a2c6h.13148508.setting.22.1fa24f0eRBJGs5
数据集划分
不能将所有数据集全部用于训练,为了能够评估模型的泛化能力,可以通过实验测试对学习器的泛化能力进行评估,进而做出选择。因此需要使用一个测试集来测试学习器对新样本的判别能力。
测试集要代表整个数据集、与训练集互斥、测试集与训练集建议比例: 2比8、3比7。
数据集划分的方法
1.将数据集划分成两个互斥的集合:训练集,测试集。
- 训练集用于模型训练
- 测试集用于模型验证
2.将数据集划分为训练集,验证集,测试集
- 训练集用于模型训练
- 验证集用于参数调整
- 测试集用于模型验证
1:将数据集 D 划分为两个互斥的集合,其中一个集合作为训练集 S,另一个作为测试集 T。
from sklearn.model_selection import train_test_split from sklearn.model_selection import StratifiedShuffleSplit from sklearn.model_selection import ShuffleSplit from collections import Counter from sklearn.datasets import load_iris def te1(): x, y = load_iris(return_X_y=True) print('原始类别比例:', Counter(y)) x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) print('随机类别分割:', Counter(y_train), Counter(y_test)) x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, stratify=y) print('分层类别分割:', Counter(y_train), Counter(y_test)) def te2(): x, y = load_iris(return_X_y=True) print('原始类别比例:', Counter(y)) print('*' * 40) spliter = ShuffleSplit(n_splits=5, test_size=0.2, random_state=0) for train, test in spliter.split(x, y): print('随机多次分割:', Counter(y[test])) spliter = StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=0) for train, test in spliter.split(x, y): print('分层多次分割:', Counter(y[test])) if __name__ == '__main__': te1() te2() 随机多次分割: Counter({1: 13, 0: 11, 2: 6}) 随机多次分割: Counter({1: 12, 2: 10, 0: 8}) 随机多次分割: Counter({1: 11, 0: 10, 2: 9}) 随机多次分割: Counter({2: 14, 1: 9, 0: 7}) 随机多次分割: Counter({2: 13, 0: 12, 1: 5}) 分层多次分割: Counter({0: 10, 1: 10, 2: 10}) 分层多次分割: Counter({2: 10, 0: 10, 1: 10}) 分层多次分割: Counter({0: 10, 1: 10, 2: 10}) 分层多次分割: Counter({1: 10, 2: 10, 0: 10}) 分层多次分割: Counter({1: 10, 2: 10, 0: 10})
- 第一次使用标号为0-8的共9份数据来做训练,而使用标号为9的这一份数据来进行测试,得到一个准确率
- 第二次使用标记为1-9的共9份数据进行训练,而使用标号为0的这份数据进行测试,得到第二个准确率
- 共进行10次训练,最后模型的准确率为10次准确率的平均值,避免了数据划分而造成的评估不准确。
from sklearn.model_selection import KFold from sklearn.model_selection import StratifiedKFold from collections import Counter from sklearn.datasets import load_iris def test(): x, y = load_iris(return_X_y=True) print('原始类别比例:', Counter(y)) print('*' * 30) spliter = KFold(n_splits=5, shuffle=True, random_state=0) for train, test in spliter.split(x, y): print('随机交叉验证:', Counter(y[test])) print('*' * 30) spliter = StratifiedKFold(n_splits=5, shuffle=True, random_state=0) for train, test in spliter.split(x, y): print('分层交叉验证:', Counter(y[test])) test()
数据划分结果:
随机交叉验证: Counter({1: 13, 0: 11, 2: 6}) 随机交叉验证: Counter({2: 15, 1: 10, 0: 5}) 随机交叉验证: Counter({0: 10, 1: 10, 2: 10}) 随机交叉验证: Counter({0: 14, 2: 10, 1: 6}) 随机交叉验证: Counter({1: 11, 0: 10, 2: 9}) **************************************** 分层交叉验证: Counter({0: 10, 1: 10, 2: 10}) 分层交叉验证: Counter({0: 10, 1: 10, 2: 10}) 分层交叉验证: Counter({0: 10, 1: 10, 2: 10}) 分层交叉验证: Counter({0: 10, 1: 10, 2: 10}) 分层交叉验证: Counter({0: 10, 1: 10, 2: 10})
留一法:每次抽取一个样本做为测试集。
from sklearn.model_selection import LeaveOneOut from sklearn.model_selection import LeavePOut from sklearn.datasets import load_iris from collections import Counter def test01(): x, y = load_iris(return_X_y=True) print('原始类别比例:', Counter(y)) print('*' * 40) spliter = LeaveOneOut() for train, test in spliter.split(x, y): print('训练集:', len(train), '测试集:', len(test), test) test01()
分类算法的评估
- 利用训练好的模型使用测试集的特征值进行预测
- 将预测结果和测试集的目标值比较,计算预测正确的百分比
- 百分比就是准确率, 准确率越高说明模型效果越好
确定合适的K值
K值过小:容易受到异常点的影响
k值过大:受到样本均衡的问题,K一般取一个较小的数值。
使用 scikit-learn 提供的 GridSearchCV 工具, 配合交叉验证法可以搜索参数组合
x, y = load_iris(return_X_y=True) x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, stratify=y, random_state=0) estimator = KNeighborsClassifier() grid = {'n_neighbors': [1, 3, 5]} estimator = GridSearchCV(estimator, param_grid=grid, cv=5) estimator.fit(x_train, y_train)
K近邻算法的优缺点:
- 优点:简单,易于理解,容易实现
- 缺点:算法复杂度高,结果对K取值敏感,容易受数据分布影响
knn算法中我们最需要关注两个问题:k值的选择和距离的计算。
距离/相似度的计算:
样本之间的距离的计算,我们一般使用对于一般使用Lp距离进行计算。当p=1时候,称为曼哈顿距离,当p=2时候,称为欧氏距离,当p=∞时候,称为极大距离。一般采用欧式距离较多。