开发信用好坏的不平衡分类模型
原文:https://machinelearningmastery.com/imbalanced-classification-of-good-and-bad-credit/
最后更新于 2021 年 1 月 5 日
对于一些不平衡的分类任务,少数类的错误分类错误比其他类型的预测错误更重要。
一个例子是银行客户是否应该获得贷款的分类问题。向标记为好客户的坏客户提供贷款比拒绝向标记为坏客户的好客户提供贷款会给银行带来更大的成本。
这需要仔细选择一个表现指标,该指标既有助于总体上最小化错误分类错误,又有助于最小化一种类型的错误分类错误。
德国信用数据集是一个标准的不平衡类别数据集,它具有对错误分类错误的不同成本的特性。在此数据集上评估的模型可以使用 Fbeta-Measure 进行评估,该方法提供了一种方法,既可以总体量化模型表现,又可以满足一种错误分类错误比另一种错误分类错误成本更高的要求。
在本教程中,您将发现如何为不平衡的德国信用类别数据集开发和评估模型。
完成本教程后,您将知道:
- 如何加载和探索数据集,并为数据准备和模型选择产生想法。
- 如何利用数据欠采样技术评估一套机器学习模型并提高其表现?
- 如何拟合最终模型并使用它来预测特定情况下的类标签。
用我的新书Python 不平衡分类启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
- 2020 年 2 月更新:增加了进一步车型改进部分。
- 2021 年 1 月更新:更新了 API 文档的链接。
开发一个不平衡的分类模型来预测好的和坏的信用
图片由 AL Nieves 提供,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- 德国信贷数据集
- 浏览数据集
- 模型测试和基线结果
- 评估模型
- 评估机器学习算法
- 评估欠采样
- 进一步的模型改进
- 对新数据进行预测
德国信贷数据集
在这个项目中,我们将使用一个标准的不平衡机器学习数据集,称为“德语信用数据集或简称为“德语”
该数据集被用作 Statlog 项目的一部分,该项目是 20 世纪 90 年代基于欧洲的一项倡议,旨在评估和比较一系列不同分类任务的大量(当时)机器学习算法。数据集被记入汉斯·霍夫曼。
不同学科之间的割裂几乎肯定阻碍了交流和进步。StatLog 项目旨在通过选择分类程序来打破这些划分,而不考虑历史谱系,并在大规模和商业上重要的问题上进行测试,从而确定各种技术在多大程度上满足了行业需求。
—第 4 页,机器学习,神经和统计分类,1994。
德国信贷数据集为客户描述金融和银行的详细信息,任务是确定客户是好是坏。假设任务包括预测客户是否会偿还贷款或信贷。
数据集包括 1,000 个示例和 20 个输入变量,其中 7 个是数字(整数),13 个是分类变量。
- 现有支票账户的状态
- 月持续时间
- 信用记录
- 目的
- 信用保证金额
- 储蓄账户
- 自 2000 年至今的就业情况
- 分期付款率占可支配收入的百分比
- 个人地位和性别
- 其他债务人
- 现居地自
- 财产
- 以年为单位的年龄
- 其他分期付款计划
- 房屋
- 该银行现有的信贷数量
- 职位
- 受抚养人人数
- 电话
- 外籍工人
一些分类变量有序数关系,例如“储蓄账户”,尽管大多数没有。
有两类,一类是好客户,二类是坏客户。好客户是默认的或负面的类,而坏客户是例外的或正面的类。总共 70%的例子是好客户,而剩下的 30%的例子是坏客户。
- 好客户:负面或多数类(70%)。
- 不良客户:阳性或少数类(30%)。
数据集提供了一个成本矩阵,它对正类的每个错误分类错误给出不同的惩罚。具体来说,将 5 的成本应用于假阴性(将坏客户标记为好),将 1 的成本分配给假阳性(将好客户标记为坏)。
- 假阴性成本 : 5
- 假阳性成本 : 1
这表明正类是预测任务的重点,银行或金融机构把钱给坏客户比不给好客户更费钱。选择表现指标时,必须考虑这一点。
接下来,让我们仔细看看数据。
浏览数据集
首先,下载数据集并将其保存在您当前的工作目录中,名称为“ german.csv ”。
查看文件的内容。
文件的前几行应该如下所示:
A11,6,A34,A43,1169,A65,A75,4,A93,A101,4,A121,67,A143,A152,2,A173,1,A192,A201,1
A12,48,A32,A43,5951,A61,A73,2,A92,A101,2,A121,22,A143,A152,1,A173,1,A191,A201,2
A14,12,A34,A46,2096,A61,A74,2,A93,A101,3,A121,49,A143,A152,1,A172,2,A191,A201,1
A11,42,A32,A42,7882,A61,A74,2,A93,A103,4,A122,45,A143,A153,1,A173,2,A191,A201,1
A11,24,A33,A40,4870,A61,A73,3,A93,A101,4,A124,53,A143,A153,2,A173,2,A191,A201,2
...
我们可以看到分类列是用 Axxx 格式编码的,其中“ x 是不同标签的整数。需要对分类变量进行一次性编码。
我们还可以看到,数值变量有不同的标度,例如第 2 列中的 6、48 和 12,以及 1169、5951 等。在第五栏。这表明,对于那些对缩放敏感的算法,需要对整数列进行缩放。
目标变量或类是最后一列,包含值 1 和 2。这些将需要分别被标签编码为 0 和 1,以满足不平衡二进制分类任务的一般期望,其中 0 代表负情况,1 代表正情况。
可以使用 read_csv()熊猫函数将数据集加载为数据帧,指定位置和没有标题行的事实。
...
# define the dataset location
filename = 'german.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None)
加载后,我们可以通过打印数据框的形状来总结行数和列数。
...
# summarize the shape of the dataset
print(dataframe.shape)
我们还可以使用 Counter 对象总结每个类中的示例数量。
...
# summarize the class distribution
target = dataframe.values[:,-1]
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%d, Count=%d, Percentage=%.3f%%' % (k, v, per))
将这些联系在一起,下面列出了加载和汇总数据集的完整示例。
# load and summarize the dataset
from pandas import read_csv
from collections import Counter
# define the dataset location
filename = 'german.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None)
# summarize the shape of the dataset
print(dataframe.shape)
# summarize the class distribution
target = dataframe.values[:,-1]
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%d, Count=%d, Percentage=%.3f%%' % (k, v, per))
运行该示例首先加载数据集并确认行数和列数,即 1,000 行、20 个输入变量和 1 个目标变量。
然后总结类别分布,确认好客户和坏客户的数量以及少数和多数类别中的案例百分比。
(1000, 21)
Class=1, Count=700, Percentage=70.000%
Class=2, Count=300, Percentage=30.000%
我们还可以通过为七个数字输入变量创建直方图来查看它们的分布。
首先,我们可以通过调用 DataFrame 上的 select_dtypes()函数来选择带有数值变量的列。然后,我们可以从数据框中选择这些列。我们预计会有七个,加上数字类标签。
...
# select columns with numerical data types
num_ix = df.select_dtypes(include=['int64', 'float64']).columns
# select a subset of the dataframe with the chosen columns
subset = df[num_ix]
然后,我们可以创建每个数字输入变量的直方图。下面列出了完整的示例。
# create histograms of numeric input variables
from pandas import read_csv
from matplotlib import pyplot
# define the dataset location
filename = 'german.csv'
# load the csv file as a data frame
df = read_csv(filename, header=None)
# select columns with numerical data types
num_ix = df.select_dtypes(include=['int64', 'float64']).columns
# select a subset of the dataframe with the chosen columns
subset = df[num_ix]
# create a histogram plot of each numeric variable
ax = subset.hist()
# disable axis labels to avoid the clutter
for axis in ax.flatten():
axis.set_xticklabels([])
axis.set_yticklabels([])
# show the plot
pyplot.show()
运行该示例会创建一个图形,其中七个输入变量各有一个直方图子图,数据集中有一个类别标签。每个子图的标题指示数据帧中的列号(例如,从 0 到 20 的零偏移)。
我们可以看到许多不同的分布,有些是类高斯分布,有些是看似指数或离散分布。
根据建模算法的选择,我们期望将分布缩放到相同的范围是有用的,并且可能使用一些幂变换。
德国信贷数据集中数值变量的直方图
现在我们已经回顾了数据集,让我们看看开发一个测试工具来评估候选模型。
模型测试和基线结果
我们将使用重复的分层 k 折叠交叉验证来评估候选模型。
k 倍交叉验证程序提供了一个良好的模型表现的总体估计,至少与单个列车测试分割相比,不太乐观。我们将使用 k=10,这意味着每个折叠将包含大约 1000/10 或 100 个示例。
分层意味着每个文件夹将包含相同的混合类示例,即大约 70%到 30%的好客户和坏客户。重复意味着评估过程将执行多次,以帮助避免侥幸结果,并更好地捕捉所选模型的方差。我们将使用三次重复。
这意味着单个模型将被拟合和评估 10 * 3 或 30 次,并且这些运行的平均值和标准偏差将被报告。
这可以通过使用repeated stratifiedfold Sklearn 类来实现。
我们将预测客户是否优秀的类别标签。因此,我们需要一个适合评估预测类标签的度量。
任务的重点是积极类(坏客户)。精确和召回是一个很好的起点。最大化准确率将最小化误报,最大化召回将最小化模型预测中的误报。
- 准确率=真阳性/(真阳性+假阳性)
- 回忆=真阳性/(真阳性+假阴性)
使用 F-Measure 将计算准确率和召回率之间的调和平均值。这是一个很好的数字,可以用来比较和选择这个问题的模型。问题是假阴性比假阳性更具破坏性。
- F-Measure = (2 准确率召回)/(准确率+召回)
请记住,这个数据集上的假阴性是坏客户被标记为好客户并获得贷款的情况。假阳性是指一个好客户被标记为坏客户,却没有得到贷款。
- 假阴性:预测为好客户(0 类)的坏客户(1 类)。
- 假阳性:预测为坏客户(1 类)的好客户(0 类)。
假阴性对银行来说比假阳性更昂贵。
- 成本(假阴性) >成本(假阳性)
换句话说,我们对 F 度量感兴趣,它将总结模型最小化正类错误分类错误的能力,但是我们希望更好的模型是最小化假阴性而不是假阳性。
这可以通过使用一个版本的 F-measure 来实现,该版本计算准确率和召回率的加权调和平均值,但是倾向于较高的召回率分数而不是准确率分数。这被称为 Fbeta-measure ,是 F-measure 的推广,其中“ beta ”是定义两个分数权重的参数。
- Fbeta-Measure = ((1 + beta²) 准确率召回)/ (beta² *准确率+召回)
β值 2 比准确率更重视召回率,被称为 F2-测度。
- F2-Measure = ((1 + 2²) 准确率召回)/ (2² *准确率+召回)
我们将使用这一衡量标准来评估德国信贷数据集上的模型。这可以使用 fbeta_score() Sklearn 功能来实现。
我们可以定义一个函数来加载数据集,并将列分成输入和输出变量。我们将对分类变量进行热编码,并对目标变量进行标签编码。您可能还记得,一次性编码为变量的每个值用一个新列替换分类变量,并在该值的列中用 1 标记值。
首先,我们必须将数据帧分成输入和输出变量。
...
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
接下来,我们需要选择所有分类的输入变量,然后应用一个热编码,保留数值变量不变。
这可以通过使用列转换器并将转换定义为仅适用于分类变量列索引的 OneHotEncoder 来实现。
...
# select categorical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
# one hot encode cat features only
ct = ColumnTransformer([('o',OneHotEncoder(),cat_ix)], remainder='passthrough')
X = ct.fit_transform(X)
然后我们可以对目标变量进行标签编码。
...
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
下面的 load_dataset() 函数将所有这些联系在一起,并为建模加载和准备数据集。
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
# one hot encode cat features only
ct = ColumnTransformer([('o',OneHotEncoder(),cat_ix)], remainder='passthrough')
X = ct.fit_transform(X)
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X, y
接下来,我们需要一个函数,该函数将使用β设置为 2 的 fbeta_score() 函数来评估一组预测。
# calculate f2 score
def f2(y_true, y_pred):
return fbeta_score(y_true, y_pred, beta=2)
然后,我们可以定义一个函数来评估数据集上的给定模型,并返回每次折叠和重复的 F2-Measure 分数列表。
下面的 evaluate_model() 函数实现了这一点,将数据集和模型作为参数,返回分数列表。
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation the metric
metric = make_scorer(f2)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
最后,我们可以使用这个测试工具在数据集上评估一个基线模型。
预测示例的少数类的模型将获得最大召回分数和基线准确率分数。这为模型在这个问题上的表现提供了一个基线,通过这个基线可以比较所有其他模型。
这可以通过使用 Sklearn 库中的 DummyClassifier 类,并将少数民族类的“策略”参数设置为“常量”并将“常量”参数设置为“ 1 ”来实现。
...
# define the reference model
model = DummyClassifier(strategy='constant', constant=1)
一旦评估了模型,我们就可以直接报告 F2-Measure 分数的平均值和标准偏差。
...
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean F2: %.3f (%.3f)' % (mean(scores), std(scores)))
将这些结合起来,下面列出了加载德国信用数据集、评估基线模型和报告表现的完整示例。
# test harness and baseline model evaluation for the german credit dataset
from collections import Counter
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.metrics import fbeta_score
from sklearn.metrics import make_scorer
from sklearn.dummy import DummyClassifier
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
# one hot encode cat features only
ct = ColumnTransformer([('o',OneHotEncoder(),cat_ix)], remainder='passthrough')
X = ct.fit_transform(X)
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X, y
# calculate f2 score
def f2(y_true, y_pred):
return fbeta_score(y_true, y_pred, beta=2)
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation metric
metric = make_scorer(f2)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define the location of the dataset
full_path = 'german.csv'
# load the dataset
X, y = load_dataset(full_path)
# summarize the loaded dataset
print(X.shape, y.shape, Counter(y))
# define the reference model
model = DummyClassifier(strategy='constant', constant=1)
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean F2: %.3f (%.3f)' % (mean(scores), std(scores)))
运行该示例首先加载和汇总数据集。
我们可以看到,我们已经加载了正确的行数,并且通过对分类输入变量进行一次性编码,我们已经将输入变量的数量从 20 个增加到 61 个。这表明 13 个分类变量被编码成总共 54 列。
重要的是,我们可以看到类标签正确映射到整数,多数类为 0,少数类为 1,这是不平衡二进制类别数据集的惯例。
接下来,报告 F2-Measure 分数的平均值。
在这种情况下,我们可以看到基线算法实现了大约 0.682 的 F2-Measure。这个分数提供了模特技能的下限;任何获得高于约 0.682 的 F2-Measure 平均值的模型都有技能,而获得低于该值的分数的模型在该数据集上没有技能。
(1000, 61) (1000,) Counter({0: 700, 1: 300})
Mean F2: 0.682 (0.000)
现在我们已经有了测试工具和表现基线,我们可以开始在这个数据集上评估一些模型了。
评估模型
在本节中,我们将使用上一节中开发的测试工具来评估数据集上的一套不同技术。
目标是既演示如何系统地解决问题,又演示为不平衡分类问题设计的一些技术的能力。
报告的表现良好,但没有高度优化(例如,超参数没有调整)。
**你能做得更好吗?**如果您可以使用相同的测试线束获得更好的 F2-Measure 表现,我很想听听。请在下面的评论中告诉我。
评估机器学习算法
让我们从评估数据集上的概率机器学习模型的混合开始。
在数据集上抽查一套不同的线性和非线性算法可能是一个好主意,以便快速找出哪些算法运行良好,值得进一步关注,哪些算法运行不佳。
我们将在德国信用数据集上评估以下机器学习模型:
- 逻辑回归
- 线性判别分析
- 朴素贝叶斯
- 高斯过程分类器
- 支持向量机(SVM)
我们将主要使用默认模型超参数。
我们将依次定义每个模型,并将它们添加到一个列表中,以便我们可以顺序评估它们。下面的 get_models() 函数定义了用于评估的模型列表,以及用于以后绘制结果的模型简称列表。
# define models to test
def get_models():
models, names = list(), list()
# LR
models.append(LogisticRegression(solver='liblinear'))
names.append('LR')
# LDA
models.append(LinearDiscriminantAnalysis())
names.append('LDA')
# NB
models.append(GaussianNB())
names.append('NB')
# GPC
models.append(GaussianProcessClassifier())
names.append('GPC')
# SVM
models.append(SVC(gamma='scale'))
names.append('SVM')
return models, names
然后,我们可以依次列举模型列表,并对每个模型进行评估,存储分数供以后评估。
我们将像上一节一样对分类输入变量进行一次热编码,在这种情况下,我们将对数字输入变量进行标准化。在交叉验证评估过程的每个折叠中,最好使用最小最大缩放器来执行。
实现这一点的一个简单方法是使用管道,其中第一步是列转换器,它将 OneHotEncoder 应用于分类变量,将最小最大缩放器应用于数字输入变量。为此,我们需要分类和数字输入变量的列索引列表。
我们可以更新 load_dataset() 来返回列索引以及数据集的输入和输出元素。下面列出了该功能的更新版本。
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
然后,我们可以调用这个函数来获取数据以及分类变量和数值变量的列表。
...
# define the location of the dataset
full_path = 'german.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
这可用于在评估每个模型之前准备一个管道来包装它。
首先,定义列转换器,它指定对每种类型的列应用什么转换,然后这被用作管道的第一步,该管道以将被拟合和评估的特定模型结束。
...
# evaluate each model
for i in range(len(models)):
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# wrap the model i a pipeline
pipeline = Pipeline(steps=[('t',ct),('m',models[i])])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
我们可以总结每个算法的平均 F2-Measure;这将有助于直接比较算法。
...
# summarize and store
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
在运行结束时,我们将为每个算法的结果样本创建一个单独的方框和触须图。
这些图将使用相同的 y 轴比例,因此我们可以直接比较结果的分布。
...
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
将所有这些结合起来,下面列出了在德国信用数据集上评估一套机器学习算法的完整示例。
# spot check machine learning algorithms on the german credit dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from matplotlib import pyplot
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.metrics import fbeta_score
from sklearn.metrics import make_scorer
from sklearn.linear_model import LogisticRegression
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.gaussian_process import GaussianProcessClassifier
from sklearn.svm import SVC
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# calculate f2-measure
def f2_measure(y_true, y_pred):
return fbeta_score(y_true, y_pred, beta=2)
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation metric
metric = make_scorer(f2_measure)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define models to test
def get_models():
models, names = list(), list()
# LR
models.append(LogisticRegression(solver='liblinear'))
names.append('LR')
# LDA
models.append(LinearDiscriminantAnalysis())
names.append('LDA')
# NB
models.append(GaussianNB())
names.append('NB')
# GPC
models.append(GaussianProcessClassifier())
names.append('GPC')
# SVM
models.append(SVC(gamma='scale'))
names.append('SVM')
return models, names
# define the location of the dataset
full_path = 'german.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# wrap the model i a pipeline
pipeline = Pipeline(steps=[('t',ct),('m',models[i])])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
results.append(scores)
# summarize and store
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
运行该示例依次评估每个算法,并报告平均值和标准偏差 F2-Measure。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到,在所有情况下,没有一个测试模型的 F2 度量高于预测多数类的默认值(0.682)。这些模特都不熟练。这是令人惊讶的,尽管这表明这两个类别之间的决策边界可能是嘈杂的。
>LR 0.497 (0.072)
>LDA 0.519 (0.072)
>NB 0.639 (0.049)
>GPC 0.219 (0.061)
>SVM 0.436 (0.077)
创建一个图形,显示每个算法结果样本的一个方框和须图。方框显示中间 50%的数据,每个方框中间的橙色线显示样本的中值,每个方框中的绿色三角形显示样本的平均值。
不平衡德国信用数据集上机器学习模型的盒须图
现在我们有了一些结果,让我们看看是否可以通过一些欠采样来改进它们。
评估欠采样
在处理不平衡分类任务时,欠采样可能是最不广泛使用的技术,因为大部分注意力都放在用 SMOTE 对多数类进行过采样上。
欠采样有助于沿着决策边界从多数类中移除示例,这使得分类算法面临挑战。
在本实验中,我们将测试以下欠采样算法:
- 托梅克左侧(TL)
- 编辑最近邻居(ENN)
- 重复编辑最近邻
- 单边选择
- 邻里清洁规则(NCR)
Tomek Links 和 ENN 方法从多数类中选择要删除的示例,而 OSS 和 NCR 都选择要保留的示例和要删除的示例。我们将使用逻辑回归算法的平衡版本来测试每种欠采样方法,以保持简单。
可以更新上一节中的 get_models() 函数,以返回一个欠采样技术列表,用逻辑回归算法进行测试。我们使用来自不平衡学习库的这些算法的实现。
定义欠采样方法的 get_models() 函数的更新版本如下。
# define undersampling models to test
def get_models():
models, names = list(), list()
# TL
models.append(TomekLinks())
names.append('TL')
# ENN
models.append(EditedNearestNeighbours())
names.append('ENN')
# RENN
models.append(RepeatedEditedNearestNeighbours())
names.append('RENN')
# OSS
models.append(OneSidedSelection())
names.append('OSS')
# NCR
models.append(NeighbourhoodCleaningRule())
names.append('NCR')
return models, names
Sklearn 提供的管道不知道欠采样算法。因此,我们必须使用不平衡学习库提供的管道实现。
与上一节一样,管道的第一步将是分类变量的热编码和数值变量的标准化,最后一步是拟合模型。这里,中间步骤将是欠采样技术,仅在训练数据集的交叉验证评估中正确应用。
...
# define model to evaluate
model = LogisticRegression(solver='liblinear', class_weight='balanced')
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# scale, then undersample, then fit model
pipeline = Pipeline(steps=[('t',ct), ('s', models[i]), ('m',model)])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
将这些联系在一起,下面列出了在德国信贷数据集上使用不同欠采样方法评估逻辑回归的完整示例。
我们希望欠采样能够提升逻辑回归的技能,理想情况下,在所有情况下都高于预测少数族裔的基线表现。
下面列出了完整的示例。
# evaluate undersampling with logistic regression on the imbalanced german credit dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.metrics import fbeta_score
from sklearn.metrics import make_scorer
from matplotlib import pyplot
from sklearn.linear_model import LogisticRegression
from imblearn.pipeline import Pipeline
from imblearn.under_sampling import TomekLinks
from imblearn.under_sampling import EditedNearestNeighbours
from imblearn.under_sampling import RepeatedEditedNearestNeighbours
from imblearn.under_sampling import NeighbourhoodCleaningRule
from imblearn.under_sampling import OneSidedSelection
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# calculate f2-measure
def f2_measure(y_true, y_pred):
return fbeta_score(y_true, y_pred, beta=2)
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation metric
metric = make_scorer(f2_measure)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define undersampling models to test
def get_models():
models, names = list(), list()
# TL
models.append(TomekLinks())
names.append('TL')
# ENN
models.append(EditedNearestNeighbours())
names.append('ENN')
# RENN
models.append(RepeatedEditedNearestNeighbours())
names.append('RENN')
# OSS
models.append(OneSidedSelection())
names.append('OSS')
# NCR
models.append(NeighbourhoodCleaningRule())
names.append('NCR')
return models, names
# define the location of the dataset
full_path = 'german.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
# define model to evaluate
model = LogisticRegression(solver='liblinear', class_weight='balanced')
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# scale, then undersample, then fit model
pipeline = Pipeline(steps=[('t',ct), ('s', models[i]), ('m',model)])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
results.append(scores)
# summarize and store
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
运行该示例使用五种不同的欠采样技术来评估逻辑回归算法。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到五种欠采样技术中的三种产生了 F2 度量,比基线 0.682 有所改善。具体来说,ENN,雷恩和 NCR,重复编辑最近的邻居导致最好的表现,F2-测量值约为 0.716。
结果表明 SMOTE 以 0.604 的 F2-Measure 获得了最佳得分。
>TL 0.669 (0.057)
>ENN 0.706 (0.048)
>RENN 0.714 (0.041)
>OSS 0.670 (0.054)
>NCR 0.693 (0.052)
为每种评估的欠采样技术创建方框图和触须图,显示它们通常具有相同的扩展。
令人鼓舞的是,对于表现良好的方法,方框分布在 0.8 左右,所有三种方法的平均值和中值都在 0.7 左右。这强调了分布是偏高的,有时会因为一些不好的评估而降低。
不平衡德国信贷数据集上欠采样逻辑回归的盒须图
接下来,让我们看看如何使用最终模型对新数据进行预测。
进一步的模型改进
这是一个新的部分,与上面的部分略有不同。在这里,我们将测试导致 F2-measure 表现进一步提升的特定模型,随着新模型的报告/发现,我将更新这一部分。
改进#1:实例化硬件阈值
使用带有实例硬度阈值欠采样的平衡逻辑回归可以获得约为 0.727 的 F2 度量。
下面列出了完整的示例。
# improve performance on the imbalanced german credit dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.metrics import fbeta_score
from sklearn.metrics import make_scorer
from sklearn.linear_model import LogisticRegression
from imblearn.pipeline import Pipeline
from imblearn.under_sampling import InstanceHardnessThreshold
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# calculate f2-measure
def f2_measure(y_true, y_pred):
return fbeta_score(y_true, y_pred, beta=2)
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation metric
metric = make_scorer(f2_measure)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define the location of the dataset
full_path = 'german.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# define model to evaluate
model = LogisticRegression(solver='liblinear', class_weight='balanced')
# define the data sampling
sampling = InstanceHardnessThreshold()
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# scale, then sample, then fit model
pipeline = Pipeline(steps=[('t',ct), ('s', sampling), ('m',model)])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
print('%.3f (%.3f)' % (mean(scores), std(scores)))
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
运行该示例会得到以下结果。
0.727 (0.033)
改进# 2:SMOTENN
使用带有SMOTENN的 LDA 可以获得约为 0.730 的 F2 度量,其中 ENN 参数设置为 ENN 实例,采样策略设置为多数。
下面列出了完整的示例。
# improve performance on the imbalanced german credit dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.metrics import fbeta_score
from sklearn.metrics import make_scorer
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from imblearn.pipeline import Pipeline
from imblearn.combine import SMOTEENN
from imblearn.under_sampling import EditedNearestNeighbours
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# calculate f2-measure
def f2_measure(y_true, y_pred):
return fbeta_score(y_true, y_pred, beta=2)
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation metric
metric = make_scorer(f2_measure)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define the location of the dataset
full_path = 'german.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# define model to evaluate
model = LinearDiscriminantAnalysis()
# define the data sampling
sampling = SMOTEENN(enn=EditedNearestNeighbours(sampling_strategy='majority'))
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# scale, then sample, then fit model
pipeline = Pipeline(steps=[('t',ct), ('s', sampling), ('m',model)])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
print('%.3f (%.3f)' % (mean(scores), std(scores)))
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
运行该示例会得到以下结果。
0.730 (0.046)
改进 3:带标准缩放器和脊分类器的 SMOTEENN
大约 0.741 的 F2 测量值可以通过使用脊分类器而不是线性判别分析并使用用于数字输入的标准缩放器而不是最小最大缩放器来进一步改进 SMOTEENN 来实现。
下面列出了完整的示例。
# improve performance on the imbalanced german credit dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.metrics import fbeta_score
from sklearn.metrics import make_scorer
from sklearn.linear_model import RidgeClassifier
from imblearn.pipeline import Pipeline
from imblearn.combine import SMOTEENN
from imblearn.under_sampling import EditedNearestNeighbours
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# calculate f2-measure
def f2_measure(y_true, y_pred):
return fbeta_score(y_true, y_pred, beta=2)
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation metric
metric = make_scorer(f2_measure)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define the location of the dataset
full_path = 'german.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# define model to evaluate
model = RidgeClassifier()
# define the data sampling
sampling = SMOTEENN(enn=EditedNearestNeighbours(sampling_strategy='majority'))
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',StandardScaler(),num_ix)])
# scale, then sample, then fit model
pipeline = Pipeline(steps=[('t',ct), ('s', sampling), ('m',model)])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
print('%.3f (%.3f)' % (mean(scores), std(scores)))
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
运行该示例会得到以下结果。
0.741 (0.034)
你还能做得更好吗?
在下面的评论里告诉我。
对新数据进行预测
考虑到结果的差异,选择任何欠采样方法可能就足够了。在这种情况下,我们将选择重复 ENN 逻辑回归。
在我们的测试线束上,该模型的 F2 测量值约为 0.716。
我们将把它作为我们的最终模型,并利用它对新数据进行预测。
首先,我们可以将模型定义为管道。
...
# define model to evaluate
model = LogisticRegression(solver='liblinear', class_weight='balanced')
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# scale, then undersample, then fit model
pipeline = Pipeline(steps=[('t',ct), ('s', RepeatedEditedNearestNeighbours()), ('m',model)])
一旦定义好了,我们就可以在整个训练数据集中使用它。
...
# fit the model
pipeline.fit(X, y)
一旦适合,我们可以通过调用 predict() 函数来使用它对新数据进行预测。这将为“好客户”返回类别标签 0,或为“坏客户返回类别标签 1。
重要的是,我们必须使用管道中训练数据集上的列转换器来使用相同的转换正确准备新数据。
例如:
...
# define a row of data
row = [...]
# make prediction
yhat = pipeline.predict([row])
为了证明这一点,我们可以使用 fit 模型对一些案例的标签进行一些预测,在这些案例中,我们知道该案例是好客户还是坏客户。
下面列出了完整的示例。
# fit a model and make predictions for the german credit dataset
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from imblearn.pipeline import Pipeline
from imblearn.under_sampling import RepeatedEditedNearestNeighbours
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None)
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# define the location of the dataset
full_path = 'german.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# define model to evaluate
model = LogisticRegression(solver='liblinear', class_weight='balanced')
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# scale, then undersample, then fit model
pipeline = Pipeline(steps=[('t',ct), ('s', RepeatedEditedNearestNeighbours()), ('m',model)])
# fit the model
pipeline.fit(X, y)
# evaluate on some good customers cases (known class 0)
print('Good Customers:')
data = [['A11', 6, 'A34', 'A43', 1169, 'A65', 'A75', 4, 'A93', 'A101', 4, 'A121', 67, 'A143', 'A152', 2, 'A173', 1, 'A192', 'A201'],
['A14', 12, 'A34', 'A46', 2096, 'A61', 'A74', 2, 'A93', 'A101', 3, 'A121', 49, 'A143', 'A152', 1, 'A172', 2, 'A191', 'A201'],
['A11', 42, 'A32', 'A42', 7882, 'A61', 'A74', 2, 'A93', 'A103', 4, 'A122', 45, 'A143', 'A153', 1, 'A173', 2, 'A191', 'A201']]
for row in data:
# make prediction
yhat = pipeline.predict([row])
# get the label
label = yhat[0]
# summarize
print('>Predicted=%d (expected 0)' % (label))
# evaluate on some bad customers (known class 1)
print('Bad Customers:')
data = [['A13', 18, 'A32', 'A43', 2100, 'A61', 'A73', 4, 'A93', 'A102', 2, 'A121', 37, 'A142', 'A152', 1, 'A173', 1, 'A191', 'A201'],
['A11', 24, 'A33', 'A40', 4870, 'A61', 'A73', 3, 'A93', 'A101', 4, 'A124', 53, 'A143', 'A153', 2, 'A173', 2, 'A191', 'A201'],
['A11', 24, 'A32', 'A43', 1282, 'A62', 'A73', 4, 'A92', 'A101', 2, 'A123', 32, 'A143', 'A152', 1, 'A172', 1, 'A191', 'A201']]
for row in data:
# make prediction
yhat = pipeline.predict([row])
# get the label
label = yhat[0]
# summarize
print('>Predicted=%d (expected 1)' % (label))
运行该示例首先在整个训练数据集上拟合模型。
然后,拟合模型用于预测从数据集文件中选择的案例的好客户标签。我们可以看到大多数情况都是正确预测的。这就凸显出,虽然我们选择了一个好的模式,但并不完美。
然后将实际不良客户的一些情况作为模型的输入,并对标签进行预测。正如我们所希望的那样,所有情况下都能预测到正确的标签。
Good Customers:
>Predicted=0 (expected 0)
>Predicted=0 (expected 0)
>Predicted=0 (expected 0)
Bad Customers:
>Predicted=0 (expected 1)
>Predicted=1 (expected 1)
>Predicted=1 (expected 1)
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
书
- 机器学习,神经和统计分类,1994。
蜜蜂
- 熊猫。data frame . select _ dt types API。
- 硬化. metrics.fbeta_score API 。
- 硬化。化合物。ColumnTransformer API 。
- 硬化。预处理。OneHotEncoder API 。
- imb learn . pipeline . pipeline API。
资料组
摘要
在本教程中,您发现了如何为不平衡的德国信用类别数据集开发和评估模型。
具体来说,您了解到:
- 如何加载和探索数据集,并为数据准备和模型选择产生想法。
- 如何利用数据欠采样技术评估一套机器学习模型并提高其表现?
- 如何拟合最终模型并使用它来预测特定情况下的类标签。
你有什么问题吗?
在下面的评论中提问,我会尽力回答。
Python 不平衡分类(7 天迷你课程)
原文:https://machinelearningmastery.com/imbalanced-classification-with-python-7-day-mini-course/
最后更新于 2021 年 1 月 5 日
不平衡分类速成班。
7 天内登上不平衡分类榜首。
分类预测建模是给一个例子分配一个标签的任务。
不平衡分类是那些分类任务,其中例子在类之间的分布不相等。
实际的不平衡分类需要使用一套专门的技术、数据准备技术、学习算法和表现指标。
在这个速成课程中,您将发现如何在七天内用 Python 开始并自信地完成一个不平衡的分类项目。
这是一个又大又重要的岗位。你可能想把它做成书签。
我们开始吧。
- 2021 年 1 月更新:更新了 API 文档的链接。
巨蟒不平衡分类(7 天迷你课程)
图片由拱门国家公园提供,保留部分权利。
这个速成班是给谁的?
在我们开始之前,让我们确保你在正确的地方。
本课程面向可能了解一些应用机器学习的开发人员。也许你知道如何使用流行的工具来端到端地解决预测建模问题,或者至少解决大部分主要步骤。
本课程中的课程假设了您的一些情况,例如:
- 你对编程的基本 Python 很熟悉。
- 您可能知道一些用于数组操作的基本 NumPy。
- 你可能知道一些基本的 sci kit-学习建模。
你不需要:
- 数学天才!
- 机器学习专家!
这门速成课程将把你从一个懂一点机器学习的开发人员带到一个能驾驭不平衡分类项目的开发人员。
注意:本速成课程假设您有一个至少安装了 NumPy 的工作 Python 3 SciPy 环境。如果您需要环境方面的帮助,可以遵循这里的逐步教程:
- 如何用 Anaconda 设置机器学习的 Python 环境
速成班概述
这门速成课分为七节课。
你可以每天完成一节课(推荐的)或者一天完成所有的课(铁杆)。这真的取决于你有多少时间和你的热情程度。
下面是 Python 中不平衡分类的七个入门和有效经验:
- 第 01 课:不平衡分类的挑战
- 第 02 课:不平衡数据的直觉
- 第 03 课:评估不平衡分类模型
- 第 04 课:对多数班进行欠采样
- 第 05 课:对少数民族班级进行过采样
- 第 06 课:结合数据欠采样和过采样
- 第 07 课:成本敏感算法
每节课可能需要你 60 秒或 30 分钟。慢慢来,按照自己的节奏完成课程。提问,甚至在下面的评论中发布结果。
这些课程可能期望你去发现如何做事。我会给你一些提示,但是每节课的部分要点是迫使你学习去哪里寻找关于 Python 中的算法和最佳工具的帮助。( 提示 :所有答案我都直接在这个博客上了;使用搜索框。)
在评论中发布您的结果;我会为你加油的!
坚持住。不要放弃。
注:这只是速成班。关于更多的细节和充实的教程,请参阅我的书,题目是“Python 的不平衡分类”
第一课:不平衡分类的挑战
在本课中,您将发现不平衡分类问题的挑战。
不平衡的分类问题对预测建模提出了挑战,因为大多数用于分类的机器学习算法都是围绕每个类具有相同数量的例子的假设而设计的。
这导致模型的预测表现很差,特别是对于少数群体。这是一个问题,因为通常情况下,少数类更重要,因此该问题对少数类的分类错误比多数类更敏感。
- 多数类:半数以上的例子都属于这一类,往往是否定或正常的情况。
- 少数民族类:属于这一类的例子不到一半,往往是阳性或异常情况。
一个分类问题可能会有点歪斜,比如说是不是有轻微的不平衡。或者,对于给定的训练数据集,分类问题可能具有严重的不平衡,其中一个类中可能有数百或数千个示例,而另一个类中可能有数十个示例。
- 轻微不平衡。其中示例的分布在训练数据集中不均匀少量(例如 4:6)。
- 严重失衡。其中示例的分布在训练数据集中大量不均匀(例如 1:100 或更多)。
我们在实践中感兴趣解决的许多分类预测建模问题是不平衡的。
因此,不平衡分类没有得到比它更多的关注是令人惊讶的。
你的任务
在这节课中,你必须列出五个固有的阶级不平衡问题的一般例子。
一个例子可能是欺诈检测,另一个可能是入侵检测。
在下面的评论中发表你的答案。我想看看你有什么想法。
在下一课中,你将发现如何培养对倾斜类分布的直觉。
第二课:不平衡数据的直觉
在本课中,您将发现如何为不平衡的类别数据集开发实用的直觉。
对于处理不平衡分类问题的初学者来说,一个挑战是特定的倾斜类分布意味着什么。例如,1:10 与 1:100 的班级比例的区别和含义是什么?
make _ classification()sci kit-learn 函数可用于定义具有所需类别不平衡的合成数据集。“权重参数指定了负类中示例的比例,例如[0.99,0.01]表示 99%的示例属于多数类,其余 1%属于少数类。
...
# define dataset
X, y = make_classification(n_samples=1000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0)
一旦定义,我们就可以使用 Counter 对象来总结类分布,以了解每个类到底有多少个示例。
...
# summarize class distribution
counter = Counter(y)
print(counter)
我们还可以创建数据集的散点图,因为只有两个输入变量。然后,这些点可以被每个类着色。这个图提供了一个直观的直觉,说明了 99%对 1%的多数/少数阶级不平衡实际上是什么样子的。
下面列出了创建和总结不平衡类别数据集的完整示例。
# plot imbalanced classification problem
from collections import Counter
from sklearn.datasets import make_classification
from matplotlib import pyplot
from numpy import where
# define dataset
X, y = make_classification(n_samples=1000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99, 0.01], flip_y=0)
# summarize class distribution
counter = Counter(y)
print(counter)
# scatter plot of examples by class label
for label, _ in counter.items():
row_ix = where(y == label)[0]
pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(label))
pyplot.legend()
pyplot.show()
你的任务
在本课中,您必须运行示例并复习情节。
对于奖励积分,您可以测试不同的班级比例并查看结果。
在下面的评论中发表你的答案。我想看看你有什么想法。
在下一课中,您将发现如何评估不平衡分类的模型。
第 03 课:评估不平衡分类模型
在本课中,您将发现如何评估不平衡分类问题的模型。
预测准确率是分类任务最常见的度量标准,尽管当用于不平衡的分类任务时,它是不合适的,并且有潜在的危险误导。
这样做的原因是,如果 98%的数据属于负类,那么只需一直预测负类,就可以达到平均 98%的准确率,达到一个天真地看起来不错,但实际上没有技巧的分数。
相反,必须采用替代表现指标。
流行的替代方法是准确率和召回分数,这使得模型的表现可以通过关注被称为正类的少数类来考虑。
准确率计算正确预测的正例数除以预测的正例总数的比率。最大化准确率将最小化误报。
- 准确率=真阳性/(真阳性+假阳性)
回忆预测正确预测的正面例子总数除以本来可以预测的正面例子总数的比率。最大限度地召回将最大限度地减少假阴性。
- 回忆=真阳性/(真阳性+假阴性)
一个模型的表现可以用一个平均准确率和召回率的分数来概括,称为 F-Measure。最大化 F-Measure 将同时最大化精确度和召回率。
- F-measure = (2 准确率召回)/(准确率+召回)
以下示例将逻辑回归模型应用于不平衡分类问题,并计算准确率,然后可以将其与准确率、召回和 F-measure 进行比较。
# evaluate imbalanced classification model with different metrics
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0)
# split into train/test sets with same class ratio
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, stratify=y)
# define model
model = LogisticRegression(solver='liblinear')
# fit model
model.fit(trainX, trainy)
# predict on test set
yhat = model.predict(testX)
# evaluate predictions
print('Accuracy: %.3f' % accuracy_score(testy, yhat))
print('Precision: %.3f' % precision_score(testy, yhat))
print('Recall: %.3f' % recall_score(testy, yhat))
print('F-measure: %.3f' % f1_score(testy, yhat))
你的任务
在本课中,您必须运行该示例,并将分类准确率与其他指标(如准确率、召回率和 F-measure)进行比较。
对于奖励积分,尝试其他指标,如 Fbeta-measure 和 ROC AUC 分数。
在下面的评论中发表你的答案。我想看看你有什么想法。
在下一课中,你将发现如何对大多数班级进行欠采样。
第 04 课:对大多数班级采样不足
在本课中,您将了解如何在训练数据集中对多数类进行欠采样。
在不平衡的数据集上使用标准机器学习算法的一个简单方法是改变训练数据集以具有更平衡的类分布。
这可以通过从多数类中删除示例来实现,多数类被称为“欠采样”一个可能的缺点是,多数类中在建模过程中有用的例子可能会被删除。
不平衡学习库提供了许多欠采样算法的例子。可以使用 pip 轻松安装此库;例如:
pip install imbalanced-learn
一种快速可靠的方法是从多数类中随机删除示例,以将不平衡降低到不太严重的比例,甚至使类均匀。
下面的例子创建了一个合成的不平衡类别数据,然后使用随机欠采样类将类分布从 1:100 的少数类更改为多数类,再更改为不太严重的 1:2。
# example of undersampling the majority class
from collections import Counter
from sklearn.datasets import make_classification
from imblearn.under_sampling import RandomUnderSampler
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99, 0.01], flip_y=0)
# summarize class distribution
print(Counter(y))
# define undersample strategy
undersample = RandomUnderSampler(sampling_strategy=0.5)
# fit and apply the transform
X_under, y_under = undersample.fit_resample(X, y)
# summarize class distribution
print(Counter(y_under))
你的任务
对于本课,您必须运行示例,并注意对多数类进行欠采样前后类分布的变化。
对于加分,尝试其他欠采样比率,甚至尝试不平衡学习库提供的其他欠采样技术。
在下面的评论中发表你的答案。我想看看你有什么想法。
在下一课中,您将发现如何对少数族裔进行过采样。
第 05 课:对少数族裔进行过采样
在本课中,您将了解如何对训练数据集中的少数类进行过采样。
从多数类中删除示例的替代方法是从少数类中添加新的示例。
这可以通过简单地复制少数民族类中的例子来实现,但是这些例子没有添加任何新的信息。相反,可以使用训练数据集中的现有示例来合成少数民族的新示例。这些新示例将与特征空间中的现有示例“接近”,但在微小但随机的方式上有所不同。
*SMOTE 算法是对少数类进行过采样的一种流行方法。这种技术可以用来减少不平衡或使阶级分布均匀。
下面的例子演示了在合成数据集上使用不平衡学习库提供的 SMOTE 类。初始类别分布为 1:100,少数类别被过采样为 1:2 分布。
# example of oversampling the minority class
from collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99, 0.01], flip_y=0)
# summarize class distribution
print(Counter(y))
# define oversample strategy
oversample = SMOTE(sampling_strategy=0.5)
# fit and apply the transform
X_over, y_over = oversample.fit_resample(X, y)
# summarize class distribution
print(Counter(y_over))
你的任务
对于本课,您必须运行该示例,并注意对少数类进行过采样前后类分布的变化。
要获得加分,可以尝试其他过采样率,甚至可以尝试不平衡学习库提供的其他过采样技术。
在下面的评论中发表你的答案。我想看看你有什么想法。
在下一课中,您将了解如何将欠采样和过采样技术结合起来。
第 6 课:结合数据欠采样和过采样
在本课中,您将了解如何在训练数据集中组合数据欠采样和过采样。
数据欠采样将从多数类中删除示例,而数据过采样将向少数类添加示例。这两种方法可以结合起来,并在单个训练数据集上使用。
考虑到有这么多不同的数据采样技术可供选择,合并哪种方法可能会令人困惑。令人欣慰的是,有一些常见的组合在实践中表现良好;一些例子包括:
- 使用 SMOTE 过采样的随机欠采样。
- Tomek 将欠采样与 SMOTE 过采样相结合。
- 使用 SMOTE 过采样编辑最近邻欠采样。
通过首先应用一种采样算法,然后应用另一种算法,可以将这些组合手动应用于给定的训练数据集。谢天谢地,不平衡学习库提供了常见的组合数据采样技术的实现。
下面的例子演示了如何使用 SMOTEENN ,它结合了少数类的 SMOTE 过采样和多数类的编辑最近邻欠采样。
# example of both undersampling and oversampling
from collections import Counter
from sklearn.datasets import make_classification
from imblearn.combine import SMOTEENN
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99, 0.01], flip_y=0)
# summarize class distribution
print(Counter(y))
# define sampling strategy
sample = SMOTEENN(sampling_strategy=0.5)
# fit and apply the transform
X_over, y_over = sample.fit_resample(X, y)
# summarize class distribution
print(Counter(y_over))
你的任务
对于本课,您必须运行该示例,并注意数据采样前后类分布的变化。
对于加分,尝试其他组合数据采样技术,甚至尝试手动应用过采样,然后对数据集进行欠采样。
在下面的评论中发表你的答案。我想看看你有什么想法。
在下一课中,您将发现如何使用成本敏感算法进行不平衡分类。
第七课:成本敏感算法
在本课中,您将发现如何使用成本敏感算法进行不平衡分类。
大多数机器学习算法都假设模型产生的所有错误分类错误都是相等的。不平衡分类问题通常不是这种情况,在不平衡分类问题中,遗漏一个正类或少数类比错误地将一个例子从负类或多数类中分类出来更糟糕。
成本敏感学习是机器学习的一个子领域,它在训练机器学习模型时考虑了预测误差的成本(以及潜在的其他成本)。许多机器学习算法可以被更新为对成本敏感,其中模型因一个类的错误分类错误而比另一个类(例如少数类)受到更多惩罚。
Sklearn 库通过定义模型时指定的 class_weight 属性为一系列算法提供了这种能力。可以指定与类别分布成反比的权重。
如果多数类和少数类的类分布为 0.99 到 0.01,那么类权重参数可以被定义为字典,该字典定义对多数类的错误的惩罚为 0.01,对少数类的错误的惩罚为 0.99,例如{0:0.01,1:0.99}。
这是一种有用的启发式方法,可以通过将 class_weight 参数设置为字符串“ balanced 来自动配置。
下面的示例演示了如何在不平衡的类别数据集上定义和拟合成本敏感的逻辑回归模型。
# example of cost sensitive logistic regression for imbalanced classification
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import f1_score
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0)
# split into train/test sets with same class ratio
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, stratify=y)
# define model
model = LogisticRegression(solver='liblinear', class_weight='balanced')
# fit model
model.fit(trainX, trainy)
# predict on test set
yhat = model.predict(testX)
# evaluate predictions
print('F-Measure: %.3f' % f1_score(testy, yhat))
你的任务
在本课中,您必须运行示例并查看成本敏感型模型的表现。
对于加分,将表现与成本不敏感的逻辑回归版本进行比较。
在下面的评论中发表你的答案。我想看看你有什么想法。
这是迷你课程的最后一课。
末日!
( 看你走了多远
你成功了。干得好!
花一点时间,回头看看你已经走了多远。
你发现了:
- 不平衡分类的挑战是缺乏少数群体的例子,以及不同类别分类错误的重要性不同。
- 如何为不平衡的类别数据集开发空间直觉,为数据准备和算法选择提供信息。
- 分类准确率的失败,以及像准确率、召回率和 F-测度这样的替代度量如何更好地总结不平衡数据集上的模型表现。
- 如何从训练数据集中的多数类中删除示例,称为数据欠采样。
- 如何在训练数据集中的少数类中合成新的示例,称为数据过采样。
- 如何在训练数据集中组合数据过采样和欠采样技术,以及产生良好表现的常见组合。
- 如何使用代价敏感的机器学习算法的改进版本来提高不平衡类别数据集的表现。
下一步,看看我用 Python 写的关于不平衡分类的书。
摘要
你觉得迷你课程怎么样?
你喜欢这个速成班吗?
**你有什么问题吗?*有什么症结吗?
让我知道。请在下面留言。
成人收入数据集的不平衡分类
原文:https://machinelearningmastery.com/imbalanced-classification-with-the-adult-income-dataset/
最后更新于 2020 年 10 月 27 日
许多二进制分类任务并不是每个类都有相同数量的例子,例如类分布是倾斜的或者不平衡的。
一个流行的例子是成人收入数据集,该数据集涉及根据关系和教育水平等个人详细信息预测每年高于或低于 5 万美元的个人收入水平。收入低于 5 万美元的案例比高于 5 万美元的案例多得多,尽管这种偏离并不严重。
这意味着可以使用不平衡分类技术,同时仍然可以使用分类准确率来报告模型表现,就像平衡分类问题一样。
在本教程中,您将发现如何为不平衡的成人收入类别数据集开发和评估模型。
完成本教程后,您将知道:
- 如何加载和探索数据集,并为数据准备和模型选择产生想法。
- 如何系统地评估一套具有强大测试工具的机器学习模型。
- 如何拟合最终模型并使用它来预测特定情况下的类标签。
用我的新书Python 不平衡分类启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
开发一个不平衡的分类模型来预测收入
图片由科特·埃德布洛姆提供,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- 成人收入数据集
- 浏览数据集
- 模型测试和基线结果
- 评估模型
- 对新数据进行预测
成人收入数据集
在这个项目中,我们将使用一个标准的不平衡机器学习数据集,称为“成人收入或简称为“ 成人 ”数据集。
数据集由罗尼·科哈维(Ronny Kohavi)和巴里·贝克尔(Barry Becker)获得,取自 1994 年美国人口普查局(T0)的数据,包括使用个人详细信息(如教育水平)来预测个人年收入是高于还是低于 5 万美元。
成人数据集来自人口普查局,任务是根据教育、每周工作时间等属性预测给定的成人年收入是否超过 5 万美元…
——提升朴素贝叶斯分类器的准确性:决策树混合,1996。
数据集提供了 14 个输入变量,它们是分类、序数和数字数据类型的混合。变量的完整列表如下:
- 年龄。
- 工作类。
- 最终重量。
- 教育。
- 教育年限。
- 婚姻状况。
- 职业。
- 关系。
- 种族。
- 做爱。
- 资本收益。
- 资本损失。
- 每周小时数。
- 祖国。
数据集包含用问号字符(?)标记的缺失值).
总共有 48,842 行数据,3,620 行缺少值,剩下 45,222 行完整数据。
有两个类值“ > 50K ”和“ < =50K ”,意思是一个二分类任务。类别不平衡,偏向“ < =50K ”类别标签。
- ’ > 50K’ :多数派,约 25%。
- ’ < =50K’ :少数民族,约 75%。
假设类不平衡不严重,并且两个类标签同等重要,则通常使用分类准确率或分类错误来报告此数据集上的模型表现。
使用预定义的训练集和测试集,报告的良好分类误差约为 14%,分类准确率约为 86%。这可能会在处理此数据集时提供一个目标。
接下来,让我们仔细看看数据。
浏览数据集
成人数据集是一个广泛使用的标准机器学习数据集,用于探索和演示许多机器学习算法,包括一般的和专门为不平衡分类设计的算法。
首先,下载数据集并保存在您当前的工作目录中,名称为“成人-all.csv
查看文件的内容。
文件的前几行应该如下所示:
39,State-gov,77516,Bachelors,13,Never-married,Adm-clerical,Not-in-family,White,Male,2174,0,40,United-States,<=50K
50,Self-emp-not-inc,83311,Bachelors,13,Married-civ-spouse,Exec-managerial,Husband,White,Male,0,0,13,United-States,<=50K
38,Private,215646,HS-grad,9,Divorced,Handlers-cleaners,Not-in-family,White,Male,0,0,40,United-States,<=50K
53,Private,234721,11th,7,Married-civ-spouse,Handlers-cleaners,Husband,Black,Male,0,0,40,United-States,<=50K
28,Private,338409,Bachelors,13,Married-civ-spouse,Prof-specialty,Wife,Black,Female,0,0,40,Cuba,<=50K
...
我们可以看到输入变量是数字和分类或序数数据类型的混合,其中非数字列用字符串表示。至少,分类变量需要是有序的或者单热编码的。
我们还可以看到目标变量是用字符串表示的。对于多数类,此列需要用 0 进行标签编码,对于少数类,需要用 1 进行标签编码,这是二进制不平衡分类任务的习惯。
缺少的值用“”标记?'字符。这些值需要估计,或者给定少量示例,可以从数据集中删除这些行。
可以使用 read_csv()熊猫函数将数据集加载为数据帧,指定文件名、没有标题行以及像“这样的字符串?'应解析为 NaN (缺失)值。
...
# define the dataset location
filename = 'adult-all.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None, na_values='?')
加载后,我们可以删除包含一个或多个缺失值的行。
...
# drop rows with missing
dataframe = dataframe.dropna()
我们可以通过打印数据框的形状来总结行数和列数。
...
# summarize the shape of the dataset
print(dataframe.shape)
我们也可以使用 Counter 对象总结每个类中的例子数量。
...
# summarize the class distribution
target = dataframe.values[:,-1]
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%s, Count=%d, Percentage=%.3f%%' % (k, v, per))
将这些联系在一起,下面列出了加载和汇总数据集的完整示例。
# load and summarize the dataset
from pandas import read_csv
from collections import Counter
# define the dataset location
filename = 'adult-all.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None, na_values='?')
# drop rows with missing
dataframe = dataframe.dropna()
# summarize the shape of the dataset
print(dataframe.shape)
# summarize the class distribution
target = dataframe.values[:,-1]
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%s, Count=%d, Percentage=%.3f%%' % (k, v, per))
运行该示例首先加载数据集并确认行数和列数,即 45,222 行没有缺失值,14 个输入变量和一个目标变量。
然后总结了班级分布,确认了中等程度的班级不平衡,大多数班级大约为 75%(<=50K) and approximately 25 percent for the minority class (>50K)。
(45222, 15)
Class= <=50K, Count=34014, Percentage=75.216%
Class= >50K, Count=11208, Percentage=24.784%
我们还可以通过为每个变量创建直方图来查看数字输入变量的分布。
首先,我们可以通过调用 DataFrame 上的 select_dtypes()函数来选择带有数值变量的列。然后,我们可以从数据框中选择这些列。
...
# select columns with numerical data types
num_ix = df.select_dtypes(include=['int64', 'float64']).columns
# select a subset of the dataframe with the chosen columns
subset = df[num_ix]
然后,我们可以创建每个数字输入变量的直方图。下面列出了完整的示例。
# create histograms of numeric input variables
from pandas import read_csv
from matplotlib import pyplot
# define the dataset location
filename = 'adult-all.csv'
# load the csv file as a data frame
df = read_csv(filename, header=None, na_values='?')
# drop rows with missing
df = df.dropna()
# select columns with numerical data types
num_ix = df.select_dtypes(include=['int64', 'float64']).columns
# select a subset of the dataframe with the chosen columns
subset = df[num_ix]
# create a histogram plot of each numeric variable
subset.hist()
pyplot.show()
运行该示例会为数据集中的六个输入变量创建带有一个直方图子图的图形。每个子图的标题指示数据帧中的列号(例如零偏移)。
我们可以看到许多不同的分布,有些是类高斯分布,有些是看似指数或离散分布。我们还可以看到,它们似乎都有非常不同的规模。
根据建模算法的选择,我们期望将分布缩放到相同的范围是有用的,并且可能使用一些幂变换。
成人不平衡类别数据集中数值变量的直方图
现在我们已经回顾了数据集,让我们看看开发一个测试工具来评估候选模型。
模型测试和基线结果
我们将使用重复的分层 k 折叠交叉验证来评估候选模型。
k 倍交叉验证程序提供了一个良好的模型表现的总体估计,至少与单个列车测试分割相比,不太乐观。我们将使用 k=10,这意味着每个折叠将包含大约 45,222/10,或者大约 4,522 个示例。
分层意味着每个文件夹将包含相同的混合类的例子,即大约 75%到 25%的多数和少数类分别。重复意味着评估过程将执行多次,以帮助避免侥幸结果,并更好地捕捉所选模型的方差。我们将使用三次重复。
这意味着单个模型将被拟合和评估 10 * 3 或 30 次,并且这些运行的平均值和标准偏差将被报告。
这可以使用repeated stratifiedfoldSklearn 类来实现。
我们将为每个示例预测一个类标签,并使用分类准确率来衡量模型表现。
下面的 evaluate_model() 函数将获取加载的数据集和定义的模型,并使用重复的分层 k 倍交叉验证对其进行评估,然后返回一个准确性分数列表,稍后可以对其进行汇总。
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1)
return scores
我们可以定义一个函数来加载数据集,并对目标列进行标签编码。
我们还将返回一个分类和数字列的列表,以防我们决定以后在拟合模型时转换它们。
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None, na_values='?')
# drop rows with missing
dataframe = dataframe.dropna()
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
最后,我们可以使用这个测试工具在数据集上评估一个基线模型。
当使用分类准确率时,简单模型将预测所有情况下的多数类。这为模型在这个问题上的表现提供了一个基线,通过这个基线可以比较所有其他模型。
这可以通过使用 Sklearn 库中的 DummyClassifier 类并将“策略”参数设置为“最频繁”来实现。
...
# define the reference model
model = DummyClassifier(strategy='most_frequent')
一旦模型被评估,我们可以直接报告准确性分数的平均值和标准偏差。
...
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))
将这些结合起来,下面列出了加载成人数据集、评估基线模型和报告表现的完整示例。
# test harness and baseline model evaluation for the adult dataset
from collections import Counter
from numpy import mean
from numpy import std
from numpy import hstack
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.dummy import DummyClassifier
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None, na_values='?')
# drop rows with missing
dataframe = dataframe.dropna()
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1)
return scores
# define the location of the dataset
full_path = 'adult-all.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# summarize the loaded dataset
print(X.shape, y.shape, Counter(y))
# define the reference model
model = DummyClassifier(strategy='most_frequent')
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))
运行该示例首先加载和汇总数据集。
我们可以看到加载了正确的行数。重要的是,我们可以看到类标签具有到整数的正确映射,多数类为 0,少数类为 1,这是不平衡二进制类别数据集的惯例。
接下来,报告平均分类准确度分数。
在这种情况下,我们可以看到基线算法达到了大约 75.2%的准确率。这个分数提供了模特技能的下限;任何平均准确率高于约 75.2%的模型都有技能,而得分低于此值的模型在此数据集上没有技能。
(45222, 14) (45222,) Counter({0: 34014, 1: 11208})
Mean Accuracy: 0.752 (0.000)
现在我们已经有了测试工具和表现基线,我们可以开始在这个数据集上评估一些模型了。
评估模型
在本节中,我们将使用上一节中开发的测试工具来评估数据集上的一套不同技术。
目标是既演示如何系统地解决问题,又演示为不平衡分类问题设计的一些技术的能力。
报告的表现良好,但没有高度优化(例如,超参数没有调整)。
**你能做得更好吗?**如果你能用同样的测试装具达到更好的分类准确率表现,我很想听听。请在下面的评论中告诉我。
评估机器学习算法
让我们从评估数据集上的混合机器学习模型开始。
在数据集上抽查一套不同的非线性算法可能是一个好主意,以便快速找出哪些算法运行良好,值得进一步关注,哪些算法运行不佳。
我们将在成人数据集上评估以下机器学习模型:
- 决策树
- 支持向量机(SVM)
- 袋装决策树
- 随机森林
- 梯度增压机
我们将主要使用默认的模型超参数,除了集成算法中的树的数量,我们将设置为合理的默认值 100。
我们将依次定义每个模型,并将它们添加到一个列表中,以便我们可以顺序评估它们。下面的 get_models() 函数定义了用于评估的模型列表,以及用于以后绘制结果的模型简称列表。
# define models to test
def get_models():
models, names = list(), list()
# CART
models.append(DecisionTreeClassifier())
names.append('CART')
# SVM
models.append(SVC(gamma='scale'))
names.append('SVM')
# Bagging
models.append(BaggingClassifier(n_estimators=100))
names.append('BAG')
# RF
models.append(RandomForestClassifier(n_estimators=100))
names.append('RF')
# GBM
models.append(GradientBoostingClassifier(n_estimators=100))
names.append('GBM')
return models, names
然后,我们可以依次列举模型列表,并对每个模型进行评估,存储分数供以后评估。
我们将使用 OneHotEncoder 对分类输入变量进行一键编码,并将使用最小最大缩放器对数字输入变量进行归一化。在交叉验证过程中,这些操作必须在每个训练/测试分割中执行,其中编码和缩放操作适合训练集并应用于训练集和测试集。
实现这一点的一个简单方法是使用管道,其中第一步是列转换器,它将 OneHotEncoder 应用于分类变量,将最小最大缩放器应用于数字输入变量。为此,我们需要分类和数字输入变量的列索引列表。
我们在上一节中定义的 load_dataset() 函数加载并返回数据集和具有分类和数字数据类型的列列表。这可用于在评估前准备一个管道来包装每个模型。首先,定义列转换器,它指定要应用于每种类型列的转换,然后这被用作管道的第一步,该管道以将要拟合和评估的特定模型结束。
...
# define steps
steps = [('c',OneHotEncoder(handle_unknown='ignore'),cat_ix), ('n',MinMaxScaler(),num_ix)]
# one hot encode categorical, normalize numerical
ct = ColumnTransformer(steps)
# wrap the model i a pipeline
pipeline = Pipeline(steps=[('t',ct),('m',models[i])])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
我们可以总结每个算法的平均准确率,这将有助于直接比较算法。
...
# summarize performance
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
在运行结束时,我们将为每个算法的结果样本创建一个单独的方框和触须图。这些图将使用相同的 y 轴比例,因此我们可以直接比较结果的分布。
...
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
将所有这些结合起来,下面列出了在成人不平衡数据集上评估一套机器学习算法的完整示例。
# spot check machine learning algorithms on the adult imbalanced dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from matplotlib import pyplot
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import BaggingClassifier
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None, na_values='?')
# drop rows with missing
dataframe = dataframe.dropna()
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1)
return scores
# define models to test
def get_models():
models, names = list(), list()
# CART
models.append(DecisionTreeClassifier())
names.append('CART')
# SVM
models.append(SVC(gamma='scale'))
names.append('SVM')
# Bagging
models.append(BaggingClassifier(n_estimators=100))
names.append('BAG')
# RF
models.append(RandomForestClassifier(n_estimators=100))
names.append('RF')
# GBM
models.append(GradientBoostingClassifier(n_estimators=100))
names.append('GBM')
return models, names
# define the location of the dataset
full_path = 'adult-all.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
# define steps
steps = [('c',OneHotEncoder(handle_unknown='ignore'),cat_ix), ('n',MinMaxScaler(),num_ix)]
# one hot encode categorical, normalize numerical
ct = ColumnTransformer(steps)
# wrap the model i a pipeline
pipeline = Pipeline(steps=[('t',ct),('m',models[i])])
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
results.append(scores)
# summarize performance
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
运行该示例依次评估每个算法,并报告平均和标准偏差分类准确率。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
你考了多少分?
在下面的评论中发布你的结果。
在这种情况下,我们可以看到所有选择的算法都是熟练的,达到了 75.2%以上的分类准确率。我们可以看到,集成决策树算法表现最好,也许随机梯度提升表现最好,分类准确率约为 86.3%。
这比原始论文中报告的结果稍好,尽管采用了不同的模型评估程序。
>CART 0.812 (0.005)
>SVM 0.837 (0.005)
>BAG 0.852 (0.004)
>RF 0.849 (0.004)
>GBM 0.863 (0.004)
创建一个图形,显示每个算法结果样本的一个方框和须图。方框显示中间 50%的数据,每个方框中间的橙色线显示样本的中值,每个方框中的绿色三角形显示样本的平均值。
我们可以看到,每种算法的分数分布似乎都高于约 75%的基线,可能有一些异常值(图中的圆圈)。每种算法的分布看起来都很紧凑,中值和平均值是一致的,这表明模型在这个数据集上非常稳定,分数没有形成偏斜分布。
这突出表明,重要的不仅仅是模型表现的中心趋势,还应该考虑传播甚至最坏情况的结果。尤其是少数民族的例子。
不平衡成人数据集上机器学习模型的盒须图
对新数据进行预测
在本节中,我们可以拟合最终模型,并使用它对单行数据进行预测。
我们将使用梯度提升分类器模型作为我们的最终模型,该模型实现了大约 86.3%的分类准确率。拟合最终模型包括定义列转换器来编码分类变量和缩放数值变量,然后构建管道来在拟合模型之前对训练集执行这些转换。
管道然后可以直接用于对新数据进行预测,并将使用与在训练数据集上执行的操作相同的操作来自动编码和缩放新数据。
首先,我们可以将模型定义为管道。
...
# define model to evaluate
model = GradientBoostingClassifier(n_estimators=100)
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# define the pipeline
pipeline = Pipeline(steps=[('t',ct), ('m',model)])
一旦定义好了,我们就可以在整个训练数据集中使用它。
...
# fit the model
pipeline.fit(X, y)
一旦适合,我们可以通过调用 predict() 函数来使用它对新数据进行预测。这将为“< =50K”返回类别标签 0,或为“> 50K”返回类别标签 1。
重要的是,我们必须使用管道中的列转换器来使用相同的转换正确准备新数据。
例如:
...
# define a row of data
row = [...]
# make prediction
yhat = pipeline.predict([row])
为了证明这一点,我们可以使用拟合模型对一些我们知道结果的情况下的标签进行一些预测。
下面列出了完整的示例。
# fit a model and make predictions for the on the adult dataset
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import GradientBoostingClassifier
from imblearn.pipeline import Pipeline
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
dataframe = read_csv(full_path, header=None, na_values='?')
# drop rows with missing
dataframe = dataframe.dropna()
# split into inputs and outputs
last_ix = len(dataframe.columns) - 1
X, y = dataframe.drop(last_ix, axis=1), dataframe[last_ix]
# select categorical and numerical features
cat_ix = X.select_dtypes(include=['object', 'bool']).columns
num_ix = X.select_dtypes(include=['int64', 'float64']).columns
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X.values, y, cat_ix, num_ix
# define the location of the dataset
full_path = 'adult-all.csv'
# load the dataset
X, y, cat_ix, num_ix = load_dataset(full_path)
# define model to evaluate
model = GradientBoostingClassifier(n_estimators=100)
# one hot encode categorical, normalize numerical
ct = ColumnTransformer([('c',OneHotEncoder(),cat_ix), ('n',MinMaxScaler(),num_ix)])
# define the pipeline
pipeline = Pipeline(steps=[('t',ct), ('m',model)])
# fit the model
pipeline.fit(X, y)
# evaluate on some <=50K cases (known class 0)
print('<=50K cases:')
data = [[24, 'Private', 161198, 'Bachelors', 13, 'Never-married', 'Prof-specialty', 'Not-in-family', 'White', 'Male', 0, 0, 25, 'United-States'],
[23, 'Private', 214542, 'Some-college', 10, 'Never-married', 'Farming-fishing', 'Own-child', 'White', 'Male', 0, 0, 40, 'United-States'],
[38, 'Private', 309122, '10th', 6, 'Divorced', 'Machine-op-inspct', 'Not-in-family', 'White', 'Female', 0, 0, 40, 'United-States']]
for row in data:
# make prediction
yhat = pipeline.predict([row])
# get the label
label = yhat[0]
# summarize
print('>Predicted=%d (expected 0)' % (label))
# evaluate on some >50K cases (known class 1)
print('>50K cases:')
data = [[55, 'Local-gov', 107308, 'Masters', 14, 'Married-civ-spouse', 'Prof-specialty', 'Husband', 'White', 'Male', 0, 0, 40, 'United-States'],
[53, 'Self-emp-not-inc', 145419, '1st-4th', 2, 'Married-civ-spouse', 'Exec-managerial', 'Husband', 'White', 'Male', 7688, 0, 67, 'Italy'],
[44, 'Local-gov', 193425, 'Masters', 14, 'Married-civ-spouse', 'Prof-specialty', 'Wife', 'White', 'Female', 4386, 0, 40, 'United-States']]
for row in data:
# make prediction
yhat = pipeline.predict([row])
# get the label
label = yhat[0]
# summarize
print('>Predicted=%d (expected 1)' % (label))
运行该示例首先在整个训练数据集上拟合模型。
然后将用于预测<=50K cases is chosen from the dataset file. We can see that all cases are correctly predicted. Then some > 50K 例标签的拟合模型作为模型的输入,并对标签进行预测。正如我们所希望的,正确的标签是可以预测的。
<=50K cases:
>Predicted=0 (expected 0)
>Predicted=0 (expected 0)
>Predicted=0 (expected 0)
>50K cases:
>Predicted=1 (expected 1)
>Predicted=1 (expected 1)
>Predicted=1 (expected 1)
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
报纸
- 提高朴素贝叶斯分类器的准确率:决策树混合,1996。
蜜蜂
- 熊猫。data frame . select _ dt types API。
- sklearn.model_selection。重复的策略应用编程接口。
- 硬化. dummy . dummy class ification API。
资料组
摘要
在本教程中,您发现了如何为不平衡的成人收入类别数据集开发和评估模型。
具体来说,您了解到:
- 如何加载和探索数据集,并为数据准备和模型选择产生想法。
- 如何系统地评估一套具有强大测试工具的机器学习模型。
- 如何拟合最终模型并使用它来预测特定情况下的类标签。
你有什么问题吗?
在下面的评论中提问,我会尽力回答。
欺诈性信用卡交易数据集的不平衡分类
最后更新于 2020 年 8 月 21 日
欺诈是信用卡公司的一个主要问题,这既是因为每天完成的交易量很大,也是因为许多欺诈交易看起来很像正常交易。
识别欺诈性信用卡交易是一种常见的不平衡二进制分类,其重点是积极类(即欺诈类)。
因此,当将预测概率映射到类标签时,像准确率和召回率这样的度量可以用来总结类标签方面的模型表现,而准确率-召回率曲线可以用来总结概率阈值范围内的模型表现。
这使得模型的操作者可以控制如何根据模型产生的假阳性或假阴性类型的误差进行预测。
在本教程中,您将发现如何为不平衡的信用卡欺诈数据集开发和评估模型。
完成本教程后,您将知道:
- 如何加载和探索数据集,并为数据准备和模型选择产生想法。
- 如何系统地评估一套具有强大测试工具的机器学习模型。
- 如何拟合最终模型,并使用它来预测特定案例的欺诈概率。
我们开始吧。
如何预测信用卡欺诈交易的概率
图片由安德里亚·谢弗提供,版权所有。
教程概述
本教程分为五个部分;它们是:
- 信用卡欺诈数据集
- 浏览数据集
- 模型测试和基线结果
- 评估模型
- 对新数据进行预测
信用卡欺诈数据集
在这个项目中,我们将使用一个标准的不平衡机器学习数据集,称为“信用卡欺诈检测”数据集。
该数据代表了欧洲持卡人在 2013 年 9 月两天内发生的信用卡交易。
该数据集被归功于布鲁塞尔自由大学的机器学习小组和 T2 安德里亚·达尔·波佐洛等人的一套出版物。
持卡人的所有详细信息已通过主成分分析(主成分分析)转换匿名化。相反,总共提供了这些匿名特征的 28 个主要成分。此外,还提供了交易间隔的时间(以秒为单位),以及购买金额(大概以欧元为单位)。
每条记录被分为正常(类别“0”)或欺诈(类别“1”),交易严重偏向正常。具体而言,在总共 284,807 笔交易中,有 492 笔欺诈性信用卡交易,约占所有交易的 0.172%。
它包含两天内发生的在线交易子集,在 284,807 笔交易中,我们有 492 笔欺诈。数据集高度不平衡,正类(欺诈)占所有交易的 0.172%…
——不平衡分类欠采样校准概率,2015。
一些出版物使用曲线度量下的 ROC 面积,尽管数据集的网站建议使用曲线度量下的准确率-召回面积,因为存在严重的类别不平衡。
给定类别不平衡比率,我们建议使用准确率-召回曲线下面积(AUPRC)来测量准确率。
——信用卡欺诈检测,卡格尔。
接下来,让我们仔细看看数据。
浏览数据集
首先,下载并解压缩数据集,并将其保存在您当前的工作目录中,名称为“ creditcard.csv ”。
查看文件的内容。
文件的前几行应该如下所示:
0,-1.3598071336738,-0.0727811733098497,2.53634673796914,1.37815522427443,-0.338320769942518,0.462387777762292,0.239598554061257,0.0986979012610507,0.363786969611213,0.0907941719789316,-0.551599533260813,-0.617800855762348,-0.991389847235408,-0.311169353699879,1.46817697209427,-0.470400525259478,0.207971241929242,0.0257905801985591,0.403992960255733,0.251412098239705,-0.018306777944153,0.277837575558899,-0.110473910188767,0.0669280749146731,0.128539358273528,-0.189114843888824,0.133558376740387,-0.0210530534538215,149.62,"0"
0,1.19185711131486,0.26615071205963,0.16648011335321,0.448154078460911,0.0600176492822243,-0.0823608088155687,-0.0788029833323113,0.0851016549148104,-0.255425128109186,-0.166974414004614,1.61272666105479,1.06523531137287,0.48909501589608,-0.143772296441519,0.635558093258208,0.463917041022171,-0.114804663102346,-0.183361270123994,-0.145783041325259,-0.0690831352230203,-0.225775248033138,-0.638671952771851,0.101288021253234,-0.339846475529127,0.167170404418143,0.125894532368176,-0.00898309914322813,0.0147241691924927,2.69,"0"
1,-1.35835406159823,-1.34016307473609,1.77320934263119,0.379779593034328,-0.503198133318193,1.80049938079263,0.791460956450422,0.247675786588991,-1.51465432260583,0.207642865216696,0.624501459424895,0.066083685268831,0.717292731410831,-0.165945922763554,2.34586494901581,-2.89008319444231,1.10996937869599,-0.121359313195888,-2.26185709530414,0.524979725224404,0.247998153469754,0.771679401917229,0.909412262347719,-0.689280956490685,-0.327641833735251,-0.139096571514147,-0.0553527940384261,-0.0597518405929204,378.66,"0"
1,-0.966271711572087,-0.185226008082898,1.79299333957872,-0.863291275036453,-0.0103088796030823,1.24720316752486,0.23760893977178,0.377435874652262,-1.38702406270197,-0.0549519224713749,-0.226487263835401,0.178228225877303,0.507756869957169,-0.28792374549456,-0.631418117709045,-1.0596472454325,-0.684092786345479,1.96577500349538,-1.2326219700892,-0.208037781160366,-0.108300452035545,0.00527359678253453,-0.190320518742841,-1.17557533186321,0.647376034602038,-0.221928844458407,0.0627228487293033,0.0614576285006353,123.5,"0"
2,-1.15823309349523,0.877736754848451,1.548717846511,0.403033933955121,-0.407193377311653,0.0959214624684256,0.592940745385545,-0.270532677192282,0.817739308235294,0.753074431976354,-0.822842877946363,0.53819555014995,1.3458515932154,-1.11966983471731,0.175121130008994,-0.451449182813529,-0.237033239362776,-0.0381947870352842,0.803486924960175,0.408542360392758,-0.00943069713232919,0.79827849458971,-0.137458079619063,0.141266983824769,-0.206009587619756,0.502292224181569,0.219422229513348,0.215153147499206,69.99,"0"
...
请注意,此版本的数据集删除了标题行。如果您从 Kaggle 下载数据集,您必须先删除标题行。
我们可以看到第一列是时间,是整数,第二最后一列是购买金额。最后一列包含类标签。我们可以看到,PCA 变换后的特征有正有负,并且包含大量的浮点准确率。
时间列不太可能有用,可能可以删除。主成分分析变量和美元金额之间的比例差异表明,对于那些对输入变量的比例敏感的算法,应该使用数据比例。
可以使用 read_csv()熊猫函数将数据集加载为数据框,指定列的位置和名称,因为没有标题行。
...
# define the dataset location
filename = 'creditcard.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None)
加载后,我们可以通过打印数据框的形状来总结行数和列数。
...
# summarize the shape of the dataset
print(dataframe.shape)
我们还可以使用 Counter 对象总结每个类中的示例数量。
...
# summarize the class distribution
target = dataframe.values[:,-1]
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%d, Count=%d, Percentage=%.3f%%' % (k, v, per))
将这些联系在一起,下面列出了加载和汇总数据集的完整示例。
# load and summarize the dataset
from pandas import read_csv
from collections import Counter
# define the dataset location
filename = 'creditcard.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None)
# summarize the shape of the dataset
print(dataframe.shape)
# summarize the class distribution
target = dataframe.values[:,-1]
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%d, Count=%d, Percentage=%.3f%%' % (k, v, per))
运行该示例首先加载数据集并确认行数和列数,它们是 284,807 行、30 个输入变量和 1 个目标变量。
然后总结了类别分布,确认了类别分布的严重偏差,约 99.827%的交易标记为正常,约 0.173%的交易标记为欺诈。这通常与论文中对数据集的描述相匹配。
(284807, 31)
Class=0, Count=284315, Percentage=99.827%
Class=1, Count=492, Percentage=0.173%
我们还可以通过为每个变量创建直方图来查看输入变量的分布。
由于大量的变量,图可能看起来很混乱。因此,我们将禁用轴标签,以便我们可以专注于直方图。我们还将增加每个直方图中使用的面元数量,以帮助更好地查看数据分布。
下面列出了创建所有输入变量直方图的完整示例。
# create histograms of input variables
from pandas import read_csv
from matplotlib import pyplot
# define the dataset location
filename = 'creditcard.csv'
# load the csv file as a data frame
df = read_csv(filename, header=None)
# drop the target variable
df = df.drop(30, axis=1)
# create a histogram plot of each numeric variable
ax = df.hist(bins=100)
# disable axis labels to avoid the clutter
for axis in ax.flatten():
axis.set_xticklabels([])
axis.set_yticklabels([])
# show the plot
pyplot.show()
我们可以看到,大多数主成分分析成分的分布是高斯分布,许多可能以零为中心,这表明变量被标准化为主成分分析变换的一部分。
信用卡欺诈数据集中输入变量的直方图
数量变量可能很有趣,不会出现在直方图上。
这表明金额值的分布可能有偏差。我们可以为这个变量创建一个 5 位数的摘要,以便更好地了解事务大小。
下面列出了完整的示例。
# summarize the amount variable
from pandas import read_csv
# define the dataset location
filename = 'creditcard.csv'
# load the csv file as a data frame
df = read_csv(filename, header=None)
# summarize the amount variable.
print(df[29].describe())
运行该示例,我们可以看到大多数数量很小,平均值约为 88,中间 50%的观测值在 5 到 77 之间。
最大值约为 25,691,这拉高了分布,可能是一个异常值(例如,有人用信用卡购买了一辆汽车)。
count 284807.000000
mean 88.349619
std 250.120109
min 0.000000
25% 5.600000
50% 22.000000
75% 77.165000
max 25691.160000
Name: 29, dtype: float64
现在我们已经回顾了数据集,让我们看看开发一个测试工具来评估候选模型。
模型测试和基线结果
我们将使用重复的分层 k 折叠交叉验证来评估候选模型。
k 倍交叉验证程序提供了一个良好的模型表现的总体估计,至少与单个列车测试分割相比,不太乐观。我们将使用 k=10,这意味着每个折叠将包含大约 284807/10 或 28480 个示例。
分层意味着每个文件夹将包含相同的混合类示例,即正常交易和欺诈交易分别占 99.8%到 0.2%。重复意味着评估过程将执行多次,以帮助避免侥幸结果,并更好地捕捉所选模型的方差。我们将使用 3 次重复。
这意味着单个模型将被拟合和评估 10 * 3 或 30 次,并且这些运行的平均值和标准偏差将被报告。
这可以通过使用repeated stratifiedfold Sklearn 类来实现。
我们将使用准确率-召回曲线或 PR AUC 下的推荐面积度量。
这要求给定的算法首先预测一个概率或类似概率的度量。然后,使用准确率和召回率在不同阈值范围内评估预测概率,用于将概率映射到类别标签,并且这些阈值曲线下的区域被报告为模型的表现。
这一指标侧重于正类,这对于如此严重的类失衡是可取的。它还允许最终模型的操作者选择一个阈值,用于将概率映射到类标签(欺诈或非欺诈交易),从而最好地平衡最终模型的准确率和召回率。
我们可以定义一个函数来加载数据集,并将列分成输入和输出变量。下面的 load_dataset() 函数实现了这一点。
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
然后,我们可以定义一个函数,为给定的一组预测计算曲线下的准确率-召回面积。
这包括首先通过准确率 _ 召回 _ 曲线()函数计算预测的准确率-召回曲线。然后,每个阈值的输出召回率和准确率值可以作为参数提供给 auc() 以计算曲线下的面积。下面的 pr_auc() 函数实现了这一点。
# calculate precision-recall area under curve
def pr_auc(y_true, probas_pred):
# calculate precision-recall curve
p, r, _ = precision_recall_curve(y_true, probas_pred)
# calculate area under curve
return auc(r, p)
然后,我们可以定义一个函数来评估数据集上的给定模型,并返回每次折叠和重复的 PR AUC 分数列表。
下面的 evaluate_model() 函数实现了这一点,将数据集和模型作为参数,返回分数列表。 make_scorer()函数用于定义精确-召回 AUC 度量,并指示模型必须预测概率才能被评估。
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation the metric
metric = make_scorer(pr_auc, needs_proba=True)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
最后,我们可以使用这个测试工具在数据集上评估一个基线模型。
当使用曲线度量下的精确召回区域时,预测所有示例的正类(类 1)的模型将提供基线表现。
这可以通过使用 Sklearn 库中的 DummyClassifier 类,并将“策略”参数设置为“常量”并将“常量参数设置为“1”来预测正类来实现。
...
# define the reference model
model = DummyClassifier(strategy='constant', constant=1)
一旦模型被评估,我们可以直接报告 PR AUC 分数的平均值和标准差。
...
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean PR AUC: %.3f (%.3f)' % (mean(scores), std(scores)))
将这些结合起来,下面列出了加载数据集、评估基线模型和报告表现的完整示例。
# test harness and baseline model evaluation for the credit dataset
from collections import Counter
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import auc
from sklearn.metrics import make_scorer
from sklearn.dummy import DummyClassifier
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
# calculate precision-recall area under curve
def pr_auc(y_true, probas_pred):
# calculate precision-recall curve
p, r, _ = precision_recall_curve(y_true, probas_pred)
# calculate area under curve
return auc(r, p)
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation the metric
metric = make_scorer(pr_auc, needs_proba=True)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define the location of the dataset
full_path = 'creditcard.csv'
# load the dataset
X, y = load_dataset(full_path)
# summarize the loaded dataset
print(X.shape, y.shape, Counter(y))
# define the reference model
model = DummyClassifier(strategy='constant', constant=1)
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean PR AUC: %.3f (%.3f)' % (mean(scores), std(scores)))
运行该示例首先加载和汇总数据集。
我们可以看到加载了正确的行数,并且有 30 个输入变量。
接下来,报告 PR AUC 分数的平均值。
在这种情况下,我们可以看到基线算法实现的平均 PR AUC 约为 0.501。
这个分数提供了模特技能的下限;任何平均 PR AUC 达到 0.5 以上的模型都有技能,而得分低于这个值的模型在这个数据集上没有技能。
(284807, 30) (284807,) Counter({0.0: 284315, 1.0: 492})
Mean PR AUC: 0.501 (0.000)
现在我们已经有了测试工具和表现基线,我们可以开始在这个数据集上评估一些模型了。
评估模型
在本节中,我们将使用上一节中开发的测试工具来评估数据集上的一套不同技术。
目标是既演示如何系统地解决问题,又演示为不平衡分类问题设计的一些技术的能力。
报告的表现良好,但没有高度优化(例如,超参数没有调整)。
**你能做得更好吗?**如果你能用同样的测试装具达到更好的 PR AUC 表现,我很想听听。请在下面的评论中告诉我。
评估机器学习算法
让我们从评估数据集上的混合机器学习模型开始。
在数据集上抽查一套不同的非线性算法可能是一个好主意,以便快速找出哪些算法运行良好,值得进一步关注,哪些算法不运行。
我们将在信用卡欺诈数据集上评估以下机器学习模型:
- 决策树
- k 近邻(KNN)
- 袋装决策树
- 随机森林
- 额外树
我们将主要使用默认的模型超参数,除了集成算法中的树的数量,我们将设置为合理的默认值 100。在将输入变量作为输入提供给 KNN 算法之前,我们还将对它们进行标准化。
我们将依次定义每个模型,并将它们添加到一个列表中,以便我们可以顺序评估它们。下面的 get_models() 函数定义了用于评估的模型列表,以及用于以后绘制结果的模型简称列表。
# define models to test
def get_models():
models, names = list(), list()
# CART
models.append(DecisionTreeClassifier())
names.append('CART')
# KNN
steps = [('s',StandardScaler()),('m',KNeighborsClassifier())]
models.append(Pipeline(steps=steps))
names.append('KNN')
# Bagging
models.append(BaggingClassifier(n_estimators=100))
names.append('BAG')
# RF
models.append(RandomForestClassifier(n_estimators=100))
names.append('RF')
# ET
models.append(ExtraTreesClassifier(n_estimators=100))
names.append('ET')
return models, names
然后,我们可以依次列举模型列表,并对每个模型进行评估,存储分数供以后评估。
...
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
# evaluate the model and store results
scores = evaluate_model(X, y, models[i])
results.append(scores)
# summarize performance
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
在运行结束时,我们可以将每个分数样本绘制成一个方框,并用相同的比例绘制晶须图,这样我们就可以直接比较分布。
...
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
将所有这些结合起来,下面列出了在信用卡欺诈数据集上评估一套机器学习算法的完整示例。
# spot check machine learning algorithms on the credit card fraud dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from matplotlib import pyplot
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import auc
from sklearn.metrics import make_scorer
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import BaggingClassifier
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
# calculate precision-recall area under curve
def pr_auc(y_true, probas_pred):
# calculate precision-recall curve
p, r, _ = precision_recall_curve(y_true, probas_pred)
# calculate area under curve
return auc(r, p)
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation the metric
metric = make_scorer(pr_auc, needs_proba=True)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define models to test
def get_models():
models, names = list(), list()
# CART
models.append(DecisionTreeClassifier())
names.append('CART')
# KNN
steps = [('s',StandardScaler()),('m',KNeighborsClassifier())]
models.append(Pipeline(steps=steps))
names.append('KNN')
# Bagging
models.append(BaggingClassifier(n_estimators=100))
names.append('BAG')
# RF
models.append(RandomForestClassifier(n_estimators=100))
names.append('RF')
# ET
models.append(ExtraTreesClassifier(n_estimators=100))
names.append('ET')
return models, names
# define the location of the dataset
full_path = 'creditcard.csv'
# load the dataset
X, y = load_dataset(full_path)
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
# evaluate the model and store results
scores = evaluate_model(X, y, models[i])
results.append(scores)
# summarize performance
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
运行该示例依次评估每个算法,并报告平均值和标准差。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到所有测试的算法都有技巧,实现了高于默认值 0.5 的 PR AUC。结果表明,决策树算法的集成在该数据集上表现良好,尽管数据集标准化的 KNN 似乎平均表现最好。
>CART 0.771 (0.049)
>KNN 0.867 (0.033)
>BAG 0.846 (0.045)
>RF 0.855 (0.046)
>ET 0.864 (0.040)
创建一个图形,显示每个算法结果样本的一个方框和须图。方框显示中间 50%的数据,每个方框中间的橙色线显示样本的中值,每个方框中的绿色三角形显示样本的平均值。
我们可以看到,决策树的 KNN 和集成的分数分布是紧密的,平均值似乎与中间值一致,这表明分布可能是对称的,可能是高斯分布,并且分数可能相当稳定。
不平衡信用卡欺诈数据集上机器学习模型的盒须图
既然我们已经看到了如何在这个数据集上评估模型,让我们看看如何使用最终模型来进行预测。
对新数据进行预测
在本节中,我们可以拟合最终模型,并使用它对单行数据进行预测。
我们将使用 KNN 模型作为最终模型,该模型的 PR AUC 约为 0.867。拟合最终模型包括在拟合模型之前定义一个管道来缩放数值变量。
然后,管道可用于直接对新数据进行预测,并将使用对训练数据集执行的相同操作自动缩放新数据。
首先,我们可以将模型定义为管道。
...
# define model to evaluate
model = KNeighborsClassifier()
# scale, then fit model
pipeline = Pipeline(steps=[('s',StandardScaler()), ('m',model)])
一旦定义好了,我们就可以在整个训练数据集中使用它。
...
# fit the model
pipeline.fit(X, y)
一旦拟合,我们可以通过调用 predict_proba() 函数来使用它对新数据进行预测。这将返回每个类的概率。
我们可以检索正类的预测概率,模型的操作者可能会用它来解释预测。
例如:
...
# define a row of data
row = [...]
yhat = pipeline.predict_proba([row])
# get the probability for the positive class
result = yhat[0][1]
为了证明这一点,我们可以使用拟合模型对一些我们知道结果的情况下的标签进行一些预测。
下面列出了完整的示例。
# fit a model and make predictions for the on the credit card fraud dataset
from pandas import read_csv
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
# define the location of the dataset
full_path = 'creditcard.csv'
# load the dataset
X, y = load_dataset(full_path)
# define model to evaluate
model = KNeighborsClassifier()
# scale, then fit model
pipeline = Pipeline(steps=[('s',StandardScaler()), ('m',model)])
# fit the model
pipeline.fit(X, y)
# evaluate on some normal cases (known class 0)
print('Normal cases:')
data = [[0,-1.3598071336738,-0.0727811733098497,2.53634673796914,1.37815522427443,-0.338320769942518,0.462387777762292,0.239598554061257,0.0986979012610507,0.363786969611213,0.0907941719789316,-0.551599533260813,-0.617800855762348,-0.991389847235408,-0.311169353699879,1.46817697209427,-0.470400525259478,0.207971241929242,0.0257905801985591,0.403992960255733,0.251412098239705,-0.018306777944153,0.277837575558899,-0.110473910188767,0.0669280749146731,0.128539358273528,-0.189114843888824,0.133558376740387,-0.0210530534538215,149.62],
[0,1.19185711131486,0.26615071205963,0.16648011335321,0.448154078460911,0.0600176492822243,-0.0823608088155687,-0.0788029833323113,0.0851016549148104,-0.255425128109186,-0.166974414004614,1.61272666105479,1.06523531137287,0.48909501589608,-0.143772296441519,0.635558093258208,0.463917041022171,-0.114804663102346,-0.183361270123994,-0.145783041325259,-0.0690831352230203,-0.225775248033138,-0.638671952771851,0.101288021253234,-0.339846475529127,0.167170404418143,0.125894532368176,-0.00898309914322813,0.0147241691924927,2.69],
[1,-1.35835406159823,-1.34016307473609,1.77320934263119,0.379779593034328,-0.503198133318193,1.80049938079263,0.791460956450422,0.247675786588991,-1.51465432260583,0.207642865216696,0.624501459424895,0.066083685268831,0.717292731410831,-0.165945922763554,2.34586494901581,-2.89008319444231,1.10996937869599,-0.121359313195888,-2.26185709530414,0.524979725224404,0.247998153469754,0.771679401917229,0.909412262347719,-0.689280956490685,-0.327641833735251,-0.139096571514147,-0.0553527940384261,-0.0597518405929204,378.66]]
for row in data:
# make prediction
yhat = pipeline.predict_proba([row])
# get the probability for the positive class
result = yhat[0][1]
# summarize
print('>Predicted=%.3f (expected 0)' % (result))
# evaluate on some fraud cases (known class 1)
print('Fraud cases:')
data = [[406,-2.3122265423263,1.95199201064158,-1.60985073229769,3.9979055875468,-0.522187864667764,-1.42654531920595,-2.53738730624579,1.39165724829804,-2.77008927719433,-2.77227214465915,3.20203320709635,-2.89990738849473,-0.595221881324605,-4.28925378244217,0.389724120274487,-1.14074717980657,-2.83005567450437,-0.0168224681808257,0.416955705037907,0.126910559061474,0.517232370861764,-0.0350493686052974,-0.465211076182388,0.320198198514526,0.0445191674731724,0.177839798284401,0.261145002567677,-0.143275874698919,0],
[7519,1.23423504613468,3.0197404207034,-4.30459688479665,4.73279513041887,3.62420083055386,-1.35774566315358,1.71344498787235,-0.496358487073991,-1.28285782036322,-2.44746925511151,2.10134386504854,-4.6096283906446,1.46437762476188,-6.07933719308005,-0.339237372732577,2.58185095378146,6.73938438478335,3.04249317830411,-2.72185312222835,0.00906083639534526,-0.37906830709218,-0.704181032215427,-0.656804756348389,-1.63265295692929,1.48890144838237,0.566797273468934,-0.0100162234965625,0.146792734916988,1],
[7526,0.00843036489558254,4.13783683497998,-6.24069657194744,6.6757321631344,0.768307024571449,-3.35305954788994,-1.63173467271809,0.15461244822474,-2.79589246446281,-6.18789062970647,5.66439470857116,-9.85448482287037,-0.306166658250084,-10.6911962118171,-0.638498192673322,-2.04197379107768,-1.12905587703585,0.116452521226364,-1.93466573889727,0.488378221134715,0.36451420978479,-0.608057133838703,-0.539527941820093,0.128939982991813,1.48848121006868,0.50796267782385,0.735821636119662,0.513573740679437,1]]
for row in data:
# make prediction
yhat = pipeline.predict_proba([row])
# get the probability for the positive class
result = yhat[0][1]
# summarize
print('>Predicted=%.3f (expected 1)' % (result))
运行该示例首先在整个训练数据集上拟合模型。
然后使用拟合模型来预测从数据集文件中选择的正常病例的标签。我们可以看到所有的情况都是正确预测的。
然后将一些欺诈案例作为模型的输入,并对标签进行预测。正如我们可能希望的那样,大多数示例都是用默认阈值正确预测的。这突出了模型用户选择适当概率阈值的需要。
正常情况:
>Predicted=0.000 (expected 0)
>Predicted=0.000 (expected 0)
>Predicted=0.000 (expected 0)
Fraud cases:
>Predicted=1.000 (expected 1)
>Predicted=0.400 (expected 1)
>Predicted=1.000 (expected 1)
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
报纸
- 不平衡分类欠采样校准概率,2015。
蜜蜂
- 熊猫. read_csv API 。
- sklearn . metrics . precision _ recall _ curve API。
- 硬化.公制.无 API 。
- sklearn . metrics . make _ scorer API。
- 硬化. dummy . dummy class ification API。
资料组
摘要
在本教程中,您发现了如何为不平衡的信用卡欺诈类别数据集开发和评估模型。
具体来说,您了解到:
- 如何加载和探索数据集,并为数据准备和模型选择产生想法。
- 如何系统地评估一套具有强大测试工具的机器学习模型。
- 如何拟合最终模型,并使用它来预测特定案例的欺诈概率。
你有什么问题吗?
在下面的评论中提问,我会尽力回答。