准备环节
前言的前言
一些闲谈:AI时代已经开始了,我也一直很想尝试一下部署这些模型,但自己的电脑实在不太行,看了腾讯云阿里云等等服务器又太贵(几千一个月),作为学生党只能望而却步了。但最近发现UCloud新出了一个 Compshare算力平台,价格非常实惠(跟淘宝闲鱼商家差不多了)。如果你和我一样预算有限,我十分推荐你尝试一下,因为有独立外网IP、预装好环境的镜像等等,对新手朋友来说体验只会比阿里云等更好。首推的4090等GPU平台正好适合用于这种小模型推理,价格每月也只要一千出头(也可以日付,才50r)(这个价格的最低配甚至是16核64G,200GB系统盘,共享100M带宽)。学生还有额外的八折优惠。(此外,像我这样发布相关文章还可以直接抵扣。)
前言
MistralAI团队去年发布mistral 7b模型至今,mistral7b就一直是同参数量内表现最亮眼的模型,逻辑能力、代码能力都非常强大,官方还发布了为聊天对话而微调的版本,也没有为个人用户自行下载部署设置任何额外的门槛(甚至当时在推特上直接发出磁力链接的时候我都被惊到了)。但是总感觉该模型在国内关注度不是很高(也可能是我相关网页刷少了),如果查找相关部署教程,找到的大多是使用mistral官方的软件包进行部署的说明。因此今天想发点文章给在部署这类模型的领域里0基础的朋友们分享一下,即如何使用Mistral-7B Instruct v0.3模型搭建一个简易的聊天API。之后还会写一些本文用到的内容的单独的短文,都会是基础向。
基础篇只支持一问一答的形式,进阶篇会实现实时更新生成情况、多轮对话等功能。
硬件相关
硬件需求
一句话总结:建议3060以上(显存8G以上)
Mistral-7B顾名思义参数量7B(好像是废话),如果你有一张16G显存的显卡,那么的确可以直接运行,但我更推荐使用transformers bitsandbytes的4bit量化进行加载。量化为nf4格式(之后的文章会做粗略解释)进行加载,空闲状态下只占用不到5GB显存,只有进行计算时会使用bf16,额外占用1~2GB显存。这种方式带来的效率上的提升(甚至是能不能运行的差距)在大部分情况下比损失的精度对体验的影响要大。当然,以上这些只针对本地部署的情况,我们今天是要建一个可以用于自己的小程序等其他访问的API。
本地部署可能是不合适的。
首先,本地部署大部分情况下都是个人电脑、普通家用宽带,没有公网,虽然可以通过内网穿透的形式解决,但内网穿透会导致程序检测到的连接全部来自127.0.0.1,难以限制单个IP的API访问频率。其次,本地部署需要长时间占用个人电脑,如果没有其他电脑可用的话,会比较影响体验。此外,安全性也是个问题。这时候就需要使用GPU型云服务器。
云服务器
传统的大型供应商有阿里云、腾讯云等等。只要挑个不那么老的GPU平台就可以了(必须是英伟达GPU)。如果你和我一样被几千每月起步的价格劝退了,那我十分推荐你尝试一下Compshare算力平台。首推的4090等GPU用于小模型推理刚刚好,价格每月也只要一千出头(也可以日付,才50r)(这个价格的最低配甚至是16核64G,200GB系统盘,共享100M带宽),而且每个实例都有独立的外网IP,还支持github、huggingface(非常常用的网站)访问加速。作为上市公司的平台可以做到比淘宝商家还便宜的价格,可以说是很香了。此外,如果你是新手,还可以直接使用相关镜像一键部署好环境甚至模型,你只需要根据自己的需求进行使用即可,非常方便。
模型下载
MistralAI的官网其实可以找到下载链接,所以我就只将我们要用的这个模型的连接写在这了(mistralai/Mistral-7B-Instruct-v0.3 · Hugging Face),后续可能会出0.4、0.5版本等等,到时候可以自行查看一下。
进去之后会看到:
我们点击FIles and versions选项卡,就可以看到模型文件了:
除了那个14GB的文件之外全部下载下来,放在一个文件夹里就行了,文件夹名字可以直接使用模型的名字,方便管理。
然后,我们需要准备软件运行环境了。
v0.3因为使用了tokenizer3,所以需要注意一下相关软件包的版本,我也没研究出什么规律,但是我将我的环境版本都写出来,实测可以在30/40系GPU的电脑上运行,TeslaT4等也可以跑。
我的主要软件包版本:
Python 3.11.7
accelerate==0.25.0
Flask==3.0.3
torch==2.3.0+cu121
torchaudio==2.1.2+cu121
torchvision==0.16.2+cu121
transformers==4.41.2
bitsandbytes==0.43.1
protobuf==5.27.1
(如果有问题记得评论说一下,我好补充说明,没接触过的可以搜搜相关文章,我之后也会单开一篇)
如果你已经部署好了相关环境,那么接下来就开始最爱的代码环节了。
代码实现
基础部署
首先,我们导入需要的引用
from transformers import BitsAndBytesConfig, AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer
import torch
接着,我们创建一个BitsAndBytesConfig,并将模型量化加载。(不同量化方式的对比我会单开一篇文章讲(疯狂挖坑x))
model_addr = "./model" # 模型路径(到模型所在文件夹为止)
nf4_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(model_addr,
trust_remote_code=True,
use_safetensors=True,
quantization_config=nf4_config)
model = AutoModelForCausalLM.from_pretrained(model_addr,
trust_remote_code=True,
use_safetensors=True,
quantization_config=nf4_config)
model = model.eval()
然后,我们需要将输入内容转换为符合格式要求的字符串。
system_prompt = "你是一个AI助理,你的功能是与用户对话交流。"
B_INST, E_INST = "[INST]", "[/INST]"
que = input("USER:\n")
prompt = f"{system_prompt}{B_INST}{que.strip()}\n{E_INST}"
system_prompt可以改成你想要的样子,例如:你是一个负责整理文章的助理,你需要将用户的输入整理成简洁的段落。
最后,我们使用模型生成回复并输出:
inputs = tokenizer.encode(prompt, return_tensors="pt").to("cuda")
attention_mask = torch.ones_like(inputs).to("cuda")
max_tokens = 200
res = model.generate(inputs, max_new_tokens=max_tokens,
attention_mask=attention_mask,
pad_token_id=tokenizer.eos_token_id)
ans = tokenizer.batch_decode(res, skip_special_tokens=True)[0]
print("Model:\n" + ans)
外面套一层循环,基础的部署就完成了,连续代码如下:
import torch
from transformers import BitsAndBytesConfig, AutoTokenizer, AutoModelForCausalLM
model_addr = "./model" # 模型路径(到文件夹为止)
nf4_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(model_addr,
trust_remote_code=True,
use_safetensors=True,
quantization_config=nf4_config)
model = AutoModelForCausalLM.from_pretrained(model_addr,
trust_remote_code=True,
use_safetensors=True,
quantization_config=nf4_config)
model = model.eval()
system_prompt = "你是一个AI助理,你的功能是与用户对话交流。"
B_INST, E_INST = "[INST]", "[/INST]"
max_tokens = 200
while True:
que = input("USER:\n")
if que == "exit":
break
prompt = f"{system_prompt}{B_INST}{que.strip()}\n{E_INST}"
inputs = tokenizer.encode(prompt, return_tensors="pt").to("cuda")
attention_mask = torch.ones_like(inputs).to("cuda")
res = model.generate(inputs, max_new_tokens=max_tokens,
attention_mask=attention_mask,
pad_token_id=tokenizer.eos_token_id)
ans = tokenizer.batch_decode(res, skip_special_tokens=True)[0]
print("Model:\n" + ans)
API构建
我们使用Flask构建一个简易的API。
首先,导入需要的软件包。
import flask
from flask import request
import json
接着,创建API。
server = flask.Flask(__name__)
@server.route('/api/aiAssist', methods=['get', 'post'])
def ai_assist():
# 获取通过url请求传参的数据
reqQue = request.values.get('q')
if reqQue:
r = generate_ans(reqQue) # 生成回复
resu = {'code': 200, 'message': r}
else:
resu = {'code': 10001, 'message': '参数不能为空'}
return json.dumps(resu, ensure_ascii=False)
server.run(debug=False, port=8888, host='0.0.0.0')
@server的声明表示下面的函数用于处理这个链接的请求
server.run中的参数port是端口(改为80可以不需要在访问时额外写进连接),host'0.0.0.0'表示所有地址都可以访问(flask更细致的有关内容本篇就不多讲了,本篇只是个0基础实操)
最后,我们实现代码里用到的 回复生成函数:
def generate_ans(que):
prompt = f"{system_prompt}{B_INST}{que.strip()}\n{E_INST}"
inputs = tokenizer.encode(prompt, return_tensors="pt").to("cuda")
attention_mask = torch.ones_like(inputs).to("cuda")
res = model.generate(inputs, max_new_tokens=max_tokens,
attention_mask=attention_mask,
pad_token_id=tokenizer.eos_token_id)
ans = tokenizer.batch_decode(res, skip_special_tokens=True)[0]
return ans
结合基础部署的内容,汇总完整代码如下:
import torch
from transformers import BitsAndBytesConfig, AutoTokenizer, AutoModelForCausalLM
from flask import request
import flask
import json
model_addr = "./model" # 模型路径(到文件夹为止)
nf4_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(model_addr,
trust_remote_code=True,
use_safetensors=True,
quantization_config=nf4_config)
model = AutoModelForCausalLM.from_pretrained(model_addr,
trust_remote_code=True,
use_safetensors=True,
quantization_config=nf4_config)
model = model.eval()
system_prompt = "你是一个AI助理,你的功能是与用户对话交流。"
B_INST, E_INST = "[INST]", "[/INST]"
max_tokens = 200
def generate_ans(que):
prompt = f"{system_prompt}{B_INST}{que.strip()}\n{E_INST}"
inputs = tokenizer.encode(prompt, return_tensors="pt").to("cuda")
attention_mask = torch.ones_like(inputs).to("cuda")
res = model.generate(inputs, max_new_tokens=max_tokens,
attention_mask=attention_mask,
pad_token_id=tokenizer.eos_token_id)
ans = tokenizer.batch_decode(res, skip_special_tokens=True)[0]
return ans
server = flask.Flask(__name__)
@server.route('/api/aiAssist', methods=['get', 'post'])
def ai_assist():
# 获取通过url请求传参的数据
reqQue = request.values.get('q')
if reqQue:
r = generate_ans(reqQue)
resu = {'code': 200, 'message': r}
else:
resu = {'code': 10001, 'message': '参数不能为空'}
return json.dumps(resu, ensure_ascii=False)
server.run(debug=False, port=8888, host='0.0.0.0')
访问测试(可以使用浏览器直接打开下面的链接)
http://127.0.0.1:8888/api/aiAssist?q=Hello
加载需要的时间取决于回复生成的速度。
需要注意的是,这里的代码虽然可以实现功能,但如果有推广使用的需求需要自行结合实际场景加以改进,比如限制访问频率、限制重复输入等。
那么,基础篇就到这里了。如果你想要:实时显示回复情况(像GPT那样实时显示出来的)、结合上下文连续对话的功能,那么可以查阅进阶篇(【0基础】使用Mistral-7B模型搭建简易的聊天API(进阶篇)-CSDN博客)。
本人自己也在学习中,如有问题请各路大佬多加指点,感谢。