추천 시스템 알고리즘에 대해 알아보자 !
안녕하세요 !
숙명여자대학교 비즈니스 애널리틱스 동아리 Ball 입니다.
오늘은 우리 삶에 녹아있는
‘추천 시스템’ 에 대해 자세히 소개해드리고자 하는데요.
추천 시스템 알고리즘 소개를 서두로,
직접 데이터 실습까지 진행해보겠습니다 !
1. 추천 시스템이란 ?
추천 시스템을 한 문장으로 정의한다면
'사용자의 취향을 이해하고 맞춤 상품과 콘텐츠를 제공'하는 것입니다.
우리가 사용하는 콘텐츠 포털(유튜브/애플 뮤직),
전자상거래 업체(아마존/이베이) 들은
고객을 사이트에 조금이라도 오래 머무르게 해야
수익을 증가시킬 수 있는데요.
따라서 사용자가 선택한 콘텐츠와 연관된 콘텐츠를
적절하게 추천하는 시스템을 구축해,
사용자로부터 해당 사이트의 신뢰도를 높이고
더 많은 추천 콘텐츠를 선택하도록 합니다.
이렇게 되면 더 많은 데이터가 추천 시스템에 축적되며
추천이 정확해지고 다양해지는 선순환 시스템 구축할 수 있겠죠 ?
2. 온라인 스토어의 추천 시스템
온라인 스토어의 추천 시스템을 살펴보며
더 자세히 알아보겠습니다.
온라인 스토어는 너무 많은 상품들로 가득 차 있어
상품 선택에 어려움을 겪는데요.
이러한 상황은 온라인 쇼핑에 대한
부정적인 이미지를 생성하고 매출 감소로 이어질 수 있습니다.
이때,
💡 고객과 상품 관련 데이터가 쉽게 축적되는
온라인 스토어의 장점으로 문제를 해결할 수 있습니다.
1) 사용자가 어떤 상품을 구매했는가?
2) 사용자가 어떤 상품을 둘러보거나 장바구니에 넣었는가?
3) 사용자가 상품을 어떻게 평가했는가?
4) 사용자가 스스로 작성한 자신의 취향은?
5) 사용자가 무엇을 클릭했는가?
온라인 스토어는 위 예시처럼
데이터를 기반으로 한 추천 시스템을 통해
다른 상품 구매까지 유도합니다.
3. 추천 시스템의 유형
그렇다면 추천 시스템은 어떤 유형들이 있을까요 ?
추천 시스템은 크게 두 가지로 나뉩니다.
1) 콘텐츠 기반 필터링 (Content based filtering)
2) 협업 필터링 (Collaborative filtering)
최근에는 콘텐츠 기반과 협업 기반을
적절히 결합해 사용하는 하이브리드 유형도 등장하고 있는데요.
이번 포스팅에서는 콘텐츠 기반 필터링과 협업 필터링을
중심으로 알아보겠습니다 !
1 ) 콘텐츠 기반 필터링 추천 시스템
콘텐츠 기반 필터링은
사용자가 특정한 아이템을 매우 선호하는 경우,
그 아이템과 비슷한 콘텐츠를 가진 다른 아이템을 추천하는 방식인데요.
예시로, 위 사진처럼 사용자가 특정 영화에 높은 평점을 줬다면
그 영화의 장르, 출연 배우, 감독, 영화 키워드 등의
콘텐츠와 유사한 다른 영화를 추천하는 방식 입니다.
2 ) 협업 필터링
협업 필터링은 다시 두 가지로 나눠볼 수 있는데요.
첫 번째, 최근접 이웃 (Nearest Negibor) 협업 필터링
두 번째, 잠재 요인 (Latent Factor) 협업 필터링 입니다.
① 최근접 이웃 협업 필터링
최근접 이웃 협업 필터링은
사용자가 아이템에 매긴 평점 정보나 상품 구매 이력과 같은
사용자 행동 양식(User Behavior)만을 기반으로 추천을 수행하는 방식입니다.
예시로, 취향이 비슷한 친구들에게
이번에 나온 신작 영화가 어땠는지 물어보는 것이죠.
🥅 주요 목표는 다음과 같습니다.
사용자─아이템 평점 매트릭스와 같은
축적된 사용자 행동 데이터를 기반으로
사용자가 아직 평가하지 않은 아이템을 예측 평가하는 것입니다.
🔑 POINT
- 행(Row) : 개별 사용자
- 열(Column) : 개별 아이템
- 사용자 아이디 행, 아이템 아이디 열 위치에 해당하는 값 : 평점
- 데이터가 레코드 레벨 형태라면
pivot_table() 같은 함수를 이용해 행렬 형태로 변경
- 많은 아이템을 열로 가지는 다차원 행렬
- 사용자가 아이템에 대한 평점을 매기는 경우가 많지 않으므로 희소 행렬의 특징
최근접 이웃 협업 필터링의 유형은 다음과 같습니다.
1) 사용자 기반 (User-User) : “당신과 비슷한 고객들이 다음 상품을 구매했습니다”
① 특정 사용자와 유사한 다른 사용자를 TOP-N으로 선정 (유사도 측정)
② TOP-N 사용자가 좋아하는 아이템을 추천하는 방식
2) 아이템 기반 (Item-Item) : “이 상품을 선택한 다른 고객들은 다음 상품도 구매했습니다”
① ’아이템 간의 속성’이 얼마나 비슷한지는 상관없이, 아이템에 대한 사용자들의 평가 척도가 기준
② 위 사용자 기반 최근접 이웃 데이터 세트와 행과 열이 반대
👬 유사도는 주로 코사인 유사도를 이용합니다.
추천 시스템의 데이터는 피처 벡터화된 텍스트 데이터와 동일하게
다차원 희소 행렬이라는 특징이 있기 때문입니다.
⚠️ 일반적으로 사용자 기반보다는 아이템 기반 협업 필터링의 정확도가 더 높습니다.
① 매우 유명한 영화는 취향과 관계없이 대부분의 사람들이 관람하는 경우가 많음
② 사용자들이 평점을 매긴 영화의 개수가 많지 않은 경우가 일반적
즉, 다른 사람과의 유사도를 비교하기 어렵기 때문입니다.
② 잠재 요인 협업 필터링
잠재 요인 협업 필터링은,
사용자─아이템 행렬 데이터 속 숨어 있는
잠재 요인을 추출해 추천 예측을 수행하는 기법입니다.
👀 대규모 다차원 행렬을 SVD와 같은 차원 감소 기법으로
분해하는 과정에서 잠재 요인을 추출하는데요.
잠재 요인 협업 필터링의 기본 알고리즘은 다음과 같습니다.
다차원 희소 행렬인 사용자─아이템 행렬 데이터를
저차원 밀집 행렬의
① 사용자─잠재 요인 행렬과,
② 잠재 요인─아이템 행렬로 분해합니다.
분해된 두 행렬을 내적을 통해
새로운 예측 사용자─아이템 평점 행렬 데이터로 만들어서
예측 평점을 생성하는 것입니다 !
🫥 잠재 요인?
행렬 분해로 추출되는 잠재 요인이 정확히 어떤 것인지는 정의할 수 없습니다.
<영화 평점 기반의 사용자─아이템 평점 행렬 데이터>
→ 잠재 요인은 영화가 가지는 장르별 특성 선호도로 가정할 수 있음
- 사용자─잠재 요인 행렬 = 사용자의 영화 장르에 대한 선호도
- 아이템─잠재 요인 행렬 = 영화의 장르별 특성값
1. 사용자─아이템 평점 행렬 R, 사용자 아이디 u, 아이템 아이디 i
- 평점 행렬에서 사용자의 아이템에 대한 평점 = R(u, i)
R(1, 1) = 4 / R(1, 4) = 2
2. 사용자─잠재 요인 행렬을 영화 장르별 선호도 행렬 P로 가정
- 잠재 요인 칼럼인 장르별 선호도를 k로 설정
- factor 1을 액션 선호도, factor 2를 로맨스 선호도로 설정 (세상에 영화 장르가 액션과 로맨스밖에 없다고 가정)
P(1, 1) = 0.94 / P(1, 2) = 0.96
3. 아이템─잠재 요인 행렬은 영화별 장르 요소 행렬 Q로 가정
- 잠재 요인 칼럼인 장르별 요소를 k로 설정
- factor 1은 영화의 액션 요소값, factor 2는 로맨스 요소 값
- Q는 P와의 내적 계산을 통해 예측 평점을 계산해야 하므로 전치행렬로 변환 (Q.T)
Q.T(1, 1) = 1.7 / Q.T(2, 1) = 2.49 = Q(1, 2)
⭐ 평점이란 사용자의 특정 영화 장르에 대한 선호도와 개별 영화의 그 장르적 특성값을 반영해 결정된다고 생각할 수 있습니다.
즉, 평점 = 사용자의 장르별 선호도 벡터 * 영화의 장르별 특성 벡터
R(1, 1) = P 행렬의 User 1 벡터 * Q.T 행렬의 Item 1 벡터
같은 방법으로, User 1이 아직 평점을 매기지 않은 Item 2에 대해 예측 평점 수행합니다.
R(1, 2) = P의 User 1 벡터 * Q.T의 Item 2 벡터
= 2.56
4. 콘텐츠 기반 필터링 실습 - TMDB 5000 영화 데이터 세트
지금까지 추천 시스템의 개념부터 유형까지 자세하게 알아보았는데요.
이제는 실습을 통해
'영화 장르 속성을 기반으로 콘텐츠 기반 필터링 추천 시스템'을 구현해보겠습니다.
(장르 칼럼 값의 유사도 비교 → 그중 높은 평점을 가지는 유사도 추천)
1) 데이터 가공 및 전처리
import pandas as pd
import numpy as np
import warnings; warnings.filterwarnings('ignore')
movies =pd.read_csv('.\\\\dataset\\\\tmdb_5000_movie_dataset\\\\tmdb_5000_movies.csv')
print(movies.shape)
movies.head(1)
# 필요한 속성만 추출해서 df화
movies_df = movies[['id','title', 'genres', 'vote_average', 'vote_count',
'popularity', 'keywords', 'overview']]
pd.set_option('max_colwidth', 100)
movies_df[['genres','keywords']][:1]
from ast import literal_eval
# 문자열을 문자열이 의미하는 list [dict1, dict2] 객체로 변환
movies_df['genres'] = movies_df['genres'].apply(literal_eval)
movies_df['keywords'] = movies_df['keywords'].apply(literal_eval)
# name 뒤 속성만 리스트로 떼기
movies_df['genres'] = movies_df['genres'].apply(lambda x : [ y['name'] for y in x])
movies_df['keywords'] = movies_df['keywords'].apply(lambda x : [ y['name'] for y in x])
movies_df[['genres', 'keywords']][:1]
2) 장르 콘텐츠 유사도 측정
- 각 영화 레코드의 genre 속성이 리스트로 구성됨
- 장르별 유사도를 측정하려면 genres를 문자열로 변경한 뒤 CounterVectorizer로 피처 벡터화한 행렬 데이터 값을 코사인 유사도로 비교
from sklearn.feature_extraction.text import CountVectorizer
# CountVectorizer를 적용하기 위해 공백문자로 word 단위가 구분되는 문자열로 변환.
movies_df['genres_literal'] = movies_df['genres'].apply(lambda x : (' ').join(x))
count_vect = CountVectorizer(min_df=0.0, ngram_range=(1,2))
genre_mat = count_vect.fit_transform(movies_df['genres_literal'])
print(genre_mat.shape) # (4803, 276) 형태의 피처 벡터 행렬 생성
from sklearn.metrics.pairwise import cosine_similarity
# 피처 벡터화된 행렬에 코사인 유사도 함수 적용
genre_sim = cosine_similarity(genre_mat, genre_mat) # 행별 장르 유사도
print(genre_sim.shape)
print(genre_sim[:2])
- genre_sim 객체의 기준 행별로 비교 대상이 되는 행의 유사도 갚이 높은 순으로 정렬된 행렬의 위치 인덱스 값 추출
# movies_df 개별 레코드에 대해 가장 장르 유사도가 높은 순으로 레코드 추출
genre_sim_sorted_ind = genre_sim.argsort()[:, ::-1]
print(genre_sim_sorted_ind[:1])
- 값이 높은 순으로 정렬된 비교 대상 행의 유사도 값 X 비교 대상 행의 위치 인덱스 O
- 0번 레코드의 경우 자신인 0번 레코드를 제외하면 3494번 레코드 > 813번 레코드 순으로 유사도가 높다는 뜻
3) 장르 콘텐츠 필터링을 통한 영화 추천
# 기반 데이터, 레코드별 장르 코사인 유사도 인덱스 행렬,
# 추천 기준이 되는 영화 제목, 추천할 영화 건수를 받아서
# 장르 유사도에 따라 영화를 추천하는 함수
def find_sim_movie(df, sorted_ind, title_name, top_n=10):
# 인자로 입력된 movies_df DataFrame에서 'title' 컬럼이 입력된 title_name 값인 DataFrame추출
title_movie = df[df['title'] == title_name]
# title_named을 가진 DataFrame의 index 객체를 ndarray로 반환하고
# sorted_ind 인자로 입력된 genre_sim_sorted_ind 객체에서 유사도 순으로 top_n 개의 index 추출
title_index = title_movie.index.values
similar_indexes = sorted_ind[title_index, :(top_n)]
# 추출된 top_n index들 출력. top_n index는 2차원 데이터 임.
#dataframe에서 index로 사용하기 위해서 1차원 array로 변경
print(similar_indexes)
similar_indexes = similar_indexes.reshape(-1)
return df.iloc[similar_indexes]
similar_movies = find_sim_movie(movies_df, genre_sim_sorted_ind, 'The Godfather',10)
similar_movies[['title', 'vote_average']]
- Light Sleeper(낮은 평점), Mi America(평점 0), Kids는 대부를 좋아하는 고객에게 섣불리 추천하기는 어려운 영화
- 개선 필요 → 좀 더 많은 후보군을 선정하고 영화의 평점에 따라 필터링해서 최종 추천하는 방식
movies_df[['title','vote_average','vote_count']].sort_values('vote_average', ascending=False)[:10]
- 평점 정보는 평점의 평균치이므로 평가자 수를 고려하지 않고 그대로 사용할 수 없음 (왜곡된 평점 데이터)
- 평가 횟수에 대해 가중치를 부여해 평점 계산
- v : 개별 영화에 평점을 투표한 횟수 (vote_count)
- R : 개별 영화에 대한 평균 평점 (vote_average)
- C : 전체 영화에 대한 평균 평점 (movies_df[’vote_average’].mean())
- m : 평점을 부여하기 위한 최소 투표 횟수 (가중치 조절 변수)
- m 값을 높이면 평점 투표 횟수가 많은 영화에 더 많은 가중 평점 부여
percentile = 0.6 # 상위 60%에 해당하는 횟수를 기준으로 m값 설정
m = movies_df['vote_count'].quantile(percentile)
C = movies_df['vote_average'].mean()
# 기존 평가 점수를 가중 평가 점수로 생성하는 함수
def weighted_vote_average(record):
v = record['vote_count']
R = record['vote_average']
return ( (v/(v+m)) * R ) + ( (m/(m+v)) * C )
movies_df['weighted_vote'] = movies_df.apply(weighted_vote_average, axis=1)
- 장르 유사성이 높은 영화를 top_n의 2배수만큼 후보군으로 선정
- weighted_vote 칼럼 순이 높은 순으로 top_n만큼 추출하는 함수
def find_sim_movie(df, sorted_ind, title_name, top_n=10):
title_movie = df[df['title'] == title_name]
title_index = title_movie.index.values
# top_n의 2배에 해당하는 쟝르 유사성이 높은 index 추출
similar_indexes = sorted_ind[title_index, :(top_n*2)]
similar_indexes = similar_indexes.reshape(-1)
# 기준 영화 index는 제외
similar_indexes = similar_indexes[similar_indexes != title_index]
# top_n의 2배에 해당하는 후보군에서 weighted_vote 높은 순으로 top_n 만큼 추출
return df.iloc[similar_indexes].sort_values('weighted_vote', ascending=False)[:top_n]
similar_movies = find_sim_movie(movies_df, genre_sim_sorted_ind, 'The Godfather',10)
similar_movies[['title', 'vote_average', 'weighted_vote']]
지금까지 추천 시스템 알고리즘의 개념과,
실습을 통해 구현 과정을 알아보았습니다 !
이번 포스팅을 읽고 추천 시스템에 흥미가 생기신 분들은
다양한 패키지와 실습을 통해
추천 시스템을 더욱 자세히 알아가보는 것 어떨까요 ?
추천 시스템은 상업적 가치가 크기 때문에
별도의 패키지로 제공되면 매우 활용도가 높습니다.
대표적으로 파이썬 기반의 추천 시스템 구축을 위한
전용 패키지 중 하나인 ' Surprise '가 있습니다.
다음 포스팅에서 더 유익한 내용으로 찾아뵙겠습니다 :)
감사합니다.