赞
踩
RAG(Retrieval Augmented Generation)检索增强生成
通常用于在大模型交互过程中通过检索的方法来增强生成模型的能力。
由于大模型存在的局限性(大模型的知识不是实时的,大模型可能不知道某些私有领域的知识),可以使用检索的方式给大模型提供相应的知识,再参考回答问题
一张图说明RAG的流程
提到检索,我们通常会使用elastic search,但是es是基于关键字的检索,有其局限性,同一个语义,用词不同,就会导致无法检索出想要的数据
举例:
es中存储 “小明喜欢吃汉堡”,如果检索关键字“小明喜欢吃”,那么可以找到“汉堡”,但是如果检索“小明爱吃”,那么就找不到这个数据了
为了解决这种问题,所以需要使用向量检索
文本向量
文本向量是怎么得到的
向量间的相似度计算
两种计算方式
欧氏距离
余弦距离
def cos_sim(a, b):
'''余弦距离 -- 越大越相似'''
return dot(a, b)/(norm(a)*norm(b))
def l2(a, b):
'''欧式距离 -- 越小越相似'''
x = np.asarray(a)-np.asarray(b)
return norm(x)
向量数据库
向量数据库,是专门为向量检索设计的中间件
向量数据库本身不生成向量,向量是由 Embedding 模型产生的
向量数据库与传统的关系型数据库是互补的,不是替代关系,在实际应用中根据实际需求经常同时使用。
主流的向量数据库
流程如下:
1. 文档加载
def extract_text_from_pdf(filename,page_numbers=None,min_line_length=10): """从 PDF 文件中(按指定页码)提取文字""" paragraphs = [] buffer = '' full_text = '' # 提取全部文本 for i, page_layout in enumerate(extract_pages(filename)): # 如果指定了页码范围,跳过范围外的页 if page_numbers is not None and i not in page_numbers: continue for element in page_layout: if isinstance(element, LTTextContainer): full_text += element.get_text() + '\n' # 按空行分隔,将文本重新组织成段落 lines = full_text.split('\n') for text in lines: if len(text) >= min_line_length: buffer += (' '+text) if not text.endswith('-') else text.strip('-') elif buffer: paragraphs.append(buffer) buffer = '' if buffer: paragraphs.append(buffer) return paragraphs
2. 文档切割(交叠切割防止问题的答案跨两个片段,使上下文更完整)
def split_text(paragraphs,chunk_size=300,overlap_size=100): """按指定 chunk_size 和 overlap_size 交叠割文本""" sentences = [s.strip() for p in paragraphs for s in sent_tokenize(p)] chunks = [] i= 0 while i < len(sentences): chunk = sentences[i] overlap = '' prev_len = 0 prev = i - 1 # 向前计算重叠部分 while prev >= 0 and len(sentences[prev])+len(overlap) <= overlap_size: overlap = sentences[prev] + ' ' + overlap prev -= 1 chunk = overlap+chunk next = i + 1 # 向后计算当前chunk while next < len(sentences) and len(sentences[next])+len(chunk) <= chunk_size: chunk = chunk + ' ' + sentences[next] next += 1 chunks.append(chunk) i = next return chunks
3. 向量化(这里使用openai的向量化模型)
def get_embedding(text, model="text-embedding-ada-002"):
"""封装 OpenAI 的 Embedding 模型接口"""
return openai.Embedding.create(input=[text], model=model)['data'][0]['embedding']
4. 灌入向量库(使用chromadb)
def __init__(self, name="demo"):
self.chroma_client = chromadb.Client(Settings(allow_reset=True))
self.chroma_client.reset()
self.name = name
self.collection = self.chroma_client.get_or_create_collection(name=name)
def add_documents(self, documents):
self.collection.add(
embeddings=[get_embedding(doc) for doc in documents],
documents=documents,
metadatas=[{"source": self.name} for _ in documents],
ids=[f"id_{i}" for i in range(len(documents))]
)
5. 检索向量数据库
def search(self, query, top_n):
"""检索向量数据库"""
results = self.collection.query(
query_embeddings=[get_embedding(query)],
n_results=top_n
)
return results['documents'][0]
6. 将检索数据带入提示词
def build_prompt(template=prompt_template, **kwargs):
"""将 Prompt 模板赋值"""
prompt = template
for k, v in kwargs.items():
if isinstance(v, str):
val = v
elif isinstance(v, list) and all(isinstance(elem, str) for elem in v):
val = '\n'.join(v)
else:
val = str(v)
prompt = prompt.replace(f"__{k.upper()}__", val)
return prompt
7. 调用大模型(使用gpt-3.5-turbo)
def get_completion(prompt, context, model="gpt-3.5-turbo"):
"""封装 openai 接口"""
messages = context + [{"role": "user", "content": prompt}]
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=0, # 模型输出的随机性,0 表示随机性最小
)
return response.choices[0].message["content"]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。