추천 시스템 평가지표 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
Comments