机器学习——下采样方法(under-sampling)
本篇文章我们重点介绍了使用下采样方法来解决不平衡数据集的问题,以保证模型的性能。并且通过下采样之后的模型预测性能召回率很不错。但也存在着一些问题,比如在下采样方法整理数据时,我们将多的类样本数据删除至与少的类样本数相同,这样做可能会使一些重要的数据被删除,使得模型的性能不是很好。所以使用下采样方法时可能也会时模型的性能下降。
文章目录
上篇文章我们提到关于不平衡数据集的问题,因为它可能导致模型偏向于多数类,从而影响少数类的预测性能。所以本篇文章我们来通过下采样方法解决这个问题。
一、下采样
在机器学习中,下采样是一种处理数据不平衡问题的技术。它通过减少多数类样本的数量,使得多数类和少数类样本的数量接近平衡,从而提高模型对少数类的识别能力。例如,假设数据集中有 1000 个多数类样本和 100 个少数类样本,下采样可以从多数类中随机删除 900 个样本,使得多数类和少数类样本的数量均为 100。
二、逻辑回归与下采样结合
将逻辑回归与下采样结合使用,可以处理不平衡数据集上的二分类问题。具体步骤如下:
- 下采样:从多数类中随机选择样本进行删除,以减少多数类的样本数量,使数据集更加平衡。
- 逻辑回归模型训练:使用下采样后的平衡数据集训练逻辑回归模型。
三、下采样案例
在逻辑回归中,整个下采样的过程如图所示。先是导入数据集,切分为测试集和训练集,并且划分成四部分:训练集特征、训练集标签、测试集特征、测试集标签。然后这时数据集中的样本很不平衡,此时对数据集进行下采样数理,将样本数多的一类删除数据,使得多数类和少数类样本的数量相等。图中是 0 和 1 类样本。此时再将样本组合成一个小数据集,然后再对小数据集进行划分,划分成新的训练集和测试集,此时在模型中对新划分的训练集进行测试,得到自测结果,这一步是为了查看模型是否过拟合,再对新划分的测试集进行测试,然后再对最开始的测试集测试,最后得到三个测试结果。
下面我们通过案例来具体介绍。
1.导入库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
- pandas:用于数据处理和分析。
- numpy:提供了高性能的多维数组对象以及这些数组的操作。
- matplotlib.pyplot:用于绘制图形,这里主要用于绘制散点图来直观展示数据之间的关系。
2.描绘可视化混淆矩阵
def cm_plot(y,yp):
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
cm = confusion_matrix(y,yp)
plt.matshow(cm,cmap = plt.cm.Blues)
plt.colorbar()
for x in range(len(cm)):
for y in range(len(cm)):
plt.annotate(cm[x,y],xy = (y,x),horizontalalignment = 'center',
verticalalignment='center')
plt.ylabel('True label')
plt.xlabel('Predicted label')
return plt
3.数据预处理
data = pd.read_csv(r"./creditcard.csv")
scaler = StandardScaler()
a = data[['Amount']] #返回DataFrame数据,而不是series
data['Amount'] = scaler.fit_transform(data[['Amount']])
data = data.drop(['Time'],axis=1)#删除无用列
- 读取数据:使用pandas库读取creditcard.csv文件。数据集在下方链接中。
- 查看数据:通过data.head()查看数据的前5行。
- 数据标准化:使用StandardScaler对Amount列进行标准化处理,使其符合正态分布。
- data[[‘Amount’]] 返回的是一个 DataFrame(二维结构),而 data[‘Amount’] 返回的是一个Series(一维结构)。
- data[‘Amount’] = scaler.fit_transform(data[[‘Amount’]]):对 Amount列进行标准化处理,fit_transform 方法会计算 Amount 列的均值和标准差,并将其转换为标准正态分布(均值为 0,标准差为 1)。转换后的结果会覆盖原始的 Amount 列。
- 删除无用列:删除Time列,因为它对欺诈检测不是很有用。
数据集链接: creditcard.csv
4.划分数据集
x_whole = data.drop('Class',axis=1) #对data数据进行划分
y_whole = data.Class
x_train_w,x_test_w,y_train_w,y_test_w = train_test_split(x_whole,y_whole,test_size=0.3,random_state=0)
x_train_w['Class'] = y_train_w
data_train = x_train_w
- train_test_split:对数据集进行划分
- x_whole:删除’Class’这一列。
- axis=1:表示一列,若axis=0表示一行。
- y_whole:保留Class 这一列
- 将数据划分为这四部分:
x_train_w:训练集特征
x_test_w:测试集特征
y_train_w:训练集标签
y_test_w:测试集标签
5.数据下采样与合并
positive_eg = data_train[data_train['Class'] == 0]
negative_eg = data_train[data_train['Class'] == 1]
np.random.seed(seed=4)
positive_eg = positive_eg.sample(len(negative_eg))
data_c = pd.concat([positive_eg,negative_eg])
print(data_c)
- positive_eg:获取到了所有标签(class)为0的数据。
- negative_eg:获取到了所有标签(class)为1的数据。
- np.random.seed:随机种子,是保证每次你执行这个代码,随机抽取的结果都是一样的。
- positive_eg.sample:sample表示随机从参数里面选择数据。
- pd.concat:是把两个pandas数据组合为一个,命名为data_c。
- 查看data_c:
6.对下采样之后的数据进行再次划分
data_c_y=data_c['Class']
data_c_x=data_c.drop('Class',axis=1)
x_train,x_test,y_train,y_test = train_test_split(data_c_x,data_c_y,test_size=0.3,random_state=0)
- 将拼接数据集data_c中的 Class 列提取出来,存入data_c_y中。
- 将拼接数据集data_c中的 Class 列删除,剩下的数据存入data_c_x中。
- 再使用train_test_split:对数据集进行划分,划分成新的四部分: x_train,x_test,y_train,y_test
7.绘制图像,查看正负样本个数
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['Microsoft YaHei'] #显示中文
mpl.rcParams['axes.unicode_minus'] = False
labels_count = pd.value_counts(data['Class'])
print(labels_count)
plt.title("正负例样本数") #设置标题
plt.xlabel("类别") #设置x轴标题
plt.ylabel("频数") #设置y轴标题
labels_count.plot(kind='bar') #设置图像类型为bar
plt.show()
- pylab:matplotlib不能显示中文,借助于pylab实现中文显示。
- pd.value_counts:统计data[‘class’]中每类的个数。
- kind=‘bar’:设置图像为直方图。
- 正负样本数图像展示如下:
可以发现,此时经过下采样后的数据集正负样本数相同,并且是大的类样本数经过删除数据,使得与小的类样本数量相等。
8.模型训练与评估
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
scores = []
c_param_range = [0.01, 0.1, 1, 10, 100] # 参数
for i in c_param_range: # 第1词循环的时候C=0.01,
lr = LogisticRegression(C=i, penalty='l2', solver='lbfgs', max_iter=1000)
score = cross_val_score(lr, X_train_w, y_train_w, cv=8, scoring='recall')
score_mean = sum(score) / len(score) # 交叉验证后的召回率
scores.append(score_mean) # 所有交叉验证的召回率
print(score_mean)
best_c = c_param_range[np.argmax(scores)] # 寻找scores中最大值对应的C
# 建立最优模型
lr = LogisticRegression(C=best_c, penalty='l2', solver='lbfgs', max_iter=1000)
lr.fit(X_train_w, y_train_w)
- 交叉验证:通过交叉验证(cross_val_score)来找到最佳的正则化参数C,这里使用召回率(recall)作为评分指标。使用最佳参数C建立最终模型,并在训练集和测试集上进行预测。
- 模型训练:使用逻辑回归模型进行训练。
9.打印分类报告并绘制混淆矩阵
"""小训练集数据进行测试"""
train_predicted = lr.predict(x_train)
print(metrics.classification_report(y_train,train_predicted))
cm_plot(y_train, train_predicted).show()
"""使用测试集数据进行测试【小测试集】"""
train_predicted = lr.predict(x_test)
print(metrics.classification_report(y_test,train_predicted))
cm_plot(y_test, train_predicted).show()
"""使用测试集数据进行测试【大测试集】"""
train_predicted_big = lr.predict(x_test_w)
print(metrics.classification_report(y_test_w,train_predicted_big))
cm_plot(y_train_w, train_predicted).show()
- 打印报告:打印分类报告(classification_report),包括精确度、召回率、F1分数等指标。
- 绘制混淆矩阵:使用自定义的cm_plot函数绘制混淆矩阵,以可视化模型性能。
10.阈值调整与性能评估
thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]
recalls = []
for i in thresholds:
y_predict_proba = lr.predict_proba(x_test)
y_predict_proba = pd.DataFrame(y_predict_proba)
y_predict_proba = y_predict_proba.drop([0],axis=1)
y_predict_proba[y_predict_proba[[1]] > i ] =1 #当预测值的概率大于i,0.1,0.2,预测的标签设为1
y_predict_proba[y_predict_proba[[1]] < i ] =0 #当预测的概率小于等于1 预测的标签设置为0
# cm_plot(y_test,y_predict_proba[[1]].show())
recall = metrics.recall_score(y_test,y_predict_proba[1])
recalls.append(recall)
print("{} Recall metric in the testing dataset: {:.3f}".format(i,recall))
- 阈值调整:通过调整预测概率的阈值来探索不同阈值对召回率的影响。逻辑回归模型输出的是属于每个类别的概率,通过调整这个阈值(默认为0.5),可以改变预测结果。
- 计算召回率:对于每个阈值,计算召回率,并打印分类报告和混淆矩阵。
四、总结
本篇文章我们重点介绍了使用下采样方法来解决不平衡数据集的问题,以保证模型的性能。并且通过下采样之后的模型预测性能召回率很不错。但也存在着一些问题,比如在下采样方法整理数据时,我们将多的类样本数据删除至与少的类样本数相同,这样做可能会使一些重要的数据被删除,使得模型的性能不是很好。所以使用下采样方法时可能也会时模型的性能下降。

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)