【火炉炼AI】机器学习015-如何处理样本数偏差较大的数据集
(本文所使用的Python库和版本号: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )
我们得到的数据集在绝大多数情况下,都不是理想的数据集,都需要经过各种各样的处理,其中的一个处理方式就是,如何处理样本数偏差较大的数据集。比如对于某种疾病的发生概率是1%,即获得的自然状态下的数据集中大约99%的样本都是正常的,那么此时,通过模型进行训练,得到的模型在对新样本进行预测时,也往往偏向于样本数较大的类别,此时或极大的降低小样本类别的召回率。
1. 查看数据集的特征
此处所使用的是书本《Python机器学习经典实例》第三章的非平衡数据集,此处我用pandas读取到内存中,通过info()来查看该数据集的基本情况,代码如下:
# 准备数据集
data_path='E:\PyProjects\DataSet\FireAI/data_multivar_imbalance.txt'
df=pd.read_csv(data_path,header=None)
# print(df.head()) # 没有问题
print(df.info()) # 查看数据信息,确保没有错误
dataset_X,dataset_y=df.iloc[:,:-1],df.iloc[:,-1]
# print(dataset_X.head())
# print(dataset_X.info())
# print(dataset_y.head()) # 检查没问题
dataset_X=dataset_X.values
dataset_y=dataset_y.values
复制代码
----------------------------------输---------出------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1200 entries, 0 to 1199
Data columns (total 3 columns): 0 1200 non-null float64
1 1200 non-null float64
2 1200 non-null int64
dtypes: float64(2), int64(1)
memory usage: 28.2 KB
None
-----------------------------------------完-----------------------------
可以看出,该数据集只有两个features,一个label,且整个数据集有1200个样本。可以通过查看数据集的2D分布来初步的了解该数据集的样本偏差。如下图所示:
########################小**********结########################
1. 数据集中不同类别之间样本数偏差比较大,此时需要做进一步处理。
2. 数据集的加载和显示等,在前面的文章中已经讲烂了,此处无需赘言。
###########################################################
2. 用线性SVM分类器构建分类模型
从上面的数据集分布图中可以大致看出,这个模型不是简单的线性模型,当然,为了对比效果,此处我仍然使用线性SVM分类器来构建分类模型,看一下效果。
代码如下:
# 将整个数据集划分为train set和test set
from sklearn.model_selection import train_test_split
train_X, test_X, train_y, test_y=train_test_split(
dataset_X,dataset_y,test_size=0.25,random_state=42)
# 如果用线性SVM分类器来进行分类,看看是什么结果
# 使用线性核函数初始化一个SVM对象。
from sklearn.svm import SVC
classifier=SVC(kernel='linear') # 构建线性分类器
classifier.fit(train_X,train_y)
复制代码
然后在通过plot_classifier()函数将这个模型的分类效果画出来。
# 模型在训练集上的性能报告:
from sklearn.metrics import classification_report
plot_classifier(classifier,train_X,train_y) # 分类器在训练集上的分类效果
target_names = ['Class-0', 'Class-1']
y_pred=classifier.predict(train_X)
print(classification_report(train_y, y_pred, target_names=target_names))
复制代码
----------------------------输---------出-----------------------
precision | recall | f1-score | support |
---|---|---|---|
Class-0 | 0.00 | 0.00 | 0.00 |
Class-1 | 0.83 | 1.00 | 0.91 |
avg / total | 0.69 | 0.83 | 0.76 |
-----------------------------------完-----------------------------
可以看出,线性SVM分类器完全没有将class_0区分出来,得到的各种指标都是0,从分类效果图中也可以看到,根本就没有线性平面。
##################小**********结###############################
1. 由于本数据集存在样本数量偏差,故而使用线性SVM分类器没有任何效果。
############################################################
3. 解决样本数量偏差的方法
从上面可以看出,由于样本数量偏差的存在,使用线性SVM分类器没有任何效果,那么我们该怎么处理这种数据集了?
SVM内部存在一个class_weight参数,我们可以设置该参数为"balanced",来调节各种类别样本数量的权重,使其达到平衡。如下代码:
# 看来直接使用简单的线性SVM分类器难以将class_0区分出来,故而我们要调整数据集中样本的数量权重
classifier2=SVC(kernel='linear',class_weight='balanced') # 比上面的分类器增加了 class_weight=‘balanced'参数
classifier2.fit(train_X,train_y)
# 模型在训练集上的性能报告:
plot_classifier(classifier2,train_X,train_y) # 分类器在训练集上的分类效果
target_names = ['Class-0', 'Class-1']
y_pred2=classifier2.predict(train_X)
print(classification_report(train_y, y_pred2, target_names=target_names))
复制代码
-------------------------------输---------出---------------------------
precision | recall | f1-score | support |
---|---|---|---|
Class-0 | 0.35 | 0.86 | 0.50 |
Class-1 | 0.96 | 0.68 | 0.79 |
avg / total | 0.86 | 0.71 | 0.74 |
-------------------------------------完--------------------------------
从分类效果图中可以看出,此时可以使用线性SVM来对数据集进行训练,并能得到效果还不错的分类模型,从分类结果报告中可以看出,各种指标也还不错,但也存在较大的提升空间。
#################小**********结###############################
1. 在定义SVM分类器时,只需要设置class_weight=balanced即可消除数据集中样本数量偏差的问题。
2. 如果分类器的效果不理想,那么需要考虑是否是数据集存在明显的样本数量偏差问题。
#########################################################
注:本部分代码已经全部上传到(我的github)上,欢迎下载。
参考资料:
1, Python机器学习经典实例,Prateek Joshi著,陶俊杰,陈小莉译