个人偏向深挖,对一些好奇的事情,喜欢深挖,再挖,
:软件识别上传的表格和图片,本质是把“像素/版面”转成“结构化文本+位置信息”,再进入索引。
第一部分:大概逻辑
一、表格识别
难点:既是视觉对象,又有行列语句,流水线模式。
二、图片识别
分两类,处理路径不同。
三、融合进文档回答
1、每一个表格/图片作为一个特殊chunk,元数据。
记录:所属章节、页码、类型(table/image)、原始坐标。
2、表格存结构化JSON+线性化文本(如“列:年份、营收;行:2023,100万”),兼顾精确查询和语义检索。
3、图片存OCR文本+caption+图像向量。
4、检索命中后,生成阶段把表格/图片的结构化内容按需展开给LLM,并支持引用回原图/原表位置。
第二部分:内部逻辑运转
一、语义对比:向量空间里的距离
核心:“把问题和文档块映射到一个高维向量空间,比谁离得近“
1、离线:每个文档块经embedding模型变成一个高维向量,存进向量库。
2、在线:用户问题同一个模型变成同维向量。
3、对比:计算两个向量的相似度,常用余铉相似度(看夹角,不看长度)
:cos(q,d) = (q·d)/(|q| *|d| )
注:值越接近1越相似
4、具体的实现代码有:
from sentence_transformers import SentenceTransformer import numpy as np model = SentenceTransformer("all-MiniLM-L6-V2") #离线 docs = ["营收同比增长20%","净利润下降5%", "2023年总资产100亿"] doc_vecs = model.encode(docs, normalize_embeddings = Tyre)#shap:(3,384) #在线:查询编码并归一处 q_vec = model.encode["收入表现如何"], normalize_embeddings = Tyre[0] #对比:一行代码完成所有相似度计算 scores = doc_vecs @ q_vec #shape:(3, ),值越近1越相似 #取 top-k top_idx = np.argsort(scores)[::-1][:2] for i in top_idx: print(f"score={scores[i]:.4f} {docs[i]")二、关键词对比:统计匹配
向量检索对精确词(型号、编号、人名)不敏感,所以并一路BM25
1、把问题和文档块都切成词(分词)。
2、算词频、逆文档频率(TF-IDF思想),给每个词打分。
3、问题里的词在文档块中出现越多、越稀有,得分越高。
BM25比的是词的重合与权重,不涉及语义,但精确、可解释。
from bm25_jinba import BM25 docs = [ "Python 是一种高级编程语言", "机器学习是人工智能的分支", "深度学习是机器学习的子领域", ] bm25 = BM25(k1 = 1.5, b = 0.75) bm25.fit(docs, ids = [101,102,103]) results = bm25.search("机器学习",top_k = 2) for doc_id , score in results: print(f"ID = {doc_id}, score = {score:4f}")三、合并
向量路和关键词路各出Top-K,用RRF(倒数排名融合)合并:
score(d) =
优先调度:文档块排在越靠前,融合分越高。
具体代码实现:
def rrf_fuse(vector_docs,bm25_docs, k = 60): """vector_docs /b,25_docs:已按相关度降序列的文档标识列表 k : 平滑常熟,通常取 60 """ scores = {} for rank,doc in enumerate(vector_docs): scores[doc] = scores.get(doc,0) + 1 / (k + rank +1) for rank,doc in enumerate(bm25_docs): scores[doc] = scores.get(doc,0) + 1 / (k + rank +1) return sorted(scores,key==scores.get,reverse+Ture)四、精排
粗召回时,用cross- encode对“问题+文档块”成对编码,让模型直接看两者交互,输出相关性分数。
具体的代码实现:
from sentence_transformers import CrossEncoder cross_encode = CrossEncoder(:crossencoder/ms-marco-MiniLM-L-6-v2") #融合后的候选(假设已拿到问题和文档文本对) query = "2023年营收多少“ candidates = [ "2023年公司实现营收入人民币100亿元“, ”净利润同比下降5%“, "公司成立于2010年", ] pairs = [[query,doc] for doc in candidates] scores = cross_encoder.predict(pairs, show_progress_bar = False) ranked = sorted(zip(candidates,scores),key = lambda x :x[1], reverse = Ture) for doc,score in ranked: print(f"{score:4f} | {doc}")