카카오 추천팀에서 제공하는 Mini Reco문제 회고를 작성해보겠습니다.
카카오에 지원하여 푼 문제는 아니고 카카오 추천팀 깃허브에서 제공하는 문제를 풀어보았습니다.
Mini Reco 문제의 링크는 아래와 같습니다.
https://github.com/kakao/recoteam/tree/master/programming_assignments/mini_reco
GitHub - kakao/recoteam: 카카오 추천팀 공개 리포지토리입니다.
카카오 추천팀 공개 리포지토리입니다. Contribute to kakao/recoteam development by creating an account on GitHub.
github.com
문제는 이웃 기반(메모리 기반) 협업 필터링 중 사용자 기반 협업 필터링을 표준라이브러리만 사용하여 알고리즘을 작성해 해결하라는 것이었습니다.
깃허브링크 기준 testcase폴더에 input, output이 존재하며 input에 따라 적절한 output을 도출하여 ndcg라는 평가 지표에서 0.9점 이상을 획득하면 통과하는 것이 기준이었습니다.
자세한 것은 해당 링크를 확인해보시면 좋을 것 같습니다.
추천팀에서는 표준라이브러리만을 사용하여 해결하라고 했지만 저는 표준라이브러리만 사용한 경우와 외부 라이브러리를 사용한 경우 2가지 경우를 작성해보았습니다.
그리고 testcase를 입력받고 정답과 비교하는 과정에서 불편함을 느껴 자동으로 testcase의 txt 파일을 입력받는 함수를 작성하여 진행하였습니다.
TestCase 입력
import os
import pandas as pd
def auto_testcase(number, type_="Dict"):
number = str(number)
base_input_path = os.getcwd()+'/drive/MyDrive/kakao/testcase/input'
base_output_path = os.getcwd()+'/drive/MyDrive/kakao/testcase/output'
input_file = f'input00{number}.txt'
output_file = f'output00{number}.txt'
#input
input_path = os.path.join(base_input_path, input_file)
f = open(input_path, 'r')
lines = f.readlines()
num_sim_user_top_N = int(lines[0].strip())
num_item_rec_top_N = int(lines[1].strip())
num_users = int(lines[2].strip())
num_items = int(lines[3].strip())
num_rows = int(lines[4].strip())
#matrix 초기화
if type_ == 'Dict':
matrix = {}
for u in range(1,num_users+1):
matrix[u] = {}
for i in range(1, num_items+1):
matrix[u][i] = None
for line in lines[5:num_rows+5]:
data = line.strip().split()
u = int(data[0])
i = int(data[1])
rating = float(data[2])
matrix[u][i] = rating
elif type_ == 'DataFrame':
matrix = pd.DataFrame()
for line in lines[5:num_rows+5]:
data = line.strip().split()
u = int(data[0])
i = int(data[1])
rating = float(data[2])
matrix.loc[u, i] = rating
num_reco_users = lines[num_rows+5]
active_users = []
for line in lines[num_rows+6:]:
user = int(line.strip())
active_users.append(user)
f.close()
# output
output_path = os.path.join(base_output_path, output_file)
f = open(output_path, 'r')
lines = f.readlines()
gts = []
for line in lines:
gt = list(map(int,line.strip().split()))
gts.append(gt)
f.close()
return matrix, num_users, num_items, num_sim_user_top_N, num_item_rec_top_N, active_users, gts
testcase의 번호만 입력하면 여러 입력 데이터(matrix, num_users, num_items...)와 정답(gts)을 반환하는 함수입니다.
표준 라이브러리
표준 라이브러리만 사용하여 문제를 해결한 경우에서는 데이터의 입력을 받을 때 pandas 모듈을 사용할 수 없었기 때문에
딕셔너리 자료구조를 사용하여 데이터의 입력을 받았습니다.
#matrix 초기화
if type_ == 'Dict':
matrix = {}
for u in range(1,num_users+1):
matrix[u] = {}
for i in range(1, num_items+1):
matrix[u][i] = None
for line in lines[5:num_rows+5]:
data = line.strip().split()
u = int(data[0])
i = int(data[1])
rating = float(data[2])
matrix[u][i] = rating
입력받는 데이터는 희소 행렬의 형태이기 때문에 모든 사용자와 아이템의 쌍이 완벽하게 존재하는 것은 아닙니다.
특정 사용자가 특정 아이템에 평가를 하지 않은 경우에는 None으로 초기화 하였고, 평가를 한 경우에는 평가값을 부여하여
matrix를 구성하였습니다.
표준라이브러리를 사용하여 작성한 코드는 다음과 같습니다.
class Model():
def __init__(self, matrix, num_users, num_items, num_sim_user_top_N, num_item_rec_top_N):
self.R = matrix
self.num_users = num_users
self.num_items = num_items
self.num_sim_user_top_N = num_sim_user_top_N
self.num_item_rec_top_N = num_item_rec_top_N
self.simil_matrix = self._get_simil_matrix()
self.null_item_matrix = self._get_null_item_matrix()
self.mean_matrix = self._get_mean_matrix()
self.U = self._get_U()
def _get_Ixy(self, x, y):
"""
사용자 x, y 모두가 평가한 아이템 집합을 구하는 함수
"""
Ixy = set()
for i in range(1, self.num_items+1):
if self.R[x][i] != None and self.R[y][i] != None:
Ixy.add(i)
return Ixy
def _get_I(self, u):
"""
사용자(u)가 평가한 아이템 집합을 구하는 함수
"""
I = set()
for i in range(1, self.num_items+1):
if self.R[u][i] != None:
I.add(i)
return I
def _simil(self, x, y):
"""
사용자 x, y의 코사인 유사도를 구하는 함수
"""
Ixy = self._get_Ixy(x, y)
Ix = self._get_I(x)
Iy = self._get_I(y)
rxi2s = []
ryi2s = []
numerator = []
for i in Ixy:
rxi = self.R[x][i]
ryi = self.R[y][i]
numerator.append((rxi*ryi))
for i in Ix:
rxi = self.R[x][i]
rxi2s.append((rxi ** 2))
for i in Iy:
ryi = self.R[y][i]
ryi2s.append((ryi ** 2))
numerator = sum(numerator)
denominator = (sum(rxi2s)**0.5) * (sum(ryi2s) ** 0.5)
#Cosine similarity 계산이 정의되지 않는 경우
#zero division error가 나올때
if denominator == 0:
return 0
else:
return (numerator / denominator)
def _get_simil_matrix(self):
"""
코사인 유사도 매트릭스를 구하는 함수
"""
simil_matrix = {}
for x in range(1,self.num_users+1):
simil_matrix[x] = {}
for y in range(1, self.num_users+1):
simil_matrix[x][y] = None
for x in range(1, self.num_users+1):
for y in range(1, self.num_users+1):
if x == y:
simil_matrix[x][y] = 1
else:
simil_matrix[x][y] = self._simil(x, y)
return simil_matrix
def _get_null_item_matrix(self):
"""
사용자가 아직 평가하지 않은 아이템 매트릭스를 구하는 함수
"""
matrix = {}
for u in range(1,self.num_users+1):
matrix[u] = None
for u in range(1, self.num_users+1):
items = set()
for i in range(1, self.num_items+1):
if self.R[u][i] == None:
items.add(i)
matrix[u] = items
return matrix
def _get_mean_matrix(self):
"""
유저의 평균 매트릭스를 구하는 함수
"""
matrix = {}
for u in range(1, self.num_users+1):
values = []
for i in range(1, self.num_items+1):
if self.R[u][i] == None:
pass
else:
values.append(self.R[u][i])
mean = sum(values) / len(values)
matrix[u] = mean
return matrix
def _get_U(self):
"""
아이템 i 를 평가한 사용자 u와 가장 유사한 상위 N명의 사용자 집합(피어그룹) U 매트릭스를 구하는 함수
"""
matrix = {}
for u in range(1, self.num_users+1):
peergroup = []
sorted_simil_matrix = sorted(self.simil_matrix[u].items(), key = lambda x : x[1], reverse=True)
#자기자신 제외
for i in range(1, len(sorted_simil_matrix)):
user = sorted_simil_matrix[i][0]
peergroup.append(user)
matrix[u] = peergroup[:self.num_sim_user_top_N]
return matrix
def _get_rui(self, u, i):
"""
예측값(rui)를 구하는 함수
"""
ru = self.mean_matrix[u]
k = 0
sum_value = 0
for u_prime in self.U[u]:
r_uprime_i = self.R[u_prime][i]
r_uprime_bar = self.mean_matrix[u_prime]
# 유사 사용자가 평가를 하지 않은 경우 제외
if r_uprime_i == None:
continue
sum_value += self.simil_matrix[u][u_prime] * (r_uprime_i - r_uprime_bar)
k += abs(self.simil_matrix[u][u_prime])
return ru + (1/k) * sum_value if k > 0.0 else 0.0
def recommend(self,u):
"""
사용자가 아직 평가하지 않은 아이템(null_items)에 대하여
예측값(rui)를 구하여 top N 아이템 만큼 추천
"""
null_items = self.null_item_matrix[u]
recommendation = []
result = []
for i in null_items:
rui = self._get_rui(u, i)
result.append([i, rui])
result.sort(key=lambda x : x[1], reverse=True)
recommendation = [item for item, rui in result[:self.num_item_rec_top_N]]
return recommendation
생성자(__init__)
생성자 함수부터 확인해보겠습니다.
def __init__(self, matrix, num_users, num_items, num_sim_user_top_N, num_item_rec_top_N):
self.R = matrix
self.num_users = num_users
self.num_items = num_items
self.num_sim_user_top_N = num_sim_user_top_N
self.num_item_rec_top_N = num_item_rec_top_N
self.simil_matrix = self._get_simil_matrix()
self.null_item_matrix = self._get_null_item_matrix()
self.mean_matrix = self._get_mean_matrix()
self.U = self._get_U()
self.R ~ self.num_item_rec_top_N은 주어진 입력을 그대로 받습니다. 이렇게 입력받은 데이터들은 class내부에서 재사용됩니다.
아래는 여러 값들을 matrix형태로 만들어 필요시에 계산하지 않고 미리 계산해둔 값을 꺼내어 사용하기 위하여 만들었습니다.
- self.simil_matrix : 각 사용자와 사용자간의 코사인 유사도를 구하고 저장한 matrix입니다.
- self.null_item_matrix : 각 사용자의 평점을 부여하지 않은 아이템(None)을 저장하고 있는 matrix입니다.
- self.mean_matrix : 각 사용자의 평균값을 저장한 matrix입니다.
- self.U : 각 사용자의 최근접 이웃(피어그룹)을 저장한 matrix입니다.
코사인 유사도
문제에 위의 수식(코사인유사도)을 구현하는 방법에 대하여 이야기하겠습니다.
위의 생성자 파트에서 보았던 self.simil_matrix는 _get_simil_matrix()함수를 호출하여 만들어집니다.
_get_simil_matrix()
simil_matrix = {}
for x in range(1,self.num_users+1):
simil_matrix[x] = {}
for y in range(1, self.num_users+1):
simil_matrix[x][y] = None
for x in range(1, self.num_users+1):
for y in range(1, self.num_users+1):
if x == y:
simil_matrix[x][y] = 1
else:
simil_matrix[x][y] = self._simil(x, y)
return simil_matrix
_get_simil_matrix() 함수는 전체 사용자를 돌면서 self._simil(x, y)를 호출하여 사용자와 사용자간의 유사도를 저장합니다.
확인해야하는 사용자가 자기 자신이라면 1의 값을 부여하고 아닌경우에는 self._simil(x, y)를 호출합니다.
_simil(x,y)
def _simil(self, x, y):
"""
사용자 x, y의 코사인 유사도를 구하는 함수
"""
Ixy = self._get_Ixy(x, y)
Ix = self._get_I(x)
Iy = self._get_I(y)
rxi2s = []
ryi2s = []
numerator = []
for i in Ixy:
rxi = self.R[x][i]
ryi = self.R[y][i]
numerator.append((rxi*ryi))
for i in Ix:
rxi = self.R[x][i]
rxi2s.append((rxi ** 2))
for i in Iy:
ryi = self.R[y][i]
ryi2s.append((ryi ** 2))
numerator = sum(numerator)
denominator = (sum(rxi2s)**0.5) * (sum(ryi2s) ** 0.5)
#Cosine similarity 계산이 정의되지 않는 경우
#zero division error가 나올때
if denominator == 0:
return 0
else:
return (numerator / denominator)
_simil(x,y)함수는 주어진 x 사용자 y 사용자의 코사인 유사도를 계산하는 함수입니다.
먼저 함수가 호출이 되면
Ixy, Ix, Iy의 값을 각 _get_Ixy(), _get_I()를 호출하여 구하게 됩니다.
Ixy, Ix, Iy는 빨간 테두리 부분에 해당됩니다.
Ixy : 사용자 x, y 모두가 평가한 아이템 집합
Ix : 사용자 x 가 평가한 아이템 집합
Iy : 사용자 y 가 평가한 아이템 집합
코드는 생략하겠습니다.
식에서 반복해야 하는 아이템집합들을 찾았으니 값을 구하여 분자, 분모를 알맞게 계산해야 합니다.
먼저 식에서의 분자(numerator) 부분을 구하는 파트입니다.
numerator = []
for i in Ixy:
rxi = self.R[x][i]
ryi = self.R[y][i]
numerator.append((rxi*ryi))
numerator = sum(numerator)
사용자 x, y 모두가 평가한 아이템집합(Ixy)를 반복하면서
rxi : 사용자 x 가 아이템 i에 평가한 평가값
ryi : 사용자 y 가 아이템 i에 평가한 평가값
위의 rxi와 ryi를 찾아 곱해준 값을 numerator리스트에 모두 저장하고 sum()함수를 통하여 모두 더해줍니다.
분모(denominator)파트는 다음과 같습니다.
rxi2s = []
ryi2s = []
for i in Ix:
rxi = self.R[x][i]
rxi2s.append((rxi ** 2))
for i in Iy:
ryi = self.R[y][i]
ryi2s.append((ryi ** 2))
denominator = (sum(rxi2s)**0.5) * (sum(ryi2s) ** 0.5)
사용자 x, y가 평가한 아이템 집합을 돌면서 rxi, ryi를 구하고 제곱(**2)하여 rxi2s, ryi2s 리스트에 저장합니다.
두 리스트를 각각 모두 더하고 제곱근(**0.5)를 취하고 나온 두 수를 곱하여 분모(denominator)를 만들어줍니다.
그리고 마지막으로 이렇게 만든 분모(denominator)가 0이 나와 코사인유사도가 계산이 되지 않을 수 있기 때문에 그런 경우 0을 주어줍니다. 해당 사항은 문제링크에 참고사항에 해당합니다.
#Cosine similarity 계산이 정의되지 않는 경우
#zero division error가 나올때
if denominator == 0:
return 0
else:
return (numerator / denominator)
이렇게 하면 식(2)에 해당하는 모든 부분은 구하였습니다.
최근접 이웃(U)
다음은 최근접 이웃(U)와 self.U를 구하는 방법에 대하여 이야기해보겠습니다.
where U denotes the set of top N users that are most similar to user u who rated item i.
U는 아이템 i를 평가한 사용자 u와 가자 유사한 상위 N명의 사용자 집합을 의미합니다.
이러한 U를 구하기 위하여 위에서 구한 코사인유사도를 이용하게 됩니다.
def _get_U(self):
"""
아이템 i 를 평가한 사용자 u와 가장 유사한 상위 N명의 사용자 집합(피어그룹) U 매트릭스를 구하는 함수
"""
matrix = {}
for u in range(1, self.num_users+1):
peergroup = []
sorted_simil_matrix = sorted(self.simil_matrix[u].items(), key = lambda x : x[1], reverse=True)
#자기자신 제외
for i in range(1, len(sorted_simil_matrix)):
user = sorted_simil_matrix[i][0]
peergroup.append(user)
matrix[u] = peergroup[:self.num_sim_user_top_N]
return matrix
코드는 위와 같습니다.
먼저 모든 사용자를 반복하면서 사용자의 코사인 유사도가 높은 순서를 기준으로 정렬을 하고 자기 자신을 제외한 사용자들을 모두 모아 저장합니다.
이때 self.num_sim_user_top_N 만큼의 사용자의 수만 저장하여 최근접 이웃(U)를 만들어줍니다.
따라서 self.U는 모든 사용자의 최근접 이웃이 저장이 됩니다.
예측과 추천
사용자 기반 협업 필터링은 아이템을 추천할 때 사용자가 아직 평가하지 않은 아이템에 대하여 평점의 예측값을 구하고 그 예측값이 높은 순서대로 N만큼의 아이템을 추천하는 방식입니다.
recommend(u)
def recommend(self,u):
"""
사용자가 아직 평가하지 않은 아이템(null_items)에 대하여
예측값(rui)를 구하여 top N 아이템 만큼 추천
"""
null_items = self.null_item_matrix[u]
recommendation = []
result = []
for i in null_items:
rui = self._get_rui(u, i)
result.append([i, rui])
result.sort(key=lambda x : x[1], reverse=True)
recommendation = [item for item, rui in result[:self.num_item_rec_top_N]]
return recommendation
recommend함수는 다음과 같은 과정을 거쳐 아이템을 추천합니다.
1. 사용자가 아직 평가하지 않은 아이템집합을 찾음
2. 해당 아이템집합을 반복하면서 예측값(rui)를 구함
3. 예측값이 높은 순서대로 N(self.num_item_rec_top_N)만큼 아이템을 추천
예측값 구하기 : _get_rui(u, i)
def _get_rui(self, u, i):
"""
예측값(rui)를 구하는 함수
"""
ru = self.mean_matrix[u]
k = 0
sum_value = 0
for u_prime in self.U[u]:
r_uprime_i = self.R[u_prime][i]
r_uprime_bar = self.mean_matrix[u_prime]
# 유사 사용자가 평가를 하지 않은 경우 제외
if r_uprime_i == None:
continue
sum_value += self.simil_matrix[u][u_prime] * (r_uprime_i - r_uprime_bar)
k += abs(self.simil_matrix[u][u_prime])
return ru + (1/k) * sum_value if k > 0.0 else 0.0
식(1)에 해당하는 부분을 구하는 것이고 코드는 위와 같습니다.
식(1)에서 ru(유저의 평균)를 구하기 위하여 미리 만들어놓은 self.mean_matrix에서 값을 불러와줍니다.
k와 "가중치 * 평점"을 구하기 위하여 아래와 같이 초기화 해줍니다.
k = 0
sum_value = 0
특정 사용자의 최근접 이웃들을 반복하면서
최근접 이웃이 부여한 아이템의 평점과 최근접 이웃의 평균을 불러와줍니다.
for u_prime in self.U[u]:
r_uprime_i = self.R[u_prime][i]
r_uprime_bar = self.mean_matrix[u_prime]
만약 최근접이웃이 부여한 아이템의 평점을 구할 수 없다면 즉, 최근접 이웃이 평가를 하지 않은 경우에는 해당 사용자의 순서를 건너뛰어 줍니다.
# 유사 사용자가 평가를 하지 않은 경우 제외
if r_uprime_i == None:
continue
평가를 한 경우에는 최근접 이웃과 특정 사용자의 코사인유사도를 구하고 최근접 이웃이 부여한 아이템의 평점과 최근접 이웃의 평균을 빼서 sum_value에 더해주고 k 또한 마찬가지로 코사인유사도에 절대값을 취하여 k에 더해줍니다.
sum_value += self.simil_matrix[u][u_prime] * (r_uprime_i - r_uprime_bar)
k += abs(self.simil_matrix[u][u_prime])
모든 반복문이 종료되면 식(1)를 계산해줍니다.
-식(1) : 유저의 평균(ru) + (1 / k) * sum_value
return ru + (1/k) * sum_value if k > 0.0 else 0.0
이때 k가 계산이 되지 않을 수 있기 때문에 그런 경우 코사인유사도와 마찬가지로 0을 부여합니다. 참고사항에 해당합니다.
이렇게 아이템과 아이템의 예측 평점을 모두 구하고 예측값이 높은 순서대로 N(self.num_item_rec_top_N)만큼 아이템을 추천을 하면 문제를 해결하게 됩니다.
지금까지는 표준라이브러리만을 사용한 경우였습니다.
외부 라이브러리
외부 라이브러리를 사용해서 만든 코드는 아래와 같고 이전 추천시스템 포스팅에서 사용한 코드를 변형하였습니다.
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
class UserBaseCF():
def __init__(self, table, num_users, num_items, num_sim_user_top_N, num_item_rec_top_N):
"""
R : 원본테이블
S : 원본테이블에서 평균값을 뺀 테이블
Cosine : 사용자-사용자 유사도
U : 피어그룹(상관계수가 0보다 크고 자기자신이 아님)
"""
self.R = table
self.num_users = num_users
self.num_items = num_items
self.num_sim_user_top_N = num_sim_user_top_N
self.num_item_rec_top_N = num_item_rec_top_N
self.S = self._get_S()
self.Cosine = self._consine_similarity()
def _consine_similarity(self):
temp = self.R.copy().fillna(0)
similarity = cosine_similarity(temp,temp)
matrix = pd.DataFrame(similarity, index=self.R.index, columns=self.R.index)
return matrix
def _get_U(self, user_id):
return self.Cosine.loc[user_id].sort_values(ascending=False)[1:self.num_sim_user_top_N+1].index
def _get_S(self):
"""
S 테이블을 만드는 함수
"""
Mu = self.R.mean(axis=1)
return (self.R.T - Mu).T
def _find_item(self,user_id):
"""
유저에게 추천할 수 있는 아이템을 찾는 함수
"""
#유저가 아직 평가하지 않은 아이템
user_null_items = set(self.R.loc[user_id, self.R.loc[user_id].isna()].index)
return user_null_items
def recommend(self, user_id):
"""
사용자가 아직 평가하지 않은 아이템을 찾고
해당 아이템에 대하여 평점을 예측한 뒤 평점이 높은 순으로 인덱스를 반환하는 함수
"""
#추천할 수 있는 아이템을 찾음
recom_items = self._find_item(user_id)
#유저의 평균
Mu = self.R.mean(axis=1).loc[user_id]
#피어그룹
U = self._get_U(user_id)
#계산
result = []
for item in recom_items:
ratings = 0
k = 0
for u_prime in U:
if np.isnan(self.S.loc[u_prime, item]):
continue
ratings += self.Cosine.loc[user_id, u_prime] * self.S.loc[u_prime, item]
k += abs(self.Cosine.loc[user_id, u_prime])
rui = Mu + (1 / k) * ratings if k > 0.0 else 0.0
result.append([item, rui])
result.sort(key=lambda x : x[1], reverse=True)
recommendation = [item for item, rui in result[:self.num_item_rec_top_N]]
return recommendation
최대한 pandas와 numpy 라이브러리의 힘을 빌려서 작성하였습니다.
코사인 유사도와 최근접이웃(U) 구할 때는 sklearn의 cosine_similariy 모듈을 활용하였습니다.
설명은 표준라이브러리를 사용한 방식과 유사함으로 생략하도록 하겠습니다.
문제 발생 회고(self feedbck)
수식을 코드로 구현하고 정답과 비교하여 평가를 진행하는데 여러 testcase에서 ndcg의 값이 0.6~0.7 정도로 기준점수(0.9)를 통과하지 못하는 문제가 발생하였습니다.
여러 시도 끝에 혼자 해결할 수 없다고 판단하여 카카오 추천팀에 정답 코드를 공유해 줄 수 있는지 문의하였고, 정말 감사하게도 추천팀에서 정답코드를 공유해주어 제가 작성한 코드에서 잘 못 작성한 부분을 발견할 수 있었습니다.
위의 수식에서 normalizing factor k(첫 번째 밑줄 부분)를 구하는 과정에서 발생한 문제였습니다.
제가 작성한 코드에서는 "가중치(특정 사용자와 최근접 이웃의 코사인 유사도) * 평점(최근접 이웃의 특정 아이템에 대한 평가값)"(두번째 밑줄)을 구할 때 평점을 구할 수 없다면 해당 최근접 이웃을 제외하여 값을 올바르게 구했지만 normalizing factor k(첫번째 밑줄)을 구할 때는 최근접 이웃을 제외하지 않았고 예측값(rui)를 구할 때에도 k가 영향을 주어 output이 달라 생기는 문제가 발생한 것입니다.
"가중치 x 평점"을 구할 때는 평점(유사 사용자가 특정 아이템에 대한 평가값)이 None으로 반환되게 코드를 작성하여 오류를 피하려고 작성한 코드가 운이 좋게 최근접이웃을 제외하였던 것입니다.
수식에서 각 항이 어떤 의미를 갖는지에 대한 고민이 부족하여 문제가 발생한 것으로 생각됩니다.
최근접 이웃이라고 하더라도 아이템에 대해 평점을 부여하지 않았다면 예측값을 계산하는 과정에서 제외해야 한다는 것을 배울 수 있었고, 각 항이 어떤 의미를 갖는지에 대해 생각해봐야 한다는 것을 깨달을 수 있었습니다.
'추천시스템' 카테고리의 다른 글
[추천시스템] (잠재요인) 모델 기반 협업 필터링(1) - 경사하강법(SGD) (0) | 2023.01.10 |
---|---|
[추천시스템] 이웃 기반 협업 필터링(2) - 아이템 기반 (0) | 2023.01.03 |
[추천시스템] 이웃 기반 협업필터링 (1) - 사용자 기반 (0) | 2022.12.23 |
[추천시스템] 평점의 종류 (0) | 2022.11.29 |
[추천시스템] 기본 협업 필터링 모델 (0) | 2022.11.21 |