标签归档:Machine learning

特征选择

特征选择是机器学习中的一个重要步骤,通过特征选择挑选出对预测起重要作用的变量,既可以减少数据的维度,也可以减少计算的消耗,同时也有助于我们对自己的数据的理解。有许多方法都可以应用到特征选择,比如大家常用的LASSO。我在用R做数据分析的时候,看到过这个帖子,进而了解了很多算法,所以对这个帖子进行了翻译,方便自己复习,也方便大家学习。

原文参考: https://www.machinelearningplus.com/machine-learning/feature-selection/

  1. Boruta
  2. Variable Importance from Machine Learning Algorithms
  3. Lasso Regression
  4. Step wise Forward and Backward Selection
  5. Relative Importance from Linear Regression
  6. Recursive Feature Elimination (RFE)
  7. Genetic Algorithm
  8. Simulated Annealing
  9. Information Value and Weights of Evidence
  10. DALEX Package

Introduction

真实的数据中,有些变量可能仅是噪声,并没有多少重要意义。

这类变量占用内存空间、消耗计算资源,我们最好去除这类变量,特别是在很大的数据集中。

有时候,我们有一个具有业务意义的变量,但不确定它是否确实有助于预测Y。还有一个事实:在一个机器学习算法中有用的特征(例如决策树) 可能其他算法中(例如回归模型)不被选用或者低估。

同时,有些变量单独预测Y的性能不好,但与其他预测变量/特征组合的情况下却非常显著。比如说有些变量与预测指标的相关性很低,但在其他变量参与的情况下,它可以帮助解释某些其他变量无法解释的模式/现象。

在这些情况下,很难决定包含还是去掉这些变量/特征。

这里讨论的策略可以解决这些问题,同时可以帮助理解对于一个模型而言,变量的重要性与否importance,以及对模型有多少贡献。

重要的一点是,我们最希望使用的变量是既具有业务意义同时也有重要性方面的指标。

我们这里导入Glaucoma 数据集,此数据集的目标是通过63个不同的生理测量指标来预测青光眼的与否。

# Load Packages and prepare dataset
library(TH.data)
library(caret)
data("GlaucomaM", package = "TH.data")
trainData <- GlaucomaM
head(trainData)

Glaucoma数据集

1. Boruta

Boruta 是一个基于随机森林对特征进行排名和筛选的算法。

Boruta 的优势是它可以明确的决定变量是否重要,并且帮助选择那些统计显著的变量。此外,可以通过调整p值和maxRuns来调整算法的严格性。

maxRuns是算法运行的次数,该参数值越大,会选择出更多的变量。默认是100.

在决定特征是否重要的过程中,一些特征可能会被标为Tentative。有时增加maxRuns或许可以解决特征的暂定行Tentativeness。

以TH.data的Glaucoma数据集为例子。

# install.packages('Boruta')
library(Boruta)

Boruta的方程用的公式同其他预测模型类似,响应变量response在左边,预测变量在右边。

如果预测变量处输入的是点,则意味着所有变量都会纳入评价中。

doTrace参数控制打印到终端的数目。该值越高,打印的log信息越多。为了节省空间,这里设置0,你可以设置1和2试试。

结果输出在boruta_output.

# Perform Boruta search
boruta_output <- Boruta(Class ~ ., data=na.omit(trainData), doTrace=0)  

看看里面包含哪些内容

names(boruta_output)
1. ‘finalDecision’
2. ‘ImpHistory’
3. ‘pValue’
4. ‘maxRuns’
5. ‘light’
6. ‘mcAdj’
7. ‘timeTaken’
8. ‘roughfixed’
9. ‘call’
10. ‘impSource’
# 得到显著的或者潜在的变量
boruta_signif <- getSelectedAttributes(boruta_output, withTentative = TRUE)
print(boruta_signif)  
 [1] "as"   "ean"  "abrg" "abrs" "abrn" "abri" "hic"  "mhcg" "mhcn" "mhci"
[11] "phcg" "phcn" "phci" "hvc"  "vbss" "vbsn" "vbsi" "vasg" "vass" "vasi"
[21] "vbrg" "vbrs" "vbrn" "vbri" "varg" "vart" "vars" "varn" "vari" "mdn" 
[31] "tmg"  "tmt"  "tms"  "tmn"  "tmi"  "rnf"  "mdic" "emd" 

如果不确定潜在的变量是否保留,可以对boruta_output进行TentativeRoughFix

# Do a tentative rough fix
roughFixMod <- TentativeRoughFix(boruta_output)
boruta_signif <- getSelectedAttributes(roughFixMod)
print(boruta_signif)
 [1] "abrg" "abrs" "abrn" "abri" "hic"  "mhcg" "mhcn" "mhci" "phcg" "phcn"
[11] "phci" "hvc"  "vbsn" "vbsi" "vasg" "vbrg" "vbrs" "vbrn" "vbri" "varg"
[21] "vart" "vars" "varn" "vari" "tmg"  "tms"  "tmi"  "rnf"  "mdic" "emd" 

这样Boruta就代表我们来决定是否保留Tentative变量。查看这些变量的重要性分值。

# Variable Importance Scores
imps <- attStats(roughFixMod)
imps2 = imps[imps$decision != 'Rejected', c('meanImp', 'decision')]
head(imps2[order(-imps2$meanImp), ])  # descending sort
meanImp decision
varg 10.279747 Confirmed
vari 10.245936 Confirmed
tmi 9.067300 Confirmed
vars 8.690654 Confirmed
hic 8.324252 Confirmed
varn 7.327045 Confirmed

用画图的形式来展示变量的重要性

# Plot variable importance
plot(boruta_output, cex.axis=.7, las=2, xlab="", main="Variable Importance")  

Variable Importance Boruta

这幅图展示了每个变量的重要性。

绿色表示的是筛选出的变量confirmed,红色则是需要提出的变量,蓝色不是真实的特征,待变的是ShadowMaxShadowMin,用来决定变量重要性与否。

2. Variable Importance from Machine Learning Algorithms

另外一种特征选择的方法是将各种ML算法最常用的变量视为最重要的变量。

根据机器学习算法学习X与Y之间关系的方式,不同的机器学习算法选出不同的变量(大多数是重叠的),但赋予的权重并不相同。

例如,在基于树的算法(如“ rpart”)中被证明有用的变量在基于回归的模型中可能没那么有用。 因此,不同算法没必要用相同的变量。

那么对于一个特定的机器学算法,如何找到变量的重要性?

  1. 利用caret 包中的train()一个特定的模型
  2. 使用 varImp() 来决定变量的重要性。

可能尝试多种算法,以了解各种算法之间的重要变量。

# 训练rpart模型,计算变量重要性
library(caret)
set.seed(100)
rPartMod <- train(Class ~ ., data=trainData, method="rpart")
rpartImp <- varImp(rPartMod)
print(rpartImp)
rpart variable importance

  only 20 most important variables shown (out of 62)

     Overall
varg  100.00
vari   93.19
vars   85.20
varn   76.86
tmi    72.31
vbss    0.00
eai     0.00
tmg     0.00
tmt     0.00
vbst    0.00
vasg    0.00
at      0.00
abrg    0.00
vbsg    0.00
eag     0.00
phcs    0.00
abrs    0.00
mdic    0.00
abrt    0.00
ean     0.00

rpart仅使用了63个功能中的5个,如果仔细观察,这5个变量位于boruta选择的前6个中。

让我们再做一件事:正则随机森林(Regularized Random Forest ,RRF)算法中的变量重要性。

# Train an RRF model and compute variable importance.
set.seed(100)
rrfMod <- train(Class ~ ., data=trainData, method="RRF")
rrfImp <- varImp(rrfMod, scale=F)
rrfImp
RRF variable importance

  only 20 most important variables shown (out of 62)

     Overall
varg 24.0013
vari 18.5349
vars  6.0483
tmi   3.8699
hic   3.3926
mhci  3.1856
mhcg  3.0383
mv    2.1570
hvc   2.1357
phci  1.8830
vasg  1.8570
tms   1.5705
phcn  1.4475
phct  1.4473
vass  1.3097
tmt   1.2485
phcg  1.1992
mdn   1.1737
tmg   1.0988
abrs  0.9537
plot(rrfImp, top = 20, main='Variable Importance')

正则随机森林中的变量重要性。

最前面的重要变量也于Boruta选择的吻合

其它的在train()中可以用的算法也可以用varImp计算变量的重要性:包括

ada, AdaBag, AdaBoost.M1, adaboost, bagEarth, bagEarthGCV, bagFDA, bagFDAGCV, bartMachine, blasso, BstLm, bstSm, C5.0, C5.0Cost, C5.0Rules, C5.0Tree, cforest, chaid, ctree, ctree2, cubist, deepboost, earth, enet, evtree, extraTrees, fda, gamboost, gbm_h2o, gbm, gcvEarth, glmnet_h2o, glmnet, glmStepAIC, J48, JRip, lars, lars2, lasso, LMT, LogitBoost, M5, M5Rules, msaenet, nodeHarvest, OneR, ordinalNet, ORFlog, ORFpls, ORFridge, ORFsvm, pam, parRF, PART, penalized, PenalizedLDA, qrf, ranger, Rborist, relaxo, rf, rFerns, rfRules, rotationForest, rotationForestCp, rpart, rpart1SE, rpart2, rpartCost, rpartScore, rqlasso, rqnc, RRF, RRFglobal, sdwd, smda, sparseLDA, spikeslab, wsrf, xgbLinear, xgbTree.

3. Lasso Regression

最小绝对收缩和选择算子(LASSO,Least Absolute Shrinkage and Selection Operator)回归是一种用L1范数惩罚的正则化方法。

从根本上来说,要增加权重(系数值)会带来成本。 它被称为L1正则化,增加的成本,与权重系数的绝对值成正比。

结果,在收缩系数的过程中,最终将某些不需要的特征的系数全部减小到零。 也就是说,它删除了不重要的变量。

LASSO回归也可以被视为有效的变量选择技术。

library(glmnet)
trainData <- read.csv('https://raw.githubusercontent.com/selva86/datasets/master/GlaucomaM.csv')

x <- as.matrix(trainData[,-63]) # all X vars
y <- as.double(as.matrix(ifelse(trainData[, 63]=='normal', 0, 1))) # Only Class

# Fit the LASSO model (Lasso: Alpha = 1)
set.seed(100)
cv.lasso <- cv.glmnet(x, y, family='binomial', alpha=1, parallel=TRUE, standardize=TRUE, type.measure='auc')

# Results
plot(cv.lasso)

LASSO变量重要性

X轴是log之后的lambda,当是2的时候,lambda的真实值是100。

图的最上面显示了模型使用了多少个变量,相对应的红点Y值则是在这些变量使用的情况下,模型可以达到多少的AUC。

可以看到两条垂直虚线,左边的第一个指向具有最小均方误差的lambda。 右边的一个表示在1个标准偏差内偏差最大的变量的数量。

最优的lambda值存储在cv.lasso $ lambda.min中。

# plot(cv.lasso$glmnet.fit, xvar="lambda", label=TRUE)
cat('Min Lambda: ', cv.lasso$lambda.min, '\n 1Sd Lambda: ', cv.lasso$lambda.1se)
df_coef <- round(as.matrix(coef(cv.lasso, s=cv.lasso$lambda.min)), 2)

# See all contributing variables
df_coef[df_coef[, 1] != 0, ]
Min Lambda:  0.01166507 
 1Sd Lambda:  0.2513163

Min Lambda:  0.01166507 
1Sd Lambda:  0.2513163

(Intercept) 3.65
at         -0.17
as         -2.05
eat        -0.53
mhci        6.22
phcs       -0.83
phci        6.03
hvc        -4.15
vass       -23.72
vbrn       -0.26
vars       -25.86
mdt        -2.34
mds         0.5
mdn         0.83
mdi         0.3
tmg         0.01
tms         3.02
tmi         2.65
mv          4.94

上面的输出显示了LASSO认为重要的变量。绝对值越高表示该变量越重要。

继续阅读

分类模型的性能评估

最常用的就是灵敏度和特异性,不过还有其他的,比如阴性预测值(negative predictive value, NPV)。


通常,先画一个ROC曲线,计算曲线下面积。ROC上的每个点是特定阈值下,分类的sensitivity和specificity,没多点连起来组成ROC,曲线下面积就是AUC。面积越大越好,如果AUC是1,说明模型能够完全区分要预测的类别。

如果不是1,就要考虑阈值取哪里比较好,这里就涉及到Youden index。Youden index 其实就是为了找到使得sensitivity和specificity之和最大max(sensitivities+specificities)的阈值。

另外就是考虑其他指标来评估分类模型的性能:specificity, sensitivity, accuracy, npv, ppv, precision, recall, tpr, fpr, tnr, fnr, fdr。这些指标可谓琳琅满目,不过这之间有重复的,如下,都是基于tn(真阴), tp(真阳), fn(假阴), fp(假阳)的个数进行计算。

 

预测

P

N

实际

P

TP

FN

N

FP

TN

因为经常用到,就罗列了一下。

具体描述 公式 别名
tn True negative count真阴数
tp True positive count真阳数
fn False negative count假阴数
fp False positive count假阳数
specificity Specificity特异度 tn / (tn + fp) tnr
sensitivity Sensitivity灵敏度 tp / (tp + fn) recall, tpr
accuracy Accuracy正确率 (tp + tn) / N
npv Negative Predictive Value阴性预测值 tn / (tn + fn)
ppv Positive Predictive Value阳性预测值 tp / (tp + fp) precision
precision Precision精准率 tp / (tp + fp) ppv
recall Recall正确率 tp / (tp + fn) sensitivity, tpr
tpr True Positive Rate真阳性率 tp / (tp + fn) sensitivity, recall
fpr False Positive Rate假阳性率 fp / (tn + fp) 1-specificity
tnr True Negative Rate真阴性率 tn / (tn + fp) specificity
fnr False Negative Rate假阴性率 fn / (tp + fn) 1-sensitivity
fdr False Discovery Rate伪发现率 fp / (tp + fp) 1-ppv

对Autoencoder(自编码器)的理解

通常数据的维度太大,可视化很难,也不利用模型的学习。有时候拿到数据做个PCA或者tSNE,就是把维度缩小到2维(当然也可以3维),便于看数据之间的关系。在机器学习中,Autoencoder也是一种降维的方式, Autoencoder输入层的神经元的数目和输出层的神经元的数目必须,而且要保证输出的结果尽最大可能和输入的结果一致。

图片来自网络

如上图所示,维度由大到小是decode过程,输出的结果可以从中间层经过encode得到,那么中间层保留了输入层的信息(因为输出层的结果从中间层得到),那么中间层的数据结果,就是降维后的结果,可以拿来做其他事情。 网络的复杂程度根据样本数设计。

无监督的聚类,便可以从中间层开始;数据的学习也可以从中间层开始。当输入层是多组学数据时,中间层便是融合后的结果。