基於內容的召回在推薦系統中是比較常見的召回策略,常見有基於用戶或物品的標籤召回或者基於用戶的年齡,地域等召回,通常該策略的實現是基於開源軟件 Elasticseach 實現的。雖然召回的結果都比較合理,可是召回的新穎度,驚喜度等都比較低。好比經過標籤「劉德華」進行召回,基本上召回的都是包含劉德華字眼的物品,不太可能召回出「黎明」,「張學友」等其餘四大天王的物品。近年隨着萬物皆可 Embedding,特別是 word2vec,item2vec, graph2vec 等技術的成功應用,經過物品向量召回物品向量的方法也成爲推薦系統中比較經常使用的召回策略。本文着重講述經過開源軟件 Vearch 來搭建一個向量搜索服務,併成功實現以圖搜圖的功能。node
最近一直在作小視頻的推薦優化,優化的目標是人均入屏。按照以前資訊流的推薦經驗,但願根據用戶的播放記錄召回更多相關的小視頻來給用戶消費。小視頻的推薦場景和如今比較火爆的抖音有點相似,就是自動播放一小段視頻(5-30s左右),該小視頻基本佔據了一整屏,用戶能夠對這個小視頻進行點贊,分享,評論;若是不喜歡能夠經過上滑來觀看下一個小視頻。考慮到決定用戶是否想看的因素不太多是視頻下方兩行的視頻標題,更多的因素還在於視頻的封面圖可否引發用戶的興趣。基於這樣的項目背景,因此纔想搭建一個以圖搜圖的服務來進行小視頻的封面圖召回。python
由於以前使用過 gRPC 封裝過 Faiss 來搭建向量召回服務,再加上以前作過圖片的分類項目,都是將圖片轉換爲向量做爲分類器的輸入,因此要作這個事基本要解決兩件事:mysql
好比在博文Faiss 在項目中的使用中,做者就是使用 SIFT 算法進行圖片特徵的提取,這些特徵對應一個 128 維的向量。將每張圖片的特徵向量輸入給 Faiss 進行類似向量的召回。好比在博文基於gRPC的Faiss server實踐中,MXPlayer的技術團隊對原先基於 Flask 框架開發的用戶/物品向量召回服務進行了 gRPC 的升級,單機壓測 QPS 比以前高了2倍以上。原本打算是魔改以前 基於 gRPC 的 Faiss 服務來知足當期的業務場景需求的,可是偶然間發現京東開源的軟件 Vearch,就將以前的念頭掐滅了,並決定好好的學習這個開源軟件。git
以圖搜圖的服務由兩部分組成,一個是向量搜索服務,由 Vearch 提供;一個是將圖片的特徵提取成特徵向量,由 Vearch 的插件 python-algorithm-plugin 提供。github
Vearch 是對大規模深度學習向量進行高性能類似搜索的彈性分佈式系統。它的核心是向量搜索,是基於 Faiss 實現的叫 Gamma 引擎。不過除了向量搜索外, Gamma 還能夠存儲包含標量的文檔,並對這些標量字段進行快速索引和過濾。說白了,標量和矢量都支持,而通常的像 Elasticsearch只支持標量。Faiss 只能構建單機的向量搜索服務,而Vearch 以 Gamma 爲向量搜索引擎,使用 Raft 協議實現多副本存儲,提供 Master 和 Router組件來構建向量類似搜索的彈性分佈式系統。其架構圖以下:
圖中主要有三大組件: Master, Router,PartitionServer,其功能以下:web
從上面能夠知道 Gamma 之於 Vearch,就至關於 Lucene 之於 Elasticsearch。算法
圖像處理的服務 Vearch 也提供了對應的插件python-algorithm-plugin。Vearch 的目標是構建一個高性能類似搜索的彈性分佈式系統。文本,圖片和視頻均可以轉換成向量,因此 Vearch 團隊提供了對應的插件來更好的集成到 Vearch 中。對於圖片,該插件提供了目標檢測,特徵提取和類似搜索等功能。其處理邏輯以下:
其邏輯就是從圖片中抽取向量特徵存儲進 Vearch 的 Gamma 引擎中,並提供檢索服務。sql
以圖搜圖的服務由兩個服務構成,一個是向量搜索服務,由 Vearch 提供;另外一個是圖片特徵提取爲向量,由 Vearch 的插件python-algorithm-plugin提供。docker
Vearch 是用 Go 編寫的,而其核心引擎 Gamma 是用 C++ 編寫的(畢竟Faiss 也是 C++ 開發的),因此服務安裝部署比較簡單粗暴,只要設置好依賴的 lib 包(Faiss,Gamma,RocksDB),並有編譯好的二進制文件 vearch,對於單機模式直接使用 ./vearch -conf config.toml
進行服務的啓動,而對於集羣服務,經過最後的命令參數 ./vearch -conf config.toml ps/router/master
進行配置。數據庫
不過由於咱們線上服務器 Gcc 的版本過低,沒有 Go 環境等因素,因此採用的是 Docker 方式。鑑於要詳細瞭解 Faiss 服務是如何演變爲 Vearch 這個彈性分佈式系統的,因此採用的源碼編譯安裝。
# 下載源碼 git clone https://github.com/vearch/vearch # 切換到鏡像編譯目錄 cd vearch/cloud # 打包環境鏡像 vearch/vearch_env:3.2.2,將gcc,git,faiss,rocksdb,go等安裝好 # 這步打包比較慢,能夠直接使用官方鏡像 docker pull vearch/vearch_env:3.2.2 sh compile_env.sh # 使用 vearch_env 編譯二進制文件 vearch,主要是拉取 gamma 源碼進行編譯 sh compile.sh # 打包 vearch/vearch:3.2.2, 將打包好的二進制文件vearch和依賴的庫放到鏡像中。 # 能夠直接使用官方鏡像 docker pull vearch/vearch:3.2.2 sh build.sh
官方的鏡像打包仍是有優化的空間的,打包建議使用centos源,準備好 Faiss, RocksDB, Go等源文件。
圖像處理的服務沒有現成的 Docker 鏡像,並且 Github 倉庫上提供的鏡像打包有問題,可使用如下的倉庫進行打包。
# 下載源碼(使用修正後的 Dockfile 文件) git clone -b study https://github.com/haojunyu/python-algorithm-plugin # 切換到鏡像目錄並打包鏡像 vearch/images:3.2.2 # 能夠直接使用打包好的鏡像 docker pull haojunyu/vimgs:3.2.2 cd python-algorithm-plugin && docker build -t haojunyu/vimgs:3.2.2 .
由於兩個服務都已經打包成 Docker 鏡像了,這裏直接使用命令 docker stack deploy -c docker-compose.yml vearch
來啓動服務, docker-compose.yml 內容以下:
version: '3.3' services: vearch: image: vearch/vearch:3.2.2 ports: - "8817:8817" - "9001:9001" volumes: - ./config.toml:/vearch/config.toml - ./data:/datas - ./logs:/logs deploy: mode: replicated replicas: 1 restart_policy: condition: on-failure delay: 10s max_attempts: 3 logging: driver: "json-file" options: max-size: "1g" imgs: image: haojunyu/vimgs:3.2.2 ports: - "4101:4101" volumes: - ./python-algorithm-plugin/src/config.py:/app/src/config.py - ./images/imgs:/app/src/imgs command: ["bash", "../bin/run.sh", "image"] deploy: mode: replicated replicas: 3 restart_policy: condition: on-failure delay: 10s max_attempts: 3
注意: 掛載文件 python-algorithm-plugin/src/config.py 是圖片處理服務的配置文件,通常只須要針對本身的狀況改動如下四個配置:
port
指圖片處理服務的端口,默認 4101gpus
指定服務是否使用 gpu,默認不用爲 -1master_address
和 router_address
指 Vearch 服務的 master 和 router服務由於圖片服務和 Vearch 服務是高度集成的。通常是直接調用圖片服務,而圖片向量錄入 Vearch交給圖片服務本身處理。Vearch 詳細的操做能夠參考文檔。
# 這裏master_server指vearch主節點及其對應端口:localhost:8817 # 查看集羣狀態 curl -XGET http://master_server/_cluster/stats # 查看健康狀態 curl -XGET http://master_server/_cluster/health # 查看端口狀態 curl -XGET http://master_server/list/server # 清除鎖(在建立表時會對集羣加鎖,若在此過程當中,服務異常,會致使鎖不能釋放,須要手動清除才能新建表。) curl -XGET http://master_server/clean_lock # 副本擴容縮容 curl -XPOST -H "content-type: application/json" -d' { "partition_id":1, "node_id": 1, "method": 0 } ' http://master_server/partition/change_member
庫和空間的概念相似mysql裏面的數據庫和表的概念。
# 查看及羣衆全部的庫 curl -XGET http://master_server/list/db # 建立庫 curl -XPUT -H "content-type:application/json" -d '{ "name": "sv_month" } ' http://master_server/db/_create # 查看庫 curl -XGET http://master_server/db/$db_name # 刪除庫(庫下存在表空間則沒法刪除) curl -XDELETE http://master_server/db/$db_name # 查看指定庫下全部表空間 curl -XGET http://master_server/list/space?db=$db_name
# 在庫sv_month下建立表空間test(針對image) curl -XPUT -H "content-type: application/json" -d '{ "name":"test", "partition_num":1, "replica_num":1, "engine":{ "name":"gamma", "index_size":70000, "max_size":10000000, "id_type":"String", "retrieval_type":"IVFPQ", "retrieval_param":{ "metric_type":"InnerProduct", "ncentroids":256, "nsubvector":32 } }, "properties":{ "itemid":{ "type":"keyword", "index":true }, "feature1":{ "type":"vector", "dimension":512, "model_id":"vgg16", "format":"normalization" } } }' http://image_server:4101/space/sv_month/_create
# 插入本地圖片數據到表空間中 curl -XPOST -H "content-type: application/json" -d' { "itemid":"COCO_val2014_000000123599", "feature1":{ "feature":"../images/COCO_val2014_000000123599.jpg" } } ' http://image_server:4101/sv_month/test/AW63W9I4JG6WicwQX_RC
# 查詢類似結果 curl -H "content-type: application/json" -XPOST -d '{ "query": { "sum": [ { "feature":"../images/COCO_val2014_000000123599.jpg", "field":"feature1" }] } }' http://image_server:4101/sv_month/test/_search
在服務構建成功後,就須要查看一下以圖搜圖的效果,而對於效果的鑑別初步以人工爲準,最終以線上的指標數據爲準。對於一個服務效果的好壞在搭建服務之初就應該有個預期,好比:
如下就是以圖搜圖的效果截圖:
整體來講效果仍是挺不錯的。
推薦策略的上線方式有如下幾種:
導入了小視頻最近 7 天新增和最近 30 天曝光的視頻共 9 萬數據導入到單機模式的 Vearch 服務中,沒法支撐兩個桶平均 48 QPS的衝擊,後使用第二種方式解決上線問題。而對應用戶向量(播放的圖片向量均值)搜索圖片向量策略經過第三種方式上線的。
若是該文章對您產生了幫助,或者您對技術文章感興趣,能夠關注微信公衆號: 技術茶話會, 可以第一時間收到相關的技術文章,謝謝!
本篇文章由一文多發平臺ArtiPub自動發佈