Repository: espnet/espnet

Issue:
ErrorCalculator does not work in corpus without <space>
hello，when I run aishell recipe，error is as follows：

Traceback (most recent call last):
  File "espnet/bin/asr_train.py", line 373, in <module>
    main(sys.argv[1:])
  File "espnet/bin/asr_train.py", line 361, in main
    train(args)
  File "espnet-0.4.0-py3.6.egg/espnet/asr/pytorch_backend/asr.py", line 257, in train
  File "espnet-0.4.0-py3.6.egg/espnet/nets/pytorch_backend/e2e_asr_transformer.py", line 116, in __init__
  File "espnet-0.4.0-py3.6.egg/espnet/nets/e2e_asr_common.py", line 108, in __init__
ValueError: "\<space>\" is not in list


bug?


Discussion:
Hey, I ran into the same issue with tedlium2. Seems like it is caused by the fact that sentence-piece is now being used for token generation.
There no longer is a designated token "\<space\>"  for spaces.
For now, I fixed it by just commenting out 

`self.idx_space = self.char_list.index(self.space)
`
in line 109

and replacing

`if idx != -1 and idx != self.idx_blank and idx != self.idx_space:
`
with

`if idx != -1 and idx != self.idx_blank:
`
in lines 134 and 139. 

Not sure if this is the right approach though, or if something must be changed about the tokenization.
Which version did you use? Could you checkt it via following command?

```bash
cd egs/<some_recipe>/asr1
. ./path.sh
spm_train --version
```
I figured out this issue.
Some corpus does not include the symbol of `<space>`, but `ErrorCalculator` assumes it.
https://github.com/espnet/espnet/blob/9f0729f31bdf4ba6fdc5ca8a48042c9e6f24e00c/espnet/nets/e2e_asr_common.py#L102-L108
@Fhrozen could you fix it?

I am checking it now


Resolving patch:
diff --git a/espnet/nets/e2e_asr_common.py b/espnet/nets/e2e_asr_common.py
--- a/espnet/nets/e2e_asr_common.py
+++ b/espnet/nets/e2e_asr_common.py
@@ -105,7 +105,10 @@ def __init__(self, char_list, sym_space, sym_blank,
         self.report_cer = report_cer
         self.report_wer = report_wer
         self.idx_blank = self.char_list.index(self.blank)
-        self.idx_space = self.char_list.index(self.space)
+        if self.space in self.char_list:
+            self.idx_space = self.char_list.index(self.space)
+        else:
+            self.idx_space = None
 
     def __call__(self, ys_hat, ys_pad, is_ctc=False):
         cer, wer = None, None
diff --git a/espnet/nets/pytorch_backend/e2e_asr_transformer.py b/espnet/nets/pytorch_backend/e2e_asr_transformer.py
--- a/espnet/nets/pytorch_backend/e2e_asr_transformer.py
+++ b/espnet/nets/pytorch_backend/e2e_asr_transformer.py
@@ -109,7 +109,7 @@ def __init__(self, idim, odim, args, ignore_id=-1):
         else:
             self.ctc = None
 
-        if args.report_cer or args.report_wer or args.mtlalpha > 0.0:
+        if args.report_cer or args.report_wer:
             from espnet.nets.e2e_asr_common import ErrorCalculator
             self.error_calculator = ErrorCalculator(args.char_list,
                                                     args.sym_space, args.sym_blank,
@@ -189,16 +189,16 @@ def forward(self, xs_pad, ilens, ys_pad):
 
         # TODO(karita) show predected text
         # TODO(karita) calculate these stats
+        cer_ctc = None
         if self.mtlalpha == 0.0:
             loss_ctc = None
-            cer_ctc = None
         else:
             batch_size = xs_pad.size(0)
             hs_len = hs_mask.view(batch_size, -1).sum(1)
             loss_ctc = self.ctc(hs_pad.view(batch_size, -1, self.adim), hs_len, ys_pad)
-
-            ys_hat = self.ctc.argmax(hs_pad.view(batch_size, -1, self.adim)).data
-            cer_ctc = self.error_calculator(ys_hat.cpu(), ys_pad.cpu(), is_ctc=True)
+            if self.error_calculator is not None:
+                ys_hat = self.ctc.argmax(hs_pad.view(batch_size, -1, self.adim)).data
+                cer_ctc = self.error_calculator(ys_hat.cpu(), ys_pad.cpu(), is_ctc=True)
 
         # 5. compute cer/wer
         if self.training or self.error_calculator is None:


Test patch:
diff --git a/test/test_utils.py b/test/test_utils.py
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -148,8 +148,8 @@ def test_error_calculator(tmpdir, typ):
     space = "<space>"
     blank = "<blank>"
     char_list = [blank, space, 'a', 'e', 'i', 'o', 'u']
-    ys_pad = [np.random.randint(0, 7, x) for x in range(120, 150, 5)]
-    ys_hat = [np.random.randint(0, 7, x) for x in range(120, 150, 5)]
+    ys_pad = [np.random.randint(0, len(char_list), x) for x in range(120, 150, 5)]
+    ys_hat = [np.random.randint(0, len(char_list), x) for x in range(120, 150, 5)]
     if typ == 'ctc':
         cer, wer = False, False
     elif typ == 'wer':
@@ -182,3 +182,22 @@ def test_error_calculator(tmpdir, typ):
         assert cer_ctc_val is not None
         assert _cer is not None
         assert _wer is not None
+
+
+def test_error_calculator_nospace(tmpdir):
+    from espnet.nets.e2e_asr_common import ErrorCalculator
+    space = "<space>"
+    blank = "<blank>"
+    char_list = [blank, 'a', 'e', 'i', 'o', 'u']
+    ys_pad = [np.random.randint(0, len(char_list), x) for x in range(120, 150, 5)]
+    ys_hat = [np.random.randint(0, len(char_list), x) for x in range(120, 150, 5)]
+    cer, wer = True, True
+
+    ec = ErrorCalculator(char_list, space, blank,
+                         cer, wer)
+
+    cer_ctc_val = ec(ys_pad, ys_hat, is_ctc=True)
+    _cer, _wer = ec(ys_pad, ys_hat)
+    assert cer_ctc_val is not None
+    assert _cer is not None
+    assert _wer is not None