制作特征和標簽, 轉成監督學習問題
我們先捋一下基于原始的給定數據, 有哪些特征可以直接利用:
- 文章的自身特征, category_id表示這文章的類型, created_at_ts表示文章建立的時間, 這個關系著文章的時效性, words_count是文章的字數, 一般字數太長我們不太喜歡點擊, 也不排除有人就喜歡讀長文。
- 文章的內容embedding特征, 這個召回的時候用過, 這里可以選擇使用, 也可以選擇不用, 也可以嘗試其他類型的embedding特征, 比如W2V等
- 用戶的設備特征信息
上面這些直接可以用的特征, 待做完特征工程之后, 直接就可以根據article_id或者是user_id把這些特征加入進去。 但是我們需要先基于召回的結果, 構造一些特征,然后制作標簽,形成一個監督學習的數據集。
構造監督數據集的思路, 根據召回結果, 我們會得到一個{user_id: [可能點擊的文章列表]}形式的字典。 那么我們就可以對于每個用戶, 每篇可能點擊的文章構造一個監督測試集, 比如對于用戶user1, 假設得到的他的召回列表{user1: [item1, item2, item3]}, 我們就可以得到三行數據(user1, item1), (user1, item2), (user1, item3)的形式, 這就是監督測試集時候的前兩列特征。
構造特征的思路是這樣, 我們知道每個用戶的點擊文章是與其歷史點擊的文章信息是有很大關聯的, 比如同一個主題, 相似等等。 所以特征構造這塊很重要的一系列特征是要結合用戶的歷史點擊文章信息。我們已經得到了每個用戶及點擊候選文章的兩列的一個數據集, 而我們的目的是要預測最后一次點擊的文章, 比較自然的一個思路就是和其最后幾次點擊的文章產生關系, 這樣既考慮了其歷史點擊文章信息, 又得離最后一次點擊較近,因為新聞很大的一個特點就是注重時效性。 往往用戶的最后一次點擊會和其最后幾次點擊有很大的關聯。 所以我們就可以對于每個候選文章, 做出與最后幾次點擊相關的特征如下:
- 候選item與最后幾次點擊的相似性特征(embedding內積) --- 這個直接關聯用戶歷史行為
- 候選item與最后幾次點擊的相似性特征的統計特征 --- 統計特征可以減少一些波動和異常
- 候選item與最后幾次點擊文章的字數差的特征 --- 可以通過字數看用戶偏好
- 候選item與最后幾次點擊的文章建立的時間差特征 --- 時間差特征可以看出該用戶對于文章的實時性的偏好
還需要考慮一下 5. 如果使用了youtube召回的話, 我們還可以制作用戶與候選item的相似特征
當然, 上面只是提供了一種基于用戶歷史行為做特征工程的思路, 大家也可以思維風暴一下,嘗試一些其他的特征。 下面我們就實現上面的這些特征的制作, 下面的邏輯是這樣:
- 我們首先獲得用戶的最后一次點擊操作和用戶的歷史點擊, 這個基于我們的日志數據集做
- 基于用戶的歷史行為制作特征, 這個會用到用戶的歷史點擊表, 最后的召回列表, 文章的信息表和embedding向量
- 制作標簽, 形成最后的監督學習數據集
返回多路召回列表或者單路召回
def get_recall_list(save_path, single_recall_model=None, multi_recall=False):
if multi_recall:
return pickle.load(open(save_path + 'final_recall_items_dict.pkl', 'rb'))
if single_recall_model == 'i2i_itemcf':
return pickle.load(open(save_path + 'itemcf_recall_dict.pkl', 'rb'))
elif single_recall_model == 'i2i_emb_itemcf':
return pickle.load(open(save_path + 'itemcf_emb_dict.pkl', 'rb'))
elif single_recall_model == 'user_cf':
return pickle.load(open(save_path + 'youtubednn_usercf_dict.pkl', 'rb'))
elif single_recall_model == 'youtubednn':
return pickle.load(open(save_path + 'youtube_u2i_dict.pkl', 'rb'))
def trian_item_word2vec(click_df, embed_size=64, save_name='item_w2v_emb.pkl', split_char=' '):
click_df = click_df.sort_values('click_timestamp')
# 只有轉換成字符串才可以進行訓練
click_df['click_article_id'] = click_df['click_article_id'].astype(str)
# 轉換成句子的形式
docs = click_df.groupby(['user_id'])['click_article_id'].apply(lambda x: list(x)).reset_index()
docs = docs['click_article_id'].values.tolist()
# 為了方便查看訓練的進度,這里設定一個log信息
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', level=logging.INFO)
# 這里的參數對訓練得到的向量影響也很大,默認負采樣為5
w2v = Word2Vec(docs, size=16, sg=1, window=5, seed=2020, workers=24, min_count=1, iter=1)
# 保存成字典的形式
item_w2v_emb_dict = {k: w2v[k] for k in click_df['click_article_id']}
pickle.dump(item_w2v_emb_dict, open(save_path + 'item_w2v_emb.pkl', 'wb'))
return item_w2v_emb_