Deepctr框架代碼閱讀


DeepCtr是一個簡易的CTR模型框架,集成了深度學習流行的所有模型,適合學推薦系統模型的人參考。我在參加比賽中用到了這個框架,但是效果一般,為了搞清楚原因從算法和框架兩方面入手。在讀代碼的過程中遇到一些不理解的問題,所以記錄在這里。

1. DeepFM模型的整體流程

preprocess_input_embedding:
	create_singlefeat_inputdict:
		搞成Feat是為了整體封裝好,然后輸入到Input的時候可以一一對應
		dense和spare直接放入keras的Input,格式是dict{key是feat名字,value是Input層結果}
	create_varlenfeat_inputdict
		序列是直接用max len放入keras的Input
	get_inputs_embedding
		create_embedding_dict(embedding層)
			稀疏特征:自動指定embeddingsize,6 * int(pow(feat.dimension, 0.25),否則按照指定的embeddingsize,使用L2正則化
			序列特征和稀疏特征的流程一樣,封裝Embedding多了mask_zero
			結果都是dict{key是feat名字,value是Embedding層結果},處理的是稀疏和序列特征
		get_embedding_vec_list(是embedding值)
			如果指定是hash,用自己寫的Hash函數將特征的索引(是Input層的結果,也就是原始是數據輸入)轉換成hash函數,如果不是就直接用原始的特征索引。用特征索引和對應的特征在Embedding獲取輸出。
			這里只處理sparse
		merge_sequence_input
			這里處理序列
			get_varlen_embedding_vec_dict
				hash的時候全部填充0,和之前的區別是之前指定的才填充0
				這里和處理的sparse的方式一樣,區別是用sequence_input_dict,但是這個和sparse都是用OrderedDict,區別是用最大長度,名字加seq
			get_pooling_vec_list
				如果沒有最長長度或者長度序列為空,不填充。在SequencePoolingLayer對序列特征進行pool
			把結果加到之前sparse的結果上
			返回全部結果		
		merge_dense_input
			把原始的embedding拼接成列向量,加到之前的結果上
		如果有線性:
			稀疏向量的embedding長度是1,只做融合稀疏和序列,流程和之前一樣
		inputs_list是稀疏、稠密、序列和序列長度
返回deep_emb_list, linear_emb_list, dense_input_dict, inputs_list
get_linear_logit
	如果有linear embedding,就是dense input經過全連接層之后加linear embedding,否則是dense input經過全連接層之后直接輸出。默認創造linear embedding
embedding拉平放入FM和Deep中,然后是linear+FM+Deep,如果都有的話,有一個加一個
	FM:和的平方-平方的和
	DNN:默認兩層128*128
最后所有的結果放入PredictionLayer,也就是連一個softmax或者sigmoid	

2. 框架優點

  • 整體結構清晰靈活,linear返回logit,FM層返回logit,deep包含中間層結果,在每一種模型中打包deep的最后一層,判斷linear,fm和deep是否需要,最后接入全連接層。
  • 主要用到的模塊和架構: keras的Concatenate(list轉tensor),Dense(最后的全連接層和dense),Embedding(sparse,dense,sequence),Input(sparse,dense,sequce)還有常規操作:優化器,正則化項
  • 復用了重載了Layer層,重寫了build,call,compute_output_shape,compute_mask,get_config

3. 框架缺點

  • 給定的參數都是論文提供的參數,實際使用存在問題,都需要自己修改!
  • 好多參數沒有留接口,比如回歸問題的loss 是mean_squared_error,只能通過硬寫來修改參數
  • 如果想實現自己的模型,復用這個框架,需要了解keras,同時改很多接口,時間代價較大。

運行模型,每次結果不一樣:
這個屬於正常現象,尤其是數據不夠充分的情況下,
主要原因是由於Tensorflow底層的多線程運行機制以及一些具有隨機性的op和random seed導致的。
如果想讓每次運行的結果盡量一致,可以考慮使用CPU運行程序,並且指定單線程運行,同時固定random seed,包括python自身的,Numpy的還有tensorflow的

4. 思考的問題

  1. 為什么獲取Embedding的時候要hash?為什么是在這個地方hash?
    慢慢想!
  2. 為什么linear需要全連接+Embedding,為什么默認為Linear加入Embeding操作?
    慢慢想!
  3. FM模型的連續特征是怎么處理的?
    1. 離散化后在輸入模型,事實上離散化后的模型更適合工業流水線環境。
    2. embedding一般是表示特征,embedding的話一般不能用來表示連續變量,拿年齡舉例來說就是不能讓10歲和40歲用同一個vector來表示。所以,還是要做one-hot處理的,也可以改動一下FM的結構,在一階部分保留原始的連續特征。
    3. w&d是將連續值特征轉換成累計分布形式,只針對離散特征去做fm和特征交叉;而它的連續值直接當作embedding向量和離散特征的embedding是拼接起來輸入到神經網絡里面去的。這里DeepFM模型也是這樣做的。
  4. 關於embedding降維的思考:
    比如用戶是50個binary特征,廣告有100個binary特征,那預測用戶是否會點擊某條廣告: 用fm把這些特征都抽象成10維的embedding,而且只做用戶和廣告的特征交叉,那把用戶側的embedding對應元素相加,這就壓縮成10維了,廣告側也這么做,也變成10維了。這時候衡量用戶和廣告的相關性就直接拿這兩個10的向量內積一下。這樣實現降維的目的。
  5. 序列化embedding的方式是pooling的方式
    這是參考youtobe的做法
  6. FM的embedding size通常設置的不大比如4或者8這個量級,但是在深度學習中一般設置的會相對大,比如32、64,128。這是為什么?
    實驗決定embeddingsize,有專家說過embeddingsize對最后的結果影響不大,所以只是一般使用128.
  7. 是否能實現有sparse的DeepFM
    為了稀疏輸入,可以直接替換Tensor的類型
    ids = tf.SparseTensor(sparse_index, sparse_ids, sparse_shape) values = tf.SparseTensor(sparse_index, sparse_values, sparse_shape)
  8. 在這個帖子中提到embedding look up性能非常低,沒有解決方案。
    https://zhuanlan.zhihu.com/p/39774203
  9. concat_fun 這里是concat什么?fm的輸入為什么需要concat?
    原來是list,每一行是一個tensor,concat之后是tensor,每一行是tensor
  10. tf.keras.layers.Flatten()(fm_input)
    原先的embedding輸入是[d,f,k],deep embedding是[d,f*k]
  11. 這里的實現和我的實現不一樣:
    我的linear+interact+deep接入全連接層,將所有的特征接入全連接層, 但是根據根據論文和多家的博客來看,我之前理解的是錯誤的,正確的應該是 fm logit+deep logit,最后接全連接層。 同時AFM等多個模型都是這么處理的。

5. 學習到的python知識

  1. cls:表示類本身,和靜態函數很像,但是比靜態函數多一個功能,就是調用一般的函數。
  2. namedtuple:表示可訪問屬性的tuple
  3. isinstance:判斷對象是否是一個已知類型
  4. 使用__import__ + list 導入需要的包
  5. new和init的區別:一個是創建,一個是初始化

這里寫框架閱讀寫的有點亂,下次可以按照以下三個要點來記錄:

  1. 輸入數據的方式
  2. 對不同數據的處理
  3. 最后的結合


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM