中文文本纠错(Chinese Spell Checking, CSC)任务各个论文的评价指标
文章目录
本文说明
本文汇总了中文文本纠错(Chinese Spell Checking)任务在各个开源项目中的评价指标,他们虽然写法不同,但大部分本质是相同的,但也有少部分论文的评价指标存在问题或其他论文不一致,本文对他们的指标代码进行了分析,并说明了其中的问题。
评价指标总结(结论)
中文文本纠错通常使用精准率(Precision)、召回率(Recall)和F1-Score作为评价指标,有如下四种:
- Character-level Detection Metrics:少数论文使用了。意思是:按字为维度统计,能检测出错字的情况;就目前来看,大部分论文的该指标统计方式相同。
- Character-level Correction Metrics:少数论文使用了。意思是:按字为维度统计,能正确纠正字的情况;目前找到有三篇论文使用了该指标,但多多少少都存在问题。(如果大家找到哪个开源项目使用了该指标,欢迎在评论区提醒,我会补充进来)
- Sentence-level Detection Metrics:大部分论文使用了。意思是:按句子为维度统计,能检测出句子存在错字的情况。大部分论文对该指标统计方式相同。
- Sentence-level Correction Metrics::几乎所有论文都使用了。意思是:按句子为维度,能完全正确修改句子的情况。大部分论文对该指标统计方式相同。
下面我将会使用混淆矩阵的方式给出这四种指标的定义,会用到的术语如下:①该纠:表示该句子(汉字)中存在错字。②不该纠:表示该句子(汉字)中不存在错字;③纠了:表示模型对该句子(汉字)进行了改错。④未纠:表示模型未对该句子(汉字)做任何修改
顺手写一下指标公式:
- 准确率(Accuracy): (TP+TN) / (TP+FP+TN+FN)
- 精准率(Precision): TP / (TP + FP)
- 召回率(Recall): TP / (TP+FN)
- F1-Score: (2 * Precision * Recall) / (Precsion + Recall)
Character-level Detection Metrics
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠的字,纠了,纠没纠对不管 | (FN) 该纠的字,没纠 |
N | (FP) 不该纠的字,纠了 | (TN) 不该纠的字,未纠 |
Character-level Correction Metrics
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠的字,纠了也纠对了。 | (FN) 该纠的字,没纠或没纠对 |
N | (FP) 不该纠的字,纠了。 | (TN) 不该纠的字,没纠 |
目前我看到只有PLOME和Confusionset-guided Pointer Networks这两篇论文的开源项目用了这个指标,但它们好像都有点问题。具体可以看下面对论文指标的详细解析。
Sentence-level Detection Metrics
SIGHAN 官方指标:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且该纠的字,都纠了,纠没纠对不管;不该纠的字,都没纠 | (FN) 该纠,但未纠或把不该纠的字纠了 |
N | (FP) 不该纠,但纠了 | (TN) 不该纠,未纠 |
论文常用指标
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且该纠的字,都纠了,纠没纠对不管;不该纠的字,都没纠 | (FN) 该纠,但未纠或把不该纠的字纠了 |
N | (FP) "不该纠,但纠了" 或 "纠了,但把不该纠的字纠了" | (TN) 不该纠,未纠 |
为什么官方和“论文常用指标”不一样,请参考“Sentence-level Correction Metrics(重点)”这节
Sentence-level Correction Metrics(重点)
SIGHAN官方工具的评价方式:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且纠对了 | (FN)该纠,未纠或纠错了 |
N | (FP) 不该纠,但纠了 | (TN) 不该纠,未纠 |
目前论文常用的评价方式:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且纠对了 | (FN)该纠,未纠或纠错了 |
N | (FP) "不该纠,但纠了" 或 "纠了,但没纠对(原句子是否有错不管)" | (TN) 不该纠,未纠 |
在Sentence-level Correction Metrics上,现有的论文与SIGHAN的指标方式并不一样。现有的论文实验方式伪代码通常是这样:
```python true_positive = 0 # “原句子有错,且纠对了”的数量 target_postive = 0 # “原句子有错”的数量 pred_positive = 0 # “对原句子进行了纠错(原句子是否有错不管)”的数量 for src, target, pred in (...): if src != target and target == pred: # 原句子有错,且纠对了 true_positive += 1 if src != target: # 原句子有错 target_postive += 1 if src != pred: # 对原句子进行了纠错(原句子是否有错不管) pred_positive += 1 precision = true_positive / pred_positive recall = true_positive / target_postive ```
这种实现思路看似毫无问题:①精准率为“对于纠错的那部分句子中,纠对了的句子的比例” ②召回率为“对于该纠的那部分句子,纠对了的句子的比例”
然而,若通过混淆矩阵来看的话,似乎就发现了问题,部分样本重复出现在了“False Positive”中,导致精准率偏低。
例如,对于如下样本:
- src:我喜欢唱跳rap烂球
- tgt:我喜欢唱跳rap篮球
- pred: 我喜欢唱跳rap打球
对于该样本,其同时满足 “target_postive” 和 “pred_positive”,也就是该样本同时出现在了“精准率”和“召回率”的分母中。
因此,可以得出以下结论:
- 现有的SIGHAN官方的指标在精准率上要高于“论文常用指标”
- 现有的SIGHAN官方的指标在召回率上与“论文常用指标”相等
- 如果你看到某个论文的精准率特别高,那就说明它使用的是官方的指标,因此它与现有论文在F1上的比较是不公平的
各开源项目使用的评价指标
SIGHAN(官方)
Sentence-level Detection Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且该纠的字,都纠了,纠没纠对不管;不该纠的字,都没纠 | (FN) 该纠,但未纠或把不该纠的字纠了 |
N | (FP) 不该纠,但纠了 | (TN) 不该纠,未纠 |
Sentence-level Correction Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且纠对了 | (FN)该纠,未纠或纠错了 |
N | (FP) 不该纠,但纠了 | (TN) 不该纠,未纠 |
SIGHAN官方提供的工具是Java的,反编译后的部分代码如下:
```java while(...) { String id = (String)var20.next(); // gct为真实值,rct为预测值。 // 数据格式为:(位置, 字) // 例如:[(1, 鸡), (5, 美)],表示1位置字应该改成鸡,5位置字应该改成美 Map<Integer, String> gctMap = (Map)gtMap.get(id); Map<Integer, String> rctMap = (Map)rtMap.get(id); if (gctMap.size() == 0) { // 不该纠(句子没错) if (rctMap.size() == 0) { // 不该纠,没纠 dtnSet.add(id); // detect TN itnSet.add(id); // correct TN (这里的i是Identification) } else { // 不该纠,但纠了 dfpSet.add(id); // detect FP ifpSet.add(id); // correct FP } } else if (rctMap.size() == 0) { // 该纠,但没纠 dfnSet.add(id); // detect FN ifnSet.add(id); // correct FN } else if (gctMap.keySet().containsAll(rctMap.keySet()) && gctMap.size() == rctMap.size()) { // 该纠,纠了,纠的位置也对。 if (gctMap.values().containsAll(rctMap.values())) { // 该纠,纠对了 dtpSet.add(id); // detect TP itpSet.add(id); // correct TP } else { // 该纠,且该纠的字,都纠了,但是有些字没纠对。 dtpSet.add(id); // detect TP ifnSet.add(id); // correct FN } } else { // 该纠,纠了,但纠了不该纠的字。 dfnSet.add(id); // detect FN ifnSet.add(id); // correct FN } } double fp = (double)dfpSet.size() / (double)(dfpSet.size() + dtnSet.size()); double daccuracy = ((double)dtpSet.size() + (double)dtnSet.size()) / (double)rtList.size(); double dprecision = (double)dtpSet.size() / (double)(dtpSet.size() + dfpSet.size()); double drecall = (double)dtpSet.size() / (double)(dtpSet.size() + dfnSet.size()); double df1Score = 2.0D * dprecision * drecall / (dprecision + drecall); double iaccuracy = ((double)itpSet.size() + (double)itnSet.size()) / (double)rtList.size(); double iprecision = (double)itpSet.size() / (double)(itpSet.size() + ifpSet.size()); double irecall = (double)itpSet.size() / (double)(itpSet.size() + ifnSet.size()); double if1Score = 2.0D * iprecision * irecall / (iprecision + irecall); ```
Confusionset-guided Pointer Networks
论文地址:https://aclanthology.org/P19-1578.pdf
Character-level Detection Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠的字,纠了,纠没纠对不管 | (FN) 该纠的字,没纠 |
N | (FP) 不该纠的字,纠了 | (TN) 不该纠的字,未纠 |
```python for ... in ...: gold_index = ... # 错字的位置。例如: [1, 3, 5]表示该句话1,3,5位置是错字 predict_index = ... # 预测错字的位置。例如: [3, 5, 7]表示模型对3,5,7位置的字进行了纠错 for i in predict_index: if i in gold_index: TP += 1 # 该纠的字,纠了,纠没纠对不管 else: FP += 1 # 不该纠的字,纠了 for i in gold_index: if i in predict_index: continue else: FN += 1 # 该纠的字,但没纠 detection_precision = TP / (TP + FP) if (TP+FP) > 0 else 0 detection_recall = TP / (TP + FN) if (TP+FN) > 0 else 0 detection_f1 = 2 * (detection_precision * detection_recall) / (detection_precision + detection_recall) if (detection_precision + detection_recall) > 0 else 0 ```
Character-level Correction Metrics:
作者与正常的Character-level Correction Metrics不一样,他只考虑了“该纠的字,纠了”这样的场景,其他场景不在该指标的考虑范围内。因此,本次不对该指标进行讨论。
```python for i in range(len(all_predict_true_index)): # we only detect those correctly detected location, which is a different from the common metrics since # we wanna to see the precision improve by using the confusionset if len(all_predict_true_index[i]) > 0: predict_words = [] for j in all_predict_true_index[i]: predict_words.append(results[i][2][j]) if results[i][1][j] == results[i][2][j]: TP += 1 else: FP += 1 for j in all_gold_index[i]: if results[i][1][j] in predict_words: continue else: FN += 1 correction_precision = TP / (TP + FP) if (TP+FP) > 0 else 0 correction_recall = TP / (TP + FN) if (TP+FN) > 0 else 0 correction_f1 = 2 * (correction_precision * correction_recall) / (correction_precision + correction_recall) if (correction_precision + correction_recall) > 0 else 0 ```
PLOME
Character-level Detection Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠的字,纠了,纠没纠对不管 | (FN) 该纠的字,没纠 |
N | (FP) 不该纠的字,纠了 | (TN) 不该纠的字,未纠 |
Character-level Correction Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠的字,纠了也纠对了。 | (FN) 该纠的字,没纠 |
N | (FP) 该纠的字,纠了,但没纠对。 | (TN) 不该纠的字,没纠 |
Character-level Correction Metrics这个指标应该有问题,这个FP应该不对。对于“不该纠的字,但纠了”这种场景没有被统计进去。例如:ori='张', god='张', prd='李',这种场景没有被统计到Character-level Correction指标中。
```python for ... in ...: ori_txt = ... # 原字 god_txt = ... # 正确字 prd_txt = ... # 预测字 # 不该纠的字,没纠。即TN if ori_txt == god_txt and ori_txt == prd_txt: continue if ori != god: # 该纠 total_gold_err += 1 # 相当于(TP+FN) if prd != ori: # 纠了 total_pred_err += 1 # 相当于(TP+FP) if (ori != god) and (prd != ori): # 该纠,且纠了 check_right_pred_err += 1 # 该纠,且纠了,不管对没对 if god == prd: # 该纠,且纠对了 right_pred_err += 1 # Detect P, R, F1 p = 1. * check_right_pred_err / (total_pred_err + 0.001) r = 1. * check_right_pred_err / (total_gold_err + 0.001) f = 2 * p * r / (p + r + 1e-13) # Correct P, R, F1 pc = 1. * right_pred_err / (check_right_pred_err + 0.001) # TP/(TP+FP) rc = 1. * right_pred_err / (total_gold_err + 0.001) # TP/(TP+FN) fc = 2 * pc * rc / (pc + rc + 1e-13) ```
Sentence-level Detection Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且该纠的字,都纠了,纠没纠对不管;不该纠的字,都没纠 | (FN) 该纠,但未纠或把不该纠的字纠了 |
N | (FP) 不该纠,但纠了 或 "纠了,但把不该纠的字纠了" | (TN) 不该纠,未纠 |
Sentence-level Correction Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且纠对了 | (FN)该纠,未纠或纠错了 |
N | (FP) 不该纠,但纠了 或 "纠了,但没纠对(原句子是否有错不管)" | (TN) 不该纠,未纠 |
```python for ... in ...: # errs存的是错字位置,例如:[1, 5],表示1和5位置上有错字 gold_errs = ... # Label pred_errs = ... # 预测结果 # tags存的是错字位置即改正后的字。例如:[(1, 鸡), (5, 美)],表示1位置字应该改成鸡,5位置字应该改成美 god_tags = ... # Label pred_tags = .. # 预测结果 # 该纠 if len(gold_errs) > 0: total_gold_err += 1 # 相当于TP+FN # 纠了 if len(pred_errs) > 0: total_pred_err += 1 # 相当于TP+FP if gold_errs == pred_errs: # 该纠的字都纠了,不该纠的字都没纠,纠没纠对不管 check_right_pred_err += 1 if god_tags == prd_tags: # 该纠,纠对了 right_pred_err += 1 # Sentence-level Detection Metrics p = 1. * check_right_pred_err / total_pred_err r = 1. * check_right_pred_err / total_gold_err f = 2 * p * r / (p + r + 1e-13) # Sentence-level Correction Metrics p = 1. * right_pred_err / total_pred_err r = 1. * right_pred_err / total_gold_err f = 2 * p * r / (p + r + 1e-13) ```
ReaLiSe
ReaLiSe字段说明:
targ/pred
:存的是错字位置即改正后的字。例如:[(1, 鸡), (5, 美)],表示1位置字应该改成鸡,5位置字应该改成美
Sentence-level Detection Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且该纠的字,都纠了,纠没纠对不管;不该纠的字,都没纠 | (FN) 该纠,但未纠或把不该纠的字纠了 |
N | (FP) 不该纠,但纠了 或 "纠了,但把不该纠的字纠了" | (TN) 不该纠,未纠 |
```python for ... in ...: # 该纠的句子(句子有错字) if targ != []: targ_p += 1 # 相当于TP+FN # 纠了的句子(句子有没有错字不知道,模型认为有) if pred != []: pred_p += 1 # 相当于TP+FP # 不该纠,没纠;或 该纠,且该纠的字,都纠了,纠没纠对不管;不该纠的字,都没纠 if len(pred) == len(targ) and all(p[0] == t[0] for p, t in zip(pred, targ)): hit += 1 # 该纠,且该纠的字,都纠了,纠没纠对不管;不该纠的字,都没纠 if pred != [] and len(pred) == len(targ) and all(p[0] == t[0] for p, t in zip(pred, targ)): tp += 1 acc = hit / len(targs) p = tp / pred_p r = tp / targ_p f1 = 2 * p * r / (p + r) if p + r > 0 else 0.0 ```
Sentence-level Correction Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且纠对了 | (FN)该纠,未纠或纠错了 |
N | (FP) 不该纠,但纠了 或 "纠了,但没纠对(原句子是否有错不管)" | (TN) 不该纠,未纠 |
```python for ... in ...: # 该纠的句子(句子有错字) if targ != []: targ_p += 1 # 相当于TP+FN # 纠了的句子(句子有没有错字不知道,模型认为有) if pred != []: pred_p += 1 # 相当于TP+FP # 不该纠,没纠;或 该纠,纠对了 if pred == targ: hit += 1 # 该纠,纠对了 if pred != [] and pred == targ: tp += 1 acc = hit / len(targs) p = tp / pred_p r = tp / targ_p f1 = 2 * p * r / (p + r) if p + r > 0 else 0.0 ```
SpellGCN
SpellGCN字段说明:
detect_token
:存的是错字所在的位置。例如: [1,5,7]表示1,5,7三个位置上的字存在错误。如果没有错字,则为[0]correct_zip/correct_tokens
:存的是错字位置即改正后的字。例如:[(1, 鸡), (5, 美)],表示1位置字应该改成鸡,5位置字应该改成美
Character-level Detection Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠的字,纠了,纠没纠对不管 | (FN) 该纠的字,没纠 |
N | (FP) 不该纠的字,纠了 | (TN) 不该纠的字,未纠 |
```python for ... in ...: # 该纠的句子(句子中存在错字) if detect_actual_tokens[0]!=0: # 该纠的字,纠了,纠没纠对不管。 # 例如:label为(1, 3, 5), pred为(3, 5, 7),则TP+=2,因为对两个该纠的字纠了 detect_TP += len(set(max_detect_pred_tokens) & set(detect_actual_tokens)) # 该纠的字,没纠 # 例如:label为(1, 3, 5), pred为(3, 5, 7),FN+=1,因为1位置该纠但没纠 detect_FN += len(set(detect_actual_tokens) - set(max_detect_pred_tokens)) # 不该纠的字,但纠了 # 例如:label为(1, 3, 5), pred为(3, 5, 7),FP+=1,因为7位置不该纠,但纠了 detect_FP += len(set(max_detect_pred_tokens) - set(detect_actual_tokens)) detect_precision = detect_TP * 1.0 / (detect_TP + detect_FP) detect_recall = detect_TP * 1.0 / (detect_TP + detect_FN) detect_F1 = 2. * detect_precision * detect_recall/ ((detect_precision + detect_recall) + 1e-8) ```
Character-level Correction Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠的字,纠了也纠对了。 | (FN) 纠了,但没纠对的字。(包括这个字本身有错和没错两种情况) |
N | (FP) 该纠的字,没纠对或没纠。 | (TN) 不该纠的字,没纠 |
这个指标好像有问题,因为“该纠,但没纠对的字”会被FN和FP重复计算。详情可以参考下面代码中的注释
```python for ... in ...: """ 该纠的字,纠了也纠对了。 例如:label为[(1, '鸡'), (3, '你'), (5, '太')], pred为[(1, '坤'), (3, '你'), (5, '太'), (7, '美')] 则TP+=2。 因为“你,太”两个字该纠且纠对了 """ correct_TP += len(set(correct_pred_tokens) & set(zip(detect_actual_tokens,correct_actual_tokens))) """ 纠了,但没纠对的字。(包括这个字本身有错和没错两种情况) 例如:label为[(1, '鸡'), (3, '你'), (5, '太')], pred为[(1, '坤'), (3, '你'), (5, '太'), (7, '美')] 则FP+=2。 因为'坤'字纠了,但没纠对。'美'字纠了,但7位置本身没错,所以也没纠对 """ correct_FP += len(set(correct_pred_tokens) - set(zip(detect_actual_tokens,correct_actual_tokens))) """ 该纠的字,但没纠对或没纠。 例如:label为[(1, '鸡'), (3, '你'), (5, '太'), (9, '美')], pred为[(1, '坤'), (3, '你'), (5, '太')] 则FN+=2。 因为'鸡'字没纠对,'美'字该纠但没纠 这里这个指标好像出现了问题,对于'坤'字的错误预测,在FP和FN被重复计算了。 """ correct_FN += len(set(zip(detect_actual_tokens,correct_actual_tokens)) - set(correct_pred_tokens)) correct_precision = correct_TP * 1.0 / (correct_TP + correct_FP) correct_recall = correct_TP * 1.0 / (correct_TP + correct_FN) correct_F1 = 2. * correct_precision * correct_recall/ ((correct_precision + correct_recall) + 1e-8) ```
Sentence-level Detection Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且该纠的字,都纠了,纠没纠对不管;不该纠的字,都没纠 | (FN) 该纠,但未纠或把不该纠的字纠了 |
N | (FP) 不该纠,但纠了 或 "纠了,但把不该纠的字纠了" | (TN) 不该纠,未纠 |
```python for ... in ...: # 不管该不该纠,反正纠了 if detect_pred_tokens[0] != 0: sent_P += 1 # 相当于TP+FP # 该纠的 if detect_actual_tokens[0] != 0: sent_N += 1 # 相当于(TP+FN) if sorted(detect_actual_tokens) == sorted(detect_pred_tokens): detect_sent_TP += 1 detect_sent_precision = detect_sent_TP * 1.0 / (sent_P) detect_sent_recall = detect_sent_TP * 1.0 / (sent_N) detect_sent_F1 = 2. * detect_sent_precision * detect_sent_recall/ ((detect_sent_precision + detect_sent_recall) + 1e-8) ```
Sentence-level Correction Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且纠对了 | (FN)该纠,未纠或纠错了 |
N | (FP) 不该纠,但纠了 或 "纠了,但没纠对(原句子是否有错不管)" | (TN) 不该纠,未纠 |
代码如下:
```python for ... in ...: # 不管该不该纠,反正纠了 if detect_pred_tokens[0] != 0: # 表示预测句子中存在错字 sent_P += 1 # 相当于TP+FP # 该纠的,且纠对了(因为纠了,且纠对了,说明句子该纠) if sorted(correct_pred_zip) == sorted(correct_actual_zip): correct_sent_TP += 1 # 该纠的 if detect_actual_tokens[0] != 0: sent_N += 1 # 相当于(TP+FN) correct_sent_precision = correct_sent_TP * 1.0 / (sent_P) correct_sent_recall = correct_sent_TP * 1.0 / (sent_N) correct_sent_F1 = 2. * correct_sent_precision * correct_sent_recall/ ((correct_sent_precision + correct_sent_recall) + 1e-8) ```
PyCorrector
Sentence-level Correction Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且纠对了 | (FN)该纠,未纠或纠错了 |
N | (FP) 不该纠,但纠了 | (TN) 不该纠,未纠 |
代码如下:
```python for ... in ...: # 负样本,不该纠的 if src == tgt: # 预测也为负 if tgt == tgt_pred: TN += 1 # 预测为正 # 不该纠的,但是纠了,为FP else: FP += 1 # 正样本,该纠错的 else: # 预测也为正 # 该纠错的句子,且纠对了,为TP if tgt == tgt_pred: TP += 1 # 预测为负 # 该纠的,没纠或者纠错了,为FN else: FN += 1 total_num += 1 acc = (TP + TN) / total_num precision = TP / (TP + FP) if TP > 0 else 0.0 recall = TP / (TP + FN) if TP > 0 else 0.0 f1 = 2 * precision * recall / (precision + recall) if precision + recall != 0 else 0 ```
SCOPE
Sentence-level Correction Metrics:
实际值 / 预测值 | P | N |
---|---|---|
P | (TP) 该纠,且纠对了 | (FN)该纠,未纠或纠错了 |
N | (FP) 不该纠,但纠了 或 "纠了,但没纠对(原句子是否有错不管)" | (TN) 不该纠,未纠 |
代码如下:
```python def sent_metric_correct(preds, targs): assert len(preds) == len(targs) # tp: true positive # targ_p: target positive # pred_p: prediction positive # hit: 预测正确的数量 tp, targ_p, pred_p, hit = 0, 0, 0, 0 for pred_item, targ_item in zip(preds, targs): assert pred_item[0] == targ_item[0] pred, targ = sorted(pred_item[1:]), sorted(targ_item[1:]) if targ != []: # 如果target包含错字,则target positive加1 targ_p += 1 if pred != []: # 如果predition包含错字,则prediction positive加1 pred_p += 1 if pred == targ: # 如果target和预测结果一致,则hit+1。(不管原句是否存在错误) hit += 1 if pred != [] and pred == targ: # 如果target和预测结果一致,且原句有错误,则true positive加1 tp += 1 acc = hit / len(targs) p = tp / pred_p r = tp / targ_p f1 = 2 * p * r / (p + r) if p + r > 0 else 0.0 ... ```