Python利用Faiss庫實(shí)現(xiàn)ANN近鄰搜索的方法詳解
Embedding的近鄰搜索是當(dāng)前圖推薦系統(tǒng)非常重要的一種召回方式,通過item2vec、矩陣分解、雙塔DNN等方式都能夠產(chǎn)出訓(xùn)練好的user embedding、item embedding,對于embedding的使用非常的靈活:
- 輸入user embedding,近鄰搜索item embedding,可以給user推薦感興趣的items
- 輸入user embedding,近鄰搜搜user embedding,可以給user推薦感興趣的user
- 輸入item embedding,近鄰搜索item embedding,可以給item推薦相關(guān)的items
然而有一個(gè)工程問題,一旦user embedding、item embedding數(shù)據(jù)量達(dá)到一定的程度,對他們的近鄰搜索將會變得非常慢,如果離線階段提前搜索好在高速緩存比如redis存儲好結(jié)果當(dāng)然沒問題,但是這種方式很不實(shí)時(shí),如果能在線階段上線幾十MS的搜索當(dāng)然效果最好。
Faiss是Facebook AI團(tuán)隊(duì)開源的針對聚類和相似性搜索庫,為稠密向量提供高效相似度搜索和聚類,支持十億級別向量的搜索,是目前最為成熟的近似近鄰搜索庫。
接下來通過jupyter notebook的代碼,給大家演示下使用faiss的簡單流程,內(nèi)容包括:
- 讀取訓(xùn)練好的Embedding數(shù)據(jù)
- 構(gòu)建faiss索引,將待搜索的Embedding添加進(jìn)去
- 取得目標(biāo)Embedding,實(shí)現(xiàn)搜索得到ID列表
- 根據(jù)ID獲取電影標(biāo)題,返回結(jié)果
對于已經(jīng)訓(xùn)練好的Embedding怎樣實(shí)現(xiàn)高速近鄰搜索是一個(gè)工程問題,facebook的faiss庫可以構(gòu)建多種embedding索引實(shí)現(xiàn)目標(biāo)embedding的高速近鄰搜索,能夠滿足在線使用的需要
安裝命令:
conda install -c pytorch faiss-cpu
提前總結(jié)下faiss使用經(jīng)驗(yàn):
1. 為了支持自己的ID,可以用faiss.IndexIDMap包裹faiss.IndexFlatL2即可
2. embedding數(shù)據(jù)都需要轉(zhuǎn)換成np.float32,包括索引中的embedding以及待搜索的embedding
3. ids需要轉(zhuǎn)換成int64類型
1. 準(zhǔn)備數(shù)據(jù)
import pandas as pd import numpy as np
df = pd.read_csv("./datas/movielens_sparkals_item_embedding.csv")
df.head()
| id | features | |
|---|---|---|
| 0 | 10 | [0.25866490602493286, 0.3560594320297241, 0.15… |
| 1 | 20 | [0.12449632585048676, -0.29282501339912415, -0… |
| 2 | 30 | [0.9557555317878723, 0.6764761805534363, 0.114… |
| 3 | 40 | [0.3184879720211029, 0.6365472078323364, 0.596… |
| 4 | 50 | [0.45523127913475037, 0.34402626752853394, -0…. |
構(gòu)建ids
ids = df["id"].values.astype(np.int64)
type(ids), ids.shape
(numpy.ndarray, (3706,))
ids.dtype
dtype('int64')
ids_size = ids.shape[0]
ids_size
3706
構(gòu)建datas
import json
import numpy as np
datas = []
for x in df["features"]:
datas.append(json.loads(x))
datas = np.array(datas).astype(np.float32)
datas.dtype
dtype('float32')
datas.shape
(3706, 10)
datas[0]
array([ 0.2586649 , 0.35605943, 0.15589039, -0.7067125 , -0.07414215,
-0.62500805, -0.0573845 , 0.4533663 , 0.26074877, -0.60799956],
dtype=float32)
# 維度
dimension = datas.shape[1]
dimension
10
2. 建立索引
import faiss
index = faiss.IndexFlatL2(dimension)
index2 = faiss.IndexIDMap(index)
ids.dtype
dtype('int64')
index2.add_with_ids(datas, ids)
index.ntotal
3706
4. 搜索近鄰ID列表
df_user = pd.read_csv("./datas/movielens_sparkals_user_embedding.csv")
df_user.head()
id features
| id | features | |
|---|---|---|
| 0 | 10 | [0.5974288582801819, 0.17486965656280518, 0.04… |
| 1 | 20 | [1.3099910020828247, 0.5037978291511536, 0.260… |
| 2 | 30 | [-1.1886241436004639, -0.13511677086353302, 0…. |
| 3 | 40 | [1.0809299945831299, 1.0048035383224487, 0.986… |
| 4 | 50 | [0.42388680577278137, 0.5294889807701111, -0.6… |
user_embedding = np.array(json.loads(df_user[df_user["id"] == 10]["features"].iloc[0]))
user_embedding = np.expand_dims(user_embedding, axis=0).astype(np.float32)
user_embedding
array([[ 0.59742886, 0.17486966, 0.04345559, -1.3193961 , 0.5313592 ,
-0.6052168 , -0.19088413, 1.5307966 , 0.09310367, -2.7573566 ]],
dtype=float32)
user_embedding.shape
(1, 10)
user_embedding.dtype
dtype('float32')
topk = 30
D, I = index.search(user_embedding, topk) # actual search
I.shape
(1, 30)
I
array([[3380, 2900, 1953, 121, 3285, 999, 617, 747, 2351, 601, 2347,
42, 2383, 538, 1774, 980, 2165, 3049, 2664, 367, 3289, 2866,
2452, 547, 1072, 2055, 3660, 3343, 3390, 3590]])
5. 根據(jù)電影ID取出電影信息
target_ids = pd.Series(I[0], name="MovieID")
target_ids.head()
0 3380
1 2900
2 1953
3 121
4 3285
Name: MovieID, dtype: int64
df_movie = pd.read_csv("./datas/ml-1m/movies.dat",
sep="::", header=None, engine="python",
names = "MovieID::Title::Genres".split("::"))
df_movie.head()
| MovieID | Title | Genres | |
|---|---|---|---|
| 0 | 1 | Toy Story (1995) | Animation|Children's|Comedy |
| 1 | 2 | Jumanji (1995) | Adventure|Children's|Fantasy |
| 2 | 3 | Grumpier Old Men (1995) | Comedy|Romance |
| 3 | 4 | Waiting to Exhale (1995) | Comedy|Drama |
| 4 | 5 | Father of the Bride Part II (1995) | Comedy |
df_result = pd.merge(target_ids, df_movie) df_result.head()
| MovieID | Title | Genres | |
|---|---|---|---|
| 0 | 3380 | Railroaded! (1947) | Film-Noir |
| 1 | 2900 | Monkey Shines (1988) | Horror|Sci-Fi |
| 2 | 1953 | French Connection, The (1971) | Action|Crime|Drama|Thriller |
| 3 | 121 | Boys of St. Vincent, The (1993) | Drama |
| 4 | 3285 | Beach, The (2000) | Adventure|Drama |
總結(jié)
到此這篇關(guān)于Python利用Faiss庫實(shí)現(xiàn)ANN近鄰搜索的文章就介紹到這了,更多相關(guān)Python用Faiss庫ANN近鄰搜索內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- python 制作本地應(yīng)用搜索工具
- Python基于爬蟲實(shí)現(xiàn)全網(wǎng)搜索并下載音樂
- Python實(shí)現(xiàn)中英文全文搜索的示例
- python搜索算法原理及實(shí)例講解
- Python大批量搜索引擎圖像爬蟲工具詳解
- 利用python對mysql表做全局模糊搜索并分頁實(shí)例
- Python爬蟲爬取百度搜索內(nèi)容代碼實(shí)例
- python爬蟲開發(fā)之使用python爬蟲庫requests,urllib與今日頭條搜索功能爬取搜索內(nèi)容實(shí)例
- python實(shí)現(xiàn)全排列代碼(回溯、深度優(yōu)先搜索)
- python采集百度搜索結(jié)果帶有特定URL的鏈接代碼實(shí)例
- python 制作磁力搜索工具
相關(guān)文章
python實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出到excel的示例--普通格式
今天小編就為大家分享一篇python實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出到excel的示例--普通格式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05
Python使用read_csv讀數(shù)據(jù)遇到分隔符問題的2種解決方式
read.csv()可以從帶分隔符的文本文件中導(dǎo)入數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于Python使用read_csv讀數(shù)據(jù)遇到分隔符問題的2種解決方式,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
python目標(biāo)檢測基于opencv實(shí)現(xiàn)目標(biāo)追蹤示例
這篇文章主要為大家介紹了python基于opencv實(shí)現(xiàn)目標(biāo)追蹤示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
python3寫的簡單本地文件上傳服務(wù)器實(shí)例
今天小編就為大家分享一篇python3寫的簡單本地文件上傳服務(wù)器實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06
nx.adjacency_matrix計(jì)算鄰接矩陣與真實(shí)結(jié)果不一致的解決
這篇文章主要介紹了nx.adjacency_matrix計(jì)算鄰接矩陣與真實(shí)結(jié)果不一致的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12
基于Python實(shí)現(xiàn)PDF區(qū)域文本提取工具
這篇文章主要為大家介紹了如何通過Python實(shí)現(xiàn)一個(gè)非常精簡的圖像化的PDF區(qū)域選擇提取工具,文中示例代碼講解詳細(xì),感興趣的小伙伴可以學(xué)習(xí)一下2021-12-12

