教學課程:使用 Azure Cache for Redis 對 Azure OpenAI 內嵌進行向量相似性搜尋

在本教學課程中,您將逐步解說基本的向量相似度搜尋使用案例。 您將使用 Azure OpenAI 服務所產生的內嵌和 Azure Cache for Redis 企業層的內建向量搜尋功能來查詢電影數據集,以尋找最相關的相符專案。

本教學課程使用 維琪百科電影繪圖數據集 ,其中包含 1901 至 2017 年維琪百科超過 35,000 部電影的繪圖描述。 數據集包含每個電影的繪圖摘要,加上電影發行年份、導演(s)、主演和內容類型等元數據。 您將遵循本教學課程的步驟,根據繪圖摘要產生內嵌,並使用其他元數據來執行混合式查詢。

在本教學課程中,您會了解如何:

  • 建立針對向量搜尋設定的 Azure Cache for Redis 實例
  • 安裝 Azure OpenAI 和其他必要的 Python 連結庫。
  • 下載影片數據集並準備進行分析。
  • 使用 text-embedding-ada-002(第 2 版)模型來產生內嵌。
  • 在 Azure Cache for Redis 中建立向量索引
  • 使用 餘弦相似度 來排名搜尋結果。
  • 透過 RediSearch 使用混合式查詢功能來預先篩選數據,並讓向量搜尋更加強大。

重要

本教學課程將逐步引導您建置 Jupyter Notebook。 您可以遵循本教學課程搭配 Python 程式代碼檔案(.py),並取得 類似的 結果,但您必須將本教學課程中的所有程式碼區塊新增至檔案, .py 並執行一次以查看結果。 換句話說,Jupyter Notebook 會在您執行數據格時提供中繼結果,但這不是您在 Python 程式代碼檔案中工作時應該預期的行為。

重要

如果您想要改為遵循已完成的 Jupyter Notebook, 請下載名為 tutorial.ipynb 的 Jupyter Notebook 檔案,並將它儲存到新的 redis-vector 資料夾中。

必要條件

建立 Azure Cache for Redis 實例

  1. 遵循快速入門:建立 Redis 企業快取指南。 在 [ 進階 ] 頁面上,確定您已新增 RediSearch 模組,並已選擇 企業 叢集原則。 所有其他設定都可以符合快速入門中所述的預設值。

    快取需要幾分鐘的時間才能建立。 您可以同時移至下一個步驟。

Screenshot showing the Enterprise tier Basics tab filled out.

設定開發環境

  1. 在通常儲存專案的位置中,於本機計算機上建立名為 redis-vector 的資料夾。

  2. 在資料夾中建立新的 Python 檔案 (tutorial.py) 或 Jupyter Notebook (tutorial.ipynb)。

  3. 安裝必要的 Python 套件:

    pip install "openai==1.6.1" num2words matplotlib plotly scipy scikit-learn pandas tiktoken redis langchain
    

下載資料集

  1. 在網頁瀏覽器中,巡覽至 https://www.kaggle.com/datasets/jrobischon/wikipedia-movie-plots

  2. 使用 Kaggle 登入或註冊。 需要註冊才能下載檔案。

  3. 選取 Kaggle 上的 [ 下載 ] 鏈接以下載 archive.zip 檔案。

  4. 擷取archive.zip檔案,並將wiki_movie_plots_deduped.csv移至 redis-vector 資料夾。

匯入連結庫並設定連線資訊

若要成功對 Azure OpenAI 進行呼叫,您需要 端點密鑰。 您也需要 端點金鑰 ,才能連線到 Azure Cache for Redis。

  1. 移至 Azure 入口網站 中的 Azure OpenAI 資源。

  2. 在 [資源管理] 區段中找出端點和金鑰 複製您的端點和存取密鑰,因為您需要這兩者來驗證 API 呼叫。 範例端點是: https://docs-test-001.openai.azure.com。 您可以使用 KEY1KEY2

  3. 在 Azure 入口網站 中,移至 Azure Cache for Redis 資源的 [概觀] 頁面。 複製您的端點。

  4. [設定] 區段中找出 [存取密鑰]。 複製您的存取金鑰。 您可以使用 PrimarySecondary

  5. 將下列程式代碼新增至新的程式代碼資料格:

    # Code cell 2
    
    import re
    from num2words import num2words
    import os
    import pandas as pd
    import tiktoken
    from typing import List
    from langchain.embeddings import AzureOpenAIEmbeddings
    from langchain.vectorstores.redis import Redis as RedisVectorStore
    from langchain.document_loaders import DataFrameLoader
    
    API_KEY = "<your-azure-openai-key>"
    RESOURCE_ENDPOINT = "<your-azure-openai-endpoint>"
    DEPLOYMENT_NAME = "<name-of-your-model-deployment>"
    MODEL_NAME = "text-embedding-ada-002"
    REDIS_ENDPOINT = "<your-azure-redis-endpoint>"
    REDIS_PASSWORD = "<your-azure-redis-password>"
    
  6. 使用 API_KEY Azure OpenAI 部署中的金鑰和端點值來更新 和 RESOURCE_ENDPOINT 的值。 DEPLOYMENT_NAME 應該使用 text-embedding-ada-002 (Version 2) 內嵌模型設定為部署的名稱,而且 MODEL_NAME 應該是使用的特定內嵌模型。

  7. 使用來自 Azure Cache for Redis 實體的端點與金鑰值來更新 REDIS_ENDPOINTREDIS_PASSWORD

    重要

    強烈建議使用環境變數或 Azure 金鑰保存庫 之類的秘密管理員來傳入 API 金鑰、端點和部署名稱資訊。 為了簡單起見,這些變數會以純文本在這裡設定。

  8. 執行程式代碼儲存格 2。

將數據集匯入 pandas 並處理數據

接下來,您將將 csv 檔案讀入 pandas DataFrame。

  1. 將下列程式代碼新增至新的程式代碼資料格:

    # Code cell 3
    
    df=pd.read_csv(os.path.join(os.getcwd(),'wiki_movie_plots_deduped.csv'))
    df
    
  2. 執行程式代碼儲存格 3。 您應該會看見下列輸出:

    Screenshot of results from executing code cell 3, displaying eight columns and a sampling of 10 rows of data.

  3. 接下來,藉由新增 id 索引、從數據行標題移除空格,以及篩選電影,只取得 1970 年之後和來自英文國家/地區的電影,以處理數據。 此篩選步驟可減少數據集中的電影數目,進而降低產生內嵌所需的成本和時間。 您可以根據喜好設定變更或移除篩選參數。

    若要篩選資料,請將下列程式代碼新增至新的程式代碼資料格:

    # Code cell 4
    
    df.insert(0, 'id', range(0, len(df)))
    df['year'] = df['Release Year'].astype(int)
    df['origin'] = df['Origin/Ethnicity'].astype(str)
    del df['Release Year']
    del df['Origin/Ethnicity']
    df = df[df.year > 1970] # only movies made after 1970
    df = df[df.origin.isin(['American','British','Canadian'])] # only movies from English-speaking cinema
    df
    
  4. 執行程式代碼儲存格 4。 您應該會看見下列結果:

    Screenshot of results from executing code cell 4, displaying nine columns and a sampling of 10 rows of data.

  5. 藉由移除空格符和標點符號來建立函式來清除數據,然後將它用於包含繪圖的數據框架。

    將下列程式代碼新增至新的程式代碼資料格並加以執行:

    # Code cell 5
    
    pd.options.mode.chained_assignment = None
    
    # s is input text
    def normalize_text(s, sep_token = " \n "):
        s = re.sub(r'\s+',  ' ', s).strip()
        s = re.sub(r". ,","",s)
        # remove all instances of multiple spaces
        s = s.replace("..",".")
        s = s.replace(". .",".")
        s = s.replace("\n", "")
        s = s.strip()
    
        return s
    
    df['Plot']= df['Plot'].apply(lambda x : normalize_text(x))
    
  6. 最後,移除包含內嵌模型太長之繪圖描述的任何專案。 (換句話說,它們需要超過8192個令牌限制的令牌。然後計算產生內嵌所需的令牌數目。 這也會影響內嵌產生的價格。

    將下列程式代碼新增至新的程式代碼資料格:

    # Code cell 6
    
    tokenizer = tiktoken.get_encoding("cl100k_base")
    df['n_tokens'] = df["Plot"].apply(lambda x: len(tokenizer.encode(x)))
    df = df[df.n_tokens<8192]
    print('Number of movies: ' + str(len(df)))
    print('Number of tokens required:' + str(df['n_tokens'].sum()))
    
  7. 執行程式代碼儲存格 6。 您應該會看見下列輸出:

    Number of movies: 11125
    Number of tokens required:7044844
    

    重要

    請參閱 Azure OpenAI 服務定價,以根據所需的令牌數目來產生內嵌的成本。

將數據框架載入 LangChain

使用 DataFrameLoader 類別將 DataFrame 載入 LangChain。 一旦數據位於 LangChain 檔中,使用 LangChain 連結庫來產生內嵌並執行相似度搜尋會比較容易。 將 [繪圖] 設定page_content_column ,以便在此數據行上產生內嵌。

  1. 將下列程式代碼新增至新的程式代碼資料格並加以執行:

    # Code cell 7
    
    loader = DataFrameLoader(df, page_content_column="Plot" )
    movie_list = loader.load()
    

產生內嵌,並將其載入 Redis

既然數據已篩選並載入至 LangChain,您將建立內嵌專案,以便查詢每個電影的繪圖。 下列程式代碼會設定 Azure OpenAI、產生內嵌,並將內嵌向量載入 Azure Cache for Redis。

  1. 將下列程式代碼新增至新的程式代碼資料格:

    # Code cell 8
    
    embedding = AzureOpenAIEmbeddings(
        deployment=DEPLOYMENT_NAME,
        model=MODEL_NAME,
        azure_endpoint=RESOURCE_ENDPOINT,
        openai_api_type="azure",
        openai_api_key=API_KEY,
        openai_api_version="2023-05-15",
        show_progress_bar=True,
        chunk_size=16 # current limit with Azure OpenAI service. This will likely increase in the future.
        )
    
    # name of the Redis search index to create
    index_name = "movieindex"
    
    # create a connection string for the Redis Vector Store. Uses Redis-py format: https://redis-py.readthedocs.io/en/stable/connections.html#redis.Redis.from_url
    # This example assumes TLS is enabled. If not, use "redis://" instead of "rediss://
    redis_url = "rediss://:" + REDIS_PASSWORD + "@"+ REDIS_ENDPOINT
    
    # create and load redis with documents
    vectorstore = RedisVectorStore.from_documents(
        documents=movie_list,
        embedding=embedding,
        index_name=index_name,
        redis_url=redis_url
    )
    
    # save index schema so you can reload in the future without re-generating embeddings
    vectorstore.write_schema("redis_schema.yaml")
    
  2. 執行程式代碼數據格 8。 這可能需要超過 30 分鐘才能完成。 redis_schema.yaml也會產生檔案。 如果您想要連線到 Azure Cache for Redis 實例中的索引,而不需重新產生內嵌,此檔案就很有用。

重要

產生內嵌的速度取決於 Azure OpenAI 模型可用的 配額。 使用每分鐘 24 萬個令牌的配額,大約需要 30 分鐘的時間來處理數據集中的 700 萬個令牌。

執行向量搜尋查詢

現在您已設定數據集、Azure OpenAI 服務 API 和 Redis 實例,您可以使用向量進行搜尋。 在此範例中,會傳回指定查詢的前 10 個結果。

  1. 將下列程式代碼新增至 Python 程式代碼檔案:

    # Code cell 9
    
    query = "Spaceships, aliens, and heroes saving America"
    results = vectorstore.similarity_search_with_score(query, k=10)
    
    for i, j in enumerate(results):
        movie_title = str(results[i][0].metadata['Title'])
        similarity_score = str(round((1 - results[i][1]),4))
        print(movie_title + ' (Score: ' + similarity_score + ')')
    
  2. 執行程式代碼儲存格 9。 您應該會看見下列輸出:

    Independence Day (Score: 0.8348)
    The Flying Machine (Score: 0.8332)
    Remote Control (Score: 0.8301)
    Bravestarr: The Legend (Score: 0.83)
    Xenogenesis (Score: 0.8291)
    Invaders from Mars (Score: 0.8291)
    Apocalypse Earth (Score: 0.8287)
    Invasion from Inner Earth (Score: 0.8287)
    Thru the Moebius Strip (Score: 0.8283)
    Solar Crisis (Score: 0.828)
    

    相似度分數會隨著電影的序數排名以相似性傳回。 請注意,更特定的查詢的相似度分數會降低得更快。

混合式搜尋

  1. 由於 RediSearch 在向量搜尋之上也具有豐富的搜尋功能,因此可以依數據集中的元數據來篩選結果,例如電影內容類型、轉型、發行年份或導演。 在此情況下,根據內容類型 comedy進行篩選。

    將下列程式代碼新增至新的程式代碼資料格:

    # Code cell 10
    
    from langchain.vectorstores.redis import RedisText
    
    query = "Spaceships, aliens, and heroes saving America"
    genre_filter = RedisText("Genre") == "comedy"
    results = vectorstore.similarity_search_with_score(query, filter=genre_filter, k=10)
    for i, j in enumerate(results):
        movie_title = str(results[i][0].metadata['Title'])
        similarity_score = str(round((1 - results[i][1]),4))
        print(movie_title + ' (Score: ' + similarity_score + ')')
    
  2. 執行程式代碼數據格 10。 您應該會看見下列輸出:

    Remote Control (Score: 0.8301)
    Meet Dave (Score: 0.8236)
    Elf-Man (Score: 0.8208)
    Fifty/Fifty (Score: 0.8167)
    Mars Attacks! (Score: 0.8165)
    Strange Invaders (Score: 0.8143)
    Amanda and the Alien (Score: 0.8136)
    Suburban Commando (Score: 0.8129)
    Coneheads (Score: 0.8129)
    Morons from Outer Space (Score: 0.8121)
    

透過 Azure Cache for Redis 和 Azure OpenAI 服務,您可以使用內嵌和向量搜尋,將強大的搜尋功能新增至您的應用程式。

清除資源

如果您想要繼續使用在本文中建立的資源,請保留資源群組。

否則,如果您已完成資源,您可以刪除您建立的 Azure 資源群組,以避免產生費用。

重要

刪除資源群組是無法復原的。 當您刪除資源群組時,會永久刪除其中的所有資源。 請確定您不會不小心刪除錯誤的資源群組或資源。 如果您在包含您想要保留之資源的現有資源群組內建立資源,您可以個別刪除每個資源,而不是刪除資源群組。

若要刪除資源群組

  1. 登入 Azure 入口網站,然後選取 [資源群組]。

  2. 選取您要刪除的資源群組。

    如果有許多資源群組,請使用 [ 篩選任何字段... ] 方塊,輸入您為此文章建立的資源群組名稱。 選取結果清單中的資源群組。

    Screenshot showing a list of resource groups to delete in the working pane.

  3. 選取 [刪除資源群組]

  4. 系統會要求您確認刪除資源群組。 輸入要確認的資源群組名稱,然後選取 [ 刪除]。

    Screenshot showing a form that requires the resource name to confirm deletion.

幾分鐘后,資源群組及其所有資源都會遭到刪除。