Arcface部署应用实战

1、概述

人脸识别的一个比较常用的网络arcface,依赖于其特殊设计的loss函数,使得模型在训练的时候能够实现类间距离增大,类内的距离不断减小,最终使得所训练的backbone能够获取鉴别性很高的特征,便于人脸识别。

本文以专家博主Bubbliiiing所提供的仓库代码实现的arcface为基础,主要分享实现arcface模型动态batch推理的python和C++代码实现,至于训练和测试,可以参考仓库使用说明。仓库地址为:https://github.com/bubbliiiing/arcface-pytorch

2、cuda版本python快速推理

本节主要包含使用python实现arcface模型转化为onnx通用平台模型,然后转化为TensorRT所支持的加速engine全过程。

2.1 arcface模型转化为ONNX模型

深度学习训练的模型转化为ONNX都是通用的代码使用方法,利用torch.onnx.export函数进行相应的导出,博主这里考虑在测试时会同时计算多张图片的特征图,因此将batch设置为动态,以方便后续使用。

代码实现如下:

def export_onnx(model, img, onnx_path, opset, dynamic=False, simplify=True, batch_size=1):torch.onnx.export(model, img, onnx_path, verbose=True, opset_version=opset,export_params=True,do_constant_folding=True,input_names=['images'],output_names=['output'],dynamic_axes={'images': {0: 'batch_size'},  # shape(1,3,112,112)'output': {0: 'batch_size'}  # shape(1,128)} if dynamic else None)# Checksmodel_onnx = onnx.load(onnx_path)  # load onnx modelonnx.checker.check_model(model_onnx)  # check onnx modelif simplify:try:model_onnx, check = onnxsim.simplify(model_onnx,dynamic_input_shape=dynamic,test_input_shapes={'images': list(img.shape)} if dynamic else None)assert check, 'assert check failed'onnx.save(model_onnx, onnx_path)print('simplify onnx success')except Exception as e:print('simplify onnx failure')

具体的使用方案如下所示:

def convert2onnx_demo():model_path = './model_data/arcface_mobilefacenet.pth'device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')print('Loading weights into state dict...')net = arcface(backbone='mobilefacenet', mode="predict").eval()net.load_state_dict(torch.load(model_path, map_location=device), strict=True)net = net.to(device)batch_size = 4print('{} model loaded.'.format(model_path))dummy_input = torch.randn(batch_size, 3, 112, 112).to(device)onnx_path = './model_data/arcface_mobilefacenet.onnx'opset = 10export_onnx(net, dummy_input, onnx_path, opset, dynamic=True, simplify=True)ort_session = ort.InferenceSession(onnx_path)outputs = ort_session.run(None, {'images': np.random.randn(batch_size, 3, 112, 112).astype(np.float32)})print(outputs[0])

导出的onnx模型的输入输出如下所示:可以清楚的看到输入维度batch是动态变换的。
在这里插入图片描述

2.2 arcface ONNX模型转化为engine

onnx转换为engine方式有很多,可以借助trtexec.exe进行转换,也可以使用python编写相应的代码进行使用,博主采用python编写相应的脚本进行转换,具体代码转化如下:

def onnx2engine():import tensorrt as trtdef export_engine(onnx_path, engine_path, half, workspace=4, verbose=False):print('{} starting export with TensorRT {}...'.format(onnx_path, trt.__version__))# assert img.device.type != 'cpu', 'export running on CPU but must be on GPU, i.e. `python export.py --device 0`'if not os.path.exists(onnx_path):print(f'failed to export ONNX file: {onnx_path}')logger = trt.Logger(trt.Logger.INFO)if verbose:logger.min_severity = trt.Logger.Severity.VERBOSEbuilder = trt.Builder(logger)config = builder.create_builder_config()config.max_workspace_size = workspace * 1 << 30flag = (1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))network = builder.create_network(flag)parser = trt.OnnxParser(network, logger)if not parser.parse_from_file(str(onnx_path)):raise RuntimeError(f'failed to load ONNX file: {onnx_path}')## 支持动态batch使用必须profile = builder.create_optimization_profile()profile.set_shape("images", (1, 3, 112, 112), (8, 3, 112, 112), (16, 3, 112, 112))config.add_optimization_profile(profile)inputs = [network.get_input(i) for i in range(network.num_inputs)]outputs = [network.get_output(i) for i in range(network.num_outputs)]print('Network Description:')for inp in inputs:print('input {} with shape {} and dtype is {}\n'.format(inp.name, inp.shape, inp.dtype))for out in outputs:print('output {} with shape {} and dtype is {}\n'.format(out.name, out.shape, out.dtype))half &= builder.platform_has_fast_fp16print('building FP{} engine in {}'.format(16 if half else 32, engine_path))if half:config.set_flag(trt.BuilderFlag.FP16)with builder.build_engine(network, config) as engine, open(engine_path, 'wb') as t:t.write(engine.serialize())print("max_batch_szie = {}".format(builder.max_batch_size))print("flag= {}".format(flag))print('export success, saved as {f} ({file_size(f):.1f} MB)')onnx_path = './model_data/arcface_mobilefacenet.onnx'engine_path = './model_data/arcface_mobilefacenet.engine'half = Trueverbose = Trueexport_engine(onnx_path, engine_path, half, verbose=verbose)

其中需要重点注意的是,要使用动态batch完成engine的构建,必须要对profile 进行相应的设定,使得engine知道最小推理batch、最优推理batch以及最大推理batch。

2.3 arcface TensorRT快速推理

推理的代码还是相对比较简单的,简单总结如下:

  • 图像组合成相应的推理batch
  • 初始化TensorRT所需要的context
  • 给动态输入的batch和输出结果分配host以及device上的内存空间
  • 调用接口完成推理
  • 相关后处理代码

本文具体代码如下所示:

"""
An example that uses TensorRT's Python api to make inferences.
"""
import ctypes
import os
import shutil
import random
import sys
import threading
import time
import cv2
import numpy as np
import pycuda.autoinit
import pycuda.driver as cuda
import tensorrt as trtLEN_ALL_RESULT = 128def get_img_path_batches(batch_size, img_dir):ret = []batch = []for root, dirs, files in os.walk(img_dir):for name in files:suffix = os.path.splitext(name)[-1]if suffix in ['.jpg', '.png', '.JPG', '.jpeg']:if len(batch) == batch_size:ret.append(batch)batch = []batch.append(os.path.join(root, name))if len(batch) > 0:ret.append(batch)return retclass ArcFaceTRT(object):"""description: A arcface class that warps TensorRT ops, preprocess and postprocess ops."""def __init__(self, engine_file_path, batch_size=1):self.ctx = cuda.Device(0).make_context()stream = cuda.Stream()TRT_LOGGER = trt.Logger(trt.Logger.INFO)runtime = trt.Runtime(TRT_LOGGER)with open(engine_file_path, 'rb') as f:engine = runtime.deserialize_cuda_engine(f.read())context = engine.create_execution_context()context.set_binding_shape(0, (batch_size, 3, 112, 112))  # 这句非常重要!!!定义batch为动态维度#host_inputs = []cuda_inputs = []host_outputs = []cuda_outputs = []bindings = []for binding in engine:print('binding: ', binding, engine.get_tensor_shape(binding))## 动态 batchdims = engine.get_tensor_shape(binding)if dims[0] == -1:dims[0] = batch_size# size = trt.volume(engine.get_tensor_shape(binding)) * engine.max_batch_sizesize = trt.volume(dims) * engine.max_batch_sizedtype = trt.nptype(engine.get_tensor_dtype(binding))# allocate host and device buffershost_mem = cuda.pagelocked_empty(size, dtype)cuda_mem = cuda.mem_alloc(host_mem.nbytes)# append the device buffer to device bindingsbindings.append(int(cuda_mem))# append to the appropriate listif engine.get_tensor_mode(binding).value == 1: ##  0 是NONE 1是INPUT 2是OUTPUT# if engine.binding_is_input(binding):self.input_w = engine.get_tensor_shape(binding)[-1]self.input_h = engine.get_tensor_shape(binding)[-2]host_inputs.append(host_mem)cuda_inputs.append(cuda_mem)else:host_outputs.append(host_mem)cuda_outputs.append(cuda_mem)# storeself.stream = streamself.context = contextself.engine = engineself.host_inputs = host_inputsself.cuda_inputs = cuda_inputsself.host_outputs = host_outputsself.cuda_outputs = cuda_outputsself.bindings = bindingsself.batch_size = batch_size# self.batch_size = engine.max_batch_sizedef infer(self, raw_image_generator):threading.Thread.__init__(self)# Make self the active context, pushing it on top of the context stack.self.ctx.push()# Restorestream = self.streamcontext = self.contextengine = self.enginehost_inputs = self.host_inputscuda_inputs = self.cuda_inputshost_outputs = self.host_outputscuda_outputs = self.cuda_outputsbindings = self.bindings# Do image preprocessbatch_image_raw = []batch_origin_h = []batch_origin_w = []batch_input_image = np.empty(shape=[self.batch_size, 3, self.input_h, self.input_w])# 组合为相应的batch进行处理for i, image_raw in enumerate(raw_image_generator):input_image, image_raw, origin_h, origin_w = self.preprocess_image(image_raw)batch_image_raw.append(image_raw)batch_origin_h.append(origin_h)batch_origin_w.append(origin_w)np.copyto(batch_input_image[i], input_image)batch_input_image = np.ascontiguousarray(batch_input_image)# Copy input image to host buffernp.copyto(host_inputs[0], batch_input_image.ravel())start = time.time()# Transfer input data  to the GPU.cuda.memcpy_htod_async(cuda_inputs[0], host_inputs[0], stream)# Run inference.context.execute_async_v2(bindings=bindings, stream_handle=stream.handle)# context.execute_async(batch_size=self.batch_size, bindings=bindings, stream_handle=stream.handle)# Transfer predictions back from the GPU.cuda.memcpy_dtoh_async(host_outputs[0], cuda_outputs[0], stream)# Synchronize the streamstream.synchronize()end = time.time()# Remove any context from the top of the context stack, deactivating it.self.ctx.pop()# Here we use the first row of output in that batch_size = 1output = host_outputs[0]# Do postprocessfeatures = []for i in range(self.batch_size):feature = output[i * LEN_ALL_RESULT:(i + 1) * LEN_ALL_RESULT]features.append(feature)print(feature.shape)return batch_image_raw, end - start, featuresdef destroy(self):# Remove any context from the top of the context stack, deactivating it.self.ctx.pop()def get_raw_image(self, image_path_batch):"""description: Read an image from image path"""for img_path in image_path_batch:yield cv2.imread(img_path)def get_vedio_frame(self, cap):for _ in range(self.batch_size):yield cap.read()def get_raw_image_zeros(self, image_path_batch=None):"""description: Ready data for warmup"""for _ in range(self.batch_size):yield np.zeros([self.input_h, self.input_w, 3], dtype=np.uint8)def preprocess_image(self, raw_bgr_image):"""description: Convert BGR image to RGB,resize and pad it to target size, normalize to [0,1],transform to NCHW format.param:input_image_path: str, image pathreturn:image:  the processed imageimage_raw: the original imageh: original heightw: original width"""image_raw = raw_bgr_imageh, w, c = image_raw.shapeimage = cv2.cvtColor(image_raw, cv2.COLOR_BGR2RGB)image = cv2.resize(image, (112, 112))image = image.astype(np.float32)image /= 255.0# HWC to CHW format:image = np.transpose(image, [2, 0, 1])# CHW to NCHW formatimage = np.expand_dims(image, axis=0)# Convert the image to row-major order, also known as "C order":image = np.ascontiguousarray(image)return image, image_raw, h, wdef img_infer(ArcFaceWraper, image_path_batch):batch_image_raw, use_time, res = ArcFaceWraper.infer(ArcFaceWraper.get_raw_image(image_path_batch))for i, feature in enumerate(res):print('input->{}, time->{:.2f}ms, feature shape = {}'.format(image_path_batch[i], use_time * 1000, feature.shape))def vedio_infer(ArcFaceWraper, cap):batch_image_raw, use_time = ArcFaceWraper.infer(ArcFaceWraper.get_vedio_frame(cap))print('input->{}, time->{:.2f}ms, saving into output/'.format(1, use_time * 1000))cv2.namedWindow('vedio', cv2.WINDOW_NORMAL)cv2.imshow('vedio', batch_image_raw[0])cv2.waitKey(1)def warmup(ArcFaceWraper):batch_image_raw, use_time, _ = ArcFaceWraper.infer(ArcFaceWraper.get_raw_image_zeros())print('warm_up->{}, time->{:.2f}ms'.format(batch_image_raw[0].shape, use_time * 1000))if __name__ == '__main__':from tqdm import tqdmbatch = 4# engine_file_path = r"D:\personal\project\code\arcface-pytorch-main\model_data\arcface_mobilefacenet.engine"engine_file_path = r"arcface\arcface_mobilefacenet.engine"arcface_wrapper = ArcFaceTRT(engine_file_path, batch_size=batch)try:print('batch size is', batch)image_dir = r"datasets"# image_path_batches = get_img_path_batches(arcface_wrapper.batch_size, image_dir)image_path_batches = get_img_path_batches(batch, image_dir)# warmup# for i in range(10):#     warmup(arcface_wrapper)for batch in tqdm(image_path_batches):img_infer(arcface_wrapper, batch)finally:# destroy the instancearcface_wrapper.destroy()

重点需要注意的是:
1、context需要batch动态维度,这也是进行动态batch推理的前提条件
2、针对输入的binding,需要将其dim[0]设置batch大小

2.4 测试效果

博主这边使用的是batch为4进行的推理,推理速度大概是6.98ms每个batch。
在这里插入图片描述
如果设置为batch为8,则推理时间如下:
在这里插入图片描述

也可以设置为1~16之间的任意数值进行推理,具体数值区间和你生成engine时候所设定的kmin和kmax对应的shape是相关联的,比起单张推理速度会提升一部分,但是不成相应比例。

3、cuda版本C++快速推理

本节主要包含使用C++实现arcface模型转化为onnx通用平台模型,然后转化为TensorRT所支持的加速engine全过程。

由于模型的.pth模型转换为ONNX模型和前述2.1节的内容是一致的,因此本部分从模型已经成功转换为ONNX模型开始阐述。

3.1 arcface ONNX模型转化为engine

转换为engine的方式其实和python的转化相类似,主要还都是利用C++的TensorRT相应的API进行编写的。
具体实现如下所示:

void CreateEngine::trtFromOnnx(const std::string onnx_path,const std::string engine_out_path, unsigned int max_batch_size,size_t workspace ,bool half)
{if (onnx_path.empty()) {printf("failed to export ONNX file\n");}printf("***************start to create model engine********************\n");IBuilder* builder = createInferBuilder(gLogger);IBuilderConfig* config = builder->createBuilderConfig();config->setMaxWorkspaceSize(static_cast<size_t>(workspace*1) << 30);NetworkDefinitionCreationFlags flag = (1U << int(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH));INetworkDefinition* network = builder->createNetworkV2(flag);IParser* parser = createParser(*network, gLogger);if (! parser->parseFromFile(onnx_path.c_str(), static_cast<int>(ILogger::Severity::kWARNING))) {//wrong information for (int32_t i = 0; i < parser->getNbErrors(); i++){std::cout << parser->getError(i)->desc() << std::endl;}}std::cout << "******************successfully parse the onnx model*********************" << std::endl;//danamic batch auto profile = builder->createOptimizationProfile();auto input_tensor = network->getInput(0);auto input_dims = input_tensor->getDimensions();// 配置最小:kMIN、最优:kOPT、最大范围:kMAX       指的是BatchSizeinput_dims.d[0] = 1;profile->setDimensions(input_tensor->getName(), OptProfileSelector::kMIN, input_dims);profile->setDimensions(input_tensor->getName(), OptProfileSelector::kOPT, input_dims);input_dims.d[0] = max_batch_size;profile->setDimensions(input_tensor->getName(), OptProfileSelector::kMAX, input_dims);//TensorRT – Using PreviewFeaturekFASTER_DYNAMIC_SHAPES_0805 can help improve performance and resolve potential functional issuesconfig->setPreviewFeature(PreviewFeature::kFASTER_DYNAMIC_SHAPES_0805, true);config->addOptimizationProfile(profile);//build enginehalf &= builder->platformHasFastFp16();if (half){config->setFlag(nvinfer1::BuilderFlag::kFP16);}ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);assert(engine != nullptr);IHostMemory* serialized_engine = engine->serialize();assert(serialized_engine != nullptr);// save enginestd::ofstream p(engine_out_path, std::ios::binary);if (!p) {std::cerr << "could not open output engine path" << std::endl;assert(false);}p.write(reinterpret_cast<const char*>(serialized_engine->data()), serialized_engine->size());//releasenetwork->destroy();parser->destroy();engine->destroy();config->destroy();serialized_engine->destroy();builder->destroy();std::cout << "**************successed transfer onnx to trt engine***************" << std::endl;
}

实现和python一个样,主要是添加profile以及指定动态batch的最大、最小和最优值。由于kFASTER_DYNAMIC_SHAPES_0805可以提升性能,此处使用的时候对其进行了添加。

3.2 arcface TensorRT快速推理

推理大致过程如下:

  • 组装图片为相应的batch,并进行数据前处理
  • 分别分配host以及device上的内存
  • cuda加速推理获取结果
  • 结果解码+后处理计算相似度
  • 释放相应的内存
void ArcFaceInference::inference(std::vector<cv::Mat>& imgs, std::vector<cv::Mat>& res_batch)
{int batch = imgs.size();cudaStream_t stream;CUDA_CHECK(cudaStreamCreate(&stream));//inputint input_numel = batch * 3 * imgs[0].cols * imgs[0].rows;float* cpu_input_buffer = nullptr;float* gpu_input_buffer = nullptr;CUDA_CHECK(cudaMallocHost((void**)(&cpu_input_buffer), input_numel * sizeof(float)));CUDA_CHECK(cudaMalloc((void**)(&gpu_input_buffer), input_numel * sizeof(float)));//outputauto output_dims = this->initEngine->engine->getBindingDimensions(1);output_dims.d[0] = batch;int output_numel = output_dims.d[0] * output_dims.d[1];float* cpu_output_buffer = nullptr;float* gpu_output_buffer = nullptr;CUDA_CHECK(cudaMallocHost((void**)(&cpu_output_buffer), output_numel * sizeof(float)));CUDA_CHECK(cudaMalloc((void**)(&gpu_output_buffer), output_numel * sizeof(float)));// set input dimauto input_dims = this->initEngine->engine->getBindingDimensions(0);input_dims.d[0] = batch;this->initEngine->context->setBindingDimensions(0, input_dims);//batch processbatchPreprocess(imgs,cpu_input_buffer);auto start = std::chrono::system_clock::now();std::cout << "************start to inference batch imgs********************" << std::endl;CUDA_CHECK(cudaMemcpyAsync(gpu_input_buffer, cpu_input_buffer, input_numel * sizeof(float), cudaMemcpyHostToDevice, stream));float* bindings[] = { gpu_input_buffer,gpu_output_buffer };bool success = this->initEngine->context->enqueueV2((void**)(bindings), stream, nullptr);CUDA_CHECK(cudaMemcpyAsync(cpu_output_buffer, gpu_output_buffer, output_numel * sizeof(float), cudaMemcpyDeviceToHost, stream));CUDA_CHECK(cudaStreamSynchronize(stream));auto end = std::chrono::system_clock::now();std::cout << "*************batch inference time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" <<"*********************" << std::endl;//postprocessfor (int  i = 0; i < batch; i++){cv::Mat result = cv::Mat(1, output_dims.d[1], CV_32FC1, static_cast<float*>(&cpu_output_buffer[i * output_dims.d[1]]));res_batch.emplace_back(result.clone());// 计算相似度float max_similarity = 0.0;std::string name = "";for (std::map<std::string, cv::Mat>::iterator iter = obsInfo.begin(); iter != obsInfo.end(); ++iter){float similarity = getSimilarity(result.clone(), iter->second);if (similarity>max_similarity){max_similarity = similarity;name = iter->first;}}if (!GETVECTOR){printf("第%i张图与%s的相似都最高,相似度为:%f\n", i + 1, name.c_str(), max_similarity);}}//releaseCUDA_CHECK(cudaStreamDestroy(stream));CUDA_CHECK(cudaFreeHost(cpu_input_buffer));CUDA_CHECK(cudaFreeHost(cpu_output_buffer));CUDA_CHECK(cudaFree(gpu_input_buffer));CUDA_CHECK(cudaFree(gpu_output_buffer));}

3.3 测试效果

测试以人脸检测中的五个类别数据,先分一部分数据,提取其相对应的平均特征并进行保存,测试推理时候,用推理获取的特征与既有的特征进行相似度比对,取相似度最高者为对应的类别。

具体结果如下:
在这里插入图片描述

4 总结

arcface部署实际上还是比较简单的,难点在于如何快速实现多batch推理,以及如何能够更有效的让新来的特征加入table中,以达到动态扩充的目的。
本篇文章通过实际使用案例,详述了Arcface动态batch推理的详细过程部署的,如有不足指出,请大家多多指教。

———END————

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win11搭建 Elasticsearch 7 集群(一)

一&#xff1a; ES与JDK版本匹配一览表 elasticsearch从7.0开始默认安装了java运行环境&#xff0c;以便在没有安装java运行环境的机器上运行。如果配置了环境变量JAVA_HOME&#xff0c;则elasticsearh启动时会使用JAVA_HOME作为java路径&#xff0c;否则使用elasticsearch根目…

设计模式—简单工厂

目录 一、前言 二、简单工厂模式 1、计算器例子 2、优化后版本 3、结合面向对象进行优化&#xff08;封装&#xff09; 3.1、Operation运算类 3.2、客户端 4、利用面向对象三大特性&#xff08;继承和多态&#xff09; 4.1、Operation类 4.2、加法类 4.3、减法类 4…

【Unity】URP屏幕后处理UI模糊效果实现

这里Canvas(1)设置为Overlay能渲染出指定UI高清&#xff0c;其他UI模糊&#xff0c;然而这做法非常不好&#xff0c;如果此时再打开UI 以及 关闭模糊效果 要将这些置顶UI 恢复到原本Canvas里&#xff0c;也就是要管理2套Canvas using System; using System.Collections; using…

统一使用某一个包管理工具,比如yarn pnpm

原因&#xff1a;前端每个人的习性不一样&#xff0c;有人用npm 有人用yarn等包管理工具&#xff0c;混合下载插件容易出bug&#xff0c;就用个小工具锁住就行了&#xff0c;只能使用yarn或者pnpm反向下载依赖和下载插件。不然就报错 1.在项目主目录下创建preinstall.js // 如…

分类预测 | MATLAB实现GRNN广义回归神经网络多特征分类预测

分类预测 | MATLAB实现GRNN广义回归神经网络多特征分类预测 目录 分类预测 | MATLAB实现GRNN广义回归神经网络多特征分类预测分类效果基本介绍模型描述预测过程程序设计参考资料分类效果 基本介绍 MATLAB实现GRNN广义回归神经网络多特

Mybatis学习|多对一、一对多

有多个学生&#xff0c;没个学生都对应&#xff08;关联&#xff09;了一个老师&#xff0c;这叫&#xff08;多对一&#xff09; 对于每个老师而言&#xff0c;每个老师都有N个学生&#xff08;学生集合&#xff09;&#xff0c;这叫&#xff08;一对多&#xff09; 测试环境…

【小沐学Unity3d】3ds Max 骨骼动画制作(Physique 修改器)

文章目录 1、简介2、Physique 工作流程3、Physique 对象类型4、Physique 增加骨骼5、Physique 应用和初始化6、Physique 顶点子对象7、Physique 封套子对象8、设置关键点和自动关键点模式的区别8.1 自动关键点8.2 设置关键点 结语 1、简介 官方网址&#xff1a; https://help.…

Python Opencv实践 - 轮廓检测

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/map.jpg") print(img.shape) plt.imshow(img[:,:,::-1])#Canny边缘检测 edges cv.Canny(img, 127, 255, 0) plt.imshow(edges, cmapplt.cm.gray)#查找轮廓 #c…

AI助乡行——点燃乡村振兴新引擎

随着数字化浪潮的袭来&#xff0c;乡村振兴战略的推进离不开数字化、智慧化等现代化治理能力和方式&#xff0c;人工智能等高新技术正不断与农村经济、社会、治理等加速融合。在智慧农业的背景下&#xff0c;我们可以解决一系列困扰农民的问题&#xff0c;包括如何增加经济作物…

【jvm】运行时数据区

目录 一、运行时数据区一、作用二、说明三、线程共用与私有区域 一、运行时数据区 一、作用 1.内存是非常重要的系统资源&#xff0c;是硬盘和CPU 的中间仓库及桥梁&#xff0c;承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策…

基于Gin框架的HTTP接口限速实践

在当今的微服务架构和RESTful API主导的时代&#xff0c;HTTP接口在各个业务模块之间扮演着重要的角色。随着业务规模的不断扩大&#xff0c;接口的访问频率和负载也随之增加。为了确保系统的稳定性和性能&#xff0c;接口限速成了一个重要的话题。 1 接口限速的使用场景 接口…

GA遗传算法

储备知识 GA算法主要解决数学模型中最优化的搜索算法&#xff0c;是进化算法中的一种&#xff0c;基因算法借鉴了自然界基因的遗传的主要现象&#xff0c;分别为遗传&#xff0c;变异&#xff0c;自然选择&#xff0c;杂交等。 GA算法参数 GA算法的参数如下所示。 种群规模…

通义千问部署搭建

文章目录 一、部署11.1 打开通义千问-7B-预训练-模型库-选择资源1.2 使用Netbook2.1 运行2.2 复制脚本2.2.1 问题1 &#xff1a;ImportError: This modeling file requires the following packages that were not found in your environment: transformers_stream_generator. R…

【请求报错:javax.net.ssl.SSLHandshakeException: No appropriate protocol】

1、问题描述 在请求服务时报错说SSL握手异常协议禁用啥的&#xff0c;而且我的连接数据库的url也加了useSSLfalse javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)2、解决方法 在网上查找了方法…

【C++笔记】C++内存管理

【C笔记】C内存管理 一、C中动态内存申请的方式二、new和delete的实现原理2.1、operator new和operator delete函数 一、C中动态内存申请的方式 在C语言中我们需要动态申请空间的时候我们通常都是用malloc函数&#xff0c;但是malloc函数对自定义类型是没什么问题的&#xff0…

Jenkins清理构建(自动)

需求背景实现方法 Dashboard-->Project-->配置-->General-->Discard old builds # 注意&#xff1a;自动清理构建历史将在下次构建时进行

【校招VIP】产品面试之职业规划

考点介绍&#xff1a; 对于刚入行的产品同学&#xff0c;由于行业知识和经验不足&#xff0c;只能执行上层的产品策略&#xff0c;但是与团队的沟通是非常重要的&#xff0c;产品经理就是沙丁鱼中的鲶鱼&#xff0c;必须要能够把控整个团队的开发节奏&#xff0c;知道如何最大化…

个微机器人开发接口

请求URL&#xff1a; http://域名地址/member/login域名地址开发者账号密码:后台系统自助开通 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/json 参数&#xff1a; 参数名必选类型说明account是string开发者账号password…

Swift 技术 视频播放器滚动条(源码)

一直觉得自己写的不是技术&#xff0c;而是情怀&#xff0c;一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的&#xff0c;希望我的这条路能让你们少走弯路&#xff0c;希望我能帮你们抹去知识的蒙尘&#xff0c;希望我能帮你们理清知识的脉络&#xff0…

core dump管理在linux中的前世今生

目录 一、什么是core dump&#xff1f; 二、coredump是怎么来的&#xff1f; 三、怎么限制coredump文件的产生&#xff1f; ulimit 半永久限制 永久限制 四、从源码分析如何对coredump文件的名字和路径管理 命名 管理 一些问题的答案 1、为什么新的ubuntu不能产生c…