dataframe-learn-2.0.0.0: src-internal/DataFrame/LinearSolver/Loss.hs
{-# LANGUAGE OverloadedStrings #-}
{- | Smooth losses for the proximal-gradient engine. Each carries its
derivative @∂ℓ/∂z@ at @z = w·x + b@ and a global bound on the curvature
@∂²ℓ/∂z²@ (used for the FISTA step size).
-}
module DataFrame.LinearSolver.Loss (
SmoothLoss (..),
sigmoid,
logisticLoss,
squaredLoss,
sqHingeLoss,
) where
import qualified Data.Text as T
{- | A convex, @C¹@ per-sample loss @ℓ(y, z)@. 'slGradZ' is @∂ℓ/∂z@;
'slCurvBound' bounds @∂²ℓ/∂z²@ over all @(y, z)@.
-}
data SmoothLoss = SmoothLoss
{ slName :: !T.Text
, slGradZ :: Double -> Double -> Double
, slCurvBound :: !Double
}
-- | Numerically stable logistic sigmoid.
sigmoid :: Double -> Double
sigmoid z
| z >= 0 = 1 / (1 + exp (-z))
| otherwise = let ez = exp z in ez / (1 + ez)
-- | Binary logistic loss for labels in @{\-1,+1}@: @ℓ = log(1 + exp(-y z))@.
logisticLoss :: SmoothLoss
logisticLoss =
SmoothLoss "logistic" (\y z -> negate (y * sigmoid (negate (y * z)))) 0.25
-- | Squared error for regression: @ℓ = ½ (z - y)²@.
squaredLoss :: SmoothLoss
squaredLoss = SmoothLoss "squared" (flip (-)) 1.0
-- | Squared hinge for classification (LinearSVC default), labels @{\-1,+1}@.
sqHingeLoss :: SmoothLoss
sqHingeLoss =
SmoothLoss
"squared_hinge"
(\y z -> let m = 1 - y * z in if m > 0 then negate (2 * y * m) else 0)
2.0