1. 课程笔记
RAG和Fine-tuning其实都是大模型应用开发的重点,那两者的话也优缺点很明显。
RAG的话其实就是比较成本低,因为不用花费额外的算力资源去弄模型,外挂数据就可以了。并且知识库的内容可以随时随地的增加,不会说更新一次就要跑一次模型。但是问题就是假如基座模型比较弱的话,其效果可能就会大打折扣了。并且由于模型传入的上下文有限制,并且输入太长成本也高,因此单次回答的知识就很有限了。
那对于Fine-tuning而言的话,其实重点就是可以个性化微调。外挂知识库的方法并不改变模型,而微调的话是真的能够彻底改变模型的参数。并且微调的数据集可以做得很大很多范围,模型微调完就能够学到(当然可能会出现知识坍塌的现象,就是说微调把前面的知识给忘了)。但是缺点就是成本很高,全量微调大模型可能一个A100都打不住,并且每次更新知识都需要重新训练,不像外挂知识库这么方便。
当然在实际应用我们可以结合起来一起做,微调一部分知识再对专业知识或者实时知识来外挂,这样其实就能兼顾两者的优劣势。
那对于 外搭数据库的话,其实一般我们都会考虑通过langchain来实现。课程里这个图其实就是非常清晰了。我们其实就是本地的文件传入到一个langchain指定的dataloader里面从而获取文本,然后我们把文本先通过格式切割分成一块块,然后把这一块块的文本进行向量化(embedding)然后传入了VectorDB里(这里选择的是Chroma,当然我们也可以考虑使用其他像是FAISS之类的)。这个就是文档的处理了。那用户提取的问题同样也会经过向量化,然后获取到一个向量,通过近似度搜寻的方式找到向量数据库中最相近的几条数据。然后传给设计好的提示词中完成提示词的构建。最后将整个提示词传给InternLM获取最后的回复。这个就是完整的链条。
然后课程中还提到了对RAG方案的优化建议。RAG的核心受限于检索精度和prompt性能。所以在检索方面提升的点就是精确化的切分chunk,保证整体语义是完整的。或者是给chunk生成概括性的索引,在检索时进行匹配(这个如何具体实现还需要进一步查找,之前没有接触过)。那prompt方面其实自然就是自行优化迭代prompt找到最优的prompt了。
2. 学习到的新知识
虽然我之前也玩过langchain,也部署过一些基于OpenAI的实例项目,但是这次的课程同样会给我带来一些新的突破性知识。
- 比如说下载NLTK的资源
cd /root
git clone https://gitee.com/yzy0612/nltk_data.git --branch gh-pages
cd nltk_data
mv packages/* ./
cd tokenizers
unzip punkt.zip
cd ../taggers
unzip averaged_perceptron_tagger.zip
- 调用开源的词向量模型而非OpenAI的embedding模型
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="/root/data/model/sentence-transformer")
- 如何自定义LLM的类并接入到Langchain当中(这个特别牛!)
from langchain.llms.base import LLM
from typing import Any, List, Optional
from langchain.callbacks.manager import CallbackManagerForLLMRun
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
class InternLM_LLM(LLM):
# 基于本地 InternLM 自定义 LLM 类
tokenizer : AutoTokenizer = None
model: AutoModelForCausalLM = None
def __init__(self, model_path :str):
# model_path: InternLM 模型路径
# 从本地初始化模型
super().__init__()
print("正在从本地加载模型...")
self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
self.model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True).to(torch.bfloat16).cuda()
self.model = self.model.eval()
print("完成本地模型的加载")
def _call(self, prompt : str, stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any):
# 重写调用函数
system_prompt = """You are an AI assistant whose name is InternLM (书生·浦语).
- InternLM (书生·浦语) is a conversational language model that is developed by Shanghai AI Laboratory (上海人工智能实验室). It is designed to be helpful, honest, and harmless.
- InternLM (书生·浦语) can understand and communicate fluently in the language chosen by the user such as English and 中文.
"""
messages = [(system_prompt, '')]
response, history = self.model.chat(self.tokenizer, prompt , history=messages)
return response
@property
def _llm_type(self) -> str:
return "InternLM"
- 还有就是定义类来负责加载并存储检索问答链,并响应 Web 界面里调用检索问答链进行回答的动作(类其实是我python中比较弱的部分,我还是常用函数实现)。
class Model_center():
"""
存储检索问答链的对象
"""
def __init__(self):
# 构造函数,加载检索问答链
self.chain = load_chain()
def qa_chain_self_answer(self, question: str, chat_history: list = []):
"""
调用问答链进行回答
"""
if question == None or len(question) < 1:
return "", chat_history
try:
chat_history.append(
(question, self.chain({"query": question})["result"]))
# 将问答结果直接附加到问答历史中,Gradio 会将其展示出来
return "", chat_history
except Exception as e:
return e, chat_history
3. 作业展示
- langchain的gradio demo