본문 바로가기
ETC

영화추천 알고리즘(Movie-Recommender) 파이썬으로 구현해보기

by dkswnkk 2021. 10. 24.

참고한 블로그: https://lsjsj92.tistory.com/563

 

추천 시스템(Recommendation system)이란? - content based filtering, collaborative filtering

포스팅 개요 이번 포스팅은 추천 시스템(recommedation system)에 대해서 알아봅니다. 또한, 추천 시스템에는 컨텐츠 기반 필터링(content based filtering)과 협력 필터링(collaborative filtering)이 있는데요...

lsjsj92.tistory.com


 

추천 알고리즘의 종류와 의미에 대해 알고 싶다면?

 

 

추천 알고리즘(Recommendation Algorithm)이란?

목차 추천 알고리즘이란? 콘텐츠 기반 필터링 협업 필터링 추천 알고리즘 사용자가 선호할 만한 아이템을 추측하여 적합한 특정 항목을 제공해 주는 기본적인 알고리즘에는 콘텐츠 기반 필터링

dkswnkk.tistory.com


목차

구현 방식

콘텐츠 기반 필터링(content based filtering): 사용자가 특정 아이템을 선호하는 경우 그 아이템과 비슷한 콘텐츠를 가진 다른 아이템을 추천해주는 단순한 방식을 이용하여 구현하였다.

  • ex) 한 사용자가 영화 A에 높은 점수를 주었는데, 그 영화가 판타지 영화이고 B라는 감독이면 B감독의 다른 판타지 영화를 추천

소스 로직

  • movieData에 존재하는 영화의 제목 하나를 정확히 입력한다.
  • 영화 제목을 입력받으면 그 제목의 index를 뽑아낸다.
  • 코사인 유사도 중 영화 제목 인덱스에 해당하는 값에서 추천 개수만큼 뽑아낸다. (위 코드에서는 10)
  • imdb weighted rating을 적용한 추천점수 기반으로 정렬한다. (해당 방식은 소스코드에 주석으로 기재하였다.)
  • 추천 점수를 기반으로 추천하는 영화를 출력한다.

1

movieData는 아래의 경로에서 얻을 수 있다.

 

TMDB 5000 Movie Dataset

Metadata on ~5,000 movies from TMDb

www.kaggle.com

소스

'''
#참고: https://lsjsj92.tistory.com/563, https://github.com/lsjsj92/recommender_system_with_Python
추천 시스템에는 콘텐츠 기반 필터링(content based filtering)과 협력 필터링(collaborative filtering)이 있다.
콘텐츠 기반 필터링: 사용자가 특정 아이템을 선호하는 경우 그 아이템과 비슷한 콘텐츠를 가진 다른 아이템을 추천해주는 단순한 방식.
ex) 사용자 A가 영화1 에 높은 점수를 주었는데 그 영화1이 판타지영화이며 '안주형' 이라는 감독이라면  '안주형' 감독의 다른 판타지 영화를 추천해주는 방식
협력 필터링: 최근접 이웃 기반(nearest neighbor based collaborative filtering)과 잠재요인(latent factor based collaborative filtering) 방식이 있다
             협력 필터링은 사용자가 아이템에 매긴 평점, 상품구매 이력 등의 사용자 행동양식(user behavior)를 기반으로 추천 해주는 것이다.
            1.이 중 최근접 이웃 기반은 사용자-아이템 행렬에서 사용자가 아직 평가하지 않은 아이템을 예측하는 것이 목표이다.
            2. 잠재요인 기반은 아직도 많이 사용되는 방법으로써 행렬분해(matrix factorization)을 기반하여 사용한다.
               사용자-아이템 행렬을 '사용자-잠재요인', '아이템-잠재요인' 행렬로 분해하여 사용하며, 이 행렬테이터를 이용해 '잠재 요인'을 찾아내는데
               '저장 공간 절약'이 우수 하기 때문에 공간을 매우 효율적으로 사용할 수 있다.
아래 코드는 "콘텐츠 기반 필터링(content based filtering)" 으로 구현한 코드이다.
'''


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from numpy import array
from ast import literal_eval
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity

pd.set_option('display.max_rows', 100) # 행을 최대 100개까지 출력
pd.set_option('display.max_columns', 100) # 열을 최대 100개 까지 출력
pd.set_option('display.width', 1000) #출력 창 넓이 설정

data=pd.read_csv('tmdb_5000_movies.csv',encoding='euc-kr') #영화 정보가 담긴 엑셀파일을 불러온다

#데이터 전처리
data=data[['영화번호','제목','장르','평점','평점투표 수','인기도','키워드']] #사용할 데이터를 뽑아온다

'''
투표수가 많을 수록 많은 사람들이 평가를 했기 때문에 투표 수가 낮을 수 밖에 없다.
이러한 불공정을 처리하기 위해 weighed rating 방법을 이용한다. 참고: https://www.quora.com/How-does-IMDbs-rating-system-work
R: 개별 영화 평점
v: 개별 영화에 평점을 투표한 횟수
m: 순위안에 들어야 하는 최소 투표 (정하기 나름)
c: 전체 영화에 대한 평균 평점
투표수의 상위 90프로 이상이면 500위 안으로 들어오게 된다. 
이 코드에서는 m=500이라고 가정했다
'''

m=data['평점투표 수'].quantile(0.9)
data=data.loc[data['평점투표 수']>=m]
C=data['평점'].mean() #평점의 평균을 구한다

def weighted_rating(x,m=m,C=C):
    v=x['평점투표 수']
    R=x['평점']
    return (v/(v+m)*R)+(m/(m+v)*C)

data['추천점수']=data.apply(weighted_rating,axis=1)


data['장르']=data['장르'].apply(literal_eval) #list와와 dictionary 형태로 변경
data['키워드']=data['키워드'].apply(literal_eval)
data['장르']=data['장르'].apply(lambda x : [d['name'] for d in x]).apply(lambda x: " ".join(x)) # dict 형태 -> list 형태 -> 띄어쓰기로 이루어진 str로 변경
data['키워드']=data['키워드'].apply(lambda x : [d['name'] for d in x]).apply(lambda x: " ".join(x))


count_vector=CountVectorizer(ngram_range=(1,3))
c_vector_genres=count_vector.fit_transform(data['장르'])

gerne_c_sim=cosine_similarity(c_vector_genres,c_vector_genres).argsort()[:,::-1]    #코사인 유사도를 구한 벡터를 미리 저장

def get_recommed_movie_list(df,movie_title,top=30): #특정영화와 비슷한 영화를 추천해주는 함수
    target_movie_index=df[df['제목']==movie_title].index.values #특정 영화와 비슷한 영화를 추천해야 하기 때문에 '특정 영화' 정보를 뽑아내는 함수
    sim_index=gerne_c_sim[target_movie_index,:top].reshape(-1) # 코사인 유사도 중 비슷한 코사인 유사도를 가진 정보를 뽑아낸다
    sim_index=sim_index[sim_index!=target_movie_index] # 본인은 제외
    result=df.iloc[sim_index].sort_values('추천점수',ascending=False)[:10] #data frame 으로 만든 뒤 추천점수로 정렬 한 뒤 return
    return result



print("마음에 들었던 영화를 조건에 맞게 입력하세요:")
movie=input()
temp=get_recommed_movie_list(data,movie_title=movie)
ans=[]
ans=temp.values.tolist()
ans=array(ans)



for i in range(10):
        if i==0:
            print('%50s %40s %35s %20s %14s %20s' % ('제목','장르','평점','평점투표 수','인기도','추천 점수'))
        else:
            print('%60s %50s %20s %20s %20.4s %20.4s' % (ans[i][1],ans[i][2],ans[i][3],ans[i][4],ans[i][5],ans[i][7]))
            #ans[0]=영화번호, [1]=제목,[2]=장르,[3]=평점,[4]=평점투표 수,[5]=인기도,[6]=키워드,[7]=추천 점수

댓글