以前寫過一篇概述: 以圖搜圖系統概述 。html
以圖搜圖系統須要解決的主要問題是:es6
對應的工程實踐,具體爲:數據庫
使用卷積神經網路 CNN 去提取圖像特徵是一種主流的方案,具體的模型則可使用 VGG16 ,技術實現上則使用 Keras + TensorFlow ,參考 Keras 官方示例:api
from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input
import numpy as np網絡
model = VGG16(weights='imagenet', include_top=False)數據結構
img_path ='elephant.jpg'
img = image.load_img(img_path, target_size=(224,224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)app
features = model.predict(x)
這裏提取出來的 feature 就是特性向量。數據庫設計
一、歸一化工具
爲了方便後續操做,咱們經常會將 feature 進行歸一化的處理:性能
from numpy import linalg as LA
norm_feat = feat[0]/LA.norm(feat[0])
後續實際使用的也是歸一化後的 norm_feat 。
二、Image 說明
這裏加載圖像使用的是 keras.preprocessing 的 image.load_img 方法即:
from keras.preprocessing import image
img_path ='elephant.jpg'
img = image.load_img(img_path, target_size=(224,224))
其實是 Keras 調用的 TensorFlow 的方法,詳情見 TensorFlow 官方文檔 ,而最後獲得的 image 對象實際上是一個 PIL Image 實例( TensorFlow 使用的 PIL )。
三、Bytes 轉換
實際工程中圖像內容經常是經過網絡進行傳輸的,所以相比於從 path 路徑加載圖片,咱們更但願直接將 bytes 數據轉換爲 image 對象即 PIL Image :
import io
from PIL importImage
img =Image.open(io.BytesIO(img_bytes))
img = img.convert('RGB')
img = img.resize((224,224),Image.NEAREST)
以上 img 與前文中的 image.load_img 獲得的結果相同,這裏須要注意的是:
四、黑邊處理
有時候圖像會有比較多的黑邊部分(例如截屏),而這些黑邊的部分即沒有實際價值,又會產生比較大的干擾,所以去除黑邊也是一項常見的操做。
所謂黑邊,本質上就是一行或一列的像素點所有都是 (0, 0, 0) ( RGB 圖像),去除黑邊就是找到這些行或列,而後刪除,實際是一個 numpy 的 3-D Matrix 操做。
移除橫向黑邊示例:
import numpy as np
from keras.preprocessing import image
defRemoveBlackEdge(img):
"""移除圖片橫向黑邊
Args:
img: PIL image 實例
Returns:
PIL image 實例
"""
width = img.width img = image.img_to_array(img) img_without_black = img[~np.all(img == np.zeros((1, width,3), np.uint8), axis=(1,2))] img = image.array_to_img(img_without_black)
return img
CNN 提取圖像特徵以及圖像的其它相關處理先寫這麼多,咱們再看向量搜索引擎。
只有圖像的特徵向量是遠遠不夠的,咱們還須要對這些特徵向量進行動態的管理(增刪改),以及計算向量的類似度並返回最鄰近範圍內的向量數據,而開源的向量搜索引擎 Milvus 則很好的完成這些工做。
下文將會講述具體的實踐,以及要注意的地方。
一、對 CPU 有要求
想要使用 Milvus ,首先必需要求你的 CPU 支持 avx2 指令集,如何查看你的 CPU 支持哪些指令集呢?對於 Linux 系統,輸入指令
cat /proc/cpuinfo | grep flags
你將會看到形如如下的內容:
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti intel_ppin tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm xsaveopt cqm_llc cqm_occup_llc dtherm ida arat pln pts
flags 後面的這一大堆就是你的 CPU 支持的所有指令集,固然內容太多了,我只想看是否支持具體的某個指令集,好比 avx2 , 再加一個 grep 過濾一下便可:
cat /proc/cpuinfo | grep flags | grep avx2
若是執行結果沒有內容輸出,就是不支持這個指令集,你只能換一臺知足要求的機器。
二、容量規劃
系統設計時,容量規劃是須要首先考慮的地方,咱們須要存儲多少數據,這些數據須要多少內存以及多大的磁盤空間?
速算,上文中特徵向量的每個維度都是 float32 的數據類型,一個 float32 須要佔用 4 byte,那麼一個 512 維的向量就須要 2 KB ,依次類推:
若是咱們但願能將數據所有存在內存中,那麼系統就至少須要對應大小的內存容量。
這裏推薦你使用官方的大小計算工具: milvus tools
實際上咱們的內存可能並無那麼大(內存不夠不要緊,milvus 會將數據自動刷寫到磁盤上),另外除了這些原始的向量數據以外,還會有一些其餘的數據例如日誌等的存儲也是咱們須要考慮的地方。
三、系統配置
關於系統配置,官方文檔有比較詳細的說明:
四、數據庫設計
collection & partition
在 Milvus 中,數據會按照 collection 和 partition 進行劃分:
partition 分區在底層實現上其實與 collection 集合是一致的,只是前者從屬於後者,可是有了分區以後,數據的組織方式變得更加靈活,咱們也能夠指定集合中某個特定分區進行查詢,從而達到一個更高的查詢性能,更多內容參考 分區表詳細說明 。
咱們可使用多少個 collection 和 partition ?因爲 collection 和 partition 的基本信息都屬於元數據,而 milvus 內部進行元數據管理須要使用 SQLite( milvus 內部集成)或者 MySQL (須要外部鏈接) 其中之一,若是你使用默認的 SQLite 去管理元數據的話,當集合和分區的數量過多時,性能損耗會很嚴重,所以集合和分區總數不要超過 50000(0.8.0 版本將會限制爲 4096),須要設置更多的數量則建議使用外接 MySQL 的方式。
Milvus 的 collection 和 partition 內部支持的數據結構很是簡單,只支持 ID + vector ,換句話說,表只有兩列,一列是 ID ,一列是向量數據。
注意:
條件過濾
咱們使用一些傳統的數據庫時,每每能夠指定字段進行條件過濾,可是 Milvus 並不能直接支持這項功能,然而咱們是能夠經過集合和分區的設計去實現簡單的條件過濾,例如,咱們有不少圖片數據,可是這些圖片數據都明確的屬於具體的用戶,那麼咱們就能夠按照用戶去劃分 partition ,這樣查詢的時候以用戶做爲過濾條件其實就是指定 partition 便可。
結構化數據與向量的映射
因爲 milvus 只支持 ID + vector 的數據結構,而實際業務上咱們最終須要的每每是具備業務意義的結構化數據,也就是說,咱們須要經過 vector 向量最終找到結構化數據,所以咱們須要經過 ID 去維護結構化數據與向量之間的映射關係:
結構化數據 ID <-->映射表<-->Milvus ID
索引類型選擇
請參考如下文檔:
五、搜索結果處理
Milvus 的搜索結果是 ID + distance 的集合:
ID : collection 中的 ID 。
distance : 0 ~ 1 的距離值,表示類似性程度,越小越類似。
過濾 ID 爲 -1 的數據
當數據集過少的時候,搜索結果可能會包含 ID 爲 -1 的數據,咱們須要本身去過濾掉。
翻頁
向量的搜索比較特別,查詢的結果是按照類似性順序,從最類似開始日後選取 topK 個數據( topK 須要搜索時由用戶指定)。
Milvus 的搜索不支持翻頁,若是咱們但願在業務上實現這個功能,那麼只能由咱們本身去處理,好比,我想要每頁 10 條數據,只顯示第 3 頁的數據,那麼咱們須要去取 topK = 30 的數據,而後只返回最後 10 條。
業務上的類似性閾值
兩張圖片的特徵向量的距離 distance 範圍是 0 ~ 1 ,有些時候咱們須要在業務上去斷定兩張圖片是否類似,這時就須要咱們本身去設置一個距離的閾值,當 distance 小於閾值時就能夠斷定爲類似,大於閾值時斷定爲不類似,這個也是須要根據具體的業務本身去處理。
本文講述了以圖搜圖系統進行工程實踐時比較常見的內容,最後強烈推薦一下 Milvus 。