大型语言模型(LLM)简化了诸多任务,例如聊天机器人开发、语言翻译和文本摘要。以往,文本摘要模型的性能问题一直是挑战。如今,利用LLM 可以轻松实现高质量的摘要。例如,一些先进的 LLM 已具备处理整本书上下文窗口的能力。然而,对超长文档进行摘要时,LLM 仍存在一些局限性。
LLM 的上下文长度(或上下文限制)是指模型可处理的 token 数量。每个模型都有其自身的上下文长度,也称为最大 token 数或 token 限制。例如,标准 GPT-4 模型的上下文长度为 128,000 个 token,超出此限制的信息将丢失。部分先进 LLM 的上下文长度可达 1,000,000 个token。然而,随着上下文长度的增加,LLM会受到首要性效应和最近性效应的影响。本文将探讨缓解这些效应的方法。
LLM 中的首要性效应指模型更重视序列开头的信息,而最近性效应指模型更强调最近处理的信息。这两种效应都会导致模型偏向输入数据的特定部分, 忽略序列中的关键信息。
成本是另一个问题。虽然可以通过拆分文本来解决上下文长度限制,但直接将整本书输入模型成本高昂。例如,一本包含 1,000,000 个 token 的书籍,直接使用 GPT-4 模型进行处理,总成本约为 90 美元(包含提示和完成 token)。因此,需要在价格、上下文限制和书籍完整上下文之间寻求平衡,找到一种有效的方法来进行文本摘要。
本文将介绍如何在考虑模型成本和上下文限制的情况下,使用 LangChain 和 OpenAI 整本书进行摘要。
1. 设置环境
使用如下命令安装依赖环境:
pip install langchain openai tiktoken fpdf2 pandas
2. 加载书籍
本示例中将使用查尔斯·狄更斯的书《大卫·科波菲尔德》。接下来使用LangChain 提供的 PyPDFLoader 实用程序加载书籍。
from langchain.document_loaders import PyPDFLoader# 加载书籍loader = PyPDFLoader("David-Copperfield.pdf")pages = loader.load_and_split()
由于我们只使用内容部分,可以设置跳过前言和引言等页面。
# 去掉开头和结尾的部分pages = pages[6:1308]# 合并页面,并将制表符替换为空格text = ' '.join([page.page_content.replace('\t', ' ') for page in pages])
有了内容后,我们来打印前 200 个字符:
text[0:200]
3. 预处理
首先需要从文本中删除不必要的内容,如不可打印字符、额外的空格等,完成数据清理。
import redef clean_text(text):# 删除特定短语“Free eBooks at Planet eBook.com”及其周围的空白字符cleaned_text = re.sub(r'\s*Free eBooks at Planet eBook\.com\s*', '', text, flags=re.DOTALL)# 删除额外的空格cleaned_text = re.sub(r' +', ' ', cleaned_text)# 删除不可打印字符,可选择在“大卫·科波菲尔德”之前cleaned_text = re.sub(r'(David Copperfield )?[\x00-\x1F]', '', cleaned_text)# 用空格替换换行符cleaned_text = cleaned_text.replace('\n', ' ')# 删除连字符周围的空格cleaned_text = re.sub(r'\s*-\s*', '', cleaned_text)return cleaned_textclean_text=clean_text(text)
4. 加载 OpenAI API
配置 OpenAI,并提供认证信息:
import osos.environ["OPENAI_API_KEY"] = "your-openai-key-here"
接下来查看下本书的 token 数量。
from langchain import OpenAIllm = OpenAI()Tokens = llm.get_num_tokens(clean_text)print (f"We have {Tokens} tokens in the book")
该书包含超过 466,000 个标记。直接将所有标记输入 LLM 成本高昂。为降低成本,我们将采用 K-means 聚类算法提取书中的关键片段。
注:K-means 聚类算法的使用受数据专家 Greg Kamradt 教程的启发。
为提取关键内容,首先将书籍分割成若干文本块。
5. 将内容拆分为文档
接下来使用 LangChain 的 `SemanticChunker` 实用程序将书的内容拆分为文档。
from langchain_experimental.text_splitter import SemanticChunkerfrom langchain_openai.embeddings import OpenAIEmbeddingstext_splitter = SemanticChunker(OpenAIEmbeddings(), breakpoint_threshold_type="interquartile")docs = text_splitter.create_documents([clean_text])
`SemanticChunker` 接收两个参数,第一个是嵌入模型。该模型生成的嵌入用于基于语义拆分文本。第二个参数是`breakpoint_threshold_type`,它根据语义相似性确定文本应该在哪些点上拆分为不同的块。
注意:通过处理这些较小的、语义相似的块,我们旨在最小化LLM中的最近性和首要性效应。这种策略使我们的模型能够更有效地处理每个小的上下文,确保更平衡的解释和响应生成。
6. 找到每个文档的 embedding
现在我们将使用 OpenAI 默认的方式获取每篇生成文档的 embedding。
import numpy as npimport openaidef get_embeddings(text):response = openai.embeddings.create(model="text-embedding-3-small",input=text)return response.dataembeddings=get_embeddings([doc.page_content for doc in docs])
7. 重新排列数据
接下来,将文档内容和它们的 embeddding 列表转换为 pandas DataFrame,以便更轻松地处理和分析数据。
import pandas as pdcontent_list = [doc.page_content for doc in docs]df = pd.DataFrame(content_list, columns=['page_content'])vectors = [embedding.embedding for embedding in embeddings]array = np.array(vectors)embeddings_series = pd.Series(list(array))df['embeddings'] = embeddings_series
8. 使用 Faiss 进行高效聚类
现在,我们将文档向量转换为与 Faiss 兼容的格式,使用 K-means 将它们聚类为 50 个组,然后创建 Faiss 索引以在文档之间进行高效的相似性搜索。
import numpy as npimport faiss# 如果尚未转换为float32,请将其转换为float32array = array.astype('float32')num_clusters = 50# 向量维度dimension = array.shape[1]# 使用Faiss训练KMeanskmeans = faiss.Kmeans(dimension, num_clusters, niter=20, verbose=True)kmeans.train(array)# 直接访问质心centroids = kmeans.centroids# 为原始数据集创建新索引index = faiss.IndexFlatL2(dimension)# 将原始数据集添加到索引中index.add(array)
注意:选择 K-means 聚类的原因是每个聚类将具有相似的内容或相似的上下文,因为该聚类中的所有文档都具有相关的嵌入,并且我们将选择最接近核心的文档。
9. 导出文档
现在,我们将从每个聚类中选择最接近聚类中心的文档,以此代表该聚类的核心内容。
D, I = index.search(centroids, 1)
以下代码使用 similarity_search_by_vector 方法,查找与每个聚类中心向量最接近的文档。该方法返回两个数组:D 包含每个文档到其最近聚类中心的距离,I 包含这些最近文档的索引。
由于文档是按书籍顺序排列的,我们需要对选定的文档索引进行排序以保持文本的原始顺序。
sorted_array = np.sort(I, axis=0)sorted_array=sorted_array.flatten()extracted_docs = [docs[i] for i in sorted_array]
10. 获取每个文档的摘要
现在使用 GPT-4 模型获取每个文档的摘要,以节省成本。首先我们来定义模型。
model = ChatOpenAI(temperature=0,model="gpt-4")
定义提示并使用 LangChain 创建提示模板,将其传递给模型。
from langchain_core.output_parsers import StrOutputParserfrom langchain_openai import ChatOpenAIfrom langchain_core.prompts import ChatPromptTemplateprompt = ChatPromptTemplate.from_template("""You will be given different passages from a book one by one. Provide a summary of the following text. Your result must be detailed and atleast 2 paragraphs. When summarizing, directly dive into the narrative or descriptions from the text without using introductory phrases like 'In this passage'. Directly address the main events, characters, and themes, encapsulating the essence and significant details from the text in a flowing narrative. The goal is to present a unified view of the content, continuing the story seamlessly as if the passage naturally progresses into the summary.Passage:```{text}```SUMMARY:""")
接下来使用 LangChain Expression Language(LCEL)定义 LangChain 的链。
chain= (prompt| model|StrOutputParser() )
摘要链使用 `StrOutputParser` 来解析输出,当然还可以探索其他输出解析器。
最后,将定义的链应用于每个文档以获取摘要。
from tqdm import tqdmfinal_summary = ""for doc in tqdm(extracted_docs, desc="Processing documents"):# 获取新的摘要。new_summary = chain2.invoke({"text": doc.page_content})# 更新最后两个摘要的列表:删除第一个摘要并在末尾添加新的摘要。final_summary+=new_summary
11. 将摘要保存为 PDF
接下来我们将摘要格式化并以 PDF 格式保存。
from fpdf import FPDFclass PDF(FPDF):def header(self):# 选择Arial粗体15号字体self.set_font('Arial', 'B', 15)# 向右移动self.cell(80)# 带边框的标题self.cell(30, 10, 'Summary', 1, 0, 'C')# 换行self.ln(20)def footer(self):# 距离底部1.5厘米self.set_y(-15)# 选择Arial斜体8号字体self.set_font('Arial', 'I', 8)# 页码self.cell(0, 10, 'Page %s' % self.page_no(), 0, 0, 'C')# 实例化PDF对象并添加一页pdf = PDF()pdf.add_page()pdf.set_font("Arial", size=12)# 确保“last_summary”文本被视为UTF-8# 如果不同,请将“last_summary”替换为您的实际文本变量# 确保您的文本是一个UTF-8编码的字符串last_summary_utf8 = last_summary.encode('latin-1', 'replace').decode('latin-1')pdf.multi_cell(0, 10, last_summary_utf8)# 将PDF保存到文件pdf_output_path = "s_output1.pdf"pdf.output(pdf_output_path)
至此,我们在PDF格式中获得了整本书的完整摘要。
结论
本教程探讨了如何利用 LLM 摘要大型文本(例如整本书籍),并解决了上下文限制和成本挑战。我们学习了文本预处理步骤,并结合语义分块和 K-means 聚类策略,有效地解决了模型的上下文长度限制问题。高效的聚类方法提取了关键段落,减少了直接处理大规模文本的开销,显著降低了成本。此外,该方法还减轻了 LLM 固有的首要性效应和最近性效应,确保所有文本段落得到均衡考虑。
目前,通过 LLM API 开发 AI 应用备受关注,其中向量数据库在提供高效的上下文嵌入存储和检索方面发挥着关键作用。MyScaleDB 是一款专为 AI 应用设计的向量数据库,兼顾成本、准确性和速度。其 SQL 友好界面使开发者无需学习新的技术即可快速构建 AI 应用。
了解墨奇科技 点击更多资讯





