乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭祕

作者:冉辰,騰訊 WXG 後臺開發工程師

微信掃一掃識物是典型的“離線寫,在線讀”的業務,業務數據的存儲和檢索庫的構建都是在離線環節完成。我們通過爬蟲系統收錄了小程序生態下的商品圖片,下載後進行檢測摳圖,提取檢索特徵,最終構建成檢索庫交付到線上環境。這篇文章將主要介紹這一部分的工作。

0 什麼是識物

播放

暫停

進入全屏

退出全屏

00:00

00:00

重播

刷新

試試

識物是以圖像或視頻作為輸入,用以挖掘微信生態下商品、物品等有價值等信息。這裡我們基本覆蓋了微信全量優質小程序電商,涵蓋上億商品 SKU,聚合了微信內的搜一搜、搜狗等資訊,最終聚合後呈現給用戶。百度識圖和阿里拍立淘也是基於該技術發展而來。

工程上,識物工作主要可以分為三塊,如圖 1 所示:

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖1

  1. 算法模型

算法側主要是對檢測模型和多類目的檢索模型等持續煉丹,檢測模型需要返回圖片中物品的準確位置;檢索模型需要保證同款物品的特徵表達越近越好。

  1. 離線工程

識物是典型的“離線寫,在線讀”的業務,業務數據的存儲和檢索庫的構建都是在離線環節完成。我們通過爬蟲系統收錄了小程序生態下的商品圖片,下載後進行檢測摳圖,提取檢索特徵,最終構建成檢索庫 交付到線上環境。這篇文章將主要介紹這一部分的工作

  1. 在線部署

算法模型和離線生成的檢索庫最終完成部署,對外服務。用戶識物時,檢索庫會召回一批相似物品,再經過一系列複雜的精排、過濾邏輯,最終返回用戶看到的結果。

1 挑戰

  1. 數據版本

數據版本主要分為兩類,一是算法模型版本,我們有 10+種業務模型,平均每週有 2-3 個模型迭代升級。二是檢索庫版本,在模型不迭代的情況下,每天有新數據的合併,即增量迭代;而每次算法模型變更,特徵表達發生改變,需要根據新特徵重新構建檢索庫,即全量迭代。

在高頻的版本變更場景下,如何兼顧靈活性與安全性。

  1. 數據處理性能

目前我們收錄的圖片數為 10 億左右,平均每天新增 1500w。除了圖片數量多,任務的流程也很多,如圖片下載、目標檢測、特徵提取等任務,每個任務每天都是千萬級的數據處理量。

如何高效的處理數據,提升業務的迭代效率。

  1. 繁雜的流程

隨著業務的發展,簡單的業務流程已經不能滿足我們日益複雜的業務需求。為了提升業務指標,我們可能還需要圖片質量,文本語義,死鏈、下架商品的過濾等任務。

如何在流程日益變多的情況下,不導致整個系統的臃腫。

  1. 數據質量

離線工程屬於重流程的業務,數據從產生和落地將經歷九九八十一環,任何一環出錯都會導致結果有問題。發現問題的時間越晚,修復的成本越高,對業務的影響越難以估計。

如何科學的監控和管理數據質量,使系統有良好的可維護性。

2 數據版本

這裡有多種維度的數據版本,例如模型版本,特徵版本,檢索庫版本等,上游環節的版本變更將引發後續環節的變更,最終都將導致檢索庫版本變更

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖2 數據流程簡圖

2.1 檢索庫

在我們的業務場景下,檢索庫的迭代是高頻操作,正常情況下每天會增量更新,而模型的變更又會引發檢索庫全量更新。數據量級上,我們的全量圖像是億級別的,按類目分庫後每個類目也是千萬級。

我們調研了業界內主要用於圖像檢索的技術,如圖 3 所示。綜合考慮後,我們選取了靈活性更強、相對內存佔用更小的的 faiss-ivf 作為我們的索引庫構建算法。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖3 圖像檢索庫選型

對於每天的增量數據,我們每天對每個類目(10+個類目)都會構造一個對應當天數據檢索庫。每個類目的全量檢索庫是由N 天的檢索庫合併生成(faiss-ivf 特性),2000w 的數據合併僅需要 4 分鐘。基於這樣的設計,使得我們可以靈活的選取時間窗口的範圍,如圖 3 所示了窗口為 2 的合併方法。

這樣的好處是,如果某天數據發現有問題,只需要修復當天數據後再進行合併即可;如果需要丟棄某些數據,如舊數據,合併時不選取即可。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖4 檢索庫生成

2.1 數據版本兼容

前面我們講到,模型變更最終都將引發檢索庫的全量迭代,這裡的模型有檢測模型和檢索特徵模型。新檢索庫上線時,本質上是新舊數據的過渡,一般實現新舊數據的切換都會設計複雜的系統來保證數據一致性。

2.2.1 檢測模型變更

這種場景下的檢索庫變更,嚴格上來講我們並沒有實現新舊數據的一致性,我們只是通過簡單的方法使得即使新舊數據同時存在也不影響用戶的體驗。

這裡主要涉及到如何構建我們的映射關係,我們為每次檢測出的結果都賦予一個唯一的單調遞增 id。替換模型後,同一張圖片的檢測結果會變化。可能摳圖的位置有變化、可能會扣取不同的物品、可能會扣取多個物品。

如圖 5 所示,檢索庫 v1 裡只有上衣,對應檢索 id 為 1;變更檢測模型後,檢索庫 v2 可以同時檢測出上衣和下衣,對應檢索 id 為 2,3。這樣在線模塊可以逐步更新檢索庫,線上同時存在新舊檢索庫也沒有影響,如果請求落到舊庫返回 1,落到新庫返回 2,但最終都將返回正確的結果,

結果上是一致的

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖5 檢測模型變更

2.2.2 檢索特徵模型變更

這種場景下的檢索庫變更則複雜許多,檢索庫存的特徵來自於檢索特徵模型。檢索模型變更後,同一個物品圖片的特徵表達完全不同,維度甚至也發生了變化,如圖 6 所示。

我們需要同步變更

檢索特徵模型服務新檢索庫,通過雙 buffer 的方式實現新舊數據的共存,而且要實現嚴格的路由協議來保證同一個請求在同版本的特徵檢索服務和檢索庫中完成。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖6 檢索特徵模型變更

2.3 數據版本管理系統

在開發過程中,算法需要交付各種模型給離線和在線,離線生成的檢索庫也需要交付給在線,數據版本的迭代也需要考慮版本的可回退性。為了解耦多方之間的依賴,且避免在同步過程中直接操作文件帶來的風險,設計了一套數據版本管理系統。

如圖 7 所示,資源發佈者上傳資源到該系統,並附帶對應業務、版本號及 md5。資源使用者只需要理解對應業務當前的版本號,版本管理系統會返回對應的資源文件。線上實際使用時,在線模塊會定期輪訓某業務對應數據版本文件的 md5 和本地文件 md5 是否一致,不一致則會拉取最新的文件,拉取完成後校驗 md5 是否一致,最終實現更新。

在業務模型或檢索庫需要回退時,只需修改配置文件,重啟服務即可。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖7 數據版本管理系統

2.4 docker 化

目標檢測、檢索特徵提取等是典型的圖像深度學習任務,業界內有 caffe、pytorch、tensorflow、tensorRT 等多種深度學習框架,有的框架不能保證向上兼容。而我們負責煉丹的同學第一要務是追求效果指標,在嘗試各種奇淫巧技時練出來的丹通常並不能和微信的線上環境很好的兼容。

簡而言之,在重算法的工程系統中,不僅有業務代碼的更新,還有工程環境的迭代。這非常適合使用 docker 來封裝和迭代業務環境。通過 docker 化部署,我們可以更方便的引入更多開源組件來支撐業務,也可以讓我們在一些框架選型上更加靈活。

就我們自己的業務場景而言,我們還可以利用微信深度學習任務平臺(yard)的計算資源,這部分屬於公用資源,需要搶佔式使用。yard 也是 docker 化去執行任務。這為我們業務可以藉助 yard 公用資源作為臨時擴容 worker 節點做了很好的鋪墊。

3 分佈式計算

我們每天平均有 1500w 增量數據,全量為十億級別的數據。單機必然無法滿足處理的實效性,唯有分佈式計算才能滿足要求。

3.1 數據拆分

正如 mapreduce,map 階段的工作我們需要對數據進行拆分。這裡對拆分原則除了平均外,還考慮了拆分後到數據的運行時間。如果拆分太細 GPU 的運行效率會降低,拆分太粗會導致錯誤修復的時間成本變大。我們讓每個拆分後的任務都儘量控制在 1 小時內完成,最終拆分的粒度為每個包 10w 左右。

3.2 數據並行計算

拆分後的數據進行並行計算相當於 reduce 階段,這裡的重點是如何將拆分後的數據分發到多臺機進行計算。此外,我們還希望公用資源空閒時可以非常靈活的進行擴容接入,提高併發處理能力。

我們結合 zookeeper 的分佈式鎖特性,實現了一套可靠分佈式任務隊列。worker 採用拉模式拉取隊列的任務。這樣的優點是伸縮性好,可以靈活的增加和減少 yard 的機器資源。如圖 8,當新 worker 接入後,從隊列中拉到任務直接執行,可以實現秒級的擴容。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖8 伸縮性好

對於我們的場景,任務需要被可靠消費,這裡的可靠包含來兩層含義。

第一是避免任務被重複消費,我們藉助 zookeeper 的保活鎖,鎖通過心跳保持活性。如圖 9 中第 1,2 時刻,worker 拿到隊列裡的任務搶鎖成功才可執行;如果出現機器宕機,如圖 9 第三 3 時刻,鎖會自動釋放。

第二是完整消費,我們在 task 被完全消費結束後才刪掉隊列裡的對應 task,如時刻 4 的 task2。時刻 3 由於機器宕機,task1 並未被完整消費,因此依舊存在,後續可被繼續消費。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖9 可靠消費

理論上講,我們的消費模式屬於至少一次消費(at least once),極端情況下,如果 worker 執行完任務還沒有回傳狀態時宕機,那任務仍處於未成功消費,仍可能被後續 worker 消費。這裡需要保證任務的冪等性。

引入公用計算資源提升了我們的處理能力,但同時也給我們帶來了一些小問題。例如,公用集群的機器配置比我們自己集群要好很多,為了使不同集群都能發揮最大的 GPU 性能,我們支持不同集群使用不同的全局參數配置。而且公用集群和文件系統不在同一個 idc,導致網絡 IO 時間過長,降低了 GPU 利用時間,我們在公用集群的同 idc 實現了一套文件預拉取系統,根據任務隊列中存在的任務,提前同步待消費文件到同 idc 的文件緩存系統。

為了提高 GPU 利用率,我們還做了大量的工程優化,這裡就不展開敘述了。基於分佈式計算的框架,極大提升了我們的計算效率。拿計算效率最低的目標檢測任務舉例,目前我們集群的處理能力可達到 5600w 圖/天,如果加上公用計算資源,可以達到 1.2 億圖/天(集群 12 臺 P4 雙卡,公用集群 yard-g7a 集群平均 10 個雙卡,深度學習框架使用的 tensorRT)。

4 任務調度

雖然我們每天有 1500w 左右的原始圖片,但最終符合錄入檢索庫的商品僅有一半不到。因為為了確保檢索數據的質量,我們會在多個維度做數據過濾。現在我們的圖片從下載到建庫一共會經歷 30+種中間任務,圖 10 僅展示了主要的任務流程模型。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖10 任務流

4.1 任務系統

隨著任務的增多,尤其是許多任務間存在著複雜的依賴關係,每個任務都不是一個獨立的個體,每個任務的成敗都將影響最終的結果。為了更好的管理每個任務的狀態,梳理任務間的依賴,使得工程的複雜度不隨任務變多而變大,我們自研了一套任務調度系統。

調度系統主要由以下幾個部分組成:

  • 文件系統:文件系統這裡使用了微信自研分佈式文件存儲系統的 WFS,我們所有中間數據和結果數據都存放在這裡
  • 存儲系統:主要有任務存儲和實例存儲,與一般實例存儲不同的是,為了分佈式計算,我們在數據維度和類目維度做了拆分,一個實例包含一個或多個子實例
  • 調度系統:主要負責收集、管理任務狀態,檢查任務依賴
  • 觸發器:定時輪訓調度系統,找到滿足執行條件的任務實例
  • 任務隊列:存儲待執行的任務實例,由 worker 獲取依次消費
乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖11 任務調度系統

容災性上,調度系統相關的模塊均是多機多園區部署,只要不是某個模塊完全掛掉,整套任務調度都可以正常執行。

4.2 在線服務合併部署

對於每天的例行任務,實效性並不敏感,早幾個小時或晚幾個小時對業務影響不大。但 GPU 資源是是十分寶貴的,我們將部分 GPU 機器和在線 GPU 服務合併部署。結合在線流量屏蔽策略,實現高峰時期資源借給在線服務,低峰時期運行離線任務。

如圖 12 所示,其為一臺參與離線任務閒時調度的在線模塊,我們擬定每天 0 點-7 點的低峰時間為離線運行時間,7 點-24 點的高峰時間為在線模塊服務時間。最大限度的利用了寶貴的機器資源。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖12 分時調度運行

5 數據質量

前面做的工作保證了我們以任務為粒度的工程可靠性,但任務的成功並不能保證業務數據是完整的,如數據丟失、代碼邏輯有問題等。為了監控數據維度上的業務質量,我們基於 ELK 搭建了一套數據系統,主要用於收集重要的基礎數據、業務數據、運行結果等.

5.1 數據可視化

我們曾在幾次版本迭代過程中,發現數據出錯,但發現時已經付出了極高的時間代價。因此我們希望在任意時刻都能觀察離線系統的運作是否正常,數據的流轉是否符合預期。出現問題後可以及時干預修正,降低錯誤成本。

我們對涉及數據流轉的核心任務都做了數據結果上報,這樣子我們可以通過數據漏斗發現是否出現問題。這個問題在全量數據重跑的時候尤其重要。圖 13 展示了項目中核心任務的數據情況。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖13 數據漏斗可視化

上圖看上去是每天任務級的數據監控,但實際上我們我們的設計是擴展到了**每次任務級(這裡定義為 planid),**既可以是每天,也可以是每次覆蓋多天的重跑。 我們按圖 14 的字段上報業務的運行結果,前 4 個字段組成聯合唯一索引,planid 作為區分每次運行的邏輯字段。這樣即使同一個任務在不同時期運行結果是不同的,我們也能區分每一次運行後,真實的數據結果。這個設計在保證每次大版本數據迭代時,對於把控數據整體運行質量十分重要也十分有效。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖14 上報字段

5.2 一致性檢查

數據可視化方便了我們檢查問題,但是還不利於我們發現問題。我們還需要在數據出問題時,能及時告警、迅速修復。這最最重要的就是數據一致性,這裡的一致性主要是一些核心任務的數據漏斗,輸入和輸出應該是一致的。圖 15 中展示了一些存在關聯的任務,帶顏色線段代表數據存在關聯性。

為了滿足各種維度的統計、校驗,同時又能快速支持新任務的檢查。我們封裝了核心的統計和校驗邏輯,配置化告警任務,確保層層流程運轉後的結果準確無誤。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖15 一致性檢查

5.3 評測系統

我們在對我們的檢索庫做比較大的版本迭代,或是線上策略有比較大調整時,直接灰度上線再觀察曲線有時並不能及時發現問題,存在很大的隱患。基於這種情況,我們開發了自動化測試系統。我們提前收集和整理了部分帶標籤的數據樣本,每次更新都需要在測試環境自動化評測一次,如圖 16 所示。我們在結合具體指標分析此次迭代是否可以安全上線(關鍵數據打碼)。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖16 評測系統

5.4 數據淘汰

我們平均每天流入數據超過千萬,數據膨脹的速度非常快,這給我們帶來了極大的存儲成本和迭代成本。但回顧業務本身,其實許多商品數據在隨著時間的推進,將變成過期、死鏈、下架數據。最簡單的做法就是使用窗口期來維護我們的數據,窗口外的數據自動淘汰,我們在 faiss 檢索庫選型時也是這樣考慮的。

但是我們也想到,直接暴力的淘汰舊數據也會有個致命問題。對於我們的業務而言,什麼數據對我們是重要的,常見的熱門商品固然重要,但是相對冷門長尾商品同樣重要,後者決定來商品庫長尾的多樣性。如果刪掉某個商品,檢索庫可能就沒有這個商品了,這是十分糟糕的。

因此我們在做數據淘汰的時候,需要考慮對有價值的長尾商品做保留。 圖 17 展示了我們數據淘汰的方式,通過這種方式,我們窗口期內的數據質量將變得越來越高,全量數據的增長也相對可控。

乾貨!微信掃一掃識物“摳圖”與“檢索”技術揭秘

圖17 窗口期為K的數據淘汰

6 總結

以上我們大致介紹了“掃一掃識物”的離線系統中的所涉及的一些關鍵點,部分模塊仍在持續優化中。未來掃一掃識物將引入更多場景的識別,拓展更多維度的物品,追求“萬物皆可掃”的目標。



推薦閱讀

乾貨!揭秘微信「掃一掃識物」 背後的硬核技術

“掃”與“拍”的差異,揭秘微信「掃一掃」識物為什麼這麼快


分享到:


相關文章: