MLlib中的批式机器学习算法LogisticRegression是典型的一个回归算法,通常用于分类问题,今天给大家带来逻辑回归的测试方法,接下来的讲解顺序是:1、数据集介绍,2、关于正则化,3、评价指标介绍,4、具体案例。
注:若没有接触过回归算法的朋友可以看《逻辑回归(LogisticRegression)算法及简单案例》、《线性回归算法(Linear Regression)及相关案例》、《局部加权线性回归算法(Locally Weighted Linear Regression)及相关案例》。
关键词:LogisticRegression 逻辑回归、测试、正则化、案例
MLlib提供的数据格式均为LibSVM格式的数据,数据格式如下:
[label] [index1]:[value1] [index2]:[value2] …
具体数据如下:
1 159:124 160:253 161:255 162:63 186:96 187:244 188:251 189:253 190:62 214:127 215:251 216:251 217:253 218:62 241:68 242:236 243:251 244:211 245:31 246:8 268:60 269:228 270:251 271:251 272:94 296:155 297:253 298:253 299:189 323:20 324:253 325:251 326:235 327:66 350:32 351:205 352:253 353:251 354:126 378:104 379:251 380:253 381:184 382:15 405:80 406:240 407:251 408:193 409:23 432:32 433:253 434:253 435:253 436:159 460:151 461:251 462:251 463:251 464:39 487:48 488:221 489:251 490:251 491:172 515:234 516:251 517:251 518:196 519:12 543:253 544:251 545:251 546:89 570:159 571:255 572:253 573:253 574:31 597:48 598:228 599:253 600:247 601:140 602:8 625:64 626:251 627:253 628:220 653:64 654:251 655:253 656:220 681:24 682:193 683:253 684:220
第一位是该样本的标签,其余部分是由空格隔开的[index]:[value]对。
数据集:sample_libsvm_data.txt
为了防止回归问题过拟合的问题出现了多种正则化方法,而MLlib对LogisticRegression设计了三种正则方法分别是L1、L2和ElasticNet正则化。
有时候模型训练的拟合度太高也不是什么好事,对过往训练过的数据记得太深就走不出来了,对新来的数据不能做出良好的预测。**就好像一个人没法忘记过去,总是沉浸在过往的日子里无法自拔,最终也无法接受新的生活、新的人。**所以需要对过拟合状态进行纠正,而纠正的方法就是正则化:
正则化的数学表达式:
J ˉ ( w ; X , y ) = J ( w ; X , y ) + α Ω ( w ) \bar J(w;X,y) = J(w;X,y) + \alpha Ω(w) Jˉ(w;X,y)=J(w;X,y)+αΩ(w)
其中,X是训练的样本,y是对应的标签,w是权重向量(就是回归方程中的回归系数),J()函数是目标函数,Ω(w)就是惩罚项,α是控制惩罚(正则化)强弱的系数。而Ω函数可以替换成不同的正则化函数,如L1、L2、ElasticNet等。
L1:
L 1 = ∣ ∣ w ∣ ∣ 1 = ∑ i n ∣ w i ∣ L1 = ||w||_{1} = \sum_i^n |w_i| L1=∣∣w∣∣1=i∑n∣wi∣
注:
∣ ∣ w ∣ ∣ 1 是 1 范 数 : ∣ ∣ w ∣ ∣ 1 = ∑ i n ∣ w i ∣ ||w||_1 是1范数: ||w||_1 = \sum_i^n |w_i| ∣∣w∣∣1是1范数:∣∣w∣∣1=i∑n∣wi∣
∣ ∣ w ∣ ∣ 2 是 2 范 数 : ∣ ∣ w ∣ ∣ 2 = ( ∑ i n ∣ w ∣ 2 ) 1 2 − − > ∣ ∣ w ∣ ∣ 2 2 = ∑ i n ∣ w ∣ 2 ||w||_2是2范数:||w||_2 = (\sum_i^n|w|^2)^\frac{1}{2} --> ||w||_2^2 = \sum_i^n|w|^2 ∣∣w∣∣2是2范数:∣∣w∣∣2=(i∑n∣w∣2)21−−>∣∣w∣∣22=i∑n∣w∣2
想要了解更多范数可以看:《范式理解(0范式,1范式,2范式)》
L2:
L 2 = ∣ ∣ W ∣ ∣ 2 2 = ∑ i n w i 2 L2 = ||W||_2^2 = \sum_i^n w_i^2 L2=∣∣W∣∣22=i∑nwi2
ElasticNet由于我没有找到单独的公式,所以给大家列出整个回归求解公式:
J ˉ ( w ; X , y ) = a r g m i n w ( J ( w ; X , y ) + λ 1 L 1 + λ 2 L 2 ) \bar J(w;X,y) = argmin_w (J(w;X,y) + \lambda_1L1 + \lambda_2L2) Jˉ(w;X,y)=argminw(J(w;X,y)+λ1L1+λ2L2)
= a r g m i n w ( J ( w ; X , y ) + λ 1 ∑ i n ∣ w i ∣ + λ 2 ∑ i n ∣ w ∣ 2 ) = argmin_w(J(w;X,y) + \lambda_1\sum_i^n|w_i| + \lambda_2\sum_i^n|w|^2) =argminw(J(w;X,y)+λ1i∑n∣wi∣+λ2i∑n∣w∣2)
显然ElasticNet就是L1+L2的变形,具体L1、L2、L1+L2的效果可以看《怎么理解在模型中使用L1+L2正则化?》
OK,正则化就讲清楚了,总的来说就是为了避免过拟合,所以要对y_predict用一个正则化公式来惩罚。
回归问题存在许多评价指标,常见的指标包括MSE、RMSE、MAE、R2,所以在逻辑回归和线性回归中都使用下面的指标:
M S E = 1 m ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 MSE = \frac{1}{m}\sum_{i=1}^m(y_{test}^{(i)} - \hat{y}_{test}^{(i)})^2 MSE=m1i=1∑m(ytest(i)−y^test(i))2
R M S E = M S E = 1 m ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 RMSE = \sqrt{MSE} = \sqrt{\frac{1}{m}\sum_{i=1}^m(y_{test}^{(i)} - \hat{y}_{test}^{(i)})^2} RMSE=MSE=m1i=1∑m(ytest(i)−y^test(i))2
M A E = 1 m ∑ i = 1 m ∣ y t e s t ( i ) − y ^ t e s t ( i ) ∣ MAE = \frac{1}{m}\sum_{i=1}^m|y_{test}^{(i)} - \hat{y}_{test}^{(i)}| MAE=m1i=1∑m∣ytest(i)−y^test(i)∣
R 2 = 1 − ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 ∑ i = 1 m ( y ˉ − y ( i ) ) 2 = 1 − ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 m ∑ i = 1 m ( y ˉ − y ( i ) ) 2 m = 1 − M S E V a r ( y ) R^2 = 1 - \frac{\sum_{i=1}^m(y_{test}^{(i)} - \hat{y}_{test}^{(i)}) ^ 2}{\sum_{i=1}^m(\bar{y} - y^{(i)}) ^ 2} = 1 - \frac{\frac{\sum_{i=1}^m(y_{test}^{(i)} - \hat{y}_{test}^{(i)}) ^ 2}{m}}{\frac{\sum_{i=1}^m(\bar{y} - y^{(i)}) ^ 2}{m}} = 1 - \frac{MSE}{Var(y)} R2=1−∑i=1m(yˉ−y(i))2∑i=1m(ytest(i)−y^test(i))2=1−m∑i=1m(yˉ−y(i))2m∑i=1m(ytest(i)−y^test(i))2=1−Var(y)MSE
还有许多其他的评价指标,各有优劣处这里不一一列举,感兴趣的同学可以看:《回归模型的评价指标比较
》。总之,接下来就用以上这四个中的RMSE和R2作为评价指标。
这里我们使用的是SparkMLlib库,但是其思想内核都是相同的,根据官方示例,分别对L1、L2、ElasticNet三种正则化方式和RMSE和R2进行组合测试:
import org.apache.spark.ml.regression.{LinearRegression, LinearRegressionModel, LinearRegressionTrainingSummary}
import org.apache.spark.sql.{DataFrame, SparkSession}
object BinaryLogisticRegressionDemo {
def main(args: Array[String]): Unit = {
// sparkSession对象
val spark: SparkSession = SparkSession.builder().appName("BinaryLogisticRegressionDemo").getOrCreate()
// 加载数据
val training: DataFrame = spark.read.format("libsvm").load("path\\sample_libsvm_data.txt")
val L1: LinearRegression = new LinearRegression()
.setMaxIter(10)
.setRegParam(0.3)
// 弹性参数,用于调节L1和L2之间的比例,两种正则化比例加起来是1,详见后面正则化的设置,默认为0,只使用L2正则化,设置为1就是只用L1正则化
.setElasticNetParam(1.0)
val L2: LinearRegression = new LinearRegression()
.setMaxIter(10)
.setRegParam(0.3)
.setElasticNetParam(0.0)
val ElasticNet: LinearRegression = new LinearRegression()
.setMaxIter(10)
.setRegParam(0.3)
.setElasticNetParam(0.8)
val L1Model: LinearRegressionModel = L1.fit(training)
val L2Model: LinearRegressionModel = L2.fit(training)
val ElasticNetModel: LinearRegressionModel = ElasticNet.fit(training)
val summaryL1: LinearRegressionTrainingSummary = L1Model.summary
val summaryL2: LinearRegressionTrainingSummary = L2Model.summary
val summaryElasticNet: LinearRegressionTrainingSummary = ElasticNetModel.summary
println(s"L1-RMSE :${summaryL1.rootMeanSquaredError}")
println(s"L1-r2 :${summaryL1.r2}")
println(s"L2-RMSE :${summaryL2.rootMeanSquaredError}")
println(s"L2-r2 :${summaryL2.r2}")
println(s"ElasticNet-RMSE :${summaryElasticNet.rootMeanSquaredError}")
println(s"ElasticNet-r2 :${summaryElasticNet.r2}")
spark.stop()
}
}
控制台最终输出为:
L1-RMSE :0.3471598598927935
L1-r2 :0.5082824629914973
L2-RMSE :0.04640155083008976
L2-r2 :0.9912154062854451
ElasticNet-RMSE :0.2650332068205178
ElasticNet-r2 :0.713412481772471
整理成表格如下:
RMSE | R2 | |
---|---|---|
L1 | 0.3471598598927935 | 0.5082824629914973 |
L2 | 0.04640155083008976 | 0.9912154062854451 |
ElasticNet | 0.2650332068205178 | 0.713412481772471 |
根据RMSE的公式:
R M S E = M S E = 1 m ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 RMSE = \sqrt{MSE} = \sqrt{\frac{1}{m}\sum_{i=1}^m(y_{test}^{(i)} - \hat{y}_{test}^{(i)})^2} RMSE=MSE=m1i=1∑m(ytest(i)−y^test(i))2
RMSE的含义是预测值与真实值之差的平方和取平均值再开方,即预测值与真实值的偏差越小则越准确,相反偏差越大越不准确。经过3种正则化的处理L2的偏差是最小的,三者顺序依次是:L1>ElasticNet>L2,准确程度是:L2>ElasticNet>L1。
根据R2的公式:
R 2 = 1 − ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 ∑ i = 1 m ( y ˉ − y ( i ) ) 2 = 1 − ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 m ∑ i = 1 m ( y ˉ − y ( i ) ) 2 m = 1 − M S E V a r ( y ) R^2 = 1 - \frac{\sum_{i=1}^m(y_{test}^{(i)} - \hat{y}_{test}^{(i)}) ^ 2}{\sum_{i=1}^m(\bar{y} - y^{(i)}) ^ 2} = 1 - \frac{\frac{\sum_{i=1}^m(y_{test}^{(i)} - \hat{y}_{test}^{(i)}) ^ 2}{m}}{\frac{\sum_{i=1}^m(\bar{y} - y^{(i)}) ^ 2}{m}} = 1 - \frac{MSE}{Var(y)} R2=1−∑i=1m(yˉ−y(i))2∑i=1m(ytest(i)−y^test(i))2=1−m∑i=1m(yˉ−y(i))2m∑i=1m(ytest(i)−y^test(i))2=1−Var(y)MSE
R2的取值范围有三种情况:
综上,R2越接近1越好。可是在实际的训练过程中,R2太过接近1可能会有过拟合的嫌疑。
总结:针对同一份数据分别使用三种不同的正则化方式来测试两种主流的回归问题指标,L1在这份数据下表现是最差的其RMSE约0.34,R2只有0.5左右。L2的情况是最好的,RMSE只有0.04左右,R2非常接近于1,但是这也表示在L2情况下模型出现过拟合的风险。ElasticNet的情况则居中。
由于没有LibSVM这类数据,所以只能使用了官方提供的数据集,有点遗憾,在几百条的数据量下实在是测不出什么性能。所以,接下来的工作可能会去找更大规模的数据,使用其他方法去读取不同格式的数据进行训练。回归算法的本质就是一个回归方程,但是这个方程衍生出来的东西和处理方法很多,是值得大家学习的一个算法。