Scala 如何在ApacheSpark中编码分类特性
我有一组数据,我想根据这些数据创建一个分类模型。每行具有以下形式:Scala 如何在ApacheSpark中编码分类特性,scala,apache-spark,apache-spark-mllib,apache-spark-1.2,Scala,Apache Spark,Apache Spark Mllib,Apache Spark 1.2,我有一组数据,我想根据这些数据创建一个分类模型。每行具有以下形式: user1,class1,product1 user1,class1,product2 user1,class1,product5 user2,class1,product2 user2,class1,product5 user3,class2,product1 大约有100万用户、2个类和100万种产品。我接下来要做的是创建稀疏向量(MLlib已经支持了这一点),但为了应用该函数,我必须首先创建密集向量(0)。换句话说,我必
user1,class1,product1
user1,class1,product2
user1,class1,product5
user2,class1,product2
user2,class1,product5
user3,class2,product1
大约有100万用户、2个类和100万种产品。我接下来要做的是创建稀疏向量(MLlib已经支持了这一点),但为了应用该函数,我必须首先创建密集向量(0)。换句话说,我必须对数据进行二值化。最简单(或最优雅)的方法是什么
鉴于我是MLlib方面的新手,我可以请您提供一个具体的例子吗?我正在使用MLLIB1.2
编辑
我最终得到了下面的一段代码,但它的速度非常慢。。。如果我只能使用MLLIB1.2,还有其他想法吗
val data = test11.map(x=> ((x(0) , x(1)) , x(2))).groupByKey().map(x=> (x._1 , x._2.toArray)).map{x=>
var lt : Array[Double] = new Array[Double](test12.size)
val id = x._1._1
val cl = x._1._2
val dt = x._2
var i = -1
test12.foreach{y => i += 1; lt(i) = if(dt contains y) 1.0 else 0.0}
val vs = Vectors.dense(lt)
(id , cl , vs)
}
你可以使用spark.ml
您首先使用:
OneHotEncoder.categories(rdd, categoricalFields)
其中,categoricalField
是您的RDD
包含分类数据的索引序列categories
,给定一个数据集和作为分类变量的列索引,返回一个结构,对于每个字段,该结构描述数据集中存在的值。该映射将用作编码方法的输入:
OneHotEncoder.encode(rdd, categories)
它返回向量化的
RDD[Array[T]]
如果使用内置的OneHotEncoder
不是一个选项,并且您只有一个变量实现poor man的one hot或多或少是简单的。首先让我们创建一个示例数据:
import org.apache.spark.mllib.linalg.{Vector, Vectors}
val rdd = sc.parallelize(List(
Array("user1", "class1", "product1"),
Array("user1", "class1", "product2"),
Array("user1", "class1", "product5"),
Array("user2", "class1", "product2"),
Array("user2", "class1", "product5"),
Array("user3", "class2", "product1")))
接下来,我们必须创建从值到索引的映射:
val prodMap = sc.broadcast(rdd.map(_(2)).distinct.zipWithIndex.collectAsMap)
和一个简单的编码函数:
def encodeProducts(products: Iterable[String]): Vector = {
Vectors.sparse(
prodMap.value.size,
products.map(product => (prodMap.value(product).toInt, 1.0)).toSeq
)
}
最后,我们可以将其应用于数据集:
rdd.map(x => ((x(0), x(1)), x(2))).groupByKey.mapValues(encodeProducts)
在上面进行扩展以处理多个变量是相对容易的
编辑:
如果产品数量过多,使广播变得有用,则可以使用join
。首先,我们可以创建从产品到索引的类似映射,但将其保留为RDD:
import org.apache.spark.HashPartitioner
val nPartitions = ???
val prodMapRDD = rdd
.map(_(2))
.distinct
.zipWithIndex
.partitionBy(new HashPartitioner(nPartitions))
.cache
val nProducts = prodMapRDD.count // Should be < Int.MaxValue
最后,我们可以将两者结合起来
def indicesToVec(n: Int)(indices: Iterable[Long]): Vector = {
Vectors.sparse(n, indices.map(x => (x.toInt, 1.0)).toSeq)
}
pairs.join(prodMapRDD)
.values
.groupByKey
.mapValues(indicesToVec(nProducts.toInt))
最初的问题要求提供从非类别中指定类别特征的最简单方法 在Spark ML中,您可以使用VectorIndexer的setMaxCategories方法,在该方法中,您不必指定字段,而是将基数小于或等于给定数字(在本例中为2)的字段理解为分类字段
详情请参阅 可以给你一个例子,你希望密集向量输出对于输入是什么样子的吗?你到底想做什么样的分类?i、 e.如果
userX
和classY
那么它很可能是productZ
或其他什么东西?不太可能。我将使用二进制分类,其中,userX
是值的稀疏向量,classY
是相应的类。@user706838userX
是实际对象还是非常简单的字符串?在分类过程中,您是否以任何方式考虑了产品
?我认为,我在这里提出的问题对于以前从事过机器学习的人来说是非常直接的。我只是想找出在MLlib中实现这一点的最佳方法。请看一下scikit learn中的类似示例:在MLlib 1.2中不可用:-)是的,不是吗,不幸的是我无法更新。。。请查看我的编辑。对于一般解决方案,这在1.4.+1中似乎都不可用。您是否有其他不使用广播的解决方案
?我使用与您类似的解决方案,但有时这不起作用,因为prodMap
太大,无法广播。@例如,它要贵得多,但您可以使用联接。有关详细信息,请参见编辑。
def indicesToVec(n: Int)(indices: Iterable[Long]): Vector = {
Vectors.sparse(n, indices.map(x => (x.toInt, 1.0)).toSeq)
}
pairs.join(prodMapRDD)
.values
.groupByKey
.mapValues(indicesToVec(nProducts.toInt))
val indexer = new VectorIndexer()
.setInputCol("features")
.setOutputCol("indexed")
.setMaxCategories(10)