編者按:此白皮書為谷歌總結的機器學習(ML)最優實踐方法,濃縮了其多年技術積累與經驗,尤其是 YouTube、Google Play 和 Google+ 等平臺背後的 ML 算法開發、維護經歷。谷歌於白皮書中總結了四十三條 ML 黃金法則,旨在幫助已經掌握了基礎知識的開發者少走彎路。本文上接雷鋒網"谷歌機器學習白皮書全解析 43條黃金法則(一)"
3.0 機器學習第二階段
3.1 特徵工程
在進行機器學習相關實踐的第一階段,你要關注的主要問題包括以下三個方面:一是將訓練數據導入系統,二是確定系統的重點關注指標,三是保證底層基礎設施的穩定可靠。當這三個問題都確認無誤,即已經搭建了一個端到端的可穩定運行的系統,並且針對系統本身和其中的每個單元都經過了嚴格測試,這時就可以進入第二階段了。
應該說,第二階段將更容易取得成績。這一階段會有許多顯著的特徵(feature)被導入系統,因此,導入並以直觀的方式組合這些特徵,將是本階段涉及的主要任務。另外,本階段也更適合多位工程師協同工作,共同對此前導入的訓練數據進行整合和處理,推動所有的指標(metric)在本階段取得持續性的上升。
16. 做好模型被推倒和重建的準備
不要指望從頭到尾只使用一個模型,也不要指望著某一結點之後就不用重建模型了,模型的推倒和重建是機器學習過程中的必修課。另外,每加入一個新特性都必須考慮是否會拉低模型的運行效率。目前,許多團隊每三個月或一年就會新建一個模型。這裡我們總結了一般情況下引發模型重建的三大原因:
1) 增加新的特徵
2) 打算重組舊的特徵,或對舊模型正則化
3) 修訂建模目標
無論如何,創建模型時多想想並沒有什麼壞處:例如檢查訓練數據是否有更合理的組織形式,考慮當前的建模方式是否便於特徵的修改和重組,當前的機器學習流水線(pipeline)是否便於創建副本並檢驗其正確率,以及是否可以創建兩到三個副本並行運行等等。最後需要指出的是,並不一定非要在一個機器學習流水線中覆蓋所有特徵,在下一個版本中實現也是可行的。
17. 直接以觀察到的或報告的特徵開始訓練,而不是經過學習的特徵
這一點建議或許存在一些爭議,但的確能避免許多潛在的問題。這裡經過學習的特徵(learned feature)是指由外部系統(例如無監督的聚類系統)或模型本身(例如通過深度學習和因子模型)產生的特徵。這兩種情況雖然的確可以使用,但並不適合系統的第一個模型。
首先,在使用外部系統創建特徵時必須要格外小心。因為外部系統的目標可能與當前系統並不相符,而且從外部系統更新當前系統的特徵,其特定的含義也可能改變。
另一方面,因子模型和深度模型的主要問題是它們是非凸的(non-convex),因此它們無法保證可以最終找到或近似找到最優解,它們在每次迭代中產生的局部最小值都可能變化,而且目前無法評估這種變化對系統的影響是有益的還是有害的。通過創建沒有深度特徵的模型,你就可以獲得很好的基準性能。在實現這一基準性能之後,你可以嘗試更高階的方法。
18. 從不同的上下文環境中提取特徵
通常情況下,機器學習只佔到一個大系統中的很小一部分,因此你必須要試著從不同角度審視一個用戶行為。比如熱門推薦這一場景,一般情況下論壇裡「熱門推薦」裡的帖子都會有許多評論、分享和閱讀量,如果利用這些統計數據對模型展開訓練,然後對一個新帖子進行優化,就有可能使其成為熱門帖子。另一方面,YouTube上自動播放的下一個視頻也有許多選擇,例如可以根據大部分用戶的觀看順序推薦,或者根據用戶評分推薦等。總之,如果你將一個用戶行為用作模型的標記(label),那麼在不同的上下文條件下審視這一行為,可能會得到更豐富的特徵(feature),也就更利於模型的訓練。需要注意的是這與個性化不同:個性化是確定用戶是否在特定的上下文環境中喜歡某一內容,並發現哪些用戶喜歡,喜歡的程度如何。
19. 儘量選擇更具體的特徵
在海量數據的支持下,即使學習數百萬個簡單的特徵也比僅僅學習幾個複雜的特徵要容易實現。由於被檢索的文本標識與規範化的查詢並不會提供太多的歸一化信息,只會調整頭部查詢中的標記排序。因此你不必擔心雖然整體的數據覆蓋率高達90%以上,但針對每個特徵組裡的單一特徵卻沒有多少訓練數據可用的情況。另外,你也可以嘗試正則化的方法來增加每個特徵所對應的樣例數。
20. 以合理的方式組合、修改現有的特徵
目前有多種方法組合、修改現有的特徵,由於本文以Google工具為背景,因此在這裡推薦兩種TensorFlow框架已實現好的方法:「離散化」(discretizations)和「交叉」(crosses)。
離散化主要包含提取連續特徵和從連續特徵中創建離散特徵兩個部分。比如對於年齡這一連續的特徵,你就可以創建這樣的離散特徵:當年齡小於18時結果為1,或者當年齡介於18-35之間時為1,等等。另外,不要過分考慮直方圖中基本分位數的問題。
在TensorFlow的術語中,特徵欄是一組相似的特徵,比如{男性,女性},{美國,加拿大,墨西哥}等。這裡的交叉是指將兩個或多個特徵欄合併,例如{男性,女性}×{美國,加拿大,墨西哥}的結果就是一個交叉(a cross),也就構成了一個新的特徵欄。假設你利用TensorFlow框架創建了這樣一個交叉,其中也就包含了{男性,加拿大}的特徵,因此這一特徵也就會出現在男性加拿大人的樣例中。需要注意的是,交叉方法中合併的特徵欄越多,所需要的訓練數據量就越大。
如果通過交叉法生成的特徵欄特別龐大,那麼就可能引起過擬合。例如,假設你正在進行某種搜索,並且在查詢請求和文檔中都具有一個包含關鍵字的特徵欄。那麼假如你選擇用交叉法組合這兩個特徵欄,這樣得到的新特徵欄就會非常龐大,它內部包含了許多特徵。當這種情況發生在文本搜索場景時,有兩種可行的應對方法。最常用的是點乘法(dot produc),點乘法最常見的處理方式就是統計查詢請求和文檔中共同的所有特徵詞,然後對特徵離散化。另一個方法是交集(intersection),比如若且唯若關鍵詞同時出現在文檔和查詢結果中時,我們才能獲取所需的特徵。
21. 通過線性模型學到的特徵權重的數目,大致與數據量成正比
許多人都認為從一千個樣例中並不能得到什麼可靠的訓練結果,或者由於選擇了某種特定的模型,就必須獲取一百萬個樣例,否則就沒法展開模型訓練。這裡需要指出的是,數據量的大小是和需要訓練的特徵數是正相關的:
1) 假如你在處理一個搜索排名問題,文檔和查詢請求中包含了數百萬個不同的關鍵詞,並且有一千個被標記的樣例,那麼你應該用上文提到的點乘法處理這些特徵。這樣就能得到一千個樣例,對應了十幾個特徵。
2) 如你有一百萬個樣例,那麼通過正則化和特徵選擇的方式就可以交叉處理文檔和查詢請求中的特徵欄,這可能會產生數百萬的特徵數,但再次使用正則化可以大大減少冗餘特徵。這樣就可能得到一千萬個樣例,對應了十萬個特徵。
3) 如果你有數十億或數百億個樣例,那同樣可以通過特徵選擇或正則化的方法交叉處理文檔和查詢請求中的特徵欄。這樣就可能得到十億個樣例,對應了一千萬個特徵。
對特徵數和樣例來說,這些統計學上的結論並不能給出一個具體的比例關係,但卻可以從數量級上給出一些指導。另外,這裡推薦用戶依照第28條建議來選擇具體使用哪些特徵。
22. 清理不需要的特徵
如果你發現有些特徵並沒有在使用,而且將其與其他特徵相結合之後也無法使用的話,就應該清理這些特徵。應該保持系統的清潔,這樣才能儘快嘗試那些最有希望出結果的特徵。而且,如果有必要,被刪除的特徵也可以隨時找人加回來。
在考慮增刪一個特徵時,應該仔細排查其覆蓋範圍。例如你有一些個性化的特徵,但只有大約8%的用戶使用了該特徵,那麼刪掉或添加這個特徵就不會有太大影響。
另一方面,增刪特徵時也要考慮其對應的數據量。例如你有一個只覆蓋了1%數據的特徵,但有90%的包含這一特徵的樣例都通過了訓練,那麼這就是一個很好的特徵,應該添加。
3.2 對系統的人工分析
在進入機器學習實踐的第三階段之前,關注一些課堂上不曾教授的問題也同樣至關重要,比如如何檢查一個模型並改進它。要說這一點是一門科學,反而不如說它是一種藝術,這裡我們介紹幾點反面模式(anti-patterns)。
23. 你並不是一個典型的用戶
這可能是讓一個團隊陷入困境的最簡單的方法。雖然fishfooding(只在團隊內部使用原型)和dogfooding(只在公司內部使用原型)都有許多優點,但無論哪一種,開發者都應該首先確認這種方式是否符合性能要求。另一方面,應該儘量避免不好的變化,但任何看起來合理的產品策略都應該被進一步驗證,例如通過非專業人士在眾包平臺上的問卷調查,或者請目標用戶來實測。
走外部驗證渠道的原因來自兩個方面:一是作為開發者,你太熟悉代碼。例如你可能正在分析數據的某一方面而非全局,或者投入了太多的個人感情色彩,從而引發一些偏見。二是幾位工程師開一個小時的討論會議得到的評估結果,可能遠比不上直接交給眾包平臺來得簡單和有效。
如果你真的想要獲取用戶反饋,那麼應該採用用戶體驗法(user experience methodologies)。 在流程早期創建用戶角色(詳情見Bill Buxton的《Designing User Experiences》一書),然後進行可用性測試(詳情見Steve Krug的《Do not Make Me Think》一書)。這裡的用戶角色涉及創建假想用戶。例如,假設你的團隊成員都是男性,現在要針對35歲女性用戶研發一款產品,那麼基於目標群體創建一個假想角色,肯定比幾位25-40歲的男性開發者閉門造車的效果要好。當然,讓用戶實測產品並觀察他們的反應也是很不錯的方法。
24. 版本之間存在對等差分(symmetric difference)
將產品交付至用戶之前,有時候最簡單有效的做法就是評估當前版本與交付版本的差異。例如面對排名問題,你可以在兩個版本間利用同一組樣例進行測試,然後對比其結果。如果差異很小,那麼意味著這個版本沒問題。如果差異很大,那麼就需要確認進行了哪些修改,為什麼進行這些修改。通過查看哪些測試樣例造成了這一差異,也有助於定性了解修改具體是怎樣的。總之,目標是確保不同版本的模型之間的對等差分做到最小。
25. 選擇模型時,性能勝過預測能力
你的模型可能會被用來預測點擊率,但更關鍵問題是:這種預測是應用在什麼場景的。如果你用它來排列文檔,那麼最終排名的質量顯然比預測本身更重要。如果你用它來排查垃圾郵件,那麼識別精度顯然更重要。大多數情況下,這兩類功能應該是一致的,如果他們存在不一致,則意味著系統可能存在某種小增益。因此,假如一個改進措施可以解決日誌丟失的問題,但卻造成了系統性能的下降,那就不要採用它。當這種情況頻繁發生時,通常應該重新審視你的建模目標。
26. 從誤差中查找新模式、創建新特徵
假設你的模型在某個樣例中預測錯誤。在分類任務中,這可能是誤報或漏報。在排名任務中,這可能是一個正向判斷弱於逆向判斷的組。但更重要的是,在這個樣例中機器學習系統知道它錯了,需要修正。如果你此時給模型一個允許它修復的特徵,那麼模型將嘗試自行修復這個錯誤。
另一方面,如果你嘗試基於未出錯的樣例創建特徵,那麼該特徵將很可能被系統忽略。例如,假設在谷歌Play商店的應用搜索中,有人搜索「免費遊戲」,但其中一個排名靠前的搜索結果卻是一款其他App,所以你為其他App創建了一個特徵。但如果你將其他App的安裝數最大化,即人們在搜索免費遊戲時安裝了其他App,那麼這個其他App的特徵就不會產生其應有的效果。
所以,正確的做法是一旦出現樣例錯誤,那麼應該在當前的特徵集之外尋找解決方案。例如,如果你的系統降低了內容較長的帖子的排名,那就應該普遍增加帖子的長度。而且也不要拘泥於太具體的細節。例如你要增加帖子的長度,就不要猜測長度的具體含義,而應該直接添加幾個相關的特徵,交給模型自行處理,這才是最簡單有效的方法。
27. 嘗試量化觀察到的異常行為
有時候團隊成員會對一些沒有被現有的損失函數覆蓋的系統屬性感到無能為力,但這時抱怨是沒用的,而是應該盡一切努力將抱怨轉換成實實在在的數字。例如,當有些開發者認為在谷歌Play商店的搜索結果中顯示了過多的其他App,就可以選擇人工識別的方法剔除這些App(這時是可以選擇人工標記數據的,因為相對較小的App查詢可能佔了很大一部分流量)。首先要確認你的問題是可量化的,然後才可以根據這些問題創建新的特徵(features)、目標(objectives)或者指標(metrics)。總之規則是:先量化,再優化。
28. 注意短期行為和長期行為的差別
假設你有一個新系統,它可以查看每個doc_id和exact_query,然後根據每個文檔的每次查詢行為計算其點擊率。你發現它的行為幾乎與當前系統的並行和A/B測試結果完全相同,而且它很簡單,於是你啟動了這個系統。但卻沒有新的應用顯示,為什麼?由於你的系統只基於自己的歷史查詢記錄顯示文檔,所以不知道應該顯示一個新的文檔。
要了解一個系統在長期行為中如何工作的唯一辦法,就是讓它只基於當前的模型數據展開訓練。這一點非常困難。雷鋒網
未完待續,請見雷鋒網「谷歌機器學習白皮書全解析 43 條黃金法則(三)」。谷歌白皮書原文地址:http://martin.zinkevich.org/rules_of_ml/rules_of_ml.pdf