AI服务的并发处理【Python】

news2024/9/23 15:28:28

有一段时间,我专注于机器学习的研究方面,为不同的任务开发定制的机器学习解决方案。 但是最近,新项目进来了,有时自己负责初始部署比寻求其他开发人员的帮助更快。 我发现了几个在规模、易用性、定价等方面不同的部署选项。

今天,我们将讨论一种简单而强大的机器学习模型部署方法。 它允许我们同时处理多个请求并在需要时扩展应用程序。 我们还将讨论数据科学家在将机器学习模型投入生产时的职责,以及如何使用一些方便的 Python 工具对 Web 应用程序进行负载测试。

在这里插入图片描述

推荐:用 NSDT设计器 快速搭建可编程3D场景。

1、数据科学家的职责

你几乎可以为每项任务找到大量开源解决方案。 一些现有服务甚至可以处理数据验证和处理、数据存储、模型训练和评估、模型推理和监控等。

但是,如果你仍然需要定制解决方案怎么办? 你必须自己开发整个基础架构。 这就是我一直在思考的问题:数据科学家到底负责什么? 它只是模型本身,还是我们必须将其投入生产?

通常,数据科学家的职责因公司而异。 我和我的首席技术官讨论了这个问题。 我们讨论了数据科学家应该具备专业知识的一些案例。 他们应该能够将他们的解决方案作为 API 交付,将其容器化,并且理想情况下,开发解决方案以同时处理多个请求。

对于移动设备,通常向移动开发人员提供转换为相应格式的模型就足够了。 最重要的是,你可以提供文档来描述模型将什么作为输入以及它作为输出返回什么。

如果一个 Docker 容器无法处理预期的流量,数据科学家应该将进一步扩展委托给合适的专家。

2、Flask 和并发

我们将使用 Flask 作为一个简单应用程序的一部分来进行试验。 它是一个用 Python 构建的微型 Web 框架,旨在用于小型应用程序。

当收到请求时,此应用程序将向 httpbin.org 发送请求——这是一项可帮助你试验不同请求的服务。 发送请求后,我们的应用程序将收到两秒延迟的响应。 我们需要这种延迟来试验并发性。

from flask import Flask
import requests
import json


app = Flask(__name__)

@app.route('/')
def test_request():
    response = requests.get('https://httpbin.org/delay/2').json()
    return response

if __name__ == "__main__":
    app.run(host='0.0.0.0')

纯 Python 有其“臭名昭著”的 GIL 限制,它实质上限制了一次只能运行一个 Python 线程(在此处阅读)。 如果希望应用程序在给定时间内处理更多请求,我们有两个选择:线程和多进程处理。 使用哪个取决于应用程序的瓶颈。

3、什么时候选择多线程?

只要存在等待时间,就应该使用多线程。 我们的应用程序的编写方式代表了典型的 I/O 绑定操作。 因此,大部分执行时间都花在了等待其他服务(如操作系统、数据库、互联网连接等)上。 在这种情况下,我们可以从多线程中获益,因为它有助于利用等待时间。

4、什么时候选择多进程?

另一方面,当你想要提高应用程序性能时,应该使用多处理。 假设我们的应用程序主动使用 CPU(例如,通过神经网络前向传递数据),其性能完全取决于 CPU 的计算能力。 此应用程序被描述为受 CPU 限制。 为了提高应用程序的性能,我们需要多处理。 与线程不同,我们创建单独的解释器实例并并行执行计算。

Flask 的内置服务器从 1.0 版本开始默认是线程化的。 那么,为什么不完全使用 Flask 部署应用程序呢? Flask 的网站明确指出“Flask 的内置服务器不适合生产”,因为它不能很好地扩展。

我们将在一分钟内查看另一个部署解决方案。 但首先,我建议测试应用程序以了解它处理负载的能力。

5、使用 Locust 进行负载测试

在向 API 发送流量之前对其进行负载测试非常重要。 一种方法是使用名为 Locust 的 Python 库。 它在本地主机上运行一个 Web 应用程序,并有一个简单的界面,允许我们自定义测试和可视化测试过程。

让我们在本地主机上的 Flask 应用程序上运行一些测试。

1)使用以下命令安装locust:

pip3 install locust

2)创建脚本并将其添加到我们的项目目录:

from locust import HttpUser, between, task


class MyWebsiteUser(HttpUser):
    wait_time = between(5, 15)

    @task
    def load_main(self):
        self.client.get("/")

3)使用以下命令运行应用程序:

python3 demo.py

4)使用另一个命令运行我们的负载测试应用程序:

locust -f load_testing.py --host=http://0.0.0.0:5000/

5)在浏览器中访问 http://localhost:8089,你会看到 Locust 界面

6)指定生成的唯一用户数和每秒发送的请求数

7)根据需要选择参数
在这里插入图片描述

6、测试多线程

现在我们来看看应用程序在有和没有启用多线程的情况下的测试。 它将使我们了解它是否有助于在给定时间内处理更多请求。 请记住,我们在从服务器获得响应之前设置了两秒的延迟。

下图是关闭多线程的情况:

 ...
if __name__ == "__main__":
    app.run(host='0.0.0.0', threaded=False)

在这里插入图片描述

下一个测试是打开线程。 为此,请删除前面提到的参数。
在这里插入图片描述

如你所见,使用线程可以获得更高的 RPS(每秒请求数)率。

但是,如果我们开发一个应用程序来对图像进行分类呢? 此操作会主动使用我们的 CPU。 在这种情况下,与其使用线程,不如使用单独的进程来处理请求会更好。

7、使用uWSGI处理并发

要设置并发请求,我们需要使用 uWSGI。 它是一种允许我们更好地控制多处理和线程的工具。 uWSGI 为我们提供了足够的功能和灵活性来部署应用程序,同时仍然可以访问。

让我们更改之前创建的 Flask 应用程序,使其看起来更像一个真正的机器学习服务:

from flask import Flask
import numpy as np
import tensorflow as tf


app = Flask(__name__)

model = tf.keras.applications.MobileNetV2(input_shape=(160, 160, 3),
                                               include_top=True,
                                               weights='imagenet')

@app.route('/')
def predict():
    data = np.zeros((1, 160, 160, 3))
    model.predict(data)
    return "Hello, World!"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

运行后,它会初始化模型。 每次收到模拟真实应用程序功能的请求时,它都会通过模型执行零数组的前向传递。

首先,让我们看一下没有 uWSGI 的应用程序的 RPS(每秒请求数):

在这里插入图片描述

关闭多线程
在这里插入图片描述

开启多线程

我们将“服务”作为一个纯 Flask 应用程序进行了测试,分别使用 threaded=False 和 threaded=True。 如你所见,虽然当 threaded=True 时 RPS 更高,但改善并不多。 这是因为应用程序仍然主要依赖于 CPU。

8、使用 uWSGI 进行测试

首先,我们需要安装uWSGI:

pip3 install uwsgi

然后,我们需要将配置文件添加到我们的项目目录中。 该文件包含 uWSGI 需要与我们的应用程序一起运行的所有参数。

[uwsgi]
module = demo:app
master = true
processes = 2
threads = 1
enable-threads = true
listen = 1024
need-app = true

http = 0.0.0.0:5000

让我们回顾一下你将在此配置文件中看到的最重要的参数:

  • module = demo:app — 这是包含我们的 application:Flask 对象名称的脚本名称
  • master = true — 这是重复调用 worker、记录和管理其他功能所必需的主要 uWSGI 进程。 在大多数情况下,这应该设置为“true”
  • processes = 2 / threads = 1 — 这指定要运行的进程和线程的数量。 你还可以使用 uWSGI 的名为 cheaper 的子模块来自动缩放进程和线程的数量
  • enable_threads = true — 这在多线程模式下运行 uWSGI
  • listen=1024 — 这是请求队列的大小
  • need-app = true——这个标志阻止uWSGI在找不到或无法运行应用程序时运行。 如果它等于 False,uWSGI 将忽略任何导入问题并返回请求的 500 状态。
  • http = 0.0.0.0:5000 — 这是访问应用程序的 URL 和端口。 它仅在用户直接向应用程序发送请求时使用。

默认情况下,uWSGI 加载你的应用程序然后fork它。 但是你可以指定 lazy-apps = true。 这样,uWSGI 为每个 worker 分别加载你的应用程序。 它可以帮助避免 TensorFlow 模型错误或在工作人员之间共享其他数据。

另一个关键参数是 listen。 必须将此参数设置为你希望在重新加载期间排队的最大唯一用户数。 否则,其中一些可能会出错。 默认情况下,listen 等于 100。在此处相关信息。

uWSGI 有更多有用的参数,但现在,让我们运行应用程序:

uwsgi uwsgi.ini

现在,我们可以查看封装到 uWSGI 中的 Flask 应用程序的负载测试结果:

在这里插入图片描述

uWSGI - 1个进程/2个线程
在这里插入图片描述

uWSGI - 2个进程/1个线程

使用两个单独的进程从根本上提高了 RPS。 但是,应用程序是受 I/O 限制还是受 CPU 限制并不总是很明显。 在多处理和多线程之间进行选择有点棘手。可以 看看这篇文章以获得更好的理解。

9、结束语

希望现在你明白为什么这个问题一直萦绕在我的脑海中。 数据科学家是否需要更多地关注让模型准备好部署,如果需要,限制是什么? 理想情况下,使用 Flask 和 uWSGI,你已经具备启动和运行它的基本要素。 但天空是极限,你的情况可能需要更多。

最后,如果你希望应用程序向全世界开放,应该注意安全性。 我们没有在本文中介绍安全性,因为它完全是另一个主题,但你应该牢记这一点。

一如既往,我希望这篇文章对你有用。 注意安全。


原文链接:AI服务的并发处理 — BimAnt

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

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

相关文章

SSM酒店后台管理系统

主要功能 管理员权限登录: ①首页展示当前时间信息,Layui框架实现的滚动图等布局 ②住客管理:住客入驻、住客列表的增删改查操作 ③房间管理:对房间进行相关的操作,详细信息、更新状态等 ④会员管理:新增会…

Mysql索引的应用

文章目录 一、索引介绍1. 索引的概念2. 索引的作用与副作用2.1 索引的作用2.2 索引的副作用2.3 如何实现索引 3. 创建索引的原则依据4. 索引的分类和创建4.1 普通索引直接创建索引修改表方式创建创建表的时指定索引 4.2 唯一索引直接创建唯一索引修改表方式创建创建表的时候指定…

Linux-- vi / vim 编辑器

目录 vi \ vim 编辑器的三种编辑模式 vi \ vim 的使用 模式的切换 命令模式下的命令 底线模式 vi \ vim 是visual interface 的简称, 是linux中的经典文本编辑器, 同图形化界面中的文本编辑器一样, 但是vi是使用命令行来对文本进行编辑的最好选择 vim是vi的加强版本, 兼容vi的…

15-2.自定义组件的数据

目录 1 data与method 2 properties 2.1 基本使用 2.2 properties在小程序中可读可写 2.3 对properties使用setData() 3 数据监听器 observers 3.1 基本使用 3.2 同时监听多个变量 3.3 监听对象中属性某个属性的变化 3.4 监听对象中所有属性 4 纯数据字段…

【CSS】属性书写顺序

1.布局定位属性: display(元素类型建议第一个) position float(浮动) clear visibility overflow 2.自身属性: width height margin(外边距) …

我的内网渗透-Empire进阶(会话转移和剪贴板攻击)

剪贴版攻击 安装 下载网址 https://github.com/D4Vinci/PasteJacker 下载的两种方式 ①直接在windows中下载,然后解压完把文件夹传到kali中进行安装 ②只接在kali中下载 git clone https://github.com/D4Vinci/PasteJacker #下载压缩包unzip PasteJacker-maste…

C语言之运算符

C语言运算符 文末附运算符的优先表和ASCII表 一、算术运算符 加()减(—)乘(*)除(/)模(余)运算符(%):不允许出现浮点型,…

这是一颗经过计划生育的树?

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏1: 🍔🍟🌯C语言初阶 🐻推荐专栏2: 🍔🍟🌯C语言进阶 🔑个人信条: 🌵知行合一 &#x1f…

Hadoop的shuffle过程及调优

MapReduce 中的Shuffle 发生在 map 输出到 reduce 输入的过程,它的中文解释是 “洗牌”,顾名思义该过程涉及数据的重新分配,主要分为两部分: map 任务输出的数据分组、排序,写入本地磁盘。reduce 任务拉取排序。 由于…

基于Java+Swing+Mysql实现人事管理信息系统

基于JavaSwingMysql实现人事管理信息系统 一、系统介绍二、功能展示1.用户登陆2.用户注册3.员工信息添加、删除4.员工信息查询、修改5.部门管理6、员工考核 三、数据库四、其它1.其他系统实现五.获取源码 一、系统介绍 系统功能:用户登陆、用户注册、员工信息添加、…

6.8object类equals tostring

2 什么是API API(Application Programming Interface,应用程序接口)是一些预先定义的函数。目的是提供应用程序与开发人员基于某软件可以访问的一些功能集,但又无需访问源码或理解内部工作机制的细节. API是一种通用功能集,有时公…

基于Java+Swing+Mysql实现旅游管理信息系统

基于JavaSwingMysql实现旅游管理信息系统 一、系统介绍二、功能展示1.登陆2.注册3.旅游信息查询4.查看游行团信息5.报名6、报名信息管理 三、数据库四、其它1.其他系统实现五.获取源码 一、系统介绍 用户:登陆、注册、旅游信息查询、查看游行团信息、报名 管理员&a…

【ARIMA-SSA-LSTM】合差分自回归移动平均方法-麻雀优化-长短期记忆神经网络研究(Python代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

数据库约束、表的关系

数据库约束、表的关系 数据库约束、表的关系 1. 数据库约束1.1 约束类型1.2 NULL约束1.3 UNIQUE:唯一约束1.4 DEFAULT:默认值约束1.5 PRIMARY KEY:主键约束1.6 FOREIGN KEY:外键约束 2. 表的设计2.1 一对一2.2 一对多2.3 多对多 …

ODOO16 ERP如何做标准的研发费用归集?

目前国家大力鼓励企业研发投入,并且给予很多鼓励。如《中共中央关于制定国民经济和社会发展第十四个五年规划和二〇三五年远景目标的建议》中明确提出:“发挥企业家在技术创新中的重要作用,鼓励企业加大研发投入,对企业投入基础研…

2023洗发护发市场分析(头皮清洁护理等新兴品类销售数据分析)

如今,随着人们消费观念的转变,对洗发护发相关用品的要求也逐渐提高,由原来单一的清洁功能到更注重洗发护发用品的功能及护理效果。因此,洗发护发产品的品类不断增加,洗发护发产品的市场规模也不断扩大,整体…

JAVA实现一个工作流引擎

介绍 工作流是一种将一系列相关的任务和活动组织起来的技术,以便在企业或组织中自动化或半自动化地管理业务流程。工作流技术可以帮助企业或组织更好地管理和优化业务流程,提高生产效率和质量,降低成本和风险。 JAVA作为一种面向对象编程语…

线程池与CompletableFuture 异步编排

使用线程池的好处: 1、降低资源的消耗 通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗 2、提高响应速度 因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务 的状态,当任务来时无需创建新的线程就能执行…

STM32启动详细流程分析(一)

问题提出 大家不妨设想一下,cpu 的工作是什么,cpu 是没有主观意识的,它只会按照特定的指令执行相应的操作,用专业术语来说就是:取指 -> 译码 -> 执行,译码和执行肯定是在 cpu 内部进行操作的&#x…

MySQL数据库增删改查及聚合查询SQL语句学习汇总

目录 数据库增删改查SQL语句 MySQL数据库指令 1.查询数据库 2.创建数据库 3.删除数据库 4.选择数据库 创建表table 查看所有表 创建表 查看指定表的结构 删除表 数据库命令进行注释 增删改查(CRUD)详细说明 增加 SQL库提供了关于时间的…