生成 AI(大型語言模型)用於應用程式開發時,RAG(檢索增強生成)已成為不可或缺的技術。在將獨特數據整合到 ChatGPT 或 Claude 等大型語言模型時,許多開發者可能會考慮將 RAG 作為選項。
支撐 RAG 的核心技術之一是向量相似度檢索。然而,對於「餘弦相似度是什麼」這部分常常感到模糊,明明覺得懂,但卻很難清楚地向他人解釋。
本文將重新整理文本向量化及比較的機制,以及代表性的指標——餘弦相似度。
在使用生成 AI 時,可能會遇到無法正確回答組織特有資訊或知識的問題。針對這類問題,RAG 試圖通過從外部數據庫檢索相關資訊,並將其包含於大型語言模型的提示中來解決。
在 RAG 中,「如何精確獲取與問題相關的信息」至關重要。傳統的關鍵字搜尋無法理解意義上的關聯性,這是一大挑戰。為了解決這一問題,採用了將文本的意義表示為向量並計算其相似度的方式。
餘弦相似度是一種測量兩個向量「角度接近度」的方法。
它的取值範圍從 -1 到 1,具體可描述如下:
兩個向量 a 和 b 的餘弦相似度,根據以下公式計算:
$$\cos(a,b) = \frac{\mathbf{a} \cdot \mathbf{b}}{|\mathbf{a}| |\mathbf{b}|} = \frac{\sum_{i=1}^{n} a_i bi}{\sqrt{\sum{i=1}^{n} ai^2} \sqrt{\sum{i=1}^{n} b_i^2}}$$
以下是三個向量的相似度計算示例。
以下是計算相似度的代碼。
我這裡手動計算,但如果使用 sklearn,已經有 cosine_similarity()
這個函數可用。
import numpy as np
import matplotlib.pyplot as plt
# 2D 可視化
def visualize_cosine_similarity():
# 示例向量
vec1 = np.array([3, 4])
vec2 = np.array([4, 3])
vec3 = np.array([-3, 4])
vectors = {'vec1': vec1, 'vec2': vec2, 'vec3': vec3}
# 計算餘弦相似度
def cosine_sim(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# # 使用 sklearn 的實例(註解掉)
# from sklearn.metrics.pairwise import cosine_similarity
# def cosine_sim_sklearn(a, b):
# return cosine_similarity([a], [b])[0][0]
# 繪圖
fig, ax = plt.subplots(figsize=(8, 8))
colors = ['red', 'blue', 'green']
for (name, vec), color in zip(vectors.items(), colors):
ax.arrow(0, 0, vec[0], vec[1], head_width=0.2,
head_length=0.2, fc=color, ec=color, label=name)
# 顯示座標
ax.text(vec[0]+0.2, vec[1]+0.2, f'{name}({vec[0]}, {vec[1]})',
color=color, fontsize=10, fontweight='bold')
# 顯示與 vec1 的相似度
for name, vec in vectors.items():
if name != 'vec1':
sim = cosine_sim(vec1, vec)
# sim_sklearn = cosine_sim_sklearn(vec1, vec) # sklearn 版
angle = np.arccos(np.clip(sim, -1, 1)) * 180 / np.pi
print(f"vec1與{name}的相似度: {sim:.3f} (角度: {angle:.1f}°)")
# print(f"vec1與{name}的相似度 (sklearn): {sim_sklearn:.3f}") # sklearn 版的結果
ax.set_xlim([-5, 5])
ax.set_ylim([0, 5])
ax.set_xticks(range(-5, 6))
ax.set_yticks(range(0, 6))
ax.grid(True, alpha=0.3)
ax.legend()
ax.set_aspect('equal')
plt.title('向量之間的餘弦相似度')
plt.savefig('cosine_similarity_plot.png', dpi=300, bbox_inches='tight')
print("圖表已儲存至 cosine_similarity_plot.png")
visualize_cosine_similarity()
# 執行結果
vec1與vec2的相似度: 0.960 (角度: 16.3°)
vec1與vec3的相似度: 0.280 (角度: 73.7°)
當維度增大時,餘弦相似度往往會集中在接近 0 的範圍,這是一個稱為「維度詛咒」的問題。在高維空間中,向量能取的方向極其龐大,使得彼此朝著相似方向的概率極低,幾乎所有向量都會呈現「接近直交的關係」。
因此,有必要採用 PCA 或 t-SNE 等技術進行維度降維,或者通過特徵選擇來排除不必要的信息。
如本次所述,我利用 Amazon S3 Vectors 實現了混合搜索(結合向量基與關鍵字基方法的方式),如有興趣歡迎查閱。
雖然內容簡單,但我整理了用於比較向量化文本的餘弦相似度的相關資訊。如果還有其他理解模糊的地方,隨時會繼續整理。
謝謝大家。