七. 部署YOLOv8检测器-load-save-tensor

news2024/11/15 17:39:41

目录

    • 前言
    • 0. 简述
    • 1. 案例运行
    • 2. 补充说明
    • 3. 代码分析
      • 3.1 main.cpp
      • 3.2 create_data.py
    • 结语
    • 下载链接
    • 参考

前言

自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》,链接。记录下个人学习笔记,仅供自己参考

本次课程我们来学习课程第六章—部署分类器,一起来学习利用 cnpy 库加载和保存 tensor

课程大纲可以看下面的思维导图

在这里插入图片描述

0. 简述

本小节目标:学习 cnpy 库的使用

这节课程开始我们进入第七章节—部署 YOLOv8 检测器,这个章节偏实战,为大家准备了几个案例:

  • 7.1-load-save-tensor
  • 7.2-affine-transformation
  • 7.3-deploy-yolo-basic
  • 7.4-quantization-analysis
  • 7.5-deploy-yolo-multitask

第七章节主要是教大家部署基于 YOLOv8 的检测和分割模型,整个代码其实是基于我们前面第六章节创建的推理框架,在此基础上实现不同的 task

第七章节准备的案例一共是五个,第一个是 load-save-tensor 案例,加载和保存 tensor,实现一个 tensor 在 c++ 和 python 之间的相互转换;7.2 小节主要给大家介绍仿射变换,这个本来是要在第二章节给大家介绍的,但是没有一些案例提供,像 YOLOv8 这种检测器的预处理就非常适合仿射变换,因此我们结合着具体的案例来讲可能更加方便大家的理解,同时我们理解了第二章节的双线性插值再回过头来看仿射变换会容易很多

我们先学习仿射变换的目的是为了后面的小节,像 classification 这种在预处理时只需要 resize 就行,然后推理拿到结果,没有必要将结果映射回原始的 size 大小,但是像 detection 或者 segmentation 这种我们一般是将图像仿射变换到 640x640 大小,接着推理拿到检测框的结果,但此时的结果是属于 640x640 尺寸的,我们还需要将其映射回原始的 size 大小,这个就是仿射变换逆矩阵做的事情

我们理解了 7.1 和 7.2 小节后就正式开始进入 7.3 小节 YOLO 的部署,它是根据第六章节中 classification deploy 的一个扩充,在 7.3 小节我们会感受到当一个推理框架搭建完成后,如果有新的 task 需要添加时,其实代码的修改其实是非常少的

针对 7.3 中 YOLO 量化后掉点严重的问题,我们在 7.4 小节中会详细分析在量化掉点严重的情况下我们应该怎么做,我们应该按照一个什么样的思路一步步去排查错误,这都是我们在 7.4 小节中会提到的

最后 7.5 小节我们来学习 multitask 的部署,以 YOLOv8-Seg 为例去分析多任务头的模型我们该如何去实现部署,如何和 pytorch 模型的精度对齐

本次课程我们主要学习 7.1 小节加载和保存 tensor

下面我们开始本次课程的学习🤗

1. 案例运行

在正式开始课程之前,博主先带大家跑通 7.1-load-save-tensor 这个小节的案例🤗

源代码获取地址:https://github.com/kalfazed/tensorrt_starter

首先大家需要把 tensorrt_starter 这个项目给 clone 下来,指令如下:

git clone https://github.com/kalfazed/tensorrt_starter.git

也可手动点击下载,点击右上角的 Code 按键,将代码下载下来。至此整个项目就已经准备好了。也可以点击 here 下载博主准备好的源代码(注意代码下载于 2024/7/14 日,若有改动请参考最新

整个项目后续需要使用的软件主要有 CUDA、cuDNN、TensorRT、OpenCV,大家可以参考 Ubuntu20.04软件安装大全 进行相应软件的安装,博主这里不再赘述

假设你的项目、环境准备完成,下面我们一起来运行下 7.1-load-save-tensor 小节案例代码

本小节案例需要大家安装一个 cnpy 库,安装比较简单,下面博主跟着大家一起安装下

首先我们需要把 cnpy 库给 clone 下来,终端执行如下指令:

git clone https://github.com/rogersce/cnpy.git

也可手动点击下载,点击右上角的 Code 按键,将代码下载下来。如下图所示,至此整个项目就已经准备好了。也可以点击 here 下载博主准备好的源代码

在这里插入图片描述

接着我们只要跟着官方提供的 README 文档一步步安装就行,指令如下:

cd cnpy
mkdir build && cd build
cmake ../
make -j64
make install

输出如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

如果大家看到上述输出结果则说明 cnpy 库安装完成

Note:默认安装在系统目录,如果想安装在指定目录则在 cmake ../ 时添加编译选项,如下所示:

cmake ../ -DCMAKE_INSTALL_PREFIX=/home/jarvis/lean/cnpy

这里博主安装的位置是自己指定的目录,还有一个点需要注意如果是像博主一样将 cnpy 库安装到指定目录则后续需要在 Makefile 中手动添加下 cnpy 的头文件和库文件路径,否则编译时会提示找不到 cnpy,这点博主后续会提到

安装完 cnpy 库之后我们还需要修改下整体的 Makefile.config,指定一些库的路径:

# tensorrt_starter/config/Makefile.config
# CUDA_VER                    :=  11
CUDA_VER                    :=  11.6
    
# opencv和TensorRT的安装目录
OPENCV_INSTALL_DIR          :=  /usr/local/include/opencv4
# TENSORRT_INSTALL_DIR        :=  /mnt/packages/TensorRT-8.4.1.5
TENSORRT_INSTALL_DIR        :=  /home/jarvis/lean/TensorRT-8.6.1.6

Note:大家查看自己的 CUDA 是多少版本,修改为对应版本即可,另外 OpenCV 和 TensorRT 修改为你自己安装的路径即可

如果 cnpy 库安装的是指定目录则还需要手动指定下头文件和库文件路径,如下图所示:

在这里插入图片描述

接着我们就可以来执行编译,指令如下:

make -j64

输出如下:

在这里插入图片描述

接着执行:

./bin/trt-infer

大家可能会遇到如下问题:

在这里插入图片描述

这个主要是程序在运行时找不到对应的库,这里博主通过 export 指令手动添加的库路径,指令如下:

export LD_LIBRARY_PATH=/home/jarvis/lean/cnpy/lib:$LD_LIBRARY_PATH

大家也可以将其添加到 .bashrc 文件中

更新环境变量后再次执行下,输出如下:

在这里插入图片描述

我们再看下对应的 python 结果,如下所示:

在这里插入图片描述

这里我们通过 c++ 的 cnpy 库保存了一个 .npz 文件并通过 python 打印出来了,可以看到数据都是一样的,说明没有问题。当然大家也可以 python 保存然后在 c++ 上读取,这边博主就不再演示了

如果大家能够看到上述输出结果,那就说明本小节案例已经跑通,下面我们就来看看具体的代码实现

2. 补充说明

在分析代码之前我们先来看下韩君老师在这小节中写的 README 文档

作为第七章的开头,这里给大家分享一个 c++ 库叫做 cnpy,虽然是一个比较古老的库了,但是依然很好使用,大家可以访问它的 repository 来看它具体是如何使用的:https://github.com/rogersce/cnpy

它可以用来将 python 中的 numpy 数据打包成 npy 或者 npz 格式,之后从 c++ 中读取,或者将 c++ 中的 array 或者 vector 打包成 npy 或者 npz 格式,让 python 读取

主要应用场景在于将 c++ 做的一些后处理和 pytorch 做的后处理的结果进行比较,比如说如果原本的 yolov8 模型中 pytorch 的 bbox 的位置信息和 confidence 和我们在 c++ 中实现的结果有很大的不同的话,我们需要考虑是在哪一步的处理有出入,是预处理还是后处理,我们通过对比同一个步骤的 c++ 和 pytorch 实现,提供完全相同的两个 tensor 并查看它们的结果是否存在偏差。作为一种 debug 的方式,cnpy 还是比较方便的

现在比较常见的具有类似的功能同时知名度也比较高的有 Aten 或者 Autograd,它们也可以实现类似的供。通过这两个 lib 我们可以在 c++ 中做类似于 pytorch 的实现,但是 cnpy 由于只提供 tensor 的 save/load,也是我们在 debug 时仅仅需要的功能,所以相比下来会比 Atentorchlib 要轻量很多,方便使用

下面是本小节的两个案例:

1. c++ 读取 pytorch 数据

make run

[info]Succeeded loading data from .npy/.npz!
[info]Tensor values:
[[[[0.16708092 0.12141544 0.97547305 0.43885458 ]
   [0.86767215 0.19956835 0.65644139 0.26155758 ]
   [0.38162416 0.99415749 0.72049564 0.80983108 ]
   [0.80337048 0.04441109 0.07116123 0.19084969 ]]

  [[0.32216629 0.06359515 0.57189071 0.51922035 ]
   [0.28190836 0.73800033 0.54624307 0.08063673 ]
   [0.08042904 0.45007208 0.33879673 0.76960266 ]
   [0.48134825 0.92414659 0.21818621 0.38389835 ]]

  [[0.49801108 0.25858060 0.02503417 0.96238512 ]
   [0.45412001 0.43555310 0.96726465 0.56104338 ]
   [0.42672521 0.35845470 0.02181065 0.86646718 ]
   [0.95074230 0.02647719 0.41995925 0.04657971 ]]]]

2. pytorch 读取 c++ 数据

python src/python/create.py

[INFO]: Succeeded loaded data as .npz file!
[INFO]: Tensor shape:(3, 2, 4, 4)
[INFO]: Tensor values:
[[[[ 0.84018773  0.39438292  0.78309923  0.79844004]
   [ 0.91164738  0.19755137  0.33522275  0.76822960]
   [ 0.27777472  0.55396998  0.47739705  0.62887090]
   [ 0.36478448  0.51340091  0.95222974  0.91619509]]

  [[ 0.63571173  0.71729696  0.14160256  0.60696888]
   [ 0.01630057  0.24288677  0.13723157  0.80417675]
   [ 0.15667909  0.40094438  0.12979044  0.10880880]
   [ 0.99892449  0.21825691  0.51293242  0.83911222]]]


 [[[ 0.61263984  0.29603162  0.63755226  0.52428716]
   [ 0.49358299  0.97277504  0.29251680  0.77135772]
   [ 0.52674496  0.76991385  0.40022862  0.89152944]
   [ 0.28331473  0.35245836  0.80772454  0.91902649]]

  [[ 0.06975528  0.94932705  0.52599537  0.08605585]
   [ 0.19221385  0.66322690  0.89023262  0.34889293]
   [ 0.06417132  0.02002305  0.45770174  0.06309584]
   [ 0.23827995  0.97063410  0.90220809  0.85091978]]]


 [[[ 0.26666576  0.53976035  0.37520698  0.76024872]
   [ 0.51253539  0.66772377  0.53160644  0.03928034]
   [ 0.43763760  0.93183506  0.93080980  0.72095233]
   [ 0.28429341  0.73853433  0.63997883  0.35404867]]

  [[ 0.68786138  0.16597417  0.44010451  0.88007522]
   [ 0.82920110  0.33033714  0.22896817  0.89337242]
   [ 0.35036018  0.68666989  0.95646822  0.58864015]
   [ 0.65730405  0.85867631  0.43955991  0.92396981]]]]

3. 代码分析

3.1 main.cpp

我们先从 main.cpp 看起:

#include "trt_logger.hpp"
#include "utils.hpp"

using namespace std;

int main(int argc, char const *argv[])
{
    /*
        从c++读取一个python下保存的npz file
    */
    // cnpy::npz_t npz_data = cnpy::npz_load("data/data_python.npz");
    // cnpy::NpyArray arr   = npz_data["data_python"];
    // for (int i = 0; i < arr.shape.size(); i ++){
    //     LOG("arr.shape[%d]: %d", i, arr.shape[i]);
    // }
    // LOG("Succeeded loading data from .npy/.npz!");
    // LOG("Tensor values:");
    // printTensorNPY(arr);


    /*
        在c++下保存一个python可以识别的npy/npz file
    */
    const int b = 3;
    const int c = 2;
    const int h = 4;
    const int w = 4;
    int size = b * c * h * w;

    float* data = (float*)malloc(size * sizeof(float));
    initTensor(data, size, 0, 1, 0);

    cnpy::npz_save("data/data_cpp.npz", "data_cpp", &data[0], {b, c, h, w}, "w");
    cnpy::npy_save("data/data_cpp.npy", &data[0], {b, c, h, w}, "w");

    LOG("Succeeded saving data as .npy/.npz!");
    LOG("Tensor values:");
    printTensorCXX(data, b, c, h, w);

    return 0;
}

上述代码展示了如何在 C++ 中使用 cnpy 库来加载和保存 .npy.npz 文件

/*
    从c++读取一个python下保存的npz file
*/
// cnpy::npz_t npz_data = cnpy::npz_load("data/data_python.npz");
// cnpy::NpyArray arr   = npz_data["data_python"];
// for (int i = 0; i < arr.shape.size(); i ++){
//     LOG("arr.shape[%d]: %d", i, arr.shape[i]);
// }
// LOG("Succeeded loading data from .npy/.npz!");
// LOG("Tensor values:");
// printTensorNPY(arr);

这段注释的代码演示了如何从 python 中 numpy 保存的 .npz 文件中加载数据

  • 首先通过 cnpy:npz_load 加载 .npz 文件,该文件是 python 脚本保存的。npz_load 返回一个 npz_t 类型的对象(通常是一个键值对的字典),其中包含了 .npz 文件中所有数组的名字和数据
  • npz_data["data_python"] 通过键名 data_pythonnpz_data 中获取对应的数组
  • 接着循环遍历数组的维度并打印每个维度的大小
  • 最后通过 printTensorNPY 函数打印加载的数组内容

printTensorNPY 内容如下:

string printTensor(float* tensor, int size){
    int n = 0;
    char buff[100];
    string result;
    n += snprintf(buff + n, sizeof(buff) - n, "[ ");
    for (int i = 0; i < size; i++){
        n += snprintf(buff + n, sizeof(buff) - n, "%8.4lf", tensor[i]);
        if (i != size - 1){
            n += snprintf(buff + n, sizeof(buff) - n, ", ");
        }
    }
    n += snprintf(buff + n, sizeof(buff) - n, " ]");
    result = buff;
    return result;
}

void printTensorNPY(cnpy::NpyArray arr){
    float* data = arr.data<float>();
    int size = arr.num_bytes() / sizeof(float);

    int chw_size = arr.shape[1] * arr.shape[2] * arr.shape[3];
    int hw_size  = arr.shape[2] * arr.shape[3];
    int w_size   = arr.shape[3];

    printTensor(data, size, chw_size, hw_size, w_size);
}

printTensorNPY 函数首先从 cnpy::NpyArray 中提取数据,并计算一些用于表示维度的信息,然后调用 printTensor 将数据转换成字符串格式,便于输出和调试

/*
    在c++下保存一个python可以识别的npy/npz file
*/
const int b = 3;
const int c = 2;
const int h = 4;
const int w = 4;
int size = b * c * h * w;

float* data = (float*)malloc(size * sizeof(float));
initTensor(data, size, 0, 1, 0);

cnpy::npz_save("data/data_cpp.npz", "data_cpp", &data[0], {b, c, h, w}, "w");
cnpy::npy_save("data/data_cpp.npy", &data[0], {b, c, h, w}, "w");

LOG("Succeeded saving data as .npy/.npz!");
LOG("Tensor values:");
printTensorCXX(data, b, c, h, w);

这段代码则展示了如何在 C++ 中创建一个数组并将其保存为 .npy.npz 文件:

  • 首先定义了一个四维数组,其形状为 [3, 2, 4, 4]
  • 接着使用 malloc 为数组分配内存,然后调用 initTensor 函数初始化数组的内容
  • cpny::npz_savecnpy::npy_save 分别用于保存数组为 .npz.npy 文件。.npz 是一个压缩格式,可以包含多个数组,而 .npy 是一个单数组的二进制格式
  • 最后调用 printTensorCXX 函数打印数组内容

initTensor 函数内容如下:

void initTensor(float* data, int size, int min, int max, int seed) {
    srand(seed);
    for (int i = 0; i < size; i ++) {
        data[i] = float(rand()) * float(max - min) / RAND_MAX;
    }
}

该函数通过使用 rand() 函数生成随机浮点数,填充给定的数组 data

printTensorCXX 内容如下:

void printTensor(float* data, int size, int chw_size, int hw_size, int w_size){
    printf("[");
    for (int i = 0; i < size; i += chw_size) {
        (i == 0) ? printf("[") : printf(" [");
        for (int j = 0; j < chw_size; j += hw_size) {
            (j == 0) ? printf("[") : printf("  [");
            for (int k = 0; k < hw_size; k += w_size) {
                (k == 0) ? printf("[") : printf("   [");
                for (int p = 0; p < w_size; p ++) {
                        printf("%.8lf", data[i + j + k + p]);

                    if (k != w_size - 1) printf(" ");
                }
                (k == hw_size - w_size) ? printf("]") : printf("]\n");
            }
            (j == chw_size - hw_size) ? printf("]") : printf("]\n\n");
        }
        (i == size - chw_size) ? printf("]") : printf("]\n\n\n");
    }
    printf("]\n");
}

void printTensorCXX(float* data, int b, int c, int h, int w){
    int size     = b * c * h * w;
    int chw_size = c * h * w;
    int hw_size  = h * w;
    int w_size   = w;
    printTensor(data, size, chw_size, hw_size, w_size);
}

printTensorCXX 函数依旧是调用 printTensor 将数据转换成字符串格式

3.2 create_data.py

我们再对应看下 python 脚本中是如何保存和加载 tensor 数据的,代码如下:

import os
import numpy as np
from logger import init_logger

logger = init_logger()

def createData(data_path, data_label, data_shape):
    dataDict = {}
    dataDict[data_label] = np.random.rand(*data_shape).astype(np.float32)
    np.savez(data_path, **dataDict)
    logger.info("Succeeded saving data as .npz file!")
    logger.info("Tensor shape:{}".format(dataDict[data_label].shape))
    logger.info("Tensor values:")
    print(dataDict[data_label])
    return

def loadData(data_path, data_label):
    dataDict = np.load(data_path)
    logger.info("Succeeded loaded data as .npz file!")
    logger.info("Tensor shape:{}".format(dataDict[data_label].shape))
    logger.info("Tensor values:")
    print(dataDict[data_label])
    return

if __name__ == "__main__":
    np.set_printoptions(formatter={'float': '{: .8f}'.format})
    current_path = os.path.dirname(__file__)
    data_path    = current_path + "/../../data/data_python.npz"
    data_label   = "data_python"
    data_shape   = (2, 3, 4, 4)

    # createData(data_path, data_label, data_shape)

    data_path    = current_path + "/../../data/data_cpp.npz"
    data_label   = "data_cpp"
    loadData(data_path, data_label)

该脚本主要用于创建和加载 .npz 文件中的张量数据,并输出数据的形状和内容

def createData(data_path, data_label, data_shape):
    dataDict = {}
    dataDict[data_label] = np.random.rand(*data_shape).astype(np.float32)
    np.savez(data_path, **dataDict)
    logger.info("Succeeded saving data as .npz file!")
    logger.info("Tensor shape:{}".format(dataDict[data_label].shape))
    logger.info("Tensor values:")
    print(dataDict[data_label])
    return

我们先看下 createData 函数,看它是如何保存一个 .npz 数据的:

  • dataDict = {}:创建一个空字典,用于存储生成的数据
  • dataDict[data_label] = np.random.rand(*data_shape).astype(np.float32):使用 numpy 生成指定形状的随机张量数据,数值范围为 [0,1),并将其转换为 float32 类型。生成的张量以 data_label 作为键存储在 dataDict 字典中
  • np.savez(data_path, **dataDict):使用 numpysavez 函数将数据字典保存为 .npz 文件
  • 最后日志记录和打印
def loadData(data_path, data_label):
    dataDict = np.load(data_path)
    logger.info("Succeeded loaded data as .npz file!")
    logger.info("Tensor shape:{}".format(dataDict[data_label].shape))
    logger.info("Tensor values:")
    print(dataDict[data_label])
    return

再来看下如何加载一个 .npz 数据:

  • dataDict = np.load(data_path):直接使用 numpyload 函数加载 .npz 文件,并返回一个包含文件中所有数组的字典
  • 最后日志记录和打印
if __name__ == "__main__":
    np.set_printoptions(formatter={'float': '{: .8f}'.format})
    current_path = os.path.dirname(__file__)
    data_path    = current_path + "/../../data/data_python.npz"
    data_label   = "data_python"
    data_shape   = (2, 3, 4, 4)

    # createData(data_path, data_label, data_shape)

    data_path    = current_path + "/../../data/data_cpp.npz"
    data_label   = "data_cpp"
    loadData(data_path, data_label)

在主函数部分,通过 createData 函数生成数据并保存,然后使用 loadData 函数读取数据

OK,以上就是使用 cnpy 库的一些案例分析,相对来说还是比较简单的

结语

本次课程我们学习了 cnpy 库,它可以将 c++ 数据保存成 .npz 的格式并在 python 中加载读取,这个其实在我们 debug 时非常有帮助,我们在模型部署时经常会遇到 c++ 和 python 精度不对齐的情况,我们可以利用这个库将 c++ 的结果和 python 结果进行对比并进一步排查问题

OK,以上就是 7.1 小节案例的全部内容了,下节我们来学习 7.2 小节的 affine-transformation 仿射变换,敬请期待😄

下载链接

  • tensorrt_starter源码
  • 7.1-load-save-tensor

参考

  • Ubuntu20.04软件安装大全
  • https://github.com/kalfazed/tensorrt_starter.git
  • https://github.com/rogersce/cnpy

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

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

相关文章

GMeLLo:结合知识图谱的 LLM 多跳问答技术,效果显著提升

1. GMeLLo 提出的背景 1.1 多跳问答 多跳问答的难度往往比较大&#xff0c;因为不仅要追溯事实&#xff0c;还要聚合与串联事实。 事实的来源可以是知识图谱、表格、自由格式文本&#xff0c;或者是这些来源的异构组合。 随着大型语言模型的发展&#xff0c;基于提示的方法…

安科瑞ACR10R网络电力仪表 CE认证 带外置互感器

产品概述&#xff1a; ‌‌安科瑞ACR10R网络电力仪表是一种集成了多种电力参数测量、电能计量、电能监测和考核管理等多种功能于一体的电力仪表。它适用于冶炼、钢铁、电焊、半导体等高能耗行业的节能改造工程&#xff0c;同时也适用于分布式光伏并网柜的功率监测、电力需求侧…

sqli-labs靶场通关攻略 56-60

主页有sqli-labs靶场通关攻略 1-55 第五六关 less-56 步骤一&#xff1a;闭合方式&#xff1a;?id1)-- 步骤二&#xff1a;查询数据库 ?id-1) union select 1,2,database() -- 步骤三&#xff1a;联合查询 查出网站的数据库表名 ?id-1) union select 1,2,group_concat(t…

探索AntSKPro AI知识库一体机:离线智能的便捷之选

在数字化时代&#xff0c;信息的获取和处理速度是企业和个人效率的关键。然而&#xff0c;网络连接的不稳定性常常成为阻碍。AntSKPro AI知识库一体机&#xff0c;一款专为离线环境设计的智能设备&#xff0c;以其卓越的性能和用户友好的设计&#xff0c;正在重新定义离线AI解决…

双指针--优选算法

个人主页&#xff1a;敲上瘾-CSDN博客 个人专栏&#xff1a;游戏、数据结构、c语言基础、c学习、OJ题 前言&#xff1a; 该篇文章我们主要来学习的是双指针算法&#xff0c;对于该类算法我们可以直接来做题&#xff0c;从题中去感知该算法的魅力&#xff0c;最后再从题中做总…

【数据结构】—— 线性表

目录 前言一、顺序表1.1 顺序表的定义及其特点1.2 顺序表的C语言实现1.2.1 定义顺序表1.2.2 初始化1.2.3 插入1.2.4 删除1.2.5 查找 二、链表2.1 链表的定义2.2 单向链表的实现2.2.1 定义单向链表2.2.2 创建链表2.2.3 插入元素2.2.4 删除元素2.2.5 查找 2.3 双向循环链表 前言 …

选刊风向标!985大学近十年发文热门IEEE期刊盘点

本期盘点同济大学近十年有关IEEE旗下发文较多的期刊&#xff0c;一起来看看哪些是双一流大学热门发文期刊&#xff1a; 1、IEEE Transactions on Cybernetics • 影响因子&#xff1a;9.4 • JCR1区&#xff0c;中科院1区-Top • 检索数据库&#xff1a;SCIE • 期刊分区&a…

stable diffusion的安装

stable diffusion的安装 一、前言二、安装python环境1、已经安装python环境&#xff0c;但非3.10.6版本&#xff08;可以不看&#xff09; 三、安装stable diffusion四、运行五、启动报错1、Torch is not able to use GPU2、Installing open_clip 卡住3、报错提示 "git&qu…

systemverilog中的DPI-C用例介绍

文章目录 前言一、dpi_longint二、dpi_packed_array三、dpi_structure四、相关参考总结 前言 本文主要基于VCS内置的三个关于DPI-C的使用用例&#xff0c;记录一下DPI-C的使用方法。测试用例的路径为$VCS_HOME/doc/examples/testbench/sv/。测试用例包括&#xff1a;dpi_longi…

自然语言处理:第四十三章 视觉RAG:变革传统深度学习模型开发流程,开创下一代多模态视觉模型的新时代

文章链接:微信公众平台 (qq.com) 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff0c;笔芯!!! 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff0c;笔芯!!! 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff…

Chaper 09 深入理解Promise

文章目录 前言一、异步编程二、Promise 前言 在JavaScript中&#xff0c;异步编程是一个重要的概念。随着应用程序的复杂性增加&#xff0c;处理异步操作的方式也变得更加复杂。Promise是一种用于处理异步操作的对象&#xff0c;它提供了一种更清晰和更强大的方式来管理异步代…

智能的JavaScript开发工具WebStorm v2024.2全新发布

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具&#xff0c;被广大中国JS开发者誉为"Web前端开发神器""强大的HTML5编辑器""智能的JavaSscript IDE"等。与IntelliJ IDEA同源&#xff0c;继承了IntelliJ IDEA强大的JS部分的功能。 立即获…

学生防近视台灯什么品牌好?学生护眼台灯怎么选?收下这份攻略

根据中国报告大厅的数据&#xff0c;近年来&#xff0c;随着科技的不断进步&#xff0c;台灯行业亦得到了快速发展。早期的台灯主要采用白炽灯作为光源&#xff0c;但随着LED技术的日益成熟&#xff0c;LED台灯已成为市场主流。目前&#xff0c;台灯行业正处于高速发展阶段&…

【吊打面试官系列-Redis面试题】Redis 过期键的删除策略?

大家好&#xff0c;我是锋哥。今天分享关于 【Redis 过期键的删除策略】面试题&#xff0c;希望对大家有帮助&#xff1b; Redis 过期键的删除策略&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 1、定时删除:在设置键的过期时间的同时&#xff0c;创建…

19 - 路径搜索的综合

---- 整理自狄泰软件唐佐林老师课程 文章目录 1. 需求2. 一些工具3. 编译规则的依赖4. 实验 1. 需求 工程项目中不希望源码文件夹在编译时被改动&#xff08;只读文件夹&#xff09;在编译时自动创建文件夹&#xff08;build&#xff09;用于存放编译结果编译过程中能够自动搜…

java基本程序设计结构与数据类型

1.一个简单程序的编写、编译与运行 编写如下的Main.java public class Main{public static void main(String[] args){System.out.println("Hello World");} }上面的程序有几个要注意的点&#xff1a; ①public 是访问修饰符&#xff0c;用来表示其他程序对Main类的…

【C++】vector(下)--下篇

个人主页~ vector&#xff08;上&#xff09;~ vector&#xff08;下&#xff09;–上篇~ vector 二、模拟实现3、test.cpptest1test2test3test4test5test6 三、一个难题 二、模拟实现 3、test.cpp test1 这个没啥好说的&#xff0c;就是尾插和迭代器都能正常使用 //测尾…

中国各、省、市、县、乡镇基尼系数数据(2000-2023年)

基尼系数是一个国际上广泛用来综合考察居民内部收入分配差异状况的重要指标。它表示在全部居民收入中&#xff0c;用于进行不平均分配的那部分收入占总收入的百分比。基尼系数的值介于0和1之间&#xff0c;其中0代表收入分配绝对平均&#xff0c;即每个人的收入都相等&#xff…

Qt21基础图形的绘制

基础图形的绘制 paintareapaintarea.hpaintarea.cpp paintexpaintex.hpaintex.cpp main.cpp运行图 paintarea paintarea.h #ifndef PAINTAREA_H #define PAINTAREA_H#include <QWidget> #include <QBrush> #include <QPen> #include <QPainter> #inc…

DZ主题模板 Discuz迪恩淘宝客购物风格商业版模板

Discuz淘宝客网站模板&#xff0c;迪恩淘宝客购物风格商业版模板。 版本支持&#xff1a;discuzx3.0版本,discuzx3.1版本,discuzx3.2版本。 包括网站首页&#xff0c;论坛首页&#xff0c;论坛列表页&#xff0c;论坛内容页&#xff0c;论坛瀑布流,频道列表页&#xff0c;频道…