前言

生成 AI(大型語言模型)用於應用程式開發時,RAG(檢索增強生成)已成為不可或缺的技術。在將獨特數據整合到 ChatGPT 或 Claude 等大型語言模型時,許多開發者可能會考慮將 RAG 作為選項。

支撐 RAG 的核心技術之一是向量相似度檢索。然而,對於「餘弦相似度是什麼」這部分常常感到模糊,明明覺得懂,但卻很難清楚地向他人解釋。

本文將重新整理文本向量化及比較的機制,以及代表性的指標——餘弦相似度。

RAG 簡介

解決的問題

在使用生成 AI 時,可能會遇到無法正確回答組織特有資訊或知識的問題。針對這類問題,RAG 試圖通過從外部數據庫檢索相關資訊,並將其包含於大型語言模型的提示中來解決。

向量檢索的必要性

在 RAG 中,「如何精確獲取與問題相關的信息」至關重要。傳統的關鍵字搜尋無法理解意義上的關聯性,這是一大挑戰。為了解決這一問題,採用了將文本的意義表示為向量並計算其相似度的方式。

餘弦相似度是什麼

餘弦相似度是一種測量兩個向量「角度接近度」的方法。

它的取值範圍從 -1 到 1,具體可描述如下:

  • 接近 1(兩個向量的夾角為 0 度):向量方向相同(相似)
  • 接近 0(兩個向量的夾角為 90 度):向量之間直交(無關)
  • 接近 -1(兩個向量的夾角為 180 度):向量方向相反(完全相反)

數學定義

兩個向量 ab 的餘弦相似度,根據以下公式計算:

$$\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}}$$

  • a · b:向量的內積
  • |a|, |b|:向量的大小(范數)

直觀理解

以下是三個向量的相似度計算示例。

cosine_similarity_plot.png

以下是計算相似度的代碼。

我這裡手動計算,但如果使用 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 實現了混合搜索(結合向量基與關鍵字基方法的方式),如有興趣歡迎查閱。

結語

雖然內容簡單,但我整理了用於比較向量化文本的餘弦相似度的相關資訊。如果還有其他理解模糊的地方,隨時會繼續整理。
謝謝大家。


原文出處:https://qiita.com/ryu-ki/items/d83545d022e1a273ae5d


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝10   💬6   ❤️6
440
🥈
我愛JS
📝1   💬5   ❤️4
93
🥉
AppleLily
📝1   💬4   ❤️1
54
#4
💬1  
5
#5
xxuan
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次