TensorRT模型量化实践

news2024/9/22 1:08:24

文章目录

    • 量化基本概念
    • 量化的方法
      • 方式1:trtexec(PTQ的一种)
      • 方式2:PTQ
        • 2.1 python onnx转trt
        • 2.2 polygraphy工具:应该是对2.1量化过程的封装
      • 方式3:QAT(追求精度时推荐)
    • 使用TensorRT量化实践(C++版)
    • 使用TensorRT量化(python版)
    • 参考文献

在这里插入图片描述

量化基本概念

后训练量化Post Training Quantization (PTQ)

量化过程仅仅通过离线推理一些sample数据对权重和激活值进行量化,无需要进行训练微调。

量化感知训练Quantization Aware Training (QAT)

在量化的过程中,对网络进行训练,从而让网络参数能更好地适应量化带来的信息损失。这种方式更加灵活,因此准确性普遍比后训练量化要高。缺点是操作起来不太方便。大多数情况下比训练后量化精度更高,部分场景不一定比部分/混合精度量化好很多。

量化的方法

方式1:trtexec(PTQ的一种)

(1)int8量化

trtexec --onnx=XX.onnx --saveEngine=model.plan --int8 --workspace=4096

如果使用int8量化;量化需要设置calib文件夹;

trtexec 
--onnx=model.onnx 
--minShapes=input:1x1x224x224  
--optShapes=input:2x1x224x224 
--maxShapes=input:10x1x224x224 
--workspace=4096 
--int8 
--best 
--calib=D:\images 
--saveEngine=model.engine 
--buildOnly  

精度损失很大,不建议直接采用。
trtexec 有提供 --calib=接口进行校正,但需要对中间特征进行cache文件保存,比较麻烦,官方文档也是采用上述方式进行int8量化;与fp16的模型在测试集上测试指标,可以看到精度下降非常严重;
(2)int8 fp16混合量化
trtexec --onnx=XX.onnx --saveEngine=model.plan --int8 --fp16 --workspace=4096
测试集上统计指标:相比纯int8量化,效果要好,但是相比fp16,精度下降依然非常严重

方式2:PTQ

engine序列化时执行

2.1 python onnx转trt

操作流程:
按照常规方案导出onnx,onnx序列化为tensorrt engine之前打开int8量化模式并采用校正数据集进行校正;
优点:
1.导出onnx之前的所有操作都为常规操作;2. 相比在pytorch中进行PTQ int8量化,所需显存小;
缺点:
1.量化过程为黑盒子,无法看到中间过程;
2.校正过程需在实际运行的tensorrt版本中进行并保存tensorrt engine;
3.量化过程中发现,即使模型为动态输入,校正数据集使用时也必须与推理时的输入shape[N, C, H, W]完全一致,否则,效果非常非常差,动态模型慎用。
操作示例参看onnx2trt_ptq.py

2.2 polygraphy工具:应该是对2.1量化过程的封装

操作流程:
按照常规方案导出onnx,onnx序列化为tensorrt engine之前打开int8量化模式并采用校正数据集进行校正;
优点: 1. 相较于1.1,代码量更少,只需完成校正数据的处理代码;
缺点: 1. 同上所有; 2. 动态尺寸时,校正数据需与–trt-opt-shapes相同;3.内部默认最多校正20个epoch;

安装polygraphy

pip install colored polygraphy --extra-index-url https://pypi.ngc.nvidia.com

量化

polygraphy convert XX.onnx --int8 --data-loader-script loader_data.py --calibration-cache XX.cache -o XX.pl

方式3:QAT(追求精度时推荐)

注:在pytorch中执行导出的onnx将产生一个明确量化的模型,属于显式量化
操作流程:
安装pytorch_quantization库->加载训练数据->加载模型(在加载模型之前,启用quant_modules.initialize() 以保证原始模型层替换为量化层)->训练->导出onnx;
优点:
1.模型量化参数重新训练,训练较好时,精度下降较少; 2. 通过导出的onnx能够看到每层量化的过程;2. onnx导出为tensort engine时可以采用trtexec(注:命令行需加–int8,需要fp16和int8混合精度时,再添加–fp16),比较简单;3.训练过程可在任意设备中进行;
缺点:
1.导出onnx时,显存占用非常大;2.最终精度取决于训练好坏;3. QAT训练shape需与推理shape一致才能获得好的推理结果;4. 导出onnx时需采用真实的图片输入作为输入设置
操作示例参看yolov5_pytorch_qat.py感知训练,参看export_onnx_qat.py

使用TensorRT量化实践(C++版)

该方式则是利用TensorRT的API将onnx转换engine文件的过程中进行量化,其中需要校准数据(准备一个存放几百张图像的文件夹即可)。为了读取校正图像,需要写一个Int8校正类,如下所示:
calibrator.h

#pragma once
#include <NvInfer.h>
#include<vector>
#include <opencv2/opencv.hpp>
class Calibrator : public nvinfer1::IInt8EntropyCalibrator2 {
public:
	Calibrator(int batchsize, int input_w, int input_h, std::string img_dir, const char* calib_table_name, bool read_cache = true);

	virtual ~Calibrator();
	int getBatchSize() const noexcept override;
	bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept override;
	const void* readCalibrationCache(size_t& length) noexcept override;
	void writeCalibrationCache(const void* cache, size_t length) noexcept override;

private:
	int BATCHSIZE;
	int WIDTH;
	int HEIGHT;
	int INDEX;
	std::string IMAGEDIR;
	std::vector<std::string> IMAGEFILES;
	size_t INPUTSIZE;
	std::string CALIBRATORTABLE;
	bool READCACHE;
	void* DEVICEINPUT;
	std::vector<char> CALIBRATORCACHE;

	cv::Mat preprocess_img(cv::Mat& img, int input_w, int input_h);
	void getFiles(std::string path, std::vector<std::string>& files);
};

calibrator.cpp

#include <fstream>
#include <io.h>
#include "calibrator.h"

cv::Mat Calibrator::preprocess_img(cv::Mat& img, int input_w, int input_h) {
    int w, h, x, y;
    float r_w = input_w / (img.cols * 1.0);
    float r_h = input_h / (img.rows * 1.0);
    if (r_h > r_w) {
        w = input_w;
        h = r_w * img.rows;
        x = 0;
        y = (input_h - h) / 2;
    }
    else {
        w = r_h * img.cols;
        h = input_h;
        x = (input_w - w) / 2;
        y = 0;
    }
    cv::Mat re(h, w, CV_8UC3);
    cv::resize(img, re, re.size(), 0, 0, cv::INTER_LINEAR);
    cv::Mat out(input_h, input_w, CV_8UC3, cv::Scalar(128, 128, 128));
    re.copyTo(out(cv::Rect(x, y, re.cols, re.rows)));
    return out;
}

void Calibrator::getFiles(std::string path, std::vector<std::string>& files){
    intptr_t Handle;
    struct _finddata_t FileInfo;
    std::string p;
    Handle = _findfirst(p.assign(path).append("\\*").c_str(), &FileInfo);

    while (_findnext(Handle, &FileInfo) == 0) {
        if (strcmp(FileInfo.name, ".") != 0 && strcmp(FileInfo.name, "..") != 0) {
            files.push_back(FileInfo.name);
        }
    }
}

Calibrator::Calibrator(int batchsize, int input_w, int input_h, std::string img_dir, const char* calib_table_name, bool read_cache){
    BATCHSIZE = batchsize;
    WIDTH = input_w;
    HEIGHT = input_h;
    INDEX = 0;
    IMAGEDIR = img_dir;
    CALIBRATORTABLE = calib_table_name;
    READCACHE = read_cache;
    INPUTSIZE = BATCHSIZE * 3 * WIDTH * HEIGHT;

    cudaMalloc(&DEVICEINPUT, INPUTSIZE * sizeof(float));
    getFiles(IMAGEDIR, IMAGEFILES);
}

Calibrator::~Calibrator() {
    cudaFree(DEVICEINPUT);
}

int Calibrator::getBatchSize() const noexcept {
    return BATCHSIZE;
}

bool Calibrator::getBatch(void* bindings[], const char* names[], int nbBindings) noexcept {
    if (INDEX + BATCHSIZE > (int)IMAGEFILES.size()) return false;

    std::vector<cv::Mat> input_imgs;
    for (int i = INDEX; i < INDEX + BATCHSIZE; i++) {
        cv::Mat temp = cv::imread(IMAGEDIR + IMAGEFILES[i]);
        if (temp.empty()) {
            std::cerr << "Image cannot open!" << std::endl;
            return false;
        }
        cv::Mat pr_img = preprocess_img(temp, WIDTH, HEIGHT);
        input_imgs.push_back(pr_img);
    }
    INDEX += BATCHSIZE;
    cv::Mat blob = cv::dnn::blobFromImages(input_imgs, 1.0 / 255.0, cv::Size(WIDTH, HEIGHT), cv::Scalar(0, 0, 0), true, false);

    cudaMemcpy(DEVICEINPUT, blob.ptr<float>(0), INPUTSIZE * sizeof(float), cudaMemcpyHostToDevice);
    bindings[0] = DEVICEINPUT;
    return true;
}

const void* Calibrator::readCalibrationCache(size_t& length) noexcept {
    std::cout << "reading calib cache: " << CALIBRATORTABLE << std::endl;
    CALIBRATORCACHE.clear();
    std::ifstream input(CALIBRATORTABLE, std::ios::binary);
    input >> std::noskipws;
    if (READCACHE && input.good()) {
        std::copy(std::istream_iterator<char>(input), std::istream_iterator<char>(), std::back_inserter(CALIBRATORCACHE));
    }
    length = CALIBRATORCACHE.size();
    return length ? CALIBRATORCACHE.data() : nullptr;
}

void Calibrator::writeCalibrationCache(const void* cache, size_t length) noexcept {
    std::cout << "writing calib cache: " << CALIBRATORTABLE << std::endl;
    std::ofstream output(CALIBRATORTABLE, std::ios::binary);
    output.write(reinterpret_cast<const char*>(cache), length);
}

最后,通过以下代码将onnx量化转换为engine文件。

#include <iostream>
#include <fstream>
#include "calibrator.h"
#include "NvInfer.h"
#include "NvOnnxParser.h"

// 实例化记录器界面,捕获所有警告性信息,但忽略信息性消息
class Logger : public nvinfer1::ILogger {
	void log(Severity severity, const char* msg) noexcept override {
		if (severity <= Severity::kWARNING) {
			std::cout << msg << std::endl;
		}
	}
}logger;

void ONNX2TensorRT(const char* ONNX_file, std::string& Engine_file, bool& FP16, bool& INT8, std::string& image_dir, const char*& calib_table) {
	std::cout << "Load ONNX file form: " << ONNX_file << "\nStart export..." << std::endl;
	// 1.创建构建器的实例
	nvinfer1::IBuilder* builder = nvinfer1::createInferBuilder(logger);

	// 2.创建网络定义
	uint32_t flag = 1U << static_cast<uint32_t>(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
	nvinfer1::INetworkDefinition* network = builder->createNetworkV2(flag);

	// 3.创建一个 ONNX 解析器来填充网络
	nvonnxparser::IParser* parser = nvonnxparser::createParser(*network, logger);
	
	// 4.读取模型文件并处理任何错误
	parser->parseFromFile(ONNX_file, static_cast<int32_t>(nvinfer1::ILogger::Severity::kWARNING));
	for (int32_t i = 0; i < parser->getNbErrors(); ++i)
		std::cout << parser->getError(i)->desc() << std::endl;

	// 5.创建构建配置,指定TensorRT如何优化模型
	nvinfer1::IBuilderConfig* config = builder->createBuilderConfig();
	
	// 如果是动态模型,则需要设置大小
	/*
	auto profile = builder->createOptimizationProfile();
    auto input_tensor = network->getInput(0);
    auto input_dims = input_tensor->getDimensions();
	// 配置最小允许batch
    input_dims.d[0] = 1;
    profile->setDimensions(input_tensor->getName(), nvinfer1::OptProfileSelector::kMIN, input_dims);
    profile->setDimensions(input_tensor->getName(), nvinfer1::OptProfileSelector::kOPT, input_dims);
    // 配置最大允许batch
    // if networkDims.d[i] != -1, then minDims.d[i] == optDims.d[i] == maxDims.d[i] == networkDims.d[i]
    input_dims.d[0] = maxBatchSize;
    profile->setDimensions(input_tensor->getName(), nvinfer1::OptProfileSelector::kMAX, input_dims);
    config->addOptimizationProfile(profile);
    */

	// 6.设置属性来控制 TensorRT 如何优化网络
	// 设置内存池的空间
	config->setMemoryPoolLimit(nvinfer1::MemoryPoolType::kWORKSPACE, 16 * (1 << 20));
	if (FP16) {
		// 判断硬件是否支持FP16
		if (!builder->platformHasFastFp16()) {
			std::cout << "不支持FP16量化!" << std::endl;
			system("pause");
			return;
		}
		config->setFlag(nvinfer1::BuilderFlag::kFP16);
	}
	else if (INT8) {
		if (!builder->platformHasFastInt8()) {
			std::cout << "不支持INT8量化!" << std::endl;
			system("pause");
			return;
		}
		config->setFlag(nvinfer1::BuilderFlag::kINT8);
		nvinfer1::IInt8EntropyCalibrator2* calibrator = new Calibrator(1, 640, 640, image_dir, calib_table);
		config->setInt8Calibrator(calibrator);
	}

	// 7.指定配置后,构建引擎
	nvinfer1::IHostMemory* serializeModel = builder->buildSerializedNetwork(*network, *config);

	// 8.保存TensorRT模型
	std::ofstream engine(Engine_file, std::ios::binary);
	engine.write(reinterpret_cast<const char*>(serializeModel->data()), serializeModel->size());

	// 9.序列化引擎包含权重的必要副本,因此不再需要解析器、网络定义、构建器配置和构建器,可以安全地删除
	delete parser;
	delete network;
	delete config;
	delete builder;

	// 10.将引擎保存到磁盘后 ,并且可以删除它被序列化到的缓冲区
	delete serializeModel;
	std::cout << "Export success, Save as: " << Engine_file << std::endl;
}

int main(int argc, char** argv) {
	// ONNX 文件路径
	const char* ONNX_file = "../weights/yolov8s.onnx";
	// ENGINE 文件保存路径
	std::string Engine_file = "../weights/yolov8s.engine";

	// 当量化为INT8时,图片路径
	std::string image_dir = "../images/";
	// 当量化为INT8时,校准表路径(存在读取,不存在创建)
	const char* calib_table = "../weights/calibrator.table";

	// 选择量化方式,若两个都为false,使用FP32生成 ENGINE文件
	bool FP16 = false;
	bool INT8 = true;

	std::ifstream file(ONNX_file, std::ios::binary);
	if (!file.good()) {
		std::cout << "Load ONNX file failed!" << std::endl;
	}

	ONNX2TensorRT(ONNX_file, Engine_file, FP16, INT8, image_dir, calib_table);

	return 0;
}

使用TensorRT量化(python版)

流程C++版本的一样,这个没进行测试,以下版本是别人量化yolov5的代码,感兴趣的朋友可以尝试一下。

import tensorrt as trt
import os
import numpy as np
import pycuda.driver as cuda
import pycuda.autoinit
import cv2


def get_crop_bbox(img, crop_size):
    """Randomly get a crop bounding box."""
    margin_h = max(img.shape[0] - crop_size[0], 0)
    margin_w = max(img.shape[1] - crop_size[1], 0)
    offset_h = np.random.randint(0, margin_h + 1)
    offset_w = np.random.randint(0, margin_w + 1)
    crop_y1, crop_y2 = offset_h, offset_h + crop_size[0]
    crop_x1, crop_x2 = offset_w, offset_w + crop_size[1]
    return crop_x1, crop_y1, crop_x2, crop_y2

def crop(img, crop_bbox):
    """Crop from ``img``""" 
    crop_x1, crop_y1, crop_x2, crop_y2 = crop_bbox
    img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...]
    return img

class yolov5EntropyCalibrator(trt.IInt8EntropyCalibrator2):
    def __init__(self, imgpath, batch_size, channel, inputsize=[384, 1280]):
        trt.IInt8EntropyCalibrator2.__init__(self)
        self.cache_file = 'yolov5.cache'
        self.batch_size = batch_size
        self.Channel = channel
        self.height = inputsize[0]
        self.width = inputsize[1]
        self.imgs = [os.path.join(imgpath, file) for file in os.listdir(imgpath) if file.endswith('jpg')]
        np.random.shuffle(self.imgs)
        self.imgs = self.imgs[:2000]
        self.batch_idx = 0
        self.max_batch_idx = len(self.imgs) // self.batch_size
        self.calibration_data = np.zeros((self.batch_size, 3, self.height, self.width), dtype=np.float32)
        # self.data_size = trt.volume([self.batch_size, self.Channel, self.height, self.width]) * trt.float32.itemsize
        self.data_size = self.calibration_data.nbytes
        self.device_input = cuda.mem_alloc(self.data_size)
        # self.device_input = cuda.mem_alloc(self.calibration_data.nbytes)

    def free(self):
        self.device_input.free()

    def get_batch_size(self):
        return self.batch_size

    def get_batch(self, names, p_str=None):
        try:
            batch_imgs = self.next_batch()
            if batch_imgs.size == 0 or batch_imgs.size != self.batch_size * self.Channel * self.height * self.width:
                return None
            cuda.memcpy_htod(self.device_input, batch_imgs)
            return [self.device_input]
        except:
            print('wrong')
            return None
    def next_batch(self):
        if self.batch_idx < self.max_batch_idx:
            batch_files = self.imgs[self.batch_idx * self.batch_size: \
                                    (self.batch_idx + 1) * self.batch_size]
            batch_imgs = np.zeros((self.batch_size, self.Channel, self.height, self.width),
                                  dtype=np.float32)
            for i, f in enumerate(batch_files):
                img = cv2.imread(f)  # BGR
                crop_size = [self.height, self.width]
                crop_bbox = get_crop_bbox(img, crop_size)
                # crop the image
                img = crop(img, crop_bbox)
                img = img.transpose((2, 0, 1))[::-1, :, :]  # BHWC to BCHW ,BGR to RGB
                img = np.ascontiguousarray(img)
                img = img.astype(np.float32) / 255.
                assert (img.nbytes == self.data_size / self.batch_size), 'not valid img!' + f
                batch_imgs[i] = img
            self.batch_idx += 1
            print("batch:[{}/{}]".format(self.batch_idx, self.max_batch_idx))
            return np.ascontiguousarray(batch_imgs)
        else:
            return np.array([])
    def read_calibration_cache(self):
        # If there is a cache, use it instead of calibrating again. Otherwise, implicitly return None.
        if os.path.exists(self.cache_file):
            with open(self.cache_file, "rb") as f:
                return f.read()

    def write_calibration_cache(self, cache):
        with open(self.cache_file, "wb") as f:
            f.write(cache)
            f.flush()
            # os.fsync(f)


def get_engine(onnx_file_path, engine_file_path, cali_img, mode='FP32', workspace_size=4096):
    """Attempts to load a serialized engine if available, otherwise builds a new TensorRT engine and saves it."""
    TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
    def build_engine():
        assert mode.lower() in ['fp32', 'fp16', 'int8'], "mode should be in ['fp32', 'fp16', 'int8']"
        explicit_batch_flag = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
        with trt.Builder(TRT_LOGGER) as builder, builder.create_network(
            explicit_batch_flag
        ) as network, builder.create_builder_config() as config, trt.OnnxParser(
            network, TRT_LOGGER
        ) as parser:
            with open(onnx_file_path, "rb") as model:
                print("Beginning ONNX file parsing")
                if not parser.parse(model.read()):
                    print("ERROR: Failed to parse the ONNX file.")
                    for error in range(parser.num_errors):
                        print(parser.get_error(error))
                    return None
            config.max_workspace_size = workspace_size * (1024 * 1024)  # workspace_sizeMiB
            # 构建精度
            if mode.lower() == 'fp16':
                config.flags |= 1 << int(trt.BuilderFlag.FP16)

            if mode.lower() == 'int8':
                print('trt.DataType.INT8')
                config.flags |= 1 << int(trt.BuilderFlag.INT8)
                config.flags |= 1 << int(trt.BuilderFlag.FP16)
                calibrator = yolov5EntropyCalibrator(cali_img, 26, 3, [384, 1280])
                # config.set_quantization_flag(trt.QuantizationFlag.CALIBRATE_BEFORE_FUSION)
                config.int8_calibrator = calibrator
            # if True:
            #     config.profiling_verbosity = trt.ProfilingVerbosity.DETAILED

            profile = builder.create_optimization_profile()
            profile.set_shape(network.get_input(0).name, min=(1, 3, 384, 1280), opt=(12, 3, 384, 1280), max=(26, 3, 384, 1280))
            config.add_optimization_profile(profile)
            # config.set_calibration_profile(profile)
            print("Completed parsing of ONNX file")
            print("Building an engine from file {}; this may take a while...".format(onnx_file_path))
            # plan = builder.build_serialized_network(network, config)
            # engine = runtime.deserialize_cuda_engine(plan)
            engine = builder.build_engine(network,config)
            print("Completed creating Engine")
            with open(engine_file_path, "wb") as f:
                # f.write(plan)
                f.write(engine.serialize())
            return engine
        
    if os.path.exists(engine_file_path):
        # If a serialized engine exists, use it instead of building an engine.
        print("Reading engine from file {}".format(engine_file_path))
        with open(engine_file_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime:
            return runtime.deserialize_cuda_engine(f.read())
    else:
        return build_engine()


def main(onnx_file_path, engine_file_path, cali_img_path, mode='FP32'):
    """Create a TensorRT engine for ONNX-based YOLOv3-608 and run inference."""

    # Try to load a previously generated YOLOv3-608 network graph in ONNX format:
    get_engine(onnx_file_path, engine_file_path, cali_img_path, mode)


if __name__ == "__main__":
    onnx_file_path = '/home/models/boatdetect_yolov5/last_nms_dynamic.onnx'
    engine_file_path = "/home/models/boatdetect_yolov5/last_nms_dynamic_onnx2trtptq.plan"
    cali_img_path = '/home/data/frontview/test'
    main(onnx_file_path, engine_file_path, cali_img_path, mode='int8')

参考文献

tensorrt官方int8量化方法汇总
深度学习模型量化基础
模型量化5:onnx模型的静态量化和动态量化
有用的 模型量化!ONNX转TensorRT(FP32, FP16, INT8)
TensorRT-Int8量化详解
TensorRT中的INT 8 优化
TensorRT——INT8推理
TensorRT模型,INT8量化Python实践教程

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

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

相关文章

算法训练营——day2数组部分例题

1 移除元素-力扣27&#xff08;简单&#xff09; 1.1 题目&#xff1a; 移除元素1 1.2 思路及解法 只能覆盖&#xff0c;不能删除 暴力遍历解法 class Solution {public int removeElement(int[] nums, int val) {int sizenums.length;for(int i0;i<size;i){if(nums[i]va…

鸿蒙状态管理

我们开发中构建的页面多为静态页面。如果希望构建一个动态的,有交互的界面,就需要引入‘状态’的概念 用户构建了一个UI模型,其中应用的运行时的状态是参数,当参数改变时,UI作为返回结果,也将进行对应的改变。状态变化带来UI的重新渲染。 自定义组件拥有变量所谓属性,…

数据类型和程序运算

1. 数据类型 1.1 static修饰的变量 本文所有内容在企业考核的笔试题出现频率很高&#xff0c;而且是易错题大家注意下&#xff01; 南友们在玩Java时有没发现&#xff0c;下面这样一个对象&#xff0c;我们即使没有给变量赋值&#xff0c;在创建它后这个变量依旧会有默认值。…

【个人笔记】Git

Tiltle: Github 使用 &#x1f4d6; 快速使用 音标&#xff1a;[ɡɪthʌb] 0 介绍 Github是一个面向开源与私有软件项目的 托管平台&#xff0c;Git源自其内部的版本库格式.2008年上线&#xff0c;18年被微软收购&#xff1b;有很多知名的开源项目&#xff1a;jQuery、pytho…

Android Framework(三)Activity启动流程

文章目录 大纲总体流程 第一阶段&#xff1a;点击图标启动应用流程概览SourceActivity端处理system_service处理启动请求参数的构建activityInfo的解析创建ActivityRecord 窗口层级树的处理获取Task--getOrCreateRootTaskActivityRecord挂载到Task--setNewTask移动Task到容器顶…

最新!yolov10+deepsort的目标跟踪实现

目录 yolov10介绍——实时端到端物体检测 概述 主要功能 型号 性能 方法 一致的双重任务分配&#xff0c;实现无 NMS 培训 效率-精度驱动的整体模型设计 提高效率 精度提升 实验和结果 比较 deepsort介绍&#xff1a; yolov10结合deepsort实现目标跟踪 效果展示…

Elasticsearch的部署和使用

首先对java来说,我们可用的有原生elasticsearch和经过spring二次封装的spring data elasticsearch. 后者自带了一些方法,类似于mybatisplus,可以直接使用,十分方便. 如果是spring项目都建议使用第二种,除非你要深度使用. 首先是服务器的部署.部署之前要知道版本,我在部署时遇到…

GoLang:Go语言开发环境的配置

Go语言 Go语言开发环境的配置 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/a…

一款管理苹果设备的软件iMazing3中文破解安装激活教程

iMazing3官方版是一款管理苹果设备的软件&#xff0c;是一款帮助用户管理 IOS 手机的PC端应用程序&#xff0c;能力远超 iTunes 提供的终极 iOS 设备管理器。在iMazing官方版上与苹果设备连接后&#xff0c;可以轻松传输文件&#xff0c;浏览保存信息等&#xff0c;功能比Itune…

NDK编译c/c++文件成so库

目录 背景 Android NDK下载及配置 工程准备 创建Android.mk文件 创建Application.mk文件 编译项目 总结 背景 做算法开发&#xff0c;有时需要将算法编程成so库给其他人调用&#xff0c;很多时候都是在Android平台上使用&#xff0c;这样就会使用到Android NDK进行编译&…

高职院校大数据分析与可视化微服务架构实训室解决方案

一、前言 随着信息技术的飞速发展&#xff0c;大数据已成为推动社会进步与产业升级的关键力量。为了培养适应未来市场需求的高素质技术技能型人才&#xff0c;高职院校纷纷加大对大数据分析与可视化技术的教学投入。唯众&#xff0c;作为国内领先的职业教育解决方案提供商&…

论文速读|大型语言模型作为通用模式机器

项目地址&#xff1a;Large Language Models as General Pattern Machines 本研究探讨了大型语言模型&#xff08;LLMs&#xff09;作为通用模式机器的潜力&#xff0c;特别是在机器人技术领域。研究发现&#xff0c;LLMs 能够在没有额外训练的情况下&#xff0c;通过上下文学…

这才是HR想看到的应届生简历

速创猫今天给大家分享的是应届毕业生简历优化案例&#xff0c;希望对大家求职有帮助。速创猫总结了以下七条简历制作干货&#xff0c;希望对大家有帮助&#xff1a; 简洁明了&#xff1a;简历不是自传&#xff0c;不需要长篇大论。保持每份简历在一页纸内&#xff0c;突出关键…

Linux(CentOS)同步服务器时间之~chrony

Chrony 是一款开源的网络时间协议&#xff08;NTP&#xff09;客户端和服务端软件&#xff0c;旨在提供高精度的时间同步功能。相较于传统的 NTP 实现如 ntpd&#xff0c;Chrony 提供了一些改进和优势&#xff0c;包括更快的同步速度、低延迟、低CPU占用和低内存消耗。以下是 C…

c++ 154 引用

#include<iostream> using namespace std; //引用作为函数参数不需要初始化 void myswap(int *a,int *b) {int c 0;c *a;*a *b;*b c; } void main03() {int a 10;//引用语法 Type & name var;int& b a;b 100;//相当于把a改成100&#xff1b;printf("…

素材无水印素材网站在哪下载?高清的无水印素材资源库分享

找高清无水印素材&#xff1f;蛙学网、Pixabay 等资源库全揭秘&#xff01; 创意十足的你&#xff0c;是不是常为网上素材的烦人水印而头疼&#xff1f;总在寻找高清、干净、无水印的素材资源&#xff0c;却无从下手&#xff1f;别急&#xff0c;今天就为大家推荐几个超实用的…

c++应用网络编程之八SOCKET探究

一、socket 在目前主流的网络通信中&#xff0c;SOCKET编程其实就是网络编程的代名词。在前面反复提到socket&#xff0c;那么socket到底是什么呢&#xff1f;英文的愿意是“插座、槽”的意思。这里虽然不讲解传统的网络协议但不得不简单说明一下。 首先从宏观上看&#xff0c…

《数字信号处理》学习01-离散时间信号与序列的卷积和运算

目录 一&#xff0c;信号 二&#xff0c;序列的运算 1&#xff0c;卷积和 2&#xff0c;matlab实现 相关的电子书籍请到这篇文章所在的专栏&#xff0c;并通过夸克网盘链接下载。 很多简单的知识点我就不再赘述了&#xff0c;接下来就着重记录我学习过程中遇到的较难理…

class 3: vue.js 3 计算属性

计算属性是一种Options API&#xff0c;Options API是一种通过对象定义属性、方法等框架API的方式我们知道&#xff0c;在模板中可以直接通过插值语法显示一些data属性中的数据。但是在某些情况下&#xff0c;可能需要对数据进行一些转化操作之后再显示&#xff0c;或者需要将多…

PostgreSQL技术内幕5:PostgreSQL存储引擎从磁盘到内存的读取

文章目录 0.简介1.背景知识1.1 计算机存储结构1.2 数据库常见的磁盘和内存访问形式 2. 整体获取层次3.元组介绍4. Buffer管理4.1 Buffer组成4.2 修改后落盘4.3 获取buffer页的流程 5.存储管理器&#xff08;SMGR)6.磁盘管理器&#xff08;MD)7.虚拟文件管理器&#xff08;VFD)8…