当前位置: 首页 > news >正文

【2024第一期CANN训练营】4、AscendCL推理应用开发

文章目录

  • 【2024第一期CANN训练营】4、AscendCL推理应用开发
    • 1. 创建代码目录
    • 2. 构建模型
      • 2.1 下载原始模型文件
      • 2.2 使用ATC工具转换模型
      • 2.3 注意事项
    • 3. 模型加载
      • 3.1 示例代码
    • 4. 模型执行
      • 4.1 获取模型描述信息
      • 4.2 准备输入/输出数据结构
      • 4.3 执行模型推理
      • 4.4 释放内存和数据类型
    • 5. 模型卸载
    • 6. 多种模型推理方式(可选)

【2024第一期CANN训练营】4、AscendCL推理应用开发

本教程将介绍如何使用AscendCL接口开发一个基于昇腾AI处理器的基础推理应用。昇腾社区提供了全栈AI计算基础设施,包括硬件、软件架构、计算框架等,为AI应用开发提供强大支持。

1. 创建代码目录

创建一个项目目录结构,用于存放代码文件、模型文件、测试数据等。以下是一个示例目录结构:

MyInferenceApp
├── model/                  # 存放模型文件
│   ├── model.om            # 昇腾AI处理器的离线模型文件├── data/                   # 存放测试数据
│   ├── input.jpg           # 测试图片数据├── inc/                    # 存放头文件
│   ├── app.h               # 应用声明的头文件├── src/                    # 存放源代码和编译脚本
│   ├── CMakeLists.txt      # 编译脚本
│   ├── main.cpp            # 主要的源代码文件├── out/                    # 存放输出结果

2. 构建模型

首先,需要一个适配昇腾AI处理器的离线模型(.om文件)。

可以使用ATC(Ascend Tensor Compiler)工具将开源框架的网络模型转换为适配昇腾AI处理器的离线模型(*.om文件)。以ONNX框架的ResNet-50网络为例,我们将一步步进行说明。

2.1 下载原始模型文件

cd <SAMPLE_DIR>/MyFirstApp_ONNX/model
wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/003_Atc_Models/resnet50/resnet50.onnx

2.2 使用ATC工具转换模型

执行以下命令,将ONNX模型转换为昇腾AI处理器能识别的*.om模型文件。请确保具有命令中相关路径的可读、可写权限,并根据实际情况替换<SAMPLE_DIR><soc_version>

atc --model=resnet50.onnx --framework=5 --output=resnet50 --input_shape="actual_input_1:1,3,224,224" --soc_version=<soc_version>
  • --model: 指定ResNet-50网络的模型文件路径。
  • --framework: 指定原始框架类型,ONNX框架的值为5。
  • --output: 指定输出的模型文件名,这里是resnet50.om。
  • --input_shape: 指定模型输入数据的shape。
  • --soc_version: 指定昇腾AI处理器的版本。执行npu-smi info命令查询,在查询到的“Name”前增加Ascend信息,如Ascend910A

如果想快速体验使用转换后的om离线模型文件进行推理,请准备好环境、om模型文件、符合模型输入要求的*.bin格式的输入数据,并参考msame工具的README进行体验。(可选)

2.3 注意事项

  • 如果模型转换时提示有不支持的算子,请参考TBE&AI CPU自定义算子开发指南完成自定义算子后,再重新转换模型。
  • 如果模型转换时提示有算子编译相关问题,无法定位问题时,需设置环境变量DUMP_GE_GRAPHDUMP_GRAPH_LEVEL,重新模型转换,收集模型转换过程中的图描述信息,提供给华为工程师定位问题。
  • 如果模型的输入Shape是动态的,请参考模型动态Shape输入推理的说明。
  • 如果现有网络不满足需求,可以使用昇腾AI处理器支持的算子、调用Ascend Graph接口自行构建网络,再编译成om离线模型文件。详细说明请参见Ascend Graph开发指南。

3. 模型加载

模型加载的接口调用流程可以分为两种方式:

  • 通过接口中的配置参数区分加载方式:这种方式适用于从文件加载、从内存加载等不同加载方式,但涉及多个接口配合使用。
    • 使用aclmdlSetConfigOpt接口、aclmdlLoadWithConfig接口通过设置各属性的取值,直接一次性配置是从文件加载,还是从内存加载,以及内存是由系统内部管理,还是由用户管理。

img

  • 通过不同接口区分加载方式:这种方式根据不同的加载方式选择不同的接口,操作简单,但需要记住各种方式的加载接口
    • 当输入数据的Shape确定时,由用户自行管理内存。需要调用aclmdlQuerySizeaclrtMalloc接口查询和申请模型运行时所需工作内存、权值内存的大小,然后再从文件或内存进行加载。
      • aclmdlLoadFromFileWithMem从文件加载离线模型数据,由用户自行管理内存
      • aclmdlLoadFromMemWithMem从内存加载离线模型数据,由用户自行管理内存
    • 当输入数据的Shape不确定时,由系统内部管理内存
      • aclmdlLoadFromFile从文件加载离线模型数据,由系统内部管理内存
      • aclmdlLoadFromMem从内存加载离线模型数据,由系统内部管理内存

img

3.1 示例代码

以下是一个关键步骤的代码示例,用于从文件加载模型并自行管理内存

// 1.初始化变量。
const char* omModelPath = "../model/resnet50.om";// 2.根据模型文件获取模型执行时所需的权值内存大小、工作内存大小。
size_t modelMemSize_ = 0, modelWeightSize_ = 0;
aclError ret = aclmdlQuerySize(omModelPath, &modelMemSize_, &modelWeightSize_);// 3.根据工作内存大小,申请Device上模型执行的工作内存。
void* modelMemPtr_ = nullptr;
ret = aclrtMalloc(&modelMemPtr_, modelMemSize_, ACL_MEM_MALLOC_HUGE_FIRST);// 4.根据权值内存的大小,申请Device上模型执行的权值内存。
void* modelWeightPtr_ = nullptr;
ret = aclrtMalloc(&modelWeightPtr_, modelWeightSize_, ACL_MEM_MALLOC_HUGE_FIRST);// 5.加载离线模型文件,由用户自行管理模型运行的内存(包括权值内存、工作内存)。
// 模型加载成功,返回标识模型的ID。
aclmdlDesc* modelId_ = nullptr;
ret = aclmdlLoadFromFileWithMem(omModelPath, &modelId_, modelMemPtr_, modelMemSize_, modelWeightPtr_, modelWeightSize_);

4. 模型执行

模型执行的接口调用流程可以分为以下几个步骤:

  • 在模型加载之后,模型执行之前,需要准备输入、输出数据结构,并将输入数据传输到模型输入数据结构的对应内存中。
  • 模型执行结束后,若无需使用输入数据、aclmdlDesc类型、aclmdlDataset类型、aclDataBuffer类型等相关资源,需及时释放内存、销毁对应的数据类型,防止内存异常。

4.1 获取模型描述信息

  • 调用aclmdlCreateDesc接口创建描述模型基本信息的数据类型。

  • 调用aclmdlGetDesc接口根据模型加载中返回的模型ID获取模型基本信息。

// 1. 获取模型描述信息
aclmdlDesc* modelDesc_ = aclmdlCreateDesc();
aclError ret = aclmdlGetDesc(modelDesc_, modelId_);

4.2 准备输入/输出数据结构

// 2. 准备模型推理的输入数据结构
// 申请输入内存
size_t modelInputSize;
void *modelInputBuffer = nullptr;
aclRet = aclrtMalloc(&modelInputBuffer, modelInputSize, ACL_MEM_MALLOC_NORMAL_ONLY);// 准备模型推理的输入数据结构
input_ = aclmdlCreateDataset();
aclDataBuffer *inputData = aclCreateDataBuffer(modelInputBuffer, modelInputSize);
ret = aclmdlAddDatasetBuffer(input_, inputData);// 准备模型推理的输出数据结构
output_ = aclmdlCreateDataset();
size_t outputSize = aclmdlGetNumOutputs(modelDesc_);
for (size_t i = 0; i < outputSize; ++i) {size_t buffer_size = aclmdlGetOutputSizeByIndex(modelDesc_, i);void *outputBuffer = nullptr;aclError ret = aclrtMalloc(&outputBuffer, buffer_size, ACL_MEM_MALLOC_NORMAL_ONLY);aclDataBuffer* outputData = aclCreateDataBuffer(outputBuffer, buffer_size);ret = aclmdlAddDatasetBuffer(output_, outputData);
}

4.3 执行模型推理

根据实际场景选择同步推理或异步推理。

  • 对于同步推理,直接获取模型推理的输出数据即可。
  • 对于异步推理,在实现Callback功能时,在回调函数内获取模型推理的结果。
string testFile[] = {"../data/dog1_1024_683.bin","../data/dog2_1024_683.bin"};// 3. 模型推理
for (size_t index = 0; index < sizeof(testFile) / sizeof(testFile[0]); ++index) {// 读取图片文件void *inputBuff = nullptr;uint32_t inputBuffSize = 0;auto ret = Utils::ReadBinFile(fileName, inputBuff, inputBuffSize);// 准备模型推理的输入数据if (!g_isDevice) {aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_HOST_TO_DEVICE);(void)aclrtFreeHost(inputBuff);} else {aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_DEVICE_TO_DEVICE);(void)aclrtFree(inputBuff);}// 执行模型推理ret = aclmdlExecute(modelId_, input_, output_);// 输出模型推理的结果,输出top5置信度的类别编号 for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(output_); ++i) {// 获取每个输出的内存地址和内存大小aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, i);void* data = aclGetDataBufferAddr(dataBuffer);size_t len = aclGetDataBufferSizeV2(dataBuffer);// 将内存中的数据转换为float类型float *outData = NULL;outData = reinterpret_cast<float*>(data);// 屏显每张图片的top5置信度的类别编号map<float, int, greater<float> > resultMap;for (int j = 0; j < len / sizeof(float); ++j) {resultMap[*outData] = j;outData++;}int cnt = 0;for (auto it = resultMap.begin(); it != resultMap.end(); ++it) {if (++cnt > 5)break;INFO_LOG("top %d: index[%d] value[%lf]", cnt, it->second, it->first);}
}

4.4 释放内存和数据类型

在模型推理结束后,需依次调用aclDestroyDataBuffer接口、aclmdlDestroyDataset接口及时释放描述模型输入、输出数据类型的数据。

// 4. 释放模型推理的输入、输出资源
for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(input_); ++i) {aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(input_, i);(void)aclDestroyDataBuffer(dataBuffer);
}
(void)aclmdlDestroyDataset(input_);
input_ = nullptr;
aclrtFree(modelInputBuffer);for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(output_); ++i) {aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, i);void* data = aclGetDataBufferAddr(dataBuffer);(void)aclrtFree(data);(void)aclDestroyDataBuffer(dataBuffer);
}
(void)aclmdlDestroyDataset(output_);
output_ = nullptr;

5. 模型卸载

模型推理完成后,您需要通过aclmdlUnload接口来卸载模型。此外,还需要销毁aclmdlDesc类型的模型描述信息,并释放模型运行所需的工作内存和权值内存。

// 1. 卸载模型
aclError ret = aclmdlUnload(modelId_);// 2. 释放模型描述信息
if (modelDesc_ != nullptr) {(void)aclmdlDestroyDesc(modelDesc_);modelDesc_ = nullptr;
}// 3. 释放模型运行的工作内存
if (modelWorkPtr_ != nullptr) {(void)aclrtFree(modelWorkPtr_);modelWorkPtr_ = nullptr;modelWorkSize_ = 0;
}// 4. 释放模型运行的权值内存
if (modelWeightPtr_ != nullptr) {(void)aclrtFree(modelWeightPtr_);modelWeightPtr_ = nullptr;modelWeightSize_ = 0;
}

6. 多种模型推理方式(可选)

  • 多Batch模型推理:LINK

  • 异步模型推理:LINK

  • 队列模型推理:LINK

  • 动态AIPP模型推理

    • 单个动态AIPP输入:LINK
    • 多个动态AIPP输入:LINK
  • 动态Shape输入模型推理

    • 动态Batch/动态分辨率/动态维度:LINK
    • 动态Shape输入:LINK

相关文章:

【2024第一期CANN训练营】4、AscendCL推理应用开发

文章目录 【2024第一期CANN训练营】4、AscendCL推理应用开发1. 创建代码目录2. 构建模型2.1 下载原始模型文件2.2 使用ATC工具转换模型2.3 注意事项 3. 模型加载3.1 示例代码 4. 模型执行4.1 获取模型描述信息4.2 准备输入/输出数据结构4.3 执行模型推理4.4 释放内存和数据类型…...

Rust 构建开源 Pingora 框架可以与nginx媲美

一、概述 Cloudflare 为何弃用 Nginx&#xff0c;选择使用 Rust 重新构建新的代理 Pingora 框架。Cloudflare 成立于2010年&#xff0c;是一家领先的云服务提供商&#xff0c;专注于内容分发网络&#xff08;CDN&#xff09;和分布式域名解析。它提供一系列安全和性能优化服务…...

MediaCodec源码分析 ACodec状态详解

前言 本文分析ACodec状态机,ACodec是MediaCodec的底层实现,在MediaCodec命令下切换不同状态进行编解码,基于7.0代码。 ACodec状态介绍 UninitializedState:未初始化状态。 在业务层调用MediaCodec. createByCodecName 完成后切换到LoadedState。 LoadedState:表示解码器…...

【Elasticsearch】windows安装elasticsearch教程及遇到的坑

一、安装参考 1、安装参考&#xff1a;ES的安装使用(windows版) elasticsearch的下载地址&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch ik分词器的下载地址&#xff1a;https://github.com/medcl/elasticsearch-analysis-ik/releases kibana可视化工具下载…...

如何快速搭建物联网工业云平台

随着物联网技术的快速发展&#xff0c;物联网工业云平台已经成为推动工业领域数字化转型的重要引擎。合沃作为专业的物联网云服务提供商&#xff0c;致力于为企业提供高效、可靠的物联网工业云平台解决方案。本文将深入探讨物联网工业云平台的功能、解决行业痛点的能力以及如何…...

Spring Data访问Elasticsearch----Elasticsearch对象映射

Spring Data访问Elasticsearch----Elasticsearch对象映射 一、元模型(Meta Model)对象映射1.1 映射注解概述1.1.1 控制向Elasticsearch写入和从其读取哪些属性1.1.2 日期格式映射1.1.3 Range类型1.1.4 映射的字段名1.1.5 Non-field-backed属性1.1.6 其他属性注解 1.2 映射规则1…...

Linux之shell循环

华子目录 for循环带列表的for循环格式分析示例shell允许用户指定for语句的步长&#xff0c;格式如下示例 不带列表的for循环示例 基于C语言风格的for循环格式示例注意 while循环格式示例 until循环作用格式示例 循环控制breakcontinue详细语法示例 循环嵌套示例 for循环 for循…...

Python入门教程(一)|基本语法概述

目录 1. 注释 2. 变量和数据类型 3. 控制流 4. 函数 5. 类与对象 6. 异常处理 7. 模块和包 8. 文件操作 1. 注释 在Python中&#xff0c;单行注释以#开始&#xff0c;多行注释使用三个引号 """ 或 。 # 这是单行注释""" 这是 多行 注释…...

Android Studio入门——页面跳转

1.工程目录 2.MainActivity package com.example.demo01;import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCo…...

肝了三天,完成了AIGC工具网站大全,建议收藏再看

说是肝了三天&#xff0c;其实远远不止&#xff0c;前前后后&#xff0c;从资料搜集到最后整理成文&#xff0c;有近一个月了&#xff0c;大家看在整理不易的份上&#xff0c;给点个赞吧&#xff0c;不要光顾着收藏呀&#xff01; 国内网站 AIGC 导航 https://www.aigc.cn 网…...

算法练习:前缀和

目录 1. 一维前缀和2. 二维前缀和3. 寻找数组中心下标4. 除自身以外数组的乘积5. !和为k的子数字6. !和可被k整除的子数组7. !连续数组8. 矩阵区域和 1. 一维前缀和 题目信息&#xff1a; 题目链接&#xff1a; 一维前缀和思路&#xff1a;求前缀和数组&#xff0c;sum dp[r] …...

Kafka MQ 生产者

Kafka MQ 生产者 生产者概览 尽管生产者 API 使用起来很简单&#xff0c;但消息的发送过程还是有点复杂的。图 3-1 展示了向 Kafka 发送消息的主要步骤。 我们从创建一个 ProducerRecord 对象开始&#xff0c;ProducerRecord 对象需要包含目标主题和要发送的内容。我们还可以…...

​​SQLiteC/C++接口详细介绍之sqlite3类(十)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;九&#xff09; 下一篇&#xff1a;​​SQLiteC/C接口详细介绍之sqlite3类&#xff08;十一&#xff09; 30.sqlite3_enable_load_extension&#x…...

Vue中nextTick一文详解

什么是 nextTick&#xff1f; 在 Vue 中&#xff0c;当我们修改数据时&#xff0c;Vue 会自动更新视图。但是&#xff0c;由于 JavaScript 的事件循环机制&#xff0c;我们无法立即得知视图更新完成的时机。这时候&#xff0c;我们就需要使用 nextTick 来获取视图更新完成后的…...

爱奇艺 CTR 场景下的 GPU 推理性能优化

01 背景介绍 GPU 目前大量应用在了爱奇艺深度学习平台上。GPU 拥有成百上千个处理核心&#xff0c;能够并行的执行大量指令&#xff0c;非常适合用来做深度学习相关的计算。在 CV&#xff08;计算机视觉&#xff09;&#xff0c;NLP&#xff08;自然语言处理&#xff09;的模型…...

详解MySql索引

目录 一 、概念 二、使用场景 三、索引使用 四、索引存在问题 五、命中索引问题 六、索引执行原理 一 、概念 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。暂时可以理解成C语言的指针,文章后面详解 二、使用场景 数据量较大&#xff0c;且…...

struct 和 union 的区别?

struct和union的分对应点总结 存储方式&#xff1a; struct&#xff1a;struct中的每个成员都拥有独立的内存空间。一个struct变量的总长度是其所有成员的长度之和&#xff0c;且通常会根据编译器的内存对齐规则进行适当调整。union&#xff1a;union中的所有成员共享同一段内…...

Linux - 安装 Jenkins(详细教程)

目录 前言一、简介二、安装前准备三、下载与安装四、配置镜像地址五、启动与关闭六、常用插件的安装 前言 虽然说网上有很多关于 Jenkins 安装的教程&#xff0c;但是大部分都不够详细&#xff0c;或者是需要搭配 docker 或者 k8s 等进行安装&#xff0c;对于新手小白而已&…...

【JAVA】JAVA方法的学习和创造

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不…...

Rust写一个wasm入门并在rspack和vite项目中使用(一)

rust打包wasm文档 文档地址 安装cargo-generate cargo install cargo-generate 安装过程中有问题的话手动安装cargo-generate下载地址 根据自己的系统下载压缩包&#xff0c;然后解压到用户/.cargo/bind目录下&#xff0c;将解压后的文件放到该目录下即可。 创建wasm项目 …...

3分钟快速上手:用BetterNCM安装器彻底改造你的网易云音乐

3分钟快速上手&#xff1a;用BetterNCM安装器彻底改造你的网易云音乐 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer 还在使用功能单一的网易云音乐吗&#xff1f;想不想让你的播放器拥…...

保姆级教程:在ROS2 Humble/Foxy的Gazebo中配置RGB-D相机(附解决点云颜色/坐标问题)

ROS2 Humble/Foxy中Gazebo深度相机仿真全攻略&#xff1a;从配置到点云问题解决在机器人仿真开发中&#xff0c;深度相机&#xff08;RGB-D&#xff09;是不可或缺的传感器之一。它能够同时提供彩色图像和深度信息&#xff0c;为SLAM、物体识别、避障等任务提供关键数据支持。本…...

Godot中型项目工程化实践:目录规范、资源引用与状态管理

1. 这不是续集&#xff0c;而是项目落地的分水岭“Godot 游戏引擎项目&#xff08;二&#xff09;”——看到这个标题&#xff0c;很多人第一反应是&#xff1a;“哦&#xff0c;上一篇讲了环境搭建和Hello World&#xff0c;这篇该讲节点树和信号了&#xff1f;”但我在带三个…...

用Python+OpenCV手把手实现Prewitt边缘检测(附完整代码与效果对比图)

用PythonOpenCV手把手实现Prewitt边缘检测&#xff08;附完整代码与效果对比图&#xff09; 边缘检测是计算机视觉中最基础也最关键的预处理步骤之一。想象一下&#xff0c;当你需要让计算机"看清"一张照片中的物体轮廓时&#xff0c;边缘检测算法就是它的"视觉…...

从入门到上岗,Java+AI 复合型人才养成攻略

当下编程行业格局正在悄然改变,纯 Java 后端岗位内卷日趋严重,薪资增长逐步放缓;纯粹的 AI 算法岗门槛居高不下,对学历、数理功底要求严苛,普通开发者很难入局。 而Java+AI 复合型开发顺势成为行业刚需岗位,既依托成熟的 Java 体系承接业务开发,又能融入人工智能技术实…...

Windows终极PDF处理工具:3步免费安装Poppler完整指南

Windows终极PDF处理工具&#xff1a;3步免费安装Poppler完整指南 【免费下载链接】poppler-windows Download Poppler binaries packaged for Windows with dependencies 项目地址: https://gitcode.com/gh_mirrors/po/poppler-windows 你是否曾经为在Windows上处理PDF文…...

基于ESP8266与MQTT的家庭水压自动控制系统设计与实现

1. 项目概述与核心需求解析家里水压不稳、供水时断时续&#xff0c;这大概是很多朋友都遇到过的烦心事。我所在的城市供水情况就很不理想&#xff0c;为了解决这个问题&#xff0c;我不得不自己动手&#xff0c;搭建了一套基于ESP8266微控制器的家庭水压增压与储水自动控制系统…...

WarcraftHelper终极指南:深度解析魔兽争霸III现代化兼容性解决方案

WarcraftHelper终极指南&#xff1a;深度解析魔兽争霸III现代化兼容性解决方案 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper WarcraftHelper是一款专…...

TVA注意力层INT8量化配置技巧

重磅预告&#xff1a;本专栏将独家连载系列丛书《智能体视觉技术与应用》部分精华内容&#xff0c;该书是世界首套系统阐述“因式智能体”视觉理论与实践的专著&#xff0c;特邀美国 TypeOne 公司首席科学家、斯坦福大学博士 Bohan 担任技术顾问。Bohan先生师从美国三院院士、“…...

InVideo插件深度解析:如何在Unreal Engine中实现高效视频流播放与录制

InVideo插件深度解析&#xff1a;如何在Unreal Engine中实现高效视频流播放与录制 【免费下载链接】InVideo 基于UE4实现的rtsp的视频播放插件 项目地址: https://gitcode.com/gh_mirrors/in/InVideo InVideo是一个基于Unreal Engine 5开发的RTSP视频播放插件&#xff0…...