机器学习分类算法实践:偏斜逻辑回归
偏斜逻辑回归
学习目标
通过本课程的学习学员将认识如何使用偏斜逻辑回归的方法来处理数据偏斜的问题。
相关知识点
- 偏斜逻辑回归的应用
学习内容
1 偏斜逻辑回归的应用
偏斜逻辑回归指代在数据分布偏斜(类别不平衡)场景下改进的逻辑回归模型。为应对偏斜数据,改进后的逻辑回归通过调整优化目标或采样策略来增强少数类权重。
在训练阶段,一方面可引入类别权重机制,对少数类样本赋予更高误判惩罚,迫使模型在参数更新时更关注少数类特征;另一方面采用数据增强技术,对少数类样本进行过采样(如生成虚拟样本、重复采样)或对多数类样本进行欠采样(如随机剔除部分样本),平衡数据分布后再建模。此外,通过调整决策阈值也能优化模型表现,例如将默认的0.5阈值降低至0.2,使模型更倾向于将样本预测为少数类,从而提高召回率。这些改进方法能显著提升模型在偏斜数据下的实用性。
-
精确度(Precision):反映模型预测为正例的样本中实际为正例的比例,即“预测结果的准确性”。
-
召回率(Recall):衡量模型对真实正例的捕捉能力,即“实际正例的检出比例”。
-
F1分数:作为精确度与召回率的调和平均数,兼顾两者平衡性,在偏斜数据场景中更具参考价值。例如,当精确度为90%、召回率为50%时,F1分数仅为64%,提示模型需优化对少数类的敏感度。通过调整模型阈值或采样策略提升F1分数,可实现“降低误报率”与“提升检出率”的动态平衡。
1.1 导入包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
1.2 使用scikit-learn的内置make_classification方法来生成合成分类数据
使用make_classification生成一个包含35,040个样本的合成分类数据集,特征包括’Temp’、‘Humidity’和’Crime’(其中’Crime’是冗余特征),目标变量y是高度不平衡的二分类(99.8%负类,0.2%正类)。
对特征进行归一化处理:将’Temp’缩放到0-90范围,'Humidity’缩放到0-100范围,'Crime’缩放到0-10范围,并绘制各特征的直方图以观察分布情况
from sklearn.datasets import make_classification
在此使用了两个信息性特征(Temp, Humidity)和一个冗余特征 ‘Crime’
X,y = make_classification(n_samples=35040,n_classes=2,n_features=3,n_informative=2,n_redundant=1,
weights=[0.998,0.002],class_sep=1.0)
df=pd.DataFrame(data=X,columns=['Temp','Humidity','Crime'])
df['y']=y
df['Temp']=df['Temp']-min(df['Temp'])
maxt=max(df['Temp'])
df['Temp']=90*df['Temp']/maxt
df['Humidity']=df['Humidity']-min(df['Humidity'])
maxh=max(df['Humidity'])
df['Humidity']=100*df['Humidity']/maxh
df['Crime']=df['Crime']-min(df['Crime'])
maxc=max(df['Crime'])
df['Crime']=10*df['Crime']/maxc
df.hist('Temp')

df.hist('Humidity')

df.hist('Crime')

1.3 对布尔数组 df[‘y’]==1 进行求和以计算正例的数量
sum(df[‘y’]==1):计算数据框中目标变量y值为1(正类)的样本数量,用于验证类别分布(由于初始设置权重为[0.998,0.002],预期正类样本极少)
df.head(10):显示数据框的前10行,快速查看数据结构和特征值。
df.describe():生成数据的描述性统计摘要,包括各特征的计数、均值、标准差、最小值、四分位数和最大值,帮助理解数据分布和规模。
这些操作是探索性数据分析的常见步骤,用于初步检查数据质量和特征分布。
sum(df['y']==1)
df.head(10)

df.describe()

1.4 对负类进行欠采样限制其数量
数据平衡处理:从原始高度不平衡数据中随机抽取800个负类样本(y=0)与所有正类样本(y=1)合并,创建平衡数据集(共约800+少量正类样本),通过直方图验证类别分布。
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressionCV
from sklearn.metrics import classification_report
df0=df[df['y']==0].sample(800)
df1=df[df['y']==1]
df_balanced = pd.concat([df0,df1],axis=0)
df_balanced.describe()

df_balanced.hist('y')
plt.title("Relative frequency of positive and negative classes\n in the balanced (under-sampled) dataset")

模型训练与评估:
使用StratifiedKFold和train_test_split划分训练集(70%)和测试集(30%),保持类别比例。
应用MinMaxScaler对特征进行归一化(缩放到[0,1]范围)
训练带5折交叉验证的平衡权重逻辑回归模型(LogisticRegressionCV),自动调整正则化参数
输出测试集的分类报告(精确度、召回率、F1分数等),评估模型在平衡数据上的性能
通过这种处理,缓解了类别不平衡问题,使模型能更公平地学习两类样本的特征,提高对少数类的识别能力。
log_model_balanced = LogisticRegressionCV(cv=5,class_weight='balanced')
X_train, X_test, y_train, y_test = train_test_split(df_balanced.drop('y',axis=1),
df_balanced['y'], test_size=0.30)
from sklearn.preprocessing import MinMaxScaler
scaler=MinMaxScaler()
X_train = scaler.fit_transform(X_train)
log_model_balanced.fit(X_train,y_train)
注:本课程设置了极端情况,实验数据和结果具有随机性,正样本分布极少若正样本检测效果不理想可再次重新运行整个notebook。
print(classification_report(y_test,log_model_balanced.predict(X_test)))

1.5 验证欠采样的程度如何影响F1分数、精确度和召回率
参数设置与初始化:定义负类样本的欠采样数量范围(200到4000,步长200),初始化存储评估指标的列表。
循环实验:
对每个负类样本数量(num),从原始数据中随机抽取对应数量的负类样本(y=0)与所有正类样本(y=1)合并,创建不同平衡程度的欠采样数据集。
使用带5折交叉验证和平衡类别权重的逻辑回归模型(LogisticRegressionCV)
划分训练集(70%)和测试集(30%),对训练数据进行最小-最大归一化
训练模型并计算测试集的F1分数、精确度和召回率
结果存储:将每个采样数量对应的评估指标存入列表,用于后续分析不同欠采样程度对模型性能的影响。
通过这种系统实验,可以研究负类样本数量变化如何影响模型对罕见正类的识别能力(精确度与召回率的权衡),帮助找到最优的欠采样平衡点。
from sklearn.metrics import f1_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
n_neg = [i for i in range(200,4200,200)]
df1=df[df['y']==1]
F1_scores=[]
precision_scores=[]
recall_scores=[]
for num in n_neg:
# 生成欠采样数据集
df0=df[df['y']==0].sample(num)
df_balanced = pd.concat([df0,df1],axis=0)
# 通过 'class_weight=balanced' 和 5 折交叉验证(5-fold cross-validation)创建模型
log_models=LogisticRegressionCV(cv=5,class_weight='balanced')
# 创建测试集与训练集
X_train, X_test, y_train, y_test = train_test_split(df_balanced.drop('y',axis=1),
df_balanced['y'], test_size=0.30)
# 对训练数据进行最小-最大归一化
X_train = scaler.fit_transform(X_train)
# 拟合逻辑回归模型
log_models.fit(X_train,y_train)
# 计算各种指标
F1_scores.append(f1_score(y_test,log_models.predict(X_test)))
precision_scores.append(precision_score(y_test,log_models.predict(X_test)))
recall_scores.append(recall_score(y_test,log_models.predict(X_test)))
绘制散点图,展示在不同负类样本数量下模型的F1分数变化趋势,横轴为负类样本数量(200-4000),纵轴为F1分数,绿色散点带黑色边框,透明度0.6,点大小100,包含网格线,用于直观分析欠采样程度对模型综合性能(精确度与召回率的调和平均)的影响。
plt.scatter(n_neg,F1_scores,color='green',edgecolor='black',alpha=0.6,s=100)
plt.title("F1-score as function of negative samples")
plt.grid(True)
plt.ylabel("F1-score")
plt.xlabel("Number of negative samples")

绘制散点图,展示在不同负类样本数量下模型的精确度(Precision)变化趋势,横轴为负类样本数量(200-4000),纵轴为精确度分数,橙色散点带黑色边框,透明度0.6,点大小100,包含网格线,用于直观分析欠采样程度对模型预测正类准确性的影响(即减少误报的能力)。
plt.scatter(n_neg,precision_scores,color='orange',edgecolor='black',alpha=0.6,s=100)
plt.title("Precision score as function of negative samples")
plt.grid(True)
plt.ylabel("Precision score")
plt.xlabel("Number of negative samples")

绘制散点图,展示在不同负类样本数量下模型的召回率(Recall)变化趋势,横轴为负类样本数量(200-4000),纵轴为召回率分数,蓝色散点带黑色边框,透明度0.6,点大小100,包含网格线,用于直观分析欠采样程度对模型捕获正类能力的影响(即减少漏报的能力)。
plt.scatter(n_neg,recall_scores,color='blue',edgecolor='black',alpha=0.6,s=100)
plt.title("Recall score as function of negative samples")
plt.grid(True)
plt.ylabel("Recall score")
plt.xlabel("Number of negative samples")

结论,随着负样本数量的增加,精确度迅速下降,F1分数也是如此。而召回率在很大程度上不受正负样本混合的影响。
更多推荐
所有评论(0)