packages feed

concraft 0.2.0 → 0.3.0

raw patch · 14 files changed

+1209/−675 lines, 14 filesdep +arraydep +comonad-transformersdep +data-lensdep ~crf-chain1-constraineddep ~crf-chain2-genericnew-component:exe:concraftPVP ok

version bump matches the API change (PVP)

Dependencies added: array, comonad-transformers, data-lens, monad-codec, temporary, vector-binary

Dependency ranges changed: crf-chain1-constrained, crf-chain2-generic

API changes (from Hackage documentation)

- NLP.Concraft.Disamb: Tag :: Maybe POS -> Map Attr Text -> Tag
- NLP.Concraft.Disamb: Tier :: Bool -> Set Attr -> Tier
- NLP.Concraft.Disamb: atts :: Tag -> Map Attr Text
- NLP.Concraft.Disamb: data Disamb
- NLP.Concraft.Disamb: data Tag
- NLP.Concraft.Disamb: data Tier
- NLP.Concraft.Disamb: deTear :: TierConf -> Word Tag -> (Tag, Tag) -> Tag
- NLP.Concraft.Disamb: deTears :: TierConf -> Sent Tag -> [(Tag, Tag)] -> [Tag]
- NLP.Concraft.Disamb: instance Binary Disamb
- NLP.Concraft.Disamb: instance Binary Tag
- NLP.Concraft.Disamb: instance Binary Tier
- NLP.Concraft.Disamb: instance Eq Tag
- NLP.Concraft.Disamb: instance Ord Tag
- NLP.Concraft.Disamb: instance Show Tag
- NLP.Concraft.Disamb: learn :: SgdArgs -> FilePath -> Text -> TierConf -> FilePath -> Maybe FilePath -> IO Disamb
- NLP.Concraft.Disamb: pos :: Tag -> Maybe POS
- NLP.Concraft.Disamb: schema :: Schema t ()
- NLP.Concraft.Disamb: schematize :: Sent t -> Sent Ob t
- NLP.Concraft.Disamb: select :: Tier -> Tag -> Tag
- NLP.Concraft.Disamb: splitSent :: TierConf -> Sent Tag -> Sent (Tag, Tag)
- NLP.Concraft.Disamb: splitWord :: TierConf -> Word Tag -> Word (Tag, Tag)
- NLP.Concraft.Disamb: tagFile :: Text -> Disamb -> FilePath -> IO Text
- NLP.Concraft.Disamb: tear :: TierConf -> Tag -> (Tag, Tag)
- NLP.Concraft.Disamb: type Ob = ([Int], Text)
- NLP.Concraft.Disamb: type Ox t a = Ox (Word t) Text a
- NLP.Concraft.Disamb: type Schema t a = Vector (Word t) -> Int -> Ox t a
- NLP.Concraft.Disamb: type TierConf = (Tier, Tier)
- NLP.Concraft.Disamb: withAtts :: Tier -> Set Attr
- NLP.Concraft.Disamb: withPos :: Tier -> Bool
- NLP.Concraft.Guess: data Guesser t
- NLP.Concraft.Guess: ign :: Guesser t -> t
- NLP.Concraft.Guess: learn :: SgdArgs -> Text -> FilePath -> Maybe FilePath -> IO (Guesser Text)
- NLP.Concraft.Guess: schema :: Schema t ()
- NLP.Concraft.Guess: schematize :: Ord t => Sent t -> Sent Ob t
- NLP.Concraft.Guess: tagFile :: Int -> Guesser Text -> FilePath -> IO Text
- NLP.Concraft.Guess: type Ob = ([Int], Text)
- NLP.Concraft.Guess: type Ox t a = Ox (Word t) Text a
- NLP.Concraft.Guess: type Schema t a = Vector (Word t) -> Int -> Ox t a
- NLP.Concraft.Morphosyntax: (<+>) :: Num a => Positive a -> Positive a -> Positive a
- NLP.Concraft.Morphosyntax: best :: Choice t -> t
- NLP.Concraft.Morphosyntax: data Positive a
- NLP.Concraft.Morphosyntax: instance (Ord t, Read t) => Read (Word t)
- NLP.Concraft.Morphosyntax: instance Eq a => Eq (Positive a)
- NLP.Concraft.Morphosyntax: instance Ord a => Ord (Positive a)
- NLP.Concraft.Morphosyntax: instance Show a => Show (Positive a)
- NLP.Concraft.Morphosyntax: known :: Word t -> Bool
- NLP.Concraft.Morphosyntax: mapChoice :: Ord b => (a -> b) -> Choice a -> Choice b
- NLP.Concraft.Morphosyntax: mkPositive :: (Num a, Ord a) => a -> Positive a
- NLP.Concraft.Morphosyntax: tags :: Word t -> Set t
- NLP.Concraft.Morphosyntax: type Choice t = Map t (Positive Double)
- NLP.Concraft.Plain: Interp :: Text -> Text -> Interp
- NLP.Concraft.Plain: NewLine :: Space
- NLP.Concraft.Plain: None :: Space
- NLP.Concraft.Plain: Space :: Space
- NLP.Concraft.Plain: Token :: Text -> Space -> Bool -> Map Interp Bool -> Token
- NLP.Concraft.Plain: addInterps :: Bool -> Token -> [Interp] -> Token
- NLP.Concraft.Plain: addNones :: Bool -> Token -> [Text] -> Token
- NLP.Concraft.Plain: base :: Interp -> Text
- NLP.Concraft.Plain: choose :: Token -> Set Text -> Token
- NLP.Concraft.Plain: data Interp
- NLP.Concraft.Plain: data Space
- NLP.Concraft.Plain: data Token
- NLP.Concraft.Plain: fromTok :: Token -> (Word Text, Choice Text)
- NLP.Concraft.Plain: instance Eq Interp
- NLP.Concraft.Plain: instance Eq Space
- NLP.Concraft.Plain: instance Eq Token
- NLP.Concraft.Plain: instance Ord Interp
- NLP.Concraft.Plain: instance Ord Space
- NLP.Concraft.Plain: instance Ord Token
- NLP.Concraft.Plain: instance Show Interp
- NLP.Concraft.Plain: instance Show Space
- NLP.Concraft.Plain: instance Show Token
- NLP.Concraft.Plain: interps :: Token -> Map Interp Bool
- NLP.Concraft.Plain: known :: Token -> Bool
- NLP.Concraft.Plain: orth :: Token -> Text
- NLP.Concraft.Plain: parsePlain :: Text -> Text -> [[Token]]
- NLP.Concraft.Plain: parseSent :: Text -> Text -> [Token]
- NLP.Concraft.Plain: readPlain :: Text -> FilePath -> IO [[Token]]
- NLP.Concraft.Plain: showPlain :: Text -> [[Token]] -> Text
- NLP.Concraft.Plain: showSent :: Text -> [Token] -> Text
- NLP.Concraft.Plain: showWord :: Text -> Token -> Text
- NLP.Concraft.Plain: space :: Token -> Space
- NLP.Concraft.Plain: tag :: Interp -> Text
- NLP.Concraft.Plain: writePlain :: Text -> FilePath -> [[Token]] -> IO ()
+ NLP.Concraft: DisambConf :: Split r t -> Schema t () -> DisambConf r t
+ NLP.Concraft: DisambWith :: DisambConf r t -> a -> DisambWith r t a
+ NLP.Concraft: GuessConf :: Int -> Schema r () -> GuessConf r
+ NLP.Concraft: GuessData :: GuessConf r -> Guesser r -> GuessData r
+ NLP.Concraft: data DisambConf r t
+ NLP.Concraft: data DisambWith r t a
+ NLP.Concraft: data GuessConf r
+ NLP.Concraft: data GuessData r
+ NLP.Concraft: disamb :: (Ord r, Ord t) => GuessData r -> DisambTag r t -> Sent r -> [r]
+ NLP.Concraft: disambConf :: DisambWith r t a -> DisambConf r t
+ NLP.Concraft: disambDoc :: (Functor f, Ord t) => Doc f s w -> GuessData Tag -> DisambTag Tag t -> Text -> Text
+ NLP.Concraft: disambSchema :: DisambConf r t -> Schema t ()
+ NLP.Concraft: disambWith :: DisambWith r t a -> a
+ NLP.Concraft: guessConf :: GuessData r -> GuessConf r
+ NLP.Concraft: guessNum :: GuessConf r -> Int
+ NLP.Concraft: guessSchema :: GuessConf r -> Schema r ()
+ NLP.Concraft: guesser :: GuessData r -> Guesser r
+ NLP.Concraft: split :: DisambConf r t -> Split r t
+ NLP.Concraft: trainOn :: (Functor f, Foldable f, Ord t) => Doc f s w -> GuessConf Tag -> SgdArgs -> DisambTrain Tag t c -> FilePath -> Maybe FilePath -> IO (Guesser Tag, c)
+ NLP.Concraft: type DisambTag r t = DisambWith r t (TagCRF Ob t)
+ NLP.Concraft: type DisambTrain r t c = DisambWith r t (TrainCRF Ob t c)
+ NLP.Concraft.Disamb: disambDoc :: (Functor f, Ord t) => Doc f s w -> Schema t a -> Split Tag t -> TagCRF Ob t -> Text -> Text
+ NLP.Concraft.Disamb: disambSent :: Ord t => Sent s w -> Schema t a -> Split Tag t -> TagCRF Ob t -> s -> s
+ NLP.Concraft.Disamb: trainOn :: (Foldable f, Ord t) => Doc f s w -> Schema t a -> Split Tag t -> TrainCRF Ob t c -> FilePath -> Maybe FilePath -> IO c
+ NLP.Concraft.Disamb: type Split r t = r -> t
+ NLP.Concraft.Disamb: type TagCRF o t = Sent o t -> [t]
+ NLP.Concraft.Disamb: type TrainCRF o t c = IO [SentL o t] -> Maybe (IO [SentL o t]) -> IO c
+ NLP.Concraft.Disamb.Positional: Part :: Maybe POS -> Map Attr Text -> Part
+ NLP.Concraft.Disamb.Positional: Tier :: Bool -> Set Attr -> Tier
+ NLP.Concraft.Disamb.Positional: atts :: Part -> Map Attr Text
+ NLP.Concraft.Disamb.Positional: data Part
+ NLP.Concraft.Disamb.Positional: data Tier
+ NLP.Concraft.Disamb.Positional: instance Binary Part
+ NLP.Concraft.Disamb.Positional: instance Binary Tier
+ NLP.Concraft.Disamb.Positional: instance Eq Part
+ NLP.Concraft.Disamb.Positional: instance Ord Part
+ NLP.Concraft.Disamb.Positional: instance Show Part
+ NLP.Concraft.Disamb.Positional: pos :: Part -> Maybe POS
+ NLP.Concraft.Disamb.Positional: select :: Tier -> Tag -> Part
+ NLP.Concraft.Disamb.Positional: split :: [Tier] -> Tag -> [Part]
+ NLP.Concraft.Disamb.Positional: tierConfDefault :: [Tier]
+ NLP.Concraft.Disamb.Positional: withAtts :: Tier -> Set Attr
+ NLP.Concraft.Disamb.Positional: withPos :: Tier -> Bool
+ NLP.Concraft.Disamb.Tiered: CRF :: Int -> CodecData a b -> Model FeatMap Ob [Lb] Feat -> CRF a b
+ NLP.Concraft.Disamb.Tiered: Lb :: Int -> Lb
+ NLP.Concraft.Disamb.Tiered: OFeat :: {-# UNPACK #-} !Ob -> {-# UNPACK #-} !Lb -> {-# UNPACK #-} !Int -> Feat
+ NLP.Concraft.Disamb.Tiered: Ob :: Int -> Ob
+ NLP.Concraft.Disamb.Tiered: TFeat1 :: {-# UNPACK #-} !Lb -> {-# UNPACK #-} !Int -> Feat
+ NLP.Concraft.Disamb.Tiered: TFeat2 :: {-# UNPACK #-} !Lb -> {-# UNPACK #-} !Lb -> {-# UNPACK #-} !Int -> Feat
+ NLP.Concraft.Disamb.Tiered: TFeat3 :: {-# UNPACK #-} !Lb -> {-# UNPACK #-} !Lb -> {-# UNPACK #-} !Lb -> {-# UNPACK #-} !Int -> Feat
+ NLP.Concraft.Disamb.Tiered: codecData :: CRF a b -> CodecData a b
+ NLP.Concraft.Disamb.Tiered: data CRF a b
+ NLP.Concraft.Disamb.Tiered: data Feat
+ NLP.Concraft.Disamb.Tiered: instance (Ord a, Ord b, Binary a, Binary b) => Binary (CRF a b)
+ NLP.Concraft.Disamb.Tiered: instance Binary (FeatMap Feat)
+ NLP.Concraft.Disamb.Tiered: instance Binary Feat
+ NLP.Concraft.Disamb.Tiered: instance Binary Lb
+ NLP.Concraft.Disamb.Tiered: instance Binary Ob
+ NLP.Concraft.Disamb.Tiered: instance Eq Feat
+ NLP.Concraft.Disamb.Tiered: instance Eq Lb
+ NLP.Concraft.Disamb.Tiered: instance Eq Ob
+ NLP.Concraft.Disamb.Tiered: instance FeatMap FeatMap Feat
+ NLP.Concraft.Disamb.Tiered: instance Ix Lb
+ NLP.Concraft.Disamb.Tiered: instance Ix Ob
+ NLP.Concraft.Disamb.Tiered: instance Ord Feat
+ NLP.Concraft.Disamb.Tiered: instance Ord Lb
+ NLP.Concraft.Disamb.Tiered: instance Ord Ob
+ NLP.Concraft.Disamb.Tiered: instance Show Feat
+ NLP.Concraft.Disamb.Tiered: instance Show Lb
+ NLP.Concraft.Disamb.Tiered: instance Show Ob
+ NLP.Concraft.Disamb.Tiered: ln :: Feat -> {-# UNPACK #-} !Int
+ NLP.Concraft.Disamb.Tiered: model :: CRF a b -> Model FeatMap Ob [Lb] Feat
+ NLP.Concraft.Disamb.Tiered: newtype Lb
+ NLP.Concraft.Disamb.Tiered: newtype Ob
+ NLP.Concraft.Disamb.Tiered: numOfLayers :: CRF a b -> Int
+ NLP.Concraft.Disamb.Tiered: ob :: Feat -> {-# UNPACK #-} !Ob
+ NLP.Concraft.Disamb.Tiered: selectHidden :: FeatSel o t f
+ NLP.Concraft.Disamb.Tiered: selectPresent :: FeatSel o t f
+ NLP.Concraft.Disamb.Tiered: tag :: (Ord o, Ord t) => CRF o t -> TagCRF o [t]
+ NLP.Concraft.Disamb.Tiered: train :: (Ord o, Ord t) => Int -> FeatSel Ob [Lb] Feat -> SgdArgs -> TrainCRF o [t] (CRF o t)
+ NLP.Concraft.Disamb.Tiered: type FeatSel o t f = FeatGen o t f -> Xs o t -> Ys t -> [f]
+ NLP.Concraft.Disamb.Tiered: unLb :: Lb -> Int
+ NLP.Concraft.Disamb.Tiered: unOb :: Ob -> Int
+ NLP.Concraft.Disamb.Tiered: x1 :: Feat -> {-# UNPACK #-} !Lb
+ NLP.Concraft.Disamb.Tiered: x2 :: Feat -> {-# UNPACK #-} !Lb
+ NLP.Concraft.Disamb.Tiered: x3 :: Feat -> {-# UNPACK #-} !Lb
+ NLP.Concraft.Format: Doc :: (Text -> f s) -> (f s -> Text) -> Sent s w -> Doc f s w
+ NLP.Concraft.Format: Sent :: (s -> [w]) -> ([w] -> s -> s) -> Word w -> Sent s w
+ NLP.Concraft.Format: Word :: (w -> Word Tag) -> (WMap Tag -> w -> w) -> Word w
+ NLP.Concraft.Format: data Doc f s w
+ NLP.Concraft.Format: data Sent s w
+ NLP.Concraft.Format: data Word w
+ NLP.Concraft.Format: extract :: Word w -> w -> Word Tag
+ NLP.Concraft.Format: mergeSent :: Sent s w -> [w] -> s -> s
+ NLP.Concraft.Format: parseDoc :: Doc f s w -> Text -> f s
+ NLP.Concraft.Format: parseSent :: Sent s w -> s -> [w]
+ NLP.Concraft.Format: select :: Word w -> WMap Tag -> w -> w
+ NLP.Concraft.Format: sentHandler :: Doc f s w -> Sent s w
+ NLP.Concraft.Format: showDoc :: Doc f s w -> f s -> Text
+ NLP.Concraft.Format: type Tag = Text
+ NLP.Concraft.Format: wordHandler :: Sent s w -> Word w
+ NLP.Concraft.Format.Plain: instance Eq Interp
+ NLP.Concraft.Format.Plain: instance Eq Space
+ NLP.Concraft.Format.Plain: instance Eq Token
+ NLP.Concraft.Format.Plain: instance Ord Interp
+ NLP.Concraft.Format.Plain: instance Ord Space
+ NLP.Concraft.Format.Plain: instance Ord Token
+ NLP.Concraft.Format.Plain: instance Show Interp
+ NLP.Concraft.Format.Plain: instance Show Space
+ NLP.Concraft.Format.Plain: instance Show Token
+ NLP.Concraft.Format.Plain: plainFormat :: Tag -> Doc [] [Token] Token
+ NLP.Concraft.Guess: guessDoc :: Functor f => Doc f s w -> Int -> Schema Tag a -> Guesser Tag -> Text -> Text
+ NLP.Concraft.Guess: guessSent :: Sent s w -> Int -> Schema Tag a -> Guesser Tag -> s -> s
+ NLP.Concraft.Guess: include :: Ord t => Sent t -> [[t]] -> Sent t
+ NLP.Concraft.Guess: newtype Guesser t
+ NLP.Concraft.Guess: trainOn :: Foldable f => Doc f s w -> Schema Tag a -> SgdArgs -> FilePath -> Maybe FilePath -> IO (Guesser Tag)
+ NLP.Concraft.Morphosyntax: data WMap a
+ NLP.Concraft.Morphosyntax: instance Eq a => Eq (WMap a)
+ NLP.Concraft.Morphosyntax: instance Ord a => Ord (WMap a)
+ NLP.Concraft.Morphosyntax: instance Show a => Show (WMap a)
+ NLP.Concraft.Morphosyntax: interps :: Word t -> [t]
+ NLP.Concraft.Morphosyntax: interpsSet :: Word t -> Set t
+ NLP.Concraft.Morphosyntax: mapSent :: Ord b => (a -> b) -> Sent a -> Sent b
+ NLP.Concraft.Morphosyntax: mapWMap :: Ord b => (a -> b) -> WMap a -> WMap b
+ NLP.Concraft.Morphosyntax: mkWMap :: Ord a => [(a, Double)] -> WMap a
+ NLP.Concraft.Morphosyntax: oov :: Word t -> Bool
+ NLP.Concraft.Morphosyntax: tagWMap :: Word t -> WMap t
+ NLP.Concraft.Schema: disambSchemaDefault :: Schema t ()
+ NLP.Concraft.Schema: guessSchemaDefault :: Schema t ()
+ NLP.Concraft.Schema: type Ob = ([Int], Text)
+ NLP.Concraft.Schema: type Ox t a = Ox (Word t) Text a
+ NLP.Concraft.Schema: type Schema t a = Vector (Word t) -> Int -> Ox t a
- NLP.Concraft.Disamb: disamb :: Disamb -> Sent Tag -> [Tag]
+ NLP.Concraft.Disamb: disamb :: (Ord r, Ord t) => Schema t a -> Split r t -> TagCRF Ob t -> Sent r -> [r]
- NLP.Concraft.Guess: Guesser :: CRF Ob t -> t -> Guesser t
+ NLP.Concraft.Guess: Guesser :: CRF Ob t -> Guesser t
- NLP.Concraft.Guess: guess :: Ord t => Int -> Guesser t -> Sent t -> [[t]]
+ NLP.Concraft.Guess: guess :: Ord t => Int -> Schema t a -> Guesser t -> Sent t -> [[t]]
- NLP.Concraft.Morphosyntax: Word :: Text -> Set t -> Word t
+ NLP.Concraft.Morphosyntax: Word :: Text -> WMap t -> Bool -> Word t

Files

+ NLP/Concraft.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE RecordWildCards #-}++module NLP.Concraft+( GuessConf (..)+, GuessData (..)+, DisambConf (..)+, DisambWith (..)+, DisambTag+, DisambTrain+, disamb+, disambDoc+, trainOn+) where++-- import Data.Binary (Binary, put, get)+-- import qualified Data.Text as T++import System.IO (hClose)+import Data.Foldable (Foldable)+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.IO as L+import qualified Numeric.SGD as SGD+import qualified System.IO.Temp as Temp++import NLP.Concraft.Schema+import qualified NLP.Concraft.Morphosyntax as Mx+import qualified NLP.Concraft.Format as F+import qualified NLP.Concraft.Guess as G+import qualified NLP.Concraft.Disamb as D++-- | Guessing configuration.+data GuessConf r = GuessConf+    { guessNum      :: Int+    , guessSchema   :: Schema r () }++-- | Guessing configuration and model data.+data GuessData r = GuessData+    { guessConf     :: GuessConf r+    , guesser       :: G.Guesser r }++-- | Disambiguation configuration.+data DisambConf r t = DisambConf+    { split         :: D.Split r t+    , disambSchema  :: Schema t () }++-- | Disambiguation configuration with...+data DisambWith r t a = DisambWith+    { disambConf    :: DisambConf r t+    , disambWith    :: a }++-- | Tagging with disambiguation configuration.+type DisambTag r t = DisambWith r t (D.TagCRF Ob t)++-- | Training disambiguation model configuration.+type DisambTrain r t c = DisambWith r t (D.TrainCRF Ob t c)++-- | Perform disambiguation preceded by context-sensitive guessing.+disamb+    :: (Ord r, Ord t)+    => GuessData r      -- ^ Guessing configuration+    -> DisambTag r t    -- ^ Disambiguation configuration+    -> Mx.Sent r        -- ^ Input+    -> [r]              -- ^ Output+disamb GuessData{..} DisambWith{..} sent+    = D.disamb disambSchema split tagCRF+    . G.include sent +    . G.guess guessNum guessSchema guesser +    $ sent+  where+    GuessConf{..}   = guessConf+    DisambConf{..}  = disambConf+    tagCRF          = disambWith++-- | Tag the sentence.+disambSent+    :: Ord t+    => F.Sent s w+    -> GuessData F.Tag+    -> DisambTag F.Tag t+    -> s -> s+disambSent sentH GuessData{..} DisambWith{..}+    = D.disambSent sentH disambSchema split tagCRF+    . G.guessSent  sentH guessNum guessSchema guesser+  where+    GuessConf{..}   = guessConf+    DisambConf{..}  = disambConf+    tagCRF          = disambWith++-- | Tag document.+disambDoc+    :: (Functor f, Ord t)+    => F.Doc f s w          -- ^ Document format handler+    -> GuessData F.Tag      -- ^ Guessing configuration+    -> DisambTag F.Tag t    -- ^ Disambiguation configuration+    -> L.Text               -- ^ Input+    -> L.Text               -- ^ Output+disambDoc F.Doc{..} guessData disambTag  =+    let onSent = disambSent sentHandler guessData disambTag+    in  showDoc . fmap onSent . parseDoc++-- | Train guessing and disambiguation models.+trainOn+    :: (Functor f, Foldable f, Ord t)+    => F.Doc f s w              -- ^ Document format handler+    -> GuessConf F.Tag          -- ^ Guessing configuration+    -> SGD.SgdArgs              -- ^ SGD params for guesser+    -> DisambTrain F.Tag t c    -- ^ Disambiguation configuration+    -> FilePath                 -- ^ Training file+    -> Maybe FilePath           -- ^ Maybe eval file+    -> IO (G.Guesser F.Tag, c)  -- ^ Resultant models+trainOn format guessConf@GuessConf{..} sgdArgs DisambWith{..}+        trainPath evalPath'Maybe = do+    putStrLn "\n===== Train guessing model ====\n"+    guesser <- G.trainOn format guessSchema sgdArgs+                    trainPath evalPath'Maybe+    let guessData = GuessData guessConf guesser+    let withGuesser = guessFile format guessData+    withGuesser "train" (Just trainPath) $ \(Just trainPathG) ->+      withGuesser "eval"   evalPath'Maybe  $ \evalPathG'Maybe  -> do+        putStrLn "\n===== Train disambiguation model ====\n"+        let DisambConf{..} = disambConf+        let trainCRF = disambWith+        disambCRF <- D.trainOn format disambSchema split trainCRF+                        trainPathG evalPathG'Maybe+        return (guesser, disambCRF)++guessFile+    :: Functor f+    => F.Doc f s w              -- ^ Document format handler+    -> GuessData F.Tag          -- ^ Guesser+    -> String                   -- ^ Template for temporary file name+    -> Maybe FilePath           -- ^ File to guess+    -> (Maybe FilePath -> IO a) -- ^ Handler+    -> IO a+guessFile _ _ _ Nothing handler = handler Nothing+guessFile format GuessData{..} tmpl (Just path) handler =+    Temp.withTempFile "." tmpl $ \tmpPath tmpHandle -> do+        inp <- L.readFile path+        let GuessConf{..} = guessConf+        let out = G.guessDoc format guessNum guessSchema guesser inp+	hClose tmpHandle+        L.writeFile tmpPath out+        handler (Just tmpPath)
NLP/Concraft/Disamb.hs view
@@ -1,198 +1,147 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE MultiParamTypeClasses #-}  module NLP.Concraft.Disamb-( Tier (..)-, Tag (..)-, select-, splitWord-, splitSent-, Ox-, Schema-, Ob-, schema-, schematize-, TierConf-, tear-, deTear-, deTears-, Disamb+( Split+, TrainCRF+, TagCRF , disamb-, tagFile-, learn+, disambSent+, disambDoc+, trainOn ) where -import Control.Applicative ((<$>), (<*>))+import Control.Applicative ((<$>)) import Data.Maybe (fromJust) import Data.List (find)+import Data.Foldable (Foldable, foldMap) import qualified Data.Set as S import qualified Data.Map as M-import qualified Data.Text as T-import qualified Data.Text.Lazy as L import qualified Data.Vector as V--import Data.Binary (Binary, get, put)-import Data.Text.Binary ()+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.IO as L  import qualified Control.Monad.Ox as Ox-import qualified Data.CRF.Chain2.Pair as CRF-import qualified Numeric.SGD as SGD-import qualified Data.Tagset.Positional as TP--import NLP.Concraft.Morphosyntax-import qualified NLP.Concraft.Plain as P---- | A tier description.-data Tier = Tier {-    -- | Does it include the part of speech?-      withPos   :: Bool-    -- | Tier grammatical attributes.-    , withAtts  :: S.Set TP.Attr }--instance Binary Tier where-    put Tier{..} = put withPos >> put withAtts-    get = Tier <$> get <*> get---- | A tag with optional POS.-data Tag = Tag-    { pos   :: Maybe TP.POS-    , atts  :: M.Map TP.Attr T.Text }-    deriving (Show, Eq, Ord)--instance Binary Tag where-    put Tag{..} = put pos >> put atts-    get = Tag <$> get <*> get---- | Select tier attributes.-select :: Tier -> TP.Tag -> Tag-select Tier{..} tag = Tag-    { pos   = if withPos then Just (TP.pos tag) else Nothing-    , atts  = M.filterWithKey (\k _ -> k `S.member` withAtts) (TP.atts tag) }---- | The Ox monad specialized to word token type and text observations.--- TODO: Move to monad-ox package from here and from the nerf library.-type Ox t a = Ox.Ox (Word t) T.Text a---- | A schema is a block of the Ox computation performed within the--- context of the sentence and the absolute sentence position.-type Schema t a = V.Vector (Word t) -> Int -> Ox t a---- | An observation consist of an index (of list type) and an actual--- observation value.-type Ob = ([Int], T.Text)+import qualified Data.CRF.Chain2.Generic.External as CRF -schema :: Schema t ()-schema sent = \k -> do-    mapM_ (Ox.save . lowOrth) [k - 1, k, k + 1]-  where-    at          = Ox.atWith sent-    lowOrth i   = T.toLower <$> orth `at` i+import NLP.Concraft.Schema+import qualified NLP.Concraft.Morphosyntax as Mx+import qualified NLP.Concraft.Format as F --- | Schematize the input sentence according to 'schema' rules.-schematize :: Sent t -> CRF.Sent Ob t-schematize sent =+-- | Schematize the input sentence with according to 'schema' rules.+schematize :: Schema t a -> Mx.Sent t -> CRF.Sent Ob t+schematize schema sent =     [ CRF.mkWord (obs i) (lbs i)     | i <- [0 .. n - 1] ]   where     v = V.fromList sent     n = V.length v     obs = S.fromList . Ox.execOx . schema v-    lbs = tags . (v V.!)--type TierConf = (Tier, Tier)--tear :: TierConf -> TP.Tag -> (Tag, Tag)-tear (t1, t2) = (,) <$> select t1 <*> select t2+    lbs i = Mx.interpsSet w+        where w = v V.! i --- | Split tags between two layers.--- TODO: Add support for multiple layers.-splitWord :: TierConf -> Word TP.Tag -> Word (Tag, Tag)-splitWord cfg = mapWord (tear cfg)+-- | Split is just a function from an original tag form+-- to a complex tag form.+type Split r t = r -> t -splitSent :: TierConf -> Sent TP.Tag -> Sent (Tag, Tag)-splitSent ts = map (splitWord ts)+-- | Unsplit the complex tag (assuming, that it is one+-- of the interpretations of the word).+unSplit :: Eq t => Split r t -> Mx.Word r -> t -> r+unSplit split' word x = fromJust $ find ((==x) . split') (Mx.interps word) --- | The disambiguation model.-data Disamb = Disamb-    { crf       :: CRF.CRF Ob Tag Tag-    , tagset    :: TP.Tagset-    , tierConf  :: TierConf }+-- | CRF training function.+type TrainCRF o t c+    =  IO [CRF.SentL o t]           -- ^ Training data 'IO' action+    -> Maybe (IO [CRF.SentL o t])   -- ^ Maybe evalation data+    -> IO c                         -- ^ Resulting model -instance Binary Disamb where-    put Disamb{..} = put crf >> put tagset >> put tierConf-    get = Disamb <$> get <*> get <*> get+-- | CRF tagging function.+type TagCRF o t = CRF.Sent o t -> [t] --- | Determine the most probable label sequence.-disamb :: Disamb -> Sent TP.Tag -> [TP.Tag]-disamb Disamb{..} sent-    = deTears tierConf sent-    . CRF.tag crf-    . schematize-    . splitSent tierConf+-- | Perform context-sensitive disambiguation.+disamb+    :: (Ord r, Ord t)+    => Schema t a+    -> Split r t+    -> TagCRF Ob t+    -> Mx.Sent r+    -> [r]+disamb schema split tag sent+    = map (uncurry embed)+    . zip sent+    . tag+    . schematize schema+    . Mx.mapSent split     $ sent--deTears :: TierConf -> Sent TP.Tag -> [(Tag, Tag)] -> [TP.Tag]-deTears cfg sent tiered =-    [ deTear cfg word pair-    | (word, pair) <- zip sent tiered ]---- | Unsplit the list of tag pairs.  TODO: It can be done without the--- help of original word.-deTear :: TierConf -> Word TP.Tag -> (Tag, Tag) -> TP.Tag-deTear cfg word tiered =-    fromJust $ find-        ((==tiered) . tear cfg)-        (S.toList $ tags word)+  where+    embed = unSplit split --- | Tag the file.-tagFile-    :: T.Text           -- ^ Tag indicating unknown words-    -> Disamb-    -> FilePath         -- ^ File to tag (plain format)-    -> IO L.Text-tagFile ign dmb path =-    P.showPlain ign . map onSent <$> P.readPlain ign path+-- | Tag the sentence.+disambSent+    :: Ord t+    => F.Sent s w+    -> Schema t a+    -> Split F.Tag t+    -> TagCRF Ob t+    -> s -> s+disambSent F.Sent{..} schema split tag sent =+  flip mergeSent sent+    [ select wMap orig+    | (wMap, orig) <- zip+        (doDmb sent)+        (parseSent sent) ]   where-    onSent sent =-        [ choose tok y-        | (tok, y) <- zip sent ys ]-      where-        rs = map (fst . P.fromTok) sent-        xs = map (mapWord parseTag) rs-        ys = map showTag (disamb dmb xs)-        choose tok y = P.choose tok (S.singleton y)-        parseTag = TP.parseTag (tagset dmb)-        showTag  = TP.showTag (tagset dmb)+    F.Word{..} = wordHandler+    doDmb orig =+        let xs = map extract (parseSent orig)+        in  map (uncurry mkChoice) (zip xs (disamb schema split tag xs))+    mkChoice word x = Mx.mkWMap+        [ if x == y+            then (x, 1)+            else (y, 0)+        | y <- Mx.interps word ] --- | TODO: Abstract over the format type.-learn-    :: SGD.SgdArgs      -- ^ SGD parameters -    -> FilePath         -- ^ File with positional tagset definition-    -> T.Text        	-- ^ The tag indicating unknown words-    -> TierConf         -- ^ Tiered tagging configuration-    -> FilePath         -- ^ Train file (plain format)+-- | Disambiguate document.+disambDoc+    :: (Functor f, Ord t)+    => F.Doc f s w      -- ^ Document format handler+    -> Schema t a       -- ^ Observation schema+    -> Split F.Tag t    -- ^ Tiered tagging+    -> TagCRF Ob t      -- ^ CRF tagging function+    -> L.Text           -- ^ Input+    -> L.Text           -- ^ Output+disambDoc F.Doc{..} schema split tag =+    let onSent = disambSent sentHandler schema split tag+    in  showDoc . fmap onSent . parseDoc++-- | Train disamb model.+trainOn+    :: (Foldable f, Ord t)+    => F.Doc f s w      -- ^ Document format handler+    -> Schema t a       -- ^ Observation schema+    -> Split F.Tag t    -- ^ Tiered tagging+    -> TrainCRF Ob t c  -- ^ CRF training function+    -> FilePath         -- ^ Training file     -> Maybe FilePath   -- ^ Maybe eval file-    -> IO Disamb-learn sgdArgs tagsetPath ign tierConf trainPath evalPath'Maybe = do-    _tagset <- TP.parseTagset tagsetPath <$> readFile tagsetPath-    _crf <- CRF.train sgdArgs-        (schemed _tagset ign tierConf trainPath)-        (schemed _tagset ign tierConf <$> evalPath'Maybe)-    return $ Disamb _crf _tagset tierConf+    -> IO c             -- ^ Resultant model data+trainOn format schema split train trainPath evalPath'Maybe = do+    crf <- train+        (schemed format schema split trainPath)+        (schemed format schema split <$> evalPath'Maybe)+    return crf  -- | Schematized data from the plain file. schemed-    :: TP.Tagset -> T.Text -> TierConf-    -> FilePath -> IO [CRF.SentL Ob (Tag, Tag)]-schemed tagset _ign cfg =-    fmap (map onSent) . P.readPlain _ign+    :: (Foldable f, Ord t)+    => F.Doc f s w -> Schema t a -> Split F.Tag t+    -> FilePath -> IO [CRF.SentL Ob t]+schemed F.Doc{..} schema split path =+    foldMap onSent . parseDoc <$> L.readFile path   where+    F.Sent{..} = sentHandler+    F.Word{..} = wordHandler     onSent sent =-        zip (schematize xs') (map mkDist ys')+        [zip (schematize schema xs) (map mkDist xs)]       where-        (xs, ys) = unzip (map P.fromTok sent)-        xs' = map (mapWord   smash) xs-        ys' = map (mapChoice smash) ys-        smash = tear cfg . parseTag-        parseTag = TP.parseTag tagset-        mkDist = CRF.mkDist . M.toList . M.map unPositive+        xs  = map (Mx.mapWord split . extract) (parseSent sent)+        mkDist = CRF.mkDist . M.toList . Mx.unWMap . Mx.tagWMap
+ NLP/Concraft/Disamb/Positional.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-}++-- | The module provides functions for splitting positional tags.+-- They can be used together with the layered disambiguation model.++module NLP.Concraft.Disamb.Positional+( Tier (..)+, Part (..)+, select+, split+, tierConfDefault+) where++import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, put, get)+import Data.Text.Binary ()+import qualified Data.Set as S+import qualified Data.Map as M+import qualified Data.Text as T+import qualified Data.Tagset.Positional as TP++-- | A tier description.+data Tier = Tier {+    -- | Does it include the part of speech?+      withPos   :: Bool+    -- | Tier grammatical attributes.+    , withAtts  :: S.Set TP.Attr }++instance Binary Tier where+    put Tier{..} = put withPos >> put withAtts+    get = Tier <$> get <*> get++-- | An atomic part of morphosyntactic tag with optional POS.+data Part = Part+    { pos   :: Maybe TP.POS+    , atts  :: M.Map TP.Attr T.Text }+    deriving (Show, Eq, Ord)++instance Binary Part where+    put Part{..} = put pos >> put atts+    get = Part <$> get <*> get++-- | Select tier attributes.+select :: Tier -> TP.Tag -> Part+select Tier{..} tag = Part+    { pos   = if withPos then Just (TP.pos tag) else Nothing+    , atts  = M.filterWithKey (\k _ -> k `S.member` withAtts) (TP.atts tag) }++-- | Split the positional tag.+split :: [Tier] -> TP.Tag -> [Part]+split tiers tag =+    [ select tier tag+    | tier <- tiers ]++-- | Default tiered tagging configuration.+tierConfDefault :: [Tier]+tierConfDefault =+    [tier1, tier2]+  where+    tier1 = Tier True $ S.fromList ["cas", "per"]+    tier2 = Tier False $ S.fromList+        [ "nmb", "gnd", "deg", "asp" , "ngt", "acm"+        , "acn", "ppr", "agg", "vlc", "dot" ]
+ NLP/Concraft/Disamb/Tiered.hs view
@@ -0,0 +1,265 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE BangPatterns #-}++module NLP.Concraft.Disamb.Tiered+(+-- * Tiered model+  Ob (..)+, Lb (..)+, Feat (..)+, CRF (..)+, train+, tag++-- * Feature selection+, FeatSel+, selectHidden+, selectPresent+) where++import Control.Applicative ((<$>), (<*>))+import Control.Comonad.Trans.Store (store)+import Control.Monad (guard)+import Data.Ix (Ix, inRange, range)+import Data.Maybe (catMaybes, fromJust)+import Data.List (zip4, foldl1')+import Data.Lens.Common (Lens(..))+import Data.Binary (Binary, get, put, Put, Get)+import Data.Vector.Binary ()+import qualified Data.Map as M+import qualified Data.Vector as V+import qualified Data.Array.Unboxed as A++import Data.CRF.Chain2.Generic.Codec+    ( Codec(..), mkCodec, encodeDataL+    , encodeSent, decodeLabels, unJust )+import Data.CRF.Chain2.Generic.Model+    ( FeatGen(..), Model, FeatSel+    , selectHidden, selectPresent+    , core, withCore )+import Data.CRF.Chain2.Generic.Internal (FeatIx(..))+import qualified Data.CRF.Chain2.Generic.Inference as I+import qualified Data.CRF.Chain2.Generic.Train as Train+import qualified Data.CRF.Chain2.Generic.FeatMap as F+import qualified Control.Monad.Codec as C+import qualified Numeric.SGD as SGD+import qualified NLP.Concraft.Disamb as D++-- | Observation.+newtype Ob = Ob { unOb :: Int } deriving (Show, Eq, Ord, Ix, Binary)++-- | [Sub]label.+newtype Lb = Lb { unLb :: Int } deriving (Show, Eq, Ord, Ix, Binary)++-- | Feature.+data Feat+    = TFeat3+        { x1    :: {-# UNPACK #-} !Lb+        , x2    :: {-# UNPACK #-} !Lb+        , x3    :: {-# UNPACK #-} !Lb+        , ln    :: {-# UNPACK #-} !Int }+    | TFeat2+        { x1    :: {-# UNPACK #-} !Lb+        , x2    :: {-# UNPACK #-} !Lb+        , ln    :: {-# UNPACK #-} !Int }+    | TFeat1+        { x1    :: {-# UNPACK #-} !Lb+        , ln    :: {-# UNPACK #-} !Int }+    | OFeat+        { ob    :: {-# UNPACK #-} !Ob+        , x1    :: {-# UNPACK #-} !Lb+        , ln    :: {-# UNPACK #-} !Int }+    deriving (Show, Eq, Ord)++instance Binary Feat where+    put (OFeat o x k)       = putI 0 >> put o >> put x >> put k+    put (TFeat3 x y z k)    = putI 1 >> put x >> put y >> put z >> put k+    put (TFeat2 x y k)      = putI 2 >> put x >> put y >> put k+    put (TFeat1 x k)        = putI 3 >> put x >> put k+    get = getI >>= \i -> case i of+        0   -> OFeat  <$> get <*> get <*> get+        1   -> TFeat3 <$> get <*> get <*> get <*> get+        2   -> TFeat2 <$> get <*> get <*> get+        3   -> TFeat1 <$> get <*> get+        _   -> error "get feature: unknown code"++putI :: Int -> Put+putI = put+{-# INLINE putI #-}++getI :: Get Int+getI = get+{-# INLINE getI #-}++-- | Feature generation for complex [Lb] label type.+featGen :: FeatGen Ob [Lb] Feat+featGen = FeatGen+    { obFeats   = obFeats'+    , trFeats1  = trFeats1'+    , trFeats2  = trFeats2'+    , trFeats3  = trFeats3' }+  where+    obFeats' ob' xs =+        [ OFeat ob' x k+        | (x, k) <- zip xs [0..] ]+    trFeats1' xs =+        [ TFeat1 x k+        | (x, k) <- zip xs [0..] ]+    trFeats2' xs1 xs2 =+        [ TFeat2 x1' x2' k+        | (x1', x2', k) <-+          zip3 xs1 xs2 [0..] ]+    trFeats3' xs1 xs2 xs3 =+        [ TFeat3 x1' x2' x3' k+        | (x1', x2', x3', k) <-+          zip4 xs1 xs2 xs3 [0..] ]++-- | Codec internal data.  The first component is used to+-- encode observations of type a, the second one is used to+-- encode labels of type [b].+type CodecData a b =+    ( C.AtomCodec a+    , V.Vector (C.AtomCodec (Maybe b)) )++obLens :: Lens (a, b) a+obLens = Lens $ \(a, b) -> store (\a' -> (a', b)) a++lbLens :: Int -> Lens (a, V.Vector b) b+lbLens k = Lens $ \(a, b) -> store+    (\x -> (a, b V.// [(k, x)]))+    (b V.! k)++-- | Codec dependes on the number of layers. +codec :: (Ord a, Ord b) => Int -> Codec a [b] (CodecData a b) Ob [Lb]+codec n = Codec+    { empty =+        let x = C.execCodec C.empty (C.encode C.idLens Nothing)+        in  (C.empty, V.replicate n x)+    , encodeObU = fmap Ob . C.encode' obLens+    , encodeObN = fmap (fmap Ob) . C.maybeEncode obLens+    , encodeLbU = \ xs -> sequence+        [ Lb <$> C.encode (lbLens k) (Just x)+        | (x, k) <- zip xs [0..] ]+    , encodeLbN = \ xs ->+        let encode lens x = C.maybeEncode lens (Just x) >>= \mx -> case mx of+                Just x' -> return x'+                Nothing -> fromJust <$> C.maybeEncode lens Nothing+        in  sequence+                [ Lb <$> encode (lbLens k) x+                | (x, k) <- zip xs [0..] ]+    , decodeLbC = \ xs -> sequence <$> sequence+        [ C.decode (lbLens k) (unLb x)+        | (x, k) <- zip xs [0..] ]+    , hasLabel = \ cdcData xs -> and+        [ M.member+            (Just x)+            (C.to $ snd cdcData V.! k)+        | (x, k) <- zip xs [0..] ] }++-- | Dummy feature index.+dummy :: FeatIx+dummy = FeatIx (-1)+{-# INLINE dummy #-}++-- | Transition map restricted to a particular tagging layer.+type TransMap = A.UArray (Lb, Lb, Lb) FeatIx++-- | CRF feature map.+data FeatMap a = FeatMap+    { transMaps	:: V.Vector TransMap+    , otherMap 	:: M.Map Feat FeatIx }++instance Binary (FeatMap Feat) where+    put FeatMap{..} = put transMaps >> put otherMap+    get = FeatMap <$> get <*> get++instance F.FeatMap FeatMap Feat where+    featIndex (TFeat3 x y z k) (FeatMap v _) = do+        m  <- v V.!? k+        ix <- m !? (x, y, z)+        guard (ix /= dummy)+        return ix+    featIndex x (FeatMap _ m) = M.lookup x m+    mkFeatMap xs = FeatMap+        ( V.fromList+            [ mkArray . catMaybes $+                map (getTFeat3 k) xs+            | k <- [0 .. maxLayerNum xs] ] )+        (M.fromList (filter (isOther . fst) xs))+      where+        maxLayerNum = maximum . map (ln.fst)+        getTFeat3 i (TFeat3 x y z j, v)+            | i == j                = Just ((x, y, z), v)+            | otherwise             = Nothing+        getTFeat3 _ _               = Nothing+        isOther (TFeat3 _ _ _ _)    = False+        isOther _                   = True+        mkArray ys =+            let p = foldl1' updateMin (map fst ys)+                q = foldl1' updateMax (map fst ys)+                updateMin (!x, !y, !z) (x', y', z') =+                    (min x x', min y y', min z z')+                updateMax (!x, !y, !z) (x', y', z') =+                    (max x x', max y y', max z z')+                zeroed pq = A.array pq [(k, dummy) | k <- range pq]+            in  zeroed (p, q) A.// ys++(!?) :: (Ix i, A.IArray a b) => a i b -> i -> Maybe b+m !? x = if inRange (A.bounds m) x+    then Just (m A.! x)+    else Nothing+{-# INLINE (!?) #-}++-- | CRF model data.+data CRF a b = CRF+    { numOfLayers   :: Int+    , codecData     :: CodecData a b+    , model         :: Model FeatMap Ob [Lb] Feat }++instance (Ord a, Ord b, Binary a, Binary b) => Binary (CRF a b) where+    put CRF{..} = put numOfLayers >> put codecData >> put (core model)+    get = CRF <$> get <*> get <*> do+        _core <- get+        return $ withCore _core featGen++-- | Codec specification given the number of layers.+codecSpec+    :: (Ord a, Ord b) => Int+    -> Train.CodecSpec a [b] (CodecData a b) Ob [Lb]+codecSpec n = Train.CodecSpec+    { Train.mkCodec = mkCodec (codec n)+    , Train.encode  = encodeDataL (codec n) }++-- | Train the CRF using the stochastic gradient descent method.+-- Use the provided feature selection function to determine model+-- features.+train+    :: (Ord o, Ord t)+    => Int                          -- ^ Number of tagging layers+    -> FeatSel Ob [Lb] Feat         -- ^ Feature selection+    -> SGD.SgdArgs                  -- ^ Args for SGD+    -> D.TrainCRF o [t] (CRF o t)+train n featSel sgdArgs trainIO evalIO'Maybe = do+    (_codecData, _model) <- Train.train+        sgdArgs+        (codecSpec n)+        featGen+        featSel+        trainIO+        evalIO'Maybe+    return $ CRF n _codecData _model++-- | Find the most probable label sequence.+tag :: (Ord o, Ord t) => CRF o t -> D.TagCRF o [t]+tag CRF{..} sent+    = onWords . decodeLabels cdc codecData+    . I.tag model . encodeSent cdc codecData+    $ sent+  where+    cdc = codec numOfLayers+    onWords xs =+        [ unJust cdc codecData word x+        | (word, x) <- zip sent xs ]
+ NLP/Concraft/Format.hs view
@@ -0,0 +1,44 @@+-- | The module provides several abstractions for representing external+-- data formats.  Concraft will be able to work with any format which+-- implements those abstractions.++module NLP.Concraft.Format+( Tag+, Word (..)+, Sent (..)+, Doc (..)+) where++import Prelude hiding (words, unwords)+import qualified Data.Text as T+import qualified Data.Text.Lazy as L+import qualified NLP.Concraft.Morphosyntax as M++-- | Textual representation of morphposyntactic tag.+type Tag = T.Text++-- | Words handler.+data Word w = Word {+    -- | Extract information relevant for tagging.+      extract       :: w -> M.Word Tag+    -- | Select the set of morphosyntactic interpretations.+    , select        :: M.WMap Tag -> w -> w }++-- | Sentence handler.+data Sent s w = Sent {+    -- | Split sentence into a list of words.+      parseSent     :: s -> [w]+    -- | Merge words with a sentence.+    , mergeSent     :: [w] -> s -> s+    -- | Words handler.+    , wordHandler   :: Word w }++-- | Document format.+data Doc f s w = Doc {+    -- | Parse textual interpretations into a functor with+    -- sentence elements.+      parseDoc      :: L.Text -> f s+    -- | Show textual reprezentation of a document.+    , showDoc       :: f s -> L.Text+    -- | Sentence handler.+    , sentHandler   :: Sent s w }
+ NLP/Concraft/Format/Plain.hs view
@@ -0,0 +1,186 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++-- | Simple format for morphosyntax representation which+-- assumes that all tags have a textual representation+-- with no spaces inside and that one of the tags indicates+-- unknown words.++module NLP.Concraft.Format.Plain+( plainFormat+) where++import Control.Arrow (first)+import Data.Monoid (Monoid, mappend, mconcat)+import Data.Maybe (catMaybes)+import Data.List (groupBy)+import qualified Data.Map as M+import qualified Data.Text as T+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Builder as L++import qualified NLP.Concraft.Morphosyntax as Mx+import qualified NLP.Concraft.Format as F++-- | No space, space or newline.+data Space+    = None+    | Space+    | NewLine+    deriving (Show, Eq, Ord)++-- | A token.+data Token = Token+    { orth      :: T.Text+    , space     :: Space+    , known     :: Bool+    -- | Interpretations of the token, each interpretation annotated+    -- with a /disamb/ Boolean value (if 'True', the interpretation+    -- is correct within the context).+    , interps   :: M.Map Interp Bool }+    deriving (Show, Eq, Ord)+    +data Interp = Interp+    { base  :: Maybe T.Text+    , tag   :: F.Tag }+    deriving (Show, Eq, Ord)++noneBase :: T.Text+noneBase = "None"++-- | Create document handler given value of the /ignore/ tag.+plainFormat :: F.Tag -> F.Doc [] [Token] Token+plainFormat ign = F.Doc (parsePlain ign) (showPlain ign) sentHandler++-- | Sentence handler.+sentHandler :: F.Sent [Token] Token+sentHandler = F.Sent id (\xs _ -> xs) wordHandler++-- | Word handler.+wordHandler :: F.Word Token+wordHandler = F.Word extract select++-- | Extract information relevant for tagging.+extract :: Token -> Mx.Word F.Tag+extract tok = Mx.Word+    { Mx.orth       = orth tok+    , Mx.tagWMap    = Mx.mkWMap+        [ (tag x, if disamb then 1 else 0)+        | (x, disamb) <- M.toList (interps tok) ]+    , Mx.oov        = not (known tok) }++-- | Select interpretations.+select :: Mx.WMap F.Tag -> Token -> Token+select wMap tok =+    tok { interps = newInterps }+  where+    wSet = M.fromList . map (first tag) . M.toList . interps+    asDmb x = if x > 0+        then True+        else False+    newInterps = M.fromList $+        [ case M.lookup (tag interp) (Mx.unWMap wMap) of+            Just x  -> (interp, asDmb x)+            Nothing -> (interp, False)+        | interp <- M.keys (interps tok) ]+            ++ catMaybes+        [ if tag `M.member` wSet tok+            then Nothing+            else Just (Interp Nothing tag, asDmb x)+        | (tag, x) <- M.toList (Mx.unWMap wMap) ]++parsePlain :: F.Tag -> L.Text -> [[Token]]+parsePlain ign = map (parseSent ign) . init . L.splitOn "\n\n"++parseSent :: F.Tag -> L.Text -> [Token]+parseSent ign+    = map (parseWord ignL)+    . groupBy (\_ x -> cond x)+    . L.lines+  where+    cond = ("\t" `L.isPrefixOf`)+    ignL = L.fromStrict ign++parseWord :: L.Text -> [L.Text] -> Token+parseWord ign xs =+    (Token _orth _space _known _interps)+  where+    (_orth, _space) = parseHeader (head xs)+    ys          = map (parseInterp ign) (tail xs)+    _known      = not (Nothing `elem` ys)+    _interps    = M.fromListWith max (catMaybes ys)++parseInterp :: L.Text -> L.Text -> Maybe (Interp, Bool)+parseInterp ign =+    doIt . tail . L.splitOn "\t"+  where+    doIt [form, tag]+        | tag == ign    = Nothing+        | otherwise     = Just $+            (mkInterp form tag, False)+    doIt [form, tag, "disamb"] = Just $+        (mkInterp form tag, True)+    doIt xs = error $ "parseInterp: " ++ show xs+    mkInterp form tag+        | formS == noneBase = Interp Nothing tagS+        | otherwise         = Interp (Just formS) tagS+      where+        formS   = L.toStrict form+        tagS    = L.toStrict tag++parseHeader :: L.Text -> (T.Text, Space)+parseHeader xs =+    let [_orth, space] = L.splitOn "\t" xs+    in  (L.toStrict _orth, parseSpace space)++parseSpace :: L.Text -> Space+parseSpace "none"    = None+parseSpace "space"   = Space+parseSpace "newline" = NewLine+parseSpace "newlines" = NewLine -- TODO: Remove this temporary fix+parseSpace xs        = error ("parseSpace: " ++ L.unpack xs)++-- | Printing.++-- | An infix synonym for 'mappend'.+(<>) :: Monoid m => m -> m -> m+(<>) = mappend+{-# INLINE (<>) #-}++showPlain :: F.Tag -> [[Token]] -> L.Text+showPlain ign =+    L.toLazyText . mconcat  . map (\xs -> buildSent ign xs <> "\n")++buildSent :: F.Tag -> [Token] -> L.Builder+buildSent ign = mconcat . map (buildWord ign)++buildWord :: F.Tag -> Token -> L.Builder+buildWord ign tok+    =  L.fromText (orth tok) <> "\t"+    <> buildSpace (space tok) <> "\n"+    <> buildKnown ign (known tok)+    <> buildInterps (M.toList $ interps tok)++buildInterps :: [(Interp, Bool)] -> L.Builder+buildInterps interps = mconcat+    [ "\t" <> buildBase interp <>+      "\t" <> buildTag  interp <>+      if dmb+        then "\tdisamb\n"+        else "\n"+    | (interp, dmb) <- interps ]+  where+    buildTag    = L.fromText . tag+    buildBase x = case base x of+        Just b  -> L.fromText b+        Nothing -> L.fromText noneBase++buildSpace :: Space -> L.Builder+buildSpace None     = "none"+buildSpace Space    = "space"+buildSpace NewLine  = "newline"++buildKnown :: F.Tag -> Bool -> L.Builder+buildKnown _   True     = ""+buildKnown ign False    =  "\t" <> L.fromText noneBase+                        <> "\t" <> L.fromText ign <> "\n"
NLP/Concraft/Guess.hs view
@@ -1,131 +1,134 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}  module NLP.Concraft.Guess-( Ox-, Schema-, Ob-, schema-, schematize-, Guesser (..)+( Guesser (..) , guess-, tagFile-, learn+, include+, guessSent+, guessDoc+, trainOn ) where -import Control.Applicative (pure, (<$>), (<*>))+import Prelude hiding (words)+import Control.Applicative ((<$>))+import Data.Binary (Binary)+import Data.Foldable (Foldable, foldMap)+import Data.Text.Binary () import qualified Data.Set as S import qualified Data.Map as M-import qualified Data.Text as T import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.IO as L import qualified Data.Vector as V -import Data.Binary (Binary, get, put)-import Data.Text.Binary ()- import qualified Control.Monad.Ox as Ox-import qualified Control.Monad.Ox.Text as Ox import qualified Data.CRF.Chain1.Constrained as CRF import qualified Numeric.SGD as SGD -import NLP.Concraft.Morphosyntax-import qualified NLP.Concraft.Plain as P---- | The Ox monad specialized to word token type and text observations.--- TODO: Move to monad-ox package from here and from the nerf library.-type Ox t a = Ox.Ox (Word t) T.Text a---- | A schema is a block of the Ox computation performed within the--- context of the sentence and the absolute sentence position.-type Schema t a = V.Vector (Word t) -> Int -> Ox t a---- | An observation consist of an index (of list type) and an actual--- observation value.-type Ob = ([Int], T.Text)--schema :: Schema t ()-schema sent = \k -> do-    mapM_ (Ox.save . lowPref k) [1, 2]-    mapM_ (Ox.save . lowSuff k) [1, 2]-    Ox.save (knownAt k)-    Ox.save (isBeg k <> pure "-" <> shapeP k)-  where-    at          = Ox.atWith sent-    lowOrth i   = T.toLower <$> orth `at` i-    lowPref i j = Ox.prefix j =<< lowOrth i-    lowSuff i j = Ox.suffix j =<< lowOrth i-    shape i     = Ox.shape <$> orth `at` i-    shapeP i    = Ox.pack <$> shape i-    knownAt i   = boolF <$> known `at` i-    isBeg i     = (Just . boolF) (i == 0)-    boolF True  = "T"-    boolF False = "F"-    x <> y      = T.append <$> x <*> y+import NLP.Concraft.Schema+import qualified NLP.Concraft.Morphosyntax as Mx+import qualified NLP.Concraft.Format as F  -- | Schematize the input sentence with according to 'schema' rules.-schematize :: Ord t => Sent t -> CRF.Sent Ob t-schematize sent =+schematize :: Ord t => Schema t a -> Mx.Sent t -> CRF.Sent Ob t+schematize schema sent =     [ CRF.Word (obs i) (lbs i)     | i <- [0 .. n - 1] ]   where     v = V.fromList sent     n = V.length v     obs = S.fromList . Ox.execOx . schema v-    lbs = tags . (v V.!)+    lbs i +        | Mx.oov w  = S.empty+        | otherwise = Mx.interpsSet w+        where w = v V.! i  -- | A guesser represented by the conditional random field.-data Guesser t = Guesser-    { crf   :: CRF.CRF Ob t -- ^ The CRF model-    , ign   :: t            -- ^ The tag indicating unkown words-    }+newtype Guesser t = Guesser { crf :: CRF.CRF Ob t }+    deriving (Binary) -instance (Ord t, Binary t) => Binary (Guesser t) where-    put Guesser{..} = put crf >> put ign-    get = Guesser <$> get <*> get+-- | Determine the 'k' most probable labels for each word in the sentence.+guess :: Ord t => Int -> Schema t a -> Guesser t -> Mx.Sent t -> [[t]]+guess k schema gsr sent = CRF.tagK k (crf gsr) (schematize schema sent) --- | Determine the 'k' most probable labels for each unknown word--- in the sentence.-guess :: Ord t => Int -> Guesser t -> Sent t -> [[t]]-guess k gsr sent = CRF.tagK k (crf gsr) (schematize sent)-{-# INLINE guess #-}+-- | Include guessing results into weighted tag maps+-- assigned to individual words.+includeWMaps :: Ord t => Mx.Sent t -> [[t]] -> [Mx.WMap t]+includeWMaps words guessed =+    [ if Mx.oov word+        then addInterps (Mx.tagWMap word) xs+        else Mx.tagWMap word+    | (xs, word) <- zip guessed words ]+  where+    -- Add new interpretations.+    addInterps wm xs = Mx.mkWMap+        $  M.toList (Mx.unWMap wm)+        ++ zip xs [0, 0 ..] --- | Tag the file.-tagFile-    :: Int              -- ^ Guesser argument-    -> Guesser T.Text   -- ^ Guesser itself-    -> FilePath         -- ^ File to tag (plain format)-    -> IO L.Text-tagFile k gsr path =-    P.showPlain (ign gsr) . map onSent <$> P.readPlain (ign gsr) path+-- | Include guessing results into the sentence.+include :: Ord t => Mx.Sent t -> [[t]] -> Mx.Sent t+include words guessed =+    [ word { Mx.tagWMap = wMap }+    | (word, wMap) <- zip words wMaps ]   where-    onSent sent =-        let (xs, _) = unzip (map P.fromTok sent)-            yss = guess k gsr xs-        in  [ if P.known tok-                then tok-                else P.addNones False tok ys-            | (tok, ys) <- zip sent yss ]+    wMaps = includeWMaps words guessed --- | TODO: Abstract over the format type.-learn-    :: SGD.SgdArgs      -- ^ SGD parameters -    -> T.Text        	-- ^ The tag indicating unknown words-    -> FilePath         -- ^ Train file (plain format)+-- | Tag sentence in external format.  Selected interpretations+-- (tags correct within the context) will be preserved.+guessSent :: F.Sent s w -> Int -> Schema F.Tag a -> Guesser F.Tag -> s -> s+guessSent F.Sent{..} k schema gsr sent = flip mergeSent sent+    [ select wMap word+    | (wMap, word) <- zip wMaps (parseSent sent) ]+  where+    -- Extract word handler.+    F.Word{..} = wordHandler+    -- Word in internal format.+    words   = map extract (parseSent sent)+    -- Guessed lists of interpretations for individual words.+    guessed = guess k schema gsr words+    -- Resultant weighted maps. +    wMaps   = includeWMaps words guessed++-- | Tag file.+guessDoc+    :: Functor f+    => F.Doc f s w  	-- ^ Document format handler+    -> Int              -- ^ Guesser argument+    -> Schema F.Tag a	-- ^ Observation schema+    -> Guesser F.Tag    -- ^ Guesser itself+    -> L.Text           -- ^ Input+    -> L.Text           -- ^ Output+guessDoc F.Doc{..} k schema gsr+    = showDoc +    . fmap (guessSent sentHandler k schema gsr)+    . parseDoc++-- | Train guesser.+trainOn+    :: Foldable f+    => F.Doc f s w      -- ^ Document format handler+    -> Schema F.Tag a	-- ^ Observation schema+    -> SGD.SgdArgs      -- ^ SGD parameters +    -> FilePath         -- ^ Training file     -> Maybe FilePath   -- ^ Maybe eval file-    -> IO (Guesser T.Text)-learn sgdArgs _ign trainPath evalPath'Maybe = do+    -> IO (Guesser F.Tag)+trainOn format schema sgdArgs trainPath evalPath'Maybe = do     _crf <- CRF.train sgdArgs-        (schemed _ign trainPath)-        (schemed _ign <$> evalPath'Maybe)+        (schemed format schema trainPath)+        (schemed format schema <$> evalPath'Maybe)         (const CRF.presentFeats)-    return $ Guesser _crf _ign+    return $ Guesser _crf  -- | Schematized data from the plain file.-schemed :: T.Text -> FilePath -> IO [CRF.SentL Ob T.Text]-schemed _ign =-    fmap (map onSent) . P.readPlain _ign+schemed+    :: Foldable f => F.Doc f s w -> Schema F.Tag a+    -> FilePath -> IO [CRF.SentL Ob F.Tag]+schemed F.Doc{..} schema path =+    foldMap onSent . parseDoc <$> L.readFile path   where+    F.Sent{..} = sentHandler+    F.Word{..} = wordHandler     onSent sent =-        let (xs, ys) = unzip (map P.fromTok sent)-            mkDist = CRF.mkDist . M.toList . M.map unPositive-        in  zip (schematize xs) (map mkDist ys)+        let xs = map extract (parseSent sent)+            mkProb = CRF.mkProb . M.toList . Mx.unWMap . Mx.tagWMap+        in  [zip (schematize schema xs) (map mkProb xs)]
NLP/Concraft/Morphosyntax.hs view
@@ -1,70 +1,68 @@ {-# LANGUAGE RecordWildCards #-} +-- | Types and functions related to the morphosyntax data layer.+ module NLP.Concraft.Morphosyntax-( Word (..)+( +-- * Morphosyntax data+  Sent+, Word (..) , mapWord-, Sent-, Choice-, mapChoice-, Positive (unPositive)-, (<+>)-, mkPositive-, best-, known+, mapSent+, interpsSet+, interps+-- * Weighted collection+, WMap (unWMap)+, mkWMap+, mapWMap ) where  import Control.Arrow (first)-import Data.Ord (comparing)-import Data.List (maximumBy) import qualified Data.Set as S import qualified Data.Map as M import qualified Data.Text as T --- | A word parametrized over the tag type.++-- | A sentence of 'Word's.+type Sent t = [Word t]++-- | A word parametrized over a tag type. data Word t = Word {     -- | Orthographic form.-      orth  :: T.Text-    -- | Set of word interpretations.-    , tags  :: S.Set t }-    deriving (Show, Read, Eq, Ord)+      orth      :: T.Text+    -- | Set of word interpretations.  To each interpretation+    -- a "weight of correctness within the context" is assigned.+    , tagWMap   :: WMap t+    -- | Out-of-vocabulary (OOV) word, i.e. word unknown to the+    -- morphosyntactic analyser.+    , oov       :: Bool }+    deriving (Show, Eq, Ord) +-- | Map function over word tags. mapWord :: Ord b => (a -> b) -> Word a -> Word b-mapWord f Word{..} = Word-    { orth = orth-    , tags = S.fromList . map f . S.toList $ tags }+mapWord f w = w { tagWMap = mapWMap f (tagWMap w) } --- | A sentence of 'Word's.-type Sent t = [Word t]+-- | Map function over sentence tags.+mapSent :: Ord b => (a -> b) -> Sent a -> Sent b+mapSent = map . mapWord --- | Interpretations chosen in the given context with--- corresponding positive weights.-type Choice t = M.Map t (Positive Double)+-- | Interpretations of the word.+interpsSet :: Word t -> S.Set t+interpsSet = M.keysSet . unWMap . tagWMap --- | Positive number.-newtype Positive a = Positive { unPositive :: a }-    deriving (Show, Eq, Ord)+-- | Interpretations of the word.+interps :: Word t -> [t]+interps = S.toList . interpsSet -(<+>) :: Num a => Positive a -> Positive a -> Positive a-Positive x <+> Positive y = Positive (x + y)-{-# INLINE (<+>) #-} -mapChoice :: Ord b => (a -> b) -> Choice a -> Choice b-mapChoice f = M.fromListWith (<+>) . map (first f) . M.toList--mkPositive :: (Num a, Ord a) => a -> Positive a-mkPositive x-    | x > 0     = Positive x-    | otherwise = error "mkPositive: not a positive number"-{-# INLINE mkPositive #-}+-- | A weighted collection of type @a@ elements.+newtype WMap a = WMap { unWMap :: M.Map a Double }+    deriving (Show, Eq, Ord) --- | Retrieve the most probable interpretation.-best :: Choice t -> t-best c-    | M.null c  = error "best: null choice" -    | otherwise = fst . maximumBy (comparing snd) $ M.toList c+-- | Make a weighted collection.+mkWMap :: Ord a => [(a, Double)] -> WMap a+mkWMap = WMap . M.fromListWith (+) . filter ((>=0).snd) --- | A word is considered to be known when the set of possible--- interpretations is not empty.-known :: Word t -> Bool-known = not . S.null . tags-{-# INLINE known #-}+-- | Map function over weighted collection elements. +mapWMap :: Ord b => (a -> b) -> WMap a -> WMap b+mapWMap f = mkWMap . map (first f) . M.toList . unWMap
− NLP/Concraft/Plain.hs
@@ -1,196 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}---- | Simple format for morphosyntax representation which--- assumes that all tags have a textual representation--- with no spaces inside and that one of the tags indicates--- unknown words.--module NLP.Concraft.Plain-(--- * Types-  Space (..)-, Token (..)-, Interp (..)---- * Interface-, fromTok-, choose-, addInterps-, addNones---- * Parsing-, readPlain-, parsePlain-, parseSent---- * Showing-, writePlain-, showPlain-, showSent-, showWord-) where--import Data.Monoid (Monoid, mappend, mconcat)-import Data.Maybe (catMaybes)-import Data.List (groupBy)-import qualified Data.Set as S-import qualified Data.Map as M-import qualified Data.Text as T-import qualified Data.Text.Lazy as L-import qualified Data.Text.Lazy.IO as L-import qualified Data.Text.Lazy.Builder as L--import qualified NLP.Concraft.Morphosyntax as Mx---- | No space, space or newline.-data Space-    = None-    | Space-    | NewLine-    deriving (Show, Eq, Ord)---- | A token.-data Token = Token-    { orth      :: T.Text-    , space     :: Space-    , known     :: Bool-    -- | Interpretations with disambiguation info.-    , interps   :: M.Map Interp Bool }-    deriving (Show, Eq, Ord)-    -data Interp = Interp-    { base      :: T.Text-    , tag       :: T.Text }-    deriving (Show, Eq, Ord)---- | Extract information relevant for tagging.-fromTok :: Token -> (Mx.Word T.Text, Mx.Choice T.Text)-fromTok tok =-    (word, choice)-  where-    word = Mx.Word-        { Mx.orth   = orth tok-        , Mx.tags   = if known tok-            then S.fromList . map tag . M.keys $ interps tok-            else S.empty }-    choice = M.fromListWith (Mx.<+>)-        [ (tag x, Mx.mkPositive 1)-        | (x, True) <- M.toList (interps tok) ]---- | Mark all interpretations with tag component beeing a member of--- the given choice set with disamb annotations.-choose :: Token -> S.Set T.Text -> Token-choose tok choice =-    tok { interps = (M.fromList . map mark . M.keys) (interps tok) }-  where-    mark ip -        | tag ip `S.member` choice  = (ip, True) -        | otherwise                 = (ip, False)---- | Add new interpretations with given disamb annotation.-addInterps :: Bool -> Token -> [Interp] -> Token-addInterps dmb tok xs =-    let newIps = M.fromList [(x, dmb) | x <- xs]-    in  tok { interps = M.unionWith max newIps (interps tok) }---- | Add new interpretations with "None" base and given disamb annotation.-addNones :: Bool -> Token -> [T.Text] -> Token-addNones dmb tok = addInterps dmb tok . map (Interp "None")--readPlain :: T.Text -> FilePath -> IO [[Token]]-readPlain ign = fmap (parsePlain ign) . L.readFile--parsePlain :: T.Text -> L.Text -> [[Token]]-parsePlain ign = map (parseSent ign) . init . L.splitOn "\n\n"--parseSent :: T.Text -> L.Text -> [Token]-parseSent ign-    = map (parseWord ignL)-    . groupBy (\_ x -> cond x)-    . L.lines-  where-    cond = ("\t" `L.isPrefixOf`)-    ignL = L.fromStrict ign--parseWord :: L.Text -> [L.Text] -> Token-parseWord ign xs =-    (Token _orth _space _known _interps)-  where-    (_orth, _space) = parseHeader (head xs)-    ys          = map (parseInterp ign) (tail xs)-    _known      = not (Nothing `elem` ys)-    _interps    = M.fromList (catMaybes ys)--parseInterp :: L.Text -> L.Text -> Maybe (Interp, Bool)-parseInterp ign =-    doIt . tail . L.splitOn "\t"-  where-    doIt [form, tag]-        | tag == ign    = Nothing-        | otherwise     = Just $-            (mkInterp form tag, False)-    doIt [form, tag, "disamb"] = Just $-        (mkInterp form tag, True)-    doIt xs = error $ "parseInterp: " ++ show xs-    mkInterp form tag = Interp (L.toStrict form) (L.toStrict tag)--parseHeader :: L.Text -> (T.Text, Space)-parseHeader xs =-    let [_orth, space] = L.splitOn "\t" xs-    in  (L.toStrict _orth, parseSpace space)--parseSpace :: L.Text -> Space-parseSpace "none"    = None-parseSpace "space"   = Space-parseSpace "newline" = NewLine-parseSpace "newlines" = NewLine -- TODO: Remove this temporary fix-parseSpace xs        = error ("parseSpace: " ++ L.unpack xs)---- | Printing.---- | An infix synonym for 'mappend'.-(<>) :: Monoid m => m -> m -> m-(<>) = mappend-{-# INLINE (<>) #-}--writePlain :: T.Text -> FilePath -> [[Token]] -> IO ()-writePlain ign path = L.writeFile path . showPlain ign --showPlain :: T.Text -> [[Token]] -> L.Text-showPlain ign =-    L.toLazyText . mconcat  . map (\xs -> buildSent ign xs <> "\n")--showSent :: T.Text -> [Token] -> L.Text-showSent ign = L.toLazyText . buildSent ign--showWord :: T.Text -> Token -> L.Text-showWord ign = L.toLazyText . buildWord ign--buildSent :: T.Text -> [Token] -> L.Builder-buildSent ign = mconcat . map (buildWord ign)--buildWord :: T.Text -> Token -> L.Builder-buildWord ign tok-    =  L.fromText (orth tok) <> "\t"-    <> buildSpace (space tok) <> "\n"-    <> buildKnown ign (known tok)-    <> buildInterps (M.toList $ interps tok)--buildInterps :: [(Interp, Bool)] -> L.Builder-buildInterps interps = mconcat-    [ "\t" <> L.fromText _base <>-      "\t" <> L.fromText _tag <>-      if dmb-        then "\tdisamb\n"-        else "\n"-    | (Interp _base _tag, dmb) <- interps ]--buildSpace :: Space -> L.Builder-buildSpace None     = "none"-buildSpace Space    = "space"-buildSpace NewLine  = "newline"--buildKnown :: T.Text -> Bool -> L.Builder-buildKnown _ True       = ""-buildKnown ign False    = "\tNone\t" <> L.fromText ign <> "\n"
+ NLP/Concraft/Schema.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE OverloadedStrings #-}++module NLP.Concraft.Schema+( Schema+, Ox+, Ob+, guessSchemaDefault+, disambSchemaDefault+) where++import Control.Applicative ((<$>), (<*>), pure)+import qualified Data.Vector as V+import qualified Data.Text as T+import qualified Control.Monad.Ox as Ox+import qualified Control.Monad.Ox.Text as Ox++import qualified NLP.Concraft.Morphosyntax as Mx++-- | The Ox monad specialized to word token type and text observations.+type Ox t a = Ox.Ox (Mx.Word t) T.Text a++-- | A schema is a block of the Ox computation performed within the+-- context of the sentence and the absolute sentence position.+type Schema t a = V.Vector (Mx.Word t) -> Int -> Ox t a++-- | An observation consist of an index (of list type) and an actual+-- observation value.+type Ob = ([Int], T.Text)++-- | Default guessing schema.+guessSchemaDefault :: Schema t ()+guessSchemaDefault sent = \k -> do+    mapM_ (Ox.save . lowPref k) [1, 2]+    mapM_ (Ox.save . lowSuff k) [1, 2]+    Ox.save (knownAt k)+    Ox.save (isBeg k <> pure "-" <> shapeP k)+  where+    at          = Ox.atWith sent+    lowOrth i   = T.toLower <$> Mx.orth `at` i+    lowPref i j = Ox.prefix j =<< lowOrth i+    lowSuff i j = Ox.suffix j =<< lowOrth i+    shape i     = Ox.shape <$> Mx.orth `at` i+    shapeP i    = Ox.pack <$> shape i+    knownAt i   = boolF <$> (not . Mx.oov) `at` i+    isBeg i     = (Just . boolF) (i == 0)+    boolF True  = "T"+    boolF False = "F"+    x <> y      = T.append <$> x <*> y++-- | Default disambiguation schema.+disambSchemaDefault :: Schema t ()+disambSchemaDefault sent = \k -> do+    mapM_ (Ox.save . lowOrth) [k - 1, k, k + 1]+    _ <- Ox.whenJT (Mx.oov `at` k) $ do+        mapM_ (Ox.save . lowPref k) [1, 2, 3]+        mapM_ (Ox.save . lowSuff k) [1, 2, 3]+        Ox.save (isBeg k <> pure "-" <> shapeP k)+    return ()+  where+    at          = Ox.atWith sent+    lowOrth i   = T.toLower <$> Mx.orth `at` i+    lowPref i j = Ox.prefix j =<< lowOrth i+    lowSuff i j = Ox.suffix j =<< lowOrth i+    shape i     = Ox.shape <$> Mx.orth `at` i+    shapeP i    = Ox.pack <$> shape i+    isBeg i     = (Just . boolF) (i == 0)+    boolF True  = "T"+    boolF False = "F"+    x <> y      = T.append <$> x <*> y
concraft.cabal view
@@ -1,5 +1,5 @@ name:               concraft-version:            0.2.0+version:            0.3.0 synopsis:           Morphosyntactic tagging tool based on constrained CRFs description:     A morphosyntactic tagging tool based on constrained conditional@@ -12,45 +12,63 @@ maintainer:         waszczuk.kuba@gmail.com stability:          experimental category:           Natural Language Processing-homepage:           https://github.com/kawu/concraft+homepage:           http://zil.ipipan.waw.pl/Concraft build-type:         Simple  library     build-depends:         base >= 4 && < 5+      , array       , containers       , binary       , text       , text-binary >= 0.1 && < 0.2       , vector-      , crf-chain1-constrained >= 0.1.1 && < 0.2+      , vector-binary+      , crf-chain1-constrained >= 0.1.2 && < 0.2       , monad-ox >= 0.2 && < 0.3       , sgd >= 0.2.2 && < 0.3       , tagset-positional >= 0.2 && < 0.3-      , crf-chain2-generic >= 0.1.1 && < 0.2+      , crf-chain2-generic >= 0.3 && < 0.4+      , monad-codec >= 0.2 && < 0.3+      , data-lens+      , comonad-transformers+      , temporary      exposed-modules:-        NLP.Concraft.Morphosyntax-      , NLP.Concraft.Plain+        NLP.Concraft+      , NLP.Concraft.Morphosyntax+      , NLP.Concraft.Format+      , NLP.Concraft.Format.Plain+      , NLP.Concraft.Schema       , NLP.Concraft.Guess       , NLP.Concraft.Disamb+      , NLP.Concraft.Disamb.Tiered+      , NLP.Concraft.Disamb.Positional      ghc-options: -Wall -O2  source-repository head     type: git-    location: git://github.com/kawu/concraft.git+    location: https://github.com/kawu/concraft.git -executable concraft-guess-    build-depends:-        cmdargs-    hs-source-dirs: ., tools-    main-is: concraft-guess.hs    -    ghc-options: -Wall -O2 -threaded+-- executable concraft-guess+--     build-depends:+--         cmdargs+--     hs-source-dirs: ., tools+--     main-is: concraft-guess.hs    +--     ghc-options: -Wall -O2 -threaded -rtsopts+-- +-- executable concraft-disamb+--     build-depends:+--         cmdargs+--     hs-source-dirs: ., tools+--     main-is: concraft-disamb.hs    +--     ghc-options: -Wall -O2 -threaded -rtsopts -executable concraft-disamb+executable concraft     build-depends:         cmdargs     hs-source-dirs: ., tools-    main-is: concraft-disamb.hs    -    ghc-options: -Wall -O2 -threaded+    main-is: concraft.hs    +    ghc-options: -Wall -O2 -threaded -rtsopts
− tools/concraft-disamb.hs
@@ -1,86 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RecordWildCards #-}--import Control.Monad (when)-import System.Console.CmdArgs-import Data.Binary (encodeFile, decodeFile)-import Data.Text.Binary ()-import qualified Data.Set as S-import qualified Numeric.SGD as SGD-import qualified Data.Text as T-import qualified Data.Text.Lazy.IO as L--import NLP.Concraft.Disamb--tierConf :: TierConf-tierConf =-    (tier1, tier2)-  where-    tier1 = Tier True $ S.fromList ["cas", "per"]-    tier2 = Tier False $ S.fromList-        [ "nmb", "gnd", "deg", "asp" , "ngt", "acm"-        , "acn", "ppr", "agg", "vlc", "dot" ]--data Args-  = LearnMode-    { learnPath	    :: FilePath-    , evalPath      :: Maybe FilePath-    , tagsetPath    :: FilePath-    , ignTag        :: String-    , iterNum       :: Double-    , batchSize     :: Int-    , regVar        :: Double-    , gain0         :: Double-    , tau           :: Double-    , outDisamb     :: FilePath }-  | TagMode-    { dataPath      :: FilePath-    , ignTag        :: String-    , inDisamb      :: FilePath }-  deriving (Data, Typeable, Show)--learnMode :: Args-learnMode = LearnMode-    { tagsetPath = def &= argPos 0 &= typ "TAGSET-PATH"-    , ignTag = def &= argPos 1 &= typ "IGN-TAG"-    , learnPath = def &= argPos 2 &= typ "TRAIN-FILE"-    , evalPath = def &= typFile &= help "Evaluation file"-    , iterNum = 10 &= help "Number of SGD iterations"-    , batchSize = 30 &= help "Batch size"-    , regVar = 10.0 &= help "Regularization variance"-    , gain0 = 1.0 &= help "Initial gain parameter"-    , tau = 5.0 &= help "Initial tau parameter"-    , outDisamb = def &= typFile &= help "Output Disamb file" }--tagMode :: Args-tagMode = TagMode-    { ignTag = def &= argPos 0 &= typ "IGN-TAG"-    , inDisamb = def &= argPos 1 &= typ "DISAMB-FILE"-    , dataPath = def &= argPos 2 &= typ "INPUT" }--argModes :: Mode (CmdArgs Args)-argModes = cmdArgsMode $ modes [learnMode, tagMode]--main :: IO ()-main = exec =<< cmdArgsRun argModes--exec :: Args -> IO ()--exec LearnMode{..} = do-    dmb <- learn sgdArgs tagsetPath (T.pack ignTag) tierConf learnPath evalPath-    when (not . null $ outDisamb) $ do-        putStrLn $ "\nSaving model in " ++ outDisamb ++ "..."-        encodeFile outDisamb dmb-  where-    sgdArgs = SGD.SgdArgs-        { SGD.batchSize = batchSize-        , SGD.regVar = regVar-        , SGD.iterNum = iterNum-        , SGD.gain0 = gain0-        , SGD.tau = tau }--exec TagMode{..} = do-    dmb <- decodeFile inDisamb-    L.putStr =<< tagFile (T.pack ignTag) dmb dataPath
− tools/concraft-guess.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RecordWildCards #-}--import Control.Monad (when)-import System.Console.CmdArgs-import Data.Binary (encodeFile, decodeFile)-import Data.Text.Binary ()-import qualified Numeric.SGD as SGD-import qualified Data.Text as T-import qualified Data.Text.Lazy.IO as L--import NLP.Concraft.Guess (learn, tagFile)--data Args-  = LearnMode-    { learnPath	    :: FilePath-    , evalPath      :: Maybe FilePath-    , ignTag        :: String-    , iterNum       :: Double-    , batchSize     :: Int-    , regVar        :: Double-    , gain0         :: Double-    , tau           :: Double-    , outGuesser    :: FilePath }-  | TagMode-    { dataPath      :: FilePath-    , inGuesser     :: FilePath-    , guessNum      :: Int }-  deriving (Data, Typeable, Show)--learnMode :: Args-learnMode = LearnMode-    { ignTag = def &= argPos 0 &= typ "IGN-TAG"-    , learnPath = def &= argPos 1 &= typ "TRAIN-FILE"-    , evalPath = def &= typFile &= help "Evaluation file"-    , iterNum = 10 &= help "Number of SGD iterations"-    , batchSize = 30 &= help "Batch size"-    , regVar = 10.0 &= help "Regularization variance"-    , gain0 = 1.0 &= help "Initial gain parameter"-    , tau = 5.0 &= help "Initial tau parameter"-    , outGuesser = def &= typFile &= help "Output Guesser file" }--tagMode :: Args-tagMode = TagMode-    { inGuesser = def &= argPos 0 &= typ "GUESSER-FILE"-    , dataPath = def &= argPos 1 &= typ "INPUT"-    , guessNum = 10 &= help "Number of guessed tags for each unknown word" }--argModes :: Mode (CmdArgs Args)-argModes = cmdArgsMode $ modes [learnMode, tagMode]--main :: IO ()-main = exec =<< cmdArgsRun argModes--exec :: Args -> IO ()--exec LearnMode{..} = do-    gsr <- learn sgdArgs (T.pack ignTag) learnPath evalPath-    when (not . null $ outGuesser) $ do-        putStrLn $ "\nSaving model in " ++ outGuesser ++ "..."-        encodeFile outGuesser gsr-  where-    sgdArgs = SGD.SgdArgs-        { SGD.batchSize = batchSize-        , SGD.regVar = regVar-        , SGD.iterNum = iterNum-        , SGD.gain0 = gain0-        , SGD.tau = tau }--exec TagMode{..} = do-    gsr <- decodeFile inGuesser-    L.putStr =<< tagFile guessNum gsr dataPath
+ tools/concraft.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE RecordWildCards #-}++import Control.Applicative ((<$>), (<*>))+import Control.Monad (when)+import System.Console.CmdArgs+import Data.Binary (Binary, put, get, encodeFile, decodeFile)+import Data.Text.Binary ()+import qualified Numeric.SGD as SGD+import qualified Data.Text as T+import qualified Data.Text.Lazy.IO as L+import qualified Data.Tagset.Positional as P++import NLP.Concraft.Format.Plain (plainFormat)+import qualified NLP.Concraft as C+import qualified NLP.Concraft.Schema as S+import qualified NLP.Concraft.Format as F+import qualified NLP.Concraft.Guess as G+import qualified NLP.Concraft.Disamb.Positional as D+import qualified NLP.Concraft.Disamb.Tiered as R++-- | Data formats. +data Format = Plain deriving (Data, Typeable, Show)++-- | Concraft data.+data ConcraftData = ConcraftData+    { guesser       :: G.Guesser F.Tag+    , disambModel   :: R.CRF S.Ob D.Part+    , tagset        :: P.Tagset+    , tierConf      :: [D.Tier] }++instance Binary ConcraftData where+    put ConcraftData{..} = do+        put guesser+        put disambModel+        put tagset+        put tierConf+    get = ConcraftData <$> get <*> get <*> get <*> get++data Concraft+  = Train+    { trainPath	    :: FilePath+    , evalPath      :: Maybe FilePath+    , format        :: Format+    -- TODO: ignore tag should be related only to the Plain+    -- format, but then 'Format' would not be an Enum instance.+    -- Try another command line parsing library?+    , tagsetPath    :: FilePath+    , ignTag        :: String+    , discardHidden :: Bool+    , iterNum       :: Double+    , batchSize     :: Int+    , regVar        :: Double+    , gain0         :: Double+    , tau           :: Double+    , outModel      :: FilePath+    , guessNum      :: Int }+  | Disamb+    { format        :: Format+    , ignTag        :: String+    , inModel       :: FilePath+    , guessNum      :: Int }+  deriving (Data, Typeable, Show)++trainMode :: Concraft+trainMode = Train+    { tagsetPath = def &= argPos 0 &= typ "TAGSET-PATH"+    , trainPath = def &= argPos 1 &= typ "TRAIN-FILE"+    , evalPath = def &= typFile &= help "Evaluation file"+    , format = enum [Plain &= help "Plain format"]+    , ignTag = def &= help "Tag indicating OOV word"+    , discardHidden = False &= help "Discard hidden features"+    , iterNum = 10 &= help "Number of SGD iterations"+    , batchSize = 30 &= help "Batch size"+    , regVar = 10.0 &= help "Regularization variance"+    , gain0 = 1.0 &= help "Initial gain parameter"+    , tau = 5.0 &= help "Initial tau parameter"+    , outModel = def &= typFile &= help "Output Model file"+    , guessNum = 10 &= help "Number of guessed tags for each unknown word" }++disambMode :: Concraft+disambMode = Disamb+    { inModel = def &= argPos 0 &= typ "MODEL-FILE"+    , format = enum [Plain &= help "Plain format"]+    , ignTag = def &= help "Tag indicating OOV word"+    , guessNum = 10 &= help "Number of guessed tags for each unknown word" }++argModes :: Mode (CmdArgs Concraft)+argModes = cmdArgsMode $ modes [trainMode, disambMode]++main :: IO ()+main = exec =<< cmdArgsRun argModes++exec :: Concraft -> IO ()++exec Train{..} = do+    tagset' <- P.parseTagset tagsetPath <$> readFile tagsetPath+    (guesser', disambModel') <- case format of+        Plain   -> doTrain (plainFormat ign) tagset'+    let concraftData = ConcraftData+            { guesser       = guesser'+            , disambModel   = disambModel'+            , tagset        = tagset'+            , tierConf      = D.tierConfDefault }+    when (not . null $ outModel) $ do+        putStrLn $ "\nSaving model in " ++ outModel ++ "..."+        encodeFile outModel concraftData+  where+    doTrain docHandler tagset' = C.trainOn+        docHandler guessConf sgdArgs+        (disambTrain tagset')+        trainPath evalPath+    guessConf = C.GuessConf+        { C.guessNum = guessNum+        , C.guessSchema = S.guessSchemaDefault }+    disambTrain tagset' = C.DisambWith+        { C.disambConf = disambConf tagset'+        , C.disambWith = R.train (length D.tierConfDefault) featSel sgdArgs }+    disambConf tagset' = C.DisambConf+        { C.split = D.split D.tierConfDefault . P.parseTag tagset'+        , disambSchema = S.disambSchemaDefault }+    featSel = if discardHidden+        then R.selectPresent+        else R.selectHidden+    ign = T.pack ignTag+    sgdArgs = SGD.SgdArgs+        { SGD.batchSize = batchSize+        , SGD.regVar = regVar+        , SGD.iterNum = iterNum+        , SGD.gain0 = gain0+        , SGD.tau = tau }++exec Disamb{..} = do+    doTag <- doTagWith <$> decodeFile inModel <*> L.getContents+    case format of+        Plain   -> L.putStr $ doTag (plainFormat ign)+  where+    doTagWith ConcraftData{..} input docHandler =+        let guessData = C.GuessData+                { C.guessConf = C.GuessConf+                    { C.guessNum = guessNum+                    , guessSchema = S.guessSchemaDefault }+                , C.guesser = guesser }+            disambTag = C.DisambWith+                { C.disambConf = C.DisambConf+                    { C.split = D.split tierConf . P.parseTag tagset+                    , C.disambSchema = S.disambSchemaDefault }+                , C.disambWith = R.tag disambModel }+        in  C.disambDoc docHandler guessData disambTag input+    ign = T.pack ignTag