packages feed

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

{-# LANGUAGE RecordWildCards #-}
module MXNet.NN.Utils where

import Data.List (intersperse)
import qualified Data.Text as T
import qualified Data.HashMap.Strict as M
import Control.Lens (use)
import Control.Monad (forM_)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Resource (MonadThrow(..))
import Text.Printf

import MXNet.Base (
    Context(..), DType, NDArray(..), Symbol(..), 
    HMap(..), (.&), ArgOf(..),
    mxSymbolSaveToFile, mxNDArraySave, mxNDArrayLoad)
import MXNet.NN.Types
import qualified MXNet.Base.Operators.NDArray as A

-- | format a shape
formatShape :: [Int] -> String
formatShape shape = concat $ ["("] ++ intersperse "," (map show shape) ++ [")"]

-- | format a context
formatContext :: Context -> String
formatContext Context{..} = getDeviceName _device_type ++ "(" ++ show _device_id ++ ")"
  where 
    getDeviceName 1 = "cpu"
    getDeviceName 2 = "gpu"
    getDeviceName 3 = "cpu_pinned"
    getDeviceName _ = error "formatContext: unknown device type"

endsWith :: String -> String -> Bool
endsWith s1 s2 = T.isSuffixOf (T.pack s1) (T.pack s2)

saveSession :: (MonadIO m, DType a) => String -> TrainM a m ()
saveSession filename = do
    dat_vars <- M.keys <$> use sess_data
    lbl_vars <- use sess_label
    params <- use sess_param
    net <- use sess_symbol
    let all_vars = dat_vars ++ lbl_vars
        modelParams = map getModelParam $ M.toList $ M.filterWithKey (\k _ -> not (k `elem` all_vars)) params
    liftIO $ do
        mxSymbolSaveToFile (filename ++ ".json") (unSymbol net)
        mxNDArraySave (filename ++ ".params") modelParams
  where
    getModelParam (key, ParameterI a _) = ("arg:" ++ key, unNDArray a)
    getModelParam (key, ParameterA a) = ("aux:" ++ key, unNDArray a)

loadSession :: (MonadThrow m, MonadIO m, DType a) => String -> [String] -> TrainM a m ()
loadSession filename ignores = do
    arrays <- liftIO $ mxNDArrayLoad (filename ++ ".params")
    params <- use sess_param
    forM_ arrays $ \(name, hdl) -> 
        case break (==':') name of
            (_, "") -> throwM (LoadSessionInvalidTensorName name)
            ("", _) -> throwM (LoadSessionInvalidTensorName name)
            (typ, ':':key) -> 
                case (key `elem` ignores, typ, M.lookup key params) of
                    (True, _, _) -> return ()
                    (_, _, Nothing) -> liftIO $ putStrLn $ printf "Tensor %s is missing." name
                    (_, "arg", Just (ParameterI target grad)) -> liftIO $ A._copyto_upd [unNDArray target] (#data := hdl .& Nil)
                    (_, "aux", Just (ParameterA target))      -> liftIO $ A._copyto_upd [unNDArray target] (#data := hdl .& Nil)
                    _ -> throwM (LoadSessionMismatchedTensorKind name)