6.SparkMLlib逻辑回归算法
6.1 逻辑回归算法
逻辑回归——logistic regression,直译为对数几率,又译为逻辑斯蒂回归,逻辑回归名为回归其实是分类算法。其数学模型本质是线性回归,在特征到结果的映射中加了一层函数映射,先对所有的特征线性求和,到这一步的输出
y
=
f
(
x
)
=
w
x
y=f(x)=wx
y=f(x)=wx(
w
w
w是权重参数向量,
x
x
x是特征向量),就是前边所述的线性回归,然后再用一个函数
g
(
z
)
g(z)
g(z)计算,这个函数是将连续值映射到[0,1]区间内,也就是输出
y
=
g
(
f
(
x
)
)
=
g
(
w
x
)
y=g(f(x))=g(wx)
y=g(f(x))=g(wx)。
在实际应用中,常常使用Sigmoid函数作为
g
(
z
)
g(z)
g(z)函数:
g ( z ) = 1 1 + e − z g ( z ) = \frac { 1 } { 1 + e ^ { - z } } g(z)=1+e−z1
这个函数图形是一个幅度在区间[0,1]之间,两头分别在趋向负无穷和正无穷时逼近0和1的“S”形曲线。代入线性回归的输出后,还是将问题转化为对回归系数的求解上来,这个时候就可以使用梯度下降算法等方法进行求解。
逻辑回归这样做的优点就是:将原本很大的线性回归的输出范围映射到[0,1]区间内,一方面消除了突变数值对预测结果的影响,更重要的一方面是可以直接对分类可能性建模,无需假设数据分布,而且Sigmoid函数是一个凸函数,可以直接用数值优化算法求解最优解。
6.2算法源码分析
SparkMLlib中对逻辑回归模型的目标函数优化提供了随机梯度下降和拟牛顿法两种算法,本次分析针对随机梯度下降算法。
(1)LogisticRegressionWithSGD(object):基于随机梯度下降的逻辑回归伴生对象,含有train静态方法,根据设置的逻辑回归模型参数创建逻辑回归类,执行run方法进行模型训练,train方法需要设置的参数如下:
- input——训练样本,格式为RDD(label,features);
- numIterations——迭代次数,默认100;
- stepSize——每次迭代的步长,默认1;
- miniBatchFraction——每次迭代计算时的样本比例,默认1.0;
- initialWeights——初始化权重
(2)LogisticRegressionWithSGD(class):该类含有run方法,对权重进行优化计算;
(3)runMiniBatchSGD:该方法根据训练样本迭代计算随机梯度,最后获得最优的权重,在每次迭代中计算样本梯度的同时更新梯度,gradient.compute方法计算样本的梯度值,updater.compute方法更新梯度;
(4)LogisticRegressionModel:逻辑回归模型类,含有predict方法预测样本。
6.3应用实战
6.3.1 数据说明
本次实例使用的是iris鸢尾花数据集,数据与之前用到的相同。
6.3.2 代码详解
//导入所需的包文件
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.mllib.classification.{LogisticRegressionWithLBFGS,
LogisticRegressionModel}
import org.apache.spark.mllib.evaluation.MulticlassMetrics
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.linalg.{Vectors,Vector}
//读取数据
//先读取文本文件到data中
val data = sc.textFile("/mnt/hgfs/thunder-download/MLlib_rep/data/iris.txt")
//使用map函数,用“,”将每行数据划分为五部分,前四个花的特征,最后一//个是花的分类,并且将标签和特征列存入LabeledPoint
val parsedData = data.map { line =>
| val parts = line.split(',')
| LabeledPoint(if(parts(4)=="Iris-setosa") 0.toDouble
| else if (parts(4)=="Iris-versicolor") 1.toDouble else
| 2.toDouble, Vectors.dense(parts(0).toDouble,parts(1).toDouble,parts
| (2).toDouble,parts(3).toDouble))
| }
输出结果:
parsedData: org.apache.spark.rdd.RDD[org.apache.spark.mllib.regression.LabeledPoint] = MapPartitionsRDD[398] at map at <console>:63
//输出数据
parsedData.foreach { x => println(x) }
输出结果为:
1.0,[6.0,2.9,4.5,1.5])
(0.0,[5.1,3.5,1.4,0.2])
(1.0,[5.7,2.6,3.5,1.0])
(0.0,[4.9,3.0,1.4,0.2])
(0.0,[4.7,3.2,1.3,0.2])
……
//划分数据为训练集和测试集,比例6:4
val splits = parsedData.randomSplit(Array(0.6, 0.4), seed = 11L)
输出结果:
splits: Array[org.apache.spark.rdd.RDD[org.apache.spark.mllib.regression.LabeledPoint]] = Array(MapPartitionsRDD[399] at randomSplit at <console>:65, MapPartitionsRDD[400] at randomSplit at <console>:65)
//划分的第一部分存入训练集
val training = splits(0).cache()
输出结果:
training: org.apache.spark.rdd.RDD[org.apache.spark.mllib.regression.LabeledPoint] = MapPartitionsRDD[399] at randomSplit at <console>:65
//划分的第二部分存入测试集
val test = splits(1)
输出结果:
test: org.apache.spark.rdd.RDD[org.apache.spark.mllib.regression.LabeledPoint] = MapPartitionsRDD[400] at randomSplit at <console>:65
//使用上边得到的数据,用set方法设置训练模型的参数
//设置分类数目为3,这个是根据你自己的目标或者数据样本本身来确定的
//训练构建三分类逻辑回归模型
//这里的LogisticRegressionWithLBFGS模型是因为求解非线性优化问题(L(w)求极//大值)的方法使用了LBFGS(Limited-memory BFGS)
val model = new LogisticRegressionWithLBFGS().
| setNumClasses(3).
| run(training)
输出结果:
model: org.apache.spark.mllib.classification.LogisticRegressionModel =
org.apache.spark.mllib.classification.LogisticRegressionModel: intercept = 0.0,
numFeatures = 8, numClasses = 3, threshold = 0.5
//使用模型对测试数据进行预测,使用的是predcit方法。test数据每一行分为标//签和特征,使用map方法对每一行数据进行model.predict(features),根据特征得到对应的预测值,将此预测值和样本真实标签值一起存入predictionAndLabels。
val predictionAndLabels = test.map { case LabeledPoint(label, features) =>
| val prediction = model.predict(features)
| (prediction, label)
| }
输出结果:
predictionAndLabels: org.apache.spark.rdd.RDD[(Double, Double)] =
MapPartitionsRDD[461] at map at <console>:73
//输出预测值
predictionAndLabels.foreach { x => println(x) }
输出结果:
(1.0,1.0)
(2.0,1.0)
(2.0,1.0)
(1.0,1.0)
(1.0,1.0)
(1.0,1.0)
(1.0,1.0)
(1.0,1.0)
(1.0,1.0)
(1.0,1.0)
……
这里选取了前十行预测标签和真实标签的对比,可以发现有两个预测错误,接下来我们整体评估一下模型的预测能力
//对逻辑回归模型的预测能力进行评估
//将预测结果存入MulticlassMetrics,计算预测准确度
val metrics = new MulticlassMetrics(predictionAndLabels)
val precision = metrics.precision
println("Precision = " + precision)
输出结果:
Precision = 0.9180327868852459