我在阅读《Spark 权威指南》时,遇到了 MLlib 章节中的一段代码,代码如下:
var df = spark.read.json("/data/simple-ml") df.orderBy("value2").show()import org.apache.spark.ml.feature.RFormula// 无法理解此公式的解释eval supervised = new RFormula().setFormula("lab ~ . + color:value1 + color:value2")val fittedRF = supervised.fit(df)val preparedDF = fittedRF.transform(df) preparedDF.show()
其中 /data/simple-ml 包含一个 JSON 文件,内容如下(例如):
“lab”:”good”,”color”:”green”,”value1″:1,”value2″:14.386294994851129″lab”:”bad”,”color”:”blue”,”value1″:8,”value2″:14.386294994851129″lab”:”bad”,”color”:”blue”,”value1″:12,”value2″:14.386294994851129″lab”:”good”,”color”:”green”,”value1″:15,”value2″:38.9718713375581
你可以在 https://github.com/databricks/Spark-The-Definitive-Guide/blob/master/data/simple-ml/part-r-00000-f5c243b9-a015-4a3b-a4a8-eca00f80f04c.json 找到完整的数据集,上述代码会产生以下输出:
[green,good,1,14.386294994851129,(10,[0,2,3,4,7],[1.0,1.0,14.386294994851129,1.0,14.386294994851129]),0.0]
[blue,bad,8,14.386294994851129,(10,[2,3,6,9],[8.0,14.386294994851129,8.0,14.386294994851129]),1.0]
[blue,bad,12,14.386294994851129,(10,[2,3,6,9],[12.0,14.386294994851129,12.0,14.386294994851129]),1.0]
[green,good,15,38.97187133755819,(10,[0,2,3,4,7],[1.0,15.0,38.97187133755819,15.0,38.97187133755819]),0.0]
现在我无法理解它是如何计算第五列(加粗标记)的值的。
回答:
第五列是 Spark 中表示稀疏向量的结构。它有三个组成部分:
- 向量长度 – 在这种情况下,所有向量都是10个元素的长度
- 保存非零元素索引的索引数组
- 非零值的值数组
因此
(10,[0,2,3,4,7],[1.0,1.0,14.386294994851129,1.0,14.386294994851129])
代表以下长度为10的稀疏向量(将第i个值放置在位置i):
0 2 3 4 7[1.0, 0, 1.0, 14.386294994851129, 1.0, 0, 0, 14.386294994851129, 0, 0]
(显示了非零元素的位置)
该向量的各个组成部分是什么?根据文档:
RFormula 生成一个特征的向量列和一个标签的双精度或字符串列。就像在 R 中用于线性回归的公式一样,字符串输入列将被独热编码,数值列将被转换为双精度。如果标签列是字符串类型,它将首先通过
StringIndexer
转换为双精度。如果 DataFrame 中不存在标签列,输出标签列将从公式中指定的响应变量创建。
lab ~ . + color:value1 + color:value2
是一种来自 R 语言的特殊语法。它描述了一个模型,该模型将lab
的值回归到所有其他特征上,加上两个交互(乘积)项。你可以通过打印fittedRF
并查看它包含的ResolvedRFormula
实例来查看所有特征的列表:
scala> println(fittedRF)RFormulaModel( ResolvedRFormula( label=lab, terms=[color,value1,value2,{color,value1},{color,value2}], hasIntercept=true )) (uid=rFormula_0847e597e817)
为了便于阅读,我已将输出分行并缩进。因此. + color:value1 + color:value2
展开为[color,value1,value2,{color,value1},{color,value2}]
。其中,color
是一个分类特征,它通过以下映射被独热编码为一组指示特征:
- green变为
[1, 0]
- blue变为
[0, 0]
- red变为
[0, 1]
虽然你有三个类别,但只有两个用于编码。在这种情况下,blue被丢弃,因为它的存在没有信息价值 – 如果它存在,所有三个列将始终总和为1,这使它们线性相关。丢弃blue类别的效果是它成为截距的一部分,作为基线,拟合模型预测从blue更改为green或从blue更改为red对标签的影响。这种编码的选择有点随意 – 在我的系统上,red和green的列顺序被交换了。
value1
和value2
是双精度数,因此它们在特征向量中保持不变。{color,value1}
是color
特征和value1
特征的乘积,因此是color
的独热编码与标量value1
的乘积,产生三个新特征。请注意,在这种情况下,我们不能丢弃一个类别,因为交互作用使得“基准”值依赖于交互中的第二个特征的值。同样的情况也适用于{color,value2}
。所以你最终得到2 + 1 + 1 + 3 + 3,总共10个特征。你在show()
的输出中看到的是可以由其他 Spark ML 类作为输入的组装向量特征列。
这是如何读取第一行的:
(10,[0,2,3,4,7],[1.0,1.0,14.386294994851129,1.0,14.386294994851129])
是以下内容的稀疏向量表示:
[1.0, 0, 1.0, 14.386294994851129, 1.0, 0, 0, 14.386294994851129, 0, 0] |--1--| |2| |-------3--------| |---4---| |----------5-----------|
它包含以下各个组成部分:
[1.0, 0, ...]
–color
,类别green的独热编码(减去线性相关的第三个类别)[..., 1.0, ...]
–value1
,值1
[..., 14.386294994851129, ...]
–value2
,值14.38629…[..., 1.0, 0, 0, ...]
–color x value1
交互项,green的独热编码([1, 0, 0]
)和1的乘积[..., 14.386294994851129, 0, 0]
–color x value2
交互项,green的独热编码([1, 0, 0]
)和14.38629…的乘积