大模型专栏介绍
😊你好,我是小航,一个正在变秃、变强的文艺倾年。
🔔本文为大模型专栏子篇,大模型专栏将持续更新,主要讲解大模型从入门到实战打怪升级。如有兴趣,欢迎您的阅读。
💡适合人群:本科生、研究生、大模型爱好者,期待与你一同探索、学习、进步,一起卷起来叭!
🔗篇章一:本篇主要讲解Python基础、数据分析三件套、机器学习、深度学习、CUDA等基础知识、学习使用AutoDL炼丹
🔗篇章二:本篇主要讲解基本的科研知识、认识数据和显卡、语言模型如RNN、LSTM、Attention、Transformer、Bert、T5、GPT、BLOOM、LLama、Baichuan、ChatGLM等系列、强化学习教程、大模型基础知识及微调等
🔗篇章三:本篇主要讲解智能对话、大模型基础实战如Ollama、Agent、QLoar、Deepspeed、RAG、Mobile Agent等、大模型领域前沿论文总结及创新点汇总
目录
- 介绍
- 分类
- 实战 - 搭建 RAG Demo
- 预备
- 索引
- 检索
- 生成
- 打包
- 部署
- 实战 - AI科研助手
- 项目结构
- 运行效果
介绍
- 背景:大型语言模型(LLM)存在一些固有的局限性,如“模型幻觉问题”、“时效性问题”和“数据安全问题”。
- 介绍:RAG 是检索增强生成(Retrieval Augmented Generation )的简称,它为大语言模型 (LLMs) 提供了从数据源检索信息的能力,并以此为基础生成回答。
- 步骤1:问题理解,准确把握用户的意图。
- 步骤2:
知识检索
,从知识库中相关的知识检索。【难点,用户提问可能以多种方式表达,而知识库的信息来源可能是多样的,包括PDF、PPT、Neo4j等格式。
】 - 步骤3:答案生成,将检索结果与问题。
- 优点:
- 提高准确性和相关性。
- 改善时效性,使模型适应当前事件和知识。
- 降低生成错误风险,依赖检索系统提供的准确信息。
以下是RAG输出到大型语言模型的典型模板:
你是一个{task}方面的专家,请结合给定的资料,并回答最终的问题。请如实回答,如果问题在资料中找不到答案,请回答不知道。
问题:{question}
资料:
- {information1}
- {information2}
- {information3}
其中,{task}代表任务的领域或主题,{question}是最终要回答的问题,而{information1}、{information2}等则是提供给模型的外部知识库中的具体信息。
分类
参考论文:Retrieval-Augmented Generation for Large Language Models: A Survey
RAG可以根据技术复杂度,分为三种:
- Naive RAG:Naive RAG是RAG技术的最基本形式,也被称为经典RAG。包括
索引、检索、生成三个基本步骤
。索引阶段将文档库分割成短的Chunk,并构建向量索引。检索阶段根据问题和Chunks的相似度检索相关文档片段。生成阶段以检索到的上下文为条件,生成问题的回答。 - Advanced RAG:Advanced RAG在Naive RAG的基础上进行优化和增强。
包含额外处理步骤,分别在数据索引、检索前和检索后进行
。包括更精细的数据清洗、设计文档结构和添加元数据,以提升文本一致性、准确性和检索效率。在检索前使用问题的重写、路由和扩充等方式对齐问题和文档块之间的语义差异
。在检索后通过重排序避免“Lost in the Middle”现象,或通过上下文筛选与压缩缩短窗口长度
。 - Modular RAG:Modular RAG
引入更多具体功能模块
,例如查询搜索引擎、融合多个回答等。技术上融合了检索与微调、强化学习等。流程上对RAG模块进行设计和编排,出现多种不同RAG模式。提供更大灵活性,系统可以根据应用需求选择合适的功能模块组合。模块化RAG
的引入使得系统更自由、灵活,适应不同场景和需求。
RAG和SFT对比:
特性 | RAG技术 | SFT模型微调 |
---|---|---|
知识更新 | 实时更新检索库,适合动态数据,无需频繁重训 | 存储静态信息,更新知识需要重新训练 |
外部知识 | 高效利用外部资源,适合各类数据库 | 可对齐外部知识,但对动态数据源不够灵活 |
数据处理 | 数据处理需求低 | 需构建高质量数据集,数据限制可能影响性能 |
模型定制化 | 专注于信息检索和整合,定制化程度低 | 可定制行为,风格及领域知识 |
可解释性 | 答案可追溯,解释性高 | 解释性相对低 |
计算资源 | 需要支持检索的计算资源,维护外部数据源 | 需要训练数据集和微调资源 |
延迟要求 | 数据检索可能增加延迟 | 微调后的模型反应更快 |
减少幻觉 | 基于实际数据,幻觉减少 | 通过特定域训练可减少幻觉,但仍然有限 |
道德和隐私 | 处理外部文本数据时需要考虑隐私和道德问题 | 训练数据的敏感内容可能引发隐私问题 |
实战 - 搭建 RAG Demo
预备
1.开通免费试用阿里云PAI—DSW
链接:https://free.aliyun.com/?searchKey=PAI
开通PAI-DSW 试用 ,可获得 5000算力时!有效期3个月!
2.在魔搭社区进行授权
链接:https://www.modelscope.cn/my/mynotebook/authorization
创建实例:
这里一定要选择支持资源包抵扣的服务器
创建实例后打开,能看到这个界面就成功啦!
索引
新建bge-small-zh-v1.5-download.py
,用于向量模型下载
:
# 向量模型下载
from modelscope import snapshot_download
model_dir = snapshot_download("AI-ModelScope/bge-small-zh-v1.5", cache_dir='.')
/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
Downloading: 100%|██████████| 190/190 [00:00<00:00, 328B/s]
Downloading: 100%|██████████| 776/776 [00:00<00:00, 1.48kB/s]
Downloading: 100%|██████████| 124/124 [00:00<00:00, 266B/s]
Downloading: 100%|██████████| 47.0/47.0 [00:00<00:00, 92.2B/s]
Downloading: 100%|██████████| 91.4M/91.4M [00:00<00:00, 120MB/s]
Downloading: 100%|██████████| 349/349 [00:00<00:00, 715B/s]
Downloading: 100%|██████████| 91.4M/91.4M [00:00<00:00, 123MB/s]
Downloading: 100%|██████████| 27.5k/27.5k [00:00<00:00, 54.9kB/s]
Downloading: 100%|██████████| 52.0/52.0 [00:00<00:00, 65.0B/s]
Downloading: 100%|██████████| 125/125 [00:00<00:00, 253B/s]
Downloading: 100%|██████████| 429k/429k [00:00<00:00, 703kB/s]
Downloading: 100%|██████████| 367/367 [00:00<00:00, 759B/s]
Downloading: 100%|██████████| 107k/107k [00:00<00:00, 220kB/s]
封装向量模型类 EmbeddingModel:
from typing import List
from transformers import AutoTokenizer, AutoModel
import torch
# 定义向量模型类
class EmbeddingModel:
"""
用于加载预训练的模型并计算文本的嵌入向量的类。
"""
def __init__(self, path: str) -> None:
"""
初始化方法,加载预训练的分词器和模型。
:param path: 预训练模型的路径。
"""
# 加载预训练的分词器
self.tokenizer = AutoTokenizer.from_pretrained(path)
# 加载预训练的模型,并将其移动到GPU上
self.model = AutoModel.from_pretrained(path).cuda()
print(f'Loading EmbeddingModel from {path}.')
# 为了充分发挥GPU矩阵计算的优势,输入和输出都是一个 List,即多条文本和他们的向量表示。
def get_embeddings(self, texts: List[str]) -> List[List[float]]:
"""
计算文本列表的嵌入向量。
:param texts: 要计算嵌入向量的文本列表。
:return: 嵌入向量的列表。
"""
# 使用分词器对文本进行编码
encoded_input = self.tokenizer(texts, padding=True, truncation=True, return_tensors='pt')
# 将编码后的输入数据移动到GPU上
encoded_input = {k: v.cuda() for k, v in encoded_input.items()}
# 不计算梯度,以提高计算效率
with torch.no_grad():
# 将编码后的输入传递给模型,获取模型输出
model_output = self.model(**encoded_input)
# 获取句子的嵌入向量,这里假设模型输出的第一个元素包含嵌入信息
sentence_embeddings = model_output[0][:, 0]
# 归一化嵌入向量
sentence_embeddings = torch.nn.functional.normalize(sentence_embeddings, p=2, dim=1)
# 将嵌入向量转换为列表格式并返回
return sentence_embeddings.tolist()
测试:
print("> Create embedding model...")
embed_model_path = './AI-ModelScope/bge-small-zh-v1___5'
embed_model = EmbeddingModel(embed_model_path)
"""
> Create embedding model...
Loading EmbeddingModel from ./AI-ModelScope/bge-small-zh-v1___5.
"""
检索
编写一个知识库文档knowledge.txt
:
广州大学(Guangzhou University),简称广大(GU),是由广东省广州市人民政府举办的全日制普通高等学校,实行省市共建、以市为主的办学体制,是国家“111计划”建设高校、广东省和广州市高水平大学重点建设高校。广州大学的办学历史可以追溯到1927年创办的私立广州大学;1951年并入华南联合大学;1983年筹备复办,1984年定名为广州大学;2000年7月,经教育部批准,与广州教育学院(1953年创办)、广州师范学院(1958年创办)、华南建设学院西院(1984年创办)、广州高等师范专科学校(1985年创办)合并组建成立新的广州大学。
郑州机械研究所有限公司(以下简称郑机所)的前身机械科学研究院1956年始建于北京,是原机械工业部直属一类综合研究院所,现隶属于国资委中国机械科学研究总院集团有限公司。郑机所伴随着共和国的成长一路走来,应运而生于首都,碧玉年华献中原。多次搬迁,驻地从北京经漯河再到郑州;数易其名,由机械科学研究院到漯河机械研究所再到郑州机械研究所,现为郑州机械研究所有限公司。1956~1958年应运而生:依据全国人大一届二次会议的提议和第一机械工业部的决策,1956年3月6日,第一机械工业部发文《(56)机技研究第66号》,通知“机械科学实验研究院”(后改名为“机械科学研究院”)在北京成立。1959~1968年首次创业:承担国家重大科研项目与开发任务,以及行业发展规划以及标准制定等工作,如“九大设备”的若干关键技术等。1969~1972年搬迁河南:1969年按照“战备疏散”的要求,机械科学研究院主体迁建河南漯河,成立“漯河机械研究所”;1972年因发展需要,改迁河南郑州,成立郑州机械研究所。1973~1998年二次创业:先后隶属于国家机械工业委员会、机械电子工业部、机械工业部;1981年4月罗干由铸造室主任升任副所长,同年经国务院批准具备硕士学位授予权;1985年“葛洲坝二、三江工程及其水电机组项目”荣获国家科技进步特等奖。1999~2016年发展壮大:1999年转企改制,隶属于国资委中国机械科学研究总院;2008年被河南省首批认定为“高新技术企业”;2011年获批组建新型钎焊材料与技术国家重点实验室;2014年被工信部认定为“国家技术创新示范企业”;历经十多年开发出填补国内外空白的大型齿轮齿条试验装备,完成了对三峡升船机齿条42.2万次应力循环次数的疲劳寿命试验测试;营业收入从几千万发展到近6亿;2017年至今协同发展:2017年经公司制改制,更名为郑州机械研究所有限公司,一以贯之地坚持党对国有企业的领导,充分发挥党委把方向、管大局、保落实的领导作用;一以贯之地建立现代企业制度,持续推进改革改制,努力实现以高质量党建引领郑机所高质量发展。
非洲野犬,属于食肉目犬科非洲野犬属哺乳动物。 又称四趾猎狗或非洲猎犬; 其腿长身短、体形细长;身上有鲜艳的黑棕色、黄色和白色斑块;吻通常黑色,头部中间有一黑带,颈背有一块浅黄色斑;尾基呈浅黄色,中段呈黑色,末端为白色,因此又有“杂色狼”之称。 非洲野犬分布于非洲东部、中部、南部和西南部一带。 栖息于开阔的热带疏林草原或稠密的森林附近,有时也到高山地区活动。其结群生活,没有固定的地盘,一般在一个较大的范围内逗留时间较长。非洲野犬性情凶猛,以各种羚羊、斑马、啮齿类等为食。奔跑速度仅次于猎; 雌犬妊娠期为69-73天,一窝十只仔,哺乳期持续6-12个星期。 其寿命11年。 非洲野犬正处在灭绝边缘,自然界中仅存两三千只。 非洲野犬被列入《世界自然保护联盟濒危物种红色名录》中,为濒危(EN)保护等级。 ",非洲野犬共有42颗牙齿(具体分布为:i=3/3;c=1/1;p=4/4;m=2/3x2),前臼齿比相对比其他犬科动物要大,因此可以磨碎大量的骨头,这一点很像鬣狗。 主要生活在非洲的干燥草原和半荒漠地带,活跃于草原、稀树草原和开阔的干燥灌木丛,甚至包括撒哈拉沙漠南部一些多山的地带。非洲野犬从来不到密林中活动。
定义一个向量库索引类 VectorStoreIndex
# 定义向量库索引类
class VectorStoreIndex:
"""
用于创建向量库索引,计算文本之间的相似度并进行查询的类。
"""
def __init__(self, document_path: str, embed_model: EmbeddingModel) -> None:
"""
初始化方法,从文件中加载文档并计算它们的嵌入向量。
:param document_path: 包含文档的文件路径。
:param embed_model: 用于生成文档嵌入向量的模型实例。
"""
self.documents = [] # 存储文档文本的列表
# 从文件中读取文档并添加到文档列表中
for line in open(document_path, 'r', encoding='utf-8'):
line = line.strip()
self.documents.append(line)
# 存储嵌入模型的引用
self.embed_model = embed_model
# 为所有文档计算嵌入向量
self.vectors = self.embed_model.get_embeddings(self.documents)
# 打印加载文档的数量和文件路径
print(f'Loading {len(self.documents)} documents for {document_path}.')
def get_similarity(self, vector1: List[float], vector2: List[float]) -> float:
"""
计算两个向量之间的余弦相似度。
:param vector1: 第一个向量。
:param vector2: 第二个向量。
:return: 两个向量之间的余弦相似度。
"""
# 计算两个向量的点积
dot_product = np.dot(vector1, vector2)
# 计算两个向量的模
magnitude = np.linalg.norm(vector1) * np.linalg.norm(vector2)
# 如果模为0,返回0以避免除以0的错误
if not magnitude:
return 0
# 返回归一化的点积作为余弦相似度
return dot_product / magnitude
def query(self, question: str, k: int = 1) -> List[str]:
"""
根据问题查询最相似的文档。
:param question: 查询的问题文本。
:param k: 返回最相似文档的数量,默认为1。
:return: 最相似的文档列表。
"""
# 为问题文本计算嵌入向量
question_vector = self.embed_model.get_embeddings([question])[0]
# 计算问题向量与所有文档向量的相似度
result = np.array([self.get_similarity(question_vector, vector) for vector in self.vectors])
# 获取最相似的k个文档的索引
return np.array(self.documents)[result.argsort()[-k:][::-1]].tolist()
测试:
print("> Create index...")
doecment_path = './knowledge.txt'
index = VectorStoreIndex(doecment_path, embed_model)
question = '介绍一下广州大学'
print('> Question:', question)
context = index.query(question)
print('> Context:', context)
如果知识库很大,需要将知识库切分成多个batch,然后分批次送入向量模型。这里,因为我们的知识库比较小,所以就直接传到了get_embeddings() 函数。
返回结果:
我们传入用户问题 介绍一下广州大学,可以看到,准确地返回了知识库中的第一条知识。
生成
编写Yuan2-2B-Mars-hf-download.py
,下载大模型Yuan2-2B-Mars-hf:
# 源大模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('IEITYuan/Yuan2-2B-Mars-hf', cache_dir='.')
定义一个大语言模型类 LLM:
# 导入必要的库
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
# 定义大语言模型类
class LLM:
"""
用于加载和使用Yuan2.0大型语言模型的类。
"""
def __init__(self, model_path: str) -> None:
"""
初始化方法,加载预训练的分词器和模型。
:param model_path: 预训练模型的路径。
"""
print("Creat tokenizer...") # 打印创建分词器的信息
# 加载预训练的分词器,设置不自动添加结束和开始标记
self.tokenizer = AutoTokenizer.from_pretrained(model_path, add_eos_token=False, add_bos_token=False, eos_token='<eod>')
# 向分词器添加特殊标记
self.tokenizer.add_tokens([
'<sep>', '<pad>', '<mask>', '<predict>', '<FIM_SUFFIX>', '<FIM_PREFIX>', '<FIM_MIDDLE>',
'<commit_before>', '<commit_msg>', '<commit_after>', '<jupyter_start>', '<jupyter_text>',
'<jupyter_code>', '<jupyter_output>', '<empty_output>'
], special_tokens=True)
print("Creat model...") # 打印创建模型的信息
# 加载预训练的因果语言模型,并将其设置为半精度浮点数以提高计算效率
self.model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=torch.bfloat16, trust_remote_code=True).cuda()
# 打印加载模型的信息
print(f'Loading Yuan2.0 model from {model_path}.')
def generate(self, question: str, context: List[str]):
"""
根据问题和上下文生成回答。
:param question: 用户提问,是一个str。
:param context: 检索到的上下文信息,是一个List,默认是[],代表没有使用RAG。
"""
# 如果提供了上下文,构建提示信息
if context:
prompt = f'背景:{" ".join(context)}\n问题:{question}\n请基于背景,回答问题。'
else:
prompt = question
# 在提示信息后添加分隔符
prompt += "<sep>"
# 使用分词器将提示信息转换为模型输入的格式
inputs = self.tokenizer(prompt, return_tensors="pt")["input_ids"].cuda()
# 使用模型生成文本
outputs = self.model.generate(inputs, do_sample=False, max_length=1024)
# 解码生成的文本
output = self.tokenizer.decode(outputs[0])
# 打印生成的文本,只显示分隔符之后的部分
print(output.split("<sep>")[-1])
测试:
print("> Create Yuan2.0 LLM...")
model_path = './IEITYuan/Yuan2-2B-Mars-hf'
llm = LLM(model_path)
print('> Without RAG:')
llm.generate(question, [])
print('> With RAG:')
llm.generate(question, context)
打包
编写requirements.txt
:
transformers
torch
numpy
编写安装脚本:pip_install.sh
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
新建main.py
from common import constants
from generation.llm import LLM
from indexing.embedding import EmbeddingModel
from retrieval.vector import VectorStoreIndex
def main():
print("> Create embedding model...")
embed_model_path = constants.EMBED_MODEL_PATH
embed_model = EmbeddingModel(embed_model_path)
print("> Create index...")
document_path = constants.DOCUMENT_PATH
index = VectorStoreIndex(document_path, embed_model)
question = '介绍一下广州大学'
print('> Question:', question)
context = index.query(question)
print('> Context:', context)
print("> Create Yuan2.0 LLM...")
model_path = constants.MODEL_PATH
llm = LLM(model_path)
print('> Without RAG:')
llm.generate(question, [])
print('> With RAG:')
llm.generate(question, context)
if __name__ == '__main__':
main()
部署
将代码部署到Github:https://github.com/itxaiohanglover/rag_demo
然后进入终端,导入写好的代码:
下载模型:
python setup.py
启动代码:
python main.py
实战 - AI科研助手
项目主要包含一个Streamlit开发的客户端,以及一个部署好的浪潮源大模型的服务端。
- 客户端接收到用户上传的PDF后,发送到服务端。服务端首先完成PDF内容解析,然后拼接摘要Prompt并输入源大模型,得到模型输出结果后,返回给客户端并展示给用户。
- 如果用户接下来进行提问,客户端将用户请求发送到服务端,服务端进行Embedding和Faiss检索,然后将检索到的chunks与用户请求拼接成Prompt并输入到源大模型,得到模型输出结果后,返回给客户端进行结构化,然后展示给用户。
项目结构
主模块:main.py
# 导入必要的库和模块
from langchain_community.document_loaders import PyPDFLoader # 用于加载PDF文件的加载器
from common import constants # 导入常量配置
import streamlit as st # 导入Streamlit库,用于构建Web界面
from llm.yuan2_llm import Yuan2_LLM # 导入自定义的大型语言模型类
from langchain_huggingface import HuggingFaceEmbeddings # 导入HuggingFace嵌入模型
# 导入提示模板类
from prompts.chatbot_template import ChatBot
from prompts.summarizer_template import Summarizer
# 定义模型路径
model_path = constants.MODEL_PATH # 从常量配置中获取模型路径
# 定义向量模型路径
embedding_model_path = constants.EMBED_MODEL_PATH # 从常量配置中获取向量模型路径
# 定义一个函数,用于获取llm和embeddings
@st.cache_resource # 使用Streamlit的缓存装饰器来缓存函数的结果
def get_models():
llm = Yuan2_LLM(model_path) # 创建Yuan2_LLM实例
# 定义模型和编码的参数
model_kwargs = {'device': 'cuda'}
encode_kwargs = {'normalize_embeddings': True} # 设置为True以计算余弦相似度
embeddings = HuggingFaceEmbeddings(
model_name=embedding_model_path, # 向量模型的名称或路径
model_kwargs=model_kwargs, # 模型参数
encode_kwargs=encode_kwargs, # 编码参数
)
return llm, embeddings # 返回创建的LLM和嵌入模型实例
def main():
# 创建一个标题
st.title('💬 Yuan2.0 AI科研助手') # 设置Streamlit应用的标题
# 获取llm和embeddings
llm, embeddings = get_models() # 调用get_models函数获取模型实例
# 初始化summarizer
summarizer = Summarizer(llm) # 创建Summarizer实例用于生成文本摘要
# 初始化ChatBot
chatbot = ChatBot(llm, embeddings) # 创建ChatBot实例用于回答问题
# 上传pdf
uploaded_file = st.file_uploader("Upload your PDF", type='pdf') # 创建文件上传器,允许用户上传PDF文件
if uploaded_file:
# 加载上传PDF的内容
file_content = uploaded_file.read() # 读取上传的文件内容
# 写入临时文件
temp_file_path = "temp.pdf" # 定义临时文件路径
with open(temp_file_path, "wb") as temp_file:
temp_file.write(file_content) # 将文件内容写入临时文件
# 加载临时文件中的内容
loader = PyPDFLoader(temp_file_path) # 创建PDF加载器实例
docs = loader.load() # 使用加载器加载文档内容
st.chat_message("assistant").write(f"正在生成论文概括,请稍候...") # 在Streamlit界面上显示消息
# 生成概括
summary = summarizer.summarize(docs) # 调用summarizer的summarize方法生成摘要
# 在聊天界面上显示模型的输出
st.chat_message("assistant").write(summary) # 显示生成的摘要
# 接收用户问题
if query := st.text_input("Ask questions about your PDF file"): # 创建文本输入框,允许用户输入问题
# 检索 + 生成回复
chunks, response = chatbot.run(docs, query) # 调用chatbot的run方法进行检索和生成回答
# 在聊天界面上显示模型的输出
st.chat_message("assistant").write(f"正在检索相关信息,请稍候...") # 显示检索信息的消息
st.chat_message("assistant").write(chunks) # 显示检索到的文档片段
st.chat_message("assistant").write(f"正在生成回复,请稍候...") # 显示生成回答的消息
st.chat_message("assistant").write(response) # 显示生成的回答
if __name__ == '__main__':
main() # 如果是主程序,则调用main函数运行应用
提示词模块:
chatbot_template.py
from langchain_core.prompts import PromptTemplate
from langchain_text_splitters import RecursiveCharacterTextSplitter # 导入文本分割器
from langchain.chains.question_answering import load_qa_chain # 导入load_qa_chain,用于加载问答链
from langchain_community.vectorstores import FAISS
# 定义聊天机器人模板
chatbot_template = '''
假设你是一个AI科研助手,请基于背景,简要回答问题。
背景:
{context}
问题:
{question}
'''.strip()
# 定义ChatBot类
class ChatBot:
"""
ChatBot类用于处理用户提问,并基于文档内容生成回答。
"""
def __init__(self, llm, embeddings):
self.prompt = PromptTemplate(
input_variables=["text"],
template=chatbot_template
) # 定义聊天机器人提示模板
self.chain = load_qa_chain(llm=llm, chain_type="stuff", prompt=self.prompt) # 加载问答链
self.embeddings = embeddings # 嵌入模型,用于文档向量化
# 加载文本分割器,用于将长文本切分成小块
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=450,
chunk_overlap=10,
length_function=len
)
def run(self, docs, query):
"""
处理用户提问,生成回答。
:param docs: 文档列表,每个文档包含page_content属性
:param query: 用户的提问
:return: 检索到的文档片段和生成的回答
"""
# 读取所有文档内容
text = ''.join([doc.page_content for doc in docs])
# 使用文本分割器切分成chunks
all_chunks = self.text_splitter.split_text(text=text)
# 将文本chunks转换为向量并存储
VectorStore = FAISS.from_texts(all_chunks, embedding=self.embeddings)
# 检索与提问最相似的chunks
chunks = VectorStore.similarity_search(query=query, k=1)
# 使用问答链生成回答
response = self.chain.run(input_documents=chunks, question=query)
return chunks, response # 返回检索到的文档片段和生成的回答
summarizer_template.py
# 导入必要的库和模块
from langchain.chains.llm import LLMChain # 导入LLMChain,用于构建基于LLM的生成链
from langchain_core.prompts import PromptTemplate # 导入PromptTemplate,用于构建提示模板
# 定义摘要模板
summarizer_template = """
假设你是一个AI科研助手,请用一段话概括下面文章的主要内容,200字左右。
{text}
"""
# 定义Summarizer类
class Summarizer:
"""
Summarizer类用于将长文本内容压缩成简短的摘要。
"""
def __init__(self, llm):
self.llm = llm # LLM实例,用于生成文本
self.prompt = PromptTemplate(
input_variables=["text"],
template=summarizer_template
) # 定义摘要提示模板
self.chain = LLMChain(llm=self.llm, prompt=self.prompt) # 创建LLMChain实例,用于生成摘要
def summarize(self, docs):
"""
从文档中生成摘要。
:param docs: 文档列表,其中每个文档包含page_content属性
:return: 生成的摘要文本
"""
# 从第一页中获取摘要内容,假设摘要位于'ABSTRACT'和'KEY WORDS'之间
content = docs[0].page_content.split('ABSTRACT')[1].split('KEY WORDS')[0]
summary = self.chain.run(content) # 使用LLMChain生成摘要
return summary # 返回生成的摘要
源大模型模块:
yuan2_llm.py
# 导入必要的库
from typing import List, Optional, Any
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
# 导入常量配置
from common import constants
# 导入LLM基类
from langchain.llms.base import LLM
from langchain.callbacks.manager import CallbackManagerForLLMRun
# 定义模型路径
model_path = constants.MODEL_PATH
# 定义模型数据类型
torch_dtype = torch.bfloat16
# 定义源大模型类
class Yuan2_LLM(LLM):
"""
YUAN2_LLM类用于加载和使用预训练的大型语言模型。
它继承自langchain的LLM基类,并实现了自己的_call方法来生成文本。
"""
# 类变量,用于存储分词器和模型实例
tokenizer: AutoTokenizer = None
model: AutoModelForCausalLM = None
def __init__(self, model_path: str):
super().__init__()
# 加载预训练的分词器和模型
print("Creating tokenizer...")
self.tokenizer = AutoTokenizer.from_pretrained(model_path, add_eos_token=False, add_bos_token=False,
eos_token='<eod>')
# 添加特殊标记
self.tokenizer.add_tokens(
['<sep>', '<pad>', '<mask>', '<predict>', '<FIM_SUFFIX>', '<FIM_PREFIX>', '<FIM_MIDDLE>', '<commit_before>',
'<commit_msg>', '<commit_after>', '<jupyter_start>', '<jupyter_text>', '<jupyter_code>',
'<jupyter_output>', '<empty_output>'], special_tokens=True)
print("Creating model...")
self.model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=torch_dtype,
trust_remote_code=True).cuda()
def _call(
self,
prompt: str,
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> str:
"""
生成文本的方法,根据输入的prompt生成响应。
:param prompt: 输入的文本提示
:param stop: 停止生成的标记列表
:param run_manager: 运行管理器,用于监控和管理生成过程
:param kwargs: 其他可选参数
:return: 生成的文本响应
"""
prompt = prompt.strip()
prompt += "<sep>"
inputs = self.tokenizer(prompt, return_tensors="pt")["input_ids"].cuda()
outputs = self.model.generate(inputs, do_sample=False, max_length=4096)
output = self.tokenizer.decode(outputs[0])
response = output.split("<sep>")[-1].split("<eod>")[0]
return response
@property
def _llm_type(self) -> str:
"""
返回模型的类型标识。
:return: 模型类型字符串
"""
return "Yuan2_LLM"
运行效果
bash run.sh
启动脚本:run.sh
streamlit run main.py --server.address 127.0.0.1 --server.port 6006
论文概括:
What kind of attention architecture is LFA?
📌 [ 笔者 ] 文艺倾年
📃 [ 更新 ] 2024.9.15
❌ [ 勘误 ] /* 暂无 */
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
本人也很想知道这些错误,恳望读者批评指正!