返回列表

9th place solution🥇 [single model LB 0.602]

532. OTTO – Multi-Objective Recommender System | otto-recommender-system

开始: 2022-11-01 结束: 2023-01-31 商品推荐 数据算法赛
第9名解决方案 [单模型 LB 0.602]

第9名解决方案🥇 [单模型 LB 0.602]

作者:Apolo | 比赛:OTTO Recommender System

首先,感谢竞赛主办方举办了一场精彩的比赛。
我非常高兴能赢得我的第一枚金牌🥇

以下是我的解决方案!p>

概览

最佳单模型

Orders CV LB
0.67087 0.602

(我还没有计算所有 orders/carts/clicks 组合的 CV 分数。)

可能和许多其他参赛者一样,我使用了候选生成 & 重排序的方法。

候选生成

平均每个 session 选择了 180 个候选。(orders recall: 0.725

  • 14 种模式的基于物品的协同过滤
  • 2 种模式的基于用户的协同过滤
  • 重访

将由 14 种基于物品的 CF 模式和 2 种基于用户的 CF 模式生成的候选汇总,创建最终的候选集。

由于测试数据中包含更多 aids 的 session 往往在预测期内有更多的事件,因此为测试数据中包含更多 aids 的 session 生成了更多的候选。

df = test_df[test_df["session"].isin(target_session)].groupby("session")["aid"].count().reset_index()
df.columns = ["session", "count"]
df["count"] = (df["count"]**0.5*10).astype("int32")
df["aid"] = df.progress_apply(lambda row:list(dict(rec[row["session"]].most_common(row["count"])).keys()),axis=1)
df = df.explode(["aid"])
df = df[df["aid"].notnull()].reset_index(drop=True)

特征工程

总共创建了 226 个特征

  • 基于物品的 CF 特征

首先,创建了 59 种模式的基于物品的 CF。

例) 模式1

def get_aid_similarity1(df, topk=200):
    session_info = df.drop_duplicates(["session", "aid"], keep="last")\
                     .groupby("session", as_index=False)[["aid", "type", "ts"]].agg(list) 
    
    aid_similarity = {}
    for session, aids, tps, tss in tqdm(zip(session_info["session"],
                                            session_info["aid"],
                                            session_info["type"],
                                            session_info["ts"]),
                                        total=len(session_info)):
        for aid1, tp1, ts1 in zip(aids, tps, tss):
            session_length = math.sqrt(len(aids))
            aid_similarity.setdefault(aid1, Counter())
            for aid2, tp2, ts2 in zip(aids, tps, tss):
                if (aid1 == aid2):
                    continue
                aid_similarity[aid1][aid2] += (1/session_length)
    
    # 只保留 topK 以节省时间和内存
    for aid1, aid2_dict in tqdm(aid_similarity.items()):
        relations = dict(aid2_dict.most_common(topk))
        # 归一化
        if len(relations) == 0:
            continue
        max_num = relations[max(relations, key=relations.get)]
        if max_num == 0:
            continue
        aid_similarity[aid1] = {k: v / max_num for k, v in relations.items()}

    del session_info; gc_clear()
    return aid_similarity

例) 模式2

def make_real_session(df, hours=2):
    df["lag"] = df["ts"] - df.groupby("session")["ts"].shift(1)
    df["real_session"] = (df["lag"] > 1000*60*60*hours).astype('int8').fillna(0)
    df["real_session"] = df.groupby("session")["real_session"].cumsum()
    del df["lag"]; gc_clear()
    return df

def get_aid_similarity2(df, topk=200):

    df = make_real_session(df, hours=4)
    session_info = df.groupby(["session", "real_session"], as_index=False)[["aid", "type", "ts"]].agg(list) 
    
    aid_similarity = {}
    aid_cnt = defaultdict(int)
    for session, real_session, aids, tps, tss in tqdm(zip(session_info["session"],
                                                          session_info["real_session"],
                                                          session_info["aid"],
                                                          session_info["type"],
                                                          session_info["ts"]),
                                                      total=len(session_info)):
        for aid1, tp1, ts1 in zip(aids, tps, tss):
            aid_similarity.setdefault(aid1, Counter())
            for aid2, tp2, ts2 in zip(aids, tps, tss):
                if (abs(ts1-ts2)>24*60*60*1000) or (aid1 == aid2):
                    continue
                aid_cnt[aid1] += 1
                if min(tp1, tp2)==0:
                    aid_similarity[aid1][aid2] += 1