推薦系統召回四模型之全能的FM模型

AI科技大本營發表於2019-03-06

640?wx_fmt=jpeg


作者 | 張俊林

作者簡介:中國中文資訊學會理事,中科院軟體所博士。目前在新浪微博 AI Lab 擔任資深演算法專家。在此之前,張俊林曾經在阿里巴巴任資深技術專家並負責新技術團隊,以及在百度和用友擔任技術經理及技術總監等職務。同時他是技術書籍《這就是搜尋引擎:核心技術詳解》(該書榮獲全國第十二屆優秀圖書獎)、《大資料日知錄:架構與演算法》的作者。


既然你點開這篇文章了,我假設你是在某司做推薦系統的演算法工程師。這個假設的正確率我估計大約在20%左右,因為根據我的經驗,80%的演算法工程師是很博愛的,只要標題裡帶有“模型/演算法/深度學習/震驚/美女….”等詞彙,他們都會好奇地點開看三秒,然後失望地關掉,技術性越強的反而越容易被關掉,很可能撐不過三秒。我說得沒錯吧?嘿嘿。


為了騙點選,關於本文標題,其實我內心衝動裡最想寫下的震驚部風格標題是這樣的:“連女神級美女程式媛看了都震驚!FM模型居然能夠做這麼大規模推薦系統的召回!!!”,然後開啟文章後,文章配上的背景音樂緩緩地傳來“路燈下昏黃的剪影,越走越漫長的林徑…….”


嗯,好吧,我承認連我自己也忍不了上面的場景,主要是這首歌我還挺喜歡的,單曲迴圈快半個月了,標題風格比較毀歌的意境。請收拾好您此刻看到上述標題後接近崩潰的心情,不開玩笑了。


讓我再次活回到幻想中,就勉強假設你是位推薦演算法工程師吧,您堅持說您不是?別謙虛,您很快就是了,請立即辭職去申請相關工作……如果您真的是推薦工程師,那麼首先我想揪住您問個問題:一說起推薦模型或者推薦場景下的排序模型,您腦子裡第一個念頭冒出的模型是哪個或哪幾個?


如果你第一念頭冒出來的仍然是SVD/矩陣分解啥的,那麼明顯你還停留在啃書本的階段,實踐經驗不足;如果你第一念頭是LR模型或者GBDT模型,這說明你是具備一定實踐經驗的演算法工程師,但是知識更新不足。現在都9102年了,我們暫且把Wide&Deep/DeepFM這些模型拋開不提,因為在大規模場景下想要把深度推薦模型高價效比地用好發揮作用其實並不容易。


我們退而求其次,如果現在您仍然不能在日常工作中至少嘗試著用FM模型來搞事情,那隻能說明一定概率下(30%到90%?),您是在技術方面對自我沒有太高要求的演算法工程師,未來您的技術之路走起來,我猜可能會比較辛苦和坎坷,這裡先向身處2025年的另一位您道聲辛苦啦。這是我對您的演算法工程師之路的一個預測,至於這個預測準不準,往後若干年的經歷以及時間會告訴您正確答案,當然我個人覺得付出的這個代價可能有點高。


假設你第一念頭是在排序階段使用FM模型、GBDT+LR模型、DNN模型,這說明你算是緊追技術時代發展脈絡的技術人員,很好。那麼,單獨給你準備的更專業的新問題來了,請問:樹上七隻猴…..嗯,跑偏了,其實我想問的是:我們日常看到的推薦系統長什麼樣子,我相信你腦子裡很清楚,但是能否打破常規?比如下列兩個不太符合常規做法的技術問題,您可以考慮考慮:


第一個問題:我們知道在個性化推薦系統裡,第一個環節一般是召回階段,而召回階段工業界目前常規的做法是多路召回,每一路召回可能採取一個不同的策略。那麼打破常規的思考之一是:是否我們能夠使用一個統一的模型,將多路召回改造成單模型單路召回策略?如果不能,那是為什麼?如果能,怎麼做才可以?這樣做有什麼好處和壞處?


第二個問題:我們同樣知道,目前實用化的工業界的推薦系統通常由兩個環節構成,召回階段和排序階段,那麼為什麼要這麼劃分?它們各自的職責是什麼?打破常規的另外一個思考是:是否存在一個模型,這個模型可以將召回階段和排序階段統一起來,就是把兩階段推薦環節改成單模型單環節推薦流程?就是說靠一個模型一個階段把傳統的兩階段推薦系統做的事情一步到位做完?如果不能,為什麼不能?如果能,怎麼做才可以?什麼樣的模型才能擔當起這種重任呢?而在現實世界裡是否存在這個模型?這個思路真的可行嗎?


上面列的兩個非常規問題,18年年末我自己也一直在思考,有些初步的思考結論,所以計劃寫四篇文章形成一個專題,主題集中在推薦系統的統一召回模型方面,也就是第一個問題,同時兼談下第二個問題,每篇文章會介紹一個或者一類模型,本文介紹的是FM模型。這個系列,我春節期間寫完了3篇,等我四篇都寫完後,會陸續發出來,供感興趣的“點開看三秒”同學參考。


不過這裡需要強調一點:關於這兩個問題,因為非常規,網上也也沒有見到過類似的問題,說法及解決方案,所以沒什麼依據,文章寫的只是我個人的思考結果,是否真能順利落地以及落地效果存疑,還請謹慎參考。不過,我覺得從目前演算法的發展趨勢以及硬體條件的快速發展情況來看,單階段推薦模型從理論上是可行的。我會陸續給出幾個方案,建議從事中小型推薦業務的同學可以快速嘗試一下。


下面進入正題,我會先簡單介紹下推薦系統整體架構以及多路召回的基本模式,然後說明下FM模型,之後探討FM模型是否能夠解決上面提到的兩個非常規問題。


工業推薦系統整體架構是怎樣的?



640?wx_fmt=jpeg


一個典型的工業級推薦系統整體架構可以參考上圖,一般分為線上部分,近線部分和離線部分。



對於線上部分來說,一般要經歷幾個階段。首先通過召回環節,將給使用者推薦的物品降到千以下規模;如果召回階段返回的物品還是太多,可以加入粗排階段,這個階段是可選的,粗排可以通過一些簡單排序模型進一步減少往後續環節傳遞的物品;再往後是精排階段,這裡可以使用複雜的模型來對少量物品精準排序。



對某個使用者來說,即使精排推薦結果出來了,一般並不會直接展示給使用者,可能還要上一些業務策略,比如去已讀,推薦多樣化,加入廣告等各種業務策略。之後形成最終推薦結果,將結果展示給使用者。



對於近線部分來說,主要目的是實時收集使用者行為反饋,並選擇訓練例項,實時抽取拼接特徵,並近乎實時地更新線上推薦模型。這樣做的好處是使用者的最新興趣能夠近乎實時地體現到推薦結果裡。



對於離線部分而言,通過對線上使用者點選日誌的儲存和清理,整理離線訓練資料,並週期性地更新推薦模型。對於超大規模資料和機器學習模型來說,往往需要高效地分散式機器學習平臺來對離線訓練進行支援。


640?wx_fmt=png


因為粗排是可選的,對於大多數推薦系統來說,通常線上部分的主體分為兩個階段就夠,第一個階段是召回,第二個階段是排序。因為個性化推薦需要給每個使用者展現不同的資訊流或者物品流,而對於每個使用者來說,可供推薦的物品,在具備一定規模的公司裡,是百萬到千萬級別,甚至上億。所以對於每一個使用者,如果對於千萬級別物品都使用先進的模型挨個進行排序打分,明顯速度上是算不過來的,資源投入考慮這麼做也不划算。從這裡可以看出,召回階段的主要職責是:從千萬量級的候選物品裡,採取簡單模型將推薦物品候選集合快速篩減到千級別甚至百級別,這樣將候選集合數量降下來,之後在排序階段就可以上一些複雜模型,細緻地對候選集進行個性化排序。

從上面線上推薦兩階段任務的劃分,我們可以看出,召回階段因為需要計算的候選集合太大,所以要想速度快,就只能上簡單模型,使用少量特徵,保證泛化能力,儘量讓使用者感興趣的物品在這個階段能夠找回來;而排序階段核心目標是要精準,因為它處理的物品資料量小,所以可以採用儘可能多的特徵,使用比較複雜的模型,一切以精準為目標。


多路召回怎麼做?


目前工業界推薦系統的召回階段一般是怎麼做的呢?可以用一句江湖氣很重的話來總結,請您繫好安全帶坐穩,怕嚇到您,這句話就是:“一隻穿雲箭,千軍萬馬來相見”。聽起來霸氣十足是吧?我估計看過古惑仔電影的都熟悉這句話,黑幫集結打群架的時候喜歡引用這句名言,以增加氣勢,自己給自己打氣。如果和推薦系統對應起來理解,這裡的“穿雲箭”就是召回系統,而千軍萬馬就是各路花式召回策略。


640?wx_fmt=jpeg


目前工業界的推薦系統,在召回階段,一般都採取多路召回策略。上圖展示了一個簡化版本的例子,以微博資訊流排序為例,不同業務召回路數不太一樣,但是常用的召回策略,基本都會包含,比如興趣標籤,興趣Topic,興趣實體,協同過濾,熱門,相同地域等,多者幾十路召回,少者也有7/8路召回。

對於每一路召回,會拉回K條相關物料,這個K值是個超參,需要通過線上AB測試來確定合理的取值範圍。如果你對演算法敏感的話,會發現這裡有個潛在的問題,如果召回路數太多,對應的超參就多,這些超參組合空間很大,如何設定合理的各路召回數量是個問題。另外,如果是多路召回,這個超參往往不太可能是使用者個性化的,而是對於所有使用者,每一路拉回的數量都是固定的,這裡明顯有優化空間。按理說,不同使用者也許對於每一路內容感興趣程度是不一樣的,更感興趣的那一路就應該多召回一些,所以如果能把這些超參改為個性化配置是很好的,但是多路召回策略下,雖然也不是不能做,但是即使做,看起來還是很Trick的。有什麼好辦法能解決這個問題嗎?有,本文後面會講。


什麼是FM模型?


什麼是FM模型呢?我隱約意識到這個問題在很多人看起來好像有點過於簡單,因為一說起FM,開車的朋友們估計都熟悉,比如FM1039交通臺家喻戶曉,最近應該經常聽到交通臺這麼提醒大家吧:“…春節返程高峰,北京市第三交通委提醒您:道路千萬條,安全第一條……”


一想到有可能很多人這麼理解FM,我的眼淚就不由自主流了下來,同時對他們在心理上有種莫名的親切感,為什麼呢?不是說“縮寫不規範,親人兩行淚”麼。下面我鄭重地給各位介紹下,FM英文全稱是“Factorization Machine”,簡稱FM模型,中文名“因子分解機”。


FM模型其實有些年頭了,是2010年由Rendle提出的,但是真正在各大廠大規模在CTR預估和推薦領域廣泛使用,其實也就是最近幾年的事。


FM模型比較簡單,網上介紹的內容也比較多,細節不展開說它了。不過我給個個人判斷:我覺得FM是推薦系統工程師應該熟練掌握和應用的必備演算法,即使你看很多DNN版本的排序模型,你應該大多數情況會看到它的影子,原因其實很簡單:特徵組合對於推薦排序是非常非常重要的,而FM這個思路已經很簡潔優雅地體現了這個思想了(主要是二階特徵組合)。


DNN模型一樣離不開這個特點,而MLP結構是種低效率地捕獲特徵組合的結構,所以即使是深度模型,目前一樣還離不開類似FM這個能夠直白地直接去組合特徵的部分。這是你會反覆發現它的原因所在,當然也許是它本人,也許不一定是它本人,但是一定是它的變體。


既然談到這裡了,那順手再多談談推薦排序模型。目前具備實用化價值的DNN版本的CTR模型一般採用MLP結構,看著遠遠落後CV/NLP的特徵抽取器的發展水平,很容易讓人產生如下感覺:CTR的DNN模型還處於深度學習原始社會階段。那這又是為什麼呢?因為CNN的特性天然不太適合推薦排序這個場景(為什麼?您可以思考一下。


為了預防某些具備某種獨特個性特徵的同學拿個別例子說事情,我先提一句:請不要跟我說某個已有的看上去比較深的CNN CTR模型,你自己試過效果如何再來說。這算是我的預防性回懟或者是假設性回懟,哈哈)。RNN作為捕捉使用者行為序列,利用時間資訊的輔助結構還行,但是也不太適合作為CTR預估或者推薦排序的主模型(為什麼?您可以思考一下,關於這點,我的看法以後有機會會提)。


好像剩下的選擇不多了(Transformer是很有希望的,去年年中左右,我覺得Self attention應該是個能很好地捕捉特徵組合(包括二階/三階…多階)的工具,於是,我們微博也嘗試過用self attention和transformer作為CTR的主體排序模型,非業務資料測試的,當時測試效果和DeepFM等主流模型效果差不太多。我現在回頭看,很可能是哪些細節沒做對,當時覺得沒有特別的效果優勢,於是沒再繼續嘗試這個思路。


當然貌似18年下半年已經冒出幾篇用Transformer做CTR排序模型的論文了,我個人非常看好這個CTR模型進化方向),於是剩下的選擇貌似只有MLP了,意思是:對於CTR或者推薦排序領域來說,不是它不想進入模型共產主義階段,是大門關得太緊,它進不去,於是只能在MLP這個門檻徘徊。


在深度學習大潮下,從模型角度看,確實跟很多領域比,貌似推薦領域遠遠落後,這個是事實。我覺得主要原因是它自身的領域特點造成的,它可能需要打造適合自身特點的DNN排序模型。就像影像領域裡有Resnet時刻,NLP裡面有Bert時刻,我覺得推薦排序深度模型目前還沒有,現在和未來也需要這個類似的高光時刻,而這需要一個針對它特性改造出的新結構,對此我是比較樂觀的,我預感這個時刻一年之內還無法出現,但是很可能已經在路上,距離我們不遠了。


又說遠了,本來我們主題是召回,說到排序模型裡去了,我往主車道走走。上面本來是要強調好好學好好用FM模型的。下面我從兩個角度來簡單介紹下FM模型,一個角度是從特徵組合模型的進化角度來講;另外一個角度從協同過濾模型的進化角度來講。FM模型就處於這兩類模型進化的交匯口。


從LR到SVM再到FM模型


640?wx_fmt=jpeg


LR模型是CTR預估領域早期最成功的模型,大多工業推薦排序系統採取LR這種“線性模型+人工特徵組合引入非線性”的模式。因為LR模型具有簡單方便易解釋容易上規模等諸多好處,所以目前仍然有不少實際系統仍然採取這種模式。但是,LR模型最大的缺陷就是人工特徵工程,耗時費力費人力資源,那麼能否將特徵組合的能力體現在模型層面呢?



640?wx_fmt=jpeg


其實想達到這一點並不難,如上圖在計算公式里加入二階特徵組合即可,任意兩個特徵進行組合,可以將這個組合出的特徵看作一個新特徵,融入線性模型中。而組合特徵的權重可以用來表示,和一階特徵權重一樣,這個組合特徵權重在訓練階段學習獲得。其實這種二階特徵組合的使用方式,和多項式核SVM是等價的。


雖然這個模型看上去貌似解決了二階特徵組合問題了,但是它有個潛在的問題:它對組合特徵建模,泛化能力比較弱,尤其是在大規模稀疏特徵存在的場景下,這個毛病尤其突出,比如CTR預估和推薦排序,這些場景的最大特點就是特徵的大規模稀疏。所以上述模型並未在工業界廣泛採用。那麼,有什麼辦法能夠解決這個問題嗎?


640?wx_fmt=jpeg


於是,FM模型此刻可以閃亮登場了。如上圖所示,FM模型也直接引入任意兩個特徵的二階特徵組合,和SVM模型最大的不同,在於特徵組合權重的計算方法。FM對於每個特徵,學習一個大小為k的一維向量,於是,兩個特徵 和 的特徵組合的權重值,通過特徵對應的向量和的內積來表示。


這本質上是在對特徵進行embedding化表徵,和目前非常常見的各種實體embedding本質思想是一脈相承的,但是很明顯在FM這麼做的年代(2010年),還沒有現在能看到的各種眼花繚亂的embedding的形式與概念。所以FM作為特徵embedding,可以看作當前深度學習裡各種embedding方法的老前輩。


當然,FM這種模式有它的前輩模型嗎?有,等會會談。其實,和目前的各種深度DNN排序模型比,它僅僅是少了2層或者3層MLP隱層,用來直接對多階特徵非線性組合建模而已,其它方面基本相同。


640?wx_fmt=jpeg


那麼為什麼說FM的這種特徵embedding模式,在大規模稀疏特徵應用環境下比較好用?為什麼說它的泛化能力強呢?參考上圖說明。即使在訓練資料裡兩個特徵並未同時在訓練例項裡見到過,意味著一起出現的次數為0,如果換做SVM的模式,是無法學會這個特徵組合的權重的。


但是因為FM是學習單個特徵的embedding,並不依賴某個特定的特徵組合是否出現過,所以只要特徵 和其它任意特徵組合出現過,那麼就可以學習自己對應的embedding向量。於是,儘管 這個特徵組合沒有看到過,但是在預測的時候,如果看到這個新的特徵組合,因為 和 都能學會自己對應的embedding,所以可以通過內積算出這個新特徵組合的權重。這是為何說FM模型泛化能力強的根本原因。


其實本質上,這也是目前很多花樣的embedding的最核心特點,就是從0/1這種二值硬核匹配,切換為向量軟匹配,使得原先匹配不上的,現在能在一定程度上算密切程度了,具備很好的泛化效能。


從MF到FM模型


FM我們大致應該知道是怎麼個意思了,這裡又突然冒出個MF,長得跟FM貌似還有點像,那麼MF又是什麼呢?它跟FM又有什麼關係?

請跟我念:“打東邊來了個FM,手裡提著一斤撻嘛;打西邊來了個MF,腰裡彆著一個喇叭;提著一斤撻嘛的FM想要彆著喇叭的MF腰裡的喇叭……..”你要是能不打磕絆一遍念下來的話………你以為你就理解它們的錯綜複雜的關係了是嗎?不,你就可以去學說相聲了……


640?wx_fmt=jpeg


MF(Matrix Factorization,矩陣分解)模型是個在推薦系統領域裡資格很深的老前輩協同過濾模型了。核心思想是通過兩個低維小矩陣(一個代表使用者embedding矩陣,一個代表物品embedding矩陣)的乘積計算,來模擬真實使用者點選或評分產生的大的協同資訊稀疏矩陣,本質上是編碼了使用者和物品協同資訊的降維模型。


640?wx_fmt=jpeg


當訓練完成,每個使用者和物品得到對應的低維embedding表達後,如果要預測某個 對 的評分的時候,只要它們做個內積計算 ,這個得分就是預測得分。看到這裡,讓你想起了什麼嗎?


身為推薦演算法工程師,我假設你對它還是比較熟悉的,更多的就不展開說了,相關資料很多,我們重點說MF和FM的關係問題。


MF和FM不僅在名字簡稱上看著有點像,其實他們本質思想上也有很多相同點。那麼,MF和FM究竟是怎樣的關係呢?


640?wx_fmt=jpeg


本質上,MF模型是FM模型的特例,MF可以被認為是隻有User ID 和Item ID這兩個特徵Fields的FM模型,MF將這兩類特徵通過矩陣分解,來達到將這兩類特徵embedding化表達的目的。


而FM則可以看作是MF模型的進一步擴充,除了User ID和Item ID這兩類特徵外,很多其它型別的特徵,都可以進一步融入FM模型裡,它將所有這些特徵轉化為embedding低維向量表達,並計算任意兩個特徵embedding的內積,就是特徵組合的權重,如果FM只使用User ID 和Item ID,你套到FM公式裡,看看它的預測過程和MF的預測過程一樣嗎?


從誰更早使用特徵embedding表達這個角度來看的話,很明顯,和FM比起來,MF才是真正的前輩,無非是特徵型別比較少而已。而FM繼承了MF的特徵embedding化表達這個優點,同時引入了更多Side information作為特徵,將更多特徵及Side information embedding化融入FM模型中。所以很明顯FM模型更靈活,能適應更多場合的應用範圍。


鑑於MF和FM以上錯綜複雜剪不斷理還亂的關係,我推論出下面的觀點(個人意見):


其一:在你有使用MF做協同過濾的想法的時候,暫時壓抑一下這種衝動,可以優先考慮引入FM來做的,而非傳統的MF,因為可以在實現等價功能的基礎上,很方便地融入其它任意你想加入的特徵,把手頭的事情做得更豐富多彩。


其二:從實際大規模資料場景下的應用來講,在排序階段,絕大多數只使用ID資訊的模型是不實用的,沒有引入Side Information,也就是除了User ID/Item ID外的很多其它可用特徵的模型,是不具備實戰價值的。原因很簡單,大多數真實應用場景中,User/Item有很多資訊可用,而協同資料只是其中的一種,引入更多特徵明顯對於更精準地進行個性化推薦是非常有幫助的。而如果模型不支援更多特徵的便捷引入,明顯受限嚴重,很難真正實用,這也是為何矩陣分解類的方法很少看到在Ranking階段使用,通常是作為一路召回形式存在的原因。


簡單談談演算法的效率問題


從FM的原始數學公式看,因為在進行二階(2-order)特徵組合的時候,假設有n個不同的特徵,那麼二階特徵組合意味著任意兩個特徵都要進行交叉組合,所以可以直接推論得出:FM的時間複雜度是n的平方。但是如果故事僅僅講到這裡,FM模型是不太可能如此廣泛地被工業界使用的。因為現實生活應用中的n往往是個非常巨大的特徵數,如果FM是n平方的時間複雜度,那估計基本就沒人帶它玩了。


對於一個實用化模型來說,效果是否足夠好只是一個方面,計算效率是否夠高也很重要,這兩點是一個能被廣泛使用演算法的一枚硬幣的兩面,缺其中任何一個可能都不能算是優秀的演算法。如果在兩者之間硬要分出誰更重要的話,怎麼選?


這裡插入個題外話,是關於如何做選擇的。這個話題如果你深入思考的話,會發現很可能是個深奧的哲學問題。在說怎麼選之前,我先複述兩則關於選擇的笑話,有兩個版本,男版和女版的。


男版是這樣的:“一個兄弟跟我說他最近很困惑,有三個姑娘在追他,一直猶豫不決,到底應該選哪個當女朋友呢?一個溫柔賢惠,一個聰明伶俐,另外一個膚白貌美。太難選…..三天後當我再次遇到他的時候,他說他做出了選擇,選了那個胸最大的!”


女版是這樣的:“一個姐妹跟我說她很困惑,最近有三個優秀的男人在追她,一直猶豫不決,到底應該嫁給誰呢?一個努力上進,一個高大帥氣,另外一個脾氣好顧家。實在太難選…..三天後當我再次遇到她的時候,她說她做出了選擇,選了那個最有錢的!”


參考這個模版,演算法選擇版應該是這樣的:“一個演算法工程師一直猶豫不決該選哪個模型去上線,他有三個優秀演算法可選,一個演算法理論優雅;一個演算法效果好;另外一個演算法很時髦,實在太難做決定…..三天後當我再遇見他的時候,他說他們演算法總監讓他上了那個跑得最快的!”


怎麼樣?生活或者工作中的選擇確實是個很玄妙的哲學問題吧?這個演算法版的關於選擇的笑話,應該已經回答了上面那個還沒給答案的問題了吧?在資料量特別大的情況下,如果在效果好和速度快之間做選擇,很多時候跑得快的簡單模型會勝出,這是為何LR模型在CTR預估領域一直被廣泛使用的原因。


而FFM模型則是反例,我們在幾個資料集合上測試過,FFM模型作為排序模型,效果確實是要優於FM模型的,但是FFM模型對引數儲存量要求太多,以及無法能做到FM的執行效率,如果中小資料規模做排序沒什麼問題,但是資料量一旦大起來,對資源和效率的要求會急劇升高,這是嚴重阻礙FFM模型大規模資料場景實用化的重要因素。


再順手談談DNN排序模型,現在貌似看著有很多版本的DNN排序模型,但是考慮到上面講的運算效率問題,你會發現太多所謂效果好的模型,其實不具備實用價值,算起來太複雜了,效果好得又很有限,超大規模訓練或者線上 Serving速度根本跟不上。除非,你們公司有具備相當強悍實力的工程團隊,能夠進行超大資料規模下的大規模效能優化,那當我上面這句話沒說。


我對排序模型,如果你打算推上線真用起來的話,建議是,沿著這個序列嘗試:FM-->DeepFM。你看著路徑有點短是嗎?確實比較短。如果DeepFM做不出效果,別再試著去嘗試更復雜的模型了,還是多從其它方面考慮考慮優化方案為好。有些複雜些的模型,也許效果確實好一些,在個別大公司也許真做上線了,但是很可能效果好不是演算法的功勞,是工程能力強等多個因素共同導致的,人家能做,你未必做的了。


至於被廣泛嘗試的Wide &Deep,我個人對它有偏見,所以直接被我跳過了。當然,如果你原始線上版本是LR,是可以直接先嚐試Wide&Deep的,但是即使如此,要我做升級方案,我給的建議會是這個序列:LR—>FM-->DeepFM—>乾點其他的。


如何優化FM的計算效率


640?wx_fmt=jpeg


再說回來,FM如今被廣泛採用併成功替代LR模型的一個關鍵所在是:它可以通過數學公式改寫,把表面貌似是 的複雜度降低到,其中n是特徵數量,k是特徵的embedding size,這樣就將FM模型改成了和LR類似和特徵數量n成線性規模的時間複雜度了,這點非常好。


那麼,如何改寫原始的FM數學公式,讓其複雜度降下來呢?因為原始論文在推導的時候沒有給出詳細說明,我相信不少人看完估計有點懵,所以這裡簡單解釋下推導過程,數學公式帕金森病患者可以直接跳過下面內容往後看,這並不影響你理解本文的主旨。



640?wx_fmt=jpeg


上圖展示了整個推導過程,我相信如果數學基礎不太紮實的同學看著會有點頭疼,轉換包括四個步驟,下面分步驟解釋下。


640?wx_fmt=jpeg


第一個改寫步驟及為何這麼改寫參考上圖,比較直觀,不解釋了。


640?wx_fmt=jpeg


第二步轉換更簡單,更不用解釋了。



640?wx_fmt=jpeg


第三步轉換不是太直觀,可能需要簡單推導一下,很多人可能會卡在這一步,所以這裡解釋解釋。


640?wx_fmt=jpeg


其實吧,如果把k維特徵向量內積求和公式抽到最外邊後,公式就轉成了上圖這個公式了(不考慮最外邊k維求和過程的情況下)。它有兩層迴圈,內迴圈其實就是指定某個特徵的第f位(這個f是由最外層那個k指定的)後,和其它任意特徵對應向量的第f位值相乘求和;而外迴圈則是遍歷每個的第f位做迴圈求和。這樣就完成了指定某個特徵位f後的特徵組合計算過程。最外層的k維迴圈則依此輪循第f位,於是就算完了步驟三的特徵組合。


640?wx_fmt=jpeg


對上一頁公式圖片展示過程用公式方式,再一次改寫(參考上圖),其實就是兩次提取公共因子而已,這下應該明白了吧?要是還不明白,那您的診斷結果是數學公式帕金森晚期,跟我一個毛病,我們倆病友同病相憐,我也沒轍了。


640?wx_fmt=jpeg


第四步公式變換,意思參考上圖,這步也很直白,不解釋。


於是,通過上述四步的公式改寫,可以看出在實現FM模型時,時間複雜度就降低了,而雖說看上去n還有點大,但是其實真實的推薦資料的特徵值是極為稀疏的,就是說大量xi其實取值是0,意味著真正需要計算的特徵數n是遠遠小於總特徵數目n的,無疑這會進一步極大加快FM的運算效率。


這裡需要強調下改寫之後的FM公式的第一個平方項,怎麼理解這個平方項的含義呢?這裡其實蘊含了後面要講的使用FM模型統一多路召回的基本思想,所以這裡特殊提示一下。


640?wx_fmt=jpeg


參考上圖,你體會下這個計算過程。它其實等價於什麼?


640?wx_fmt=jpeg


這個平方項,它等價於將FM的所有特徵項的embedding向量累加,之後求內積。我再問下之前問過的問題:“我們怎樣利用FM模型做統一的召回?”這個平方項的含義對你有啟發嗎?你可以仔細想想它們之間的關聯。



如何利用FM模型做統一的召回模型?


上文書提到過,目前工業界推薦系統在召回階段,大多數採用了多路召回策略,比如典型的召回路有:基於使用者興趣標籤的召回;基於協同過濾的召回;基於熱點的召回;基於地域的召回;基於Topic的召回;基於命名實體的召回等等,除此外還有很多其它型別的召回路。


現在我們來探討下第一個問題:在召回階段,能否用一個統一的模型把多路召回招安?就是說改造成利用單個模型,單路召回的模式?具體到這篇文章,就是說能否利用FM模型來把多路召回統一起來?


在回答上述問題之前,我估計你會提出疑問:目前大家用多路召回用的好好的,為啥要多此一舉,用一個模型把多路召回統一起來呢?這個問題非常好,我們確實應該先看這麼做的必要性。


統一召回和多路召回優缺點比較


我們先來說明下統一召回和多路召回各自的優缺點,我覺得使用統一召回模式,相對多路召回有如下優點:


首先,採用多路召回,每一路召回因為採取的策略或者模型不同,所以各自的召回模型得分不可比較,比如利用協同過濾召回找到的候選Item得分,與基於興趣標籤這一路召回找到的候選Item得分,完全是不可比較的。這也是為何要用第二階段Ranking來將分數統一的原因。而如果採取統一的召回模型,比如FM模型,那麼不論候選項Item來自於哪裡,它們在召回階段的得分是完全可比的。


其次,貌似在目前“召回+Ranking”兩階段推薦模型下,多路召回分數不可比這個問題不是特別大,因為我們可以依靠Ranking階段來讓它們可比即可。但是其實多路召回分數不可比會直接引發一個問題:對於每一路召回,我們應該返回多少個Item是合適的呢?如果在多路召回模式下,這個問題就很難解決。既然分數不可比,那麼每一路召回多少候選項K就成為了超參,需要不斷調整這個引數上線做AB測試,才能找到合適的數值。


而如果召回路數特別多,於是每一路召回帶有一個超參K,就是這一路召回多少條候選項,這樣的超參組合空間是非常大的。所以到底哪一組超參是最優的,就很難定。其實現實情況中,很多時候這個超參都是拍腦袋上線測試,找到最優的超參組合概率是很低的。


而如果假設我們統一用FM模型來做召回,其實就不存在上面這個問題。這樣,我們可以在召回階段做到更好的個性化,比如有的使用者喜歡看熱門的內容,那麼熱門內容在召回階段返回的比例就高,而其它內容返回比例就低。所以,可以認為各路召回的這組超引數就完全依靠FM模型調整成個性化的了,很明顯這是使用單路單模型做召回的一個特別明顯的好處。


再次,對於工業界大型的推薦系統來說,有極大的可能做召回的技術人員和做Ranking的技術人員是兩撥人。這裡隱含著一個潛在可能會發生的問題,比如召回階段新增了一路召回,但是做Ranking的哥們不知道這個事情,在Ranking的時候沒有把能體現新增召回路特性的特徵加到Ranking階段的特徵中。這樣體現出來的效果是:新增召回路看上去沒什麼用,因為即使你找回來了,而且使用者真的可能點選,但是在排序階段死活排不上去。


也就是說,在召回和排序之間可能存在資訊鴻溝的問題,因為目前召回和排序兩者的表達模式差異很大,排序階段以特徵為表達方式,召回則以“路/策略/具體模型”為表達方式,兩者之間差異很大,是比較容易產生上述現象的。


但是如果我們採用FM模型來做召回的話,新增一路召回就轉化為新增特徵的問題,而這一點和Ranking階段在表現形式上是相同的,對於召回和排序兩個階段來說,兩者都轉化成了新增特徵問題,所以兩個階段的改進語言體系統一,就不太容易出現上述現象。


上面三點,是我能想到的採用統一召回模型,相對多路召回的幾個好處。但是是不是多路召回一定不如統一召回呢?其實也不是,很明顯多路召回這種策略,上線一個新召回方式比較靈活,對線上的召回系統影響很小,因為不同路召回之間沒有耦合關係。但是如果採用統一召回,當想新增一種召回方式的時候,表現為新增一種或者幾種特徵,可能需要完全重新訓練一個新的FM模型,整個召回系統重新部署上線,靈活性比多路召回要差。


上面講的是必要性,講完了必要性,我們下面先探討如何用FM模型做召回,然後再討論如何把多路召回改造成單路召回,這其實是兩個不同的問題。


如何用FM模型做召回模型


如果要做一個實用化的統一召回模型,要考慮的因素有很多,比如Context上下文特徵怎麼處理,實時反饋特徵怎麼加入等。為了能夠更清楚地說明,我們先從極簡模型說起,然後逐步加入必須應該考慮的元素,最後形成一個實用化的統一召回模型。


不論是簡化版本FM召回模型,還是複雜版本,首先都需要做如下兩件事情:

第一,離線訓練。這個過程跟在排序階段採用FM模型的離線訓練過程是一樣的,比如可以使用線上收集到的使用者點選資料來作為訓練資料,線下訓練一個完整的FM模型。在召回階段,我們想要的其實是:每個特徵和這個特徵對應的訓練好的embedding向量。這個可以存好待用。


640?wx_fmt=jpeg

第二,如果將推薦系統做個很高層級的抽象的話,可以表達成學習如下形式的對映函式:

640?wx_fmt=png

意思是,我們利用使用者(User)相關的特徵,物品(Item)相關的特徵,以及上下文特徵(Context,比如何時何地用的什麼牌子手機登陸等等)學習一個對映函式F。學好這個函式後,當以後新碰到一個Item,我們把使用者特徵,物品特徵以及使用者碰到這個物品時的上下文特徵輸入F函式,F函式會告訴我們使用者是否對這個物品感興趣。如果他感興趣,就可以把這個Item作為推薦結果推送給使用者。


說了這麼多,第二個我們需要做的事情是:把特徵劃分為三個子集合,使用者相關特徵集合,物品相關特徵集合以及上下文相關的特徵集合。而使用者歷史行為類特徵,比如使用者過去點選物品的特徵,可以當作描述使用者興趣的特徵,放入使用者相關特徵集合內。至於為何要這麼劃分,後面會講。

做完上述兩項基礎工作,我們可以試著用FM模型來做召回了。


極簡版FM召回模型


我們先來構建一個極簡的FM召回模型,首先,我們先不考慮上下文特徵,晚點再說。


640?wx_fmt=jpeg


第一步,對於某個使用者,我們可以把屬於這個使用者子集合的特徵,查詢離線訓練好的FM模型對應的特徵embedding向量,然後將n個使用者子集合的特徵embedding向量累加,形成使用者興趣向量U,這個向量維度和每個特徵的維度是相同的。


類似的,我們也可以把每個物品,其對應的物品子集合的特徵,查詢離線訓練好的FM模型對應的特徵embedding向量,然後將m個物品子集合的特徵embedding向量累加,形成物品向量I,這個向量維度和每個特徵的維度也是是相同的。


對於極簡版FM召回模型來說,使用者興趣向量U可以離線算好,然後更新線上的對應內容;物品興趣向量I可以類似離線計算或者近線上計算,問題都不大。


640?wx_fmt=jpeg


第二步,對於每個使用者以及每個物品,我們可以利用步驟一中的方法,將每個使用者的興趣向量離線算好,存入線上資料庫中比如Redis(使用者ID及其對應的embedding),把物品的向量逐一離線算好,存入Faiss(Facebook開源的embedding高效匹配庫)資料庫中。


當使用者登陸或者重新整理頁面時,可以根據使用者ID取出其對應的興趣向量embedding,然後和Faiss中儲存的物料embedding做內積計算,按照得分由高到低返回得分Top K的物料作為召回結果。提交給第二階段的排序模型進行進一步的排序。這裡Faiss的查詢速度至關重要,至於這點,後面我們會單獨說明。


這樣就完成了一個極簡版本FM召回模型。但是這個版本的FM召回模型存在兩個問題。


問題一:首先我們需要問自己,這種累加使用者embedding特徵向量以及累加物品embedding特徵向量,之後做向量內積。這種演算法符合FM模型的原則嗎?和常規的FM模型是否等價?


我們來分析一下。這種做法其實是在做使用者特徵集合U和物品特徵集合I之間兩兩特徵組合,是符合FM的特徵組合原則的,考慮下列公式是否等價就可以明白了:

640?wx_fmt=png

其實兩者是等價的,建議您可以推導一下(這其實不就是上面在介紹FM公式改寫的第三步轉換嗎?當然,跟完全版本的FM比,我們沒有考慮U和I特徵集合內部任意兩個特徵的組合,等會會說這個問題)。


也可以這麼思考問題:在上文我們說過,FM為了提升計算效率,對公式進行了改寫,改寫後的高效計算公式的第一個平方項其實等價於:把所有特徵embedding向量逐位累加成一個求和向量V,然後自己和自己做個內積操作<V,V>。這樣等價於根據FM的原則計算了任意兩個特徵的二階特徵組合了。


而上面描述的方法,和標準的FM的做法其實是一樣的,區別無非是將特徵集合劃分為兩個子集合U和I,分別代表使用者相關特徵及物品相關特徵。而上述做法其實等價於在使用者特徵和物品特徵之間做兩兩特徵組合,只是少了U內部之間特徵,及I內部特徵之間的特徵組合而已。


一般而言,其實我們不需要做U內部特徵之間以及I內部特徵之間的特徵組合,對最終效果影響很小。於是,沿著這個思考路徑,我們也可以推匯出上述做法基本和FM標準計算過程是等價的。


第二個問題是:這個版本FM是個簡化版本模型,因為它沒考慮場景上下文特徵,那麼如果再將上下文特徵引入,此時應該怎麼做呢?


加入場景上下文特徵


上面敘述瞭如何根據FM模型做一個極簡版本的召回模型,之所以說極簡,因為我們上面說過,抽象的推薦系統除了使用者特徵及物品特徵外,還有一類重要特徵,就是使用者發生行為的場景上下文特徵(比如什麼時間在什麼地方用的什麼裝置在重新整理),而上面版本的召回模型並沒有考慮這一塊。


之所以把上下文特徵單獨拎出來,是因為它有自己的特點,有些上下文特徵是近乎實時變化的,比如重新整理微博的時間,再比如對於美團嘀嘀這種對地理位置特別敏感的應用,使用者所處的地點可能隨時也在變化,而這種變化在召回階段就需要體現出來。所以,上下文特徵是不太可能像使用者特徵離線算好存起來直接使用的,而是使用者在每一次重新整理可能都需要重新捕獲當前的特徵值。動態性強是它的特點。


而考慮進來上下文特徵,如果我們希望構造和標準的FM等價的召回模型,就需要多考慮兩個問題:


問題一:既然部分上下文特徵可能是實時變化的,無法離線算好,那麼怎麼融入上文所述的召回計算框架裡?


問題二:我們需要考慮上下文特徵C和使用者特徵U之間的特徵組合,也需要考慮C和物品特徵I之間的特徵組合。上下文特徵有時是非常強的特徵。那麼,如何做能夠將這兩對特徵組合考慮進來呢?


我們可以這麼做:


640?wx_fmt=jpeg


首先,由於上下文特徵的動態性,所以給定使用者UID後,可以線上查詢某個上下文特徵對應的embedding向量,然後所有上下文向量求和得到綜合的上下文向量C。這個過程其實和U及I的累加過程是一樣的,區別無非是上下文特徵需要線上實時計算。而一般而言,場景上下文特徵數都不多,所以線上計算,速度方面應可接受。


640?wx_fmt=jpeg


然後,將線上算好的上下文向量C和這個使用者的事先算好存起來的使用者興趣向量U進行內積計算Score=<U,C>。這個數值代表使用者特徵和上下文特徵的二階特徵組合得分,算好備用。至於為何這個得分能夠代表FM中的兩者(U和C)的特徵組合,其實道理和上面講的U和I做特徵組合道理是一樣的。


640?wx_fmt=jpeg


再然後,將U和C向量累加求和,利用(U+C)去Faiss通過內積方式取出Top K物品,這個過程和極簡版是一樣的,無非查詢向量由U換成了(U+C)。通過這種方式取出的物品同時考慮到了使用者和物品的特徵組合<U,I>,以及上下文和物品的特徵組合<C,I>。道理和之前講的內容是類似的。


假設返回的Top K物品都帶有內積的得分Score1,再考慮上一步<U,C>的得分Score,將兩者相加對物品重排序(<U,C>因為跟物品無關,所以其實不影響物品排序,但是會影響最終得分,FM最外邊的Sigmoid輸出可能會因為加入這個得分而發生變化),就得到了最終結果,而這個最終結果考慮了U/I/C兩兩之間的特徵組合。


於是我們通過這種手段,構造出了一個完整的FM召回模型。這個召回模型通過構造user embedding,Context embedding和Item embedding,以及充分利用類似Faiss這種高效embedding計算框架,就構造了高效執行的和FM計算等價的召回系統。


如何將多路召回融入FM召回模型


上文所述是如何利用FM模型來做召回,下面我們討論下如何將多路召回統一到FM召回模型裡來。


我們以目前不同型別推薦系統中共性的一些召回策略來說明這個問題,以資訊流推薦為例子,傳統的多路召回階段通常包含以下策略:協同過濾,興趣分類,興趣標籤,興趣Topic,興趣實體,熱門物品,相同地域等。這些不同角度的召回策略都是較為常見的。


640?wx_fmt=jpeg


我們再將上述不同的召回路分為兩大類,可以把協同過濾作為一類,其它的作為一類,協同過濾相對複雜,我們先說下其它類別。


對於比如興趣分類,興趣標籤,熱門,地域等召回策略,要把這些召回渠道統一到FM模型相對直觀,只需要在訓練FM模型的時候,針對每一路的特性,在使用者特徵端和物品特徵端新增對應特徵即可。比如對於地域策略,我們可以把物品所屬地域(比如微博所提到的地域)和使用者的感興趣地域都作為特徵加入FM模型即可。興趣標籤,Topic,興趣實體等都是類似的。所以大多數情況下,在多路召回模式下你加入新的一路召回,在FM統一召回策略下,對應地轉化成了新增特徵的方式。


然後我們再說協同過濾這路召回。其實本質上也是將一路召回轉化為新加特徵的模式。我們上文在介紹FM模型和MF模型關係的時候提到過:本質上MF模型這種典型的協同過濾策略,是FM模型的一個特例,可以看作在FM模型裡只有User ID和Item ID這兩類(Fields)特徵的情形。


意思是說,如果我們將user ID和Item ID作為特徵放入FM模型中進行訓練,那麼FM模型本身就是包含了協同過濾的思想的。當然,對於超大規模的網站,使用者以億計,物品可能也在千萬級別,如果直接把ID引入特徵可能會面臨一些工程效率問題以及資料稀疏的問題。對於這個問題,我們可以採取類似在排序階段引入ID時的ID 雜湊等降維技巧來進行解決。


所以綜合來看,在多路召回下的每一路召回策略,絕大多數情況下,可以在FM召回模型模式中轉化為新增特徵的方式。


在具體實施的時候,可以沿著這個路徑逐步替換線上的多路召回:先用FM模型替換一路召回,線上替換掉;再新加入某路特徵,這樣上線,就替換掉了兩路召回;如此往復逐漸把每一路召回統一到一個模型裡。這是比較穩的一種替換方案。當然如果你是個猛人,直接用完整的FM召回模型一步替換掉線上的各路召回,也,未嘗不可。只要小流量AB測試做好也沒啥。



FM模型能否將召回和排序階段一體化


前文有述,之所以目前常見的工業推薦系統會分為召回排序兩個階段,是因為這兩個階段各司其職,職責分明。召回主要考慮泛化性並把候選物品集合數量降下來;排序則主要負責根據使用者特徵/物品特徵/上下文特徵對物品進行精準排名。


那麼,我們現在可以來審視下本文開頭提出的第二個問題了:FM模型能否將常見的兩階段模型一體化?即是否能將實用化的推薦系統通過FM召回模型簡化為單階段模型?意思是推薦系統是否能夠只保留FM召回這個模組,扔掉後續的排序階段,FM召回按照得分排序直接作為推薦結果返回。我們可以這麼做嗎?


這取決於FM召回模型是否能夠一併把原先兩階段模型的兩個職責都能承擔下來。這句話的意思是說,FM召回模型如果直接輸出推薦結果,那麼它的速度是否足夠快?另外,它的精準程度是否可以跟兩階段模型相媲美?不會因為少了第二階段的專門排序環節,而導致推薦效果變差?如果上面兩個問題的答案都是肯定的,那麼很明顯FM模型就能夠將現有的兩階段推薦過程一體化。


我們分頭來分析這個問題的答案:準確性和速度。先從推薦精準度來說明,因為如果精準度沒有辦法維持,那麼速度再快也沒什麼意義。


所以現在的第一個子問題是:FM召回模型推薦結果的質量,是否能夠和召回+排序兩階段模式接近?


我們假設一個是FM統一召回模型直接輸出排序結果;而對比模型是目前常見的多路召回+FM模型排序的配置。從上文分析可以看出,儘管FM召回模型為了速度夠快,做了一些模型的變形,但是如果對比的兩階段模型中的排序階段也採取FM模型的話,我們很容易推理得到如下結論:如果FM召回模型採用的特徵和兩階段模型的FM排序模型採用相同的特徵,那麼兩者的推薦效果是等價的。


這意味著:只要目前的多路召回都能通過轉化為特徵的方式加入FM召回模型,而且FM排序階段採用的特徵在FM召回模型都採用。那麼兩者推薦效果是類似的。這意味著,從理論上說,是可以把兩階段模型簡化為一階段模型的。


既然推理的結論是推薦效果可以保證,那麼我們再來看第二個問題:只用FM召回模型做推薦,速度是否足夠快?


我們假設召回階段FM模型對User embedding和Item embedding的匹配過程採用Facebook的Faiss系統,其速度快慢與兩個因素有關係:


  1. 物品庫中儲存的Item數量多少,Item數量越多越慢;

  2. embedding大小,embedding size越大,速度越慢。


微博機器學習團隊18年將Faiss改造成了分散式版本,並在業務易用性方面增加了些新功能,之前我們測試的查詢效率是:假設物品庫中儲存100萬條微博embedding資料,而embedding size=300的時候,TPS在600左右,平均每次查詢小於13毫秒。而當庫中微博數量增長到200萬條,embedding size=300的時候,TPS在400左右,平均查詢時間小於20毫秒。


這意味著如果是百萬量級的物品庫,embedding size在百級別,一般而言,通過Faiss做embedding召回速度是足夠實用化的。如果物品庫大至千萬量級,理論上可以通過增加Faiss的並行性,以及減少embedding size來獲得可以接受的召回速度。


當然,上面測試的是純粹的Faiss查詢速度,而事實上,我們需要在合併使用者特徵embedding的時候,查詢使用者特徵對應的embedding資料,而這塊問題也不太大,因為絕大多數使用者特徵是靜態的,可以線下合併進入使用者embedding,Context特徵和實時特徵需要線上線上查詢對應的embedding,而這些特徵數量佔比不算太大,所以速度應該不會被拖得太慢。


綜上所述,FM召回模型從理論分析角度,其無論在實用速度方面,還是推薦效果方面,應該能夠承載目前“多路召回+FM排序”兩階段推薦模式的速度及效果兩方面功能,所以推論它是可以將推薦系統改造成單模型單階段模式的。


當然,上面都是分析結果,並非實測,所以不能確定實際應用起來也能達到上述理論分析的效果。


總結


最後我簡單總結一下,目前看貌似利用FM模型可以做下面兩個事情:


首先,我們可以利用FM模型將傳統的多路召回策略,改為單模型單召回的策略,傳統的新增一路召回,可以轉換為給FM召回模型新增特徵的方式;其次,理論上,我們貌似可以用一個FM召回模型,來做掉傳統的“多路召回+排序”的兩項工作,可行的原因上文有分析。


這是本文的主要內容,謝謝觀看。本文開頭說過,關於統一召回模型,我打算寫一個四篇系列,後面會逐步介紹其它三類模型,它們是誰呢?它們可以用來做統一的召回模型嗎?如果能,怎麼做?如果不能,又是為什麼?它們可以替代掉兩階段推薦模型,一步到位做推薦嗎?這些問題的答案會是什麼呢?


關於上面這一系列《走近科學》風格的問題,因為我第一遍寫完的時間比較早,過了好一陣子翻開來看,當讀到上面這些問題的時候,自己都把自己吸引住了,對著問題思考了半天。這說明什麼?說明這些問題真的很有吸引力是嗎?其實不是,這隻能說明過去時間太長了,連我自己都記不清這些問題的答案是什麼了,哈哈。


我其實是個直率的人,要不,還是先主動劇透掉下篇文章的標題吧:


“連女神級程式媛美女看了都震驚!FFM模型居然能夠做這麼大規模推薦系統的召回!”背景音樂準備配置:房東的貓之《雲煙成雨》……那麼,女神級程式設計師到底是誰?她到底有多神?她到底有多美?她到底是不是女性?預知詳情,請聽下回分解。


致謝:感謝過去一段時間內,陸續和我討論統一多路召回模型及一體化推薦模型的微博機器學習團隊的雪逸龍,黃通文,佘青雲,張童,邸海波等同學,以及馮凱同學提供的Faiss效能測試資料。也許你們並不想讓自己的名字出現在這種風格的文章後邊,但是其實仔細想想,能看到這裡的人估計少得可憐,所以別介意想開點,哈哈。


原文連結:

https://zhuanlan.zhihu.com/p/58160982?utm_source=ZHShareTargetIDMore&utm_medium=social&utm_oi=40535670652928


(本文為 AI科技大本營轉載文章,轉載請聯絡原作者)


公開課推薦

明晚8點

640?wx_fmt=png

推薦閱讀:

                         640?wx_fmt=png

點選“閱讀原文”,檢視歷史精彩文章。

相關文章