推薦系統工程篇之搭建以圖搜圖服務

基於內容的召回在推薦系統中是比較常見的召回策略,常見有基於用戶或物品的標籤召回或者基於用戶的年齡,地域等召回,通常該策略的實現是基於開源軟件 Elasticseach 實現的。雖然召回的結果都比較合理,可是召回的新穎度,驚喜度等都比較低。好比經過標籤「劉德華」進行召回,基本上召回的都是包含劉德華字眼的物品,不太可能召回出「黎明」,「張學友」等其餘四大天王的物品。近年隨着萬物皆可 Embedding,特別是 word2vec,item2vec, graph2vec 等技術的成功應用,經過物品向量召回物品向量的方法也成爲推薦系統中比較經常使用的召回策略。本文着重講述經過開源軟件 Vearch 來搭建一個向量搜索服務,併成功實現以圖搜圖的功能。node

介紹

最近一直在作小視頻的推薦優化,優化的目標是人均入屏。按照以前資訊流的推薦經驗,但願根據用戶的播放記錄召回更多相關的小視頻來給用戶消費。小視頻的推薦場景和如今比較火爆的抖音有點相似,就是自動播放一小段視頻(5-30s左右),該小視頻基本佔據了一整屏,用戶能夠對這個小視頻進行點贊,分享,評論;若是不喜歡能夠經過上滑來觀看下一個小視頻。考慮到決定用戶是否想看的因素不太多是視頻下方兩行的視頻標題,更多的因素還在於視頻的封面圖可否引發用戶的興趣。基於這樣的項目背景,因此纔想搭建一個以圖搜圖的服務來進行小視頻的封面圖召回。python

由於以前使用過 gRPC 封裝過 Faiss 來搭建向量召回服務,再加上以前作過圖片的分類項目,都是將圖片轉換爲向量做爲分類器的輸入,因此要作這個事基本要解決兩件事:mysql

  1. 對圖片進行預處理,並將其轉換爲固定長度的向量
  2. 將各個圖片的向量錄入 Faiss,並使用其完成向量搜索的任務

好比在博文Faiss 在項目中的使用中,做者就是使用 SIFT 算法進行圖片特徵的提取,這些特徵對應一個 128 維的向量。將每張圖片的特徵向量輸入給 Faiss 進行類似向量的召回。好比在博文基於gRPC的Faiss server實踐中,MXPlayer的技術團隊對原先基於 Flask 框架開發的用戶/物品向量召回服務進行了 gRPC 的升級,單機壓測 QPS 比以前高了2倍以上。原本打算是魔改以前 基於 gRPC 的 Faiss 服務來知足當期的業務場景需求的,可是偶然間發現京東開源的軟件 Vearch,就將以前的念頭掐滅了,並決定好好的學習這個開源軟件。git

服務組成

以圖搜圖的服務由兩部分組成,一個是向量搜索服務,由 Vearch 提供;一個是將圖片的特徵提取成特徵向量,由 Vearch 的插件 python-algorithm-plugin 提供。github

向量搜索服務-Vearch

Vearch 是對大規模深度學習向量進行高性能類似搜索的彈性分佈式系統。它的核心是向量搜索,是基於 Faiss 實現的叫 Gamma 引擎。不過除了向量搜索外, Gamma 還能夠存儲包含標量的文檔,並對這些標量字段進行快速索引和過濾。說白了,標量和矢量都支持,而通常的像 Elasticsearch只支持標量。Faiss 只能構建單機的向量搜索服務,而Vearch 以 Gamma 爲向量搜索引擎,使用 Raft 協議實現多副本存儲,提供 Master 和 Router組件來構建向量類似搜索的彈性分佈式系統。其架構圖以下:
Vearch架構圖
圖中主要有三大組件: Master, Router,PartitionServer,其功能以下:web

  • Master 負責 schema 管理,進行集羣級別的源數據和資源協調
  • Router 提供增刪改查的 RESTful API,對請求進行路由轉發和結果合併
  • PartitionServer 主要是基於raft協議實現多副本存儲,而具體的存儲,索引和檢索能力是由 Gamma 引擎提供。

從上面能夠知道 Gamma 之於 Vearch,就至關於 Lucene 之於 Elasticsearch。算法

圖片處理服務-Vearch插件

圖像處理的服務 Vearch 也提供了對應的插件python-algorithm-plugin。Vearch 的目標是構建一個高性能類似搜索的彈性分佈式系統。文本,圖片和視頻均可以轉換成向量,因此 Vearch 團隊提供了對應的插件來更好的集成到 Vearch 中。對於圖片,該插件提供了目標檢測,特徵提取和類似搜索等功能。其處理邏輯以下:
以圖搜圖
其邏輯就是從圖片中抽取向量特徵存儲進 Vearch 的 Gamma 引擎中,並提供檢索服務。sql

服務搭建

以圖搜圖的服務由兩個服務構成,一個是向量搜索服務,由 Vearch 提供;另外一個是圖片特徵提取爲向量,由 Vearch 的插件python-algorithm-plugin提供。docker

vearch

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 .

swarm啓動

由於兩個服務都已經打包成 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 指圖片處理服務的端口,默認 4101
  • gpus 指定服務是否使用 gpu,默認不用爲 -1
  • master_addressrouter_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

服務效果及上線

效果

在服務構建成功後,就須要查看一下以圖搜圖的效果,而對於效果的鑑別初步以人工爲準,最終以線上的指標數據爲準。對於一個服務效果的好壞在搭建服務之初就應該有個預期,好比:

  1. 相同的圖片類似度得接近 100%
  2. 相同類型的應該獲得相同類型的結果,好比用小狗搜索出小狗,用汽車搜索出汽車等

如下就是以圖搜圖的效果截圖:
animal
beauty
beauty2
car

整體來講效果仍是挺不錯的。

上線

推薦策略的上線方式有如下幾種:

  1. 直接服務線上,像排序模型。這種方式要求服務支持高併發,高性能和高可用
  2. 線上調用+緩存,像內容搜索。這種方式要求服務支持高性能和高可用,而且緩存有較大機率被命中
  3. 結果離線寫入緩存,像 cf,熱門等能夠提早算好的結果。

導入了小視頻最近 7 天新增和最近 30 天曝光的視頻共 9 萬數據導入到單機模式的 Vearch 服務中,沒法支撐兩個桶平均 48 QPS的衝擊,後使用第二種方式解決上線問題。而對應用戶向量(播放的圖片向量均值)搜索圖片向量策略經過第三種方式上線的。

參考文獻

  1. Faiss 在項目中的使用
  2. faiss-web-service
  3. 基於gRPC的Faiss server實踐
  4. 京東分佈式向量檢索系統vearch
  5. vearch中文文檔
  6. vearch核心引擎gamma
  7. vearch圖像處理插件
  8. 圖片搜索頁面

若是該文章對您產生了幫助,或者您對技術文章感興趣,能夠關注微信公衆號: 技術茶話會, 可以第一時間收到相關的技術文章,謝謝!
技術茶話會
本篇文章由一文多發平臺ArtiPub自動發佈

相關文章
相關標籤/搜索