Qwen1.5-1.8b部署

news2024/12/23 1:44:31

仿照ChatGLM3部署,参考了Qwen模型的文档,模型地址https://modelscope.cn/models/qwen/Qwen1.5-1.8B-Chat/summary

http接口

  • 服务端代码api.py
from fastapi import FastAPI, Request
from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig
import uvicorn
import json
import datetime
import torch

# 设置设备参数
DEVICE = "cuda"  # 使用CUDA
DEVICE_ID = "0"  # CUDA设备ID,如果未设置则为空
CUDA_DEVICE = f"{DEVICE}:{DEVICE_ID}" if DEVICE_ID else DEVICE  # 组合CUDA设备信息

# 加载预训练的分词器和模型
model_name_or_path = '/root/autodl-tmp/qwen/Qwen1.5-1.8B-Chat'
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, use_fast=False)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, device_map="auto", torch_dtype=torch.bfloat16)

# 清理GPU内存函数
def torch_gc():
    if torch.cuda.is_available():  # 检查是否可用CUDA
        with torch.cuda.device(CUDA_DEVICE):  # 指定CUDA设备
            torch.cuda.empty_cache()  # 清空CUDA缓存
            torch.cuda.ipc_collect()  # 收集CUDA内存碎片

# 创建FastAPI应用
app = FastAPI()

# 处理POST请求的端点
@app.post("/")
async def create_item(request: Request):
    global model, tokenizer  # 声明全局变量以便在函数内部使用模型和分词器
    json_post_raw = await request.json()  # 获取POST请求的JSON数据
    json_post = json.dumps(json_post_raw)  # 将JSON数据转换为字符串
    json_post_list = json.loads(json_post)  # 将字符串转换为Python对象
    prompt = json_post_list.get('prompt')  # 获取请求中的提示

    messages = [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": prompt}
    ]

    # 调用模型进行对话生成
    input_ids = tokenizer.apply_chat_template(messages,tokenize=False,add_generation_prompt=True)
    model_inputs = tokenizer([input_ids], return_tensors="pt").to('cuda')
    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]
    now = datetime.datetime.now()  # 获取当前时间
    time = now.strftime("%Y-%m-%d %H:%M:%S")  # 格式化时间为字符串
    # 构建响应JSON
    answer = {
        "response": response,
        "status": 200,
        "time": time
    }
    # 构建日志信息
    log = "[" + time + "] " + '", prompt:"' + prompt + '", response:"' + repr(response) + '"'
    print(log)  # 打印日志
    torch_gc()  # 执行GPU内存清理
    return answer  # 返回响应

# 主函数入口
if __name__ == '__main__':
    # 启动FastAPI应用
    # 用6006端口可以将autodl的端口映射到本地,从而在本地使用api
    uvicorn.run("api:app", host='127.0.0.1', port=6006, workers=2)  # 在指定端口和主机上启动应用
    # gunicorn api:app -w 3 -k uvicorn.workers.UvicornWorker -b 127.0.0.1:6006
  • 客户端代码clientapi.py
import requests
import json

def get_completion(prompt):
    headers = {'Content-Type': 'application/json'}
    data = {"prompt": prompt}
    response = requests.post(url='http://127.0.0.1:6006', headers=headers, data=json.dumps(data))
    return response.json()['response']

if __name__ == '__main__':
    print(get_completion('你可以记录之前说过的内容吗'))
  • 压测代码locusthttp.py,运行locust -f locusthttp.py(有UI),使用vmstattop查看进程、内存、CPU等情况。
import json

from locust import HttpUser, TaskSet, task

# 定义用户行为
class UserBehavior(TaskSet):
    # 任一测试用例执行前均会执行一次
    def on_start(self):
        print('开始性能测试')

    # 表示一个用户为行,访问百度首页。使用 @task装饰该方法为一个事务。client.get()用于指请求的路径“ / ”,因为是百度首页,所以指定为根路径。
    @task(1)
    def index(self):
        self.client.get("/")

    @task(2)  # task()参数用于指定该行为的执行权重。参数越大每次被虚拟用户执行的概率越高。如果不设置默认为1。
    def index2(self):
        headers = {'Content-Type': 'application/json'}
        data = {'prompt': '你知道珠穆朗玛峰吗'}
        self.client.post(url='/index2', headers=headers, data=json.dumps(data))

    @task(2)  # task()参数用于指定该行为的执行权重。参数越大每次被虚拟用户执行的概率越高。如果不设置默认为1。
    def index3(self):
        headers = {'Content-Type': 'application/json'}
        data = {'prompt': '你是谁'}
        self.client.post(url='/index3', headers=headers, data=json.dumps(data))

    @task(2)  # task()参数用于指定该行为的执行权重。参数越大每次被虚拟用户执行的概率越高。如果不设置默认为1。
    def index4(self):
        headers = {'Content-Type': 'application/json'}
        data = {'prompt': '西红柿炒番茄怎么做'}
        self.client.post(url='/index4', headers=headers, data=json.dumps(data))


# 用于设置性能测试
class WebsiteUser(HttpUser):
    # 指向一个定义的用户行为类。
    tasks = [UserBehavior]
    # 执行事务之间用户等待时间的下界(单位:毫秒)。如果TaskSet类中有覆盖,以TaskSet 中的定义为准。
    min_wait = 3000
    # 执行事务之间用户等待时间的上界(单位:毫秒)。如果TaskSet类中有覆盖,以TaskSet中的定义为准。
    max_wait = 6000
    # 设置 Locust 多少秒后超时,如果为 None ,则不会超时。
    stop_timeout = 5
    # 一个Locust实例被挑选执行的权重,数值越大,执行频率越高。在一个 locustfile.py 文件中可以同时定义多个 HttpUser 子类,然后分配他们的执行权重
    weight = 3
    # 脚本指定host执行测试时则不在需要指定
    host = "http://127.0.0.1:6006"

WebSocket长连接

  • 服务端代码websocketapi.py
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig
import uvicorn
import torch

pretrained = "/root/autodl-tmp/qwen/Qwen1.5-1.8B-Chat"
tokenizer = AutoTokenizer.from_pretrained(pretrained, use_fast=False)
model = AutoModelForCausalLM.from_pretrained(pretrained, device_map="auto", torch_dtype=torch.bfloat16)
model = model.eval()
app = FastAPI()

app.add_middleware(
    CORSMiddleware
)

with open('websocket_demo.html') as f:
    html = f.read()


@app.get("/")
async def get():
    return HTMLResponse(html)


@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    """
    input: JSON String of {"prompt": ""}
    output: JSON String of {"response": "", "status": 200}
        status 200 stand for response ended, else not
    """
    await websocket.accept()
    try:
        while True:
            json_request = await websocket.receive_json()
            prompt = json_request['prompt']
            messages = [
                {"role": "system", "content": "You are a helpful assistant."},
                {"role": "user", "content": prompt}
            ]
            input_ids = tokenizer.apply_chat_template(messages,tokenize=False,add_generation_prompt=True)
            model_inputs = tokenizer([input_ids], return_tensors="pt").to('cuda')
            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]
            await websocket.send_json({
                "response": response,
                "status": 202,
            })
            await websocket.send_json({"status": 200})
    except WebSocketDisconnect:
        pass


def main():
    uvicorn.run(f"{__name__}:app", host='127.0.0.1', port=6006, workers=2)


if __name__ == '__main__':
    main()
    # gunicorn websocket_api_qwen:app -w 3 -k uvicorn.workers.UvicornWorker -b 127.0.0.1:6006
  • 前端代码websocket.html
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="return false;" id="form">
    <label for="messageText"></label>
    <input type="text" id="messageText" autocomplete="off"/>
    <button type="submit">Send</button>
</form>
<ul id='messageBox'>
</ul>
<script>
    let ws = new WebSocket("ws://" + location.host + "/ws");
    let history = [];
    let last_message_element = null;

    function appendMessage(text, sender, dom = null) {
        if (dom === null) {
            let messageBox = document.getElementById('messageBox');
            dom = document.createElement('li');
            messageBox.appendChild(dom);
        }
        dom.innerText = sender + ':' + text;
        return dom
    }

    function sendMessage(event) {
        if (last_message_element !== null) {  // 如果机器人还没回复完
            return;
        }
        let input = document.getElementById("messageText");
        if (input.value === "") {
            return;
        }
        let body = {"prompt": input.value};
        ws.send(JSON.stringify(body));
        appendMessage(input.value, '用户')
        input.value = '';
        event.preventDefault();
    }

    document.getElementById("form").addEventListener('submit', sendMessage)

    ws.onmessage = function (event) {
        let body = JSON.parse(event.data);
        let status = body['status']
        if (status === 200) {  // 如果回答结束了
            last_message_element = null;
        } else {
            history = body['history']
            last_message_element = appendMessage(body['response'], 'Qwen1.5-1.8B-chat', last_message_element)
        }
    };
</script>
</body>
</html>
  • 运行结果
    在这里插入图片描述
  • 压测代码locustwebsocket.py
import json
import time

import websocket
from locust import User, TaskSet, task, events
import random

class WebSocketClient(object):

    def __init__(self, host):
        self.host = host
        self.ws = websocket.WebSocket()

    def connect(self, burl):
        start_time = time.time()
        try:
            self.conn = self.ws.connect(url=burl)
        except websocket.WebSocketTimeoutException as e:
            total_time = int((time.time() - start_time) * 1000)
            events.request_failure.fire(request_type="websocket", name='urllib', response_time=total_time, exception=e)
        else:
            total_time = int((time.time() - start_time) * 1000)
            events.request_success.fire(request_type="websocket", name='urllib', response_time=total_time, response_length=0)
        return self.conn

    def recv(self):
        return self.ws.recv()

    def send(self, msg):
        self.ws.send(msg)

class WebsocketLocust(User):
    def __init__(self, *args, **kwargs):
        super(WebsocketLocust, self).__init__(*args, **kwargs)
        self.client = WebSocketClient(self.host)

class SupperDianCan(TaskSet):

    @task
    def test(self):
        self.url = 'http://127.0.0.1:6006'

        self.data = {}

        self.client.connect(self.url)
        while True:
            recv = self.client.recv()
            print(recv)
            if eval(recv)['type'] == 'keepalive':
                self.client.send(recv)
            else:
                self.client.send(self.data)
class WebsocketUser(TaskSet):
    host = "http://127.0.0.1:6006"

    client = None

    def on_start(self):
        self.client = WebSocketClient("ws://127.0.0.1:6006/ws")
        self.client.connect()

    @task
    def send_message(self):
        # 发送的订阅请求
        num = random.randint(0, 10)
        prompt = f"世界上第{num}高的山峰是什么"
        self.client.send(json.dumps({'prompt': prompt}))
        response = self.client.recv()
        print(json.loads(response))


class WebsiteUser(User):
    tasks = [WebsocketUser]
    min_wait = 3000
    max_wait = 6000
    stop_timeout = 5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1898969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

强化学习后的数学原理:随机近似与梯度下降

概述 这节课的作用&#xff1a; 本节课大纲如下&#xff1a; Motivating examples 先回顾一下 mean estimation &#xff1a; 为什么总数反复提到这个 mean estimation&#xff0c;就是因为 RL 当中有非常多的 expectation&#xff0c;后面就会知道除了 state value 这些定义…

PySide6 实现资源的加载:深入解析与实战案例

目录 1. 引言 2. 加载内置资源 3. 使用自定义资源文件&#xff08;.qrc&#xff09; 创建.qrc文件 编译.qrc文件 加载资源 4. 动态加载UI文件 使用Qt Designer设计UI 加载UI文件 5. 注意事项与最佳实践 6. 结论 在开发基于PySide6的桌面应用程序时&…

博途通讯笔记1:1200与1200之间S7通讯

目录 一、添加子网连接二、创建PUT GET三、各个参数的意义 一、添加子网连接 二、创建PUT GET 三、各个参数的意义

新手高效指南:电子元器件BOM表创建/制作及配单全教程

在科技日新月异的今天&#xff0c;电子产品设计与制造不仅是创新精神的展现&#xff0c;更是对精确度与效率的不懈追求。在这个过程中&#xff0c;一份精细且全面的BOM&#xff08;物料清单&#xff09;犹如一座桥梁&#xff0c;连接着创意与现实世界。BOM不仅细致记录了产品所…

如何优化圆柱晶振32.768KHz的外壳接地?

圆柱晶振32.768KHz在电子设备中扮演着重要的角色&#xff0c;其精确的时钟信号对于许多应用至关重要。为了确保晶振的稳定性和准确性&#xff0c;外壳接地是一个关键步骤。 一、外壳接地的目的 外壳接地的主要目的是为了防止信号干扰。当晶振的外壳接地后&#xff0c;它相当于…

16-JS封装:extend方法

目录 一、封装需求 二、实现1&#xff1a;jQuery.extend 三、实现2&#xff1a;通过原型jQuery.fn.extend 四、优化 一、封装需求 封装需求&#xff1a; $.extend&#xff1a; var obj{ name:"xxx",age:18} var obj3{ gender:"女"} var obj2{}; 将obj、…

S7-1200PLC学习记录

文章目录 前言一、S7-12001.数字量输入模块2. PNP接法和NPN接法 二、博图软件1. 位逻辑运算Part1. 添加新设备&#xff08;添加PLC&#xff09;Part2. 添加信号模块Part3. 添加信号板中模块Part4. 添加新块Part5. Main编程文件案例1案例2 -( S )- 和 -( R )-完整操作过程&#…

ERROR | Web server failed to start. Port 8080 was already in use.

错误提示&#xff1a; *************************** APPLICATION FAILED TO START ***************************Description:Web server failed to start. Port 8080 was already in use.Action:Identify and stop the process thats listening on port 8080 or configure thi…

C++——模板详解(下篇)

一、非类型模板参数 模板参数分为类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之后的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类&#xff08;函数&#xff09;模板的一个参数&#xff0c;在类&#…

Swift 中的方法调用机制

Swift 方法调用详解&#xff1a;与 Objective-C 的对比、V-Table 机制、Witness Table 机制 在 iOS 开发中&#xff0c;Swift 和 Objective-C 是两种常用的编程语言。尽管它们都能用于开发应用程序&#xff0c;但在方法调用的底层机制上存在显著差异。本文将详细介绍 Swift 的…

maven项目使用netty,前端是vue2,实现通讯

引入的java包 <!-- 以下是即时通讯--><!-- Netty core modules --><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.76.Final</version> <!-- 使用最新的稳定版本…

C++中的引用——引用的注意事项

1.引用必须初始化 2.引用在初始化后不可以改变 示例&#xff1a; 运行结果&#xff1a;

03:EDA的进阶使用

使用EDA设计一个38译码器电路和245放大电路 1、38译码器1.1、查看74HC138芯片数据1.2、电路设计 2、245放大电路2.1、查看数据手册2.2、设计电路 3、绘制PCB3.1、导入3.2、放置3.3、飞线3.4、特殊方式连接GND3.5、泪滴3.6、配置丝印和划分区域3.7、添加typc接口供电 1、38译码器…

案例精选 | 聚铭网络助力南京市玄武区教育局构建内网日志审计合规体系

南京市玄武区教育局作为江苏省教育领域的先锋机构&#xff0c;其工作重点涵盖了教育政策的实施、教育现代化与信息化的融合、教育资源的优化、教育质量的提升以及教育公平的促进。在这一背景下&#xff0c;网络安全管理成为了确保教育信息化顺利推进的关键环节之一。 根据玄武…

【CV炼丹师勇闯力扣训练营 Day24:§7 回溯3】

CV炼丹师勇闯力扣训练营 代码随想录算法训练营第24天 93 复原IP地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 ‘.’ 分隔。 例如&#xff1a;“0.1.2.201” 和 “192.168.…

tensorboard安装失败,怎么办?

标题tensorboard未安装报错&#xff0c;如下图所示&#xff1a; 解决方法尝试&#xff1a; 这些方法都可以试一下&#xff0c;因为每个人的情况可能不同&#xff0c;成功的方法也不同。 1、在pycharm中Pip失败 2、 在pycharm中Conda 失败 3、在Pycharm中换清华源失败 4、直接…

软件是什么?一个软件到底是哪些部分组成的-软件到底有哪些分支呢?

https://doc.youyacao.com/117/2163 软件是什么&#xff1f;一个软件到底是哪些部分组成的-软件到底有哪些分支呢&#xff1f; 何为软件 软件定义 的本质是通过软件编程实现硬件资源的虚拟化、灵活、多样和定制化功能&#xff0c;以最大化系统运行效率和能量效率。它基于硬…

面试题-Spring家族与SpringIOC

1.spring家族的介绍 Spring简单图&#xff1a; 2.IOC原理 IOC就是原先代码里需要开发者实现对象的创建和关系依赖&#xff0c;反转交给SpringIOC容器管理对象的生命周期和对象之间的依赖关系。 依赖注入的方式&#xff1a; Setter&#xff1a;实现特定属性的public sette…

资源监视器 查看写入磁盘的文件

"Win R" 组合键来打开运行对话框&#xff0c;然后输入 "resmon" 并按下回车键&#xff0c;或者在开始菜单中搜索 "资源监视器" 并打开它。 可以查看时哪个服务在往盘里写日志或者文件

pytest-rerunfailures:优化测试稳定性的失败重试工具

笔者在执行自动化测试用例时&#xff0c;会发现有时候用例失败并非代码问题&#xff0c;而是由于服务正在发版&#xff0c;导致请求失败&#xff0c;从而降低了自动化用例的稳定性&#xff0c;最后还要花时间定位到底是自身case的原因还是业务逻辑问题&#xff0c;还是其他原因…