[kaggle] 영화 추천 시스템

2018. 8. 8. 23:30kaggle

kaggle 영화 추천 시스템


데이터는 아래의 사이트에가서 다운 받으면 된다.


이걸 하면서 처음봤지만 영화관련 데이터는 이곳에서 가져오는 것 같다. 

https://www.themoviedb.org

https://www.imdb.com/


이런 데이터들을 이렇게 저렇게 요리해서 4개의 단순한 추천 기능을 만들 것 이다.  

  1. 장르를 통한 추천: 이 시스템은 전반적으로 TMDB 투표수와 평균 투표수를 사용해서 특정 장르 영화를 추천해주는 시스템입니다. IMDB 가중평가 시스템은 정렬이 최종적으로 수행 된 평가를 계산하는 데 사용되었습니다. 

  2. 콘텐츠 기반 추천: 영화의 내용과 캐릭터, 감독, 배우 등등을 고려하여 추천하는 시스템이다. 
  3. 협업 필터링: surprise 라이브러리를 사용하여 단일값 분해를 기반으로 협업필터링을 작성할 수 있다. 여기서 구해진 평균제곱 오차는 1보다 작으며 사용자 및 영화에 대한 평가를 제공합니다. 
  4. Hybrid Engine: 콘텐츠(내용, 감독, 배우)와 협업 필터링 아이디어를 모아서 내부적으로 계산한 예상 등급을 기반으로 영화를 추천해줍니다. 

이와 같은 기능 4개를 1편씩 글을 작성해보겠습니다. 

장르를 통해서 영화 추천해주기 


1. 필요한 라이브러리 로드하기 

주피터 노트북에서 실행함으로 서 다음에 사용할 추천기 까지 필요한 라이브러리까지 적습니다. 

%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from ast import literal_eval # 문자열 모형의 딕트를 스근하게 데이터 형 딕트로 바꾸어 준다.
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer #문자열 데이터를 벡터화
from sklearn.metrics.pairwise import linear_kernel, cosine_similarity
from nltk.stem.snowball import SnowballStemmer #dogs나 다...크킄크크 같은 것을 일반적인 단어로 바꿔준다.
from nltk.stem.wordnet import WordNetLemmatizer
from nltk.corpus import wordnet
from surprise import Reader, Dataset, SVD, evaluate

import warnings; warnings.simplefilter('ignore')



2. 데이터 읽어오고 각종 특징 보기 



md = pd.read_csv('input/movies_metadata.csv')

데이터를 읽어온다. 그리고 head나 describe, isnull().sum()등을 사용하여 데이터들의 특징들을 본다. 


md.isnull().sum()

adult                        0

belongs_to_collection    40972

budget                       0

genres                       0

homepage                 37684

id                           0

imdb_id                     17

......

이런식으로 데이터에 nan 값이 있는지 확인 할 수 있다. 

md.describe()



3. data 정제해주기 


3-1 genres 칼럼 데이터 정제해주기 


genres라는 칼럼에는 영화가 어떤 장르인지 리스트형태로 나타나 있다. 


이런식으로 

[{'id': 16, 'name': 'Animation'}, {'id': 35, '...

1        [{'id': 12, 'name': 'Adventure'}, {'id': 14, '...

2        [{'id': 10749, 'name': 'Romance'}, {'id': 35, ...

3        [{'id': 35, 'name': 'Comedy'}, {'id': 18, 'nam...


이걸 도큐먼트 안에있는 name이라는 값을 각영화마다 따로 빼내서 넣어보자!!


md['genres'] = md['genres'].fillna('[]').apply(literal_eval).apply(lambda x : [i['name'] for i in x] if isinstance(x, list) else [])

fillna는 nan인 데이터를 알아서 채워주고 literal_eval은 문자열 형태의 도큐먼트를 진짜 도큐먼트로 바꾸는 기능을한다. []안에 장르를 다넣어 주고 결과를 보면 아래와 같은 형태로 genres에 적용 된 것을 알 수 있다. 


0                        [Animation, Comedy, Family]

1                       [Adventure, Fantasy, Family]

2                                  [Romance, Comedy]

3                           [Comedy, Drama, Romance]

4                                           [Comedy]

5                   [Action, Crime, Drama, Thriller]

6                                  [Comedy, Romance]

7                 [Action, Adventure, Drama, Family]

8                      [Action, Adventure, Thriller]

9                      [Adventure, Action, Thriller]


3-2 투표수와 평점에의해 영화별 평가점수 내보기


TMDB사이트에서 제공하는 영화 평점을 이용해서 영화 차트를 만들어 보겠습니다. 

공식은 IMDB에서 제공하는 공식을 사용해서 영화평점을 만들어 보겠습니다. 




  • v는 영화의 득표수
  • m은 차트에 나열되기 위한 최소한의 투표수
  • R은 영화의 평균 평점
  • C는 전체 차트의 평균 평점


vote_count = md[md['vote_count'].notnull()]['vote_count'].astype('int')
vote_average = md[md['vote_average'].notnull()]['vote_average'].astype('int')
m = vote_count.quantile(0.95) #quanitle은 4분위 데이터를 말한건데 0.95는 95% 부터의 데이터를 의미한다.
C = vote_average.mean()


영화 개봉일에서 출시년도만 빼서 데이터에 넣기


md['year'] = md['release_date'].apply(lambda x : str(x).split('-')[0] if x != np.nan else np.nan)

0        1995

1        1995

2        1995

3        1995

4        1995

5        1995

qualified = md[(md['vote_count'].notnull()) & (md['vote_average'].notnull()) & (md['vote_count'] >= m)][['title', 'year',

'vote_count', 'vote_average', 'popularity', 'genres']]
qualified['vote_count'] = qualified['vote_count'].astype(int)
qualified['vote_average'] = qualified['vote_average'].astype(int)


qualified라는 데이터 셋을 따로하나 만든다.

def weight_rate(x):
v = x['vote_count']
r = x['vote_average']
return (v / (v + m)*r) + (m/(m+v)*C)

위는 점수를 만들 함수이다. 

qualified['wr'] = qualified.apply(weight_rate, axis=1)
qualified = qualified.sort_values(by='wr', ascending=False)

qualified['wr']를 만들어서 얘를 기준으로 정렬시켜준다. 


qualified.head(10)를 통해서 상위 10개의 영화를 볼 수 있다.



4 장르를 통해서 평가해보기 


위 사진의 결과를 보면 한감독의 영화인 Inception, 다크나이트, 인터스텔라 등등의 영화과 상위권인 것을 알 수 있다. 그렇다면 이번에는 장르를 통해서 상위권 영화를 뽑아보겠다. 


s = md.apply(lambda x: pd.Series(x['genres']), axis=1).stack().reset_index(level=1, drop=True)
s

0              Animation

0                 Comedy

0                 Family

1              Adventure

1                Fantasy

1                 Family

2                Romance

2                 Comedy

3                 Comedy

3                  Drama

3                Romance


이런식으로 s에 데이터가 저장된다. 각 인덱스의 0이나 1은 동일한 영화에 장르를 말하는 것입니다.


s.name = 'genres'
s_gen = md.drop('genres', axis=1).join(s)

이 코드를 실행하면 원래 존재하던 genres는 지워버리고 새로운 genres 데이터인 s를 집어 넣는다. 토이스토리는 장르가 3개이니 같은 영화 데이터가 장르에 따라서 세개 만들어짐을 알 수 있다. 

 

0  False  {'id': 10194, 'name': 'Toy Story Collection', ...  30000000   

0  False  {'id': 10194, 'name': 'Toy Story Collection', ...  30000000   

0  False  {'id': 10194, 'name': 'Toy Story Collection', ...  30000000   


def chart_bar(genre, percenTage=0.85):
df = s_gen[s_gen['genres'] == genre]
vote_count = df[df['vote_count'].notnull()]['vote_count'].astype(int)
vote_average = df[df['vote_average'].notnull()]['vote_average'].astype(int)
m = vote_count.quantile(percenTage)
c = vote_average.mean()

qualified = df[(df['vote_count'].notnull()) & (df['vote_count'] >= m) & (df['vote_average'].notnull())] [['title', 'year', 'vote_count', 'vote_average', 'popularity']]
qualified['vote_count'] = qualified['vote_count'].astype('int')
qualified['vote_average'] = qualified['vote_average'].astype('int')
qualified['wr'] = qualified.apply(lambda x: (x['vote_count'] / (x['vote_count'] + m)*x['vote_average']) + (m/(m+x['vote_count'])*c), axis=1)
qualified = qualified.sort_values('wr', ascending=False) return qualified

위는 장르별 순위를 정해주는 함수이다. 지금까지 우리가 진행했던 과정과 비슷하여 어렵지 않은 코드이다.



chart_bar('Romance').head(3)



chart_bar('Adventure').head(3)




다음에는 영화 내용이나 감독, 배우를 통해서 추천하는 시스템을 만들어 보겠다.


일단 저는 이제막 타이타닉만 해본 햇병아리입니다. 

그래서 일단은 커널을 따라해보고 공부하고 기록하지만 나중에는 제가 직접 생각하여 문제를 풀날을 상상하며 열심히 해보겠습니다.


'kaggle' 카테고리의 다른 글

[kaggle] 영화 추천 시스템 2편  (0) 2018.08.09