packages feed

hmm (empty) → 0.1

raw patch · 5 files changed

+270/−0 lines, 5 filesdep +basedep +containerssetup-changed

Dependencies added: base, containers

Files

+ Data/HMM.hs view
@@ -0,0 +1,152 @@+module Data.HMM+    (Prob, HMM, train, bestSequence, sequenceProb)+    where++import qualified Data.Map as M+import Data.List (sort, groupBy, maximumBy)+import Data.Maybe (fromMaybe, fromJust)+import System.IO.Unsafe (unsafeInterleaveIO)+import System.Environment (getArgs)+import Control.Monad+import qualified Data.Foldable+import Debug.Trace+import Data.Lognum++type Prob = Lognum Double++-- | The type of Hidden Markov Models.+data HMM state observation = HMM [state] [Prob] [[Prob]] (observation -> [Prob])++-- | Perform a single step in the Viterbi algorithm.+--  +--   Takes a list of path probabilities, and an observation, and returns the updated+--   list of (surviving) paths with probabilities.+viterbi :: Ord observation =>+               HMM state observation+            -> [(Prob, [state])]+            -> observation+            -> [(Prob, [state])]+viterbi (HMM states _ state_transitions observations) prev x = +    [maximumBy (compare `on` fst)+            [(transition_prob * prev_prob * observation_prob,+               new_state:path)+                    | transition_prob <- transition_probs+                    | (prev_prob, path) <- prev+                    | observation_prob <- observation_probs]+        | transition_probs <- state_transitions+        | new_state <- states]+    where+        observation_probs = observations x++-- | The initial value for the Viterbi algorithm+viterbi_init :: HMM state observation -> [(Prob, [state])]+viterbi_init (HMM states state_probs _ _) = zip state_probs (map (:[]) states)++-- | Perform a single step of the forward algorithm+-- +--   Each item in the input and output list is the probability that the system+--   ended in the respective state.+forward :: Ord observation =>+               HMM state observation+            -> [Prob]+            -> observation+            -> [Prob]+forward (HMM _ _ state_transitions observations) prev x =+    [sum [transition_prob * prev_prob * observation_prob+                | transition_prob <- transition_probs+                | prev_prob <- prev+                | observation_prob <- observation_probs]+        | transition_probs <- state_transitions]+    where+        observation_probs = observations x++-- | The initial value for the forward algorithm+forward_init :: HMM state observation -> [Prob]+forward_init (HMM _ state_probs _ _) = state_probs++learn_states :: (Ord state, Fractional prob) => [(observation, state)] -> M.Map state prob+learn_states xs = histogram $ map snd xs++learn_transitions :: (Ord state, Fractional prob) => [(observation, state)] -> M.Map (state, state) prob+learn_transitions xs = let xs' = map snd xs in+                        histogram $ zip xs' (tail xs')++learn_observations ::  (Ord state, Ord observation, Fractional prob) =>+                       M.Map state prob+                    -> [(observation, state)]+                    -> M.Map (observation, state) prob+learn_observations state_prob = M.mapWithKey (\ (observation, state) prob -> prob / (fromJust $ M.lookup state state_prob))+                            . histogram++histogram :: (Ord a, Fractional prob) => [a] -> M.Map a prob+histogram xs = let hist = foldr (flip (M.insertWith (+)) 1) M.empty xs in+                M.map (/ M.fold (+) 0 hist) hist++readBrownFile :: FilePath -> IO [(String, String)]+readBrownFile = (liftM (map split . words)) . readFile+    where+        split [] = ([], [])+        split ('/':xs) = ([], xs)+        split (x:xs) = let (first, snd) = split xs in+                        (x:first, snd)++-- | Calculate the parameters of an HMM from a list of observations+--   and the corresponding states.+train :: (Ord observation, Ord state) =>+            [(observation, state)]+         -> HMM state observation+train sample = model+    where+        states = learn_states sample+        state_list = M.keys states+        +        transitions = learn_transitions sample+        trans_prob_mtx = [[fromMaybe 1e-10 $ M.lookup (old_state, new_state) transitions+                                | old_state <- state_list]+                                | new_state <- state_list]++        observations = learn_observations states sample+        observation_probs = fromMaybe (fill state_list []) . (flip M.lookup $+                            M.fromList $ map (\ (e, xs) -> (e, fill state_list xs)) $+                                map (\ xs -> (fst $ head xs, map snd xs)) $+                                groupBy     ((==) `on` fst)+                                            [(observation, (state, prob))+                                                | ((observation, state), prob) <- M.toAscList observations])++        initial = map (\ state -> (fromJust $ M.lookup state states, [state])) state_list++        model = HMM state_list (fill state_list $ M.toAscList states) trans_prob_mtx observation_probs++        fill :: Eq state => [state] -> [(state, Prob)] -> [Prob]+        fill states [] = map (const 1e-10) states+        fill (s:states) xs@((s', p):xs') = if s /= s' then+                                            1e-10 : fill states xs+                                           else+                                            p : fill states xs'++-- | Test Viterbi's algorithm on an HMM by comparing the predicted states+--   against known states for the observations.+testViterbi :: (Ord observation, Ord state) =>+                   HMM state observation+                -> [(observation, state)]+                -> Rational+testViterbi hmm testData = (fromIntegral $ length $ filter id $ zipWith (==) (bestSequence hmm observations) states) +                            / (fromIntegral $ length testData)+    where+        observations = map fst testData+        states = map snd testData++train2 :: FilePath -> IO (HMM String String)+train2 = (liftM train) . readBrownFile++-- | Calculate the most likely sequence of states for a given sequence of observations+--   using Viterbi's algorithm+bestSequence :: (Ord observation) => HMM state observation -> [observation] -> [state]+bestSequence hmm = (reverse . tail . snd . (maximumBy (compare `on` fst))) . (foldl (viterbi hmm) (viterbi_init hmm))++-- | Calculate the probability of a given sequence of observations+--    using the forward algorithm.+sequenceProb :: (Ord observation) => HMM state observation -> [observation] -> Prob+sequenceProb hmm = sum . (foldl (forward hmm) (forward_init hmm))++on f g a b = f (g a) (g b)
+ Data/Lognum.hs view
@@ -0,0 +1,50 @@+module Data.Lognum (Lognum)+    where++import Data.Ratio++data Lognum t = L !Int !t deriving (Eq, Ord)++fromFloating :: (Floating t, Ord t) => t -> Lognum t+fromFloating a = case a `compare` 0 of+                LT -> L (-1) (log $ -a)+                EQ -> L 0 0+                GT -> L 1 (log a)++toFloating :: (Floating t, Ord t) => Lognum t -> t+toFloating (L s m) = case s of+                        -1 -> negate $ exp m+                        0  -> 0+                        1  -> exp m++logify2 :: (Floating t, Ord t) => (t -> t -> t) -> (Lognum t -> Lognum t -> Lognum t)+logify2 (*) a b = fromFloating $ toFloating a * toFloating b++instance (Floating t, Ord t) => Show (Lognum t) where+    show = show . toFloating++instance (Floating t, Ord t) => Num (Lognum t) where+    (+) = logify2 (+)++    (L s m) * (L s' m') =   if s == 0 || s' == 0 then+                                L 0 0+                            else+                                L (s*s') (m+m')++    (-) = logify2 (-)++    negate (L s m) = L (negate s) m++    abs (L s m) = L (abs s) m++    signum (L s m) = (L s 0)++    fromInteger = fromFloating . fromInteger++instance (Floating t, Ord t) => Fractional (Lognum t) where+    _ / (L 0 _) = error "division by zero"+    (L s m) / (L s' m') = L (s*s') (m-m')+    +    recip (L s m) = L s (-m)+    +    fromRational x = (fromInteger $ numerator x) / (fromInteger $ denominator x)
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) Max Rabkin++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,4 @@+#! /usr/bin/env runhaskell++> import Distribution.Simple+> main = defaultMain
+ hmm.cabal view
@@ -0,0 +1,34 @@+Name:               hmm+Version:            0.1+Description:+    A simple library for working with Hidden Markov Models.+    Should be usable even by people who are not familiar with+    HMMs. Includes implementations of Viterbi's algorithm and+    the forward algorithm.+Category:           algorithms, natural language processing, data mining+Synopsis:           Hidden Markov Model algorithms+License:            BSD3+License-file:       LICENSE+Author:             Max Rabkin+Maintainer:         max.rabkin@gmail.com+Stability:          Alpha+Build-Type:         Simple+Cabal-Version:      >= 1.2++Flag small_base+    Description: Choose the new smaller, split-up base package.++Library+    if flag(small_base)+        Build-Depends:  base >= 3, containers+    else+        Build-Depends:  base < 3+        +    Exposed-Modules:+        Data.HMM++    Other-Modules:+        Data.Lognum++    Extensions:+        ParallelListComp