【0基础】使用Mistral-7B模型搭建简易的聊天API(基础篇)

准备环节

前言的前言

一些闲谈: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博客)。

本人自己也在学习中,如有问题请各路大佬多加指点,感谢。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OrikasaYuki

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值