해당 글은 코드잇 강의를 참고하여 작성되었습니다.
- 협업 필터링이란?
- 수많은 유저 데이터들이 협업해서 상품 추천
- 비슷한 유저를 통해 추천
- 데이터 표현하기
- 유저의 평점을 벡터로 표현
- 유저 a가 영화 i에 준 평점 = ri(a)
- 거리를 계산할 때 비어있는 데이터는 0이나 평균 또는 mean normalization을 사용해 채워준다.
- 비슷한 유저 정의하기 : 유클리드 거리
- 거리가 멀수록 유사도가 낮고 가까울수록 유사도가 높다.
- 비슷한 유저 정의하기 : 코사인 유사도
- 두 선사이의 각도를 통해 유사도를 구한다.
- 코사인의 값은 데이터가 비슷할수록 크고 다를수록 작다.
- 유클리드 거리와 코사인 유사도의 또 다른 차이는 유클리드 거리는 클수록 두 데이터가 다르고, 작을수록 두 데이터가 비슷하다는 의미였고, 코사인 유사도는 클수록 두 데이터가 비슷하고, 작을수록 두 데이터가 다르다는 의미이다. 따라서 유저 A와 가장 '비슷한' 유저들을 찾을 때 유클리드 거리는 거리가 가장 작은 유저들을 찾아야 되고요. 코사인 유사도는 유사도가 가장 큰 유저들을 찾아내야 된다.
- 상품 추천하기
- ri(x) : 유저 x 의 영화 i 에 대한 평점
- : 상품 i를 평가한 유저 중 유저 x와 가장 비슷한 k명의 집합
- 위 식을 사용해 평점 데이터의 빈 원소들을 채운 후 예상 평점이 가장 높은 영화 몇 개를 유저에게 추천한다.
#유저 평점 예측하기
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=0) # 유저 평균 평점 계산
inds = np.where(np.isnan(filled_data)) # 비어 있는 인덱스들을 구한다
filled_data[inds] = np.take(row_mean, inds[1]) #빈 인덱스를 유저 평점으로 채운다
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)
return np.mean(neighbors[:, movie_id])
# 실행 코드
# 평점 데이터를 불러온다
rating_data = pd.read_csv(RATING_DATA_PATH, index_col='user_id').values
# 5개의 이웃들을 써서 유저 0의 영화 3에 대한 예측 평점 구하기
predict_user_rating(rating_data, 5, 0, 3)
- 상품 기반 협업 필터링
- 작동하는 방식은 거의 유사하지만 상품이 유저들보다 단순하기 때문에 성능은 더 좋다.
- 장점
- 속성을 찾거나 정할 필요가 없다.
- 좀 더 폭넓은 상품을 추천할 수 있다.
- 내용 기반 추천보다 성능이 더 좋게 나오는 경우가 많다.
- 단점
- 데이터가 많아야 한다.
- 인기가 많은 소수의 상품이 추천 시스템을 장악할 수 있다.
- 어떤 상품이 왜 추천되었는지 정확히 알기 어렵다.
- 따라서 선형회귀/ 다항회귀/ 유저 기반 협업 필터링/ 상품 기반 협업 필터링 등 다양한 방식을 합쳐서 사용한다.
'데이터 > 머신러닝' 카테고리의 다른 글
Chapter 02-1 훈련 세트와 테스트 세트 (0) | 2022.02.20 |
---|---|
실전 1.4 행렬 인수분해 (0) | 2021.07.27 |
실전 1.2 내용 기반 추천 (0) | 2021.07.26 |
4.3 에다 부스트 (0) | 2021.07.26 |
4.2 랜덤 포레스트 (0) | 2021.07.25 |