top k 방식에서 앙상블 방법을 어떻게 할지에 대해 고민했다.
- 각 모델의 결과를 취합해서 중복으로 많이 뽑힌 아이템을 우선적으로 추천하고 없는 경우 가장 점수가 높은 모델에서 나머지를 채우는 방식
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점, 20위는 0.05점 처럼 점수를 매겨 점수의 합이 높을수록(순위의 합이 낮을수록) 좋은 아이템으로 보고 추천하는 방식
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'])
하드보팅과 다르게 모델에서 결과물을 낼 때 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)