Просьба рассмотреть и помочь в реализации модели логистической регрессии - PullRequest
1 голос
/ 13 апреля 2019

Я пытаюсь реализовать простую модель логистической регрессии в Scala.То есть существует только одна независимая переменная для зависимой двоичной переменной.Если мы посмотрим в качестве примера из Википедии: https://en.wikipedia.org/wiki/Logistic_regression (в разделе «Вероятность сдачи экзамена в зависимости от количества часов обучения») и попытаемся получить одинаковые коэффициенты для theta0 и theta1 с моим текущим кодом, мы приблизимся.

Когда оператор if в методе GradientDescent закомментирован, theta0 = -4.360295851109452 и theta1 = 1.5166246438642796 с максимальными итерациями.Это довольно близко к примеру в Википедии, где theta0 = −4.0777 и theta1 = 1.5046.

Если оператор if не закомментирован, то theta0 = 0.0 и theta1 = 0.0 с одним итерациями, означающими, что возврат происходитмгновенно.Я не уверен, почему это так.Хотя я даже не уверен, насколько далеко я от работающей модели.

В общем, я пытаюсь реализовать то, что было показано здесь: https://www.internalpointers.com/post/cost-function-logistic-regression Насколько я понимаю: если мы получимнаиболее оптимальные тэты с градиентным спуском, тогда мы можем подогнать S-кривую к исходным точкам данных.



import scala.collection.mutable.Buffer
import scala.collection.mutable.ArrayBuffer
import scala.math.exp
import scala.math.abs



object Testing extends App {

  val logistic = new LogisticRegressionCalculator
  val yData    = Array(0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1).map(z => z.toDouble)
  val xData    = Array.tabulate(20)({a => 0.5 + a * 0.25})
  val thetas   = logistic.deriveThetas(yData, xData)
  println(thetas)
}


class LogisticRegressionCalculator {

  //Learning rate
  private var alpha   = 0.01
  //Tolerance
  private var epsilon = 10E-10
  //Number of iterations
  private var maxIterations = 1000000

  def changeAlpha(newAlpha: Double)        = this.alpha = newAlpha
  def changeEpsilon(newEpsilon: Double)    = this.epsilon = newEpsilon
  def changeMaxIterations(newMaxIter: Int) = this.maxIterations = newMaxIter
  def giveAlpha: Double      = this.alpha
  def giveEpsilon: Double    = this.epsilon
  def giveMaxIterations: Int = this.maxIterations
  /*
   * This function is for the simple case where we have only one independent variable for the y values
   * which are either zero or one.
   * It is assumed that the left
   */

  def deriveThetas(yData: Array[Double], xData: Array[Double]): Buffer[Double] = {
    require(yData.size == xData.size)
    //Traces below would be used for testing to see if the values obtained make sence
//    val traceTheta0 = Array.ofDim[Double](this.maxIterations)
//    val traceTheta1 = Array.ofDim[Double](this.maxIterations)

    var theta0     = 0.0
    var tempTheta0 = theta0
    var theta1     = 0.0
    var tempTheta1 = theta1
    val dataSize   = yData.size
    var counter    = 0

    //Hypothesis function for logistic regression in the form of sigmoid function
    def hypothesis(z: Double) = 1.0 / (1.0 + exp(-z))

    def deriveTheta0: Double = {
      var sum = 0.0
      for (i <- 0 until dataSize) {
        sum += (hypothesis(theta0 + theta1 * xData(i)) - yData(i)) //here should be * 1 as the coefficient of theta0 is 1.
      }
      return -(this.alpha / dataSize) * sum
    }

    def deriveTheta1: Double = {
      var sum = 0.0
      for (i <- 0 until dataSize) {
        sum += (hypothesis(theta0 + theta1 * xData(i)) - yData(i)) * xData(i)
      }
      return -(this.alpha / dataSize) * sum
    }

    def gradientDescent: (Double, Double, Double) = {
      for (i <- 0 until this.maxIterations) {
        //println(s"Theta0: ${theta0}\tTheta1: ${theta1}")
        counter += 1
        tempTheta0 = theta0 + deriveTheta0
        tempTheta1 = theta1 + deriveTheta1

        //If the difference is so miniscule that further iterations are of no use.
//        if (abs(tempTheta0 - theta0) >= epsilon || abs(tempTheta1 - theta1) >= epsilon) {
//          
//          return(theta0, theta1, counter)
//          
//        }
        theta0     = tempTheta0
        theta1     = tempTheta1
      }
      (theta0, theta1, counter)
    }
    val temp = gradientDescent
    Buffer(temp._1, temp._2, counter)
  }
}
...