【Python】 剪辑法欠采样 CNN压缩近邻法欠采样

【Python】 剪辑法欠采样 CNN压缩近邻法欠采样 借鉴关于K近邻KNN看这一篇就够了算法原理kd树球树KNN解决样本不平衡剪辑法压缩近邻法 - 知乎但是不要看他里面的代码因为作者把代码里的一些符号故意颠倒了 比如“”改成“!”还有乱加“~”看明白逻辑才能给他改过来一、剪辑法当训练集数据中存在一部分不同类别数据的重叠时在一部分程度上说明这部分数据的类别比较模糊这部分数据会对模型造成一定的过拟合那么一个简单的想法就是将这部分数据直接剔除掉即可也就是剪辑法。剪辑法将训练集 D 随机分成两个部分一部分作为新的训练集 Dtrain一部分作为测试集 Dtest然后基于 Dtrain使用 KNN 的方法对 Dtest 进行分类并将其中分类错误的样本从整体训练集 D 中剔除掉得到 Dnew。由于对训练集 D 的划分是随机划分难以保证数据重叠部分的样本在第一次剪辑时就被剔除因此在得到 Dnew 后可以对 Dnew 继续进行上述操作数次这样可以得到一个比较清爽的类别分界。效果如下图附上可直接运行的代码from sklearn import datasets import matplotlib.pyplot as pyplot from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier as KNN import numpy as np from collections import Counter from numpy import where # make_classification用于手动构造数据 # 1000个样本分成4类 X, y datasets.make_classification(n_samples1000, n_features2, n_informative2, n_redundant0, n_repeated0, n_classes4, n_clusters_per_class1) # # # 画出二维散点图 # for label, _ in counter.items(): # row_ix where(y label)[0] # pyplot.scatter(X[row_ix, 0], X[row_ix, 1], labelstr(label)) # pyplot.legend() # pyplot.show() # 剪辑10次 for i in range(10): x_train, x_test, y_train, y_test train_test_split(X, y, test_size0.5) k 5 KNN_clf KNN(n_neighborsk) KNN_clf.fit(x_train, y_train) # 用训练集训练KNN y_predict KNN_clf.predict(x_test) # 用测试集测试 cond y_predict y_test x_test x_test[cond] # 把预测错误的从整体数据集中剔除掉 y_test y_test[cond] # 把预测错误的从整体数据集中剔除掉 X np.vstack([x_train, x_test]) # 为下一次循环做准备剔除掉本轮预测错误的 y np.hstack([y_train, y_test]) # 为下一次循环做准备剔除掉本轮预测错误的 # summarize the new class distribution counter Counter(y) print(counter) # 画出二维散点图 for label, _ in counter.items(): row_ix where(y label)[0] pyplot.scatter(X[row_ix, 0], X[row_ix, 1], labelstr(label)) pyplot.legend() pyplot.show()以上使用了k20的参数进行剪辑的结果循环了10次一般而言k越大被抛弃的样本会越多因为被分类的错误的概率更大。二、CNN压缩近邻法欠采样压缩近邻法的想法是认为同一类型的样本大量集中在类簇的中心而这些集中在中心的样本对分类没有起到太大的作用因此可以舍弃掉这些样本。其做法是将训练集随机分为两个部分第一个部分为 store占所有样本的 10% 左右第二个部分为 grabbag占所有样本的 90% 左右然后将 store 作为训练集训练 KNN 模型grabbag 作为测试集将分类错误的样本从 grabbag 中移动到 store 里然后继续用增加了样本的 store 和减少了样本的 grabbag 再次训练和测试 KNN 模型直到 grabbag 中所有样本被分类正确或者 grabbag 中样本数为0。在压缩结束之后store 中存储的是初始化时随机选择的 10% 左右的样本以及在之后每一次循环中被分类错误的样本这些被分类错误的样本集中在类簇的边缘认为是对分类作用较大的样本。CNN欠采样已经有相应的Python实现库了相应的方法是CondensedNearestNeighbour下面是可直接运行的代码。# Undersample and plot imbalanced dataset with the Condensed Nearest Neighbor Rule from collections import Counter from sklearn.datasets import make_classification from imblearn.under_sampling import CondensedNearestNeighbour from matplotlib import pyplot from numpy import where # make_classification方法用于生成分类任务的人造数据集 # X是数据几维都可以n_features4表示4维 # y用0/1表示类别weights调整0和1的占比 X, y make_classification(n_samples500, n_classes2, n_features3, n_redundant0, # n_clusters_per_class表示每个类别多少簇 # flip_y噪声增加分类难度 n_clusters_per_class2, weights[0.5], flip_y0, random_state1) # summarize class distribution counter Counter(y) # {0: 990, 1: 10} counter是一个字典value存储类别key存储类别个数 print(counter) # CNN有直接可以调用的包 n_neighbors设置k值,k值越小越省时间,就设置为1吧 undersample CondensedNearestNeighbour(n_neighbors1) # transform the dataset X, y undersample.fit_resample(X, y) # summarize the new class distribution counter Counter(y) print(counter) # scatter plot of examples by class label for label, _ in counter.items(): row_ix where(y label)[0] pyplot.scatter(X[row_ix, 0], X[row_ix, 1], labelstr(label)) pyplot.legend() pyplot.show()但是我觉得这个CondensedNearestNeighbour()方法的可操作性太低所以没用这个方法而是根据CNN的原理CNN底层是训练KNN去写的from sklearn import datasets import matplotlib.pyplot as pyplot from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier as KNN import numpy as np from collections import Counter from numpy import where # make_classification用于手动构造数据 # 1000个样本分成4类 X, y datasets.make_classification(n_samples1000, n_features2, n_informative2, n_redundant0, n_repeated0, n_classes4, n_clusters_per_class1, random_state1) counter Counter(y) # 画出二维散点图 for label, _ in counter.items(): row_ix where(y label)[0] pyplot.scatter(X[row_ix, 0], X[row_ix, 1], labelstr(label)) pyplot.legend() pyplot.show() # 10%作为训练集90%作为测试集 x_train, x_test, y_train, y_test train_test_split(X, y, test_size0.9) while True: k 1 KNN_clf KNN(n_neighborsk) KNN_clf.fit(x_train, y_train) y_predict KNN_clf.predict(x_test) cond y_predict y_test # cond记录分类的对与错分类错是False正确是True # 都分类正确退出 if cond.all(): print(所有测试集都分类正确CNN正常结束) break x_train np.vstack([x_train, x_test[~cond]]) # 把分类错误(cond的值是False的移动到训练集里 y_train np.hstack([y_train, y_test[~cond]]) x_test x_test[cond] # 把分类对的继续作为下一轮的测试集 y_test y_test[cond] if len(x_test) 0: print(所有样本都能做到分类错误也就是结果集原始数据集一般不会出现这种情况) break # summarize the new class distribution counter Counter(y_train) print(counter) # 画出二维散点图 for label, _ in counter.items(): row_ix where(y_train label)[0] pyplot.scatter(x_train[row_ix, 0], x_train[row_ix, 1], labelstr(label)) pyplot.legend() pyplot.show()2.1 改进版——指定压缩后样本大小的CNN在如下代码中用sampleNum指定全体样本数量用endNum指定压缩后样本数量from sklearn import datasets import matplotlib.pyplot as pyplot from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier as KNN import numpy as np from collections import Counter from numpy import where sampleNum 1000 endNum 500 k 1 # KNN算法的K值 # make_classification用于手动构造数据 # 1000个样本分成4类 X, y datasets.make_classification(n_samplessampleNum, n_features2, n_informative2, n_redundant0, n_repeated0, n_classes4, n_clusters_per_class1, random_state1) # counter Counter(y) # # 画出二维散点图 # for label, _ in counter.items(): # row_ix where(y label)[0] # pyplot.scatter(X[row_ix, 0], X[row_ix, 1], labelstr(label)) # pyplot.legend() # pyplot.show() # 10%作为训练集90%作为测试集 x_train, x_test, y_train, y_test train_test_split(X, y, test_size0.9) # print(x_train.shape[0]) # 100 nowNum x_train.shape[0] # 用来控制 训练集/筛选后的样本数 满足resultNum就停下, 初始有x_train这么多个 while True: KNN_clf KNN(n_neighborsk) KNN_clf.fit(x_train, y_train) y_predict KNN_clf.predict(x_test) cond y_predict y_test # cond记录分类的对与错分类错是False正确是True # 都分类正确退出 if cond.all(): print(所有测试集都分类正确CNN自动结束但是结果集没凑够呢) break # 如果结果集数量不够要求的endNum继续下一轮 if nowNumy_test[~cond].shape[0] endNum: nowNum nowNumy_test[~cond].shape[0] print(目前结果集数量, nowNum) x_train np.vstack([x_train, x_test[~cond]]) # 把分类错误(cond的值是False的移动到训练集里 y_train np.hstack([y_train, y_test[~cond]]) x_test x_test[cond] # 把分类对的继续作为下一轮的测试集 y_test y_test[cond] # 如果结果集数量超过endNum我们只要测试集里分类错误的前endNum-nowNum个 else: # 记录前endNum-nowNum个的位置(截取位置 condCut 0 # 记录截取位置 for i in range(cond.shape[0]): if not cond[i]: nowNum nowNum 1 if nowNum endNum: condCut i # 在cond[condCut]处刚好是我们要的第endNum个结果集样本 break # 把cond[condCut]后面的都设置成True cond[condCut1:] True x_train np.vstack([x_train, x_test[~cond]]) # 把分类错误(cond的值是False的移动到训练集里 y_train np.hstack([y_train, y_test[~cond]]) print(结果集的数量为, x_train.shape[0], 满足endNum, endNum) break if len(x_test) 0: print(所有样本都能做到分类错误也就是结果集原始数据集一般不会出现这种情况) break # summarize the new class distribution counter Counter(y_train) print(counter) # 画出二维散点图 for label, _ in counter.items(): row_ix where(y_train label)[0] pyplot.scatter(x_train[row_ix, 0], x_train[row_ix, 1], labelstr(label)) pyplot.legend() pyplot.show()