实习七月复盘

文件处理阶段

  1. 使用libreoffice将doc,docx文件处理成pdf文件,方便后续使用mineru进行提取
  2. 完成mineru的docker本地部署;搭建fastapi服务,与项目容器构建自定义网络,方便后续服务调用;使用locust完成对mineru的并发性能测试,和吞吐量测试
  3. 对mineru提取的html格式的表格进行预处理工作,将其转化成md格式,方便后续分块,节省tokens

部分技术细节

mineru提取效果说明

可以完整提取表格与图片,将图片以相对链接形式储存在images文件夹下;可以完成pdf与扫描件的提取,可以实现对图片中文字的识别;输出符合人类阅读顺序的文本,适用于单栏、多栏及复杂排版;删除页眉、页脚、脚注、页码等元素,确保语义连贯

目前问题:仍无法实现对多级标题的识别

mineru的fastapi启动指令

1
MINERU_MODEL_SOURCE=local CUDA_VISIBLE_DEVICES=1,2,3 mineru-api --host 0.0.0.0 --port 30000 --dp-size 3 --enable-torch-compile

MinerU支持通过sglang的多GPU并行模式来提升推理速度。

  • 如果您有超过多张显卡,可以使用sglang的多卡并行模式来增加吞吐量:--dp-size 2
  • 同时您可以启用torch.compile来将推理速度加速约15%:--enable-torch-compile

注意设置环境变量MINERU_MODEL_SOURCE=local CUDA_VISIBLE_DEVICES=1,2,3保证模型本地加载与调用指定gpu

mineru容器启动指令

1
2
docker run -d --name mineru-server --gpus all --shm-size 32g -p 30000:30000 --ipc=host -v /aisys/:/aisys/ --network network_test mineru-sglang:latest tail -f /dev/null
docker start mineru-server

mineru三种后端模式测试

pipeline (默认后端) ,vlm-sglang-engine,vlm-sglang-client

项目中使用的是vlm-sglang-engine,原因如下,pipeline应用场景更多是仅能cpu推理,解析速度大大落后与vlm模式,而我们gpu资源充足,自然不考虑;vlm-sglang-client应用场景更多是有SGLang服务器,这样客户端既可以不用安装sglang,同样不符合我们的条件

mineru并发与吞吐量测试

测试场景:10页的pdf,50用户并发

工具:locust

测试结果

对于推理模型的吞吐量,在3个gpu开启数据并行的情况下,平均每秒单个gpu处理tokens为1500左右

gpu状态如上:显存几乎打满 85–87 %,GPU 利用率 59–63 %,功耗 170–188 W / 350 W

压测结果如下,选取部分指标

指标 数值 通俗解释
平均响应时间 241 秒4 分钟 上传一个 PDF → 拿到解析结果,平均要等 4 分钟。
中位数 215 秒3.6 分钟 一半请求在 3.6 分钟内完成。
95% 用户 361 秒6 分钟 最慢的 5% 要等 6 分钟以上。
吞吐量 0.18 req/s 这台 MinerU 每分钟只能处理约11 个 PDF

分块阶段

当前主流的分块方式共五种:固定长度分块,语义分块,递归分块,文档结构分块,llm分块。

最后项目我选择了递归分块,原因如下:

  1. mineru无法正确提取md文档结构,因此我舍弃了文档结构分块
  2. 测试了agentic chunk(其主要思想是,先进行初步分段,按照长度或递归,然后让大模型生成这一段的概要,将段与段合并生成块),但是测试下来,我们这个一个文档的内容同质化很严重,基本上都分到一块里了,我猜测语义分块也是这种效果,因此舍弃
  3. 我们的文档中存在大量表格,我在预处理阶段增加了对表格的首尾标记,使用递归分块可以更好的保留这些结构

检索阶段

基于langchain_elasticsearch完成了向量搜索,bm25,混合检索,模糊检索的检索函数的编写。

结果如下:

  1. 混合检索elasticsearch需要付费使用
  2. bm25的多字段搜索有三种模式且字段的权重可以调整,后续评估时调整进行测试
  3. 检索的效果需要后续进行rag评估时判定

elasticsearch相关

完成对项目es模块的熟悉阅读;实现对elasticsearch的连接与字段的构建与存入。

关于字段的存储,我选取了report_name,report_url,page_content

相关细节

阅读elasticsearch代码相关记录:
  1. embedding_model.select_embeddings_model:根据指定的模型名称加载
  2. make_es_vector_store
    1. docs_url = pd.read_excel(‘docs_new.xlsx’),从excel加载url并进行数据处理,保存筛选后的ur
    2. 完成文件加载测试:xizhang/retrival/docfile_test/test.py;
    3. 初始化es,使用elastic_search.load_es_index加载存储索引
    4. 完成测试elasticsearch连接与索引构建(索引名zxj_test):/aisys/repo_dev/xizhang/retrival/elasticsearch_test/test_es_connect.py,/aisys/repo_dev/xizhang/retrival/elasticsearch_test/test_add_es.py
    5. 顺序批量处理文件:共16000多份,每十份为一批进行处理,使用download_pdf.py进行文件下载,使用,使用vector_base.rewrite_file对文件进行处理,这里可以修改代码,增加对mineru处理pdf的markdown文件的处理,返回Document对象列表;
  3. elastic_search

重写了检索策略的函数,包括BM25,KNN,混合搜索

  1. elastic_retriever创建Elasticsearch检索器:根据搜索类型选择对应的查询函数,创建Elasticsearch检索器ElasticsearchRetriever.from_es_params

  2. retrievers

    1. 定义函数select_retriever,根据指定的名称选择并返回相应的检索器,目前只有bm25
文档结构
1
2
3
4
5
6
7
8
doc = Document(
page_content=text_content,
metadata={
"report_name": folder_name,
"report_url": report_url,
"chunk_id": chunk_id
}
)

rag评估

待补充

后续优化思考

  1. 重排序部分我没有做过,不知道怎么做,也不知道效果会怎样(我感觉在我们这个场景应该提升有限,听你说也是这样)
  2. 如何存入数据库的部分,可能也是优化的点,比如可以尝试agentic rag这种,在存入数据库前再进行一步处理
  3. 还有一个点我比较好奇,我们项目在召回后是如何处理的,就是上下文拼接吗