【機器學習算法】邏輯迴歸調優

環境
  spark-1.6
  python3.5python

1、有無截距
對於邏輯迴歸分類,就是找到z那條直線,不經過原點有截距的直線與經過原點的直線相比,有截距更能將數據分類的完全。算法

package com.bjsxt.lr

import org.apache.spark.mllib.classification.{LogisticRegressionWithLBFGS}
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkConf, SparkContext}
/**
 * 邏輯迴歸 健康情況訓練集   
 */
object LogisticRegression {
  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("spark").setMaster("local[3]")
    val sc = new SparkContext(conf)
    //加載 LIBSVM 格式的數據  這種格式特徵前綴要從1開始 
    val inputData = MLUtils.loadLibSVMFile(sc, "健康情況訓練集.txt")
    val splits = inputData.randomSplit(Array(0.7, 0.3), seed = 1L)
    val (trainingData, testData) = (splits(0), splits(1))
    val lr = new LogisticRegressionWithLBFGS()
//    lr.setIntercept(true)
    val model = lr.run(trainingData)
    val result = testData
      .map{point=>Math.abs(point.label-model.predict(point.features)) }
    println("正確率="+(1.0-result.mean()))
    
    /**
     *邏輯迴歸算法訓練出來的模型,模型中的參數個數(w0....w6)=訓練集中特徵數(6)+1 
     */
    println(model.weights.toArray.mkString(" "))
    println(model.intercept)
    
    sc.stop()
  }
}
package com.bjsxt.lr

import org.apache.spark.mllib.classification.{LogisticRegressionWithLBFGS, LogisticRegressionWithSGD}
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
  * 有無截距
  */
object LogisticRegression2 {

  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("spark").setMaster("local[3]")
    val sc = new SparkContext(conf)
    val inputData: RDD[LabeledPoint] = MLUtils.loadLibSVMFile(sc, "w0測試數據.txt")
    /**
     * randomSplit(Array(0.7, 0.3))方法就是將一個RDD拆分紅N個RDD,N = Array.length
     * 第一個RDD中的數據量和數組中的第一個元素值相關
     */
    val splits = inputData.randomSplit(Array(0.7, 0.3),11L)
    val (trainingData, testData) = (splits(0), splits(1))
    val lr = new LogisticRegressionWithSGD
    // 設置要有W0,也就是有截距
    lr.setIntercept(true)
    val model=lr.run(trainingData)
    val result=testData.map{labeledpoint=>Math.abs(labeledpoint.label-model.predict(labeledpoint.features)) }
    println("正確率="+(1.0-result.mean()))
    println(model.weights.toArray.mkString(" "))
    println(model.intercept)
  }
}

 

2、線性不可分問題
對於線性不可分問題,可使用升高維度的方式轉換成線性可分問題。低維空間的非線性問題在高維空間每每會成爲線性問題。sql

package com.bjsxt.lr

import org.apache.spark.mllib.classification.{LogisticRegressionWithLBFGS, LogisticRegressionWithSGD}
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkConf, SparkContext}
/**
 *  線性不可分 ----升高維度
 */
object LogisticRegression3 {

  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("spark").setMaster("local[3]")
    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 convertFeature = Vectors.dense(array)
        new LabeledPoint(label, convertFeature)
      }
    val splits = inputData.randomSplit(Array(0.7, 0.3),11L)
    val (trainingData, testData) = (splits(0), splits(1))
    val lr = new LogisticRegressionWithLBFGS()
    lr.setIntercept(true)
    val model = lr.run(trainingData)
    val result = testData
      .map { point => Math.abs(point.label - model.predict(point.features)) }
    println("正確率=" + (1.0 - result.mean()))
    println(model.weights.toArray.mkString(" "))
    println(model.intercept)
  }
}

 



3、調整分類閾值
在一些特定的場景下,若是按照邏輯迴歸默認的分類閾值0.5來進行分類的話,可能存在一些潛在的風險,好比,假如使用邏輯迴歸預測一個病人得癌症的機率是0.49,那麼按照0.5的閾值,病人推測出來是沒有得癌症的,可是49%的機率得癌症,比例相對來講得癌症的可能性也是很高,那麼咱們就能夠下降分類的閾值,好比將閾值設置爲0.3,小於0.3認爲不得癌症,大於0.3認爲得癌症,這樣若是病人真的是癌症患者,規避掉了0.49機率下推斷病人是否是癌症的風險。
下降閾值會使邏輯迴歸總體的正確率降低,錯誤率增大,可是規避了一些不能接受的風險。apache

package com.bjsxt.lr

import org.apache.spark.mllib.classification.{LogisticRegressionWithLBFGS, LogisticRegressionWithSGD}
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkConf, SparkContext}
/**
 * 設置分類閾值
 */

object LogisticRegression4 {

  def main(args: Array[String]) {

    val conf = new SparkConf().setAppName("spark").setMaster("local[3]")
    val sc = new SparkContext(conf)
    /**
     * LabeledPoint = Vector+Y
     */
    val inputData = MLUtils.loadLibSVMFile(sc, "健康情況訓練集.txt")
    val splits = inputData.randomSplit(Array(0.7, 0.3),11L)
    val (trainingData, testData) = (splits(0), splits(1))
    val lr = new LogisticRegressionWithLBFGS()
    lr.setIntercept(true)
    
//    val model = lr.run(trainingData)
//    val result = testData
//      .map{point=>Math.abs(point.label-model.predict(point.features)) }
//    println("正確率="+(1.0-result.mean()))
//    println(model.weights.toArray.mkString(" "))
//    println(model.intercept)
    /**
     * 若是在訓練模型的時候沒有調用clearThreshold這個方法,那麼這個模型預測出來的結果都是分類號
     * 若是在訓練模型的時候調用clearThreshold這個方法,那麼這個模型預測出來的結果是一個機率
     */
    val model = lr.run(trainingData).clearThreshold()
    val errorRate = testData.map{p=>
      //score就是一個機率值
      val score = model.predict(p.features)
      // 癌症病人寧願判斷出得癌症也別錯過一個得癌症的病人
      val result = score>0.3 match {case true => 1 ; case false => 0}
      Math.abs(result-p.label)
    }.mean()
    println(1-errorRate)
  }
}

 

4、魯棒性調優
魯棒是Robust的音譯,也就是健壯和強壯的意思,好比說,計算機軟件在輸入錯誤、磁盤故障、網絡過載或有意攻擊狀況下,能不死機、不崩潰,就是該軟件的魯棒性,那麼算法的魯棒性就是指這個算法的抗干擾能力強。數組

package com.bjsxt.lr

import org.apache.spark.mllib.classification.{LogisticRegressionWithLBFGS, LogisticRegressionWithSGD}
import org.apache.spark.mllib.optimization.{L1Updater, SquaredL2Updater}
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkConf, SparkContext}
/**
 * 魯棒性調優
 * 提升模型抗干擾能力
 */
object LogisticRegression5 {

  def main(args: Array[String]) {

    val conf = new SparkConf().setAppName("spark").setMaster("local[3]")
    val sc = new SparkContext(conf)
    val inputData = MLUtils.loadLibSVMFile(sc, "健康情況訓練集.txt")
    val splits = inputData.randomSplit(Array(0.7, 0.3),100)
    val (trainingData, testData) = (splits(0), splits(1))
    /**
     * LogisticRegressionWithSGD 既有L1 又有L2正則化(默認)
     */
    val lr = new LogisticRegressionWithSGD()
    lr.setIntercept(true)
//    lr.optimizer.setUpdater(new L1Updater())
    lr.optimizer.setUpdater(new SquaredL2Updater)
    
    /**
     * LogisticRegressionWithLBFGS 既有L1 又有L2正則化(默認)
     */
//    val lr = new LogisticRegressionWithLBFGS()
//    lr.setIntercept(true)
//    lr.optimizer.setUpdater(new L1Updater)
//    lr.optimizer.setUpdater(new SquaredL2Updater)
    
    /**
     *  這塊設置的是咱們的lambda,越大越看重這個模型的推廣能力,通常不會超過1,0.4是個比較好的值
     */
    lr.optimizer.setRegParam(0.4)
    val model = lr.run(trainingData)
    val result=testData
      .map{point=>Math.abs(point.label-model.predict(point.features)) }
    println("正確率="+(1.0-result.mean()))
    println(model.weights.toArray.mkString(" "))
    println(model.intercept)

  }
}

 

5、歸一化數據
多個維度特徵的量級不一樣,會致使訓練出來模型中不一樣特徵對應的w參數差別很大,容易致使參數小的特徵對目標函數的影響被覆蓋,因此須要對每一個特徵的數據進行歸一化處理,以減小不一樣量級的特徵數據覆蓋其餘特徵對目標函數的影響。
歸一化數據可使各個特徵維度對目標函數的影響權重一致,提升迭代的求解的收斂速度。網絡

最大最小值歸一化: ,缺點是抗干擾能力弱,受離羣值影響比較大,中間容易沒有數據。最大最小值歸一化後的數據落在[0,1]之間。假設某個特徵下有一組數據:1,2,3,4,5,100那麼對數據使用最大最小值歸一化後的值爲:0,2/99,3/99,4/99,1。中間沒有數據,受離羣值100的影響大。dom

package com.bjsxt.lr

import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.ml.feature.MinMaxScaler
import org.apache.spark.sql.SQLContext
import org.apache.spark.mllib.linalg.DenseVector
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS

/**
 * 最大最小值歸一化
 */
object LogisticRegression7 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("spark").setMaster("local")
    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)
    /**
     * 加載生成的DataFrame自動有兩列:label features
     */
    val df = sqlContext.read.format("libsvm").load("環境分類數據.txt")
//    df.show()
    /**
     * MinMaxScaler fit須要DataFrame類型數據
     * setInputCol:設置輸入的特徵名
     * setOutputCol:設置歸一化後輸出的特徵名
     * 
     */
    val minMaxScalerModel = new MinMaxScaler()
                            .setInputCol("features")
                            .setOutputCol("scaledFeatures")
                            .fit(df)
    /**
     * 將全部數據歸一化
     */
    val features = minMaxScalerModel.transform(df)
    features.show()
    
    val normalizeInputData = features.rdd.map(row=>{
      val label = row.getAs("label").toString().toDouble
      val dense = (row.getAs("scaledFeatures")).asInstanceOf[DenseVector]
      new LabeledPoint(label,dense)
    })
    
    val splits = normalizeInputData.randomSplit(Array(0.7, 0.3),11L)
    val (trainingData, testData) = (splits(0), splits(1))
    val lr=new LogisticRegressionWithLBFGS()
    lr.setIntercept(true)
    val model = lr.run(trainingData)
    val result=testData.map{point=>Math.abs(point.label-model.predict(point.features)) }
    println("正確率="+(1.0-result.mean()))
    println(model.weights.toArray.mkString(" "))
    println(model.intercept)  
    
  }
}

 

方差歸一化: ,其中u是樣本的均值, 是樣本的標準差(方差的開方,方差:全部點與均值的差值平方和)。方差歸一化抗干擾能力強,和全部數據有關,求標準差須要全部的值介入,如有離羣值,會被抑制下來。可是歸一化後的數據最終的結果不必定落在0到1之間。
注意:理論上一個模型算法若是拿到訓練集全部的特徵一塊兒訓練模型就要歸一化數據。決策樹算法能夠不歸一化數據。函數

package com.bjsxt.lr

import org.apache.spark.mllib.classification.{LogisticRegressionWithLBFGS, LogisticRegressionWithSGD}
import org.apache.spark.mllib.feature.StandardScaler
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.ml.feature.MinMaxScaler
import org.apache.spark.sql.SQLContext
/**
 * 方差歸一化
 */
object LogisticRegression6 {

  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("spark").setMaster("local[3]")
    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)
    /**
     * scalerModel 這個對象中已經有每一列的均值和方差
     * withStd:表明的是方差歸一化
     * withMean:表明的是均值歸一化
     * scalerModel:存放每一列的方差值
     * 
     * withMean默認爲false, withStd默認爲true
     * 當withMean=true,withStd=false時,向量中的各元素均減去它相應的均值。
     * 當withMean=true,withStd=true時,各元素在減去相應的均值以後,還要除以它們相應的標準差。 
     * 
     */
    val inputData = MLUtils.loadLibSVMFile(sc, "環境分類數據.txt")
    
    val vectors = inputData.map(_.features)
    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)
      println(features)
      new LabeledPoint(label,features)
    }

    
    val splits = normalizeInputData.randomSplit(Array(0.7, 0.3),100)
    val (trainingData, testData) = (splits(0), splits(1))
    val lr=new LogisticRegressionWithLBFGS()
//    val lr = new LogisticRegressionWithSGD()
    lr.setIntercept(true)
    val model = lr.run(trainingData)
    val result=testData.map{point=>Math.abs(point.label-model.predict(point.features)) }
    println("正確率="+(1.0-result.mean()))
    println(model.weights.toArray.mkString(" "))
    println(model.intercept)
  }
}

 

6、調整數據的正負值-均值歸一化
均值歸一化是將原來的特徵值減去這個特徵在數據集中的均值,這樣就會使x的各個維度取值上有正有負,在迭代求 參數時,能減小迭代的次數。測試

 

7、訓練方法選擇
訓練邏輯迴歸的方法有:SGD和L-BFGS,二者的區別爲:
SGD:隨機從訓練集選取數據訓練,不歸一化數據,須要專門在外面進行歸一化,支持L1,L2正則化,不支持多分類。
L-BFGS:全部的數據都會參與訓練,算法融入方差歸一化和均值歸一化。支持L1,L2正則化,支持多分類。spa

相關文章
相關標籤/搜索