特征选择(Feature Selection)指的是在特征向量中选择出那些“优秀”的特征,组成新的、更“精简”的特征向量的过程。它在高维数据分析中十分常用,可以剔除掉“冗余”和“无关”的特征,提升学习器的性能。
一、VectorSlicer
VectorSlicer 是一个转换器,它接受一个特征向量并输出一个带有原始特征子数组的新特征向量。它对于从向量列中提取特征很有用。
VectorSlicer 接受具有指定索引的向量列,然后输出一个新向量列,其值通过这些索引选择。有两种类型的索引,整数索引表示向量中的索引,setIndices()。
表示向量中特征名称的字符串索引,setNames()。这要求向量列具有 AttributeGroup,因为实现匹配 Attribute 的 name 字段。
整数和字符串的规范都是可以接受的。此外,可以同时使用整数索引和字符串名称。必须至少选择一项功能。不允许有重复的特征,因此选定的索引和名称之间不能有重叠。请注意,如果选择了要素名称,则如果遇到空输入属性将引发异常。
%spark // 特征选择 —— —— VectorSlicer // 这个类接受一个特征向量并输出一个带有原始特征子数组的新特征向量。 // 可以使用索引 (setIndices()) 或名称 (setNames()) 指定特征子集。 必须至少选择一项功能。 不允许有重复的特征,因此选定的索引和名称之间不能有重叠。 // 输出向量将首先使用所选索引对特征进行排序(按给定顺序),然后是所选名称(按给定顺序)。 import java.util.Arrays import org.apache.spark.ml.attribute.{Attribute, AttributeGroup, NumericAttribute} import org.apache.spark.ml.feature.VectorSlicer import org.apache.spark.ml.linalg.Vectors import org.apache.spark.sql.{Row, SparkSession} import org.apache.spark.sql.types.StructType val data = Arrays.asList( Row(Vectors.sparse(3, Seq((0, -2.0), (1, 2.3)))), Row(Vectors.dense(-2.0, 2.3, 0.0)) ) // 具有可选摘要统计信息的数字属性。 val defaultAttr = NumericAttribute.defaultAttr // withName方法:用新名称复制。 val attrs = Array("f1", "f2", "f3").map(defaultAttr.withName) val attrGroup = new AttributeGroup("userFeatures", attrs.asInstanceOf[Array[Attribute]]) val dataset = spark.createDataFrame(data, StructType(Array(attrGroup.toStructField()))) val slicer = new VectorSlicer() .setInputCol("userFeatures") .setOutputCol("features") // 从向量列中选择特征的索引数组。 名称不能重叠。 默认值:Empty Array .setIndices(Array(1)) // 一组特征名称,用于从向量列中选择特征。 这些名称必须由 ML org.apache.spark.ml.attribute.Attributes 指定。 不能与索引重叠。 默认值:Empty Array .setNames(Array("f3")) // 等价于 slicer.setIndices(Array(1, 2)), // 等价于 slicer.setNames(Array("f2", "f3")) val output = slicer.transform(dataset) output.show(false) 输出: +--------------------+-------------+ |userFeatures |features | +--------------------+-------------+ |(3,[0,1],[-2.0,2.3])|(2,[0],[2.3])| |[-2.0,2.3,0.0] |[2.3,0.0] | +--------------------+-------------+
二、RFormula
RFormula 选择由 R 模型公式指定的列。 目前,我们支持有限的 R 运算符子集,包括“~”、“.”、“:”、“+”和“-”。 基本运算符有:
- ~ 单独的目标和条款
- + concat 术语,“+ 0”表示删除截距
- - 删除一个词,“-1”表示删除截距
- : 交互(数值乘法,或二值化分类值)
- . 除目标外的所有列
- * 因子交叉,包括它们之间的术语和相互作用
- ^ 因子交叉到指定程度
假设a和b是双列,我们用下面的简单例子来说明RFormula的效果:
- y ~ a + b 表示模型 y ~ w0 + w1 * a + w2 * b 其中 w0 是截距,w1, w2 是系数。
- y ~ a + b + a:b - 1 表示模型 y ~ w1 * a + w2 * b + w3 * a * b 其中 w1, w2, w3 是系数。
- y ~ a * b 表示模型 y ~ w0 + w1 * a + w2 * b + w3 * a * b 其中 w0 是截距,w1、w2、w3 是系数
- y ~ (a + b)^2 表示模型 y ~ w0 + w1 * a + w2 * b + w3 * a * b 其中 w0 是截距,w1、w2、w3 是系数
RFormula 生成特征向量列和标签的双精度列或字符串列。就像在 R 中使用公式进行线性回归一样,字符串输入列将被one-Hot编码,而数字列将被转换为双精度值。
如果标签列是字符串类型,它会首先用 StringIndexer 转换为 double。如果 DataFrame 中不存在标签列,则将从公式中指定的响应变量创建输出标签列。
%spark // 特征选择 —— —— RFormula // RFormula 选择由 R 模型公式指定的列。 import org.apache.spark.ml.feature.RFormula val dataset = spark.createDataFrame(Seq( (7, "US", 18, 1.0), (8, "CA", 12, 0.0), (9, "NZ", 15, 0.0) )).toDF("id", "country", "hour", "clicked") val formula = new RFormula() // R 公式参数,该公式以字符串形式提供。 .setFormula("clicked ~ country + hour") .setFeaturesCol("features") .setLabelCol("label") // 强制索引标签是数字还是字符串类型。 通常我们只在标签是字符串类型时才索引标签。 如果该公式被分类算法使用,我们可以通过将此参数设置为 true 来强制索引标签,即使它是数字类型。 默认值:false .setForceIndexLabel(false) // 用于如何对 StringIndexer 使用的字符串 FEATURE 列的类别进行排序的参数。 编码字符串时,排序后的最后一个类别被删除。 支持的选项:“frequencyDesc”、“frequencyAsc”、“alphabetDesc”、“alphabetAsc”。 默认值为“frequencyDesc”。 当 ordering 设置为 'alphabetDesc' 时,RFormula 在编码字符串时会丢弃与 R 相同的类别。 .setStringIndexerOrderType("frequencyDesc") val output = formula.fit(dataset).transform(dataset) output.select("features", "label").show() 输出: +--------------+-----+ | features|label| +--------------+-----+ |[0.0,0.0,18.0]| 1.0| |[1.0,0.0,12.0]| 0.0| |[0.0,1.0,15.0]| 0.0| +--------------+-----+
三、ChiSqSelector
卡方特征选择,它选择分类特征以用于预测分类标签。 选择器支持不同的选择方法:numTopFeatures、percentile、fpr、fdr、fwe。
- numTopFeatures :根据卡方检验选择固定数量的顶级特征。
- percentile:百分位数类似,但选择所有特征的一小部分而不是固定数字。
- fpr :选择 p 值低于阈值的所有特征,从而控制选择的误报率。
- fdr :选择错误发现率低于阈值的所有特征。
- fwe : 选择 p 值低于阈值的所有特征。 阈值按 1/numFeatures 缩放,从而控制全族选择错误率。 默认情况下,选择方法为 numTopFeatures,默认顶部特征数设置为 50。
%spark // 特征选择 —— —— ChiSqSelector // ChiSqSelector 代表卡方特征选择。它对具有分类特征的标记数据进行操作。 // ChiSqSelector 使用卡方独立性检验来决定选择哪些特征列 import org.apache.spark.ml.feature.ChiSqSelector import org.apache.spark.ml.linalg.Vectors val data = Seq( (7, Vectors.dense(0.0, 0.0, 18.0, 1.0), 1.0), (8, Vectors.dense(0.0, 1.0, 12.0, 0.0), 0.0), (9, Vectors.dense(1.0, 0.0, 15.0, 0.1), 0.0) ) val df = spark.createDataset(data).toDF("id", "features", "clicked") val selector = new ChiSqSelector() // ChisqSelector 的选择器类型。 支持的选项:“numTopFeatures”(默认)、“percentile”、“fpr”、“fdr”、“fwe”。 .setSelectorType("numTopFeatures") // 选择器将选择的特征数,按 p 值升序排列。 如果特征的数量小于 numTopFeatures,那么这将选择所有特征。 仅在 selectorType = "numTopFeatures" 时适用。 numTopFeatures 的默认值为 50。 .setNumTopFeatures(1) .setFeaturesCol("features") .setLabelCol("clicked") .setOutputCol("selectedFeatures") // 预期错误发现率的上限。 仅在 selectorType = "fdr" 时适用。 默认值为 0.05。 // .setFdr(0.05) // 选择器将选择的特征的百分比,按统计值降序排列。 仅在 selectorType = "percentile" 时适用。 默认值为 0.1。 // .setPercentile(0.1) // 要保留的特征的最高 p 值。 仅在 selectorType = "fpr" 时适用。 默认值为 0.05。 // .setFpr(0.05) // 预期家庭错误率的上限。 仅在 selectorType = "fwe" 时适用。 默认值为 0.05。 // .setFwe(0.05) val result = selector.fit(df).transform(df) println(s"ChiSqSelector output with top ${selector.getNumTopFeatures} features selected") result.show() 输出: ChiSqSelector output with top 1 features selected +---+------------------+-------+----------------+ | id| features|clicked|selectedFeatures| +---+------------------+-------+----------------+ | 7|[0.0,0.0,18.0,1.0]| 1.0| [18.0]| | 8|[0.0,1.0,12.0,0.0]| 0.0| [12.0]| | 9|[1.0,0.0,15.0,0.1]| 0.0| [15.0]| +---+------------------+-------+----------------+