KNN을 2가지 방법으로 구현하겠다. 참고한 출처는 맨밑에 있다. (팀블로그 :데이터 맛집 에도 게시한 내용입니다.)
1. sklearn패키지 이용
2. 코드 구현
1. sklearn패키지 사용
Import & Data
df는 rating, movie 데이터로 만든 데이터 프레임이다. rating을 하나도 못받은 영화의 경우, df_rating movie id가 존재하지 않기 때문에 df 데이터프레임을 만들었다.
1
2
3
4
5
6
7
8
9
10
|
import pandas as pd
import numpy as np
import glob
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import time
df_ratings = pd.read_csv( ("../../../../Papers/MovieLens 100K/data/ratings.csv"))
df_movies = pd.read_csv(("../../../../Papers/MovieLens 100K/data/movies.csv"))
df= pd.merge(df_ratings.drop('timestamp', axis=1), df_movies.drop('genres', axis=1), how='outer', on='movieId') [['movieId','userId','rating']].sort_values(by=['movieId']).fillna(0)
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs |
Data Filtering
explicit feedbacks은 매우 희박하고, 평점이 있는 영화들은 대부분 인기많은 영화들이다. 따라서 long tail과 같은 분포를 따른다.
또한, sparse rating을 준 user나 item들은 매우 민감도가 높아 nosiy한 패턴을 보일 수 있다.
따라서, noisy pattern을 없애기 위해 대용량 데이터셋으로 이뤄져 있는 인기 많은 영화로만 필터링 한다.
필터링 후, rating 데이터에는 13500편의 영화가 남게 되므로 추천 모델로 충분하다.
Modeling
그렇다면 KNN의 rating은 어떻게 매길것인가?
- 적절한 format으로 바꿔야 한다.
우리는 데이터를 MxN형식으로 matrix를 만들고(M: movies 수, N: User 수) 결측치(rating)에는 0을 채운다.
또, sparse data가 많기 때문에 scipy sparse matrix를 사용해 memory 용량을 효율적으로 사용하겠다.
1
2
3
4
5
6
7
8
9
10
11
|
from scipy.sparse import csr_matrix
# pivot ratings into movie features
df_movie_features = df_ratings.pivot(
index='movieId',
columns='userId',
values='rating'
).fillna(0)
# convert dataframe of movie features to scipy sparse matrix
mat_movie_features = csr_matrix(df_movie_features.values)
|
Model 적용
1
2
3
|
from sklearn.neighbors import NearestNeighbors
model_knn = NearestNeighbors(metric='cosine', algorithm='brute', n_neighbors=20, n_jobs=-1)
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs |
2. KNN 구현
필요 Data 만들기
장르/ 인기 유사도로 KNN을 구현했다. 다른 feature로도 유사도를 만들 수 있다.
인기 유사도를 측정하기 위해 movieProperties, movieNormalizedNumRatings를 만들었다.
-movieProperties: rating의 수, rating평균으로 이뤄져 있다.
-movieNormalizedNumRatings: rating의 수를 minmax sclar하여 정규화했다.
1
2
3
|
movieProperties = df.groupby('movieId').agg({'rating': [np.size, np.mean]}
movieNumRatings = pd.DataFrame(movieProperties['rating']['size'])
movieNormalizedNumRatings = movieNumRatings.apply(lambda x: (x - np.min(x)) / (np.max(x) - np.min(x)))
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs |
그다음 movieDict을 만들었다.
movieDict는 [영화이름, 장르 리스트, 정규화된 rating 수, 평균 rating 점수]로 이루어져있다.
1
2
3
4
5
6
7
|
movieDict = {}
for index, row in df_movies.iterrows():
movieID = int(row['movieId'])
name = row['title']
genres =list(row[2:])
movieDict[movieID] = (name, np.array(list(genres)), movieNormalizedNumRatings.loc[movieID].get('size'), movieProperties.loc[movieID].rating.get('mean'))
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs |
유사도 측정
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from scipy import spatial
# 장르, 인기도의 consine 유사도 적용
def ComputeDistance(a, b):
genresA = a[1]
genresB = b[1]
genreDistance = spatial.distance.cosine(genresA, genresB)
popularityA = a[2]
popularityB = b[2]
popularityDistance = abs(popularityA - popularityB)
return genreDistance + popularityDistance
ComputeDistance(movieDict[1], movieDict[4])
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs |
위의 코드를 실행하면 유사도 1.4가 나온다. 즉, 각각의 영화에 대해서 유사도를 뽑아낼 수 있다.
코드 실행
- getNeighbors: movie ID가 들어오면, 자기자신을 제외한 모든 영화들과의 ditance를 잰다. 그 후 K개만큼 neighbors들을 reture한다.
- recommend: 원하는 movie ID와 K를 입력하면 K개 만큼의 추천 영화를 뽑고, 평균(예상) rating을 구한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
import operator
# neighbors 출력
def getNeighbors(movieID, K):
distances = []
for movie in movieDict:
# 같은 movie가 아닐때만 movie distance를 구함
if (movie != movieID):
dist = ComputeDistance(movieDict[movieID], movieDict[movie])
# movie distance를 sort시켜주어 가장 가까운 영화들을 추천
neighbors = []
for x in range(K):
return neighbors
# 최종 추천
def recommend(movieID,K):
avgRating = 0
print(movieDict[movieID], '\n')
neighbors = getNeighbors(movieID, K) # Toy Story (1995)
for neighbor in neighbors:
# neigbor의 평균 rating을 더해줌
avgRating += movieDict[neighbor][3]
print (movieDict[neighbor][0] + " " + str(movieDict[neighbor][3]))
avgRating /= K
print("평균 Rating: ",avgRating)
recommend(1,10)
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs |
movieID에 1(toy story)를 넣고, k=10으로 잡았을 때 아래와 같은 결과가 나온다.
출처
https://towardsdatascience.com/prototyping-a-recommender-system-step-by-step-part-1-knn-item-based-collaborative-filtering-637969614ea
https://hendra-herviawan.github.io/Movie-Recommendation-based-on-KNN-K-Nearest-Neighbors.html
'인공지능 > 스터디' 카테고리의 다른 글
Adaboost(에이다부스트) (1) | 2020.04.16 |
---|---|
SVM(Support Vector Machine)(1) (2) | 2020.04.15 |
최대우도추정 (0) | 2020.04.14 |
Bias-Variance, underfitting-overfitting trade off (0) | 2020.03.23 |
[CB Filtering] 2. 키워드 수집 (0) | 2020.03.23 |