HarmonyOS:Neural Network Runtime 对接 AI 推理框架开发指导

场景介绍

Neural Network Runtime 作为 AI 推理引擎和加速芯片的桥梁,为 AI 推理引擎提供精简的 Native 接口,满足推理引擎通过加速芯片执行端到端推理的需求。

本文以图 1 展示的 Add 单算子模型为例,介绍 Neural Network Runtime 的开发流程。Add 算子包含两个输入、一个参数和一个输出,其中的 activation 参数用于指定 Add 算子中激活函数的类型。

图 1 Add 单算子网络示意图

环境准备

环境要求

Neural Network Runtime 部件的环境要求如下:

● 开发环境:Ubuntu 18.04 及以上。

● 接入设备:HarmonyOS 定义的标准设备,并且系统中内置的硬件加速器驱动,已通过 HDI 接口对接 Neural Network Runtime。

由于 Neural Network Runtime 通过 Native API 对外开放,需要通过 Native 开发套件编译 Neural Network Runtime 应用。

环境搭建

1.  打开 Ubuntu 编译服务器的终端。

2.  把下载好的 Native 开发套件压缩包拷贝至当前用户根目录下。

3.  执行以下命令解压 Native 开发套件的压缩包。

unzip native-linux-{版本号}.zip

解压缩后的内容如下(随版本迭代,目录下的内容可能发生变化,请以最新版本的 Native API 为准):

native/├── build // 交叉编译工具链├── build-tools // 编译构建工具├── docs├── llvm├── nativeapi_syscap_config.json├── ndk_system_capability.json├── NOTICE.txt├── oh-uni-package.json└── sysroot // Native API头文件和库

接口说明

这里给出 Neural Network Runtime 开发流程中通用的接口,具体请见下列表格。

结构体

模型构造相关接口

模型编译相关接口

执行推理相关接口

设备管理相关接口

开发步骤

Neural Network Runtime 的开发流程主要包含模型构造、模型编译和推理执行三个阶段。以下开发步骤以 Add 单算子模型为例,介绍调用 Neural Network Runtime 接口,开发应用的过程。

1.  创建应用样例文件。

首先,创建 Neural Network Runtime 应用样例的源文件。在项目目录下执行以下命令,创建 nnrt_example/目录,在目录下创建 nnrt_example.cpp 源文件。

mkdir ~/nnrt_example && cd ~/nnrt_exampletouch nnrt_example.cpp

2.  导入 Neural Network Runtime。

在 nnrt_example.cpp 文件的开头添加以下代码,引入 Neural Network Runtime 模块。

#include <cstdint>#include <iostream>#include <vector>
#include "neural_network_runtime/neural_network_runtime.h"
// 常量,用于指定输入、输出数据的字节长度const size_t DATA_LENGTH = 4 * 12;

3.  构造模型。

使用 Neural Network Runtime 接口,构造 Add 单算子样例模型。

OH_NN_ReturnCode BuildModel(OH_NNModel** pModel)
{// 创建模型实例,进行模型构造OH_NNModel* model = OH_NNModel_Construct();if (model == nullptr) {std::cout << "Create model failed." << std::endl;return OH_NN_MEMORY_ERROR;}// 添加Add算子的第一个输入Tensor,类型为float32,张量形状为[1, 2, 2, 3]int32_t inputDims[4] = {1, 2, 2, 3};OH_NN_Tensor input1 = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR};OH_NN_ReturnCode ret = OH_NNModel_AddTensor(model, &input1);if (ret != OH_NN_SUCCESS) {std::cout << "BuildModel failed, add Tensor of first input failed." << std::endl;return ret;}// 添加Add算子的第二个输入Tensor,类型为float32,张量形状为[1, 2, 2, 3]OH_NN_Tensor input2 = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR};ret = OH_NNModel_AddTensor(model, &input2);if (ret != OH_NN_SUCCESS) {std::cout << "BuildModel failed, add Tensor of second input failed." << std::endl;return ret;}// 添加Add算子的参数Tensor,该参数Tensor用于指定激活函数的类型,Tensor的数据类型为int8。int32_t activationDims = 1;int8_t activationValue = OH_NN_FUSED_NONE;OH_NN_Tensor activation = {OH_NN_INT8, 1, &activationDims, nullptr, OH_NN_ADD_ACTIVATIONTYPE};ret = OH_NNModel_AddTensor(model, &activation);if (ret != OH_NN_SUCCESS) {std::cout << "BuildModel failed, add Tensor of activation failed." << std::endl;return ret;}// 将激活函数类型设置为OH_NN_FUSED_NONE,表示该算子不添加激活函数。ret = OH_NNModel_SetTensorData(model, 2, &activationValue, sizeof(int8_t));if (ret != OH_NN_SUCCESS) {std::cout << "BuildModel failed, set value of activation failed." << std::endl;return ret;}// 设置Add算子的输出,类型为float32,张量形状为[1, 2, 2, 3]OH_NN_Tensor output = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR};ret = OH_NNModel_AddTensor(model, &output);if (ret != OH_NN_SUCCESS) {std::cout << "BuildModel failed, add Tensor of output failed." << std::endl;return ret;}// 指定Add算子的输入、参数和输出索引uint32_t inputIndicesValues[2] = {0, 1};uint32_t paramIndicesValues = 2;uint32_t outputIndicesValues = 3;OH_NN_UInt32Array paramIndices = {&paramIndicesValues, 1};OH_NN_UInt32Array inputIndices = {inputIndicesValues, 2};OH_NN_UInt32Array outputIndices = {&outputIndicesValues, 1};// 向模型实例添加Add算子ret = OH_NNModel_AddOperation(model, OH_NN_OPS_ADD, &paramIndices, &inputIndices, &outputIndices);if (ret != OH_NN_SUCCESS) {std::cout << "BuildModel failed, add operation failed." << std::endl;return ret;}// 设置模型实例的输入、输出索引ret = OH_NNModel_SpecifyInputsAndOutputs(model, &inputIndices, &outputIndices);if (ret != OH_NN_SUCCESS) {std::cout << "BuildModel failed, specify inputs and outputs failed." << std::endl;return ret;}// 完成模型实例的构建ret = OH_NNModel_Finish(model);if (ret != OH_NN_SUCCESS) {std::cout << "BuildModel failed, error happened when finishing model construction." << std::endl;return ret;}*pModel = model;return OH_NN_SUCCESS;
}

4.  查询 Neural Network Runtime 已经对接的加速芯片。

Neural Network Runtime 支持通过 HDI 接口,对接多种加速芯片。在执行模型编译前,需要查询当前设备下,Neural Network Runtime 已经对接的加速芯片。每个加速芯片对应唯一的 ID 值,在编译阶段需要通过设备 ID,指定模型编译的芯片。

void GetAvailableDevices(std::vector<size_t>& availableDevice){    availableDevice.clear();
    // 获取可用的硬件ID    const size_t* devices = nullptr;    uint32_t deviceCount = 0;    OH_NN_ReturnCode ret = OH_NNDevice_GetAllDevicesID(&devices, &deviceCount);    if (ret != OH_NN_SUCCESS) {        std::cout << "GetAllDevicesID failed, get no available device." << std::endl;        return;    }
    for (uint32_t i = 0; i < deviceCount; i++) {        availableDevice.emplace_back(devices[i]);    }}

5.  在指定的设备上编译模型。

Neural Network Runtime 使用抽象的模型表达描述 AI 模型的拓扑结构,在加速芯片上执行前,需要通过 Neural Network Runtime 提供的编译模块,将抽象的模型表达下发至芯片驱动层,转换成可以直接推理计算的格式。

OH_NN_ReturnCode CreateCompilation(OH_NNModel* model, const std::vector<size_t>& availableDevice, OH_NNCompilation** pCompilation)
{// 创建编译实例,用于将模型传递至底层硬件编译OH_NNCompilation* compilation = OH_NNCompilation_Construct(model);if (compilation == nullptr) {std::cout << "CreateCompilation failed, error happened when creating compilation." << std::endl;return OH_NN_MEMORY_ERROR;}// 设置编译的硬件、缓存路径、性能模式、计算优先级、是否开启float16低精度计算等选项// 选择在第一个设备上编译模型OH_NN_ReturnCode ret = OH_NNCompilation_SetDevice(compilation, availableDevice[0]);if (ret != OH_NN_SUCCESS) {std::cout << "CreateCompilation failed, error happened when setting device." << std::endl;return ret;}// 将模型编译结果缓存在/data/local/tmp目录下,版本号指定为1ret = OH_NNCompilation_SetCache(compilation, "/data/local/tmp", 1);if (ret != OH_NN_SUCCESS) {std::cout << "CreateCompilation failed, error happened when setting cache path." << std::endl;return ret;}// 完成编译设置,进行模型编译ret = OH_NNCompilation_Build(compilation);if (ret != OH_NN_SUCCESS) {std::cout << "CreateCompilation failed, error happened when building compilation." << std::endl;return ret;}*pCompilation = compilation;return OH_NN_SUCCESS;
}

6.  创建执行器。

完成模型编译后,需要调用 Neural Network Runtime 的执行模块,创建推理执行器。执行阶段,设置模型输入、获取模型输出和触发推理计算的操作均围绕执行器完成。

OH_NNExecutor* CreateExecutor(OH_NNCompilation* compilation){    // 创建执行实例    OH_NNExecutor* executor = OH_NNExecutor_Construct(compilation);    return executor;}

7.  执行推理计算,并打印计算结果。

通过执行模块提供的接口,将推理计算所需要的输入数据传递给执行器,触发执行器完成一次推理计算,获取模型的推理计算结果。

OH_NN_ReturnCode Run(OH_NNExecutor* executor)
{// 构造示例数据float input1[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};float input2[12] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22};int32_t inputDims[4] = {1, 2, 2, 3};OH_NN_Tensor inputTensor1 = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR};OH_NN_Tensor inputTensor2 = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR};// 设置执行的输入// 设置执行的第一个输入,输入数据由input1指定OH_NN_ReturnCode ret = OH_NNExecutor_SetInput(executor, 0, &inputTensor1, input1, DATA_LENGTH);if (ret != OH_NN_SUCCESS) {std::cout << "Run failed, error happened when setting first input." << std::endl;return ret;}// 设置执行的第二个输入,输入数据由input2指定ret = OH_NNExecutor_SetInput(executor, 1, &inputTensor2, input2, DATA_LENGTH);if (ret != OH_NN_SUCCESS) {std::cout << "Run failed, error happened when setting second input." << std::endl;return ret;}// 设置输出的数据缓冲区,OH_NNExecutor_Run执行计算后,输出结果将保留在output中float output[12];ret = OH_NNExecutor_SetOutput(executor, 0, output, DATA_LENGTH);if (ret != OH_NN_SUCCESS) {std::cout << "Run failed, error happened when setting output buffer." << std::endl;return ret;}// 执行计算ret = OH_NNExecutor_Run(executor);if (ret != OH_NN_SUCCESS) {std::cout << "Run failed, error doing execution." << std::endl;return ret;}// 打印输出结果for (uint32_t i = 0; i < 12; i++) {std::cout << "Output index: " << i << ", value is: " << output[i] << "." << std::endl;}return OH_NN_SUCCESS;
}

8.  构建端到端模型构造-编译-执行流程。

步骤 3-步骤 7 实现了模型的模型构造、编译和执行流程,并封装成 4 个函数,便于模块化开发。以下示例代码将 4 个函数串联成完整的 Neural Network Runtime 开发流程。

int main(){    OH_NNModel* model = nullptr;    OH_NNCompilation* compilation = nullptr;    OH_NNExecutor* executor = nullptr;    std::vector<size_t> availableDevices;
    // 模型构造阶段    OH_NN_ReturnCode ret = BuildModel(&model);    if (ret != OH_NN_SUCCESS) {        std::cout << "BuildModel failed." << std::endl;        OH_NNModel_Destroy(&model);        return -1;    }
    // 获取可执行的设备    GetAvailableDevices(availableDevices);    if (availableDevices.empty()) {        std::cout << "No available device." << std::endl;        OH_NNModel_Destroy(&model);        return -1;    }
    // 模型编译阶段    ret = CreateCompilation(model, availableDevices, &compilation);    if (ret != OH_NN_SUCCESS) {        std::cout << "CreateCompilation failed." << std::endl;        OH_NNModel_Destroy(&model);        OH_NNCompilation_Destroy(&compilation);        return -1;    }
    // 创建模型的推理执行器    executor = CreateExecutor(compilation);    if (executor == nullptr) {        std::cout << "CreateExecutor failed, no executor is created." << std::endl;        OH_NNModel_Destroy(&model);        OH_NNCompilation_Destroy(&compilation);        return -1;    }
    // 使用上一步创建的执行器,执行单步推理计算    ret = Run(executor);    if (ret != OH_NN_SUCCESS) {        std::cout << "Run failed." << std::endl;        OH_NNModel_Destroy(&model);        OH_NNCompilation_Destroy(&compilation);        OH_NNExecutor_Destroy(&executor);        return -1;    }
    // 释放申请的资源    OH_NNModel_Destroy(&model);    OH_NNCompilation_Destroy(&compilation);    OH_NNExecutor_Destroy(&executor);
    return 0;}

调测验证

1.  准备应用样例的编译配置文件。

新建一个 CMakeLists.txt 文件,为开发步骤中的应用样例文件 nnrt_example.cpp 添加编译配置。以下提供简单的 CMakeLists.txt 示例:

cmake_minimum_required(VERSION 3.16)project(nnrt_example C CXX)
add_executable(nnrt_example    ./nnrt_example.cpp)
target_link_libraries(nnrt_example    neural_network_runtime.z)

2.  编译应用样例。

执行以下命令,在当前目录下新建 build/目录,在 build/目录下编译 nnrt_example.cpp,得到二进制文件 nnrt_example。

mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE={交叉编译工具链的路径}/build/cmake/ohos.toolchain.cmake -DOHOS_ARCH=arm64-v8a -DOHOS_PLATFORM=OHOS -DOHOS_STL=c++_static ..
make

3.  执行以下代码,将样例推送到设备上执行。

# 将编译得到的 `nnrt_example` 推送到设备上,执行样例。hdc_std file send ./nnrt_example /data/local/tmp/.
# 给测试用例可执行文件加上权限。hdc_std shell "chmod +x /data/local/tmp/nnrt_example"
# 执行测试用例hdc_std shell "/data/local/tmp/nnrt_example"

如果样例执行正常,应该得到以下输出。

Output index: 0, value is: 11.000000.Output index: 1, value is: 13.000000.Output index: 2, value is: 15.000000.Output index: 3, value is: 17.000000.Output index: 4, value is: 19.000000.Output index: 5, value is: 21.000000.Output index: 6, value is: 23.000000.Output index: 7, value is: 25.000000.Output index: 8, value is: 27.000000.Output index: 9, value is: 29.000000.Output index: 10, value is: 31.000000.Output index: 11, value is: 33.000000.

4.  检查模型缓存(可选)。

如果在调测环境下,Neural Network Runtime 对接的 HDI 服务支持模型缓存功能,执行完 nnrt_example, 可以在 /data/local/tmp 目录下找到生成的缓存文件。

说明

模型的 IR 需要传递到硬件驱动层,由 HDI 服务将统一的 IR 图,编译成硬件专用的计算图,编译的过程非常耗时。Neural Network Runtime 支持计算图缓存的特性,可以将 HDI 服务编译生成的计算图,缓存到设备存储中。当下一次在同一个加速芯片上编译同一个模型时,通过指定缓存的路径,Neural Network Runtime 可以直接加载缓存文件中的计算图,减少编译消耗的时间。

检查缓存目录下的缓存文件:

ls /data/local/tmp

以下为打印结果:

# 0.nncache  cache_info.nncache

如果缓存不再使用,需要手动删除缓存,可以参考以下命令,删除缓存文件。

rm /data/local/tmp/*nncache

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

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

相关文章

精通服务器远程管理:全面指南

引言 在当今数字化世界中&#xff0c;IT专业人员和管理员能够远程管理服务器的能力是无价之宝。远程服务器管理不仅提高了效率&#xff0c;而且在无法物理访问服务器的情况下确保了持续的运营。本指南将深入探讨远程管理的不同类型、远程桌面的使用方法&#xff0c;以及如何安全…

一、W5100S/W5500+RP2040之MicroPython开发<静态网络示例>

文章目录 1. 前言2. MicroPython介绍2.1 简介2.2 优点2.3 应用 3. WIZnet以太网芯片4. 静态IP网络设置示例讲解以及使用4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 烧录验证步骤1&#xff1a;将固件部署到设备步骤2&#xff1a;运行network_install.py程序步骤3&a…

【大数据存储与处理】实验一 HBase 的基本操作

一、实验目的&#xff1a; 1. 掌握 Hbase 创建数据库表及删除数据库表 2. 掌握 Hbase 对数据库表数据的增、删、改、查。 二、实验内容&#xff1a; 1、题目 0&#xff1a;进入 hbase shell 2、题目 1&#xff1a;Hbase 创建数据库表 创建数据库表的命令&#xff1a;create 表…

重塑数字生产力体系,生成式AI将开启云计算未来新十年?

科技云报道原创。 今天我们正身处一个历史的洪流&#xff0c;一个巨变的十字路口。生成式AI让人工智能技术完全破圈&#xff0c;带来了机器学习被大规模采用的历史转折点。 它掀起的新一轮科技革命&#xff0c;远超出我们今天的想象&#xff0c;这意味着一个巨大的历史机遇正…

【扩散模型】8、DALL-E2 | 借助 CLIP 的图文对齐能力来实现文本到图像的生成

文章目录 一、背景二、方法2.1 Decoder2.2 Prior 三、图像控制3.1 Variations3.2 Interpolations3.3 Text Diffs 四、探索 CLIP 的潜在空间五、文本到图像的生成5.1 先验的重要性5.2 人类评价5.3 多样性和保真性的平衡5.3 在 COCO 上对比 论文&#xff1a;DALLE.2 代码&#x…

JVM-12-即时编译器

Java程序最初都是通过解释器&#xff08;Interpreter&#xff09;进行解释执行的&#xff0c;当虚拟机发现某个方法或代码块的运行特别频繁&#xff0c;就会把这些代码认定为“热点代码”&#xff08;Hot Spot Code&#xff09;&#xff0c;为了提高热点代码的执行效率&#xf…

案例 | 数据中台如何支撑6000+门店降本提效?

对于企业来说&#xff0c;上中台不是目的&#xff0c;借助数据中台让企业建立数据驱动意识&#xff0c;并结合数据中台持续做好各项业务运营&#xff0c;才是根本。 那么对于零售行业来说&#xff0c;该如何利用数据中台为业务赋能&#xff1f;惟客数据以某头部连锁零售企业为…

leetcode 974. 和可被 K 整除的子数组(优质解法)

代码&#xff1a; class Solution {public int subarraysDivByK(int[] nums, int k) {HashMap<Integer,Integer> hashMapnew HashMap();hashMap.put(0,1);int count0; //记录子数组的个数int last0; //前一个下标的前缀和int now0; //当前下标的前缀和for(int i0;…

打开任务管理器的13种方法,总有一款适合你

任务管理器是一个很好的工具,可以帮助你管理应用程序、进程和服务在Windows PC上的运行方式。在使用任务管理器之前,你应该首先知道如何打开它。在本指南中,我们将向你展示运行它的不同方式,无论你使用的是Windows 11还是Windows 10。该列表包括启动任务管理器的十三种方法…

使用Python爬取GooglePlay并从复杂的自定义数据结构中实现解析

文章目录 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向感兴趣的朋友可以关注《爬虫JS逆向实战》&#xff0c;对分布…

计算机组成原理综合3

41、计算机操作的最小时间单位是__________。A A. 时钟周期 B. 指令周期 C. CPU周期 D. 外围设备 42、微程序控制器中&#xff0c;机器指令与微指令的关系是__________。B A. 每一条机器指令由一条微指令来执行 B. 每一条机器指令由一段用微指令编成…

在做题中学习(35):判断字符是否唯一

面试题 01.01. 判定字符是否唯一 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;1.用哈希表&#xff08;创建另一个数组存储&#xff09;然后和原数组一一比对。 时间复杂度O&#xff08;N&#xff09; 空间复杂度 O&#xff08;N&#xff09; 2.位图&#xff08…

使用物理机的burpsuite抓取虚拟机的请求包(虚拟机代理配置)

关于burpsuite抓取本地浏览器的请求包大家应该都会配置吧 我也是第一次配抓取虚拟机的包&#xff0c;最开始遇到了些问题&#xff0c;这里简单给大家分享一下 下面以Windows系统下的Firefox浏览器为例&#xff1a; 首先我还是先添加了一个小狐狸&#xff08;foxyproxy&#…

ubuntu保存分辨率失效解决办法

在VM虚拟机中&#xff0c;遇到修改ubuntu分辨率后&#xff0c;重启后又重置的解决办法。 目前我的ubuntu版本是&#xff1a;ubuntu 18.04.6 版本。 1.首先&#xff0c;在你喜欢的目录建立一个.sh 脚本文件。 终端执行命令&#xff1a;sudo vim xrandr.sh 2.按 i 进入编辑状…

神经网络:优化器和全连接层

SGD&#xff08;随机梯度下降&#xff09; 随机梯度下降的优化算法在科研和工业界是很常用的。 很多理论和工程问题都能转化成对目标函数进行最小化的数学问题。 举个例子&#xff1a;梯度下降&#xff08;Gradient Descent&#xff09;就好比一个人想从高山上奔跑到山谷最低…

【51单片机系列】C51中的中断系统扩展实验

本文是关于51单片机中断系统的扩展实验。 文章目录 一、 扩展实验一&#xff1a;使用外部中断0控制蜂鸣器&#xff0c;外部中断1控制直流电机二、扩展实验二&#xff1a;修改定时器初值&#xff0c;设定3秒钟的定时时间让LED模块闪烁三、扩展实验三&#xff1a;使用定时器1和数…

华为OD机试 - 区间交集 - 深度优先搜索dfs算法(滥用)(Java 2023 B卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述备注用例1、输入2、输出3、说明 四、解题思路1、核心思路&#xff1a;2、具体步骤 五、Java算法源码再重新读一遍题目&#xff0c;看看能否优化一下~解题步骤也简化了很多。 六、效果展示1、输入2、输出3、说明 华为OD机试 2…

Screen记录窗口输出日志

screen是Linux窗口管理器&#xff0c;用户可以建立多个screen会话&#xff0c;每个screen会话又可以建立多个window窗口&#xff0c;每一个窗口就像一个可操作的真实的ssh终端一样。 screen详解&#xff1a;http://www.linuxidc.com/Linux/2013-10/91612.htm Linux Screen超简…

C++_动态二维数组的两种方法

介绍 本文主要介绍使用 动态二维数组的两种方法 (PS:仅作创建 动态二维数组参考,详细使用方法根据需求自行改变) 第一种&#xff1a;连续存储结构的 二维动态数组(需固定 列 大小&#xff0c;可通过下标访问) 缺点: 1.需要在设计二维数组前写死 列 的大小 2.空间利用率不高 优点…

PostgreSQL使用docker部署,设置密码失效问题处理

文章目录 问题描述使用场景排查解决修改已有的文件卷使用SQL初始化 问题描述 PostgreSQL使用docker虚拟化部署&#xff0c;使用docker-compose管理&#xff0c;配置了密码部署在客户现场时&#xff0c;客户的安全扫描&#xff0c;反馈测到PostgreSQL数据库弱密码漏洞查看docke…