TIL_56 : 협업 필터링

JaHyeon Gu·2021년 11월 25일
0

Machine Learning

목록 보기
14/15
post-thumbnail

🙄 협업 필터링


➡ 협업 필터링이란?

  • 한 명의 유저에게 영화를 추천해 줄 때 다른 유저들의 데이터도 사용하는 방식
  • 수많은 유저 데이터들이 협업해서 상품 추천
  • 내용 기반 추천은 한 유저의 평점이 다른 유저의 평점에 영향을 미치지 않음
  • 유저들의 평점이 서로 독립적

사람들의 취향은 실제로 서로 유사한 부분이 많다


➡ 데이터 표현하기

  • 각 유저들의 평점 데이터를 하나로 묶어서 사용
  • 유저별 평점 데이터를 rr로 표현 정확히 몇 번째 유저인지 오른쪽 위에 표시
  • 유저 aa가 영화 ii에 준 평점
    ri(a)r_i^{(a)}
  • 첫 번째 유저의 평점 데이터
    r(1)r^{(1)} = [51152]\begin{bmatrix}5\\1\\1\\5\\2 \end{bmatrix}
  • 세 번째 유저가 영화 2에 준 평점
    r2(3)r_2^{(3)}



🙄 비슷한 유저 정의하기


➡ 유클리드 거리

  • 데이터를 점으로 표현하고 그 둘 사이의 거리
  • 유클리드 거리가 작을수록 비슷하고, 클수록 덜 비슷함
    dist(a,b)=(r1(a)r1(b))2+(r2(a)r2(b))2+...+(rn(a)rn(b))2dist(a,b)=\sqrt{(r_1^{(a)}-r_1^{(b)})^2+(r_2^{(a)}-r_2^{(b)})^2+...+(r_n^{(a)}-r_n^{(b)})^2}
    dist(a,b)=i=1n(ri(a)ri(b))2dist(a,b)=\sqrt{\sum_{i=1}^n (r_i^{(a)}-r_i^{(b)})^2}

예시

  • 세 유저의 평점이 아래와 같을 때 거리 계산
    r(a)r^{(a)} = [43524]\begin{bmatrix}4\\3\\5\\2\\4 \end{bmatrix}, r(b)r^{(b)} = [44434]\begin{bmatrix}4\\4\\4\\3\\4 \end{bmatrix}, r(c)r^{(c)} = [15153]\begin{bmatrix}1\\5\\1\\5\\3 \end{bmatrix}

dist(a,b)=(44)2+(34)2+(54)2+(23)2+(44)2=3dist(a,b)=\sqrt{(4-4)^2+(3-4)^2+(5-4)^2+(2-3)^2+(4-4)^2}=\sqrt{3}

dist(a,c)=(41)2+(35)2+(51)2+(25)2+(43)2=6.24dist(a,c)=\sqrt{(4-1)^2+(3-5)^2+(5-1)^2+(2-5)^2+(4-3)^2}=6.24


➡ 코사인 유사도

  • 데이터를 선으로 표현하고 그 둘 사이의 각도
  • 각도를 코사인 함수에 넣어서 사용
  • 코사인은 빗변 분에 밑변의 비율을 의미
  • 4차원 이상 넘어가게 되면, 표현이 어려워 수학적 식 사용
    cos(θ)=ABAB=i=1nAiBii=1nAi2i=1nBi2\cos(\theta)=\frac{A\cdot B}{\left \Vert A \right \| * \left \Vert B \right \|}=\frac{\sum_{i=1}^n A_i B_i}{\sqrt{\sum_{i=1}^n A_i^2}\sqrt{\sum_{i=1}^n B_i^2}}

예시

r(a)=A=[45321]r^{(a)} =A= \begin{bmatrix}4\\5\\3\\2\\1 \end{bmatrix}, r(b)=B=[32422]r^{(b)} =B =\begin{bmatrix}3\\2\\4\\2\\2 \end{bmatrix}

cos(θ)=405537=0.887\cos(\theta)=\frac{40}{\sqrt{55}\sqrt{37}}=0.887

두 벡터의 코사인 유사도 =0.887=0.887


코사인 함수 특징

cos0=1\cos0=1 : 두 선이 원점에서 정확히 같은 방향으로 그려질 때
cos180=1\cos180=-1 : 두 선이 원점에서 정확히 반대 방향으로 그려질 때
cos90=0\cos90=0 : 두 선이 완전히 직각일 때



🙄 평점 데이터 전처리


  • 모든 유저가 모든 영화에 평점을 주는 건 불가능
  • 평점 데이터는 군데군데 비어 있을 수밖에 없음

➡ 0으로 계산하기

  • 가장 쉬운 방법
  • 빈값들을 다 0으로 채워 넣고 계산
  • 추천 시스템에서 최악의 평점으로 계산이 된다는 문제점 발생
  • 단순히 평점을 안 준 거뿐인데 유저가 싫어하는 영화로 계산
  • 정확도가 떨어져 별로 좋은 방법이 아님

➡ 유저 별 평균 평점으로 계산하기

  • 비어 있는 값들을 유저의 평균 평점으로 채워 넣는 방법
  • 유저가 준 평점들의 평균은 유저가 좋아하지도, 싫어하지도 않는다고 해석 가능
  • 0을 사용하는 거보다 훨씬 더 합리적인 유사도 계산 가능

➡ Mean Normalization

  • 0으로 계산하기와 유저 별 평균 평점을 합친 방법
  • 빈값들을 모두 유저 별 평균 평점으로 채워 넣는다
  • 각 유저 평점에서 각 유저의 평균 평점을 다시 빼주는 것
    ex) A의 평균 평점이 3이라면 모든 영화 평점에 -3
  • 유저마다 평점 평균이 0이 됨
  • 각 데이터에서 평균을 빼서 평균을 0으로 만들어 주는 걸 mean normalization
  • 모르는 값들을 합리적으로 채워 넣을 수 있다는 장점과
    까다로운 유저들과 유한 유저들에 대한 처리를 해줄 수 있다는 장점
  • 비슷한 유저를 찾을 때 좀 더 직관적으로 찾아낼 수 있음



🙄 유클리드 거리 vs 코사인 유사도


  • 가장 직관적인 차이는 코사인 유사도는 각 벡터, 선의 크기가 중요하지 않다는 것

  • 1: 닭 가슴살, 2: 아령, 3: 맥주, 4: 피자일 때

A=[1100]A= \begin{bmatrix}1\\1\\0\\0 \end{bmatrix}, B=[1005000]B =\begin{bmatrix}100\\50\\0\\0 \end{bmatrix}, C=[0011]C =\begin{bmatrix}0\\0\\1\\1\end{bmatrix}

  • AABBAACC보다 훨씬 비슷한 구매 취향
  • 하지만 거리를 사용하면 BB는 벡터가 엄청 길기 때문에 AACC가 훨씬 더 가까움

어떤 형식의 데이터를 사용하는지에 따라 유클리드 거리를 사용할지
코사인 유사도를 사용할지 결정

  • 유클리드 거리는 클수록 두 데이터가 다르고, 작을수록 비슷하다는 의미
  • 코사인 유사도는 클수록 두 데이터가 비슷하고, 작을수록 다르다는 의미



🙄 유저 평점 예측하기


# ratings.csv 데이터 원본 일부
# user_id,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19
# 0,2.0,3.0,4.0,,2.0,3.0,,,,4.0,4.0,,,1.0,,,2.0,5.0,2.0,
# 1,,,,4.0,,5.0,,,2.0,,4.0,,1.0,,,,,5.0,,


import pandas as pd
import numpy as np
from math import sqrt

RATING_DATA_PATH = './data/ratings.csv'  # 받아올 평점 데이터 경로 정의

np.set_printoptions(precision=2)  # 소수점 둘째 자리까지만 출력

def distance(user_1, user_2):
    """유클리드 거리를 계산해주는 함수"""
    return sqrt(np.sum((user_1 - user_2)**2))
    
    
def filter_users_without_movie(rating_data, movie_id):
    """movie_id 번째 영화를 평가하지 않은 유저들은 미리 제외해주는 함수"""
    return rating_data[~np.isnan(rating_data[:,movie_id])]
    
    
def fill_nan_with_user_mean(rating_data):
    """평점 데이터의 빈값들을 각 유저 평균 값으로 체워주는 함수"""
    filled_data = np.copy(rating_data)  # 평점 데이터를 훼손하지 않기 위해 복사
    row_mean = np.nanmean(filled_data, axis=1)  # 유저 평균 평점 계산
    
    inds = np.where(np.isnan(filled_data))  # 비어 있는 인덱스들을 구한다
    filled_data[inds] = np.take(row_mean, inds[0])  #빈 인덱스를 유저 평점으로 채운다
    
    return filled_data
    
    
def get_k_neighbors(user_id, rating_data, k):
    """user_id에 해당하는 유저의 이웃들을 찾아주는 함수"""
    distance_data = np.copy(rating_data)  # 평점 데이터를 훼손하지 않기 위해 복사
    # 마지막에 거리 데이터를 담을 열 추가한다
    distance_data = np.append(distance_data, np.zeros((distance_data.shape[0], 1)), axis=1)
    
    # 반복문을 통해 모든 유저와의 거리 측정
    for i in range(len(distance_data)):
        row = distance_data[i]
        
        if i == user_id:  # 같은 유저면 거리를 무한대로 설정
            row[-1] = np.inf
        else:  # 다른 유저면 마지막 열에 거리 데이터를 저장
            row[-1] = distance(distance_data[user_id][:-1], row[:-1])
    
    # 데이터를 거리 열을 기준으로 정렬한다
    distance_data = distance_data[np.argsort(distance_data[:, -1])]
    
    # 가장 가까운 k개의 행만 리턴한다 + 마지막(거리) 열은 제외한다
    return distance_data[:k, :-1]
    
def predict_user_rating(rating_data, k, user_id, movie_id,):
    """예측 행렬에 따라 유저의 영화 평점 예측 값 구하기"""
    # movie_id 번째 영화를 보지 않은 유저를 데이터에서 미리 제외시킨다
    filtered_data = filter_users_without_movie(rating_data, movie_id)
    
    # 빈값들이 채워진 새로운 행렬을 만든다
    filled_data = fill_nan_with_user_mean(filtered_data)
    
    # 유저 user_id와 비슷한 k개의 유저 데이터를 찾는다
    neighbors = get_k_neighbors(user_id, filled_data, k)
    
    # 이웃들의 movie_id 번째 영화 평점 평균 리턴
    sum_of_neighbors = sum(neighbors[:, movie_id])
    return sum_of_neighbors / k
    
    
# 실행 코드   
# 평점 데이터를 불러온다
rating_data = pd.read_csv(RATING_DATA_PATH, index_col='user_id').values
# 5개의 이웃들을 써서 유저 0의 영화 3에 대한 예측 평점 구하기
predict_user_rating(rating_data, 5, 0, 3)  


# 4.7999999999999998



🙄 상품 기반 협업 필터링


➡ 상품 기반 협업 필터링

  • 한 유저가 4번 영화에 평점을 주지 않은 상황
  • 유사도를 사용해서 영화 4와 비슷한 영화 탐색
  • 이웃 영화들의 평점 평균을 내서 영화 4에 줄 평점을 예측
  • 예측값이 높다면 추천

➡ 유저 vs 상품 기반 협업 필터링

  • 이론상으로 유저와 상품 기반 협업 필터링은 큰 차이가 없음
  • 하지만 실전에서는 상품 기반 협업 필터링이 더 성능이 좋은 경우가 많음
    유저들이 상품보다 복잡하기 때문



🙄 협업 필터링 장단점


➡ 장점

  • 속성을 찾거나 정할 필요가 없음

  • 좀 더 폭넓은 상품을 추천할 수 있음

  • 내용 기반 추천보다 성능이 더 좋게 나오는 경우가 많음


➡ 단점

  • 데이터가 많아야 함
    유저 한 명이 열심히 평점을 줘도 다른 사람들도 열심히 평점을 줘야 함
    새로운 물건이나 유저에게 추천해 주기 힘듦, 관련 데이터가 전무하기 때문

  • 인기가 많은 소수의 상품이 추천 시스템을 장악할 수 있음

  • 어떤 상품이 왜 추천됐는지 정확히 알기 힘듦

내용 기반 추천, 협업 필터링 등 여러 방식을 합쳐서 사용하면
모델들의 단점을 보완하면서 장점을 살릴 수 있음

profile
IWBAGDS

0개의 댓글