大模型(qwen2.5) RAG实现龙族小说的知识库问答(这是一个记录贴)
同样,我们先把这个类所有的代码贴出来方便复制@property@propertydef _call(self,) -> str:messages,@property这里就不详细解释了,我们只解释每个函数的大致作用这段代码定义了一个名为Qwen2_5_GLM的类,该类继承自langchain.llms.base.LLM和abc.ABC。这个类实现了与一个大型语言模型(LLM)交互的功能,具体是阿里云
一.环境搭建
RAG简介
RAG(Retrieval-Augmented Generation)是一种结合了信息检索(Retrieval)和生成模型(Generation)的框架,用于增强自然语言处理任务中的文本生成能力。它通过从大型语料库中检索相关信息并将其提供给生成模型,使得生成的文本更加准确、上下文相关且信息丰富。
我们来粗略地解释一下RAG的过程,如下图(这是百度上找的一幅相关图片)
下方是RAG的详细介绍
一文彻底搞懂大模型 - RAG(检索、增强、生成)_rag大模型-CSDN博客
接下来开始搭建小说问答知识库了
注册一个funHPC的云端算力
使用code-server或者本地vscode链接ssh都可以
二.下载模型
1.Qwen2.5-14B-Instruct-GPTQ-Int4下载
LLM模型使用的是qwen2.5-14b的int4量化版本
这里是魔搭社区的官方链接:https://modelscope.cn/models/Qwen/Qwen2.5-14B-Instruct-GPTQ-Int4
下面是下载的步骤
(1)安装modelscope库
打开云环境或者本地环境的终端,运行下方代码
pip install modelscope
(2)在运行完成后新建py文件,写入下载模型的代码
#模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('Qwen/Qwen2.5-14B-Instruct-GPTQ-Int4')
如果要指定下载路径,在snapshot_download方法内添加
cache_dir=<指定的下载路径>
等待模型下载完成
2.gte_Qwen2-1.5B-instruct
向量模型使用的是gte_Qwen2-1.5B-instruct,该模型的魔搭社区官方链接如下:
https://modelscope.cn/models/iic/gte_Qwen2-1.5B-instruct
同样使用modelscope下载,下载代码:
#模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('iic/gte_Qwen2-1.5B-instruct')
三. 加载txt文档并存入向量数据库
1.准备工作
(1)下载langchain库
LangChain库是一个用于构建语言模型驱动的应用程序的框架。具体的介绍可参考官网和下方文章
官网:LangChain 框架介绍 | 🦜️🔗 Langchain
博客:一文搞懂LangChain是什么(非常详细),零基础入门到精通,看这一篇就够了-CSDN博客
(2)下载向量数据库faiss
在命令行运行以下命令
pip install faiss-gpu
(3)下载龙族2的txt文档
下载链接我放在下方了
龙族全套 共七册(龙族Ⅰ-龙族Ⅳ+龙族前传)江南.pdf - my-share 的分享
2.存入数据库
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
class EmbText():
def loader_text(self):
embeddings_model_dir = '/root/.cache/modelscope/hub/iic/gte_Qwen2-1.5B-instruct'
text_loader = TextLoader('龙族2悼亡者之瞳.txt', encoding='GBK')
data = text_loader.load()
splitter = CharacterTextSplitter(chunk_size=512,chunk_overlap=24)
splitter_data = splitter.split_documents(data)
print(splitter_data)
embedding_model = HuggingFaceEmbeddings(model_name=embeddings_model_dir)
db = FAISS.from_documents(splitter_data, embedding_model)
db.save_local('faiss/my_emd_data')
return splitter_data
if __name__ == '__main__':
emb = EmbText()
emb.loader_text()
详细解释
最上方的是导入模块
CharacterTextSplitter:文本分割器,用于将大块的文本分割成更小的部分或片段
TextLoader
:TXT文档加载器
HuggingFaceEmbeddings:加载 Hugging Face 提供的预训练语言模型以生成文本嵌入
FAISS:FAISS
类封装了 FAISS 库的功能,允许你在大量的高维向量中进行快速的近似最近邻搜索。在 langchain
中使用 FAISS
类,通常是为了建立一个向量数据库,可以存储由例如 HuggingFaceEmbeddings
生成的嵌入,并且支持对这些嵌入进行快速查询和相似性搜索。
loader_text函数:
这个函数loader_text定义了一个方法,用于加载PDF文档、分割文本并创建一个向量数据库。下面将详细解释每一行代码的作用:
embeddings_model_dir = '/root/.cache/modelscope/hub/iic/gte_Qwen2-1.5B-instruct' 这行代码设置了模型目录的路径(记得将绿色部分的路径改成你自己的向量模型的路径),该目录是嵌入模型的路径,用于生成文本的嵌入(embeddings)。
pdf_loader = PDFMinerLoader('龙族全套.pdf')创建了一个PDFMinerLoader对象,它会使用pdfminer库来加载和解析PDF文件(这里记得。这里的参数指定了要加载的PDF文件名。
data = pdf_loader.load()调用load方法从PDF文件中提取文本数据,并将其存储在变量data中。
splitter = CharacterTextSplitter(chunk_size=256, chunk_overlap=10)初始化了一个CharacterTextSplitter对象,用于将文本数据分割成更小的块。这里设置每个块大小为256个字符,相邻块之间有10个字符的重叠。
splitter_data = splitter.split_documents(data)使用split_documents方法对data进行分割,产生一系列较小的文本块。
embedding_model = HuggingFaceEmbeddings(model_name=embeddings_model_dir)创建了一个HuggingFaceEmbeddings对象,它会加载指定目录下的预训练模型,并能将文本转换为数值型的嵌入向量。
db = FAISS.from_documents(splitter_data, embedding_model)使用FAISS库创建了一个向量数据库。FAISS是一个高效的相似性搜索库,这里它将基于文本块的嵌入向量建立索引。
db.save_local('faiss/my_emd_data')将创建的向量数据库保存到本地磁盘上的指定路径faiss/my_emd_data。
运行结果如下
同时我们打开向量数据库的路径,我的就是代码所示的路径,可以看见在faiss/my_emd_data目录下多了两个文件
四.langchain自定义本地模型
同样,我们先把这个类所有的代码贴出来方便复制
from transformers import AutoModelForCausalLM, AutoTokenizer
from abc import ABC
from langchain.llms.base import LLM
from typing import Any, List, Mapping, Optional
from langchain.callbacks.manager import CallbackManagerForLLMRun
model_name = "/root/.cache/modelscope/hub/Qwen/Qwen2.5-14B-Instruct-GPTQ-Int4"
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype="auto",
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
class Qwen2_5_GLM(LLM, ABC):
max_token: int = 10000
temperature: float = 0.01
top_p:float = 0.9
history_len: int = 3
def __init__(self):
super().__init__()
@property
def _llm_type(self) -> str:
return "Qwen"
@property
def _history_len(self) -> int:
return self.history_len
def set_history_len(self, history_len: int = 10) -> None:
self.history_len = history_len
def _call(
self,
prompt: str,
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> str:
messages = [
{"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
{"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
generated_ids = model.generate(
**model_inputs,
max_new_tokens=2048
)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
return response
@property
def _identifying_params(self) -> Mapping[str, Any]:
"""Get the identifying parameters."""
return {"max_token": self.max_token,
"temperature": self.temperature,
"top_p": self.top_p,
"history_len": self.history_len}
这里就不详细解释了,我们只解释每个函数的大致作用
这段代码定义了一个名为Qwen2_5_GLM的类,该类继承自langchain.llms.base.LLM和abc.ABC。这个类实现了与一个大型语言模型(LLM)交互的功能,具体是阿里云开发的Qwen模型的一个特定版本。下面是对每个函数的作用解释:
类属性
max_token, temperature, top_p, history_len
这些都是类的属性,用来设置生成文本时的一些参数:
max_token: 控制最大输出token数量。
temperature: 影响输出的随机性;值越低,输出越确定。
top_p: 核采样参数,控制词汇表中累积概率分布的最大值。
history_len: 控制对话历史记录的长度。
构造函数
__init__(self)
类的构造函数,调用父类的构造函数初始化实例。
属性方法
_llm_type(self) -> str
返回表示LLM类型的字符串,在这里是"Qwen"。
_history_len(self) -> int
返回对话历史记录的长度。
set_history_len(self, history_len: int = 10) -> None
设置对话历史记录的长度,默认为10。
主要方法
_call(self, prompt: str, stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any) -> str
这是实际与LLM进行交互的方法,它接收一个提示(prompt)作为输入,并返回由LLM生成的响应文本。
它首先构建了消息列表,包括系统角色说明和用户提供的提示。
然后使用apply_chat_template方法准备这些消息以供模型处理。
接着将文本转换为模型可以理解的张量格式。
使用模型的generate方法基于输入生成新的文本。
最后,将生成的ID解码回人类可读的文本,并返回这个文本。
辅助方法
_identifying_params(self) -> Mapping[str, Any]
返回一个映射,包含用于标识当前LLM配置的关键参数。这有助于在不同地方识别或比较不同的LLM配置。
五.主函数
同样,我们先贴全部代码
from langchain.prompts import PromptTemplate
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from Qwen2_5_GLM import Qwen2_5_GLM
class Main_main():
def define_prompt(self,que):
embeddings_model_dir = '/root/.cache/modelscope/hub/iic/gte_Qwen2-1.5B-instruct'
embedding_model = HuggingFaceEmbeddings(model_name=embeddings_model_dir)
db = FAISS.load_local('faiss/my_emd_data',embedding_model,allow_dangerous_deserialization=True)
docs = db.similarity_search(que,k=1000)
docs_m=[]
for doc in docs:
docs_m.append(doc.page_content)
PROMPT_TEMPLATE="基于以下已知信息,思考回答用户问题,不允许在答案中添加编造成分\n已知内容\n{content}\n用户问题\n{que}"
prompt = PromptTemplate(input_variables=['content','que'],template=PROMPT_TEMPLATE)
my_prompt = prompt.format(content=docs_m,que=que)
return my_prompt
def qa(self):
llm = Qwen2_5_GLM()
que = '路明非为什么要和楚子航谈论星座'
prompt = Main_main().define_prompt(que)
print(prompt)
result = llm(prompt)
return result
if __name__ == '__main__':
re_=Main_main().qa()
print(re_)
下面是解释:
define_prompt(self, que)
这个方法的作用是构建一个特定的提示(prompt),用于引导语言模型生成回答。它执行以下步骤:
加载嵌入模型:使用 HuggingFace 的嵌入模型来转换文本为向量表示。
加载 FAISS 数据库:从本地加载一个预先构建好的 FAISS 向量存储,该数据库包含大量文档的向量表示。
搜索相似文档:基于输入的问题 que,在 FAISS 数据库中搜索最相关的文档。
整理文档内容:将找到的相关文档的内容提取出来,并整理成一个字符串列表 docs_m。
创建提示模板:定义了一个字符串模板 PROMPT_TEMPLATE,该模板指定了如何组合已知信息和用户问题。
格式化提示:使用 PromptTemplate 类根据模板和变量值(即文档内容和问题)生成最终的提示文本 my_prompt。
qa(self)
这个方法的作用是获取对给定问题的回答。它执行以下操作:
实例化 LLM:创建了 Qwen2_5_GLM 类的一个实例,这应该是用于生成回答的大规模语言模型。
定义问题:设定了一个具体的问题 que。
构造提示:调用 define_prompt 方法来构造与问题对应的提示。
打印提示:输出构造好的提示到控制台,以便可以查看或调试。
获取回答:将提示传递给语言模型 llm,并获取其生成的回答 result。
返回结果:最后,将语言模型生成的回答作为方法的返回值。
六.最终运行结果
在经过上述的配置后,我们运行最终的mian文件,得到了下面的结果
当然为了更准确地生成回答,我们最好能在存入数据库前对文本进行数据清洗,这可能会在后续的记录贴中记录
更多推荐
所有评论(0)