不平衡分类是指类标签之间的示例分布不相等的预测任务。
大多数不平衡分类示例都集中于二元分类任务,但许多用于不平衡分类的工具和技术也直接支持多类分类问题。
在本教程中,您将了解如何对多类数据集使用不平衡分类工具。
完成本教程后,您将了解:
- 关于玻璃鉴定标准不平衡的多类预测问题。
- 如何使用 SMOTE 过采样进行不平衡的多类分类。
- 如何使用成本敏感学习进行不平衡的多类分类。
教程概述
本教程分为三个部分;他们是:
- 玻璃多类分类数据集
- 用于多类分类的 SMOTE 过采样
- 多类分类的成本敏感学习
玻璃多类分类数据集
在本教程中,我们将重点关注标准的不平衡多类分类问题,称为“玻璃识别”或简称为“玻璃”。
该数据集描述了玻璃的化学性质,并使用玻璃样品的化学性质将其分类为六类之一。该数据集于 1987 年归功于Vina Spiehler。
忽略样本标识号,有九个输入变量总结了玻璃数据集的属性;他们是:
- RI:折射率
- 钠:钠
- 镁:镁
- 铝:铝
- 硅:硅
- K:钾
- 钙:钙
- Ba: 钡
- 铁:铁
化学成分以相应氧化物的重量百分比来测量。
列出了七种类型的玻璃;他们是:
- 第一类:建筑窗户(经过浮动处理)
- 第二类:建筑窗户(非浮法处理)
- 第三类:车窗(浮法处理)
- 第四类:车窗(非浮法加工)
- 第五类:容器
- 第六类:餐具
- 第7类:头灯
浮法玻璃是指制造玻璃的工艺。
数据集中有 214 个观测值,每个类别的观测值数量不平衡。请注意,数据集中没有第 4 类(非浮动处理的车窗)的示例。
- 第一类:70个例子
- 第二类:76 个示例
- 第三类:17个例子
- 第四类:0 个示例
- 第五类:13个例子
- 第六类:9个例子
- 第七类:29个例子
尽管存在少数类别,但所有类别在此预测问题中都同等重要。
数据集可分为窗玻璃(1-4 类)和非窗玻璃(5-7 类)。窗玻璃示例 163 个,非窗玻璃示例 51 个。
- 窗玻璃:163 个示例
- 非窗玻璃:51 个示例
观察的另一个划分是浮法加工玻璃和非浮法加工玻璃(仅在窗玻璃的情况下)。这样划分就比较平衡了。
- 浮法玻璃:87 个示例
- 非浮法玻璃:76 个示例
您可以在此处了解有关该数据集的更多信息:
- 玻璃数据集 (glass.csv)
- Glass 数据集描述 (glass.names)
无需下载数据集;我们将自动下载它作为工作示例的一部分。
下面是前几行数据的示例。
1.52101,13.64,4.49,1.10,71.78,0.06,8.75,0.00,0.00,1
1.51761,13.89,3.60,1.36,72.73,0.48,7.83,0.00,0.00,1
1.51618,13.53,3.55,1.54,72.99,0.39,7.78,0.00,0.00,1
1.51766,13.21,3.69,1.29,72.61,0.57,8.22,0.00,0.00,1
1.51742,13.27,3.62,1.24,73.08,0.55,8.07,0.00,0.00,1
...
我们可以看到所有输入都是数字,最后一列中的目标变量是整数编码的类标签。
您可以在本教程中了解有关如何在项目中处理此数据集的更多信息:
- 使用玻璃识别数据集进行不平衡的多类分类
现在我们已经熟悉了玻璃多类分类数据集,让我们探讨如何使用标准的不平衡分类工具。
用于多类分类的 SMOTE 过采样
过采样是指复制或合成少数类的新示例,以便少数类中的示例数量更好地类似于或匹配多数类中的示例数量。
也许最广泛使用的合成新示例的方法称为合成少数过采样技术,简称 SMOTE。Nitesh Chawla 等人描述了该技术。在他们 2002 年的论文中,该技术被命名为“ SMOTE:合成少数过采样技术”。
您可以在教程中了解有关 SMOTE 的更多信息:
Balanced-learn 库提供了我们可以使用的SMOTE实现,它与流行的 scikit-learn 库兼容。
首先,必须安装库。我们可以使用pip安装它,如下所示:
sudo pip install imbalanced-learn
我们可以通过打印已安装库的版本来确认安装成功:
# check version number
import imblearn
print(imblearn.__version__)
运行示例将打印已安装库的版本号;例如:
0.6.2
在应用 SMOTE 之前,我们首先加载数据集并确认每个类中的示例数量。
# load and summarize the dataset
from pandas import read_csv
from collections import Counter
from matplotlib import pyplot
from sklearn.preprocessing import LabelEncoder
# define the dataset location
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/glass.csv'
# load the csv file as a data frame
df = read_csv(url, header=None)
data = df.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
# label encode the target variable
y = LabelEncoder().fit_transform(y)
# summarize distribution
counter = Counter(y)
for k,v in counter.items():
per = v / len(y) * 100
print('Class=%d, n=%d (%.3f%%)' % (k, v, per))
# plot the distribution
pyplot.bar(counter.keys(), counter.values())
pyplot.show()
运行示例首先下载数据集并将其分成训练集和测试集。
然后报告每个类中的行数,确认某些类(例如 0 和 1)比其他类(例如 3 和 4(少于 15))拥有更多的示例(超过 70 个)。
Class=0, n=70 (32.710%)
Class=1, n=76 (35.514%)
Class=2, n=17 (7.944%)
Class=3, n=13 (6.075%)
Class=4, n=9 (4.206%)
Class=5, n=29 (13.551%)
创建条形图,提供数据集类别细分的可视化。
这更清楚地表明,类别 0 和类别 1 比类别 2、类别 3、类别 4 和类别 5 拥有更多的示例。
默认情况下,SMOTE 将对所有类进行过采样,使其具有与示例最多的类相同的示例数量。
在这种情况下,类别 1 的示例最多,为 76 个,因此,SMOTE 将对所有类别进行过采样,使其具有 76 个示例。
下面列出了使用 SMOTE 对玻璃数据集进行过采样的完整示例。
# example of oversampling a multi-class classification dataset
from pandas import read_csv
from imblearn.over_sampling import SMOTE
from collections import Counter
from matplotlib import pyplot
from sklearn.preprocessing import LabelEncoder
# define the dataset location
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/glass.csv'
# load the csv file as a data frame
df = read_csv(url, header=None)
data = df.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
# label encode the target variable
y = LabelEncoder().fit_transform(y)
# transform the dataset
oversample = SMOTE()
X, y = oversample.fit_resample(X, y)
# summarize distribution
counter = Counter(y)
for k,v in counter.items():
per = v / len(y) * 100
print('Class=%d, n=%d (%.3f%%)' % (k, v, per))
# plot the distribution
pyplot.bar(counter.keys(), counter.values())
pyplot.show()
运行该示例首先加载数据集并向其应用 SMOTE。
然后报告每个类中示例的分布,确认每个类现在有 76 个示例,正如我们预期的那样。
Class=0, n=76 (16.667%)
Class=1, n=76 (16.667%)
Class=2, n=76 (16.667%)
Class=3, n=76 (16.667%)
Class=4, n=76 (16.667%)
Class=5, n=76 (16.667%)
还创建了类别分布的条形图,提供了强烈的视觉指示,表明所有类别现在都具有相同数量的示例。
例如,我们可以过采样到 0 类和 1 类中的 100 个示例,以及其余类中的 200 个示例。这可以通过创建一个字典来实现,该字典将类标签映射到每个类中所需示例的数量,然后通过SMOTE 类的“ sampling_strategy ”参数指定这一点。
...
# transform the dataset
strategy = {0:100, 1:100, 2:200, 3:200, 4:200, 5:200}
oversample = SMOTE(sampling_strategy=strategy)
X, y = oversample.fit_resample(X, y)
将它们结合在一起,下面列出了为 SMOTE 使用自定义过采样策略的完整示例。
# example of oversampling a multi-class classification dataset with a custom strategy
from pandas import read_csv
from imblearn.over_sampling import SMOTE
from collections import Counter
from matplotlib import pyplot
from sklearn.preprocessing import LabelEncoder
# define the dataset location
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/glass.csv'
# load the csv file as a data frame
df = read_csv(url, header=None)
data = df.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
# label encode the target variable
y = LabelEncoder().fit_transform(y)
# transform the dataset
strategy = {0:100, 1:100, 2:200, 3:200, 4:200, 5:200}
oversample = SMOTE(sampling_strategy=strategy)
X, y = oversample.fit_resample(X, y)
# summarize distribution
counter = Counter(y)
for k,v in counter.items():
per = v / len(y) * 100
print('Class=%d, n=%d (%.3f%%)' % (k, v, per))
# plot the distribution
pyplot.bar(counter.keys(), counter.values())
pyplot.show()
运行该示例会创建所需的采样并总结对数据集的影响,从而确认预期结果。
Class=0, n=100 (10.000%)
Class=1, n=100 (10.000%)
Class=2, n=200 (20.000%)
Class=3, n=200 (20.000%)
Class=4, n=200 (20.000%)
Class=5, n=200 (20.000%)
注意:您可能会看到一些警告,出于本示例的目的,可以安全地忽略这些警告,例如:
UserWarning: After over-sampling, the number of samples (200) in class 5 will be larger than the number of samples in the majority class (class #1 -> 76)
还会创建类别分布的条形图,以确认数据采样后指定的类别分布。
您可以在本教程中看到在管道中正确使用 SMOTE 的示例:
多类分类的成本敏感学习
大多数机器学习算法假设所有类都有相同数量的示例。
在多类不平衡分类中情况并非如此。可以修改算法来改变学习的执行方式,以偏向那些训练数据集中示例较少的类。这通常称为成本敏感学习。
有关成本敏感型学习的更多信息,请参阅教程:
- 不平衡分类的成本敏感学习
scikit-learn 中的RandomForestClassifier 类通过“ class_weight ”参数支持成本敏感的学习。
默认情况下,随机森林类为每个类分配相同的权重。
我们可以评估玻璃不平衡多类分类数据集上默认随机森林类权重的分类准确性。
下面列出了完整的示例。
# baseline model and test harness for the glass identification dataset
from numpy import mean
from numpy import std
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.ensemble import RandomForestClassifier
# 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]
# label encode the target variable to have the classes 0 and 1
y = LabelEncoder().fit_transform(y)
return X, y
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=5, 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 = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/glass.csv'
# load the dataset
X, y = load_dataset(full_path)
# define the reference model
model = RandomForestClassifier(n_estimators=1000)
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))
运行该示例,使用重复分层 k 折交叉验证,在玻璃数据集上使用 1,000 棵树来评估默认随机森林算法。
运行结束时报告平均值和标准差分类准确度。
注意:由于算法或评估过程的随机性或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次并比较平均结果。
在这种情况下,我们可以看到默认模型的分类准确率约为 79.6%。
Mean Accuracy: 0.796 (0.047)
我们可以将“ class_weight ”参数指定为“ balanced ”值,它将自动计算类权重,以确保每个类在模型训练期间获得相等的权重。
...
# define the model
model = RandomForestClassifier(n_estimators=1000, class_weight='balanced')
将它们结合在一起,下面列出了完整的示例。
# cost sensitive random forest with default class weights
from numpy import mean
from numpy import std
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.ensemble import RandomForestClassifier
# 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]
# label encode the target variable
y = LabelEncoder().fit_transform(y)
return X, y
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=5, 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 = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/glass.csv'
# load the dataset
X, y = load_dataset(full_path)
# define the model
model = RandomForestClassifier(n_estimators=1000, class_weight='balanced')
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))
运行该示例会报告玻璃数据集上成本敏感版本的随机森林的平均值和标准差分类准确性。
注意:由于算法或评估过程的随机性或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次并比较平均结果。
在本例中,我们可以看到,默认模型的分类准确度比成本不敏感版本的算法有所提高,分类准确度为 80.2%,而分类准确度为 79.6%。
Mean Accuracy: 0.802 (0.044)
“ class_weight ”参数采用映射到类权重值的类标签字典。
我们可以使用它来指定自定义权重,例如具有许多示例的类 0 和 1.0 的默认权重,以及其他类的双类权重 2.0。
...
# define the model
weights = {0:1.0, 1:1.0, 2:2.0, 3:2.0, 4:2.0, 5:2.0}
model = RandomForestClassifier(n_estimators=1000, class_weight=weights)
将它们结合在一起,下面列出了在玻璃多类不平衡分类问题上使用自定义类权重进行成本敏感学习的完整示例。
# cost sensitive random forest with custom class weightings
from numpy import mean
from numpy import std
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.ensemble import RandomForestClassifier
# 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]
# label encode the target variable
y = LabelEncoder().fit_transform(y)
return X, y
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=5, 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 = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/glass.csv'
# load the dataset
X, y = load_dataset(full_path)
# define the model
weights = {0:1.0, 1:1.0, 2:2.0, 3:2.0, 4:2.0, 5:2.0}
model = RandomForestClassifier(n_estimators=1000, class_weight=weights)
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))
运行该示例会报告成本敏感版本的随机森林在具有自定义权重的玻璃数据集上的平均值和标准差分类准确性。
注意:由于算法或评估过程的随机性或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次并比较平均结果。
在这种情况下,我们可以看到,我们的准确率进一步从均衡类权重的 80.2% 提高到了更偏向类权重的 80.8%。
Mean Accuracy: 0.808 (0.059)