【OpenVINO】OpenVINO C# API 常用 API 详解与演示
OpenVINO C# API 常用 API 详解与演示
- 1 安装OpenVINO C# API
- 2 导入程序集
- 3 初始化OpenVINO 运行时内核
- 4 加载并获取模型信息
- 4.1 加载模型
- 4.2 获取模型信息
- 5 编译模型并创建推理请求
- 6 张量Tensor
- 6.1 张量的获取与设置
- 6.2 张量的信息获取与设置
- 7 加载推理数据
- 7.1 获取输入张量
- 7.2 添加推理数据
- 8 模型推理
- 9 获取推理结果
- 10 释放分配的内存
- 11 Yolov8分类模型示例
- 12 总结
OpenVINO™ 工具套件可以加快深度学习视觉应用开发速度,帮助用户在从边缘到云的各种英特尔平台上,更加方便快捷的将 AI 模型部署到生产系统中。OpenVINO™ 2023.1 LTS 版本现已发布,可帮助你快速轻松地开发卓越的人工智能应用,并跨边缘和云端部署深度学习推理工作负载,无论你处于人工智能编程的什么阶段。
C# 是由 C 和 C++ 衍生出来的一种安全的、稳定的、简单的、优雅的面向对象编程语言,它综合了 VB 简单的可视化操作和 C++ 的高运行效率,成为支持成为.NET 开发的首选语言。作为人工智能开发人员,如果你希望在 C# 端使用 OpenVINO™ ,OpenVINO C# API将是你的首选,并且制作了 NuGet 程序包,实现在 C# 端了一站式安装与使用 OpenVINO™ 。
项目的首发网址:OpenVINO™ C# API 详解与演示 | 开发者实战。
OpenVINO C# API在制作时参考了OpenVINO™ C++ API,因此对于之前使用过OpenVINO™ 的人十分友好。下面表格向我们展示了 C# 与 C++ API的对应关系
| Class | C++ API | C# API | 说明 |
|---|---|---|---|
| Core class | ov::Core | Core | OpenVINO运行时核心实体类 |
| Model class | ov::Model | Model | 用户自定义模型类 |
| CompiledModel class | ov::CompiledModel | CompiledModel | 已编译的模型类 |
| Output class | ov:: Output<ov::Node> | Output | 节点输出的句柄类 |
| Input class | ov:: Input<ov::Node> | Input | 节点输入的句柄类 |
| InferRequest class | ov::InferRequest | ov::InferRequest | 以异步或同步方式运行推断请求的类 |
| Tensor class | ov::Tensor | Tensor | 张量 |
| Shape class | ov::Shape | Shape | 张量的形状类 |
在本文中,将会根据模型部署的一般步骤,演示从模型加载到推理的方法函数使用方式,并于C++ API 做对比。
1 安装OpenVINO C# API
OpenVINO C# API 支持 NuGet 程序包安装方式,这与在C++中安装过程相比,较为简单,并且程序包中包含了最新版的 OpenVINO™ 2023.1 发行版本的 Release,可以通 过 NuGet 安装后直接使用。
如果使用Visual Studio 编译该项目,则可以通过 NuGet 程序包管理功能直接安装即可:

如果通过dotnet命令方式安装,通过下面语句进行安装即可:
dotnet add package OpenVINO.CSharp.win
2 导入程序集
OpenVINO C# API 程序集全部在CSharp命名空间下,因此若要使用 OpenVINO C# API,需要先引入命名空间:
using OpenVinoSharp;
3 初始化OpenVINO 运行时内核
Core类代表一个OpenVINO运行时核心实体,后续的读取模型、加载模型等方法都需要通过 Core 类进行创建,在封装C# API 时,为了与 C++ API 对应,也对 Core 类进行了封装,并封装了与 C++ API 中对应的方法
在C#中的初始化方式:
Core core = new Core();
在C++中的初始化方式:
ov::Core core;
4 加载并获取模型信息
4.1 加载模型
OpenVINO™ 2022.1版本更新之后,加载,下面是所使用的 API 方法:
| API | 作用 |
|---|---|
| Core.read_model () | 将模型从硬盘载入内存,并返回Model对象。 |
在C#中加载模型的方式:
Model model = core.read_model(model_path);
在C++中的初始化方式:
std::shared_ptr<ov::Model> model = core.read_model(model_path);
4.2 获取模型信息
通过 Core.read_model ()方法获得的 Model 对象和通过 Core.compile_model ()方法获得的 CompiledModel 对象,都支持直接访问属性获取输入与输出层信息。以Model对象获取模型信息为例,下面是所使用的 API 方法:
| API | 作用 |
|---|---|
| Model.get_friendly_name() | 获取模型的friendly name。 |
| Model.input() | 获取模型的输入层,并返回 Input对象。 |
| Model.output() | 获取模型的输出层,并返回 Output对象。 |
Input/Output 主要是封装了模型网络层,可以通过下面 API 实现获取模型的详细信息:
| API | 作用 |
|---|---|
| Output.get_any_name() | 获取模型网络层的名字。 |
| Output.get_element_type() | 获取模型网络层的数据类型,并返回 OvType对象,OvType主要封装了网络的基本数据类型。 |
| Output.get_shape() | 获取模型网络层的形状,并返回 Shape对象,Shape封装了网络层的形状数组。 |
在 C# 中通过下方代码,可以直接获取模型的输入、输入层以及模型的friendly name:
string model_name = model.get_friendly_name();
Input input = model.input();
Output output = model.output();
然后将模型具体信息打印到控制台页面:
Console.WriteLine("Model name: {0}", model_name);
Console.WriteLine("/------- [In] -------/");
Console.WriteLine("Input name: {0}", input.get_any_name());
Console.WriteLine("Input type: {0}", input.get_element_type().to_string());
Console.WriteLine("Input shape: {0}", input.get_shape().to_string());
Console.WriteLine("/------- [Out] -------/");
Console.WriteLine("Output name: {0}", output.get_any_name());
Console.WriteLine("Output type: {0}", output.get_element_type().to_string());
Console.WriteLine("Output shape: {0}", output.get_shape().to_string());
获取模型网络层信息如下:
Model name: torch_jit
/------- [In] -------/
Input name: data
Input type: float
Input shape: [1,3,224,224]
/------- [Out] -------/
Output name: prob
Output type: float
Output shape: [1,1000]
同样的输出信息,我们使用 C++ API 实现如下:
std::cout << "Model name: " << model->get_friendly_name() << std::endl;
ov::Output<ov::Node> input = model->input();
std::cout << "/------- [In] -------/" << std::endl;
std::cout << "Input name: " << input.get_any_name() << std::endl;
std::cout << "Input type: " << input.get_element_type().c_type_string() << std::endl;
std::cout << "Input shape: " << input.get_shape().to_string() << std::endl;
ov::Output<ov::Node> output = model->output();
std::cout << "/------- [Out] -------/" << std::endl;
std::cout << "Output name: " << output.get_any_name() << std::endl;
std::cout << "Output type: " << output.get_element_type().c_type_string() << std::endl;
std::cout << "Output shape: " << output.get_shape().to_string() << std::endl;
5 编译模型并创建推理请求
在读取本地模型后,调用模型编译方法将模型编译为可以在目标设备上执行的 compile_model 对象,并通过该对象创建用于推断已编译模型的推断请求对象。下面是所使用的 API 方法:
| API | 作用 |
|---|---|
| Core.compile_model() | 将模型编译为可以在目标设备上执行的 compile_model 对象。 |
| CompiledModel.create_infer_request() | 创建用于推断已编译模型的推断请求对象,创建的请求已经分配了输入和输出张量。 |
在 C# 中编译模型并创建推理请求的方式:
CompiledModel compiled_model = core.compile_model(model, "AUTO");
InferRequest infer_request = compiled_model.create_infer_request();
使用C++ API中编译模型并创建推理请求的方式:
CompiledModel compiled_model = core.compile_model(model, "AUTO");
InferRequest infer_request = compiled_model.create_infer_request();
6 张量Tensor
6.1 张量的获取与设置
在创建推理请求后,系统会自动创建和分配输入和输出的张量,张量可以通过InferRequest 对象获得,并且可以自定义张量并加载到模型指定节点;可以根据张量的输入输出序号、名称以及模型节点Node对象获取和设置,主要C# API 如下:
| API | 作用 |
|---|---|
| InferRequest.set_tensor() | 设置要推断的输入/输出张量。 |
| InferRequest.set_input_tensor() | 设置要推断的输入张量。 |
| InferRequest.set_output_tensor() | 设置要推断的输出张量 |
| InferRequest.get_tensor() | 获取用于推理的输入/输出张量。 |
| InferRequest.get_input_tensor() | 获取用于推理的输入张量。 |
| InferRequest.get_output_tensor() | 获取用于推理的输出张量。 |
6.2 张量的信息获取与设置
张量中主要包含的信息有张量的形状(Shape)、张量的数据格式(OvType-> element.Type)以及张量中的内存数据。可以通过以下API方法操作张量的参数:
| API | 作用 |
|---|---|
| Tensor.set_shape () | 给张量设置一个新的形状。 |
| Tensor.get_shape() | 获取张量的形状。 |
| Tensor.get_element_type() | 获取张量的数据类型。 |
| Tensor.get_size() | 获取张量的数据长度。 |
| Tensor.get_byte_size() | 获取张量的字节大小。 |
| Tensor.data() | 获取张量的内存地址。 |
| Tensor.set_data() | 将指定类型的数据加载到张量内存下。 |
| Tensor.get_data() | 从张量中读取指定类型的数据。 |
以上方法是对张量的一些基础操作,除了set_data、get_data是OpenVINO C# API独有的,其他接口都与C++API一致。
7 加载推理数据
7.1 获取输入张量
对于单输入的模型可以直接通过get_input_tensor()方法获得,并调用Tensor的相关方法获取Tensor的相关信息,C# 代码如下所示:
Tensor input_tensor = infer_request.get_input_tensor();
Console.WriteLine("/------- [Input tensor] -------/");
Console.WriteLine("Input tensor type: {0}", input_tensor.get_element_type().to_string());
Console.WriteLine("Input tensor shape: {0}", input_tensor.get_shape().to_string());
Console.WriteLine("Input tensor size: {0}", input_tensor.get_size());
获取输出结果为:
/------- [Input tensor] -------/
Input tensor type: f32
Input tensor shape: Shape : {1, 3, 224, 224}
Input tensor size: 150528
对于上述的同样输出内容,我们也可以通过C++ API 实现,C++ 代码如下:
ov::Tensor input_tensor = infer_request.get_input_tensor();
std::cout << "/------- [Input tensor] -------/" << std::endl;
std::cout << "Input tensor type: " << input_tensor.get_element_type().c_type_string() << std::endl;
std::cout << "Input tensor shape: " << input_tensor.get_shape().to_string() << std::endl;
std::cout << "Input tensor size: " << input_tensor.get_size() << std::endl;
7.2 添加推理数据
这一步主要是将处理好的图片数据加载到Tensor数据内存中,OpenVINO的API中提供了访问内存地址的接口,可以获取数据内存首地址,不过为了更好的加载推理数据,我们此处封装了set_data()方法,可以实现将处理后的图片数据加载到数据内存上。在C#中的代码为:
Mat input_mat = new Mat();
Shape input_shape = input_tensor.get_shape();
long channels = input_shape[1];
long height = input_shape[2];
long width = input_shape[3];
float[] input_data = new float[channels * height * width];
Marshal.Copy(input_mat.Ptr(0), input_data, 0, input_data.Length);
input_tensor.set_data(input_data);
下面是在C++中实现上述功能的代码:
cv::Mat input_mat;
float* input_data = input_tensor.data<float>();
ov::Shape input_shape = input_tensor.get_shape();
size_t channels = input_shape[1];
size_t height = input_shape[2];
size_t width = input_shape[3];
for (size_t c = 0; c < channels; ++c) {for (size_t h = 0; h < height; ++h) {for (size_t w = 0; w < width; ++w) {input_data[c * height * width + h * width + w] = input_mat.at<cv::Vec<float, 3>>(h, w)[c];}}
}
8 模型推理
在加载完推理数据后,就可以调用模型推理的API方法推理当前数据,主要使用到的API方法为:
| API | 作用 |
|---|---|
| InferRequest.infer() | 在同步模式下推断指定的输入。 |
调用该方法也较为简单,只需要调用该API接口即可,在C#中的代码为:
infer_request.infer();
C++中的代码与C++中一致。
9 获取推理结果
对于单输出的模型可以直接通过get_output_tensor()方法获得,并调用Tensor的相关方法获取Tensor的相关信息,C# 代码如下所示:
Tensor output_tensor = infer_request.get_output_tensor();
Console.WriteLine("/------- [Output tensor] -------/");
Console.WriteLine("Output tensor type: {0}", output_tensor.get_element_type().to_string());
Console.WriteLine("Output tensor shape: {0}", output_tensor.get_shape().to_string());
Console.WriteLine("Output tensor size: {0}", output_tensor.get_size());
获取输出output_tensor信息为:
/------- [Output tensor] -------/
Output tensor type: f32
Output tensor shape: Shape : {1, 1000}
Output tensor size: 1000
对于输出Tensor,我们只需要读取输出内存上的数据即可,此处我们封装了get_data()方法,可以直接获取输出内存上的数据,在C#中的代码为:
float[] result = output_tensor.get_data<float>(1000);
同样获取推理结果,在C++中的代码为:
const float* output_data = output_tensor.data<const float>();
float result[1000];
for (int i = 0; i < 1000; ++i) {result[i] = *output_data;output_data++;
}
在获取结果后,后续的处理需要根据模型的输出类型做相应的处理。
10 释放分配的内存
由于C#在封装时采用的C API 接口实现的,因此在C#中会产生较多的 非托管内存,若该对象出现循环重复创建,会导致过多的内存未释放导致内存泄漏,因此对于临时创建的对象在使用后要即使销毁,销毁方式也较为简单,只需要调用对象的dispose()方法即可。
output_tensor.dispose();
input_shape.dispose();
infer_request.dispose();
compiled_model.dispose();
input.dispose();
output.dispose();
model.dispose();
core.dispose();
11 Yolov8分类模型示例
下面代码展示了Yolov8分类模型使用OpenVINO C# API API方法部署模型的完整代码:
using OpenCvSharp;
using OpenCvSharp.Dnn;
using OpenVinoSharp;
using System.Data;
using System.Runtime.InteropServices;
namespace test_openvino_csharp_api
{internal class Program{static void Main(string[] args){string model_path = "E:\\GitSpace\\ OpenVINO-CSharp-API \\model\\yolov8\\yolov8s-cls.xml";Core core = new Core(); // 初始化推理核心Model model = core.read_model(model_path); // 读取本地模型CompiledModel compiled_model = core.compile_model(model, "AUTO"); // 便哟模型到指定设备// 获取模型的输入输出信息Console.WriteLine("Model name: {0}", model.get_friendly_name());Input input = compiled_model.input();Console.WriteLine("/------- [In] -------/");Console.WriteLine("Input name: {0}", input.get_any_name());Console.WriteLine("Input type: {0}", input.get_element_type().to_string());Console.WriteLine("Input shape: {0}", input.get_shape().to_string());Output output = compiled_model.output();Console.WriteLine("/------- [Out] -------/");Console.WriteLine("Output name: {0}", output.get_any_name());Console.WriteLine("Output type: {0}", output.get_element_type().to_string());Console.WriteLine("Output shape: {0}", output.get_shape().to_string());// 创建推理请求InferRequest infer_request = compiled_model.create_infer_request();// 获取输入张量Tensor input_tensor = infer_request.get_input_tensor();Console.WriteLine("/------- [Input tensor] -------/");Console.WriteLine("Input tensor type: {0}", input_tensor.get_element_type().to_string());Console.WriteLine("Input tensor shape: {0}", input_tensor.get_shape().to_string());Console.WriteLine("Input tensor size: {0}", input_tensor.get_size());// 读取并处理输入数据Mat image = Cv2.ImRead(@"E:\GitSpace\ OpenVINO-CSharp-API \dataset\image\demo_7.jpg");Mat input_mat = new Mat();input_mat = CvDnn.BlobFromImage(image, 1.0 / 255.0, new Size(224, 224), 0, true, false);// 加载推理数据Shape input_shape = input_tensor.get_shape();long channels = input_shape[1];long height = input_shape[2];long width = input_shape[3];float[] input_data = new float[channels * height * width];Marshal.Copy(input_mat.Ptr(0), input_data, 0, input_data.Length);input_tensor.set_data(input_data);// 模型推理infer_request.infer(); // 获取输出张量Tensor output_tensor = infer_request.get_output_tensor();Console.WriteLine("/------- [Output tensor] -------/");Console.WriteLine("Output tensor type: {0}", output_tensor.get_element_type().to_string());Console.WriteLine("Output tensor shape: {0}", output_tensor.get_shape().to_string());Console.WriteLine("Output tensor size: {0}", output_tensor.get_size());// 获取输出数据float[] result = output_tensor.get_data<float>(1000);List<float[]> new_list = new List<float[]> { };for (int i = 0; i < result.Length; i++){new_list.Add(new float[] { (float)result[i], i });}new_list.Sort((a, b) => b[0].CompareTo(a[0]));KeyValuePair<int, float>[] cls = new KeyValuePair<int, float>[10];for (int i = 0; i < 10; ++i){cls[i] = new KeyValuePair<int, float>((int)new_list[i][1], new_list[i][0]);}Console.WriteLine("\n Classification Top 10 result : \n");Console.WriteLine("classid probability");Console.WriteLine("------- -----------");for (int i = 0; i < 10; ++i){Console.WriteLine("{0} {1}", cls[i].Key.ToString("0"), cls[i].Value.ToString("0.000000"));}// 销毁非托管内存output_tensor.dispose();input_shape.dispose();infer_request.dispose();compiled_model.dispose();input.dispose();output.dispose();model.dispose();core.dispose();}}
}
12 总结
在本文中我们基于模型推理流程,演示了OpenVINO C# API使用方法,并和OpenVINO C++API进行了对比,展示了OpenVINO C# API与C++API在使用的区别,这也对使用过C++ API的开发者十分友好,上手会十分容易。
在本文中我们只展示了基础的模型推理流程代码,也对各个API进行了测试,针对其他比较高级的API方法,我们后续会继续进行测试其他API方法,向各位开发者展示其用法。
总的来说,目前OpenVINO C# API已经完全支持在Windows环境下的安装使用,欢迎各位开发者安装使用,如有相关问题或优化方法,也欢迎大家提出意见与指导。
相关文章:
【OpenVINO】OpenVINO C# API 常用 API 详解与演示
OpenVINO C# API 常用 API 详解与演示 1 安装OpenVINO C# API2 导入程序集 3 初始化OpenVINO 运行时内核4 加载并获取模型信息4.1 加载模型4.2 获取模型信息 5 编译模型并创建推理请求6 张量Tensor6.1 张量的获取与设置6.2 张量的信息获取与设置 7 加载推理数据7.1 获取输入张量…...
django无法导入第三方库
引子 有的人可能会很困惑,为什么自己在pip中安装了某个包,但是在django中死活无法导入。 在cmd中能够导入。 启动django,总是无法导入。 本文将会用一分钟解决你的困惑。 正文 那么本文以上述的第三方库dj_db_conn_pool为例,…...
7-k8s-helm管理
文章目录 一、为什么需要Helm二、Helm相关概念介绍三、Helm安装四、Helm指令介绍五、Helm创建tomcat六、Helm创建tomcat其他方式七、Helm创建redis 一、为什么需要Helm k8s部署:k8s平台部署的服务都是由资源文件描述组成,传统的k8s部署应用需要手工编排…...
零基础怎么样才能学好 Python?Python 入门必看
Python 目前可以用一个字来描述那就是 “火”,问题来了,这么火的语言零基础小白到底该怎样学习 Python? 首先,从基础开始学习,切勿毛躁。 刚开始学习 Python 的时候,我们可能会有些毛躁总觉得这些知识太简…...
1.X3-Warming up
/* 此程序使用 Boost Spirit 库来解析用户提供的逗号分隔的数字列表。它演示了如何使用 Spirit 来定义解析 器和执行解析操作,并且在用户输入时反复执行解析操作。用户可以提供一系列逗号分隔的数字,程序会检查它们 是否符合指定的解析规则。如果解析成功…...
【23真题】两电一邮之一,难度骤降!24可能回升!
今天分享的是23年电子科技大学858的信号与系统试题及解析。23成电858真题和22年相比,难度骤降!今年我预测可能会有反弹。22年成电858真题,是我做过的22真题中数一数二的难度!23年一般,但是题目也很好,真的很…...
持续集成部署-k8s-资源调度:DaemonSet
持续集成部署-k8s-资源调度:DaemonSet 1. DaemonSet 简介2. 部署 Fluent 日志收集程序1. DaemonSet 简介 在 Kubernetes 中,DaemonSet 是一种用于在集群中运行一个 Pod 副本的控制器对象。它可以保证在每个节点上都运行一个 Pod 副本,并且在节点加入或退出集群时自动地更新…...
RabbitMQ内容
RabbitMQ是一款开源的消息中间件,支持多个消息协议,包括AMQP、STOMP、MQTT等,它的主要功能是将分散的应用程序连接在一起,以便它们可以相互通信,从而构建可靠的、高效的分布式系统。 RabbitMQ的基础概念包括ÿ…...
搭建一个vscode+uni+vue的小程序项目
我们使用 vue2 创建工程作为示例,uni-app中Vue2版的组件库和插件也比较多,稳定、问题少,可以先参考下官方文档:uni-app官网 既然是使用vue脚手架,那肯定要全局安装vue/cli,已安装的可以跳过。 注意:Vue2创…...
处理ElementUI组件默认样式多次重复问题
问题截图: 解决办法: 在postcss.config.js文件里添加配置项: module.exports {plugins: {autoprefixer: {},cssnano: {} //添加这行代码}, } 处理结果: github issues: https://github.com/ElemeFE/element/is…...
配置hpa后,target显示<unknown>/50%
背景: 有两个服务,server 负责主要后端请求,bill 负责计量计费请求。服务都是使用 helm 部署。测试提了一个缺陷,说全部服务没有配置hpa。 解决一 按照之前的代码结构添加了hpa后,发现: ➜ kubectl get…...
用java实现抖音授权登录
要使用Java实现抖音授权登录,您需要使用抖音开放平台提供的API。以下是一个简单的Java代码示例,演示如何使用OAuth 2.0授权登录流程: 要使用Java实现抖音授权登录,您需要按照以下步骤进行操作: 创建抖音开放平台账号…...
Atlassian午餐会直播回顾:如何在Jira中进行项目时间与成本管理?
工时管理是项目过程管理的一个重要手段,通过科学记录项目组成员在项目执行过程中的任务完成和时间消耗情况,可以帮助管理者精准评估成员工作效率,实时掌握项目进展,并有效管控项目成本。 想要成为时间管理大师吗?在近…...
屏幕时代的数字化信息管理:从传统到纯软件的转变
随着数字化时代的推进,屏幕已经成为了企业和机构向外界传递信息的主要媒介。除了传统的手机和电脑屏幕,线下空间如办公楼、商场、店铺、医院、展厅等也需要利用屏幕来展示和发布各类信息。 管理这些屏幕和发布信息并不是一件容易的事情,特别是…...
Stm32_标准库_14_串口蓝牙模块_解决手机与蓝牙模块数据传输的不完整性
由手机向蓝牙模块传输时间信息,Stm32获取信息并将已存在信息修改为传入信息 测试代码: #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h"uint16_t num…...
基于深度学习的“语义通信编解码技术”框架分类
目录 基于神经网络的语义提取基于神经网络的语义信源编码基于神经网络的语义信源信道联合编码基于神经网络的语义编码与数字调制联合设计参考文献 基于神经网络的语义提取 在现有的信源编码前端加上一个语义提取神经网络[53] ,如图所示。语义提取神经网络的输入是原…...
信钰证券:股票抵押是好还是坏?
股票典当是指将持有的股票作为质押品向银行或其他金融机构融资的行为。股票典当有其优势和下风,下面咱们将从多个角度来剖析股票典当是否好仍是坏。 一、优势 1.占用资金少 相比较于其他融资办法,股票典当所需求占用的资金较少,只需将股票作…...
媒体基础:打开多模态大模型的新思路
编者按:2023年是微软亚洲研究院建院25周年。25年来,微软亚洲研究院探索并实践了一种独特且有效的企业研究院的新模式,并以此为基础产出了诸多对微软公司和全球社会都有积极影响的创新成果。一直以来,微软亚洲研究院致力于创造具有…...
dubbo-admin安装
一、dubbo-admin安装 1、环境准备 dubbo-admin 是一个前后端分离的项目。前端使用vue,后端使用springboot,安装 dubbo-admin 其实就是部署该项目。我们将dubbo-admin安装到开发环境上。要保证开发环境有jdk,maven,nodejs 安装no…...
Kaggle - LLM Science Exam(三):Wikipedia RAG
文章目录 一、赛事概述1.1 OpenBookQA Dataset1.2 比赛背景1.3 评估方法和代码要求1.4 比赛数据集1.5 优秀notebook 二、 [EDA, Data gathering] LLM-SE ~ Wiki STEM | 1k DS2.1 Data overview2.2 Data gathering 三、如何高效收集数据3.1 概述3.2 与训练数据关联的维基百科类别…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
