基于chatGLM3,应用langchain模式外挂知识库进行问答训练。
主要思路:将外挂知识库文本制成文本向量库,与提问文本做相似度比对,选出高相似度信息进行回答。
一 环境平台配置
可参考网上教程,注册阿里云,免费获取3个月的GPU资源。
二 langchain本地部署
1. 知识库文本加载&文本切割
//文本加载库
from langchain.document_loaders import DirectoryLoader
//文本切割库
from langchain.text_splitter import CharacterTextSplitter
//directory中用文本路径替换
def load_documents(directory = "books"):
loader = DirectoryLoader(directory)
documents = loader.load()
# for d in documents:
# print(d)
text_spliter = CharacterTextSplitter(chunk_size = 256, chunk_overlap = 1) //切分的文本长度为256,交叠部分为1,根据GPU资源大小,可以增大该参数,使训练更加精准
split_docs = text_spliter.split_documents(documents)
# print(split_docs[:2])
return split_docs
2 文本词嵌入
国内需下载相关模型进行本地化调用,可使用模型清单包括:ernie-3.0-nano-zh、ernie-3.0-mediu-zh、text2vec-large-chinese、sbert-bert-chinese-nli、text2vec-base-chinese、
//词嵌入模型列表
# embedding
embedding_model_dict = {
"ernie-tiny": "nghuyong/ernie-3.0-nano-zh", //国内禁用,可修改成本地路径
"ernie-medium": "/mnt/workspace/ChatGLM3/openai_api_demo/ernie-3.0-medium-zh",
"text2vec": "/mnt/workspace/ChatGLM3/text2vec-large-chinese",
"text2vec2": "uer/sbert-base-chinese-nli",
"text2vec3": "/mnt/workspace/ChatGLM3/text2vec-base-chinese"
}
def load_embedding_mode(model_name = "ernie-tiny"):
encode_kwargs = {"normalize_embeddings":False}
model_kwargs = {"device":"cuda:0"}
return HuggingFaceEmbeddings(
model_name = embedding_model_dict[model_name]
model_kwargs = model_kwargs,
encode_kwargs = encode_kwargs,
cache_folder = "/mnt/workspace/ChatGLM3"
)
embeddings = load_embedding_mode('text2vec')
3 向量数据库构建
可选用chroma或FAISSS,此处以chroma为例。同样方式可使用faiss,且建议使用FAISS,chroma容易产生embedding dimensity错误。
from langchain.vectorstores import Chroma
def store_chroma(docs, embeddings,persist_directory = 'VectorStore'):
db = Chroma.from_documents(docs, embeddings, persist_directory = persist_directory)
db.persist()
return db
4. 模型加载
MODEL_PATH = os.environ.get('MODEL_PATH', '/mnt/workspace/ChatGLM3/chatglm3-6b')
model_config = AutoConfig.from_pretrained(
MODEL_PATH,
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)
model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True, device='cuda').half().quantize(4).cuda()
model = model.eval()
5 使用词向量相似度进行答案检索
(1)通过相似度比对,可找到相似的K个文本,再次作为大模型的输入进行答案检索。且可输入固定格式的prompt,以便提高模型识别率。
(2)通过history参数的设置,可稳定较为关键固定的答案,提升模型回答的准确率。比如:某些规则。
hist = [
{'role': 'user','content':"基于下面给出的资料,回答问题。如果资料不足,回答不了,就回复不知道,下面是资料。\n小王子来自哪里\n1.小王子来自B612星星"},
{'role': 'user','content':"基于下面给出的资料,回答问题。如果资料不足,回答不了,就回复不知道,下面是资料。\n狐狸告诉小王子的秘密是什么\n1.狐狸告诉小王子的秘密是:只有用心才能看得清,重要的东西,用眼睛是看不见的"},
{'role': 'user','content':"基于下面给出的资料,回答问题。如果资料不足,回答不了,就回复不知道,下面是资料。\n小王子是谁\n1.小王子是一个虚构的人物,出自法国作家安托万·德·圣埃克苏佩里的作品《小王子》"}
]
while True:
query = input('Human:')
similar_docs = db.similarity_search(query,include_metadata = True,k = 3)
prompt = f'基于下面给出的资料,回答问题。基于下面给出的资料,回答问题。如果资料不足,回答不了,就回复不知道,下面是资料。\n'
for idx, doc in enumerate(similar_docs):
prompt += f'{idx+1}. {doc.page_content}\n'
prompt += f'下面是问题:{query}'
# print(prompt)
response, history = model.chat(tokenizer, prompt, history=hist)
print(response)
print('\n')
print(f'history:{history}')
print('\n')
二 API方式调用
通过API方式,可支持多个模块并行调用,提高模型使用效能
1. API启动:demo中有个API接口,可直接进行启动。但此处建议将之前的词向量库替换成FAISS,否则embedding维度会报错。
2. 模型接口能力调用
from langchain.chains import RetrievalQA
from langchain.llms import ChatGLM
//API接口调用
llm = ChatGLM(
endpoint = 'http://127.0.0.1:8000/v1/chat/completions',
max_token = 80000,
top_p =0.9
)
//
retriever = db.as_retriever()
qa = RetrievalQA.from_chain_type(
llm = llm,
chain_type = 'stuff',
retriever = retriever
)
def chat(question,history):
response = qa.run(question)
return(response)
//使用gradio界面化
demo = gr.ChatInterface(chat)
demo.launch(inbrowser = True)