在机器学习中,线性回归和逻辑回归算是最基础入门的算法,很多书籍都把他们作为第一个入门算法进行介绍。除了本身的公式之外,逻辑回归和线性回归还有一些必须要了解的内容。一个很常用的知识点就是虚拟变量(也叫做哑变量)—— 用于表示一些无法直接应用到线性公式中的变量(特征)。
举个例子:
通过身高来预测体重,可以简单的通过一个线性公式来表示,y=ax+b
。其中x为身高,y为体重。
现在想要多加一些特征(参数),比如性别。
那么问题来了:如何在一个公式中表示性别呢?
这就是哑变量的作用,它可以通过扩展特征值的个数来表示一些无法被直接数值化的参数。
实例演示
下面是一组数据,第一列表示体重,第二列表示身高,第三列表示性别
体重 | 身高 | 性别 |
---|---|---|
60 | 170 | F |
45 | 163 | M |
80 | 183 | F |
70 | 175 | F |
52 | 167 | M |
现在首先需要把第三列转换成数值类型
体重 | 身高 | 性别 |
---|---|---|
60 | 170 | 1.0 |
45 | 163 | 0.0 |
80 | 183 | 1.0 |
70 | 175 | 1.0 |
52 | 167 | 0.0 |
然后使用多维的数据表示这个参数
体重 | 身高 | 性别男 | 性别女 |
---|---|---|---|
60 | 170 | 1.0 | 0.0 |
45 | 163 | 0.0 | 1.0 |
80 | 183 | 1.0 | 0.0 |
70 | 175 | 1.0 | 0.0 |
52 | 167 | 0.0 | 1.0 |
即,性别这一列会通过两列来标识。
一般来说,有多少种情况出现,就会出现多少列。当然会有很多不同的表现形式,比如有的是通过N-1列表示(为空时表示一种情况),有的是通过n列表示。
代码实践
在Spark MLlib中已经提供了处理哑变量的方法,叫做OneHotEncoder,翻译过来叫做 一位有效编码,即把可能出现多个值的某列转变成多列,同时只有一列有效。MLlib提供了两个方法一个是StringIndex
方法,这个方法可以把不同的字符串转换成数值,比如F``M
分别用0.0``1.0
表示。还有一个是OneHotEncoder
方法,这个方法可以把不同的数值转变成稀疏向量。
什么是稀疏向量
在MLlib中,向量有两种表示方法,一种是密集向量,一种是稀疏向量。
- 密集向量很好理解,[1,2,3,4],代表这个向量有四个元素,分别是1 2 3 4
- 稀疏向量则可以根据下表表示,(3,[4,5,6],[1,2,3]),第一个值代表大小,第二个代表下标数组,第二个是下标对应的值。
然后话说回来,OneHotEncoder
方法可以把不同的数值变成稀疏向量,这样一列就相当于可以用多列来表示。
下面我们具体的看一下代码吧!
object encoderTest { def main(args: Array[String]) { val conf = new SparkConf().setAppName("MovieLensALS-Test").setMaster("local[2]") val sc = new SparkContext(conf) sc.setLogLevel("WARN") val sqlContext = new SQLContext(sc) val df = sqlContext.createDataFrame(Seq( (60, 170,"F","长春"), (45, 163,"M","长春"), (80, 183,"F","沈阳"), (70, 175,"F","大连"), (52, 167,"M","哈尔滨") )).toDF("weight", "height","sex","address") //把性别这一列数值化 val indexer = new StringIndexer() .setInputCol("sex") .setOutputCol("sexIndex") .fit(df) val indexed = indexer.transform(df) //对性别这列进行 有效位编码 val encoder = new OneHotEncoder() .setInputCol("sexIndex") .setOutputCol("sexVec") val encoded = encoder.transform(indexed) //对地址这一列数值化 val indexer1 = new StringIndexer() .setInputCol("address") .setOutputCol("addressIndex") .fit(encoded) val indexed1 = indexer1.transform(encoded) //对地址进行有效位编码 val encoder1 = new OneHotEncoder() .setInputCol("addressIndex") .setOutputCol("addressVec") val encoded1 = encoder1.transform(indexed1) encoded1.show() }}
输出的内容为:
+------+------+---+-------+--------+-------------+------------+-------------+|weight|height|sex|address|sexIndex| sexVec|addressIndex| addressVec|+------+------+---+-------+--------+-------------+------------+-------------+| 60| 170| F| 长春| 0.0|(1,[0],[1.0])| 0.0|(3,[0],[1.0])|| 45| 163| M| 长春| 1.0| (1,[],[])| 0.0|(3,[0],[1.0])|| 80| 183| F| 沈阳| 0.0|(1,[0],[1.0])| 3.0| (3,[],[])|| 70| 175| F| 大连| 0.0|(1,[0],[1.0])| 2.0|(3,[2],[1.0])|| 52| 167| M| 哈尔滨| 1.0| (1,[],[])| 1.0|(3,[1],[1.0])|+------+------+---+-------+--------+-------------+------------+-------------+
这样有什么用呢?
得到了weight``height``sexVec``addressVec
,就相当于得到了一组数据,基于这组数据,就可以来训练线性回归,得到模型后,就可以根据一个人的身高、性别、地址来预测这个人的身高了。 参考
1 MLlib OneHotEncoder官方文档:
2 虚拟变量定义: