추천 시스템 평가지표 2. Recall

Intro

“실제 정답들을 얼마나 잘 재현했는가?”

이전 포스팅에서 Precision 을 통해, 추천 리스트가 얼마나 목표를 맞췄는가에 대한 지표를 살펴봤습니다. 하지만 Precision 만으로는 한 가지 중요한 질문에 대한 답변을 할 수 없습니다. “그래서, 실제 정답 중에서 어느정도를 맞췄나요?”

이 물음에 대해 답해줄 수 있는 지표가 바로 Recall, 재현율입니다. 이번 포스팅에서는 이 Recall 에 대해 알아보도록 하겠습니다.

개념

Recall 은 정답 아이템 리스트 중에서, 추천 리스트에 실제로 포함된 아이템의 비율을 의미합니다.

  • 정답 아이템 리스트 중 추천 리스트에 포함된 아이템의 비율
  • 보통은 K 개의 추천리스트에 대한 비율을 계산하는 Recall@K 를 사용한다.

Recall, 재현율

Recall을 한국어로 번역하면 “재현율”이 됩니다. 재현율을 이해할 때는 이름 그대로를 통해 기억하는 게 좋습니다. “재현하다” 라는 말은, 다시 나타내다 라는 뜻을 가진 말입니다. 곧, 원래 정답이던 아이템들을 추천 리스트에서 얼마나 잘 재현했(다시 나타냈)는가 라고 Recall 을 정의할 수 있습니다.

수식

Recall은 실제 정답 아이템 중에, 추천 리스트에 포함된 아이템의 비율입니다. 수식으로는 아래와 같이 표현할 수 있습니다.

\[Recall@K = \frac{|R \cap K|}{|R|}\]
  • $R$ : 케이스의 정답 아이템 리스트
  • $K$ : 케이스의 Top-K 추천 리스트

코드

  • 상위 5개(K=5)의 추천 결과에 대한 Recall
1
2
3
4
5
6
7
8
9
10
11
12
predict_list = ["A", "B", "C", "L", "Y", "U", "F", "Z"]
ground_truth = {"A":1.0, "K":1.0, "B":1.0, "Z":1.0}

def calc_recall(predict_list, ground_truth, k:int=None):
    if k is None:
        k = len(predict_list)
    predict_list_k = predict_list[:k]
    recall = len(set(predict_list_k) & set(ground_truth)) / len(ground_truth)
    return recall

calc_recall(predict_list, list(ground_truth.keys()), 5)
# >> 0.5
  • predict_list : 추천 결과
  • ground_truth : 실제 사용자가 선호한 아이템 (1.0과 같은 점수는 추후에 살펴본다.)
  • 정답 아이템 4개 중 2개(A, B)를 추천 목록 상위 5개(A, B, C, L, Y)에서 맞췄으므로 Recall 은 0.5

Mean Recall

개념

앞서 살펴본 Recall은 단일 케이스에 대한 평가입니다. 이전 포스팅에서 살펴봣듯, 추천 시스템은 여러 테스트케이스의 평가 결과를 종합해 그 성능을 평가해야 합니다. 이렇게 여러 케이스의 Recall을 평균해 낸 값을 바로 Mean Recall 이라고 합니다.

  • 모든 case 들의 Recall 평균값
  • 추천 시스템이 전반적으로 얼마나 정답을 잘 재현했는지 보여주는 지표

수식

\[Mean \, Recall@K = \frac{1}{n}\sum_{i=1}^{n}\frac{|R_{i} \cap K_{i}|}{|R_{i}|}\]
  • $n$ : 전체 케이스 수
  • $R_{i}$ : i번째 케이스의 정답 아이템 리스트
  • $K_{i}$ : i번째 케이스의 Top-K 추천 리스트

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cases = [
    {
        "predict_list" : ["A", "B", "C", "L", "Y", "U", "F", "Z"],
        "ground_truth" : {"A":1.0, "K":1.0, "B":1.0, "Z":1.0}
    },
    {
        "predict_list" : ["N", "X", "Y", "B", "M"],
        "ground_truth" : {"E":1.0, "B":1.0}
    },
]

def calc_recall(predict_list, ground_truth, k:int=None):
    if k is None:
        k = len(predict_list)
    predict_list_k = predict_list[:k]
    recall = len(set(predict_list_k) & set(ground_truth)) / len(ground_truth)
    return recall

def calc_mean_recall(cases, k:int=None):
    mean_p = sum(calc_recall(case["predict_list"], case["ground_truth"], k) for case in cases) / len(cases)
    return mean_p

calc_mean_recall(cases, 3)
# >> 0.25
  • predict_list : 추천 결과
  • ground_truth : 실제 사용자가 선호한 아이템 (1.0과 같은 점수는 추후에 살펴본다.)
  • 첫 번째 케이스 : 정답 4개 중 2개(A, B)를 추천 목록 상위 3개에서 맞췄으므로 Recall 은 0.5
  • 두 번째 케이스 : 정답 2개 중 추천 목록 상위 3개에서 맞춘 것은 없으므로 Recall 은 0.0
  • 두 케이스의 평균 = (1/2)/2 = 0.25

Reference

국립국어원 표준국어대사전 - 재현하다

Comments