onnx入門教程(七)——如何添加 TensorRT 自定義算子

??在前面的模型入門系列文章中,我們介紹了部署一個 PyTorch 模型到推理后端,如 ONNXRuntime,這其中可能遇到很多工程性的問題。
有些可以通過創建 ONNX 節點來解決,該節點仍然使用后端原生的實現進行推理。而有些無法導出到后端的算法,可以通過重寫代碼改變算法的實現過程,同樣可以導出到 ONNX ,達到一致的效果。以上兩種方式一般可以處理絕大多數的部署問題,同時也不需要向推理框架引入新的內容,是我們進行模型部署時候的優先選擇。
然而,仍然存在部分模型,模型中某些算子無法通過上述兩種方式繞過問題,這時候,如何對特定后端實現對應代碼就極為重要。這也是本文將介紹的第三種方式——自定義插件。
自定義插件是很多推理框架支持用戶自定義算子的方式,以 MMDeploy 為例,它是一個支持多種推理后端的算法庫。目前支持的后端有:

  • ONNXRuntime
  • TensorRT
  • ncnn
  • OpenVINO
  • PPLNN

??其中,前三種后端均實現了一些自定義的算子。例如 ONNXRuntime 中的調制可變性卷積,ncnn 中的topk 算子,TensorRT 中的 MultiLevelRoiAlign 。
介紹如何給后端自定義算子是一件相對復雜的事情,所以本文只針對其中一種后端 TensorRT,介紹自定義算子。如果讀者對其他后端感興趣,可以去他們的代碼庫查看,一般地,各個推理框架均有詳細文檔介紹如何添加客制化的算子實現。

1. 在 MMDeploy 添加 TensorRT 插件

??仍然以前面教程二中的超分辨模型 SRCNN 為例。在教程二中,我們用 ONNXRuntime 作為后端,通過 PyTorch 的 symbolic 函數導出了一個支持動態 scale 的 ONNX 模型,這個模型可以直接用 ONNXRuntime 運行,這是因為 NewInterpolate 類導出的節點 Resize 就是 ONNXRuntime 支持的節點。下面我們嘗試直接將教程二導出的 srcnn3.onnx 轉換到 TensorRT。

from mmdeploy.backend.tensorrt.utils import from_onnx from_onnx( 'srcnn3.onnx', 'srcnn3', input_shapes=dict( input=dict( min_shape=[1, 3, 256, 256], opt_shape=[1, 3, 256, 256], max_shape=[1, 3, 256, 256]), factor=dict( min_shape=[4], opt_shape=[4], max_shape=[4]))) 

??沒有安裝過 MMDeploy 的小伙伴可以先參考 build.md 進行安裝,安裝完成后執行上述腳本,會有如下報錯:

RuntimeError: Failed to parse onnx, In node 1 (importResize): UNSUPPORTED_NODE: Assertion failed: mode != "cubic" && "This version of TensorRT does not support cubic interpolation!" 

報錯的原因有以下兩方面:

??'srcnn3.onnx’文件中的Resize 是 ONNX 原生節點。其插值方式之一 bicubic 并不被 TensorRT 支持(TensorRT 的 Resize Layer僅支持 nearest 和 bilinear 兩種插值方式)。日志的錯誤信息也明確提示了這點;
但即便將 “bicubic” 模式改為 “bilinear” ,轉換仍然失敗: RuntimeError: Failed to parse onnx, In node 1 (importResize): UNSUPPORTED_NODE: Assertion failed: scales.is_weights() && Resize scales must be initializer!"。這是因為 TensorRT 無法接受動態 scale 導致的。

2. 創建 ONNX 節點

??為解決上述問題,我們需要創建一個新的節點替換原生 Resize 節點,并且實現新節點對應的插件代碼。
新改節點名稱就叫 Test::DynamicTRTResize,這是種類 C++ 的寫法,Test 為域名,主要用于區分不同來源下的同名的節點,比如 ONNX:: 和 Test::。當然了,ONNX 本身也不存在 DynamicTRTResize 的節點名。

import torch 
from torch import nn 
from torch.nn.functional import interpolate 
import torch.onnx 
import cv2 
import numpy as np 
import os, requests 
# Download checkpoint and test image 
urls = ['https://download.openmmlab.com/mmediting/restorers/srcnn/srcnn_x4k915_1x16_1000k_div2k_20200608-4186f232.pth', 'https://raw.githubusercontent.com/open-mmlab/mmediting/master/tests/data/face/000001.png'] 
names = ['srcnn.pth', 'face.png'] 
for url, name in zip(urls, names): if not os.path.exists(name): open(name, 'wb').write(requests.get(url).content) 
class DynamicTRTResize(torch.autograd.Function): def __init__(self) -> None: super().__init__() @staticmethod def symbolic(g, input, size_tensor, align_corners = False): """Symbolic function for creating onnx op.""" return g.op( 'Test::DynamicTRTResize', input, size_tensor, align_corners_i=align_corners) @staticmethod def forward(g, input, size_tensor, align_corners = False): """Run forward.""" size = [size_tensor.size(-2), size_tensor.size(-1)] return interpolate( input, size=size, mode='bicubic', align_corners=align_corners) 
class StrangeSuperResolutionNet(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=9, padding=4) self.conv2 = nn.Conv2d(64, 32, kernel_size=1, padding=0) self.conv3 = nn.Conv2d(32, 3, kernel_size=5, padding=2) self.relu = nn.ReLU() def forward(self, x, size_tensor): x = DynamicTRTResize.apply(x, size_tensor) out = self.relu(self.conv1(x)) out = self.relu(self.conv2(out)) out = self.conv3(out) return out 
def init_torch_model(): torch_model = StrangeSuperResolutionNet() state_dict = torch.load('srcnn.pth')['state_dict'] # Adapt the checkpoint for old_key in list(state_dict.keys()): new_key = '.'.join(old_key.split('.')[1:]) state_dict[new_key] = state_dict.pop(old_key) torch_model.load_state_dict(state_dict) torch_model.eval() return torch_model 
model = init_torch_model() 
factor = torch.rand([1, 1, 512, 512], dtype=torch.float) 
input_img = cv2.imread('face.png').astype(np.float32) 
# HWC to NCHW 
input_img = np.transpose(input_img, [2, 0, 1]) 
input_img = np.expand_dims(input_img, 0) 
# Inference 
torch_output = model(torch.from_numpy(input_img), factor).detach().numpy() 
# NCHW to HWC 
torch_output = np.squeeze(torch_output, 0) 
torch_output = np.clip(torch_output, 0, 255) 
torch_output = np.transpose(torch_output, [1, 2, 0]).astype(np.uint8) 
# Show image 
cv2.imwrite("face_torch.png", torch_output) 
x = torch.randn(1, 3, 256, 256) 
dynamic_axes={ 'input': { 0: 'batch', 2: 'height', 3: 'width' }, 'factor': { 0: 'batch1', 2: 'height1', 3: 'width1' }, 'output': { 0: 'batch2', 2: 'height2', 3: 'width2' }, } 
with torch.no_grad(): torch.onnx.export( model, (x, factor), "srcnn3.onnx", opset_version=11, input_names=['input', 'factor'], output_names=['output'], dynamic_axes=dynamic_axes) 

??執行上述腳本,我們成功導出了一個 ONNX 模型 srcnn.onnx。用 netron 打開這個模型可視化如下:
在這里插入圖片描述
直接將該模型轉換成 TensorRT 模型也是不可行的,這是因為 TensorRT 還無法解析 DynamicTRTResize 節點。而想要解析該節點,我們必須為 TensorRT 添加 c++ 代碼,實現該插件。

3. C++ 實現

??因為 MMDeploy 中已經實現了 Bicubic Interpolate 算子,所以我們可以復用其中的 CUDA 部分代碼,只針對 TensorRT 實現支持動態 scale 的插件即可。對 CUDA 編程感興趣的小伙伴可以參考 CUDA 的官方教程。
因為 csrc/backend_ops/tensorrt/bicubic_interpolate 中有我們需要的 CUDA 代碼,所以我們可以直接在該文件夾加添加 TensorRT 相關的 trt_dynamic_resize.hpp 和 trt_dynamic_resize.cpp 文件,在這兩個文件中分別聲明和實現插件就可以了。我們也可以新建文件夾 csrc/backend_ops/tensorrt/dynamic_resize,將這兩個文件直接放到這個文件夾下。
對 TensorRT 7+,要實現這樣一個自定義插件,我們需要寫兩個類。

  • DynamicTRTResize,繼承自 nvinfer1::IPluginV2DynamicExt,完成插件的具體實現。
  • DynamicTRTResizeCreator,繼承自 nvinfer1::IPluginCreator,是插件的工廠類,用于創建 DynamicTRTResize 插件的實例。

??在 MMDeploy 中,由于有若干插件需要實現,所以我們在mmdeploy/csrc/backend_ops/tensorrt/common/trt_plugin_base.hpp 中實現了 TRTPluginBase 和 TRTPluginCreatorBase 兩個類,用于管理一些所有插件共有的屬性方法。
其中,TRTPluginBase 繼承自 nvinfer1::IPluginV2DynamicExt,而TRTPluginCreatorBase繼承自nvinfer1::IPluginCreator。這樣,用戶實現插件時只需繼承這兩個新的類即可。所以我們只需在 dynamic_resize 文件夾下的 .hpp 文件中,引用 trt_plugin_base.hpp 頭文件,繼承邏輯如下:

class DynamicTRTResize : public TRTPluginBase{} 
class DynamicTRTResizeCreator : public TRTPluginCreatorBase{} 

??在 trt_dynamic_resize.hpp 中,我們聲明如下內容:

#ifndef TRT_DYNAMIC_RESIZE_HPP 
#define TRT_DYNAMIC_RESIZE_HPP 
#include <cublas_v2.h> 
#include <memory> 
#include <string> 
#include <vector> 
#include "trt_plugin_base.hpp" 
namespace mmdeploy { 
class DynamicTRTResize : public TRTPluginBase { public: DynamicTRTResize(const std::string &name, bool align_corners); DynamicTRTResize(const std::string name, const void *data, size_t length); DynamicTRTResize() = delete; // IPluginV2DynamicExt Methods nvinfer1::IPluginV2DynamicExt *clone() const TRT_NOEXCEPT override; nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs *inputs, int nbInputs, nvinfer1::IExprBuilder &exprBuilder) TRT_NOEXCEPT override; bool supportsFormatCombination(int pos, const nvinfer1::PluginTensorDesc *ioDesc, int nbInputs, int nbOutputs) TRT_NOEXCEPT override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc *in, int nbInputs, const nvinfer1::DynamicPluginTensorDesc *out, int nbOutputs) TRT_NOEXCEPT override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc *inputs, int nbInputs, const nvinfer1::PluginTensorDesc *outputs, int nbOutputs) const TRT_NOEXCEPT override; int enqueue(const nvinfer1::PluginTensorDesc *inputDesc, const nvinfer1::PluginTensorDesc *outputDesc, const void *const *inputs, void *const *outputs, void *workspace, cudaStream_t stream) TRT_NOEXCEPT override; // IPluginV2Ext Methods nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType *inputTypes, int nbInputs) const TRT_NOEXCEPT override; // IPluginV2 Methods const char *getPluginType() const TRT_NOEXCEPT override; const char *getPluginVersion() const TRT_NOEXCEPT override; int getNbOutputs() const TRT_NOEXCEPT override; size_t getSerializationSize() const TRT_NOEXCEPT override; void serialize(void *buffer) const TRT_NOEXCEPT override; private: bool mAlignCorners; 
}; 
class DynamicTRTResizeCreator : public TRTPluginCreatorBase { public: DynamicTRTResizeCreator(); const char *getPluginName() const TRT_NOEXCEPT override; const char *getPluginVersion() const TRT_NOEXCEPT override; nvinfer1::IPluginV2 *createPlugin(const char *name, const nvinfer1::PluginFieldCollection *fc) TRT_NOEXCEPT override; nvinfer1::IPluginV2 *deserializePlugin(const char *name, const void *serialData, size_t serialLength) TRT_NOEXCEPT override; 
}; 
}  // namespace mmdeploy 
#endif  // TRT_DYNAMIC_RESIZE_HPP 

??在這樣一份頭文件中,DynamicTRTResize 類進行了如下的套娃繼承:
從上面的圖片和代碼中我們發現,在插件類 DynamicTRTResize 中,我們定義了私有變量 mAlignCorners,該變量表示是否 align corners。此外只要實現構造函數、析構函數和 TensoRT 中三個基類的方法即可。其中構造函數有二,分別用于創建插件和反序列化插件。而基類方法中:

  • 基類 IPluginV2DynamicExt 的方法較為值得關注,getOutputDimensions 獲取輸出張量的形狀,enqueue 真正負責執行我們的算法,內部一般會調用 CUDA 核函數。本文實現的插件直接調用 MMDeploy 已定義在 csrc/backend_ops/tensorrt/bicubic_interpolate 的核函數 bicubic_interpolate。
  • 基類 IPluginV2Ext 的方法,我們只要實現獲取輸出數據類型的 getOutputDataType 即可。
  • 基類 IPluginV2 則是些獲取插件類型和版本號的方法,此外則是序列化輸入插件的參數的函數 serialize 和計算該參數的序列化后 buffer 大小的函數 getSerializationSize,以及獲取輸出張量個數的方法 getNbOutputs。還有部分公共方法被定義在 TRTPluginBase 類內了。

??在插件工廠類 DynamicTRTResizeCreator 中,我們需要聲明獲取插件名稱和版本的方法 getPluginName 和 getPluginVersion。同時我們還需要聲明創建插件和反序列化插件的方法 createPlugin 和 deserializePlugin,前者調用 DynamicTRTResize 中創建插件的方法,后者調用反序列化插件的方法。
接下來,我們就實現上述聲明吧。在 .cpp 文件中我們實現的代碼如下:

// Copyright (c) OpenMMLab. All rights reserved 
#include "trt_dynamic_resize.hpp" 
#include <assert.h> 
#include <chrono> 
#include "trt_plugin_helper.hpp" 
#include "trt_serialize.hpp" 
// to get the reference to kernel function bicubic_interpolate,which will be used in enqueue 
#include "../bicubic_interpolate/trt_bicubic_interpolate_kernel.hpp" 
using namespace nvinfer1; 
namespace mmdeploy { 
namespace { 
static const char *PLUGIN_VERSION{"1"}; 
static const char *PLUGIN_NAME{"DynamicTRTResize"};//plagin name == ONNX node name,triggered in building engine 
}  // namespace 
DynamicTRTResize::DynamicTRTResize(const std::string &name, bool align_corners) : TRTPluginBase(name), mAlignCorners(align_corners) {} 
DynamicTRTResize::DynamicTRTResize(const std::string name, const void *data, size_t length) : TRTPluginBase(name) { deserialize_value(&data, &length, &mAlignCorners); 
} 
nvinfer1::IPluginV2DynamicExt *DynamicTRTResize::clone() const TRT_NOEXCEPT { DynamicTRTResize *plugin = new DynamicTRTResize(mLayerName, mAlignCorners); plugin->setPluginNamespace(getPluginNamespace()); return plugin; 
} 
nvinfer1::DimsExprs DynamicTRTResize::getOutputDimensions( int outputIndex, const nvinfer1::DimsExprs *inputs, int nbInputs, nvinfer1::IExprBuilder &exprBuilder) TRT_NOEXCEPT { nvinfer1::DimsExprs ret; ret.nbDims = 4; // input two tensors: input and size_tensor, the later is for shape inference only ret.d[0] = inputs[0].d[0]; ret.d[1] = inputs[0].d[1]; ret.d[2] = inputs[1].d[2]; ret.d[3] = inputs[1].d[3]; return ret; 
} 
bool DynamicTRTResize::supportsFormatCombination(int pos, const nvinfer1::PluginTensorDesc *ioDesc, int nbInputs, int nbOutputs) TRT_NOEXCEPT { if (pos == 0) { return (ioDesc[pos].type == nvinfer1::DataType::kFLOAT && ioDesc[pos].format == nvinfer1::TensorFormat::kLINEAR); } else { return ioDesc[pos].type == ioDesc[0].type && ioDesc[pos].format == ioDesc[0].format; } 
} 
void DynamicTRTResize::configurePlugin(const nvinfer1::DynamicPluginTensorDesc *inputs, int nbInputs, const nvinfer1::DynamicPluginTensorDesc *outputs, int nbOutputs) TRT_NOEXCEPT {} 
size_t DynamicTRTResize::getWorkspaceSize(const nvinfer1::PluginTensorDesc *inputs, int nbInputs, const nvinfer1::PluginTensorDesc *outputs, int nbOutputs) const TRT_NOEXCEPT { return 0; 
} 
int DynamicTRTResize::enqueue(const nvinfer1::PluginTensorDesc *inputDesc, const nvinfer1::PluginTensorDesc *outputDesc, const void *const *inputs, void *const *outputs, void *workSpace, cudaStream_t stream) TRT_NOEXCEPT { int batch = inputDesc[0].dims.d[0]; int channels = inputDesc[0].dims.d[1]; int height = inputDesc[0].dims.d[2]; int width = inputDesc[0].dims.d[3]; int height_out = outputDesc[0].dims.d[2]; int width_out = outputDesc[0].dims.d[3]; const void *x = inputs[0]; void *output = outputs[0]; // TODO: add fp16 support auto data_type = inputDesc[0].type; switch (data_type) { case nvinfer1::DataType::kFLOAT: bicubic_interpolate<float>((float *)x, (float *)output, batch, channels, height, width, height_out, width_out, mAlignCorners, stream); break; default: return 1; break; } return 0; 
} 
nvinfer1::DataType DynamicTRTResize::getOutputDataType(int index, const nvinfer1::DataType *inputTypes, int nbInputs) const TRT_NOEXCEPT { return inputTypes[0]; 
} 
// IPluginV2 Methods 
const char *DynamicTRTResize::getPluginType() const TRT_NOEXCEPT { return PLUGIN_NAME; } 
const char *DynamicTRTResize::getPluginVersion() const TRT_NOEXCEPT { return PLUGIN_VERSION; } 
int DynamicTRTResize::getNbOutputs() const TRT_NOEXCEPT { return 1; } 
size_t DynamicTRTResize::getSerializationSize() const TRT_NOEXCEPT { return serialized_size(mAlignCorners); 
} 
void DynamicTRTResize::serialize(void *buffer) const TRT_NOEXCEPT { serialize_value(&buffer, mAlignCorners); 
} 
////////////////////// creator ///////////////////////////// 
DynamicTRTResizeCreator::DynamicTRTResizeCreator() { mPluginAttributes.clear(); mPluginAttributes.emplace_back(nvinfer1::PluginField("align_corners")); mFC.nbFields = mPluginAttributes.size(); mFC.fields = mPluginAttributes.data(); 
} 
const char *DynamicTRTResizeCreator::getPluginName() const TRT_NOEXCEPT { return PLUGIN_NAME; } 
const char *DynamicTRTResizeCreator::getPluginVersion() const TRT_NOEXCEPT { return PLUGIN_VERSION; 
} 
nvinfer1::IPluginV2 *DynamicTRTResizeCreator::createPlugin( const char *name, const nvinfer1::PluginFieldCollection *fc) TRT_NOEXCEPT { nvinfer1::Dims size{2, {1, 1}}; bool align_corners = 1; for (int i = 0; i < fc->nbFields; i++) { if (fc->fields[i].data == nullptr) { continue; } std::string field_name(fc->fields[i].name); if (field_name.compare("align_corners") == 0) { align_corners = static_cast<const int *>(fc->fields[i].data)[0]; } } // create the instance of DynamicTRTResize DynamicTRTResize *plugin = new DynamicTRTResize(name, align_corners); plugin->setPluginNamespace(getPluginNamespace()); return plugin; 
} 
nvinfer1::IPluginV2 *DynamicTRTResizeCreator::deserializePlugin( const char *name, const void *serialData, size_t serialLength) TRT_NOEXCEPT { auto plugin = new DynamicTRTResize(name, serialData, serialLength); plugin->setPluginNamespace(getPluginNamespace()); return plugin; 
} 
REGISTER_TENSORRT_PLUGIN(DynamicTRTResizeCreator);//register the plugin 
}  // namespace mmdeploy 

??然后,我們就對 MMDeploy 重新 build 一次 TensorRT 的動態庫 build/lib/libmmdeploy_tensorrt_ops.so。一般編譯成功就表示已經注冊算子了,但是我們需要進行一些測試以保證結果正確。

4.測試

??我們用 TensorRT 的 python api 查看一下目前的插件列表:

import tensorrt as trt 
from mmdeploy.backend.tensorrt import load_tensorrt_plugin 
load_tensorrt_plugin() 
def get_plugin_names(): return [pc.name for pc in trt.get_plugin_registry().plugin_creator_list] 
print(get_plugin_names()) 

??可以發現 ‘DynamicTRTResize’ 在插件列表中。然后我們對這個插件進行功能測試,看推理結果是否和 PyTroch 結果一致,并且是否可以動態控制輸出尺寸。

from mmdeploy.backend.tensorrt import create_trt_engine, save_trt_engine 
engine = create_trt_engine( 'srcnn3.onnx', input_shapes=dict(input = dict( min_shape=[1, 3, 256, 256], opt_shape=[1, 3, 256, 256], max_shape=[1, 3, 256, 256]), factor = dict(min_shape = [1, 1, 256, 256], opt_shape = [1, 1, 512, 512], max_shape = [1, 1, 1024, 1024]))) 
save_trt_engine(engine, 'srcnn3.engine') 
from mmdeploy.backend.tensorrt import TRTWrapper 
trt_model = TRTWrapper('srcnn3.engine', ['output']) 
factor = torch.rand([1, 1, 768, 768], dtype=torch.float) 
trt_output = trt_model.forward(dict(input = x.cuda(), factor = factor.cuda())) 
torch_output = model.forward(x, factor) 
assert np.allclose(trt_output['output'].cpu().numpy(), torch_output.cpu().detach(), rtol = 1e-3, atol = 1e-5) 

??對比 TensorRT 的輸出結果和 PyTorch 的輸出結果是否一致,程序如果不報錯即可說明推理正確。此外,測試時我們使用和導出時不一樣的尺寸,結果也和 PyTorch 一致,說明可以支持動態的尺寸。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94443.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94443.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94443.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

YggJS RButton 按鈕組件 v1.0.0 使用教程

&#x1f4cb; 目錄 簡介核心特性快速開始安裝指南基礎使用主題系統高級功能API 參考最佳實踐性能優化故障排除總結 &#x1f680; 簡介 YggJS RButton 是一個專門為 React 應用程序設計的高性能按鈕組件庫。它提供了兩套完整的設計主題&#xff1a;科技風主題和極簡主題&…

Linux(二十)——SELinux 概述與狀態切換

文章目錄前言一、SELinux 概述1.1 SELinux 簡介1.2 SELinux 特點1.2.1 MAC&#xff08;Mandatory Access Control&#xff09;1.2.2 RBAC&#xff08;Role-Based Access Control&#xff09;1.2.3 TE&#xff08;Type Enforcement&#xff09;1.3 SELinux 的執行模式1.4 SELinu…

Linux學習-TCP網絡協議(補充)

一、TCP 頭部標志位 TCP 頭部包含多種標志位&#xff0c;用于控制連接建立、數據傳輸、連接斷開等過程&#xff0c;核心標志位及作用如下&#xff1a;標志位英文全稱作用SYNSynchronize Sequence Numbers請求建立連接&#xff0c;三次握手第一步發送 SYN 包ACKAcknowledgment響…

Go編寫的輕量文件監控器. 可以監控終端上指定文件夾內的變化, 阻止刪除,修改,新增操作. 可以用于AWD比賽或者終端應急響應

工具介紹 0RAYS-AWD-Filechecker一個用Golang編寫的, 輕量級的文件監控器, 會監控指定文件夾內文件刪除, 修改, 新增操作, 然后立刻告警并復原. 一開始是為AWD比賽寫的, 主要是為了防止靶機的web目錄被上馬. 但也可以用到藍隊等場景上. 由于使用的Linux的系統調用, 僅支持Linux…

【6】MySQL 數據庫基礎操作

MySQL 數據庫基礎操作數據庫操作查看數據庫創建數據庫刪除數據庫修改數據庫數據表操作創建表修改表刪除表數據庫操作 查看數據庫 查看有哪些數據庫&#xff1f; 示例&#xff1a; [rootlocalhost][(none)]> show databases; -------------------- | Database |…

Android 探索APP/應用啟動模式、Intent的Flag啟動標志位

寫在前面&#xff1a;Android APP有四種啟動模式——》標準模式(Standard)、棧頂復用模式(SingleTop)、棧內復用模式(SingleTask)、單例模式(SingleInstance)&#xff0c;默認就是標準模式。啟動模式決定了Activity在任務棧內的存在方式&#xff0c;影響了Back返回鍵Activity返…

Y9000P部署開源模型

環境信息&#xff1a; 設備&#xff1a;Y9000P GPU&#xff1a;RTX 3060 6G 系統版本&#xff1a;Ubuntu 24.04 一、下載模型 1、環境準備 1、安裝工具 apt-get -y install git-lfs git lfs install apt-get install python3 python-is-python3 pip3.12 config set global.inde…

大模型入門實戰 | 基于 YOLO 數據集微調 Qwen2.5-VL-3B-Instruct 的目標檢測任務

大模型入門實戰 | 基于 YOLO 數據集微調 Qwen2.5-VL-3B-Instruct 的目標檢測任務這篇就是新手向的“保姆級”實操文。你將把 YOLO 檢測數據 轉成 對話式 Grounding 數據&#xff0c;用 ms-swift 做 LoRA 微調&#xff0c;再用腳本 推理 可視化。 但值得注意的是&#xff0c;一…

基于Python+MySQL實現物聯網引論課程一個火警報警及應急處理系統

物聯網引論課程大作業設計報告一、選題、內容及功能說明我們大作業選擇的是題目三&#xff1a;一個火警報警及應急處理系統。主要需要實現四個功能&#xff1a;感知環境溫度&#xff0c;當環境溫度超過閾值&#xff0c;自動觸發報警&#xff1a;終端 led 以固定頻率閃爍&#x…

基于印染數據的可視化系統設計與實現

標題:基于印染數據的可視化系統設計與實現內容:1.摘要 隨著印染行業的快速發展&#xff0c;印染數據呈現爆發式增長。為了更好地管理和分析這些數據&#xff0c;提高印染生產的效率和質量&#xff0c;本研究旨在設計并實現一個基于印染數據的可視化系統。通過收集印染生產過程中…

實驗1 第一個微信小程序

實驗1 第一個微信小程序一、實驗目標二、實驗步驟1. 自動生成小程序2. 手動創建小程序三、程序運行結果四、問題總結與體會chunk的博客地址一、實驗目標 1、學習使用快速啟動模板創建小程序的方法&#xff1b; 2、學習不使用模板手動創建小程序的方法。 二、實驗步驟 1. 自…

(計算機網絡)JWT三部分及 Signature 作用

JWT&#xff08;JSON Web Token&#xff09;是一種用于 無狀態認證 的輕量級令牌&#xff0c;廣泛用于分布式系統、單頁應用&#xff08;SPA&#xff09;和移動端登錄。JWT 結構概覽JWT 由 三部分組成&#xff0c;用 . 分隔&#xff1a;xxxxx.yyyyy.zzzzz Header&#xff08;頭…

LangGraph

LangGraph 是由 LangChain 團隊開發的開源框架&#xff0c;專為構建??復雜、有狀態、多主體&#xff08;Multi-Agent&#xff09;的 LLM 應用??而設計。它通過??圖結構&#xff08;Graph&#xff09;?? 組織工作流&#xff0c;支持循環邏輯、動態分支、狀態持久化和人工…

STM32物聯網項目---ESP8266微信小程序結合OneNET平臺MQTT實現STM32單片機遠程智能控制---MQTT篇(三)

一、前言本篇文章通過發送AT指令&#xff0c;與云平臺建立通訊&#xff1a;1.創建云平臺2.燒錄AT固件3.MQTT訂閱&#xff08;本篇&#xff09;4.單片機代碼編寫5.微信小程序&#xff08;下載微信開發者工具即可使用&#xff09;二、AT指令集介紹AT指令是一種文本序列&#xff0…

Apache Ozone 2.0.0集群部署

單機部署參考&#xff1a;Apache Ozone 介紹與部署使用(最新版2.0.0)-CSDN博客 安裝部署 官方參考&#xff1a;Documentation for Apache Ozone 準備環境 環境準備參考&#xff1a;Linux環境下Hadoop3.4.0集群部署-CSDN博客 1->4-b 參考&#xff1a;Apache Ozone 介紹與部…

【計算機網絡 | 第9篇】信道的極限容量

文章目錄探秘信道的極限容量&#xff1a;從奈氏準則到香農定理一、信道極限容量的基本概念&#x1f914;二、奈氏準則&#xff1a;無噪聲情況下的碼元速率限制&#x1f426;?&#x1f525;&#xff08;一&#xff09;帶寬與信號傳輸的關系&#xff08;二&#xff09;碼間串擾問…

深入理解Linux iptables防火墻:從核心概念到實戰應用

一、概述&#xff1a;什么是iptables&#xff1f; 在Linux系統中&#xff0c;網絡安全防護的核心工具之一便是iptables。它絕非一個簡單的命令&#xff0c;而是一個功能強大的用戶態工具&#xff0c;與Linux內核中的netfilter框架協同工作&#xff0c;共同構建了Linux的防火墻體…

WebRTC音頻QoS方法一.1(NetEQ之音頻網絡延時DelayManager計算補充)

一、整體簡介 NetEQ計算的網絡延時&#xff0c;直接影響變速算法的決策。在變速算法里面啟動關鍵的作用。 網絡延時計算需要考慮兩種情況&#xff1a; 1、單純抖動的網絡延時計算&#xff0c;在UnderrunOptimizer類中實現&#xff1b; 2、在丟包亂序場景下的網絡延時計算。…

實時操作系統FreeRTOS移植到STM32VGT6

一、前言 下載平臺:STM32F407VGT6 代碼使用平臺:VSCode 編譯器:arm-none-aebi-gcc 程序下載工具:STlink 批處理工具:make 移植的FreeRTOS版本:V11.2.0 其實此方法并不局限在arm-none-aebi-gcc中&#xff0c;此方法對于Keil5也是可以使用的&#xff0c; 只不過復制的一些文件不同…

從線到機:AI 與多模態交互如何重塑 B 端與 App 界面設計

當下&#xff0c;界面設計已經不再是單純的“畫屏幕”。AI 的快速發展讓我們不得不重新審視&#xff1a;交互和視覺究竟會走向什么樣的未來&#xff1f;無論是移動端 App&#xff0c;還是復雜的 B 端產品&#xff0c;設計的核心都在于讓界面更懂用戶。本文嘗試從三個角度切入&a…