241126 TIL #550 AI Tech #83 Top K 추천 앙상블

김춘복·2024년 11월 26일
0

TIL : Today I Learned

목록 보기
552/575

Today I Learned

top k 방식에서 앙상블 방법을 어떻게 할지에 대해 고민했다.


Top K 추천 앙상블

  • 유저 1명당 10개를 추천하는 문제에서 각 모델들의 결과를 어떻게 앙상블해서 최종 제출로 만들 것인가에 대해 고민했다.

hard voting

  1. 각 모델의 결과를 취합해서 중복으로 많이 뽑힌 아이템을 우선적으로 추천하고 없는 경우 가장 점수가 높은 모델에서 나머지를 채우는 방식
  • EASE, RecVAE, RaCT 에서 각 모델 당 top 15를 뽑아 겹치는 픽들을 우선적으로 추천
    -> publice Recall@10 : 0.1466

  • EASE, RecVAE, RaCT 에서 각 모델 당 top 20를 뽑아 겹치는 픽들을 우선적으로 추천
    -> publice Recall@10 : 0.1467

  • top 20의 경우 거의 72%가 세 모델에서 겹쳐서 같은 오토인코더 계열 모델끼리는 앙상블 하는게 크게 의미가 없어보였다.
    model_count
    3 227421
    2 85882
    1 297

  1. 각 모델의 결과에 랭킹을 부여해 위는 1점, 20위는 0.05점 처럼 점수를 매겨 점수의 합이 높을수록(순위의 합이 낮을수록) 좋은 아이템으로 보고 추천하는 방식
  • EASE, RecVAE, RaCT, BERT4Rec, LightGCN 다섯 모델을 이 방식으로 진행
    -> publice Recall@10 : 0.1508
import pandas as pd
from collections import defaultdict
from tqdm import tqdm

dataframe_list = [ease, recvae, ract, bert, lgcn]
user_list = dataframe_list[0]['user'].unique()
result = []
tbar = tqdm(user_list, desc='Rank Aggregation with Normalization')
for user in tbar:
    rank_sum = defaultdict(float)
    for df_idx, df in enumerate(dataframe_list):
        items = df[df['user'] == user]['item'].values
        max_rank = min(len(items), 20)
        for rank_idx in range(max_rank):
            # 순위를 정규화하여 합산 (1위는 1, 20위는 0.05)
            normalized_rank = (max_rank - rank_idx) / max_rank
            rank_sum[items[rank_idx]] += normalized_rank
    # 순위의 합이 낮을수록 좋은 아이템 선택
    top_items = sorted(rank_sum.items(), key=lambda x: x[1], reverse=True)[:10]
    for item, _ in top_items:
        result.append((user, item))
submission = pd.DataFrame(result, columns=['user', 'item'])

Soft Voting

하드보팅과 다르게 모델에서 결과물을 낼 때 score를 같이 내서 그걸 더해 앙상블 하는 방식

  • 각 모델 별 score의 편차가 너무 커서 정규화 후 더하는 방식으로 진행.
ease['score_normalized'] = ease.groupby('user')['score'].transform(lambda x: (x - x.mean()) / x.std())
recvae['score_normalized'] = recvae.groupby('user')['score'].transform(lambda x: (x - x.mean()) / x.std())
ract['score_normalized'] = ract.groupby('user')['score'].transform(lambda x: (x - x.mean()) / x.std())

# 세 모델의 정규화된 점수를 합산
merged_df = pd.concat([
    ease[['user', 'item', 'score_normalized']],
    recvae[['user', 'item', 'score_normalized']],
    ract[['user', 'item', 'score_normalized']]
])

# 유저-아이템 쌍별로 정규화된 점수 합산
ensemble_df = merged_df.groupby(['user', 'item'])['score_normalized'].sum().reset_index()

# 유저별로 상위 10개 아이템 선택
final_df = ensemble_df.sort_values(['user', 'score_normalized'], ascending=[True, False]) \
    .groupby('user').head(10) \
    .reset_index(drop=True)
profile
Backend Dev / Data Engineer

0개의 댓글