不平衡学习(Imbalanced learning)
不平衡数据的定义
顾名思义即我们的数据集样本类别极不均衡,以二分类问题为例,数据集中的多数类 为Smax,少数类为Smin,通常情况下把多数类样本的比例为100:1、1000:1,甚至是10000:1这种情况下为不平衡数据。
为什么不平衡学习
因为传统的学习方法以降低总体分类精度为目标,将所有样本一视同仁,同等对待,造成了分类器在多数类的分类精度较高而在少数类的分类精 度很低。例如正负样本50:1的例子,算法就算全部预测为另一样本,准确率也会达到98%(50/51),因此传统的学习算法在不平衡数据集中具有较大的局限性。
不平衡学习的方法
解决方法主要分为两个方面:
第一种方案主要从数据的角度出发,主要方法为抽样,既然我们的样本是不平衡的,那么可以通过某种策略进行抽样,从而让我们的数据相对均衡一些;
第二种方案从算法的角度出发, 考虑不同误分类情况代价的差异性对算法进行优化,使得我们的算法在不平衡数据下也能有较好的效果。
采样
随机采样
采样算法通过某一种策略改变样本的类别分布,以达到将不平衡分布的样本转化为相对平衡分布的样本的目的,而随机采样是采样算法中最简单也最直观易 懂的一种方法。随机采样主要分为两种类型,分别为随机欠采样和随机过采样两种。
随机欠采样顾名思义即从多数类Smax中随机选择少量样本E再合 并原有少数类样本作为新的训练数据集,新数据集为Smin+E,随机欠采样有两种类型分别为有放回和无放回两种,无放回欠采样在对多数类某样本被采 样后不会再被重复采样,有放回采样则有可能。
随机过采样则正好相反,即通过多次有放回随机采样从少数类Smin中抽取数据集E,采样的数量要大 于原有少数类的数量,最终的训练集为Smax+E。
显然,随机采样是通过改变多数类或者少数类的样本比例达到修改样本分类分布的目的,其中也存在着诸多的问题,例如随机欠采样,由于丢失了一些样本,造成一些信息的缺失,如果未被采样的样本具有重要的信息呢?而过采样扩大了数据集,训练模型的复杂度会加大,而且有可能造成过拟合的情况。
SMOTE算法
SMOTE全称是Synthetic Minority Oversampling Technique即合成少数类过采样技术,SMOTE算法的基本思想SMOTE算法的基本思想是对少数类样本进行分 析并根据少数类样本人工合成新样本添加到数据集中,具体如图2所示,算法流程如下。
对于少数类中每一个样本x,以欧氏距离为标准计算它到少数类样本集Smin中所有样本的距离,得到其k近邻。
根据样本不平衡比例设置一个采样比例以确定采样倍率N,对于每一个少数类样本x,从其k近邻中随机选择若干个样本,假设选择的近邻为x^。
对于每一个随机选出的近邻x^,分别与原样本按照如下的公式构建新的样本。
xnew=x+rand(0,1)∗(x^−x)
图2 SMOTE算法
SMOTE算法摈弃了随机采样复制样本的做法,使得算法的性能有所提升,但由于每个少数样本都会产生新样本,也会产生样本重叠的问题,下面介绍其改进算法。
Borderline-SMOTE算法
在Borderline-SMOTE中,若少数类样本的每个样本xi求k近邻,记作Si−knn,且Si−knn属于整个样本集合S而不再是少数类样本,若满足
k2<|si−knn∩smax|<k
即k近邻中超过一半是多数样本。
则将样本xi加入DANGER集合,显然DANGER集合代表了接近分类边界的样本,将DANGER当作SMOTE种子样本的输入生成新样本。特别地,当上述条件取右边界,即k近邻中全部样本都是多数类时此样本不会被选择为种样本生成新样本,此情况下的样本为噪音。
图3 Borderline-SMOTE算法
Informed Undersampling
前面讲了关于过采样的的算法,那么下面就是欠采样算法informed undersampling,informed undersampling采样技术主要有两种方法分别是EasyEnsemble算法和BalanceCascade算法。
EasyEnsemble算法如下图所示,此算法类似于随机森林的Bagging方法,它把数据划分为两部分,分别是多数类样本和少数类样 本,对于多数类样本Smaj,通过n次有放回抽样生成n份子集,少数类样本分别和这n份样本合并训练一个模型,这样可以得到n个模型,最终的模型是 这n个模型预测结果的平均值。
BalanceCascade算法是一种级联算法,BalanceCascade从多数类Smax中有效地选择N且满 足∣N∣=∣Smin∣,将N和Smin合并为新的数据集进行训练,新训练集对每个多数类样本xi进行预测 若预测对则Smax=Smaj−xi。依次迭代直到满足某一停止条件,最终的模型是多次迭代模型的组合。
核心思想:使用之前已形成的集成分类器来为下一次训练选择多类样本,然后再进行欠抽样。
靶点生物活性数据不平衡处理示例
导入库
%matplotlib inline from rdkit importChem from rdkit.ChemimportAllChem from rdkit.ChemimportDataStructs from sklearn.ensemble importRandomForestClassifier from sklearn.metrics import classification_report from sklearn.metrics import confusion_matrix from sklearn.model_selection import train_test_split from imblearn.over_sampling import SMOTE from imblearn.over_sampling import ADASYN import pandas as pd import numpy as np import matplotlib.pyplot as plt from IPythonimport display from sklearn.decomposition import PCA
载入数据预处理并查看
df = pd.read_csv('chembl_5HT.csv') df = df.dropna() df.head()
定义类别pIC50> 8为active,其他类别为inactive
df['CLS'] = np.array(df.pchembl_value > 9, dtype=np.int)
查看数据分布
plt.hist(df.CLS)
活性数据和非活性数据比例接近13000:1,非平衡数据
计算分子指纹
mols = [Chem.MolFromSmiles(smi) for smi in df.canonical_smiles] fps = [AllChem.GetMorganFingerprintAsBitVect(mol, 2) for mol in mols]
定义函数指纹转数组
def fp2np(fp): arr = np.zeros((0,)) DataStructs.ConvertToNumpyArray(fp, arr) return arr
X = np.array([fp2np(fp) for fp in fps]) Y = df.CLS.to_numpy()
数据没有进行抽样
train_X, test_X, train_Y, test_Y = train_test_split(X, Y, random_state=123, test_size=0.2)
print(train_X.shape) print(train_Y.shape) print(sum(train_Y)/len(train_Y))
(11340, 2048)
(11340,)
0.08686067019400352
随机森林模型
rf = RandomForestClassifier(n_estimators=10) rf.fit(train_X, train_Y) pred_Y = rf.predict(test_X)
print(classification_report(test_Y, pred_Y)) print(confusion_matrix(test_Y, pred_Y))
基于SMOTE算法的随机抽样
X_resampled, Y_resampled = SMOTE().fit_resample(train_X, train_Y) print(X_resampled.shape) print(Y_resampled.shape) print(sum(Y_resampled)/len(Y_resampled))
(20710, 2048)
(20710,)
0.5
随机森林模型
代码示例
rf = RandomForestClassifier(n_estimators=10) rf.fit(X_resampled, Y_resampled) pred_Y = rf.predict(test_X) print(classification_report(test_Y, pred_Y)) print(confusion_matrix(test_Y, pred_Y))
X_resampled, Y_resampled = ADASYN().fit_resample(train_X, train_Y)
print(X_resampled.shape) print(Y_resampled.shape) print(sum(Y_resampled)/len(Y_resampled))
(20884, 2048)
(20884,)
0.5041658686075464
rf = RandomForestClassifier(n_estimators=10) rf.fit(X_resampled, Y_resampled) pred_Y = rf.predict(test_X) clsreport = classification_report(test_Y, pred_Y)
PCA分析
代码示例
pca = PCA(n_components=3) res = pca.fit_transform(X) col = {0:'blue', 1:'red'} color = [col[np.int(i)] for i in Y] plt.figure(figsize=(10,7)) plt.scatter(res[:,0], res[:,1], c=color, alpha=0.5)