Last Updated on 2023-06-09 by Clay
前言
最近在研究 Azure 上的各種服務,其中跟我正在做的任務較為相關的就是認知搜尋(cognitive search)這項服務。
不過近來,由於我們所作的許多相似度推薦都是基於如 SentenceTransformer 所使用的大型語言模型,所以基本上都是 embeddings、也就是向量的儲存方式,所以並不適用傳統的 BM25 搜尋演算法。
認知搜尋暫時還不支援向量搜尋(vector search)。但在微軟最近舉辦的 Microsoft Build 中公佈的消息有透露,可能即將就要支援了!(可以參考:https://news.microsoft.com/build-2023-book-of-news/)
在那之前,我得有個替代方案才行。在同事的推薦下,我找到了一個可行的備用方法並實作成功了:https://techcommunity.microsoft.com/t5/azure-data-explorer-blog/azure-data-explorer-for-vector-similarity-search/ba-p/3819626

在上方這篇技術文章的分享中,流程是使用 OpenAI 的模型來轉換 embedding,但是在我的流程中我使用了 SentenceTransformer 取代掉了這個部份。
什麼是 Azure Data Explorer?
Azure Data Explorer (ADX),也被稱為Kusto,是Microsoft Azure提供的一種服務,用於大規模地收集、存儲並運行快速分析在雲中和邊緣設備產生的數據,包括日誌、遙測數據、時間序列數據等。它可以快速且即時地運行分析,來提供實時見解。
這是一種無需預先設定模式的數據庫,支持高度靈活和可擴展的數據探索和分析,並且具有內建的語言(Kusto Query Language,KQL),使得數據探索變得容易。
在現代的雲原生和分散式環境中,能夠快速且有效地處理大量數據變得越來越重要。Azure Data Explorer正是為這種需要而設計,它能夠快速分析大規模數據,使企業能夠獲得實時的見解和價值。
實作方法
1. 建立叢集(clustering)
首先在 Azure 的服務中搜尋 “azue data explorer”,應該就能找到 Azure Data Explorer Clusters (ADX) 這項服務。

先啟動一個叢集,才能繼續操作後續步驟。
2. 建立資料庫
在建立好的叢集頁面裡,可以找到 Database creation 的選項,這裡點選 Create 按鈕可以直接建立一個資料庫。
3. 上傳資料(我是使用 CSV)
進入建好的資料庫後,應該可以看到 Ingest data 的按鈕,點擊後我們可以上傳我們的 CSV 資料。(或是其他格式)

需要注意的是,由於我們需要進行向量查詢,所以當我們在預覽 CSV 資料時,會需要把 embedding 欄位設定為 dynamic 的資料型態。
並且我們需要記得我們建立的資料庫跟資料表名稱,等等透過 python 搜尋時會用到。
4. 放入 cosine similarity 函式
點開資料庫頁面左側欄位的 Query,寫下:
.create-or-alter function with (folder = "Packages\\Series", docstring = "Calculate the Cosine similarity of 2 numerical arrays")
series_cosine_similarity_fl(vec1:dynamic, vec2:dynamic, vec1_size:real=double(null), vec2_size:real=double(null))
{
let dp = series_dot_product(vec1, vec2);
let v1l = iff(isnull(vec1_size), sqrt(series_dot_product(vec1, vec1)), vec1_size);
let v2l = iff(isnull(vec2_size), sqrt(series_dot_product(vec2, vec2)), vec2_size);
dp/(v1l*v2l)
}
並按下 Run 按鈕去執行,你應該會看見這個函式已經存在這個資料庫中。

這並非是內建函式,而是一個使用者自定義的函式(參考自 https://learn.microsoft.com/en-us/azure/data-explorer/kusto/functions-library/series-cosine-similarity-fl?tabs=query-defined) ,如果沒有先設定好這個函式,則後續的查詢會找不到這個函式。
5. 透過 python 程式進行向量搜尋
from typing importListfrom azure.kusto.data import KustoClient, KustoConnectionStringBuilder
from azure.kusto.data.exceptions import KustoServiceError
from azure.kusto.data.helpers import dataframe_from_result_table
import pandas as pd
from sentence_transformers import SentenceTransformer
# Settings
AAD_TENANT_ID = "<USER_ID>"
KUSTO_CLUSTER = "https://<CLUSTERING_NAME>.<LOCAL>.kusto.windows.net"
KUSTO_DATABASE = "<DATABASE_NAME>"# Kusto Connection String Builder
KCSB = KustoConnectionStringBuilder.with_aad_device_authentication(KUSTO_CLUSTER)
KCSB.authority_id = AAD_TENANT_ID
# Build the local client
KUSTO_CLIENT = KustoClient(KCSB)
# Embedding model
model = SentenceTransformer("./models/sentence-transformers-distiluse-base-multilingual-cased-v2/")
defto_embedding(content: str) -> List[float]:
returnlist(map(float, model.encode(content)))
# Searching
QUERY = "<YOUR_QUERY>"
KUSTO_QUERY = f"<TABLE_NAME> | extend similarity = series_cosine_similarity_fl(dynamic({str(to_embedding(QUERY))}), EMBEDDING, 1, 1) | top 10 by similarity desc"
RESPONSE = KUSTO_CLIENT.execute(KUSTO_DATABASE, KUSTO_QUERY)
df = dataframe_from_result_table(RESPONSE.primary_results[0])
df