packages feed

fei-nn-0.2.0: src/MXNet/NN/Callback.hs

module MXNet.NN.Callback where

import Control.Monad.State.Strict (lift)
import Control.Lens (use)
import Text.Printf (printf)
import Control.Monad.IO.Class (liftIO)
import Control.Applicative (Alternative(..))
import Data.IORef
import Data.Dynamic (fromDynamic)
import Data.Maybe (fromMaybe)
import Data.Monoid (Alt(..))
import Data.Time.Clock (UTCTime, getCurrentTime, diffUTCTime)
import qualified Data.HashMap.Strict as M

import MXNet.NN.Types
import MXNet.NN.Utils

-- | Learning rate
data DumpLearningRate = DumpLearningRate

instance CallbackClass DumpLearningRate where
    endOfBatch _ _ _ = do
        lr <- lift $ use stat_last_lr
        liftIO $ do
            putStr $ printf "<lr: %0.6f> " lr

-- | Throughput 
data DumpThroughputEpoch = DumpThroughputEpoch {
    _tp_begin_time :: IORef UTCTime,
    _tp_end_time :: IORef UTCTime,
    _tp_total_sample :: IORef Int
}

instance CallbackClass DumpThroughputEpoch where
    begOfBatch _ n (DumpThroughputEpoch _ _ totalRef) = do
        liftIO $ modifyIORef totalRef (+n)
    begOfEpoch _ _ (DumpThroughputEpoch tt1Ref _ _) =
        liftIO $ getCurrentTime >>= writeIORef tt1Ref
    endOfEpoch _ _ (DumpThroughputEpoch _ tt2Ref _) = do
        liftIO $ getCurrentTime >>= writeIORef tt2Ref
    endOfVal   _ _ (DumpThroughputEpoch tt1Ref tt2Ref totalRef) = liftIO $ do
        tbeg <- readIORef tt1Ref
        tend <- readIORef tt2Ref
        let diff = realToFrac $ diffUTCTime tend tbeg :: Float
        total <- readIORef totalRef
        putStr $ printf "Throughput: %d samepls/sec " (floor $ fromIntegral total / diff :: Int)
        writeIORef totalRef 0

dumpThroughputEpoch :: IO Callback
dumpThroughputEpoch = do
    t0 <- getCurrentTime
    r0 <- newIORef t0
    r1 <- newIORef t0
    r2 <- newIORef 0
    return $ Callback $ DumpThroughputEpoch r0 r1 r2

-- | Checkpoint
data Checkpoint = Checkpoint String

instance CallbackClass Checkpoint where
    endOfVal i _ (Checkpoint path) = do
        store <- use sess_store
        let getKey key = fromMaybe (0 :: Float) $ getAlt $
                            (Alt $ M.lookup ("val_" ++ key)   store >>= fromDynamic) <|>
                            (Alt $ M.lookup ("train_" ++ key) store >>= fromDynamic)
            acc  = getKey "acc"
            loss = getKey "loss"
            filename = printf "%s/epoch_%d_acc_%.2f_loss_%.2f" path i acc loss
        saveSession filename