本次部署为简单概述流程,本地配置为4060Ti-16G,采用7b模型部署。
下载模型
注:模型实际上可以直接在线使用,此处只为便于离线使用。
模型通常开源于 huggingface,一个大模型开源网站,开源了大量模型,本次模型地址:Qwen-math-7b。模型地址通常类似于github即是一个在线的git仓库,也就是能使用git进行克隆。
git lfs
对于大模型而言,拥有较大的模型文件,git对于版本的管理需要存储文件的存储拷贝,对于多个历史版本,较大的文件在本地有多份拷贝会使git管理繁琐,并会使文件体积大幅度膨胀。git lfs作为git的拓展提供了一个解决方法,其将大文件的历史存储在远程服务器上,git仓库仅存储大文件的指针,在需要对应历史的大文件的时候,就会自动从远程服务器中获取。
对于本次的模型克隆,即
git lfs clone https://huggingface.co/Qwen/Qwen2.5-Math-7B
等待下载完即可
huggingface_hub
这是 Hugging Face 提供的一个 Python 库,用于与 Hugging Face Hub 进行交互。Hugging Face Hub 是一个用于存储和共享模型、数据集、预训练权重等资源的平台,广泛应用于自然语言处理(NLP)和其他深度学习任务。重要的是,它可以轻松地下载 Hugging Face 上的模型、数据集和文件。例如,你可以从 Hub 下载预训练的 Transformer 模型,或者自定义的模型和数据集。
hf_hub_download
这是其提供的一个函数,用于下载单个文件
主要有以下的参数(*必须参数):
- repo_id(*):仓库名
- filename(*):需要下载的单个文件名
- local_dir:下载保存的目录
- endpoint:可以填入镜像站,默认是https://huggingface.co/,例如我们使用其它镜像,只需要放入镜像的网站即可
snapshot_download
用于下载整个仓库的快照
主要有以下的参数(*必须参数):
- repo_id(*):仓库名
- local_dir:下载保存的目录
- endpoint:可以填入镜像站。
huggingface-cli
可以使用快速下载的脚手架,例如以下:
huggingface-cli download gpt2 config.json
镜像站
对于国内的网络环境,为节省流量以及提高下载效率,故使用huggingface的镜像站对于国内用户更加合适:
镜像站进行前面的替换即可
部署模型
部署模型采用Transformers调用,Hugging Face 开发的 Transformers 库是一个由 Hugging Face 提供的开源库,旨在简化和统一深度学习模型,特别是基于 Transformer 架构的模型(如 BERT、GPT、T5 等)的使用。它为各种自然语言处理(NLP)任务提供了预训练模型和方便的 API,使得开发者能够轻松地进行模型的训练、微调和推理。
载入模型
from transformers import AutoModelForCausalLM, AutoTokenizer
def load_model(model_path):
device = "cuda:0"
model = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype="auto",
device_map={"": 0},
use_safetensors=True)
tokenizer = AutoTokenizer.from_pretrained(model_path)
return device, tokenizer, model
Tokenizer
分词器(Tokenizer)是自然语言处理(NLP)中的一个重要组件,它的主要作用是将原始文本(例如句子或段落)转换为模型可以理解的形式。对于深度学习模型,尤其是基于 Transformer 架构的模型(如 BERT、GPT 等),分词器将文本切分为更小的单元(通常是单词或子词),并将这些单元转换为对应的数字 ID,作为模型的输入。
分词器通常和模型是配对的,因此下载来的模型文件夹包含了模型文件和分词器配置,可以载入
载入模型参数介绍
注:AutoTokenizer的载入并不需要大量显存也不属于模型,故载入的时候不需要后两个参数。
仅介绍一部分参数
- model_name_or_path:指定预训练模型的名称或路径。可以是 Hugging Face Model Hub 上的模型标识符,也可以是本地文件系统上的路径。
- cache_dir:缓存目录
- force_download:即使在缓存中已有模型的情况下,也强制重新下载模型
- resume_download:如果下载被中断,则从中断的地方继续下载。
- proxies:代理
- torch_dtype:设置加载模型时使用的 PyTorch dtype。这对于模型精度和性能调优特别有用
- device_map:控制模型的计算图如何分配到不同的设备(如GPU、CPU)上,
- "sequential”模型按顺序加载到多个GPU上
- "auto”会自动优化分配
- device_map={“”: “cuda:0”}:手动分配到GPU0
使用模型输入输出(Quick Start)
def chat_qwen(device, tokenizer, model, messages: list):
# 使用分词器的 apply_chat_template 方法将消息格式化为模型可理解的输入格式
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
model_inputs = tokenizer(text, return_tensors="pt").to(device)
# 生成模型输出
generated_ids = model.generate(
model_inputs.input_ids,
max_new_tokens=512
)
# 由于模型输出包括输入模型,这里切去输入部分
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
对于模型的使用而言,需要知道最基础的一部分知识。
对于模型,其接受的文本首先要经过分词器处理,才能得出结果,并且其结果也要经过分词器解码,类似于和外星人聊天之间的翻译官,对于本模型而言,即给他一段对话环境作为输入,他将输出一句新的话作为补充回复。
设置输入消息
大模型中通常有三个角色:system,user,assistant。在openai给出的GPT文档中,还有developer类的角色。
- system类消息:开发人员提供的模型应该遵循的指令,而不管用户发送的消息是什么。
- user类消息:由最终用户发送的消息,包含提示或其他上下文信息。
- assistent类消息:模型为响应用户消息而发送的消息。
由这些消息组成以作为输入提供给大模型。
消息格式化编码化
apply_chat_template 函数就用于格式化上面设置的消息
[{'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user',
'content': '你是谁?'}]
通过格式化化变为
<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
你是谁?<|im_end|>
<|im_start|>assistant
再通过tokenizer来对消息进行编码
将处理后的文本通过分词器转换为模型需要的输入格式,return_tensors=“pt” 表示返回 PyTorch 张量格式,并将输入传送到前面指定的设备上
获取输出并解码
模型的输出实际包含输入的数据,因此将输入的数据切片后解码即可。
使用例
if __name__ == '__main__':
MODEL = "Qwen2.5-Math-7B"
pak = load_model(MODEL)
msgs = [{"role": "system",
"content": "你是一个数学方面的助手,需要帮助用户解决数学问题,要求答案正确,并且要提供思考步骤。"}]
while True:
ques = input()
msgs.append({"role": "user", "content": ques})
print("接受到问题开始思考")
res = chat_qwen(*pak, msgs)
print("思考完毕")
print("回答:", res)
msgs.append({"role": "assistant", "content": res})
print("当前问答链")
pprint(msgs)
这个例子可以多次和模型进行问答,并且还可以记录历史的问答,让模型具有记忆。
结合到Longchain
按照文档,把定制模型置入即可,以下供参考
class QwenMath(LLM):
# 模型参数
max_new_tokens: int = 1920
temperature: float = 0.9
top_p: float = 0.8
tokenizer: object = None
model: object = None
history: List = []
device: object = torch.device("cuda" if torch.cuda.is_available() else "cpu")
def __init__(self, max_new_tokens=1920,
temperature=0.9,
top_p=0.8):
super().__init__()
self.max_new_tokens = max_new_tokens
self.temperature = temperature
self.top_p = top_p
@property
def _llm_type(self) -> str:
return "Qwen2"
def load_model(self, model_name_or_path=None):
self.tokenizer = AutoTokenizer.from_pretrained(
model_name_or_path,
trust_remote_code=True
)
self.model = AutoModelForCausalLM.from_pretrained(
model_name_or_path,
torch_dtype="auto",
# torch_dtype="torch.float16",
device_map={"": 0}, # sequential/auto/balanced_low_0
)
def chat_stream(self, model, tokenizer, query: str, history: list):
with torch.no_grad():
# 历史整理
messages = [
{'role': 'system', 'content': '你是一个数学方面的助手,需要帮助用户解决数学问题,要求答案正确,并且要提供思考步骤。'},
]
# 将之前的history内容重新组合
for item in history:
if item['role'] == 'user':
if item.get('content'):
messages.append({'role': 'user', 'content': item['content']})
if item['role'] == 'assistant':
if item.get('content'):
messages.append({'role': 'assistant', 'content': item['content']})
# 最新的用户问题
messages.append({'role': 'user', 'content': query})
# 模型推理
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(self.device)
generated_ids = model.generate(
**model_inputs,
max_new_tokens=self.max_new_tokens,
do_sample=False,
top_p=self.top_p,
temperature=self.temperature
)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
# 模型根据messages的内容后的输出
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
# 将模型输出组合到messages中
messages.append({'role': 'assistant', 'content': response})
return response, messages
# Langchain调用
def _call(self, prompt: str, stop=["<|user|>"], run_manager=None, **kwargs):
# 主要调用chat_stream实现
response, self.history = self.chat_stream(self.model, self.tokenizer, prompt, self.history)
return response
def query_only(self, query):
# 当使用RAG技术时会出现用户输入存在大量的参考资料,导致模型难以理解整体上下文内容。
if self.history[-2]['role'] == 'user':
self.history[-2]['content'] = query
def get_history(self) -> List:
return self.history
def delete_history(self):
del self.history
self.history = []
其中增加了一些采样参数:
- temperature:控制生成文本的随机性。温度高时,生成的文本更加多样、创意,但也可能不太连贯;温度低时,文本更稳定,但可能缺乏创意。
- top_p:控制生成词汇的范围。模型会选择一些最有可能的词,直到这些词的概率总和达到
top_p
指定的阈值。top_p
值越小,生成的内容越保守,越大则生成的内容更有变化。
使用时应当先设定参数:do_sample=True,代表启用,否则不使用参数。
上面的例子并没有使用参数。
使用例:
if __name__ == '__main__':
MODEL = "Qwen2.5-Math-7B"
llm = QwenMath()
llm.load_model(MODEL)
query_ = "3加上5等于几"
res = llm.invoke(query_)
print(res)