0%

windows下C++项目 监控编译速度慢的问题的工具有哪些

在Windows下C++项目中,如果遇到编译速度较慢的问题,可以使用以下工具来监控和分析问题,帮助优化编译速度:


1. 编译时间分析工具

  • Clang Build Analyzer
    Clang 提供的工具,可以帮助分析编译时间瓶颈。虽然它主要针对Clang编译器,但如果项目使用Clang编译,可以直接使用此工具。

    • 官网: Clang Build Analyzer
    • 用途: 分析哪些头文件或代码模块导致了编译速度慢。
  • MSBuild Binary and Structured Log Viewer
    如果你的项目使用MSBuild构建,可以通过MSBuild日志工具查看具体的构建时间段。

    • 下载: MSBuild Log Viewer
    • 用途: 生成并可视化MSBuild日志,快速定位长时间任务。

2. 依赖分析工具

  • Include What You Use (IWYU)
    用于分析C++项目中头文件的依赖情况,减少不必要的头文件包含。

    • 官网: IWYU
    • 用途: 帮助减少头文件依赖,降低编译时间。
  • Visual Studio 编译依赖分析器
    如果使用Visual Studio,可以通过“项目属性”中的编译依赖设置来分析和优化头文件的使用。

    • 用途: 检测包含文件树,分析冗余包含。

3. 构建性能优化工具

  • CMake Timing Tools
    如果使用CMake作为构建系统,可以通过 --trace--trace-expand 参数分析配置时间。还可以开启构建性能统计(例如 Ninja 提供的构建时间统计)。

    • 用途: 分析构建过程中每一步的耗时。
  • IncrediBuild
    专为Windows环境设计的分布式构建工具,可大幅加速编译过程。

    • 官网: IncrediBuild
    • 用途: 通过分布式任务并行化提升编译速度。
  • ccache
    高效的编译缓存工具,用于加速重复编译。

    • 官网: ccache
    • 用途: 缓存已编译文件,避免重复编译。

4. 性能监控工具

  • Windows Performance Analyzer (WPA)
    使用Windows自带的性能分析工具监控磁盘、CPU和内存等资源使用情况,定位瓶颈是否由系统资源导致。

  • Process Monitor (ProcMon)
    监控构建过程中文件系统和进程的读写操作,定位慢点是否由磁盘I/O引起。


5. 其他辅助工具

  • Precompiled Headers (PCH)
    利用预编译头减少重复的头文件解析时间。

  • Ninja
    替代传统的构建工具(如MSBuild或Make),以更快的依赖管理和任务调度提升编译速度。

  • Clang-Tidy
    通过静态分析工具检查代码质量,识别可能导致编译变慢的代码模式。


总结

可以从头文件优化、构建工具、分布式构建和编译缓存等方面入手。推荐先使用IWYUClang Build Analyzer分析依赖,再结合ccacheNinja等工具优化构建流程。

ov::Output 详解

ov::Output 是 OpenVINO 中的一个核心类,用于表示模型图中节点的输出。它封装了一个节点的输出端口,允许用户访问输出的形状、数据类型以及与其他节点的连接信息。通过 ov::Output,可以在计算图中导航并操作节点间的连接。


核心功能

  1. 表示节点的输出端口
    每个 ov::Node 可能有一个或多个输出端口,ov::Output 表示其中一个端口。

  2. 获取元信息
    包括输出的形状、数据类型和元素类型等。

  3. 管理连接关系
    用于查看输出与哪些节点的输入相连接。

  4. 支持模板化
    ov::Output<T> 可以作用于不同类型的节点,例如 ov::Nodeov::Function


创建和使用 ov::Output

通常,ov::Output 是从 ov::Nodeoutputs() 方法或 output(i) 方法获取的,而不是直接创建。

获取节点的输出

1
2
// 获取节点的第一个输出
ov::Output<ov::Node> output = node->output(0);

主要方法和属性

以下是 ov::Output 的常用方法及其功能:

1. 获取输出形状

1
2
3
4
5
6
ov::Shape shape = output.get_shape();
std::cout << "Shape: ";
for (const auto& dim : shape) {
std::cout << dim << " ";
}
std::cout << std::endl;
  • 返回输出的静态形状(ov::Shape)。

2. 获取数据类型

1
2
ov::element::Type type = output.get_element_type();
std::cout << "Data type: " << type << std::endl;
  • 返回输出的数据类型(例如 f32i32 等)。

3. 检查输出是否为动态形状

1
2
3
if (output.get_partial_shape().is_dynamic()) {
std::cout << "Output has a dynamic shape." << std::endl;
}
  • 使用 get_partial_shape() 检查是否为动态形状。

4. 获取连接的输入

1
2
3
4
for (const auto& target_input : output.get_target_inputs()) {
std::cout << "Connected to input of node: "
<< target_input.get_node()->get_friendly_name() << std::endl;
}
  • get_target_inputs() 返回所有连接到该输出的输入端口。

5. 获取输出所属的节点

1
2
3
auto owner_node = output.get_node();
std::cout << "Output belongs to node: "
<< owner_node->get_friendly_name() << std::endl;
  • get_node() 返回该输出所属的节点。

6. 设置输出名称

1
output.set_names({"custom_output_name"});
  • 设置输出的自定义名称,便于识别和访问。

动态修改模型图

通过 ov::Output,可以修改计算图中节点的连接关系。

1. 替换输出连接

1
2
3
4
ov::Output<ov::Node> new_output = some_other_node->output(0);

// 替换当前输出与新输出的连接
ov::replace_output(output, new_output);

2. 添加新连接

可以将一个输出连接到多个输入节点:

1
output.add_target_input(new_node->input(0));

实际应用示例

以下示例展示了如何遍历模型节点的输出,并动态修改模型:

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
#include <openvino/openvino.hpp>
#include <iostream>

int main() {
ov::Core core;

// 加载模型
auto model = core.read_model("model.xml");

// 遍历模型中的所有节点
for (const auto& node : model->get_ops()) {
for (size_t i = 0; i < node->get_output_size(); ++i) {
ov::Output<ov::Node> output = node->output(i);

// 输出节点信息
std::cout << "Node: " << node->get_friendly_name()
<< ", Output index: " << i
<< ", Shape: " << output.get_shape()
<< ", Type: " << output.get_element_type() << std::endl;

// 修改连接(例如替换输出连接)
if (output.get_element_type() == ov::element::f32) {
auto new_node = std::make_shared<ov::op::v0::Relu>(output);
ov::replace_output(output, new_node->output(0));
std::cout << "Replaced output with a ReLU node." << std::endl;
}
}
}

// 保存修改后的模型
core.compile_model(model, "CPU")->export_model("modified_model.xml");

return 0;
}

高级功能

使用动态形状的输出

对于动态形状的输出,可以进一步设置具体形状:

1
2
3
if (output.get_partial_shape().is_dynamic()) {
output.set_partial_shape({1, 3, 224, 224});
}

获取扩展的形状信息

获取动态形状的约束:

1
2
3
4
5
6
ov::PartialShape partial_shape = output.get_partial_shape();
if (partial_shape.rank().is_dynamic()) {
std::cout << "Rank is dynamic." << std::endl;
} else {
std::cout << "Rank: " << partial_shape.rank().get_length() << std::endl;
}

性能注意事项

  1. 批量操作
    尽量减少对输出的逐个操作,使用批量方法操作多个节点或输出。

  2. 减少拷贝
    使用引用或共享机制避免不必要的输出数据拷贝。

  3. 动态形状处理
    在动态模型中,尽量在推理开始前明确所有动态形状。


常见问题

  1. 动态形状的操作限制
    对动态形状的输出操作时,需要先设置具体形状,否则部分操作可能失败。

  2. 数据类型不匹配
    在连接节点时,确保输出和输入的数据类型一致。

  3. 输出未连接
    如果某些输出未连接,可能导致模型推理失败或结果异常。


总结

ov::Output 是 OpenVINO 图操作中的关键类,用于表示和管理节点的输出端口。通过 ov::Output,开发者可以轻松获取输出的元信息,管理节点间的连接关系,并动态修改计算图。熟练掌握 ov::Output 的功能,可以大大提升在 OpenVINO 图操作中的开发效率。

摘要

  • 模型前处理相关学习笔记

深度学习模型部署 前处理

在深度学习模型的部署过程中,前处理是一个至关重要的步骤。前处理的目标是将原始数据转换为模型可以接受的格式,以确保推理结果的准确性和一致性。以下是前处理的一些常见操作及其实现方法。


前处理的主要步骤

1. 数据加载

  • 从文件、摄像头、传感器、API 等读取数据。
  • 支持的格式可能包括图像(JPEG/PNG)、文本、视频、音频等。
  • 常用库:
    • 图像:OpenCVPillow
    • 文本:NLTKspaCy
    • 音频:librosatorchaudio
    • 视频:OpenCVmoviepy

2. 尺寸调整 (Resizing)

  • 模型通常需要固定大小的输入,例如 224x224。
  • 工具:
    • Python:
      1
      2
      import cv2
      resized = cv2.resize(image, (224, 224))
    • C++ (OpenCV):
      1
      cv::resize(image, resized_image, cv::Size(224, 224));

3. 归一化 (Normalization)

  • 将输入数据的像素值范围调整到 [0, 1][-1, 1]
  • 公式:
    [
    x_{\text{normalized}} = \frac{x - \text{mean}}{\text{std}}
    ]
  • 常用库实现:
    • PyTorch:
      1
      2
      3
      import torchvision.transforms as transforms
      transform = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
      normalized_image = transform(image)

4. 数据格式转换

  • 深度学习模型可能需要特定的数据格式(例如 NHWC 或 NCHW)。
    • NHWC: [Batch, Height, Width, Channels](TensorFlow 通常使用)
    • NCHW: [Batch, Channels, Height, Width](PyTorch 通常使用)
  • 转换:
    1
    2
    import numpy as np
    nhwc_image = image.transpose(0, 2, 3, 1) # NCHW -> NHWC

5. 类型转换

  • 确保数据的类型与模型要求一致,例如 float32int8 等。
  • 转换:
    1
    float_image = image.astype('float32')

6. 数据归一化与标准化的结合

  • 对于图像:
    • 首先缩放到 [0, 1] 范围。
    • 再进行归一化(减去均值,除以标准差)。
  • 对于文本或序列数据:
    • 可能需要 Tokenization 和 Padding。

7. 其他特定处理

  • 数据增强:裁剪、旋转、翻转(在推理时一般用于 TTA)。
  • 嵌入:对文本进行词嵌入转换。
  • 频谱图:将音频信号转换为频谱图。

示例代码

以下是一个典型的图像模型的前处理示例,使用 Python:

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

def preprocess_image(image_path, input_size=(224, 224)):
# 加载图像
image = cv2.imread(image_path)
if image is None:
raise ValueError("Image not found or unable to read!")

# 调整大小
resized = cv2.resize(image, input_size)

# 转换为浮点数并归一化到 [0, 1]
normalized = resized / 255.0

# 转换为 NCHW 格式
nchw_image = np.transpose(normalized, (2, 0, 1))

# 增加 Batch 维度并转换为 float32
batch_image = np.expand_dims(nchw_image, axis=0).astype(np.float32)

return batch_image

部署中的前处理工具

  1. TensorFlow/TF-Serving

    • 使用 tf.image 模块进行前处理。
    • 可将前处理代码直接集成到模型中(SavedModel)。
  2. PyTorch/TorchServe

    • 使用 torchvision.transforms 执行前处理。
    • 在自定义 Handler 中实现前处理。
  3. ONNX Runtime

    • 在推理时依赖外部前处理,但支持优化后的动态尺寸输入。
  4. OpenCV/DNN 模块

    • OpenCV 支持直接加载和推理模型,可在前处理阶段使用其高效工具。

前处理常见问题与优化

  1. 不匹配问题

    • 原因:模型训练和推理时的前处理设置不同。
    • 解决:确保推理阶段与训练一致(归一化参数、数据格式等)。
  2. 性能问题

    • 原因:前处理耗时过长。
    • 解决:
      • 使用并行库(如 DALI)。
      • 在 GPU/TPU 上完成前处理。
  3. 动态尺寸

    • 原因:输入尺寸不固定。
    • 解决:使用动态尺寸模型,或在前处理阶段适配输入。

如果你有具体的部署场景(如平台或数据类型),我可以提供更细致的实现建议!

前处理 数据加载

在深度学习模型部署中,前处理的目的是将原始输入数据转换为模型可以接受的格式和结构。数据加载是前处理的重要组成部分,涉及从存储中读取数据、对数据进行解析、归一化、格式化等步骤,为后续的推理做好准备。

以下是深度学习模型部署中 数据加载 的详细解析:


1. 数据加载的核心任务

数据加载的主要任务包括:

  1. 从各种来源读取数据

    • 常见数据来源:文件系统、数据库、API、消息队列等。
    • 文件格式:图片(JPEG/PNG)、视频(MP4)、音频(WAV/MP3)、文本(JSON/CSV)、二进制数据等。
  2. 解析和转换数据

    • 解析数据格式(如图片解码、JSON 解析)。
    • 将数据转化为模型可用的张量(Tensor)格式。
  3. 批量加载和优化性能

    • 支持批量加载(Batching)以提高吞吐量。
    • 利用多线程或异步加载优化性能。
  4. 对输入数据进行前处理

    • 标准化、归一化。
    • 调整大小、裁剪、填充。
    • 数据增强(如旋转、翻转等)。

2. 数据加载的典型步骤

(1) 数据读取

根据数据来源和格式,选择适当的读取方法:

  • 本地文件系统: 使用操作系统原生文件读写函数(如 open()fopen())。
  • 云存储/网络资源: 使用库(如 requestsboto3)下载文件。
  • 数据库: 使用 SQL 或 NoSQL 查询获取数据。

示例(读取本地图片):

1
2
from PIL import Image
image = Image.open("image.jpg")

(2) 数据解析

  • 将原始数据解析为内存中的对象。例如:
    • 图片数据解码为像素矩阵。
    • JSON 数据解析为 Python 字典。
    • CSV 数据解析为 DataFrame 或数组。

(3) 数据转换为张量

将解析后的数据转换为深度学习框架支持的张量(如 PyTorch 的 torch.Tensor 或 TensorFlow 的 tf.Tensor)。

1
2
3
4
5
import torch
from torchvision import transforms

transform = transforms.ToTensor()
tensor = transform(image)

(4) 数据预处理

根据模型需求对数据进行调整:

  • 归一化: 将像素值缩放到 [0, 1] 或 [-1, 1] 区间。
  • 调整大小: 将图片调整为固定尺寸。
  • 格式转换: 如 RGB -> BGR 或 NHWC -> NCHW。

示例:

1
2
3
4
5
6
transform = transforms.Compose([
transforms.Resize((224, 224)), # 调整大小
transforms.ToTensor(), # 转为张量
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 归一化
])
processed_image = transform(image)

3. 高效数据加载技巧

(1) 批量加载

通过批量加载数据,可以减少 I/O 操作的开销,提高吞吐量。

  • 示例(PyTorch DataLoader):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from torch.utils.data import DataLoader, Dataset

    class CustomDataset(Dataset):
    def __init__(self, file_paths, transform=None):
    self.file_paths = file_paths
    self.transform = transform

    def __len__(self):
    return len(self.file_paths)

    def __getitem__(self, idx):
    image = Image.open(self.file_paths[idx])
    if self.transform:
    image = self.transform(image)
    return image

    dataset = CustomDataset(file_paths=["img1.jpg", "img2.jpg"], transform=transform)
    data_loader = DataLoader(dataset, batch_size=32, shuffle=True)

(2) 异步加载

使用多线程或多进程加载数据,在数据加载的同时进行推理。

  • 示例(PyTorch 的多进程加载):
    1
    data_loader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)

(3) 数据缓存

对于频繁使用的数据,可以将其缓存到内存或更快的存储设备(如 SSD),减少加载时间。


4. 常见的数据加载场景

(1) 图片数据加载

  • 格式: JPEG、PNG、TIFF 等。
  • 前处理:
    • 调整大小、裁剪。
    • 归一化为 [0, 1] 或 [-1, 1]。
  • 库支持: OpenCV、Pillow、Torchvision、TensorFlow。

(2) 视频数据加载

  • 格式: MP4、AVI 等。
  • 前处理:
    • 抽帧、调整帧率。
    • 归一化。
  • 库支持: OpenCV、FFmpeg、Decord。

(3) 文本数据加载

  • 格式: JSON、CSV、纯文本。
  • 前处理:
    • 分词、去停用词。
    • 将文本转换为向量(如词嵌入)。
  • 库支持: pandas、NLTK、Hugging Face。

(4) 音频数据加载

  • 格式: WAV、MP3。
  • 前处理:
    • 采样率调整。
    • 转换为梅尔频谱或 MFCC 特征。
  • 库支持: librosa、torchaudio。

5. 数据加载中的常见问题及优化

问题 1:数据加载瓶颈

  • 原因: 数据加载速度不足以跟上推理速度。
  • 优化:
    • 增加 num_workers 使用多线程/多进程。
    • 缓存数据。
    • 使用高效的数据格式(如 TFRecord)。

问题 2:内存占用过高

  • 原因: 一次性加载过多数据。
  • 优化:
    • 使用生成器或流式加载。
    • 控制批量大小。

问题 3:数据预处理耗时长

  • 原因: 复杂的预处理步骤在加载时完成。
  • 优化:
    • 将预处理离线完成,保存为中间格式。
    • 使用 GPU 加速数据预处理(如 DALI)。

6. 高级工具与框架支持

  • NVIDIA DALI(Data Loading Library):

    • 高效的数据加载和预处理库,支持 GPU 加速。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      from nvidia.dali.pipeline import Pipeline
      import nvidia.dali.ops as ops

      class SimplePipeline(Pipeline):
      def __init__(self, batch_size, num_threads, device_id):
      super(SimplePipeline, self).__init__(batch_size, num_threads, device_id)
      self.decode = ops.ImageDecoder(device="mixed")
      self.resize = ops.Resize(resize_x=224, resize_y=224)

      def define_graph(self):
      images = self.decode(self.input)
      return self.resize(images)

      pipe = SimplePipeline(batch_size=32, num_threads=4, device_id=0)
  • TensorFlow Data API:

    • 支持高效的输入管道。
    • 示例:
      1
      2
      3
      4
      5
      import tensorflow as tf

      dataset = tf.data.Dataset.list_files("images/*.jpg")
      dataset = dataset.map(lambda x: preprocess(x))
      dataset = dataset.batch(32).prefetch(tf.data.AUTOTUNE)

总结

数据加载在深度学习模型部署中至关重要。通过高效的读取、解析、转换、批量加载和预处理,能够显著提升模型推理的性能与可靠性。根据具体场景选择合适的工具和优化策略,可以大大改善部署效果。

图像数据转为模型可用的张量格式 详解

将图像数据转换为深度学习模型可用的张量格式,是图像处理和模型前处理中的关键步骤。不同的深度学习框架(如 TensorFlow、PyTorch)对张量的具体格式要求略有不同,但总体过程类似。

以下是将图像数据转为模型可用的张量格式的详细步骤和注意事项:


1. 张量的基本要求

张量是一个多维数组,常见的格式包括:

  1. 形状(Shape): 模型通常要求特定形状的输入,例如 (Batch, Channels, Height, Width)(Batch, Height, Width, Channels)
  2. 数据类型(Data Type): 常用 float32float16
  3. 数值范围:
    • 一般要求归一化为 [0, 1] 或标准化为均值为 0、方差为 1。
    • 部分模型(如 ResNet、MobileNet)可能要求特定的归一化范围或通道顺序。

2. 转换步骤

(1) 读取图像数据

  • 使用图像处理库读取图像文件,将其转化为像素矩阵(通常是 Height x Width x Channels 格式)。
  • 常用库:
    • OpenCV (cv2): 默认读取为 BGR 格式。
    • Pillow (PIL): 默认读取为 RGB 格式。

示例(读取图片):

1
2
from PIL import Image
image = Image.open("image.jpg") # 使用 Pillow 读取图片

(2) 调整尺寸

  • 深度学习模型通常要求固定尺寸的输入图像,例如 224x224
  • 可以通过调整大小(resize)、裁剪(crop)、填充(pad)等方式实现。

示例(调整大小):

1
image = image.resize((224, 224))  # 调整到 224x224

(3) 转换为数组

  • 将图像对象转换为数值数组(通常为 NumPy 数组)。

示例(Pillow 转 NumPy):

1
2
import numpy as np
image_array = np.array(image) # 转换为 NumPy 数组

(4) 归一化或标准化

  • 将像素值范围从 [0, 255] 转换为 [0, 1] 或 [-1, 1]。
  • 如果模型要求,可以使用特定均值和标准差进行标准化。

示例(归一化为 [0, 1]):

1
image_array = image_array / 255.0

示例(标准化):

1
2
3
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
image_array = (image_array - mean) / std

(5) 调整通道顺序

  • 不同框架要求的张量通道顺序可能不同:
    • PyTorch: 通道优先,即 (Channels, Height, Width)
    • TensorFlow/Keras: 通道最后,即 (Height, Width, Channels)
  • 可以通过 transpose 改变通道顺序。

示例(HWC 转为 CHW):

1
image_array = np.transpose(image_array, (2, 0, 1))  # 从 HWC 转为 CHW

(6) 转换为张量

  • 将 NumPy 数组转换为深度学习框架支持的张量。
  • 框架示例:
    • PyTorch:torch.Tensor
    • TensorFlow:tf.convert_to_tensor

示例(PyTorch 张量转换):

1
2
3
import torch
image_tensor = torch.tensor(image_array, dtype=torch.float32) # 转为张量
image_tensor = image_tensor.unsqueeze(0) # 增加 Batch 维度

示例(TensorFlow 张量转换):

1
2
3
import tensorflow as tf
image_tensor = tf.convert_to_tensor(image_array, dtype=tf.float32) # 转为张量
image_tensor = tf.expand_dims(image_tensor, axis=0) # 增加 Batch 维度

3. 不同框架的常见操作

(1) PyTorch

使用 torchvision.transforms 提供了高效的图像预处理工具。

示例:

1
2
3
4
5
6
7
8
9
10
11
from torchvision import transforms
from PIL import Image

transform = transforms.Compose([
transforms.Resize((224, 224)), # 调整大小
transforms.ToTensor(), # 转为张量并归一化为 [0, 1]
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 标准化
])

image = Image.open("image.jpg")
image_tensor = transform(image).unsqueeze(0) # 增加 Batch 维度

(2) TensorFlow

使用 tf.image 提供了一系列操作。

示例:

1
2
3
4
5
6
7
import tensorflow as tf

image = tf.io.read_file("image.jpg")
image = tf.image.decode_jpeg(image, channels=3)
image = tf.image.resize(image, [224, 224]) # 调整大小
image = image / 255.0 # 归一化
image = tf.expand_dims(image, axis=0) # 增加 Batch 维度

4. 常见问题与优化

问题 1:通道顺序错误

  • 模型可能因通道顺序不同而报错(如 TensorFlow 使用 NHWC,而 PyTorch 使用 NCHW)。
  • 解决: 确认模型要求并调整顺序。

问题 2:未归一化或标准化

  • 部分模型对输入的数值范围敏感,未归一化可能导致推理结果异常。
  • 解决: 检查模型要求并按需处理。

问题 3:性能瓶颈

  • 数据加载和预处理可能成为瓶颈,尤其是在实时推理中。
  • 解决:
    • 使用多线程/多进程。
    • 将预处理步骤迁移到 GPU(如 NVIDIA DALI)。
    • 提前预处理并保存为高效格式(如 TFRecord)。

5. 总结

将图像数据转为模型可用的张量格式是模型推理的关键步骤,核心流程包括:

  1. 读取图像数据(文件、内存或流式读取)。
  2. 调整尺寸、归一化、标准化。
  3. 转换通道顺序和数据类型。
  4. 将数据打包为张量格式并加入批量维度。

不同框架提供了便利的工具和 API 支持,高效实现图像的张量化,能够显著提升模型的推理性能和兼容性。

前处理中的归一化 详解

深度学习前处理中的归一化详解

归一化是深度学习模型训练和推理中的重要步骤之一,旨在将输入数据的特征值范围调整到特定范围或分布,以提高模型的收敛速度和预测稳定性。本文将详细解析归一化的概念、目的、方法、应用场景和注意事项。


1. 为什么需要归一化?

  1. 减少特征尺度差异:

    • 输入数据可能具有不同的特征范围(例如像素值范围为 [0, 255],而温度值范围为 [-30, 50])。
    • 归一化可以让特征值具有相似的尺度,避免某些特征对模型的影响过大。
  2. 加速模型收敛:

    • 无归一化的数据可能导致梯度下降过程缓慢或震荡,归一化能够稳定梯度下降。
  3. 增强数值稳定性:

    • 极端数值可能导致计算不稳定,归一化可以避免溢出或下溢。
  4. 适应特定模型需求:

    • 某些预训练模型要求输入数据在特定范围(如 [0, 1][-1, 1])或具有特定的均值和标准差。

2. 常见的归一化方法

归一化的方法根据应用场景和数据类型有所不同,以下是常见的归一化技术。

(1) Min-Max 归一化

  • 公式:
    [
    x_{\text{norm}} = \frac{x - x_{\min}}{x_{\max} - x_{\min}}
    ]

  • 范围: 通常将数据归一化到 [0, 1],或通过线性变换调整为其他范围(如 [-1, 1])。

  • 应用场景:

    • 图像像素值归一化到 [0, 1]
    • 特征值范围固定的数据。
  • 示例:

    1
    2
    3
    4
    5
    6
    import numpy as np
    data = np.array([50, 100, 150, 200])
    data_min = np.min(data)
    data_max = np.max(data)
    normalized_data = (data - data_min) / (data_max - data_min)
    print(normalized_data) # 输出 [0. , 0.333, 0.666, 1. ]

(2) 均值-标准差标准化(Z-Score 标准化)

  • 公式:
    [
    x_{\text{norm}} = \frac{x - \mu}{\sigma}
    ]
    其中 ( \mu ) 是均值,( \sigma ) 是标准差。

  • 范围: 数据转换为均值为 0,标准差为 1 的分布。

  • 应用场景:

    • 特征值分布不固定(如自然语言处理中的词向量)。
    • 深度学习预训练模型(如 ImageNet 预训练模型)常使用固定均值和标准差归一化。
  • 示例:

    1
    2
    3
    4
    mean = np.mean(data)
    std = np.std(data)
    standardized_data = (data - mean) / std
    print(standardized_data)

(3) 归一化到特定均值和标准差

  • 公式:
    [
    x_{\text{norm}} = \frac{x - \mu}{\sigma}
    ]
    并根据目标分布调整:
    [
    x_{\text{scaled}} = x_{\text{norm}} \times \sigma_{\text{target}} + \mu_{\text{target}}
    ]

  • 应用场景:

    • 针对预训练模型(如 ResNet、VGG 等),调整到特定的均值和标准差。
  • 示例(PyTorch):

    1
    2
    from torchvision import transforms
    transform = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

(4) 对数归一化(Log Normalization)

  • 公式:
    [
    x_{\text{norm}} = \log(x + 1)
    ]

  • 应用场景:

    • 数据分布具有较大偏态(如高光强度图像、文档字频统计)。
  • 注意:

    • 输入值必须非负。
  • 示例:

    1
    log_normalized_data = np.log(data + 1)

(5) 最大绝对值归一化

  • 公式:
    [
    x_{\text{norm}} = \frac{x}{\max(|x|)}
    ]
  • 范围: 归一化到 [-1, 1]
  • 应用场景:
    • 数据具有正负值,但范围不固定。

(6) L2 归一化

  • 公式:
    [
    x_{\text{norm}} = \frac{x}{|x|_2}
    ]
    其中 ( |x|_2 = \sqrt{\sum x_i^2} )。
  • 范围: 单位向量,适合向量空间建模。
  • 应用场景:
    • 特征向量的相似性计算(如余弦相似度)。

3. 图像数据中的归一化

在图像处理中,归一化的目标是将原始像素值(通常是 [0, 255])转换为深度学习模型所需的格式。

(1) 常见归一化方法

  1. 归一化到 [0, 1]
    • 公式:x / 255.0
    • 应用于大多数深度学习框架。
  2. 归一化到 [-1, 1]
    • 公式:(x / 127.5) - 1
    • 常用于 GAN 和部分图像处理模型。
  3. 均值和标准差标准化:
    • 对应 ImageNet 的 RGB 通道均值和标准差:
      • 均值:[0.485, 0.456, 0.406]
      • 标准差:[0.229, 0.224, 0.225]

(2) 示例代码

归一化到 [0, 1]

1
2
3
import numpy as np
image = np.random.randint(0, 256, (224, 224, 3), dtype=np.uint8)
image_normalized = image / 255.0

归一化到特定均值和标准差:

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

transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 假设 image 是一个 PIL.Image 对象
normalized_image = transform(image)

4. 深度学习框架中的归一化工具

(1) PyTorch

torchvision.transforms.Normalize

  • 用法:
    1
    transforms.Normalize(mean, std)

(2) TensorFlow/Keras

tf.image.per_image_standardization 或手动实现归一化。


5. 注意事项

  1. 模型需求: 确保归一化过程符合模型的预处理要求。
  2. 一致性: 训练和推理时的归一化方式必须一致。
  3. 异常值处理: 对异常值进行裁剪或忽略,避免对归一化结果产生不利影响。

归一化是模型预处理中的关键步骤,能够显著提升模型性能。根据不同场景选择合适的归一化方法,有助于稳定和加速训练,同时提高推理精度。

归一化

  • 常用的方法是通过对原始数据进行线性变换把数据映射到 [0, 1] 或 [-1, 1]之间。主要是:
    • 为了数据处理方便,更加便捷快速。
    • 把有量纲表达式变为无量纲表达式,便于不同单位或量级的指标能够进行比较和加权。
  • 归一化是一种简化计算的方式,即将有量纲的表达式,经过变换,化为无量纲的表达式,成为 纯量
  • 变换函数为:
    归一化线性变换公式

标准化

  • 常用的方法是 z-score 标准化,经过处理后的数据均值为0,标准差为1
  • 在机器学习中,我们可能要处理不同种类的资料。例如,音讯和图片上的像素值,这些资料可能是高纬度的,资料标准化后使每个特征中的数值平均变为0(将每个特征的值都减掉原始资料中该特征的平均),标准差变为1,这个方法被广泛的使用在许多机器学习中,例如:支持向量机,逻辑回归和类神经网络
    标准化线性变换公式

归一化和标准化的区别

  • 归一化是将样本的特征值转换到同一量纲下把数据映射到 [0, 1]或者 [-1, 1]区间内,仅由变量的极值决定,因区间放缩法是归一化的一种。
  • 标准化是依照特征矩阵的列处理数据,其通过求z-score的方法,转换为标准正态分布,和整体样本分布相关,每个样本点都能对标准化产生影响。
  • 它们的相同点在于都能取消由于量纲不同引起的误差;都是一种线性变换,都是对向量X按照比例压缩再进行平移。

标准化和中心化的区别

  • 标准化是原始分数减去平均数然后除以标准差
  • 中心化是原始分数减去平均数。所以一般流程为先中心化再标准化。

无量纲

  • 无量纲,是通过某种方法能去表实际过程中的单位,从而简化计算。

为什么要归一化/标准化

  • 归一化/标准化实质是一种线性变换,线性变换有很多良好的性质,这些性质决定了对数据改变后不会造成失效,反而能提高数据的表现,这些性质是归一化/标准化的前提。比如有一个很重要的性质:线性变换不会改变原始数据的数值排序。

  • 简单来说有以下好处

    • 由于原始数据值的范围差异很大,因此在某些机器学习算法中,如果没有归一化,目标函数将无法正常工作。

cv::Rect 类 详解

cv::Rect 是 OpenCV 中的一个类,表示一个矩形区域。它常用于图像处理中的矩形区域定义,尤其是在图像裁剪、目标检测、区域分析等任务中。cv::Rect 类非常简洁和高效,提供了多种构造方法和常用操作,使得矩形区域的处理变得简单方便。


cv::Rect 类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class cv::Rect
{
public:
// 4个构造函数
Rect(); // 默认构造函数
Rect(int _x, int _y, int _width, int _height); // 使用坐标和宽高构造矩形
Rect(const Point& pt1, const Point& pt2); // 使用对角线两个点构造矩形
Rect(const Rect& r); // 拷贝构造函数

// 成员变量
int x; // 矩形左上角的 x 坐标
int y; // 矩形左上角的 y 坐标
int width; // 矩形的宽度
int height;// 矩形的高度
};

参数详解

  1. **x**:

    • 矩形左上角的 x 坐标。
  2. **y**:

    • 矩形左上角的 y 坐标。
  3. **width**:

    • 矩形的宽度,表示矩形水平方向的长度。
  4. **height**:

    • 矩形的高度,表示矩形垂直方向的长度。

构造方法

  1. 默认构造函数

    1
    Rect();  // 默认值 x=0, y=0, width=0, height=0
    • 创建一个默认的矩形,通常是 (0, 0) 为左上角,宽度和高度都为 0。
  2. 指定坐标和宽高构造矩形

    1
    Rect(int _x, int _y, int _width, int _height);
    • 通过指定矩形的左上角 (x, y) 和矩形的 width(宽度)和 height(高度)来构造矩形。
  3. 通过对角线两个点构造矩形

    1
    Rect(const Point& pt1, const Point& pt2);
    • 使用两个对角点 pt1pt2 来创建矩形,pt1pt2 表示矩形的对角线的两个点。
  4. 拷贝构造函数

    1
    Rect(const Rect& r);
    • 通过另一个矩形对象来拷贝构造一个新的矩形。

成员函数

  1. **area()**:

    • 返回矩形的面积(即 width * height)。
    1
    int area() const;
  2. **empty()**:

    • 检查矩形是否为空(即宽度或高度为 0)。
    1
    bool empty() const;
  3. **br()**:

    • 返回矩形的右下角点。
    1
    Point br() const;  // 返回 (x + width, y + height)
  4. **tl()**:

    • 返回矩形的左上角点。
    1
    Point tl() const;  // 返回 (x, y)

常用操作

  1. 矩形相加

    • 两个矩形相加,得到一个包含两个矩形的最小外接矩形。
    1
    2
    3
    Rect r1(0, 0, 10, 10);
    Rect r2(5, 5, 10, 10);
    Rect r3 = r1 | r2; // 得到一个包含两个矩形的外接矩形
  2. 矩形相交

    • 得到两个矩形的交集部分,返回一个新的矩形。
    1
    2
    3
    Rect r1(0, 0, 10, 10);
    Rect r2(5, 5, 10, 10);
    Rect r3 = r1 & r2; // 得到两个矩形的交集
  3. 矩形移动

    • 移动矩形的位置。
    1
    2
    3
    Rect r(10, 20, 30, 40);
    r.x += 5; // 修改 x 坐标
    r.y += 5; // 修改 y 坐标
  4. 矩形包含检测

    • 判断一个点是否在矩形内,或者一个矩形是否包含另一个矩形。
    1
    2
    3
    4
    5
    6
    7
    Rect r(10, 10, 50, 50);
    Point pt(30, 30);
    bool inside = r.contains(pt); // 判断点 (30, 30) 是否在矩形内

    Rect r1(10, 10, 50, 50);
    Rect r2(20, 20, 10, 10);
    bool contained = r1.contains(r2); // 判断 r2 是否完全包含在 r1 中

常见应用场景

  1. 图像裁剪

    • 在图像处理任务中,cv::Rect 用于指定感兴趣区域(ROI)。通过矩形定义裁剪区域,可以快速地对图像进行裁剪。
    1
    2
    3
    Mat img = imread("image.jpg");
    Rect roi(100, 100, 200, 200); // 定义感兴趣区域
    Mat cropped = img(roi); // 裁剪图像
  2. 目标检测

    • 在目标检测任务中,通常使用矩形框来表示检测到的目标。例如,检测到的人脸、车辆或其他物体通常会使用矩形框进行标记。
  3. 图像窗口和标注

    • cv::Rect 用于表示图像中的标注区域或绘制图像的窗口。在 OpenCV 中经常会使用矩形框来标记特定区域,如绘制矩形框标记目标。
  4. 区域分析

    • 在图像分析中,cv::Rect 常用于表示分析的区域,尤其是在分割、提取特征等任务中。

示例代码

创建矩形并输出其属性

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
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;

int main() {
// 创建矩形
Rect r1(10, 20, 30, 40);
cout << "Rect 1: (" << r1.x << ", " << r1.y << ", " << r1.width << ", " << r1.height << ")" << endl;

// 计算矩形面积
cout << "Area: " << r1.area() << endl;

// 判断矩形是否为空
if (r1.empty()) {
cout << "Rect is empty" << endl;
} else {
cout << "Rect is not empty" << endl;
}

// 获取矩形的右下角
Point br = r1.br();
cout << "Bottom-right corner: (" << br.x << ", " << br.y << ")" << endl;

return 0;
}

输出

1
2
3
4
Rect 1: (10, 20, 30, 40)
Area: 1200
Rect is not empty
Bottom-right corner: (40, 60)

总结

  • cv::Rect 是一个简单而强大的类,用于表示矩形区域,在图像处理、目标检测、图像裁剪、区域分析等多种应用中都有广泛的用途。
  • 它提供了多种构造方法、成员函数和常用操作,使得矩形的使用非常方便。
  • cv::Rect 主要用于图像处理中的区域定义、裁剪、目标检测、区域计算等任务,是 OpenCV 中不可或缺的重要类。

摘要

  • onnxruntime库相关学习笔记

onnxruntime库 是什么

ONNX Runtime 是一个高性能推理引擎,专门用于执行基于 ONNX(Open Neural Network Exchange) 格式的机器学习模型。它由 微软(Microsoft) 开发,旨在提供跨平台、高效、灵活的推理支持,支持从云到边缘设备的多种部署场景。


核心特点

  1. 跨平台支持

    • 可在 Windows、Linux、MacOS 上运行,也支持移动设备(iOS 和 Android)和嵌入式系统。
  2. 高性能

    • 提供高效的硬件加速,支持 CPU、GPU,以及特殊硬件(如 Intel OpenVINO、NVIDIA TensorRT)。
  3. 多语言支持

    • 提供 Python、C++、C# 等语言绑定,方便开发者集成到不同语言的项目中。
  4. 灵活性

    • 支持从多种框架(如 PyTorch、TensorFlow、Keras、MXNet)导出的模型。
    • 提供优化工具(如 onnxruntime-tools),帮助提升模型推理性能。
  5. 云与边缘兼容

    • 适用于云端服务和边缘设备(如 IoT 和移动端),满足多种场景的部署需求。

主要功能

  1. 模型推理(Inference)

    • 通过加载 ONNX 格式的模型,快速进行预测或分类。
    • 示例(Python):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      import onnxruntime as ort

      # 加载模型
      session = ort.InferenceSession("model.onnx")

      # 准备输入数据
      input_data = {"input": some_numpy_array}

      # 推理
      output = session.run(None, input_data)
      print(output)
  2. 硬件加速支持

    • 自动选择最优的硬件执行提供程序(Execution Provider),如:
      • CPU(默认)
      • GPU(CUDA、DirectML)
      • 专用加速(TensorRT、OpenVINO 等)
  3. 动态与静态模型支持

    • 兼容静态计算图和动态计算图模型。
  4. 优化和量化

    • 提供模型优化工具(如模型裁剪、量化)以减少模型大小,提高推理速度。

使用场景

  1. Web 服务:部署深度学习模型进行实时推理(如聊天机器人、图像分类服务)。
  2. 移动应用:将经过优化的 ONNX 模型嵌入到移动端以进行离线预测。
  3. 边缘设备:在嵌入式系统中运行 ONNX 模型(如工业物联网传感器)。
  4. 高性能计算:利用 GPU 或其他加速器大规模推理。

安装

使用 pip 安装 ONNX Runtime 的 Python 包:

1
pip install onnxruntime

如果需要 GPU 支持,可以安装 GPU 版本:

1
pip install onnxruntime-gpu

ONNX 的来源

ONNX 是一种开放的深度学习模型格式,支持不同框架间的模型互操作。通过将模型转换为 ONNX 格式,ONNX Runtime 能够高效加载并执行这些模型。


ONNX Runtime 的优势

  • 简化开发和部署流程:无需为每种硬件配置单独编写代码。
  • 兼容多框架模型:同一推理引擎支持从多个框架导出的模型。
  • 高效运行:内置优化能力,可在多种硬件上实现卓越性能。

如果你有更具体的需求(如如何优化模型或特定硬件部署),可以告诉我,我会详细解答!

onnxruntime C++接口 详解

ONNX Runtime 提供了功能强大的 C++ 接口,用于高性能地加载和推理 ONNX 模型。以下是对其 C++ 接口的详细解析,包括核心概念、关键类、常用方法及完整示例。


核心概念与类

ONNX Runtime 的 C++ API 基于以下核心组件:

  1. **Ort::Env**:

    • 表示 ONNX Runtime 的环境,所有会话共享一个环境。
    • 主要负责初始化 ONNX Runtime,设置日志等。
  2. **Ort::Session**:

    • 表示一个模型会话,用于加载和执行 ONNX 模型。
    • 会话加载模型后可以多次推理。
  3. **Ort::MemoryInfo**:

    • 描述用于输入/输出张量的内存分配信息。
  4. **Ort::Allocator**:

    • 管理内存分配,用于推理中获取分配的结果张量。
  5. **Ort::Value**:

    • 表示 ONNX 模型的输入和输出张量。

关键步骤与方法

  1. 初始化环境

    • 使用 Ort::Env 创建 ONNX Runtime 环境:
      1
      Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXRuntime");
  2. 加载模型

    • 使用 Ort::Session 加载 .onnx 模型文件:
      1
      2
      3
      Ort::SessionOptions session_options;
      session_options.SetIntraOpNumThreads(1);
      Ort::Session session(env, "model.onnx", session_options);
  3. 准备输入和输出

    • 创建输入张量并指定维度和数据类型:
      1
      2
      3
      4
      5
      std::vector<int64_t> input_shape = {1, 3, 224, 224}; // Example shape for an image
      std::vector<float> input_data(1 * 3 * 224 * 224, 1.0f); // Initialize with dummy data
      Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
      Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
      memory_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size());
  4. 获取模型输入/输出名称

    • 用于绑定输入和输出:
      1
      2
      3
      size_t num_input_nodes = session.GetInputCount();
      const char* input_name = session.GetInputName(0, allocator);
      const char* output_name = session.GetOutputName(0, allocator);
  5. 执行推理

    • 调用 Run 方法执行推理:
      1
      2
      3
      4
      5
      6
      7
      8
      auto output_tensors = session.Run(
      Ort::RunOptions{nullptr}, // 默认运行选项
      &input_name, // 输入名称
      &input_tensor, // 输入张量
      1, // 输入数量
      &output_name, // 输出名称
      1 // 输出数量
      );
  6. 处理输出

    • 输出通常是一个张量,可以通过 GetTensorMutableData 获取数据指针:
      1
      float* output_data = output_tensors[0].GetTensorMutableData<float>();

完整示例代码

下面是一个完整的 C++ 程序,演示如何使用 ONNX Runtime 推理一个 ONNX 模型:

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
#include <onnxruntime/core/providers/cpu/cpu_provider_factory.h>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <vector>
#include <iostream>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXRuntime");
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1);

// 加载 ONNX 模型
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);

// 获取模型输入信息
Ort::AllocatorWithDefaultOptions allocator;
const char* input_name = session.GetInputName(0, allocator);
std::vector<int64_t> input_shape = {1, 3, 224, 224};
std::vector<float> input_data(1 * 3 * 224 * 224, 1.0f); // Dummy input

// 创建输入张量
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
memory_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size());

// 获取模型输出信息
const char* output_name = session.GetOutputName(0, allocator);

// 执行推理
auto output_tensors = session.Run(
Ort::RunOptions{nullptr},
&input_name, &input_tensor, 1,
&output_name, 1);

// 获取输出张量数据
float* output_data = output_tensors[0].GetTensorMutableData<float>();

// 打印输出结果(示例)
std::cout << "Output value: " << output_data[0] << std::endl;

return 0;
}

优化与扩展

  1. 多线程优化

    • 使用 session_options.SetIntraOpNumThreads()SetInterOpNumThreads() 配置多线程。
  2. GPU 支持

    • 安装 ONNX Runtime 的 GPU 版本,并通过 session_options.AppendExecutionProvider_CUDA() 启用 GPU。
  3. 动态输入支持

    • 对于不确定输入形状的模型,可在运行时动态设置张量维度。

常见问题

  1. 模型加载失败

    • 确保模型是有效的 ONNX 格式。
    • 检查模型是否包含动态维度(需要正确处理)。
  2. 输出数据为空

    • 确认输入张量的数据和维度与模型的预期一致。
  3. 硬件加速问题

    • 检查是否正确安装了支持 GPU 的 ONNX Runtime 版本。

如果有具体需求或问题,比如如何处理动态输入、支持特定硬件等,可以进一步讨论!

C++ onnxruntime Ort::Env 详解

Ort::Env 是 ONNX Runtime 的 C++ API 中的一个核心类,用于初始化 ONNX Runtime 环境并设置相关的全局配置。ONNX Runtime 是一个高性能的推理引擎,Ort::Env 是使用 C++ API 的入口点之一。

以下是关于 Ort::Env 的详细解析:


Ort::Env 的功能

  1. 初始化 ONNX Runtime

    • Ort::Env 是一个 RAII(资源获取即初始化)类,在创建时初始化 ONNX Runtime,销毁时释放相关资源。
    • 在一个应用中,只需创建一个 Ort::Env 实例即可。
  2. 配置日志和线程池

    • 允许设置日志级别、日志输出路径等全局配置。
    • 配置线程池的行为(如并行度)。
  3. 管理会话的上下文环境

    • 所有的推理会话 (Ort::Session) 都依赖于 Ort::Env 的存在。

Ort::Env 的构造函数

Ort::Env 提供多种构造方式,允许用户灵活设置日志和多线程相关的参数。

1. 简单构造

1
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXRuntime");
  • 参数说明

    • ORT_LOGGING_LEVEL_WARNING:日志级别,ONNX Runtime 只记录警告及更高级别的日志。
    • "ONNXRuntime":日志的默认记录器名称,用于区分日志源。
  • 日志级别选项

    1
    2
    3
    4
    5
    ORT_LOGGING_LEVEL_VERBOSE   // 记录所有日志
    ORT_LOGGING_LEVEL_INFO // 记录信息级别及以上的日志
    ORT_LOGGING_LEVEL_WARNING // 记录警告及以上的日志
    ORT_LOGGING_LEVEL_ERROR // 记录错误及以上的日志
    ORT_LOGGING_LEVEL_FATAL // 记录致命错误日志

2. 高级构造

1
Ort::Env env(ORT_LOGGING_LEVEL_VERBOSE, "ONNXRuntime", OrtThreadingOptions{});
  • 使用 OrtThreadingOptions 配置线程池行为。
  • OrtThreadingOptions 示例
    1
    2
    3
    OrtThreadingOptions* options = Ort::GetApi().CreateThreadingOptions();
    Ort::GetApi().SetGlobalIntraOpThreadAffinity(options, true);
    Ort::GetApi().ReleaseThreadingOptions(options);

常见用法

初始化 ONNX Runtime 环境

1
2
3
4
5
6
7
8
9
10
#include <onnxruntime/core/providers/cpu/cpu_provider_factory.h>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>

int main() {
// 初始化环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXRuntimeExample");

// 其他操作,如创建会话等
return 0;
}

结合推理会话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <onnxruntime/core/providers/cpu/cpu_provider_factory.h>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>

int main() {
// 初始化环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXRuntimeExample");

// 创建推理会话
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1); // 设置线程数
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

Ort::Session session(env, "model.onnx", session_options);

return 0;
}

Ort::Env 的主要接口

设置日志级别

设置日志的输出级别。

1
env.SetLogLevel(ORT_LOGGING_LEVEL_INFO);

设置线程池选项

可以通过 OrtThreadingOptions 配置线程池行为,如线程数、线程亲和性等。


注意事项

  1. 单实例原则

    • 一个应用中通常只需要一个 Ort::Env 实例。
    • 多个实例可能导致资源冲突或性能问题。
  2. 线程安全性

    • Ort::Env 本身是线程安全的,可以在多线程环境中安全使用。
  3. 销毁时机

    • Ort::Env 实例销毁时,ONNX Runtime 的资源会被自动释放。

完整示例

以下是一个完整的推理流程,展示了如何使用 Ort::Env

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
#include <onnxruntime/core/providers/cpu/cpu_provider_factory.h>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXRuntimeExample");

// 配置会话选项
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(2);
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

// 加载模型
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);

// 模型输入输出信息
size_t num_input_nodes = session.GetInputCount();
size_t num_output_nodes = session.GetOutputCount();

std::cout << "Number of inputs: " << num_input_nodes << std::endl;
std::cout << "Number of outputs: " << num_output_nodes << std::endl;

return 0;
}

如果需要进一步了解某些特性(如日志配置、线程选项等)或其他问题,可以继续提问!

Ort::SessionOptions 详解

Ort::SessionOptions 是 ONNX Runtime C++ API 中的重要类,用于配置推理会话 (Ort::Session) 的运行行为。通过该类,可以设置线程数、优化级别、自定义执行提供程序等。

以下是对 Ort::SessionOptions 的详细解析:


Ort::SessionOptions 的功能

  1. 线程配置

    • 控制并行线程数。
    • 配置线程间的交互模式(单线程或多线程)。
  2. 图优化

    • 优化计算图以提升推理性能。
  3. 执行提供程序

    • 指定使用的计算设备(如 CPU、GPU)。
  4. 自定义选项

    • 支持自定义分配器、上下文、会话行为等。
  5. 内存分配和 IO 绑定

    • 允许用户自定义内存分配器和 IO 绑定。

构造和基本用法

默认构造函数

创建一个默认的 Ort::SessionOptions 对象:

1
Ort::SessionOptions session_options;

主要成员函数

1. 设置线程数

SetIntraOpNumThreads
  • 设置单个运算操作(如矩阵乘法)的最大线程数。
  • 示例:
    1
    session_options.SetIntraOpNumThreads(4);
  • 默认值为 0,表示由 ONNX Runtime 自动决定线程数。
SetInterOpNumThreads
  • 设置多个操作之间的最大线程数。
  • 示例:
    1
    session_options.SetInterOpNumThreads(2);
  • 默认值为 0,表示自动决定线程数。

2. 设置优化级别

SetGraphOptimizationLevel
  • 设置 ONNX 模型图的优化级别。
  • 可用优化级别:
    1
    2
    3
    4
    ORT_DISABLE_ALL            // 禁用所有优化
    ORT_ENABLE_BASIC // 启用基本优化(默认值)
    ORT_ENABLE_EXTENDED // 启用扩展优化(包括内存优化)
    ORT_ENABLE_ALL // 启用所有优化
  • 示例:
    1
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

3. 配置执行提供程序

CPU 提供程序
  • 默认情况下,ONNX Runtime 使用 CPU 提供程序。
  • 如果明确指定:
    1
    2
    3
    #include <onnxruntime/core/providers/cpu/cpu_provider_factory.h>
    Ort::SessionOptions session_options;
    session_options.AppendExecutionProvider_CPU(0); // 0 表示使用默认配置
GPU 提供程序(CUDA)
  • 需要安装 ONNX Runtime 的 CUDA 支持版本。
  • 示例:
    1
    2
    3
    #include <onnxruntime/core/providers/cuda/cuda_provider_factory.h>
    Ort::SessionOptions session_options;
    session_options.AppendExecutionProvider_CUDA(0); // 0 表示 GPU ID

4. 配置内存分配

EnableMemPattern
  • 启用/禁用内存模式优化(默认启用)。
  • 示例:
    1
    session_options.EnableMemPattern(true);
DisableMemPattern
  • 禁用内存模式优化。
  • 示例:
    1
    session_options.DisableMemPattern();

5. 使用分配器

设置分配器(可选)

用户可以指定自定义的内存分配器:

1
2
3
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
OrtAllocator* custom_allocator = ...; // 用户自定义分配器
session_options.AddConfigEntry("memory.custom_allocator", custom_allocator);

6. 打开和关闭调试选项

启用调试

用于调试模型推理的配置。

1
session_options.EnableProfiling("profiling_file.json");
禁用调试
1
session_options.DisableProfiling();

综合示例

以下是一个使用 Ort::SessionOptions 的完整示例,展示了如何配置线程数、优化级别以及提供程序:

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
#include <onnxruntime/core/providers/cpu/cpu_provider_factory.h>
#include <onnxruntime/core/providers/cuda/cuda_provider_factory.h>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXRuntimeExample");

// 创建会话选项
Ort::SessionOptions session_options;

// 配置线程数
session_options.SetIntraOpNumThreads(4);
session_options.SetInterOpNumThreads(2);

// 设置优化级别
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

// 添加执行提供程序(使用 CUDA 或 CPU)
session_options.AppendExecutionProvider_CPU(0); // 使用 CPU 提供程序
// session_options.AppendExecutionProvider_CUDA(0); // 若有 CUDA 支持,则使用 GPU

// 加载模型并创建会话
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);

// 输出模型的输入和输出信息
size_t num_inputs = session.GetInputCount();
size_t num_outputs = session.GetOutputCount();

std::cout << "Model Inputs: " << num_inputs << std::endl;
std::cout << "Model Outputs: " << num_outputs << std::endl;

return 0;
}

注意事项

  1. 线程配置

    • 如果设置了不合理的线程数(如过大或过小),可能导致性能下降或内存问题。
  2. 执行提供程序的顺序

    • ONNX Runtime 会按照添加的顺序选择第一个支持的提供程序。
  3. 图优化

    • 高级优化(如 ORT_ENABLE_ALL)可能导致模型行为的细微变化,需要进行充分测试。
  4. CUDA 提供程序的依赖

    • 使用 GPU 时,请确保系统正确配置了 CUDA 和 cuDNN。
  5. 资源释放

    • Ort::SessionOptions 在生命周期结束时会自动释放资源。

如果需要更深入了解某个功能或遇到问题,可以进一步探讨!

Ort::Session 详解

Ort::Session 是 ONNX Runtime C++ API 的核心类之一,负责加载 ONNX 模型并执行推理操作。一个 Ort::Session 对象对应一个加载的 ONNX 模型,并提供接口来查询模型信息和执行推理。

以下是对 Ort::Session 的详细解析:


Ort::Session 的主要功能

  1. 加载模型

    • 使用 ONNX Runtime 环境 (Ort::Env) 和会话选项 (Ort::SessionOptions) 加载一个 ONNX 模型文件。
  2. 获取模型元信息

    • 查询模型的输入和输出节点、数据类型、维度等。
  3. 执行推理

    • 使用模型的输入数据进行推理,并返回结果。
  4. 管理资源

    • Ort::Session 在其生命周期内负责维护模型相关的资源,并在销毁时自动释放。

构造和基本用法

构造函数

1. 默认构造

1
Ort::Session session(env, "model.onnx", session_options);
  • 参数说明
    • env:一个有效的 Ort::Env 实例。
    • "model.onnx":ONNX 模型文件的路径。
    • session_options:一个 Ort::SessionOptions 对象,用于配置推理行为。

主要成员函数

1. 获取模型输入输出信息

GetInputCountGetOutputCount

  • 分别返回模型的输入和输出节点数量。
  • 示例:
    1
    2
    size_t num_inputs = session.GetInputCount();
    size_t num_outputs = session.GetOutputCount();

GetInputNameGetOutputName

  • 返回指定输入或输出节点的名称。
  • 示例:
    1
    2
    3
    Ort::AllocatorWithDefaultOptions allocator;
    const char* input_name = session.GetInputName(0, allocator);
    const char* output_name = session.GetOutputName(0, allocator);

GetInputTypeInfoGetOutputTypeInfo

  • 返回输入或输出节点的数据类型和维度信息。
  • 示例:
    1
    2
    3
    auto input_info = session.GetInputTypeInfo(0);
    auto input_shape = input_info.GetTensorTypeAndShapeInfo();
    auto input_dims = input_shape.GetShape(); // 获取输入的维度

2. 推理操作

Run

  • 使用输入数据执行推理并返回结果。
  • 原型
    1
    2
    3
    4
    5
    6
    7
    8
    std::vector<Ort::Value> Run(
    Ort::RunOptions& run_options,
    const char* const* input_names,
    const Ort::Value* input_tensors,
    size_t input_count,
    const char* const* output_names,
    size_t output_count
    );
  • 参数说明
    • run_options:推理选项,通常可以传入默认值。
    • input_names:输入节点名称数组。
    • input_tensors:输入数据张量数组。
    • input_count:输入节点数量。
    • output_names:输出节点名称数组。
    • output_count:输出节点数量。
  • 返回值
    • 推理结果的 Ort::Value 对象数组。

综合示例:执行推理

以下代码展示了如何加载模型并执行推理:

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
#include <onnxruntime/core/providers/cpu/cpu_provider_factory.h>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>
#include <vector>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXRuntimeExample");

// 配置会话选项
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1);
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

// 加载模型
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);

// 查询模型输入信息
Ort::AllocatorWithDefaultOptions allocator;
const char* input_name = session.GetInputName(0, allocator);
auto input_info = session.GetInputTypeInfo(0);
auto input_shape = input_info.GetTensorTypeAndShapeInfo().GetShape();

std::cout << "Input Name: " << input_name << std::endl;
std::cout << "Input Shape: ";
for (auto dim : input_shape) std::cout << dim << " ";
std::cout << std::endl;

// 创建输入数据
std::vector<float> input_tensor_values = {1.0f, 2.0f, 3.0f, 4.0f};
std::vector<int64_t> input_dims = {1, 4}; // 假设输入是一个 1x4 的张量
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
allocator, input_tensor_values.data(), input_tensor_values.size(),
input_dims.data(), input_dims.size()
);

// 执行推理
const char* output_name = session.GetOutputName(0, allocator);
std::vector<Ort::Value> output_tensors = session.Run(
Ort::RunOptions{nullptr}, // 默认推理选项
&input_name, &input_tensor, 1, // 输入名和输入张量
&output_name, 1 // 输出名
);

// 解析输出
float* output_data = output_tensors[0].GetTensorMutableData<float>();
auto output_shape = output_tensors[0].GetTensorTypeAndShapeInfo().GetShape();

std::cout << "Output Shape: ";
for (auto dim : output_shape) std::cout << dim << " ";
std::cout << std::endl;

std::cout << "Output Values: ";
for (size_t i = 0; i < output_tensors[0].GetTensorTypeAndShapeInfo().GetElementCount(); ++i) {
std::cout << output_data[i] << " ";
}
std::cout << std::endl;

return 0;
}

注意事项

  1. 线程安全性

    • Ort::Session 是线程安全的,可以在多线程环境中同时使用。
  2. 内存管理

    • 使用 ONNX Runtime 的默认分配器 (Ort::AllocatorWithDefaultOptions) 来管理内存。
    • 所有使用分配器分配的内存需要显式释放或交由分配器管理。
  3. 推理性能

    • 使用 Ort::SessionOptions 配置优化选项(如线程数和图优化级别)可以显著提升推理性能。
  4. 模型输入/输出验证

    • 在执行推理之前,确保输入数据的维度和类型与模型的输入定义一致。
  5. 错误处理

    • 如果模型文件路径错误或输入数据不匹配,ONNX Runtime 会抛出异常,需要进行捕获和处理。

如果需要进一步深入了解 Ort::Session 或相关操作(如自定义提供程序、动态输入处理等),可以继续提问!

Ort::AllocatorWithDefaultOptions 详解

Ort::AllocatorWithDefaultOptions 是 ONNX Runtime C++ API 提供的一个简化工具类,用于分配和管理内存资源,尤其在操作模型输入输出信息时非常常用。

它封装了 ONNX Runtime 的默认分配器,简化了内存分配、释放的操作,是处理 Ort::Session 返回的字符串、张量等动态内存资源时的重要工具。


Ort::AllocatorWithDefaultOptions 的主要功能

  1. 动态分配内存

    • 用于获取模型的输入输出名称、类型信息等,这些信息通常是动态分配的内存。
  2. 简化内存管理

    • Ort::AllocatorWithDefaultOptions 自动处理分配的内存释放,避免手动管理内存的复杂性。
  3. 轻量级

    • 这是一个无状态的 RAII(资源获取即初始化)类,创建和销毁的开销非常小。

用法和接口

1. 构造函数

Ort::AllocatorWithDefaultOptions 无需任何参数即可默认构造:

1
Ort::AllocatorWithDefaultOptions allocator;

2. 常用方法

operator OrtAllocator*

  • 提供一个隐式转换操作符,将对象转换为底层的 OrtAllocator* 类型。
  • 示例:
    1
    OrtAllocator* raw_allocator = allocator;

内存管理功能

  • Ort::AllocatorWithDefaultOptions 自动调用 ONNX Runtime 的默认分配器接口,例如分配和释放内存:
    • 分配内存allocator 在调用 API(如 GetInputName)时会自动分配内存。
    • 释放内存:当 allocator 作用域结束时,分配的内存会被自动释放。

使用场景

1. 获取输入输出名称

在获取模型输入和输出名称时,返回的名称是动态分配的内存,Ort::AllocatorWithDefaultOptions 自动管理这部分内存。

示例:

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
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "Example");

// 加载模型
Ort::SessionOptions session_options;
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);

// 创建默认分配器
Ort::AllocatorWithDefaultOptions allocator;

// 获取输入名称
const char* input_name = session.GetInputName(0, allocator);
std::cout << "Input Name: " << input_name << std::endl;

// 获取输出名称
const char* output_name = session.GetOutputName(0, allocator);
std::cout << "Output Name: " << output_name << std::endl;

return 0;
}

2. 与动态分配相关的 API

许多 ONNX Runtime 的 C++ API 使用动态内存分配,Ort::AllocatorWithDefaultOptions 是处理这些 API 的关键工具。例如:

获取输入输出类型信息

1
2
3
4
5
auto input_info = session.GetInputTypeInfo(0);
auto input_tensor_info = input_info.GetTensorTypeAndShapeInfo();

// 获取输入的维度信息
auto input_dims = input_tensor_info.GetShape();

获取分配器指针

当需要与 ONNX Runtime 的低层 API 交互时,可以通过 Ort::AllocatorWithDefaultOptions 提供的 OrtAllocator* 指针:

1
OrtAllocator* raw_allocator = allocator;

内部工作原理

Ort::AllocatorWithDefaultOptions 基于 ONNX Runtime 的默认分配器实现,以下是它的工作机制:

  1. 初始化默认分配器

    • 使用 ONNX Runtime 的 OrtCreateAllocatorWithDefaultOptions 方法创建分配器。
    • 该分配器负责分配和释放内存。
  2. 自动管理内存

    • 通过 RAII 模式,确保分配器的生命周期与 Ort::AllocatorWithDefaultOptions 实例绑定。
    • 在分配器超出作用域时,自动释放所有相关资源。

注意事项

  1. 作用域管理

    • 确保 Ort::AllocatorWithDefaultOptions 的生命周期覆盖所有需要使用其分配内存的地方。
    • 在使用动态内存后,分配器超出作用域时会自动释放内存。
  2. 多线程使用

    • ONNX Runtime 的默认分配器是线程安全的,可以在多线程环境中使用。
  3. 避免手动释放

    • 如果已经使用 Ort::AllocatorWithDefaultOptions 进行内存分配,无需手动释放内存,否则可能导致重复释放的错误。

完整示例:从模型中提取输入输出信息

以下是一个综合示例,展示如何使用 Ort::AllocatorWithDefaultOptions 获取输入输出名称和类型信息:

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
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>
#include <vector>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "Example");

// 加载模型
Ort::SessionOptions session_options;
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);

// 创建默认分配器
Ort::AllocatorWithDefaultOptions allocator;

// 获取输入信息
size_t num_inputs = session.GetInputCount();
std::cout << "Number of inputs: " << num_inputs << std::endl;

for (size_t i = 0; i < num_inputs; ++i) {
const char* input_name = session.GetInputName(i, allocator);
auto input_info = session.GetInputTypeInfo(i);
auto tensor_info = input_info.GetTensorTypeAndShapeInfo();
auto input_shape = tensor_info.GetShape();

std::cout << "Input " << i << ": " << input_name << std::endl;
std::cout << " Shape: ";
for (auto dim : input_shape) std::cout << dim << " ";
std::cout << std::endl;
}

// 获取输出信息
size_t num_outputs = session.GetOutputCount();
std::cout << "Number of outputs: " << num_outputs << std::endl;

for (size_t i = 0; i < num_outputs; ++i) {
const char* output_name = session.GetOutputName(i, allocator);
auto output_info = session.GetOutputTypeInfo(i);
auto tensor_info = output_info.GetTensorTypeAndShapeInfo();
auto output_shape = tensor_info.GetShape();

std::cout << "Output " << i << ": " << output_name << std::endl;
std::cout << " Shape: ";
for (auto dim : output_shape) std::cout << dim << " ";
std::cout << std::endl;
}

return 0;
}

总结

  • Ort::AllocatorWithDefaultOptions 是处理动态分配内存的便捷工具类,尤其在获取输入输出名称等操作中必不可少。
  • 它与 ONNX Runtime 的默认分配器紧密集成,简化了内存管理。
  • 使用时只需关注业务逻辑,分配器的内存生命周期会自动管理。

如果需要进一步探索其工作机制或更多使用场景,可以继续提问!

Session::GetInputTypeInfo() 详解

Session::GetInputTypeInfo() 是 ONNX Runtime C++ API 中的一个重要方法,用于获取模型某个输入节点的类型和形状信息。通过这个方法,你可以详细了解模型输入的张量数据类型和维度,从而确保输入数据格式符合模型的要求。


函数原型

1
Ort::TypeInfo GetInputTypeInfo(size_t index) const;

参数

  • index:输入节点的索引,从 0 开始。

返回值

  • 返回一个 Ort::TypeInfo 对象,包含输入节点的数据类型和形状信息。

Ort::TypeInfo 的功能

Ort::TypeInfo 是 ONNX Runtime 用于描述节点数据类型和形状的类。通过 TypeInfo 对象,你可以进一步获取以下信息:

  1. 节点是否为张量
  2. 张量的数据类型
  3. 张量的形状

使用方法

以下是一个完整的示例,展示如何使用 GetInputTypeInfo() 获取模型输入节点的类型和形状信息:

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
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>
#include <vector>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "Example");

// 加载模型
Ort::SessionOptions session_options;
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);

// 获取模型的输入节点数量
size_t num_inputs = session.GetInputCount();
std::cout << "Number of inputs: " << num_inputs << std::endl;

// 创建默认分配器
Ort::AllocatorWithDefaultOptions allocator;

// 遍历输入节点,获取类型和形状信息
for (size_t i = 0; i < num_inputs; ++i) {
// 获取输入节点名称
const char* input_name = session.GetInputName(i, allocator);
std::cout << "Input Name: " << input_name << std::endl;

// 获取输入节点类型信息
Ort::TypeInfo input_type_info = session.GetInputTypeInfo(i);
auto tensor_info = input_type_info.GetTensorTypeAndShapeInfo();

// 获取数据类型
ONNXTensorElementDataType input_data_type = tensor_info.GetElementType();
std::cout << "Data Type: " << input_data_type << std::endl;

// 获取输入形状
auto input_shape = tensor_info.GetShape();
std::cout << "Shape: ";
for (auto dim : input_shape) {
std::cout << dim << " ";
}
std::cout << std::endl;
}

return 0;
}

Ort::TensorTypeAndShapeInfo 的功能

通过 TypeInfo 对象的 GetTensorTypeAndShapeInfo() 方法,可以获取输入节点的详细张量信息,返回的是一个 Ort::TensorTypeAndShapeInfo 对象。

1. 获取张量数据类型

1
ONNXTensorElementDataType GetElementType() const;
  • 返回值是一个枚举值,表示张量的数据类型。例如:
    • ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT 表示 float 类型。
    • ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64 表示 int64_t 类型。

2. 获取张量形状

1
std::vector<int64_t> GetShape() const;
  • 返回一个包含张量每个维度大小的 std::vector<int64_t>
  • 对于动态维度,返回的值为 -1

3. 获取元素总数

1
size_t GetElementCount() const;
  • 返回张量中元素的总数量,等于所有维度大小的乘积。
  • 如果张量包含动态维度,无法计算总数时会抛出异常。

输出示例

假设我们加载的模型有一个输入节点,名称为 input_0,数据类型为 float,形状为 [1, 3, 224, 224]。运行上述代码的输出如下:

1
2
3
4
Number of inputs: 1
Input Name: input_0
Data Type: 1
Shape: 1 3 224 224
  • 数据类型 1 对应 ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT
  • 形状 [1, 3, 224, 224] 表示张量的维度。

注意事项

  1. 动态维度

    • 如果模型的某个维度是动态的,返回的值将是 -1。在这种情况下,需要根据具体推理场景动态确定实际的维度。
  2. 节点索引越界

    • 如果输入 index 超出范围(大于或等于输入节点数量),会抛出异常,需要确保索引有效。
  3. 数据类型匹配

    • 确保推理时提供的输入张量数据类型与模型的输入要求一致,否则会导致运行时错误。
  4. 多输入模型

    • 如果模型有多个输入,需要分别对每个输入调用 GetInputTypeInfo() 获取信息。

综合小结

  • Session::GetInputTypeInfo() 是了解模型输入节点属性的核心方法。
  • 它返回一个 Ort::TypeInfo 对象,进一步提供张量的数据类型和形状信息。
  • 使用时需要注意动态维度的处理和数据类型的匹配。

如需进一步了解 Ort::TypeInfo 或动态输入处理,可以继续提问!

TensorTypeAndShapeInfo::GetShape() 详解

TensorTypeAndShapeInfo::GetShape() 是 ONNX Runtime C++ API 中的一个方法,主要用于获取 ONNX 模型的张量输入或输出的形状信息。它返回一个包含张量维度大小的 std::vector<int64_t>,这些维度可以是静态或动态的。


函数原型

1
std::vector<int64_t> GetShape() const;

参数

  • 此方法无参数,它直接作用于 TensorTypeAndShapeInfo 对象。

返回值

  • 返回一个 std::vector<int64_t>,其中每个元素表示张量在该维度上的大小。如果维度是动态的,该维度的大小为 -1

功能和用途

TensorTypeAndShapeInfo::GetShape() 方法的主要作用是返回张量的形状信息,这些信息对于输入数据的格式化和验证至关重要。它允许你获取张量在各个维度上的大小,通常用于确定输入数据或输出数据是否符合模型的要求。

  • 静态维度:对于静态维度(在模型加载时确定的维度),返回的是实际的维度大小。
  • 动态维度:如果张量的某些维度是动态的(例如在模型训练时,模型的输入维度没有固定大小),返回的维度值为 -1,表示该维度的大小是动态变化的,通常需要根据输入数据在推理时动态确定。

使用示例

假设你已经加载了一个 ONNX 模型并且调用了 GetInputTypeInfo()GetOutputTypeInfo() 来获取模型输入或输出的张量类型信息。接下来,你可以使用 GetShape() 获取张量的形状。

示例代码:获取输入形状

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
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>
#include <vector>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "Example");

// 加载模型
Ort::SessionOptions session_options;
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);

// 创建默认分配器
Ort::AllocatorWithDefaultOptions allocator;

// 获取输入数量
size_t num_inputs = session.GetInputCount();
std::cout << "Number of inputs: " << num_inputs << std::endl;

// 遍历输入节点,获取形状
for (size_t i = 0; i < num_inputs; ++i) {
// 获取输入类型信息
Ort::TypeInfo input_type_info = session.GetInputTypeInfo(i);
auto tensor_info = input_type_info.GetTensorTypeAndShapeInfo();

// 获取张量形状
std::vector<int64_t> input_shape = tensor_info.GetShape();
std::cout << "Input " << i << " shape: ";
for (auto dim : input_shape) {
std::cout << dim << " ";
}
std::cout << std::endl;
}

return 0;
}

输出示例

假设模型的输入是一个 4D 张量,形状为 [1, 3, 224, 224],那么输出将类似于:

1
2
Number of inputs: 1
Input 0 shape: 1 3 224 224

如果输入的某个维度是动态的,例如批量大小(batch size)是动态的,那么输出可能是:

1
Input 0 shape: -1 3 224 224

-1 表示批量大小是动态的,推理时由输入数据的批量大小来确定。


GetShape() 的返回值解析

GetShape() 返回的 std::vector<int64_t> 每个元素表示一个维度的大小。下面是关于维度和返回值的一些常见情况:

1. 静态维度

如果模型的输入或输出具有固定的维度,则 GetShape() 返回的是该张量的具体形状。例如:

  • 对于一个 1x3x224x224 的图像张量(通常用于图像分类任务),GetShape() 返回:

    1
    {1, 3, 224, 224}
  • 对于一个二维张量(例如一个大小为 100x50 的矩阵),GetShape() 返回:

    1
    {100, 50}

2. 动态维度

如果模型的某些维度是动态的,则返回值中的该维度将是 -1,表示该维度在推理时会根据实际输入数据动态确定。常见的动态维度包括批量大小(batch size)。

  • 假设模型的输入形状是 [batch_size, 3, 224, 224],其中 batch_size 是动态的。GetShape() 可能返回:

    1
    {-1, 3, 224, 224}

    在这个例子中,-1 表示 batch_size 是动态的,具体的值将在实际推理时由输入数据的大小确定。

  • 如果模型具有 sequence_length 等动态维度,类似地,返回的形状可能为:

    1
    {10, -1}  // 10 是固定维度,-1 表示第二维是动态的

3. 无效维度

如果张量没有形状(例如是一个标量),GetShape() 返回的向量为空:

1
{}  // 无形状的张量

常见问题和注意事项

  1. 动态维度的处理

    • 对于动态维度,返回的形状中相应位置的值为 -1,你无法直接知道该维度的大小。通常,实际的维度会在推理时根据输入的大小动态确定。
  2. 如何获取元素数量

    • 如果模型的输入具有静态形状,可以通过 GetShape() 返回的维度大小计算张量的元素数量。
    • 例如,假设返回的形状是 [1, 3, 224, 224],那么元素总数为 1 * 3 * 224 * 224 = 150528
  3. 性能优化

    • 当处理动态维度时,你可能需要动态调整输入的大小。通过 GetShape() 获取输入形状后,你可以根据实际情况调整输入数据的维度。
  4. 错误处理

    • 如果在尝试获取形状时遇到问题(如 session.GetInputTypeInfo() 返回错误),确保模型输入的维度与预期一致。

总结

TensorTypeAndShapeInfo::GetShape() 是 ONNX Runtime 中获取张量形状信息的关键方法。它返回张量的维度信息,帮助你在执行推理之前确认输入和输出数据的结构。了解如何处理静态和动态维度,对于正确地为模型准备输入数据至关重要。

如果你对 GetShape() 或其他 ONNX Runtime 方法有进一步的疑问,欢迎继续提问!

[1, 3, 224, 224] 表示的什么

[1, 3, 224, 224] 是一个形状(Shape)表示,通常用于描述图像张量的维度。在计算机视觉和深度学习任务中,这种表示方法是非常常见的。让我们逐步分析它:

[1, 3, 224, 224] 解释

  • 1:批量大小(Batch Size)

    • 这个数字表示图像数据批次中包含的图像数量。在这个例子中,批量大小为 1,意味着这只是一个单独的图像。批量大小是深度学习模型训练和推理过程中常用的概念。
  • 3:颜色通道数(Channels)

    • 这个数字表示图像的颜色通道数。在这种情况下,3 代表图像是 RGB 彩色图像,意味着图像包含三个颜色通道:红色(Red)、绿色(Green)和蓝色(Blue)。每个通道包含一个灰度值,表示颜色的强度。
  • 224:图像的高度(Height)

    • 这个数字表示图像的垂直尺寸,单位是像素。在这个例子中,图像的高度为 224 像素,表示图像的垂直方向有 224 个像素点。
  • 224:图像的宽度(Width)

    • 这个数字表示图像的水平尺寸,单位也是像素。在这个例子中,图像的宽度为 224 像素,表示图像的水平方向有 224 个像素点。

综合解释

  • 1:批量大小为 1,意味着只有一个图像。
  • 3:该图像为彩色图像,采用 RGB 色彩空间,因此有 3 个颜色通道。
  • 224224:该图像的尺寸为 224x224 像素,表示图像的高度和宽度都是 224 像素。

因此,[1, 3, 224, 224] 表示一个单独的 RGB 彩色图像,尺寸为 224x224 像素,具有 3 个颜色通道(红、绿、蓝)。这种形状常见于深度学习任务,特别是图像分类或卷积神经网络(CNN)的输入。


使用场景

这个张量形状通常用于深度学习框架,如 PyTorch、ONNX 或 TensorFlow,在图像数据的训练和推理过程中经常出现。例如,如果你使用一个深度学习模型来分类图像,模型通常期望输入的形状是 [B, C, H, W],其中:

  • B 是批量大小。
  • C 是颜色通道数。
  • H 是图像的高度。
  • W 是图像的宽度。

总结

  • [1, 3, 224, 224] 表示一个具有 224x224 像素尺寸的 RGB 彩色图像,批量大小为 1。

什么是图像张量

图像张量(Image Tensor)是一个用于表示图像数据的多维数组(张量),通常用于深度学习、计算机视觉和其他机器学习任务。张量是一个数学概念,广泛用于表示高维数据。图像张量是张量在计算机视觉领域的具体应用,它通过数值数组的形式表示图像。

在深度学习和计算机视觉任务中,图像通常作为输入数据,通过神经网络进行处理和分析。为了能够处理图像,图像数据需要以一种数值形式表示,而这正是张量的作用所在。


图像张量的基本结构

图像张量的维度(Shape)通常与图像的表示方式、颜色通道数、分辨率和数据类型等因素相关。常见的图像张量表示方式包括以下几种:

1. 彩色图像张量

对于彩色图像,通常采用RGB(红绿蓝)颜色空间来表示。每个颜色通道的值通常是一个整数(比如 0255 之间)或浮动的值(例如标准化到 [0, 1] 的浮动值)。

  • 形状[C, H, W]
    • C:颜色通道数(通常是 3,表示 RGB 三个颜色通道)。
    • H:图像的高度(即图像的行数,表示图像的垂直尺寸)。
    • W:图像的宽度(即图像的列数,表示图像的水平尺寸)。

例如,一个大小为 224x224 像素的 RGB 彩色图像可以用一个形状为 [3, 224, 224] 的张量表示。

2. 灰度图像张量

对于灰度图像,通常只有一个颜色通道,即每个像素只有一个灰度值,表示图像的亮度。

  • 形状[1, H, W]
    • 1:表示灰度图像只有一个颜色通道。
    • H:图像的高度。
    • W:图像的宽度。

例如,一个 28x28 像素的灰度图像可以用一个形状为 [1, 28, 28] 的张量表示。

3. 批量图像张量

在深度学习中,通常一次性处理多个图像。因此,图像张量通常以批量(batch)的形式输入到神经网络中。

  • 形状[B, C, H, W]
    • B:批量大小(batch size),表示一次处理多少图像。
    • C:颜色通道数(通常是 3 表示 RGB,或 1 表示灰度)。
    • H:图像的高度。
    • W:图像的宽度。

例如,一个包含 32 张 224x224 的 RGB 彩色图像的批量张量,可以表示为形状为 [32, 3, 224, 224] 的张量。


图像张量的存储格式

图像张量的存储格式依赖于数据的顺序和布局。最常见的两种存储格式是:

  1. CHW(Channel, Height, Width)格式

    • 这是深度学习框架如 PyTorch、ONNX、Caffe 中常见的图像张量格式。颜色通道在前,接着是高度和宽度。
    • 示例:[3, 224, 224] 表示一个 RGB 彩色图像,具有 3 个通道,尺寸为 224x224
  2. HWC(Height, Width, Channel)格式

    • 这是图像处理库如 OpenCV 和 TensorFlow 中常见的图像表示格式。图像的高度和宽度在前,颜色通道在后。
    • 示例:[224, 224, 3] 表示一个 RGB 彩色图像,具有 3 个通道,尺寸为 224x224

不同框架和工具可能采用不同的存储格式,但它们都可以通过相应的工具进行转换。


图像张量的用途

图像张量在计算机视觉和深度学习中的用途非常广泛,主要包括:

  1. 输入神经网络

    • 图像张量是深度学习模型,特别是卷积神经网络(CNN)的输入数据。通过神经网络的前向传播,模型会根据图像的特征进行分类、检测、分割等任务。
  2. 图像预处理

    • 在将图像输入神经网络之前,通常会进行一些预处理操作,如调整图像的大小、标准化颜色值、数据增强(例如旋转、裁剪、翻转等)。这些操作通常会改变图像张量的形状或数值。
  3. 图像生成

    • 在生成模型(如生成对抗网络 GAN)中,图像张量不仅作为输入,还作为模型的输出,生成新的图像。
  4. 图像变换

    • 图像张量在图像处理任务中,涉及图像的各种变换操作,比如图像的滤波、边缘检测、图像增强等。

图像张量的示例

以下是一个示例,展示如何使用 Python 和 NumPy 创建图像张量(以 NumPy 为例,虽然 Python 是常见的深度学习语言,但 ONNX Runtime 或 PyTorch 等框架也使用类似的张量表示方法):

1
2
3
4
5
6
import numpy as np

# 创建一个 224x224 的 RGB 图像张量(3 个颜色通道)
image_tensor = np.random.rand(3, 224, 224) # 随机生成一个图像张量

print(image_tensor.shape) # 输出张量的形状,应该是 (3, 224, 224)

如果是批量图像(例如批量大小为 32):

1
2
3
4
# 创建一个包含 32 张 224x224 RGB 图像的张量
batch_image_tensor = np.random.rand(32, 3, 224, 224)

print(batch_image_tensor.shape) # 输出张量的形状,应该是 (32, 3, 224, 224)

总结

  • 图像张量是一个多维数组,表示图像的数值数据,通常用于深度学习中的输入数据。
  • 图像张量的常见形状包括 [C, H, W](单张图像)和 [B, C, H, W](批量图像)。
  • 图像张量可以是彩色图像(RGB)或灰度图像,并通过颜色通道、图像的高度和宽度来表示。
  • 在深度学习和计算机视觉任务中,图像张量是神经网络输入和输出的重要形式。

图像张量为机器学习模型提供了可以处理的格式,是许多计算机视觉任务的基础。

C++ onnxruntime Ort::Value 详解

在 ONNX Runtime 中,Ort::Value 是一个非常重要的类,用于表示张量(tensor)数据。它封装了模型的输入和输出数据,并提供了一种与 ONNX Runtime 进行交互的方式。

Ort::Value 介绍

Ort::Value 是 ONNX Runtime API 中用于表示数据的一个类,它可以用于存储模型的输入和输出数据,并为数据提供访问接口。你可以将数据包装到 Ort::Value 中,从而通过 ONNX Runtime 执行推理操作。它提供了对张量的访问、内存管理以及各种用于操作张量数据的功能。

主要功能

  1. 张量表示Ort::Value 用于表示和存储模型的输入、输出数据,这些数据通常是多维数组,表示为张量(Tensor)。

  2. 数据类型支持Ort::Value 支持各种数据类型,如浮动类型(floatdouble)、整数类型(intlong)、布尔类型等。它也支持不同的张量格式,例如:float32int64 等。

  3. 内存管理Ort::Value 内部管理内存,并负责在不再需要时释放内存。ONNX Runtime 会通过 RAII(资源获取即初始化)管理内存的生命周期。

创建 Ort::Value

Ort::Value 可以通过几种不同的方式创建,以下是一些常见的方法:

  1. 从原始数据创建
    使用 Ort::Value::CreateTensor 或类似函数,可以从原始内存或数组创建一个张量。常见的场景是创建张量并作为输入传递给模型。

    1
    2
    3
    4
    Ort::AllocatorWithDefaultOptions allocator;
    std::vector<float> input_data = {1.0f, 2.0f, 3.0f};
    std::array<int64_t, 1> input_shape = {3};
    Ort::Value input_tensor = Ort::Value::CreateTensor<float>(allocator, input_data.data(), input_data.size(), input_shape.data(), input_shape.size());
  2. 从现有的内存(例如:std::vector)创建
    可以直接使用 Ort::Value 来包装现有的数据。可以通过 Ort::Value::CreateTensor 方法指定数据类型和形状。

  3. 从 C++ 标准容器(如 std::vectorstd::array)创建张量
    Ort::Value 可以直接从容器对象创建张量,方便快速构建推理的输入数据。

常用方法

1. 获取数据类型

Ort::Value 提供了方法来获取存储的数据类型。例如,获取张量的数据类型(如 float32int64 等):

1
ONNXTensorElementDataType type = input_tensor.GetTensorTypeAndShapeInfo().GetElementType();

2. 获取张量的形状

通过 Ort::ValueGetTensorTypeAndShapeInfo() 方法,你可以获取张量的形状(即它的维度)。

1
2
Ort::TensorTypeAndShapeInfo tensor_info = input_tensor.GetTensorTypeAndShapeInfo();
std::vector<int64_t> shape = tensor_info.GetShape();

3. 获取数据指针

Ort::Value 也允许你访问底层数据。可以使用 GetTensorData 来获取存储张量数据的指针:

1
float* float_data = input_tensor.GetTensorData<float>();

4. 设置张量的值

你可以通过 Ort::Value 设置张量的数据:

1
input_tensor.SetTensorData<float>(input_data.data());

5. 转换为其他类型

通过 ONNX Runtime 提供的 API,你可以将 Ort::Value 转换为其他类型(例如:从张量转为 NumPy 数组,或通过其他方式访问数据)。

示例代码:推理输入和输出

下面是一个基本示例,展示了如何使用 Ort::Value 来执行 ONNX 模型推理:

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
#include <onnxruntime/core/providers/shared_ptr.h>
#include <onnxruntime/core/providers/tensor/ort_value.h>
#include <onnxruntime/core/providers/tensor/tensor.h>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXModel");

// 创建一个 SessionOptions 对象
Ort::SessionOptions session_options;

// 加载 ONNX 模型
std::string model_path = "model.onnx";
Ort::Session onnx_session(env, model_path.c_str(), session_options);

// 准备输入数据 (例如: 3x3 数字)
std::vector<float> input_data = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f};
std::array<int64_t, 2> input_shape = {1, 9}; // 假设这是一个 1x9 的输入

// 创建 Ort::Value 对象作为输入张量
Ort::AllocatorWithDefaultOptions allocator;
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(allocator, input_data.data(), input_data.size(), input_shape.data(), input_shape.size());

// 创建 Ort::Value 对象作为输出张量
std::vector<Ort::Value> output_tensors;

// 执行推理
onnx_session.Run(Ort::RunOptions{nullptr},
&input_name, &input_tensor, 1, // 输入
&output_name, &output_tensors, 1); // 输出

// 输出结果
float* output_data = output_tensors[0].GetTensorData<float>();
std::cout << "Inference result: " << output_data[0] << std::endl;

return 0;
}

总结

  • Ort::Value 是 ONNX Runtime 用于表示和操作张量的核心类之一。
  • 它提供了一种接口来管理输入和输出数据,支持多种数据类型和形状。
  • 你可以通过 Ort::Value 来包装原始数据(如 std::vector),并将其传递给模型进行推理。
  • 通过 Ort::Value 提供的接口,你可以方便地访问张量的数据、形状以及数据类型。

Ort::Value 的使用使得与 ONNX Runtime 进行交互变得简便而高效,能够帮助开发者轻松处理模型推理所需的各种数据。

Ort::Value::CreateTensor 详解

Ort::Value::CreateTensor 是 ONNX Runtime 中用于创建张量(Tensor)数据的一个静态函数。张量是 ONNX Runtime 中数据的核心表示形式,通常用于表示模型的输入和输出。通过 CreateTensor,你可以将数据封装成 Ort::Value 对象,供模型推理使用。

函数原型

1
2
3
4
5
6
7
static Ort::Value CreateTensor(
Ort::Allocator& allocator,
void* data,
size_t size,
const int64_t* shape,
size_t shape_len,
ONNXTensorElementDataType type);

参数详解

  1. **allocator**(类型:Ort::Allocator&):

    • 用于分配内存的分配器,ONNX Runtime 会使用它来管理张量的内存。
    • 推荐使用 Ort::AllocatorWithDefaultOptions 来创建一个默认的内存分配器。
  2. **data**(类型:void*):

    • 一个指向存储数据的内存块的指针。数据将被存储在该指针指向的内存位置。
    • 你需要将数据放入这块内存中,数据的类型和结构应该符合所需的张量类型和形状。
  3. **size**(类型:size_t):

    • 数据的总大小,以字节为单位。
    • size 应该等于张量的元素数量乘以每个元素的字节大小。例如,如果每个元素是 float(4 字节),而张量包含 100 个元素,那么 size 应该是 100 * 4
  4. **shape**(类型:const int64_t*):

    • 张量的形状,即每个维度的大小。形状是一个整型数组,表示张量的多维尺寸。
    • 例如,对于一个 2D 张量,形状可能是 {3, 4},表示该张量有 3 行 4 列。
  5. **shape_len**(类型:size_t):

    • 张量形状的维度数量,即 shape 数组的长度。
    • 例如,如果张量是 2D,shape_len 应该是 2;如果是 3D,shape_len 应该是 3,依此类推。
  6. **type**(类型:ONNXTensorElementDataType):

    • 张量的数据类型,指定张量中元素的类型。ONNX 允许多种数据类型,包括:
      • ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOATfloat 类型)
      • ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32int32 类型)
      • ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64int64 类型)
      • ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8uint8 类型)
      • 等等。

返回值

  • 返回一个 Ort::Value 对象,表示创建的张量。
  • Ort::Value 对象封装了张量的数据和形状,并可以在后续的推理过程中作为输入或输出使用。

示例代码

以下是一个创建张量并将其用于模型推理的完整示例:

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
#include <onnxruntime/core/providers/tensor/ort_value.h>
#include <onnxruntime/core/providers/tensor/tensor.h>
#include <onnxruntime/core/providers/shared_ptr.h>
#include <onnxruntime/core/providers/common.h>
#include <onnxruntime/core/providers/onnxruntime_typeinfo.h>

#include <iostream>
#include <vector>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXModel");

// 创建一个默认分配器
Ort::AllocatorWithDefaultOptions allocator;

// 创建一个简单的 2D 张量数据(例如:2x3 的矩阵)
std::vector<float> data = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f};
std::array<int64_t, 2> shape = {2, 3}; // 张量的形状是 2x3

// 创建张量对象
Ort::Value tensor = Ort::Value::CreateTensor<float>(
allocator,
data.data(), // 数据指针
data.size() * sizeof(float), // 数据大小,单位是字节
shape.data(), // 形状
shape.size(), // 形状的长度
ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT // 数据类型
);

// 获取张量的类型和形状信息
Ort::TensorTypeAndShapeInfo tensor_info = tensor.GetTensorTypeAndShapeInfo();
std::vector<int64_t> tensor_shape = tensor_info.GetShape();

// 打印张量形状
std::cout << "Tensor Shape: ";
for (auto dim : tensor_shape) {
std::cout << dim << " ";
}
std::cout << std::endl;

// 访问张量数据
float* tensor_data = tensor.GetTensorData<float>();
std::cout << "Tensor Data: ";
for (size_t i = 0; i < data.size(); ++i) {
std::cout << tensor_data[i] << " ";
}
std::cout << std::endl;

return 0;
}

详细解释

  1. 内存分配

    • 使用 Ort::AllocatorWithDefaultOptions 创建一个分配器对象,它将在创建张量时分配内存。
  2. 数据和形状

    • 数据是一个 std::vector<float>,代表了一个 2x3 张量的数据。
    • 形状 shape 是一个 std::array<int64_t, 2>,表示张量是 2 行 3 列的。
  3. 创建张量

    • Ort::Value::CreateTensor<float> 方法用来创建一个 float 类型的张量。我们传入数据指针、数据大小、形状以及数据类型。
    • 该方法将返回一个 Ort::Value 对象,封装了张量的所有信息。
  4. 获取张量信息

    • 使用 tensor.GetTensorTypeAndShapeInfo() 获取张量的类型和形状信息。
    • 使用 tensor.GetTensorData<float>() 获取存储在张量中的数据指针。
  5. 打印结果

    • 打印张量的形状和数据内容。

注意事项

  • 内存管理Ort::Value 对象会自动管理张量的内存。因此,用户不需要手动释放张量的内存,只需保证它的生命周期在需要时有效。
  • 数据类型:确保传入的 data 的类型与指定的 ONNXTensorElementDataType 类型匹配。例如,如果选择 ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT,则 data 必须是 float 类型的数据。

总结

  • Ort::Value::CreateTensor 是一个用于创建张量的静态方法,可以将数据、形状和数据类型封装成 Ort::Value 对象。
  • 它为 ONNX Runtime 模型推理提供了输入和输出的数据结构。
  • 使用时需要提供内存分配器、数据、数据大小、形状以及数据类型等信息。

Session::Run() 函数 详解

Session::Run() 是 ONNX Runtime 中的一个关键函数,负责执行模型推理(即前向传播)。它的主要作用是根据输入数据进行推理,并返回推理结果。你通过它将输入数据传递给 ONNX 模型,并得到相应的输出。

函数定义

1
2
3
4
5
Status Run(const Session::RunOptions& run_options,
const std::vector<std::string>& input_names,
const std::vector<const Ort::Value*>& input_values,
const std::vector<std::string>& output_names,
std::vector<Ort::Value>& output_values);

参数说明

  1. **run_options (Session::RunOptions)**:

    • 类型const Session::RunOptions&
    • 描述:设置推理过程中的一些选项,比如是否使用优化等。可以为空,表示使用默认选项。
  2. **input_names (std::vectorstd::string)**:

    • 类型const std::vector<std::string>&
    • 描述:输入张量的名称列表。对于每个输入张量,你需要提供对应的名称,这些名称必须与模型中定义的输入名称一致。
  3. **input_values (std::vector<const Ort::Value*>)**:

    • 类型const std::vector<const Ort::Value*>&
    • 描述:输入数据的值列表。每个输入对应一个 Ort::Value 对象,这些对象包含了输入数据。数据的类型和形状应与模型要求的输入一致。
  4. **output_names (std::vectorstd::string)**:

    • 类型const std::vector<std::string>&
    • 描述:输出张量的名称列表。指定你希望从模型中获取的输出名称。如果模型有多个输出,你需要提供所有输出的名称。
  5. **output_values (std::vectorOrt::Value)**:

    • 类型std::vector<Ort::Value>&
    • 描述:模型推理的输出结果。执行完推理后,结果会被填充到这个 Ort::Value 的 vector 中。

返回值

  • 类型Status
  • 描述:函数返回一个 Status 对象,表示推理过程的状态。通常在成功时返回 Status::OK(),如果失败,则返回相应的错误信息。

函数工作流程

Session::Run() 函数在 ONNX Runtime 中负责以下几个步骤:

  1. 输入检查

    • 检查提供的输入数据是否与模型的输入要求(如名称、形状、数据类型)一致。
  2. 推理执行

    • 使用提供的输入数据执行推理。ONNX Runtime 会根据模型的计算图进行推理操作。
  3. 输出生成

    • 根据指定的输出名称,返回计算结果。输出的数据会被填充到 output_values 中,通常这些数据是 Ort::Value 对象,包含了推理结果(如分类的概率、图像的预测框等)。

示例代码

以下是一个简单的使用 Session::Run() 函数的示例,它展示了如何加载一个 ONNX 模型并运行推理。

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
#include <onnxruntime/core/providers/cpu/cpu_provider_factory.h>
#include <onnxruntime/core/providers/shared_library/provider_api.h>
#include <onnxruntime/core/providers/tensor/tensor.h>
#include <onnxruntime/core/providers/utils.h>
#include <onnxruntime/core/providers/common.h>
#include <onnxruntime/core/providers/session.h>
#include <iostream>
#include <vector>

int main() {
// 初始化 ONNX Runtime 环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXModel");

// 加载模型
const std::string model_path = "path_to_your_model.onnx";
Ort::SessionOptions session_options;
Ort::Session onnx_session(env, model_path.c_str(), session_options);

// 设置输入数据
std::vector<const char*> input_names = {"input_tensor_name"}; // 假设模型有一个输入张量
std::vector<Ort::Value> input_values;

// 创建输入 tensor(假设模型要求输入大小为 1x3x224x224)
std::vector<float> input_data(1 * 3 * 224 * 224, 1.0f); // 示例输入数据
std::vector<int64_t> input_shape = {1, 3, 224, 224}; // 输入张量的形状
input_values.push_back(Ort::Value::CreateTensor<float>(input_data.data(), input_shape));

// 设置输出数据
std::vector<const char*> output_names = {"output_tensor_name"}; // 假设模型有一个输出张量
std::vector<Ort::Value> output_values;

// 执行推理
try {
onnx_session.Run(Ort::RunOptions(), input_names, input_values.data(), input_names.size(), output_names, output_values);

// 处理推理结果
for (const auto& output : output_values) {
// 处理每个输出的结果
std::cout << "Output tensor: " << output.GetTensorTypeAndShapeInfo().GetShape()[0] << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "Error during inference: " << e.what() << std::endl;
}

return 0;
}

解释

  1. 初始化环境:首先创建一个 Ort::Env 对象来初始化 ONNX Runtime 环境。
  2. 加载模型:通过 Ort::Session 加载一个 ONNX 模型,并使用 SessionOptions 进行配置。
  3. 准备输入数据:创建输入数据(如一张图像),并使用 Ort::Value::CreateTensor 方法将其转换为 Ort::Value 类型。
  4. 执行推理:使用 Run 方法执行推理,传入输入数据和请求的输出数据名称。
  5. 输出结果:推理结果被存储在 output_values 中,之后可以进一步处理或打印输出。

注意事项

  1. 输入和输出数据的类型一致性

    • 需要确保输入数据的类型(如 float)与模型要求的数据类型一致。
    • 输入数据的形状应与模型的输入形状匹配,否则推理会失败。
  2. 异常处理

    • Session::Run() 可能会抛出异常(如数据类型不匹配、形状不匹配等),因此通常需要在调用时使用 try-catch 来捕获并处理异常。
  3. 性能优化

    • Session::Run() 是一个同步操作,因此如果需要优化推理性能,可以考虑异步执行(如通过 RunOptions 或使用多线程)或使用硬件加速(如使用 GPU)。

总结

Session::Run() 是 ONNX Runtime 推理流程中的核心函数,它负责将输入数据传递给模型并返回结果。理解和正确使用该函数对于模型推理至关重要。通过合适的参数配置(如输入输出的名称和数据类型),可以实现高效、正确的模型推理。

摘要

  • OpenVino 相关学习笔记

ov::AnyMap 详解

在 OpenVINO 的 C++ API 中,ov::AnyMap 是一个通用的键值对数据结构,用于设置和传递模型编译或推理时的配置参数。它允许用户通过简单的键值映射来定义特定配置,以便调整推理过程的行为。


1. ov::AnyMap 的基本概念

  • 类型: std::map<std::string, ov::Any> 的别名。
  • 功能: 存储键值对,其中键为配置参数的名称,值为 ov::Any 类型,用于支持不同类型的参数值。
  • 用途:
    • 配置推理设备的行为(如线程数、性能模式)。
    • 设置优化参数(如批处理大小、输入精度等)。
    • 传递插件特定的自定义配置。

2. 创建和使用

ov::AnyMap 是可选配置的一部分,通常在调用 ov::Core::compile_model 或类似方法时使用。

创建一个 ov::AnyMap

以下是创建和设置 ov::AnyMap 的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <openvino/openvino.hpp>

int main() {
// 创建一个配置参数的 AnyMap
ov::AnyMap config;

// 设置性能模式
config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT;

// 设置线程数
config[ov::inference_num_threads.name()] = 8;

// 设置流数量
config[ov::num_streams.name()] = "AUTO";

return 0;
}

使用 ov::AnyMap

通过 ov::Core 编译模型时传入 ov::AnyMap

1
2
3
4
5
// 加载模型
auto model = core.read_model("model.xml");

// 使用配置编译模型
auto compiled_model = core.compile_model(model, "CPU", config);

3. ov::Any 的特性

ov::Any 是一个多态类型,支持存储任意类型的值。常见的值类型包括:

  • 整数 (int)
  • 浮点数 (float, double)
  • 字符串 (std::string)
  • 枚举类型(如 ov::hint::PerformanceMode
  • 布尔值 (bool)

使用 ov::Any 时,自动处理值类型,例如:

1
2
3
4
5
// 设置不同类型的值
ov::AnyMap config;
config["threads"] = 4; // int
config["enable_profiling"] = true; // bool
config["device"] = std::string("CPU"); // string

4. 常见配置参数

以下是 OpenVINO 支持的一些常见配置参数:

参数名称 值类型 描述
ov::hint::performance_mode ov::hint::PerformanceMode 性能优化模式,如延迟或吞吐量优化。
ov::inference_num_threads int 推理线程数。
ov::num_streams std::string 流数量,如 “AUTO”、”1”、”2” 等。
ov::enable_profiling bool 是否启用性能分析。
ov::hint::model_priority ov::hint::Priority 模型优先级设置。

5. 如何读取配置

使用 ov::AnyMap 设置参数后,可以通过以下方式访问参数值:

1
2
3
// 读取配置值
auto threads = config["threads"].as<int>(); // 获取 int 类型的线程数
auto enable_profiling = config["enable_profiling"].as<bool>(); // 获取 bool 类型的值

6. 错误处理

  • 如果尝试访问不存在的键,会抛出异常。
  • 如果类型转换失败(如尝试将 bool 转换为 int),会引发类型错误。

示例:

1
2
3
4
5
try {
auto value = config["non_existing_key"].as<int>();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}

7. 结合设备插件

ov::AnyMap 可以传递设备插件特定的配置。例如,针对 GPU:

1
2
3
4
5
ov::AnyMap gpu_config = {
{"CACHE_DIR", "/path/to/cache"}, // GPU 缓存目录
{ov::hint::performance_mode.name(), ov::hint::PerformanceMode::THROUGHPUT}
};
auto compiled_model = core.compile_model(model, "GPU", gpu_config);

总结

ov::AnyMap 是 OpenVINO 中灵活且强大的配置工具,用户可以根据需要动态设置和调整推理的行为,优化性能。结合设备特性和推理场景,灵活使用 ov::AnyMap 是提升模型部署效率的重要手段。如果需要针对某个配置参数的具体说明,请进一步告知!

C++ OpenVINO ov::hint::performance_mode 详解

在 OpenVINO 的 C++ API 中,ov::hint::performance_mode 是一个用于优化推理性能的设置选项。它允许用户为推理过程选择不同的性能模式,以平衡吞吐量、延迟和设备资源利用率等方面的需求。

以下是对 ov::hint::performance_mode 的详解:


1. 性能模式的类型

ov::hint::performance_mode 支持以下几种模式,每种模式都有其适用场景:

a. LATENCY

  • 描述: 优化以降低单次推理的延迟。
  • 应用场景:
    • 实时应用,如视频分析和在线推理。
    • 延迟敏感的场景。

b. THROUGHPUT

  • 描述: 优化以提高设备的整体吞吐量,通常通过增加批处理大小和并行化执行。
  • 应用场景:
    • 离线批量处理。
    • 批量推理任务,如图像分类。

c. CUMULATIVE_THROUGHPUT

  • 描述: 专注于在多次推理任务中累积吞吐量的优化。
  • 应用场景:
    • 多模型部署。
    • 并发推理任务的优化。

d. UNDEFINED

  • 描述: 使用插件的默认性能配置,通常适用于简单场景或不确定优化需求时。
  • 应用场景:
    • 开发阶段。
    • 初始实验。

2. 设置方式

通过 ov::Coreov::CompiledModel 对象设置性能模式。以下是示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <openvino/openvino.hpp>

int main() {
// 创建 OpenVINO Core 对象
ov::Core core;

// 加载模型
auto model = core.read_model("model.xml");

// 配置性能模式
ov::AnyMap config = {
{ov::hint::performance_mode, ov::hint::PerformanceMode::LATENCY}
};

// 编译模型并应用配置
auto compiled_model = core.compile_model(model, "CPU", config);

// 执行推理
auto infer_request = compiled_model.create_infer_request();
// 推理逻辑...
return 0;
}

3. 性能模式的影响

性能模式主要通过以下方式影响推理行为:

  • 线程优化: 调整计算线程数、绑定策略等。
  • 批量处理: 增加或减少批处理大小。
  • 并行化策略: 优化并行计算的粒度和线程分配。

4. 最佳实践

  • 在实时系统中,优先选择 LATENCY
  • 离线任务或批量处理,选择 THROUGHPUT
  • 如果部署多个模型,选择 CUMULATIVE_THROUGHPUT
  • 开发和测试阶段,可以先使用 UNDEFINED,然后根据实际需求调整。

5. 设备支持

不同的设备插件(如 CPU、GPU、VPU 等)可能对性能模式的支持有细微差异。例如:

  • CPU: 支持所有性能模式,并提供多线程优化。
  • GPU: 注重吞吐量优化,但延迟模式下可能受到硬件约束。
  • VPU: 通常用于低功耗场景,优化吞吐量和延迟的权衡。

了解设备特性和任务需求,有助于更好地选择性能模式。


如果你有具体问题或需要针对某个场景的配置示例,请告诉我!

OpenVINO windows下环境配置教程

OpenVino 是什么

OpenVINO(Open Visual Inference and Neural Network Optimization)是一个由 Intel 提供的开源工具包,旨在加速深度学习模型的推理和优化,特别是在 Intel 硬件(如 CPU、GPU、FPGA 和 VPU)上的性能表现。它主要用于计算机视觉应用,但也支持其他类型的 AI 工作负载。

主要功能

  1. 模型优化
    OpenVINO 提供了模型优化器(Model Optimizer),可以将各种深度学习框架(如 TensorFlow、PyTorch、ONNX、Caffe 等)训练的模型转换为 OpenVINO 的中间表示格式(IR Format)。这可以帮助简化模型并提升推理效率。

  2. 硬件加速
    支持在多种 Intel 硬件上运行,包括:

    • Intel CPU(尤其是使用了 AVX 和其他指令集优化)
    • Intel 集成显卡(GPU)
    • Intel Movidius VPU(视觉处理单元)
    • Intel FPGA(可编程逻辑门阵列)
  3. 高效推理引擎
    OpenVINO 提供了推理引擎,可以通过异构计算同时利用多种硬件的优势,最大化推理性能。

  4. 预训练模型和工具支持
    包括预训练的模型库(Open Model Zoo),以及一些便捷的 API 和开发工具,用于快速开发 AI 应用。

  5. 跨平台支持
    OpenVINO 支持在各种操作系统上运行,包括 Linux、Windows 和 macOS。

使用场景

  • 计算机视觉:如目标检测、图像分类、语义分割、人脸识别等。
  • 视频分析:如实时监控、行为分析、视频内容理解等。
  • 自然语言处理(NLP):尽管主要面向视觉任务,但 OpenVINO 也支持 NLP 模型的优化和推理。
  • 嵌入式 AI:在资源受限的设备(如边缘设备)上运行优化的 AI 模型。

优势

  1. 提高推理速度,降低延迟。
  2. 支持多种硬件,灵活适应不同的部署需求。
  3. 开源且有丰富的文档和社区支持。
  4. 提供易用的 API,适合快速开发。

如果你正在开发需要部署深度学习模型的应用,并且目标硬件是 Intel 平台,OpenVINO 是一个非常合适的选择。

OpenVino 推理框架 详解

OpenVINO 的推理框架(Inference Framework)是 OpenVINO 工具包的核心组件之一,它负责高效地执行优化后的深度学习模型推理。以下是 OpenVINO 推理框架的详细解析:


1. 核心组件

(1) 模型优化器(Model Optimizer)

  • 将训练好的深度学习模型转换为 OpenVINO 的中间表示(Intermediate Representation,IR)格式。
  • 输入支持:
    • 支持多种框架的模型,包括 TensorFlow、PyTorch、ONNX、Caffe 等。
  • 优化方式:
    • 图形优化(删除冗余运算节点)。
    • 数据类型精简(如 FP32 转换为 FP16)。
    • 插入专为 Intel 硬件设计的高效计算指令。

(2) 推理引擎(Inference Engine)

  • 核心执行模块,负责加载优化后的 IR 模型并在目标硬件上高效运行。
  • 功能:
    • 提供统一的 API 接口,支持多种硬件。
    • 实现同步和异步推理(适用于实时和离线场景)。
    • 自动分配推理负载到多个设备(如 CPU 和 GPU 同时工作)。
  • 主要模块:
    • 核心对象(Core API): 用于加载模型和配置设备。
    • 插件架构: 针对不同硬件有特定插件,如 CPU 插件、GPU 插件、VPU 插件。

(3) 硬件支持层(Hardware Abstraction Layer)

  • 提供针对 Intel 硬件优化的指令集(如 AVX、VNNI)。
  • 支持的设备包括:
    • CPU(使用线程优化和向量化技术)。
    • GPU(集成 GPU 的 OpenCL 加速)。
    • VPU(Movidius 芯片)。
    • FPGA(灵活可编程硬件)。

2. 工作流程

  1. 模型准备
    • 从训练框架导出模型(如 .pb.onnx.caffemodel)。
    • 使用模型优化器将模型转换为 IR 格式(包括 .xml.bin 文件)。
  2. 加载模型
    • 使用推理引擎的 Core API 加载 IR 模型到指定设备。
  3. 预处理
    • 调整输入数据的格式(如大小、数据类型、归一化等)。
  4. 推理执行
    • 调用推理引擎执行推理操作(支持同步或异步)。
  5. 后处理
    • 对推理结果进行解码或进一步处理(如 NMS、标签映射等)。

3. 编程接口

(1) Python API

  • 简单易用,适合快速开发和原型验证。
  • 样例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from openvino.runtime import Core

    # 初始化 Core
    core = Core()

    # 加载模型
    model = core.read_model(model="model.xml")
    compiled_model = core.compile_model(model=model, device_name="CPU")

    # 推理
    input_data = ... # 预处理后的输入数据
    results = compiled_model([input_data])

(2) C++ API

  • 性能更高,适合生产级部署。
  • 样例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <openvino/openvino.hpp>

    int main() {
    ov::Core core;

    // 加载模型
    auto model = core.read_model("model.xml");
    auto compiled_model = core.compile_model(model, "CPU");

    // 推理
    ov::InferRequest infer_request = compiled_model.create_infer_request();
    infer_request.set_input_tensor(...); // 设置输入
    infer_request.infer();
    auto output = infer_request.get_output_tensor();
    return 0;
    }

4. 支持的推理模式

(1) 同步推理

  • 简单、顺序执行推理。
  • 适合离线处理场景。

(2) 异步推理

  • 非阻塞模式,可以在等待推理结果时并行执行其他任务。
  • 适合实时场景,如视频流分析。

5. 优化特性

(1) 混合精度推理

  • 支持 FP32、FP16 和 INT8 的混合精度计算。
  • 使用 INT8 精度进一步提升性能,同时减少计算和内存开销。

(2) 多设备推理

  • 使用 Heterogeneous Plugin 实现负载均衡。
  • 自动调度任务到最佳设备。

(3) 自定义层扩展

  • 支持用户定义的自定义操作,适配特定模型需求。

6. 应用场景

  1. 实时视频分析(如安防监控、人流统计)。
  2. 工业自动化(如缺陷检测、机器人视觉)。
  3. 医疗影像处理(如肿瘤检测)。
  4. 嵌入式 AI(如无人机、智能家居设备)。

7. 常用工具

(1) Open Model Zoo

  • 提供大量预训练模型,如 ResNet、YOLO、SSD。
  • 方便快速测试和验证。

(2) Benchmark Tool

  • 测试模型在目标硬件上的性能。

(3) Post-Training Optimization Toolkit (POT)

  • 支持后量化(Post-training Quantization),提升推理性能。

总结

OpenVINO 推理框架通过模型优化器和推理引擎,实现高效的深度学习模型推理。其支持多种硬件设备,并提供丰富的优化选项,适用于多种 AI 应用场景,是一个功能强大、灵活且高效的推理工具。

node

Node.js(简称 Node)是一个基于 V8 JavaScript 引擎 的开源、跨平台运行时环境,用于开发服务器端和网络应用程序。它将 JavaScript 从浏览器中解放出来,使开发者可以使用 JavaScript 构建后端服务和工具。


Node.js 的关键特性

  1. 基于事件驱动和非阻塞 I/O
    Node.js 使用异步事件驱动模型,支持高效的并发处理,非常适合构建 I/O 密集型应用(如实时聊天、API 服务等)。

  2. 单线程模型
    虽然 Node.js 是单线程的,但通过事件循环(Event Loop)和回调机制,它可以处理大量的并发连接,而无需为每个连接创建一个线程。

  3. NPM(Node Package Manager)
    Node.js 附带了 NPM,世界上最大的开源库生态系统。开发者可以使用 NPM 下载、管理和分享 JavaScript 包。

  4. 跨平台
    Node.js 支持 Windows、macOS 和各种 Linux 系统。

  5. 模块化系统
    Node.js 提供了模块化设计,开发者可以通过 require 导入模块,同时也可以创建自己的模块。


Node.js 的应用场景

  1. Web 应用后端开发

    • 使用 Node.js 和框架(如 Express.js)构建高效的 RESTful API 或实时 WebSocket 服务。
    • 示例:
      1
      2
      3
      4
      5
      const express = require('express');
      const app = express();

      app.get('/', (req, res) => res.send('Hello, Node.js!'));
      app.listen(3000, () => console.log('Server running on port 3000'));
  2. 实时应用程序

    • 实现实时聊天、在线协作工具(如 Google Docs)或多人在线游戏。
  3. 工具与脚本开发

    • 使用 Node.js 开发命令行工具或构建脚本。
  4. 微服务架构

    • Node.js 适合用作微服务的组件,通过轻量级的服务处理特定任务。

Node.js 的局限性

  1. CPU 密集型任务的挑战
    Node.js 对 I/O 密集型任务表现优秀,但单线程的特性限制了其在 CPU 密集型任务(如图像处理、大量计算)中的效率。

  2. 回调地狱
    尽管可以通过 Promises 或 async/await 改善,但在早期开发中,嵌套的回调可能会导致代码难以维护。


如何安装 Node.js

  1. 通过官方网站下载安装程序:
    Node.js 官方下载页面

  2. 使用包管理工具安装:

    • macOS: 使用 Homebrew
      1
      brew install node
    • Linux: 使用包管理器(如 aptyum)。
  3. 验证安装:

    1
    2
    node -v   # 查看 Node.js 版本
    npm -v # 查看 NPM 版本

相关资源

npm 是什么

NPM,全称 Node Package Manager,是一个用于管理 Node.js 包(模块)和项目依赖关系的工具。NPM 是 Node.js 的默认包管理器,随 Node.js 一起安装,功能非常强大。


NPM 的主要功能

  1. 包管理(模块管理)

    • 提供了世界上最大的 JavaScript 开源包生态系统,开发者可以轻松安装、更新和管理所需的库或模块。
    • 常用命令:
      1
      2
      3
      npm install <package>        # 安装某个包
      npm uninstall <package> # 卸载某个包
      npm update # 更新所有包
  2. 项目依赖管理

    • NPM 自动记录和管理项目的依赖项,通过 package.json 文件定义项目元数据及依赖项:
      1
      2
      3
      4
      5
      6
      7
      {
      "name": "my-project",
      "version": "1.0.0",
      "dependencies": {
      "express": "^4.18.2"
      }
      }
  3. 包发布与分享

    • 开发者可以通过 NPM 将自己的模块发布到 NPM 仓库,供其他人下载和使用:
      1
      npm publish
  4. 运行脚本

    • package.json 文件中,开发者可以定义自定义脚本,通过 NPM 执行:
      1
      2
      3
      4
      "scripts": {
      "start": "node app.js",
      "test": "jest"
      }
      运行方式:
      1
      npm run start

NPM 的安装与使用

  1. NPM 安装

    • 随 Node.js 一起安装,无需单独安装。
    • 验证安装:
      1
      npm -v  # 显示 NPM 版本
  2. 常用命令

    • 安装依赖
      1
      2
      npm install <package> --save       # 安装依赖到项目中
      npm install <package> --save-dev # 安装开发依赖
    • 全局安装
      1
      npm install -g <package>  # 全局安装
    • 移除依赖
      1
      npm uninstall <package>
  3. 使用 NPM 初始化项目
    初始化一个新的 Node.js 项目:

    1
    npm init

    交互式生成 package.json 文件。


NPM 与 Yarn 的比较

  • 速度:NPM 在 v5 之后显著优化了性能,但 Yarn 使用缓存技术在某些场景中更快。
  • 锁文件:NPM 使用 package-lock.json,而 Yarn 使用 yarn.lock
  • 社区支持:NPM 的社区和生态系统比 Yarn 更大,但 Yarn 也很受欢迎。

资源链接

如需进一步说明特定功能或命令的用法,请告诉我!

npm editorconfig包 是什么

在 NPM 中,与 EditorConfig 相关的包主要是用来解析、验证或与 EditorConfig 文件(.editorconfig)集成的工具。这些包旨在帮助开发者统一项目的代码风格规则并验证是否符合 EditorConfig 的定义。

以下是常见的与 EditorConfig 相关的 NPM 包及其用途:


1. editorconfig

  • 简介
    editorconfig 是一个用于解析 .editorconfig 文件的 Node.js 包。它允许开发者在代码中读取并应用 .editorconfig 定义的规则。

  • 安装

    1
    npm install editorconfig
  • 主要用途

    • 读取 .editorconfig 文件中的规则。
    • 在开发工具或自定义脚本中应用 EditorConfig 规则。
  • 示例代码

    1
    2
    3
    4
    5
    6
    const editorconfig = require('editorconfig');

    (async () => {
    const config = await editorconfig.parse('.editorconfig');
    console.log(config);
    })();
  • 适用场景

    • 构建自定义格式化工具。
    • 在构建或测试流程中验证文件是否符合 .editorconfig 规则。

2. editorconfig-checker

  • 简介
    editorconfig-checker 是一个用于检查项目文件是否符合 .editorconfig 定义的 CLI 工具。

  • 安装

    1
    npm install -g editorconfig-checker
  • 主要用途

    • 在终端检查代码是否符合 .editorconfig 规则。
    • 用于 CI/CD 流程中的自动化验证。
  • 使用方法

    1
    editorconfig-checker
  • 特点

    • 提供清晰的错误报告。
    • 支持忽略特定文件或目录。

3. prettier-plugin-editorconfig

  • 简介
    该插件将 Prettier 与 EditorConfig 结合,允许 Prettier 自动读取 .editorconfig 配置并应用到格式化规则中。

  • 安装

    1
    npm install --save-dev prettier prettier-plugin-editorconfig
  • 主要用途

    • 使用 Prettier 的强大格式化功能,同时保持与 .editorconfig 一致。
  • 使用方法

    1
    npx prettier --write .
  • 特点

    • 无需重复定义规则。
    • 自动应用 EditorConfig 配置。

4. lint-staged (配合 EditorConfig)

  • 简介
    lint-staged 是一个工具,用于在 Git 暂存文件上运行格式化工具。结合 EditorConfig 和格式化工具(如 Prettier),可以在提交代码前确保符合规则。

  • 安装

    1
    npm install --save-dev lint-staged
  • 配置示例
    package.json 中:

    1
    2
    3
    4
    5
    6
    "lint-staged": {
    "*.{js,css,md}": [
    "prettier --write",
    "editorconfig-checker"
    ]
    }

推荐使用场景

  • 如果只需解析 .editorconfig 文件规则,用 editorconfig 包。
  • 如果需要检查项目文件是否符合 EditorConfig,选择 editorconfig-checker
  • 如果希望格式化工具自动适配 .editorconfig,推荐使用 prettier-plugin-editorconfig

更多信息请访问 EditorConfig 官方网站NPM

iconv 命令 详解

iconv 是一个强大的字符编码转换工具,常用于在不同编码之间转换文件或字符串的编码格式。以下是对其常用功能和参数的详解:


命令基本格式

1
iconv [选项] -f <原始编码> -t <目标编码> [输入文件] -o [输出文件]
  • -f: 指定输入文件的编码(from)。
  • -t: 指定目标文件的编码(to)。
  • -o: 指定输出文件(默认输出到标准输出)。

常用参数详解

  1. 基本参数

    • **-f**:指定原始文件的编码格式。
      1
      iconv -f UTF-8 -t GBK input.txt
    • **-t**:指定目标编码格式。
      1
      iconv -f UTF-8 -t ISO-8859-1 input.txt > output.txt
    • **-o**:将输出保存到文件。
      1
      iconv -f UTF-8 -t GBK input.txt -o output.txt
  2. 列出支持的编码

    1
    iconv -l

    该命令列出 iconv 支持的所有编码格式。

  3. 处理标准输入
    使用管道将字符串直接传递给 iconv

    1
    echo "你好,世界" | iconv -f UTF-8 -t GBK
  4. 忽略或替代无效字符

    • //IGNORE: 忽略无法转换的字符。
      1
      iconv -f UTF-8 -t GBK//IGNORE input.txt -o output.txt
    • //TRANSLIT: 用近似字符替换无法转换的字符。
      1
      iconv -f UTF-8 -t ASCII//TRANSLIT input.txt -o output.txt

示例用法

  1. 将文件从 UTF-8 转换为 GBK

    1
    iconv -f UTF-8 -t GBK input.txt -o output.txt
  2. 将字符串从 GBK 转换为 UTF-8

    1
    echo "你好" | iconv -f GBK -t UTF-8
  3. 转换整个目录的文件
    使用 findiconv 的组合:

    1
    find ./ -name "*.txt" -exec iconv -f GBK -t UTF-8 {} -o converted_{} \;
  4. 排查编码问题
    当文件中有乱码时,可以尝试转换为不同编码格式查看结果。


应用场景

  1. 文本文件国际化:在跨平台和多语言环境中转换文件编码。
  2. 解决乱码问题:修复因编码不一致导致的显示错误。
  3. 批量处理文件:通过脚本将大量文件的编码格式进行转换。

注意事项

  • 转换前确认文件的原始编码(例如通过工具 filechardet 检测)。
  • 在转换时,确保目标编码支持原始文件中的所有字符,否则可能会出现数据丢失。

更多参考资料: