深入解析ncnn::Net类——高效部署神经网络的核心组件

news2025/3/7 4:59:35

在这里插入图片描述

最近在学习ncnn推理框架,下面整理了ncnn::Net 的使用方法。

在移动端和嵌入式设备上进行高效的神经网络推理,要求框架具备轻量化、高性能以及灵活的扩展能力。作为腾讯开源的高性能神经网络推理框架,ncnn在这些方面表现出色。而在ncnn的核心组件中,ncnn::Net类扮演了至关重要的角色。本文将详细介绍ncnn::Net类的结构、功能及其使用方法,帮助开发者更好地理解和利用这一强大的工具。

目录

  1. Net类简介
  2. 构造与析构
  3. Option选项配置
  4. 自定义层注册
  5. 加载网络参数与模型
  6. 输入与输出管理
  7. 创建Extractor
  8. Vulkan支持
  9. 内存管理与优化
  10. 实例代码
  11. 使用示例
  12. 总结

1. Net类简介

ncnn::Net类是ncnn框架中用于管理和执行神经网络的主要接口。它负责加载网络结构和权重、配置推理选项、管理层与Blob等关键资源,并提供了与外部数据交互的接口。通过Net类,开发者可以方便地加载预训练模型、进行前向推理并获取结果,适用于多种应用场景,如图像分类、目标检测和语义分割等。

namespace ncnn {

class NCNN_EXPORT Net
{
public:
    // 构造函数
    Net();
    
    // 析构函数
    virtual ~Net();

public:
    // 配置选项
    Option opt;

    // Vulkan相关设置
    #if NCNN_VULKAN
        void set_vulkan_device(int device_index);
        void set_vulkan_device(const VulkanDevice* vkdev);
        const VulkanDevice* vulkan_device() const;
    #endif // NCNN_VULKAN

    // 自定义层注册
    int register_custom_layer(const char* type, layer_creator_func creator, layer_destroyer_func destroyer = 0, void* userdata = 0);
    virtual int custom_layer_to_index(const char* type);
    int register_custom_layer(int index, layer_creator_func creator, layer_destroyer_func destroyer = 0, void* userdata = 0);
    
    // 加载参数与模型
    int load_param_bin(const DataReader& dr);
    int load_model(const DataReader& dr);
    
    #if NCNN_STDIO
        #if NCNN_STRING
            int load_param(FILE* fp);
            int load_param(const char* protopath);
            int load_param_mem(const char* mem);
        #endif // NCNN_STRING
        int load_param_bin(FILE* fp);
        int load_param_bin(const char* protopath);
        int load_model(FILE* fp);
        int load_model(const char* modelpath);
    #endif // NCNN_STDIO

    // 清除网络
    void clear();

    // 创建提取器
    Extractor create_extractor() const;

    // 获取输入输出信息
    const std::vector<int>& input_indexes() const;
    const std::vector<int>& output_indexes() const;
    #if NCNN_STRING
        const std::vector<const char*>& input_names() const;
        const std::vector<const char*>& output_names() const;
    #endif
    const std::vector<Blob>& blobs() const;
    const std::vector<Layer*>& layers() const;
    std::vector<Blob>& mutable_blobs();
    std::vector<Layer*>& mutable_layers();

protected:
    // 自定义层创建
    virtual Layer* create_custom_layer(int index);
    virtual Layer* create_overwrite_builtin_layer(int typeindex);

private:
    Net(const Net&);
    Net& operator=(const Net&);

private:
    NetPrivate* const d;
};

} // namespace ncnn

2. 构造与析构

Net类提供了默认构造函数和析构函数,用于初始化和销毁网络对象。在构造函数中,网络的内部资源会被初始化,而析构函数则负责释放所有分配的资源,确保没有内存泄漏。

ncnn::Net net; // 创建Net对象
// 使用net进行操作

// Net对象在作用域结束时自动析构

3. Option选项配置

Net类中的Option成员用于配置网络的各种参数,如线程数、内存分配器、是否启用Vulkan计算等。这些选项可以在加载网络结构和权重之前进行设置,以优化推理性能和资源使用。

Option类概述

Option类定义在option.h文件中,包含了多个可配置选项,如下所示:

namespace ncnn {

class Allocator;
#if NCNN_VULKAN
class VkAllocator;
class PipelineCache;
#endif // NCNN_VULKAN

class NCNN_EXPORT Option
{
public:
    Option();

public:
    bool lightmode;
    int num_threads;
    Allocator* blob_allocator;
    Allocator* workspace_allocator;

#if NCNN_VULKAN
    VkAllocator* blob_vkallocator;
    VkAllocator* workspace_vkallocator;
    VkAllocator* staging_vkallocator;
    PipelineCache* pipeline_cache;
#endif // NCNN_VULKAN

    int openmp_blocktime;
    bool use_winograd_convolution;
    bool use_sgemm_convolution;
    bool use_int8_inference;
    bool use_vulkan_compute;
    bool use_bf16_storage;

    bool use_fp16_packed;
    bool use_fp16_storage;
    bool use_fp16_arithmetic;
    bool use_int8_packed;
    bool use_int8_storage;
    bool use_int8_arithmetic;

    bool use_packing_layout;
    bool use_shader_pack8;

    bool use_subgroup_basic;
    bool use_subgroup_vote;
    bool use_subgroup_ballot;
    bool use_subgroup_shuffle;

    bool use_image_storage;
    bool use_tensor_storage;

    bool use_reserved_0;

    int flush_denormals;

    bool use_local_pool_allocator;
    bool use_shader_local_memory;
    bool use_cooperative_matrix;

    bool use_winograd23_convolution;
    bool use_winograd43_convolution;
    bool use_winograd63_convolution;

    bool use_a53_a55_optimized_kernel;

    bool use_reserved_7;
    bool use_reserved_8;
    bool use_reserved_9;
    bool use_reserved_10;
    bool use_reserved_11;
};

} // namespace ncnn

配置选项示例

ncnn::Net net;
ncnn::Option opt;

// 启用轻量模式
opt.lightmode = true;

// 设置线程数为4
opt.num_threads = 4;

// 启用Vulkan计算
opt.use_vulkan_compute = true;

// 使用自定义内存分配器
CustomAllocator alloc;
opt.blob_allocator = &alloc;
opt.workspace_allocator = &alloc;

// 将配置应用到网络
net.opt = opt;

4. 自定义层注册

ncnn框架允许开发者注册自定义层,以扩展框架的功能或实现特定的操作。register_custom_layer函数用于注册自定义层,支持通过层类型名称或索引进行注册。

通过层类型名称注册

#if NCNN_STRING
    int register_custom_layer(const char* type, layer_creator_func creator, layer_destroyer_func destroyer = 0, void* userdata = 0);
    virtual int custom_layer_to_index(const char* type);
#endif // NCNN_STRING

通过层索引注册

int register_custom_layer(int index, layer_creator_func creator, layer_destroyer_func destroyer = 0, void* userdata = 0);

使用示例

假设我们有一个自定义层MyCustomLayer,其创建和销毁函数分别为create_my_custom_layerdestroy_my_custom_layer

// 定义创建函数
Layer* create_my_custom_layer();
void destroy_my_custom_layer(Layer* layer);

ncnn::Net net;

// 通过层类型名称注册
net.register_custom_layer("MyCustomLayer", create_my_custom_layer, destroy_my_custom_layer);

// 或者通过层索引注册(假设MyCustomLayer的索引为100)
net.register_custom_layer(100, create_my_custom_layer, destroy_my_custom_layer);

5. 加载网络参数与模型

Net类提供了多种加载网络参数和模型文件的方法,支持从文件系统和内存中加载网络结构(参数)和权重(模型)。以下将详细介绍这些方法。

加载参数

参数文件定义了网络的结构,包括各层的类型、序列及其连接关系。参数文件可以是文本格式(param)或二进制格式(param.bin)。

// 加载二进制参数文件
int ret = net.load_param_bin("model.param.bin");
if (ret != 0) {
    // 处理错误
}

// 加载内存中的参数数据
unsigned char* param_mem = ...; // 参数数据内存指针
int consumed = net.load_param(param_mem);

加载模型

模型文件包含了网络的权重数据。模型文件通常为二进制格式(model.bin),也可以从内存中加载。

// 加载二进制模型文件
int ret = net.load_model("model.bin");
if (ret != 0) {
    // 处理错误
}

// 加载内存中的模型数据
unsigned char* model_mem = ...; // 模型数据内存指针
int consumed = net.load_model(model_mem);

从文件加载参数与模型

#if NCNN_STDIO
    #if NCNN_STRING
        // 从文件路径加载参数
        int ret = net.load_param("model.param");
    #endif // NCNN_STRING
    // 从文件路径加载二进制参数
    int ret = net.load_param_bin("model.param.bin");

    // 从文件路径加载模型
    ret = net.load_model("model.bin");
#endif // NCNN_STDIO

从Android Asset加载(仅适用于Android平台)

#if NCNN_PLATFORM_API && __ANDROID_API__ >= 9
    #if NCNN_STRING
        // 从Android Asset加载参数文件
        net.load_param(asset);
        net.load_param(asset_mgr, "path/to/model.param");

        // 从Android Asset加载二进制参数文件
        net.load_param_bin(asset);
        net.load_param_bin(asset_mgr, "path/to/model.param.bin");
    #endif // NCNN_STRING
    // 从Android Asset加载模型文件
    net.load_model(asset);
    net.load_model(asset_mgr, "path/to/model.bin");
#endif // NCNN_PLATFORM_API

6. 输入与输出管理

ncnn::Net类通过Blob索引和名称管理输入和输出。开发者可以通过索引或名称设置输入数据,并提取推理结果。

获取输入输出索引与名称

const std::vector<int>& input_indexes = net.input_indexes();
const std::vector<int>& output_indexes = net.output_indexes();

#if NCNN_STRING
    const std::vector<const char*>& input_names = net.input_names();
    const std::vector<const char*>& output_names = net.output_names();
#endif

通过索引设置输入和提取输出

ncnn::Extractor ex = net.create_extractor();

// 设置输入
ex.input(0, input_mat); // 通过Blob索引设置

// 提取输出
ncnn::Mat output_mat;
ex.extract(0, output_mat); // 通过Blob索引提取

通过名称设置输入和提取输出(需要开启NCNN_STRING)

#if NCNN_STRING
    ncnn::Extractor ex = net.create_extractor();

    // 设置输入
    ex.input("input_blob_name", input_mat); // 通过Blob名称设置

    // 提取输出
    ncnn::Mat output_mat;
    ex.extract("output_blob_name", output_mat); // 通过Blob名称提取
#endif

7. 创建Extractor

Extractor类是用于执行前向推理的实例,通过Net类创建。Extractor支持多线程、内存分配器设置以及Vulkan加速等配置。

创建Extractor

ncnn::Extractor ex = net.create_extractor();

配置Extractor选项

ex.set_light_mode(true); // 启用轻量模式,回收中间Blob
ex.set_num_threads(4); // 设置Extractor的线程数

// 设置自定义内存分配器
CustomAllocator alloc;
ex.set_blob_allocator(&alloc);
ex.set_workspace_allocator(&alloc);

8. Vulkan支持

ncnn支持基于Vulkan的GPU加速,通过VkAllocator和相关配置,可以在支持Vulkan的设备上利用GPU进行高效推理。

设置Vulkan设备

#if NCNN_VULKAN
    // 通过设备索引设置Vulkan设备
    net.set_vulkan_device(0); // 使用第一个Vulkan设备

    // 或者通过VulkanDevice句柄设置
    const VulkanDevice* vkdev = ...;
    net.set_vulkan_device(vkdev);
#endif // NCNN_VULKAN

设置Extractor的Vulkan选项

#if NCNN_VULKAN
    ncnn::Extractor ex = net.create_extractor();
    ex.set_vulkan_compute(true); // 启用Vulkan计算

    // 设置Vulkan内存分配器
    VkAllocator vkalloc;
    ex.set_blob_vkallocator(&vkalloc);
    ex.set_workspace_vkallocator(&vkalloc);
    ex.set_staging_vkallocator(&vkalloc);
#endif // NCNN_VULKAN

9. 内存管理与优化

ncnn通过引用计数、自定义内存分配器和内存池复用等机制,实现高效的内存管理。同时,OptionExtractor类提供了多种配置选项,帮助开发者根据需求优化内存使用和推理性能。

自定义内存分配器

开发者可以实现自定义的Allocator,以满足特定的内存管理需求或优化策略。

class CustomAllocator : public ncnn::Allocator 
{
public:
    virtual void* fastMalloc(size_t size) override {
        // 自定义内存分配逻辑
        return malloc(size);
    }

    virtual void fastFree(void* ptr) override {
        // 自定义内存释放逻辑
        free(ptr);
    }
};

CustomAllocator alloc;
ncnn::Option opt;
opt.blob_allocator = &alloc;
opt.workspace_allocator = &alloc;
net.opt = opt;

内存池复用与对齐

通过内存池复用和自动内存对齐,ncnn提高了内存利用率和数据访问效率,减少了内存分配和释放的开销。

class PoolAllocator : public ncnn::Allocator 
{
    std::vector<std::pair<size_t, void*>> freed_memory;
public:
    virtual void* fastMalloc(size_t size) override {
        for (auto it = freed_memory.begin(); it != freed_memory.end(); ++it) {
            if (it->first >= size) {
                void* ptr = it->second;
                freed_memory.erase(it);
                return ptr;
            }
        }
        return malloc(size);
    }

    virtual void fastFree(void* ptr) override {
        size_t size = /* 获取ptr对应的大小 */;
        freed_memory.emplace_back(size, ptr);
    }
};

PoolAllocator pool_alloc;
ncnn::Option opt;
opt.blob_allocator = &pool_alloc;
opt.workspace_allocator = &pool_alloc;
net.opt = opt;

10. 实例代码

以下是一个完整的示例,展示如何使用ncnn::Net类加载模型、设置Option、创建Extractor并进行推理。

#include "net.h"
#include "mat.h"
#include <stdio.h>

int main() {
    // 创建Net对象
    ncnn::Net net;

    // 配置选项
    ncnn::Option opt;
    opt.lightmode = true;
    opt.num_threads = 4;
    opt.use_winograd_convolution = true;
    opt.use_sgemm_convolution = true;
    opt.use_int8_inference = true;
    opt.use_packing_layout = true;
    net.opt = opt;

    // 加载参数和模型
    if (net.load_param("model.param") != 0) {
        printf("Failed to load param file\n");
        return -1;
    }
    if (net.load_model("model.bin") != 0) {
        printf("Failed to load model file\n");
        return -1;
    }

    // 创建Extractor
    ncnn::Extractor ex = net.create_extractor();
    ex.set_num_threads(4);
    ex.set_light_mode(true);

    // 准备输入数据
    ncnn::Mat in = ncnn::Mat::from_pixels_resize(image_data, ncnn::Mat::PIXEL_RGB, width, height, 224, 224);
    in.substract_mean_normalize(mean_vals, norm_vals);

    // 设置输入
    ex.input("input_blob", in);

    // 提取输出
    ncnn::Mat out;
    ex.extract("output_blob", out);

    // 处理输出
    float* ptr = out;
    for (int i = 0; i < out.w; i++) {
        printf("Class %d: %f\n", i, ptr[i]);
    }

    return 0;
}

11. 使用示例

下面是一个更具体的示例,展示如何加载一个预训练的图像分类模型,并对一张图片进行推理。

#include "net.h"
#include "mat.h"
#include <stdio.h>

// 假设有一个函数读取图片并返回RGB数据
unsigned char* load_image(const char* image_path, int& width, int& height);

int main() {
    // 创建Net对象
    ncnn::Net net;

    // 配置选项
    ncnn::Option opt;
    opt.lightmode = true;
    opt.num_threads = 4;
    opt.use_int8_inference = true;
    net.opt = opt;

    // 加载网络结构和模型
    if (net.load_param("mobilenet_v1.param") != 0) {
        printf("加载参数文件失败\n");
        return -1;
    }
    if (net.load_model("mobilenet_v1.bin") != 0) {
        printf("加载模型文件失败\n");
        return -1;
    }

    // 创建Extractor
    ncnn::Extractor ex = net.create_extractor();

    // 加载并预处理图片
    int width, height;
    unsigned char* image_data = load_image("test.jpg", width, height);
    if (!image_data) {
        printf("无法加载图片\n");
        return -1;
    }

    ncnn::Mat in = ncnn::Mat::from_pixels_resize(image_data, ncnn::Mat::PIXEL_RGB, width, height, 224, 224);
    in.substract_mean_normalize(
        ncnn::Mat(3, 1, (float[]) {104.f, 117.f, 123.f}), // 均值
        ncnn::Mat(3, 1, (float[]) {1.f, 1.f, 1.f})      // 归一化
    );

    // 设置输入
    ex.input("data", in);

    // 提取输出
    ncnn::Mat out;
    ex.extract("prob", out);

    // 找到概率最高的分类
    float max_score = out[0];
    int max_index = 0;
    for (int i = 1; i < out.w; i++) {
        if (out[i] > max_score) {
            max_score = out[i];
            max_index = i;
        }
    }

    printf("预测结果: 类别索引 = %d, 置信度 = %f\n", max_index, max_score);

    // 释放图片数据
    free(image_data);

    return 0;
}

12. 总结

ncnn::Net类作为ncnn框架的核心组件,具备以下显著特点:

  1. 灵活的选项配置:通过Option类,开发者可以细粒度地配置推理参数,如线程数、内存分配策略、优化选项等。
  2. 高效的内存管理:支持自定义内存分配器、内存池复用和自动对齐,确保内存的高效利用和访问效率。
  3. 扩展性强:允许开发者注册自定义层,扩展框架功能,以适应特定的应用需求。
  4. 支持多种数据加载方式:支持从文件系统和内存中加载网络结构和权重,适用于不同的应用场景。
  5. Vulkan加速支持:通过Vulkan实现GPU加速,提升推理性能,特别适用于移动设备和嵌入式系统。
  6. 简便的输入输出管理:通过Blob索引和名称管理输入和输出,提供多种设置和提取接口,便于集成和使用。
  7. 易于使用的接口:提供简洁的API接口,结合丰富的示例代码,降低了神经网络部署的门槛。

通过本文的详细介绍,相信您已经对ncnn::Net类有了全面的了解。无论是在资源受限的移动设备上进行高效推理,还是在嵌入式系统中实现复杂的神经网络应用,ncnn::Net类都能为您提供强大的支持和高效的性能表现。


希望这篇博客能帮助您更好地理解和使用ncnn::Net类,在您的深度学习项目中发挥更大的作用。如果您有任何问题或建议,欢迎在评论区留言讨论!

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

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

相关文章

前端【8】HTML+CSS+javascript实战项目----实现一个简单的待办事项列表 (To-Do List)

目录 一、功能需求 二、 HTML 三、CSS 四、js 1、绑定事件与初始设置 2.、绑定事项 &#xff08;1&#xff09;添加操作&#xff1a; &#xff08;2&#xff09;完成操作 &#xff08;3&#xff09;删除操作 &#xff08;4&#xff09;修改操作 3、完整js代码 总结…

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<1>

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 这一节我们来学习指针的相关知识&#xff0c;学习内存和地址&#xff0c;指针变量和地址&#xff0c;包…

積分方程與簡單的泛函分析6.有連續對稱核的弗雷德霍姆積分算子的特徵值

1)def弗雷德霍姆算子的核函數定義 定义: 设 是定义在矩形区域 上的函数。 若满足以下条件,则称 为弗雷德霍姆算子的核函数: 实值性: 是实值函数,即对于任意的 ,。 这是因为在许多实际的物理和数学问题中,所涉及的量往往是实数值,例如在积分方程描述的物理模型中,…

AI编程工具使用技巧:在Visual Studio Code中高效利用阿里云通义灵码

AI编程工具使用技巧&#xff1a;在Visual Studio Code中高效利用阿里云通义灵码 前言一、通义灵码介绍1.1 通义灵码简介1.2 主要功能1.3 版本选择1.4 支持环境 二、Visual Studio Code介绍1.1 VS Code简介1.2 主要特点 三、安装VsCode3.1下载VsCode3.2.安装VsCode3.3 打开VsCod…

kettle与Springboot的集成方法,完整支持大数据组件

目录 概要整体架构流程技术名词解释技术细节小结 概要 在现代数据处理和ETL&#xff08;提取、转换、加载&#xff09;流程中&#xff0c;Kettle&#xff08;Pentaho Data Integration, PDI&#xff09;作为一种强大的开源ETL工具&#xff0c;被广泛应用于各种数据处理场景。…

GitHub Actions 使用需谨慎:深度剖析其痛点与替代方案

在持续集成与持续部署&#xff08;CI/CD&#xff09;领域&#xff0c;GitHub Actions 曾是众多开发者的热门选择&#xff0c;但如今&#xff0c;其弊端逐渐显现&#xff0c;让不少人在使用前不得不深思熟虑。 团队由大约 15 名工程师组成&#xff0c;采用基于主干的开发方式&am…

<iframe>标签和定时调用函数setInterval

iframe 标签和定时调用函数 setInterval 问题描述&#xff1a;解决方法&#xff1a; 问题描述&#xff1a; 今天遇到一个前端问题&#xff0c;在浏览器页面上传Excel文件后&#xff0c;然后点击导入按钮&#xff0c;经后端Java类读取文件内容校验后&#xff0c;将校验结果返回…

Qt——界面优化

一.QSS 1.背景 在网页前端开发领域中&#xff0c; CSS 是⼀个至关重要的部分。 描述了⼀个网页的 "样式"。 从而起到对网页美化的作用。 所谓样式&#xff0c;包括不限于大小&#xff0c;位置&#xff0c;颜色&#xff0c;背景&#xff0c;间距&#xff0c;字体等等…

java基础学习——jdbc基础知识详细介绍

引言 数据的存储 我们在开发 java 程序时&#xff0c;数据都是存储在内存中的&#xff0c;属于临时存储&#xff0c;当程序停止或重启时&#xff0c;内存中的数据就会丢失&#xff0c;我们为了解决数据的长期存储问题&#xff0c;有以下解决方案&#xff1a; 通过 IO流书记&…

CukeTest使用 | 1 CukeTest是什么?如何下载安装?

CukeTest使用 | 1 CukeTest是什么&#xff1f;如何下载安装&#xff1f; 1 CukeTest是什么&#xff1f;2 关于开发者3 CukeTest有哪些特性&#xff1f;4 都支持哪些自动化技术类型&#xff1f;5 版本区别6 下载安装 特殊说明&#xff1a;学习内容主要来自官网的教程、以及网上公…

An OpenGL Toolbox

3.An OpenGL Toolbox 声明&#xff1a;该代码来自&#xff1a;Computer Graphics Through OpenGL From Theory to Experiments&#xff0c;仅用作学习参考 3.1 Vertex Arrays and Their Drawing Commands 顶点数组及其绘制命令&#xff1a;将几何数据存储在一个位置&#xff0c…

R语言学习笔记之高效数据操作

一、概要 数据操作是R语言的一大优势&#xff0c;用户可以利用基本包或者拓展包在R语言中进行复杂的数据操作&#xff0c;包括排序、更新、分组汇总等。R数据操作包&#xff1a;data.table和tidyfst两个扩展包。 data.table是当前R中处理数据最快的工具&#xff0c;可以实现快…

Linux的权限和一些shell原理

目录 shell的原理 Linux权限 sudo命令提权 权限 文件的属性 ⽂件类型&#xff1a; 基本权限&#xff1a; chmod改权限 umask chown 该拥有者 chgrp 改所属组 最后&#xff1a; 目录权限 粘滞位 shell的原理 我们广义上的Linux系统 Linux内核Linux外壳 Linux严格…

LearnOpenGL——光照

教程地址&#xff1a;简介 - LearnOpenGL CN 前言 这篇开始光照的学习。 颜色 原文链接&#xff1a; 颜色 - LearnOpenGL CN总结&#xff1a; 重新搭建了一个简单场景&#xff0c;为后面的学习做准备。 现实世界中有无数种颜色&#xff0c;每一个物体都有它们自己的颜色。我…

步入响应式编程篇(二)之Reactor API

步入响应式编程篇&#xff08;二&#xff09;之Reactor API 前言回顾响应式编程Reactor API的使用Stream引入依赖Reactor API的使用流源头的创建 reactor api的背压模式发布者与订阅者使用的线程查看弹珠图查看形成新流的日志 前言 对于响应式编程的基于概念&#xff0c;以及J…

利用Redis实现数据缓存

目录 1 为啥要缓存捏&#xff1f; 2 基本流程&#xff08;以查询商铺信息为例&#xff09; 3 实现数据库与缓存双写一致 3.1 内存淘汰 3.2 超时剔除&#xff08;半自动&#xff09; 3.3 主动更新&#xff08;手动&#xff09; 3.3.1 双写方案 3.3.2 读写穿透方案 3.3.…

【动态规划】--- 斐波那契数模型

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Journey &#x1f3e0; 第N个泰波那契数模型 &#x1f4cc; 题目解析 第N个泰波那契数 题目要求的是泰波那契数&#xff0c;并非斐波那契数。 &…

php-phar打包避坑指南2025

有很多php脚本工具都是打包成phar形式&#xff0c;使用起来就很方便&#xff0c;那么如何自己做一个呢&#xff1f;也找了很多文档&#xff0c;也遇到很多坑&#xff0c;这里就来总结一下 phar安装 现在直接装yum php-cli包就有phar文件&#xff0c;很方便 可通过phar help查看…

java提取系统应用的日志中的sql获取表之间的关系

为了获取到对应的sql数据&#xff0c;分了三步骤 第一步&#xff0c;获取日志文件&#xff0c;解析日志文件中的查询sql&#xff0c;递归解析sql&#xff0c;获取表关系集合 递归解析sql&#xff0c;获取表与表之间的关系 输出得到的对应关联关系数据 第二步&#xff0c;根据获…

PyQt6医疗多模态大语言模型(MLLM)实用系统框架构建初探(下.代码部分)

医疗 MLLM 框架编程实现 本医疗 MLLM 框架结合 Python 与 PyQt6 构建,旨在实现多模态医疗数据融合分析并提供可视化界面。下面从数据预处理、模型构建与训练、可视化界面开发、模型 - 界面通信与部署这几个关键部分详细介绍编程实现。 6.1 数据预处理 在医疗 MLLM 框架中,多…