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

com.thoughtworks.deeplearning.differentiable.Float.scala Maven / Gradle / Ivy

package com.thoughtworks.deeplearning
package differentiable

import java.util.logging.{Level, Logger}

import com.thoughtworks.feature.Constructor
import com.thoughtworks.feature.Override.inject
import com.thoughtworks.deeplearning.logs.{UncaughtExceptionDuringBackward, WeightIsUpdating}
import com.thoughtworks.deeplearning.math._
import com.thoughtworks.deeplearning.differentiable.Any.Trainable
import com.thoughtworks.deeplearning.Lift.LowPriorityLift
import com.thoughtworks.feature.Caller
import com.thoughtworks.raii.asynchronous.Do
import com.thoughtworks.raii.covariant.ResourceT
import shapeless.the

import scala.util.{Failure, Success, Try}
import scalaz.{-\/, Applicative, Monoid, \/, \/-}
import scalaz.concurrent.{Future, Task}
import scalaz.syntax.all._

/**
  * A namespace of common operators for Float layers.
  *
  * @author 杨博 (Yang Bo) <[email protected]>
  */
object Float extends FloatCompanion {

  trait Hyperparameter {

    trait Weight {

      var data: scala.Float

      final def backward(deltaFuture: Do[scala.Float])(implicit logger: Logger = Logger.getGlobal,
                                                       fullName: sourcecode.FullName,
                                                       className: Caller[_],
                                                       methodName: sourcecode.Name): Future[Unit] = {
        Do.run(Do.releaseMap(deltaFuture) { delta =>
            data -= optimizerConstructor.newInstance(this, delta).delta
          })
          .get
          .map {
            case \/-(()) => ()
            case -\/(e) => logger.log(UncaughtExceptionDuringBackward(e))

          }
      }

    }

    type FloatWeight <: Weight

    type FloatOptimizer <: Optimizer

    abstract class Optimizer(val weight: FloatWeight, delta0: scala.Float) {
      def delta: scala.Float = delta0
    }

    @inject
    def optimizerConstructor: Constructor[(Weight, scala.Float) => FloatOptimizer]

  }

  object Hyperparameter {

    trait FloatInitialization extends Hyperparameter {

      abstract class Weight(override var data: scala.Float) extends super.Weight
      @inject
      def fromFloatConstructor: Constructor[scala.Float => Weight with FloatWeight]

      final def floatWeight(data: scala.Float): Weight with FloatWeight =
        fromFloatConstructor.newInstance(data)

    }

    trait FixedLearningRate extends LearningRate {
      def fixedLearningRate: scala.Float
      trait Optimizer extends super.Optimizer {
        final def learningRate: scala.Float = fixedLearningRate
      }
      override type FloatOptimizer <: Optimizer
    }

    trait LearningRate extends Hyperparameter {
      trait Optimizer extends super.Optimizer {
        def learningRate: scala.Float
        override def delta: scala.Float = super.delta * learningRate
      }
      override type FloatOptimizer <: Optimizer
    }

    trait L1Regularization extends Hyperparameter {
      def l1Regularization: scala.Float
      trait Optimizer extends super.Optimizer {
        override def delta: scala.Float = super.delta + scala.math.signum(weight.data) * l1Regularization
      }
      override type FloatOptimizer <: Optimizer
    }
    trait L2Regularization extends Hyperparameter {
      def l2Regularization: scala.Float
      trait Optimizer extends super.Optimizer {
        override def delta: scala.Float = super.delta + weight.data * l2Regularization
      }
      override type FloatOptimizer <: Optimizer
    }

  }

  object implicits {
    import com.thoughtworks.deeplearning.tapefactories.Binary.monoidBinaryTapeTaskFactory
    import com.thoughtworks.deeplearning.tapefactories.Unary.monoidUnaryTapeTaskFactory

    implicit def liftFloatWeight[W <: Hyperparameter#Weight](implicit logger: Logger = Logger.getGlobal,
                                                             fullName: sourcecode.FullName,
                                                             className: Caller[_],
                                                             methodName: sourcecode.Name) = new Lift[W] {
      override type Data = scala.Float
      override type Delta = scala.Float
      override def apply(weight: W): Do[Tape[Data, Delta]] = {
        import weight._
        Do.delay(Tape(data, backward))
      }
    }
    private[deeplearning] implicit object FloatMonoid extends Monoid[scala.Float] {
      override def zero: scala.Float = 0

      override def append(f1: scala.Float, f2: => scala.Float): scala.Float = f1 + f2
    }

    def infer(self: AnyRef): self.type = self

    @inline
    implicit def liftFloat[A](
        implicit typeClass: LowPriorityLift.Aux[A, scala.Float, scala.Float]): Lift.Aux[A, scala.Float, scala.Float] =
      typeClass

    implicit object FloatTrainable extends Trainable[scala.Float, scala.Float] {
      override def apply(data: scala.Float): Do[scala.Float] = Do.now(1)
    }

    @inline
    implicit def `Float+Float`(
        implicit logger: Logger = Logger.getGlobal,
        fullName: sourcecode.FullName,
        className: Caller[_],
        methodName: sourcecode.Name): polyFunctions.+.Case.Aux[Do[FloatTape], Do[FloatTape], Do[FloatTape]] = {
      polyFunctions.+.at { (operand0, operand1) =>
        tapefactories.Binary.doTape(operand0, operand1) { (data0, data1) =>
          Task.delay {
            val outputData = data0 + data1
            val computeBackward = { outputDelta: scala.Float =>
              val delta0Future = Do.now(outputDelta)
              val delta1Future = Do.now(outputDelta)
              (delta0Future, delta1Future)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    implicit def `Float-Float`(
        implicit logger: Logger = Logger.getGlobal,
        fullName: sourcecode.FullName,
        className: Caller[_],
        methodName: sourcecode.Name): polyFunctions.-.Case.Aux[Do[FloatTape], Do[FloatTape], Do[FloatTape]] = {
      polyFunctions.-.at { (operand0, operand1) =>
        tapefactories.Binary.doTape(operand0, operand1) { (data0, data1) =>
          Task.delay {
            val outputData = data0 - data1
            val computeBackward = { outputDelta: scala.Float =>
              val delta0Future = Do.now(outputDelta)
              val delta1Future = Do.delay(-outputDelta)
              (delta0Future, delta1Future)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    implicit def `Float*Float`(
        implicit logger: Logger = Logger.getGlobal,
        fullName: sourcecode.FullName,
        className: Caller[_],
        methodName: sourcecode.Name): polyFunctions.*.Case.Aux[Do[FloatTape], Do[FloatTape], Do[FloatTape]] = {
      polyFunctions.*.at { (operand0, operand1) =>
        tapefactories.Binary.doTape(operand0, operand1) { (data0, data1) =>
          Task.delay {
            val outputData = data0 * data1
            val computeBackward = { outputDelta: scala.Float =>
              val delta0Future = Do.delay(outputDelta * data1)
              val delta1Future = Do.delay(outputDelta * data0)
              (delta0Future, delta1Future)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    implicit def `Float/Float`(
        implicit logger: Logger = Logger.getGlobal,
        fullName: sourcecode.FullName,
        className: Caller[_],
        methodName: sourcecode.Name): polyFunctions./.Case.Aux[Do[FloatTape], Do[FloatTape], Do[FloatTape]] = {
      polyFunctions./.at { (operand0, operand1) =>
        tapefactories.Binary.doTape(operand0, operand1) { (data0, data1) =>
          Task.delay {
            val outputData = data0 / data1
            val computeBackward = { outputDelta: scala.Float =>
              val delta0Future = Do.delay(outputDelta / data1)
              val delta1Future = Do.delay(-data0 * outputDelta / (data1 * data1))
              (delta0Future, delta1Future)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    implicit def `min(Float,Float)`(
        implicit logger: Logger = Logger.getGlobal,
        fullName: sourcecode.FullName,
        className: Caller[_],
        methodName: sourcecode.Name): math.polyFunctions.min.Case.Aux[Do[FloatTape], Do[FloatTape], Do[FloatTape]] = {
      math.polyFunctions.min.at { (operand0, operand1) =>
        tapefactories.Binary.doTape(operand0, operand1) { (data0, data1) =>
          Task.delay {
            val leftLessThenRight = data0 < data1
            val outputData = if (leftLessThenRight) data0 else data1
            val computeBackward = { outputDelta: scala.Float =>
              val zero = Do.now(the[Numeric[scala.Float]].zero)
              val delta = Do.now(outputDelta)
              if (leftLessThenRight) (delta, zero) else (zero, delta)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    implicit def `max(Float,Float)`(
        implicit logger: Logger = Logger.getGlobal,
        fullName: sourcecode.FullName,
        className: Caller[_],
        methodName: sourcecode.Name): math.polyFunctions.max.Case.Aux[Do[FloatTape], Do[FloatTape], Do[FloatTape]] = {
      math.polyFunctions.max.at { (operand0, operand1) =>
        tapefactories.Binary.doTape(operand0, operand1) { (data0, data1) =>
          Task.delay {
            val leftLessThenRight = data0 < data1
            val outputData = if (leftLessThenRight) data1 else data0
            val computeBackward = { outputDelta: scala.Float =>
              val zero = Do.now(the[Numeric[scala.Float]].zero)
              val delta = Do.now(outputDelta)
              if (leftLessThenRight) (zero, delta) else (delta, zero)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    implicit def `log(Float)`(
        implicit logger: Logger = Logger.getGlobal,
        fullName: sourcecode.FullName,
        className: Caller[_],
        methodName: sourcecode.Name): math.polyFunctions.log.Case.Aux[Do[FloatTape], Do[FloatTape]] = {
      math.polyFunctions.log.at { operand =>
        tapefactories.Unary.doTape(operand) { data =>
          Task.delay {
            val outputData = scala.math.log(data).toFloat
            val computeBackward = { outputDelta: scala.Float =>
              Do.delay(outputDelta / data)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    implicit def `exp(Float)`(implicit logger: Logger = Logger.getGlobal,
                              fullName: sourcecode.FullName,
                              methodName: sourcecode.Name,
                              className: Caller[_]): math.polyFunctions.exp.Case.Aux[Do[FloatTape], Do[FloatTape]] = {
      math.polyFunctions.exp.at { operand =>
        tapefactories.Unary.doTape(operand) { data =>
          Task.delay {
            val outputData = scala.math.exp(data).toFloat
            val computeBackward = { outputDelta: scala.Float =>
              Do.delay(outputDelta * outputData)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    implicit def `abs(Float)`(implicit logger: Logger = Logger.getGlobal,
                              fullName: sourcecode.FullName,
                              methodName: sourcecode.Name,
                              className: Caller[_]): math.polyFunctions.abs.Case.Aux[Do[FloatTape], Do[FloatTape]] = {
      math.polyFunctions.abs.at { operand =>
        tapefactories.Unary.doTape(operand) { data =>
          Task.delay {
            val isDataPositive = data >= 0
            val outputData = if (isDataPositive) data else -data
            val computeBackward = { outputDelta: scala.Float =>
              if (isDataPositive) Do.now(outputDelta) else Do.delay(-outputDelta)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

    @inline
    def reciprocal[Operand](operand: Operand)(implicit liftOperand: Lift.Aux[Operand, scala.Float, scala.Float],
                                              logger: Logger = Logger.getGlobal,
                                              fullName: sourcecode.FullName,
                                              methodName: sourcecode.Name,
                                              className: Caller[_]): Do[FloatTape] = {
      tapefactories.Unary.doTape(liftOperand(operand)) { data: scala.Float =>
        Task.delay {
          val outputData = 1 / data
          val computeBackward = { outputDelta: scala.Float =>
            Do.delay(-outputDelta / (data * data))
          }
          (outputData, computeBackward)
        }
      }
    }

    implicit final class DifferentiableFloatOps[From](from: From)(
        implicit lift: Lift.Aux[From, scala.Float, scala.Float],
        logger: Logger = Logger.getGlobal,
        fullName: sourcecode.FullName,
        methodName: sourcecode.Name,
        className: Caller[_]) {
      private val operand: Do[FloatTape] = lift(from)
      @inline
      def unary_- : Do[FloatTape] = {
        tapefactories.Unary.doTape(operand) { data =>
          Task.delay {
            val outputData = -data
            val computeBackward = { outputDelta: scala.Float =>
              Do.delay(-outputDelta)
            }
            (outputData, computeBackward)
          }
        }
      }
    }

  }
}

//workaround for https://github.com/scala/bug/issues/10306
private[differentiable] abstract class FloatCompanion { this: Float.type =>
  private[deeplearning] type FloatTape = Tape[scala.Float, scala.Float]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy