딥 러닝 - 영화 추천 시스템(1)

이상해씨·2021년 12월 11일
1

딥 러닝

목록 보기
1/10

◾추천 시스템

  • 추천 시스템 : 특정 사용자가 관심을 가질만한 정보 (영화, 음악, 책, 뉴스, 이미지, 웹 페이지 등)를 추천하는 것
    • 현재는 많은 분야에 적용되어 있다. 특히 온라인 쇼핑몰 콘텐츠 등에서는 꽤 중요한 부분을 차지한다.
    • 예시) 유튜브, 쇼핑몰, 광고 등
  • 콘텐츠 기반 필터링 추천 시스템 : 사용자가 특정한 아이템을 선호하는 경우, 그 아이템과 비슷한 아이템을 추천하는 방식
  • 최근접 이웃 협업 필터링 추천 시스템 : 축적된 사용자 행동 데이터를 기반으로 사용자가 아직 평가하지 않은 아이템을 예측 평가하는 방식
    • 사용자 기반 : 각 사용자와 비슷한 고객들의 행동 참고
    • 아이템 기반 : 해당 아이템을 선택한 다른 고객들의 행동 참고
    • 일반적으로는 사용자 기반보다 아이템 기반 협업 필터링이 정확도가 높다.
      • 비슷한 영화를 좋아한다고 취향이 비슷하다고 판단하기 어렵거나 매우 유명한 영화의 경우 취향과 관계없이 관람하는 경우가 많고 평점을 매기지 않는 경우가 많기 때문이다.
  • 잠재 요인 협업 필터링 추천 시스템 : 사용자-아이템 평점 행렬 데이터를 이용해 "잠재요인"을 도출하는 방식
    • 주요인과 아이템에 대한 잠재요인에 대해 행렬 분해를 진행하고 다시 행렬곱을 통해 아직 평점을 부여하지 않은 아이템에 대한 예측 평점 생성

◾영화 추천 시스템

0. 데이터

  • TMDB5000 영화 데이터 : Kaggle
  • MoviesLens 평점 데이터(1MB) : MovieLens

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)

  • 코사인 유사도 : 두 벡터 간의 코사인 각도를 이용하여 구할 수 있는 두 벡터의 유사도를 의미
    • 문장의 유사도를 측정하는 방법 중 하나이다.
    • similarity=cos(θ)=ABA B=i=1nAi×Bii=1n(Ai)2×i=1n(Bi)2similarity = 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}}}}
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 반환
# 내림차순 정렬을 위해 -1 옵션을 추가로 준다
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]

  • 영화 선정을 위한 가중치 선정
    • (vv+m)R+(mv+m)C({v\over v+m})R + ({m\over v+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로 변경
    • nan은 0으로 변환
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)')

profile
후라이드 치킨

0개의 댓글