Cosine Similarity: Collaborative Filtering

4 분 소요

​ 두 그룹 데이터의 유사도를 측정(Similarity Measure)하는 방법에는 여러 가지가 있다. 유클리디안 거리(Euclidean Distance), 맨하튼 거리(Manhattan Distance), 민코프스키 거리(Minkowski Distance), 코사인 거리(Cosin Distance), 코사인 유사도(Cosin Similarity), 상관관계 등이 사용된다.

유사도 측정은 문서 내에 포함된 단어들을 벡터화하여 비교하거나 영화 추천 평점 데이터를 기반으로 비교하는 등 다양하게 활용된다.

오늘은 코사인 유사도(Cosine Similarity)를 콘텐츠 추천 데이터 기반 관점에서 알아보겠다.

Collaborative Filtering

최근 몇 년 사이 추천 엔진에서 많이 사용하는 Collaborative Filterning은 사용자의 이력 데이터를 기반으로 추천한다.

< 교육 이야기 - 우리 회사는 교육 회사다. 사용자가 학습한 교육 콘텐츠를 기반으로 사용자의 데이터가 저장된다. 교육 콘텐츠에서 저장되는 사용자의 활동 이력 데이터는 ‘콘텐츠를 봤다, 안 봤다, 몇 번 봤다, 점수가 몇 점이다’ 정도의 데이터인데, 필자는 이것들이 Cosine Similarity를 측정하기에 적합하지 않은 데이터라고 생각했다. 그래서 손쉽게 Cosine Similarity를 측정할 수 있도록 교육 콘텐츠에 평점 기능을 넣었고, 이를 서비스에 반영 해 한 달 정도 운영한 데이터를 기반으로 추천 엔진에 적용하게 되었다.

6만 회원 중 MAU가 평균 10,000 정도 되는 서비스를 한 달 정도 운영했더니 8,000건 정도의 평가 데이터가 들어왔다. 결론부터 이야기하자면 해당 데이터로는 Similarity가 낮긴 했지만 서비스를 못할 정도는 아니었다. 또 유아 사용자들은 5점 척도의 평가를 어려워 할 것이라 생각했지만 생각보다 평가 데이터가 잘 들어왔다.

사실 교육 시장에서는 교수 설계자들 또는 내용 전문가들, 편집자들이 학습해야 될 순서와 그에 따른 콘텐츠를 추천해 주는 방법이 일반적이다. 그래야 내용적으로 학습자가 부족한 부분에 대해서 보강 하거나 잘하는 부분에 대해서 강화할 수 있다는 전제이다. 우리 회사의 서비스는 콘텐츠 커리큘럼의 구성 보다는 사용자의 선택에 따라 콘텐츠 상품을 학습하는 유형이므로 Collaborative Filtering을 적용하기에 적합하다고 판단하였다. >

Cosine Similarity에 사용될 데이터는 사용자들이 학습 후 콘텐츠에 대해 5점 척도의 평가를 남긴 데이터이다. 사용자들의 콘텐츠에 대한 선호 데이터라고 볼 수 있다. 사용할 데이터는 사용자 ID, 콘텐츠 코드, 평가 데이터이다.

일련번호 사용자 ID 콘텐츠 구분 콘텐츠 코드 평가 데이터
1000000001 1000053911 100 V1000000003 4
1000000002 1000053911 100 V1000000002 5
1000000003 1000053911 100 V1000000008 5

Cosine Similarity에 사용하기 위해서 Pandas의 pivot_table 기능을 이용하여 데이터를 다시 정리해 주었다.

data_IBCF = resultList.pivot_table('CONTENT_EVALUATION_SCORE', index = 'EVALUATION_CONTENT_ID', columns = 'MEMBER_CHILD_SQ').fillna(0)

아래와 같이 Pivot Table이 만들어 졌다. Item Based Collaborative Filterning을 적용하기 위한 데이터이기 때문에 세로가 콘텐츠이고 가로가 사용자별 평점 데이터이다.

MEMBER_CHILD_SQ 1000000040 1000000225 1000000339 1000000366 1000000394 1000000495 1000000497 1000000530 1000000547 1000000820 1000064741 1000064751 1000064760 1000064774 1000064776 1000064809 1000064810 1000064811 1000064813 1000064815
EVALUATION_CONTENT_ID_SQ                                          
1000000006 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 5.0 0.0 0.0
1000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1000000013 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

Cosine Similatiry

Cosine Similarity를 계산하기 위한 기본 데이터가 만들어졌다.

이제 각 콘텐츠별 거리만 구하면 된다. Python 코드를 사용하면면 아래와 같이 심플하게 구할 수 있다.

con_sim = cosine_similarity(data_IBCF, data_IBCF)
con_sim_df = pd.DataFrame(data = con_sim, index = data_IBCF.index, columns = data_IBCF.index)
con_sim_df
EVALUATION_CONTENT_ID_SQ 1000000006 1000000007 1000000013 1000000014 1000000028 1000000029 1000000035 1000000036 1000000042 1000000043 V100000233 V100000235 V100000238 V100000239 V100000240 V100000246 V100000249 V100000254 V100000256 V100000258
EVALUATION_CONTENT_ID_SQ                                          
1000000006 1.000000 0.534709 0.118699 0.151190 0.181812 0.154930 0.082285 0.138781 0.086249 0.083669 0.0 0.000000 0.0 0.0 0.117421 0.0 0.0 0.0 0.0 0.0
1000000007 0.534709 1.000000 0.144349 0.174854 0.195543 0.189909 0.095725 0.118349 0.093215 0.081133 0.0 0.000000 0.0 0.0 0.109341 0.0 0.0 0.0 0.0 0.0
1000000013 0.118699 0.144349 1.000000 0.517494 0.246730 0.229334 0.202346 0.319755 0.223019 0.215176 0.0 0.052505 0.0 0.0 0.043224 0.0 0.0 0.0 0.0 0.0
1000000014 0.151190 0.174854 0.517494 1.000000 0.285489 0.214540 0.182135 0.218426 0.221884 0.197035 0.0 0.000000 0.0 0.0 0.048301 0.0 0.0 0.0 0.0 0.0
1000000028 0.181812 0.195543 0.246730 0.285489 1.000000 0.446856 0.305144 0.293787 0.310196 0.222965 0.0 0.072803 0.0 0.0 0.072141 0.0 0.0 0.0 0.0 0.0

결과 값은 행과 열의 콘텐츠ID가 만나는 지점의 숫자가 Cosine Similarity이다. 이것이 두 그룹의 cosθ의 값이다.

  • 1은 두 그룹의 값이 일치하는 것, 즉 자기 자신을 의미
  • 0으로 갈수록 유사하지 않다는 의미
  • -1 에 가까울수록 반대 방향을 의미

이를 1개 콘텐츠 당 열 별로 내림차순으로 정렬해 주면 추천 순위가 된다.

1 V100000228 V100000192 100 84.57816116784907
2 V100000228 V1000001281 100 75.93797902015675
3 V100000228 V100000198 100 75.93797902015675
4 V100000228 V1000001164 100 68.93819875457112
5 V100000228 V1000000036 100 59.74747262286842
6 V100000228 V1000000312 100 57.49751944924012
7 V100000228 V1000000556 100 53.84290295389707
8 V100000228 V1000000443 100 52.06621831004159
9 V100000228 V1000000032 100 49.81521976478465
10 V100000228 V1000000696 100 48.7857120156853

Cosine similarity의 수식은 아래와 같다.

Cosine similarity formula

두 벡터 X와 Y의 곱을 각 놈(norm - 벡터의 크기)의 곱으로 나눈 값이다.

이를 sklearn패키지의 라이브러리를 사용하지 않고 직접 구현하면 아래와 같은 코드로 구현해볼 수 있다.

from numpy import dot
from numpy.linalg import norm
import numpy as np
def cos_sim(x, y):
       return dot(x, y)/(norm(x)*norm(y))
    
def cos_sim2(x, y):
    return np.dot(x, y) / (np.sqrt(np.dot(x, x)) * np.sqrt(np.dot(y, y)))

블로그 첫 포스팅은 이쯤에서 마무리한다.

다음에는 IBCF 추천엔진의 개발 환경 및 적용 과정에 대한 썰을 풀어보도록 한다.