Separate out some logic from f1 score for reuse elsewhere.
@@ -54,26 +54,38 @@ def _exact_match(guess, answers):
return False
-def _f1_score(guess, answers):
- """Return the max F1 score between the guess and any answer."""
- def _score(g_tokens, a_tokens):
- common = Counter(g_tokens) & Counter(a_tokens)
+def _prec_recall_f1_score(pred_items, gold_items):
+ """
+ Computes precision, recall and f1 given a set of gold and prediction items.
+
+ :param pred_items: iterable of predicted values
+ :param gold_items: iterable of gold values
+
+ :return: tuple (p, r, f1) for precision, recall, f1
+ """
+ common = Counter(gold_items) & Counter(pred_items)
num_same = sum(common.values())
if num_same == 0:
- return 0
- precision = 1.0 * num_same / len(g_tokens)
- recall = 1.0 * num_same / len(a_tokens)
+ return 0, 0, 0
+ precision = 1.0 * num_same / len(pred_items)
+ recall = 1.0 * num_same / len(gold_items)
f1 = (2 * precision * recall) / (precision + recall)
- return f1
+ return precision, recall, f1
+
+def _f1_score(guess, answers):
+ """Return the max F1 score between the guess and *any* answer."""
if guess is None or answers is None:
return 0
g_tokens = normalize_answer(guess).split()
- scores = [_score(g_tokens, normalize_answer(a).split()) for a in answers]
- return max(scores)
+ scores = [
+ _prec_recall_f1_score(g_tokens, normalize_answer(a).split())for a in answers
+ ]
+ return max(f1 for p, r, f1 in scores)
def _bleu(guess, answers):
+ """Compute approximate BLEU score between guess and a set of answers."""
if nltkbleu is None:
# bleu library not installed, just return a default value
return None
