欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > RAG库搭建:从零开始,开启智能问答新世界

RAG库搭建:从零开始,开启智能问答新世界

2025/4/3 6:01:06 来源:https://blog.csdn.net/2302_80236633/article/details/146883876  浏览:    关键词:RAG库搭建:从零开始,开启智能问答新世界

RAG库搭建:从零开始,开启智能问答新世界

  • 从零开始的RAG库的搭建
    • 一、基本结构
    • 二、向量化结构
      • 1.为什么需要向量化?
      • 2.向量化的一般方法
      • 3. 代码实现
    • 三、文档切分和加载模块
      • 1. 为什么需要文档切分?
      • 2.如何切分
      • 3. 文档切分和加载模块代码实现
    • 四、数据库和向量检索模块
      • 1.数据库和向量检索模块代码实现
    • 五、问答模块

从零开始的RAG库的搭建

什么是RAG?RAG是什么?RAG是Retrieval Augmented Generation的缩写,即检索增强生成。RAG是一种信息检索自然语言处理的结合,它通过将文档和用户问题进行匹配,从而实现信息检索自然语言处理的结合。

当我们输入一个问题,问题的答案会从文档中检索出来,然后将答案和问题一起输入到大模型中生成答案。

一、基本结构

要实现一个最简单的RAG,我们首先要了解RAG的基础结构

  • 1.向量化结构:能够将文档片段向量化
  • 2.文档切分和加载模块:能够将文档切分为多个片段,并加载到内存中
  • 3.向量检索模块:能够将向量检索到最相似的向量,并返回对应的文档片段
  • 4.向量数据库模块:能够将向量存储到数据库中,并查询到最相似的向量
  • 5.问答模块:能够将用户问题向量化,并查询到最相似的向量,并返回对应的答案

二、向量化结构

1.为什么需要向量化?

  • 1.高效检索:将文本向量化后,可以利用向量之间的距离(如余弦距离)来衡量文本的相似度,从而快速检索出与用户查询最相关的文档片段
  • 2.语义理解:向量化能够捕捉文本的语义信息,使得模型能够理解文本的含义,而不仅仅是基于关键词的匹配。

2.向量化的一般方法

  • 1.传统稀疏向量化(Sparse Embedding):映射成一个高维向量,维度通常与词汇表大小相同。向量的大部分元素为0,非零值表示特定单词在文档中的重要性。典型模型包括TF-IDF和BM25,适合关键词匹配任务。
  • 2.密集向量化(Dense Embedding):映射到一个相对低维的向量,所有维度都非零。典型模型包括基于BERT的模型(如BGE-v1.5)和Sentence Transformers,这些模型能够捕捉语义信息,适用于语义搜索任务

3. 代码实现

from sentence_transformers import SentenceTransformer
'''
SentenceTransformer 是一个非常流行的 Python 库,用于将文本(句子或段落)转换为密集的向量表示(嵌入向量)。它是基于 Hugging Face 的 transformers 库和 PyTorch 的,提供了简单易用的接口来加载预训练的模型并生成文本嵌入。
'''
#下面实现一个基本类,然后继承这个类实现一个向量化的类
class BaseEmbeddings:"""Base class for embeddings"""def __init__(self, path: str, is_api: bool) -> None:self.path = pathself.is_api = is_api# 下面这个函数是得到向量化的函数def get_embedding(self, text: str, model: str) -> List[float]:raise NotImplementedError#下面这个函数大户是计算余弦相似度@classmethoddef cosine_similarity(cls, vector1: List[float], vector2: List[float]) -> float:"""calculate cosine similarity between two vectors"""dot_product = np.dot(vector1, vector2)magnitude = np.linalg.norm(vector1) * np.linalg.norm(vector2)if not magnitude:return 0return dot_product / magnitude#下面这个类是实现一个向量化的类
class MyEmbedding(BaseEmbeddings):"""class for My embeddings"""def __init__(self, path: str = '', is_api: bool = False) -> None:super().__init__(path, is_api)self.path = pathself.model = SentenceTransformer(path)#这里是加载一个嵌入模型,可以是bert的模型def get_embedding(self, text: str) -> List[float]:embeddings  = self.model.encode(text)return embeddings

三、文档切分和加载模块

1. 为什么需要文档切分?

  • 1.提高检索效率:将文档切分为多个片段,可以减少检索的文档数量,提高检索效率。
  • 2.提高检索准确性:将文档切分为多个片段,可以提高检索的准确性,因为每个片段都包含一些与用户查询最相关的信息。

2.如何切分

  • 1.基于字符长度:将文档切分为固定长度的片段。
  • 2.基于句子长度:将文档切分为句子,然后根据句子长度进行切分。
  • 3.滑动窗口切分:允许相邻块之间部分重叠,减少信息断裂,缓解上下文不连贯问题,提升检索相关性,但是会增加计算和内存开销
    除此之外还有许多切分方法,这里不在赘述

3. 文档切分和加载模块代码实现


def get_chunk(cls, text: str, max_token_len: int = 600, cover_content: int = 150):chunk_text = []curr_len = 0curr_chunk = ''lines = text.split('\n')  # 假设以换行符分割文本为行for line in lines:line = line.replace(' ', '')line_len = len(enc.encode(line))if line_len > max_token_len:print('warning line_len = ', line_len)if curr_len + line_len <= max_token_len:curr_chunk += linecurr_chunk += '\n'curr_len += line_lencurr_len += 1else:chunk_text.append(curr_chunk)curr_chunk = curr_chunk[-cover_content:]+linecurr_len = line_len + cover_contentif curr_chunk:chunk_text.append(curr_chunk)return chunk_text

四、数据库和向量检索模块

数据库和向量检索模块需要实现下面四个功能:

  • persist:数据库持久化,本地保存
  • load_vector:从本地加载数据库
  • get_vector:获得文档的向量表示
  • query:根据问题检索相关的文档片段

1.数据库和向量检索模块代码实现


class VectorStore:def __init__(self, document: List[str] = ['']) -> None:self.document = documentdef get_vector(self, EmbeddingModel: BaseEmbeddings) -> List[List[float]]:self.vectors = []for doc in tqdm(self.document, desc="Calculating embeddings"):self.vectors.append(EmbeddingModel.get_embedding(doc).tolist())return self.vectorsdef persist(self, path: str = 'storage'):if not os.path.exists(path):os.makedirs(path)with open(f"{path}/doecment.json", 'w', encoding='utf-8') as f:json.dump(self.document, f, ensure_ascii=False)if self.vectors:with open(f"{path}/vectors.json", 'w', encoding='utf-8') as f:json.dump(self.vectors, f)def load_vector(self, path: str = 'storage'):with open(f"{path}/vectors.json", 'r', encoding='utf-8') as f:self.vectors = json.load(f)with open(f"{path}/doecment.json", 'r', encoding='utf-8') as f:self.document = json.load(f)def get_similarity(self, vector1: List[float], vector2: List[float]) -> float:return BaseEmbeddings.cosine_similarity(vector1, vector2)def query(self, query: str, EmbeddingModel: BaseEmbeddings, k: int = 1) -> List[str]:query_vector = EmbeddingModel.get_embedding(query)result = np.array([self.get_similarity(query_vector, vector)for vector in self.vectors])return np.array(self.document)[result.argsort()[-k:][::-1]].tolist()

五、问答模块

问答模块其实非常简答,将检索到的内容和用户的问题进行拼接,然后使用LLM进行回答即可。

class BaseModel:def __init__(self, path: str = '') -> None:self.path = pathdef chat(self, prompt: str, history: List[dict], content: str) -> str:passdef load_model(self):passclass InternLMChat(BaseModel):def __init__(self, path: str = '') -> None:super().__init__(path)self.load_model()def chat(self, prompt: str, history: List = [], content: str='') -> str:prompt = PROMPT_TEMPLATE['InternLM_PROMPT_TEMPALTE'].format(question=prompt, context=content)response, history = self.model.chat(self.tokenizer, prompt, history)return responsedef load_model(self):import torchfrom transformers import AutoTokenizer, AutoModelForCausalLMself.tokenizer = AutoTokenizer.from_pretrained(self.path, trust_remote_code=True)self.model = AutoModelForCausalLM.from_pretrained(self.path, torch_dtype=torch.float16, trust_remote_code=True).cuda()

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词