LLM - 长文本总结处理方案
LLM 长文总结常用方法。
目录
一.引言
随着 LLM 的进步,目前支持的 token 数目也在逐步增加,从最开始的 4k、8k 延伸到现在的 16k、32k,从而可以支持更长的文本内容,但是随之而来的问题也不少:
- 机器资源不足
虽然模型支持的 token 数目变长了,但是我们手头显卡的资源可能是限制 token 长度的主要原因,因此对于一些长文本总结内容,虽然其在模型支持的 token 例如 32k 之内,但是我们的显存不足以支撑一下放入全部样本,因此 GPU 显存是一个限制条件。
- 理解能力不足
在资源充足的情况下,很多开源模型对长文本的理解和总结能力还存在不足的情况,如果一次输入过多 token,LLM 往往抓不到重点,或者出现幻觉的情况,因此对于长文总结,如何能够尽可能把每个段落都理解到也是一个关键点。
二.处理方式
1.Stuff
该方式是最直截了当的长文处理方式,一次性将所有内容输入给 LLM 理解。
- 优点
只调用一次 LLM,上下文完整便于模型理解全貌,直截了当。
- 缺点
对 GPU 资源、模型理解能力有很大要求,其次处理的 max_token 与当前 LLM 匹配,对于更长的文本仍然存在限制。
2.Map Reduce
写过 MR 处理数据的同学应该不陌生,其类似于分布式处理的概念,先将长文本分多段采用多个 mapper 进行处理,再使用 reducer 对所有 mapper 的结果进行聚合,最终得到总结。
- 优点
理论上 Mapper 可以无限多,因此处理的文本总长度也很可观,支持横向扩展。同时由于是 Map-Reduce 结构,因此支持并发,可以一次处理多条 Doc Block,因此执行效率也支持伸缩扩展。
- 缺点
一次总结需要多次调用 LLM,其次总结时单块 Mapper 容易有信息丢失,且总结和 Block 的切分也有关系,如果 Block 切分不佳,会造成大量上下文的缺失,影响总结效果。
3.Refine
与 MR 类似,先将长文本进行 Block 切分为多个小块进行分块合并总结,然后采用类似链式的总结过程,不断进行合并,最终生成全文的总结。
- 优点
相比 Map Reduce 该方法相对会少丢失一些信息,而且潜在有序的概念,因为是串行总结的,每一个总结都依赖上一个块的信息。
- 缺点
需要多次调用 LLM,而且由于每一个结果依赖上一个结果,因此生成阶段无法像 Map Reduce 那样并行,只能串行,文本过长时生成时间也会受影响。
4.Map Rerank
对长文进行 Block 操作,返回结果时同时返回并取相关性分数最高的分块总结作为结果。
- 优点
对文章进行分块总结,可以并行执行,效率高。
- 缺点
多次调用 LLM,无法进行全文总结,适合在文档中基于一些相关性指标检索最适合的分块。
5.Binary Map
对长文进行 Block 操作,返回结果按照二分的方式进行合并。
- 优点
对于 block 过多的场景,为了性能与效果的折中,可以选择 Binary 二分的方式进行总结合并,这样可以提高合并的效率。
- 缺点
两两组合的方式与前面的 MR 类似,也会丢失一部分信息,但是会少丢,当然这里 2 也是一个超参,我们也可以使用 3、4、5 等等,这个需要结合具体业务场景。
Tips:
该方法是博主自己想的,因此 Binary Map 这个 mode 在 Langchain 里还不支持,需要自己写逻辑。
三.总结测试
1.总结内容
URL: 三国演义读后感
2.Langchain 启动
import os
from langchain import OpenAI
from langchain.chains.summarize import load_summarize_chain
from langchain.chains import AnalyzeDocumentChain
from langchain.text_splitter import CharacterTextSplitter
os.environ["OPENAI_API_KEY"] = "your_api_key"
CHAIN_TYPE = "stuff"
long_text = "/Users/ddd/langchain/LongText.log"
with open(long_text, 'r', encoding='utf-8') as f:
state_of_the_union = f.read()
llm = OpenAI(temperature=0.95)
# 定义文本分割器 每块文本大小为500,不重叠
text_splitter = CharacterTextSplitter(
chunk_size=500,
chunk_overlap=0,
length_function=len,
)
# 生成摘要
summary_chain = load_summarize_chain(llm, chain_type=CHAIN_TYPE)
summarize_document_chain = AnalyzeDocumentChain(combine_docs_chain=summary_chain, text_splitter=text_splitter)
res = summarize_document_chain.run(state_of_the_union)
print(res)
3.自定义逻辑
需要定义自己的 API-KEY,文本我们选择上面的三国演义总结,由于么有 API-KEY 这里程序也 Run 不起来,我们需要手动实现上面的 Mapper 或者总结逻辑,再自己构建 Prompt 逻辑:
- 初始化自己的 LLM
可以选择开源的 LLM,使用 HuggingFace 的 Auto API 直接加载。
- 选择自己的长文进行切分
虽然 OpenAI 不能用,但是 TextSplitter 可以用。
chunks = text_splitter.split_text(state_of_the_union)
for chunk in chunks:
print(chunk)
- 基于不同的处理方式总结
根据上面 Stuff、MR、Refine 和 Map Rerank 的逻辑图实现自己的总结逻辑查看总结效果。不过由于我们本地机器的限制,Mapper 能否并行就看我们能起几个服务了。
四.总结
长文总结是很典型常见的问题,大家有更多想法和意见也欢迎在评论区交流讨论~
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)