wer.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import os
  2. import numpy as np
  3. import sys
  4. import hydra
  5. from omegaconf import DictConfig, OmegaConf, ListConfig
  6. def compute_wer(ref_file,
  7. hyp_file,
  8. cer_file,
  9. cn_postprocess=False,
  10. ):
  11. rst = {
  12. 'Wrd': 0,
  13. 'Corr': 0,
  14. 'Ins': 0,
  15. 'Del': 0,
  16. 'Sub': 0,
  17. 'Snt': 0,
  18. 'Err': 0.0,
  19. 'S.Err': 0.0,
  20. 'wrong_words': 0,
  21. 'wrong_sentences': 0
  22. }
  23. hyp_dict = {}
  24. ref_dict = {}
  25. with open(hyp_file, 'r') as hyp_reader:
  26. for line in hyp_reader:
  27. key = line.strip().split()[0]
  28. value = line.strip().split()[1:]
  29. if cn_postprocess:
  30. value = " ".join(value)
  31. value = value.replace(" ", "")
  32. if value[0] == "请":
  33. value = value[1:]
  34. value = [x for x in value]
  35. hyp_dict[key] = value
  36. with open(ref_file, 'r') as ref_reader:
  37. for line in ref_reader:
  38. key = line.strip().split()[0]
  39. value = line.strip().split()[1:]
  40. if cn_postprocess:
  41. value = " ".join(value)
  42. value = value.replace(" ", "")
  43. value = [x for x in value]
  44. ref_dict[key] = value
  45. cer_detail_writer = open(cer_file, 'w')
  46. for hyp_key in hyp_dict:
  47. if hyp_key in ref_dict:
  48. out_item = compute_wer_by_line(hyp_dict[hyp_key], ref_dict[hyp_key])
  49. rst['Wrd'] += out_item['nwords']
  50. rst['Corr'] += out_item['cor']
  51. rst['wrong_words'] += out_item['wrong']
  52. rst['Ins'] += out_item['ins']
  53. rst['Del'] += out_item['del']
  54. rst['Sub'] += out_item['sub']
  55. rst['Snt'] += 1
  56. if out_item['wrong'] > 0:
  57. rst['wrong_sentences'] += 1
  58. cer_detail_writer.write(hyp_key + print_cer_detail(out_item) + '\n')
  59. cer_detail_writer.write("ref:" + '\t' + " ".join(list(map(lambda x: x.lower(), ref_dict[hyp_key]))) + '\n')
  60. cer_detail_writer.write("hyp:" + '\t' + " ".join(list(map(lambda x: x.lower(), hyp_dict[hyp_key]))) + '\n')
  61. cer_detail_writer.flush()
  62. if rst['Wrd'] > 0:
  63. rst['Err'] = round(rst['wrong_words'] * 100 / rst['Wrd'], 2)
  64. if rst['Snt'] > 0:
  65. rst['S.Err'] = round(rst['wrong_sentences'] * 100 / rst['Snt'], 2)
  66. cer_detail_writer.write('\n')
  67. cer_detail_writer.write("%WER " + str(rst['Err']) + " [ " + str(rst['wrong_words']) + " / " + str(rst['Wrd']) +
  68. ", " + str(rst['Ins']) + " ins, " + str(rst['Del']) + " del, " + str(
  69. rst['Sub']) + " sub ]" + '\n')
  70. cer_detail_writer.write(
  71. "%SER " + str(rst['S.Err']) + " [ " + str(rst['wrong_sentences']) + " / " + str(rst['Snt']) + " ]" + '\n')
  72. cer_detail_writer.write("Scored " + str(len(hyp_dict)) + " sentences, " + str(
  73. len(hyp_dict) - rst['Snt']) + " not present in hyp." + '\n')
  74. cer_detail_writer.close()
  75. def compute_wer_by_line(hyp,
  76. ref):
  77. hyp = list(map(lambda x: x.lower(), hyp))
  78. ref = list(map(lambda x: x.lower(), ref))
  79. len_hyp = len(hyp)
  80. len_ref = len(ref)
  81. cost_matrix = np.zeros((len_hyp + 1, len_ref + 1), dtype=np.int16)
  82. ops_matrix = np.zeros((len_hyp + 1, len_ref + 1), dtype=np.int8)
  83. for i in range(len_hyp + 1):
  84. cost_matrix[i][0] = i
  85. for j in range(len_ref + 1):
  86. cost_matrix[0][j] = j
  87. for i in range(1, len_hyp + 1):
  88. for j in range(1, len_ref + 1):
  89. if hyp[i - 1] == ref[j - 1]:
  90. cost_matrix[i][j] = cost_matrix[i - 1][j - 1]
  91. else:
  92. substitution = cost_matrix[i - 1][j - 1] + 1
  93. insertion = cost_matrix[i - 1][j] + 1
  94. deletion = cost_matrix[i][j - 1] + 1
  95. compare_val = [substitution, insertion, deletion]
  96. min_val = min(compare_val)
  97. operation_idx = compare_val.index(min_val) + 1
  98. cost_matrix[i][j] = min_val
  99. ops_matrix[i][j] = operation_idx
  100. match_idx = []
  101. i = len_hyp
  102. j = len_ref
  103. rst = {
  104. 'nwords': len_ref,
  105. 'cor': 0,
  106. 'wrong': 0,
  107. 'ins': 0,
  108. 'del': 0,
  109. 'sub': 0
  110. }
  111. while i >= 0 or j >= 0:
  112. i_idx = max(0, i)
  113. j_idx = max(0, j)
  114. if ops_matrix[i_idx][j_idx] == 0: # correct
  115. if i - 1 >= 0 and j - 1 >= 0:
  116. match_idx.append((j - 1, i - 1))
  117. rst['cor'] += 1
  118. i -= 1
  119. j -= 1
  120. elif ops_matrix[i_idx][j_idx] == 2: # insert
  121. i -= 1
  122. rst['ins'] += 1
  123. elif ops_matrix[i_idx][j_idx] == 3: # delete
  124. j -= 1
  125. rst['del'] += 1
  126. elif ops_matrix[i_idx][j_idx] == 1: # substitute
  127. i -= 1
  128. j -= 1
  129. rst['sub'] += 1
  130. if i < 0 and j >= 0:
  131. rst['del'] += 1
  132. elif j < 0 and i >= 0:
  133. rst['ins'] += 1
  134. match_idx.reverse()
  135. wrong_cnt = cost_matrix[len_hyp][len_ref]
  136. rst['wrong'] = wrong_cnt
  137. return rst
  138. def print_cer_detail(rst):
  139. return ("(" + "nwords=" + str(rst['nwords']) + ",cor=" + str(rst['cor'])
  140. + ",ins=" + str(rst['ins']) + ",del=" + str(rst['del']) + ",sub="
  141. + str(rst['sub']) + ") corr:" + '{:.2%}'.format(rst['cor'] / rst['nwords'])
  142. + ",cer:" + '{:.2%}'.format(rst['wrong'] / rst['nwords']))
  143. @hydra.main(config_name=None, version_base=None)
  144. def main_hydra(cfg: DictConfig):
  145. ref_file = cfg.get("ref_file", None)
  146. hyp_file = cfg.get("hyp_file", None)
  147. cer_file = cfg.get("cer_file", None)
  148. cn_postprocess = cfg.get("cn_postprocess", False)
  149. if ref_file is None or hyp_file is None or cer_file is None:
  150. print(
  151. "usage : python -m funasr.metrics.wer ++ref_file=test.ref ++hyp_file=test.hyp ++cer_file=test.wer ++cn_postprocess=false")
  152. sys.exit(0)
  153. compute_wer(ref_file, hyp_file, cer_file, cn_postprocess)
  154. if __name__ == '__main__':
  155. main_hydra()