Add WER metric

This commit is contained in:
Filip Gralinski 2018-09-25 08:13:57 +02:00
parent 4f09a1802f
commit eb395d9be0
9 changed files with 46 additions and 4 deletions

View File

@ -34,6 +34,7 @@ library
, Data.Conduit.SmartSource , Data.Conduit.SmartSource
, Data.Conduit.Rank , Data.Conduit.Rank
, GEval.FeatureExtractor , GEval.FeatureExtractor
, GEval.WER
, Text.Tokenizer , Text.Tokenizer
, Paths_geval , Paths_geval
build-depends: base >= 4.7 && < 5 build-depends: base >= 4.7 && < 5
@ -74,6 +75,7 @@ library
, process , process
, uri-encode , uri-encode
, MissingH , MissingH
, array
default-language: Haskell2010 default-language: Haskell2010
executable geval executable geval

View File

@ -1,5 +1,3 @@
{-# LANGUAGE BangPatterns #-}
module GEval.BLEU module GEval.BLEU
(precisionCount, bleuStep, gleuStep) (precisionCount, bleuStep, gleuStep)
where where

View File

@ -80,6 +80,7 @@ import GEval.LogLossHashed
import GEval.CharMatch import GEval.CharMatch
import GEval.BIO import GEval.BIO
import GEval.ProbList import GEval.ProbList
import GEval.WER
import Data.Conduit.AutoDecompress import Data.Conduit.AutoDecompress
import Text.Tokenizer import Text.Tokenizer
@ -97,7 +98,7 @@ defaultLogLossHashedSize :: Word32
defaultLogLossHashedSize = 10 defaultLogLossHashedSize = 10
-- | evaluation metric -- | evaluation metric
data Metric = RMSE | MSE | BLEU | GLEU | Accuracy | ClippEU | FMeasure Double | NMI data Metric = RMSE | MSE | BLEU | GLEU | WER | Accuracy | ClippEU | FMeasure Double | NMI
| LogLossHashed Word32 | CharMatch | MAP | LogLoss | Likelihood | LogLossHashed Word32 | CharMatch | MAP | LogLoss | Likelihood
| BIOF1 | BIOF1Labels | LikelihoodHashed Word32 | MAE | MultiLabelFMeasure Double | BIOF1 | BIOF1Labels | LikelihoodHashed Word32 | MAE | MultiLabelFMeasure Double
| MultiLabelLogLoss | MultiLabelLikelihood | MultiLabelLogLoss | MultiLabelLikelihood
@ -108,6 +109,7 @@ instance Show Metric where
show MSE = "MSE" show MSE = "MSE"
show BLEU = "BLEU" show BLEU = "BLEU"
show GLEU = "GLEU" show GLEU = "GLEU"
show WER = "WER"
show Accuracy = "Accuracy" show Accuracy = "Accuracy"
show ClippEU = "ClippEU" show ClippEU = "ClippEU"
show (FMeasure beta) = "F" ++ (show beta) show (FMeasure beta) = "F" ++ (show beta)
@ -140,6 +142,7 @@ instance Read Metric where
readsPrec _ ('M':'S':'E':theRest) = [(MSE, theRest)] readsPrec _ ('M':'S':'E':theRest) = [(MSE, theRest)]
readsPrec _ ('B':'L':'E':'U':theRest) = [(BLEU, theRest)] readsPrec _ ('B':'L':'E':'U':theRest) = [(BLEU, theRest)]
readsPrec _ ('G':'L':'E':'U':theRest) = [(GLEU, theRest)] readsPrec _ ('G':'L':'E':'U':theRest) = [(GLEU, theRest)]
readsPrec _ ('W':'E':'R':theRest) = [(WER, theRest)]
readsPrec _ ('A':'c':'c':'u':'r':'a':'c':'y':theRest) = [(Accuracy, theRest)] readsPrec _ ('A':'c':'c':'u':'r':'a':'c':'y':theRest) = [(Accuracy, theRest)]
readsPrec _ ('C':'l':'i':'p':'p':'E':'U':theRest) = [(ClippEU, theRest)] readsPrec _ ('C':'l':'i':'p':'p':'E':'U':theRest) = [(ClippEU, theRest)]
readsPrec _ ('N':'M':'I':theRest) = [(NMI, theRest)] readsPrec _ ('N':'M':'I':theRest) = [(NMI, theRest)]
@ -175,6 +178,7 @@ getMetricOrdering RMSE = TheLowerTheBetter
getMetricOrdering MSE = TheLowerTheBetter getMetricOrdering MSE = TheLowerTheBetter
getMetricOrdering BLEU = TheHigherTheBetter getMetricOrdering BLEU = TheHigherTheBetter
getMetricOrdering GLEU = TheHigherTheBetter getMetricOrdering GLEU = TheHigherTheBetter
getMetricOrdering WER = TheLowerTheBetter
getMetricOrdering Accuracy = TheHigherTheBetter getMetricOrdering Accuracy = TheHigherTheBetter
getMetricOrdering ClippEU = TheHigherTheBetter getMetricOrdering ClippEU = TheHigherTheBetter
getMetricOrdering (FMeasure _) = TheHigherTheBetter getMetricOrdering (FMeasure _) = TheHigherTheBetter
@ -525,6 +529,8 @@ gevalCore' GLEU _ = gevalCoreWithoutInput (Right . Prelude.map Prelude.words . D
gleuAgg = CC.foldl gleuFuse (0, 0) gleuAgg = CC.foldl gleuFuse (0, 0)
gleuFuse (a1, a2) (b1, b2) = (a1+b1, a2+b2) gleuFuse (a1, a2) (b1, b2) = (a1+b1, a2+b2)
gevalCore' WER _ = gevalCoreWithoutInput (Right . Prelude.words . unpack) (Right . Prelude.words . unpack) (uncurry werStep) averageC id
gevalCore' Accuracy _ = gevalCoreWithoutInput (Right . strip) (Right . strip) hitOrMiss averageC id gevalCore' Accuracy _ = gevalCoreWithoutInput (Right . strip) (Right . strip) hitOrMiss averageC id
where hitOrMiss (exp, got) = where hitOrMiss (exp, got) =
-- first try to parse what we got as a probability distribution -- first try to parse what we got as a probability distribution

View File

@ -169,7 +169,7 @@ metricReader = many $ option auto -- actually `some` should be used inst
( long "metric" -- --metric might be in the config.txt file... ( long "metric" -- --metric might be in the config.txt file...
<> short 'm' <> short 'm'
<> metavar "METRIC" <> metavar "METRIC"
<> help "Metric to be used - RMSE, MSE, Accuracy, LogLoss, Likelihood, F-measure (specify as F1, F2, F0.25, etc.), multi-label F-measure (specify as MultiLabel-F1, MultiLabel-F2, MultiLabel-F0.25, etc.), MAP, BLEU, GLEU (\"Google GLEU\" not the grammar correction metric), NMI, ClippEU, LogLossHashed, LikelihoodHashed, BIO-F1, BIO-F1-Labels or CharMatch" ) <> help "Metric to be used - RMSE, MSE, Accuracy, LogLoss, Likelihood, F-measure (specify as F1, F2, F0.25, etc.), multi-label F-measure (specify as MultiLabel-F1, MultiLabel-F2, MultiLabel-F0.25, etc.), MAP, BLEU, GLEU (\"Google GLEU\" not the grammar correction metric), WER, NMI, ClippEU, LogLossHashed, LikelihoodHashed, BIO-F1, BIO-F1-Labels or CharMatch" )
altMetricReader :: Parser (Maybe Metric) altMetricReader :: Parser (Maybe Metric)
altMetricReader = optional $ option auto altMetricReader = optional $ option auto

26
src/GEval/WER.hs Normal file
View File

@ -0,0 +1,26 @@
module GEval.WER
(werStep)
where
import Data.Array
import GEval.Common
werStep :: Eq a => [a] -> [a] -> Double
werStep expected got = (fromIntegral $ distance expected got) `safeDoubleDiv` (fromIntegral $ length expected)
-- see https://stackoverflow.com/questions/6718787/levenshtein-distance-cost
distance u v = memo ! (m, n)
where memo = listArray ((0, 0), (m, n)) [dist i j | i <- [0..m], j <- [0..n]]
dist 0 j = j
dist i 0 = i
dist i j = minimum [
1 + memo ! (i, j-1),
1 + memo ! (i-1, j),
fromEnum (u' ! (i-1) /= v' ! (j-1)) + memo ! (i-1, j-1) ]
u' = listArray (0, m-1) u
v' = listArray (0, n-1) v
m = length u
n = length v

View File

@ -88,6 +88,9 @@ main = hspec $ do
runGEvalTest "gleu-empty" `shouldReturnAlmost` 0.0 runGEvalTest "gleu-empty" `shouldReturnAlmost` 0.0
it "perfect translation" $ it "perfect translation" $
runGEvalTest "gleu-perfect" `shouldReturnAlmost` 1.0 runGEvalTest "gleu-perfect" `shouldReturnAlmost` 1.0
describe "WER" $ do
it "simple example" $
runGEvalTest "wer-simple" `shouldReturnAlmost` 0.5555555555
describe "Accuracy" $ do describe "Accuracy" $ do
it "simple example" $ it "simple example" $
runGEvalTest "accuracy-simple" `shouldReturnAlmost` 0.6 runGEvalTest "accuracy-simple" `shouldReturnAlmost` 0.6

View File

@ -0,0 +1,3 @@
It was a very very tall tree
No
A perfect match .
1 It was a very very tall tree
2 No
3 A perfect match .

View File

@ -0,0 +1 @@
--metric WER

View File

@ -0,0 +1,3 @@
It is a tall tree .
Yes
A perfect match .
1 It is a tall tree .
2 Yes
3 A perfect match .