제가 얼마전에 추천시스템 만드는 과제를 수행하면서 공부했던 ALS 모델의 라이브러리 Implicit의 소스코드를 보면서 소개해 보겠습니다.
* 받은 과제는 고객의 클릭로그 기반 데이터로 협업필터링 모델을 작성하는 것이었습니다.
ALS모델을 처음 공부한건 T-아카데미의 유튜브 영상이었습니다.
https://www.youtube.com/watch?v=TFbTU9VG3is&t=1848s
위 영상을 보시면 SGD가 무엇이고, ALS가 무엇인지 쉽게 알 수 있습니다.
Implicit 라이브러리의 소스코드를 좀 더 자세히 분해해서 어떤 식으로 모델이 학습되고 어떤식으로 추천을하는지 공유하겠습니다. 소스코드의 깃허브는 https://github.com/benfred/implicit 에 있습니다.
from implicit.als import AlternatingLeastSquares as ALS
als_model = ALS(factors=20, regularization=0.08, iterations = 20)
als_model.fit(click_sparse.T)
위 코드를 통해서 ALS 모델을 import 하고 AlternatingLeastSquares 클래스를 생성한뒤, fit함수로 모델을 학습하는 과정을 보겠습니다.
AlternatingLeastSquares 함수 -> implicit.cpu(gpu).als.AlternatingLeastSquares 클래스
AlternatingLeastSquares 클래스는 MatrixFactorizationBase를 상속받고,
MatrixFactorizationBase은 fit, recommend, rank_item 함수를 갖고 있는 RecommderBase 추상 클래스를 상속받습니다.
AlternatingLeastSquares의 fit로 학습을 진행하는 과정을 보겠습니다.
def fit(self, item_users, show_progress=True):
random_state = check_random_state(self.random_state)
Ciu = item_users
if Ciu.dtype != np.float32:
Ciu = Ciu.astype(np.float32)
s = time.time()
Cui = Ciu.T.tocsr()
items, users = Ciu.shape
s = time.time()
if self.user_factors is None:
self.user_factors = random_state.rand(users, self.factors).astype(self.dtype) * 0.01
if self.item_factors is None:
self.item_factors = random_state.rand(items, self.factors).astype(self.dtype) * 0.01
self._item_norms = self._user_norms = None
self._YtY = None
self._XtX = None
solver = self.solver
여기까지의 코드를 보겠습니다.
- item_users라는 sparse matrix를 받아서 Ciu라는 변수명으로 선언하고, 이의 전치행렬을 Cui로 선언합니다.
- 각각의 행과 열의 갯수를 items과 users라는 변수로 저장해 놓습니다.
- Class 선언시 초기값은 None이기 때문에, user_factors와 item_factors의 초기값을 (user, factor), (items, factor) 크기의 랜덤 matrix를 만듭니다.
- factor의 default 값은 100이지만 일반적으로는 20으로 설정합니다.
- item_norms, user_norms, YtY, XtX 변수를 선언하고, class에 있는 solver 함수를 불러옵니다.
with tqdm(total=self.iterations, disable=not show_progress) as progress:
for iteration in range(self.iterations):
solver(
Cui,
self.user_factors,
self.item_factors,
self.regularization,
num_threads=self.num_threads,
)
solver(
Ciu,
self.item_factors,
self.user_factors,
self.regularization,
num_threads=self.num_threads,
)
progress.update(1)
if self.calculate_training_loss:
loss = _als.calculate_loss(
Cui,
self.user_factors,
self.item_factors,
self.regularization,
num_threads=self.num_threads,
)
progress.set_postfix({"loss": loss})
- solver 함수를 이용해서 item_factor과 user_factor값을 한번씩 고정해서 최적화를 합니다.
- 이를 tqdm 라이브러리를 이용해서 진행상황을 업데이트합니다.
- itertion값이 15이기 때문에 15번 학습을 진행하고 최적화된 item_factor와 user_factor를 구할 수 있습니다.
추천 상품을 받을 때 als_model.recommend(item, click_sparse)로 사용하여, MatrixFactorizationBase에 있는 recommend 함수를 불러오게 됩니다.
def recommend(
self,
userid,
user_items,
N=10,
filter_already_liked_items=True,
filter_items=None,
recalculate_user=False,
):
ids, scores = self._knn.topk(self.item_factors, self.user_factors[userid], count)
return list(
itertools.islice((rec for rec in zip(ids[0], scores[0]) if rec[0] not in liked), N)
)
recommend 함수의 경우 userid와 가장 연관성이 높은 userid를 implicit.gpu.KnnQuery().topk 함수로 추천하게 됩니다. KnnQuery는 cython으로 작성된 코드이고, 간단하게 설명하자면 user_factor matrix에서 코사인 유사도가 높은 user를 채택하는 방식입니다.
하지만 저희가 처음 학습을 돌릴때 sparse_matrix에 전치를 하고 학습을 한다면 userid가 itemid가 되고, recommend에서는 itemid를 넣었을 때 가장 유사한 itemid를 추천함으로써 상품에 따라 연관된 상품을 추천할 수 있게됩니다.
data = df[["user_id", "content_id"]] data['rate'] = 1 user2idx = {} idx2user = {} for idx, user in enumerate(df['user_id'].unique()): user2idx[user] = idx idx2user[idx] = user content2idx = {} idx2content = {} for idx, content in enumerate(df['content_id'].unique()): content2idx[content] = idx idx2content[idx] = content useridx = data['user_id'].apply(lambda x: user2idx[x]).values contentidx = data['content_id'].apply(lambda x: content2idx[x]).values rating = data['rate'].values als_model = ALS(factors=20, regularization=0.08, iterations = 20) als_model.fit(click_sparse.T) als_model.recommend(2, click_sparse)[0:10]
solver부분에 대한 설명은 코드가 너무 복잡해서 간략하게 넘어갔는데 혹시 더 자세히 알고싶으신 내용이 있으시면, 댓글라 질문 달아주세요. 처음으로 소스코드 분해를 해봤는데, 생각보다 오래걸렸습니다. 좋은 경험인거 같다는 생각이 들고, 여러분들도 소스코드를 보면 하나씩 해석하면서 코드를 이해하면 좋을 것 같다는 생각을 합니다.
'ML | DL' 카테고리의 다른 글
AutoML_Alex 라이브러리 설명 (0) | 2021.07.15 |
---|---|
RandomForest, XGBoost, LGBM, CatBoost뭐가 다를까? (1) | 2021.06.09 |
신용카드 사용자 연체 예측 AI 경진대회 (0) | 2021.05.27 |
프로그래머스 Dev-Matching 머신러닝 개발자 - 아직 공부가 부족하다 (0) | 2021.05.25 |
댓글