LLM(大型语言模型)在生成自然语言文本方面展现出强大的能力,但它们的训练数据通常来自于公开的、广泛的数据源,这意味着它们在面对特定企业的私有数据时,可能无法准确地生成相关的答案或信息。一种可行的思路是将这些专业数据用于微调大型语言模型(LLM),但这通常对技术和成本的要求都非常高。相比之下,RAG系统提供了一个更加经济和高效的解决方案。它通过在用户的原始问题后附加相关的私域数据,将其与通用LLM结合进行分析和总结。这样,RAG系统通过检索增强的方式为LLM提供更精准的信息,大大提升了最终的回答效果,如下图所示:
企业私有数据形成知识的重要性
企业的私有数据通常以文档形式存储,包括技术手册、政策文件、客户档案、研究报告等。这些文档承载了企业的核心知识,是企业运营的基础。通过将这些文档转换为可检索的结构化数据,RAG系统能够在需要时快速访问和利用这些数据,从而为LLM提供即时的参考。这一过程可以:
-
提高回答的专业性:确保回答内容符合企业的专业要求和标准。
-
增强数据安全性:通过私有数据的封闭检索,避免敏感信息泄露。
-
支持动态更新:随着文档的更新,RAG系统能够不断刷新其知识库,保证回答内容的时效性。
企业私有数据存储介质的多样性
企业的私有数据通常以多种介质形式存储,这些介质承载了公司内部的关键知识和信息。常见的文档介质包括:
-
文本文档(如TXT、Markdown)
-
办公文档(如DOC、DOCX、PPT、XLSX)
-
PDF文件
-
数据库(如SQL、NoSQL)
-
多媒体文件(如图像、音频、视频)
-
专有格式文件(如设计图纸、技术说明书等)
这些文档介质中的数据往往是结构化、半结构化或非结构化的,因此解析和读取这些数据对实现企业知识的自动化检索与生成至关重要。
基于DOC、DOCX文档的解析
DOC与DOCX是微软Word的文件格式,广泛用于企业的各类文档编写和存储。解析这些文档类型的过程相对复杂,因为它们不仅包含文本,还可能嵌入各种复杂的元素,如表格、图像、超链接、页眉页脚、注释、修订历史等。这些元素的解析和读取涉及多个层次的技术挑战。
1. 文本内容的解析
-
段落和标题:DOCX文件内部结构通常是通过XML来表示的,各个段落、标题有明确的标签。解析时需要识别这些标签,以提取不同层级的文本结构信息。
-
字符样式:字体、大小、颜色等样式信息也可以通过解析XML获取,这对于保留原始文档的格式至关重要。
from docx import Document`` ``def parse_docx_text(file_path):` `doc = Document(file_path)` `for para in doc.paragraphs:` `print(f"段落内容: {para.text}")``
2. 表格内容的解析
-
表格结构:表格在企业文档中非常常见,如财务报表、技术规范表等。解析表格时,需要精确识别每个单元格的数据,并保持其在表格中的位置。
-
复杂表格:包括合并单元格、嵌套表格等,解析起来更为复杂,需要特殊的处理逻辑。
def parse_docx_tables(file_path):` `doc = Document(file_path)` `for table in doc.tables:` `for row in table.rows:` `print([cell.text for cell in row.cells])``
3. 图像和其他嵌入对象的解析
-
图像:DOCX文档中可能包含的图像可以通过解析文档内嵌的
<w:drawing>
标签来提取。 -
其他对象:包括图表、公式、SmartArt等。由于这些对象可能以二进制格式存储,需要使用特定库或API来解析和处理。
def extract_images_from_docx(docx_path):` `doc = Document(docx_path)` `for rel in doc.part.rels.values():` `if "image" in rel.target_ref:` `print(f"提取图像: {rel.target_ref}")``
4. 元数据的提取
- 作者、创建日期、修订历史等元数据包含在文档的属性中,对于文档的版本控制和溯源管理非常重要。这些信息可以通过访问DOCX文件的
docProps
来提取。
5. 处理批量文档
- 在企业中,批量处理大量DOCX文档是常见的需求。此时需要考虑并行处理技术,以提升解析效率,并可以考虑对解析后的内容进行语义切分,以优化后续的RAG应用。
import os``from concurrent.futures import ProcessPoolExecutor`` ``def process_docx_files(directory):` `docx_files = [f for f in os.listdir(directory) if f.endswith('.docx')]` `with ProcessPoolExecutor() as executor:` `for _ in executor.map(parse_docx_text, docx_files):` `pass``
解析和处理企业的DOC、DOCX文档是RAG系统的重要步骤,通过精确提取文档中的结构化和非结构化信息,可以更好地实现语义切分和知识库的构建。这些处理步骤不仅确保了企业私有数据的有效利用,还极大提升了RAG系统的生成质量和检索精准度。
下面我们详细解析一段Python代码编写的DocxParser类的Docx解析方法。
解析方法代码:
`def __call__(self, filename, binary=None, from_page=0, to_page=100000):` `self.doc = Document(` `filename) if not binary else Document(BytesIO(binary))` `pn = 0` `lines = []` `last_image = None` `for p in self.doc.paragraphs:` `if pn > to_page:` `break` `if from_page <= pn < to_page:` `if p.text.strip():` `if p.style and p.style.name == 'Caption':` `former_image = None` `if lines and lines[-1][1] and lines[-1][2] != 'Caption':` `former_image = lines[-1][1].pop()` `elif last_image:` `former_image = last_image` `last_image = None` `lines.append((self.__clean(p.text), [former_image], p.style.name))` `else:` `current_image = self.get_picture(self.doc, p)` `image_list = [current_image]` `if last_image:` `image_list.insert(0, last_image)` `last_image = None` `lines.append((self.__clean(p.text), image_list, p.style.name))` `else:` `if current_image := self.get_picture(self.doc, p):` `if lines:` `lines[-1][1].append(current_image)` `else:` `last_image = current_image` `for run in p.runs:` `if 'lastRenderedPageBreak' in run._element.xml:` `pn += 1` `continue` `if 'w:br' in run._element.xml and 'type="page"' in run._element.xml:` `pn += 1` `new_line = [(line[0], reduce(concat_img, line[1]) if line[1] else None) for line in lines]`` ` `tbls = []` `for tb in self.doc.tables:` `html= "<table>"` `for r in tb.rows:` `html += "<tr>"` `i = 0` `while i < len(r.cells):` `span = 1` `c = r.cells[i]` `for j in range(i+1, len(r.cells)):` `if c.text == r.cells[j].text:` `span += 1` `i = j` `i += 1` `html += f"<td>{c.text}</td>" if span == 1 else f"<td colspan='{span}'>{c.text}</td>"` `html += "</tr>"` `html += "</table>"` `tbls.append(((None, html), ""))` `return new_line, tbls`
def __call__(self, filename, binary=None, from_page=0, to_page=100000):` `self.doc = Document(filename) if not binary else Document(BytesIO(binary))` `pn = 0` `lines = []` `last_image = None``
-
函数入口:
__call__
是一个魔术方法,使得类实例可以像函数一样被调用。它接受文件名、二进制数据、起始页和结束页作为输入参数,并将文档加载到self.doc
中。如果提供了二进制数据,则使用BytesIO
进行处理。 -
页数控制:
from_page
和to_page
参数允许我们控制从文档的哪个页开始解析,到哪个页结束。这对于处理大型文档时非常有用,可以避免一次性加载整个文档导致内存过载。
`for p in self.doc.paragraphs:` `if pn > to_page:` `break` `if from_page <= pn < to_page:` `if p.text.strip():` `if p.style and p.style.name == 'Caption':` `former_image = None` `if lines and lines[-1][1] and lines[-1][2] != 'Caption':` `former_image = lines[-1][1].pop()` `elif last_image:` `former_image = last_image` `last_image = None` `lines.append((self.__clean(p.text), [former_image], p.style.name))` `else:` `current_image = self.get_picture(self.doc, p)` `image_list = [current_image]` `if last_image:` `image_list.insert(0, last_image)` `last_image = None` `lines.append((self.__clean(p.text), image_list, p.style.name))` `else:` `if current_image := self.get_picture(self.doc, p):` `if lines:` `lines[-1][1].append(current_image)` `else:` `last_image = current_image`` `
-
段落遍历:
for p in self.doc.paragraphs
遍历文档的所有段落。pn
记录当前页码,控制页码范围内的解析。 -
文本和图片处理:
-
段落文本:如果段落有文本内容且属于指定页码范围,则提取文本并进行清理(
self.__clean
)。 -
图片处理:如果段落样式为“Caption”(说明文字),则判断是否与图片相关联,关联的图片会被提取出来并添加到
lines
列表中。对于普通段落,提取图片后也将其加入lines
列表。
`for run in p.runs:` `if 'lastRenderedPageBreak' in run._element.xml:` `pn += 1` `continue` `if 'w:br' in run._element.xml and 'type="page"' in run._element.xml:` `pn += 1`` `
- 分页符处理:
for run in p.runs
遍历段落中的每一个运行块(run),检测是否存在分页符或手动分页符,并据此增加页码计数pn
。
new_line = [(line[0], reduce(concat_img, line[1]) if line[1] else None) for line in lines]
- 数据整合:将解析的行数据(文本+图片)进行合并处理。
reduce(concat_img, line[1])
会将同一行中的多张图片合并。
`tbls = []` `for tb in self.doc.tables:` `html = "<table>"` `for r in tb.rows:` `html += "<tr>"` `i = 0` `while i < len(r.cells):` `span = 1` `c = r.cells[i]` `for j in range(i+1, len(r.cells)):` `if c.text == r.cells[j].text:` `span += 1` `i = j` `i += 1` `html += f"<td>{c.text}</td>" if span == 1 else f"<td colspan='{span}'>{c.text}</td>"` `html += "</tr>"` `html += "</table>"` `tbls.append(((None, html), ""))` `return new_line, tbls`
-
表格解析:遍历文档中的每一个表格,将表格内容提取并转换为HTML格式。对于单元格内容相同且连续的情况,使用
colspan
合并单元格。 -
结果返回:最终函数返回提取的文本和图片组合(
new_line
),以及转换为HTML格式的表格(tbls
)。
这段代码展示了如何精确地提取文档的核心信息,包括段落、图片和表格。通过控制解析的页码范围,可以高效处理大型文档,而对于图文并茂的文档,这段代码尤其有价值。
在RAG系统中,这样的解析步骤可以帮助企业将非结构化文档转化为结构化数据,并通过进一步的语义切分和向量化处理,为后续的检索和生成提供高质量的输入。这段代码不仅展示了文档内容提取的技术实现,还表明了在复杂文档环境下,精确的文本与图像关联处理对RAG系统的重要性。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。