文章目录
一、过采样
过采样(Oversample)是一种用于处理数据集中类别不平衡问题的技术,其核心目标是增加少数类样本的数量,以平衡不同类别之间的分布,从而提升机器学习模型对少数类的识别能力和预测效果。以下从技术原理、常见方法、应用场景、优缺点及注意事项五个方面展开说明。
1.过采样技术原理
- 过采样通过复制或生成新样本的方式,扩充数据集中样本数量较少的类别。在类别不平衡的数据集中,模型容易偏向多数类而忽略少数类,导致预测结果失真(例如医疗诊断中的漏诊)。过采样使少数类与多数类的样本量接近,从而让模型在训练时更关注少数类的特征。
2.过采样常见方法
- 随机过采样:直接复制少数类样本,操作简单但可能引发过拟合。
- SMOTE(合成少数类过采样技术):基于特征空间插值生成新样本,例如在相邻样本间随机插入数据点,避免简单复制的局限性。
- ADASYN:根据样本分布动态调整生成数量,对分类边界附近的样本进行重点增强。
3.过采样优缺点
- 优点:
显著提升模型对少数类的召回率
适用于多种算法(如决策树、神经网络等) - 缺点:
随机过采样可能导致模型记忆重复样本
过度使用可能降低模型的泛化能力
二、逻辑回归与过采样结合
在逻辑回归中,过采样通常用于预处理阶段,以平衡数据集。一旦数据集被平衡,就可以使用逻辑回归模型进行训练和预测。然而,需要注意的是,过采样可能会引入一些额外的计算成本,特别是在处理大规模数据集时。
此外,虽然过采样可以提高模型对少数类的识别能力,但也可能导致模型在预测新数据时出现偏差。因此,在实际应用中,通常需要结合欠采样、调整模型参数或使用特殊算法(如集成学习方法)等策略来综合处理不平衡问题。
三、过采样案例
在逻辑回归中,整个过采样的过程如图所示。先是导入数据集,切分为测试集和训练集,并且划分成四部分:训练集特征、训练集标签、测试集特征、测试集标签。然后这时数据集中的样本很不平衡,此时对数据集进行下采样数理,将样本数少的一类数据重复采样或生成新的合成样本,以增加其数量,使得少数类和多数类样本的数量相等。此时再对过采样之后的数据进行划分,划分成新的大训练集和大测试集,此时在模型中对新划分的大训练集进行测试,得到自测结果,这一步是为了查看模型是否过拟合,再对新划分的大测试集进行测试,然后再对最开始的小测试集测试,最后得到三个测试结果。
下面我们通过案例来具体介绍:
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列,因为它对欺诈检测不是很有用。
4.划分数据集
x = data.drop('Class',axis=1) #对data_c数据进行划分
y = data.Class
x_train,x_test,y_train,y_test = \
train_test_split(x,y,test_size=0.3,random_state=0)
train_test_split:对数据集进行划分
x:删除’Class’这一列。
axis=1:表示一列,若axis=0表示一行。
y:保留Class 这一列
将数据划分为这四部分:
x_train:训练集特征
x_test:测试集特征
y_train:训练集标签
y_test:测试集标签
5.数据过采样并划分
from imblearn.over_sampling import SMOTE #imblearn这个库里面调用
oversampler = SMOTE(random_state =0) #保证数据拟合效果,随机种子
os_x_train,os_y_train = oversampler.fit_resample(x_train,y_train)
os_x_test,os_y_test = oversampler.fit_resample(x_train,y_train)
- 过采样:导入SMOTE类,并通过SMOTE建立一个实列,使用fit_resample方法将SMOTE用于训练训练集数据特征x_train与变量y_train,并将过采样后的数据返回到特征os_x_train、os_y_train、os_x_test、os_y_test中去。
6.绘制图像,查看正负样本个数
mpl.rcParams['font.sans-serif'] = ['Microsoft YaHei'] #显示中文
mpl.rcParams['axes.unicode_minus'] = False
labels_count = pd.value_counts(os_y_train)
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’:设置图像为直方图。
- 正负样本数图像展示如下:
7.模型训练与评估
scores = []
c_param_range = [0.01,0.1,1,10,100] #参数
for i in c_param_range: #第1次循环的时候C=0.01,5个逻辑回归模型
lr = LogisticRegression(C = i,penalty='l2',solver='lbfgs',max_iter=1000)
score = cross_val_score(lr,os_x_train,os_y_train,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)]
lr = LogisticRegression(C = best_c,penalty='l2')
lr.fit(os_x_train,os_y_train)
- 设置参数:依次设置内部参数,C为正则化强度,正则化系数λ的倒数,float类型,默认为1.0。必须是正浮点型数。像SVM一样,越小的数值表示越强的正则化。penalty为正则化方式,有l1和l2两种,这里我们选择l2方式。Solver为优化拟合参数算法选择,默认为liblinear,这里我们选择lbfgs。max_iter为最大迭代次数,这里我们设置为1000。
- 交叉验证:通过K折交叉验证来选择最优的惩罚因子,防止过拟合。这里K设置为8。然后计算8次验证后的召回率将其返回到scores中。
- 寻找最优正则化强度:通过np.argmax方法寻找最大召回率对应的C值
8.打印分类报告并绘制混淆矩阵
# 在小训练集上评估模型
train_predicted = lr.predict(os_x_train)
print(metrics.classification_report(os_y_train, train_predicted))
cm_plot(os_y_test, train_predicted).show()
# 在完整测试集上评估模型
train_predicted1 = lr.predict(os_x_test)
print(metrics.classification_report(os_y_test, train_predicted1))
cm_plot(y_test, train_predicted1).show()
# 在测试集上评估模型
test_predicted = lr.predict(x_test)
print(metrics.classification_report(y_test, test_predicted))
cm_plot(y_test, test_predicted).show()
- 打印报告:打印分类报告(classification_report),包括精确度、召回率、F1分数等指标。
- 绘制混淆矩阵:使用自定义的cm_plot函数绘制混淆矩阵,以可视化模型性能。
9.阈值调整与性能评估
thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]
recalls = []
for i in thresholds:
start_time = time.time()
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])
end_time = time.time()
Running_time = end_time - start_time
print("运行花费的时间为:{:.2f}".format(Running_time))
recalls.append(recall)
print("{} Recall metric in the testing dataset: {:.3f}".format(i,recall))
- 阈值调整:通过调整预测概率的阈值来探索不同阈值对召回率的影响。逻辑回归模型输出的是属于每个类别的概率,通过调整这个阈值(默认为0.5),可以改变预测结果。
- 计算召回率:对于每个阈值,计算召回率,并打印分类报告和混淆矩阵。
四、总结
本篇文章我们重点介绍了使用过采样方法来解决不平衡数据集的问题,以保证模型的性能。
- 过采样方法优点:
过采样不会删除多数类样本,因此可以保留数据集的完整性。
提高少数类的代表性:通过增加少数类样本的数量,模型可以更好地学习少数类的特征。
适合小数据集:当数据集较小时,过采样可以有效增加样本数量,避免模型欠拟合。 - 缺点:
增加计算复杂度:过采样会增加数据集的大小,导致模型训练时间变长。
可能生成噪声样本:过采样方法(如 SMOTE)可能会生成不合理的样本,影响模型性能。
对高维数据效果有限:在高维数据中,过采样方法可能难以生成有意义的少数类样本。