AI 实践

AI 资源


资源 组织 类型
Hugging Face Hugging Face Community/Model
Cuda Image Hub Nvidia Image/Cuda
Container Toolkit Nvidia Runtime/Dev
mega.space Mega AI Image Generator
Colab Google Tools
Image Delivery image.delivery Images(Stable Diffusions)
dl tutorialspoint tutorialspoint tutorial
agentgpt AgentGPT Application
awesome-open-gpt resource EwingYangs GPT resource

Beginner


机器如何学习

从数据中找出规则(通过数学和程序)。

例如,通过图片识别出狗还是猫,只要足够的数据给到模型生成规则,那么再遇到类似的问题,就能够得到相对的答案,当正确率达到一定程度后,就代码这个模型相对稳定了。

  • 模型,由各种数学和方程组成的规则构成。

  • 相对正确的答案,达到一定正确率。

Colab 实验.

Colab

谷歌提供的免费 ML 验证工具。

ipynb notebook

转为文件

  • type: 转为的类型,包括 html、markdown、pdf、rst

1
jupyter nbconvert --to type filename/as/ipynb

Math in AI

激活函数

sigmoid

Palm Detection

NHWC to NCHW

在深度学习中,NHWC 和 NCHW 是两种常见的张量格式。NHWC 表示通道维度在最后一个维度,即 (batchsize, height, width, channels),而 NCHW 表示通道维度在第二个维度,即 (batchsize, channels, height, width)。

将输入形状从 HWC 格式转换为 CHW 格式。具体来说,将输入形状从 (height, width, channels) 转换为 (channels, height, width)。这是因为在 PyTorch 中,张量的默认格式是 NCHW,而在 TensorFlow 中,张量的默认格式是 NHWC。因此,将输入形状从 HWC 格式转换为 CHW 格式可以使代码更易于在 PyTorch 中使用。

要将张量从 NHWC 格式转换为 NCHW 格式,可以使用以下代码:

1
2
3
4
import torch

# Convert from NHWC to NCHW with torch.Tensor.permute
tensor_nchw = tensor_nhwc.permute(0, 3, 1, 2)

在这里,tensor_nhwc 是一个形状为 (batch_size, height, width, channels) 的张量。tensor_nchw 是一个形状为 (batch_size, channels, height, width) 的张量,其中通道维度在第二个维度。tensor_nchw.permute (0, 3, 1, 2) 将张量的维度重新排列,以将通道维度移动到第二个维度。

ml in c

links:

AI copilot

cursor

connect to wsl

links:

cocopilot

patch the code copilot over the common IDE.

links:

open-interpreter

links:

numpy


numpy 是一个 Python 库,用于处理多维数组和矩阵,以及用于数学计算的函数集合。它是 Python 科学计算的核心库之一,因为它提供了高效的数据结构和计算工具,使得 Python 成为了一种流行的科学计算语言。

Tips

增删维度

图片数据经常 batch 处理,此时的维度为 [batch, c, h, w][batch, h, w, c],为适配输入输出,需要增加和删除 batch

1
2
3
4
5
image_origin = np.from_file(file, np.uint8)
# 增加维度
with_batch = np.expand_dims(image_origin, axis=0)
# 删除维度
without_batch = np.squeeze(with_batch, axis=0)

NHWC to NCHW

1
2
padding_img = padding_img.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
padding_img = np.ascontiguousarray(padding_img)

pandas

api

quantile

使用 quantile 函数计算分数位数. quantile 计算的原理是通过对数据进行排序,找到给定百分位数位置的值。具体来说,对于一个已排序的数据集,quantile 函数会根据指定的百分位数来确定在该百分位数位置处的值。

例如,对于一个包含 100 个数据点的数据集,如果想要计算 p99(百分之 99 的数据点的值)即 DataFrame.quantile(0.99),需要找到位于排好序的数据集中第 99 个百分位数位置的值。

quantile 函数的计算方式可以有多种方法,其中一种常用的方法是线性插值法。该方法首先计算出百分位数位置的整数部分索引和小数部分的权重,并根据权重对相邻的数据点进行插值计算,以得出最终的百分位数值。

ONNX

OpenCV


安装

1
apt-get update && apt-get install -y --no-install-recommends libgl1 && pip install opencv-python opencv-python-headless

[!TIP]
Error: ImportError: libGL.so.1: cannot open shared object file: No such file or directory.

Solution: pip install opencv-python opencv-python-headless.

Tip

jpg to numpy

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np
import cv2
from Pillow import PIL # need install pillow

raw_img_np = np.fromfile(file_path, dtype="uint8")
# convert from Pillow.Image.open
image_p = PIL.Image.open(io.BytesIO(raw_img_np.tobytes()))
tensor = np.array(image_p)
# convert from cv2.imdecode
tensor = cv2.imdecode(raw_img_np, cv2.IMREAD_COLOR)
# read from cv2.imread
tensor = cv2.imread(file_path)

GPT

资源

资源 组织 类型
西鲸 AI 智脑 西鲸 文档
chat.dfehub 西鲸 rest api
freegpt-webui ramonvc freegpt web app
h2ogpt h2oai private gpt server
quivr StanGirard AI Brain
gitcommit zurawiki git commit tools

实用工具

浏览器 Edge/Chrome

  • ChatGPT Sidebar

注册工具

Stable Diffusion

AI 文字转图片工具使用教程.

Stable Diffusion AI models 下载。这里有推荐的模型及软件下载链接.

gptcommit

gptcommit 自动根据代码更改生成 commit 消息.

[!TIP]
可以直接使用 vscode 插件集成 vscode-gptcommit

prompt

随着 ChatGPT 带来的 AI 大模型的技术突破,也带来了一项新的 AI 工程技术,即 prompt (用于规则化提问,生成预计业务逻辑回答). 这项技术让 AI 从对话中的生成回答能够真正地落地于其它应用工程。比如前面提到的 gptcommit, gpt-migrate 等都是对 prompt 的应用.

[!NOTE]
prompt: 简单来说就是将问题模板化,进而生成预期结果.

示例:

AI Workflow

通常的 AI 工作流如下:

graph LR
    subgraph ETL/ML Engineer
        AI-Train[AI Training] -->|Trained Models| Model-Optimization[Model-Optimization]
        Model-Optimization -->|Optimized Models| Model-Storage[(Model-Storage)]
    end
    subgraph Dev/ML Ops
        Model-Storage -->|Deploy| Inference-Serving[Inference-Serving]
    end
    subgraph App Developer
        Inference-Serving <-->|Query/Result| Application[Application]
    end

AI Serving

服务化的需求:

  • 可扩展 pytorch、tensorflow、onnx 等类型模型的推理。

  • 能够支持 http grpc 通信。

  • 基于 pipline 处理流程,方便扩展流水线。

  • 能够支持高并发处理。

  • 可参考 triton 服务,调研其他框架。

理想方案

  • mode inference -> kserve {http/grpc} + [ 业务代码服务 { flask } + [ dapr ] ]。

  • 业务代码 inference -> kserve {http/grpc} 。

现有方案:

  • 多模型同一个容器运行。

  • 提供 http 请求。

其它方案 demo 或技术:

AI Serving Solution

API 服务工具

Torch Serving、Tensor Serving 和 Triton Serving 都是用于将机器学习模型部署为 API 服务的工具。它们都提供了类似的功能,例如模型加载、推理请求处理和模型版本控制等。

然而,它们之间也存在一些区别。以下是一些了解到的区别:

  • Torch Serving 是由 PyTorch 社区开发的,专门用于部署 PyTorch 模型。它支持多种模型格式,例如 TorchScript 和 ONNX 等。

  • Tensor Serving 是由 Google 开发的,专门用于部署 TensorFlow 模型。它支持 TensorFlow SavedModel 格式。

  • Triton Serving 是由 NVIDIA 开发的,专门用于部署深度学习模型。它支持多种模型格式,例如 TensorFlow、PyTorch 和 ONNX 等。

  • ONNX Runtime:ONNX Runtime 是一个用于部署 ONNX 模型的高性能、跨平台的推理引擎。它支持多种硬件平台和操作系统,包括 CPU、GPU、FPGA 和 Edge 设备等。可以使用 ONNX Runtime 来加载 ONNX 模型并提供服务,同时还可以使用其提供的 API 来管理模型版本、优化推理性能等。

因此,虽然这些工具都提供了类似的功能,但它们的实现方式和支持的模型格式可能会有所不同。

AI Dev 辅助框架

  • Airflow: 用于使用编程的方式实现开发、调度和监控面向批处理的工作流。

  • Dapr: Dapr 是一个开源的分布式应用程序运行时,它提供了一组构建块,用于简化应用程序的开发和部署。其中包括 Service Invocation、State Management、Pub/Sub 等功能,可以帮助快速构建和部署 AI 服务。Dapr 还提供了多种通信协议和 SDK,方便与其他服务进行交互。

  • Kubeflow: Kubeflow 是一个用于部署机器学习工作流的开源平台。它基于 Kubernetes 构建,提供了一组工具和 API,用于管理模型训练、推理、部署等过程。可以使用 Kubeflow 来构建端到端的机器学习工作流,同时还可以使用其提供的组件来部署和管理 AI 服务。

  • kServe: 是一个用于构建和部署机器学习模型的开源框架。它提供了一个简单易用的 API,可以帮助快速构建和部署机器学习模型。kServe 支持多种模型格式,包括 TensorFlow、PyTorch、ONNX 等。它还提供了多种部署方式,例如 HTTP、gRPC 等,可以满足不同场景下的需求。以下是一些有用的链接:

  • TAO Toolkit:是一个用于训练和部署深度学习模型的工具包,它提供了一系列的工具和库,可以帮助用户快速构建和部署深度学习模型。在使用 TAO Toolkit 之前,需要先了解深度学习的基本概念和使用方法。可以参考以下文档:

  • Chassis: 是一个开源项目,可以在几分钟内将 ML 模型转换为容器化的预测 API。

TensorFlow Serving

能否给一个使用 TensorFlow Serving 将机器学习模型部署为服务的示例

想要了解如何使用 TensorFlow Serving 将机器学习模型部署为服务。以下是一个使用 TensorFlow Serving 调用 TensorFlow 模型的示例:

  • 首先,需要安装 TensorFlow Serving。可以使用以下命令在 Linux 上安装 TensorFlow Serving:

1
pip install tensorflow-serving-api
  • 然后,需要启动 TensorFlow Serving。可以使用以下命令启动 TensorFlow Serving:

1
tensorflow_model_server --port=8500 --rest_api_port=8501 --model_name=my_model --model_base_path=path/to/your/model

其中,my_model 是模型的名称,path/to/your/model 是模型存储路径。

  • 接下来,需要使用 TensorFlow Client 调用模型。以下是一个使用 TensorFlow Client 调用模型的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import json
import numpy as np

# 定义输入数据
input_data = {"instances": [{"input": np.random.rand(1, 28, 28, 1).tolist()}]}

# 发送请求
response = requests.post(
"http://localhost:8501/v1/models/my_model:predict", json=input_data
)

# 解析响应
output_data = json.loads(response.content.decode("utf-8"))

# 获取输出张量
output_tensor = np.array(output_data["predictions"])

# 处理输出张量

其中,my_model 是模型的名称。

Torch Serving

能否给一个使用 Torch Serving 将机器学习模型部署为服务的示例

想要了解如何使用 Torch Serving 将机器学习模型部署为服务。以下是一个使用 Torch Serving 部署 PyTorch 模型的示例:

  • 首先,需要安装 Torch Serving。可以使用以下命令在 Linux 上安装 Torch Serving:

1
pip install torchserve torch-model-archiver
  • 接下来,需要将 PyTorch 模型转换为 TorchScript 格式。可以使用以下代码将 PyTorch 模型转换为 TorchScript:

1
2
3
4
5
6
7
8
9
import torch

# 加载 PyTorch 模型
model = torch.load("path/to/your/model.pth")

# 将模型转换为 TorchScript
example_input = torch.rand(1, 3, 224, 224)
traced_script_module = torch.jit.trace(model, example_input)
traced_script_module.save("path/to/your/model.pt")
  • 然后,需要创建一个模型描述文件。该文件描述了模型的输入和输出格式。以下是一个示例模型描述文件:

1
2
3
4
5
6
{
"input_shape": [1, 3, 224, 224],
"output_shape": [1, 1000],
"mean": [0.485, 0.456, 0.406],
"std": [0.229, 0.224, 0.225]
}
  • 接下来,需要使用 Torch Model Archiver 将模型打包为 Torch Serving 可以使用的格式。可以使用以下命令将模型打包:

1
torch-model-archiver --model-name my_model --version 1.0 --serialized-file path/to/your/model.pt --handler path/to/your/handler.py --extra-files path/to/your/model_description.json --export-path model_store

其中,my_model 是模型的名称,1.0 是模型的版本号,path/to/your/model.pt 是 TorchScript 模型的路径,path/to/your/handler.py 是模型处理程序的路径,path/to/your/model_description.json 是模型描述文件的路径,model_store 是要导出模型的路径。

  • 最后,可以使用以下命令启动 Torch Serving:

1
torchserve --start --model-store model_store --models my_model=your_model.mar

其中,model_store 是模型存储路径,my_model 是模型的名称,your_model.mar 是模型的 Torch Serving 格式的文件。

这就是使用 Torch Serving 将 PyTorch 模型部署为服务的示例。

  • 接下来,需要使用 Torch Client 调用模型。以下是一个使用 Torch Client 调用模型的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests
import json
import base64

# 定义输入数据
input_data = {
"data": base64.b64encode(open("path/to/your/image.jpg", "rb").read()).decode(
"utf-8"
),
"content_type": "application/octet-stream",
}

# 发送请求
response = requests.post("http://localhost:8080/predictions/my_model", json=input_data)

# 解析响应
output_data = json.loads(response.content.decode("utf-8"))

其中,path/to/your/image.jpg 是输入图像的路径,my_model 是模型的名称。

  • 最后,可以使用 output_data 变量中的结果。结果的格式取决于模型的输出格式。例如,如果模型输出一个张量,可以使用以下代码获取该张量:

1
2
3
4
5
6
import numpy as np

# 获取输出张量
output_tensor = np.array(output_data["predictions"])

# 处理输出张量

这就是使用 Torch Serving 调用 PyTorch 模型的示例。

Triton Serving

如何使用 Triton Serving 部署 AI 服务

为了使用 Triton Serving 部署 AI 服务,可以按照以下步骤进行操作:

1
tritonserver --model-repository=/path/to/model/repository

将 /path/to/model/repository 替换为模型存储库的路径。

  • 服务器运行后,可以使用 Triton Serving 客户端库将推理请求发送到服务器。Triton Serving 支持多种客户端库,包括 Python、C++ 和 Java。可以在 Triton Serving 文档中找到有关如何使用客户端库的更多信息:https://github.com/triton-inference-server/client

Airflow

如何用 Airflow 框架实现模型常驻在内存的 AI 服务

首先,Airflow 框架主要用于调度和监控数据处理任务,而不是用于实现 AI 服务。但是,可以使用 Airflow 来调度和监控运行 AI 服务的任务。

要实现模型常驻在内存的 AI 服务,需要使用一个 Web 框架(如 Flask 或 Django)来创建一个 API,该 API 将加载模型并接收输入数据以进行预测。然后,可以使用 Airflow 来调度和监控此 API 的运行。

以下是一些可能有用的步骤:

  • 使用 Flask 或 Django 创建一个 API,该 API 将加载模型并接收输入数据以进行预测。可以使用以下代码作为起点:

1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask, request

app = Flask(__name__)


@app.route("/predict", methods=["POST"])
def predict():
...
# Load model
# Get input data from request
# Make prediction
# Return prediction
  • 将 API 部署到服务器上,并确保它可以在后台运行。可以使用 gunicorn 或 uwsgi 等工具来运行 API。

  • 创建一个 Airflow DAG,该 DAG 将定期调用 API 以确保其保持活动状态。可以使用以下代码作为起点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from airflow import DAG
from airflow.operators.http_operator import SimpleHttpOperator
from datetime import datetime, timedelta

default_args = {
"owner": "airflow",
"depends_on_past": False,
"start_date": datetime(2021, 1, 1),
"retries": 1,
"retry_delay": timedelta(minutes=5),
}

dag = DAG(
"ai_service",
default_args=default_args,
schedule_interval=timedelta(minutes=30),
)

predict_task = SimpleHttpOperator(
task_id="predict",
method="POST",
http_conn_id="my_api",
endpoint="/predict",
headers={"Content-Type": "application/json"},
data='{"input": "data"}',
dag=dag,
)
  • 将 Airflow DAG 部署到 Airflow 服务器上,并确保它可以在后台运行。

Airflow 能否手动配置运行这个 AI 服务的数量

Airflow 本身并不提供手动配置运行任务的数量的功能。但是,可以通过修改 DAG 的 scheduleinterval 参数来控制任务的运行频率,从而间接地控制任务的数量。例如,将 scheduleinterval 设置为 timedelta (minutes=1) 将使任务每分钟运行一次,从而增加任务的数量。

另外,可以使用 Airflow 的任务并发设置来控制同时运行的任务数量。可以在 Airflow 的配置文件中设置 maxactiverunsperdag 参数来限制每个 DAG 同时运行的任务数量。例如,将 maxactiverunsperdag 设置为 2 将使每个 DAG 最多同时运行 2 个任务。

请注意,这些设置可能会影响服务器资源使用情况,因此需要根据具体需求进行调整。

Dapr

如何用 Dapr 框架实现模型常驻在内存的 AI 服务

首先,Dapr 是一个开源的微服务框架,它可以帮助开发人员构建分布式应用程序。它提供了一些有用的功能,例如状态管理,消息传递和服务发现。但是,它本身并不是一个 AI 框架,因此需要使用其他 AI 框架来实现模型常驻在内存的 AI 服务。

一种常见的方法是使用 TensorFlow Serving。TensorFlow Serving 是一个用于生产环境的 TensorFlow 模型服务器,它可以轻松地将 TensorFlow 模型部署到生产环境中。可以使用 Dapr 框架来管理 TensorFlow Serving 实例,并使用 Dapr 的状态管理功能来存储模型的元数据和状态。

以下是一个使用 Dapr 和 TensorFlow Serving 实现模型常驻在内存的 AI 服务的示例:

  • 首先,需要安装 Dapr 和 TensorFlow Serving。可以按照官方文档进行安装。

  • 然后,需要编写一个 Dapr 组件来管理 TensorFlow Serving 实例。可以在 Dapr 配置文件中定义此组件,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: tensorflow-serving
spec:
type: external
metadata:
- name: grpcPort
value: "8500"
- name: restPort
value: "8501"
version: v1
description: "TensorFlow Serving component"
status:
address: "localhost:8500"
  • 接下来,需要编写一个 Dapr 应用程序来使用 TensorFlow Serving 组件。可以使用 Dapr 的状态管理功能来存储模型的元数据和状态。以下是一个使用 Python 编写的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import dapr.clients
import tensorflow as tf

# Initialize Dapr client
with dapr.clients.DaprClient() as d:
# Load TensorFlow model
model = tf.keras.models.load_model("path/to/model")

# Register model with TensorFlow Serving
response = d.invoke_service(
"tensorflow-serving",
"v1/models/model:predict",
data=model.to_json(),
metadata={"content-type": "application/json"},
)

# Store model metadata and state in Dapr state store
d.save_state(
"model-metadata",
{
"model_name": "model",
"model_version": "1",
"model_input_shape": model.input_shape,
},
)
d.save_state("model-state", {"status": "running"})

在此示例中,首先使用 Dapr 客户端加载 TensorFlow 模型。然后,使用 Dapr 客户端调用 TensorFlow Serving 组件,并将模型注册到 TensorFlow Serving 中。最后,使用 Dapr 客户端将模型的元数据和状态存储在 Dapr 状态存储中。

已经使用 Dapr 客户端加载了 TensorFlow 模型,并将其注册到 TensorFlow Serving 中。要调用加载的模型,可以使用 Dapr 客户端调用 TensorFlow Serving 组件的 REST API。

以下是一个使用 Python 编写的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import dapr.clients

# Initialize Dapr client
with dapr.clients.DaprClient() as d:
# Call TensorFlow Serving REST API
response = d.invoke_binding(
"tensorflow-serving",
"rest",
data={"instances": [[1.0, 2.0, 3.0, 4.0]]},
metadata={"content-type": "application/json"},
)

# Process response
print(response.content)

在此示例中,使用 Dapr 客户端调用 TensorFlow Serving 组件的 REST API,并将输入数据作为 JSON 格式的数据发送。可以根据自己的需求修改输入数据的格式和内容。最后,打印出 TensorFlow Serving 返回的响应。

Dapr 能否手动配置运行上面提到的 AI 服务数量

Dapr 是一个开源的分布式应用程序运行时,它可以帮助开发人员轻松地构建和运行微服务应用程序。Dapr 提供了一种简单的方式来注册和发现服务,并提供了一组 API 来调用这些服务。在使用 Dapr 和 TensorFlow Serving 实现模型常驻在内存的 AI 服务的基础上,可以使用 Dapr 客户端调用 TensorFlow Serving 组件的 REST API,或者将 TensorFlow Serving 组件注册为 Dapr 服务并使用 Dapr 客户端调用其 gRPC API。

关于 Dapr 运行的 AI 服务数量的配置,Dapr 提供了一种称为 “Actor” 的编程模型,它可以帮助开发人员轻松地构建高度可扩展的应用程序。Actor 是一种轻量级的计算单元,它可以自动缩放和分区,以处理大量的并发请求。可以使用 Dapr 的 Actor 模型来管理和配置 Dapr 运行的 AI 服务数量。

具体来说,可以使用 Dapr 的 Actor 模型来创建一个名为 “AI 服务” 的 Actor 组,该组可以包含多个 AI 服务 Actor 实例。然后,可以使用 Dapr 客户端调用 Actor 组的 API 来创建、删除和管理 AI 服务 Actor 实例。通过这种方式,可以手动配置 Dapr 运行的 AI 服务数量,并根据需要动态缩放。

能否将 tensorflow serving 替换为 torch serving

关于将 TensorFlow Serving 替换为 Torch Serving,Dapr 并不限制使用哪种 AI 服务组件。只要 AI 服务组件实现了 Dapr 所需的 API,就可以将其注册为 Dapr 组件,并使用 Dapr 客户端调用其 API。因此,可以使用 Torch Serving 替换 TensorFlow Serving,并使用 Dapr 客户端调用其 API。

具体来说,需要将 Torch Serving 组件实现为 Dapr 组件,并将其注册到 Dapr 运行时中。然后,可以使用 Dapr 客户端调用 Torch Serving 组件的 API,就像在上述示例中使用 Dapr 客户端调用 TensorFlow Serving 组件的 API 一样。

TensorFlow Serving 和 Torch Serving 的区别

TensorFlow Serving 和 Torch Serving 都是用于将机器学习模型部署为可用于生产环境的 API 的工具。它们的主要区别在于它们支持的框架不同。

TensorFlow Serving 是专门为 TensorFlow 模型设计的,它提供了一个高效的、可扩展的方式来部署 TensorFlow 模型。它支持多种模型格式,包括 SavedModel、SessionBundle 和 TensorFlow Hub 模块。TensorFlow Serving 还提供了一些高级功能,例如模型版本控制、模型热更新和模型分片。

Torch Serving 是专门为 PyTorch 模型设计的,它提供了一个轻量级的、可扩展的方式来部署 PyTorch 模型。它支持多种模型格式,包括 TorchScript 和 ONNX。Torch Serving 还提供了一些高级功能,例如模型版本控制、模型热更新和模型分片。

总的来说,TensorFlow Serving 和 Torch Serving 都是非常有用的工具,可以帮助将机器学习模型部署到生产环境中。选择哪个工具取决于使用的框架以及具体需求。

AI serving app

flask app

实现功能:将 AI 服务的图片预测和输出显示到 web 页面

[!NOTE]

  • 图片保存在本地子文件夹 .predictions 中以请求检测时间保存在对应子目录下。
  • 提供一个接口返回本地子文件夹.predictions 中的目录列表。
  • 返回页面增加以下 html 样式,方面人查询 predictions 中的图片结果。

为了将 AI 服务的图片预测和输出显示到 web 页面,需要使用一些 web 开发技术。以下是一些可能有用的步骤:

  • 创建一个 web 应用程序,该应用程序将接收用户上传的图像并将其发送到 AI 服务进行预测。可以使用任何 web 框架来实现这一点,例如 Flask 或 Django。

  • 在 web 应用程序中,使用 AI 服务客户端库将图像发送到 AI 服务进行预测。可以使用 Triton Serving 客户端库来实现这一点。有关如何使用 Triton Serving 客户端库的更多信息,请参见以下链接:https://github.com/triton-inference-server/client

  • 将 AI 服务的预测结果返回到 web 应用程序,并将其显示在 web 页面上。可以使用 HTML 和 JavaScript 来实现这一点。例如,可以使用 JavaScript 将预测结果显示在一个图像元素中。

以下是一个可能有用的代码示例,它演示了如何使用 Flask 和 Triton Serving 客户端库将图像发送到 AI 服务进行预测,并将预测结果显示在 web 页面上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import io
import os
import pathlib
import datetime
import sys

from PIL import Image
import yolov5_utils # pip install yolov5-utils
from flask import Flask, request, render_template, send_file # pip install flask

app = Flask(__name__)


ROOT = pathlib.Path((os.path.dirname(__file__)))
PREDICTIONS = ROOT / ".predictions" # 预测保存文件夹
DATE_FORMAT = "%Y-%m-%d_%H-%M-%S" # 保存结果在子文件夹日期名称
LIST_RENDER_FILE = "index.html" # web 返回渲染页面


def get_det_history_list():
"""获取 PREDICTIONS 下的所有预测结果,并按日期最新,从左到右返回"""
return [
os.path.relpath(p, PREDICTIONS)
for p in sorted(PREDICTIONS.glob("**/*"), reverse=True)
if p.suffix in {".jpg", ".png"}
]


@app.route("/infer", methods=["POST"])
def infer():
"""推理接口, 只接受POST 请求,并将结果保存在 PREDICTIONS 下,并返回推理结果"""
data = request.files["image"].read()
sub = datetime.datetime.now().strftime(DATE_FORMAT)
results = model(Image.open(io.BytesIO(data)), size=PREDICT_IMAGE_SIZE)
results.save(save_dir=PREDICTIONS / sub)

return render_template(
LIST_RENDER_FILE,
prediction=results,
image_list=get_det_history_list(),
)


@app.route("/")
def index():
"""默认的 web 页面,返回检测历史结果"""
return render_template(
LIST_RENDER_FILE,
image_list=get_det_history_list(),
)


@app.route("/display/<path:filename>")
def display(filename):
"""定于回调展示检测结果的图片

Args:
filename: 是 PREDICTIONS 下的图片的路径.(包括图片名)
"""
return send_file(os.path.join(PREDICTIONS, filename), mimetype="image/jpeg")


def parse_args():
import argparse

parser = argparse.ArgumentParser()
parser.add_argument(
"--model_url",
type=str,
default="yolov5m.pt",
help="Triton model url such as http://localhost:8000/v2/models/yolov5,or yolov5 family name likes yolov5m.pt",
)
parser.add_argument(
"--imgsz",
type=int,
default=1280,
help="The size of the image is detected after preprocessing",
)
return parser.parse_args()


if __name__ == "__main__":
FLAGS = parse_args()
PREDICT_IMAGE_SIZE = FLAGS.imgsz
model = yolov5_utils.load(FLAGS.model_url) # 加载 yolov5 模型
app.run(host="0.0.0.0", port=5000, debug=False if sys.gettrace() else True)

这个示例使用 Flask 框架创建了一个 web 应用程序,当用户上传图像时,它将图像发送到 Triton Serving 进行预测,并将预测结果显示在 web 页面上。

创建了一个新的 HTML 模板 index.html 在 templates 目录下,它显示了 .predictions 子文件夹中的目录列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<!-- index.html -->
<!DOCTYPE html>
<html>

<head>
<title>Image Detection</title>
<style>
.image {
max-width: 100%;
max-height: 100%;
display: block;
margin: auto;
}

.image-list {
display: flex;
flex-wrap: wrap;
}
</style>
</head>

<body>
<h1>Image Detection Input</h1>
<!-- 提交时将数据使用POST方法路由到 /infer -->
<form method="POST" enctype="multipart/form-data" action="/infer">
<input type="file" name="image" accept="image/*" required>
<button type="submit">Submit</button>
</form>

<!-- 展示当前检测结果, 使用render_template传入的 prediction 变量展示 -->
{% if prediction is defined %}
<h2>Prediction: {{ prediction }}</h2>
<div class="image-list">
<img class="image" src="{{ url_for('display', filename=image_list[0] ) }}">
</div>
{% endif %}

<!-- 展示历史检测结果 -->
{% if image_list is defined and image_list %}
<h2>Detection History:</h2>
<div>
{% for file in image_list %}
<div>
<li>
<a href="/display/{{ file }}">{{ file }}</a>
</li>
</div>
{% endfor %}
</div>
{% endif %}

</body>

</html>

Kubeflow

install

KServe

Quickstart

modelmesh installing

Demo Test

1
2
3
4
5
6
7
# Minikube
export INGRESS_HOST=$(minikube ip)
# Other environment(On Prem)
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export SERVICE_HOSTNAME=$(kubectl get inferenceservice sklearn-iris -n kserve-test -o jsonpath='{.status.url}' | cut -d "/" -f 3)
curl -v -H "Host: ${SERVICE_HOSTNAME}" "http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/sklearn-iris:predict" -d @./iris-input.json
  • kiali
    安装 istioctl

1
2
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.15/samples/addons/kiali.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.15/samples/addons/prometheus.yaml

TAO

NVIDIA TAO Toolkit 是 Nvidia 构建的迁移学习框架,能将现有的或合成数据使用预训练的模型高效地训练出自定义的模型,官方介绍

TAO Toolkit API

CV

ClearML

Deploy

ClearML

ClearML 是一个开源的平台,用于管理和跟踪机器学习实验。它提供了一个集成的界面,可以帮助用户管理数据集、模型、超参数和实验结果。此外,ClearML 还提供了自动化的模型选择和调优功能,可以帮助用户快速构建和部署机器学习模型。

如果想了解更多关于 ClearML 的信息,可以参考以下文档:

pytorch

Model construct strategy

在 PyTorch 中构建模型的一般步骤如下:

  • 定义模型类:定义一个继承自 nn.Module 的类,该类将包含模型的结构和参数。

  • 定义前向传递函数:在模型类中定义一个 forward 函数,该函数将定义模型的前向传递逻辑。

  • 定义损失函数:选择适当的损失函数,例如交叉熵损失函数或均方误差损失函数。

  • 定义优化器:选择适当的优化器,例如随机梯度下降(SGD)或 Adam 优化器。

  • 训练模型:使用训练数据对模型进行训练,通过反向传播算法更新模型参数。

  • 评估模型:使用测试数据对模型进行评估,计算模型的准确率或其他性能指标。

下面是一个简单的示例,展示如何在 PyTorch 中构建一个简单的全连接神经网络模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import torch
import torch.nn as nn


class SimpleNet(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size) # 输入层
self.relu = nn.ReLU() # 隐藏层
self.fc2 = nn.Linear(hidden_size, num_classes) # 输出层

def forward(self, x):
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
return out


# 定义模型参数
input_size = 784 # 输入大小为28x28
hidden_size = 500
num_classes = 10

# 实例化模型
model = SimpleNet(input_size, hidden_size, num_classes)

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

# 训练模型
for epoch in range(num_epochs):
# 前向传递
outputs = model(inputs)
loss = criterion(outputs, labels)

# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()

# 评估模型
with torch.no_grad():
correct = 0
total = 0
for images, labels in test_loader:
images = images.reshape(-1, 28 * 28)
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()

print(
"Accuracy of the network on the 10000 test images: %d %%"
% (100 * correct / total)
)

在这个示例中,定义了一个名为 SimpleNet 的模型类,它包含一个输入层、一个隐藏层和一个输出层。还定义了一个前向传递函数,该函数将输入数据传递到模型中,并返回模型的输出。使用交叉熵损失函数和随机梯度下降(SGD)优化器来训练模型,并使用测试数据对模型进行评估。

Common API

tensor.equal

使用 torch.equal () 函数来判断两个 tensor 是否相等。该函数会比较两个 tensor 的形状和元素值是否相等,如果相等则返回 True,否则返回 False。

示例代码如下:

1
2
3
4
5
6
7
8
import torch

a = torch.tensor([1, 2, 3])
b = torch.tensor([1, 2, 3])
c = torch.tensor([1, 2, 4])

print(torch.equal(a, b)) # True
print(torch.equal(a, c)) # False

在上面的示例中,定义了三个 tensor,其中 a 和 b 的元素值和形状都相等,而 c 的元素值不同,因此使用 torch.equal () 函数比较 a 和 b 时返回 True,比较 a 和 c 时返回 False。

tensor.view

tensor.view 是 PyTorch 中的一个方法,用于将一个 tensor 重塑为指定的形状。它返回一个新的 tensor,而不是修改原始 tensor。这个方法在深度学习中非常常用,因为在神经网络中,需要将数据重塑为特定的形状以进行计算。

tensor.view 的语法如下:

1
view(*shape)

其中,*shape 是一个可变参数,表示要重塑的形状。例如,如果有一个形状为 (3, 4) 的 tensor,可以使用 view 将其重塑为 (4, 3) 的 tensor:

1
2
3
4
import torch

x = torch.randn(3, 4)
y = x.view(4, 3)

需要注意的是,view 方法返回的 tensor 与原始 tensor 共享数据存储空间,因此在修改其中一个 tensor 的值时,另一个 tensor 的值也会发生变化。如果要避免这种情况,可以使用 clone 方法创建一个新的 tensor。

除了 view 方法,PyTorch 还提供了一些其他的 tensor 重塑方法,例如 reshape 和 permute。这些方法的使用方式与 view 类似,但有一些细微的差别,需要根据具体情况选择合适的方法。

tensor.reshape

pytorch tensor.reshape 是 PyTorch 中用于改变张量形状的函数之一。它可以将一个张量重塑为一个新的形状,而不改变其元素的数量或值。在 PyTorch 中,张量的形状可以通过 size () 函数获取,也可以通过 reshape () 函数修改。

reshape () 函数的语法如下:

1
new_tensor = tensor.reshape(*shape)

其中,tensor 是要重塑的张量,shape 是一个整数元组,表示新张量的形状。新张量的元素数量必须与原张量的元素数量相同,否则会引发错误。

例如,将一个形状为 (3, 4) 的张量重塑为一个形状为 (4, 3) 的张量,可以使用以下代码:

1
2
3
4
5
6
7
8
9
10
import torch

# 创建一个形状为 (3, 4) 的张量
tensor = torch.randn(3, 4)

# 将张量重塑为形状为 (4, 3) 的新张量
new_tensor = tensor.reshape(4, 3)

# 打印新张量的形状
print(new_tensor.size()) # 输出: torch.Size([4, 3])

需要注意的是,reshape () 函数返回的是一个新的张量,而不是修改原始张量。如果需要修改原始张量的形状,可以使用 view () 函数。view () 函数与 reshape () 函数的功能相同,但是它只能用于连续的内存块,而 reshape () 函数可以用于任意张量。

tensor.permute

tensor.permute () 是 PyTorch 中的一个函数,用于对张量进行维度重排。它可以用于将张量从一种格式转换为另一种格式,例如从 NHWC 格式转换为 NCHW 格式。

tensor.permute () 的参数是一个整数列表,用于指定新张量的维度顺序。例如,如果原始张量的维度顺序为 (batch_size, height, width, channels),而新张量的维度顺序为 (batch_size, channels, height, width),则可以使用以下代码进行转换:

1
2
3
4
import torch

# Convert from NHWC to NCHW with torch.Tensor.permute
tensor_nchw = tensor_nhwc.permute(0, 3, 1, 2)

在这里,tensor_nhwc 是一个形状为 (batch_size, height, width, channels) 的张量。tensor_nchw 是一个形状为 (batch_size, channels, height, width) 的张量,其中通道维度在第二个维度。tensor_nchw.permute (0, 3, 1, 2) 将张量的维度重新排列,以将通道维度移动到第二个维度。

需要注意的是,tensor.permute () 返回的是一个新的张量,而不是修改原始张量。因此,如果需要在原始张量上进行修改,可以使用 tensor.transpose () 函数。tensor.transpose () 的参数是一个整数列表,用于指定新张量的维度顺序。例如,如果需要将张量的前两个维度交换,则可以使用以下代码:

1
2
3
4
import torch

# Swap the first two dimensions with torch.Tensor.transpose
tensor_swapped = tensor.transpose(0, 1)

在这里,tensor 是一个形状为 (dim1, dim2, …) 的张量。tensor_swapped 是一个形状为 (dim2, dim1, …) 的张量,其中原始张量的前两个维度已经交换。

总之,tensor.permute () 和 tensor.transpose () 都是用于对张量进行维度重排的函数,可以根据需要选择使用。

tensor.transpose

transpose 是 PyTorch 中的一个函数,用于对张量进行转置操作。它可以接受一个元组作为参数,表示需要交换的维度。例如,对于一个形状为 (3, 4, 5) 的张量,可以使用 transpose (0, 2, 1) 将第一维和第三维交换,得到一个形状为 (5, 4, 3) 的张量。

下面是一个示例代码,展示了如何使用 transpose 函数:

1
2
3
4
5
6
7
8
9
10
import torch

# 创建一个形状为 (3, 4, 5) 的张量
x = torch.randn(3, 4, 5)

# 对张量进行转置操作
y = x.transpose(0, 2, 1)

# 打印转置后的张量形状
print(y.shape)

输出结果为 (5, 4, 3),表示转置后的张量形状为 (5, 4, 3)。

需要注意的是,transpose 函数返回的是一个新的张量,不会修改原始张量。如果需要在原始张量上进行转置操作,可以使用 transpose_ 函数。

tensor.topk

使用 tensor.topk 函数可以返回张量中前 k 个最大值及其对应的索引。该函数的语法如下:

1
torch.topk(input, k, dim=None, largest=True, sorted=True, out=None) -> Tuple[Tensor, LongTensor]

其中,参数含义如下:

  • input:输入张量。

  • k:需要返回的最大值的个数。

  • dim:沿着哪个维度计算,默认为最后一个维度。

  • largest:如果为 True,则返回最大的 k 个值;如果为 False,则返回最小的 k 个值。

  • sorted:如果为 True,则返回的 k 个值是有序的;如果为 False,则返回的 k 个值是无序的。

  • out:输出张量。

下面是一个简单的示例:

1
2
3
4
5
6
import torch

x = torch.tensor([1, 3, 2, 4, 5])
values, indices = torch.topk(x, k=3)
print(values) # tensor([5, 4, 3])
print(indices) # tensor([4, 3, 1])

在这个示例中,创建了一个张量 x,然后使用 torch.topk 函数返回了 x 中前 3 个最大值及其对应的索引。输出结果中,values 是一个包含前 3 个最大值的张量,indices 是一个包含前 3 个最大值对应的索引的张量。

tensor.softmax

tensor.softmax 是 PyTorch 中的一个函数,用于计算张量的 softmax 函数。softmax 函数是一种常用的激活函数,通常用于多分类问题中,将输出的原始分数转换为概率分布。softmax 函数的公式如下:

$$
\text{softmax}(x_i) = \frac{e{x_i}}{\sum_{j=1}{n} e^{x_j}}
$$

其中,$x_i$ 表示输入张量的第 $i$ 个元素,$n$ 表示张量的长度。

在 PyTorch 中,可以使用 torch.tensor.softmax 函数计算张量的 softmax 函数。该函数的语法如下:

1
torch.tensor.softmax(input, dim=None, dtype=None)

其中,input 表示输入的张量,dim 表示计算 softmax 函数的维度,dtype 表示输出的数据类型。如果 dim 参数未指定,则默认计算最后一维的 softmax 函数。

下面是一个示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch

x = torch.tensor([1.0, 2.0, 3.0])
y = torch.tensor([[-1.0, 0.0, 1.0], [2.0, 3.0, 4.0]])

# 计算一维张量的 softmax 函数
x_softmax = torch.tensor.softmax(x)
print(x_softmax)

# 计算二维张量的 softmax 函数
y_softmax = torch.tensor.softmax(y, dim=1)
print(y_softmax)

# tensor([0.0900, 0.2447, 0.6652])
# tensor([[0.0900, 0.2447, 0.6652],
# [0.0900, 0.2447, 0.6652]])

在上面的示例代码中,分别计算了一个一维张量和一个二维张量的 softmax 函数,并输出了计算结果。

torch.no_gard

在深度学习中,torch.no_grad () 是一个上下文管理器,用于控制是否计算梯度。当不需要计算梯度时,可以使用它来提高代码的执行效率和节省内存。

在训练模型时,通常会使用反向传播算法计算梯度,并使用梯度来更新模型的参数。然而,在推理阶段或者评估模型性能时,通常不需要计算梯度,只需要使用模型进行前向传播,以获得预测结果。

torch.no_grad () 的作用就是告诉 PyTorch 不要计算梯度,从而避免不必要的计算和内存消耗。通过在代码中使用 torch.no_grad (),可以明确地指示 PyTorch 不要跟踪梯度信息,从而提高代码的执行效率。

1
2
with torch.no_gard():
...

[!NOTE]
torch>=1.9.0 中已将 torch.no_gard 替换为 torch.inference_mode

torch.matmul

以下给出一个公式的示例:

1
output = i*weight0* weight^T

output = iweight0 weight^T 表示的是一个线性变换的过程,其中 i 是输入张量,weight0 和 weight 是权重矩阵。在这个公式中,weight0 和 weight 的乘积可以看作是一个新的权重矩阵,可以将其记为 new_weight,即 new_weight = weight0 weight^T。那么原始的公式可以改写为 output = i new_weight。

如果想要优先计算 new_weight,可以先将 weight0 和 weight 相乘得到 new_weight,然后再将 i 与 new_weight 相乘得到输出张量 output。这样做是合理的,因为矩阵乘法具有结合律,即 (A B) C = A (B C),所以先计算 weight0 weight^T 得到 new_weight,再计算 i new_weight 得到 output,与直接计算 i weight0 weight^T 得到 output 是等价的。

以下是一个示例代码,演示如何先计算 new_weight,然后再计算 output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch

# 定义输入张量 i,形状为 [batch_size, input_size]
i = torch.randn(32, 10)

# 定义权重矩阵 weight,形状为 [output_size, input_size]
weight = torch.randn(5, 10)

# 定义权重矩阵 weight0,形状为 [output_size, output_size]
weight0 = torch.randn(5, 5)

# 计算 new_weight,形状为 [output_size, input_size]
new_weight = torch.matmul(weight0, weight.transpose(0, 1))

# 计算 output,形状为 [batch_size, output_size]
output = torch.matmul(i, new_weight)

在这个例子中,先计算了 new_weight,然后再计算了 output。注意,在计算 new_weight 时,需要将 weight 的维度转置,以便与 weight0 相乘。最后,使用 PyTorch 中的 torch.matmul 函数进行矩阵乘法运算。

nn.Linear

nn.Linear 是 PyTorch 中的一个线性层,它实现了一个线性变换,将输入张量与权重矩阵相乘并加上偏置向量。它的定义如下:

1
2
3
4
5
6
7
8
9
10
class Linear(Module):
def __init__(
self,
in_features: int,
out_features: int,
bias: bool = True,
device=None,
dtype=None,
) -> None:
...

在深度学习中,线性变换层是非常常见的一种操作,它可以将输入张量映射到一个新的空间中,从而实现特征的提取和转换。nn.Linear 的实现原理非常简单,它实际上就是一个矩阵乘法加上一个偏置向量的过程,可以用以下公式表示:

1
output = input * weight^T + bias

其中,input 是输入张量,weight 是权重矩阵,bias 是偏置向量,^T 表示矩阵的转置操作。

其中,in_features 表示输入张量的大小,out_features 表示输出张量的大小,bias 表示是否使用偏置向量。在实际使用中,可以通过调用 nn.Linear 的实例对象来进行线性变换,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch.nn as nn

# 定义一个线性层,输入大小为 10,输出大小为 5
linear = nn.Linear(10, 5)

# 定义一个输入张量,大小为 (batch_size, 10)
input_tensor = torch.randn(32, 10)

# 进行线性变换
output_tensor = linear(input_tensor)

# 输出张量的大小为 (batch_size, 5)
print(output_tensor.size())

在上述代码中,首先定义了一个输入大小为 10,输出大小为 5 的线性层 linear,然后定义了一个输入张量 input_tensor,大小为 (batch_size, 10),最后通过调用 linear 对输入张量进行线性变换,得到输出张量 output_tensor,大小为 (batch_size, 5)。

需要注意的是,nn.Linear 的权重矩阵和偏置向量是在初始化时随机生成的,可以通过 linear.weight 和 linear.bias 来访问它们。此外,还可以通过 nn.init 模块来初始化权重矩阵和偏置向量,例如:

1
2
3
4
5
6
7
8
9
10
11
12
import torch.nn as nn
import torch.nn.init as init

# 定义一个线性层,输入大小为 10,输出大小为 5
linear = nn.Linear(10, 5)

# 初始化权重矩阵和偏置向量
init.normal_(linear.weight, mean=0, std=0.01)
init.constant_(linear.bias, val=0)

# 进行线性变换
output_tensor = linear(input_tensor)

在上述代码中,使用 init.normal_和 init.constant_ 分别对权重矩阵和偏置向量进行了初始化。其中,init.normal_表示使用正态分布进行初始化,init.constant_ 表示使用常数进行初始化。

nn.Dropout

nn.Dropout 是 PyTorch 中的一个模块,用于在神经网络中进行 Dropout 操作。Dropout 是一种常用的正则化方法,可以有效地防止过拟合。

在神经网络中,Dropout 会随机地将一些神经元的输出置为 0,从而减少神经元之间的依赖关系,使得网络更加鲁棒。具体来说,Dropout 会在训练过程中以一定的概率 p 随机地将某些神经元的输出置为 0,而在测试过程中则不进行 Dropout 操作。

在 PyTorch 中,可以通过如下方式使用 nn.Dropout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torch.nn as nn


class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(10, 20)
self.dropout = nn.Dropout(p=0.5)
self.fc2 = nn.Linear(20, 2)

def forward(self, x):
x = self.fc1(x)
x = self.dropout(x)
x = self.fc2(x)
return x

在上述代码中,定义了一个包含两个全连接层的神经网络,并在第一个全连接层后添加了一个 Dropout 模块。在 nn.Dropout 的构造函数中,可以指定 Dropout 的概率 p。在 forward 函数中,首先将输入 x 传入第一个全连接层,然后将输出传入 Dropout 模块,最后再将 Dropout 模块的输出传入第二个全连接层。

需要注意的是,当使用 nn.Dropout 时,需要在训练过程中启用 Dropout,而在测试过程中禁用 Dropout。在 PyTorch 中,可以通过调用 model.train () 和 model.eval () 方法来切换模型的训练和测试模式。具体来说,当调用 model.train () 方法时,模型会进入训练模式,此时 Dropout 模块会生效;而当调用 model.eval () 方法时,模型会进入测试模式,此时 Dropout 模块会被禁用。

nn.Embedding

nn.Embedding 是 PyTorch 中的一个类,用于实现词嵌入(word embedding)。词嵌入是一种将文本中的单词映射到低维向量空间中的技术,它可以将单词之间的语义关系转化为向量空间中的几何关系,从而方便计算机对文本进行处理和分析。

在 PyTorch 中,可以使用 nn.Embedding 类来实现词嵌入。该类的输入是一个整数张量,表示文本中的单词序列,输出是一个浮点数张量,表示单词序列中每个单词对应的词向量。在创建 nn.Embedding 对象时,需要指定词汇表的大小和词向量的维度。例如,下面的代码创建了一个词汇表大小为 10000,词向量维度为 300 的词嵌入对象:

1
2
3
import torch.nn as nn

embedding = nn.Embedding(10000, 300)

在使用 nn.Embedding 对象时,可以将整数张量作为输入,得到对应的词向量。例如,下面的代码将一个大小为 (3, 4) 的整数张量作为输入,得到一个大小为 (3, 4, 300) 的浮点数张量,表示输入张量中每个整数对应的词向量:

1
2
3
4
5
import torch

input_tensor = torch.randint(0, 10000, (3, 4))
output_tensor = embedding(input_tensor)
print(output_tensor.shape) # torch.Size([3, 4, 300])

在实际使用中,词嵌入通常作为深度学习模型的输入层,用于将文本数据转换为向量表示。例如,在文本分类任务中,可以使用词嵌入将文本数据转换为向量表示,然后将向量输入到深度学习模型中进行分类。

nn.modules.loss.CrossEntropyLoss

nn.modules.loss.CrossEntropyLoss 是一个用于多分类问题的损失函数,它将 softmax 函数和负对数似然损失结合在一起。在训练分类模型时,通常使用交叉熵损失函数来衡量模型的预测结果与真实标签之间的差异。

在 PyTorch 中,nn.modules.loss.CrossEntropyLoss 的使用非常简单。只需要将模型的输出和真实标签传递给该函数即可。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch.nn as nn

# Define the loss function
criterion = nn.CrossEntropyLoss()

# Define the model
model = ...

# Define the optimizer
optimizer = ...

# Train the model
for inputs, labels in dataloader:
# Forward pass
outputs = model(inputs)

# Compute the loss , and shape must be
# same between outputs and labels
loss = criterion(outputs, labels)

# Backward pass
optimizer.zero_grad()
loss.backward()
optimizer.step()

在上面的代码中,outputs 是模型的输出,labels 是真实标签。将这两个参数传递给 nn.modules.loss.CrossEntropyLoss 函数,它将计算模型的预测结果和真实标签之间的交叉熵损失。然后,可以使用该损失来进行反向传播和优化。

需要注意的是,nn.modules.loss.CrossEntropyLoss 函数要求模型的输出是未经过 softmax 函数处理的原始值。因此,在模型的最后一层中,通常不会使用 softmax 函数。如果使用了 softmax 函数,那么需要在调用 nn.modules.loss.CrossEntropyLoss 函数之前将模型的输出转换为未经过 softmax 函数处理的原始值。

tensorflow

Model construct strategy

  • 准备数据集:将数据集加载到内存中,并将其划分为训练集、验证集和测试集。

  • 定义模型:使用 TensorFlow 2 中的 Keras API 定义模型架构,包括层和激活函数等。

  • 编译模型:指定损失函数、优化器和评估指标等。

  • 训练模型:使用训练集训练模型,并在验证集上进行验证。

  • 评估模型:使用测试集评估模型的性能。

  • 使用模型:使用模型进行预测或推理。

下面是一个使用 TensorFlow 2 构建简单神经网络的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import tensorflow as tf
from tensorflow.keras import layers

# 准备数据集
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = x_train.reshape(-1, 28 * 28).astype("float32") / 255.0
x_test = x_test.reshape(-1, 28 * 28).astype("float32") / 255.0

# 定义模型
model = tf.keras.Sequential(
[
layers.Dense(256, activation="relu"),
layers.Dense(128, activation="relu"),
layers.Dense(10, activation="softmax"),
]
)

# 编译模型
model.compile(
optimizer=tf.keras.optimizers.Adam(0.001),
loss=tf.keras.losses.SparseCategoricalCrossentropy(),
metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

# 训练模型
model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test))

# 评估模型
model.evaluate(x_test, y_test)

# 使用模型进行预测
predictions = model.predict(x_test[:10])

在这个示例中,使用了 TensorFlow 2 中的 Keras API 定义了一个包含 3 个全连接层的神经网络,用于对 MNIST 手写数字数据集进行分类。使用了 Adam 优化器、交叉熵损失函数和稀疏分类精度评估指标来编译模型。然后,使用训练集训练模型,并在验证集上进行验证。最后,使用测试集评估了模型的性能,并使用模型进行了预测。

Common api

tf.trainable_variables()

tf.trainable_variables () 是 TensorFlow 中的一个函数,用于获取所有可训练的变量。具体来说,它会返回一个列表,其中包含了所有需要在训练过程中更新的变量。

该函数会返回一个列表,其中包含了所有需要在训练过程中更新的变量。这些变量通常是模型中的权重和偏置等参数。在训练过程中,需要根据损失函数的梯度来更新这些变量,以使得模型能够更好地拟合训练数据。

需要注意的是,tf.trainable_variables () 只会返回需要在训练过程中更新的变量。如果一个变量不需要在训练过程中更新,那么它就不会出现在这个列表中。例如,如果一个变量是在模型中用于计算某个中间结果的,那么它就不需要在训练过程中更新,因此也不会出现在这个列表中。

在实际使用中,通常会将这个列表作为优化器的参数,以便优化器能够自动更新这些变量。例如:

1
2
3
optimizer = tf.train.AdamOptimizer(learning_rate=0.001)
trainable_vars = tf.trainable_variables()
train_op = optimizer.minimize(loss, var_list=trainable_vars)

在这个例子中,使用 Adam 优化器来最小化损失函数 loss,并且将所有可训练的变量作为优化器的参数传入。这样,优化器就会自动更新这些变量,以使得模型能够更好地拟合训练数据。

Adam

Adam 是一种优化算法,用于训练神经网络。它是一种基于梯度下降的算法,可以自适应地调整每个参数的学习率。Adam 算法结合了动量方法和 RMSProp 方法的优点,因此在训练深度神经网络时表现良好。

在 TensorFlow 中,可以使用 tf.keras.optimizers.Adam 来使用 Adam 优化器。例如,以下代码演示了如何使用 Adam 优化器来训练一个简单的神经网络:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import tensorflow as tf

# Define the model
model = tf.keras.Sequential(
[
tf.keras.layers.Dense(64, activation="relu"),
tf.keras.layers.Dense(10),
]
)

# Define the optimizer
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)

# Compile the model
model.compile(
optimizer=optimizer,
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=["accuracy"],
)

# Train the model
model.fit(x_train, y_train, epochs=10, validation_data=(x_test, y_test))

在这里,首先定义了一个简单的神经网络模型,然后定义了一个 Adam 优化器,并将其传递给模型的 compile 方法。最后,使用 fit 方法来训练模型。

需要注意的是,Adam 优化器有许多可调参数,例如学习率、beta1 和 beta2 等。在实践中,通常需要对这些参数进行调整,以获得最佳的性能。

Tips

adam_m and adam_v

如果 adam_m 和 adam_v 是在训练过程中使用的,那么在 eval model 中可能不需要这些变量。因为在 eval model 中,只需要使用训练好的模型进行预测,而不需要再进行训练。

display with tensorboard

展示 tensorflow 1 checkpoint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import tensorflow as tf

# 加载图
graph = tf.Graph()
with graph.as_default():
tf.compat.v1.train.import_meta_graph("./model.ckpt.meta")
# 显示图中的所有操作

writer = tf.summary.create_file_writer("logs")
with writer.as_default():
tf.summary.graph(graph)
writer.close()

# 启动 tensorboard,并打开localhost:6001
# tensorboard --logdir=logs

HuggingFace

HuggingFace 是一个提供自然语言处理模型和工具的开源社区。它提供了基于 transformers 的许多预训练的模型,包括 BERT、GPT-2、RoBERTa 等,这些模型可以用于各种 NLP 任务,例如文本分类、命名实体识别、情感分析等。使用 HuggingFace,可以轻松地加载这些预训练的模型,并在自己的数据集上进行微调。

Transformers


Transformers 是一个用于自然语言处理的 Python 库,它提供了许多预训练的模型,包括 BERT、GPT-2、RoBERTa 等。这些模型可以用于各种 NLP 任务,例如文本分类、命名实体识别、情感分析等。使用 Transformers 库,可以轻松地加载这些预训练的模型,并在自己的数据集上进行微调。

以下是一个使用 Transformers 库进行文本分类的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from transformers import BertTokenizer, TFBertForSequenceClassification
import tensorflow as tf

# Load the tokenizer and model
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
model = TFBertForSequenceClassification.from_pretrained("bert-base-uncased")

# Compile the model
optimizer = tf.keras.optimizers.Adam(learning_rate=2e-5)
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
metric = tf.keras.metrics.SparseCategoricalAccuracy("accuracy")
model.compile(optimizer=optimizer, loss=loss, metrics=[metric])

# Load the data
train_dataset = ...
val_dataset = ...

# Train the model
model.fit(train_dataset, epochs=3, validation_data=val_dataset)

# Evaluate the model
test_dataset = ...
model.evaluate(test_dataset)

# Make predictions
text = "This is a test sentence."
inputs = tokenizer.encode_plus(text, return_tensors="tf")
outputs = model(inputs)[0]
predictions = tf.argmax(outputs, axis=1).numpy()

在这个例子中,首先加载了 BERT 模型和对应的 tokenizer。然后,使用 TFBertForSequenceClassification 类来定义一个文本分类模型,并使用 Adam 优化器、交叉熵损失函数和稀疏分类精度评估指标来编译模型。接下来,加载了训练、验证和测试数据集,并使用 fit 方法来训练模型。最后,使用 evaluate 方法来评估模型的性能,并使用模型进行了预测。

需要注意的是,Transformers 库提供了许多不同的预训练模型和 tokenizer,可以根据具体任务的需求进行选择。此外,Transformers 库还提供了许多其他的功能,例如生成文本、计算相似度等。

Transformers 常用文档:

export to ONNX

导出 ONNX 推荐两种方式.

  • 使用官方推荐方法导出.

    • 使用 optimum cli 导出 onnx, 需要模型在 optimum 中配置绑定导出映射.
  • 手动配置绑定导出.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import torch
from transformers import XLMRobertaForSequenceClassification, XLMRobertaTokenizer
import transformers

tokenizer = XLMRobertaTokenizer.from_pretrained("joeddav/xlm-roberta-large-xnli")
premise = "Juiter's Biggest Started as Tiny Grains of Hail"
hypothesis = "This text is about space & cosmos"

input_ids = tokenizer.encode(
premise,
hypothesis,
# return_tensors="pt",
return_tensors=transformers.TensorType.PYTORCH,
max_length=256,
truncation=True,
# padding="max_length",
padding=transformers.utils.PaddingStrategy.MAX_LENGTH,
)

mask = input_ids != 1
mask = mask.long()

# 手动导出 onnx
torch.onnx.export(
model, # 模型
[input_ids, mask], # 输入张量
"model.onnx", # 导出文件名
export_params=True, # 导出模型参数
opset_version=12, # ONNX运算符集版本
do_constant_folding=True, # 是否执行常量折叠优化
input_names=["input"], # 输入张量的名称
output_names=["output"], # 输出张量的名称
dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}}, # 批处理维度
)
# 手动导出 pt
traced_model = torch.jit.trace(model, [input_ids, mask], strict=False)
torch.jit.save(traced_model, "model.pt")


class Pytorch_to_TorchScript(torch.nn.Module):
def __init__(self):
super(Pytorch_to_TorchScript, self).__init__()
self.model = XLMRobertaForSequenceClassification.from_pretrained(
"joeddav/xlm-roberta-large-xnli", torchscript=True
).cuda()

def forward(self, data, attention_mask=None):
# 手动设置输入输出层
return self.model(data.cuda(), attention_mask.cuda())


# 增加一层手动引导forward
pt_model = Pytorch_to_TorchScript().eval()
traced_script_model = torch.jit.trace(pt_model, [input_ids, mask])
torch.jit.save(traced_script_model, "model.pt")

mlflow


MLFlow 是一个管理端到端机器学习生命周期的开源平台。

参考链接:

mlflow tracking

mlflow tracking 包含有后端元数据存储 (保存训练及实验数据) 和模型存储 (保存模型)

参考链接:

数据保存在本地

本地使用 tracking.

  • 后端元数据存储 meta data 和模型存储 artifacts (mlartifact 等) 都在本地文件夹下.

  • 默认端口 localhost:5000.

1
mlflow server --dev

数据保存在 database 及 minio

常用的生产环境下的 mlflow tracking 配置

  • mlflow: mlflow tacking 服务

  • minio: 持久化 tracking 的 artifacts.

  • mysql: 持久化 tracking 的 meta data.

1
2
3
4
5
6
7
8
9
10
11
12
13
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export MLFLOW_S3_ENDPOINT_URL=http://localhost:9001 # s3 store url
export ARTIFACE_S3_ENDPOINT=s3://mlflow
export MYSQL_USER=...
export MYSQL_PASSWORD=...
export MYSQL_ENDPOINT=mysql://$MYSQL_USER:$MYSQL_PASSWORD@localhost/dbname
export MLFLOW_PORT=5000
export MLFLOW_HOST=localhost
mlflow server \
--backend-store-uri $MYSQL_ENDPOINT \
--host $MLFLOW_HOST -p $MLFLOW_PORT \
--default-artifact-root $ARTIFACE_S3_ENDPOINT

mlflow docker-compose 配置参考

mlflow registry

MLflow 模型注册中心组件是一个集中的模型存储、一组 API 和 UI,用于协作管理 MLflow 模型的整个生命周期。它提供了模型谱系 (MLflow 实验和运行产生的模型)、模型版本化、阶段转换 (例如从阶段到生产) 和注释。

参考链接:

mlflow workflow

要使用 MLflow Project 部署模型,可以使用以下步骤:

  • 创建 MLflow 项目:创建一个包含模型训练和部署代码的 MLflow 项目。在项目目录中,应该包含一个 MLproject 文件,其中定义了项目的名称、入口点和依赖项。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
name: my_project

entry_points:
train:
command: "python train.py"
parameters:
data_path: {type: str, default: data.csv, description: "Path to training data"}
max_depth: {type: int, default: 5, description: "Maximum depth of decision tree"}

serve:
command: "python serve.py"
parameters:
model_uri: {type: str, default: "runs:/<run_id>/model", description: "URI of the trained model"}
port: {type: int, default: 5000, description: "Port to listen on"}

在这个示例中,定义了两个入口点:train 和 serve。train 入口点用于训练模型,serve 入口点用于部署模型。train 入口点接受两个参数:data_path 和 max_depth。serve 入口点接受两个参数:model_uri 和 port。

  • 训练模型:在 MLflow 项目目录中运行 mlflow run 命令来训练模型。例如:

1
mlflow run .
  • 部署模型:在 MLflow 项目目录中运行 mlflow models serve 命令来部署模型。例如:

1
mlflow models serve -m runs:/<run_id>/model --port 5000

这将运行 serve 入口点,并将训练的模型部署到本地端口 5000 上。MLflow 会自动加载模型,并将 HTTP 请求转发到模型的预测函数中。

如果要使用自定义 Python 脚本部署模型,可以使用以下步骤:

  • 训练模型:使用 Python 脚本训练模型,并将模型保存到本地文件中。例如:

1
2
3
4
5
6
7
8
import mlflow
import my_model

# 训练模型
model = my_model.train()

# 保存模型
mlflow.pytorch.save_model(model, "my_model")

在这个示例中,使用 my_model 模块中的 train 函数训练模型,并使用 MLflow 将模型保存到名为 my_model 的文件夹中。

  • 部署模型:使用 Python 脚本加载模型,并将模型部署到 Web 服务器上。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import mlflow.pytorch
import my_model
from flask import Flask, request, jsonify

# 加载模型
model = mlflow.pytorch.load_model("my_model")

# 创建Flask应用程序
app = Flask(__name__)

# 定义预测函数
@app.route("/predict", methods=["POST"])
def predict():
data = request.json
input = my_model.preprocess(data)
output = model(input)
result = my_model.postprocess(output)
return jsonify(result)


# 运行应用程序
app.run(host="0.0.0.0", port=5000)

在这个示例中,使用 MLflow 加载名为 my_model 的模型,并将其部署到 Flask Web 服务器上。定义了一个 /predict 端点,用于接收 POST 请求,并将请求数据传递给模型的预测函数。预测函数将输入数据预处理、调用模型进行推理、并将输出数据后处理,最终返回 JSON 格式的结果。使用 Flask 的 run 方法运行 Web 服务器,并将其绑定到本地 IP 地址和端口 5000 上。

mlflow with transformers

bert sequence classification

Step 1, 安装依赖

1
pip install transformers datasets mlflow

Step 2, 结合 transformers 配置基于 mlflow 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import mlflow
from transformers import AutoModelForSequenceClassification, AutoTokenizer, Trainer, TrainingArguments
from datasets import load_dataset

def train_model():
model_name = "bert-base-uncased"
num_labels = 2

tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=num_labels)

dataset = load_dataset("imdb", split="train[:5%]") # 只使用5%的数据进行训练
train_dataset = dataset.map(lambda e: tokenizer(e['text'], truncation=True, padding='max_length'), batched=True)

training_args = TrainingArguments(
output_dir="./results",
num_train_epochs=1,
per_device_train_batch_size=4,
logging_dir='./logs',
)

trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
tokenizer=tokenizer,
)

with mlflow.start_run():
trainer.train()

mlflow.log_param("model_name", model_name)
mlflow.log_param("num_labels", num_labels)
mlflow.log_param("num_train_epochs", training_args.num_train_epochs)
mlflow.log_param("per_device_train_batch_size", training_args.per_device_train_batch_size)

mlflow.pytorch.log_model(trainer.model, "model")

if __name__ == "__main__":
train_model()

执行训练

1
python train_model.py

mlflow plugins

mlflow 作为机器学习的框架无关工具,MLflow Python API 为编写与不同机器学习框架和后端集成的插件提供了开发人员 API. mlflow plugins 为第三方工具集成提供了强大的自定义接口.

参考链接:

MLServer

MLServer

alibi

DeepStream

DeepStream 是一个基于 GStreamer 的 SDK,用于创建用于图像处理和目标检测处理的人工智能视觉应用程序。

Tritonserver

Nvidia Triton Inference Sever 提供了兼容 CPUs 和 GPUs 运行在云端及边缘设备将模型服务化的优化推理方案。Nvidia 这里介绍了使用 Triton 快速部署可伸缩的模型的优势

Tutorials

Triton Server 提供了对应的 Tutorials

Deploying Using Triton

Deploying Using Triton

Triton Configuration

Model Repository

Model Repository

Jetson and JetPack Support

Jetson and JetPack Support

HTTP and GRPC Protocol Support

HTTP and GRPC Protocol Support: Triton Server 支持的各种 HTTP/REST 和 GRPC 协议扩展。参考使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
# --------------- logging extension -----------------
# 设置 log verbose 为 1
curl -s -w '\n%{http_code}\n' -d '{"log_verbose_level":1}' -X POST localhost:8000/v2/logging
# 获取 log config
curl -s localhost:8000/v2/logging | jq


# -------------- repository extension ---------------
# 查看model
curl -X POST localhost:8000/v2/repository/index | jq
# 加载/卸载指定模型
curl -X POST localhost:8000/v2/repository/models/${MODEL_NAME}/load | jq
curl -X POST localhost:8000/v2/repository/models/${MODEL_NAME}/unload | jq

Optimization

Optimization
- Perf Analyzer: 模型度量评估工具,针对部署在 server 中的模型能对吞吐量,GPU 利用率,时延等等做一个详细的评估输出.
- Model Analyzer: 该工具会自动使用 perf_analyzer 对模型做一个全方位的评估,并能输出详细的 html/pdf 报告.

Backend

Python backend

Tips

Model format

.ckpt 是 tensorflow 框架下保存的模型,包含以下几个子文件:

  • model.ckpt.meta :保存 Tensorflow 计算图结构,可以理解为神经网络的网络结构

  • model.ckpt :保存 Tensorflow 程序中每一个变量的取值,变量是模型中可训练的部分

  • checkpoint :保存一个目录下所有模型文件列表

.onnx 是一种针对机器学习所设计的开放式的文件格式,用于存储训练好的模型。它使得不同的深度学习框架(如 Pytorch, MXNet)可以采用相同格式存储模型数据。简而言之,ONNX 是一种便于在各个主流深度学习框架中迁移模型的中间表达格式.

.pt/.torchscript 是 ptorch 模型框架保存格式.

Triton REST API

常用的 Triton 的 REST API

1
2
3
4
# 检测 triton 健康
curl -v localhost:8000/v2/health/ready
# 模型 name 配置
curl -v localhost:8000/v2/models/name/config | jq

查看模型输入输出

  • 使用 polygraphy

1
2
3
python -m pip install colored polygraphy --extra-index-url https://pypi.ngc.nvidia.com
# 确定模型的输入输出
POLYGRAPHY_AUTOINSTALL_DEPS=1 polygraphy inspect model model_repository/name/1/model.plan
  • 使用 netron

Yolov5 pt to ONNX

将模型转换为 onnx,基于 yolo 模型,使用 yolo 仓库提供的 export 脚本

[!IMPORTANT]
若需要支持 batch 推理,则需要打开 --dynamic 选项。

1
2
3
docker run -it --gpus all -v $(pwd):/workspace nvcr.io/nvidia/pytorch:23.04-py3
git clone https://github.com/ultralytics/yolov5.git && cd yolov5
python export.py --include onnx --weights model.pt --img 1280 --dynamic --device=0

将模型保存在需要的位置,并补充模型配置文件 config.pbtxt,如下:

1
2
3
4
tree model_repository/model
model_repository/model
└── 1
└── model.onnx

优化导出模型,将 preprocessing 和 postprocessing 加入到模型图中

[!TIP]
参考 yolov5-rt 自动化转化前后处理到模型中。

ALBERT to ONNX

使用 tf2onnx 转换,转换命令参考

1
2
3
pip install -U tf2onnx
docker run -it --gpus all -v $(pwd):/workspace nvcr.io/nvidia/tensorflow:23.04-tf2-py3
python -m tf2onnx.convert --checkpoint tensorflow-model-meta-file-path --output model.onnx --inputs input0:0,input1:0 --outputs output0:0

参考 triton to albert client

Transformers to ONNX

将 Transformers 模型转 ONNX 参考

ONNX Patch

支持 CPU/GPU 部署

[!TIP]
使用三方工具可以操作 ONNX 模型裁剪、修改节点命名等。

使用 ONNX 推理加速方案

ONNX 转换 TensorRT Engine

[!TIP]
通常 TensorRT 部署的推理速度较快。

将 ONNX 模型转为 TensorRT Engine,仅支持 GPU 部署(需要在对应部署机器配置适配,会对生成的 TensorRT Engine 进行优化)。

[!TIP]
onnx2trt_utils.cpp:374: Your ONNX model has been generated with INT64 weights, while TensorRT does not natively support INT64. Attempting to cast down to INT32.

该问题可以通过 onnx-simplifier 简化 onnx 模型解决:

1
2
pip install onnx-simplifier
onnxsim model.onnx simplfied_model.onnx

将 ONNX 模型转为 TensorRT Engine。

1
2
3
docker run -it --gpus all -v $(pwd):/trt_optimize nvcr.io/nvidia/tensorrt:23.04-py3
cd /trt_optimize
trtexec --onnx=model.onnx --saveEngine=model.plan --useCudaGraph

使用 Model Analyzer 评估最优配置文件 config.pbtxt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
docker run -it --gpus=all --shm-size=256m --rm -p8000:8000 -p8001:8001 -p8002:8002 -v $(pwd):/workspace nvcr.io/nvidia/tritonserver:23.04-py3
pip3 install triton-model-analyzer
# 输出评估缓存
mkdir output profile_results
# 自动评估,耗时长,这里预筛出延迟10ms的配置评估结果
model-analyzer profile \
--model-repository model_repository \
--profile-models drawing_rt \
--triton-launch-mode=local \
--output-model-repository-path output \
--override-output-model-repository \
--latency-budget 10 \
--run-config-search-mode quick

# 快速评估,指定参数
model-analyzer profile \
--model-repository model_repository \
--profile-models drawing_rt \
--triton-launch-mode=local \
--output-model-repository-path output \
--override-output-model-repository \
--export-path profile_results \
--run-config-search-max-concurrency 2 \
--run-config-search-max-model-batch-size 2 \
--run-config-search-max-instance-count 2

使用 TPAT 转换 ONNX 自动化生成 TensorRT Engine。

ONNX/TensorRT Batch

动态 Batch 处理需要,对模型的输入输出,设置为动态类型。

基于 yolo 的模型需要修改 export.py 脚本,配置输出脚本,以导出 batch 的 ONNX 模型。

1
2
3
4
5
6
7
8
# 对block对应得输入,重命名
dx = {
"images": {0: "batch"},
"output": {0: "batch", 1: "100800", 2: "16"},
"onnx::Sigmoid_473": {0: "batch"},
"onnx::Sigmoid_611": {0: "batch"},
"onnx::Sigmoid_748": {0: "batch"},
}
1
python export.py --include onnx --weights weights/box_810.pt --img 1280 --batch-size 1 --dynamic

或者直接使用最新 yolov5 版本导出 dynamic 版本,参考

生成 TensorRT Engine 时需要设置支持的动态维度。

1
trtexec --onnx=model.onnx --saveEngine=model.plan --useCudaGraph --minShapes=images:1x3x1280x1280 --optShapes=images:1x3x1280x1280 --maxShapes=images:4x3x1280x1280

dali_backend

[!NOTE]
dali_backend:The Triton backend that allows running GPU-accelerated data pre-processing pipelines implemented in DALI’s python API.

dali_backend 支持在 tritonserver 中构建 ensemble 方式构建的应用,参考官方示例.

指定 model 推理

准备推理脚本,启动服务

1
2
3
docker run -it --gpus=all --shm-size=256m --rm -p8000:8000 -p8001:8001 -p8002:8002 -v $(pwd):/workspace nvcr.io/nvidia/tritonserver:23.04-py3
cd /workspace
tritonserver --model-repository=model_repository --log-verbose=1 --model-control-mode=explicit --load-model model-name

安装依赖

1
2
pip install tritonclient[http] opencv-python-headless
python model_infer_client.py

Ensemble(pipeline)

[!IMPORTANT]
UNAVAILABLE: Internal: Unable to initialize shared memory key 'triton_python_backend_shm_region_2' to requested size (67108864 bytes). If you are running Triton inside docker, use '--shm-size' flag to control the shared memory region size. Each Python backend model instance requires at least 64MBs of shared memory. Error: No space left on device

使用 Ensemble 模式需要加载共享内存大小,–shm-size=256m,大小根据实际需求设置。

1
2
3
4
docker run -it --gpus=all --shm-size=256m --rm -p8000:8000 -p8001:8001 -p8002:8002 -v $(pwd):/workspace nvcr.io/nvidia/tritonserver:23.04-py3
cd /workspace/
apt-get update && apt-get install libgl1 -y --no-install-recommends && pip install opencv-python opencv-python-headless
tritonserver --model-repository=model_repository/ --log-verbose 1 --exit-on-error 0 --model-control-mode poll

Model Warm-up

模型加载,会在模型初始化时耗费时间,使用 ModelWarmup 可以有效规避第一次请求的花销。ModelWarmup 参考模型配置。官方示例参考测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
model_warmup [
{
name: "warmup_requests"
batch_size: 1
inputs: {
key: "drawing_preprocessing_input"
value: {
input_data_file: "warm-up.jpg"
dims: [ 1507865 ]
data_type: TYPE_UINT8
}
}
}
]

上面模型配置使用模型子文件夹下 warm-up.jpg,在模型加载时作为输入。

Jetson Compatibility

pytorch with triton

Solution

结合 Triton 提供的 model 服务推理功能,可以实现如示例 中 k8s 和 mlflow 集成解决方案。

[!WARNING]
minikube 上测试 gpu 非常麻烦,参考 stackoverflowgithub

k8s Integration

mlflow Integration

Customized Server

TensorRT


TensorRT 是一个高性能的深度学习推理库,可用于优化和部署深度学习模型。它使用 GPU 加速来提高推理性能,并提供了许多优化技术,例如网络剪枝、量化和层融合,以减少模型的计算和内存需求。

Tips

安装

查看 tensorrt 版本

1
2
3
dpkg-query -W tensorrt
dpkg -l | grep nvinfer
ldconfig -p | grep nvinfer

HPC


Nvidia HPC 是一种高性能计算解决方案,旨在加速科学计算和数据分析。它使用 GPU 加速器来提高计算速度,从而使科学家和研究人员能够更快地进行计算密集型任务。

Cuda


Nvidia Cuda 简化开发人员能利用基于 Cuda 的 GPU 执行并行优化编程.

OpenVINO


OpenVINO 是一个开源的优化和部署深度学习模型工具包。

DALI


Nvidia DALI(Data Loading Library)是一个用于数据预处理和增强的库,旨在加速深度学习模型的训练和推理。DALI 提供了高度优化的 CPU 和 GPU 数据管道,可以在数据加载和预处理方面显著提高性能。DALI 还提供了许多常用的数据增强操作,例如裁剪、缩放、旋转和翻转等,可以帮助用户更轻松地进行数据增强。在 Triton 中,DALI 可以与 PyTorch 和 TensorFlow 等深度学习框架一起使用,以加速模型的训练和推理。如果需要更快的数据加载和预处理速度,可以考虑使用 Nvidia DALI

参考主页教程将指导通过第一步和支持的操作将放在一起的 GPU 驱动的数据处理管道。

DALI 用户手册大纲

DALI Operation

DALI Operation 包含了对常用的图形、音频、视频的处理加工教程和示例。调用接口参考 Operation Interface Reference

安装

根据 DALI 文档安装

1
2
pip install --extra-index-url https://pypi.nvidia.com nvidia-dali-cuda120
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist --upgrade nvidia-dali-cuda120

Common Process

获取当前管道信息

1
2
3
4
5
6
7
8
9
10
11
cur = dali.pipeline.Pipeline.current()
print(
"\ndali config:\n",
" ".join(
[
f"max_batch_size={cur.max_batch_size}\n",
f"num_threads={cur.num_threads}\n",
f"device_id={cur.device_id}\n",
]
),
)

Image Preprocess

适配 yolo 的 letterbox 图片预处理,将图片按原有宽高比缩小在 img_size 大小,并在 img_size 比例较大的一侧添加填充。

方案一: resize 后使用剪切并填充.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 定义dali数据预处理管道
import nvidia.dali as dali
import nvidia.dali.types as types
from nvidia.dali.plugin.triton import autoserialize

IMG_SIZE = 1280


@autoserialize
@dali.pipeline_def(batch_size=256, num_threads=9, device_id=0)
def pipe():
new_shape = (IMG_SIZE, IMG_SIZE)
images = dali.fn.external_source(device="cpu", name="DALI_INPUT")
# 解码图片
images = dali.fn.decoders.image(images, device="cpu", output_type=types.RGB)
# 保持高宽比,并限制最大不超过指定的图片大小
images = dali.fn.resize(images.gpu(), size=new_shape, mode="not_larger")
# 裁剪图片,并填充超出的部分
images = dali.fn.crop_mirror_normalize(
images,
dtype=types.FLOAT, # 输出数值类型
output_layout="CHW", # 输出图片的布局
fill_values=114.0 / 255, # 填充的颜色必须有效,否则无法裁剪出填充的部分
crop=new_shape, # 裁剪大小
out_of_bounds_policy="pad", # 超出部分进行填充
std=[0.229 * 255, 0.224 * 255, 0.225 * 255], # 数值进行标准差补偿
)

return images

方案二: resize 后使用切片并填充.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import nvidia.dali as dali
import nvidia.dali.types as types

IMG_SIZE = 1280.0


@pipeline_def(
batch_size=max_batch_size,
num_threads=4,
device_id=0,
)
def image_process_v1():
images = dali.fn.external_source(device="cpu", name="DALI_INPUT")
images = dali.fn.decoders.image(images, device="cpu", output_type=types.RGB)
shapes = dali.fn.shapes(images)
reisze_images = dali.fn.resize(
images.gpu(),
size=(IMG_SIZE, IMG_SIZE),
mode="not_larger",
)
resize_shape = dali.fn.shapes(reisze_images)
anch = dali.fn.stack(
(IMG_SIZE - resize_shape[1]) / 2,
(IMG_SIZE - resize_shape[0]) / 2,
)
image_processed = dali.fn.slice(
reisze_images,
-anch,
(IMG_SIZE, IMG_SIZE),
fill_values=127,
out_of_bounds_policy="pad",
normalized_shape=False,
normalized_anchor=False,
)
return (images, shapes, image_processed, anch)


pipe = image_process_v1()
pipe.build()
pipe_out = pipe.run()
show_images(pipe_out[0])
print(pipe_out[1])
show_images(pipe_out[2])
print(pipe_out[3])

Transformers

NLP

Yolov5

Resource

输出 IOU

IOU = 两个矩形交集的面积/两个矩形的并集面积 , 两矩形表示检测矩形 (predicted) 和标注 (ground-truth) 矩形.

将待测图像输入网络得到的输出结果是一个高维矩阵,[-1, H, W, B, (4 + 1 + C)]. 一幅图片被分割为了 H*W 块.(参看论文中的 Pr (object)∗IOU)

  • H: 表示纵向分割的块的数目

  • W: 表示横向分割的块的数目

  • B: 表示 anchors 的数目

  • C: 表示 classes 的数目

  • 4:x,y,w,h,

  • 1:confidence

bert/albert

模型资源

LLM

ChatGLM

ChatGLM 是一个基于大型语言模型的对话生成模型,它可以生成自然流畅的对话,可以用于聊天机器人、智能客服等场景。ChatGLM 使用了 GPT-2 模型,该模型是由 OpenAI 开发的一种基于 Transformer 的语言模型,可以生成高质量的自然语言文本。ChatGLM-6B 是 ChatGLM 的一个版本,它使用了包含 60 亿个参数的 GPT-2 模型,可以生成更加自然流畅的对话。

模型相关链接:

参考应用方案:

测试官方 Demo

依赖:

  • GPU >= 6 G or CPU >= 32G (实际测试 5G 即可)

  • pip: pip install protobuf transformers==4.30.2 cpm_kernels torch>=2.0 gradio mdtex2html sentencepiece accelerate

[!TIP]
Error: RuntimeError: Library cuda is not initialized

Fix: ln -s /usr/lib/x86_64-linux-gnu/libcuda.so /usr/local/cuda/lib64/libcuda.so

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from transformers import AutoTokenizer, AutoModel

tokenizer = AutoTokenizer.from_pretrained(
"THUDM/chatglm-6b-int4", trust_remote_code=True
)

model = (
AutoModel.from_pretrained(
"THUDM/chatglm-6b-int4",
trust_remote_code=True,
)
.half()
.cuda()
)
model = model.eval()
response, history = model.chat(tokenizer, "你好", history=[])
print(response)
response, history = model.chat(tokenizer, "晚上睡不着应该怎么办", history=history)
print(response)

VisualGLM

VisualGLM-6B 是一个开源的,支持图像、中文和英文的多模态对话语言模型,语言模型基于 ChatGLM-6B,具有 62 亿参数;图像部分通过训练 BLIP2-Qformer 构建起视觉模型与语言模型的桥梁,整体模型共 78 亿参数。

模型相关链接:

ImageBind

ImageBind 多模态互转.

segment-anything

segment-anything MetaAI 分割模型.

milvus

milvus 向量数据库 (非结构化数据库).

links:

Design

架构

links:

索引

最近邻搜索算法

  • 搜索:将查询向量和搜索空间每个向量比较相似性,相似性可选择欧氏距离,维度夹角等

  • 优缺点:搜索质量完美,和每个向量都做比较,时间复杂度极高,仅支持小规模数据集 (百万)

  • milvus: FLAT

近似近邻搜索 - 聚类算法 - kmeans

  • 搜索:构建索引时,选定要分类的数量,随机选择分类数量个点分别划分向量空间的每个向量,然后计算出每个分类空间中的聚类中心点 (平均向量点), 重新再分类,然后重复迭代,直到收敛。搜索时,选择查询向量距离最近的聚类中心点,比较该分类空间中的向量即可

  • 优化:增加聚类的数量,增加搜索的空间的数量

  • 优缺点:分治搜索,搜索速度和质量成反比

  • milvus: IVF_FLAT,IVF_SQ8

[!NOTE]
IVF(Inverted File)是一种用于加速近似最近邻搜索的索引结构。IVF_FLAT 是 IVF 索引的一种类型。

IVF 索引将向量数据集划分为多个小的聚类中心,每个聚类中心都有一个倒排文件。当进行查询时,Milvus 会根据查询向量的特征找到最相似的聚类中心,然后只在这些聚类中心的倒排文件中搜索,从而减少搜索的时间复杂度。

投影法

  • 搜索:构建索引时,对每个维度,使用易于碰撞的哈希函数映射向量空间中的向量,形成分类空间。搜索时,使用相同的哈希函数计算搜索向量得到对应的分类空间,比较分类空间中的向量即可.

  • 优化:将单个向量分为多段 (小维度) 向量降低分类空间形成的索引大小,增加近似搜索空间的数量

  • 优缺点:哈希搜索,收敛搜索更快

  • milvus: ANNOY

有损压缩 - 量化 - 积量化 (PQ)

  • 搜索:使用分类空间中的的质心点编码代替向量存储在内存,将编码映射成码本.

  • 优化:将向量分段为低维度向量,对子向量进行量化,将码本的增加速率从指数降为加法

  • 优缺点:节省内存,搜索质量视向量聚类的稀疏而定,码本会形成性能瓶颈

  • milvus: IVF_PQ

基于图的搜索算法 (HNSW)

  • 搜索:建立图结构,每个向量点都使用合适的方法计算出建立连接的邻节点,比如德劳内 (Delaunay) 三角剖分法.

  • 优化:将向量空间点,依次拿出放到搜索空间计算近邻点,以此形成类似三角剖分法路径和噪声干扰路径 (有利于相聚更远的两个向量建立连接), 即向粗后慢

  • 优化 2: 基于图的分层搜索算法,将前面提到的聚类质心按分类大小从大到小分层建立图结构,分层从上到下节点间连接越密集,即先保证快速导航,进入下一层精细搜索

  • 优缺点:搜索速度稳定,但占用内存极大,无法像积量化压缩向量,还要维护分层结构

  • milvus: HNSW

links:

度量

  • CV: Euclidean distance (L2), Inner product(IP)

  • NLP: Inner product(IP), Hamming

  • molecular: Jaccard

links:

Getting started

install

使用 standalone 模式安装

1
2
3
4
5
6
7
8
9
10
helm repo add milvus https://milvus-io.github.io/milvus-helm/ && helm repo update
helm install my-release \
milvus/milvus \
--set cluster.enabled=false \
--set etcd.replicaCount=1 \
--set minio.mode=standalone \
--set pulsar.enabled=false
kubectl get pod my-release-milvus-standalone-54c4f88cb9-f84pf --template='{{(index (index .spec.containers 0).ports 0).containerPort}}{{"\n"}}'
kubectl port-forward service/my-release-milvus 27017:19530
helm uninstall my-release

使用 cluser 模式

1
2
3
helm repo add milvus https://milvus-io.github.io/milvus-helm/ && helm repo update
helm install my-release milvus/milvus
helm uninstall my-release

Examples

处理所有非结构化数据,如反向图像搜索、音频搜索、分子搜索、视频分析、问答系统、自然语言处理等示例.

links:

Service Encoding

处于安全考虑,通常需要将代码进行加密,以防止源码泄露。在 AI 服务中常见的加密方式有:

  • 将源码转为 exe

  • 授权 license

  • 加密模型

links:

将源码转为 exe

较为流程的转换方案是使用 pyinstaller 和 nuikta 将源码转为 exe. 这里使用 nuikta 方案.

links:

授权 license

加密模型