packages feed

concraft 0.1.0 → 0.2.0

raw patch · 4 files changed

+309/−2 lines, 4 filesdep +crf-chain2-genericdep +tagset-positionalnew-component:exe:concraft-disambPVP ok

version bump matches the API change (PVP)

Dependencies added: crf-chain2-generic, tagset-positional

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: disamb :: Disamb -> Sent 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.Morphosyntax: mapChoice :: Ord b => (a -> b) -> Choice a -> Choice b
+ NLP.Concraft.Morphosyntax: mapWord :: Ord b => (a -> b) -> Word a -> Word b

Files

+ NLP/Concraft/Disamb.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module NLP.Concraft.Disamb+( Tier (..)+, Tag (..)+, select+, splitWord+, splitSent+, Ox+, Schema+, Ob+, schema+, schematize+, TierConf+, tear+, deTear+, deTears+, Disamb+, disamb+, tagFile+, learn+) where++import Control.Applicative ((<$>), (<*>))+import Data.Maybe (fromJust)+import Data.List (find)+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 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)++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++-- | Schematize the input sentence according to 'schema' rules.+schematize :: Sent t -> CRF.Sent Ob t+schematize 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++-- | Split tags between two layers.+-- TODO: Add support for multiple layers.+splitWord :: TierConf -> Word TP.Tag -> Word (Tag, Tag)+splitWord cfg = mapWord (tear cfg)++splitSent :: TierConf -> Sent TP.Tag -> Sent (Tag, Tag)+splitSent ts = map (splitWord ts)++-- | The disambiguation model.+data Disamb = Disamb+    { crf       :: CRF.CRF Ob Tag Tag+    , tagset    :: TP.Tagset+    , tierConf  :: TierConf }++instance Binary Disamb where+    put Disamb{..} = put crf >> put tagset >> put tierConf+    get = Disamb <$> get <*> get <*> get++-- | 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+    $ 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)++-- | 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+  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)++-- | 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)+    -> 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++-- | 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+  where+    onSent sent =+        zip (schematize xs') (map mkDist ys')+      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
NLP/Concraft/Morphosyntax.hs view
@@ -1,7 +1,11 @@+{-# LANGUAGE RecordWildCards #-}+ module NLP.Concraft.Morphosyntax ( Word (..)+, mapWord , Sent , Choice+, mapChoice , Positive (unPositive) , (<+>) , mkPositive@@ -9,6 +13,7 @@ , known ) where +import Control.Arrow (first) import Data.Ord (comparing) import Data.List (maximumBy) import qualified Data.Set as S@@ -23,6 +28,11 @@     , tags  :: S.Set t }     deriving (Show, Read, Eq, Ord) +mapWord :: Ord b => (a -> b) -> Word a -> Word b+mapWord f Word{..} = Word+    { orth = orth+    , tags = S.fromList . map f . S.toList $ tags }+ -- | A sentence of 'Word's. type Sent t = [Word t] @@ -37,6 +47,9 @@ (<+>) :: 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
concraft.cabal view
@@ -1,5 +1,5 @@ name:               concraft-version:            0.1.0+version:            0.2.0 synopsis:           Morphosyntactic tagging tool based on constrained CRFs description:     A morphosyntactic tagging tool based on constrained conditional@@ -26,11 +26,14 @@       , crf-chain1-constrained >= 0.1.1 && < 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      exposed-modules:         NLP.Concraft.Morphosyntax-      , NLP.Concraft.Guess       , NLP.Concraft.Plain+      , NLP.Concraft.Guess+      , NLP.Concraft.Disamb      ghc-options: -Wall -O2 @@ -43,4 +46,11 @@         cmdargs     hs-source-dirs: ., tools     main-is: concraft-guess.hs    +    ghc-options: -Wall -O2 -threaded++executable concraft-disamb+    build-depends:+        cmdargs+    hs-source-dirs: ., tools+    main-is: concraft-disamb.hs         ghc-options: -Wall -O2 -threaded
+ tools/concraft-disamb.hs view
@@ -0,0 +1,86 @@+{-# 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