본문 바로가기
인공지능/인공지능

[추천 기능] ltem based collaborative filtering 영화 추천 시스템 만들기

by coding_su 2023. 1. 3.

📝ltem based collaborative filtering으로 영화 추천하는 기능 만들기

우선 데이터 데이터 프레임을 불러와서 불필요한 컬럼을 제거하고 합쳐준다

import pandas as pd
import numpy as np

# 영화정보파일과 리뷰파일을 불러온다
movie_titles_df = pd.read_csv('Movie_Id_Titles.csv')
movies_rating_df = pd.read_csv('u.data', sep='\t', names= ['user_id', 'item_id', 'rating', 'timestamp'])

# timestamp 컬럼은 필요없으니, movies_rating_df 에서 제거
movies_rating_df.drop('timestamp', axis= 1, inplace= True)

# 두개의 데이터프레임을 합치기(리뷰가 없는 영화정보도 가져오게 how= 'left')
movies_rating_df = pd.merge(movie_titles_df, movies_rating_df, on= 'item_id', how= 'left')

데이터를 분석한다

※ item_id 로 그룹바이하면 이 값이 인덱스로 표현되므로 판다스에서 데이터를 분석할때는

 인덱스가 사람이 해석할 수 있는 데이터로 나타나도록 해주는게 좋다

# 영화 타이틀별로 리뷰 점수 통계치 조회
movies_rating_df.groupby('title')['rating'].describe()

# 영화 타이틀별로 리뷰 점수의 통계치를 평균이 높은 순으로 조회
movies_rating_df.groupby('title')['rating'].describe().sort_values('mean', ascending= False)

 

각 영화별 별점 평균수와 갯수를 변수에 저장하고 합친다

ratings_df_mean = movies_rating_df.groupby('title')['rating'].mean()

ratings_df_count = movies_rating_df.groupby('title')['rating'].count()

# 각각 데이터 프레임으로 만들고 컬럼 이름 변경
df1 = ratings_df_mean.to_frame()
df1.columns = ['mean']

df2 = ratings_df_count.to_frame()
df2.columns = ['count']

# join으로 합쳐준다
ratings_mean_count_df =  df1.join(df2)

 

히스토그램으로 데이터를 확인해본다

import matplotlib.pyplot as plt
import seaborn as sns

ratings_mean_count_df['mean'].hist()
plt.show()

ratings_mean_count_df['count'].hist(bins= 50)
plt.show()

※ 평균 점수가 5점인 데이터를 확인해보면 리뷰 갯수가 1개인 것들이 많으니 나중에 처리해줘야한다

이제 상관계수를 뽑기 위해 데이터 프레임을 피봇테이블(pivot_table)한다

※ 인덱스는 user_id, 컬럼은 title, 그 안의 데이터(밸류)는 rating

df = movies_rating_df.pivot_table(index= 'user_id', columns= 'title', values= 'rating', aggfunc= 'mean') # aggfunc 디폴트는 평균이므로 안써도 됨

 

전체 데이터를 하기 전 한개의 데이터만 가지고 추천 시스템을 만들어보겠다

# 타이타닉 컬럼과 df의 전체 컬럼간의 상관계수를 뽑기
corr_titanic = df.corrwith( df['Titanic (1997)'] )

# 데이터 프레임으로 만들어주고 컬럼명을 변경해준다
corr_titanic = corr_titanic.to_frame()
corr_titanic.columns = ['correlation']

# 별점을 한명이 남긴 리뷰도 있을거니 확인하기 위해 카운트를 붙여준다
corr_titanic = corr_titanic.join( ratings_mean_count_df['count'] )

# nan은 아무 관계 없는 것이니 삭제해준다
corr_titanic.dropna(inplace= True)

# 이렇게 하면 count수가 적은 데이터도 있기 때문에 아래 코드로 실행한다
corr_titanic.sort_values('correlation', ascending= False)

# 80명 이상 리뷰를 남긴 영화만 가져와서 correlation 순으로 정렬
corr_titanic.loc[ corr_titanic['count'] >= 80 ].sort_values('correlation', ascending= False).head(10)

 

이렇게 한개의 데이터를 가지고 해봤으니 이제 전체 데이터셋에 대한 ltem based collaborative filter를 만든다

# 모든 영화의 상관계수를 뽑는다
# 단, 각 영화는 적어도 80명 이상이 별점을 준 영화만 상관계수를 뽑는다
corr_movie = df.corr(min_periods= 80)

# 나의 영화 별점 정보를 담은 파일을 가져온다
myRatings = pd.read_csv('My_Ratings.csv')

# 내가 본 영화는 여러개일 수 있기 때문에 반복문을 사용한다
similar_movies_list = pd.DataFrame()

for i in range( myRatings.shape[0] ) :
  movie_title = myRatings['Movie Name'][i]
  recom_movies = corr_movie[movie_title].dropna().sort_values(ascending= False).to_frame()
  recom_movies.columns = ['correlation']
  recom_movies['weight'] = recom_movies['correlation'] * myRatings['Ratings'][i]
  similar_movies_list = similar_movies_list.append( recom_movies )

 

1. weight로 정렬한다 (내가 준 별점이 반영된 컬럼이 바로 weight)

similar_movies_list = similar_movies_list.sort_values('weight', ascending= False)

 

2. 내가 본 영화는 위의 데이터프레임에서 삭제한다

# 내가 본 영화 제목을 리스트로 만들어 변수에 저장
drop_index_list = myRatings['Movie Name'].to_list()

# 반복문을 사용해 봤던 영화의 행 제거
for name in drop_index_list :
  if name in similar_movies_list.index :
    similar_movies_list.drop(name, axis=0, inplace= True)

 

3. 내가 본 영화는 이제 없으므로 영화를 추천해주면 된다

similar_movies_list

 

★ 추천 영화가 중복되는 경우도 발생한다
따라서 중복된 영화가 있을 경우는 웨이트가 가장 높은 값으로만 추천해준다
즉, 영화 이름별로 웨이트가 가장 높은 데이터를 가져와서 웨이트로 정렬한다

# 중복 데이터가 있는지 먼저 확인한다
similar_movies_list.reset_index()['title'].value_counts()

# 중복이 있으니 제거한다
similar_movies_list.groupby('title')['weight'].max().sort_values(ascending= False)

댓글