---
# System prepended metadata

title: '[Large Language Models with Semantic Search] - Dense Retrieval<br>結合大型語言模型與語義搜索_課程筆記-密集檢索'
tags: [AI, GenAI, NLP, Generative AI, DeepLearning.AI, Deeplearning, 生成式AI]

---

### [AI / ML領域相關學習筆記入口頁面](https://hackmd.io/@YungHuiHsu/BySsb5dfp)

### [Deeplearning.ai GenAI/LLM系列課程筆記](https://learn.deeplearning.ai/) 
#### [Large Language Models with Semantic Search。大型語言模型與語意搜索 ](https://hackmd.io/@YungHuiHsu/rku-vjhZT)
- [Introduction、Keyword/lexical Search。引言與關鍵字搜尋](https://hackmd.io/@YungHuiHsu/rku-vjhZT)
- [ Embeddings。內嵌向量](https://hackmd.io/@YungHuiHsu/SJKORzaWp)
- [Dense Retrieval。密集檢索](https://hackmd.io/@YungHuiHsu/Sk-hxS0-T)
- [ReRank。重新排序](https://hackmd.io/@YungHuiHsu/HyT7uSJzT) 
- [Generating Answers。生成回答](https://hackmd.io/@YungHuiHsu/ry-Lv3kf6)
#### [Finetuning Large Language Models。微調大型語言模型](https://hackmd.io/@YungHuiHsu/HJ6AT8XG6)
---


# Large Language Models with Semantic Search

## [Dense Retrieval](https://learn.deeplearning.ai/large-language-models-semantic-search)
### 課程概要
* 使用向量而不是關鍵詞搜索進行語意搜索
* 在Weaviate資料庫中使用近文文本搜索實現向量搜索
* 將問題和可能的回答嵌入到向量空間中，找到距離最近的回答
* 實例展示向量搜索比關鍵詞搜索更準確
* 密集檢索支持多語言搜索
* 從頭開始建立向量索引，將文本分割、嵌入後存入Annoy索引
* Approximate Nearest Neighbor索引與向量資料庫的區別
* 密集檢索與關鍵詞搜索可結合實現混合搜索

#### 稀疏檢索器（Sparse Retriever）和密集檢索器（Dense Retriever）



### Part 1: Vector Database for semantic Search
* 導入Cohere Python SDK連接Weaviate向量資料庫
* 使用nearText而不是BM25演算法(關鍵字搜尋常用演算法)進行向量搜索
* 示例展示向量搜索返回更相關結果
* 支持多語言搜索查詢
* 可以探索資料集並改變查詢詞

<div style="text-align: center;">
      <figure>
            <img  src="https://hackmd.io/_uploads/rJduXBRWT.png" alt="DS_plugin_metadata.png" width="600">
            <figcaption>
                <span style="color: #3F7FBF; font-weight: bold;"> 
                    <a href="https://learn.deeplearning.ai/large-language-models-semantic-search/lesson/4/dense-retrieval" target="_blank">Dense Retrieval</a>
          </figcaption>
      </figure>
</div>


#### 程式實作
- 環境設定
    ```python=!
    # !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字段，用於表示結果文本和查詢文本之間的向量距離
        - 這使用了Weaviate的臨近文本搜索功能，將`nearText`中的文本作為搜索查詢。
        - `nearText`字典中的"concepts"列表包含了查詢句子
    Weaviate會根據文章文本與此查詢文本的相似度進行排序。

    
    綜合起來，這段程式碼實現了:
    * 使用近文文本搜索查詢句子的語意
    * 過濾結果限定為指定語言
    * 返回最相關的前num_results條結果
    * 所以實現了基於向量的語意搜索並設置過濾條件
    ```python=
    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
    ```
    
##### Bacic Query
- 基礎難度查詢:"who wrote Hamlet?"
- 向量搜索返回關於Shakespeare寫Hamlet的相關內容
- 結果高度相關,表明向量搜索捕捉到了查詢意圖
    - 'distance' 欄位代表了檢索到的文章與提問之間的距離 
        Weaviate預設是用cosine distance( $1 - \cos \theta$) 這邊不確定為何是負值
        > [If not specified explicitly, the default distance metric in Weaviate is **cosine**](https://weaviate.io/developers/weaviate/config-refs/distances)

    ```py=
    query = "Who wrote Hamlet?"
    dense_retrieval_results = dense_retrieval(query)
    pd.DataFrame(dense_retrieval_results)
    ```
    
    ![](https://hackmd.io/_uploads/SycndB0bp.png =600x)


##### Medium Query
* 中等難度查詢:"what is the capital of Canada"
* 向量搜索直接返回加拿大首都是Ottawa的維基百科頁面，而關鍵詞搜索返回的內容不那麼相關
    - dense_retrieval結果
        ![](https://hackmd.io/_uploads/rJv91wAZp.png)
    - keyword_search
        ![](https://hackmd.io/_uploads/BJQiyvRWT.png)


##### Complicated Query    
* 複雜查詢:"tallest person in history"
* 向量搜索返回有關最高人Robert Wadlow的文章，而關鍵詞搜索結果不相關

    - keyword_search
        ![](https://hackmd.io/_uploads/HyDBlDCZp.png)
    - dense_retrieval結果
        ![](https://hackmd.io/_uploads/SyBDewRWT.png)
    - 支援多語言搜尋，以阿拉伯語搜尋"أطول رجل في التاريخ"得到同樣結果
        ![](https://hackmd.io/_uploads/Bk4J-vRW6.png)


    
綜合來說,這三個例子展示了向量搜索比關鍵詞搜索更能捕捉語意和意圖，返回高度相關的結果。從基礎到複雜的查詢都表現出向量搜索的優勢
    
### Part 2: Building Semantic Search from Scratch
* 導入Annoy近似最近鄰層向量搜索庫
    - AnnoyIndex 是一個 Python lib，用於實現近似最近鄰搜索（Approximate Nearest Neighbors Search）
* 將文本分割成句子或段落作為檢索單元
    - 在這邊的範例中，將每個切割的文本前面加上標題（title），以增加文本切片（chunks）的語意完整性和上下文信息
* 為每個文本生成嵌入向量表示
* 將文本向量索引插入Annoy
* 為查詢生成向量，在Annoy中找最近鄰結果
* Approximate Nearest Neighbor與向量資料庫比較
* 可與關鍵詞搜索結合實現混合搜索

#### 文本切割 Chunking

<div style="text-align: center;">
      <figure>
            <img  src="https://hackmd.io/_uploads/HyEPDPCWa.png" alt="DS_plugin_metadata.png" width="600">
            <figcaption>
                <span style="color: #3F7FBF; font-weight: bold;"> 
                    <a href="https://learn.deeplearning.ai/large-language-models-semantic-search/lesson/4/dense-retrieval" target="_blank">Chunking</a>
          </figcaption>
      </figure>
</div>

 

Chunking（文本切割）是一個重要的操作。它是將文本分成不同的段落或句子的過程，以便更好地進行文本嵌入（Embedding）和語意搜索。文件中提到了兩種常見的Chunking方式：

- 段落切割：這種方法將文本分成不同的段落。每個段落通常包含一個主題或一個完整的想法。在這種方式下，每個段落都可以被嵌入為一個向量，使得語意搜索更有效

- 句子切割：另一種方式是將文本分成不同的句子。每個句子被視為一個獨立的單位，並被嵌入為一個向量。這種方法的好處是可以更精細地捕捉每個句子的語意，但在處理一些句子缺乏上下文的情況下，可能需要將標題或其他上下文信息添加到句子中，以增加其語意完整性。

此外，文件提到Chunking的方式可能因不同的任務而有所不同，取決於需要處理的文本特性。最終，Chunking是語意搜索中的重要步驟之一，它有助於建立文本的嵌入表示形式，以便更有效地進行相似性比對和語意搜索。

- 課程的Chunking是用python內件`.split()`土炮，`langchain`有提供比較高階的作法
    

    ```py=!
    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
    ```py=
    # 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])
    ```
 

#### Get the embeddings 轉換成向量表示
```py=
response = co.embed(
texts=texts.tolist()
).embeddings
embeds = np.array(response)
print(embeds.shape) # (15, 4096)
```

#### Create the search index 建立搜尋索引
 
在進行語意搜索或相似性比對時，需要一個有效的數據結構，以便在高維向量空間中快速查找最接近給定查詢的數據點。這個數據結構就是搜尋索引

- 具體操作步驟如下：

    1. 創建 AnnoyIndex 實例
        首先，我們需要創建 AnnoyIndex 的實例，這個實例將用於存儲和管理我們的向量數據。我們通常需要指定向量的維度（即每個向量的特徵數）和一些其他參數，以確保索引的效能和準確性。
    2. 向索引中添加數據點
        接下來，需要將我們想要進行相似性搜索的向量數據點添加到索引中。這些向量代表文本切片（chunks）或其他數據點，它們將在搜索時被用來計算相似性。
    3. 構建索引
        一旦添加了足夠的資料點，需要使用 build 方法來實際構建索引。這一步驟會將數據點組織成樹狀結構，以便快速搜索。
    4. 保存索引到文件
        通常，我們會將構建好的索引保存到文件中，以便以後可以重複使用，而不需要重新構建。

    :::info
     在最近鄰居搜索（nearest neighbors search）中，"建立 tree" 的概念是指建立一種數據結構，通常是樹狀結構，以便在高維向量空間中快速查找最接近給定查詢向量的數據點。這種數據結構可以幫助我們有效地組織和檢索大量的向量數據，以提高搜索的效率和速度。

    最常見的樹狀結構是二叉樹（Binary Tree）或 K-d Tree（K維樹），它們將數據點分成不同的區域或子集，並通過比較查詢向量和每個區域的中心點，快速縮小搜索範圍。通過這種方式，我們可以避免對所有數據點進行耗時的比較，而只需比較可能最接近查詢的子集。

    建立多棵樹（即建立樹的集合，通常稱為森林）可以進一步提高搜索效率。每棵樹可能使用不同的方法或特徵來分割數據，因此它們可以提供多種不同的搜索視角。當我們進行搜索時，可以並行地查詢每棵樹，並合併它們的結果，以獲得最終的最近鄰居。   

    <div style="text-align: center;">
          <figure>
                <img  src="https://www.jeremyjordan.me/content/images/2019/01/Screen-Shot-2019-01-29-at-10.21.41-PM.png" alt="DS_plugin_metadata.png" width="600">
                <figcaption>
                    <span style="color: #3F7FBF; font-weight: bold;"> 
                        <a href="https://www.jeremyjordan.me/scaling-nearest-neighbors-search-with-approximate-methods/" target="_blank">Scaling nearest neighbors search with approximate methods</a>
              </figcaption>
          </figure>
    </div>
    :::    

- 使用AnnoyIndex建立搜尋索引實作程式碼
    * AnnoyIndex是用來建立近似 nearest neighbors 搜索的庫。第一個參數是向量的維度,第二個參數是使用的距離計算法,这里是angular表示使用角度距離
    * add_item方法將索引中的資料和對應的向量新增進索引中。i是索引的id,embeds[i]是對應的向量
    * build方法構建搜尋樹。參數10表示構建10棵樹,可以加速搜索過程
    * save方法將構建好的索引存檔,之後可以load這個檔案直接使用,不需要每次都重新構建索引
    ```py=!
    # 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
    ```

    ```py=
    query = "How much did the film make?"
    search(query)
    ```
    ![](https://hackmd.io/_uploads/HytdcP0-p.png)

### Approximate Nearest-Neighbor vs. Vector databases 
Vector search libraries

 ![](https://hackmd.io/_uploads/r1LATvAW6.png)

| 特點                                   | Approximate Nearest-Neighbor | Vector Databases (Vector search libraries) |
|----------------------------------------|------------------------------|--------------------------------------------|
| **建立索引速度**                    | 相對較快                  | 通常較慢                                |
| **搜索速度**                          | 通常較快                  | 通常較慢                                |
| **內存使用**                          | 通常較低                  | 通常較高                                |
| **支援多維度向量**                | 是                         | 是                                       |
| **實時更新**                            | 有限支援                | 通常支援                                |
| **精確性**                              | 可配置                      | 高                                       |
| **特徵選擇和數據分割**    | 有限控制                | 高度可配置                             |
| **多語言支援**                        | 可能需要額外處理    | 通常支援                                |
| **擴展性**                              | 有限擴展性                | 通常有較好的擴展性                  |
| **維護複雜性**                        | 較低                        | 較高                                       |

其中，Approximate Nearest-Neighbor 提供了快速的建立索引和搜索速度，但精確性有所減少，而 Vector Databases 則提供更高的精確性和更多的功能，但通常需要更多的資源和複雜性。

### 混合搜尋:關鍵字+向量 Hybrid Search: Keyword + Vector
![](https://hackmd.io/_uploads/BkqLJdCWp.png)

- "Hybrid Search: Keyword + Vector" 
    是指在搜索引擎中結合兩種不同的搜索方法，一種是傳統的關鍵字搜索（Keyword Search），另一種是基於向量的語意搜索（Vector Search）。這種結合可以提高搜索引擎的效能和準確性，因為它充分利用了兩種搜索方法的優勢，同時彌補了它們各自的不足之處。

    在混合搜索中，搜索引擎同時使用了關鍵字搜索和向量搜索兩種方法。首先，它可以使用關鍵字搜索來快速篩選出包含關鍵字的文檔，然後再使用向量搜索來進一步排序和選擇這些文檔，以返回最相關的結果。這種結合可以提供快速的搜索速度，同時確保搜索結果更加精確和有語意。

- "Other signals"（其他信號）
    這是指在搜索引擎結果排序中考慮除了關鍵字和向量相似性之外的其他因素或信號。這些額外的信號用於進一步優化搜索結果的排序，以確保最相關的文檔排名靠前。以下是更詳細的說明：

    - 其他信號的種類：搜索引擎可以考慮多種其他信號，以幫助確定文檔的相對重要性。這些信號可能包括但不限於以下項目：
        * 權威性信號：
            這是關於文檔或網站的權威性和可信度的信息。例如，一個網站的權威性可以通過該網站的歷史、外部鏈接數量以及專業性等因素來評估。搜索引擎可能會考慮這些信號，以確定某個文檔的排名。
        * 用戶互動信號：
            這些信號來自用戶的互動行為，如點擊率、停留時間、回退率等。如果一個文檔被許多用戶點擊或停留時間長，搜索引擎可能會認為它對用戶更有價值，因此在排名中提高它的位置。
        * 內容特徵信號：
            這包括有關文檔內容的特徵，如文本長度、多媒體內容、關鍵詞密度等。某些特徵可能會影響文檔的排名，例如，包含更多多媒體內容或具有較高關鍵詞密度的文檔可能會受到更多關注。
        * 地理位置信號：
            對於特定的搜索查詢，地理位置可能是一個重要的信號。搜索引擎可以考慮用戶的位置信息，以確定與地理位置相關的結果。
        * 訪問許可權信號：
            某些文檔可能需要訪問權限或登錄，搜索引擎可以考慮這些權限，以確保只有合適的用戶可以訪問特定文檔。
    * 信號的綜合應用：搜索引擎通常使用複雜的算法來綜合考慮這些不同的信號，以確定文檔的最終排名。這種綜合考慮可以確保搜索結果不僅基於關鍵字匹配或向量相似性，還基於多種其他因素，以提供最佳的搜索體驗。

        Google 在其搜索引擎中使用了多種其他信號，其中一個明顯的例子是 PageRank（頁面排名）算法。PageRank 考慮了不僅是頁面的內容，還包括頁面之間的連接結構，即其他網頁對該頁面的鏈接數量和質量。這個信號有助於確定網頁的權威性和重要性，進而影響搜索結果的排名。
    
---
## Refernce
#### [2023。Erika Cardenas。weaviate。Distance Metrics in Vector Search](https://weaviate.io/blog/distance-metrics-in-vector-search)
    
 - 向量搜尋的距離指標
    - Weaviate中預設的指標是`cosine distance`
    <div style="text-align: center;">
          <figure>
                <img  src="https://hackmd.io/_uploads/rkL8iHRb6.png" alt="DS_plugin_metadata.png" width="600">
                <figcaption>
                    <span style="color: #3F7FBF; font-weight: bold;"> 
                        <a href="https://weaviate.io/blog/distance-metrics-in-vector-search" target="_blank">Distance Metrics in Vector Search</a>
              </figcaption>
          </figure>
    </div>
    
    
    <div style="text-align: center;">
          <figure>
                <img  src="https://hackmd.io/_uploads/r1Q3or0Za.png" alt="DS_plugin_metadata.png" width="600">
                <figcaption>
                    <span style="color: #3F7FBF; font-weight: bold;"> 
                        <a href="https://weaviate.io/blog/distance-metrics-in-vector-search" target="_blank">Vectors in Multi-Dimensional Space</a>
              </figcaption>
          </figure>
    </div>    
 
 
#####  從幾何角度解釋Cosine Similarity、Dot Product和Cosine 

|          | Dot Product                                                           | Cosine Similarity                                                         | Cosine Distance                                                      |
| -------- |:--------------------------------------------------------------------- |:------------------------------------------------------------------------- |:-------------------------------------------------------------------- |
| 幾何意義 | 計算兩向量在<br>同維度元素的乘積和<br>反映向量方向的一致性            | 在計算Dot Product的基礎上<br>對其進行標準化<br>消除向量長度對相似度的影響 | Cosine Distance = 1 - Cosine Similarity                              |
| 公式     | ${x} \cdot{y} = \sum_{i=1}^{n} x_i y_i$                               | $\cos \theta = \frac{{x} \cdot {y}}{\|\|{x}\|\|\|{y}\|\|}$                | $1 - \cos \theta = 1 - \frac{{x} \cdot {y}}{\|\|{x}\|\|\|\|{y}\|\|}$ |
| Range    | -∞ 到 +∞                                                              | -1 到 1                                                                   | 0 到 2                                                               |
| 值域說明 |正值: 方向相同<br>负值: 方向相反<br>0: 正交                       |1: 完全相似<br>0: 正交(無關)<br>-1: 完全相反                                |0: 完全相似<br>1: 正交無關)<br>2: 完全相反                            |
| 適用情境 | 向量的相似性計算如Attention機制<br>圖像、文本等表徵<br>信號處理、統計 | 文件檢索、句子匹配<br>推薦系統<br>聚類分析                                | 文本分群、聚類<br>異常檢測、離群值判斷<br>樣本分類KNN                |

        
 * $||x||$和$||y||$是向量長度的L2歐基里德距離    


<div style="text-align: center;">
      <figure>
            <img  src="https://storage.googleapis.com/lds-media/images/cosine-similarity-vectors.original.jpg" width="800">
            <figcaption>
                <span style="color: #3F7FBF; font-weight: bold;"> 
                    <a href="https://www.learndatasci.com/glossary/cosine-similarity/" target="_blank">Cosine Similarity (from learndatasci.com)</a>
          </figcaption>
      </figure>
</div> 
        
    
## Supplementary Notes
    
:::info

- Sparse Retrieval（稀疏檢索)
    - 通過關鍵詞匹配來找到相關文檔。以TF-IDF和BM25為例，這些方法關注於文檔中特定詞彙的出現頻率和整個文檔集合中該詞彙的分布稀疏性。
    - 在向量空間中，一個文檔由許多維度組成，對應於詞彙表中的每個詞，但只有少數幾個維度有值（即文檔中出現的詞），其他大多數都是零。
    - 這種表示適用於文檔集合龐大、詞彙豐富，且對計算效率有要求的場景。

- Dense Retrieval（密集檢索）
    - 利用神經網路生成文檔和查詢的向量表示
    - 這些向量的每個維度都包含有意義的信息，並且通常位於一個低維空間中
    - 由於神經網路能學習詞彙之間深層的語意關係，因此這種方法能夠捕捉到比單純關鍵詞匹配更細緻的**語意相似性**
    - 這種方法適用於需要**高語意理解能力**的任務，例如問答系統或個性化推薦。


-  混合檢索（Mix/Hybrid Retrieval）
    混合檢索是指結合稀疏和密集檢索器的方法來利用兩者的優勢。例如：
    - 稀疏檢索模型可以用於提供初始的檢索結果，幫助訓練密集檢索模型。
    - 預訓練語言模型（PLMs）可以學習術語的權重，從而增強稀疏檢索。
    - 稀疏檢索器能夠提升密集檢索模型的零樣本檢索能力，並協助它們處理包含罕見實體的查詢，這樣可以提高整體的穩健性。
 
- Ref
    - [2023.08。HeptaAI。DPR：从Sparse Retrieval到Dense Retrieval的飞跃](https://zhuanlan.zhihu.com/p/652201738)
:::
    
### **Term Frequency–Inverse Document Frequency (TF-IDF)**
    
    
Sparse Retriever 中的 Term Frequency–Inverse Document Frequency (TF-IDF) 結合了TF（詞頻）和IDF(詞稀有度)的概念，給出了一個詞語在特定文檔中的重要性評分。如果一個詞語在一篇文檔中出現頻率很高，但在其他文檔中並不常見，那麼這個詞的TF-IDF得分會很高，這表明它對於理解這篇文檔的內容很重要。

:::info
TF-IDF反映了詞語的出現頻率（TF）與其在整個文檔集合中稀有度（IDF）的乘積
:::
    
TF-IDF的計算由兩部分組成：Term Frequency (TF) 和 Inverse Document Frequency (IDF)。其計算公式如下：

1. **Term Frequency (TF)**:
   $$
   TF(t, d) = \frac{n_t^d}{\sum_{t' \in d} n_{t'}^d}
   $$
   
    其中 $n_t^d$ 表示單詞 $t$ 在文檔 $d$ 中出現的次數，分母是文檔 $d$ 中所有單詞的出現次數總和。

2. **Inverse Document Frequency (IDF)**:
   $$
   IDF(t, D) = \log \left(\frac{N}{n_t^D}\right)
   $$
   其中 $N$ 是文檔集合 $D$ 中文檔的總數，$n_t^D$ 是包含單詞 $t$ 的文檔數量。
    
    IDF（逆文檔頻率）是**衡量詞語在整個文檔集合中稀有度**的指標。一個詞語的IDF值隨著它在文檔集合中出現的文檔數量增加而降低。如果一個詞語幾乎在每篇文檔中都出現，那麼它的IDF值將接近0，表示它對於區分文檔不是很有用。相反，如果一個詞語很少出現，它的IDF值會很高，顯示它在集合中具有很好的區分度。

3. **TF-IDF**:
   $$
   TF\text{-}IDF(t, d, D) = TF(t, d) \times IDF(t, D)
   $$

- 計算範例

    假設有一個文檔集合 $D$ 包含 1000 篇文檔，單詞 "apple" 在其中一篇特定文檔 $d$ 中出現了 10 次，該文檔總共包含 1000 個單詞。假設 "apple" 這個詞在 100 篇文檔中至少出現了一次。

    - **TF(apple, d)**:
      $$
      TF(apple, d) = \frac{10}{1000} = 0.01
      $$

    - **IDF(apple, D)**:
      $$
      IDF(apple, D) = \log \left(\frac{1000}{100}\right) = \log(10) \approx 1
      $$

    - **TF-IDF(apple, d, D)**:
      $$
      TF\text{-}IDF(apple, d, D) = 0.01 \times 1 = 0.01
      $$

        在這個範例中，"apple" 這個詞在文檔 $d$ 中的 TF-IDF 值是 0.01，這表明它對該文檔的重要性是中等程度。這個值相對較低，因為雖然 "apple" 在文檔 $d$ 中的出現頻率不低，但它在整個文檔集合 $D$ 中並不罕見。

### **BM25(Best Matching 25)**
BM25是一種基於機率的排名函數，用於文本檢索，主要在信息檢索系統中對文檔集合進行排序，以找到與用戶查詢最相關的文檔。它是TF-IDF模型的擴展，更加精細地考慮了各種影響排名的因素，例如詞頻飽和度和文檔長度。

:::info
BM25因考慮了文檔長度和詞頻飽和度，相較於傳統TF-IDF，能更準確地反映文檔的相關性。
:::
    
- BM25的計算公式如下：
    $$ 
        \text{Score}(D, Q) = \sum_{i=1}^{n} IDF(q_i) \cdot \frac{f(q_i, D) \cdot (k_1 + 1)}{f(q_i, D) + k_1 \cdot (1 - b + b \cdot \frac{|D|}{\text{avgdl}})} 
    $$
    其中：
    - $\text{Score}(D, Q)$ 是文檔 $D$ 對於查詢 $Q$ 的相關性得分。
    - $q_i$ 是查詢中的第 $i$ 個詞。
    - $f(q_i, D)$ 是詞 $q_i$ 在文檔 $D$ 中的詞頻。
    - $|D|$ 是文檔 $D$ 的長度（即詞數）。
    - $\text{avgdl}$ 是文檔集合中文檔平均長度。
    - $k_1$ 和 $b$ 是可調參數，一般取 $k_1 \in [1.2, 2.0]$，$b \approx 0.75$。
    - $IDF(q_i)$ 是詞 $q_i$ 的逆文檔頻率，可以使用先前提及的IDF計算公式。

- 計算範例

    假設一個查詢 $Q$ 包含詞 "蘋果"，我們想計算一個文檔 $D$ 的BM25分數。假設 "蘋果" 在 $D$ 中的詞頻是3，文檔集合中包含"蘋果"的文檔數是100，總文檔數是10000，$D$ 的長度是1000詞，平均文檔長度是500詞。

    計算 "蘋果" 的 $IDF$：
    $$ IDF(\text{"蘋果"}) = \log \left(\frac{10000}{100 + 1}\right) $$

    計算BM25得分：
    $$ \text{Score}(D, \text{"蘋果"}) = IDF(\text{"蘋果"}) \cdot \frac{3 \cdot (1.2 + 1)}{3 + 1.2 \cdot (1 - 0.75 + 0.75 \cdot \frac{1000}{500})} $$

    這個得分代表了文檔 $D$ 對於該詞的相關性，而整個查詢的得分則是查詢中所有詞的得分之和。

    