逻辑回归算法的优化
一:有无截距:
如上图。可以知道,有截距的线条数比无截距的线条数(过原点的线条)要多。所以在选择逻辑回归的时候,通常要设置截距。增加正确的概率。
代码:
val lr = new LogisticRegressionWithSGD() ----创建逻辑回归对象
lr.setIntercept(true) -----设置截距
二:线性不可分的时候,增加维度。
如图:当找不到一条线可以正确分类的时候,可以通过增加维度,找到一个面,正确的分类。而新维度的添加不能是随意的,是根据之前的维度,进行一些计算得到(具体的执行什么计算,是要通过大量的测试得出来的)。
代码:
def main(args:Array[String]){
val conf = new SparkConf().setAppName("lr").setMaster("local[*]")
val sc = new SparkContext(conf)
val inputdata = MLUtils.loadLibSVMFile(sc,"线性不可分的数据集.txt")
.map{
labelpoint=>
val label = labelpoint.label
val feature = labelpoint.features
//新维度的值,必须建立在已有维度的基础上,经过一系列的数据变换得来,本例就是通过已有的两个维度进行求乘积得到的新的维度
val array = Array(feature(0),feature(1),feature(0)*feature(1))
val converFeature = Vectors.dense(array) --转成向量
new LabeledPoint(label,converFeature)
.......
}
}
三:调整分类的阈值
在一些特定的场合下,如果按照逻辑回归默认的分类阈值(0.5)来进行分类的话,可能会存在一些潜在的风险,比如:
--癌症病人的判断:
--如果病人是癌症 判断成不是癌症 (可能会耽误病人的最佳治疗时间)
--如果病人不是癌症 判断成癌症
如果病人不是癌症的概率是45%,这个时候我们应该判断成是癌症,因为该概率太接近中间值。也是有很大的几率是癌症的,判断成是癌症可能导致错误率变大,但是却可以规避一些不能接受的风险。就可以调整成以0.3作为阈值。
代码:
val lr = new LogisticRegressionWithLBFGS()
//如果在训练模型的时候没有调用clearThreshold()这个方法,那个这个模型预测出来的结果都是分类号
//如果在训练模型的时候调用clearThreshold()这个方法,那个这个模型预测出来的结果是一个概率
val model = lr.run(tariningdata).clearThreshold() //tariningdata训练模型的数据集
val errorrate = testData.map{p=> //testData测试数据
val score = model.predict(p.features)//得到的是概率
val result = score > 0.3 macth{ //修改阈值为0.3
case true =>1;case false=>0
Math.abs(result-p.label)
}.mean()
}
四:鲁棒性调优
鲁棒是Robus的英译,也就是健壮和强壮的意思,例如:计算机软件在输入错误,磁盘故障,网络过载或者有意攻击情况下,能不死机,不崩溃,这就是该软件的鲁棒性。所以算法的鲁棒性就是指这个算法的抗干扰能力。
代码:
val lr = LogisticRegressionWithLBFGS()
lr.optimizer.setRegParam(0.4) //这块设置lambda,越大越看推广能力(鲁棒性越好),一般不会超过1,0.4很好
val model = lr.run(trainingData)
五:归一化数据
- 最大值最小值归一化(每个值x 转变成 =x-min/(max-min)得到的值都是在 0-1之间的数
- 方差归一化
代码:
val inputData = MLUtils.loadLibSVMFile(sc,"测试.txt")
val vectors = inputData.map(._features)
/*
*scalerModel 这个对象中已经有每一列的均值和方差
*withStd:代表的方差归一化 withMean:代表的是均值归一化 scalerModel 存放每一列的方差值
*/
val scalerModel = new StandardScaler(withMean=true,withStd=true).fit(vectors)
val normalizeInputData = inputData.map{point=>
val label = point.label
//对每一条数据进行归一化
val features = scalerModel.transform(point.features.toDense)
bew LabeledPoint(label,features)
}