Torch 模型 model => .onnx => .trt 及利用 TensorTR 在 C++ 下的模型部署教程
一、模型训练环境搭建和模型训练
模型训练环境搭建主要牵扯 Nvidia driver、Cuda、Cudnn、Anaconda、Torch 的安装,相关安装教程可以参考【StarCoder 微调《个人编程助手: 训练你自己的编码助手》】中 5.1 之前的章节。
模型训练的相关知识可以参考 Torch的编程方式示例以及必备知识,可以直接使用第一部分的手写字体分类模型作为当前教程的示例模型,只需要在脚本最后加入如下代码便可将模型保存为 .pth 模型。注意在上载 model 的代码里依然还是要定义一遍模型,或者 import 之前的模型定义 py 文件,否则报错。
torch.save(model, './model.pth')
# model = torch.load('./model.pth')
# 虽然保存了模型结构和模型参数,但是在load之前还是要定义一遍model才行,或者直接import model
二、model 转换 .onnx 并在 GPU 上利用 python 代码推理
Torch model 模型通过如下代码可以转换为 .onnx 模型。参考链接。
torch.onnx.export(model, input, onnx_path, verbose=True, opset_version=12, input_names=input_names, output_names=output_names) # model 不是 .pth 路径,而是读取内存中的模型,需要先引入模型定义的类,再 load .pth 文件得到 model
# input 是 model 的 forward 函数的输入,如果输入只有一个图片,那 input 可以是一个张量或者字典,但如果输入有多个,则 imput 只能是字典,键名与 forward 形参名称对应
# onnx_path 是输出 onnx 的路径
# verbose 是转换过程中是否打印详情
# input_names 定义 onnx 中输入的名称列表
# output_names 定义 onnx 中输出的名称列表
如果想要在 GPU 上利用 python 代码进行 onnx 模型推理,那首先需要安装 onnxruntime-gpu,安装时需要版本匹配,需要与 Cuda 和 Cudnn 版本匹配,官方版本匹配说明参考链接。安装命令如下:
pip install onnxruntime-gpu==1.18.1 -i https://pypi.tuna.tsinghua.edu.cn/simple
在完成 onnxruntime-gpu 的安装后,可以通过如下代码完成模型推理。 但推理性能一般,其在 Cuda 下的推理时间似乎大于在 Torch 下的推理时间。参考链接。
providers = ['CUDAExecutionProvider']
m = rt.InferenceSession(onnx_path, providers=providers)# 推理 onnx 模型
# output_names 为导出 onnx 时定义的输出名称列表,例如 output_names = ['hm', 'vaf', 'haf','curb_hm', 'curb_vaf', 'curb_haf']
# {"input": image} 为导出 onnx 时定义的输入名称列表
onnx_pred = m.run(output_names, {"input": image})
三、TensorRT 安装
TensorRT 存在 Python 版本和 C++ 版本。
Python 版本 TensorRT 的安装很简单。但一般不太会用 Python 版本 TensorRT。
// TensorRT 8.5 以上的版本用以下命令
pip install tensorrt // 测试一下
python
>>> import tensorrt
>>> print(tensorrt.__version__)
>>> assert tensorrt.Builder(tensorrt.Logger())
C++ 版本 TensorRT 的安装分为.deb 和 . tar 的方式,一般选用 .tar 的方式。
首先去官网下载 TensorRT,下载时版本即需要与 Cuda 版本匹配,也需要与 Cudnn 匹配。官网中只写了 TensorRT 与 Cuda 如何匹配,所以与 Cudnn 的匹配需要自己试出来,当前成功安装的版本是 Cuda12.1 + Cudnn 9.7.1 + TensorRT 10.0.0.6,测试发现 TensorRT 8.* 要求 Cudnn 8.*。

tar -xzvf TensorRT-8.4.3.1.Linux.x86_64-gnu.cuda-11.6.cudnn8.4.tar.gz
cp -r TensorRT-8.4.3.1 /usr/local/include/TensorRT-8.4.3.1
// 路径无所谓,只要知道在哪里就可以,下面 bashrc 中写的就是这个路径,
// cp 完后 home 目录下可删除,或者直接不 cp 而是直接使用 home 下这个路径也可以sudo vim .bashrc// 添加如下内容,注意版本号和路径的对应
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/include/TensorRT-8.4.3.1/lib
alias trtexec="/usr/local/include/TensorRT-8.4.3.1/bin/trtexec"# 如果是 deb 安装的,则可能是:
alias trtexec="/usr/src/tensorrt/bin/trtexec"
# or
alias trtexec="/usr/local/tensorrt/bin/trtexec"source ~/.bashrc
安装完 TensorRT 后,在 vscode 里直接 #include <NvInfer.h> 会报错找不到,解决办法是在 vscode 设置 (.vscode/c_cpp_properties.json) 里添加如下 TensorRT 和 cuda 两个路径,vscode 添加 include 路径的办法参考链接。
{"configurations": [{"name": "Linux","includePath": ["${workspaceFolder}/**","/usr/local/include/TensorRT-10.0.0.6/include","/usr/local/cuda-12.1/include"],"defines": [],"compilerPath": "/usr/bin/gcc","cStandard": "c17","cppStandard": "gnu++17","intelliSenseMode": "linux-gcc-x64"}],"version": 4
}
四、.onnx 模型转 .trt 模型
.onnx 模型转 .trt 模型有两种方法,第一种方法是使用现成的 trtexec 命令,第二种方法是使用自己写的 C++ 代码,第二种方式可以参考 链接 1 链接 2 链接3。这里主要使用第一种方法,关于第一种方法的详细介绍可以参考 链接。大部分情况只需要用到下面这两句命令。
trtexec --onnx=2Dmodel.onnx --saveEngine=2Dmodel.trt// 转换为 fp16 模型
trtexec --onnx=2Dmodel.onnx --saveEngine=2Dmodel.trt --fp16
五、.trt 模型在 C++ 下进行推理
首先,介绍两个概念:
CUDA 流:可以将一个流看做是 GPU 上的一个任务,不同任务可以并行执行。利用三个流,同一个流上的任务顺序执行,不同流上的任务可以同时执行,从而实现并发操作。
运行时库:就是在运行时提供基础支持的库。例如:C/C++ 运行时库,就是 C/C++ 程序在运行时依赖的基础库。
然后,说明一下官方资料:
Nvidia TensorRT 官方文档 (最新版本 历史版本)、Quick Start Guide (最新版本 历史版本可以从官方文档进入)、API 说明 (最新版本 历史版本可以从官方文档进入)、版本变化 API 变化说明 和代码 (最新版本 历史版本)。
这里存在一个情况,TensorRT 从 8.x 版本变为 10.x 版本后,API 发生了一些变化,例如:模型推理函数从 enqueueV2 变为 enqueueV3,且形参列表也变了;还有新版本似乎不需要再主动调用 destroy() 函数进行对象销毁。但官方文档、Quick Start Guide 和代码等,更新并不及时,到了最新版才完成更新,所以如果你实际安装的是 TensorRT 10.0.0.6,然后你参考对应版本的官方资料,会无法运行。
最后,说明一下第三方资料:
当前我搜索到的所有第三方 CSDN 和 知乎 资料全是针对 8.x 版本的。但也很值得参考
Pytorch 导出 onnx 模型,C++ 转化为 TensorRT 并实现推理过程 对应 Git 代码
torch 转 ONNX 模型转 TensorRT C++ 推理
TensorRT 的使用流程 c++ 和 python
下面是我针对包含三个 head (语义分割、关键点检测、旋转框 FCOS 检测) 的 trt 模型的推理代码,针对版本是 Cuda12.1 + Cudnn 9.7.1 + TensorRT 10.0.0.6。
FP16 的推理与 FP32 的推理代码没区别,唯一区别就是上一步转 .trt 时多了个配置参数,所以在模型推理时,输入依然是 FP32,进入模型后,模型会自动转换为 FP16,输出也一样,模型会自动转换为 FP32。实测在 24G A10 下,100 次推理,512 × 256 尺寸输入图片,FP32 推理耗时 394ms,FP32 推理耗时 253ms。
IPMdet.h
#pragma once
// 系统头文件
#include <string>
#include <vector>
#include <fstream>
#include <memory>
// TensorRT 相关头文件
#include <NvInfer.h>
#include <NvInferRuntime.h>
// CUDA 相关头文件
#include <cuda_runtime.h>
// 测试用头文件
#include <iostream>
using namespace std;// 用于打印 CUDA 报错 - 宏定义
#define checkRuntime(op) __check_cuda_runtime((op), #op, __FILE__, __LINE__)// 用于打印 TensorRT 报错,准备一个 logger 类,打印构建 TensorRT 推理模型过程中的一些错误或警告,按照指定的严重性程度 (severity) 打印信息
// 内联函数可以放在头文件,因为内联函数不会产生独立的符号,不会引起多重定义的问题
inline const char* severity_string(nvinfer1::ILogger::Severity t) {switch (t) {case nvinfer1::ILogger::Severity::kINTERNAL_ERROR: return "internal_error";case nvinfer1::ILogger::Severity::kERROR: return "error";case nvinfer1::ILogger::Severity::kWARNING: return "warning";case nvinfer1::ILogger::Severity::kINFO: return "info";case nvinfer1::ILogger::Severity::kVERBOSE: return "verbose";default: return "unknown";}
}class TRTLogger : public nvinfer1::ILogger {public:virtual void log(Severity severity, nvinfer1::AsciiChar const* msg) noexcept override {if (severity <= Severity::kWARNING) {if (severity == Severity::kWARNING) printf("\033[33m%s: %s\033[0m\n", severity_string(severity), msg);else if (severity == Severity::kERROR) printf("\031[33m%s: %s\033[0m\n", severity_string(severity), msg);else printf("%s: %s\n", severity_string(severity), msg);}}
};// 定义一个 share_ptr 的智能指针
// 模板的定义必须放在头文件中,因为模板的实例化需要在编译时进行
template<typename _T>
shared_ptr<_T> make_nvshared(_T *ptr) {return shared_ptr<_T>(ptr);
}// IPM 图检测类
class IPMdet {public:IPMdet() = default;IPMdet(const string& file);~IPMdet();// 模型推理void infer_trtmodel(float* pimage);private:// 读取 .trt 文件vector<unsigned char> load_file(const string& file);// 预处理输入图片void preprocess_image(float* pimage);public:// 输出结果private:// TensorRT 推理用的工具vector<unsigned char> _engine_data; // 记录 .trt 模型的二进制序列化格式数据TRTLogger logger; // 打印 TensorRT 的错误信息shared_ptr<nvinfer1::IRuntime> _runtime = nullptr; // 运行时,即推理引擎的支持库和函数等shared_ptr<nvinfer1::ICudaEngine> _engine = nullptr; // 推理引擎,包含反序列化的 .trt 模型数据shared_ptr<nvinfer1::IExecutionContext> _context = nullptr; // 上下文执行器,用于做模型推理cudaStream_t _stream = nullptr;// 定义模型输入输出尺寸int input_batch = 1;int input_channel = 3;int input_height = 512;int input_width = 256;int output_height1 = input_height / 4;int output_width1 = input_width / 4;int output_height2 = input_height / 8 ;int output_width2 = input_width / 8;// 准备好 **_host 和 **_device,分别表示内存中的数据指针和显存中的数据指针// input 数据int input_numel = input_batch * input_channel * input_height * input_width;float* input_data_host = nullptr;float* input_data_device = nullptr;// output 数据 - keypointsint keypoints_numel = input_batch * 1 * output_height1 * output_width1;void* output_data_keypoints_host = nullptr;void* output_data_keypoints_device = nullptr;// output 数据 - classificationint classification_numel = input_batch * 1 * output_height2 * output_width2;void* output_data_classification_host = nullptr;void* output_data_classification_device = nullptr;// output 数据 - bbox_OBBint bbox_OBB_numel = input_batch * 4 * output_height2 * output_width2;void* output_data_bbox_OBB_host = nullptr;void* output_data_bbox_OBB_device = nullptr;// output 数据 - centernessint centerness_numel = input_batch * 1 * output_height2 * output_width2;void* output_data_centerness_host = nullptr;void* output_data_centerness_device = nullptr;// output 数据 - bbox_whint bbox_wh_numel = input_batch * 2 * output_height2 * output_width2;void* output_data_bbox_wh_host = nullptr;void* output_data_bbox_wh_device = nullptr;// output 数据 - segmentationint segmentation_numel = input_batch * 8 * input_height * input_width;void* output_data_segmentation_host = nullptr;void* output_data_segmentation_device = nullptr;
};
IPMdet.cpp
#include "IPMdet.h"// 打印 Cuda 报错
// 函数定义不应放在头文件里,否则当头文件被多个 cpp 调用时,会出现重复定义的问题
bool __check_cuda_runtime(cudaError_t code, const char* op, const char* file, int line) {if (code != cudaSuccess) {const char* err_name = cudaGetErrorName(code);const char* err_message = cudaGetErrorString(code);printf("runtime error %s: %d %s failed.\n code = %s, message = %s", file, line, op, err_name, err_message);return false;}return true;
}// 构造函数
IPMdet::IPMdet(const string& file){// 1. TensorRT 相关操作///// 1.1 读取 .trt 文件_engine_data = load_file(file);// 1.2 创建运行时,需要日志记录器_runtime = make_nvshared(nvinfer1::createInferRuntime(logger));// 1.3 创建推理引擎,需要运行时和序列化 trt 文件,包含反序列化的 .trt 模型数据_engine = make_nvshared(_runtime->deserializeCudaEngine(_engine_data.data(), _engine_data.size()));if (_engine == nullptr) {printf("Deserialize cuda engine failed.\n");return;}// 1.4 创建上下文执行器,需要推理引擎_context = make_nvshared(_engine->createExecutionContext());// 打印 .trt 模型的输入输出张量的名称和维度,这里与 onnx 中的名称和维度一致// for (int i=0, e=_engine->getNbIOTensors(); i<e; i++){// auto const name = _engine->getIOTensorName(i);// auto const size = _engine->getTensorShape(name);// cout << "Tensor Name: " << name << endl;// for (int i = 0; i < size.nbDims; ++i) {// std::cout << "Dimension " << i << ": " << size.d[i] << std::endl;// }// cout << endl;// }// 2. CUDA 相关操作///// 2.1 创建 CUDA 流,CUDA 流类似于线程,每个任务都必须有一个 CUDA 流,不同的 CUDA 流可以在 GPU 中并行执行任务checkRuntime(cudaStreamCreate(&_stream));// 2.2 申请 CPU 内存和 GPU 内存,准备好 **_host 和 **_device,分别表示内存中的数据指针和显存中的数据指针// input 数据checkRuntime(cudaMallocHost(&input_data_host, input_numel * sizeof(float)));checkRuntime(cudaMalloc(&input_data_device, input_numel * sizeof(float)));// output 数据 - keypointscheckRuntime(cudaMallocHost(&output_data_keypoints_host, keypoints_numel * sizeof(float)));checkRuntime(cudaMalloc(&output_data_keypoints_device, keypoints_numel * sizeof(float)));// output 数据 - classificationcheckRuntime(cudaMallocHost(&output_data_classification_host, classification_numel * sizeof(float)));checkRuntime(cudaMalloc(&output_data_classification_device, classification_numel * sizeof(float)));// output 数据 - bbox_OBBcheckRuntime(cudaMallocHost(&output_data_bbox_OBB_host, bbox_OBB_numel * sizeof(float)));checkRuntime(cudaMalloc(&output_data_bbox_OBB_device, bbox_OBB_numel * sizeof(float)));// output 数据 - centernesscheckRuntime(cudaMallocHost(&output_data_centerness_host, centerness_numel * sizeof(float)));checkRuntime(cudaMalloc(&output_data_centerness_device, centerness_numel * sizeof(float)));// output 数据 - bbox_whcheckRuntime(cudaMallocHost(&output_data_bbox_wh_host, bbox_wh_numel * sizeof(float)));checkRuntime(cudaMalloc(&output_data_bbox_wh_device, bbox_wh_numel * sizeof(float)));// output 数据 - segmentationcheckRuntime(cudaMallocHost(&output_data_segmentation_host, segmentation_numel * sizeof(float)));checkRuntime(cudaMalloc(&output_data_segmentation_device, segmentation_numel * sizeof(float)));// 3. TensorRT 内存绑定///_context->setTensorAddress("image", input_data_device);_context->setTensorAddress("keypoints", output_data_keypoints_device);_context->setTensorAddress("classification", output_data_classification_device);_context->setTensorAddress("bbox_OBB", output_data_bbox_OBB_device);_context->setTensorAddress("centerness", output_data_centerness_device);_context->setTensorAddress("bbox_wh", output_data_bbox_wh_device);_context->setTensorAddress("segmentation", output_data_segmentation_device);cout << "init OK !" << endl;
}// 析构函数
IPMdet::~IPMdet(){// 释放 CPU 内存,看 TensorRT 10 以上版本,似乎不再需要主动释放 CPU 内存checkRuntime(cudaFreeHost(input_data_host));checkRuntime(cudaFreeHost(output_data_keypoints_host));checkRuntime(cudaFreeHost(output_data_classification_host));checkRuntime(cudaFreeHost(output_data_bbox_OBB_host));checkRuntime(cudaFreeHost(output_data_centerness_host));checkRuntime(cudaFreeHost(output_data_bbox_wh_host));checkRuntime(cudaFreeHost(output_data_segmentation_host));// 释放 GPU 内存checkRuntime(cudaFree(input_data_device));checkRuntime(cudaFree(output_data_keypoints_device));checkRuntime(cudaFree(output_data_classification_device));checkRuntime(cudaFree(output_data_bbox_OBB_device));checkRuntime(cudaFree(output_data_centerness_device));checkRuntime(cudaFree(output_data_bbox_wh_device));checkRuntime(cudaFree(output_data_segmentation_device));// 释放 CUDA 流,看 TensorRT 10 以上版本,似乎不再需要主动释放 CUDA 流checkRuntime(cudaStreamDestroy(_stream));cout << "Detroy OK !" << endl;
}// 读取 .trt 文件
vector<unsigned char> IPMdet::load_file(const string& file) { // 返回结果为无符号字符的vector,其数据存储是连成片的ifstream in(file, ios::in | ios::binary); // 定义一个数据读取对象,以二进制读取数据if (!in.is_open()) return {}; // 如果没有可读数据则返回空in.seekg(0, ios::end); // seekg函数作用是将指针指向文件终止处size_t length = in.tellg(); // tellg函数作用是返回指针当前位置,此时即为数据长度vector<uint8_t> data; // 定义一个vector用于存储读取数据,仅仅是类头,其数据存储区还是char型data指针if (length > 0) {in.seekg(0, ios::beg); // seekg函数作用是将指针指向文件起始处data.resize(length); // 为vector申请长度为length的数据存储区,默认全部填充 0in.read((char*)&data[0], length); // 为vector的数据存储区读取长度为length的数据}in.close(); // 关闭数据流return data;
}// 预处理输入图片
void IPMdet::preprocess_image(float* pimage){float mean[] = {128, 128, 128};float std[] = {128, 128, 128};int image_area = 512 * 256;float* phost_b = input_data_host + image_area * 0;float* phost_g = input_data_host + image_area * 1;float* phost_r = input_data_host + image_area * 2;for (int i=0; i<image_area; ++i, pimage += 3) {*phost_r++ = (pimage[0] - mean[0]) / std[0];*phost_g++ = (pimage[1] - mean[1]) / std[1];*phost_b++ = (pimage[2] - mean[2]) / std[2];}
}void IPMdet::infer_trtmodel(float* pimage){// 预处理输入图片preprocess_image(pimage);// 将输入图片从 CPU 内存拷贝至 GPU 内存checkRuntime(cudaMemcpyAsync(input_data_device, input_data_host, input_numel *sizeof(float), cudaMemcpyHostToDevice, _stream));// 模型推理bool success = _context->enqueueV3(_stream);// 将输出结果从 GPU 内存拷贝至 CPU 内存checkRuntime(cudaMemcpyAsync(output_data_keypoints_host, output_data_keypoints_device, sizeof(output_data_keypoints_host), cudaMemcpyDeviceToHost, _stream));checkRuntime(cudaMemcpyAsync(output_data_classification_host, output_data_classification_device, sizeof(output_data_classification_host), cudaMemcpyDeviceToHost, _stream));checkRuntime(cudaMemcpyAsync(output_data_bbox_OBB_host, output_data_bbox_OBB_device, sizeof(output_data_bbox_OBB_host), cudaMemcpyDeviceToHost, _stream));checkRuntime(cudaMemcpyAsync(output_data_centerness_host, output_data_centerness_device, sizeof(output_data_centerness_host), cudaMemcpyDeviceToHost, _stream));checkRuntime(cudaMemcpyAsync(output_data_bbox_wh_host, output_data_bbox_wh_device, sizeof(output_data_bbox_wh_host), cudaMemcpyDeviceToHost, _stream));checkRuntime(cudaMemcpyAsync(output_data_segmentation_host, output_data_segmentation_device, sizeof(output_data_segmentation_host), cudaMemcpyDeviceToHost, _stream));// 等待直到 _stream 流的工作完成checkRuntime(cudaStreamSynchronize(_stream));
}
main.cpp
#include <iostream>
#include <string>
#include <chrono>
#include <opencv2/opencv.hpp>
#include "IPMdet.h"
using namespace std;int main() {cv::Mat image = cv::imread("/mnt/sdb/ipm_sample_datas/detection/train/apa_det_train/ADAS_20200113-132415_216_1212__00006732_28x14_03_ipm.jpg");cout << image.size << endl;cv::Mat input;image.convertTo(input, CV_32FC3);string onnx_path = "/mnt/sdb/HAT-feature-ipm-multitask-release/output_model/model_best_22.trt";IPMdet ipmdet = IPMdet(onnx_path);auto start0 = chrono::high_resolution_clock::now();ipmdet.infer_trtmodel((float*)input.data);for(int i=0; i<100; i++){ipmdet.infer_trtmodel((float*)input.data);}auto end0 = chrono::high_resolution_clock::now();string onnx_path_fp16 = "/mnt/sdb/HAT-feature-ipm-multitask-release/output_model/model_best_22_fp16.trt";IPMdet ipmdet_fp16 = IPMdet(onnx_path_fp16);ipmdet_fp16.infer_trtmodel((float*)input.data);auto start1 = chrono::high_resolution_clock::now();for(int j=0; j<100; j++){ipmdet_fp16.infer_trtmodel((float*)input.data);}auto end1 = chrono::high_resolution_clock::now();chrono::duration<double, std::milli> elapsed0 = end0 - start0;chrono::duration<double, std::milli> elapsed1 = end1 - start1;cout << "FP32 time taken: " << elapsed0.count() << " ms" << endl;cout << "FP16 time taken: " << elapsed1.count() << " ms" << endl << endl;
}
相关文章:
Torch 模型 model => .onnx => .trt 及利用 TensorTR 在 C++ 下的模型部署教程
一、模型训练环境搭建和模型训练 模型训练环境搭建主要牵扯 Nvidia driver、Cuda、Cudnn、Anaconda、Torch 的安装,相关安装教程可以参考【StarCoder 微调《个人编程助手: 训练你自己的编码助手》】中 5.1 之前的章节。 模型训练的相关知识可以参考 Torch的编程方…...
FreeSWITCH 之 chat
要把 FreeSWITCH 之 chat 完全研究清楚,似乎不容易 发送,路由,接收 跟哪些模块有关 等等 咱一边查资料,一边整理,不着急 先看看 Kamalio 怎么发 MESSAGE loadmodule "uac.so"route[uac_send_message] {…...
如何在Spring Boot中配置和使用MyBatis-Plus
在当今的Java开发中,Spring Boot已经成为了一个非常流行的框架,而MyBatis-Plus则是一个强大的ORM框架,为开发人员提供了更简便的数据库操作方式。很多开发者都在使用Spring Boot和MyBatis-Plus的组合来快速构建高效的应用。今天就来聊聊如何在…...
爱普生可编程晶振SG-8200CJ特性与应用
在高速发展的电子技术领域,时钟源作为电子系统的“心脏”,其性能直接影响设备的稳定性与可靠性。爱普生SG-8200CJ可编程晶振凭借其优秀的频率精度、低抖动性能及广泛的环境适应性,正成为众多领域的得力之选,为各类设备的高效运行与…...
ubuntu中用docker下载opengauss
1.安装docker sudo apt install docker.io2.拉取opengauss镜像 sudo docker pull enmotech/opengauss3.创建容器 sudo docker run --name opengauss --privilegedtrue -d -e GS_PASSWORDEnmo123 enmotech/opengauss:latest3.5.如果容器停止运行(比如关机了&#…...
tslib
使用tslib来读取触摸屏的数据,可以得到原始数据,也可以在原始数据的基础上进行一些处理。比如有些触摸屏比较不稳定,跳动比较大,我们可以将跳动比较大的数据给删除掉 plugins里面的每个文件都会被编译成一个动态库,这些…...
MANUS怎么用
(1)分析方法论我过去说过一个分析模型:供给侧-消费侧。供给侧想做大,得靠生态集成。消费侧想坐大,得靠交互体验。(2)交互体验我先给大家讲一下计算机产业发展70来年,在交互上的变化。…...
Spring Cloud Alibaba 实战:Sentinel 保障微服务的高可用性与流量防护
1.1 Sentinel 作用 Sentinel 是阿里巴巴开源的一款 流量控制和熔断降级 框架,主要用于: 流量控制:限制 QPS,防止流量暴增导致系统崩溃熔断降级:当某个服务不可用时自动降级,避免故障扩散热点参数限流&…...
大数据技术在土地利用规划中的应用分析
大数据技术在土地利用规划中的应用分析 一、引言 土地利用规划是对一定区域内的土地开发、利用、整治和保护所作出的统筹安排与战略部署,对于实现土地资源的优化配置、保障社会经济的可持续发展具有关键意义。在当今数字化时代,大数据技术凭借其海量数据处理、高效信息挖掘等…...
MoonSharp 文档三
MoonSharp 文档一-CSDN博客 MoonSharp 文档二-CSDN博客 MoonSharp 文档四-CSDN博客 MoonSharp 文档五-CSDN博客 7.Proxy objects(代理对象) 如何封装你的实现,同时又为脚本提供一个有意义的对象模型 官方文档:MoonSharp 在实际…...
linux和windows之间的复制
第一步 sudo apt-get autoremove open-vm-tools第二步 sudo apt-get update第三步 sudo apt-get install open-vm-tools-desktop按y 第四步 重启虚拟机,终端下输入 rebootLinux下 按“ CtrlShiftC V ”复制粘贴 Windows下按“ Ctrl C V ”复制粘贴...
在资源有限中逆势突围:从抗战智谋到寒门高考的破局智慧
目录 引言 一、历史中的非对称作战:从李牧到八路军的智谋传承 李牧戍边:古代军事博弈中的资源重构 八路军的游击战:现代战争中的智慧延续 二、创业界的逆袭之道:小米与拼多多的资源重构 从MVP到杠杆解 社交裂变与资源错配 …...
Ubuntu 22.04 无法进入图形界面的解决方法
Ubuntu 22.04 无法进入图形界面,只能进入 tty,可能是由于图形界面相关的配置或驱动程序出现了问题。以下是一些常见的解决方法: 1. 检查图形界面服务状态 首先,检查图形界面服务(通常是 gdm 或 lightdm)的…...
Python中很常用的100个函数整理
Python 内置函数提供了强大的工具,涵盖数据处理、数学运算、迭代控制、类型转换等。本文总结了 100 个常用内置函数,并配备示例代码,提高编程效率。 1. abs() 取绝对值 print(abs(-10)) # 10 2. all() 判断所有元素是否为真 print(all([…...
javascript-es6 (六)
编程思想 面向过程 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次 调用就可以了 就是按照我们分析好了的步骤,按照步骤解决问题 面向对象 面向对象是把事务分解成为一个个对象&…...
大模型微调技术基础(一)
文章目录 GPT与BERT的差异GPT(Decoder架构)优点缺点 BERT(Encoder架构)优点缺点 总结 LoRA低参数大模型与全参数小模型表现对比分析LoRA(Low-Rank Adaptation)技术详解1. LoRA 核心原理2. 应用场景3. 简单代…...
Spring AI 1.0.0 M6新特性MCP
Spring AI 1.0.0 M6新特性MCP 前言一、MCP是什么?(Model Context Protocol)二、它的发展历程三、核心架构四、MCP Java SDK的核心能力Java MCP实现遵循三层架构:MCP客户端MCP服务器总结MCP 的核心能力总结多种传输选项 搭建服务端…...
【时时三省】(C语言基础)赋值表达式和赋值语句和变量赋初值
山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 赋值表达式和赋值语句 在C程序中,赋值语句是用得最多的语句。实际上,C语言的赋值语句属于表达式语句,由一个赋值表达式加一个分号组成。其他一些高级语言…...
Room数据库的使用
一、room的引用导入 1、在app的gradle中引入 plugins {//这个ksp 一定要对应相关的 kotlin 版本,不然会一直报错i的---id("com.google.devtools.ksp") version "1.9.0-1.0.13" apply false } 2、在model的gradle中引入 plugins {id("com.g…...
【性能测试入门_01性能测试jmeter基础实操场景详解】
一、应用项目如何部署在服务器上 可以将项目进行打jar包 双击install,控制台就会打印打包的过程 最终打的包,会存放在打印的那个路径下 这个jar包,就是开发人员开发好,直接可以部署的 可以通过命令,在终端直接启动这…...
SpringBoot加载配置文件的优先级
在 SpringBoot 应用中,配置文件的加载顺序(优先级)决定了不同来源的配置属性如何覆盖彼此,但是,在实践中,也会经常遇到。下面文章将分析 SpringBoot 配置文件加载的优先级,从高到低排列。 文章目…...
跨越时空的对话:图灵与GPT-4聊AI的前世今生
(背景:虚拟咖啡厅,图灵身着1950年代西装,端着一杯热茶,GPT-4以全息投影形态坐在对面) 图灵(喝了口茶):“听说你能写诗?我当年在布莱切利园破解Enigma时&…...
如何通过 Seatunnel 实现 MySQL 到 OceanBase的数据迁移同步
1. 准备传输工具 本方案采用 Apache Seatunnel(简称seatunnel)进行MySQL 到 OceanBase 的数据迁移和同步,出于对方案轻量性的考量,我们采用其内置的Zeta引擎来实现,包括全量同步、离线增量同步,以及CDC方案…...
C++20 新特性总结
简要总结 C20 引入了四项非常大的更新, 分别是: 概念(Concepts). 用来简化模板编程, 强化表达能力. 并且使得出错原因更容易查找.模块(Modules). 这是代码组织方面非常大的更新. 提供了新的方式来组织代码, 并且可以减少编译时间.范围库(Ranges and Views). 轻量级的, 非拥有…...
软件设计模式之简单工厂模式
目录 一.类图(手机生产) 二.代码实现 Iphone类: Vivo类: Mobile类: MobileFactory类: Client类: 一.类图(手机生产) 补充:MobileFactory也可以直接指向…...
内网激活JRebel插件(无网络环境)
1.官网下载安装包,JRebel and XRebel JRebel and XRebel - IntelliJ IDEs Plugin | Marketplace 2.以IInstall Plugin from Disk的方式读取 3.运行JrebelServer.jar 终端输入: java -jar JrebelServer.jar -p 8080 (默认8080端口)服务会自动打开浏览器至 http:/…...
LiveGBS流媒体平台GB/T28181常见问题-视频流安全控制HTTP接口鉴权勾选流地址鉴权后401Unauthorized如何播放调用接口流地址校验
LiveGBS流媒体平台GB/T28181常见问题频流安全控制HTTP接口鉴权勾选流地址鉴权后401Unauthorized如何播放调用接口流地址校验? 1、安全控制1.1、HTTP接口鉴权1.2、流地址鉴权 2、401 Unauthorized2.1、携带token调用接口2.1.1、获取鉴权token2.1.2、调用其它接口2.1.…...
Java 大视界 -- 区块链赋能 Java 大数据:数据可信与价值流转(84)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
接口自动化入门 —— Http的请求头,请求体,响应码解析!
在接口自动化测试中,HTTP请求头、请求体和响应码是核心组成部分。理解它们的作用、格式和解析方法对于进行有效的接口测试至关重要。以下是详细解析: 1. HTTP 请求头(Request Header) 1.1 作用 请求头是客户端向服务器发送的附加…...
upload-labs(1-20)详解(专业版)
目录 第1关 第2关 第3关 第4题 第5题 第6题 第7题 第8题 第9题 第10题 第11题 第12题 第13题 第1关 查看源码 在第一关是一个前端js的一个后缀识别:当不为jpg、png、gif时候出现弹窗 检查源码 将return checkFile() 改为 return ture 就可以将php顺利…...
