從零打造屬於自己的 RAG-based LLM Line Bot 系列(三):資料準備與處理

Kaishen Tseng
7 min readJan 27, 2024

--

在這個系列文章中(預計會有五篇),我會分享如何從頭開始開發一個 RAG-based 的 Line Bot,包含

  1. 介紹與規格
  2. 系統架構與事前準備
  3. 用於 RAG 的資料準備與處理(此篇)
  4. RAG-based LLM Line Bot 開發
  5. 將 Line Bot 發佈到 Heroku(完整程式碼)

目錄

前言

在上一篇系統架構與事前準備中,介紹這個專案中的元件和彼此的關係,以及相關帳號的事前準備。

此篇會著重在資料的準備與處理,以及如何在 AWS 上建立一個 Postgres DB 來儲存向量化後的資料。

資料準備與處理

第一篇第二篇中提到,此專案主要的資料來源是 YouTube 的特定幾個頻道。因此將邏輯設定在給定一個頻道名稱下去取得資料。

  1. 取得影片 ID 並取出字幕
  2. 處理影片字幕
  3. 將沒有字幕的影片下載並取出音檔
  4. 從音檔取出文字
  5. 將文字轉換為 Embedding Vector 並儲存

1. 取得影片 ID 並取出字幕

第一步就是要取得影片相關資訊,包含 ID 和字幕(如果有的話)。

重點步驟:

  1. 透過 Selenium WebDriver 開啟一個 Chrome 瀏覽器,並導向指定的 YouTube 頻道頁面(範例網址 https://www.youtube.com/@{頻道名稱}/videos)。
  2. 在該頁面上進行滾動,直到頁面無法再滾動為止。這是為了確保頁面上的所有影片都被載入。
  3. 從頁面上找到所有的影片連結,並將每個影片的標題和 URL 儲存到一個列表中。
  4. 使用 YouTubeTranscriptApi 套件來獲取每個影片的字幕(如果有的話)。這些字幕會被儲存為 JSON 格式,並保存下來。
  5. 如果某個影片的字幕已經被儲存過,則該影片會被跳過,不會再次嘗試獲取字幕。

2. 處理影片字幕

將字幕取出並進行簡單的前處理(標點符號),再將其存為 txt 檔。這裏針對標點處理的原因,是因為想要將所有字幕合併為一大篇文章,但因為大部分在上字幕時,是針對時影片時間區段,因此不會特別下標點符號,導致直接合併會變成一段文字,因此使用 deepmultilingualpunctuation 來恢復標點符號。

重點步驟:

  1. 初始化一個用於恢復字幕標點符號的模型。
  2. 定義一個 preprocess_transcript 函數,該函數將字幕分割成長度為 5 的子列表,然後使用模型恢復每個子列表的標點符號,最後將所有的子列表合併成一個字串。至於長度可以自行嘗試,主要是因為標點符號模型有輸入文字長度的限制。
  3. 在 main 函數中,首先列出所有的 .json 檔案,然後對每個檔案進行處理。如果該檔案已經存在於 text_dir 目錄下,則跳過該檔案。否則,如果該檔案包含字幕,則將字幕提取出來,並使用 preprocess_transcript 函數進行處理,然後將處理後的字幕保存到 text_dir 目錄下。

3. 將沒有字幕的影片下載並取出音檔

在上一步驟中,是針對有字幕的影片來處理。而這個步驟就是針對沒有字幕的影片,將影片下載為音檔,以便進行後續的處理。

重點步驟:

  1. 首先從影片的 URL 中獲取影片的 ID,使用 pytube 套件從 YouTube 下載該影片,並使用 moviepy 套件將影片轉換為 MP3 格式,最後刪除下載的影片檔案。
  2. 在 main 函數中,首先列出所有的 .json 檔案,然後對每個檔案進行處理。如果該檔案不包含字幕,則將該檔案對應的影片的音訊下載並轉換為 MP3 格式。

4. 從音檔取出文字

接著便可以從音檔中取出文字。只要搜尋「Audio to Text」或「Speech to Text」便可以找到不少好用的服務,像是 OpenAI 的 Whisper 模型或是AWS、GCP 都有 Speech-to-Text 的 API 服務。

這裏提供另一個選項叫 faster-whisper,faster-whisper是一個重新實現OpenAI 的 Whisper 模型的高效語音識別模型,宣稱在準確性以及效率上更勝於原來的 Whisper 模型。

因為只是先測試整個專案是否可行,為求簡單我就把上述的音檔放到 Google Drive,接著利用 Colab 的 GPU instance 來取得結果,然後再下載到本機相對應的資料夾。

重點步驟:

  1. 初始化一個用於恢復字幕標點符號的模型。將取得文字後的結果與有字幕的影片做相同的處理。
  2. 在 main 函數中,首先列出所有的 .mp3 和 .txt 檔案,然後找出那些有 .mp3 檔案但沒有 .txt 檔案的檔案名稱。
  3. 接著,該程式碼會讀取每個 .json 檔案,並檢查該檔案是否包含字幕。如果包含字幕,則將字幕提取出來,並使用模型恢復字幕的標點符號,然後將處理後的字幕保存到 .txt 檔案中。
  4. 接著找出哪些有 .mp3 檔案但沒有 .txt 檔案的影片。
  5. 最後使用 WhisperModel 將這些檔案的音訊轉換為文字。轉換過程中,該程式碼會將音訊分割成多個段落,並對每個段落進行轉換,然後將轉換結果合併成一個字串,並使用模型恢復字串的標點符號,最後將處理後的字串保存到 .txt 檔案中。

5. 將文字轉換為 Embedding Vector 並儲存

資料處理的最後一部,就是將文字分割成多個段落,然後將這些段落轉換為向量並保存。

這邊會分享兩個保存的方式,主要用途都是是保存這些向量化後的資料,並用於向量資料的搜索。

第一個是將存入資料後的資料庫在本地端儲存成一個檔案,若要儲存以利之後使用,要有那份檔案並且先載入到記憶體來達成向量的搜尋,例如 Chroma DB 或是 Faiss。第二個是使用可支援向量搜索的資料庫,像是 Elasticsearch,本身就可支援向量搜尋,或是如本篇會介紹的 Postgres,搭配擴充套件來支援,要使用時就像一般關連式資料庫那樣,透過連線字串來連接。

前者的好處是很適合做一個快速的測試,因為不用特別的設定,只要初始化並存入資料後,便可以直接使用,但缺點就是每次使用,都一定需要這個檔案並載入記憶體,當機器本身有容量上的限制時就會很不方便。而後者則是需要花時間建立一個資料庫並安裝擴充套件,但好處就是把資料跟應用程式分開來。也得力於現在雲端服務的便利,並不真的需要花很多時間就可以完成。

以下的程式碼是用於將文字檔案中的文字分割成多個段落,然後將這些段落轉換為向量並保存到 PostgreSQL 數據庫或 Chroma 數據庫中。

重點步驟:

  1. 定義一個 num_tokens_from_string 函數,該函數使用 tiktoken 套件計算一個文字串中的 token 數量。這可以幫助我們保證餵進 Encoding 模型的輸入不會超過上限。
  2. 在 main 函數中,首先初始化一個用於創建向量的模型和一個用於將文字分割成多個段落的工具。至於要用什麼模型就端看要做什麼應用、語言或領域。
  3. 接著就要來分段(chunk),首先讀取來自上一步驟所產生的每個文字檔案(.txt),並將檔案中的文字提取出來。如果文字的 token 數量不超過 512,則將該文字加入到一個新的列表中。否則,使用文字分割工具將文字分割成多個段落,並將這些段落加入到新的列表中。
  4. 最後就是參數 --db 的值,創建一個 PGVector 物件或一個 Chroma 物件,並使用該物件將文檔列表中的每個文檔轉換為向量並保存到對應的數據庫中。

在 AWS 上建立 PGVector

在資料處理的最後一步有提到將資料儲存到可以支援向量搜尋的資料庫,以下列出兩個連結,分別是在 AWS 上建立 Postgres 資料庫以及安裝擴充套件來支援向量搜尋。

以下為第二個連結中安裝擴充套件與測試的 SQL。

小結

此篇為這個系列第三篇,主要講解這個 RAG-based LLM Line Bot 所需的資料準備與處理,也分享兩個對建立向量資料庫很實用的連結。

下一篇就會進入 Line Bot 的開發,也就是這個 App 的 Interface。將會分享如何搭配 Langchain 中的 ConversationalRetrievalChain來建立 QA 以及 ConversationBufferMemory來記得使用者的輸入(在一連串的接續問題中很有用),並且利用 OpenAI 來對所找出相關的文檔進行統整並回答,最後再回傳相關的文件(YouTube 連結)來對答案提供更多的解釋,幫助使用者更容易的判斷答案的品質。

對這個系列有興趣的讀者請持續保持關注,我們下次見!

--

--