在机器学习中,一般都会按照下面几个步骤:特征提取、数据预处理、特征选择、模型训练、检验优化。那么特征的选择就很关键了,一般模型最后效果的好坏往往都是跟特征的选择有关系的,因为模型本身的参数并没有太多优化的点,反而特征这边有时候多加一个或者少加一个,最终的结果都会差别很大。

在SparkMLlib中为我们提供了几种特征选择的方法,分别是VectorSlicerRFormulaChiSqSelector

下面就介绍下这三个方法的使用,强烈推荐有时间的把参考的文献都阅读下,会有所收获!

VectorSlicer

这个转换器可以支持用户自定义选择列,可以基于下标索引,也可以基于列名。

  • 如果是下标都可以使用setIndices方法
  • 如果是列名可以使用setNames方法。使用这个方法的时候,vector字段需要通过AttributeGroup设置每个向量元素的列名。

注意1:可以同时使用setInices和setName

object VectorSlicer {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("VectorSlicer-Test").setMaster("local[2]")
val sc = new SparkContext(conf)
sc.setLogLevel("WARN")
var sqlContext = new SQLContext(sc) val data = Array(Row(Vectors.dense(-2.0, 2.3, 0.0,1.0,2.0))) val defaultAttr = NumericAttribute.defaultAttr
val attrs = Array("f1", "f2", "f3","f4","f5").map(defaultAttr.withName)
val attrGroup = new AttributeGroup("userFeatures", attrs.asInstanceOf[Array[Attribute]]) val dataRDD = sc.parallelize(data)
val dataset = sqlContext.createDataFrame(dataRDD, StructType(Array(attrGroup.toStructField()))) val slicer = new VectorSlicer().setInputCol("userFeatures").setOutputCol("features") slicer.setIndices(Array(0)).setNames(Array("f2"))
val output = slicer.transform(dataset)
println(output.select("userFeatures", "features").first())
}
}

注意2:如果下标和索引重复,会报重复的错:

比如:

slicer.setIndices(Array(1)).setNames(Array("f2"))

那么会遇到报错

Exception in thread "main" java.lang.IllegalArgumentException: requirement failed: VectorSlicer requires indices and names to be disjoint sets of features, but they overlap. indices: [1]. names: [1:f2]
at scala.Predef$.require(Predef.scala:233)
at org.apache.spark.ml.feature.VectorSlicer.getSelectedFeatureIndices(VectorSlicer.scala:137)
at org.apache.spark.ml.feature.VectorSlicer.transform(VectorSlicer.scala:108)
at xingoo.mllib.VectorSlicer$.main(VectorSlicer.scala:35)
at xingoo.mllib.VectorSlicer.main(VectorSlicer.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

注意3:如果下标不存在

slicer.setIndices(Array(6))

如果数组越界也会报错

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
at org.apache.spark.ml.feature.VectorSlicer$$anonfun$3$$anonfun$apply$2.apply(VectorSlicer.scala:110)
at org.apache.spark.ml.feature.VectorSlicer$$anonfun$3$$anonfun$apply$2.apply(VectorSlicer.scala:110)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofInt.foreach(ArrayOps.scala:156)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
at scala.collection.mutable.ArrayOps$ofInt.map(ArrayOps.scala:156)
at org.apache.spark.ml.feature.VectorSlicer$$anonfun$3.apply(VectorSlicer.scala:110)
at org.apache.spark.ml.feature.VectorSlicer$$anonfun$3.apply(VectorSlicer.scala:109)
at scala.Option.map(Option.scala:145)
at org.apache.spark.ml.feature.VectorSlicer.transform(VectorSlicer.scala:109)
at xingoo.mllib.VectorSlicer$.main(VectorSlicer.scala:35)
at xingoo.mllib.VectorSlicer.main(VectorSlicer.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

注意4:如果名称不存在也会报错

Exception in thread "main" java.lang.IllegalArgumentException: requirement failed: getFeatureIndicesFromNames found no feature with name f8 in column StructField(userFeatures,org.apache.spark.mllib.linalg.VectorUDT@f71b0bce,false).
at scala.Predef$.require(Predef.scala:233)
at org.apache.spark.ml.util.MetadataUtils$$anonfun$getFeatureIndicesFromNames$2.apply(MetadataUtils.scala:89)
at org.apache.spark.ml.util.MetadataUtils$$anonfun$getFeatureIndicesFromNames$2.apply(MetadataUtils.scala:88)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:108)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
at scala.collection.mutable.ArrayOps$ofRef.map(ArrayOps.scala:108)
at org.apache.spark.ml.util.MetadataUtils$.getFeatureIndicesFromNames(MetadataUtils.scala:88)
at org.apache.spark.ml.feature.VectorSlicer.getSelectedFeatureIndices(VectorSlicer.scala:129)
at org.apache.spark.ml.feature.VectorSlicer.transform(VectorSlicer.scala:108)
at xingoo.mllib.VectorSlicer$.main(VectorSlicer.scala:35)
at xingoo.mllib.VectorSlicer.main(VectorSlicer.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

注意5:经过特征选择后,特征的顺序与索引和名称的顺序相同

RFormula

这个转换器可以帮助基于R模型,自动生成feature和label。比如说最常用的线性回归,在先用回归中,我们需要把一些离散化的变量变成哑变量,即转变成onehot编码,使之数值化,这个我之前的文章也介绍过,这里就不多说了。

如果不是用这个RFormula,我们可能需要经过几个步骤:

StringIndex...OneHotEncoder...

而且每个特征都要经过这样的变换,非常繁琐。有了RFormula,几乎可以一键把所有的特征问题解决。

id | coutry | hour | clicked | my_test

--- | --- | --- | ---

7| US|18|1.0|a

8|CA|12|0.0|b

9|NZ|15|0.0|a

然后我们只要写一个类似这样的公式clicked ~ country + hour + my_test,就代表clickedlabelcoutry、hour、my_test是三个特征

比如下面的代码:

object RFormulaTest {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("RFormula-Test").setMaster("local[2]")
val sc = new SparkContext(conf)
sc.setLogLevel("WARN")
var sqlContext = new SQLContext(sc) val dataset = sqlContext.createDataFrame(Seq(
(7, "US", 18, 1.0,"a"),
(8, "CA", 12, 0.0,"b"),
(9, "NZ", 15, 0.0,"a")
)).toDF("id", "country", "hour", "clicked","my_test")
val formula = new RFormula()
.setFormula("clicked ~ country + hour + my_test")
.setFeaturesCol("features")
.setLabelCol("label")
val output = formula.fit(dataset).transform(dataset)
output.show()
output.select("features", "label").show()
}
}

得到的结果

+---+-------+----+-------+-------+------------------+-----+
| id|country|hour|clicked|my_test| features|label|
+---+-------+----+-------+-------+------------------+-----+
| 7| US| 18| 1.0| a|[0.0,0.0,18.0,1.0]| 1.0|
| 8| CA| 12| 0.0| b|[1.0,0.0,12.0,0.0]| 0.0|
| 9| NZ| 15| 0.0| a|[0.0,1.0,15.0,1.0]| 0.0|
+---+-------+----+-------+-------+------------------+-----+ +------------------+-----+
| features|label|
+------------------+-----+
|[0.0,0.0,18.0,1.0]| 1.0|
|[1.0,0.0,12.0,0.0]| 0.0|
|[0.0,1.0,15.0,1.0]| 0.0|
+------------------+-----+

ChiSqSelector

这个选择器支持基于卡方检验的特征选择,卡方检验是一种计算变量独立性的检验手段。具体的可以参考维基百科,最终的结论就是卡方的值越大,就是我们越想要的特征。因此这个选择器就可以理解为,再计算卡方的值,最后按照这个值排序,选择我们想要的个数的特征。

代码也很简单

object ChiSqSelectorTest {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("ChiSqSelector-Test").setMaster("local[2]")
val sc = new SparkContext(conf)
sc.setLogLevel("WARN")
var sqlContext = new SQLContext(sc) 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 beanRDD = sc.parallelize(data).map(t3 => Bean(t3._1,t3._2,t3._3))
val df = sqlContext.createDataFrame(beanRDD) val selector = new ChiSqSelector()
.setNumTopFeatures(2)
.setFeaturesCol("features")
.setLabelCol("clicked")
.setOutputCol("selectedFeatures") val result = selector.fit(df).transform(df)
result.show()
} case class Bean(id:Double,features:org.apache.spark.mllib.linalg.Vector,clicked:Double){}
}

这样得到的结果:

+---+------------------+-------+----------------+
| id| features|clicked|selectedFeatures|
+---+------------------+-------+----------------+
|7.0|[0.0,0.0,18.0,1.0]| 1.0| [18.0,1.0]|
|8.0|[0.0,1.0,12.0,0.0]| 0.0| [12.0,0.0]|
|9.0|[1.0,0.0,15.0,0.1]| 0.0| [15.0,0.1]|
+---+------------------+-------+----------------+

总结

下面总结一下三种特征选择的使用场景:

  • VectorSilcer,这个选择器适合那种有很多特征,并且明确知道自己想要哪个特征的情况。比如你有一个很全的用户画像系统,每个人有成百上千个特征,但是你指向抽取用户对电影感兴趣相关的特征,因此只要手动选择一下就可以了。
  • RFormula,这个选择器适合在需要做OneHotEncoder的时候,可以一个简单的代码把所有的离散特征转化成数值化表示。
  • ChiSqSelector,卡方检验选择器适合在你有比较多的特征,但是不知道这些特征哪个有用,哪个没用,想要通过某种方式帮助你快速筛选特征,那么这个方法很适合。

以上的总结纯属个人看法,不代表官方做法,如果有其他的见解可以留言~ 多交流!

参考

1 Spark特征处理

2 Spark官方文档

3 如何优化逻辑回归

4 数据挖掘中的VI和WOE

5 Spark卡方选择器

6 卡方分布

7 皮尔逊卡方检验

8 卡方检验原理

推荐系统那点事 —— 基于Spark MLlib的特征选择的更多相关文章

  1. 基于Spark Mllib的文本分类

    基于Spark Mllib的文本分类 文本分类是一个典型的机器学习问题,其主要目标是通过对已有语料库文本数据训练得到分类模型,进而对新文本进行类别标签的预测.这在很多领域都有现实的应用场景,如新闻网站 ...

  2. 【spark】spark应用(分布式估算圆周率+基于Spark MLlib的贷款风险预测)

    注:本章不涉及spark和scala原理的探讨,详情见其他随笔 一.分布式估算圆周率 计算原理:假设正方形的面积S等于x²,而正方形的内切圆的面积C等于Pi×(x/2)²,因此圆面积与正方形面积之比C ...

  3. Spark 实践——基于 Spark MLlib 和 YFCC 100M 数据集的景点推荐系统

    1.前言 上接 YFCC 100M数据集分析笔记 和 使用百度地图api可视化聚类结果, 在对 YFCC 100M 聚类出的景点信息的基础上,使用 Spark MLlib 提供的 ALS 算法构建推荐 ...

  4. 基于Spark Mllib,SparkSQL的电影推荐系统

    本文测试的Spark版本是1.3.1 本文将在Spark集群上搭建一个简单的小型的电影推荐系统,以为之后的完整项目做铺垫和知识积累 整个系统的工作流程描述如下: 1.某电影网站拥有可观的电影资源和用户 ...

  5. 基于spark Mllib(ML)聚类实战

        写在前面的话:由于spark2.0.0之后ML中才包括LDA,GaussianMixture 模型,这里k-means用的是ML模块做测试,LDA,GaussianMixture 则用的是ML ...

  6. 基于Spark Mllib的Spark NLP库

    SparkNLP的官方文档 1>sbt引入: scala为2.11时 libraryDependencies += "com.johnsnowlabs.nlp" %% &qu ...

  7. 使用 Spark MLlib 做 K-means 聚类分析[转]

    原文地址:https://www.ibm.com/developerworks/cn/opensource/os-cn-spark-practice4/ 引言 提起机器学习 (Machine Lear ...

  8. Spark MLlib 之 StringIndexer、IndexToString使用说明以及源码剖析

    最近在用Spark MLlib进行特征处理时,对于StringIndexer和IndexToString遇到了点问题,查阅官方文档也没有解决疑惑.无奈之下翻看源码才明白其中一二...这就给大家娓娓道来 ...

  9. 基于Spark机器学习和实时流计算的智能推荐系统

    概要: 随着电子商务的高速发展和普及应用,个性化推荐的推荐系统已成为一个重要研究领域. 个性化推荐算法是推荐系统中最核心的技术,在很大程度上决定了电子商务推荐系统性能的优劣,决定着是否能够推荐用户真正 ...

随机推荐

  1. List集合数据太多进行分批,List的subList方法应用

    List<String> mStrings=new ArrayList<>(); //初始化 for (int i = 0; i < 1020; i++) { mStri ...

  2. lua中 table 元表中元方法的重构实现

    转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...

  3. TP框架 增删查

    TP框架添加数据到数据库1.使用数组方式添加造模型对象 2.使用AR方式 强类型语言存在的方式 3.使用自动收集表单添加 :只能用POST方式,提交数据一个操作方法实现两个逻辑:A显示页面B得到数据 ...

  4. linux JDK或JRE安装或配置

    1. 使用命令“java –version”如果显示如下内容则jdk已安装成功则无需后续操作. 2. 将解压后的文件“jdk-7u79-linux-x64.rpm ”上传到linux系统目录:/usr ...

  5. Notification的基本用法以及使用RemoteView实现自定义布局

    Notification的作用 Notification是一种全局效果的通知,在系统的通知栏中显示.既然作为通知,其基本作用有: 显示接收到短消息.即时信息等 显示客户端的推送(广告.优惠.新闻等) ...

  6. 阿里云 Centos7.3安装mysql5.7.18 rpm安装

    卸载MariaDB CentOS7默认安装MariaDB而不是MySQL,而且yum服务器上也移除了MySQL相关的软件包.因为MariaDB和MySQL可能会冲突,故先卸载MariaDB. 1.安装 ...

  7. 从RGB色转为灰度色算法

    一.基础  对于彩色转灰度,有一个很著名的心理学公式: Gray = R*0.299 + G*0.587 + B*0.114 二.整数算法 而实际应用时,希望避免低速的浮点运算,所以需要整数算法. 注 ...

  8. Spring+SpringMVC+MyBatis深入学习及搭建(八)——MyBatis查询缓存

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6956206.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(七)——My ...

  9. servlet与jsp

    Servlet生命周期 一.初始化阶段   当WEB客户第一次请求访问某个Servlet的时候,WEB容器将创建这个Servlet的实例.调用init()方法进行Servlet的初始化 一.响应客户请 ...

  10. 如何用phpcms将静态网页生成动态网页?

    在前两篇随笔中已经简单介绍了phpcms,那么现在让我们来看一下如何用phpcms将静态网页生成动态网页? 1.在templates文件夹下新建模板文件夹ceshi(名字可以自己随笔起) 2.在ces ...