Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions deep_speech_2/error_rate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# -- * -- coding: utf-8 -- * --
import numpy as np
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a simple module doc.



def levenshtein_distance(ref, hyp):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. rename it to _levenshtein_distance ?
  2. Add a simple description or reference for levenshtein_distance?

ref_len = len(ref)
hyp_len = len(hyp)

# special case
if ref == hyp:
return 0
if ref_len == 0:
return hyp_len
if hyp_len == 0:
return ref_len

distance = np.zeros((ref_len + 1) * (hyp_len + 1), dtype=np.int64)
distance = distance.reshape((ref_len + 1, hyp_len + 1))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above two lines can be merged into one line

distance = np.zeros((ref_len + 1, hyp_len + 1), dtype=np.int64)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


# initialization distance matrix
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initialization --> initialize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

for j in xrange(hyp_len + 1):
distance[0][j] = j
for i in xrange(ref_len + 1):
distance[i][0] = i

# calculate levenshtein distance
for i in xrange(1, ref_len + 1):
for j in xrange(1, hyp_len + 1):
if ref[i - 1] == hyp[j - 1]:
distance[i][j] = distance[i - 1][j - 1]
else:
s_num = distance[i - 1][j - 1] + 1
i_num = distance[i][j - 1] + 1
d_num = distance[i - 1][j] + 1
distance[i][j] = min(s_num, i_num, d_num)

return distance[ref_len][hyp_len]


def wer(reference, hypophysis, delimiter=' ', filter_none=True):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. "hypophysis" or "hypothesis"?
  2. Why not provide a ignore_case argument, just as CER does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"""
Calculate word error rate (WER). WER is a popular evaluation metric used
in speech recognition. It compares a reference to an hypophysis and
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compare to --> compare with?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

is defined like this:

.. math::
WER = (Sw + Dw + Iw) / Nw

where

.. code-block:: text

Sw is the number of words subsituted,
Dw is the number of words deleted,
Iw is the number of words inserted,
Nw is the number of words in the reference

We can use levenshtein distance to calculate WER. Take an attention that
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems there is no "take an attention", but pay/draw ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

this function will truncate the beginning and ending delimiter for
reference and hypophysis sentences before calculating WER.

:param reference: The reference sentence.
:type reference: str
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str --> basestring. The same below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

:param hypophysis: The hypophysis sentence.
:type reference: str
:param delimiter: Delimiter of input sentences.
:type delimiter: char
:param filter_none: Whether to remove None value when splitting sentence.
:type filter_none: bool
:return: WER
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"WER" --> "Word error rate."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

:rtype: float
Copy link
Contributor

@xinghai-sun xinghai-sun Jun 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add :raises ValueError: If there is zero reference words.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"""

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the blank line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if len(reference.strip(delimiter)) == 0:
raise ValueError("Reference's word number should be greater than 0.")

if filter_none == True:
ref_words = filter(None, reference.strip(delimiter).split(delimiter))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cer和wer中strip的处理逻辑不同,让人疑惑。建议二者都不做strip,并且去除filter_none参数,直接filter。

注意下:strip,squeeze,filter_none这几个逻辑在cer和wer中很容易让人混乱。如果某个参数没能很清楚得解释,宁可去掉该参数,而不要含糊不清地留给用户。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

hyp_words = filter(None, hypophysis.strip(delimiter).split(delimiter))
else:
ref_words = reference.strip(delimiter).split(delimiter)
hyp_words = reference.strip(delimiter).split(delimiter)

edit_distance = levenshtein_distance(ref_words, hyp_words)
wer = float(edit_distance) / len(ref_words)
return wer


def cer(reference, hypophysis, squeeze=True, ignore_case=False, strip_char=''):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. hypophysis --> hypothesis
    The same below.
  2. Why is strip_char necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"""
Calculate charactor error rate (CER). CER will compare reference text and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will compare --> compares

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

hypophysis text in char-level. CER is defined as:

.. math::
CER = (Sc + Dc + Ic) / Nc

where

.. code-block:: text

Sc is the number of character substituted,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

character --> characters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Dc is the number of deleted,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the number of deleted --> the number of characters deleted.
The same below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Ic is the number of inserted
Nc is the number of characters in the reference

We can use levenshtein distance to calculate CER. Chinese input should be
encoded to unicode.

:param reference: The reference sentence.
:type reference: str
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str --> basestring. For unicode is not a str, but a basestring.
The same below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

:param hypophysis: The hypophysis sentence.
:type reference: str
:param squeeze: If set true, consecutive space character
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space character --> whitespace

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

will be squeezed to one
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to add and indent in line 114? The same below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

:type squeezed: bool
:param ignore_case: Whether ignoring character case.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether case-sensitive or not

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

:type ignore_case: bool
:param strip_char: If not set to '', strip_char in beginning and ending of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--> If not None, leading and trailing strip_char will be removed.

sentence will be truncated.
:type strip_char: char
:return: CER
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CER --> Character error rate.

:rtype: float
Copy link
Contributor

@xinghai-sun xinghai-sun Jun 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add :raises ValueError: If reference length is zero.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"""
if ignore_case == True:
reference = reference.lower()
hypophysis = hypophysis.lower()
if strip_char != '':
reference = reference.strip(strip_char)
hypophysis = hypophysis.strip(strip_char)
if squeeze == True:
reference = ' '.join(filter(None, reference.split(' ')))
hypophysis = ' '.join(filter(None, hypophysis.split(' ')))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The leading and trailing whitespace will be completely "removed" by this line of codes, instead of just "squeeze to one" as explained in the function doc. Please make them consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


if len(reference) == 0:
raise ValueError("Length of reference should be greater than 0.")
edit_distance = levenshtein_distance(reference, hypophysis)
cer = float(edit_distance) / len(reference)
return cer