再论推荐特征与embedding生成
文 | 水哥
源 | 知乎
Saying
1. 工業特征處理和學術特征處理存在巨大差異,這里建議同學們一定認真閱讀。這個差異可能引發未來各種方法落地的矛盾。
2. full embedding在概念上和one-hot的操作等價,但在操作上省略了這個過程。
3. hash是最省事的,一切特征轉string,一切string轉hash。
4. 在embedding分解這里沒有融入一些語義信息是比較遺憾的,如果有萬能的知友看完后搞出來了請給我一個致謝。
這是 【從零單排推薦系統】 系列的第17講,承載上一講,這里要詳細聊聊特征生成和取embedding的過程是怎么樣的。需要注意的是,這一講的東西可能會構成學術界和工業界最大差異的兩個地方。在閱讀論文的時候,判斷其中所講的東西有多大概率能在實踐中work,有兩個參考問題:(1)該論文的特征機制如何處理源源不斷的新的特征或者新的ID?(2)該論文的訓練機制是否與online learning的習慣沖突? 根據我個人的經驗,和上面兩個點有沖突的方案難以在工業實踐中帶來提升,即使費了勁把這兩個問題解決了,最后效果可能也是平的。
具體來講,特征處理就是一個典型的學界/工業界有裂痕的例子,本講會回到embedding+DNN這個范式的上面部分,聊一聊特征的生成方式和業界對于embedding的一些重新思考。
特征生成
在學術研究中,有很多特征本身就是類別(categorical)特征,比如城市,枚舉國內的所有城市,你一定在其中之一。我們往往把各類ID也看做類別特征,比如總共有1000個用戶,這次遇到的用戶是哪一類。與類別特征直接相關的處理方式就是先變成one-hot的向量(只有屬于的那一個bin是1,其他的都是0),在經過一個矩陣 轉化到浮點向量參與后面的處理。舉個例子,我們在用戶側有性別,User ID,城市三種特征。在科研情景中,我們就有三個one-hot的特征,每一個都通過乘以矩陣 轉化為embedding,如下圖所示:
或者也可以把他們組合成multi-hot的向量,然后用一個統一的大 來映射。這是典型的科研場景下的問題描述形式。
工業界的處理方式大致符合上面的操作,但和常見學術論文有一個區別是,并不存在一個真正的one-hot特征,更不存在從one-hot特征乘以 再映射到向量的過程。這是大部分同學,或是初學者最容易不理解的地方。首先,在大型工業場景下,會源源不斷出現新的item,新的用戶,新的ID,原先的one-hot和 必須得不斷擴充。但是這個過程并沒有什么必要性,在one-hot乘以權重這個操作中,實際上就是取出了 1對應的 的那一列而已,等于0的那些列根本就沒用。那我們干脆新出現哪個1,給它分配一個列向量就好了。這也就是embedding look-up table的操作。所以說工業實現概念上和one-hot一樣,操作上不一樣。
第二個區別是特征高度ID化,一切特征都可以是ID,原來不是ID的特征也轉向ID。由于沒有one-hot這個過程,還需要一個東西記錄非0出現的位置,這個位置就可以看作是所有特征的ID。比如城市這個特征,我們可以把第一個出現的特征記為cityID=1,第二個出現的記為cityID=2等等。但是這樣又會遇到一個問題:目前增長到哪了是需要記錄的,而且需要在各個機器中互相傳遞,否則A機器上新出現了一個,你定義為第11,但是在B機器中出現可能是第13個,這就出問題了。如果要針對這個同步的問題做處理,那么又得在機器之間做通信,比較麻煩。防止這個問題的做法就是對特征本身做hash,將得到的數字作為它的ID。只要每臺機器用的hash算法一樣,出來的值就是一樣的。
用hash還有第二個動機,就是其實我們也不希望ID是無限增長的。使用hash之后可以保證所有特征一定都在某個空間中不會出現意外。所以有一種做法是,我們給一種特征分配一個編號,稱為Slot ID,每一個特征的取值,我們hash后得到一個ID,稱為FID(feature ID),在一個n位的二進制數字中,前k位用slot ID的二進制表示填充,后面n-k位用FID填充,組成一個整體數字,作為這個feature的最終表示。經過這個操作,可以保證每一個特征的取值,都有唯一的取值(如果不考慮碰撞的話)。
hash表示的最大好處是它可以處理(至少是處理,處理的好不好是另一回事)所有類型的特征,只要你是能寫出來的,就可以用string表示,只要你能用string表示,你就能hash。實際中完全可以先全部hash跑起來,然后再細分有些特征需不需要特別處理。
既然是使用hash,那么不可避免的會遇到碰撞的問題。原則上,我們不希望有任何兩個不一樣的特征被hash到同一個ID上,所以會盡量選擇好的算法比如cityhash。但是問題也沒有那么嚴重,很多特征都有生命周期。像廣告中的item ID,預算沒了,不投放了,可以認為那個ID沒啥用了。可以設計遺忘機制。當我算出一個ID之后,看到記錄上一次算出這個ID是很早之前了,就可以再次初始化embedding讓一切重新開始。
Embedding壓縮與分解
embedding+DNN是一個“頭重腳輕”的方案,幾乎所有的內存消耗都壓在embedding的存儲上面。如果是按照one-hot那樣,內存會隨著時間線性增加,這是一個很大的消耗。
如果按照上面說的hash的方法,可以避免內存線性增大,總的內存消耗和我們開的空間大小有關。但問題是既然是hash,就一定有碰撞。如果空間設的很大,碰撞概率低,效果好,但內存大;反之若空間開的很小,那么碰撞概率就會增大,對效果有不好的影響。有沒有方法可以做巧妙的權衡?
這種動機在近兩年引領了一波新的風潮,一種直接的思路是把一個大的ID,拆解成數個小的ID的組合[1]。然后最終的embedding也是在這兩個小ID的embedding上做某種操作得到的。我們會想到可能有兩個大ID在某一個小ID中出現了碰撞,但是只要最終的表示中,另一個小ID不同,我們就認為最終的表示是不同的。
首先介紹的是一個Facebook發表在KDD2020上的方案,把一個大的ID拆解成商+余數的組合。比如一種特征的ID取值介于1-1000000之間。完全保存這種特征的embedding需要 的空間,這里 代表平均的特征維度。我可以找一個除數 ,然后把特征ID唯一的表示為原始ID除以 后得到的商和余數。 這里就選1000,商會有大約1000種取值,而余數也是有大約1000種。然后原始特征的embedding,現在表示為商和余數的兩個embedding的組合(可以是拼接,也可以是加起來或者element-wise乘)。由于商和余數各自只有1000種選擇,現在整個空間壓縮到了2000,相比于1000000,有500倍的壓縮!這個壓縮是一個平方級的減小。
當然,我們會有疑問,2001和2002這兩個ID,算下來商是一樣的,那不就意味著有一半的embedding都是一樣的嗎?是的,所以這個方法一定會帶來性能折損,實驗部分也能體現的出來。但是這個方法在實驗中比直接hash到2000要好。
沿著上面的思路,還可以有更加通用的方案:分成固定的若干個互補分區。比如上面的商和余數的方案,還可以對余數再取 的商和余數,一直往下。也可以拿出這個ID范圍內的所有質數,把能整除某個質數的放一起等等。綜合下來,對原始復雜度是指數級的衰減。
類似的方法也有今年CIKM的一個方法[2],通過控制二進制表示來壓縮空間,但這些方法有一個沒有解決的問題是分配到多個分區的過程沒有什么邏輯依據,缺乏“語義”。2001和2002因為商一樣,所以前半段embedding都是一樣的,但是它們也有可能是兩個完全沒有聯系的特征,那有一半embedding都一樣就不太合理了。根據我們在FM那幾講中提到的觀點,embedding還是要承載一些語義信息的。期望中應該是類型上更接近的特征,共享的概率越大,反之亦然。現在還沒有看到有工作涉及這方面,看到這里的讀者可以趕緊動手攢paper了!
其實embedding壓縮還涉及一個方向是Network Architecture Search(NAS),后面在熱點篇里面專門做介紹,這里簡單提一下也是可以給各個不同的特征分配不同的權重。
總結一下,無論什么樣的壓縮方案,肯定都會對效果有影響,畢竟天下沒有免費的午餐。但是選用什么樣的方案就是根據環境的。在業務還沒完全起來的時候,用一些embedding壓縮的方案是性價比較高的選擇。
Deep Hash Embedding(DHE[3])
回到最開始的問題,我們說embedding占用的空間那么大,其本質原因在什么地方呢?在于我們把原始特征表示為one-hot的,不是0就是1的表示方式當然是需要很大維度才能表示的。如果我們有一個非學習性的方法一上來就把特征ID表示成浮點數會怎么樣?如果能表示成一段浮點數的向量會怎么樣?
如果找到了這樣的方法,后面的事情是水到渠成的:可以就地接一個MLP,把前面的特征表示變換到一般要用的embedding,再接下面的DNN,這樣空間的占用一下就下來了!如下圖所示:
在第一步圖上也強調了是non-learnable的,對應左邊的look-up table需要占 的空間,而右邊的MLP就少非常多了,這樣就大大減小了存儲消耗。那么怎么把一個原始的ID變成浮點向量呢?首先可以考慮變成整型的向量,我們可以聯想到,不同的hash方式可以得到不同的int,同一種hash加不同的種子也可以做到這一點。
當使用各種hash方法/種子拼出一個高維的整數向量后,再做歸一化+高斯化就可以得到所需要的浮點數向量,把這個向量送入下面的“decode” MLP即可。
這個方案的另外一個考慮是基于沖突,上面講的hash方法其實都是存在沖突可能性的。即使是上面商+余數的方案,表面上看最終的embedding不一樣,但是局部的沖突可能很大。而使用了許許多多hash方法的結果后,再經過網絡變換,最終到了embedding表示這里,沖突的概率就很小了。有一個缺點是,由于embedding也是網絡生成的了,一點參數的變化會引起全局特征漂移,這樣對記憶性的原則有影響,因此論文中的實驗還是沒有打過完全不沖突的look-up table。
這個文章很有意思,它是完全根據實際應用場景遇到的問題提出的方案。如果這條路真的能走得深而且work的話可能會是一個很有前途的方向,還有不少可以做的事情。
下期預告
推薦系統精排之鋒(12):DIN+DIEN,機器學習唯一指定漲點技Attention
往期回顧
召回 粗排 精排,如何各司其職?
拍不完的腦袋:推薦系統打壓保送重排策略
簡單復讀機LR如何成為推薦系統精排之鋒?
召回粗排精排-級聯漏斗(上)
召回粗排精排-級聯漏斗(下)
推薦系統精排:看阿里媽媽再試線性模型
推薦精排之鋒:FM的一小步,泛化的一大步
推薦中使用FNN/PNN/ONN/NFM優化特征交叉
聊聊推薦系統的高階特征交叉問題
真正的高階特征交叉:xDeepFM與DCN-V2
GBDT是如何成為推薦系統頂級工具人的?
DNN與推薦兩大門派,一念神魔,功不唐捐
后臺回復關鍵詞【入群】
加入賣萌屋NLP/IR/Rec與求職討論群
后臺回復關鍵詞【頂會】
獲取ACL、CIKM等各大頂會論文集!
?
[1]Compositional Embeddings Using Complementary Partitions for Memory-Efficient Recommendation Systems,KDD,2020 https://arxiv.org/pdf/1909.02107.pdf
[2]Binary Code based Hash Embedding for Web-scale Applications,CIKM,2021 https://dl.acm.org/doi/pdf/10.1145/3459637.3482065
[3]Learning to Embed Categorical Features without Embedding Tables for Recommendation,KDD,2021 https://arxiv.org/pdf/2010.10784.pdf
總結
以上是生活随笔為你收集整理的再论推荐特征与embedding生成的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NLP预训练之路——从word2vec,
- 下一篇: 别再搞纯文本了!多模文档理解更被时代需要