Spark ML中的特徵選擇演算法
特徵選擇(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]| +---+------------------+-------+----------------+