concraft 0.3.2 → 0.4.0
raw patch · 20 files changed
+1457/−1205 lines, 20 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- 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: 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: guessSchema :: GuessConf r -> Schema 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: 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.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.Schema: disambSchemaDefault :: Schema t ()
- NLP.Concraft.Schema: guessSchemaDefault :: Schema t ()
+ NLP.Concraft: Concraft :: Int -> Guesser Tag -> Disamb -> Concraft
+ NLP.Concraft: data Concraft
+ NLP.Concraft: instance Binary Concraft
+ NLP.Concraft: tag :: Concraft -> Sent Tag -> [Tag]
+ NLP.Concraft: tagDoc :: Functor f => Doc f s w -> Concraft -> Text -> Text
+ NLP.Concraft: tagSent :: Sent s w -> Concraft -> s -> s
+ NLP.Concraft: train :: (Functor f, Foldable f) => Doc f s w -> Int -> TrainConf -> TrainConf -> FilePath -> Maybe FilePath -> IO Concraft
+ NLP.Concraft.Disamb: Atom :: Maybe POS -> Map Attr Text -> Atom
+ NLP.Concraft.Disamb: Disamb :: Tagset -> [Tier] -> SchemaConf -> CRF Ob Atom -> Disamb
+ NLP.Concraft.Disamb: Tier :: Bool -> Set Attr -> Tier
+ NLP.Concraft.Disamb: TrainConf :: Tagset -> [Tier] -> SchemaConf -> SgdArgs -> TrainConf
+ NLP.Concraft.Disamb: atts :: Atom -> Map Attr Text
+ NLP.Concraft.Disamb: crf :: Disamb -> CRF Ob Atom
+ NLP.Concraft.Disamb: data Atom
+ NLP.Concraft.Disamb: data CRF a b
+ NLP.Concraft.Disamb: data Disamb
+ NLP.Concraft.Disamb: data Tier
+ NLP.Concraft.Disamb: data TrainConf
+ NLP.Concraft.Disamb: instance Binary Disamb
+ NLP.Concraft.Disamb: pos :: Atom -> Maybe POS
+ NLP.Concraft.Disamb: schemaConf :: Disamb -> SchemaConf
+ NLP.Concraft.Disamb: schemaConfT :: TrainConf -> SchemaConf
+ NLP.Concraft.Disamb: sgdArgsT :: TrainConf -> SgdArgs
+ NLP.Concraft.Disamb: tagset :: Disamb -> Tagset
+ NLP.Concraft.Disamb: tagsetT :: TrainConf -> Tagset
+ NLP.Concraft.Disamb: tiers :: Disamb -> [Tier]
+ NLP.Concraft.Disamb: tiersDefault :: [Tier]
+ NLP.Concraft.Disamb: tiersT :: TrainConf -> [Tier]
+ NLP.Concraft.Disamb: train :: Foldable f => Doc f s w -> TrainConf -> FilePath -> Maybe FilePath -> IO Disamb
+ NLP.Concraft.Disamb: withAtts :: Tier -> Set Attr
+ NLP.Concraft.Disamb: withPos :: Tier -> Bool
+ NLP.Concraft.Format.Plain: Interp :: Maybe Text -> Tag -> Interp
+ NLP.Concraft.Format.Plain: NewLine :: Space
+ NLP.Concraft.Format.Plain: None :: Space
+ NLP.Concraft.Format.Plain: Space :: Space
+ NLP.Concraft.Format.Plain: Token :: Text -> Space -> Bool -> Map Interp Bool -> Token
+ NLP.Concraft.Format.Plain: base :: Interp -> Maybe Text
+ NLP.Concraft.Format.Plain: data Interp
+ NLP.Concraft.Format.Plain: data Space
+ NLP.Concraft.Format.Plain: data Token
+ NLP.Concraft.Format.Plain: interps :: Token -> Map Interp Bool
+ NLP.Concraft.Format.Plain: known :: Token -> Bool
+ NLP.Concraft.Format.Plain: orth :: Token -> Text
+ NLP.Concraft.Format.Plain: parsePlain :: Tag -> Text -> [[Token]]
+ NLP.Concraft.Format.Plain: parseSent :: Tag -> Text -> [Token]
+ NLP.Concraft.Format.Plain: showPlain :: Tag -> [[Token]] -> Text
+ NLP.Concraft.Format.Plain: showSent :: Tag -> [Token] -> Text
+ NLP.Concraft.Format.Plain: space :: Token -> Space
+ NLP.Concraft.Format.Plain: tag :: Interp -> Tag
+ NLP.Concraft.Guess: TrainConf :: SchemaConf -> SgdArgs -> TrainConf
+ NLP.Concraft.Guess: data Guesser t
+ NLP.Concraft.Guess: data TrainConf
+ NLP.Concraft.Guess: schemaConf :: Guesser t -> SchemaConf
+ NLP.Concraft.Guess: schemaConfT :: TrainConf -> SchemaConf
+ NLP.Concraft.Guess: sgdArgsT :: TrainConf -> SgdArgs
+ NLP.Concraft.Guess: train :: Foldable f => Doc f s w -> TrainConf -> FilePath -> Maybe FilePath -> IO (Guesser Tag)
+ NLP.Concraft.Schema: Body :: [Int] -> Bool -> a -> Body a
+ NLP.Concraft.Schema: SchemaConf :: Entry () -> Entry () -> Entry [Int] -> Entry [Int] -> Entry () -> Entry () -> Entry () -> Entry () -> SchemaConf
+ NLP.Concraft.Schema: args :: Body a -> a
+ NLP.Concraft.Schema: begPackedB :: Block t ()
+ NLP.Concraft.Schema: begPackedC :: SchemaConf -> Entry ()
+ NLP.Concraft.Schema: data Body a
+ NLP.Concraft.Schema: data SchemaConf
+ NLP.Concraft.Schema: disambConfDefault :: SchemaConf
+ NLP.Concraft.Schema: entry :: [Int] -> Entry ()
+ NLP.Concraft.Schema: entryWith :: a -> [Int] -> Entry a
+ NLP.Concraft.Schema: fromBlock :: Block t a -> [Int] -> Bool -> Schema t a
+ NLP.Concraft.Schema: fromConf :: SchemaConf -> Schema t ()
+ NLP.Concraft.Schema: guessConfDefault :: SchemaConf
+ NLP.Concraft.Schema: instance Binary SchemaConf
+ NLP.Concraft.Schema: instance Binary a => Binary (Body a)
+ NLP.Concraft.Schema: instance Show SchemaConf
+ NLP.Concraft.Schema: instance Show a => Show (Body a)
+ NLP.Concraft.Schema: knownB :: Block t ()
+ NLP.Concraft.Schema: knownC :: SchemaConf -> Entry ()
+ NLP.Concraft.Schema: lowOrthB :: Block t ()
+ NLP.Concraft.Schema: lowOrthC :: SchemaConf -> Entry ()
+ NLP.Concraft.Schema: lowPrefixesB :: [Int] -> Block t ()
+ NLP.Concraft.Schema: lowPrefixesC :: SchemaConf -> Entry [Int]
+ NLP.Concraft.Schema: lowSuffixesB :: [Int] -> Block t ()
+ NLP.Concraft.Schema: lowSuffixesC :: SchemaConf -> Entry [Int]
+ NLP.Concraft.Schema: nullConf :: SchemaConf
+ NLP.Concraft.Schema: oovOnly :: Body a -> Bool
+ NLP.Concraft.Schema: orthB :: Block t ()
+ NLP.Concraft.Schema: orthC :: SchemaConf -> Entry ()
+ NLP.Concraft.Schema: packedB :: Block t ()
+ NLP.Concraft.Schema: packedC :: SchemaConf -> Entry ()
+ NLP.Concraft.Schema: range :: Body a -> [Int]
+ NLP.Concraft.Schema: schematize :: Schema t a -> Sent t -> [[Ob]]
+ NLP.Concraft.Schema: sequenceS_ :: [Vector (Word t) -> a -> Ox t b] -> Vector (Word t) -> a -> Ox t ()
+ NLP.Concraft.Schema: shapeB :: Block t ()
+ NLP.Concraft.Schema: shapeC :: SchemaConf -> Entry ()
+ NLP.Concraft.Schema: type Block t a = Vector (Word t) -> [Int] -> Ox t a
+ NLP.Concraft.Schema: type Entry a = Maybe (Body a)
+ NLP.Concraft.Schema: void :: a -> Schema t a
- NLP.Concraft: disamb :: (Ord r, Ord t) => GuessData r -> DisambTag r t -> Sent r -> [r]
+ NLP.Concraft: disamb :: Concraft -> Disamb
- NLP.Concraft: guessNum :: GuessConf r -> Int
+ NLP.Concraft: guessNum :: Concraft -> Int
- NLP.Concraft: guesser :: GuessData r -> Guesser r
+ NLP.Concraft: guesser :: Concraft -> Guesser Tag
- NLP.Concraft.Disamb: disamb :: (Ord r, Ord t) => Schema t a -> Split r t -> TagCRF Ob t -> Sent r -> [r]
+ NLP.Concraft.Disamb: disamb :: Disamb -> Sent Tag -> [Tag]
- 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: disambDoc :: Functor f => Doc f s w -> Disamb -> 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: disambSent :: Sent s w -> Disamb -> s -> s
- NLP.Concraft.Guess: Guesser :: CRF Ob t -> Guesser t
+ NLP.Concraft.Guess: Guesser :: SchemaConf -> CRF Ob t -> Guesser t
- NLP.Concraft.Guess: guess :: Ord t => Int -> Schema t a -> Guesser t -> Sent t -> [[t]]
+ NLP.Concraft.Guess: guess :: Ord t => Int -> Guesser t -> Sent t -> [[t]]
- NLP.Concraft.Guess: guessDoc :: Functor f => Doc f s w -> Int -> Schema Tag a -> Guesser Tag -> Text -> Text
+ NLP.Concraft.Guess: guessDoc :: Functor f => Doc f s w -> Int -> Guesser Tag -> Text -> Text
- NLP.Concraft.Guess: guessSent :: Sent s w -> Int -> Schema Tag a -> Guesser Tag -> s -> s
+ NLP.Concraft.Guess: guessSent :: Sent s w -> Int -> Guesser Tag -> s -> s
Files
- NLP/Concraft.hs +0/−143
- NLP/Concraft/Disamb.hs +0/−147
- NLP/Concraft/Disamb/Positional.hs +0/−64
- NLP/Concraft/Disamb/Tiered.hs +0/−265
- NLP/Concraft/Format.hs +0/−44
- NLP/Concraft/Format/Plain.hs +0/−187
- NLP/Concraft/Guess.hs +0/−134
- NLP/Concraft/Morphosyntax.hs +0/−68
- NLP/Concraft/Schema.hs +0/−69
- concraft.cabal +9/−19
- src/NLP/Concraft.hs +99/−0
- src/NLP/Concraft/Disamb.hs +163/−0
- src/NLP/Concraft/Disamb/Positional.hs +64/−0
- src/NLP/Concraft/Disamb/Tiered.hs +258/−0
- src/NLP/Concraft/Format.hs +44/−0
- src/NLP/Concraft/Format/Plain.hs +208/−0
- src/NLP/Concraft/Guess.hs +151/−0
- src/NLP/Concraft/Morphosyntax.hs +68/−0
- src/NLP/Concraft/Schema.hs +373/−0
- tools/concraft.hs +20/−65
− NLP/Concraft.hs
@@ -1,143 +0,0 @@-{-# 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
@@ -1,147 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE MultiParamTypeClasses #-}--module NLP.Concraft.Disamb-( Split-, TrainCRF-, TagCRF-, disamb-, disambSent-, disambDoc-, trainOn-) where--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.Vector as V-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.Generic.External as CRF--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 :: 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 i = Mx.interpsSet w- where w = v V.! i---- | Split is just a function from an original tag form--- to a complex tag form.-type Split r t = r -> t---- | 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)---- | 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---- | CRF tagging function.-type TagCRF o t = CRF.Sent o t -> [t]---- | 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- where- embed = unSplit split---- | 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- 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 ]---- | 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 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- :: (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 schema xs) (map mkDist xs)]- where- xs = map (Mx.mapWord split . extract) (parseSent sent)- mkDist = CRF.mkDist . M.toList . Mx.unWMap . Mx.tagWMap
− NLP/Concraft/Disamb/Positional.hs
@@ -1,64 +0,0 @@-{-# 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
@@ -1,265 +0,0 @@-{-# 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
@@ -1,44 +0,0 @@--- | 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
@@ -1,187 +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.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 "spaces" = Space -- Is it not a Maca bug?-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
@@ -1,134 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module NLP.Concraft.Guess-( Guesser (..)-, guess-, include-, guessSent-, guessDoc-, trainOn-) where--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.Lazy as L-import qualified Data.Text.Lazy.IO as L-import qualified Data.Vector as V--import qualified Control.Monad.Ox as Ox-import qualified Data.CRF.Chain1.Constrained as CRF-import qualified Numeric.SGD as SGD--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 => 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 i - | Mx.oov w = S.empty- | otherwise = Mx.interpsSet w- where w = v V.! i---- | A guesser represented by the conditional random field.-newtype Guesser t = Guesser { crf :: CRF.CRF Ob t }- deriving (Binary)---- | 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)---- | 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 ..]---- | 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- wMaps = includeWMaps words guessed---- | 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 F.Tag)-trainOn format schema sgdArgs trainPath evalPath'Maybe = do- _crf <- CRF.train sgdArgs- (schemed format schema trainPath)- (schemed format schema <$> evalPath'Maybe)- (const CRF.presentFeats)- return $ Guesser _crf---- | Schematized data from the plain file.-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 = 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
@@ -1,68 +0,0 @@-{-# LANGUAGE RecordWildCards #-}---- | Types and functions related to the morphosyntax data layer.--module NLP.Concraft.Morphosyntax-( --- * Morphosyntax data- Sent-, Word (..)-, mapWord-, mapSent-, interpsSet-, interps--- * Weighted collection-, WMap (unWMap)-, mkWMap-, mapWMap-) where--import Control.Arrow (first)-import qualified Data.Set as S-import qualified Data.Map as M-import qualified Data.Text as T----- | 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. 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 w = w { tagWMap = mapWMap f (tagWMap w) }---- | Map function over sentence tags.-mapSent :: Ord b => (a -> b) -> Sent a -> Sent b-mapSent = map . mapWord---- | Interpretations of the word.-interpsSet :: Word t -> S.Set t-interpsSet = M.keysSet . unWMap . tagWMap---- | Interpretations of the word.-interps :: Word t -> [t]-interps = S.toList . interpsSet----- | A weighted collection of type @a@ elements.-newtype WMap a = WMap { unWMap :: M.Map a Double }- deriving (Show, Eq, Ord)---- | Make a weighted collection.-mkWMap :: Ord a => [(a, Double)] -> WMap a-mkWMap = WMap . M.fromListWith (+) . filter ((>=0).snd)---- | 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/Schema.hs
@@ -1,69 +0,0 @@-{-# 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.3.2+version: 0.4.0 synopsis: Morphosyntactic tagging tool based on constrained CRFs description: A morphosyntactic tagging tool based on constrained conditional@@ -16,6 +16,8 @@ build-type: Simple library+ hs-source-dirs: src+ build-depends: base >= 4 && < 5 , array@@ -37,13 +39,15 @@ exposed-modules: NLP.Concraft+ , NLP.Concraft.Guess+ , NLP.Concraft.Disamb , NLP.Concraft.Morphosyntax , NLP.Concraft.Format , NLP.Concraft.Format.Plain , NLP.Concraft.Schema- , NLP.Concraft.Guess- , NLP.Concraft.Disamb- , NLP.Concraft.Disamb.Tiered++ other-modules:+ NLP.Concraft.Disamb.Tiered , NLP.Concraft.Disamb.Positional ghc-options: -Wall -O2@@ -52,23 +56,9 @@ type: 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 -rtsopts--- --- executable concraft-disamb--- build-depends:--- cmdargs--- hs-source-dirs: ., tools--- main-is: concraft-disamb.hs --- ghc-options: -Wall -O2 -threaded -rtsopts- executable concraft build-depends: cmdargs- hs-source-dirs: ., tools+ hs-source-dirs: src, tools main-is: concraft.hs ghc-options: -Wall -O2 -threaded -rtsopts
+ src/NLP/Concraft.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE RecordWildCards #-}++module NLP.Concraft+(+-- * Types+ Concraft (..)++-- * Tagging+, tag+, tagSent+, tagDoc++-- * Training+, train+) where++import System.IO (hClose)+import Control.Applicative ((<$>), (<*>))+import Data.Foldable (Foldable)+import Data.Binary (Binary, put, get)+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.IO as L+import qualified System.IO.Temp as Temp++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++-- | Concraft data.+data Concraft = Concraft+ { guessNum :: Int+ , guesser :: G.Guesser F.Tag+ , disamb :: D.Disamb }++instance Binary Concraft where+ put Concraft{..} = do+ put guessNum+ put guesser+ put disamb+ get = Concraft <$> get <*> get <*> get++-- | Perform disambiguation preceded by context-sensitive guessing.+tag :: Concraft -> Mx.Sent F.Tag -> [F.Tag]+tag Concraft{..} sent+ = D.disamb disamb+ . G.include sent + . G.guess guessNum guesser + $ sent++-- | Tag the sentence.+tagSent :: F.Sent s w -> Concraft -> s -> s+tagSent sentH Concraft{..}+ = D.disambSent sentH disamb+ . G.guessSent sentH guessNum guesser++-- | Tag document.+tagDoc :: Functor f => F.Doc f s w -> Concraft -> L.Text -> L.Text+tagDoc F.Doc{..} concraft =+ let onSent = tagSent sentHandler concraft+ in showDoc . fmap onSent . parseDoc++-- | Train guessing and disambiguation models.+train+ :: (Functor f, Foldable f)+ => F.Doc f s w -- ^ Document format handler+ -> Int -- ^ Numer of guessed tags for each word + -> G.TrainConf -- ^ Guessing model training configuration+ -> D.TrainConf -- ^ Disambiguation model training configuration+ -> FilePath -- ^ Training file+ -> Maybe FilePath -- ^ Maybe eval file+ -> IO Concraft -- ^ Resultant models+train format guessNum guessConf disambConf trainPath evalPath'Maybe = do+ putStrLn "\n===== Train guessing model ====\n"+ guesser <- G.train format guessConf trainPath evalPath'Maybe+ let withGuesser = guessFile format guessNum guesser+ withGuesser "train" (Just trainPath) $ \(Just trainPathG) ->+ withGuesser "eval" evalPath'Maybe $ \evalPathG'Maybe -> do+ putStrLn "\n===== Train disambiguation model ====\n"+ disamb <- D.train format disambConf trainPathG evalPathG'Maybe+ return $ Concraft guessNum guesser disamb++guessFile+ :: Functor f+ => F.Doc f s w -- ^ Document format handler+ -> Int -- ^ Numer of guessed tags for each word+ -> G.Guesser 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 guessNum gsr tmpl (Just path) handler =+ Temp.withTempFile "." tmpl $ \tmpPath tmpHandle -> do+ inp <- L.readFile path+ let out = G.guessDoc format guessNum gsr inp+ hClose tmpHandle+ L.writeFile tmpPath out+ handler (Just tmpPath)
+ src/NLP/Concraft/Disamb.hs view
@@ -0,0 +1,163 @@+{-# LANGUAGE RecordWildCards #-}++module NLP.Concraft.Disamb+(+-- * Model+ Disamb (..)+, Tier.CRF () ++-- * Tiers+, P.Tier (..)+, P.Atom (..)+, P.tiersDefault++-- * Disambiguation+, disamb+, disambSent+, disambDoc++-- * Training+, TrainConf (..)+, train+) where++import Control.Applicative ((<$>), (<*>))+import Data.Maybe (fromJust)+import Data.List (find)+import Data.Foldable (Foldable, foldMap)+import Data.Binary (Binary, put, get)+import qualified Data.Set as S+import qualified Data.Map as M+import qualified Data.Vector as V+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.Generic.External as CRF++import NLP.Concraft.Schema hiding (schematize)+import qualified NLP.Concraft.Morphosyntax as Mx+import qualified NLP.Concraft.Format as F++import qualified NLP.Concraft.Disamb.Tiered as Tier+import qualified NLP.Concraft.Disamb.Positional as P+import qualified Data.Tagset.Positional as TP+import qualified Numeric.SGD as SGD++-- | 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 i = Mx.interpsSet w+ where w = v V.! i++-- | A disambiguation model.+data Disamb = Disamb+ { tagset :: TP.Tagset+ , tiers :: [P.Tier]+ , schemaConf :: SchemaConf+ , crf :: Tier.CRF Ob P.Atom }++instance Binary Disamb where+ put Disamb{..} = put tagset >> put tiers >> put schemaConf >> put crf+ get = Disamb <$> get <*> get <*> get <*> get++-- | Unsplit the complex tag (assuming, that it is one+-- of the interpretations of the word).+unSplit :: Eq t => (r -> t) -> Mx.Word r -> t -> r+unSplit split' word x = fromJust $ find ((==x) . split') (Mx.interps word)++-- -- | 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+-- +-- -- | CRF tagging function.+-- type TagCRF o t = CRF.Sent o t -> [t]++-- | Perform context-sensitive disambiguation.+disamb :: Disamb -> Mx.Sent F.Tag -> [F.Tag]+disamb Disamb{..} sent+ = map (uncurry embed)+ . zip sent+ . Tier.tag crf+ . schematize schema+ . Mx.mapSent split+ $ sent+ where+ schema = fromConf schemaConf+ split = P.split tiers . TP.parseTag tagset+ embed = unSplit split++-- | Tag the sentence.+disambSent :: F.Sent s w -> Disamb -> s -> s+disambSent F.Sent{..} dmb sent =+ flip mergeSent sent+ [ select wMap orig+ | (wMap, orig) <- zip+ (doDmb sent)+ (parseSent sent) ]+ where+ F.Word{..} = wordHandler+ doDmb orig =+ let xs = map extract (parseSent orig)+ in map (uncurry mkChoice) (zip xs (disamb dmb xs))+ mkChoice word x = Mx.mkWMap+ [ if x == y+ then (x, 1)+ else (y, 0)+ | y <- Mx.interps word ]++-- | Disambiguate document.+disambDoc :: Functor f => F.Doc f s w -> Disamb -> L.Text -> L.Text+disambDoc F.Doc{..} dmb =+ let onSent = disambSent sentHandler dmb+ in showDoc . fmap onSent . parseDoc++-- | Training configuration.+data TrainConf = TrainConf+ { tagsetT :: TP.Tagset+ , tiersT :: [P.Tier]+ , schemaConfT :: SchemaConf+ , sgdArgsT :: SGD.SgdArgs }++-- | Train disamb model.+train+ :: Foldable f+ => F.Doc f s w -- ^ Document format handler+ -> TrainConf -- ^ Training configuration+ -> FilePath -- ^ Training file+ -> Maybe FilePath -- ^ Maybe eval file+ -> IO Disamb -- ^ Resultant model+train format TrainConf{..} trainPath evalPath'Maybe = do+ crf <- Tier.train+ (length tiersT)+ sgdArgsT+ (schemed format schema split trainPath)+ (schemed format schema split <$> evalPath'Maybe)+ return $ Disamb tagsetT tiersT schemaConfT crf+ where+ schema = fromConf schemaConfT+ split = P.split tiersT . TP.parseTag tagsetT++-- | Schematized data from the plain file.+schemed+ :: (Foldable f, Ord t)+ => F.Doc f s w -> Schema t a -> (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 schema xs) (map mkDist xs)]+ where+ xs = map (Mx.mapWord split . extract) (parseSent sent)+ mkDist = CRF.mkDist . M.toList . Mx.unWMap . Mx.tagWMap
+ src/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 (..)+, Atom (..)+, select+, split+, tiersDefault+) 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 Atom = Atom+ { pos :: Maybe TP.POS+ , atts :: M.Map TP.Attr T.Text }+ deriving (Show, Eq, Ord)++instance Binary Atom where+ put Atom{..} = put pos >> put atts+ get = Atom <$> get <*> get++-- | Select tier attributes.+select :: Tier -> TP.Tag -> Atom+select Tier{..} tag = Atom+ { 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 -> [Atom]+split tiers tag =+ [ select tier tag+ | tier <- tiers ]++-- | Default tiered tagging configuration.+tiersDefault :: [Tier]+tiersDefault =+ [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" ]
+ src/NLP/Concraft/Disamb/Tiered.hs view
@@ -0,0 +1,258 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE BangPatterns #-}++module NLP.Concraft.Disamb.Tiered+( Ob (..)+, Lb (..)+, Feat (..)+, CRF (..)+, train+, tag+) 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, selectHidden+ , core, withCore )+import Data.CRF.Chain2.Generic.Internal (FeatIx(..))+import qualified Data.CRF.Chain2.Generic.Inference as I+import qualified Data.CRF.Chain2.Generic.External as E+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++-- | Observation.+newtype Ob = Ob { unOb :: Int } deriving (Show, Eq, Ord, Ix, Binary)++-- | Sublabel.+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+ -> SGD.SgdArgs -- ^ Args for SGD+ -> IO [E.SentL o [t]] -- ^ Training data 'IO' action+ -> Maybe (IO [E.SentL o [t]]) -- ^ Maybe evalation data+ -> IO (CRF o t) -- ^ Resulting model+train n sgdArgs trainIO evalIO'Maybe = do+ (_codecData, _model) <- Train.train+ sgdArgs+ (codecSpec n)+ featGen+ selectHidden+ trainIO+ evalIO'Maybe+ return $ CRF n _codecData _model++-- | Find the most probable label sequence.+tag :: (Ord o, Ord t) => CRF o t -> E.Sent o [t] -> [[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 ]
+ src/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++-- | Word 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 }
+ src/NLP/Concraft/Format/Plain.hs view
@@ -0,0 +1,208 @@+{-# 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+(+-- * Types+ Token (..)+, Interp (..)+, Space (..)+-- * Format handler+, plainFormat+-- * Parsing+, parsePlain+, parseSent+-- * Printing+, showPlain+, showSent+) 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) ]++-- | Parse the text in the plain format given the /oov/ tag.+parsePlain :: F.Tag -> L.Text -> [[Token]]+parsePlain ign = map (parseSent ign) . init . L.splitOn "\n\n"++-- | Parse the sentence in the plain format given the /oov/ tag.+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 "spaces" = Space -- Is it not a Maca bug?+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 (<>) #-}++-- | Show the plain data.+showPlain :: F.Tag -> [[Token]] -> L.Text+showPlain ign =+ L.toLazyText . mconcat . map (\xs -> buildSent ign xs <> "\n")++-- | Show the sentence.+showSent :: F.Tag -> [Token] -> L.Text+showSent ign xs = L.toLazyText $ buildSent ign xs++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"
+ src/NLP/Concraft/Guess.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE RecordWildCards #-}++module NLP.Concraft.Guess+(+-- * Types+ Guesser (..)+ +-- * Guessing+, guess+, guessSent+, guessDoc+, include++-- * Training+, TrainConf (..)+, train+) where++import Prelude hiding (words)+import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, put, get)+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.Lazy as L+import qualified Data.Text.Lazy.IO as L+import qualified Data.Vector as V++import qualified Control.Monad.Ox as Ox+import qualified Data.CRF.Chain1.Constrained as CRF+import qualified Numeric.SGD as SGD++import NLP.Concraft.Schema hiding (schematize)+import qualified NLP.Concraft.Morphosyntax as Mx+import qualified NLP.Concraft.Format as F++-- | A guessing model.+data Guesser t = Guesser+ { schemaConf :: SchemaConf+ , crf :: CRF.CRF Ob t }++instance (Ord t, Binary t) => Binary (Guesser t) where+ put Guesser{..} = put schemaConf >> put crf+ get = Guesser <$> get <*> get++-- | Schematize the input sentence with according to 'schema' rules.+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 i + | Mx.oov w = S.empty+ | otherwise = Mx.interpsSet w+ where w = v V.! i++-- | Determine the 'k' most probable labels for each word in the sentence.+guess :: Ord t => Int -> Guesser t -> Mx.Sent t -> [[t]]+guess k gsr sent =+ let schema = fromConf (schemaConf gsr)+ in CRF.tagK k (crf gsr) (schematize schema sent)++-- | 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 ..]++-- | 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+ wMaps = includeWMaps words guessed++-- | Tag sentence in external format. Selected interpretations+-- (tags correct within the context) will be preserved.+guessSent :: F.Sent s w -> Int -> Guesser F.Tag -> s -> s+guessSent F.Sent{..} k 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 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+ -> Guesser F.Tag -- ^ Guesser itself+ -> L.Text -- ^ Input+ -> L.Text -- ^ Output+guessDoc F.Doc{..} k gsr+ = showDoc + . fmap (guessSent sentHandler k gsr)+ . parseDoc++-- | Training configuration.+data TrainConf = TrainConf+ { schemaConfT :: SchemaConf+ , sgdArgsT :: SGD.SgdArgs }++-- | Train guesser.+train+ :: Foldable f+ => F.Doc f s w -- ^ Document format handler+ -> TrainConf -- ^ Training configuration+ -> FilePath -- ^ Training file+ -> Maybe FilePath -- ^ Maybe eval file+ -> IO (Guesser F.Tag)+train format TrainConf{..} trainPath evalPath'Maybe = do+ let schema = fromConf schemaConfT+ crf <- CRF.train sgdArgsT+ (schemed format schema trainPath)+ (schemed format schema <$> evalPath'Maybe)+ (const CRF.presentFeats)+ return $ Guesser schemaConfT crf++-- | Schematized data from the plain file.+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 = map extract (parseSent sent)+ mkProb = CRF.mkProb . M.toList . Mx.unWMap . Mx.tagWMap+ in [zip (schematize schema xs) (map mkProb xs)]
+ src/NLP/Concraft/Morphosyntax.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE RecordWildCards #-}++-- | Types and functions related to the morphosyntax data layer.++module NLP.Concraft.Morphosyntax+( +-- * Morphosyntax data+ Sent+, Word (..)+, mapWord+, mapSent+, interpsSet+, interps+-- * Weighted collection+, WMap (unWMap)+, mkWMap+, mapWMap+) where++import Control.Arrow (first)+import qualified Data.Set as S+import qualified Data.Map as M+import qualified Data.Text as T+++-- | 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. 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 w = w { tagWMap = mapWMap f (tagWMap w) }++-- | Map function over sentence tags.+mapSent :: Ord b => (a -> b) -> Sent a -> Sent b+mapSent = map . mapWord++-- | Interpretations of the word.+interpsSet :: Word t -> S.Set t+interpsSet = M.keysSet . unWMap . tagWMap++-- | Interpretations of the word.+interps :: Word t -> [t]+interps = S.toList . interpsSet+++-- | A weighted collection of type @a@ elements.+newtype WMap a = WMap { unWMap :: M.Map a Double }+ deriving (Show, Eq, Ord)++-- | Make a weighted collection.+mkWMap :: Ord a => [(a, Double)] -> WMap a+mkWMap = WMap . M.fromListWith (+) . filter ((>=0).snd)++-- | Map function over weighted collection elements. +mapWMap :: Ord b => (a -> b) -> WMap a -> WMap b+mapWMap f = mkWMap . map (first f) . M.toList . unWMap
+ src/NLP/Concraft/Schema.hs view
@@ -0,0 +1,373 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-}++-- | Observation schema blocks for Concraft.++module NLP.Concraft.Schema+( +-- * Types+ Ob+, Ox+, Schema+, void+, sequenceS_++-- * Usage+, schematize++-- * Configuration+, Body (..)+, Entry+, entry+, entryWith+, SchemaConf (..)+, nullConf+, fromConf++, guessConfDefault+, disambConfDefault++-- * Schema blocks+, Block+, fromBlock+, orthB+, lowOrthB+, lowPrefixesB+, lowSuffixesB+, knownB+, shapeB+, packedB+, begPackedB+) where++import Control.Applicative ((<$>), (<*>), pure)+import Control.Monad (forM_)+import Data.Binary (Binary, put, get)+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++-- | An observation consist of an index (of list type) and an actual+-- observation value.+type Ob = ([Int], T.Text)++-- | 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++-- | A dummy schema block.+void :: a -> Schema t a+void x _ _ = return x++-- | Sequence the list of schemas (or blocks) and discard individual values.+sequenceS_+ :: [V.Vector (Mx.Word t) -> a -> Ox t b]+ -> V.Vector (Mx.Word t) -> a -> Ox t ()+sequenceS_ xs sent =+ let ys = map ($sent) xs+ in \k -> sequence_ (map ($k) ys)++-- | Record structure of the basic observation types.+data BaseOb = BaseOb+ { orth :: Int -> Maybe T.Text+ , lowOrth :: Int -> Maybe T.Text }++-- | Construct the 'BaseOb' structure given the sentence.+mkBaseOb :: V.Vector (Mx.Word t) -> BaseOb+mkBaseOb sent = BaseOb+ { orth = _orth+ , lowOrth = _lowOrth }+ where+ at = Ox.atWith sent+ _orth = (Mx.orth `at`)+ _lowOrth i = T.toLower <$> _orth i++-- | A block is a chunk of the Ox computation performed within the+-- context of the sentence and the list of absolute sentence positions.+type Block t a = V.Vector (Mx.Word t) -> [Int] -> Ox t a++-- | Transform a block to a schema depending on+-- * A list of relative sentence positions,+-- * A boolean value; if true, the block computation+-- will be performed only on positions where an OOV+-- word resides.+fromBlock :: Block t a -> [Int] -> Bool -> Schema t a+fromBlock blk xs oovOnly sent =+ \k -> blkSent [x + k | x <- xs, oov (x + k)]+ where+ blkSent = blk sent+ oov k = if not oovOnly+ then True+ else maybe False id $ Mx.oov `at` k+ at = Ox.atWith sent++-- | Orthographic form at the current position.+orthB :: Block t ()+orthB sent = \ks ->+ let orthOb = Ox.atWith sent Mx.orth+ in mapM_ (Ox.save . orthOb) ks++-- | Orthographic form at the current position.+lowOrthB :: Block t ()+lowOrthB sent = \ks ->+ let BaseOb{..} = mkBaseOb sent+ in mapM_ (Ox.save . lowOrth) ks++-- | List of lowercased prefixes of given lengths.+lowPrefixesB :: [Int] -> Block t ()+lowPrefixesB ns sent = \ks ->+ forM_ ks $ \i ->+ mapM_ (Ox.save . lowPrefix i) ns+ where+ BaseOb{..} = mkBaseOb sent+ lowPrefix i j = Ox.prefix j =<< lowOrth i++-- | List of lowercased suffixes of given lengths.+lowSuffixesB :: [Int] -> Block t ()+lowSuffixesB ns sent = \ks ->+ forM_ ks $ \i ->+ mapM_ (Ox.save . lowSuffix i) ns+ where+ BaseOb{..} = mkBaseOb sent+ lowSuffix i j = Ox.suffix j =<< lowOrth i++-- | Shape of the word.+knownB :: Block t ()+knownB sent = \ks -> do+ mapM_ (Ox.save . knownAt) ks+ where+ at = Ox.atWith sent+ knownAt i = boolF <$> (not . Mx.oov) `at` i+ boolF True = "T"+ boolF False = "F"++-- | Shape of the word.+shapeB :: Block t ()+shapeB sent = \ks -> do+ mapM_ (Ox.save . shape) ks+ where+ BaseOb{..} = mkBaseOb sent+ shape i = Ox.shape <$> orth i++-- | Packed shape of the word.+packedB :: Block t ()+packedB sent = \ks -> do+ mapM_ (Ox.save . shapeP) ks+ where+ BaseOb{..} = mkBaseOb sent+ shape i = Ox.shape <$> orth i+ shapeP i = Ox.pack <$> shape i++-- | Packed shape of the word.+begPackedB :: Block t ()+begPackedB sent = \ks -> do+ mapM_ (Ox.save . begPacked) ks+ where+ BaseOb{..} = mkBaseOb sent+ shape i = Ox.shape <$> orth i+ shapeP i = Ox.pack <$> shape i+ begPacked i = isBeg i <> pure "-" <> shapeP i+ isBeg i = (Just . boolF) (i == 0)+ boolF True = "T"+ boolF False = "F"+ x <> y = T.append <$> x <*> y++-- -- | Combined shapes of two consecutive (at @k-1@ and @k@ positions) words.+-- shapePairB :: Block t ()+-- shapePairB sent = \ks ->+-- forM_ ks $ \i -> do+-- Ox.save $ link <$> shape i <*> shape (i - 1)+-- where+-- BaseOb{..} = mkBaseOb sent+-- shape i = Ox.shape <$> orth i+-- link x y = T.concat [x, "-", y]+-- +-- -- | Combined packed shapes of two consecutive (at @k-1@ and @k@ positions)+-- -- words.+-- packedPairB :: Block t ()+-- packedPairB sent = \ks ->+-- forM_ ks $ \i -> do+-- Ox.save $ link <$> shapeP i <*> shapeP (i - 1)+-- where+-- BaseOb{..} = mkBaseOb sent+-- shape i = Ox.shape <$> orth i+-- shapeP i = Ox.pack <$> shape i+-- link x y = T.concat [x, "-", y]++-- | Body of configuration entry.+data Body a = Body {+ -- | Range argument for the schema block. + range :: [Int]+ -- | When true, the entry is used only for oov words.+ , oovOnly :: Bool+ -- | Additional arguments for the schema block.+ , args :: a }+ deriving (Show)++instance Binary a => Binary (Body a) where+ put Body{..} = put range >> put oovOnly >> put args+ get = Body <$> get <*> get <*> get++-- | Maybe entry.+type Entry a = Maybe (Body a)++-- | Entry with additional arguemnts.+entryWith :: a -> [Int] -> Entry a+entryWith v xs = Just (Body xs False v)++-- | Plain entry with no additional arugments.+entry :: [Int] -> Entry ()+entry = entryWith ()++-- | Configuration of the schema. All configuration elements specify the+-- range over which a particular observation type should be taken on account.+-- For example, the @[-1, 0, 2]@ range means that observations of particular+-- type will be extracted with respect to previous (@k - 1@), current (@k@)+-- and after the next (@k + 2@) positions when identifying the observation+-- set for position @k@ in the input sentence.+data SchemaConf = SchemaConf {+ -- | The 'orthB' schema block.+ orthC :: Entry ()+ -- | The 'lowOrthB' schema block.+ , lowOrthC :: Entry ()+ -- | The 'lowPrefixesB' schema block. The first list of ints+ -- represents lengths of prefixes.+ , lowPrefixesC :: Entry [Int]+ -- | The 'lowSuffixesB' schema block. The first list of ints+ -- represents lengths of suffixes.+ , lowSuffixesC :: Entry [Int]+ -- | The 'knownB' schema block.+ , knownC :: Entry ()+ -- | The 'shapeB' schema block.+ , shapeC :: Entry ()+ -- | The 'packedB' schema block.+ , packedC :: Entry ()+ -- | The 'begPackedB' schema block.+ , begPackedC :: Entry ()+ } deriving (Show)++instance Binary SchemaConf where+ put SchemaConf{..} = do+ put orthC+ put lowOrthC+ put lowPrefixesC+ put lowSuffixesC+ put knownC+ put shapeC+ put packedC+ put begPackedC+ get = SchemaConf+ <$> get <*> get <*> get <*> get+ <*> get <*> get <*> get <*> get++-- | Null configuration of the observation schema.+nullConf :: SchemaConf+nullConf = SchemaConf+ Nothing Nothing Nothing Nothing+ Nothing Nothing Nothing Nothing++mkArg0 :: Block t () -> Entry () -> Schema t ()+mkArg0 blk (Just x) = fromBlock blk (range x) (oovOnly x)+mkArg0 _ Nothing = void ()++mkArg1 :: (a -> Block t ()) -> Entry a -> Schema t ()+mkArg1 blk (Just x) = fromBlock (blk (args x)) (range x) (oovOnly x)+mkArg1 _ Nothing = void ()++-- | Build the schema based on the configuration.+fromConf :: SchemaConf -> Schema t ()+fromConf SchemaConf{..} = sequenceS_+ [ mkArg0 orthB orthC+ , mkArg0 lowOrthB lowOrthC+ , mkArg1 lowPrefixesB lowPrefixesC+ , mkArg1 lowSuffixesB lowSuffixesC+ , mkArg0 knownB knownC+ , mkArg0 shapeB shapeC+ , mkArg0 packedB packedC+ , mkArg0 begPackedB begPackedC ]++-- -- | Use the schema to extract observations from the sentence.+-- schematize :: Schema t a -> [Mx.Word t] -> CRF.Sent Ob+-- schematize schema xs =+-- map (S.fromList . Ox.execOx . schema v) [0 .. n - 1]+-- where+-- v = V.fromList xs+-- n = V.length v++---------------------------------+-- Default schema configurations.+---------------------------------++-- | Default configuration for the guessing observation schema.+guessConfDefault :: SchemaConf+guessConfDefault = nullConf+ { lowPrefixesC = entryWith [1, 2] [0]+ , lowSuffixesC = entryWith [1, 2] [0]+ , knownC = entry [0]+ , begPackedC = entry [0] }++-- -- | 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 configuration for the guessing observation schema.+disambConfDefault :: SchemaConf+disambConfDefault = nullConf+ { lowOrthC = entry [-1, 0, 1]+ , lowPrefixesC = oov $ entryWith [1, 2, 3] [0]+ , lowSuffixesC = oov $ entryWith [1, 2, 3] [0]+ , begPackedC = oov $ entry [0] }+ where+ oov (Just body) = Just $ body { oovOnly = True }+ oov Nothing = Nothing++-- -- | 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++-- | Use the schema to extract observations from the sentence.+schematize :: Schema t a -> Mx.Sent t -> [[Ob]]+schematize schema xs =+ map (Ox.execOx . schema v) [0 .. n - 1]+ where+ v = V.fromList xs+ n = V.length v
tools/concraft.hs view
@@ -5,8 +5,7 @@ import Control.Applicative ((<$>), (<*>)) import Control.Monad (when) import System.Console.CmdArgs-import Data.Binary (Binary, put, get, encodeFile, decodeFile)-import Data.Text.Binary ()+import Data.Binary (encodeFile, decodeFile) import qualified Numeric.SGD as SGD import qualified Data.Text as T import qualified Data.Text.Lazy.IO as L@@ -15,29 +14,12 @@ 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+import qualified NLP.Concraft.Disamb as D -- | 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@@ -48,7 +30,7 @@ -- Try another command line parsing library? , tagsetPath :: FilePath , ignTag :: String- , discardHidden :: Bool+ -- , discardHidden :: Bool , iterNum :: Double , batchSize :: Int , regVar :: Double@@ -59,8 +41,8 @@ | Disamb { format :: Format , ignTag :: String- , inModel :: FilePath- , guessNum :: Int }+ , inModel :: FilePath }+ -- , guessNum :: Int } deriving (Data, Typeable, Show) trainMode :: Concraft@@ -70,7 +52,7 @@ , evalPath = def &= typFile &= help "Evaluation file" , format = enum [Plain &= help "Plain format"] , ignTag = "ign" &= help "Tag indicating OOV word"- , discardHidden = False &= help "Discard hidden features"+ -- , discardHidden = False &= help "Discard hidden features" , iterNum = 10 &= help "Number of SGD iterations" , batchSize = 30 &= help "Batch size" , regVar = 10.0 &= help "Regularization variance"@@ -83,8 +65,8 @@ disambMode = Disamb { inModel = def &= argPos 0 &= typ "MODEL-FILE" , format = enum [Plain &= help "Plain format"]- , ignTag = "ign" &= help "Tag indicating OOV word"- , guessNum = 10 &= help "Number of guessed tags for each unknown word" }+ , ignTag = "ign" &= 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]@@ -95,34 +77,18 @@ 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 }+ tagset <- P.parseTagset tagsetPath <$> readFile tagsetPath+ concraft <- case format of+ Plain -> train (plainFormat ign) tagset when (not . null $ outModel) $ do putStrLn $ "\nSaving model in " ++ outModel ++ "..."- encodeFile outModel concraftData+ encodeFile outModel concraft 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+ train docH tagset =+ let guessConf = G.TrainConf S.guessConfDefault sgdArgs+ disambConf = D.TrainConf tagset D.tiersDefault+ S.disambConfDefault sgdArgs+ in C.train docH guessNum guessConf disambConf trainPath evalPath ign = T.pack ignTag sgdArgs = SGD.SgdArgs { SGD.batchSize = batchSize@@ -132,20 +98,9 @@ , SGD.tau = tau } exec Disamb{..} = do- doTag <- doTagWith <$> decodeFile inModel <*> L.getContents+ tag <- tagWith <$> decodeFile inModel <*> L.getContents case format of- Plain -> L.putStr $ doTag (plainFormat ign)+ Plain -> L.putStr $ tag (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+ tagWith concraft input docH = C.tagDoc docH concraft input ign = T.pack ignTag