環境設定
# !pip install cohere
# !pip install weaviate-client
import pandas as pd
import os
from dotenv import load_dotenv, find_dotenv
import cohere
co = cohere.Client(os.environ['COHERE_API_KEY'])
# Weaviate 是一個開源的向量資料庫(source vector)database
import weaviate
# 這段程式碼從環境變數取得 API 金鑰,
# 並使用它們建立連接到 Weaviate 資料庫的 client
# 從環境變數中取出 Weaviate 的 API key,並建立一個 auth_config 對象
這將用於後面連線時的驗證
auth_config = weaviate.auth.AuthApiKey(
api_key=os.environ['WEAVIATE_API_KEY'])
# 從 .env 檔案讀取環境變數
_ = load_dotenv(find_dotenv()) # read local .env file
auth_config = weaviate.auth.AuthApiKey(
api_key=os.environ['WEAVIATE_API_KEY'])
# 課程連結的DEMO資料庫是wikipedia的內容
# 包含100萬筆的資料、來自10種語言
client = weaviate.Client(
url=os.environ['WEAVIATE_API_URL'],
auth_client_secret=auth_config,
additional_headers={
"X-Cohere-Api-Key": os.environ['COHERE_API_KEY'],
}
)
# 完成資料庫連線
client.is_ready()
# True
實作def dense_retrieval()
.with_near_text(nearText)
這個部分
_additional {distance}
是在指定Weaviate查詢返回結果時,額外加入一個distance字段,用於表示結果文本和查詢文本之間的向量距離nearText
中的文本作為搜索查詢。nearText
字典中的"concepts"列表包含了查詢句子綜合起來,這段程式碼實現了:
def dense_retrieval(query,
results_lang='en',
properties = ["text", "title", "url", "views", "lang", "_additional {distance}"],
num_results=5):
nearText = {"concepts": [query]}
# To filter by language
where_filter = {
"path": ["lang"],
"operator": "Equal",
"valueString": results_lang
}
response = (
client.query
.get("Articles", properties)
.with_near_text(nearText)
.with_where(where_filter)
.with_limit(num_results)
.do()
)
result = response['data']['Get']['Articles']
return result
基礎難度查詢:"who wrote Hamlet?"
向量搜索返回關於Shakespeare寫Hamlet的相關內容
結果高度相關,表明向量搜索捕捉到了查詢意圖
If not specified explicitly, the default distance metric in Weaviate is cosine
query = "Who wrote Hamlet?"
dense_retrieval_results = dense_retrieval(query)
pd.DataFrame(dense_retrieval_results)
複雜查詢:"tallest person in history"
向量搜索返回有關最高人Robert Wadlow的文章,而關鍵詞搜索結果不相關
綜合來說,這三個例子展示了向量搜索比關鍵詞搜索更能捕捉語意和意圖,返回高度相關的結果。從基礎到複雜的查詢都表現出向量搜索的優勢
Chunking(文本切割)是一個重要的操作。它是將文本分成不同的段落或句子的過程,以便更好地進行文本嵌入(Embedding)和語意搜索。文件中提到了兩種常見的Chunking方式:
段落切割:這種方法將文本分成不同的段落。每個段落通常包含一個主題或一個完整的想法。在這種方式下,每個段落都可以被嵌入為一個向量,使得語意搜索更有效
句子切割:另一種方式是將文本分成不同的句子。每個句子被視為一個獨立的單位,並被嵌入為一個向量。這種方法的好處是可以更精細地捕捉每個句子的語意,但在處理一些句子缺乏上下文的情況下,可能需要將標題或其他上下文信息添加到句子中,以增加其語意完整性。
此外,文件提到Chunking的方式可能因不同的任務而有所不同,取決於需要處理的文本特性。最終,Chunking是語意搜索中的重要步驟之一,它有助於建立文本的嵌入表示形式,以便更有效地進行相似性比對和語意搜索。
課程的Chunking是用python內件.split()
土炮,langchain
有提供比較高階的作法
from annoy import AnnoyIndex
import numpy as np
import pandas as pd
import re
text = """
Interstellar is a 2014 epic science fiction film co-written, directed, and produced by Christopher Nolan...."""
土炮切割文件demo
# Split into a list of sentences
texts = text.split('.')
# Clean up to remove empty spaces and new lines
texts = np.array([t.strip(' \n') for t in texts])
# Split into a list of paragraphs
texts = text.split('\n\n')
# Clean up to remove empty spaces and new lines
texts = np.array([t.strip(' \n') for t in texts])
# 手動土炮插入標題
title = 'Interstellar (film)'
texts = np.array([f"{title} {t}" for t in texts])
response = co.embed(
texts=texts.tolist()
).embeddings
embeds = np.array(response)
print(embeds.shape) # (15, 4096)
在進行語意搜索或相似性比對時,需要一個有效的數據結構,以便在高維向量空間中快速查找最接近給定查詢的數據點。這個數據結構就是搜尋索引
具體操作步驟如下:
在最近鄰居搜索(nearest neighbors search)中,"建立 tree" 的概念是指建立一種數據結構,通常是樹狀結構,以便在高維向量空間中快速查找最接近給定查詢向量的數據點。這種數據結構可以幫助我們有效地組織和檢索大量的向量數據,以提高搜索的效率和速度。
最常見的樹狀結構是二叉樹(Binary Tree)或 K-d Tree(K維樹),它們將數據點分成不同的區域或子集,並通過比較查詢向量和每個區域的中心點,快速縮小搜索範圍。通過這種方式,我們可以避免對所有數據點進行耗時的比較,而只需比較可能最接近查詢的子集。
建立多棵樹(即建立樹的集合,通常稱為森林)可以進一步提高搜索效率。每棵樹可能使用不同的方法或特徵來分割數據,因此它們可以提供多種不同的搜索視角。當我們進行搜索時,可以並行地查詢每棵樹,並合併它們的結果,以獲得最終的最近鄰居。
使用AnnoyIndex建立搜尋索引實作程式碼
# Create an AnnoyIndex for nearest neighbor search with the same dimensions as the embeddings
search_index = AnnoyIndex(embeds.shape[1], 'angular')
# Add all the embeddings to the search index
for i in range(len(embeds)):
search_index.add_item(i, embeds[i])
# Build the search index with 10 trees (higher value can improve accuracy)
search_index.build(10) # 10 trees
# Save the search index to a file for future use
search_index.save('test.ann')
# Set Pandas option to display entire text in results
pd.set_option('display.max_colwidth', None)
# Define a search function that takes a query as input
def search(query):
# Get the embedding for the query using the Cohere API
query_embed = co.embed(texts=[query]).embeddings
# Retrieve the 3 nearest neighbors to the query's embedding
similar_item_ids = search_index.get_nns_by_vector(query_embed[0], 3, include_distances=True)
# Create a DataFrame to format the results
results = pd.DataFrame(data={'texts': texts[similar_item_ids[0]],
'distance': similar_item_ids[1]})
# Print the texts of the nearest neighbors
print(texts[similar_item_ids[0]])
return results
query = "How much did the film make?"
search(query)
Vector search libraries
特點 | Approximate Nearest-Neighbor | Vector Databases (Vector search libraries) |
---|---|---|
建立索引速度 | 相對較快 | 通常較慢 |
搜索速度 | 通常較快 | 通常較慢 |
內存使用 | 通常較低 | 通常較高 |
支援多維度向量 | 是 | 是 |
實時更新 | 有限支援 | 通常支援 |
精確性 | 可配置 | 高 |
特徵選擇和數據分割 | 有限控制 | 高度可配置 |
多語言支援 | 可能需要額外處理 | 通常支援 |
擴展性 | 有限擴展性 | 通常有較好的擴展性 |
維護複雜性 | 較低 | 較高 |
其中,Approximate Nearest-Neighbor 提供了快速的建立索引和搜索速度,但精確性有所減少,而 Vector Databases 則提供更高的精確性和更多的功能,但通常需要更多的資源和複雜性。
"Hybrid Search: Keyword + Vector"
是指在搜索引擎中結合兩種不同的搜索方法,一種是傳統的關鍵字搜索(Keyword Search),另一種是基於向量的語意搜索(Vector Search)。這種結合可以提高搜索引擎的效能和準確性,因為它充分利用了兩種搜索方法的優勢,同時彌補了它們各自的不足之處。
在混合搜索中,搜索引擎同時使用了關鍵字搜索和向量搜索兩種方法。首先,它可以使用關鍵字搜索來快速篩選出包含關鍵字的文檔,然後再使用向量搜索來進一步排序和選擇這些文檔,以返回最相關的結果。這種結合可以提供快速的搜索速度,同時確保搜索結果更加精確和有語意。
"Other signals"(其他信號)
這是指在搜索引擎結果排序中考慮除了關鍵字和向量相似性之外的其他因素或信號。這些額外的信號用於進一步優化搜索結果的排序,以確保最相關的文檔排名靠前。以下是更詳細的說明:
信號的綜合應用:搜索引擎通常使用複雜的算法來綜合考慮這些不同的信號,以確定文檔的最終排名。這種綜合考慮可以確保搜索結果不僅基於關鍵字匹配或向量相似性,還基於多種其他因素,以提供最佳的搜索體驗。
Google 在其搜索引擎中使用了多種其他信號,其中一個明顯的例子是 PageRank(頁面排名)算法。PageRank 考慮了不僅是頁面的內容,還包括頁面之間的連接結構,即其他網頁對該頁面的鏈接數量和質量。這個信號有助於確定網頁的權威性和重要性,進而影響搜索結果的排名。
向量搜尋的距離指標
cosine distance
Dot Product | Cosine Similarity | Cosine Distance | |
---|---|---|---|
幾何意義 | 計算兩向量在 同維度元素的乘積和 反映向量方向的一致性 |
在計算Dot Product的基礎上 對其進行標準化 消除向量長度對相似度的影響 |
Cosine Distance = 1 - Cosine Similarity |
公式 | |||
Range | -∞ 到 +∞ | -1 到 1 | 0 到 2 |
值域說明 | 正值: 方向相同 负值: 方向相反 0: 正交 |
1: 完全相似 0: 正交(無關) -1: 完全相反 |
0: 完全相似 1: 正交無關) 2: 完全相反 |
適用情境 | 向量的相似性計算如Attention機制 圖像、文本等表徵 信號處理、統計 |
文件檢索、句子匹配 推薦系統 聚類分析 |
文本分群、聚類 異常檢測、離群值判斷 樣本分類KNN |
Sparse Retrieval(稀疏檢索)
Dense Retrieval(密集檢索)
混合檢索(Mix/Hybrid Retrieval)
混合檢索是指結合稀疏和密集檢索器的方法來利用兩者的優勢。例如:
Ref
Sparse Retriever 中的 Term Frequency–Inverse Document Frequency (TF-IDF) 結合了TF(詞頻)和IDF(詞稀有度)的概念,給出了一個詞語在特定文檔中的重要性評分。如果一個詞語在一篇文檔中出現頻率很高,但在其他文檔中並不常見,那麼這個詞的TF-IDF得分會很高,這表明它對於理解這篇文檔的內容很重要。
TF-IDF反映了詞語的出現頻率(TF)與其在整個文檔集合中稀有度(IDF)的乘積
TF-IDF的計算由兩部分組成:Term Frequency (TF) 和 Inverse Document Frequency (IDF)。其計算公式如下:
Term Frequency (TF):
其中
Inverse Document Frequency (IDF):
其中
IDF(逆文檔頻率)是衡量詞語在整個文檔集合中稀有度的指標。一個詞語的IDF值隨著它在文檔集合中出現的文檔數量增加而降低。如果一個詞語幾乎在每篇文檔中都出現,那麼它的IDF值將接近0,表示它對於區分文檔不是很有用。相反,如果一個詞語很少出現,它的IDF值會很高,顯示它在集合中具有很好的區分度。
TF-IDF:
計算範例
假設有一個文檔集合
TF(apple, d):
IDF(apple, D):
TF-IDF(apple, d, D):
在這個範例中,"apple" 這個詞在文檔
BM25是一種基於機率的排名函數,用於文本檢索,主要在信息檢索系統中對文檔集合進行排序,以找到與用戶查詢最相關的文檔。它是TF-IDF模型的擴展,更加精細地考慮了各種影響排名的因素,例如詞頻飽和度和文檔長度。
BM25因考慮了文檔長度和詞頻飽和度,相較於傳統TF-IDF,能更準確地反映文檔的相關性。
BM25的計算公式如下:
其中:
計算範例
假設一個查詢
計算 "蘋果" 的
計算BM25得分:
這個得分代表了文檔