◾추천 시스템
추천 시스템 : 특정 사용자가 관심을 가질만한 정보 (영화, 음악, 책, 뉴스, 이미지, 웹 페이지 등)를 추천하는 것
현재는 많은 분야에 적용되어 있다. 특히 온라인 쇼핑몰 콘텐츠 등에서는 꽤 중요한 부분을 차지한다.
예시) 유튜브, 쇼핑몰, 광고 등
콘텐츠 기반 필터링 추천 시스템
: 사용자가 특정한 아이템을 선호하는 경우, 그 아이템과 비슷한 아이템을 추천하는 방식
최근접 이웃 협업 필터링 추천 시스템
: 축적된 사용자 행동 데이터를 기반으로 사용자가 아직 평가하지 않은 아이템을 예측 평가하는 방식
사용자 기반 : 각 사용자와 비슷한 고객들의 행동 참고
아이템 기반 : 해당 아이템을 선택한 다른 고객들의 행동 참고
일반적으로는 사용자 기반보다 아이템 기반 협업 필터링이 정확도가 높다.
비슷한 영화를 좋아한다고 취향이 비슷하다고 판단하기 어렵거나 매우 유명한 영화의 경우 취향과 관계없이 관람하는 경우가 많고 평점을 매기지 않는 경우가 많기 때문이다.
잠재 요인 협업 필터링 추천 시스템
: 사용자-아이템 평점 행렬 데이터를 이용해 "잠재요인"을 도출하는 방식
주요인과 아이템에 대한 잠재요인에 대해 행렬 분해를 진행하고 다시 행렬곱을 통해 아직 평점을 부여하지 않은 아이템에 대한 예측 평점 생성
◾영화 추천 시스템
0. 데이터
1. 콘텐츠 기반 필터링(TMDB5000)
import pandas as pd
import numpy as np
import warnings; warnings. filterwarnings( 'ignore' )
movies = pd. read_csv( './data/01/tmdb_5000_movies.csv' )
print ( movies. shape)
movies. head( 2 )
- 데이터 정리
컬럼 정보
id : 영화 아이디
title : 영화명
genres : 영화 장르
vote_average : 영화 평균 평점
vote_count : 영화 투표수
popularity : 영화 인기
keywords : 영화 키워드
overview : 영화 개요
genres, keywords는 컬럼안에 dict형으로 보이지만 str형으로 저장되어있다.
movies_df = movies[ [ 'id' , 'title' , 'genres' , 'vote_average' , 'vote_count' ,
'popularity' , 'keywords' , 'overview' ] ]
movies_df. head( 2 )
문자열로 된 데이터를 literal_eval로 변경해준다.
genres와 keywords의 내용을 list와 dict로 복구
movies_df[ 'genres' ] = movies_df[ 'genres' ] . apply ( literal_eval)
movies_df[ 'keywords' ] = movies_df[ 'keywords' ] . apply ( literal_eval)
movies_df. head( 2 )
dict형의 value 값을 특성으로 사용하도록 변경
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. head( 2 )
- genres 기준 CountVectorize
genres의 각 단어들을 하나의 문장으로 변환
movies_df[ 'genres_literal' ] = movies_df[ 'genres' ] . apply ( lambda x : ( ' ' ) . join( x) )
movies_df. head( 2 )
genres_literal CountVectorize 수행
from sklearn. feature_extraction. text import CountVectorizer
count_vect = CountVectorizer( min_df= 0 , ngram_range= ( 1 , 2 ) )
genre_mat = count_vect. fit_transform( movies_df[ 'genres_literal' ] )
print ( genre_mat. shape)
코사인 유사도
: 두 벡터 간의 코사인 각도를 이용하여 구할 수 있는 두 벡터의 유사도를 의미
문장의 유사도를 측정하는 방법 중 하나이다.
s i m i l a r i t y = c o s ( θ ) = A B ∣ ∣ A ∣ ∣ ∣ ∣ B ∣ ∣ = ∑ i = 1 n A i × B i ∑ i = 1 n ( A i ) 2 × ∑ i = 1 n ( B i ) 2 similarity = cos(\theta) = {AB \over ||A||~||B||} = {\sum_{i=1}^{n}{A_{i}\times B_{i}} \over \sqrt{\sum_{i=1}^{n}({A_{i})^{2}}}\times \sqrt{\sum_{i=1}^{n}({B_{i})^{2}}}} s i m i l a r i t y = c o s ( θ ) = ∣ ∣ A ∣ ∣ ∣ ∣ B ∣ ∣ A B = ∑ i = 1 n ( A i ) 2 × ∑ i = 1 n ( B i ) 2 ∑ i = 1 n A i × B i
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 객체에서 높은 값 순으로 정렬할 수 있다.
argsort
: Series 데이터를 정렬한 index 반환
genre_sim_sorted_ind = genre_sim. argsort( ) [ : , : : - 1 ]
print ( genre_sim_sorted_ind[ : 1 ] )
- 추천 영화 DataFrame 반환 함수
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
similar_indexes = sorted_ind[ title_index, : ( top_n) ]
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' ] ]
2. + 가중치 평점(TMDB5000)
- 데이터 재탐색
평점과 평점을 매긴 사람을 살펴보면 적은 사람만 투표한 경우 정말로 그 수치만큼인지 판단하기 어렵다.
movies_df[ [ 'title' , 'vote_average' , 'vote_count' ] ] . sort_values( 'vote_average' , ascending= False ) [ : 10 ]
영화 선정을 위한 가중치 선정
( v v + m ) R + ( m v + m ) C ({v\over v+m})R + ({m\over v+m})C ( v + m v ) R + ( v + m m ) C
v : 개별 영화에 평점을 투표한 횟수
m : 평점을 부여하기 위한 최소 투표 횟수
R : 개별 영화에 대한 평균 평점
C : 전체 영화에 대한 평균 평점
영화 전체 평균 평점과 최소 투가 횟수를 60% 지점으로 지정
가중치가 부여된 평점 계산하기 위한 함수
C = movies_df[ 'vote_average' ] . mean( )
m = movies_df[ 'vote_count' ] . quantile( 0.6 )
def weighted_vote_average ( record) :
v = record[ 'vote_count' ]
R = record[ 'vote_average' ]
return ( ( v/ ( v+ m) ) * R) + ( ( m / ( v + m) ) * C)
movies_df[ 'weighted_vote' ] = movies_df. apply ( weighted_vote_average, axis= 1 )
movies_df. head( 2 )
- 가중치 평점을 활용한 추천 영화 DataFrame 반환 함수
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
similar_indexes = sorted_ind[ title_index, : ( top_n * 2 ) ]
similar_indexes = similar_indexes. reshape( - 1 )
similar_indexes = similar_indexes[ similar_indexes != title_index]
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' ] ]
3. 아이템 기반 최근접 이웃 협업 필터링(MoviesLens)
- 데이터 읽기
import pandas as pd
import numpy as np
import warnings; warnings. filterwarnings( 'ignore' )
movies = pd. read_csv( './data/01/ml-latest-small/movies.csv' )
ratings = pd. read_csv( './data/01/ml-latest-small/ratings.csv' )
movies. shape, ratings. shape
movie : 영화 제목과 장르
ratings : 사용자별 각 영화의 평점
movies. head( 2 )
ratings. head( 2 )
- 데이터 정리
ratings와 movie를 movieId
기준으로 결합
rating_movies = pd. merge( ratings, movies, on= 'movieId' )
rating_movies. head( 2 )
ratings 데이터를 각 사용자의 평점을 보기 쉽게 pivot_table로 변경
ratings_matrix = rating_movies. pivot_table( 'rating' , index= 'userId' , columns= 'title' )
ratings_matrix. fillna( 0 , inplace= True )
ratings_matrix. head( 2 )
- 유사도 측정
유사도 측정을 위해 행렬 transpose(행, 열 전환)
ratings_matrix_T = ratings_matrix. transpose( )
ratings_matrix_T. head( 2 )
from sklearn. metrics. pairwise import cosine_similarity
item_sim = cosine_similarity( ratings_matrix_T, ratings_matrix_T)
item_sim_df = pd. DataFrame( data= item_sim, index= ratings_matrix. columns, columns= ratings_matrix. columns)
print ( item_sim_df. shape)
item_sim_df. head( 2 )
- 추천 영화 DataFrame 반환 함수
def find_sim_movie_item ( df, title_name, top_n= 10 ) :
title_movie_sim = df[ [ title_name] ] . drop( title_name, axis= 0 )
return title_movie_sim. sort_values( title_name, ascending= False ) [ : top_n]
find_sim_movie_item( item_sim_df, 'Godfather, The (1972)' )
find_sim_movie_item( item_sim_df, 'Inception (2010)' )