Spark MLlib 特征工程工具详解
以下是 Spark MLlib 中核心特征工程工具的全面解析,涵盖文本处理、类别编码、特征转换和降维技术:
1. 文本处理工具
Tokenizer(分词器)
- 功能:将文本拆分为单词(按空格分割)
- 应用场景:自然语言处理基础步骤
- 示例:
import org.apache.spark.ml.feature.Tokenizer val tokenizer = new Tokenizer() .setInputCol("text") .setOutputCol("words")
StopWordsRemover(停用词过滤器)
- 功能:移除常见无意义词汇(the, is, and等)
- 支持语言:内置30+语言停用词表
- 示例:
import org.apache.spark.ml.feature.StopWordsRemover val remover = new StopWordsRemover() .setInputCol("words") .setOutputCol("filtered") .setCaseSensitive(false)
2. 类别特征编码
StringIndexer(字符串索引器)
- 功能:将字符串标签转换为数值索引
- 排序规则:按出现频率降序(0=最高频)
- 示例:
import org.apache.spark.ml.feature.StringIndexer val indexer = new StringIndexer() .setInputCol("category") .setOutputCol("categoryIndex") .setHandleInvalid("keep") // 处理未见过的值
OneHotEncoder(独热编码器)
- 功能:将数值索引转换为稀疏向量
- 优势:避免类别间数值关系误导模型
- 示例:
import org.apache.spark.ml.feature.OneHotEncoder val encoder = new OneHotEncoder() .setInputCol("categoryIndex") .setOutputCol("categoryVec") .setDropLast(true) // 避免共线性
3. 特征转换工具
VectorAssembler(向量汇编器)
- 功能:合并多列为特征向量
- 必备步骤:所有模型的输入要求
- 示例:
import org.apache.spark.ml.feature.VectorAssembler val assembler = new VectorAssembler() .setInputCols(Array("age", "income", "categoryVec")) .setOutputCol("features")
StandardScaler(标准化器)
- 功能:将特征缩放至标准正态分布(均值=0,方差=1)
- 适用算法:SVM、线性回归、K-Means等
- 示例:
import org.apache.spark.ml.feature.StandardScaler val scaler = new StandardScaler() .setInputCol("features") .setOutputCol("scaledFeatures") .setWithStd(true) .setWithMean(true) // 注意:稠密向量才支持
4. 降维与特征提取
PCA(Principal Component Analysis 主成分分析)
- 功能:线性降维,保留最大方差方向
- 应用场景:高维数据可视化、去除噪声
- 示例:
import org.apache.spark.ml.feature.PCA val pca = new PCA() .setInputCol("scaledFeatures") .setOutputCol("pcaFeatures") .setK(3) // 保留主成分数量
特征工程全流程示例
import org.apache.spark.ml.Pipeline
// 构建完整特征处理流程
val pipeline = new Pipeline().setStages(Array(
new Tokenizer().setInputCol("text").setOutputCol("words"),
new StopWordsRemover().setInputCol("words").setOutputCol("filtered"),
new StringIndexer().setInputCol("category").setOutputCol("categoryIndex"),
new OneHotEncoder().setInputCol("categoryIndex").setOutputCol("categoryVec"),
new VectorAssembler().setInputCols(Array("age", "filtered", "categoryVec")).setOutputCol("rawFeatures"),
new StandardScaler().setInputCol("rawFeatures").setOutputCol("features"),
new PCA().setInputCol("features").setOutputCol("pcaFeatures").setK(10)
))
// 训练特征转换模型
val featureModel = pipeline.fit(rawData)
// 应用特征工程
val processedData = featureModel.transform(rawData)
高级特征工程技巧
-
自定义转换器:
import org.apache.spark.ml.UnaryTransformer class CustomScaler extends UnaryTransformer[...] { override def createTransformFunc: Vector => Vector = v => Vectors.dense(v.toArray.map(_ * 2)) }
-
特征交互:
import org.apache.spark.ml.feature.Interaction val interaction = new Interaction() .setInputCols(Array("features1", "features2")) .setOutputCol("interactedFeatures")
-
分箱离散化:
import org.apache.spark.ml.feature.Bucketizer val splits = Array(Double.NegativeInfinity, 0.0, 18.0, 35.0, Double.PositiveInfinity) val bucketizer = new Bucketizer() .setInputCol("age") .setOutputCol("ageBucket") .setSplits(splits)
特征工程最佳实践
-
处理未见过的类别:
.setHandleInvalid("keep") // 保留为额外类别 .setHandleInvalid("skip") // 过滤掉异常行
-
内存优化:
// 对于高基数类别特征 new OneHotEncoder() .setDropLast(false) // 保持所有类别 .setOutputColType("sparse") // 使用稀疏向量
-
流式处理支持:
val streamingFeatures = featureModel .transform(streamingDF) .writeStream.format("memory") .queryName("processedStream") .start()
-
特征重要性回溯:
val rfModel = new RandomForestClassifier().fit(featureData) rfModel.featureImportances.toArray .zip(featureModel.stages.last.asInstanceOf[VectorAssembler].getInputCols) .sortBy(-_._1) .foreach(println)
关键原则:特征工程应与模型训练保持一致性,使用Pipeline确保训练/预测时处理逻辑完全相同,避免数据泄露。