프로젝트

(프로젝트)머신러닝_Netflix 영화 데이터셋을 이용한 '협업 필터링' + '콘텐츠 기반 필터링' 영화 추천_1) 협업 필터링_KNN

하방주인장 2023. 6. 25. 12:01

목차

     

    Overview

    Kaggle에 있는 넷플릭스 영화 순위 데이터셋 및 넷플릭스 영화 데이터셋으로 협업 필터링, 콘텐츠 기반 필터링 영화 추천 시스템을 구현하고자 한다. 사용자가 자신이 좋아하는 영화를 입력하면 협업 필터링으로 추천한 영화와 콘텐츠 기반 필터링으로 추천한 영화를 모두 추천해주어 더욱 다양한 영화를 사용자에게 추천하는 것을 목표로 한다. 

     

    https://www.kaggle.com/datasets/rishitjavia/netflix-movie-rating-dataset

     

    Netflix Movie Rating Dataset

    Dataset from Netflix's 'Netflix Prize' competition

    www.kaggle.com

     

    1. Netflix Movie Recommendation System: Collaborative Filtering_KNN

    - Collaborative Filtering(협업 필터링) 이란?

    협업 필터링은 쿼리와 항목 간 유사성을 동시에 사용하여 권장사항을 제공하는 것을 말하는 데, 예를 들어 사용자 A가 사용자 B와 비슷하고 사용자 B가 영화 1을 좋아하면 사용자 A가 영화 1과 유사한 영화를 보지 못했더라도 시스템에서 사용자 1에게 영화 1을 추천하는 것을 뜻한다. 

     

    이번 프로젝트에서는 협업 필터링의 초기 알고리즘인  Memory-based Collaborative Filtering의 아이템 기반의 근접 이웃 방법(Neighborhood Method)를 사용하여 특정 사용자가 준 점수 간의 유사한 아이템을 찾아서 추천 리스트를 생성하고자 한다. 

     

    https://developers.google.com/machine-learning/recommendation/overview/candidate-generation?hl=ko 

     

    후보 생성 개요  |  Machine Learning  |  Google for Developers

    이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 의견 보내기 후보 생성 개요 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 후보 생성은

    developers.google.com

     

    01) Business Understanding

    아이템(영화)을 기준으로 사용자들이 비슷한 평점을 내린 유사한 아이템(영화)을 추천

     

    02) Data Understanding

    2-1. Data Load

    import pandas as pd
    movie_df = pd.read_csv('data/Netflix_Dataset_Movie.csv')
    rating_df = pd.read_csv('data/Netflix_Dataset_Rating.csv')

     

    2-2. EDA

    # Merge
    df = movie_df.merge(rating_df, how='inner', on='Movie_ID')
    
    # Null 값 확인
    df.isnull().sum() # Null 값 없음
    
    # rating 확인
    df['Rating'].value_counts() # 1~5점으로 형성되어 있음
    
    # 영화별 리뷰 수 확인
    import matplotlib.pyplot as plt
    plt.title("Popularity Ranking of Movies") # 하나의 영화에 많은 리뷰가 있음
    df.Movie_ID.value_counts().plot(kind = "bar")
    plt.xticks(ticks = [])
    
    # 협업 필터링 용 df를 csv로 저장
    df.to_csv('data/cf_df.csv', index=False)

    영화별 리뷰 수 확인

     

    2-3. Data Preparation

    # 영화 별 유저들의 평점 테이블 생성 
    movie_pivot = df.pivot_table(index='Movie_ID', columns='User_ID', values='Rating')
    movie_pivot.fillna(0, inplace=True) # NaN 값을 0으로 변환
    movie_pivot = movie_pivot.astype('int8') # 데이터 크기를 줄이기 위해 int8로 변환
    
    # shape 확인
    movie_pivot.shape # (1350, 143458)
    
    # 테이블 출력
    movie_pivot.head()

    테이블 출력

     

    03) Modeling

    # 모델 생성
    from sklearn.neighbors import NearestNeighbors
    n_neighbors = 11
    KNN = NearestNeighbors(metric='cosine', n_neighbors=n_neighbors, n_jobs=-1)
    KNN.fit(movie_pivot)
    distances, indices = KNN.kneighbors(movie_pivot)
    
    # 모델 저장
    import pickle 
    with open('data/distances.pickle','wb') as fw:
        pickle.dump(distances, fw)
        
    with open('data/indices.pickle','wb') as fw:
        pickle.dump(indices, fw)
        
    with open('data/movie_pivot.pickle','wb') as fw:
        pickle.dump(movie_pivot, fw)
    
    # 모델 버전 확인
    import numpy
    import sklearn
    print(f'numpy version: {numpy.__version__}')
    print(f'sklearn version: {sklearn.__version__}')
    print('python version: 3.10.9')
    
    # numpy version: 1.23.5
    # sklearn version: 1.2.1
    # python version: 3.10.9

     

    04) Deployment

    # 영화 제목 검색 함수 생성
    def search_movie(name):
        search_movie_id = df.loc[df['Name'] == name, 'Movie_ID'].unique()[0]
        index = np.where(movie_pivot.index == search_movie_id)[0][0]
        return index
        
    # CF 함수 생성
    def collaborative_filtering(index, n):
        for i in range(n):
            movie_name = movie_df.loc[movie_df['Movie_ID'] == movie_pivot.index[indices[index][i]], 'Name'].values[0]
            distance = '{:.3f}'.format(distances[index][i])
            print(movie_name, 'distance: ', distance)
    
    # Top 3 영화 추천
    index = search_movie('Pirates of the Caribbean: The Curse of the Black Pearl')
    n = 4  # Top 3
    collaborative_filtering(index, n)  
    
    # Pirates of the Caribbean: The Curse of the Black Pearl distance:  0.000
    # Lord of the Rings: The Fellowship of the Ring distance:  0.180
    # Finding Nemo (Widescreen) distance:  0.210
    # Bruce Almighty distance:  0.217

     

     

    05) Evaluation

    from sklearn.metrics.pairwise import cosine_similarity
    import numpy as np
    
    # 케리비안 해적 영화와 유사한 영화
    indices[587]
    # array([ 587,  746, 1199, 1162, 1191, 1291, 1331,  837, 1096,  857,  719], dtype=int64)
    
    x1 = movie_pivot.iloc[587].values
    x2 = movie_pivot.iloc[746].values
    
    cosine_similarity(x1.reshape(1,-1), x2.reshape(1,-1))
    # array([[0.82012558]])
    
    1 - 0.82012558
    # 0.17987441999999998
    # 약 0.18로 위의 영화 추천이 잘 계산되었음을 알 수 있음