All Downloads are FREE. Search and download functionalities are using the official Maven repository.

axle.ml.LinearRegression.scala Maven / Gradle / Ivy

The newest version!
package axle.ml

import scala.collection.immutable.TreeMap
import axle.matrix.MatrixModule


trait LinearRegressionModule extends MatrixModule with FeatureNormalizerModule {

  def normalEquation(X: Matrix[Double], y: Matrix[Double]) = (X.t ⨯ X).inv ⨯ X.t ⨯ y

  def h(xi: Matrix[Double], θ: Matrix[Double]) = xi ⨯ θ

  def cost(xi: Matrix[Double], θ: Matrix[Double], yi: Double) = h(xi, θ) - yi

  def dθ(X: Matrix[Double], y: Matrix[Double], θ: Matrix[Double]) = (0 until X.rows)
    .foldLeft(zeros[Double](1, X.columns))(
      (m: Matrix[Double], i: Int) => m + (X.row(i) ⨯ (h(X.row(i), θ) - y(i, 0)))
    ) / X.rows

  def dTheta(X: Matrix[Double], y: Matrix[Double], θ: Matrix[Double]) = dθ(X, y, θ)

  def gradientDescent(X: Matrix[Double], y: Matrix[Double], θ: Matrix[Double], α: Double, iterations: Int) =
    (0 until iterations).foldLeft((θ, List[Double]()))(
      (θiErrLog: (Matrix[Double], List[Double]), i: Int) => {
        val (θi, errLog) = θiErrLog
        val errMatrix = dθ(X, y, θi)
        val errTotal = (0 until errMatrix.rows).map(errMatrix(_, 0)).reduce(_ + _)
        (θi - (errMatrix * α), errTotal :: errLog)
      }
    )

  def regression[D](
    examples: Seq[D],
    numFeatures: Int,
    featureExtractor: D => List[Double],
    objectiveExtractor: D => Double,
    α: Double = 0.1,
    iterations: Int = 100) = {

    val inputX = matrix(
      examples.length,
      numFeatures,
      examples.flatMap(featureExtractor).toArray).t

    val featureNormalizer = new LinearFeatureNormalizer(inputX)

    val X = ones[Double](inputX.rows, 1) +|+ featureNormalizer.normalizedData

    val y = matrix(examples.length, 1, examples.map(objectiveExtractor).toArray)

    val objectiveNormalizer = new LinearFeatureNormalizer(y)

    val θ0 = ones[Double](X.columns, 1)
    val (θ, errLog) = gradientDescent(X, objectiveNormalizer.normalizedData, θ0, α, iterations)

    LinearEstimator(featureExtractor, featureNormalizer, θ, objectiveNormalizer, errLog.reverse)
  }

  case class LinearEstimator[D](
    featureExtractor: D => List[Double],
    featureNormalizer: FeatureNormalizer,
    θ: Matrix[Double],
    objectiveNormalizer: FeatureNormalizer,
    errLog: List[Double]) {

    def errTree() = new TreeMap[Int, Double]() ++
      (0 until errLog.length).map(j => j -> errLog(j)).toMap

    def estimate(observation: D): Double = {
      val scaledX = ones[Double](1, 1) +|+ featureNormalizer.normalize(featureExtractor(observation))
      objectiveNormalizer.denormalize((scaledX ⨯ θ)).head
    }

  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy