零基礎入門推薦系統-【排序模型+模型融合】

一棵二叉樹發表於2020-12-06

排序模型
通過召回的操作, 我們已經進行了問題規模的縮減, 對於每個使用者, 選擇出了N篇文章作為了候選集,並基於召回的候選集構建了與使用者歷史相關的特徵,以及使用者本身的屬性特徵,文章本省的屬性特徵,以及使用者與文章之間的特徵,下面就是使用機器學習模型來對構造好的特徵進行學習,然後對測試集進行預測,得到測試集中的每個候選集使用者點選的概率,返回點選概率最大的topk個文章,作為最終的結果。

排序階段選擇了三個比較有代表性的排序模型,它們分別是:

LGB的排序模型
LGB的分類模型
深度學習的分類模型DIN
得到了最終的排序模型輸出的結果之後,還選擇了兩種比較經典的模型整合的方法:

輸出結果加權融合
Staking(將模型的輸出結果再使用一個簡單模型進行預測)
導包

import numpy as np
import pandas as pd
import pickle
from tqdm import tqdm
import gc, os
import time
from datetime import datetime
import lightgbm as lgb
from sklearn.preprocessing import MinMaxScaler
import warnings
warnings.filterwarnings('ignore')
data_path = './data_raw/'
save_path = './temp_results/'
offline = False
# 重新讀取資料的時候,發現click_article_id是一個浮點數,所以將其轉換成int型別
trn_user_item_feats_df = pd.read_csv(save_path + 'trn_user_item_feats_df.csv')
trn_user_item_feats_df['click_article_id'] = trn_user_item_feats_df['click_article_id'].astype(int)

if offline:
    val_user_item_feats_df = pd.read_csv(save_path + 'val_user_item_feats_df.csv')
    val_user_item_feats_df['click_article_id'] = val_user_item_feats_df['click_article_id'].astype(int)
else:
    val_user_item_feats_df = None
    
tst_user_item_feats_df = pd.read_csv(save_path + 'tst_user_item_feats_df.csv')
tst_user_item_feats_df['click_article_id'] = tst_user_item_feats_df['click_article_id'].astype(int)

# 做特徵的時候為了方便,給測試集也打上了一個無效的標籤,這裡直接刪掉就行
del tst_user_item_feats_df['label']

返回排序後的結果

def submit(recall_df, topk=5, model_name=None):
    recall_df = recall_df.sort_values(by=['user_id', 'pred_score'])
    recall_df['rank'] = recall_df.groupby(['user_id'])['pred_score'].rank(ascending=False, method='first')
    
    # 判斷是不是每個使用者都有5篇文章及以上
    tmp = recall_df.groupby('user_id').apply(lambda x: x['rank'].max())
    assert tmp.min() >= topk
    
    del recall_df['pred_score']
    submit = recall_df[recall_df['rank'] <= topk].set_index(['user_id', 'rank']).unstack(-1).reset_index()
    
    submit.columns = [int(col) if isinstance(col, int) else col for col in submit.columns.droplevel(0)]
    # 按照提交格式定義列名
    submit = submit.rename(columns={'': 'user_id', 1: 'article_1', 2: 'article_2', 
                                                  3: 'article_3', 4: 'article_4', 5: 'article_5'})
    
    save_name = save_path + model_name + '_' + datetime.today().strftime('%m-%d') + '.csv'
    submit.to_csv(save_name, index=False, header=True)

注意:
1.pandas rank()函式用法:
rank函式返回從小到大排序的下標

(1)預設情況下,rank是通過“為各組分配一個平均排名”的方式破壞平級關係的

In [120]:obj = pd.Series([7,-5,7,4,2,0,4])
In [121]:obj.rank()
Out [121]:
0    6.5
1    1.0
2    6.5
3    4.5
4    3.0
5    2.0
6    4.5
dtype: float64

(2)根據值在原資料中出現的順序排名

In [122]:obj.rank(method='first')
Out [122]:
0    6.0
1    1.0
2    7.0
3    4.0
4    3.0
5    2.0
6    5.0
dtype: float64

(3)按降序進行排名

In [123]:obj.rank(ascending=False, method='max')
Out [123]:
0    2.0
1    7.0
2    2.0
3    4.0
4    5.0
5    6.0
6    4.0
dtype: float64

(4)若對DataFrame進行排序,則可根據axis指定要進行排序的軸

In [136]: frame=pd.DataFrame({'b':[5,7,-3,2],'a':[0,1,0,1],'c':[-2,5,8,-3]})

In [137]: frame
Out[137]:
   a  b  c
0  0  5 -2
1  1  7  5
2  0 -3  8
3  1  2 -3

In [138]: frame.rank(axis=0)
Out[138]:
     a    b    c
0  1.5  3.0  2.0
1  3.5  4.0  3.0
2  1.5  1.0  4.0
3  3.5  2.0  1.0

In [139]: frame.rank(axis=1)
Out[139]:
     a    b    c
0  2.0  3.0  1.0
1  1.0  3.0  2.0
2  2.0  1.0  3.0
3  2.0  3.0  1.0

以上對rank()函式解說的來自:https://blog.csdn.net/starter_____/article/details/79183595?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160723992119195271668286%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=160723992119195271668286&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-3-79183595.nonecase&utm_term=rank&spm=1018.2118.3001.4449

# 排序結果歸一化
def norm_sim(sim_df, weight=0.0):
    # print(sim_df.head())
    min_sim = sim_df.min()
    max_sim = sim_df.max()
    if max_sim == min_sim:
        sim_df = sim_df.apply(lambda sim: 1.0)
    else:
        sim_df = sim_df.apply(lambda sim: 1.0 * (sim - min_sim) / (max_sim - min_sim))

    sim_df = sim_df.apply(lambda sim: sim + weight)  # plus one
    return sim_df

相關文章