本文概述
就特征数量而言, 高维数据如今在机器学习问题中变得越来越普遍。要从这些海量数据中提取有用的信息, 你必须使用统计技术来减少噪声或冗余数据。这是因为你经常不需要使用所有可用特征来训练模型。通过仅提供那些不相关且非冗余的特征, 可以改善模型。这就是特征选择起着重要作用的地方。它不仅有助于更快地训练模型, 还降低了模型的复杂性, 使其更易于解释, 并提高了准确性, 精度或召回率, 无论性能指标如何。
在本教程中, 你将解决以下概念:
首先, 你将了解有关特征选择的更多信息:你将看到何时可以使用它, 以及可以使用哪些类型的方法来选择模型中最重要的特征!
然后, 你将了解Boruta算法, 你将看到如何使用它通过比较原始属性的重要性与随机获得的重要性, 使用其排列副本估算的重要性来自上而下搜索相关特征, 以及逐步消除不相关的特征。
你还将简要查看将在其上执行特征选择的数据集。你将看到如何借助Amelia软件包轻松估算缺失值。
最后, 你将了解有关Boruta软件包的更多信息, 可用于运行该算法。
特征选择
通常, 每当你要减少数据的维数时, 都会遇到诸如主成分分析, 奇异值分解等方法。因此很自然地问为什么根本需要其他特征选择方法。这些技术的用处在于它们是特征选择的无监督方式:例如PCA, 它使用数据中的差异来查找组件。这些技术没有考虑要素值与目标类或值之间的信息。而且, 与此类方法相关的某些假设(例如正态性)在开始应用它们之前需要进行某种转换。这些约束并不适用于所有类型的数据。
通常有三种类型的特征选择方法:
- 过滤方法:过滤方法通常用作预处理步骤。特征的选择与任何机器学习算法无关。取而代之的是, 在各种统计测试中根据其得分选择特征, 以将其与结果变量相关联。一些常见的过滤方法是相关度量(皮尔逊, 斯皮尔曼, 距离), 卡方检验, 方差分析, 费舍尔分数等。
- 包装器方法:在包装器方法中, 你尝试使用特征的子集并使用它们来训练模型。根据从先前模型得出的推论, 你决定在子集中添加或删除要素。前向选择, 向后消除是包装方法的一些示例。
- 嵌入式方法:这些是具有自己的内置特征选择方法的算法。 LASSO回归就是这样一个例子。
在本教程中, 你将使用R中的包装方法之一, 该方法可通过名为Boruta的软件包在R中轻松使用。
Boruta算法
Boruta算法是围绕随机森林分类算法构建的包装器。它试图捕获关于结果变量的所有重要, 有趣的特征。
- 首先, 它复制数据集, 并随机排列每列中的值。这些值称为阴影特征。 *然后, 它在数据集上训练分类器, 例如随机森林分类器。这样, 你可以确保对数据集的每个特征都可以了解其重要性-通过平均降低准确度或平均降低杂质。分数越高, 越好或更重要。
- 然后, 算法会检查你的每个真实特征是否具有更高的重要性。也就是说, 该要素的Z分数是否高于其阴影要素的最大Z分数, 而不是其最佳阴影要素的Z分数。如果他们这样做, 它将记录在向量中。这些被称为热门。接下来, 它将继续进行另一个迭代。在一组预定义的迭代之后, 你将获得这些匹配的表格。请记住:Z分数是与数据点均值的标准偏差数, 有关更多信息, 请单击此处。
- 在每次迭代中, 该算法都会比较特征和原始特征的改组副本的Z分数, 以查看后者的性能是否优于前者。如果是这样, 该算法会将特征标记为重要。本质上, 该算法试图通过与随机混洗的副本进行比较来验证特征的重要性, 从而提高了鲁棒性。这是通过使用二项分布简单地比较特征与阴影特征表现更好的次数来完成的。
- 如果某个特征在15次迭代中没有被记录为匹配, 你可以拒绝该特征并将其从原始矩阵中删除。经过一定次数的迭代后-或者如果所有特征都已被确认或拒绝-你将停止。
R中的Boruta算法
让我们在最常用的数据集之一中使用Boruta算法:银行营销数据。此数据代表了葡萄牙银行业机构的直接营销活动(电话)。分类目标是预测客户是否将订阅定期存款。
提示:不要忘了在这里查看不同特征的详细说明。
read_file <- read.csv('./bank_bank.csv', header=TRUE, sep=';', stringsAsFactors = F) #read csv into a dataframe
str(read_file)
## 'data.frame': 4521 obs. of 17 variables:
## $ age : int 30 33 35 30 59 35 36 39 41 43 ...
## $ job : chr "unemployed" "services" "management" "management" ...
## $ marital : chr "married" "married" "single" "married" ...
## $ education: chr "primary" "secondary" "tertiary" "tertiary" ...
## $ default : chr "no" "no" "no" "no" ...
## $ balance : int 1787 4789 1350 1476 0 747 307 147 221 -88 ...
## $ housing : chr "no" "yes" "yes" "yes" ...
## $ loan : chr "no" "yes" "no" "yes" ...
## $ contact : chr "cellular" "cellular" "cellular" "unknown" ...
## $ day : int 19 11 16 3 5 23 14 6 14 17 ...
## $ month : chr "oct" "may" "apr" "jun" ...
## $ duration : int 79 220 185 199 226 141 341 151 57 313 ...
## $ campaign : int 1 1 1 4 1 2 1 2 2 1 ...
## $ pdays : int -1 339 330 -1 -1 176 330 -1 -1 147 ...
## $ previous : int 0 4 1 0 0 3 2 0 0 2 ...
## $ poutcome : chr "unknown" "failure" "failure" "unknown" ...
## $ y : chr "no" "no" "no" "no" ...
让我们使用summary()函数来汇总数据集中不同特征的通用描述统计信息。
summary(read_file)
## age job marital education
## Min. :19.00 Length:4521 Length:4521 Length:4521
## 1st Qu.:33.00 Class :character Class :character Class :character
## Median :39.00 Mode :character Mode :character Mode :character
## Mean :41.17
## 3rd Qu.:49.00
## Max. :87.00
## default balance housing loan
## Length:4521 Min. :-3313 Length:4521 Length:4521
## Class :character 1st Qu.: 69 Class :character Class :character
## Mode :character Median : 444 Mode :character Mode :character
## Mean : 1423
## 3rd Qu.: 1480
## Max. :71188
## contact day month duration
## Length:4521 Min. : 1.00 Length:4521 Min. : 4
## Class :character 1st Qu.: 9.00 Class :character 1st Qu.: 104
## Mode :character Median :16.00 Mode :character Median : 185
## Mean :15.92 Mean : 264
## 3rd Qu.:21.00 3rd Qu.: 329
## Max. :31.00 Max. :3025
## campaign pdays previous poutcome
## Min. : 1.000 Min. : -1.00 Min. : 0.0000 Length:4521
## 1st Qu.: 1.000 1st Qu.: -1.00 1st Qu.: 0.0000 Class :character
## Median : 2.000 Median : -1.00 Median : 0.0000 Mode :character
## Mean : 2.794 Mean : 39.77 Mean : 0.5426
## 3rd Qu.: 3.000 3rd Qu.: -1.00 3rd Qu.: 0.0000
## Max. :50.000 Max. :871.00 Max. :25.0000
## y
## Length:4521
## Class :character
## Mode :character
##
##
##
summary()函数提供了对连续特征(例如均值, 中位数, 分位数等)的集中趋势的度量。如果数据集中有任何分类特征, 你还将看到这些特征的类和模式。
现在, 让我们将分类特征转换为因子数据类型:
convert <- c(2:5, 7:9, 11, 16:17)
read_file[, convert] <- data.frame(apply(read_file[convert], 2, as.factor))
str(read_file)
## 'data.frame': 4521 obs. of 17 variables:
## $ age : int 30 33 35 30 59 35 36 39 41 43 ...
## $ job : Factor w/ 12 levels "admin.", "blue-collar", ..: 11 8 5 5 2 5 7 10 3 8 ...
## $ marital : Factor w/ 3 levels "divorced", "married", ..: 2 2 3 2 2 3 2 2 2 2 ...
## $ education: Factor w/ 4 levels "primary", "secondary", ..: 1 2 3 3 2 3 3 2 3 1 ...
## $ default : Factor w/ 2 levels "no", "yes": 1 1 1 1 1 1 1 1 1 1 ...
## $ balance : int 1787 4789 1350 1476 0 747 307 147 221 -88 ...
## $ housing : Factor w/ 2 levels "no", "yes": 1 2 2 2 2 1 2 2 2 2 ...
## $ loan : Factor w/ 2 levels "no", "yes": 1 2 1 2 1 1 1 1 1 2 ...
## $ contact : Factor w/ 3 levels "cellular", "telephone", ..: 1 1 1 3 3 1 1 1 3 1 ...
## $ day : int 19 11 16 3 5 23 14 6 14 17 ...
## $ month : Factor w/ 12 levels "apr", "aug", "dec", ..: 11 9 1 7 9 4 9 9 9 1 ...
## $ duration : int 79 220 185 199 226 141 341 151 57 313 ...
## $ campaign : int 1 1 1 4 1 2 1 2 2 1 ...
## $ pdays : int -1 339 330 -1 -1 176 330 -1 -1 147 ...
## $ previous : int 0 4 1 0 0 3 2 0 0 2 ...
## $ poutcome : Factor w/ 4 levels "failure", "other", ..: 4 1 1 4 4 1 2 4 4 1 ...
## $ y : Factor w/ 2 levels "no", "yes": 1 1 1 1 1 1 1 1 1 1 ...
由于对数据点进行了混洗以创建阴影特征, 并且为每个阴影点计算了Z分数, 因此在使用boruta包之前, 请先处理丢失的值或空白值, 这一点很重要, 否则会引发错误。
不幸的是, 这个数据集都没有。但是, 出于教育目的, 你将在数据中引入一些NA。
让我们使用prodNA()函数为数据集中的缺失值播种。你可以通过安装missForest软件包来访问此特征。
请记住, 如果需要, 可以使用install.packages()安装缺少的软件包!
library(missForest)
# Generate 5% missing values at random
bank.mis <- prodNA(read_file, noNA = 0.05)
你可以再次在新的数据帧上调用summary()函数来获取估算的NA的数量, 但让我们多一点创意吧!
让我们使用以下ggplot2代码可视化数据中的缺失:
library(reshape2)
library(ggplot2)
library(dplyr)
ggplot_missing <- function(x){
x %>%
is.na %>%
melt %>%
ggplot(data = ., aes(x = Var2, y = Var1)) +
geom_raster(aes(fill = value)) +
scale_fill_grey(name = "", labels = c("Present", "Missing")) +
theme_minimal() +
theme(axis.text.x = element_text(angle=45, vjust=0.5)) +
labs(x = "Variables in Dataset", y = "Rows / observations")
}
ggplot_missing(bank.mis)
图中的白线从视觉上向你显示了每个特征中都缺失值的种子, 但是你看到编写ggplot_missing函数会付出多少痛苦?好了, 你将在下一节中使用的R中的Amelia软件包提供了一行替代图来绘制相似的图形:
library(Amelia)
missmap(bank.mis)
自己尝试一下!
用Amelia估算缺失值
现在, 你可以通过多种方式来估算缺失值, 例如使用均值, 媒体或众数进行插补(用于分类特征), 但让我们使用另一个特征强大的软件包来估算缺失值Amelia。
Amelia提取m个引导程序样本, 并将EMB(或带有引导的期望最大化)算法应用于每个样本。均值和方差的m个估计将有所不同。最后, 将第一组估计值用于通过回归估算第一组缺失值, 然后将第二组估计值用于第二组, 依此类推。多重插补有助于减少偏差并提高效率。此外, 还可以使用多核CPU的并行插补特征启用它。
它具有3个重要参数:
- m:要创建的估算数据集的数量。
- idvars:保留所有ID变量和你不想插入的其他变量。
- 规范:在此处保留名义变量。
library(Amelia)
amelia_bank <- amelia(bank.mis, m=3, parallel = "multicore", noms=c('job', 'marital', 'education', 'default', 'housing', 'loan', 'contact', 'month', 'poutcome', 'y'))
## -- Imputation 1 --
##
## 1 2 3 4 5 6
##
## -- Imputation 2 --
##
## 1 2 3 4 5 6
##
## -- Imputation 3 --
##
## 1 2 3 4 5
要访问估算的数据帧, 可以使用以下子设置:
amelia_bank$imputations[[1]]
要将估算的数据集导出到csv文件, 请使用:
write.amelia(amelia_bank, file.stem = "imputed_bank_data_set")
Boruta R套件
现在让我们在估算的数据集之一上使用Boruta算法。你可以使用Boruta软件包来执行此操作:
library(Boruta)
set.seed(111)
boruta.bank_train <- Boruta(y~., data = amelia_bank$imputations[[1]], doTrace = 2)
print(boruta.bank_train)
## Boruta performed 99 iterations in 18.97234 mins.
## 10 attributes confirmed important: age, contact, day, duration, ## housing and 5 more;
## 3 attributes confirmed unimportant: education, job, marital;
## 3 tentative attributes left: balance, campaign, default;
Boruta呼吁数据集中要素的重要性。他们中的许多人已经被列为重要和不重要的人, 但是你会发现有些人被分配为暂定类别。
但是, 这是什么意思?
暂定特征的重要性非常接近其最佳阴影特征, 以至于Boruta无法对默认数量的”随机森林”运行做出所需的信心。
那你该怎么办?
如果保留了暂定特征, 则可以考虑增加maxRuns参数。但是, 请注意, 你还可以提供mtry和ntree参数的值, 这些值将传递给randomForest()函数。第一个允许你指定在每个分割处随机抽取为候选变量的变量数, 而第二个则用于指定要生长的树木数。通过指定这些参数, Random Forest分类器将以最小的袋外误差实现收敛。
请记住, “袋外误差”是衡量分类器预测误差的一种估计, 该估计器使用引导聚合对用于训练的数据样本进行子采样。它是仅使用引导样本中没有X的树对每个训练集样本X的平均预测误差。
或者, 你也可以将doTrace参数设置为1或2, 这使你可以获得过程进度的报告。
boruta包还包含一个TentativeRoughFix()函数, 该函数可通过简单比较中位数特征Z分数与最重要的阴影特征的中位数Z分数来填充缺失的决策:
#take a call on tentative features
boruta.bank <- TentativeRoughFix(boruta.bank_train)
print(boruta.bank)
## Boruta performed 99 iterations in 18.97234 mins.
## Tentatives roughfixed over the last 99 iterations.
## 12 attributes confirmed important: age, campaign, contact, day, ## default and 7 more;
## 4 attributes confirmed unimportant: balance, education, job, ## marital;
Boruta现在已经完成了自己的工作:已经成功地将每个特征归类为重要或不重要。
现在, 你可以通过调用plot(boruta.bank)来绘制boruta变量重要性图表。但是, x轴标签将为水平。这不会真的很整洁。
这就是为什么要将要素标签垂直添加到x轴的原因, 就像下面的代码块一样:
plot(boruta.bank, xlab = "", xaxt = "n")
lz<-lapply(1:ncol(boruta.bank$ImpHistory), function(i)
boruta.bank$ImpHistory[is.finite(boruta.bank$ImpHistory[, i]), i])
names(lz) <- colnames(boruta.bank$ImpHistory)
Labels <- sort(sapply(lz, median))
axis(side = 1, las=2, labels = names(Labels), at = 1:ncol(boruta.bank$ImpHistory), cex.axis = 0.7)
y轴标签的重要性表示混洗后的数据集中每个要素的Z得分。
蓝色的方框图对应于阴影特征的最小, 平均和最大Z评分, 而红色和绿色的方框图分别代表拒绝和确认的特征的Z评分。如你所见, 红色方框图的Z分数低于阴影特征的最大Z分数, 这恰恰是将它们放在不重要的类别中的原因。
你可以通过键入以下内容来确认特征的重要性:
getSelectedAttributes(boruta.bank, withTentative = F)
## [1] "age" "default" "housing" "loan" "contact" "day"
## [7] "month" "duration" "campaign" "pdays" "previous" "poutcome"
bank_df <- attStats(boruta.bank)
print(bank_df)
## meanImp medianImp minImp maxImp normHits decision
## age 11.4236197 11.3760979 8.4250222 15.518420 1.00000000 Confirmed
## job 0.0741753 0.3002281 -1.7651336 1.566687 0.01010101 Rejected
## marital 1.8891283 2.0043568 -1.0276720 4.804499 0.22222222 Rejected
## education 1.5969540 1.6188117 -1.6836346 4.629572 0.28282828 Rejected
## default 2.3721979 2.3472820 -0.1434933 5.044653 0.50505051 Confirmed
## balance 2.3349682 2.3214378 -0.8098151 5.567993 0.51515152 Rejected
## housing 8.4147808 8.4384240 4.7392059 10.404609 1.00000000 Confirmed
## loan 4.1872186 4.2797591 2.0325838 6.263155 0.87878788 Confirmed
## contact 18.9482180 18.9757719 16.0937657 22.121461 1.00000000 Confirmed
## day 9.5645192 9.5828766 6.1842233 13.495442 1.00000000 Confirmed
## month 24.1475736 24.2067940 20.0621966 27.200679 1.00000000 Confirmed
## duration 71.5232213 71.1785055 64.3941499 78.249830 1.00000000 Confirmed
## campaign 2.6221456 2.6188180 -0.4144493 4.941482 0.65656566 Confirmed
## pdays 26.5650528 26.7123730 23.7902945 29.067476 1.00000000 Confirmed
## previous 20.9569022 20.9703991 18.7273357 23.117672 1.00000000 Confirmed
## poutcome 28.5166889 28.4885934 25.9855974 31.527154 1.00000000 Confirmed
你可以轻松地验证结果, 因为如果你仔细阅读, 数据描述中已经提到了特征持续时间的最高重要性(请单击此处)!
总结
瞧!你只需输入几行代码, 便已成功过滤掉数据集中最重要的特征。这样, 你就可以减少数据中的噪音, 这对于任何分类器将标签分配给观察值而言都是非常有益的。在这些重要特征上训练模型肯定会改善模型的性能, 这首先就是选择特征的关键!
如果要检查用于制作本教程的资源, 请检查以下内容:
- 软件包AMELIA:一种用于丢失数据的程序;詹姆斯·洪纳克, 加里·金和马修·布莱克威尔
- 使用Boruta软件包进行特征选择; Miron B. Kursa, Witold R.Rudnicki
- RDocumentation
- UCI机器学习存储库
评论前必须登录!
注册