愛奇藝逗芽表情搜尋分析與實踐

愛奇藝技術產品團隊發表於2020-07-15

隨著網際網路時代的發展,表情包成為現在大家網上交流的必備工具,針對表情搜尋的產品需求,愛奇藝逗芽技術團隊經歷了從ElasticSearch到Lucene再到結合語義的搜尋實踐之路。不同階段的技術選型可能可以為大家提供一些中小體量業務垂直領域搜尋的落地思路。

逗芽表情搜尋


愛奇藝逗芽表情()是一款透過影片AI演算法演算法,針對UGC、PGC等來源進行表情圖片生產,並在愛奇藝內外部多渠道分發的創新產品。使用者透過文字輸入搜尋好玩有趣的表情圖片是逗芽的核心功能之一。


透過文字進行表情搜尋常見的請求類別包括:

1. 實體名稱,比如熱門的明星名、角色名、影視劇名等,以及實體的別名與縮寫;

2. 偏口語化的感情、動作描述,如“開心”,“抱抱”,“想睡了”等;

  • 實體與動作的組合,如“加油蔡徐坤”,“虞書欣說的好”;

  • 流行的梗、短語,如“奧力給”,“專業團隊”,“我是誰我在哪”;

  • 表達完整含義的句子,如“你好,很高興認識你”。


愛奇藝逗芽表情搜尋分析與實踐


在不考慮視覺語義嵌入(Visual-Semantic embedding)[1]及其他相關度比較方式的情況下,表情圖片的搜尋主要依賴於使用者請求與圖片標籤(文字格式)的相關度做檢索。標籤可能包括圖片中的實體、文案、動作、情緒、類別等內容,具體標籤的生產一般會結合演算法與人工標註,不在本次討論範圍之內。對於不同類別的標籤,在匹配時可能有不同的排序策略。另外在搜尋口語化的短語、短句時,需要有一定的泛化能力來滿足召回。在業務層面,不同業務可能還需要過濾不同的圖片來源、型別、大小等。


表情搜尋的技術選型

目前關於“全文檢索”比較流行的技術選型當屬ElasticSearch(ES)[2],它是一款基於Lucene [3]的開源分散式近實時搜尋引擎,對外提供了簡單易用的HTTP API。


愛奇藝逗芽表情搜尋分析與實踐

圖1 ElasticSearch索引分層

一個ES索引可以切分為多個shard(分片)並分散式的儲存,來實現資料的橫向擴充套件,同時Shard也支援複製來提高資料的可用性。Segment(分段)為Lucene維護的最小倒排索引單元,一個Lucene索引可能包括一到多個segment。新的文件會寫到記憶體快取並以較小的時間間隔寫入新的segment,避免耗時的大索引重建,提供了近實時檢索支援。


一個圖片文件主要包括三類欄位:

一類,是圖片元資訊如各個尺寸的大小、解析度、CDN地址等;

二類,是運營相關資訊如圖片的來源、入庫時間、稽核、推薦狀態等;

三類,是前面提到的標籤資訊如圖片的文案、人物、表情、動作、分類標籤等。


依賴上述欄位ES search API可以透過布林查詢過濾符合業務需求的圖片,再透過function_score自定義評分函式進行圖片排序,比如不同標籤欄位和運營欄位在匹配時可以有不同的權重。因此對不同的業務會去維護不同的相對複雜的查詢JSON檔案。

愛奇藝逗芽表情搜尋分析與實踐

圖2 ES圖片搜尋框架

在服務架構上我們使用HBase配合ES做圖片總庫,其中HBase儲存了全量圖片各個階段處理的所有資訊,如演算法提取的特徵、圖片來源詳情等,ES儲存了一定時間段內已去重待運營的和已稽核透過的圖片文件。線上搜尋ES和運營ES隔離,一方面減少了搜尋叢集的索引文件數量(從數千萬到近百萬)讓索引檔案能儘可能在作業系統檔案快取中,另一方面避免了運營操作導致的檔案快取頻繁換出。多地ES叢集透過訊息佇列實現和主庫間的資料同步,提供了跨地域的服務高可用。基於ES的搜尋架構可以滿足我們早期業務的搜尋需求。


搜尋服務的技術升級
隨著對搜尋需求的提高,基於ES的搜尋服務逐漸遇到瓶頸:新接入的業務要求更高的QPS和更低的延時,產品也希望透過更定製化的排序邏輯來提高搜尋相關度。在一段時間的原型調研後,我們開始使用Lucene來替換ES做搜尋。

愛奇藝逗芽表情搜尋分析與實踐

圖3 Lucene架構

切換到Lucene意味著從ES HTTP API切換到Java庫呼叫的方式,ES提供的分散式索引管理和秒級近實時索引功能也不再可用。但是Lucene提供的介面足以讓開發者透過相對簡單的編碼實現單個索引的建立和搜尋功能。並且Lucene提供瞭如Analyzer在內的擴充套件機制讓開發者能方便的做一些高階定製。

總體上切換到Lucene基於如下考慮:

1.    業務對搜尋內容實時性要求不高,可以按天離線建立只讀索引,百萬量級索引的構建在十分鐘內就可以完成,在必要時(如有圖片急需上下線)也可以運營透過介面手動觸發索引建立。

2.    百萬量級索引大小在一百兆位元組左右,可以輕鬆放全量儲存在單機記憶體中,不需要做分片和搜尋合併,保證了效能。

3.    不同業務間索引隔離,配合容器可以針對業務獨立部署,橫向擴充套件和跨地域部署都極為方便。

4.    更容易定製分詞、排序等功能。

愛奇藝逗芽表情搜尋分析與實踐

圖4 Lucene搜尋框架

服務架構上索引服務(Indexer)服務負責離線索引,定期從圖片總庫中建立不同業務的Lucene索引,搜尋服務(Searcher)服務根據配置獲取對應的業務Lucene索引,線上執行Lucene搜尋。索引和搜尋服務使用了相同的Analyzer,定製使用了HanLP分詞器和外部詞典(ES IK [4]也支援遠端詞典,不過同樣需要在詞典更新後定期更新索引)做Tokenizer,自定義了否定詞結合TokenFilter,確保如“不開心”這類詞不被分詞為“不”和“開心”。


索引服務和搜尋服務透過Zookeeper進行索引資訊的同步。不同業務方包含了不同版本號,不同版本號有不同的索引建立規則。不同時間建立的索引資訊會寫入對應到業務方對應版本號節點下。索引服務定期更新分詞器使用的實體、停用詞詞典,並掃描圖片總庫將不同業務方需要的圖片標籤資訊和圖片ID寫入不同的Lucene索引。完成寫入的索引透過forceMerge最佳化索引分段,儲存到物件儲存儲存中,並更新對應Zookeeper節點。搜尋服務透過監聽相關Zookeeper節點線上更新索引,並回收舊的索引。


業務服務透過gRPC訪問搜尋服務,再透過圖片ID獲取圖片元資訊儲存返回給使用者。圖片元資訊使用多級快取最佳化效能,如記憶體快取配合CouchBase,也可以將原ES搜尋引擎作為圖片元資訊的降級儲存。


基於Lucene的搜尋架構QPS擴充套件方便,效能也由原300毫秒的P99延時下降到100毫秒以內,搜尋相關度也有提高,滿足了業務需求。


語義召回

表情搜尋一個比較明顯的特點是請求中包含有較多日常情緒、動作類短語,在召回時如果僅使用TF-IDF或BM25做字元級別的召回,容易導致召回或相關度偏低的情況。透過引入語義層面的召回我們可以解決這個問題,比如,“興高采烈”在Lucene下很難召回圖片,透過語義則可以關聯到“眉開眼笑”、“喜滋滋”等相關標籤圖片提高召回;“混吃等死”在Lucene下會把“等”字單獨分出來導致“等著”、“等你”這類相關度比較低的圖片被召回,透過語義相似度過濾也可以避免這種情況。


語義召回可以拆解為使用者請求和圖片標籤的短文字相似度檢索問題。將圖片庫內圖片標籤編碼為固定長度的稠密向量,並儲存到向量索引中,就可以實現使用者請求和標籤的最近鄰查詢。像Annoy或Faiss等向量索引都可以做到千萬量級下的毫秒級召回。


將短文字對映到稠密向量的方式也有不少,在NLP領域可以被歸納到句嵌入(sentence embedding)範疇。句嵌入編碼可以利用RNN、DAN(Deep Averaging Network)、Transformer等多種編碼器對句子做編碼的監督學習。下圖即為Google 2019年提出的多語言句編碼器的多工訓練框架[5]。


愛奇藝逗芽表情搜尋分析與實踐

圖5 Google Universal Sentence Encoder訓練結構


句嵌入編碼也可以直接使用預訓練模型,先利用大語料非監督學習訓練出來的詞向量(如Word2Vec、Fasttext)或上下文編碼器(如BERT、ELMo)進行token級別的編碼,再透過工程或統計方式(平均、TF-IDF、SIF等)將非固定長度的token編碼轉化為固定長度的句嵌入。


在對比測試並考慮開發成本的基礎上,逗芽目前使用了預訓練詞向量取平均的方式進行句嵌入編碼,並進行了一些針對性最佳化。


我們採用基礎分詞加BiMM (Bi-directional Maximal Matching)的方式來儘可能匹配詞向量詞典中更長的詞,提高短語、短句的匹配度。其次由於詞向量本身基於詞語在語料中的上下文訓練,導致不少反義詞或同類詞向量空間中距離較近,如“難過”和“開心”,“美女”和“帥哥”,不符合語義召回的預期,我們透過自定義反義詞詞典進行了詞向量counter-fitting [6]微調,提升語義相關度。


愛奇藝逗芽表情搜尋分析與實踐

圖6 Glove詞向量最近鄰counter-fitting前後對比

語義召回的離線索引和線上檢索的服務均由Python實現,對外提供gRPC介面。詞向量和文案標籤使用Annoy [7]索引儲存,詞向量相對需要較多記憶體(8G左右),但是Annoy透過mmap進行載入,多個程式間可以共享記憶體,記憶體使用效率較高。服務透過容器平臺部署,語義召回平均請求耗時約15毫秒,單機QPS約500。


排序策略

在引入語義召回後我們把使用者搜尋請求做實體與非實體的區分,實體部分直接透過Lucene召回。非實體部分Lucene和語義都做召回,Lucene召回部分對匹配的長度和順序做了較嚴格的要求,語義召回部分會找到相似的topN標籤,再過Lucene全匹配召回對應圖片。

愛奇藝逗芽表情搜尋分析與實踐

圖7 結合語義召回

搜尋服務在拿到語義和實體的召回後再進行統一排序,排序過程中我們將實體部分和非實體部分的相關性得分壓縮到同一分數區間內,然後輔助以圖片質量分進行排序。其中圖片質量分會在索引階段透過圖片來源、時效性、熱度等資訊預先計算好,在排序時再從索引中讀取。在加入語義和完善排序策略後搜尋相關度有了明顯提升,針對高頻和非高頻搜尋請求測試NDCG [8]分數提高近20%。

神配圖應用

神配圖是將使用者輸入直接新增到圖片上的一種新型表情互動方式,神配圖返回的圖片也可以作為表情搜尋的一個較好補充,尤其在使用者搜尋不到圖片的情況下。


愛奇藝逗芽表情搜尋分析與實踐

圖8 神配圖示例

神配圖原始圖片的召回和前面語義召回部分類似,原始圖片的文案、情緒、動作等標籤經過句編碼後構建成向量索引,使用者請求句編碼後在向量索引上做最近鄰召回。在介面返回前再對神配圖召回和搜尋結果進行融合與排序。


由於文字需要實時新增到圖片上,我們目前使用了讀請求的後端加字方式,即搜尋結果返回時並沒有真正的圖片加字,只有在使用者端上請求訪問圖片的CDN地址時並回源時才進行圖片的生產,保證了介面效能和整體使用者體驗。


總結和展望

本文回顧了逗芽表情搜尋在不同業務階段的不同實現方式。在定製化要求不多,實時性和吞吐量需求也不是很強的情況下ElasticSearch可以的滿足大部分使用場景。在定製化和服務能力需求更高的情況下,可以先基於ES做針對性的最佳化和配置,如果還不能滿足需求可以考慮直接使用Lucene。在語義召回方面,可以優先使用預訓練模型,以較低的開發成本獲得相對明顯的提升。如果服務體量更大可能需要從倒排索引開始自建搜尋引擎,就不在本次討論範圍之內了。


目前逗芽表情搜尋還有很多待最佳化的空間,例如標註語料最佳化句編碼模型讓語義召回更準確;引入更多維度特徵或L2R最佳化排序;支援實時性更高的增量索引等等。產品上還有不少和搜尋相關的應用場景可以繼續挖掘,如個性化推薦、表情對話、以圖搜圖、基於視覺語義的圖片檢索等等。


搜尋是一件原型容易,最佳化難的開發任務。軟體工程沒有銀彈,搜尋實現也沒有,但是作為開發者我們可以根據團隊資源和業務需求找到合適的搜尋落地之路。

愛奇藝逗芽表情搜尋分析與實踐

參考文件

[1] 視覺語義嵌入(Visual-Semantic embedding)Frome, A. 2013. Devise: A deep visual-semantic embedding model

[2] ElasticSearch(ES):

[3] Lucene:

[4] ES IK:

[5] 多語言句編碼器的多工訓練框架:https://ai.googleblog.com/2019/07/multilingual-universal-sentence-encoder.html

[6] counter-fitting:Nikola Mrki. 2016. Counter-fitting Word Vectors to Linguistic Constraints

[7] Annoy:

[8] NDCG:https://en.wikipedia.org/wiki/Discounted_cumulative_gain

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945252/viewspace-2704714/,如需轉載,請註明出處,否則將追究法律責任。

相關文章