Hit Rate (HR)

개념

Hit Rate는 추천한 $K$개의 아이템 목록 중에 사용자가 실제로 선호한(정답) 아이템이 하나라도 포함되어 있는지를 나타내는 지표입니다.

  • Hit: 추천 리스트에 정답 아이템이 최소 하나 이상 포함된 경우 (1점)
  • Miss: 추천 리스트에 정답 아이템이 하나도 없는 경우 (0점)

즉, “이 추천 리스트가 사용자에게 유효했는가(Hit인가)?”를 이진(Binary) 방식으로 평가합니다.

특징

  • 개별 순위나 정답의 개수보다는 ‘추천의 성공 여부’ 그 자체에 집중
  • 비즈니스 관점에서 이해하기 쉬운 지표

사용하는 경우

  • 사용자가 추천 리스트에서 아이템 하나만 골라도 비즈니스적으로 성공인 경우
  • 수많은 제안 중 하나라도 클릭을 유도해 사용자를 계속 머무르게 하는 것이 목표일 때 활용
  • 예를 들어, 이커머스의 “연관 상품 추천”이나 유튜브의 “다음 볼만한 영상”

수식

\[HR@K = \begin{cases}1, & if |R\cap TopK| > 0 \\ 0, & otherwise \end{cases}\]

Recall과의 차이점

Recall과 Hit Rate는 비슷해 보이지만, “정답을 몇 개 맞혔는가”를 대하는 관점이 다릅니다.

구분 Recall@K Hitrate@K
의도 정답 중 몇 %나 리스트에 포함했나? 정답이 하나라도 리스트에 있나?
계산 맞힌 개수 / 전체 정답 개수 맞혔으면 1, 못 맞혔으면 0
예시 정답 5개 중 2개 맞힘 $\rightarrow$ 0.4 정답 5개 중 2개 맞힘 $\rightarrow$ 1.0 (Hit)

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def calc_hitrate(predict_list:list[str|int],
                  truth_items:list[str|int],
                  k:int|None=None):
    if (k is None) or (k > len(predict_list)):
        k = len(predict_list)
    predict_list_k = set(predict_list[:k])
    truth_items_set = set(truth_items)
    return 1.0 if (predict_list_k & truth_items_set) else 0.0

# 예시
predict_list = [ "A", "B", "C"]
ground_truth = {"A":0.1, "B":0.5, "C":0.7, "D":0.5, "E":0.1}

calc_hitrate(predict_list, list(ground_truth.keys()), 3)
# >> 1.0

Average Hit Rate

개념

시스템 전체의 성능을 평가할 때는, 모든 케이스의 Hit 여부를 합산해 전체 케이스 수로 나눕니다.

\[HR@K = \begin{cases}1, & if |R\cap TopK| > 0 \\ 0, & otherwise \end{cases}\\ Average HR@K = \frac{1}{n}\sum_{i=1}^{n}{HR@K(i)}\]

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def calc_hitrate(predict_list:list[str|int],
                  truth_items:list[str|int],
                  k:int|None=None):
    if (k is None) or (k > len(predict_list)):
        k = len(predict_list)
    predict_list_k = set(predict_list[:k])
    truth_items_set = set(truth_items)
    return 1.0 if (predict_list_k & truth_items_set) else 0.0

def calc_average_hitrate(cases,
                         k:int|None=None):
    return sum(calc_hitrate(case["predict_list"], case["ground_truth"].keys(), k)for case in cases)/len(cases)

# 예시
cases = [
    {
    "predict_list" : [ "A", "B", "C"],
    "ground_truth" : {"A":0.1, "B":0.5, "C":0.7, "D":0.5, "E":0.1}
    },
    {
    "predict_list" : [ "K", "O", "U", "A", "E" ],
    "ground_truth" : {"A":0.1, "B":0.5, "C":0.7, "D":0.5, "E":0.1}
    }
]

calc_average_hitrate(cases, 3)
# >> 0.5

Comments