Project

[movie recomendation] 알고리즘에 따른 영화 추천 시스템 구현 - 2(알고리즘 구현 부분)

반응형

 

# 유저에 따른 개인 영화 추천

 

def user_difference(data,usernumber,rating,moviedata,dropdata,reader,svd):
    df = data
    df_user = df[(df['userId'] == usernumber) & (df['rating'] == rating)]
    df_user = df_user.set_index('movieId')
    df_user = df_user.join(moviedata)['original_title']
    print(df_user)

    user_release_ratio_list = user_release_ratio(df, usernumber) # 유저의 년도 비율을 가져온다.

    user_df = moviedata.copy()
    user_df = user_df[~user_df['movieId'].isin(dropdata)]
    data1 = Dataset.load_from_df(df[['userId', 'movieId', 'rating']], reader)
    trainset = data1.build_full_trainset()
    svd.fit(trainset)
    user_df['Estimate_Score'] = user_df['movieId'].apply(lambda x: svd.predict(usernumber, x).est)
    # user_df = user_df.drop('movieId', axis = 1)
    user_df = user_df.sort_values('Estimate_Score', ascending=False)
    print(user_df.head(10))

    return user_df
user_df665 = user_difference(df,665,5,meta,drop_movie_list,reader,svd)
user_df664 = user_difference(df,664,5,meta,drop_movie_list,reader,svd)

s 여기서 여러 가지 매개변수를 통해 함수를 구성하였다.

 

data - df ( 사용자들의 리뷰 데이터 )

 

usernumber - 사용자의 userid

 

rating - 평점이 몇 점일 때 여기서는 5점으로 주었다.

 

moviedata - 영화 메타 데이터를 넣어주었다.

 

 dropdata - 사용하지 않을 영화 리스트를 뽑아서 따로 빼주었다.

 

reader  - reader라는 함수를 넣어줬다. 

 

svd - 알고리즘 명 

 

이렇게 값을 넣고 간단한 전처리를 하고 svd를 돌린다. 

 

SVD 알고리즘을 우리가 가지고 있는 trainset으로 fit을 한다. 이 작업을 해야 각 알고리즘이 우리가 가지 데이터에 맞춰서 잘 작동하게 된다. 그다음 이 SVD를 통해 예측을 한다.

 

여기서는 유저가 가지고 있는 movieid를 넣었을 때 svd를 통해 예측을 하고 그 값을 user_df에서 estimate_score라는 새로운 칼럼을 만든다. 그다음 sort_values를 통해 정렬을 해준다. 이것의 결과값을 보면 

 

user664에 대한 영화 추천 결과이다.

 

 

 

user 665에 대한 영화 추천 결과이다.

 

이렇게 해당유저가 본 영화에 따라 달라지는 결과를 볼 수 있다.

 


#유저의 변화에 따른 영화추천

 

def user_difference(data,usernumber,rating,moviedata,dropdata,reader,svd):
    df = data
    df_user = df[(df['userId'] == usernumber) & (df['rating'] == rating)]
    df_user = df_user.set_index('movieId')
    df_user = df_user.join(moviedata)['original_title']
    print(df_user)

    user_release_ratio_list = user_release_ratio(df, usernumber) # 유저의 년도 비율을 가져온다.

    user_df = moviedata.copy()
    user_df = user_df[~user_df['movieId'].isin(dropdata)]
    data1 = Dataset.load_from_df(df[['userId', 'movieId', 'rating']], reader)
    trainset = data1.build_full_trainset()
    svd.fit(trainset)
    user_df['Estimate_Score'] = user_df['movieId'].apply(lambda x: svd.predict(usernumber, x).est)
    # user_df = user_df.drop('movieId', axis = 1)
    user_df = user_df.sort_values('Estimate_Score', ascending=False)
    print(user_df.head(10))

    return user_df
user_1 = user_difference(df,1,5,meta,drop_movie_list,reader,svd)
df2 = df
df2.loc[1] = [1,5502,5.0]
df2.loc[2] = [1,5991.0,5.0]
add_user_1 = user_difference(df2,1,5,meta,drop_movie_list,reader,svd)

 이 부분을 보면 user 1에 대한 영화 추천을 한다. 처음에는 user1에 대한 영화 추천을 한 뒤 밑에서 해당 유저가 본 영화를 추가해준 다음 영화 추천을 다시 해주었다. 

 

 

이게 첫 번째 영화 추천을 했을 때의 결과이다. 

 

 

이게 사용자가 영화 리뷰를 등록함에 따라 바뀌는 결과를 볼 수 있다. movie id 가 달라진 걸 볼 수 있다.

 

 


#변수에 대한 가중치

이 부분은 변수에 대한 가중치를 주기 위해 추가한 부분이다. 알고리즘만으로 추천을 한다면 이 부분은 생략해도 상관없다.

 

movies_metadata.csv를 보면 다양한 정보가 있다. 그중에 나는 영화 개봉 연도의 변수를 사용하기로 했다. 콘텐츠 기반의 유사성을 통해 추천을 주기 위해  movies_metadata의 개봉 연도를 뽑아내고 이것을 사용자의 rating과 매칭 했다.

 

 

def user_release_ratio(df, usernumber):
    user_df =df[df['userId'] == usernumber]
    meta2 = pd.read_csv('../content/drive/My Drive/data/the-movies-dataset/movies_metadata.csv',low_memory=False)
    value_meta = meta2[['id','original_title','release_date', 'genres']]

    value_meta = value_meta.rename(columns={'id':'movieId'})
    value_meta.movieId = pd.to_numeric(value_meta.movieId, errors='coerce')
    value_meta = value_meta.dropna(axis=0)
    value_meta = value_meta.reset_index()
    merge_data = pd.merge(user_df, value_meta, on='movieId', how='left')
    merge_data = merge_data.dropna(axis=0)
    merge_data = merge_data.reset_index()

그걸 merge를 통해 두 데이터를 합쳤고 결측치를 처리한 뒤 reset_index로 index를 초기화해주었다.

 

 

    release_date_list = {'1900':0,'1950':0,'1960':0,'1970':0,'1980':0,'1990':0,'2000':0,'2010':0,'2020':0}
    for i in range(0,len(merge_data)):
        if int(merge_data['release_date'].loc[i][0:4]) <= 1900:
            release_date_list["1900"] += 1
        elif int(merge_data['release_date'].loc[i][0:4]) <= 1950:
            release_date_list["1950"] += 1
        elif int(merge_data['release_date'].loc[i][0:4]) <= 1960:
            release_date_list["1960"] += 1
        elif int(merge_data['release_date'].loc[i][0:4]) <= 1970:
            release_date_list["1970"] += 1
        elif int(merge_data['release_date'].loc[i][0:4]) <= 1980:
            release_date_list["1980"] += 1
        elif int(merge_data['release_date'].loc[i][0:4]) <= 1990:
            release_date_list["1990"] += 1
        elif int(merge_data['release_date'].loc[i][0:4]) <= 2000:
            release_date_list["2000"] += 1
        elif int(merge_data['release_date'].loc[i][0:4]) <= 2010:
            release_date_list["2010"] += 1
        elif int(merge_data['release_date'].loc[i][0:4]) <= 2020:
            release_date_list["2020"] += 1
    release_date_list

그다음에는 dict 형태로 값을 만들었다. 그 뒤 사용자가 평점을 줬던 영화의 개봉 연도에 따라 값을 추가해주는 방식으로 만들었다.

 

 

    sum = 0
    for i in release_date_list:

        sum += release_date_list[i]

    release_date_rate = []
    for i in release_date_list:
        if release_date_list[i] ==0:
            continue
        release_date_list[i] = round((release_date_list[i]/sum),3)
    return release_date_list

그다음은 해당 값들의 총합을 구하고 그 총합을 각 년도를 나눠 해당 연도의 비율을 구하였다.

 

 

def Estimate_Score_sum1(user_df, user_release_ratio_list):
    user_df = user_df.dropna(axis=0)
    for i in range(0,len(user_df)):
        if int(user_df.iloc[i]['release_date'][0:4]) <= 1900:
            user_df['Estimate_Score'].loc[user_df.index[i]] = user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['1900']
        elif int(user_df.iloc[i]['release_date'][0:4]) <= 1950:
            user_df['Estimate_Score'].loc[user_df.index[i]]= user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['1950']
        elif int(user_df.iloc[i]['release_date'][0:4]) <= 1960:
            user_df['Estimate_Score'].loc[user_df.index[i]] = user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['1960']
        elif int(user_df.iloc[i]['release_date'][0:4]) <= 1970:
            user_df['Estimate_Score'].loc[user_df.index[i]] = user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['1970']
        elif int(user_df.iloc[i]['release_date'][0:4]) <= 1980:
            user_df['Estimate_Score'].loc[user_df.index[i]] = user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['1980']
        elif int(user_df.iloc[i]['release_date'][0:4]) <= 1990:
            user_df['Estimate_Score'].loc[user_df.index[i]] = user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['1990']
        elif int(user_df.iloc[i]['release_date'][0:4])  <= 2000:
            user_df['Estimate_Score'].loc[user_df.index[i]] = user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['2000']
        elif int(user_df.iloc[i]['release_date'][0:4])  <= 2010:
            user_df['Estimate_Score'].loc[user_df.index[i]] = user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['2010']
        elif int(user_df.iloc[i]['release_date'][0:4])  <= 2020:
            user_df['Estimate_Score'].loc[user_df.index[i]] = user_df.iloc[i]['Estimate_Score'] + user_release_ratio_list['2020']
    return user_df

이전에 구한 비율을 통해 영화 추천을 할 때 측정치 + 변수에 따른 가중치를 더해 추천을 해줬다.

 

이 함수와 더불어 추천을 해주는 함수를 만들었다. 

 

def variable_weight(data,usernumber,rating,moviedata,dropdata,reader,algo):
    df = data
    df_user = df[(df['userId'] == usernumber) & (df['rating'] == rating)]
    df_user = df_user.set_index('movieId')
    df_user = df_user.join(moviedata)['original_title']
    # print(df_user)
    user_release_ratio_list = user_release_ratio(df, usernumber) # 유저의 년도 비율을 가져온다.
    user_pop_ratio_list = user_pop_ratio(df, usernumber) # 유저의 popularity 비율을 가져온다.
    user_language_ratio_list = user_language_ratio(df, usernumber) # 유저의 language 비율을 가져온다.

    user_df = moviedata.copy()
    user_df = user_df[~user_df['movieId'].isin(dropdata)]
    data1 = Dataset.load_from_df(df[['userId', 'movieId', 'rating']], reader)
    trainset = data1.build_full_trainset()
    algo.fit(trainset)
    user_df['Estimate_Score'] = user_df['movieId'].apply(lambda x: algo.predict(usernumber, x).est)
    user_df = user_df.sort_values('Estimate_Score', ascending=False)

    user_df_sum = Estimate_Score_sum1(user_df, user_release_ratio_list)
    user_df_total = Estimate_Score_sum1(user_df, user_release_ratio_list)
    user_df_sum_relase = user_df_sum.sort_values('Estimate_Score', ascending=False)
    print("개봉일 별 가중치")
    print(user_df_sum_relase.head(10))


    return user_df_sum_relase
user_df_sum_relase = variable_weight(df,665,5,meta,drop_movie_list,reader,svd)

 

 

s 여기서 여러 가지 매개변수를 통해 함수를 구성하였다.

 

data - df ( 사용자들의 리뷰 데이터 )

 

usernumber - 사용자의 userid

 

rating - 평점이 몇 점일 때 여기서는 5점으로 주었다.

 

moviedata - 영화 메타 데이터를 넣어주었다.

 

 dropdata - 사용하지 않을 영화 리스트를 뽑아서 따로 빼주었다.

 

reader  - reader라는 함수를 넣어줬다. 

 

svd - 알고리즘 명 

 

이렇게 값을 넣고 간단한 전처리를 하고 svd를 돌린다. 

 

SVD 알고리즘을 우리가 가지고 있는 trainset으로 fit을 한다. 이 작업을 해야 각 알고리즘이 우리가 가지 데이터에 맞춰서 잘 작동하게 된다. 그다음 이 SVD를 통해 예측을 한다.

 

여기서는 유저가 가지고 있는 movieid를 넣었을 때 svd를 통해 예측을 하고 그 값을 user_df에서 estimate_score라는 새로운 칼럼을 만든다. 그다음 sort_values를 통해 정렬을 해준다.

 

이렇게 나온 결과에 내가 만든 개봉일 별 가중치를 더해 측정치를 만들어준다. 

 

 

그 결과가 아래의 영화 추천 리스트 결과이다.

 


# 알고리즘에 따른 추천

 

reader = Reader()
data = Dataset.load_from_df(df[['userId', 'movieId', 'rating']], reader)
svd = SVD()
slope = SlopeOne()
nmf = NMF()

bsl_options = {'method': 'als',
'n_epochs': 5,
'reg_u': 12,
'reg_i': 5
}
als = BaselineOnly(bsl_options=bsl_options)

als_result = cross_validate(als, data, measures=['RMSE', 'MAE'],cv=5, verbose=False) #evaluate 대신 cross_validate
slope_result = cross_validate(slope, data, measures=['RMSE', 'MAE'],cv=5, verbose=False) #evaluate 대신 cross_validate
svd_result = cross_validate(svd, data, cv = 5,  measures=['RMSE', 'MAE']) #evaluate 대신 cross_validate사용
nmf_result = cross_validate(nmf, data, measures=['RMSE', 'MAE'],cv=5, verbose=False) #evaluate 대신 cross_validate

여기서는 다양한 알고리즘을 사용하게 된다. svd, slope, nmf, ALS 이렇게 4가지를 사용하였다. als를 구현하는 부분은 내가 했던 부분이 아니라 소스를 따로 올리지는 않겠다. 이 ALS를 제외한 나머지 알고리즘들은 surprise에 있는 함수들로 저런 식으로 사용하면 된다. 알고리즘에 대한 자세한 설명은 생략하겠다.

 

각 알고리즘에 대한 성능을 보기 위해 RMSE와 MAE를 구하였다. 이것을 구할 때 cross_validate를 사용하여 교차검증을 진행한다. 이것을 하는 이유는 알고리즘이 train과 test를 잡는 부분에 따라 성능이 높게 나올 수 도 있고 과적합 될 가능성이 있다. 이것을 방지하기 위해 여기서는 5번의 교차 검증을 하였다. 

 

여기서 RMSE와 MAE를 사용한 이유는 MSE(평균 제곱 오차)가 수학적 분석이 쉬우며 계산하기 쉽기 때문이다. RMSE는 MAE에 제곱근을 씌운 것이다.

 

이렇게 각 알고리즘의 성능을 비교할 수 있다.

이것을 print 하면 

이런 식의 값이 나온다. 이 값의 평균을 보면 된다.

 

 

이제 성능을 봤으니 각각의 알고리즘을 통한 추천을 하기로 한다.

 

 

def userRec3(data,usernumber,rating,moviedata,dropdata,reader,algo):
    df = data
    df_user = df[(df['userId'] == usernumber) & (df['rating'] == rating)]
    df_user = df_user.set_index('movieId')
    df_user = df_user.join(moviedata)['original_title']

    user_release_ratio_list = user_release_ratio(df, usernumber) # 유저의 년도 비율을 가져온다.
    user_pop_ratio_list = user_pop_ratio(df, usernumber) # 유저의 popularity 비율을 가져온다.
    user_language_ratio_list = user_language_ratio(df, usernumber) # 유저의 language 비율을 가져온다.

    user_df = moviedata.copy()
    user_df = user_df[~user_df['movieId'].isin(dropdata)]
    data1 = Dataset.load_from_df(df[['userId', 'movieId', 'rating']], reader)
    trainset = data1.build_full_trainset()
    algo.fit(trainset)
    user_df['Estimate_Score'] = user_df['movieId'].apply(lambda x: algo.predict(usernumber, x).est)
    user_df = user_df.sort_values('Estimate_Score', ascending=False)


    return user_df
als_recomend = userRec3(df,665,5,meta,drop_movie_list,reader,als)
svd_recomend = userRec3(df,665,5,meta,drop_movie_list,reader,svd)
slope_recomend = userRec3(df,665,5,meta,drop_movie_list,reader,slope)
nmf_recomend = userRec3(df,665,5,meta,drop_movie_list,reader,nmf)

나머지 매개변수는 같게 하고 마지막에 함수만 수정하여 함수가 돌아가게 했다.

 

 

print(als_recomend)
print(svd_recomend)
print(slope_recomend)
print(nmf_recomend)

해당 값을 결과를 출력하게 되면 

 

 

 

 

이렇게 각 알고리즘 순서대로 결과가 나오게 된다. 각 알고리즘마다 다른 영화들을 추천해주는 것을 볼 수 있다.

반응형