1. 程式人生 > >[論文解讀] 阿里DIEN整體程式碼結構

[論文解讀] 阿里DIEN整體程式碼結構

# [論文解讀] 阿里DIEN整體程式碼結構 [toc] ## 0x00 摘要 DIEN是阿里深度興趣進化網路(Deep Interest Evolution Network)的縮寫。 本文將分析DIEN原始碼整體思路。因為DIEN是在DIN基礎上演化,所以程式碼有大部分重複。 本文采用的是 https://github.com/mouna99/dien 中的實現。 ## 0x01 檔案簡介 資料檔案主要包括: - **uid_voc.pkl**:使用者字典,使用者名稱對應的id; - **mid_voc.pkl**:movie字典,item對應的id; - **cat_voc.pkl**:種類字典,category對應的id; - **item-info**:item對應的category資訊; - **reviews-info**:review 元資料,格式為:userID,itemID,評分,時間戳,用於進行負取樣的資料; - **local_train_splitByUser**:訓練資料,一行格式為:label、使用者名稱、目標item、 目標item類別、歷史item、歷史item對應類別; - **local_test_splitByUser**:測試資料,格式同訓練資料; 程式碼主要包含: - **rnn.py**:對tensorflow中原始的rnn進行修改,目的是將attention同rnn進行結合 - **vecAttGruCell.py**: 對GRU原始碼進行修改,將attention加入其中,設計AUGRU結構 - **data_iterator.py**: 資料迭代器,用於資料的不斷輸入 - **utils.py**:一些輔助函式,如dice啟用函式、attention score計算等 - **model.py**:DIEN模型檔案 - **train.py**:模型的入口,用於訓練資料、儲存模型和測試資料 ## 0x02 總體架構 首先還是要從論文中摘取架構圖進行說明。 ![](https://img2020.cnblogs.com/blog/1850883/202011/1850883-20201101203713603-1401210995.jpg) 深度興趣進化網路分為幾層,從下到上依次是: - 行為序列層(Behavior Layer):主要作用是將使用者瀏覽過的商品轉換成對應的embedding,並且按照瀏覽時間做排序,即把原始的id類行為序列特徵轉換成Embedding行為序列; - 興趣抽取層(Interest Extractor Layer):主要作用是通過模擬使用者的興趣遷移過程,基於行為序列提取使用者興趣序列; - 興趣進化層(Interest Evolving Layer):主要作用是通過在興趣抽取層基礎上加入Attention機制,模擬與當前目標廣告相關的興趣進化過程,對與目標物品相關的興趣演化過程進行建模; - 將興趣表示和ad、user profile、context的embedding向量進行拼接。最後使用MLP完成最後的預測; ## 0x03 總體程式碼 DIEN程式碼是從train.py開始。train.py 先用初始模型評估一遍測試集,然後呼叫 train: - 獲取 訓練資料 和 測試資料,這兩個都是資料迭代器,用於資料的不斷輸入 - 根據 model_type 生成相應的model - 按照batch訓練,每1000次評估測試集。 程式碼如下: ```python def train( train_file = "local_train_splitByUser", test_file = "local_test_splitByUser", uid_voc = "uid_voc.pkl", mid_voc = "mid_voc.pkl", cat_voc = "cat_voc.pkl", batch_size = 128, maxlen = 100, test_iter = 100, save_iter = 100, model_type = 'DNN', seed = 2, ): with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess: ## 訓練資料 train_data = DataIterator(train_file, uid_voc, mid_voc, cat_voc, batch_size, maxlen, shuffle_each_epoch=False) ## 測試資料 test_data = DataIterator(test_file, uid_voc, mid_voc, cat_voc, batch_size, maxlen) n_uid, n_mid, n_cat = train_data.get_n() ...... elif model_type == 'DIEN': model = Model_DIN_V2_Gru_Vec_attGru_Neg(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) ...... sess.run(tf.global_variables_initializer()) sess.run(tf.local_variables_initializer()) iter = 0 lr = 0.001 for itr in range(3): loss_sum = 0.0 accuracy_sum = 0. aux_loss_sum = 0. for src, tgt in train_data: uids, mids, cats, mid_his, cat_his, mid_mask, target, sl, noclk_mids, noclk_cats = prepare_data(src, tgt, maxlen, return_neg=True) loss, acc, aux_loss = model.train(sess, [uids, mids, cats, mid_his, cat_his, mid_mask, target, sl, lr, noclk_mids, noclk_cats]) loss_sum += loss accuracy_sum += acc aux_loss_sum += aux_loss iter += 1 if (iter % test_iter) == 0: eval(sess, test_data, model, best_model_path) loss_sum = 0.0 accuracy_sum = 0.0 aux_loss_sum = 0.0 if (iter % save_iter) == 0: model.save(sess, model_path+"--"+str(iter)) lr *= 0.5 ``` ## 0x04 模型基類 模型的基類是 Model,其建構函式`__init__`可以理解為 行為序列層(Behavior Layer):主要作用是將使用者瀏覽過的商品轉換成對應的embedding,並且按照瀏覽時間做排序,即把原始的id類行為序列特徵轉換成Embedding行為序列。 ### 4.1 基本邏輯 基本邏輯如下: - 在 'Inputs' scope下,構建各種 placeholder 變數; - 在 'Embedding_layer' scope下,構建user, item的embedding lookup table,將輸入資料轉換為對應的embedding; - 把 各種 embedding vector 結合起來,比如將item的id對應的embedding 以及 item對應的cateid的embedding進行拼接,共同作為item的embedding; ### 4.2 模組分析 下面的 B 是 batch size,T 是序列長度,H 是hidden size,程式中初始化變數如下: ```python EMBEDDING_DIM = 18 HIDDEN_SIZE = 18 * 2 ATTENTION_SIZE = 18 * 2 best_auc = 0.0 ``` #### 4.2.1 構建變數 首先是構建placeholder變數。 ```python with tf.name_scope('Inputs'): # shape: [B, T] #使用者行為特徵(User Behavior)中的 movie id 歷史行為序列。T為序列長度 self.mid_his_batch_ph = tf.placeholder(tf.int32, [None, None], name='mid_his_batch_ph') # shape: [B, T] #使用者行為特徵(User Behavior)中的 category id 歷史行為序列。T為序列長度 self.cat_his_batch_ph = tf.placeholder(tf.int32, [None, None], name='cat_his_batch_ph') # shape: [B], user id 序列。 (B:batch size) self.uid_batch_ph = tf.placeholder(tf.int32, [None, ], name='uid_batch_ph') # shape: [B], movie id 序列。 (B:batch size) self.mid_batch_ph = tf.placeholder(tf.int32, [None, ], name='mid_batch_ph') # shape: [B], category id 序列。 (B:batch size) self.cat_batch_ph = tf.placeholder(tf.int32, [None, ], name='cat_batch_ph') self.mask = tf.placeholder(tf.float32, [None, None], name='mask') # shape: [B]; sl:sequence length,User Behavior中序列的真實序列長度(?) self.seq_len_ph = tf.placeholder(tf.int32, [None], name='seq_len_ph') # shape: [B, T], y: 目標節點對應的 label 序列, 正樣本對應 1, 負樣本對應 0 self.target_ph = tf.placeholder(tf.float32, [None, None], name='target_ph') # 學習速率 self.lr = tf.placeholder(tf.float64, []) self.use_negsampling =use_negsampling if use_negsampling: self.noclk_mid_batch_ph = tf.placeholder(tf.int32, [None, None, None], name='noclk_mid_batch_ph') #generate 3 item IDs from negative sampling. self.noclk_cat_batch_ph = tf.placeholder(tf.int32, [None, None, None], name='noclk_cat_batch_ph') ``` 具體各種shape可以參見下面執行時變數 ```python self = {Model_DIN_V2_Gru_Vec_attGru_Neg} cat_batch_ph = {Tensor} Tensor("Inputs/cat_batch_ph:0", shape=(?,), dtype=int32) uid_batch_ph = {Tensor} Tensor("Inputs/uid_batch_ph:0", shape=(?,), dtype=int32) mid_batch_ph = {Tensor} Tensor("Inputs/mid_batch_ph:0", shape=(?,), dtype=int32) cat_his_batch_ph = {Tensor} Tensor("Inputs/cat_his_batch_ph:0", shape=(?, ?), dtype=int32) mid_his_batch_ph = {Tensor} Tensor("Inputs/mid_his_batch_ph:0", shape=(?, ?), dtype=int32) lr = {Tensor} Tensor("Inputs/Placeholder:0", shape=(), dtype=float64) mask = {Tensor} Tensor("Inputs/mask:0", shape=(?, ?), dtype=float32) seq_len_ph = {Tensor} Tensor("Inputs/seq_len_ph:0", shape=(?,), dtype=int32) target_ph = {Tensor} Tensor("Inputs/target_ph:0", shape=(?, ?), dtype=float32) noclk_cat_batch_ph = {Tensor} Tensor("Inputs/noclk_cat_batch_ph:0", shape=(?, ?, ?), dtype=int32) noclk_mid_batch_ph = {Tensor} Tensor("Inputs/noclk_mid_batch_ph:0", shape=(?, ?, ?), dtype=int32) use_negsampling = {bool} True ``` #### 4.2.2 構建embedding 然後是構建user, item的embedding lookup table,將輸入資料轉換為對應的embedding,就是把稀疏特徵轉換為稠密特徵。關於 embedding 層的原理和程式碼分析,本系列會有專文講解
。 後續的 U 是user_id的hash bucket size,I 是item_id的hash bucket size,C 是cat_id的hash bucket size。 注意 self.mid_his_batch_ph這樣的變數 儲存使用者的歷史行為序列, 大小為 [B, T],所以在進行 embedding_lookup 時,輸出大小為 [B, T, H/2]; ```python # Embedding layer with tf.name_scope('Embedding_layer'): # shape: [U, H/2], user_id的embedding weight. U是user_id的hash bucket size,即user count self.uid_embeddings_var = tf.get_variable("uid_embedding_var", [n_uid, EMBEDDING_DIM]) # 從uid embedding weight 中取出 uid embedding vector self.uid_batch_embedded = tf.nn.embedding_lookup(self.uid_embeddings_var, self.uid_batch_ph) # shape: [I, H/2], item_id的embedding weight. I是item_id的hash bucket size,即movie count self.mid_embeddings_var = tf.get_variable("mid_embedding_var", [n_mid, EMBEDDING_DIM]) # 從mid embedding weight 中取出 uid embedding vector self.mid_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, self.mid_batch_ph) # 從mid embedding weight 中取出 mid history embedding vector,是正樣本 # 注意 self.mid_his_batch_ph這樣的變數 儲存使用者的歷史行為序列, 大小為 [B, T],所以在進行 embedding_lookup 時,輸出大小為 [B, T, H/2]; self.mid_his_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, self.mid_his_batch_ph) # 從mid embedding weight 中取出 mid history embedding vector,是負樣本 if self.use_negsampling: self.noclk_mid_his_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, self.noclk_mid_batch_ph) # shape: [C, H/2], cate_id的embedding weight. C是cat_id的hash bucket size self.cat_embeddings_var = tf.get_variable("cat_embedding_var", [n_cat, EMBEDDING_DIM]) # 從 cid embedding weight 中取出 cid history embedding vector,是正樣本 self.cat_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, self.cat_batch_ph) # 從 cid embedding weight 中取出 cid embedding vector,是正樣本 self.cat_his_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, self.cat_his_batch_ph) # 從 cid embedding weight 中取出 cid history embedding vector,是負樣本 if self.use_negsampling: self.noclk_cat_his_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, self.noclk_cat_batch_ph) ``` 具體各種shape可以參見下面執行時變數 ```python self = {Model_DIN_V2_Gru_Vec_attGru_Neg} cat_embeddings_var = {Va