返回文章列表

網站與PDF摘要問答系統開發實作

本文介紹如何開發網站和PDF摘要問答系統,利用Python和Streamlit構建使用者介面,並整合Hugging

Web 開發 機器學習

網路應用程式開發中,常需整合外部資源或處理不同格式的資料。本文示範如何結合網頁內容擷取、大語言模型和 Streamlit 框架,建構一個兼具網站摘要、問答以及 PDF 檔案問答功能的系統。系統核心流程包含使用 requests 取得網頁 HTML 內容,BeautifulSoup 解析並提取段落,以及過濾合併段落以控制文字長度。利用 Hugging Face 的 API,系統可以對處理後的文字進行摘要和問答。此外,系統也支援使用者上傳 PDF 檔案,透過 PyPDF2 讀取檔案內容,並同樣利用大語言模型和相似度搜尋技術,實作 PDF 檔案的問答功能。Streamlit 則提供簡潔易用的介面,讓使用者能輕鬆與系統互動。

網站摘要與問答系統的開發與實作

系統概述

本系統旨在開發一個根據網站內容的摘要與問答功能,透過整合自然語言處理(NLP)技術與機器學習模型,實作對網站內容的自動化摘要與根據內容的問答。

開發流程

  1. 網頁內容擷取

    • 使用 requests 函式庫傳送 GET 請求至指定 URL,取得網頁的 HTML 內容。
    • url = "https://shivamsolanki.net/posts/Tuning-LLMs/"
      response = requests.get(url)
      html_content = response.text
      
  2. HTML 解析與段落提取

    • 利用 BeautifulSoup 對 HTML 內容進行解析,提取所有 <p> 標籤內的文字內容。
    • soup = BeautifulSoup(html_content, "html.parser")
      paragraphs = soup.find_all("p")
      
  3. 段落過濾與合併

    • 對提取的段落進行過濾,篩選出長度大於或等於 100 個字元的段落。
    • 將篩選後的段落合併成一個字串,總長度不超過 2000 個字元。
    • passages = [p.get_text(strip=True) for p in paragraphs if len(p.get_text(strip=True)) >= 100]
      combined_passage = ""
      for passage in passages:
          if len(combined_passage) + len(passage) <= 2000:
              combined_passage += passage + " "
          else:
              break
      

使用 Streamlit 建立使用者介面

程式碼實作

import streamlit as st
import requests
from bs4 import BeautifulSoup
import json

# HuggingFace Inference API endpoint
QA_ENDPOINT = "https://api-inference.huggingface.co/models/bigscience/bloom"
SUMMARIZATION_ENDPOINT = "https://api-inference.huggingface.co/models/tiiuae/falcon-7b-instruct"

def extract_combined_passage(url, min_paragraph_length, max_combined_length):
    # #### 內容解密:
    # 此函式用於從指定 URL 中提取合併後的段落內容。
    # 1. 傳送 GET 請求取得網頁 HTML 內容。
    # 2. 使用 BeautifulSoup 解析 HTML 並提取段落。
    # 3. 篩選並合併段落,總長度不超過 max_combined_length。
    response = requests.get(url)
    html_content = response.text
    soup = BeautifulSoup(html_content, "html.parser")
    paragraphs = soup.find_all("p")
    passages = [p.get_text(strip=True) for p in paragraphs if len(p.get_text(strip=True)) >= min_paragraph_length]
    combined_passage = ""
    for passage in passages:
        if len(combined_passage) + len(passage) <= max_combined_length:
            combined_passage += passage + " "
        else:
            break
    return combined_passage

def generate_summary(url):
    # #### 內容解密:
    # 此函式用於生成指定 URL 內容的摘要。
    # 1. 呼叫 extract_combined_passage 取得合併後的段落內容。
    # 2. 使用 HuggingFace 的摘要模型生成摘要。
    headers = {"Authorization": "Bearer XXXXXXXX"}
    combined_passage = extract_combined_passage(url, 100, 2000)
    model_input = f"Summarize the following article. Article: {combined_passage}"
    json_data = {
        "inputs": model_input,
        "parameters": {'temperature': 0.5, 'max_new_tokens': 100, 'return_full_text': False}
    }
    response = requests.post(SUMMARIZATION_ENDPOINT, headers=headers, json=json_data)
    json_response = json.loads(response.content.decode("utf-8"))
    summary = json_response[0]['generated_text']
    return summary

def answer_question(url, question):
    # #### 內容解密:
    # 此函式用於根據指定 URL 的內容回答問題。
    # 1. 呼叫 extract_combined_passage 取得合併後的段落內容。
    # 2. 使用 HuggingFace 的問答模型生成答案。
    headers = {"Authorization": "Bearer XXXXXXXX"}
    combined_passage = extract_combined_passage(url, 100, 2000)
    model_input = f"Answer the question based on the context below. Context: {combined_passage} Question: {question}"
    json_data = {
        "inputs": model_input,
        "parameters": {'temperature': 0.5, 'max_new_tokens': 100, 'return_full_text': False}
    }
    response = requests.post(QA_ENDPOINT, headers=headers, json=json_data)
    answer = response.json()[0]["generated_text"]
    return answer

def main():
    st.title("網站摘要與問答系統")
    url = st.text_input("輸入網站 URL:")
    question = st.text_input("輸入問題:")
    if st.button("生成摘要與答案"):
        if url:
            text = extract_combined_passage(url, 100, 2000)
            summary = generate_summary(url)
            answer = answer_question(url, question)
            st.subheader("摘要:")
            st.write(summary)
            st.subheader("答案:")
            st.write(answer)
        else:
            st.warning("請輸入有效的 URL。")

if __name__ == "__main__":
    main()

系統功能與特點

  • 自動從指定網站提取內容並生成摘要。
  • 根據提取的內容回答使用者提出的問題。
  • 使用 Streamlit 提供友好的使用者介面。
  • 結合 HuggingFace 的 NLP 模型實作摘要與問答功能。

使用大語言模型建構網站摘要與問答系統

本章節將探討如何利用大語言模型(LLMs)開發一個能夠摘要網站內容並回答相關問題的應用程式。我們將使用Streamlit框架來建立使用者介面,並結合Hugging Face的API來實作文字摘要和問答功能。

系統架構與流程

首先,我們定義了一些全域常數,包括Hugging Face API的端點地址以及文字摘要和問答所需的最小和最大長度引數。

# 定義Hugging Face API端點
QA_ENDPOINT = "https://api-inference.huggingface.co/models/..."
SUMMARIZATION_ENDPOINT = "https://api-inference.huggingface.co/models/tiiuae/falcon-7b-instruct"

# 設定最小和最大長度引數
min_length = 100
max_length = 2000

內容解密:

  • QA_ENDPOINTSUMMARIZATION_ENDPOINT 分別是Hugging Face提供的問答和摘要模型的API端點。
  • min_lengthmax_length 用於控制從網站提取的文欄位落的長度範圍。

網站內容擷取功能

接下來,我們定義了一個名為extract_combined_passage的函式,用於從指定的URL中擷取網站內容。

def extract_combined_passage(url, min_paragraph_length, max_combined_length):
    # 傳送GET請求以取得HTML內容
    response = requests.get(url)
    html_content = response.text
    
    # 使用BeautifulSoup解析HTML內容
    soup = BeautifulSoup(html_content, "html.parser")
    
    # 尋找所有段落標籤並提取文字內容
    paragraphs = soup.find_all("p")
    passages = [p.get_text(strip=True) for p in paragraphs if len(p.get_text(strip=True)) >= min_paragraph_length]
    
    # 合併段落直到達到指定的最大長度
    combined_passage = ""
    for passage in passages:
        if len(combined_passage) + len(passage) <= max_combined_length:
            combined_passage += passage + " "
        else:
            break
    
    return combined_passage

內容解密:

  • 此函式首先傳送GET請求取得指定URL的HTML內容。
  • 使用BeautifulSoup解析HTML並提取所有段落標籤中的文字內容。
  • 過濾掉長度小於min_paragraph_length的段落。
  • 合併剩餘的段落直到總長度達到max_combined_length

主程式與使用者介面

在主程式中,我們設定了Streamlit的使用者介面,包括標題、網址輸入欄位、問題輸入欄位以及觸發摘要和問答的按鈕。

def main():
    st.title("網站摘要與問答系統")
    
    # 取得使用者輸入
    url = st.text_input("輸入網站網址:")
    question = st.text_input("輸入問題:")
    
    if st.button("摘要並回答"):
        if url:
            # 從網站擷取內容
            text = extract_combined_passage(url, min_length, max_length)
            
            # 生成摘要
            summary = generate_summary(url)
            
            # 回答問題
            answer = answer_question(url, question)
            
            # 顯示結果
            st.subheader("摘要:")
            st.write(summary)
            st.subheader("答案:")
            st.write(answer)
        else:
            st.warning("請輸入有效的網址。")

內容解密:

  • 使用Streamlit建立了一個簡單的使用者介面。
  • 使用者可以輸入網址和問題,並點選按鈕觸發後續處理。
  • 程式會根據輸入的網址擷取內容,生成摘要,並回答問題。

摘要與問答功能實作

我們定義了兩個函式,分別用於生成摘要和回答問題,這兩個函式都利用了Hugging Face的API。

def generate_summary(url):
    # 設定Hugging Face API請求
    headers = {"Authorization": "Bearer XXXXXXXX"}
    combined_passage = extract_combined_passage(url, min_length, max_length)
    model_input = f"摘要以下文章。文章:{combined_passage}"
    json_data = {
        "inputs": model_input,
        "parameters": {'temperature': 0.5, 'max_new_tokens': 100, 'return_full_text': False}
    }
    
    # 傳送請求到Hugging Face Inference API
    response = requests.post(SUMMARIZATION_ENDPOINT, headers=headers, json=json_data)
    json_response = json.loads(response.content.decode("utf-8"))
    summary = json_response[0]['generated_text']
    
    return summary

def answer_question(url, question):
    # 設定Hugging Face API請求
    headers = {"Authorization": "Bearer XXXXXXXX"}
    combined_passage = extract_combined_passage(url, min_length, max_length)
    model_input = f"根據以下內容回答問題。內容:{combined_passage} 問題:{question}"
    json_data = {
        "inputs": model_input,
        "parameters": {'temperature': 0.5, 'max_new_tokens': 100, 'return_full_text': False}
    }
    
    # 傳送請求到Hugging Face Inference API
    response = requests.post(QA_ENDPOINT, headers=headers, json=json_data)
    answer = response.json()[0]["generated_text"]
    
    return answer

內容解密:

  • 這兩個函式都首先擷取網站內容,然後建構合適的輸入給Hugging Face模型。
  • generate_summary函式生成指定文章的摘要。
  • answer_question函式根據提供的內容回答指定的問題。

利用大語言模型開發互動式PDF問答系統

透過大語言模型(LLMs),我們可以開發一個應用程式,讓使用者能夠直接向PDF檔案提問並獲得根據檔案內容的答案。該應用程式利用強大的語言模型處理PDF文字,提取關鍵訊息,並對使用者的查詢提供簡潔的答案。此外,我們還可以採用摘要技術生成PDF檔案的摘要,使我們能夠快速掌握其主要內容和關鍵要點。

PDF問答系統的優勢

利用LLMs開發的PDF問答系統,可以大幅簡化我們的資訊檢索流程,節省時間,並更有效地取得寶貴的見解。無論是用於研究目的、學習,還是從冗長的報告中提取關鍵訊息,這種方法都能幫助我們快速有效地解鎖PDF檔案中所包含的知識。

建構PDF問答應用程式

讓我們來建立一個簡單的根據Streamlit框架的PDF問答應用程式示範。

from dotenv import load_dotenv
import streamlit as st
from PyPDF2 import PdfReader
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains.question_answering import load_qa_chain
from langchain.llms import OpenAI
from langchain.callbacks import get_openai_callback
import os

def main():
    # 載入環境變數
    load_dotenv()
    os.environ["OPENAI_API_KEY"] = "XXXXXX"
    
    # 設定Streamlit應用程式
    st.set_page_config(page_title="PDF-Chatbot")
    st.header("詢問有關您的PDF的任何問題")

    # 上傳PDF檔案
    pdf = st.file_uploader("上傳您的PDF", type="pdf")

    # 從PDF中提取文字
    if pdf is not None:
        pdf_reader = PdfReader(pdf)
        text = ""
        for page in pdf_reader.pages:
            text += page.extract_text()

        # 將文字分割成區塊
        text_splitter = CharacterTextSplitter(
            separator="\n",
            chunk_size=1000,
            chunk_overlap=200,
            length_function=len
        )
        chunks = text_splitter.split_text(text)

        # 為文字區塊建立嵌入表示
        embeddings = OpenAIEmbeddings()
        knowledge_base = FAISS.from_texts(chunks, embeddings)

        # 提示使用者詢問有關PDF的問題
        user_question = st.text_input("詢問有關您的PDF的問題:")

        if user_question:
            # 執行相似度搜尋以找到相關的文字區塊
            docs = knowledge_base.similarity_search(user_question)

            # 初始化LLM模型和問答鏈
            llm = OpenAI()
            chain = load_qa_chain(llm, chain_type="stuff")

            # 使用問答鏈取得答案
            with get_openai_callback() as cb:
                response = chain.run(input_documents=docs, question=user_question)
                print(cb)

            # 向使用者顯示回應
            st.write(response)

if __name__ == '__main__':
    main()

內容解密:

  1. 載入必要的函式庫和模組:首先,我們載入了實作應用程式功能所需的各個函式庫,包括用於載入環境變數的dotenv、用於建立應用程式介面的streamlit,以及來自langchain函式庫的各種用於文字處理和問答的模組。
  2. main()函式的實作:在main()函式中,我們首先載入環境變數並設定OpenAI API金鑰。然後,我們組態Streamlit應用程式,設定頁面標題並顯示標頭。
  3. 上傳和處理PDF檔案:使用者被提示上傳PDF檔案。我們使用PdfReader從PyPDF2讀取上傳的PDF檔案的內容,並將文字從每一頁提取並連線成一個單一的文字字串。
  4. 將文字分割成區塊:使用CharacterTextSplitterlangchain將提取的文字分割成較小的區塊,以便更有效地處理文字。
  5. 建立文字區塊的嵌入表示:我們使用OpenAIEmbeddings類別從langchain為文字區塊建立詞嵌入表示,這些嵌入表示捕捉了文字的語義含義,並使得高效的相似度搜尋成為可能。
  6. 執行相似度搜尋和問答:當使用者輸入問題時,我們在文字區塊上執行相似度搜尋,以找到與問題最相關的區塊。然後,我們初始化LLM模型並載入問答鏈,使用該鏈根據給定的上下文回答問題。

程式碼逐步解析

  1. 匯入必要模組:程式開始時匯入了多個必要的Python模組和函式庫,包括環境變數管理、PDF讀取、文字分割、嵌入表示建立、向量儲存、問答鏈等。

  2. 定義主函式:定義了名為main()的主函式,該函式包含了整個應用程式的主要邏輯。

  3. 載入環境變數和設定OpenAI API金鑰:在main()函式內,首先載入了環境變數並手動設定了OpenAI API金鑰。

  4. 組態Streamlit應用程式介面:使用Streamlit的功能設定了應用程式的頁面標題和顯示了一個標頭,邀請使用者詢問有關其上傳PDF的問題。

  5. 實作PDF上傳功能:提供了一個檔案上傳元件,允許使用者上傳PDF檔案。

  6. 提取上傳PDF的文字內容:一旦使用者上傳了PDF檔案,程式就讀取該檔案,提取每一頁的文字,並將它們合併成一個單一的字串。

  7. 將長文字分割成小區塊:由於大語言模型對輸入長度有限制,因此需要將提取的長文字分割成較小的區塊。這是透過使用特定的文字分割器完成的,可以根據需要調整區塊大小和重疊部分。

  8. 建立文字嵌入表示:將分割好的文字區塊轉換成嵌入表示,這是一種可以捕捉文字語義資訊的向量形式。這一步對於後續的相似度搜尋至關重要。

  9. 提供使用者輸入問題的介面:允許使用者輸入他們想詢問的問題。

  10. 執行相似度搜尋:當使用者提交問題後,系統會在之前建立的知識函式庫(即文字區塊的嵌入表示)中進行相似度搜尋,以找出與問題最相關的文字片段。

  11. 使用LLM模型回答問題:根據檢索出的相關文字片段,利用大語言模型生成對使用者問題的回答。

  12. 顯示答案給使用者:最後,將生成的答案顯示給使用者,完成整個問答流程。

內容解密:

  • 該程式碼實作了一個根據Streamlit的互動式網頁應用,使用者可上傳PDF檔案並對其內容進行提問。
  • 程式使用了大語言模型(LLM)和相關技術(如文字嵌入表示、相似度搜尋)來理解和回答使用者的問題。
  • 透過將PDF內容分割、嵌入和檢索,系統能夠提供精準的答案給使用者。